1.简介
在hal库中我们经常会用到HAL_GPIO_TogglePin这个翻转电平的函数,对IO口的输出电平进行反转,但大部分初学者也只止步于会用 对底层函数和如何结合参考手册来配置寄存器并没有深入的了解,所以本人今天尝试用一篇文章进行讲解。
2.讲解部分
读懂底层函数HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin) 以hal库为例。
我们来看这个函数,第一这是个无返回值的函数,有两个参数,第一个参数是一个指向GPIO_Typedef结构的指针GPIOX,第二个参数是一个uint16_t 类型的变量GPIO_Pin,进入函数定义了一个uint32_t类型的变量 odr ,然后就是检测GPIO口是否正常,odr = GPIOx->ODR; 将GPIOX这个结构体中的ODR寄存器的值赋值给odr这个变量,我们继续来学习ODR这个寄存器
从参考手册中我们可以知道的是,这个寄存器是与端口输出值有关的寄存器,通过读取这个寄存器,我们可以知道某个具体的GPIO口的输出值,然后就是该寄存器总共有32位(对应32单片机),前16位保存,后16可以进行读取和编写,后16的每一位都依次对应某个GPIO端的16个IO口 如GPIOA的pin0~pin15。
下一句话GPIOx->BSRR = ((odr & GPIO_Pin) << GPIO_NUMBER) | (~odr & GPIO_Pin); ,我们将一串复杂的操作符的值赋值给了GPIOx->BSRR寄存器,我们继续来学习这个寄存器 这个寄存器的名字叫做端口位设置或清除寄存器,顾名思义这个寄存器是用来配置端口的输出值的,总共有32位,我们分为低16位和高16位
1)置GPIOA->BSRR低16位的某位为’1’,则对应的I/O端口管脚置’1’;
置GPIOA->BSRR低16位的某位为’0’,则对应的I/O端口管脚保持不变。
2)置GPIOA->BSRR高16位的某位为’1’,则对应的I/O端口管脚置’0’;
从上面的话中我们可以知道的时候低16位,我们只要写入1相应的IO口就会输出高电平,而高16位 我们只要写入1它对应的IO口输出低电平也就是输出0
我们继续来看它的运算操作 ((odr & GPIO_Pin) << GPIO_NUMBER) | (~odr & GPIO_Pin);这是一段复杂的运算操作我们一句一句看,(odr & GPIO_Pin) << GPIO_NUMBER) 将odr与GPIO_Pin按位与操作,然后再将按位与操作之后的数值左移GPIO_NUMBER 个位置,即16个位置,如下图中的宏定义(
),然后继续将这个结果与~odr & GPIO_Pin 取或操作,~odr & GPIO_Pin 这句话是先对~odr 进行取反操作后再与GPIO_Pin 进行按位与操作,我们用实际列子进行讲解
HAL_GPIO_TogglePin(GPIOA, GPIO_Pin_0);且默认输出高电平 我们传入两个参数 GPIOA,GPIO_Pin_0,,此时我们GPIOx->ODR的值为0x01(16进制 因为我们上面说了默认输出高电平所以时0x01第一位是1), GPIO_Pin_0,对应的宏定义为,对应的数值为0x0001。
我们对这两个数值进行按位与(odr & GPIO_Pin_0)得到0x0001,然后再对0x0001进行16位移位操作,变为0x10000(注移位时要先将其转换为二进制进行移位) ,
(~odr & GPIO_Pin) 对odr进行取反操作,变为1111111111111110与GPIO_Pin_0,,进行按位与操作得到0x00(全部为0),最后将0x00与0x10000进行按位或操作,得到0x10000最后将这个值写入GPIOx->BSRR这个寄存器,我们刚才讲过这个寄存器,将0x10000写入后,该寄存器的17位写为1(高位),根据我们上面的讲解,高16位写1会输出低电平,那这样我们的PIN 0就会输出一个低电平,也就实现了我们的反转电平的操作,因为我们刚才说刚开始的时候输出高电平,现在经过一系列运算输出变为了低电平。