What Is STM32 GPIO Register Access?

Accessing GPIO through BSRR and IDR instead of HAL_GPIO_WritePin/ReadPin: why, when it's worth it, and how it applies to row-by-row matrix scanning on STM32.

Updated 6 min read
Đọc bằng English Tiếng Việt
STM32 cover

HAL_GPIO_WritePin() and HAL_GPIO_ReadPin() are the standard way to work with GPIO in the STM32 HAL. They work fine for most cases. But in certain specific situations, scanning every row and reading every column of a keyboard matrix inside a tight loop, for example, accessing registers directly lets you write shorter code, read several pins in a single instruction, and cut out unnecessary overhead.

This note isn’t an argument for “never use HAL.” HAL is the right call for 90% of cases. The goal here is to explain exactly how BSRR and IDR work, so you can reach for them correctly when you genuinely need to.

1. GPIOB->BSRR: setting and resetting pins in one write

BSRR (Bit Set/Reset Register) is a 32-bit register split into two halves:

Bits [15:0]  - Set field:   writing 1 to bit N drives pin N HIGH
Bits [31:16] - Reset field: writing 1 to bit N+16 drives pin N LOW
Writing 0 to any bit: no effect

The important property here is that BSRR is atomic. You can set some pins and reset others in the same 32-bit write, with no intermediate glitch.

/* Drive PB3 LOW (reset field: bit 3+16 = bit 19) */GPIOB->BSRR = (uint32_t)(1U << 3) << 16U;/* Drive PB3 HIGH (set field: bit 3) */GPIOB->BSRR = (uint32_t)(1U << 3);/* Drive PB3 LOW and PB5 HIGH at the same time */GPIOB->BSRR = ((uint32_t)(1U << 3) << 16U) | (uint32_t)(1U << 5);

Compared to HAL:

/* HAL, one call per pin */HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET);/* Register, can handle multiple pins in one write */GPIOB->BSRR = (uint32_t)GPIO_PIN_3 << 16U;

For a keyboard matrix that needs to set and reset row pins on every scan loop iteration, the speed difference isn’t huge, but the code ends up shorter and clearer about intent.

2. GPIOB->IDR: reading every pin in one read

IDR (Input Data Register) is a 16-bit register, with each bit corresponding to one GPIO pin. A single IDR read returns the state of every pin at once.

/* Read the state of every GPIOB pin */uint32_t idr = GPIOB->IDR;/* Check a specific pin */if ((idr & GPIO_PIN_4) == 0U) {    /* PB4 is LOW */}/* Check several pins at once */uint32_t col_mask = GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7;uint32_t col_state = idr & col_mask;

Compared to HAL:

/* HAL, one call per pin, four times over */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, one read, mask the result */uint32_t idr = GPIOB->IDR;/* process idr & pin mask */

For matrix scanning, which needs to read 4 column pins after activating each row, reading IDR once and masking the result is cleaner than looping over HAL calls.

3. Applying it: keyboard matrix scanning

Here’s how the STM32 USB HID Keyboard project uses BSRR + IDR to scan a 4x4 matrix on 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++) {        /* Drive the row LOW */        MATRIX_GPIO->BSRR = (uint32_t)gRowPins[row] << 16U;        /* A short delay to let the signal settle */        __NOP(); __NOP(); __NOP(); __NOP();        /* Read every column at once */        for (uint8_t col = 0; col < 4; col++) {            if ((MATRIX_GPIO->IDR & gColPins[col]) == 0U) {                state |= (uint16_t)(1U << (row * 4 + col));            }        }        /* Return the row to HIGH */        MATRIX_GPIO->BSRR = (uint32_t)gRowPins[row];    }    return state;  /* bit N = keyLoc N is pressed */}

The function returns a 16-bit word, with each bit corresponding to a keyLoc, the key’s position in the matrix. The caller never needs to know anything about GPIO. It just works with this bitmask.

4. Counting set bits, Kernighan’s bit-counting trick

Once you have the 16-bit raw state, you need to count how many keys are currently pressed to detect a simultaneous press. Kernighan’s bit-counting trick is simple and needs no 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);  /* clear the lowest set bit */        count++;    }    return count;}

Each iteration clears exactly one set bit. The number of iterations equals the number of set bits. With a 16-bit word, and a keyboard matrix that only ever has a few keys pressed at once, this loop runs at most 2-3 times in practice.

5. When to use registers, when to use HAL

Criteria Direct register access HAL
Portability Only valid for that specific STM32 family Easier to port to a different line
Handling multiple pins One write/read covers several pins One call per pin
Atomic operation BSRR guarantees atomicity Need to check the HAL implementation
Code readability Requires understanding the register map Function names are self-explanatory
Debug friendliness Need to check the datasheet while reading code Easier for newcomers to read
Best fit for Scan loops that need speed or multi-pin access Ordinary GPIO in init code or callbacks

The general rule: default to HAL, and only switch to registers when you have a specific reason, with a comment explaining why.

/* * 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;

Public references

Found this article useful?

Share, give feedback, or support if you find this content valuable.

Feedback

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.

Biến note thành bài viết hoàn chỉnh

Notes là nơi ghi nhanh khái niệm.