hello,小伙伴们,大家好。这里是左左右。在上期视频中我们通过修改ODR这个寄存器中的值也同样实现了小灯闪烁的功能。这样的写法在这个案例中是没什么问题的。但他其实是存在一定的隐患的,我们在给寄存器中PA15,这一位,置1和置0时。也同时把PA0到PA15的位也置零了。
我们在做一个完整的项目时。前面的代码可能已经对PA0到PA15中的某些引脚置1了。我们在这时将他们全部置0。程序不就乱套了?那么,有没有什么办法将PA15这一位单独置零或者置1呢?答案肯定是有的,首先,我们来尝试一下将PA15这一位单独置1。其实,也很简单。我们都知道1与任意值做或运算都等于1,而0与任意值做或运算都等于这个值本身。

基于这一原理。我们将GPIOA ODR这个指针指向的值与这个数做或运算。这样我们就实现了将PA15这一位单独置1,而不影响其他位的值。在代码中我们要将它转换为16进制的数。
那么,每次都要将2进制的数转换为16进制的数。是不是很容易出错?那么,有什么更简单的方法呢?其实,我们只要将16进制的0x1向左移动15位就可以了。接下来我们要将PA15这一位单独置0。我们都知道0与任意值做与运算都等于0,而1与任意值做与运算都等于这个值本身。这时将GPIO A ODR这个指针指向的值与这个数做与运算。
我们就实现了将PA15这一位单独置0,而不影响其他位的值。我们要将这个2进制的数转换为16进制数是不是更容易出错啊?其实,我们只要将另外一个2进制数取反就等价与这个2进制的数。这么看是不是就很眼熟了?我们只要将16进制的0x1向左移动15位就可以了。在STM 3 2 GPIO寄存器中除了ODR寄存器还有一个非常重要的寄存器,那就是BSRR寄存器。
这是它的偏移量。一会会用到,STM 3 2将32比特的BSRR寄存器分为高16位和低16位。高16位是用来清除ODR中0到15位的值。命名为BR位 这里的R就是RESET。设置为1会将ODR对应的位的值置为0,设置为0不影响,而低16位是用来设置ODR中0到15位的值。
命名为BS位 这里的S就是SET。设置为1会将ODR对应的位的值置为1,设置为0不影响,也就是说我们只需通过在BSSR这个寄存器中置1的操作。就可以实现ODR对应位置1或置0的操作。同样我们通过改变BSRR这个寄存器中的值也可以间接实现对小灯亮灭的控制。

那么,当高16位和低16位都同时置1该怎么办?在文档中也有说明,当高16位和低16位都同时置1时,BS位起作用。接下来我们打开STM 32 cube IDE来试一下。点击运行,小灯同样实现了亮灭的功能。接着,我们通过改变BSSR这个寄存器中值来实现小灯的亮灭。点击运行,是不是也没有问题?
接下来我们看看hal库具体是怎么操作寄存器的?我们按下Ctrl点击HAL_GPIO_WritePin这个函数。接着,顺藤摸瓜找到和它同名的.c文件。这两行代码作用是判断我们输入的这两个参数是否合法?不用管他。在这个函数中我们传入的PinState的值是GPIO_PIN_RESET。
所以,程序会跳转到这里。当我们看到这个符号,是不是就瞬间想到结构体指针了?这是因为HAL_GPIO_WritePin这个函数传入的第一个参数就是结构体指针。我们这里传入的GPIOA。所以GPIOA就是就是指向GPIO_TypeDef这个结构体的指针。指针的两个功能分别是指向功能和存放地址的功能。
指向的功能在传参时已经赋予了。接着,来找一下GPIOA中存放的地址。顺藤摸瓜我们可以找到GPIOA中存放的就是手册中GPIOA的基地址0x40010800。接着,我们找到定义GPIO_TypeDef这个结构体的地方。由于GPIOA的地址是0x40010800,由此就可推出ODR和BSRR的地址。

是不是很眼熟?这不就是手册中ODR和BSRR寄存器的地址嘛。接着,找到GPIO_PIN_15对应的值。此时的1处于BSRR寄存器低16位的最高位。将它左移16位。对应BSRR寄存器高16位的最高位置1。此时ODR PA15的值置0。所以hal库源码做的事和我们前面操作寄存器是一模一样的。
那么 ODR和 BSSR到底是什么呢?他们对应的就是前面讲过的位设置清除寄存器。和输出数据寄存器。这里有两条路可以控制IO口输出高低电平。一条是通过改变BSRR中值来改变ODR。另一条就是直接改变ODR中的值。和输出数据寄存器。这就将前面视频的知识串在一起了。通过这两期视频我们完成了ODR和 BSSR两个寄存器的讲解。
同时,也完成了HAL_GPIO_WritePin这个函数的源码讲解。其他内容我也会在后面的HAL库源码解析的课程中陆续讲解到的。好的,今天的视频就到这里了。为了能让大家高效、易懂的学习,每期视频我都倾注了大量的时间和心血在里面。您的关注,点赞和收藏是我持续更新下去的最大动力。
我们下期见。
我们在给寄存器中P A 15,这一位,置1和,置0时。设置为1,会,将ODR对应的位的值,置为0。设置为1,会,将ODR对应的位的值,置为1。
那么,我们该如何改变其中某一位中的值呢?stm32已经替我想好了。接着,我们来看BSRR这个寄存器。这个寄存器分为低16位和高16位。这里也是分别控制PA0到PA15。
typedef struct
{
__IO uint32_t CRL;
__IO uint32_t CRH;
__IO uint32_t IDR;
__IO uint32_t ODR;
__IO uint32_t BSRR;
__IO uint32_t BRR;
__IO uint32_t LCKR;
} GPIO_TypeDef;
#define GPIOA ((GPIO_TypeDef *)GPIOA_BASE)
#define GPIO_PIN_15 ((uint16_t)0x8000)
#define GPIOA_BASE (APB2PERIPH_BASE + 0x00000800UL)
#define APB2PERIPH_BASE (PERIPH_BASE + 0x00010000UL)
#define PERIPH_BASE 0x40000000ULuint32_t *GPIOA_ODR = (uint32_t*)0x40010800 + 0x0000000C;
*GPIOA_ODR = 0x00008000;
HAL_Delay(500);
*GPIOA_ODR = 0x00000000;HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET);
HAL_Delay(500);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_SET);
HAL_Delay(500);

这里我们打开实现LED小灯闪烁的那期视频的程序。来看看hal库具体是怎么操作寄存器的?打开HAL_GPIO_WritePin这个函数。这两行代码作用是判断我们输入的这两个参数是否合法?
下期视频 改代码 讲bssr寄存器 看输出数据寄存器图片 读源码。
uint32_t *GPIOA_ODR = (uint32_t*)(0x40010800 + 0x0000000C);
*GPIOA_ODR = 0x00008000;
HAL_Delay(500);
*GPIOA_ODR = 0x00000000;
HAL_Delay(500);uint32_t *GPIOA_ODR = (uint32_t*)(0x40010800 + 0x0000000C);
*GPIOA_ODR = *GPIOA_ODR | 0x1 << 15;
*GPIOA_ODR |= 0x1 << 15;
HAL_Delay(500);
*GPIOA_ODR = *GPIOA_ODR & ~(0x1 << 15);
*GPIOA_ODR &= ~(0x1 << 15);
HAL_Delay(500);