类似于51单片机中 sbit LED = P0^1;
直接操作 LED
变量就可以控制 P0_1
端口了。
STM32 中是通过对处在 位带区
的寄存器的对应位映射到 别名区
,再通过操作 别名区
来控制对应寄存器的位。
STM32 是32位系统总线,所以位带区的一个位对应别名区会膨胀为32位,即四个字节。
位带区
外设位带区的地址为:0X40000000 ~ 0X40100000
SRAM
的位带区的地址为:0X20000000 ~ X20100000
位带别名区地址
对于片上外设位带区的某个比特,记它所在字节的地址为 A
,位序号为 n∈[0,7]
,
则该比特在别名区的地址为:
AliasAddr = 0x42000000 + (A - 0x40000000)*8*4 + n*4
0x42000000
:别名区起始地址(A - 0x40000000)
:外设地址相对基地址偏移多少个字节(A - 0x40000000)*8
:偏移的字节总共有多少位,所以乘 8(A - 0x40000000)*8*4
:每一位膨胀为 32 位(四个字节),所以乘 4n*4
:所在字节上的第 n 位膨胀为 32 位,所以乘 4
操作别名区只对 LSB 有效,即第 0 位
同样对于 SRAM
位带区的某个比特在别名区的地址为:
AliasAddr = 0x22000000 + (A - 0x20000000)*8*4 + n*4
统一公式
#define BITBAND(addr, bitnum) ((addr & 0xF0000000) + 0x02000000 + ((addr & 0x000FFFFF)<<5) + (bitnum<<2))
最后我们就可以通过指针的形式操作这些位带别名区地址,最终实现位带区的比特位操作。
// 把一个地址转换成一个指针
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
// 把位带别名区地址转换成指针
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
GPIO 位带操作
#define BITBAND(addr, bitnum) ((addr & 0xF0000000) + 0x02000000 + ((addr & 0x000FFFFF)<<5) + (bitnum<<2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
// GPIOA ODR 寄存器地址映射
#define GPIOA_ODR_Addr (GPIOA_BASE+0x0C)
// 单独操作 GPIO 的某一个 IO 口,n(0,1,2...16),n 表示具体是哪一个 IO 口
#define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n)
int main(void){
/* LED 端口初始化 */
LED_GPIO_Config();
while(1){
PAout(10) = 0; // PA10 = 0,点亮 LED
SOFT_Delay(0x0FFFFF);
PAout(10) = 1; // PA10 = 1,熄灭 LED
SOFT_Delay(0x0FFFFF);
}
}