STM32 位带操作——像 51 一样操作 GPIO

📅 2026/7/9 10:59:19
STM32 位带操作——像 51 一样操作 GPIO
问题STM32 的 GPIO 操作通常写HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET)很长。如果想像 51 那样P0_0 1一样简洁怎么办原理Cortex-M3/M4 支持位带操作Bit-Banding——将 1 bit 的地址映射到 1 个 32 位的别名地址。写这个别名地址就是操作对应的那一位。F407 的位带区域区域普通地址范围位带别名范围SRAM0x20000000~0x200FFFFF0x22000000~0x23FFFFFF外设0x40000000~0x400FFFFF0x42000000~0x43FFFFFFGPIO 的寄存器在外设区所以可以用位带操作。SRAM 位带区0x20000000 起同样支持位带操作用法类似。实现定义宏// 位带地址转换公式alias 0x42000000 (addr - 0x40000000) * 32 bit * 4 #define BITBAND(addr, bit) (*(volatile uint32_t *)(0x42000000 ((uint32_t)(addr) - 0x40000000) * 32 (bit) * 4)) ​ // GPIO 输出数据寄存器 (ODR) 地址 // 以 GPIOA 为例基址 0x40020000ODR 偏移 0x14 #define GPIOA_ODR (GPIOA_BASE 0x14) #define GPIOB_ODR (GPIOB_BASE 0x14) // 以此类推 ​ // 定义引脚位带别名 #define PA0 BITBAND(GPIOA_ODR, 0) #define PA1 BITBAND(GPIOA_ODR, 1) #define PB0 BITBAND(GPIOB_ODR, 0) // ... 需要哪个定义哪个使用PA0 1; // PA0 输出高 PA0 0; // PA0 输出低读引脚状态ODR 是输出寄存器读的是你设的值。如果要读引脚实际电平用输入数据寄存器IDR#define GPIOA_IDR (GPIOA_BASE 0x10) #define PA0_IN BITBAND(GPIOA_IDR, 0) ​ uint8_t val PA0_IN; // 读 PA0 引脚电平注意事项位带操作不经过 HAL 库GPIO 初始化还是要用 CubeMX 配位带别名地址是32 位访问操作是原子的单条 STR 指令硬件总线层级完成读-改-写注意 DMA 等总线主控器不受此原子性保护位带宏定义已含 volatile编译器不会将其优化掉但非 volatile 的普通变量仍需注意适用于性能敏感的场合如软件模拟 I2C/SPI 时序实用宏封装// 输出 #define PA_out(n) BITBAND(GPIOA_ODR, n) #define PB_out(n) BITBAND(GPIOB_ODR, n) ​ // 输入 #define PA_in(n) BITBAND(GPIOA_IDR, n) #define PB_in(n) BITBAND(GPIOB_IDR, n) ​ // 使用 PA_out(0) 1; // PA0 输出高 uint8_t key PA_in(1); // 读 PA1 电平相比 HAL 库的 WritePin/ReadPin位带操作编译后只有一条汇编指令速度快得多。