欧启标O老师STM32课程笔记(三)——GPIO模块寄存器的封装

3.1 结构体基础知识

3.1.1 结构体类型的定义

struct 结构体类型名
{
	成员列表
}

 例如:

struct student{
					long num; 
					char name[20];
					char sex;
					float score;
				};                                                              

3.1.2 结构体类型的变量和指针变量的声明

struct student stu_1, *p;//stu_1为结构体类型的变量,p为结构体类型的指针变量                                         

3.1.3 结构体指针及其成员的访问

 可以将stu的地址赋给指针变量p,然后可以使用p结合指针运算符"->"访问变量stu中的成员,例如:

p=&stu;//可以用p->num访问

3.1.4 结构体变量在内存中的存储

 结构体所占内存等于各成员所占内存之和,且各成员的存储空间依次连续排列。

3.2 GPIO寄存器的排列

3.2.1 STM32F1系列GPIO寄存器的排列

 STM32F1有7组IO口,分别为GPIOA、GPIOB、……、GPIOG,每组IO口有7个相关寄存器,各寄存器的名称、偏移地址及说明如下表所示。

序号寄存器名称偏移地址寄存器描述备注
1GPIOx_CRL0x00端口配置低寄存器stm32F1有7组GPIO,每组GPIO有16个GPIO口(0-15),配置每个GPIO口需要4位配置寄存器,所以需要64位,即两个32位,0-7为端口配置低寄存器GPIOx_CRL ,8-15为端口配置高寄存器GPIOx_CRH。
2GPIOx_CRH0x04端口配置高寄存器端口配置高寄存器
3GPIOx_IDR0x08端口输入数据寄存器共32位,高16位保留,低16位每个IO口对应一位。
4GPIOx_ODR0x0C端口输出数据寄存器共32位,高16位保留,低16位每个IO口对应一位。
5GPIOx_BSRR0x10端口位设置/清除寄存器共32位,高位BR:每个io对应一位,若设置为1,则设置对应的DDR寄存器位为0,若设置为0,则对对应的DDR位不产生影响。
低位BS:每个io对应一位,若设置为1,则设置对应的DDR寄存器位为1,若设置为0,则对对应的DDR位不产生影响。
BS优先级大于BR。
6GPIOx_BRR0x14端口位清除寄存器共32位,高16位保留,低16位每个IO口对应一位。
7GPIOx_LCKR0x18端口配置锁定寄存器共32位,高16位保留,低16位每个IO口对应一位。

3.2.2 STM32F4系列GPIO寄存器的排列

 STM32F4ZGT6有7组IO口,分别为GPIOA、GPIOB、……、GPIOG,每组IO口有10个相关寄存器,各寄存器的名称、偏移地址及说明如下表所示。
在这里插入图片描述

3.3 GPIO模块寄存器的封装

 由3.2可以看出,GPIO相关寄存器的偏移地址是连续的,每个寄存器占4个字节,另外,我们在3.1.4 中说到,结构体中各成员的存储空间依次连续排列,所以,若令结构体的首地址与GPIO寄存器的基地址相同,并使结构体的成员大小与寄存器大小相同,就可以通过访问结构体成员的方式访问寄存器。
 我们可以将STM32F1系列GPIO模块相关寄存器封装进一个结构体:

struct
{
  volatile uint32_t CRL;
  volatile uint32_t CRH;
  volatile uint32_t IDR;
  volatile uint32_t ODR;
  volatile uint32_t BSRR;
  volatile uint32_t BRR;
  volatile uint32_t LCKR;
} ;

 然后再为该结构体定义一个别名:

typedef struct
{
  volatile uint32_t CRL;
  volatile uint32_t CRH;
  volatile uint32_t IDR;
  volatile uint32_t ODR;
  volatile uint32_t BSRR;
  volatile uint32_t BRR;
  volatile uint32_t LCKR;
} GPIO_TypeDef;

 该结构体中成员大小均为32位,与寄存器大小一致,再令结构体的首地址与GPIO寄存器的基地址相同,即可利用结构体访问寄存器。

3.4 GPIO端口的定义

 用3.3所说的结构体类型定义一个指针变量,比如:

GPIO_TypeDef* GPIOx;			

​   这样,只要给变量GPIOx赋予正确的端口地址,则可以应用 GPIOx->成员 的方式访问特定端口的特定寄存器。比如GPIOB基地址为0x40021400,则只需将数据0x40021400进行如下的强制类型转换并赋值给GPIOx:

GPIOx = (GPIO_TypeDef*)(0X40010C00);         (1)			

​   即可通过GPIOx访问GPIOF的各个寄存器,如GPIOx->CRL访问GPIOF的CRL寄存器,GPIOx->CRH访问GPIOB的CRH寄存器。
​  通常,为了更加直观,我们都是先对代码段(1)的右边进行宏定义后再使用,比如

#define GPIOB    ((GPIO_TypeDef *) 0x0X40010C00)

 然后执行如下语句

GPIOx = GPIOB;					

​  即可将某GPIO口的基地址赋给指针变量GPIOx。然后使用GPIOx->CRL等方式实现对GPIOF的寄存器的访问。

3.5 实操-配实现LED0闪烁

 电路原理与寄存器的配置与本专栏第一篇文章一致,不再赘述,STM32F1系列完整代码如下:

//寄存器定义
#define RCC_APB2ENR  (*(volatile unsigned int*)(0x40021018))

//类型定义
#define uint16_t unsigned short
#define uint32_t unsigned int

//GPIO端口定义
typedef struct
{
  volatile uint32_t CRL;
  volatile uint32_t CRH;
  volatile uint32_t IDR;
  volatile uint32_t ODR;
  volatile uint32_t BSRR;
  volatile uint32_t BRR;
  volatile uint32_t LCKR;
} GPIO_TypeDef;

GPIO_TypeDef* GPIOB;

//函数声明
void LED_Init(void);
void Delay(void);

int main()
{
	GPIOB = (GPIO_TypeDef*) 0X40010C00;
	//初始化--LED0模块进行初始化
	LED_Init();
	
	//灯闪烁
	while(1)
	{
		
		GPIOB->ODR |= (1<<5);;
		Delay();
        GPIOB->ODR &=~(1<<5);//LED亮
		Delay();
	
	}
}

//延时函数
void Delay()
{
	uint16_t i,j;
	for(i=0;i<1000;i++)
		for(j=0;j<300;j++);
	return;
}

//LED0初始化函数
void LED_Init()
{
	//使能GPIOB时钟
    RCC_APB2ENR |= (1<<3);
    //设置PB5的功能
    GPIOB->CRL&= ~(15<<20);//对应位清零
    GPIOB->CRL|=1<<20;//设置为推挽输出,最大速度为10MHz
    //设置PB5输出高电平,LED灭
    GPIOB->ODR |= (1<<5);;
}

 欧启标O老师STM32F4系列完整代码如下:

/* 常用数据类型定义 */
#define uint8_t unsigned char 
#define uint16_t unsigned short
#define uint32_t unsigned int
/* 将寄存器封装成类,抽象成结构体数据类型 */
typedef struct
{
	volatile uint32_t MODER; //模式寄存器
	volatile uint32_t OTYPER; //电路驱动方式寄存器
	volatile uint32_t OSPEEDR; //响应速度寄存器
	volatile uint32_t PUPDR; //上下拉电路配置寄存器
	volatile uint32_t IDR; //输入数据寄存器
	volatile uint32_t ODR; //输出数据寄存器
	volatile uint32_t BSRR; //置位复位寄存器
	volatile uint32_t LCKR; volatile uint32_t AFR[2];
}GPIO_TypeDef;
#define GPIOF ((GPIO_TypeDef *) 0x40021400) //定义 GPIOF 代表 GPIOF 模块的首地址
/* 时钟系统相关寄存器的定义 */
#define RCC_AHB1ENR (*(volatile unsigned *)0x40023830) //外设时钟使能寄存器。=1 对应外设时钟使能
/*LED0 初始化函数和延时函数的声明*/
void Led_Init(void);
void Delay(void);
/*主函数*/
int main(void)
{
	Led_Init(); while(1)
	{
		GPIOF->ODR &= ~(1<<9); //LED0 亮
		Delay();
		GPIOF->ODR |= (1<<9); //LED0 灭
		Delay();
	}
}
/*LED0 的初始化*/ 
void Led_Init(void)
{
	RCC_AHB1ENR |= 1<<5; //使能 PORTF 时钟
	GPIOF->MODER &= ~(3<<(9*2)); //将配置 PF9 引脚相关位 bit18,bit19 清 0
	GPIOF->MODER |= (1<<(9*2)); //配置 PF9 为输出
	GPIOF->OTYPER &= ~(1<<9); //电路工作方式为推挽
	GPIOF->OSPEEDR &= ~(3<<(9*2)); //对应位清 0
	GPIOF->OSPEEDR |= (2<<(9*2)); //响应速度 50M,其他值亦可
}
/*延时函数*/ 
void Delay(void)
{
	int i, j;
	for(i = 0; i < 200; i ++)
	for(j = 0; j < 3000; j ++);
}


  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

硬件梁朝伟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值