简介:
在学完51单片机之后,我们去学习32的时候,会发现编程的方法有很大的区别,让人非常的不适应,但是通过不断的调用相应外设的库函数之后,你也可以去编程STM32,来实现功能,但是你真的了解标准库吗?不少人只会局限在调用标准库,不论你是学完标准库还是初学标准库,都很有必要了解以下标准库的原理。
寄存器:
我们首先要明确我们编程到底在编些什么?
单片机寄存器在单片机编程中起着至关重要的作用,它们是与单片机硬件紧密关联的特殊存储单元,用于控制各种硬件功能和状态。单片机寄存器与编程之间的联系主要体现在以下几个方面:
1. 硬件控制:单片机寄存器直接控制着单片机的各种硬件功能,如输入/输出端口、定时器、串口通信等。通过编程操作这些寄存器,可以实现对硬件的控制和配置。
2. 状态监测:单片机寄存器中存储着各种硬件的状态信息,如中断标志、定时器计数值、输入端口状态等。通过编程读取这些寄存器的值,可以实时监测硬件的状态,并根据需要进行相应的处理。
3. 中断处理:单片机中断是一种重要的事件处理机制,通过编程配置中断寄存器,可以实现对各种中断事件的响应和处理。例如,可以通过设置中断使能位和中断优先级来控制中断的触发和处理顺序。
4. 外设通信:单片机通常需要与外围设备进行通信,如传感器、执行器、显示器等。编程时需要操作相应的寄存器来配置通信接口和协议,以实现与外设的数据交换和控制。
5. 优化性能:直接操作寄存器可以提高程序的执行效率和性能,因为与使用高级函数库相比,直接操作寄存器可以减少额外的开销和延迟,从而更好地满足实时性要求。
因此,单片机编程中的许多操作都是通过操作寄存器来实现的,程序员需要深入了解单片机寄存器的功能和用法,以充分利用单片机的硬件资源,并编写出高效可靠的程序。
标准库的本质:
别看标准库调用一大堆函数,其实你不断的通过拆开函数的一层层嵌套,最后还是发现,他还是通过写单片机的寄存器,来实现功能。
寄存器映射:
既然写寄存器,那就要知道寄存器的地址,这就是寄存器映射。存储器本身不具有地址信息,它的地址是由芯片厂商或用户分配,给存储器分配地址的过程就称为存储器映射,具体见图存储器映射。如果给存储器再分配一个地址就叫存储器重映射。
储存器区域功能划分:
*(unsigned int*)(0x4001 0C0C) = 0xFFFF;
// GPIOB 端口全部输出 高电平
#define GPIOB_ODR *(unsigned int*)(GPIOB_BASE+0x0C)
GPIOB_ODR = 0xFF;
记住绝对地址或许有点难,但是记住个寄存器的名字, 似乎简单不少,我们这里直接通过宏定义,实现直接往寄存器赋值。本质还是通过寄存器的地址,通过指针来对该寄存器进行读写。
stm32外设地址映射:
总线基地址 :
外设基地址:
这里面我们以 GPIO 这个外设来讲解外设的基地址,GPIO 属于高速的外设,挂载到APB2总线 上,具体见表格外设 GPIO 基地址。
外设寄存器:
c语言对寄存器的封装:
可以说,只要你看懂这个,那么你几乎就能知道标准库大体是怎么写出来的。
封装总线和外设基地址:
封装寄存器列表:
/* GPIO 寄存器列表 */
typedef struct
{
uint32_t CRH; /*GPIO 端口配置高寄存器 地址偏移: 0x04 */
uint32_t IDR; /*GPIO 数据输入寄存器 地址偏移: 0x08 */
uint32_t ODR; /*GPIO 数据输出寄存器 地址偏移: 0x0C */
uint32_t BSRR; /*GPIO 位设置/清除寄存器 地址偏移: 0x10 */
uint32_t BRR; /*GPIO 端口位清除寄存器 地址偏移: 0x14 */
uint16_t LCKR; /*GPIO 端口配置锁定寄存器 地址偏移: 0x18 */
} GPIO_TypeDef; uint32_t CRL; /*GPIO 端口配置低寄存器 地址偏移: 0x00 */
/* 使用 GPIO_TypeDef 把地址强制转换成指针 */
#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE)
#define GPIOC ((GPIO_TypeDef *) GPIOC_BASE)
#define GPIOD ((GPIO_TypeDef *) GPIOD_BASE)
#define GPIOE ((GPIO_TypeDef *) GPIOE_BASE)
#define GPIOF ((GPIO_TypeDef *) GPIOF_BASE)
#define GPIOG ((GPIO_TypeDef *) GPIOG_BASE)
#define GPIOH ((GPIO_TypeDef *) GPIOH_BASE)
/* 使用定义好的宏直接访问 */
/* 访问 GPIOB 端口的寄存器 */
GPIOB->BSRR = 0xFFFF; //通过指针访问并修改 GPIOB_BSRR 寄存器
GPIOB->CRL = 0xFFFF; //修改 GPIOB_CRL 寄存器
GPIOB->ODR =0xFFFF; //修改 GPIOB_ODR 寄存器
uint32_t temp;
temp = GPIOB->IDR; //读取 GPIOB_IDR 寄存器的值到变量 temp 中
/* 访问 GPIOA 端口的寄存器 */
GPIOA->BSRR = 0xFFFF;
GPIOA->CRL = 0xFFFF;
GPIOA->ODR =0xFFFF;
uint32_t temp;
temp = GPIOA->IDR; //读取 GPIOA_IDR 寄存器的值到变量 temp 中
总结:
因为32的寄存器太多也太复杂,如果说像51单片机来直接对寄存器编程,会非常的麻烦,需要不断的去查询地址,和各个寄存器和各个位的功能,虽然说,他为我们提供了便利,但是,真正的学会这个单片机,还是需要去查阅芯片手册,看看各个寄存器的功能以及单片机整体的架构。
这篇只是大概告诉读者标准库是如何封装寄存器来对stm32中的寄存器进行读写,真正的去看懂标准库和标准库的函数,还是需要读者仔细的去研究标准库的每行代码。
最后,十分感谢野火科技,这些知识点都是在他那里学来的,也很推荐大家去他那里进行学习。