目录
(3)给BSRR寄存器的相应位赋值就可以改变PB3的引脚状态
1.GPIO端口寄存器
在结构体GPIO_TypeDef中,11个GPIO端口寄存器都是32位。其中,有4个配置寄存器(GPIOx_MODER,GPIOx_OTYPER,GPIOx_OSPEEDR和GPIOx_PUPDR)、2个数据寄存器(GPIOx_IDR和GPIOx_ODR)、1个置位/复位寄存器(GPIOx_BSRR)、1个锁定(locking)配置寄存器(GPIOx_LCKR)、2个功能选择寄存器(alternate function selection registers,GPIOx_AFRH和GPIOx_AFRL)和1个按位复位寄存器GPIO_BRR。因为STM32G4xx中有7个GPIO端口,所以上述寄存器中的x是指A、B、C、D、E、F、G。
譬如GPIO端口B的输出寄存器,就是GPIOB_ODR。
2.HAL_GPIO_TogglePin()函数的定义
重写函数HAL_GPIO_TogglePin():
void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin)
{
uint32_t odr;
assert_param(IS_GPIO_PIN(GPIO_Pin));
odr = GPIOx->ODR;
GPIOx->BSRR = ((odr & GPIO_Pin)<<GPIO_NUMBER)|(~odr & GPIO_Pin);
}
第一个参数GPIOx的类型为GPIO_TypeDef,也就是说,GPIOx只能是结构体GPIO_TypeDef的或员,即GPIO端口的寄存器。注意,在函数HAL_GPIO_TogglePin()的参数义中,参数GPIOx的前面有个“*”,表示GPIOx这个变量是一个指针型变量。因此,在访问结构体成员时,就要采用指针的形式。
(1) 详细解读odr=GPIOx->ODR
首先,语句odr=GPIOx->ODR,表示用指计形式访间GPIO端口的输出数据寄有(ODR),输出寄存器ODR的值也就是该寄存器所对应端口的状态。前面调用函数HAL_GPIO_TogglePin时,用的参数是LED3_GPIO_Port,而LEDB_GPIO_Port在main.h文件中定义为GPIOB,所以程序运行到此处,odr = GPIOx→ODR就是取出GPIOB的输出数寄存器的值,赋给变量odr。
(2)详细解读给BSRR寄存器赋值
其次,函数的最后一条语句,是一个给BSRR寄存器赋值的语句:
GPIOx->BSRR = ((odr & GPIO_Pin)<<GPIO_NUMBER)|(~odr & GPIO_Pin);
语句中,等号右侧将odr“与”GPIO_Pin后左移GPIO_NUMBER(在stm32g4xx_hal_gpio.c中有定义,为16)位,然后与~odr &.GPIO_Pin的结果相“或”,结果赋值给BSRR寄存器。
GPIO_Pin是HAL_GPIO_TogglePin函数的第2个参数,它的值为16位无符号数(uint16_t),本例对应的是PB3,所以该值为0x0008(从右侧数第3位为1,其余均为0)。
将odr“与”GPIO_Pin后即可得到当前PB3引脚的状态:如果PB3引脚的状态为1(高电平),“与”后的结果为0x0008,将此结果左移16位(GPIO_NUMBER),也就是移位到高16位(odr被定义为32位无符号数),移位后第19位为1;如果PB3引脚的状态为0(低电平),相“与”后的结果为0x0,移位后第19位同样也为0。
odr取“反”后与GPIO_Pin相“与”的含义。仍然以PB3为例,如果PB3引脚当前状态为1,取反(~)后为0,则与GPIO_Pin相“与”后的结果为0x0;如果PB3引脚当前状态0,则相“与”后的结果为0x0008。
这样,这条赋值语句执行后的结果就比较清楚了:当I/O引脚状态为1时,等号右侧“或”之前括号内的值为1,之后的为0;当I/O引脚状态为0时,之前的值为0,之后的值为1。但“或”之前的值会移位到高16位,“或”之后的值则没有移位。也就是说,当I/O引脚状态为1时,给BSRR寄存器的高16位赋值;当I/O引脚状态为0时,给低16位赋值。
(3)给BSRR寄存器的相应位赋值就可以改变PB3的引脚状态
在STM32G4系列MCU的参考手册中,查到BSRR寄存器的结构:
31 |
30 |
29 |
28 |
27 |
26 |
25 |
24 |
23 |
22 |
21 |
20 |
19 |
18 |
17 |
16 |
BR15 |
BR14 |
BR13 |
BR12 |
BR11 |
BR10 |
BR9 |
BR8 |
BR7 |
BR6 |
BR5 |
BR4 |
BR3 |
BR2 |
BR1 |
BR0 |
w |
w |
w |
w |
w |
w |
w |
w |
w |
w |
w |
w |
w |
w |
w |
w |
15 |
14 |
13 |
12 |
11 |
10 |
9 |
8 |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
BS15 |
BS14 |
BS13 |
BS12 |
B811 |
BS10 |
BS9 |
BS8 |
BS7 |
BS6 |
BS5 |
BS4 |
BS3 |
BS2 |
BS1 |
BS0 |
w |
w |
w |
w |
w |
w |
w |
w |
w |
w |
w |
w |
w |
w |
w |
w |
BSRR寄存器的高16位为BR[15:0],其中BR是指bit reset,按位复位;低16位为BS[15:0],其中BS是指bit set,按位置位。这些位都是只写(write-only,字母w表示只写)的,读它是没有意义的只是返回0而已。BR[15;0]中的某位为1,会复位相应的输出数据位(ODx,即输出数据寄存器中的某位);SR[15:0]中的某位为1,会置位相应的输出数据位(ODx)。当然,要在硬件上实现端口或引脚输出状态的变化,是需要相应电路来实现的;不过,对使用者来说,需要的只是配置相应的的寄存器。
于是,库函数HAL_GPIO_TogglePin()通过操作GPIO的BSRR寄理了翻转I/0引脚状态的目的。
(4)BRR寄存器
在STM32G4系列MCU的参考手册中,查到BRR寄存器的结构:
31 |
30 |
29 |
28 |
27 |
26 |
25 |
24 |
23 |
22 |
21 |
20 |
19 |
18 |
17 |
16 |
Res |
Res |
Res |
Res |
Res |
Res |
Res |
Res |
Res |
Res |
Res |
Res |
Res |
Res |
Res |
Res |
15 |
14 |
13 |
12 |
11 |
10 |
9 |
8 |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
BR15 |
BR14 |
BR13 |
BR12 |
BR11 |
BR10 |
BR9 |
BR8 |
BR7 |
BR6 |
BR5 |
BR4 |
BR3 |
BR2 |
BR1 |
BR0 |
w |
w |
w |
w |
w |
w |
w |
w |
w |
w |
w |
w |
w |
w |
w |
w |
BRR寄存器虽然是32位的,但实际用到的只是低16位,分别对应该GPIO端口的16个I/O。BRR寄存器的低16位与BSRR寄存器的高16都是按位复位寄存器(BR[15:0]),所起的作用是一样的。
(5)ODR寄存器
GPIO的BRR和BSRR对输出引脚状态的改变,是与ODR相一致的。也就是说,PB3通过BRR复位后,GPIOB的ODR寄存器的相应位(第3位)的值也会变为0。ODR也只是用了低16位,分别对应GPIO端口的16个引脚,OD[15:0]就是相应引脚的状态数据,rw表示该位可读可写。
在STM32G4系列MCU的参考手册中,查到ODR寄存器的结构:
31 |
30 |
29 |
28 |
27 |
26 |
25 |
24 |
23 |
22 |
21 |
20 |
19 |
18 |
17 |
16 |
Res |
Res |
Res |
Res |
Res |
Res |
Res |
Res |
Res |
Res |
Res |
Res |
Res |
Res |
Res |
Res |
15 |
14 |
13 |
12 |
11 |
10 |
9 |
8 |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
OD15 |
OD14 |
OD13 |
OD12 |
OD11 |
OD10 |
OD9 |
OD8 |
OD7 |
OD6 |
OD5 |
OD4 |
OD3 |
OD2 |
OD1 |
OD0 |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |