当前位置:   article > 正文

零基础自学STM32-复习篇2——使用结构体封装GPIO寄存器_gpio_typedef

gpio_typedef

我们首先要了解寄存器的一个特点,他不是只针对一个外设,而是所有的外设都。
就拿GPIO的CRL,ODR寄存器来说
对于GPIOA——GPIOE都有一组功能相同的寄存器只是地址不一样而已A(主要是外设地址不同,比如GPIOA的外设地址和GPIOB的外设地址就不一样,即便寄存器的偏移量一样的),没必要每个寄存器都配置一遍。

这里就引入一个基本操作那就是:使用结构体对GPIO寄存器进行一次封装
主要参考:野火的《开发指南》

typedef unsigned  int uint32_t;
typedef unsigned short int uint16_t;
// unsigned int 占32位unsigned short int占16位
//下面使用结构体来封装
/*********************************/
//定义一个结构体变量使用关键字struct,并将这个结构体命名为GPIO_Typdef如下(不懂结构体的可以去找个视频看看看,不必深究会用就行。)
typedef struct  
{
   
	uint32_t CRL;//GPIO端口配置低寄存器器
	uint32_t CRH;//GPIO端口配置高寄存器
	uint32_t IDR;//GPIO端口输入寄存器
	uint32_t ODR;//GPIO端口输出寄存器
	uint32_t BSRR;//GPIO端口位置位/清除寄存器
	uint32_t BRR;//GPIO端口位清除寄存器
	uint32_t LCKR;//GPIO端口配置锁定寄存器
}GPIO_TypeDef;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

这里还使用了typedef关键字命名了所创建的结构体类型为GPIO_TypeDef
其中结构体成员有7个变量,变量名正是所对应的寄存器名字。
C语言规定了结构体变量的存储空间是连续的(这个正好和我们的寄存器的地址是连续的特点相对应起来。这一点很重要,我们在封装的时候一定要按顺序去封装)。

在这里插入图片描述
比如我们定义的这个结构体GPIO_TypeDef。这个结构体的首个寄存器的地址就是CRL寄存器的地址也就是0x4001 0C00(这个地址也是GPIOB这个外设的总线地址),那么结构体第二个成员CRH的地址就是0x4001 0C00再加上一个0x04这个偏移量,为什么?
我们的寄存器是32位的,也就是4个字节为一个寄存器的存储空间,从CRL到CRH加上0x04也就是CRH的地址。其他成员也是相应的去加上偏移量就可以。(注意这个偏移量是相对于总线APB2的基地址的偏移量)

我们这里依然以代码为分析对象

//这里是stm32f103.h头文件
//用来存放stm32寄存器映射的代码
//GPIOB

//外设基地址 peripheral

# define PERIPHBASE  ((unsigned int)0x40000000)   //片内外设基地址
	
//总线基地址

# define APB1PERIPH_BASE	PERIPHBASE   //APB1总线基地址与片内外设基地址是一样的
# define APB2PERIPH_BASE  (PERIPHBASE + 0x10000)   //APB2总线基地址
# define AHBPERIPH_BASE   (PERIPHBASE + 0x20000)    //AHB总线基地址(为了后面操作RCC时钟),这里采用的DMA1的地址作为基地址


# define RCC_BASE	(AHBPERIPH_BASE + 0x1000)	//RCC复位时钟控制地址,想让IO工作必须配置


# define GPIOB_BASE	(APB2PERIPH_BASE + 0x0C00)	//GPIOB地址,操作GPIO的前提是找到总线地址


# define RCC_APB2ENR	*(unsigned int* )(RCC_BASE + 0x18)  //APB2外设时钟使能,想让IO工作必须配置   0x18就是APB2使能寄存器相对于RCC时钟总线的偏移量



//下面的代码使用宏定义重新命名一个指针变量,并通过操作寄存器的绝对地址指针从而操作寄存器工作
//# define GPIOB_CRL	 *(unsigned int*)(GPIOB_BASE  + 0x00)	//端口配置地寄存器
//# define GPIOB_CRH 	 *(unsigned int*)(GPIOB_BASE  + 0x04)	//端口配置高寄存器
//# define GPIOB_ODR   *(unsigned int*)(GPIOB_BASE  + 0x0C)	//数据输出寄存器
//# define GPIOB_IDR   *(unsigned int*)(GPIOB_BASE + 0x08)  //输入数据寄存器
//# define GPIOB_BSRR  *(unsigned int*)(GPIOB_BASE + 0x10)  //端口位设置/清除寄存器
//# define GPIOB_BRR   *(unsigned int*)(GPIOB_BASE + 0x14)  //端口位清除寄存器
//# define GPIOB_LCKR  *(unsigned int*)(GPIOB_BASE + 0x18)  //端口配置锁定寄存器

typedef unsigned int uint32_t;//声明一个32位的变量类型
typedef unsigned short uint16_t;//声明一个16位的变量类型
	
typedef struct
{
   
	 /*
	这里解释一下为什么可以直接操作而不需要加偏移量
	我们的stm32是32位单片机,每次操作32位数,而寄存器也是每个寄存器占用4个字节,也正好是32位,
创建结构体本身就会分配一个连续的内存空间,而我们定义了每个成员作为32位的变量,那正好跟寄存器本身的地址相对应起来。
	
	
	*/
	uint32_t  CRL;  
	uint32_t  CRH;
	uint32_t  IDR;
	uint32_t  ODR;
	uint32_t  BSRR;
	uint32_t  BRR;
	uint32_t  LCKR;
	
}GPIO_TypeDef;

//让GPIOB的地址GPIOB_BASE转化为一个结构体类类型的变量,并重新命名为GPIOB
# define GPIOB  ((GPIO_TypeDef*)(GPIOB_BASE))



  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
//32部分
#include "stm32f10x.h"

void SystemInit(void);//不用管这条代码

int main (void)
{
   
	
	
	# if 0  //使用这个语句相当于注释掉下面的代码
	//使用寄存器绝对地址操作
	*(unsigned int *)0x40021018 |=((1)<<3);        //打开gpiob端口时钟
	//*(unsigned int *)0x40010C0C &=~(1<<0);          //Data_Output  CRL_REG   通用推挽输出模式
	
	*(unsigned int *)0x40010C0C &=~((1)<<0);	//Data_Output  CRL_REG
	
	*(unsigned int *)0x40010C00 |=((1)<<(4*0));   //Set_Output   ODR_REG  //数据输出寄存器
	
	#elif 0   //注释掉下面的代码
	
	RCC_APB2ENR |= ((1)<<3);          //GPIOB时钟使能,在头文件中用宏定义声明过了

	GPIOB_CRL |= ((1)<<(4*0));         //端口配置低寄存器
	GPIOB_ODR &= ~(unsigned int)(1<<0);   			//数据输出寄存器
	//GPIOB_ODR |= (1<<0); 
	
	#elif 1  //执行这部分代码,使用结构封装好的函数去配置寄存器的值(重点掌握)
	
	RCC_APB2ENR |= ((1)<<3);          //GPIOB时钟使能

	GPIOB->CRL |= ((1)<<(4*0)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/article/detail/56870
推荐阅读
相关标签
  

闽ICP备14008679号