STM32库函数
一、什么是库函数
库函数其实就是固件的API (Application Program Interface),调用这些函数接口就可以很方便的配置STM32的寄存器,使开发人员可以脱离最底层的寄存器操作,简化开发。
二、采取什么方法
主要用的方法就是进行寄存器映射,通过宏定义、结构体、枚举的方式完成对底层代码的封装。同时寄存器内的每一种寄存器地址是16个口连着的,因此不同的设置却被分在了不同的寄存器上,这样子在配置的时候就比较麻烦,所以库函数封装了之后可以将1个口的多个设置连着设置。
之后为将这些基本的配置实现,就会写一些函数,将设置好的信息写入单片机内存里。
三、C语言对寄存器的封装
以GPIO口的寄存器为例
1、封装总线和外设基地址
用宏定义对外设和总线的基地址进行取别名,如下
/* 外设基地址 Block2 */
#define PERIPH_BASE ((unsigned int)0x40000000)
/* 总线基地址 */
#define APB1PERIPH_BASE PERIPH_BASE
#define APB2PERIPH_BASE (PERIPH_BASE + 0x00010000)
#define AHBPERIPH_BASE (PERIPH_BASE + 0x00020000)
/* GPIO 外设基地址 */
#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)
#define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00)
2、封装寄存器列表
如果用上述办法定义每一个GPIO的寄存器的话略显繁琐,因为每一个GPIO口都是有着一组相同功能的寄存器的。为了更方便的访问不同GPIO的寄存器,可以用C语言中结构体的语法对寄存器进行封装。
为何用结构体?
结构体可以用来对一段连续地址进行访问,正好可以用来访问一段连着的寄存器地址。
如下:
typedef unsigned int uint32_t; /*无符号 32 位变量*/
typedef unsigned short int uint16_t; /*无符号 16 位变量*/
/* GPIO 寄存器列表 */
typedef struct {
uint32_t CRL; /*GPIO 端口配置低寄存器 地址偏移: 0x00 */
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;
之后便可以通过结构体访问寄存器了,如下:
GPIO_TypeDef * GPIOx; //定义一个 GPIO_TypeDef 型结构体指针 GPIOx
GPIOx = GPIOB_BASE; //把指针地址设置为宏 GPIOH_BASE 地址
GPIOx->IDR = 0xFFFF;
GPIOx->ODR = 0xFFFF;
uint32_t temp;
temp = GPIOx->IDR; //读取 GPIOB_IDR 寄存器的值到变量 temp 中
3、修改寄存器的位操作方法
如果只希望修改某长串地址中的某一位的话可以通过如下的方式:
置1
unsigned char a = 0x02;
//a = 0000 0010
//现在希望将a的最高位置1
a |= (1<<7);//将1左移7位,然后进行或赋值
置0
unsigned char a = 0x02;
//a = 0000 0010
//现在希望将a的第2位置0
a &= ~(1<<1);//将1左移1位,取反,然后进行与赋值
取反
unsigned char a = 0x02;
//a = 0000 0010
//现在希望将a的第2位取反
a ^= (1<<1);//将1左移1位,然后进行取反赋值
如果想操作很多位的话可以如下:
unsigned char a = 0x02;
//a = 0000 0010
//现在希望将a改为0x32
a |= 0x30;