构建库函数的雏形——参照野火的手册

这部分我没按照视频进行(视频中手法流畅节奏快,对我这种慢节奏的略显吃力),所以这里我选择以野火的教学手册进行学习。

自己构建库函数的雏形

定义外设寄存器结构体
我们在操作寄存器的时候,操作的是都寄存器的绝对地址,如果每个外设寄存器都这样操作,那将非常麻烦。我们考虑到外设寄存器的地址都是基于外设基地址的偏
移地址,都是在外设基地址上逐个连续递增的,每个寄存器占 32 个字节,这种方式跟结构体里面的成员类似。所以我们可以定义一种外设结构体,结构体的地址等于外设的基地址
(比如:

typedef unsigned int      uint32_t;
typedef unsigned short    uint16_t;

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

#define GPIOB   ((GPIO_TypeDef*)GPIOB_BASE)

),
这个结构体的成员就是外设下的所有寄存器,成员的排列顺序跟寄存器的顺序一样。
这样我们操作寄存器的时候就不用每次都找到绝对地址,只要知道外设的基地址就可以操作外设的全部寄存器,操作结构体的成员就成了直接配置寄存器。
那么首先就是结构体的定义:

#define __IO volatile//volatile 表示易变的变量

typedef struct  //定义一个结构体,GPIO的存器部分
{
   

	uint32_t CRL;// 端口配置低寄存器, 地址偏移 0X00
	uint32_t CRH;// 端口配置高寄存器, 地址偏移 0X04
	uint32_t IDR;// 端口数据输入寄存器, 地址偏移 0X08
	uint32_t ODR;// 端口数据输出寄存器, 地址偏移 0X0C
	uint32_t BSRR;// 端口位设置/清除寄存器,地址偏移 0X10
	uint32_t BRR;// 端口位清除寄存器, 地址偏移 0X14
	uint32_t LCKR;// 端口配置锁定寄存器, 地址偏移 0X18
	
}GPIO_TypeDef;  //这是结构体类型名GPIO_TypeDef


typedef struct//定义一个结构体,RCC的存器部分
{
   

	uint32_t CR;//时钟控制寄存器 偏移地址0x00
	uint32_t CFGR;// 时钟配置寄存器 地址偏移 0X04
	uint32_t CIR;// 时钟中断寄存器, 地址偏移 0X08
	uint32_t APB2RSTR;// APB2外设复位寄存器, 地址偏移 0X0C
	uint32_t APB1RSTR;// APB1外设复位寄存器,地址偏移 0X10
	
	uint32_t AHBENR;// AHB外设时钟使能寄存器, 地址偏移 0X14
	uint32_t APB2ENR;// APB2外设时钟使能寄存器, 地址偏移 0X18
	uint32_t APB1ENR;// APB1外设时钟使能寄存器, 地址偏移 0X1C
	uint32_t BDCR ;//备份域控制寄存器, 地址偏移 0X20
	uint32_t CSR;// 控制/状态寄存器, 地址偏移 0X24
	
	
}RCC_TypeDef;//结构体的类型名RCC_TypeDef

#define GPIOB   ((GPIO_TypeDef*)GPIOB_BASE)//先使用结构体指针指向我们的外设基地址,然后使用宏定义重新命名

这段代码在每个结构体成员前增加了一个“__IO”前缀,它的原型在这段代码的第一行,代表了 C 语言中的关键字“volatile”,在 C 语言中该关键字用于表示变量是易变的,要求编译器不要优化。
这些结构体内的成员,都代表着寄存器,而寄存器很多时候是由外设或 STM32 芯片状态修改的,也就是说即使 CPU 不执行代码修改这些变量,变量的值也有可能被外设修改、更新,所以每次使用这些变量的时候,我们都要求 CPU 去该变量的地址重新访问。
若没有这个关键字修饰,在某些情况下,编译器认为没有代码修改该变量,就直接从 CPU 的某个缓存获取该变量值,这时可以加快执行速度,但该缓存中的是陈旧数据,与我们要求的寄存器最新状态可能会有出入。

外设存储器映射
回看上面封装好的寄存器的结构体,我们应该能意识到。在操作寄存器的前提是找到外设的地址。所以我们仍然需要使用宏定义去重新命名。
在这里插入图片描述
在这里插入图片描述

//首先是片内外设BLOCK2的起始地址
# define PERIPH_BASE	((unsigned int)0x40000000) //如果直接写0x4000 0000那编译器并不会将这个数当做地址,所以我们使用强制类型转化(unsigned int)
//我们门查看参考手册值到GPIO都是挂在APB2这一条总线上的
//我们用BLOCK2的起始地址加上偏移量就能找到总线基地址
# define APB2PERIPH_BASE	(PERIPH_BASE + 0x10000 )//通过加上偏移量,就可以得到总线APB2的基地址

//操作GPIO的话还需要配置RCC时钟寄存器,而RCC是挂接在AHB总线上的,这里我们将DMA1的地址作为AHB的总线基地址来使用,偏移量就是0x00020000
# define AHBPERIPH_BASE		(PERIPH_BASE + 0x20000)

//接下来就需要配置外设地址
//GPIO这些端口都是在APB2这条总线上的
//只需要直接加上他们的偏移量就可以
 #define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)
 #define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00)
 #define GPIOC_BASE (APB2PERIPH_BASE + 0x1000)
 #define GPIOD_BASE (APB2PERIPH_BASE + 0x1400)
 #define GPIOE_BASE (APB2PERIPH_BASE + 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值