STM32第四节:自己写库——构建库函数雏形(第一节)

本文介绍了如何在STM32F10x开发中通过自定义库函数,从基本的GPIOB寄存器宏定义扩展到使用结构体封装GPIOx寄存器,以提高编程效率和可维护性。
摘要由CSDN通过智能技术生成

目录

前言

自己写库——构建库函数雏形(第一节)

基本宏定义(GPIOB)

定义结构体(GPIOx)

如何转换基地址为GPIO_TypeDef的指针?

关于为什么使用int

小结


前言

        我们以前一直使用寄存器编程,通过参观官方手册进行寄存器的配置,这些是最底层的东西。但是我们不能一直用寄存器编程,在基本学会寄存器编程后,我们应该接着学习固件库的编程。


自己写库——构建库函数雏形(第一节)

基本宏定义(GPIOB)

        备份上一节的代码,打开stm32f10x.h文件,输入以下代码,这些代码通过宏定义#define定义了GPIOB端口的寄存器,通过编写地址再将地址赋给设定好的寄存器名称,即可以从该名称读取地址以及调用寄存器。

//用来存放STM32寄存器映射的代码

//外设  perirhral

#define  PERIPH_BASE      		 ((unsigned int)0x40000000)
#define  APB1PERIPH_BASE  		  PERIPH_BASE
#define  APB2PERIPH_BASE  		 (PERIPH_BASE + 0x10000)
#define  AHBPERIPH_BASE   		 (PERIPH_BASE + 0x20000)

#define  RCC_BASE         		 (AHBPERIPH_BASE + 0x1000)
#define  GPIOB_BASE       		 (APB2PERIPH_BASE + 0x0c00)

#define  RCC_APB2ENR      		 *(unsigned int*)(RCC_BASE + 0x18)

#define  GPIOB_CRL        		 *(unsigned int*)(GPIOB BASE+0x00)
#define  GPIOB_CRH        		 *(unsigned int*)(GPIOB BASE+0x04)
#define  GPIOB_IDR        		 *(unsigned int*)(GPIOB BASE+0x08)
#define  GPIOB_ODR        		 *(unsigned int*)(GPIOB BASE+0x0c)
#define  GPIOB_BSRR       		 *(unsigned int*)(GPIOB BASE+0x10)
#define  GPIOB_BRR        		 *(unsigned int*)(GPIOB BASE+0x14)
#define  GPIOB_LCKR       		 *(unsigned int*)(GPIOB BASE+0x18)

定义结构体(GPIOx)

        通过学习官方手册,我们可以发现,在GPIO寄存器地址映像中,GPIOx所含的7个寄存器都是32位,占用了四个字节,而且逐级递增。那么,学习过C语言的同学就能发现,这和结构体类型非常相似,那么我们是否可以尝试定义一个结构体,把我们所需要的GPIOx里的7个寄存器包含进来呢?接下来我们一起来尝试一下。

        至于为什么定义结构体,接下来写代码的时候就明白了。接下来我们定义一个类型为GPIO_TypeDef的结构体,把7个寄存器的名称用定义形式通过结构体进行定义。这样我们就定义了一个结构体。

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;

        当然,在此之前,我们要用typedef关键字重新定义关于int和short的名称:

typedef unsigned int  uint32_t; 
typedef unsigned short  uint16_t;

        那么为什么呢?

        GPIO_BASE是32位的,通过指针只能访问4字节的内存,而如果我们想要通过外设的基地址来访问外设所有的寄存器的话,那么我们如果使用结构体类型的指针,那么我们就可以访问一大块空间。那么就比直接访问每个外设的每个寄存器的地址要方便很多。

如何转换基地址为GPIO_TypeDef的指针?

        在C语言中,有一种转换叫强制类型转换,如下代码:

#define GPIOB  	((GPIO_TypeDef*)GPIOB_BASE)

        我们将基地址强制转换成GPIO_TypeDef结构体的指针之后,不妨宏定义该指针为GPIOB,而GPIOB就指向了GPIOB寄存器的内存,在这个内存里有着GPIOB端口的所有寄存器。

        接下来我们就可以通过GPIOB调用GPIO_TypeDef这个结构体中的寄存器地址,代码如图所示:

#include "stm32f10x.h"

int main(void)
{
#if 0
	//打开 GPIOB 端口的时钟
	*(unsigned int*)0x40021018 |= (1<<3);
	
	//配置IO口为输出
	*(unsigned int*)0x40010c00 |= ((1)<<(4*5));//*0,*1,*5
	
	//控制 ODR 寄存器
	*(unsigned int*)0x40010c0c &= ~(1<<0);
#elif 0
    //打开 GPIOB 端口的时钟
	RCC_APB2ENR |= (1<<3);

	//配置IO口为输出
	GPIOB_CRL |= ((1)<<(4*5));//*0,*1,*5
		
	//控制 ODR 寄存器
	GPIOB_ODR &= ~(1<<0);

#elif 1
	//打开 GPIOB 端口的时钟
	RCC_APB2ENR |= (1<<3);

	//配置IO口为输出
	GPIOB->CRL |= ((1)<<(4*5));//*0,*1,*5
	
	//控制 ODR 寄存器
	GPIOB->ODR &= ~(1<<0);
#endif
}

void SystemInit(void)
{
	//函数体为空,目的是为了骗过编译器不报错
}

        我们可以看到虽然在代码层面没有什么较大的变化,但是从方法论来说已经进步不少了。在创建结构体时,结构体有很多成员,而类型及成员分布的顺序都和寄存器排列的顺序一样。我们找到这个外设的基地址,以GPIOB端口为例,我们只要找到GPIOB_BASE这个基地址,然后把这个地址强制类型转换成结构体类型的指针,就可以操作所有外设的寄存器了。

关于为什么使用int

        使用int类型的定义,占四个字节,而正好GPIOx每个寄存器占用四个字节的地址,所以使用int类型。

小结

        本节课讲解了自己写库——构建库函数雏形,包括基本的宏定义(直接操作寄存器)以及使用结构体(库函数)。下节课我们讲解如何实现 RCC 这个外设的寄存器结构体声明,把时钟相关的代码改成寄存器结构体操作的方式,以及第二节内容。


点个关注不迷路,三连支持一下吧!

  • 35
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值