[STM32F1] 从寄存器到固件库--stm31f103RBT6

[复制链接]
913|18
 楼主| 雨果喝水 发表于 2022-5-29 23:26 | 显示全部楼层 |阅读模式
目的:将寄存器编程逐步向固件库编程转变

一、寄存器结构体定义
好处:GPIO有A、B、C、D等等的端口,每个端口都有相同配置的寄存器,使用结构体只需要写一次结构体,就可以使结构体指针变量指向GPIO口的基地址,结构体成员会自动顺序分配地址
如果不了解结构体为什么实现,建议先学c语言。。。

评论

原文链接:https://blog.csdn.net/qq_42424228/article/details/103674875  发表于 2022-5-29 23:28
 楼主| 雨果喝水 发表于 2022-5-29 23:27 | 显示全部楼层
  1. #define PERIPH_BASE        ((unsigned int)0x40000000)
  2. //总线
  3. #define APB1PERIPH_BASE    PERIPH_BASE
  4. #define APB2PERIPH_BASE    (PERIPH_BASE+0X10000)
  5. #define AHBPERIPH_BASE     (PERIPH_BASE+0X20000)
  6. //寄存器组
  7. #define RCC_BASE                          (AHBPERIPH_BASE+0X1000)
  8. #define GPIOC_BASE                  (APB2PERIPH_BASE+0X1000)
  9. //寄存器 ;   此处不再专门宏定义GPIO的寄存器地址,直接用结构体实现
  10. typedef unsigned int    uint32_t;
  11. typedef unsigned short  uint16_t;
  12. typedef struct{
  13.          uint32_t CRL;
  14.          uint32_t CRH;
  15.          uint32_t IDR;
  16.          uint32_t ODR;
  17.          uint32_t BSRR;
  18.          uint32_t BRR;
  19.          uint32_t LCKR;
  20. }GPIO_typedef;
  21. typedef struct{
  22.          uint32_t CR;
  23.          uint32_t CFGR;
  24.          uint32_t CIR;
  25.          uint32_t APB2RSTR;
  26.          uint32_t APB1RSTR;
  27.          uint32_t AHBENR;
  28.          uint32_t APB2ENR;
  29.          uint32_t APB1ENR;
  30.          uint32_t BDCR;
  31.          uint32_t CSR;
  32. }RCC_typedef;
  33. #define GPIOC  ((GPIO_typedef*)GPIOC_BASE)//直接把GPIOC定义为一个结构体
  34. //或使用GPIO_typedef *GPIOC;
  35. #define RCC    ((RCC_typedef*)RCC_BASE)

  36. //此时main函数可以写成:
  37. int main()
  38. {
  39. //打开GPIOC的时钟
  40.         RCC->APB2ENR |=((1)<<4);
  41. //配置IO口为输出
  42.         GPIOC->CRH =0x11111111;

  43. //控制ODR寄存器        ,开启LED                                  
  44.         GPIOC->ODR =0xFEFF;
  45. }
  46. void SystemInit()
  47. {
  48. }
 楼主| 雨果喝水 发表于 2022-5-29 23:27 | 显示全部楼层
二、把引脚也定义成宏
同时使用BSRR置位,BRR清0来代替ODR寄存器进行操作。BSRR和BRR在硬件结构上位于ODR之前,会将操作的结果送到ODR当中,所以直接操作BSRR和BRR也同样可以。
 楼主| 雨果喝水 发表于 2022-5-29 23:28 | 显示全部楼层
  1. #define GPIO_Pin_0    ((uint16_t)0x0001)/*选择pin0*/
  2. #define GPIO_Pin_1    ((uint16_t)0x0002)/*选择pin1*/
  3. #define GPIO_Pin_2    ((uint16_t)0x0004)/*选择pin2*/
  4. #define GPIO_Pin_3    ((uint16_t)0x0008)/*选择pin3*/
  5. #define GPIO_Pin_4    ((uint16_t)0x0010)/*选择pin4*/
  6. #define GPIO_Pin_5    ((uint16_t)0x0020)/*选择pin5*/
  7. #define GPIO_Pin_6    ((uint16_t)0x0040)/*选择pin6*/
  8. #define GPIO_Pin_7    ((uint16_t)0x0080)/*选择pin7*/
  9. #define GPIO_Pin_8    ((uint16_t)0x0100)/*选择pin8*/
  10. #define GPIO_Pin_9    ((uint16_t)0x0200)/*选择pin9*/
  11. #define GPIO_Pin_10   ((uint16_t)0x0400)/*选择pin10*/
  12. #define GPIO_Pin_11   ((uint16_t)0x0800)/*选择pin11*/
  13. #define GPIO_Pin_12   ((uint16_t)0x1000)/*选择pin12*/
  14. #define GPIO_Pin_13   ((uint16_t)0x2000)/*选择pin13*/
  15. #define GPIO_Pin_14   ((uint16_t)0x4000)/*选择pin14*/
  16. #define GPIO_Pin_15   ((uint16_t)0x8000)/*选择pin15*/
  17. #define GPIO_Pin_ALL  ((uint16_t)0xFFFF)/*选择全部引脚*/

  18. /*一般使用BSRR置位,BRR清0,且都是为0时不产生影
  19. 响,为1时设置对应引脚。GPIO每一个端口16个引脚*/

  20. //GPIO置位函数,参数:端口  引脚
  21. void GPIO_SetBits(GPIO_typeDef *GPIOx,uint16_t GPIO_Pin)
  22. {
  23.         GPIOx->BSRR |=GPIO_Pin;       
  24. }
  25. //GPIO清0函数,参数:端口  引脚
  26. void GPIO_ResetBits(GPIO_typeDef *GPIOx,uint16_t GPIO_Pin)
  27. {
  28.         GPIOx->BRR |= GPIO_Pin;       
  29. }
  30. void delay(uint32_t time)
  31. {
  32. while(time--)
  33.         ;
  34. }
  35. //此时main可以写成:
  36. int main()
  37. {
  38. //打开GPIOC的时钟
  39.         RCC->APB2ENR |=((1)<<4);
  40. //配置IO口为输出
  41.         GPIOC->CRH =0x11111111;
  42.        
  43.         while(1)
  44.         {       
  45.                 GPIO_ResetBits(GPIOC,GPIO_Pin_8);//开启LED
  46.                 delay(0x10000);
  47.                  GPIO_SetBits(GPIOC,GPIO_Pin_8);//关闭LED
  48.                 delay(0x10000);
  49.         }

  50.        
  51. }
  52. //这里结果和上面稍有不同,由于是只设置了LED8的开启和关闭,所以其他7个LED会一直亮着。
 楼主| 雨果喝水 发表于 2022-5-29 23:29 | 显示全部楼层
三、把GPIO的初始化参数也定义为宏
枚举类型:将某个结构体或其他变量的参数限制在一定范围内,告诉读者,你不能随便的修改这个参数,必须使用这些固定参数。而寄存器编程当中,参数的值往往是连续的00、01、10、11;枚举类型则是如果不给其赋值,会在上一个成员的值上自动加1,极为方便。

 楼主| 雨果喝水 发表于 2022-5-29 23:29 | 显示全部楼层
下图是固件库所定义的在软件实现时对8种IO模式的设置,我们在自己写的时候,也可以自定义。
在这里,如果配置的是上拉/下拉输入模式,那么上拉输入配置BSRR的bit[0]为1,下拉输入配置BRR的bit[0]为1;以此来在硬件方面区分上拉和下拉。
 楼主| 雨果喝水 发表于 2022-5-29 23:30 | 显示全部楼层
 楼主| 雨果喝水 发表于 2022-5-29 23:31 | 显示全部楼层
 楼主| 雨果喝水 发表于 2022-5-29 23:31 | 显示全部楼层
  1. typedef enum//自定义的软件实现,限定了模式的取值
  2. {
  3. GPIO_Mode_AIN = 0x0,         //模拟输入(0000 0000)b
  4. GPIO_Mode_IN_FLOATING = 0x04,//浮空输入(00000100)b
  5. GPIO_Mode_IPD = 0x28,        //下拉输入(0010 1000)b
  6. GPIO_Mode_IPU = 0x48,        //上拉输入(0100 1000)b
  7. GPIO_Mode_Out_OD =0x14,      //开漏输出(00010100)b
  8. GPIO_Mode_Out_PP = 0x10,    //推挽输出(0001 0000)b
  9. GPIO_Mode_AF_OD = 0x1C,      //复用开漏输出(0001 1100)b
  10. GP1O_Mode_AF_PP = 0x18       //复用推挽输出(0001 1000)b
  11. }GPIOMode_TypeDef;

  12. typedef struct{//用于模式配置的结构体
  13.         uint16_t GPIO_Pin; //要配置的引脚
  14.         uint16_t GPIO_Speed; //速率,只有输出模式使用
  15.         uint16_t GPIO_Mode;         //模式
  16. }GPIO_InitTypeDef;
  17. void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)//初始化GPIO
  18. {
  19.   uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00;
  20.   uint32_t tmpreg = 0x00, pinmask = 0x00;

  21. /* GPIO 模式配置 */
  22.   currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F);//把输入参数GPIO_Mode的低4为暂存
  23.   if ((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00)//bit4如果是1为输出模式,0为输入模式
  24.   {
  25.     /* 输出模式 */
  26.       currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;;//配置速度  
  27.   }
  28. /* GPIO CRL 寄存器配置,CRL控制GPIO某一端口的低8位 */
  29.   
  30.   if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00)
  31.   {
  32.     tmpreg = GPIOx->CRL; //暂存CRL的值
  33.     for (pinpos = 0x00; pinpos < 0x08; pinpos++)//寻找对应引脚
  34.     {
  35.       pos = ((uint32_t)0x01) << pinpos;
  36.       /* Get the port pins position */
  37.       currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
  38.       if (currentpin == pos)  //找到了对应引脚
  39.       {
  40.         pos = pinpos << 2; //乘4,因为CRL中每4位配置一个引脚
  41.         pinmask = ((uint32_t)0x0F) << pos;
  42.         tmpreg &= ~pinmask;        //把这几个位先清零
  43.         tmpreg |= (currentmode << pos);//写入模式的值
  44.                 //GPIO_Mode_IPD和GPIO_Mode_IPU是自定义的宏,可以自己设置
  45.         if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)//判断是上拉还是下拉
  46.         {   //下拉
  47.           GPIOx->BRR = (((uint32_t)0x01) << pinpos);//将BRR最低位置1
  48.         }
  49.         else//上拉
  50.         {
  51.           if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
  52.           {
  53.             GPIOx->BSRR = (((uint32_t)0x01) << pinpos);//将BSRR最低位置1
  54.           }
  55.         }
  56.       }
  57.     }
  58.     GPIOx->CRL = tmpreg;//写入寄存器
  59.   }
  60.   /* GPIO CRH 寄存器配置 ,过程和CRL配置基本相同*/
  61.   if (GPIO_InitStruct->GPIO_Pin > 0x00FF)
  62.   {
  63.     tmpreg = GPIOx->CRH;
  64.     for (pinpos = 0x00; pinpos < 0x08; pinpos++)
  65.     {
  66.       pos = (((uint32_t)0x01) << (pinpos + 0x08));

  67.       currentpin = ((GPIO_InitStruct->GPIO_Pin) & pos);
  68.       if (currentpin == pos)
  69.       {
  70.         pos = pinpos << 2;
  71.         pinmask = ((uint32_t)0x0F) << pos;
  72.         tmpreg &= ~pinmask;
  73.         tmpreg |= (currentmode << pos);

  74.         if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
  75.         {
  76.           GPIOx->BRR = (((uint32_t)0x01) << (pinpos + 0x08));
  77.         }
  78.         if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
  79.         {
  80.           GPIOx->BSRR = (((uint32_t)0x01) << (pinpos + 0x08));
  81.         }
  82.       }
  83.     }
  84.     GPIOx->CRH = tmpreg;
  85.   }
  86. }
  87. int main()
  88. {
  89.         GPIO_InitTypeDef GPIO_Inits;       
  90. //打开GPIOC的时钟
  91.         RCC->APB2ENR |=((1)<<4);
  92. //在此,GPIO模式的配置也实现了屏蔽寄存器,在不知道寄存器取值的情况下可以编程,逐步将寄存器编程进化至固件库编程
  93.         GPIO_Inits.GPIO_Pin=GPIO_Pin_8;        //对第8个引脚进行配置
  94.         GPIO_Inits.GPIO_Mode=GPIO_Mode_Out_PP; //推挽输出模式
  95.         GPIO_Inits.GPIO_Speed=SPIO_Speed_50MHZ;//推挽输出50mhz
  96.         GPIO_Init(GPIOC,&GPIO_Inits);//初始化GPIOC
  97.         while(1)
  98.         {       
  99.                 GPIO_ResetBits(GPIOC,GPIO_Pin_8);//开启LED
  100.                 delay(0x10000);
  101.                  GPIO_SetBits(GPIOC,GPIO_Pin_8);//关闭LED
  102.                 delay(0x10000);
  103.         }
  104. }
  105. void delay(uint32_t time)
  106. {
  107. while(time--)
  108.         ;
  109. }`
  110.        
 楼主| 雨果喝水 发表于 2022-5-29 23:32 | 显示全部楼层
四、硬件的隔离
此时,大部分操作我们都已经隔离了寄存器来实现,只需知道想要达成什么操作,就可以直接调用函数和宏来实现。但如果我们要点亮其他的LED,那么还需要知道这个LED具体是挂接在那个引脚上,不同的开发板,肯定挂接位置不同,为提高可移植性,将一些和硬件相关的也定义为宏,实现硬件的屏蔽。
 楼主| 雨果喝水 发表于 2022-5-29 23:32 | 显示全部楼层
  1. #define LED0_GPIO_PORT      GPIOC
  2. #define LED0_GPIO_CLK_ENR       (RCC->APB2ENR |=((1)<<4))//RCC我没有实现固件库,所以直接复制过来
  3. #define LED0_GPIO_Pin            GPIO_Pin_8
  4. `int main()
  5. {
  6.         GPIO_InitTypeDef GPIO_Inits;       
  7. //打开GPIOC的时钟
  8.         LED0_GPIO_CLK_ENR;
  9. //配置IO口为输出
  10.         /*GPIOC->CRH |= (0xffffffff<<0);
  11.         GPIOC->CRH &= ~(0x0f<<0);
  12.         GPIOC->CRH |= (0x1<<0);   */
  13.         GPIO_Inits.GPIO_Pin=LED0_GPIO_Pin;        //对第8个引脚进行配置
  14.         GPIO_Inits.GPIO_Mode=GPIO_Mode_Out_PP; //推挽输出模式
  15.         GPIO_Inits.GPIO_Speed=GPIO_Speed_10MHz;//推挽输出50mhz
  16.         GPIO_Init(GPIOC,&GPIO_Inits);//初始化GPIOC       
  17.         while(1)
  18.         {       
  19.                 GPIO_ResetBits(GPIOC,LED0_GPIO_Pin);//开启LED
  20.                 delay(0x100000);
  21.                  GPIO_SetBits(GPIOC,LED0_GPIO_Pin);//关闭LED
  22.                 delay(0x100000);
  23.         }                 
  24. }``

  25. **写下面的几个示例时,需要把上面的除主函数外都附带上。

  26. 如果出现错误:
  27. *** Error: 'C:\Keil\ARM\BIN\coocox-agdi.dll' invalid peripheral driver
  28. 如果配置都没发生错误,或者原来配置有误,但已经改正的情况下,仍然报错,那么关闭这个程序,重新启动一下就可以了。亲测。。。满满的辛酸泪!!!       











 楼主| 雨果喝水 发表于 2022-5-29 23:33 | 显示全部楼层
  1. #define LED0_GPIO_PORT      GPIOC
  2. #define LED0_GPIO_CLK_ENR       (RCC->APB2ENR |=((1)<<4))//RCC我没有实现固件库,所以直接复制过来
  3. #define LED0_GPIO_Pin            GPIO_Pin_8
  4. `int main()
  5. {
  6.         GPIO_InitTypeDef GPIO_Inits;       
  7. //打开GPIOC的时钟
  8.         LED0_GPIO_CLK_ENR;
  9. //配置IO口为输出
  10.         /*GPIOC->CRH |= (0xffffffff<<0);
  11.         GPIOC->CRH &= ~(0x0f<<0);
  12.         GPIOC->CRH |= (0x1<<0);   */
  13.         GPIO_Inits.GPIO_Pin=LED0_GPIO_Pin;        //对第8个引脚进行配置
  14.         GPIO_Inits.GPIO_Mode=GPIO_Mode_Out_PP; //推挽输出模式
  15.         GPIO_Inits.GPIO_Speed=GPIO_Speed_10MHz;//推挽输出50mhz
  16.         GPIO_Init(GPIOC,&GPIO_Inits);//初始化GPIOC       
  17.         while(1)
  18.         {       
  19.                 GPIO_ResetBits(GPIOC,LED0_GPIO_Pin);//开启LED
  20.                 delay(0x100000);
  21.                  GPIO_SetBits(GPIOC,LED0_GPIO_Pin);//关闭LED
  22.                 delay(0x100000);
  23.         }                 
  24. }``

  25. **写下面的几个示例时,需要把上面的除主函数外都附带上。

  26. 如果出现错误:
  27. *** Error: 'C:\Keil\ARM\BIN\coocox-agdi.dll' invalid peripheral driver
  28. 如果配置都没发生错误,或者原来配置有误,但已经改正的情况下,仍然报错,那么关闭这个程序,重新启动一下就可以了。亲测。。。满满的辛酸泪!!!       











Pulitzer 发表于 2022-10-6 07:05 | 显示全部楼层

51 单片机不使用线性编址
Uriah 发表于 2022-10-6 11:07 | 显示全部楼层

small 模式下未指存储类型的变量默认为data型
Uriah 发表于 2022-10-6 14:06 | 显示全部楼层

一般要进行内存优化,尽量提高内存的使用效率
Bblythe 发表于 2022-10-6 17:05 | 显示全部楼层

访问时采用不同的指令,所以并不会占用 RAM 空间
Pulitzer 发表于 2023-5-1 07:05 | 显示全部楼层

当PWM频率较高时,频繁的中断将影响程序运行的效率
公羊子丹 发表于 2023-5-1 08:08 | 显示全部楼层

从定时器为TIM2,从模式选择为门控模式,触发源选择ITR0,开启定时器2中断。
公羊子丹 发表于 2023-5-1 09:01 | 显示全部楼层

都可以产生指定个数的PWM脉冲
Wordsworth 发表于 2023-5-1 10:04 | 显示全部楼层

输出了5个频率为10KHz的PWM脉冲
您需要登录后才可以回帖 登录 | 注册

本版积分规则

90

主题

1213

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部