外设寄存器结构体定义

上一节中我们在操作寄存器的时候,操作的是都寄存器的绝对地址,如果每个外设寄存器都这样操作,那将非常麻烦。

我们考虑到外设寄存器的地址都是基于外设基地址的偏移地址,都是在外设基地址上逐个连续递增的,每个寄存器占 32 个字节,这种方式跟结构体里面的成员类似。所以我们可以定义一种外设结构体,结构体的地址等于外设的基地址,结构体的成员等于寄存器,成员的排列顺序跟寄存器的顺序一样。这样我们操作寄存器的时候就不用每次都找到绝对地址,只要知道外设的基地址就可以操作外设的全部寄存器,即操作结构体的成员即可

在工程中的“stm32f10x.h”文件中,我们使用结构体封装 GPIO 及 RCC 外设的的寄存器。结构体成员的顺序按照寄存器的偏移地址从低到高排列,成员类型跟寄存器类型一样。
stm32f10x.h

  1. #define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00)
  2. #define RCC_APB2ENR *(unsigned int*)(RCC_BASE + 0x18)
  3. //#define GPIOB_CRL *(unsigned int*)(GPIOB_BASE + 0x00)
  4. //#define GPIOB_CRH *(unsigned int*)(GPIOB_BASE + 0x04)
  5. //#define GPIOB_IDR *(unsigned int*)(GPIOB_BASE + 0x08)
  6. //#define GPIOB_ODR *(unsigned int*)(GPIOB_BASE + 0x0C)
  7. //#define GPIOB_BSRR *(unsigned int*)(GPIOB_BASE + 0x10)
  8. //#define GPIOB_BRR *(unsigned int*)(GPIOB_BASE + 0x14)
  9. //#define GPIOB_LCKK *(unsigned int*)(GPIOB_BASE + 0x18)
  10. typedef unsigned int uint32_t;
  11. typedef unsigned short uint16_t;
  12. typedef struct
  13. {
  14. uint32_t CRL;
  15. uint32_t CRH;
  16. uint32_t IDR;
  17. uint32_t ODR;
  18. uint32_t BSRR;
  19. uint32_t BRR;
  20. uint32_t LCKK;
  21. }GPIO_TypeDef;
  22. //声明了一个结构体,结构体中有很多成员,
  23. #define GPIOB ((GPIO_TypeDef*)GPIOB_BASE) //强制转换成结构体类型指针

构建库函数

在文件夹下新建gpio.c和.h文件
image.png
添加.h文件
image.png

双击Source Grouup,添加.c文件
image.png
stm32f10x_gpio.c

  1. #include "stm32f10x_gpio.h"
  2. void GPIO_SetBits(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin)
  3. {
  4. GPIOx->BSRR |= GPIO_Pin;
  5. }
  6. void GPIO_ResetBits(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin)
  7. {
  8. GPIOx->BRR |= GPIO_Pin;
  9. }

stm32f10x_gpio.h
把stm32f10x_gpio.c程序中定义的置位、复位函数的声明部分放到gpio.h中,还有GPIO的引脚号定义。

  1. #ifndef __STM32F10X_GPIO_H
  2. #define __STM32F10X_GPIO_H
  3. #include "stm32f10x.h"
  4. /* GPIO引脚号定义*/
  5. #define GPIO_Pin_0 ((uint16_t)0x0001) /*!< 选择Pin0 */ //(00000000 00000001)b
  6. #define GPIO_Pin_1 ((uint16_t)0x0002) /*!< 选择Pin1 */ //(00000000 00000010)b
  7. #define GPIO_Pin_2 ((uint16_t)0x0004) /*!< 选择Pin2 */ //(00000000 00000100)b
  8. #define GPIO_Pin_3 ((uint16_t)0x0008) /*!< 选择Pin3 */ //(00000000 00001000)b
  9. #define GPIO_Pin_4 ((uint16_t)0x0010) /*!< 选择Pin4 */ //(00000000 00010000)b
  10. #define GPIO_Pin_5 ((uint16_t)0x0020) /*!< 选择Pin5 */ //(00000000 00100000)b
  11. #define GPIO_Pin_6 ((uint16_t)0x0040) /*!< 选择Pin6 */ //(00000000 01000000)b
  12. #define GPIO_Pin_7 ((uint16_t)0x0080) /*!< 选择Pin7 */ //(00000000 10000000)b
  13. #define GPIO_Pin_8 ((uint16_t)0x0100) /*!< 选择Pin8 */ //(00000001 00000000)b
  14. #define GPIO_Pin_9 ((uint16_t)0x0200) /*!< 选择Pin9 */ //(00000010 00000000)b
  15. #define GPIO_Pin_10 ((uint16_t)0x0400) /*!< 选择Pin10 */ //(00000100 00000000)b
  16. #define GPIO_Pin_11 ((uint16_t)0x0800) /*!< 选择Pin11 */ //(00001000 00000000)b
  17. #define GPIO_Pin_12 ((uint16_t)0x1000) /*!< 选择Pin12 */ //(00010000 00000000)b
  18. #define GPIO_Pin_13 ((uint16_t)0x2000) /*!< 选择Pin13 */ //(00100000 00000000)b
  19. #define GPIO_Pin_14 ((uint16_t)0x4000) /*!< 选择Pin14 */ //(01000000 00000000)b
  20. #define GPIO_Pin_15 ((uint16_t)0x8000) /*!< 选择Pin15 */ //(10000000 00000000)b
  21. #define GPIO_Pin_All ((uint16_t)0xFFFF) /*!< 选择全部引脚*/ //(11111111 11111111)b
  22. void GPIO_SetBits(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin) ; //置位函数声明,记得加;号
  23. void GPIO_ResetBits(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin); //复位函数声明
  24. #endif /*__STM32F10X_GPIO_H */

main.c

  1. #include"stm32f10x.h"
  2. #include"stm32f10x_gpio.h"
  3. int main(void)
  4. {
  5. #if 0
  6. //打开GPIOB端口时钟
  7. *(unsigned int *)0x40021018 |= (1<<3);
  8. //配置IO口为输出
  9. *(unsigned int *)0x40010C00 |= ((1)<<(4*0));
  10. *(unsigned int *)0x40010C00 |= ((1)<<(4*1));
  11. *(unsigned int *)0x40010C00 |= ((1)<<(4*5));
  12. //配置ODR寄存器
  13. *(unsigned int *)0x40010C0C &= ~(1<<0);
  14. *(unsigned int *)0x40010C0C &= ~(1<<1);
  15. *(unsigned int *)0x40010C0C &= ~(1<<5);
  16. #elif 0
  17. RCC_APB2ENR |= (1<<3);
  18. GPIOB_CRL &= ~((0x0f)<<(4*0));//端口配置寄存器清零
  19. GPIOB_CRL |= (1<<(4*0));
  20. GPIOB_ODR &= ~(1<<0);
  21. #elif 1
  22. //打开GPIO端口B的时钟
  23. RCC->APB2ENR |= (1<<3);
  24. //配置IO口为输出
  25. GPIOB->CRL &= ~((0x0f)<<(4*0)); //端口配置寄存器清零
  26. GPIOB->CRL |= (1<<(4*0));
  27. //控制ODR寄存器
  28. //GPIOB->ODR &= ~(1<<0);
  29. //GPIOB->ODR |= (1<<0);
  30. GPIO_SetBits(GPIOB,GPIO_Pin_0);
  31. GPIO_ResetBits(GPIOB,GPIO_Pin_0);
  32. #endif
  33. }
  34. void SystemInit(void)
  35. {
  36. //函数体为空,目的为骗过编译器不报错
  37. }

定义GPIO_TypeDef

定义初始化结构体 GPIO_InitTypeDef

详见零死角玩转STM32—指南者(野火)
文件地址:G:\开发板学习\野火指南者F103

小知识

头文件多次引用

include文件的一个不利之处在于一个头文件可能会被多次包含,为了说明这种错误,考虑下面的代码:
#include “x.h”
#include “x.h”
显然,这里文件x.h被包含了两次,没有人会故意编写这样的代码。但是下面的代码:
#include “a.h”
#include “b.h”
看上去没什么问题。如果a.h和b.h都包含了一个头文件x.h。那么x.h在此也同样被包含了两次,只不过它的形式不是那么明显而已。如果头文件被多次引用,会出现下列提示
error: #256: invalid redeclaration of type name “GPIO_TypeDef” (declared at line 42)

多重包含在绝大多数情况下出现在大型程序中,它往往需要使用很多头文件,因此要发现重复包含并不容易。要解决这个问题,我们可以使用条件编译。如果所有的头文件都像下面这样编写:
#ifndef HEADERNAME_H **(前面加下滑线,是为了区别普通的头文件宏定义)**
#define __HEADERNAME_H

#endif

那么多重包含的危险就被消除了。当头文件第一次被包含时,它被正常处理,符号_HEADERNAME_H被定义为1。如果头文件被再次包含,通过条件编译,它的内容被忽略。符号_HEADERNAME_H按照被包含头文件的文件名进行取名,以避免由于其他头文件使用相同的符号而引起的冲突。

但是,你必须记住预处理器仍将整个头文件读入,即使这个头文件所有内容将被忽略。由于这种处理将托慢编译速度,所以如果可能,应该避免出现多重包含。

使用#ifndef只是防止了头文件被重复包含(其实本例中只有一个头件,不会存在重复包含的问题),但是无法防止变量被重复定义。

配置上拉输入/下拉输入

image.png
在CRL寄存器中,输入模式下的10为上拉/下拉输入模式,具体是上拉还是下拉由固件库软件实现。
实现方式:
image.png
配置GPIO端口工作模式时,在gpio.h中,对GPIO 工作模式枚举定义。
当bit5为1时,配置成下拉输入。下拉输入为复位,配置BRR(端口清除寄存器),如PB0为下拉输入,则使BRR的BR0为1,则PB0=0.
当bit6为1时,配置成上拉输入。下拉输入为置位,配置BSRR(端口设置/清除寄存器),如PB0为上拉输入,则使BRR的BR0为1,则PB0=1.
gpio.c中对应代码

  1. // 判断是否为下拉输入模式
  2. if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
  3. {
  4. // 下拉输入模式,引脚默认置0,对BRR寄存器写1可对引脚置0
  5. GPIOx->BRR = (((uint32_t)0x01) << pinpos);
  6. }
  7. else
  8. {
  9. // 判断是否为上拉输入模式
  10. if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
  11. {
  12. // 上拉输入模式,引脚默认值为1,对BSRR寄存器写1可对引脚置1
  13. GPIOx->BSRR = (((uint32_t)0x01) << pinpos);
  14. }

11.构建库函数雏形 - 图6

C语言条件编译

  1. C 语言条件编译
  2. 1 /*
  3. 2 * C 语言知识,条件编译
  4. 3 * #if 为真
  5. 4 * 执行这里的程序
  6. 5 * #else
  7. 6 * 否则执行这里的程序
  8. 7 * #endif
  9. 8 */

image.png
语雀内容