STM32学习(基础)——从流水灯开始了解GPIO

初学者学习笔记,错误不足望指正。

上一篇:搭建开发环境,建立工程模板 https://blog.csdn.net/qq_37241109/article/details/104098239

本篇任务:

1.完成第一个实例,用库函数操作流水灯

2.挖掘代码

一、用库函数操作流水灯

User文件夹下新建文件夹Led。

Led文件夹中新建main.cbsp_led.cbsp_led.h这三个文件。

打开工程,将main.cbsp_led.cbsp_led.h添加到工程的USER组中。

通过各种途径,我们不难模仿写出代码或者找出现成代码。我综合书上和教学视频上写出一个版本。

ps:发光二极管接GPIOB端口8,9,10。

bsp_led.h

bsp_led.c

main.c

二、挖掘代码

由于从应用角度学习,我并没有足够扎实的基础。所以对于代码理解并不一定清晰,所以我打算整理一下程序思路。

2.1 寄存器初识

1.时钟源

STM32 F103 时钟树详解STM32 F103 时钟树详解_我是唐的博客-CSDN博客_stm32f103时钟树

知道普通I/O端口(PA~PE)连接在APB2设备上,需要初始化APB2的时钟,即时钟控制(RCC)d的对应使能寄存器。

例如:开启PB口时钟的寄存器的代码为:

RCC->APB2ENR |=(1<<3);  //将1左移3位

PS:没有深入讨论,时钟不是本例了解的重点内容。

2.I/O端口寄存器

每个I/O端口有7个寄存器控制,分别是:

两个32位配置寄存器(端口配置低寄存器,端口配置高寄存器):GPIOx_CRL,GPIOx_CRH

两个32位数据寄存器(端口输入数据寄存器,端口输出数据寄存器):GPIOx_IDR,GPIOx_ODR

一个32位置位/复位寄存器:GPIOx_BSRR

一个16位复位寄存器:GPIOx_BRR

一个32位锁位寄存器:GPIOx_LCKR

与本例相关的寄存器为端口配置高寄存器GPIOx_CRH(端口配置低寄存器0~7,端口配置高寄存器8~15),端口输出寄存器GPIOx_ODR。由于是库函数操作所以或许还有GPIOx_BSRR,GPIOx_BRR,GPIOx_LCKR,我们并不清楚。针对寄存器的操作封装在库函数之中,但是我们还是需要了解一下。

例如,选择通用推挽输出模式,设置熟读为2MHz,代码为:

GPIOB->CRH = 0x2222222222;

例如,PB端口连接都是LED小灯,控制LED灯亮灭的代码为:

GPIOB->ODR = 0x0000;//灭

GPIOB->ODR = 0xFFFF;//亮

2.2 代码分析

从main.c文件入手。

1.头文件引用

查看stm32f10x.h这个头文件,通过阅读简介我们可以大概了解到它包含……所有外设寄存器的定义,位定义和存储器映射……该文件是应用程序程序员在C源代码(通常在main.c中)中使用的唯一包含文件

借用野火教学视频里老师的说法“上帝之手”,很厉害就对了。这个头文件从上至下大概看一眼,发现很多结构体定义以及很多看不懂的内容,先不细究。

2.延时函数的声明、定义及调用

延时函数的作用应该不用细说,这里关注一下数据类型_IO uint32_t,uint32_t为无符号整型。

volatile为关键字。volatile的作用是告知编译器,它修饰的变量随时都可能被改变,因此,编译后的程序每次在使用该变量的值时,都会从变量的地址中读取数据,而不是从寄存器中获取。

3.端口初始化函数(LED_GPIO_Config())的调用

LED_GPIO_Config()的定义在bsp_led.c文件中,打开这个文件

首先了解GPIO_InitTypeDef类型的结构体

三个成员类型:

分别为无符号短整型,两个枚举类型。

再看程序中对于GPIO_Pin,GPIO_Speed,GPIO_Mode的赋值。

这是程序中GPIO_Pin_8,GPIO_Pin_9,GPIO_Pin_10的宏定义。他们对应的值应该是对应端口配置寄存器中的一位的赋值。另外,端口速度选择50MHz,模式是通用推挽输出。

将赋值好的结构体写入寄存器进行端口初始化。

GPIO_Init()函数定义如下:

/**
  * @brief  Initializes the GPIOx peripheral according to the specified
  *         parameters in the GPIO_InitStruct.
  * @param  GPIOx: where x can be (A..G) to select the GPIO peripheral.
  * @param  GPIO_InitStruct: pointer to a GPIO_InitTypeDef structure that
  *         contains the configuration information for the specified GPIO peripheral.
  * @retval None
  */
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 ------------------------*/
  /* Configure the eight high port pins */
  if (GPIO_InitStruct->GPIO_Pin > 0x00FF)
  {
    tmpreg = GPIOx->CRH;
    for (pinpos = 0x00; pinpos < 0x08; pinpos++)
    {
      pos = (((uint32_t)0x01) << (pinpos + 0x08));
      /* Get the port pins position */
      currentpin = ((GPIO_InitStruct->GPIO_Pin) & pos);
      if (currentpin == pos)
      {
        pos = pinpos << 2;
        /* Clear the corresponding high 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 + 0x08));
        }
        /* Set the corresponding ODR bit */
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
        {
          GPIOx->BSRR = (((uint32_t)0x01) << (pinpos + 0x08));
        }
      }
    }
    GPIOx->CRH = tmpreg;
  }
}

这个程序不详细分析。

接下来分析时钟。

形参中RCC_APB2Periph_GPIOB、RCC_APB2Periph_GPIOC、ENABLE宏定义如下:

RCC_APB2PeriphClockCmd( )函数定义如下:

自此,寄存器初始化配置完成,我们开启PB,PC口时钟,控制PB端口8,PC端口9、10有效,端口速度50MHz,输出模式为通用推挽输出。

4.端口引脚控制函数(LEDx())

main函数中LED1,LED2,LED3函数的函数定义在bsp_led.h 中

关注函数GPIO_SetBits(),GPIO_ResetBits()

重点是两个赋值语句中GPIOx->BSRR,GPIOx->BRR。BRR和BSRR的最显著用处就是可以只改变某一个或某几个针脚的值而不改变其他,即针对于位。

STM32 通用输入输出端口GPIO BRR、BSRR、ODR寄存器详解STM32 通用输入输出端口GPIO BRR、BSRR、ODR寄存器详解_alanzjl的博客-CSDN博客_odr寄存器

三、总结

选择用库函数操作可以在某种程度上简化代码,提高编程效率,作为初学者还是要力求深究原理。不得不赞叹,封装成结构体是一个很聪明的做法。

  • 6
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于在STM32F407上实现流水灯,你可以按照以下步骤进行操作: 1. 首先,需要配置GPIO引脚为输出模式,并设置相应的引脚电平。可以使用STM32CubeMX工具来生成初始化代码,或者手动编写代码进行配置。 2. 创建一个循环,在循环中依次将各个引脚置高电平,然后延时一段时间,再依次将引脚置低电平,再延时一段时间。 3. 重复上述步骤,就可以实现流水灯效果。 下面是一个简单的示例代码,演示了如何在STM32F407上实现流水灯效果: ```c #include "stm32f4xx.h" #include "stm32f4xx_hal.h" // 定义LED引脚 #define LED_PIN_1 GPIO_PIN_12 #define LED_PIN_2 GPIO_PIN_13 #define LED_PIN_3 GPIO_PIN_14 #define LED_PIN_4 GPIO_PIN_15 // 定义延时时间 #define DELAY_MS 500 int main(void) { // 初始化HAL库 HAL_Init(); // 初始化GPIO库 __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.Pin = LED_PIN_1 | LED_PIN_2 | LED_PIN_3 | LED_PIN_4; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // 循环流水灯 while (1) { // 依次点亮LED HAL_GPIO_WritePin(GPIOA, LED_PIN_1, GPIO_PIN_SET); HAL_Delay(DELAY_MS); HAL_GPIO_WritePin(GPIOA, LED_PIN_2, GPIO_PIN_SET); HAL_Delay(DELAY_MS); HAL_GPIO_WritePin(GPIOA, LED_PIN_3, GPIO_PIN_SET); HAL_Delay(DELAY_MS); HAL_GPIO_WritePin(GPIOA, LED_PIN_4, GPIO_PIN_SET); HAL_Delay(DELAY_MS); // 依次熄灭LED HAL_GPIO_WritePin(GPIOA, LED_PIN_1 | LED_PIN_2 | LED_PIN_3 | LED_PIN_4, GPIO_PIN_RESET); HAL_Delay(DELAY_MS); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值