前言
提示:以下是本篇文章正文内容
一、原理分析
前面用C语言编写 LED 灯驱动的时候,每个寄存器的地址都需要写宏定义,这样做实在是太麻烦了,在STM32中,比如访问GPIOB 的寄存器 ODR :GPIOB->ODR
我们也可以参照32的形式将某个外设的所有寄存器写入到一个结构体里面,定义一个结构体指针指向外设的寄存器基地址,这样就可以通过这个结构体指针来访问这个外设的所有寄存器,因为在结构体中成员地址递增,并且属于同一个外设的寄存器地址基本上是相邻的(有些是保留寄存器)
在32中使用寄存器操作GPIO
GPIOE->CRL&=0XFF0FFFFF;
GPIOE->CRL|=0X00300000; //PE5 推挽输出
GPIOE->ODR|=1<<5; //PE5 输出高
需要配置GPIOE结构体指针的寄存器 CRL 和 ODR
GPIOE的宏定义:
#define GPIOE ((GPIO_TypeDef *) GPIOE_BASE)
GPIOE是一个指向地址GPIOE_BASE 的结构体指针,结构体为GPIO_TypeDef
GPIO_TypeDef结构体如下
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;
注:_IO是宏定义 一般是volatile修饰符
GPIO_TypeDef 结构体的成员变量有 CRL、 CRH、 IDR、 ODR,BSRR、 BRR 和 LCKR,都是 GPIO 的寄存器
每个成员变量都是 32 位(4 字节),寄存器在结构体中的位置都是按照其地址值从小到大排序的
结构体的地址的定义
#define GPIOE_BASE (APB2PERIPH_BASE + 0x1800)
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
#define PERIPH_BASE ((uint32_t)0x40000000)
GPIOE_BASE 就是 GPIOE 的基地址,GPIOE指针指向这个地址,每个寄存器之间相隔4个字节,只要设置好了基地址就可以访问到结构体成员
二、MX6U寄存器定义
1.外设结构体
将同属于一个外设的所有寄存器定义到一个结构体里面
eg: 定义GPIO外设寄存器组
参考如下:
typedef struct
{
volatile unsigned int DR;
volatile unsigned int GDIR;
volatile unsigned int PSR;
volatile unsigned int ICR1;
volatile unsigned int ICR2;
volatile unsigned int IMR;
volatile unsigned int ISR;
volatile unsigned int EDGE_SEL;
}GPIO_Type;
注:每个成员都使用“volatile”进行了修饰,目的是防止编译器优化
如果某个寄存器组中中间地址不连续需要将地址保留出来
eg:
在时钟外设寄存器组中,CCM_CCGR6和CCM_CMEOR寄存器之间相差了8个字节,我们必须将空的四个字节地址留出来,如果不添加保留位来占位的话就会导致寄存器地址错位
volatile unsigned int CCGR6;
volatile unsigned int RESERVED_3[1];
volatile unsigned int CMEOR;
RESERVED_3[1]就是预留出来的空间,保证了结构体地址连续递增的特点
2.结构体基地址
定义结构体寄存器组的基地址,也就是结构体中第一个成员变量
对于GPIO1结构体而言其基地址为:0X0209C000
#define GPIO1_BASE (0x0209C000)
3.结构体访问指针
定义访问结构体的指针,就可以通过该指针实现对寄存器组的操作,将结构体的基地址强制转换为结构体类型的指针
#define GPIO1 ((GPIO_Type *)GPIO1_BASE)
我们就可以通过GPIO1->GDIR访问寄存器
三、编译下载
基本上和前一节的一样,Makefile和链接脚本都一样。
总结
提示:这里对文章进行总结: