STM32+Cubemx
仔细回想,本科做比赛以来,对单片机的态度就是能用就行,能跑就行,并没有去深入追究里面寄存器或者嵌入式代码结构之类的东西。但是现在既然要读研究生了,就应该改变一下学习态度,学习到本质的东西。
本文是基于我的毕业设计要搭建一个实物雷达小车,其中嵌入式的部分需要能够对电机进行pid控制,采集编码器信息对速度进行反馈,陀螺仪数据的采集以及与树莓派之间的串口通信等。将记录对若干传感器的数据采集以及研究一下产品级的嵌入式代码结构是怎么写的,以便以后再次接触嵌入式代码的时候能够更加清晰,得心应手一些。
一.位带操作
作为HAL库的使用者,都知道HAL库一键配置的好用,但是对于写IO口的高地电平没有库函数PAout(n) 以及直接操作寄存器方便。当然可能会有说我直接移植一下sys文件中的地址宏定义就好了,没啥毛病,但是以后万一换了个芯片呢?我能自己写出来吗?这就是一个隐患。
首先我们要了解,32中并不是所有的寄存器都能进行位带操作,CM3权威指南中是这样写的:在CM3中,有两个区实现了位带,其中一个是 SRAM 区的最低 1MB 范围,第二个则是片内外设 区的最低 1MB 范围。而我们在这里介绍的是GPIO中的ODR和IDR寄存器的位带操作。
说一下ODR寄存器和IDR寄存器
ODR寄存器:是一个可读写寄存器,读数据时,读出来的数据可以判断当前IO口的高地电平,当 向寄存器写入时,就可以控制某一个IO口的高低电平输出。尽管这是一个32位寄存 器,但实际上只用到了16位(因为IO口只有PA0~PA15)。HAL库中的 HAL_GPIO_WritePin()就是调用的这个寄存器。
IDR寄存器:是一个只读寄存器,通过读寄存器的某一个位可以读取某一个引脚的高低电平 HAL库中的HAL_GPIO_ReadPin()就是调用的这个寄存器。
其实BRR和BSRR寄存器也想说一下的,但是查完手册发现本质上还是在控制ODR寄存器,话不多说直接上图:
介绍完寄存器重点说一下偏移关系,膨胀的本质是把每一个位都膨胀成32位,如何膨胀 呢乘以32再加上一个常量呗!(很直白)
上图,关系是这样的,看不懂吧,看不懂就对了我也看不懂,直接给公式:
这里我们只要在意最后一个等号后面的公式就行,我们来看一下代码是如何实现的:
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x02000000+((addr & 0x000FFFFF)<<5)+(bitnum<<2))
(addr & 0xF0000000):取地址的高四位,因为如果是SRAM的话是0x2000_0000高四位是0010,如果是外设地址的话是0x4000_0000,高四位是0100。取完高四位后再加上0x0200_0000,这样第一个加号前的常数就解决了。
((addr & 0x00FFFFFF)<<5)+(bitnum<<2):是啥意思呢,这里我们先理清楚一个概念,二进制数如何编程原来2倍的,就是左移运算,乘二倍就是左移一位,四倍就是左移两位依次类推。然后我们看下公式中(A-0x20000000)是如何实现的,根据第三个图我们可以看到,SRAM区中的所要进行位带的范围是0x2000_0000到0x200F_FFFF,那我们其实问题就是如何把最高三位的十六进制数去掉,留下低五位的16进制数,OK那就是做最简单的与运算,留下低五位的16进制数,乘以32就是再左移5位。最后的n*4,就是对应寄存器第几个端口了,就是bitnum*4。
二.寄存器地址的映射
这个其实没啥可说的,说白了就是相对地址,例如ODR寄存器的地址相对于GPIOA地址有个相对的偏移量,而GPIOA的地址又相对于APB2总线有一个偏移量,偏移量是多少就去看不同的开发手册了。
到这里我们应该对正点原子中sys文件夹中的
不会在感觉熟悉又陌生了。
三.Header.h头文件
Header.h文件是我在查看一个淘宝店铺要来的开发板资料中,他们的一款嵌入式小车代码中学到的,上述的位带操作定义宏就存在于这个文件中,此外Header.h中还会包含所有的我们所自己写的.h头文件(包括main.h)以及所有的全局变量和全局宏定义。