241|1

55

帖子

0

TA的资源

一粒金砂(中级)

楼主
 

【STM32H7S78-DK】GPIO流水灯 [复制链接]

 
开发环境:
IDE:MKD 5.38a
STM32CubeMX: V6.12.0
开发板:STM32H7S78-DK开发板
MCU:STM32H7S7L8H6H

1 GPIO工作原理

熟悉单片机的朋友都知道,学习的第一个例程就是流水灯,要想实现流水灯,首先必须了解GPIO的工作原理。GPIO的基本结构如图所示。
Figure 1-1 GPIO的基本结构
STM32 的 IO 口可以由软件配置成如下 8 种模式:
  • 输入模式
  • 浮空输入:浮空(floating)就是逻辑器件的输入引脚即不接高电平,也不接低电平。由于逻辑器件的内部结构,当它输入引脚悬空时,相当于该引脚接了高电平。一般实际运用时,引脚不建议悬空,易受干扰。 通俗讲就是让管脚什么都不接,浮空着。信号进入芯片内部后,既没有接上拉电阻也没有接下拉电阻,经由触发器输入。配置成这个模式后,用电压变量引脚电压为1点几伏,这是个不确定值。由于其输入阻抗比较大,一般把这种模式用于标准的通讯协议,比如IIC、USART的等。该模式是STM32复位之后的默认模式。

    Figure 1-2 输入浮空模式
  • 上拉输入:上拉就是把电位拉高,比如拉到Vcc。上拉就是将不确定的信号通过一个电阻嵌位在高电平,电阻同时起限流作用,弱强只是上拉电阻的阻值不同,没有什么严格区分。上拉输入就是信号进入芯片后加了一个上拉电阻,再经过施密特触发器转换成0、1信号,读取此时的引脚电平为高电平;

    Figure 1-3 输入上拉模式
  • 下拉输入:就是把电压拉低,拉到GND。与上拉原理相似。下拉输入就是信号进入芯片后加了一个下拉电阻,再经过施密特触发器转换成0、1信号,读取此时的引脚电平为低电平;

    Figure 1-4 输入下拉模式
  • 模拟输入:信号进入后不经过上拉电阻或者下拉电阻,关闭施密特触发器,经由另一线路把电压信号传送到片上外设模块。模拟输入是指传统方式的输入,数字输入是输入PCM数字信号,即0、1的二进制数字信号,通过数模转换,转换成模拟信号,经前级放大进入功率放大器,功率放大器还是模拟的。比如传送给ADC模块,由ADC采集电压信号。所以可以理解为模拟输入的信号是未经处理的信号,是原汁原味的信号。
Figure 1-5 模拟输入模式
  • 输出模式
  • 开漏输出:一般用在电平不匹配的场合,如需要输出5V的高电平。输出端相当于三极管的集电极,要得到高电平状态需要上拉电阻才行。适合于做电流型的驱动,其吸收电流的能力相对强(一般20mA以内)。

    Figure 1-6 开漏输出模式
  • 复用开漏输出:可以理解为GPIO口被用作第二功能时的配置情况(即并非作为通用IO口使用)。端口必须配置成复用开漏功能输出模式。

    Figure 1-7 开漏复用功能
  • 推挽式输出:可以输出高、低电平,连接数字器件;推挽结构一般是指两个三极管分别受两个互补信号的控制,总是在一个三极管导通的时候另一个截止。高低电平由IC的电源决定。推挽电路是两个参数相同的三极管或MOSFET,以推挽方式存在于电路中,各负责正负半周的波形放大任务,电路工作时,两只对称的功率开关管每次只有一个导通,所以导通损耗小、效率高。输出既可以向负载灌电流,也可以从负载抽取电流。推拉式输出级既提高电路的负载能力,又提高开关速度。

    Figure 1-8 推挽式输出
  • 推挽式复用输出
Figure 1-9 推挽式复用功能

2 I/O复用和重映射

2.1 I/O复用

STM32 有很多的内置外设,这些外设的外部引脚都是与 GPIO 复用的。也就是说,一个 GPIO如果可以复用为内置外设的功能引脚,那么当这个 GPIO 作为内置外设使用的时候,就叫做复用。当I/O端口被配置为复用功能时:
● 在开漏或推挽式配置中,输出缓冲器被打开
● 内置外设的信号驱动输出缓冲器(复用功能输出)
● 施密特触发输入被激活
● 弱上拉和下拉电阻被禁止
● 在每个APB2时钟周期,出现在I/O脚上的数据被采样到输入数据寄存器
● 开漏模式时,读输入数据寄存器时可得到I/O口状态
● 在推挽模式时,读输出数据寄存器时可得到最后一次写的值
Figure 2-1 复用功能配置
大家都知道,MCU都有串口,STM32H7S7L8H6H有好几个串口。我们可以查手册知道,串口 1 的引脚对应的 IO 为PA9、PA10。PA9、PA10 默认功能是 GPIO,所以当PA9、PA10 引脚作为串口 1 的 TX,RX引脚使用的时候,那就是端口复用。

2.2 I/O重映射

为了使不同器件封装的外设 IO 功能数量达到最优,可以把一些复用功能重新映射到其他一些引脚上。 STM32 中有很多内置外设的输入输出引脚都具有重映射(remap)的功能。 我们知道每个内置外设都有若干个输入输出引脚,一般这些引脚的输出端口都是固定不变的,为了让设计工程师可以更好地安排引脚的走向和功能,在 STM32 中引入了外设引脚重映射的概念,即一个外设的引脚除了具有默认的端口外,还可以通过设置重映射寄存器的方式,把这个外设的引脚映射到其它的端口。
Table 2-1 USART1重映像
复用功能 USART1_REMAP = 0 USART1_REMAP = 1
USART1_TX PA9 PB6
USART1_RX PA10 PB7
从表中可以看出,默认情况下,串口 1 复用的时候的引脚位 PA9、PA10,同时我们可以将 TX 和 RX 重新映射到管脚 PB6 和 PB7 上面去。所以重映射我们同样要使能复用功能的时候讲解的 2 个时钟外,还要使能 AFIO 功能时钟,然后要调用重映射函数。

3 GPIO流水灯硬件电路分析

发光二极管是属于二极管的一种,具有二级管单向导电特性,即只有在正向电压(二极管的正极接正,负极接负)下才能导通发光。PO1引脚接发光二极管(LED1)的负极,所以PO1引脚输出低电平LED1亮,PO1引脚输出低电平LED1熄灭,,LED2/LED3/LED4同理。
Figure 3-1 LED电路图
值得注意的,不同的开发板,LED连接的GPIO一般是不同的,请注意修改。

4 GPIO流水灯寄存器分析

要想真正掌握一款单片机,分析寄存器是必不可少,但是对于STM32来再说,ST已经将寄存器操作封装成库函数,开发者只需要调用库函数即可,对于初学者来说,只需学会使用函数即可,对于没有基础的读者朋友就不必细究每个寄存器,当学到一定程度,再来一探究竟吧,笔者在这里只是给出GPIO的寄存配置相关配置表,在后面的章节也是如此。好了,继续进入正题吧。
每个GPIO端口都有一个32位模式寄存器 (GPIOx_MODER),一个32位输出类型寄存器 (GPIOx_ OTYPER),一个32位输出速度寄存器 (GPIOx_OSPEEDR),一个32位输出上拉/下拉寄存器 (GPIOx_PUPDR),两个32位数据寄存器 (GPIOx_IDR和GPIOx_ODR),一个32位置位/ 复位寄存器(GPIOx_BSRR),一个32位锁定寄存器(GPIOx_LCKR),两个复用功能寄存器(GPIOx_AFRL和GPIOx_AFRH)。每个I/O端口位可以自由编程,I/O端口寄存器可通过字节( 8 位)、半字( 16 位)或字( 32 位)对 GPIO 寄存器进行访问
点亮LED,基本步骤是:配置寄存器;控制寄存器。库开发只是将传统的配置方式编程函数,是的单片机开发变得简单方便快捷。
我们常用的IO端口寄存器只有几个:MODER、OTYPER、OSPEEDR、PUPDR、IDR、ODR。其中MODER、OTYPER、OSPEEDR、PUPDR控制着每个IO口的模式及输出速率。
Table 4-1 端口位配置表
Table 4-2 输出模式位
GPIO 端口模式寄存器 (GPIOx_MODER)描述如下图所示。
Figure 4-1 模式寄存器(GPIOx_MODER)
GPIO 端口输出类型寄存器 (GPIOx_OTYPER)如下图所示。
Figure 4-2 输出类型寄存器 (GPIOx_OTYPER)
GPIO 端口输出速度寄存器 (GPIOx_OSPEEDR)如下图所示。
Figure 4-3 输出速度寄存器 (GPIOx_OSPEEDR)
GPIO 端口上拉/下拉寄存器 (GPIOx_PUPDR) 如下图示所示。
Figure 4-4 上拉/下拉寄存器 (GPIOx_PUPDR)

5 GPIO 流水灯实现流程

笔者在上文已经分析了GPIO的原理及操作步骤,现在我们就来写代码吧。本书是用库来对STM32来开发的,这是本书的第一个实例,笔者为了读者比较直接配置寄存和库开发的区别,笔者在此用了两种方式进行开发,希望读者能自己体会两种方式的优劣。这里笔者会使用两种库:标准库和HAL库。
不管使用何种实现方式,其流程都是一样的。
GPIO是开发STM32最基本的配置,所以掌握GPIO的配置显得尤为重要。要实现流水灯,一般步骤可以总结为如下:
  1. GPIO 时钟使能;
  2. GPIO 端口模式设置;
  3. 初始化IO口;
  4. 编写处理函数;

6 GPIO 流水灯实现

6.1 STM32Cube新建工程

关于如何使用STM32Cube新建工程在前文已经讲解过了,这里直说配置GPIO部分内容。本文要实现流水灯,其实输出为初始化设置为高电平还是低电平都可以,因为流水灯需要不断反转。
1.首先进行系统配置,debug模式选择可以选择其他模式,默认选择Disable,Timebase Source选择SysTick,关于Timebase Source在后文会详细讲解。
Figure 6-1 系统配置
2.时钟配置,外部高速时钟。
Figure 6-2 时钟选择
STM32CubeMX配置时钟的界面非常方便,有很多提示(比如最大时钟),很多地方直接点击选择就行了。默认高速时钟是使用内部(HSI),而且CPU时钟配置的比较低。以我选择的STM32H7S,外部24M晶振为例(如下图)。配置系统时钟配置600MHz,APB1总线配置150MHz。
Figure 6-3 时钟配置
3. GPIO初始化配置
我们将PO1、PO5、PM2、PM3配置输出模式(高电平、低电平均可)、输出速率、上/下拉等,默认即可。
Figure 6-4 GPIO初始化
4.工程生成设置
Figure 6-5 工程配置
其他默认即可。最后点击“生成代码”即可生成代码。

6.2 GPIO流水灯实现

我们要实现流水灯,需要在主函数添加以下代码。
 
    HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
    /* Insert delay 200 ms */
    HAL_Delay(200);
    HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);
    /* Insert delay 200 ms */
    HAL_Delay(200);
    HAL_GPIO_TogglePin(LED3_GPIO_Port, LED3_Pin);
    /* Insert delay 200 ms */
    HAL_Delay(200);
    HAL_GPIO_TogglePin(LED4_GPIO_Port, LED4_Pin);
    /* Insert delay 200 ms */
    HAL_Delay(200);

 

 

6.3 流水灯代码分析

在分析代码前,先看看流水灯编程流程:
1>使能GPIO端口时钟;
2>初始化GPIO引脚,即为GPIO初始化结构体赋值,并调用相应初始化函数完成初始化配置;
3>根据实际需求控制流水灯。
STM32H7S HAL库开发,代码比较多,因此,我们在看一个实际工程时,只需从主函数开始,好了,接下来,笔者就带领大家一步一步看看流水灯是怎么实现的。笔者先贴出主函数代码。
 
 
/**
  * @brief The application entry point.
  * @retval int
  */
int main(void)
{

  /* USER CODE BEGIN 1 */
  /* Enable the CPU Cache */
  /* Enable I-Cache---------------------------------------------------------*/
  SCB_EnableICache();

  /* Enable D-Cache---------------------------------------------------------*/
  SCB_EnableDCache();
  /* USER CODE END 1 */

  /* MPU Configuration--------------------------------------------------------*/
  MPU_Config();

  /* MCU Configuration--------------------------------------------------------*/

  /* Update SystemCoreClock variable according to RCC registers values. */
  SystemCoreClockUpdate();

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
    /* Insert delay 200 ms */
    HAL_Delay(200);
    HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);
    /* Insert delay 200 ms */
    HAL_Delay(200);
    HAL_GPIO_TogglePin(LED3_GPIO_Port, LED3_Pin);
    /* Insert delay 200 ms */
    HAL_Delay(200);
    HAL_GPIO_TogglePin(LED4_GPIO_Port, LED4_Pin);
    /* Insert delay 200 ms */
    HAL_Delay(200);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

 

 
注释部分不用管,我们只看代码。
1.复位所有硬件
HAL_Init()函数为复位所有硬件,这个就不细说了。
/**
  * @brief  Configure the time base source, NVIC and any required global low level hardware
  *         by calling the HAL_MspInit() callback function to be optionally defined in user file
  *         stm32h7rsxx_hal_msp.c.
  *
  * @note   HAL_Init() function is called at the beginning of program after reset and before
  *         the clock configuration.
  *
  * @note   In the default implementation the System Timer (Systick) is used as source of time base.
  *         The Systick configuration is based on HSI clock, as HSI is the clock
  *         used after a system Reset and the NVIC configuration is set to Priority group 4.
  *         Once done, time base tick starts incrementing: the tick variable counter is incremented
  *         each 1ms in the SysTick_Handler() interrupt handler.
  *
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_Init(void)
{
  HAL_StatusTypeDef  status = HAL_OK;

  /* Set Interrupt Group Priority */
  HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);

  /* Use SysTick as time base source and configure 1ms tick */
  if (HAL_InitTick(TICK_INT_PRIORITY) != HAL_OK)
  {
    status = HAL_ERROR;
  }
  else
  {
    /* Init the low level hardware */
    HAL_MspInit();
  }

  /* Return function status */
  return status;
}

 

 
 
 
 
2.时钟初始化函数
SystemCoreClockUpdate ()函数用于时钟初始化,也就是通过STM32Cube实现。
时钟初始化函数如下:
/**
  * @brief  Update SystemCoreClock variable according to RCC registers values.
  *         The SystemCoreClock variable contains the core clock (sys_cpu_ck), it can
  *         be used by the user application to setup the SysTick timer or configure
  *         other parameters.
  *
  * @note   Each time the core clock changes, this function must be called
  *         to update SystemCoreClock variable value. Otherwise, any configuration
  *         based on this variable will be incorrect.
  *
  * @note   - The system frequency computed by this function is not the real
  *           frequency in the chip. It is calculated based on the predefined
  *           constant and the selected clock source:
  *
  *           - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(*)
  *
  *           - If SYSCLK source is CSI, SystemCoreClock will contain the CSI_VALUE(**)
  *
  *           - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(***)
  *
  *           - If SYSCLK source is PLL, SystemCoreClock will contain the HSI_VALUE(*)
  *             or CSI_VALUE(**) or HSE_VALUE(***) multiplied/divided by the PLL factors.
  *
  *         (*) HSI_VALUE is a constant defined in stm32h7rsxx_hal.h file (default value
  *              64 MHz) but the real value may vary depending on the variations
  *              in voltage and temperature.
  *
  *         (**) CSI_VALUE is a constant defined in stm32h7rsxx_hal.h file (default value
  *             4 MHz) but the real value may vary depending on the variations
  *             in voltage and temperature.
  *
  *         (***) HSE_VALUE is a constant defined in stm32h7rsxx_hal.h file (default value
  *              24 MHz), user has to ensure that HSE_VALUE is same as the real
  *              frequency of the crystal used. Otherwise, this function may
  *              have wrong result.
  *
  *         - The result of this function could be not correct when using fractional
  *           value for HSE crystal.
  *
  * @retval None
  */
void SystemCoreClockUpdate(void)
{
  uint32_t sysclk, hsivalue, pllsource, pllm, pllp, core_presc;
  float_t pllfracn, pllvco;

  /* Get SYSCLK source -------------------------------------------------------*/
  switch (RCC->CFGR & RCC_CFGR_SWS)
  {
  case 0x00:  /* HSI used as system clock source (default after reset) */
    sysclk = (HSI_VALUE >> ((RCC->CR & RCC_CR_HSIDIV) >> RCC_CR_HSIDIV_Pos));
    break;

  case 0x08:  /* CSI used as system clock source */
    sysclk = CSI_VALUE;
    break;

  case 0x10:  /* HSE used as system clock source */
    sysclk = HSE_VALUE;
    break;

  case 0x18:  /* PLL1 used as system clock  source */
    /* PLL1_VCO = (HSE_VALUE or HSI_VALUE or CSI_VALUE/ PLLM) * PLLN
       SYSCLK = PLL1_VCO / PLL1R
       */
    pllsource = (RCC->PLLCKSELR & RCC_PLLCKSELR_PLLSRC);
    pllm = ((RCC->PLLCKSELR & RCC_PLLCKSELR_DIVM1) >> RCC_PLLCKSELR_DIVM1_Pos)  ;
    if ((RCC->PLLCFGR & RCC_PLLCFGR_PLL1FRACEN) != 0U)
    {
      pllfracn = (float_t)(uint32_t)(((RCC->PLL1FRACR & RCC_PLL1FRACR_FRACN)>> RCC_PLL1FRACR_FRACN_Pos));
    }
    else
    {
      pllfracn = (float_t)0U;
    }

    if (pllm != 0U)
    {
      switch (pllsource)
      {
      case 0x02:  /* HSE used as PLL1 clock source */
        pllvco = ((float_t)HSE_VALUE / (float_t)pllm) * ((float_t)(uint32_t)(RCC->PLL1DIVR1 & RCC_PLL1DIVR1_DIVN) + (pllfracn/(float_t)0x2000) +(float_t)1 );
        break;

      case 0x01:  /* CSI used as PLL1 clock source */
        pllvco = ((float_t)CSI_VALUE / (float_t)pllm) * ((float_t)(uint32_t)(RCC->PLL1DIVR1 & RCC_PLL1DIVR1_DIVN) + (pllfracn/(float_t)0x2000) +(float_t)1 );
        break;

      case 0x00:  /* HSI used as PLL1 clock source */
      default:
        hsivalue = (HSI_VALUE >> ((RCC->CR & RCC_CR_HSIDIV) >> RCC_CR_HSIDIV_Pos));
        pllvco = ( (float_t)hsivalue / (float_t)pllm) * ((float_t)(uint32_t)(RCC->PLL1DIVR1 & RCC_PLL1DIVR1_DIVN) + (pllfracn/(float_t)0x2000) +(float_t)1 );
        break;
      }
      pllp = (((RCC->PLL1DIVR1 & RCC_PLL1DIVR1_DIVP) >> RCC_PLL1DIVR1_DIVP_Pos) + 1U ) ;
      sysclk =  (uint32_t)(float_t)(pllvco/(float_t)pllp);
    }
    else
    {
      sysclk = 0U;
    }
    break;

  default:  /* Unexpected, default to HSI used as system clock source (default after reset) */
    sysclk = (HSI_VALUE >> ((RCC->CR & RCC_CR_HSIDIV) >> RCC_CR_HSIDIV_Pos));
    break;
  }

  /* system clock frequency : CM7 CPU frequency  */
  core_presc = (RCC->CDCFGR & RCC_CDCFGR_CPRE);
  if (core_presc >= 8U)
  {
    SystemCoreClock = (sysclk >> (core_presc - RCC_CDCFGR_CPRE_3 + 1U));
  }
  else
  {
    SystemCoreClock = sysclk;
  }
}

 

 
 
 
 
 
RCC_TypeDef结构体类型定义时钟来源和系统时钟生成配置,本例程使用外部24MHz晶振,通过PLL锁相环后得到600M系统时钟。一般选择使能系统时钟、AHB、APB1和APB2总线时钟。
值得注意的是,这是使用的App部分工程,Boot工程的时钟配置为SystemClock_Config()。
 
3.GPIO初始化函数
MX_GPIO_Init()函数用于LED的GPIO初始化,代码如下:
 
/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOM_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOO_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOM, LED4_Pin|LED3_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOO, LED1_Pin|LED2_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pins : LED4_Pin LED3_Pin */
  GPIO_InitStruct.Pin = LED4_Pin|LED3_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOM, &GPIO_InitStruct);

  /*Configure GPIO pin : PD0 */
  GPIO_InitStruct.Pin = GPIO_PIN_0;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  GPIO_InitStruct.Alternate = GPIO_AF8_UART4;
  HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

  /*Configure GPIO pins : LED1_Pin LED2_Pin */
  GPIO_InitStruct.Pin = LED1_Pin|LED2_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOO, &GPIO_InitStruct);

/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}

 

 
 
 
以上就是LED的初始化函数,初始化GPIO有个重要的结构体GPIO_InitTypeDef,先看看该结构体。
/** 
  * @brief GPIO Init structure definition  
  */ 
typedef struct
{
  uint32_t Pin;       /*!< Specifies the GPIO pins to be configured.
                           This parameter can be any value of [url=home.php?mod=space&uid=1064992]@ref[/url] GPIO_pins_define */
  uint32_t Mode;      /*!< Specifies the operating mode for the selected pins.
                           This parameter can be a value of @ref GPIO_mode_define */
  uint32_t Pull;      /*!< Specifies the Pull-up or Pull-Down activation for the selected pins.
                           This parameter can be a value of @ref GPIO_pull_define */
  uint32_t Speed;     /*!< Specifies the speed for the selected pins.
                           This parameter can be a value of @ref GPIO_speed_define */
  uint32_t Alternate;  /*!< Peripheral to be connected to the selected pins. 
                            This parameter can be a value of @ref GPIO_Alternate_function_selection */
}GPIO_InitTypeDef;

 

 
  • Pin:引脚编号选择,一个GPIO有16个引脚可选,参数可选有:GPIO_PIN_0、….、GPIO_PIN_15和GPIO_PIN_ALL。很多时候我们可以使用或运算进行选择:GPIO_PIN_0| GPIO_PIN_4。
  • Mode:引脚模式选择,在前文笔者已经介绍了引脚有8中工作模式,根据外设选择相应模式。
Table 6-1 GPIO引脚工作模式
  • Pull:上下拉,用于输入模式,可选:GPIO_NOPULL不上下拉;GPIO_PULLUP:使能下拉;GPIO_PULLDOWN使能下拉;
  • Speed:引脚速度,可选:GPIO_SPEED_FREQ_LOW:低速(2MHz);中速(10MHz);高速(50MHz)。
在初始化代码中,还有一个重要的函数HAL_GPIO_WritePin()。HAL_GPIO_WritePin()函数为2个LED灯时钟初始化状态,这里设置为低电平,所以初始化状态2个LED都是暗的。
4.流水灯实现
在前文已经贴出了流水灯的相应代码。
  while (1)
  {
    HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
    /* Insert delay 200 ms */
    HAL_Delay(200);
    HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);
    /* Insert delay 200 ms */
    HAL_Delay(200);
    HAL_GPIO_TogglePin(LED3_GPIO_Port, LED3_Pin);
    /* Insert delay 200 ms */
    HAL_Delay(200);
    HAL_GPIO_TogglePin(LED4_GPIO_Port, LED4_Pin);
    /* Insert delay 200 ms */
    HAL_Delay(200);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }

 

 
HAL_GPIO_TogglePin函数用于GPIO电平反转,HAL_Delay用于延时,单位是毫秒,以此不断将LED1、LED2、LED3、LED4关灭,这就是实现流水灯的效果。

7 实验现象

将编译好的程序下载到板子中,可以看到四个LED灯不同地闪烁。
本章虽然用不同的方式实现了流水灯,也就是不断控制GPIO的高低电平,我想大家会有很多疑问,寄存器、表混库、HAL的区别是什么,流水灯是怎么一步步调用等等,后面的章节会一一解答,尽请期待!
 

此帖出自stm32/stm8论坛

最新回复

要想真正掌握一款单片机,分析和掌握寄存器编程是必不可少。   详情 回复 发表于 2024-9-23 07:26
点赞 关注
 

回复
举报

6538

帖子

0

TA的资源

五彩晶圆(高级)

沙发
 

要想真正掌握一款单片机,分析和掌握寄存器编程是必不可少。

此帖出自stm32/stm8论坛
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
推荐帖子
ARM开发详解

ARM开发详解

版主推荐:让你的下载明明白白(长期持续更新中)

俗话说,宝物沉底。 论坛一个吸引人的地方就是有很多的资料可分享。 另外就是,作为一个坛友,从很久以前就希望坛子里分享 ...

Arduino AR-15机枪设计-目标自动检测--EEWORLD大学堂

Arduino AR-15机枪设计-目标自动检测:https://training.eeworld.com.cn/course/2095使用了摄像头、AR-15、Arduino和步进电机,并 ...

Seeeduino XIAO 做的冰与火迷你壁炉

来自:https://www.hackster.io/Makerming/mini-fireplace-a-song-of-ice-and-fire-seeeduino-xiao-a064ea 494419 迷 ...

滤波器求资料

HXDL-5 JLC101-5(DC28V5A) 两款滤波器的型号,大家有没有遇到过,想知道封装的情况

【基于NUCLEO-F746ZG电机开发应用】12.参数配置-定时器TIM1配置

在伺服电机的控制过程中,使电机能够按照自己的想法转起来,一定要用到PWM输出控制,但是PWM该如何产生,频率如何控制,占空比 ...

汇总:RCSN体验W806联盛德 9.9元开发板

@RCSN W806 联盛德 9.9元开发板体验--- linux环境开发 W806 联盛德 9.9元开发板体验之二---littlevgl8.0移植 W806 ...

硬核拆解|看看千元智能电表内部如何连接!50元京东卡等您拿

智能电表是当今智能电网的一个重要组成部分。智能电网不仅仅支持发电和配电;它是一个由物联网(IoT)技术和通信网络组成的生态 ...

Matter 的数据模型

文章来源:https://blog.csdn.net/espressif/article/details/122596475 设备开发人员通常会通过数据模型来定义 Matt ...

【初学者】抑制音频噪声的措施

抑制音频噪声的措施 开关电源的音频噪声就是用耳朵能听到的,主要由电容和变压器造成 (1)电容噪声 在周期性尖峰电压 ...

关闭
站长推荐上一条 1/10 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表