打印
[其他ST产品]

Stm32之流水灯,深入了解寄存器

[复制链接]
501|17
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
通过LED流水灯,由浅入深,理解寄存器和与GPIO相关的函数,比如GPIO_Init()。

流水灯,设计思路很简单,只需要将多个连续的GPIO口依次设置电平,根据你LED连接线路,选择是高电平触发还是低电平触发。每个灯亮几毫秒然后熄灭,再延时,你当然可以通过GPIO_ResetBits(GPIOA,GPIO_Pin_0)这种方式一个一个写,但是,注意,观察GPIO_Pin_对应寄存器的值,不难发现。只需要将GPIO_Pin_0的寄存器值左移一位,就是GPIO_Pin_1的值,所以思路就有了,我的代码如下

#include "stm32f10x.h"  
#include  "Delay.h"            // Device header
int main(void){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef g;
g.GPIO_Mode=GPIO_Mode_Out_PP;
g.GPIO_Pin=GPIO_Pin_All;
g.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&g);

while (1)
{
int a=0x0001;
for (int i = 0; i < 7; i++)
{
    int b=a<<i;
    GPIO_Write(GPIOA,~b);
    Delay_ms(200);
}
}
}


使用特权

评论回复
沙发
在水一方00|  楼主 | 2023-11-20 22:30 | 只看该作者
至于为啥,右键gpio_pin_跳转到定义。如下图


可以看到,对应pin口的寄存器值,我们知道,Stm32中,GPIOA这一组IO口由7个寄存器控制,单片机编程的本质就是操作寄存器。为什么这里没有指定电平,却能正确输出低电平的,这就要从寄存器说起。

使用特权

评论回复
板凳
在水一方00|  楼主 | 2023-11-20 22:30 | 只看该作者
IDR,ODR,BRR,LCKR四个寄存器的高16位都是保留的。对于GPIO_ResetBits,内部是对BRR寄存器写入了值。对于GPIO_SetBits,是对BSRR寄存器写入了值,写入的什么值?就是上图对应引脚的值。比如GPIO_ResetBits(GPIOA,GPIO_Pin_0)。GPIO_Pin_0对应0000 0000 0000 0001,即BRR寄存器的BR0为1,表示清除ODR0为1。清除这个步骤应该是硬件电路决定的。总结就是。BRR寄存器哪一位为1就清除ODR对应位。BSRR寄存器低16位哪一位为1就设置ODR对应位为1。BSRR寄存器高16位哪一位为1就清除ODR对应位为0。

使用特权

评论回复
地板
在水一方00|  楼主 | 2023-11-20 22:31 | 只看该作者


那GPIO_Write是怎么回事呢。跳转查看,内部是GPIOx->ODR = PortVal。什么是ODR,就是端口输出数据寄存器,比如PortVal为0x0001,就表示将ODR寄存器低16位设为了0000 0000 0000 0001。根据下图,就是ODR0=1,也就是使GPIO_Pin_0输出高电平,其余为0的位对应端口输出模式输出低电平。根据我的理解这是硬件电路的连接规则。如果设为0000 0000 0100 0000。就是ODR6=1,也就是使GPIO_Pin_6输出高电平,其余为0的位端口输出低电平。

使用特权

评论回复
5
在水一方00|  楼主 | 2023-11-20 22:31 | 只看该作者
如果想使两个端口输出高电平呢,直接用或,比如,PA0和PA2,0000 0000 0000 0001  |  0000 0000 0000 0100。按位或,得0000 0000 0000 0101。即可实现PA0与PA2输出高电平。

说到这里,不如再说说GPIO_Init函数的本质。如下,是八种输入输出模式。还有速度。

使用特权

评论回复
6
在水一方00|  楼主 | 2023-11-20 22:31 | 只看该作者
枚举,GPIO_Speed_2MHz=2。GPIO_Speed_50MHz=3

使用特权

评论回复
7
在水一方00|  楼主 | 2023-11-20 22:32 | 只看该作者
注意,根据CRL寄存器发现 ,这32位共控制8个IO口的配置,每个IO口对应四位。mode0和cnf0对应GPIO_Pin_0,mode1和cnf对应GPIO_Pin_1,以此类推。mode控制是输入还是输出(也包含了速度),cnf控制配置,同样为00,如果是输入则为模拟输入,如果是输出则为通用推挽输出。

使用特权

评论回复
8
在水一方00|  楼主 | 2023-11-20 22:32 | 只看该作者

使用特权

评论回复
9
在水一方00|  楼主 | 2023-11-20 22:34 | 只看该作者
GPIO_Init分析如下

currentmode = ((uint32_t)GPIO_InitStruct-> & ((uint32_t)0x0F)   //这是将GPIO_Mode的低四位取出存入currentmode,比如OUT_PP为0x10,计算currentmode=0x10&0x0F=0001 0000 & 0000 1111=0000 0000=0x00。因为A&1=A,0&B=0。

if ((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00)   //这是判断是否为输出模式。比如通用推挽输出0x10。0x10&0x10=0x10。上拉输入为0x48。0x48&0x10=0100 10000&0001 0000=0000 0000=0x00。

currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;  //这是取出结构体的speed放入crrentmode低二位。比如速度为GPIO_Speed_50MHz。currentmode=currentmode | GPIO_Speed_50MHz=0x00 | 0x03=0000 0000 | 0000 0011=0000 0011=0x03。currentmode虽然保存了引脚的模式速度信息,但是信息保存在current最低4位,所以要想配置具体某个引脚,还要移动currentmode到对应位。

使用特权

评论回复
10
在水一方00|  楼主 | 2023-11-20 22:34 | 只看该作者
if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00)   //这是这是判断引脚定义没有,并判断是否是Pin0-7。因为Pin0-7的格式都为0x00mm,而Pin8-15为0xnn00,通过&0x00FF就可以判断。如果是Pin8-15结果应为0x0000。结果为0x00mm就是Pin1-7。

for (pinpos = 0x00; pinpos < 0x08; pinpos++)

    {

      pos = ((uint32_t)0x01) << pinpos;

      currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;

      if (currentpin == pos)

使用特权

评论回复
11
在水一方00|  楼主 | 2023-11-20 22:34 | 只看该作者
这部分是获取io口位置,我假定选的是GPIO_Pin_5,即0x0020运算过程如图。可以看到,当pinpos加到0x05时,pos=crrentpin=0x20。这时候pinpos就是引脚号

使用特权

评论回复
12
在水一方00|  楼主 | 2023-11-20 22:34 | 只看该作者
找到对应pin口时,进入if判断语句

if (currentpin == pos)

      {

        pos = pinpos << 2;//因为CRL寄存器每四位配置一个io口,所以这里左移两位,也就是*4。

        /* Clear the corresponding low control register bits */

        pinmask = ((uint32_t)0x0F) << pos;//将0000 1111左移引脚号*4,如果是pin5,就是左移20位

        tmpreg &= ~pinmask。//将引脚对应的CRL的4位清零。~表示按位取反。式子为tmpreg=tmpreg&~pinmask。取反的好处,&之后不影响其他引脚配置位,还可以把要配置的引脚4位清0.

        /* Write the mode configuration in the corresponding bits */

        tmpreg |= (currentmode << pos);//配置对应引脚的4位,为什么用或,因为0|A=A,也就是不影响其他的引脚对应的配置位。

使用特权

评论回复
13
在水一方00|  楼主 | 2023-11-20 22:34 | 只看该作者
        /* Reset the corresponding ODR bit */

        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)

        {

          GPIOx->BRR = (((uint32_t)0x01) << pinpos);//如果配置为下拉输入,将对应引脚对应的ODR位清0。

        }

        else

        {

          /* Set the corresponding ODR bit */

          if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)

          {

            GPIOx->BSRR = (((uint32_t)0x01) << pinpos);//如果是上拉输入,将BSRR低16位的对应引脚位置1,然后硬件会自动将对应ODR位置1

使用特权

评论回复
14
在水一方00|  楼主 | 2023-11-20 22:34 | 只看该作者
  }

        }

      }

使用特权

评论回复
15
在水一方00|  楼主 | 2023-11-20 22:35 | 只看该作者
void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_InitStruct)
{
  uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00;
  uint32_t tmpreg = 0x00, pinmask = 0x00;
  /* Check the parameters */
  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
  assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode));
  assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin));

  /*---------------------------- GPIO Mode Configuration -----------------------*/
  currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F);
  if ((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00)
  {
    /* Check the parameters */
    assert_param(IS_GPIO_SPEED(GPIO_InitStruct->GPIO_Speed));
    /* Output mode */
    currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;
  }
  /*---------------------------- GPIO CRL Configuration ------------------------*/
  /* Configure the eight low port pins */
  if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00)
  {
    tmpreg = GPIOx->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;
        /* Clear the corresponding low control register bits */
        pinmask = ((uint32_t)0x0F) << pos;
        tmpreg &= ~pinmask;
        /* Write the mode configuration in the corresponding bits */
        tmpreg |= (currentmode << pos);
        /* Reset the corresponding ODR bit */
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
        {
          GPIOx->BRR = (((uint32_t)0x01) << pinpos);
        }
        else
        {
          /* Set the corresponding ODR bit */
          if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
          {
            GPIOx->BSRR = (((uint32_t)0x01) << pinpos);
          }
        }
      }
    }
    GPIOx->CRL = tmpreg;
  }
  /*---------------------------- GPIO CRH Configuration高寄存器差不多。省略 ------------------------*/
}

使用特权

评论回复
16
jf101| | 2023-11-30 12:29 | 只看该作者
其实任何程序最后控制的都是寄存器的内容

使用特权

评论回复
17
Undshing| | 2023-11-30 16:29 | 只看该作者
看着好麻烦

使用特权

评论回复
18
储小勇_526| | 2023-12-14 11:31 | 只看该作者
只要能点亮,方法很多种,看自己代码风格

使用特权

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

本版积分规则

40

主题

508

帖子

0

粉丝