STM32 第12讲 GPIO:结构/8种工作模式/寄存器/驱动模型/配置步骤/实验

2023-05-16

文章目录

  • GPIO简介
    • GPIO特点
    • 电气特性
    • GPIO引脚分布
  • GPIO8种工作模式
    • GPIO的基本结构
    • 8种工作模式
  • GPIO寄存器
      • GPIO端口模式寄存器(GPIOx_MODER)
      • GPIO端口输出类型寄存器(GPIOx_OTYPER)
      • GPIO端口输出速度寄存器(GPIOx_OSPEEDR)
      • GPIO端口上拉/下拉寄存器(GPIOx_PUPDR)
      • 端口输入数据寄存器(GPIOx_IDR)
      • 端口输出数据寄存器(GPIOx_ODR)
      • 端口置位/复位寄存器(GPIOx_BSRR)
  • 通用外设驱动模型
  • GPIO配置步骤
    • 使能时钟(__HAL_RCC_GPIOx_CLK_ENABLE())
    • 设置工作模式(HAL_GPIO_Init())
    • 设置输出状态(HAL_GPIO_WritePin()/HAL_GPIO_Togglepin())
    • 设置输入状态(HAL_GPIO_ReadPin())
  • 跑马灯实验
    • 硬件原理图
    • 代码
    • 下载验证
    • 通过按键控制LED亮灭

GPIO简介

General Purpose Input Output: 通用输入输出端口。负责采集外部器件的信息或者控制外部器件工作。

GPIO特点

  • 不同型号的IO口数量不同,查阅选型手册进行查询
  • 快速翻转功能,每次翻转最快只需要两个时钟周期
  • 每个IO口都可以用作中断
  • 具有8种工作模式

电气特性

  • STM32工作电压范围:2V ≤ VDD ≤ 3.6V
  • GPIO识别电压范围:
    对于COMS端口: -0.3V ≤ VIL ≤ 1.164V 1.833V ≤ VIH ≤ 3.6V
  • GPIO输出电流: 单个IO最大25mA

GPIO引脚分布

对于STM32F407ZGT6芯片,一共有144个引脚,其中有112个可供编程的IO口,分为7组:GPIOA、GPIOB、GPIOC、GPIOD、GPIOE、GPIOF 和 GPIOG。
查找数据手册,对于定义了FT的引脚可以兼容5V电压,可以直接接 5V 的外设(注意:如果引脚设置的是模拟输入模式,则不能接 5V!)
在这里插入图片描述

GPIO8种工作模式

GPIO的基本结构

在这里插入图片描述

  • 1.保护二极管
    当引脚输入电压高于
    VDD 时,上面的二极管导通,当引脚输入电压低于 VSS 时,下面的二极管导通,从而使输入芯片内部的电压处于比较稳定的值,但这样保护是有限的。
  • 2.上拉、下拉电阻
    它们阻值大概在 30~50K 欧之间,可以通过上、下两个对应的开关控制,这两个开关由寄存器控制。当引脚外部的器件没有干扰引脚的电压时,即没有外部的上、下拉电压,引脚的电平由引脚内部上、下拉决定,开启内部上拉电阻工作,引脚电平为高,开启内部下拉电阻工作,则引脚电平为低。同样,如果内部上、下拉电阻都不开启,这种情况就是我们所说的浮空模式。浮空模式下,引脚的电平是不可确定的。引脚的电平可以由外部的上、下拉电平决定。需要注意的是,STM32 的内部上拉是一种**“弱上拉”**,这样的上拉电流很弱,如果有要求大电流还是得外部上拉。
  • 3.施密特触发器
    施密特触发器是一种整形电路,可以将非标准的方波,整形成方波。
    当输入电压高于正向阈值电压,输出为高;
    当输入电压低于负向阈值电压,输出为低;
    当输入在正负向阈值电压之间,输出不改变。
    下图为A–比较器与B–施密特触发器的工作比较,可以看出施密特触发器具有滞回特性,具有一定的抗干扰能力。
    在这里插入图片描述
  • 4.P-MOS管与N-MOS管
    MOS管是压控型元件,通过控制栅源电压( Vgs )来实现导通或关闭。
    在这里插入图片描述
    开漏输出:输出端相当于三极管的集电极,要得到高电平状态需要上拉电阻才行。推挽输出:这两只对称的 MOS 管每次只有一只导通,所以导通损耗小、效率高。输出既可以向负载灌电流,也可以从负载拉电流。推拉式输出既能提高电路的负载能力,又能提高开关速度。

8种工作模式

在这里插入图片描述

  • 1.输入浮空
    ①上拉电阻关闭
    ②下拉电阻关闭
    ③施密特触发器打开
    ④双MOS管不导通
    特点:空闲时,IO状态不确定,由外部环境决定
    应用:按键检测等
    在这里插入图片描述
  • 2.输入上拉
    ①上拉电阻打开
    ②下拉电阻关闭
    ③施密特触发器打开
    ④双MOS管不导通
    特点:空闲时,IO呈现高电平
    应用:在需要外部上拉电阻的时候,可以使用内部上拉电阻,这样可以节省一个外部电阻,但是内部上拉电阻的阻值较大,所以只是“弱上拉”,不适合做电流型驱动
    在这里插入图片描述
  • 3.输入下拉
    ①上拉电阻关闭
    ②下拉电阻打开
    ③施密特触发器打开
    ④双MOS管不导通
    特点:空闲时,IO呈现低电平
    应用:在需要外部下拉电阻的时候,可以使用内部下拉电阻,这样可以节省一个外部电阻,但是内部下拉电阻的阻值较大,所以不适合做电流型驱动。
    在这里插入图片描述
  • 4.模拟功能
    ①上拉电阻关闭
    ②下拉电阻关闭
    ③施密特触发器关闭
    ④双MOS管不导通
    特点:专门用于模拟信号输入或输出,如:ADC和DAC
    在这里插入图片描述
  • 5.开漏输出
    ①P-MOS 被“输出控制”控制在截止状态,因此 IO 的状态取决于 N-MOS 的导通状况。
    ②只有N-MOS还受控制于输出寄存器,“输出控制器”对输入信号进行了逻辑非的操作。
    ③IO 到输入电路的采样电路仍被打开,且可以选择是否使用上下拉电阻。
    特点:不能输出高电平,只能接入上拉电阻后才能输出高电平。

⚫如果输出数据寄存器设置为 0 时,经过“输出控制”的逻辑非操作后,输出逻辑 1 到 N-MOS 管的栅极,这时 N-MOS 管就会导通,使得 I/O 引脚接到 VSS,即输出低电平。
⚫如果输出数据寄存器设置为 1 时,经过“输出控制器”的逻辑非操作后,输出逻辑 0 到 N-MOS 管的栅极,这时 N-MOS 管就会截止。因为 P-MOS 管是一直截止的,使得 I/O 引脚呈现高阻态,即不输出低电平,也不输出高电平。因此要 I/O 引脚输出高电平就必须接上拉电阻。
这时可以接内部上拉电阻,或者接一个外部上拉电阻。由于内部上拉电阻的阻值较大,所以只是“弱上拉”。需要大电流驱动,请接外部的上拉电阻。此外,上拉电阻具有线与特性,即如果有很多开漏模式的引脚连在一起的时候,只有当所有引脚都输出高阻态,电平才为 1,只要有其中一个为低电平时,就等于接地,使得整条线路都为低电平 0。我们的 IIC 通信(IIC_SDA)就用到这个原理。

在开漏输出模式下,施密特触发器是打开的,所以 IO 口引脚的电平状态会被采集到输入数据寄存器中,如果对输入数据寄存器进行读访问可以得到 IO 口的状态。也就是说开漏输出模式下,我们可以对 IO 口进行读数据。
在这里插入图片描述

  • 6.开漏式复用功能
    一个 IO 口可以是通用的 IO 口功能,还可以是其他外设的特殊功能引脚,这就是 IO 口的复用功能。
    当选择复用功能时,引脚的状态是由对应的外设控制,而不是输出数据寄存器。
    ①施密特触发器打开
    ② P-MOS管始终不导通
    特点:
    1、不能输出高电平,必须有外部(或内部)上拉才能输出高电平
    2、由其他外设控制输出

在开漏式复用功能模式下,施密特触发器也是打开的,我们可以读取 IO 口的电平状态,同时外设可以读取 IO 口的信息
在这里插入图片描述

  • 7.推挽输出
    ①施密特触发器打开
    ②往ODR对应位写0,N-MOS管导通,写1则P-MOS管导通
    特点:可输出高低电平,驱动能力强

可以把“输出控制”简单地等效为一个非门。
⚫如果输出数据寄存器设置为 0 时,经过“输出控制”的逻辑非操作后,输出逻辑 1 到 P-MOS管的栅极,这时 P-MOS 管就会截止,同时也会输出逻辑 1 到 N-MOS 管的栅极,这时 N-MOS管就会导通,使得 I/O 引脚接到 VSS,即输出低电平。
⚫如果输出数据寄存器设置为 1 时,经过“输出控制”的逻辑非操作后,输出逻辑 0 到 N-MOS 管的栅极,这时 N-MOS 管就会截止,同时也会输出逻辑 0 到 P-MOS 管的栅极,这时 P-MOS 管就会导通,使得 I/O 引脚接到 VDD,即输出高电平。

在推挽输出模式下,施密特触发器也是打开的,我们可以读取 IO 口的电平状态。
在这里插入图片描述

  • 8.推挽式复用功能
    特点:
    1、可输出高低电平, 驱动能力强
    2、由其他外设控制输出

在这里插入图片描述

GPIO寄存器

STM32F4系列寄存器包括:
4 个 32 位配置寄存器(MODER、OTYPER、OSPEEDR 和 PUPDR)
2 个 32 位数据寄存器(IDR 和 ODR)
1 个 32 位置位/复位寄存器 (BSRR)
1 个 32 位锁定寄存器 (LCKR)
2 个 32 位复用功能选择寄存器(AFRH 和 AFRL)

GPIO端口模式寄存器(GPIOx_MODER)

在这里插入图片描述

GPIO端口输出类型寄存器(GPIOx_OTYPER)

在这里插入图片描述

GPIO端口输出速度寄存器(GPIOx_OSPEEDR)

在这里插入图片描述

GPIO端口上拉/下拉寄存器(GPIOx_PUPDR)

在这里插入图片描述
上面这 4 个配置寄存器就是用来配置 GPIO 的相关模式和状态,它们通过不同的配置组合方法,就决定我们所说的 8 种工作模式。
在这里插入图片描述

端口输入数据寄存器(GPIOx_IDR)

在这里插入图片描述

端口输出数据寄存器(GPIOx_ODR)

在这里插入图片描述

端口置位/复位寄存器(GPIOx_BSRR)

在这里插入图片描述
首先 BSRR 是只写权限,而 ODR 是可读可写权限。我们首先需要读出来 ODR 寄存器的值,然后对整个 ODR 寄存器重新赋值来达到设置某个或者某些 IO 口的目的,而 BSRR 寄存器,我们就不需要先读,而是直接设置即可,这在多任务实时操作系统中作用很大。BSRR 寄存器还有一个好处,就是 BSRR 寄存器改变引脚状态的时候,不会被中断打断,而 ODR 寄存器有被中断打断的风险。

通用外设驱动模型

  • 初始化(时钟设置、参数设置、IO设置、中断设置)
  • 读函数(从外设读取数据)
  • 写函数(往外设写入数据)
  • 中断服务函数(根据中断标志,处理外设各种中断事务)

GPIO配置步骤

使能时钟(__HAL_RCC_GPIOx_CLK_ENABLE())

#define __HAL_RCC_GPIOA_CLK_ENABLE()   do { \
                                        __IO uint32_t tmpreg = 0x00U; \
                                        SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOAEN);\
                                        /* Delay after an RCC peripheral clock enabling */ \
                                        tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOAEN);\
                                        UNUSED(tmpreg); \
                                          } while(0U)

设置工作模式(HAL_GPIO_Init())

void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)
{
 uint32_t position;
 uint32_t ioposition = 0x00U;
 uint32_t iocurrent = 0x00U;
 uint32_t temp = 0x00U;

 /* Check the parameters */
 assert_param(IS_GPIO_ALL_INSTANCE(GPIOx));
 assert_param(IS_GPIO_PIN(GPIO_Init->Pin));
 assert_param(IS_GPIO_MODE(GPIO_Init->Mode));
 assert_param(IS_GPIO_PULL(GPIO_Init->Pull));

 /* Configure the port pins */
 for(position = 0U; position < GPIO_NUMBER; position++)
 {
   /* Get the IO position */
   ioposition = 0x01U << position;
   /* Get the current IO position */
   iocurrent = (uint32_t)(GPIO_Init->Pin) & ioposition;

   if(iocurrent == ioposition)
   {
     /*--------------------- GPIO Mode Configuration ------------------------*/
     /* In case of Output or Alternate function mode selection */
     if(((GPIO_Init->Mode & GPIO_MODE) == MODE_OUTPUT) || \
         (GPIO_Init->Mode & GPIO_MODE) == MODE_AF)
     {
       /* Check the Speed parameter */
       assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
       /* Configure the IO Speed */
       temp = GPIOx->OSPEEDR; 
       temp &= ~(GPIO_OSPEEDER_OSPEEDR0 << (position * 2U));
       temp |= (GPIO_Init->Speed << (position * 2U));
       GPIOx->OSPEEDR = temp;

       /* Configure the IO Output Type */
       temp = GPIOx->OTYPER;
       temp &= ~(GPIO_OTYPER_OT_0 << position) ;
       temp |= (((GPIO_Init->Mode & GPIO_OUTPUT_TYPE) >> 4U) << position);
       GPIOx->OTYPER = temp;
      }

     if((GPIO_Init->Mode & GPIO_MODE) != MODE_ANALOG)
     {
       /* Activate the Pull-up or Pull down resistor for the current IO */
       temp = GPIOx->PUPDR;
       temp &= ~(GPIO_PUPDR_PUPDR0 << (position * 2U));
       temp |= ((GPIO_Init->Pull) << (position * 2U));
       GPIOx->PUPDR = temp;
     }

     /* In case of Alternate function mode selection */
     if((GPIO_Init->Mode & GPIO_MODE) == MODE_AF)
     {
       /* Check the Alternate function parameter */
       assert_param(IS_GPIO_AF(GPIO_Init->Alternate));
       /* Configure Alternate function mapped with the current IO */
       temp = GPIOx->AFR[position >> 3U];
       temp &= ~(0xFU << ((uint32_t)(position & 0x07U) * 4U)) ;
       temp |= ((uint32_t)(GPIO_Init->Alternate) << (((uint32_t)position & 0x07U) * 4U));
       GPIOx->AFR[position >> 3U] = temp;
     }

     /* Configure IO Direction mode (Input, Output, Alternate or Analog) */
     temp = GPIOx->MODER;
     temp &= ~(GPIO_MODER_MODER0 << (position * 2U));
     temp |= ((GPIO_Init->Mode & GPIO_MODE) << (position * 2U));
     GPIOx->MODER = temp;

     /*--------------------- EXTI Mode Configuration ------------------------*/
     /* Configure the External Interrupt or event for the current IO */
     if((GPIO_Init->Mode & EXTI_MODE) == EXTI_MODE)
     {
       /* Enable SYSCFG Clock */
       __HAL_RCC_SYSCFG_CLK_ENABLE();

       temp = SYSCFG->EXTICR[position >> 2U];
       temp &= ~(0x0FU << (4U * (position & 0x03U)));
       temp |= ((uint32_t)(GPIO_GET_INDEX(GPIOx)) << (4U * (position & 0x03U)));
       SYSCFG->EXTICR[position >> 2U] = temp;

       /* Clear EXTI line configuration */
       temp = EXTI->IMR;
       temp &= ~((uint32_t)iocurrent);
       if((GPIO_Init->Mode & GPIO_MODE_IT) == GPIO_MODE_IT)
       {
         temp |= iocurrent;
       }
       EXTI->IMR = temp;

       temp = EXTI->EMR;
       temp &= ~((uint32_t)iocurrent);
       if((GPIO_Init->Mode & GPIO_MODE_EVT) == GPIO_MODE_EVT)
       {
         temp |= iocurrent;
       }
       EXTI->EMR = temp;

       /* Clear Rising Falling edge configuration */
       temp = EXTI->RTSR;
       temp &= ~((uint32_t)iocurrent);
       if((GPIO_Init->Mode & RISING_EDGE) == RISING_EDGE)
       {
         temp |= iocurrent;
       }
       EXTI->RTSR = temp;

       temp = EXTI->FTSR;
       temp &= ~((uint32_t)iocurrent);
       if((GPIO_Init->Mode & FALLING_EDGE) == FALLING_EDGE)
       {
         temp |= iocurrent;
       }
       EXTI->FTSR = temp;
     }
   }
 }
}

对于关键结构体

typedef struct
{
 __IO uint32_t MODER;    /*!< GPIO port mode register,               Address offset: 0x00      */
 __IO uint32_t OTYPER;   /*!< GPIO port output type register,        Address offset: 0x04      */
 __IO uint32_t OSPEEDR;  /*!< GPIO port output speed register,       Address offset: 0x08      */
 __IO uint32_t PUPDR;    /*!< GPIO port pull-up/pull-down register,  Address offset: 0x0C      */
 __IO uint32_t IDR;      /*!< GPIO port input data register,         Address offset: 0x10      */
 __IO uint32_t ODR;      /*!< GPIO port output data register,        Address offset: 0x14      */
 __IO uint32_t BSRR;     /*!< GPIO port bit set/reset register,      Address offset: 0x18      */
 __IO uint32_t LCKR;     /*!< GPIO port configuration lock register, Address offset: 0x1C      */
 __IO uint32_t AFR[2];   /*!< GPIO alternate function registers,     Address offset: 0x20-0x24 */
} GPIO_TypeDef;

typedef struct
{
 uint32_t Pin;       /*!< Specifies the GPIO pins to be configured.
                          This parameter can be any value of @ref 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;

设置输出状态(HAL_GPIO_WritePin()/HAL_GPIO_Togglepin())

void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
{
  /* Check the parameters */
  assert_param(IS_GPIO_PIN(GPIO_Pin));
  assert_param(IS_GPIO_PIN_ACTION(PinState));

  if(PinState != GPIO_PIN_RESET)
  {
    GPIOx->BSRR = GPIO_Pin;
  }
  else
  {
    GPIOx->BSRR = (uint32_t)GPIO_Pin << 16U;
  }
}

void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
  uint32_t odr;

  /* Check the parameters */
  assert_param(IS_GPIO_PIN(GPIO_Pin));

  /* get current Ouput Data Register value */
  odr = GPIOx->ODR;

  /* Set selected pins that were at low level, and reset ones that were high */
  GPIOx->BSRR = ((odr & GPIO_Pin) << GPIO_NUMBER) | (~odr & GPIO_Pin);
}

设置输入状态(HAL_GPIO_ReadPin())

GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
  GPIO_PinState bitstatus;

  /* Check the parameters */
  assert_param(IS_GPIO_PIN(GPIO_Pin));

  if((GPIOx->IDR & GPIO_Pin) != (uint32_t)GPIO_PIN_RESET)
  {
    bitstatus = GPIO_PIN_SET;
  }
  else
  {
    bitstatus = GPIO_PIN_RESET;
  }
  return bitstatus;
}

跑马灯实验

硬件原理图

在这里插入图片描述
在此原理图中,使用开漏输出或推挽输出均可。

代码

led.h文件

#ifndef __LED_H
#define __LED_H

#include "./SYSTEM/sys/sys.h"


/******************************************************************************************/
/* 引脚 定义 */

#define LED0_GPIO_PORT                  GPIOF
#define LED0_GPIO_PIN                   GPIO_PIN_9
#define LED0_GPIO_CLK_ENABLE()          do{ __HAL_RCC_GPIOE_CLK_ENABLE(); }while(0)             /* PF口时钟使能 */

#define LED1_GPIO_PORT                  GPIOF
#define LED1_GPIO_PIN                   GPIO_PIN_10
#define LED1_GPIO_CLK_ENABLE()          do{ __HAL_RCC_GPIOE_CLK_ENABLE(); }while(0)             /* PF口时钟使能 */

/******************************************************************************************/

/* LED端口定义 */
#define LED0(x)   do{ x ? \
                      HAL_GPIO_WritePin(LED0_GPIO_PORT, LED0_GPIO_PIN, GPIO_PIN_SET) : \
                      HAL_GPIO_WritePin(LED0_GPIO_PORT, LED0_GPIO_PIN, GPIO_PIN_RESET); \
                  }while(0)       /* LED0 = RED */

#define LED1(x)   do{ x ? \
                      HAL_GPIO_WritePin(LED1_GPIO_PORT, LED1_GPIO_PIN, GPIO_PIN_SET) : \
                      HAL_GPIO_WritePin(LED1_GPIO_PORT, LED1_GPIO_PIN, GPIO_PIN_RESET); \
                  }while(0)       /* LED1 = GREEN */

/* LED取反定义 */
#define LED0_TOGGLE()    do{ HAL_GPIO_TogglePin(LED0_GPIO_PORT, LED0_GPIO_PIN); }while(0)       /* LED0 = !LED0 */
#define LED1_TOGGLE()    do{ HAL_GPIO_TogglePin(LED1_GPIO_PORT, LED1_GPIO_PIN); }while(0)       /* LED1 = !LED1 */

/******************************************************************************************/
/* 外部接口函数*/
void led_init(void);                                                                            /* 初始化 */

#endif

led.c文件

#include "./BSP/LED/led.h"


/**
 * @brief       初始化LED相关IO口, 并使能时钟
 * @param       无
 * @retval      无
 */
void led_init(void)
{
    GPIO_InitTypeDef gpio_init_struct;
    
    LED0_GPIO_CLK_ENABLE();                                 /* LED0时钟使能 */
    LED1_GPIO_CLK_ENABLE();                                 /* LED1时钟使能 */

    gpio_init_struct.Pin = LED0_GPIO_PIN;                   /* LED0引脚 */
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;            /* 推挽输出 */
    gpio_init_struct.Pull = GPIO_PULLUP;                    /* 上拉 */
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;          /* 高速 */
    HAL_GPIO_Init(LED0_GPIO_PORT, &gpio_init_struct);       /* 初始化LED0引脚 */

    gpio_init_struct.Pin = LED1_GPIO_PIN;                   /* LED1引脚 */
    HAL_GPIO_Init(LED1_GPIO_PORT, &gpio_init_struct);       /* 初始化LED1引脚 */
    
    LED0(1);                                                /* 关闭 LED0 */
    LED1(1);                                                /* 关闭 LED1 */
}

main.c文件

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"


void led_init(void);                    /* LED初始化函数声明 */

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */
    delay_init(168);                    /* 延时初始化 */
    led_init();                         /* 初始化LED */
    
    while(1)
    {
        HAL_GPIO_WritePin(GPIOF,GPIO_PIN_9,GPIO_PIN_RESET);     /* LED0 亮 */
        HAL_GPIO_WritePin(GPIOF,GPIO_PIN_10,GPIO_PIN_SET);      /* LED1 灭 */
        delay_ms(500);
        HAL_GPIO_WritePin(GPIOF,GPIO_PIN_9,GPIO_PIN_SET);       /* LED0 灭 */
        HAL_GPIO_WritePin(GPIOF,GPIO_PIN_10,GPIO_PIN_RESET);    /* LED1 亮 */
        delay_ms(500);
    }
}

下载验证

在这里插入图片描述
Code:表示程序所占用 FLASH 的大小(FLASH)
RO-data:即 Read Only-data,表示程序定义的常量(FLASH)
RW-data:即 Read Write-data,表示已被初始化的变量(FLASH + RAM)
ZI-data:即 Zero Init-data,表示未被初始化的变量(RAM)

通过按键控制LED亮灭

按键在闭合和断开的时候,都存在抖动现象,即按键在闭合时不会马上就稳定的连接,断开时也不会马上断开。这是机械触点,无法避免。独立按键抖动波形图如下:
在这里插入图片描述
软件消抖:通过延时跳过抖动的时间段,再判断IO输入电平。
硬件消抖:利用 RC 电路的电容充放电特性来对抖动产生的电压毛刺进行平滑出来,从而实现消抖,但是成本会更高一点,本着能省则省的原则,我们推荐使用软件消抖即可。

在该实验中最重要的是按键的去抖操作,其余配置和上面的实验类似。

  • 原理图
    在这里插入图片描述
    按键扫描函数
/**
* @brief 按键扫描函数
* @note 该函数有响应优先级(同时按下多个按键): WKUP > KEY2 > KEY1 > KEY0!!
* @param mode:0 / 1, 具体含义如下:
* @arg 0, 不支持连续按(当按键按下不放时, 只有第一次调用会返回键值,
* 必须松开以后, 再次按下才会返回其他键值)
* @arg 1, 支持连续按(当按键按下不放时, 每次调用该函数都会返回键值)
* @retval 键值, 定义如下:
* KEY0_PRES, 1, KEY0 按下
* KEY1_PRES, 2, KEY1 按下
* KEY2_PRES, 3, KEY2 按下
* WKUP_PRES, 4, WKUP 按下
*/
uint8_t key_scan(uint8_t mode)
{
 static uint8_t key_up = 1; /* 按键按松开标志 */
 uint8_t keyval = 0;
 if (mode) key_up = 1; /* 支持连按 */
 if (key_up && (KEY0 == 0 || KEY1 == 0 || KEY2 == 0 || WK_UP == 1))
 { /* 按键松开标志为 1, 且有任意一个按键按下了 */
 delay_ms(10); /* 去抖动 */
 key_up = 0;
 if (KEY0 == 0) keyval = KEY0_PRES;
 if (KEY1 == 0) keyval = KEY1_PRES;
 if (KEY2 == 0) keyval = KEY2_PRES;
 if (WK_UP == 1) keyval = WKUP_PRES;
 }
 else if (KEY0 == 1 && KEY1 == 1 && KEY2 == 1 && WK_UP == 0)
 { /* 没有任何按键按下, 标记按键松开 */
 key_up = 1;
  }
 return keyval; /* 返回键值 */
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32 第12讲 GPIO:结构/8种工作模式/寄存器/驱动模型/配置步骤/实验 的相关文章

  • 前端 | 数据可视化之ECharts

    文章目录 一 数据可视化1 1 什么是数据可视化1 2 数据可视化的使用场景1 3 常见可视化库1 4 小结 二 ECharts简介2 1 什么是ECharts 三 ECharts的快速入门3 1 ECharts使用五部曲3 2 选择不同类
  • Oracle数据库修改账户密码

    Oracle数据库用户密码忘记了怎么办 xff1f 1 首先需要进入cmd命令格式 xff1b 2 输入sqlplus as sysdba 超级用户角色 xff1b 3 SQL命令下输入alter user 用户名 account unlo
  • 如何让进程后台运行?(TX)

    一 运行指令 43 amp xff08 如 a out amp xff09 这样是将命令放入到一个作业队列中了 表现 xff1a 1 结果会输出到终端 2 前台出现进程号 3 使用Ctrl 43 C发送SIGINT信号 xff0c 程序免疫
  • Ubuntu20.04.2+ROS noetic打开rviz报错:...symbol lookup error...librviz.so: undefined symbol:

    打开rviz闪退 xff0c shell显示如下 xff1a 一开始我的独立显卡是安装好了的 xff0c 界面显示的OpenGL也是独显的 xff0c 但是用的其他博客的方法 xff1a span class token function
  • 计算机网络谢希仁第七版第四章习题

    4 09 xff1a xff08 1 xff09 子网掩码为 255 255 255 0 代表什么意思 xff1f xff08 2 xff09 一个网络的现在掩码为 255 255 255 248 xff0c 问该网络能够连接多少个主机 x
  • Ubuntu Linux操作系统——图形界面与命令行

    文章目录 Linux和Ubuntu命令行界面使用仿真终端窗口Shell基础正则表达式通配符模式表达式 Shell中的特殊字符 Linux命令行的使用命令行语法格式命令行基本用法命令行输入与输出执行Shell脚本vi编辑器vi操作模式打开vi
  • SDN控制器Ryu、Floodlight、OpenDayLight的安装以及Mininet连接

    文章中文件名内的xxx需要替换成自己文件的具体版本 ubuntu下安装之前可以先用 sudo apt cache madison soft name查看一下apt安装的版本 xff0c 如果版本合适的话用apt更加方便 Ryu控制器 Ryu
  • 调试时出现:undefined Expecting 'EOF','}',',',']', got STRING以下错误的解决方法

    网上查了很多跟此问题相关的答案 xff0c 都没彻底解决 xff0c 今天亲自遇到这个问题和解决方法了 xff0c 特写下来 问题描述 xff1a 代码是这样的 xff1a VM523 1 undefined Expecting EOF g
  • tensorflow详细安装过程

    我电脑安装的python是3 7 4的 xff0c 所以python如果版本不一样的话 xff08 不是3 7的 xff09 xff0c 下边的内容不建议完全参考 xff0c 可以适当参考 主要是注意很多numpy和models与你安装的t
  • FOC——无刷电机的简单驱动

    文章目录 一 什么是无刷电机 xff1f 1 长什么样 xff1f 2 怎么工作 xff1f 二 试着让它转起来1 STM32CubeMX配置2 keil Clion代码编写3 结果分析 参考的资料 写这个是为了记录学习过程 xff0c 为
  • C++ 链表(list)使用简述

    目录 1 有关函数的作用 2 测试用例 C 43 43 STL 库的 list 容器是一个双向链表 包含在头文件 lt list gt 中 1 有关函数的作用 list 本身 xff1a list lt type gt li 定义一个参数类
  • KEIL5打开KEIL4工程的方法

    解决的问题 xff1a 当使用KEIL5打开KEIL4工程的时候会提示让你下载支持包 xff0c 可以参考以下流程安装你的KEI5版本对应的支持包 步骤 xff1a 一 打开KEIL5 xff0c 点击左上角的HELP About uVis
  • Ubuntu shell脚本自动输入密码

    Ubuntu脚本实现自动输入密码 执行shell脚本的时候若遇到权限问题 xff0c 会需要手动输入密码 xff0c 自动化脚本就变得加个引号了 解决方法 描述太麻烦 xff0c 举例说明 xff1a 想要获取权限删除文件 密码为00000
  • H3C华三链路聚合的原理及配置

    1 链路聚合的作用 xff1a 将多条物理链路捆绑在一起形成一条以太网逻辑链路 xff0c 实现增加链路带宽 的目的 xff0c 同时这些捆绑在一起的链路通过相互动态备份 xff0c 可以有效地提高链路的可靠性 2 聚合模式 xff1a 静
  • 【算法】电机-几种直流无刷电机控制优化算法

    除了电机控制里经常使用的经典PID控制方法 xff0c 目前还存在几种常用的优化控制算法在这里给大家普及一下 xff0c 明白它们的大概原理及相互之间的区别 目录 xff1a 1 经典PID控制 2 模糊控制 3 滑膜变结构控制 1 经典P
  • 记一次 jenkins 构建失败 “Cannot find module ‘core-js/modules/es.promise.finally‘”

    目录 前言排查过程解决方案总结 前言 这是一次前端项目构建失败的惨案 xff0c 项目已经部署很久了 xff0c 一直相安无事 因为开发更新了代码 xff0c 在构建的时候报错 xff1a main js Cannot find modul
  • VSCode主题颜色的更改,让字体变暗一些,不那么刺眼(类IDEA风)

    VSCode默认主题就是Dark 43 直接打开settings json文件更改 1 工作区界面的颜色更改 xff0c 主要是背景色2 代码颜色3 总的代码 1 工作区界面的颜色更改 xff0c 主要是背景色 在这一部分 xff0c 我主
  • Error committing transaction. Cause: org.apache.ibatis.executor.ExecutorException: Cannot commit, t

    一 出现这个问题是因为在你的事务提交的时候 关闭sqlSession会话已经执行了 导致会话无法提交 解决方法就是先提交事务 再关闭流操作
  • 考研复试——C、C++

    文章目录 C语言 C 43 43 1 C和C 43 43 的区别 xff1f 2 封装 继承 多态分别是什么意思 xff1f 3 new delete malloc free的关系 xff1f 4 什么是引用 xff0c 引用和指针的区别
  • 运行ROS程序与CMakeList文件

    一 图概念概述 Nodes 节点 一个节点即为一个可执行文件 xff0c 它可以通过ROS与其它节点进行通信 Messages 消息 xff0c 消息是一种ROS数据类型 xff0c 用于订阅或发布到一个话题 Topics 话题 节点可以发

随机推荐

  • OpenvSLAM编译与安装

    OpenvSLAM编译与安装 1 安装依赖2 安装Eigen3 安装Opencv 参考另一篇安装opencv3 4 9 4 安装自定义DBoW25 安装g2o6 安装PangolinViewer7 编译openvSLAM8 检查是否编译成功
  • torch.triu 与 numpy.triu 函数

    triu 61 tri angle u p xff08 我猜的 xff09 顾名思义 xff0c 这个函数的作用相同 xff0c 都是返回上三角矩阵 xff0c 定义分别如下 xff1a numpy triu m k torch triu
  • OpenVSLAM-全局优化模块(global optimization module)

    开源SLAM框架学习 OpenVSLAM源码解析 xff1a 全局优化模块 xff08 global optimization module xff09 xff1a 回环检测 pse graph优化 global BA优化 这篇博客主要介绍
  • Ubuntu20.04系统安装与基本配置

    Ubuntu20 04安装与配置 一 ubuntu20 04 5 系统重装 xff08 联想y7000p xff09 步骤一 xff1a 在 WIN10系统下创建空白磁盘分区步骤二 xff1a 镜像文件写入U盘步骤三 xff1a U 盘安装
  • 【向日葵】连接linux版向日葵出现瞬间断开的情况

    向日葵 连接linux版向日葵出现瞬间断开的情况 问题描述 xff1a 连接到Linux时就会在连接完成的瞬间出现连接已断开 xff0c 我的Linux发行版是Ubuntu18 04 解决 xff1a 这个问题出现的原因是向日葵不支持Ubu
  • 51单片机数码管显示数字及小数点

    51单片机数码管显示 共阴极 1 先看一下显示的结果 源代码 span class token macro property span class token directive hash span span class token dir
  • 数据结构之循环队列基本操作(c语言)

    队列 xff1a 队列是一种先进先出 First In First Out 的线性表 它只允许在表的一端进行插入 xff0c 在另一端删除元素 允许插入的一端成为队尾 xff0c 允许删除的一端成为队头 循环队列的顺序表示和实现 xff1a
  • 数据结构——先序遍历的顺序创建二叉链表并中序遍历(C语言)

    先序遍历的顺序创建二叉链表并中序遍历 1 算法步骤 xff1a 1 xff09 扫描数字序列 xff0c 读入数字n 2 xff09 如果n是一个 0 数字 xff0c 则表明该二叉树为空树 xff0c 即T 61 NULL 否则执行一下操
  • 51单片机的系统扩展之8255A

    8255 xff1a 8255芯片是Intel公司生产的可编程并行I O接口芯片 xff0c 有3个8位并行I O口 具有3个通道3种工作方式的可编程并行接口芯片 xff08 40引脚 xff09 其各口功能可由软件选择 xff0c 使用灵
  • ESP8266一直闪蓝灯,不停复位的解决办法

    问题 xff1a 在一次下载中无意间将下载的文件选错 xff0c 再次下载完成后就突然一直闪蓝灯 xff0c 不停复位 这并不是ESP8266模组坏了 解决办法 xff1a 1 我们平常下载程序选择eagle flash bin和eagle
  • Markdown快速入门

    Markdown快速入门 1 代码块 96 96 96 c include lt iostream gt using namespace std int main cout lt lt 34 hello world 34 lt lt end
  • VSCode 如何让去掉 Pylint 展示的花里胡哨的警告

    Pylint 是一个 python 的语法检测器 xff0c 提升编程效率的同时其带来的花里胡哨的警告也是真让人看着难受 xff0c 就像下面这花花绿绿的波浪线 xff1a 这些警告种类极其丰富 xff0c 比如下面这样 xff1a Met
  • 01_HC-SR04超声波传感器(GPIO中断+定时器方式)

    1 简介 xff1a HC SR04 超声波测距模块可提供 2cm 400cm 的非接触式距离感测功能 xff0c 测 距精度可达高 3mm xff1b 模块包括超声波发射器 接收器与控制电路 2 工作原理 xff1a 1 采用 IO 口
  • Faster R-CNN论文解读

    文章目录 AbstractIntroduction缘由RPN训练方案 Faster R CNN整体流程Conv layersRPNclsreganchorTranslation Invariant AnchorsMuti Scale Anc
  • c语言输入字符时控制符%c前加空格的原因解释

    文章目录 一 前景知识1 缓冲区2 标准输入流 二 scanf语句的执行1 scanf对于整形 d的输入2 scanf对于字符 c的输入 在编一个代码时偶然间发现一个知识盲点 用scanf语句输入字符时需要在控制符 c前加空格 在解释相关这
  • 解决c++中头文件重复包含的问题

    前言 c 43 43 项目中经常会使用到自己定义的一些函数和接口 xff0c 我们通常在头文件中包含进来 xff0c 但这样存在头文件被多次包含的危险 xff0c 导致编译报错 xff0c 以下介绍了几种常用的解决方法 一 采用宏定义的方法
  • 华为交换机5700 SSH配置

    一 在本地设备服务端生成密匙 Huawei rsa local span class token operator span span class token keyword key span span class token operat
  • 函数模板、类模板

    泛型编程 泛型编程 xff1a 编写与类型无关的通用代码 xff0c 是代码复用的一种手段 模板是泛型编程的基础 函数模板 函数模板代表了一个函数家族 xff0c 该函数模板与类型无关 xff0c 在使用时被参数化 xff0c 根据实参类型
  • STM32 第4讲 STM32原理图

    本文为学习正点原子得笔记 xff0c 主要讲解STM32原理图绘制 xff0c 主要由最小系统 43 IO口分布两步完成 引脚分布 STM引脚分类 xff1a 电源引脚晶振引脚复位引脚下载引脚 xff1a JTAG SWD 串口 JTAG
  • STM32 第12讲 GPIO:结构/8种工作模式/寄存器/驱动模型/配置步骤/实验

    文章目录 GPIO简介GPIO特点电气特性GPIO引脚分布 GPIO8种工作模式GPIO的基本结构8种工作模式 GPIO寄存器GPIO端口模式寄存器 xff08 GPIOx MODER xff09 GPIO端口输出类型寄存器 xff08 G