STM32 GPIO Register Access là gì?
Truy cập GPIO bằng BSRR và IDR thay vì HAL_GPIO_WritePin/ReadPin: tại sao, khi nào phù hợp, và cách áp dụng cho matrix scan row-by-row trên STM32.
HAL_GPIO_WritePin() và HAL_GPIO_ReadPin() là cách tiêu chuẩn để thao tác GPIO trong
STM32 HAL. Chúng hoạt động tốt cho phần lớn trường hợp. Tuy nhiên trong một số tình
huống cụ thể, ví dụ scan toàn bộ hàng và đọc toàn bộ cột của keyboard matrix trong một
vòng lặp nhanh, truy cập thẳng vào register cho phép viết code ngắn hơn, đọc nhiều pin
trong một instruction, và giảm overhead không cần thiết.
Ghi chú này không phải lập luận “đừng dùng HAL”. HAL là đúng cho 90% trường hợp. Mục tiêu là giải thích cụ thể BSRR và IDR hoạt động như thế nào để dùng đúng khi thật sự cần.
1. GPIOB->BSRR, set và reset pin trong một write
BSRR (Bit Set/Reset Register) là register 32-bit chia làm hai nửa:
Bits [15:0] - Set field: ghi 1 vào bit N → pin N lên HIGH
Bits [31:16] - Reset field: ghi 1 vào bit N+16 → pin N xuống LOW
Ghi 0 vào bất kỳ bit nào: không tác dụng
Ưu điểm quan trọng: BSRR là atomic. Có thể set một số pin và reset số pin khác trong cùng một write 32-bit, không xảy ra glitch trung gian.
/* Kéo PB3 xuống LOW (reset field: bit 3+16 = bit 19) */GPIOB->BSRR = (uint32_t)(1U << 3) << 16U;/* Kéo PB3 lên HIGH (set field: bit 3) */GPIOB->BSRR = (uint32_t)(1U << 3);/* Kéo PB3 xuống LOW và PB5 lên HIGH cùng lúc */GPIOB->BSRR = ((uint32_t)(1U << 3) << 16U) | (uint32_t)(1U << 5);So sánh với HAL:
/* HAL, một call cho một pin */HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET);/* Register, có thể xử lý nhiều pin trong một write */GPIOB->BSRR = (uint32_t)GPIO_PIN_3 << 16U;Với keyboard matrix cần set/reset từng row pin trong vòng lặp scan, sự chênh lệch không lớn về tốc độ nhưng code ngắn và rõ ý định hơn.
2. GPIOB->IDR, đọc tất cả pin trong một read
IDR (Input Data Register) là register 16-bit, mỗi bit ứng với một pin GPIO. Đọc IDR
một lần trả về trạng thái tất cả pin cùng lúc.
/* Đọc trạng thái tất cả GPIOB pins */uint32_t idr = GPIOB->IDR;/* Kiểm tra pin cụ thể */if ((idr & GPIO_PIN_4) == 0U) { /* PB4 đang LOW */}/* Kiểm tra nhiều pin cùng lúc */uint32_t col_mask = GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7;uint32_t col_state = idr & col_mask;So sánh với HAL:
/* HAL, một call cho một pin, bốn lần */if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4) == GPIO_PIN_RESET) { ... }if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_5) == GPIO_PIN_RESET) { ... }if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_6) == GPIO_PIN_RESET) { ... }if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7) == GPIO_PIN_RESET) { ... }/* Register, một lần đọc, bitmask trên kết quả */uint32_t idr = GPIOB->IDR;/* process idr & pin mask */Với matrix scan cần đọc 4 column pin sau mỗi row activation, đọc IDR một lần rồi mask là sạch hơn vòng lặp HAL call.
3. Ứng dụng: keyboard matrix scan
Đây là cách project STM32 USB HID Keyboard dùng BSRR + IDR để scan 4×4 matrix trên GPIOB:
#define MATRIX_GPIO GPIOB/* Precomputed pin arrays */static const uint16_t gRowPins[4] = { GPIO_PIN_0, GPIO_PIN_1, GPIO_PIN_2, GPIO_PIN_3 };static const uint16_t gColPins[4] = { GPIO_PIN_4, GPIO_PIN_5, GPIO_PIN_6, GPIO_PIN_7 };uint16_t MatrixScan_ReadRaw(void){ uint16_t state = 0U; for (uint8_t row = 0; row < 4; row++) { /* Kéo row xuống LOW */ MATRIX_GPIO->BSRR = (uint32_t)gRowPins[row] << 16U; /* Delay ngắn để tín hiệu ổn định */ __NOP(); __NOP(); __NOP(); __NOP(); /* Đọc tất cả column cùng lúc */ for (uint8_t col = 0; col < 4; col++) { if ((MATRIX_GPIO->IDR & gColPins[col]) == 0U) { state |= (uint16_t)(1U << (row * 4 + col)); } } /* Trả row về HIGH */ MATRIX_GPIO->BSRR = (uint32_t)gRowPins[row]; } return state; /* bit N = keyLoc N đang nhấn */}Hàm trả về 16-bit word, mỗi bit tương ứng với một keyLoc (vị trí phím trong matrix). Caller không cần biết về GPIO, chỉ cần làm việc với bitmask này.
4. Đếm số bit đang set, Kernighan bit-count
Sau khi có 16-bit raw state, cần đếm bao nhiêu phím đang nhấn để phát hiện simultaneous press. Kernighan bit-count là cách đơn giản và không cần lookup table:
uint8_t MatrixScan_CountPressed(uint16_t rawState){ uint8_t count = 0U; uint16_t mask = rawState; while (mask != 0U) { mask &= (uint16_t)(mask - 1U); /* xóa bit thấp nhất đang set */ count++; } return count;}Mỗi iteration xóa đúng một bit set. Số iteration bằng số bit đang set. Với 16-bit word và keyboard matrix chỉ có một vài phím nhấn cùng lúc, vòng lặp này chạy tối đa 2-3 lần trong thực tế.
5. Khi nào dùng register, khi nào dùng HAL
| Tiêu chí | Register trực tiếp | HAL |
|---|---|---|
| Portability | Chỉ đúng với STM32 family đó | Có thể port sang dòng khác dễ hơn |
| Xử lý nhiều pin | Một write/read cho nhiều pin | Một call cho một pin |
| Atomic operation | BSRR đảm bảo atomic | Cần check HAL implementation |
| Code readability | Cần hiểu register map | Tên hàm tự mô tả |
| Debug friendly | Phải check datasheet khi đọc code | Dễ đọc hơn với người mới |
| Phù hợp với | Scan loop cần tốc độ hoặc multi-pin | GPIO thông thường trong init/callback |
Nói chung: dùng HAL làm default, chuyển sang register khi có lý do cụ thể và ghi comment rõ lý do.
/* * Direct register access (BSRR/IDR) instead of HAL_GPIO for matrix scan: * - All matrix pins are on GPIOB, single IDR read covers all columns * - BSRR allows atomic set/reset of row pins * - Eliminates per-pin HAL call overhead in the tight scan loop */MATRIX_GPIO->BSRR = (uint32_t)gRowPins[row] << 16U;Bài liên quan
- Keyboard Matrix Fundamentals là gì?
- Matrix Scanning Algorithm là gì?
- Debounce Algorithm là gì?
- Project: STM32G0 USB Lab: Key Input Pipeline
Nguồn tham khảo public
- STM32G0B1RE Reference Manual, Chapter: General-purpose I/Os (GPIO), BSRR và IDR register description
- STM32 HAL GPIO source,
xem
HAL_GPIO_WritePinimplementation để hiểu HAL dùng BSRR internally
Bài viết này hữu ích với bạn?
Chia sẻ, góp ý, hoặc ủng hộ nếu bạn thấy nội dung này có giá trị.
Nội dung liên quan
Một số bài viết, ghi chú hoặc project có liên quan đến nội dung bạn vừa đọc.
STM32G0 USB Lab: Key Input Pipeline
Keypad 4x4 matrix trên STM32G0B1RE: quét bằng BSRR/IDR trực tiếp, TIM6 5ms, debounce 2 mẫu, event queue và chính sách simultaneous-error cho ma trận không diode.
Debounce Algorithm là gì?
Tại sao phím cơ học bounce và cách xử lý bằng counter-based debounce, timestamp debounce, scan buffer và state machine trong firmware.
Thiết kế input pipeline trước tầng USB
Giải thích thiết kế tạo input từ keypad qua các tầng scan, debounce, key policy, event queue và những đánh đổi trước khi tạo HID report.
Biến note thành bài viết hoàn chỉnh
Notes là nơi ghi nhanh khái niệm.