STM32教程第四节:自己编写库函数——构建库函数雏形(第二节)

目录

前言

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

RCC外设寄存器结构体声明

端口置位或复位函数

防止重复定义框架

编写端口置位或复位函数代码

 编写到主函数实现替换ODR寄存器的作用

   代码展示

代码解释       

小结


前言

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


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


RCC外设寄存器结构体声明

        首先,我们要定义RCC的基地址,通过PERIPH_BASE定义AHBPERIPH_BASE,然后就可以定义RCC_BASE。在定义完RCC_BASE后,我们就可以像上一节的GPIOB宏定义一样,使用#define RCC对其进行基地址定义。

#define  PERIPH_BASE      		 ((unsigned int)0x40000000)
#define  AHBPERIPH_BASE   		 (PERIPH_BASE + 0x20000)
#define  RCC_BASE         		 (AHBPERIPH_BASE + 0x1000)

#define  RCC  	((RCC_TypeDef*)RCC_BASE)

         然后我们只需要改变寄存器名称,就可以定义好RCC时钟端口的结构体。

typedef struct
{
	uint32_t CR;
	uint32_t CFGR;
	uint32_t CIR;
	uint32_t APB2RSTR;
	uint32_t APB1RSTR;
	uint32_t AHBENR;
	uint32_t APB2ENR;
	uint32_t APB1ENR;
	uint32_t BDCR;
	uint32_t CSR;
}RCC_TypeDef;

#define  RCC  	((RCC_TypeDef*)RCC_BASE)

端口置位或复位函数

防止重复定义框架

        之前我们编写的函数是针对于GPIOB这个外设,那我们不妨新建两个驱动库文件,把宏定义,函数,声明什么的都放进去。于是就有了stm32f10x_gpio.c和stm32f10x_gpio.h文件。那么在stm32f10x_gpio.c中,我们要先包含头文件stm32f10x_gpio.h,把stm32f10x_gpio.h包含到stm32f10x_gpio.c中。

        然后打开stm32f10x_gpio.h文件,写下如下代码:

#ifndef __STM32F10X_GPIO_H
#define __STM32F10X_GPIO_H

#include "stm32f10x.h"

#endif /* __STM32F10X_GPIO_H */

        这段代码用于在STM32上操作GPIO(通用输入输出)引脚。接下来逐行解释下代码:

    #ifndef __STM32F10X_GPIO_H: 这是一个预处理指令,用于检查是否已经定义了__STM32F10X_GPIO_H宏。如果未定义,则继续执行下面的代码,否则跳过。

    #define __STM32F10X_GPIO_H: 定义了__STM32F10X_GPIO_H宏,以防止头文件被多次包含。

    #include "stm32f10x.h": 包含了STM32F10X头文件,其中包含了STM32的寄存器定义和常用函数等。

    #endif /* __STM32F10X_GPIO_H */: 结束了条件编译指令,确保只有在未定义__STM32F10X_GPIO_H宏时才会包含该头文件。

        这段代码的作用是定义了一个用于STM32F10X GPIO操作的头文件,并防止多次包含同一个头文件。通过包含这个头文件,可以在STM32上进行GPIO引脚的操作。

#include "stm32f10x.h"
#include "stm32f10x_gpio.h"

        但是当我们把stm32f10x_gpio.h头文件包含进main.c主函数里时,发现有两个error,检查发现是重复定义,原因是我们在stm32f10x_gpio.h中已经引入stm32f10x.h文件,而这个操作会把stm32f10x.h文件中的所有代码拷贝一份到stm32f10x_gpio.h文件中。那么怎么处理这个问题呢?

        我们可以选择直接注释掉上面一行代码,使得不再是重复定义,但是我们有更加严谨的做法,我们打开stm32f10x.h文件,写一个相类似的代码:

#ifndef __STM32F10X_H
#define __STM32F10X_H
//用来存放STM32寄存器映射的代码

//外设  perirhral

#endif /* __STM32F10X_H */

        我们在以后的代码编写过程中,要保持这个良好的习惯,不管该头文件中是否有内容,都在头文件中都以此格式编写框架,防止重复调用同一个文件。

编写端口置位或复位函数代码

        打开stm32f10x_gpio.c文件,开始编写函数。我们知道,要定义一个置位或复位函数,要有两个输入变量,即GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin,第一个GPIO结构体类型变量,第二个short类型的Pin变量。而Pin变量就是调节哪一位置1的变量代码,如此这般,我们就可以通过宏定义来定义GPIO_Pin_x的地址,如下图所示:

#define GPIO_Pin_0    ((uint16_t)0x0001)  /*!< 选择Pin0 */    //(00000000 00000001)b
#define GPIO_Pin_1    ((uint16_t)0x0002)  /*!< 选择Pin1 */    //(00000000 00000010)b
#define GPIO_Pin_2    ((uint16_t)0x0004)  /*!< 选择Pin2 */    //(00000000 00000100)b
#define GPIO_Pin_3    ((uint16_t)0x0008)  /*!< 选择Pin3 */    //(00000000 00001000)b
#define GPIO_Pin_4    ((uint16_t)0x0010)  /*!< 选择Pin4 */    //(00000000 00010000)b
#define GPIO_Pin_5    ((uint16_t)0x0020)  /*!< 选择Pin5 */    //(00000000 00100000)b
#define GPIO_Pin_6    ((uint16_t)0x0040)  /*!< 选择Pin6 */    //(00000000 01000000)b
#define GPIO_Pin_7    ((uint16_t)0x0080)  /*!< 选择Pin7 */    //(00000000 10000000)b

#define GPIO_Pin_8    ((uint16_t)0x0100)  /*!< 选择Pin8 */    //(00000001 00000000)b
#define GPIO_Pin_9    ((uint16_t)0x0200)  /*!< 选择Pin9 */    //(00000010 00000000)b
#define GPIO_Pin_10   ((uint16_t)0x0400)  /*!< 选择Pin10 */   //(00000100 00000000)b
#define GPIO_Pin_11   ((uint16_t)0x0800)  /*!< 选择Pin11 */   //(00001000 00000000)b
#define GPIO_Pin_12   ((uint16_t)0x1000)  /*!< 选择Pin12 */   //(00010000 00000000)b
#define GPIO_Pin_13   ((uint16_t)0x2000)  /*!< 选择Pin13 */   //(00100000 00000000)b
#define GPIO_Pin_14   ((uint16_t)0x4000)  /*!< 选择Pin14 */   //(01000000 00000000)b
#define GPIO_Pin_15   ((uint16_t)0x8000)  /*!< 选择Pin15 */   //(10000000 00000000)b
#define GPIO_Pin_All  ((uint16_t)0xFFFF)  /*!< 选择全部引脚*/ //(11111111 11111111)b

void GPIO_SetBits(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin);
void GPIO_ResetBits( GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin );

         第一个是置位函数,第二个是复位函数,写在stm32f10x_gpio.c文件中,并且在stm32f10x_gpio.h文件中引用。函数内容的话,就是通过GPIOB结构体寄存器->引用BSRR或BRR寄存器,和我们刚刚建立的GPIO_Pin_x宏定义地址进行相与操作置位。并且记得要在stm32f10x_gpio.h文件中进行函数的声明。

#include "stm32f10x_gpio.h"

void GPIO_SetBits(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin)
{
	GPIOx->BSRR |= GPIO_Pin;
}

void GPIO_ResetBits( GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin )
{
	GPIOx->BRR |= GPIO_Pin;
}

 编写到主函数实现替换ODR寄存器的作用

        把原主函数上面的复制一份,#elif改为1,取消ODR寄存器的代码,把我们的置位函数和复位函数写入,观察PB口,即LED灯是否亮起熄灭。

#elif 1
	//打开 GPIOB 端口的时钟
	RCC->APB2ENR |= ((1)<<3);
	
	//配置IO口为输出
	GPIOB->CRL &= ~((0x0f)<<(4*0));
	GPIOB->CRL |= ((1)<<(4*0));//*0,*1,*5
	
	GPIO_SetBits(GPIOB,GPIO_Pin_0);
	GPIO_ResetBits(GPIOB,GPIO_Pin_0);

   代码展示

         接下来展示主函数完整代码:

#include "stm32f10x.h"
#include "stm32f10x_gpio.h"

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

#elif 0
	//打开 GPIOB 端口的时钟
	RCC->APB2ENR |= ((1)<<3);
	
	//配置IO口为输出
	GPIOB->CRL &= ~((0x0f)<<(4*0));//*0,*1,*5
	GPIOB->CRL |= ((1)<<(4*0));
	
	//控制 ODR 寄存器
	GPIOB->ODR &= ~(1<<0);
	//GPIOB->ODR |= (1<<0);
	
#elif 1
	//打开 GPIOB 端口的时钟
	RCC->APB2ENR |= ((1)<<3);
	
	//配置IO口为输出
	GPIOB->CRL &= ~((0x0f)<<(4*0));
	GPIOB->CRL |= ((1)<<(4*0));//*0,*1,*5
	
	GPIO_SetBits(GPIOB,GPIO_Pin_0);
	GPIO_ResetBits(GPIOB,GPIO_Pin_0);
#endif
}

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

代码解释       

        这段代码片段是用于控制STM32微控制器上的GPIO引脚的。该代码演示了使用寄存器级别访问和STM32标准外设库实现相同功能的不同方法。

        在main函数中,有四个部分被#if 0#elif 1预处理指令包围。每个部分演示了控制GPIO引脚的不同方法:

  1. 第一部分使用直接寄存器访问通过写入内存地址来控制GPIOB引脚0。
  2. 第二部分使用STM32外设库提供的寄存器定义来实现相同的功能。
  3. 第三部分类似于第二部分,但使用箭头运算符(->)来访问结构成员,而不是点运算符。
  4. 第四部分使用STM32自定义外设库中的函数来设置和复位GPIOB上的GPIO引脚0。

   SystemInit函数是空的,目的是欺骗编译器不报错。

小结

        本节课我们讲解了如何实现 RCC 这个外设的寄存器结构体声明,把时钟相关的代码改成寄存器结构体操作的方式,以及编写函数库(端口置位以及复位函数),包括防止重复定义框架·,具体代码以及主函数的更新还有main函数代码的解释。下节课我们学习如何更加系统的优化程序以及运用STM32官方给出的标准库。

作者:Zhang-jk

物联沃分享整理
物联沃-IOTWORD物联网 » STM32教程第四节:自己编写库函数——构建库函数雏形(第二节)

发表评论