打印
[STM32F1]

从寄存器到固件库--stm31f103RBT6

[复制链接]
538|18
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
目的:将寄存器编程逐步向固件库编程转变

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

使用特权

评论回复
评论
雨果喝水 2022-5-29 23:28 回复TA
原文链接:https://blog.csdn.net/qq_42424228/article/details/103674875 
沙发
雨果喝水|  楼主 | 2022-5-29 23:27 | 只看该作者
#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 GPIOC_BASE                  (APB2PERIPH_BASE+0X1000)
//寄存器 ;   此处不再专门宏定义GPIO的寄存器地址,直接用结构体实现
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;
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 GPIOC  ((GPIO_typedef*)GPIOC_BASE)//直接把GPIOC定义为一个结构体
//或使用GPIO_typedef *GPIOC;
#define RCC    ((RCC_typedef*)RCC_BASE)

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

//控制ODR寄存器        ,开启LED                                  
        GPIOC->ODR =0xFEFF;
}
void SystemInit()
{
}

使用特权

评论回复
板凳
雨果喝水|  楼主 | 2022-5-29 23:27 | 只看该作者
二、把引脚也定义成宏
同时使用BSRR置位,BRR清0来代替ODR寄存器进行操作。BSRR和BRR在硬件结构上位于ODR之前,会将操作的结果送到ODR当中,所以直接操作BSRR和BRR也同样可以。

使用特权

评论回复
地板
雨果喝水|  楼主 | 2022-5-29 23:28 | 只看该作者
#define GPIO_Pin_0    ((uint16_t)0x0001)/*选择pin0*/
#define GPIO_Pin_1    ((uint16_t)0x0002)/*选择pin1*/
#define GPIO_Pin_2    ((uint16_t)0x0004)/*选择pin2*/
#define GPIO_Pin_3    ((uint16_t)0x0008)/*选择pin3*/
#define GPIO_Pin_4    ((uint16_t)0x0010)/*选择pin4*/
#define GPIO_Pin_5    ((uint16_t)0x0020)/*选择pin5*/
#define GPIO_Pin_6    ((uint16_t)0x0040)/*选择pin6*/
#define GPIO_Pin_7    ((uint16_t)0x0080)/*选择pin7*/
#define GPIO_Pin_8    ((uint16_t)0x0100)/*选择pin8*/
#define GPIO_Pin_9    ((uint16_t)0x0200)/*选择pin9*/
#define GPIO_Pin_10   ((uint16_t)0x0400)/*选择pin10*/
#define GPIO_Pin_11   ((uint16_t)0x0800)/*选择pin11*/
#define GPIO_Pin_12   ((uint16_t)0x1000)/*选择pin12*/
#define GPIO_Pin_13   ((uint16_t)0x2000)/*选择pin13*/
#define GPIO_Pin_14   ((uint16_t)0x4000)/*选择pin14*/
#define GPIO_Pin_15   ((uint16_t)0x8000)/*选择pin15*/
#define GPIO_Pin_ALL  ((uint16_t)0xFFFF)/*选择全部引脚*/

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

//GPIO置位函数,参数:端口  引脚
void GPIO_SetBits(GPIO_typeDef *GPIOx,uint16_t GPIO_Pin)
{
        GPIOx->BSRR |=GPIO_Pin;       
}
//GPIO清0函数,参数:端口  引脚
void GPIO_ResetBits(GPIO_typeDef *GPIOx,uint16_t GPIO_Pin)
{
        GPIOx->BRR |= GPIO_Pin;       
}
void delay(uint32_t time)
{
while(time--)
        ;
}
//此时main可以写成:
int main()
{
//打开GPIOC的时钟
        RCC->APB2ENR |=((1)<<4);
//配置IO口为输出
        GPIOC->CRH =0x11111111;
       
        while(1)
        {       
                GPIO_ResetBits(GPIOC,GPIO_Pin_8);//开启LED
                delay(0x10000);
                 GPIO_SetBits(GPIOC,GPIO_Pin_8);//关闭LED
                delay(0x10000);
        }

       
}
//这里结果和上面稍有不同,由于是只设置了LED8的开启和关闭,所以其他7个LED会一直亮着。

使用特权

评论回复
5
雨果喝水|  楼主 | 2022-5-29 23:29 | 只看该作者
三、把GPIO的初始化参数也定义为宏
枚举类型:将某个结构体或其他变量的参数限制在一定范围内,告诉读者,你不能随便的修改这个参数,必须使用这些固定参数。而寄存器编程当中,参数的值往往是连续的00、01、10、11;枚举类型则是如果不给其赋值,会在上一个成员的值上自动加1,极为方便。

使用特权

评论回复
6
雨果喝水|  楼主 | 2022-5-29 23:29 | 只看该作者
下图是固件库所定义的在软件实现时对8种IO模式的设置,我们在自己写的时候,也可以自定义。
在这里,如果配置的是上拉/下拉输入模式,那么上拉输入配置BSRR的bit[0]为1,下拉输入配置BRR的bit[0]为1;以此来在硬件方面区分上拉和下拉。

使用特权

评论回复
7
雨果喝水|  楼主 | 2022-5-29 23:30 | 只看该作者

使用特权

评论回复
8
雨果喝水|  楼主 | 2022-5-29 23:31 | 只看该作者

使用特权

评论回复
9
雨果喝水|  楼主 | 2022-5-29 23:31 | 只看该作者
typedef enum//自定义的软件实现,限定了模式的取值
{
GPIO_Mode_AIN = 0x0,         //模拟输入(0000 0000)b
GPIO_Mode_IN_FLOATING = 0x04,//浮空输入(00000100)b
GPIO_Mode_IPD = 0x28,        //下拉输入(0010 1000)b
GPIO_Mode_IPU = 0x48,        //上拉输入(0100 1000)b
GPIO_Mode_Out_OD =0x14,      //开漏输出(00010100)b
GPIO_Mode_Out_PP = 0x10,    //推挽输出(0001 0000)b
GPIO_Mode_AF_OD = 0x1C,      //复用开漏输出(0001 1100)b
GP1O_Mode_AF_PP = 0x18       //复用推挽输出(0001 1000)b
}GPIOMode_TypeDef;

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

/* GPIO 模式配置 */
  currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F);//把输入参数GPIO_Mode的低4为暂存
  if ((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00)//bit4如果是1为输出模式,0为输入模式
  {
    /* 输出模式 */
      currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;;//配置速度  
  }
/* GPIO CRL 寄存器配置,CRL控制GPIO某一端口的低8位 */
  
  if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00)
  {
    tmpreg = GPIOx->CRL; //暂存CRL的值
    for (pinpos = 0x00; pinpos < 0x08; pinpos++)//寻找对应引脚
    {
      pos = ((uint32_t)0x01) << pinpos;
      /* Get the port pins position */
      currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
      if (currentpin == pos)  //找到了对应引脚
      {
        pos = pinpos << 2; //乘4,因为CRL中每4位配置一个引脚
        pinmask = ((uint32_t)0x0F) << pos;
        tmpreg &= ~pinmask;        //把这几个位先清零
        tmpreg |= (currentmode << pos);//写入模式的值
                //GPIO_Mode_IPD和GPIO_Mode_IPU是自定义的宏,可以自己设置
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)//判断是上拉还是下拉
        {   //下拉
          GPIOx->BRR = (((uint32_t)0x01) << pinpos);//将BRR最低位置1
        }
        else//上拉
        {
          if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
          {
            GPIOx->BSRR = (((uint32_t)0x01) << pinpos);//将BSRR最低位置1
          }
        }
      }
    }
    GPIOx->CRL = tmpreg;//写入寄存器
  }
  /* GPIO CRH 寄存器配置 ,过程和CRL配置基本相同*/
  if (GPIO_InitStruct->GPIO_Pin > 0x00FF)
  {
    tmpreg = GPIOx->CRH;
    for (pinpos = 0x00; pinpos < 0x08; pinpos++)
    {
      pos = (((uint32_t)0x01) << (pinpos + 0x08));

      currentpin = ((GPIO_InitStruct->GPIO_Pin) & pos);
      if (currentpin == pos)
      {
        pos = pinpos << 2;
        pinmask = ((uint32_t)0x0F) << pos;
        tmpreg &= ~pinmask;
        tmpreg |= (currentmode << pos);

        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
        {
          GPIOx->BRR = (((uint32_t)0x01) << (pinpos + 0x08));
        }
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
        {
          GPIOx->BSRR = (((uint32_t)0x01) << (pinpos + 0x08));
        }
      }
    }
    GPIOx->CRH = tmpreg;
  }
}
int main()
{
        GPIO_InitTypeDef GPIO_Inits;       
//打开GPIOC的时钟
        RCC->APB2ENR |=((1)<<4);
//在此,GPIO模式的配置也实现了屏蔽寄存器,在不知道寄存器取值的情况下可以编程,逐步将寄存器编程进化至固件库编程
        GPIO_Inits.GPIO_Pin=GPIO_Pin_8;        //对第8个引脚进行配置
        GPIO_Inits.GPIO_Mode=GPIO_Mode_Out_PP; //推挽输出模式
        GPIO_Inits.GPIO_Speed=SPIO_Speed_50MHZ;//推挽输出50mhz
        GPIO_Init(GPIOC,&GPIO_Inits);//初始化GPIOC
        while(1)
        {       
                GPIO_ResetBits(GPIOC,GPIO_Pin_8);//开启LED
                delay(0x10000);
                 GPIO_SetBits(GPIOC,GPIO_Pin_8);//关闭LED
                delay(0x10000);
        }
}
void delay(uint32_t time)
{
while(time--)
        ;
}`
       

使用特权

评论回复
10
雨果喝水|  楼主 | 2022-5-29 23:32 | 只看该作者
四、硬件的隔离
此时,大部分操作我们都已经隔离了寄存器来实现,只需知道想要达成什么操作,就可以直接调用函数和宏来实现。但如果我们要点亮其他的LED,那么还需要知道这个LED具体是挂接在那个引脚上,不同的开发板,肯定挂接位置不同,为提高可移植性,将一些和硬件相关的也定义为宏,实现硬件的屏蔽。

使用特权

评论回复
11
雨果喝水|  楼主 | 2022-5-29 23:32 | 只看该作者
#define LED0_GPIO_PORT      GPIOC
#define LED0_GPIO_CLK_ENR       (RCC->APB2ENR |=((1)<<4))//RCC我没有实现固件库,所以直接复制过来
#define LED0_GPIO_Pin            GPIO_Pin_8
`int main()
{
        GPIO_InitTypeDef GPIO_Inits;       
//打开GPIOC的时钟
        LED0_GPIO_CLK_ENR;
//配置IO口为输出
        /*GPIOC->CRH |= (0xffffffff<<0);
        GPIOC->CRH &= ~(0x0f<<0);
        GPIOC->CRH |= (0x1<<0);   */
        GPIO_Inits.GPIO_Pin=LED0_GPIO_Pin;        //对第8个引脚进行配置
        GPIO_Inits.GPIO_Mode=GPIO_Mode_Out_PP; //推挽输出模式
        GPIO_Inits.GPIO_Speed=GPIO_Speed_10MHz;//推挽输出50mhz
        GPIO_Init(GPIOC,&GPIO_Inits);//初始化GPIOC       
        while(1)
        {       
                GPIO_ResetBits(GPIOC,LED0_GPIO_Pin);//开启LED
                delay(0x100000);
                 GPIO_SetBits(GPIOC,LED0_GPIO_Pin);//关闭LED
                delay(0x100000);
        }                 
}``

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

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











使用特权

评论回复
12
雨果喝水|  楼主 | 2022-5-29 23:33 | 只看该作者
#define LED0_GPIO_PORT      GPIOC
#define LED0_GPIO_CLK_ENR       (RCC->APB2ENR |=((1)<<4))//RCC我没有实现固件库,所以直接复制过来
#define LED0_GPIO_Pin            GPIO_Pin_8
`int main()
{
        GPIO_InitTypeDef GPIO_Inits;       
//打开GPIOC的时钟
        LED0_GPIO_CLK_ENR;
//配置IO口为输出
        /*GPIOC->CRH |= (0xffffffff<<0);
        GPIOC->CRH &= ~(0x0f<<0);
        GPIOC->CRH |= (0x1<<0);   */
        GPIO_Inits.GPIO_Pin=LED0_GPIO_Pin;        //对第8个引脚进行配置
        GPIO_Inits.GPIO_Mode=GPIO_Mode_Out_PP; //推挽输出模式
        GPIO_Inits.GPIO_Speed=GPIO_Speed_10MHz;//推挽输出50mhz
        GPIO_Init(GPIOC,&GPIO_Inits);//初始化GPIOC       
        while(1)
        {       
                GPIO_ResetBits(GPIOC,LED0_GPIO_Pin);//开启LED
                delay(0x100000);
                 GPIO_SetBits(GPIOC,LED0_GPIO_Pin);//关闭LED
                delay(0x100000);
        }                 
}``

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

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











使用特权

评论回复
13
Pulitzer| | 2022-10-6 07:05 | 只看该作者

51 单片机不使用线性编址

使用特权

评论回复
14
Uriah| | 2022-10-6 11:07 | 只看该作者

small 模式下未指存储类型的变量默认为data型

使用特权

评论回复
15
Uriah| | 2022-10-6 14:06 | 只看该作者

一般要进行内存优化,尽量提高内存的使用效率

使用特权

评论回复
16
Bblythe| | 2022-10-6 17:05 | 只看该作者

访问时采用不同的指令,所以并不会占用 RAM 空间

使用特权

评论回复
17
Pulitzer| | 2023-5-1 07:05 | 只看该作者

当PWM频率较高时,频繁的中断将影响程序运行的效率

使用特权

评论回复
18
公羊子丹| | 2023-5-1 08:08 | 只看该作者

从定时器为TIM2,从模式选择为门控模式,触发源选择ITR0,开启定时器2中断。

使用特权

评论回复
19
公羊子丹| | 2023-5-1 09:01 | 只看该作者

都可以产生指定个数的PWM脉冲

使用特权

评论回复
20
Wordsworth| | 2023-5-1 10:04 | 只看该作者

输出了5个频率为10KHz的PWM脉冲

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

85

主题

1153

帖子

0

粉丝