STM32-GPIO学习-跑马灯实验和按键实验-寄存器版本和HAL库版本

一、stm32跑马灯实验

a.GPIO

  • general purpose input output

  • 通用输入输出端口,可以做输入也可做输出,GPIO端口可通过程序配置成输入或输出。

  • STM32FXXXIGT6一共9组IO:PA~ PI,其中PA~ PH每组16个IO,PI只有PI0~PI11。16*8+12=140,一共140个IO口。

  • STM32大部分引脚除了当GPIO用,还可复用为外设功能引脚,比如串口。节省引脚资源。

b.GPIO的8种工作模式

  • 4种输入模式:输入浮空、输入上拉、输入下拉、模拟输入
  • 4种输出模式带上下拉:开漏输出(带上拉或下拉)、开漏复用功能、推挽式输出、推挽式复用
  • 推挽输出可以输出强高低电平,连接数字器件
  • 开漏输出只可以输出强低电平,高电平得靠外部电阻拉高。输出端相当于三级管的集电极

c.GPIO寄存器

4个32位配置寄存器

GPIOx_MODER 模式

GPIOx_OTYPER 输出类型

GPIOx_OSPEEDR 输出速度

GPIOx_PUPDR 上拉下拉

2个32位数据寄存器

GPIOx_IDR 输入数据

GPIOx_ODR 输出数据

1个32位置位/复位寄存器

GPIOx_BSRR 置位/复位

1个32位锁存寄存器

GPIOx_LCKR 配置锁存

2个32位复用功能寄存器

GPIOx_AFRL&GPIOx_AFRH 复用功能

  • 每组IO口由10个寄存器组成
  • 如果配置一个IO口需要2个位,32位寄存器配置一组IO口,16个IO口
  • 如果配置一个IO口只需要1个位,高16位保留

1.寄存器定义

F767:stm32f767xx.h

文件中查找GPIO得到:

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;

再查找GPIO_TypeDef找到:

#define GPIOA               ((GPIO_TypeDef *) GPIOA_BASE)
#define GPIOB               ((GPIO_TypeDef *) GPIOB_BASE)
#define GPIOC               ((GPIO_TypeDef *) GPIOC_BASE)
#define GPIOD               ((GPIO_TypeDef *) GPIOD_BASE)
#define GPIOE               ((GPIO_TypeDef *) GPIOE_BASE)
#define GPIOF               ((GPIO_TypeDef *) GPIOF_BASE)
#define GPIOG               ((GPIO_TypeDef *) GPIOG_BASE)
#define GPIOH               ((GPIO_TypeDef *) GPIOH_BASE)
#define GPIOI               ((GPIO_TypeDef *) GPIOI_BASE)
#define GPIOJ               ((GPIO_TypeDef *) GPIOJ_BASE)
#define GPIOK               ((GPIO_TypeDef *) GPIOK_BASE)

可以看出GPIOA是一个结构体指针,它指向基地址。

GPIOA->ODR即可访问GPIOA端口的ODR输出寄存器。

2.跑马灯硬件连接

在这里插入图片描述

可以看到,两个LED灯,一端连到LED0、LED1,另一端通过上拉电阻连到VCC。如果PB0输出0,那么LED1导通。

GPIO输出方式:采用可以输出高低电平的 推挽输出(上拉)

3.配置寄存器操作IO口步骤

  1. 初始化HAL库:HAL_Init();

  2. 初始化系统时钟:

    stm32F767:Stm32_Clock_Init(431,25,2,9);

  3. 使能IO口时钟。配置IO口时钟使能寄存器:RCC->AHB1ENR

    RCC AHB1 外设时钟寄存器 (RCC_AHB1ENR)

    在这里插入图片描述

    在这里插入图片描述

  4. 初始化IO口模式。配置四个配置寄存器

    GPIOx_MODER、GPIOx_OTYPER、GPIOx_OSPEEDR、GPIOx_PUPDR

  5. 操作IO口,输出高低电平

    配置寄存器GPIOX_ODR或者GPIOx_BSRR

4.手写跑马灯

在HALLIB里面的stm32f7xx_hal.c文件中可以找到HAL_Init函数,复制过来粘到main()文件中,完成了初始化HAL库。

在SYSTEM里面的sys.c文件中找到Stm32_Clock_Init函数,复制过来粘到main()文件中,完成了初始化系统时钟。

在HALLIB里面的stm32f7xx_hal.c文件中搜索RCC_TypeDef,可以看到:

typedef struct
{__IO uint32_t CR;            /*!< RCC clock control register,                                  Address offset: 0x00 */__IO uint32_t PLLCFGR;       /*!< RCC PLL configuration register,                              Address offset: 0x04 */__IO uint32_t CFGR;          /*!< RCC clock configuration register,                            Address offset: 0x08 */__IO uint32_t CIR;           /*!< RCC clock interrupt register,                                Address offset: 0x0C */__IO uint32_t AHB1RSTR;      /*!< RCC AHB1 peripheral reset register,                          Address offset: 0x10 */__IO uint32_t AHB2RSTR;      /*!< RCC AHB2 peripheral reset register,                          Address offset: 0x14 */__IO uint32_t AHB3RSTR;      /*!< RCC AHB3 peripheral reset register,                          Address offset: 0x18 */uint32_t      RESERVED0;     /*!< Reserved, 0x1C                                                                    */__IO uint32_t APB1RSTR;      /*!< RCC APB1 peripheral reset register,                          Address offset: 0x20 */__IO uint32_t APB2RSTR;      /*!< RCC APB2 peripheral reset register,                          Address offset: 0x24 */uint32_t      RESERVED1[2];  /*!< Reserved, 0x28-0x2C                                                               */__IO uint32_t AHB1ENR;       /*!< RCC AHB1 peripheral clock register,                          Address offset: 0x30 */__IO uint32_t AHB2ENR;       /*!< RCC AHB2 peripheral clock register,                          Address offset: 0x34 */__IO uint32_t AHB3ENR;       /*!< RCC AHB3 peripheral clock register,                          Address offset: 0x38 */uint32_t      RESERVED2;     /*!< Reserved, 0x3C                                                                    */__IO uint32_t APB1ENR;       /*!< RCC APB1 peripheral clock enable register,                   Address offset: 0x40 */__IO uint32_t APB2ENR;       /*!< RCC APB2 peripheral clock enable register,                   Address offset: 0x44 */uint32_t      RESERVED3[2];  /*!< Reserved, 0x48-0x4C                                                               */__IO uint32_t AHB1LPENR;     /*!< RCC AHB1 peripheral clock enable in low power mode register, Address offset: 0x50 */__IO uint32_t AHB2LPENR;     /*!< RCC AHB2 peripheral clock enable in low power mode register, Address offset: 0x54 */__IO uint32_t AHB3LPENR;     /*!< RCC AHB3 peripheral clock enable in low power mode register, Address offset: 0x58 */uint32_t      RESERVED4;     /*!< Reserved, 0x5C                                                                    */__IO uint32_t APB1LPENR;     /*!< RCC APB1 peripheral clock enable in low power mode register, Address offset: 0x60 */__IO uint32_t APB2LPENR;     /*!< RCC APB2 peripheral clock enable in low power mode register, Address offset: 0x64 */uint32_t      RESERVED5[2];  /*!< Reserved, 0x68-0x6C                                                               */__IO uint32_t BDCR;          /*!< RCC Backup domain control register,                          Address offset: 0x70 */__IO uint32_t CSR;           /*!< RCC clock control & status register,                         Address offset: 0x74 */uint32_t      RESERVED6[2];  /*!< Reserved, 0x78-0x7C                                                               */__IO uint32_t SSCGR;         /*!< RCC spread spectrum clock generation register,               Address offset: 0x80 */__IO uint32_t PLLI2SCFGR;    /*!< RCC PLLI2S configuration register,                           Address offset: 0x84 */__IO uint32_t PLLSAICFGR;    /*!< RCC PLLSAI configuration register,                           Address offset: 0x88 */__IO uint32_t DCKCFGR1;      /*!< RCC Dedicated Clocks configuration register1,                 Address offset: 0x8C */__IO uint32_t DCKCFGR2;      /*!< RCC Dedicated Clocks configuration register 2,               Address offset: 0x90 */} RCC_TypeDef;

可以找到AHB1ENR。由寄存器可知只需要改变位1。在main中写RCC->AHB1ENR |=1<<1(1左移一位,然后或上RCC->AHB1ENR可以把位1置为1),或者写成RCC->AHB1ENR=0x02。

  • 配置GPIO端口模式寄存器GPIOx_MODER

在这里插入图片描述

在这里插入图片描述

因为需要配置的端口位是0和1,所以需要配置MODER1和MODER0.因为是推挽输出,所以设置最后四个位0101,那么也就是5,最终MODER寄存器设置成0x05。在main函数中写上GPIOB->MODER=0x05。

  • 接着配置GPIO 端口输出类型寄存器 (GPIOx_OTYPER)

在这里插入图片描述

由于是推挽输出,所以端口0和1都是0。在main函数中写上GPIOB->OTYPER=0x00;

  • 接着配置GPIO 端口输出速度寄存器 (GPIOx_OSPEEDR)

在这里插入图片描述

设置为高速的话后面两个端口的两个位也都是11。这样的话设置成GPIOB->OSPEEDR=0x0f;虽然只写了8位,但是他其实一共有32位,前面都是零,可以省略不写,但是要规范的话是需要把0全写上补满32位。

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

在这里插入图片描述

这里要配置为上拉,所以后面两个端口都是0101,也就是要设置成GPIOB->PUPDR=0x05;

然后操作IO口,输出高低电平,配置寄存器GPIOX_ODR或者GPIOx_BSRR。

在这里插入图片描述

要配置端口1和0输出高电平,也就是需要BS1和BS0为1,其它位保持不变为0,也就是说要配置成0x03 。如果配置端口1和0输出低电平,也就是需要BR1和BR0为1进行复位,其它位保持不变为0,也就是说要配置成0x00030000 。写成代码也就是GPIOB->BSRR=0x00000003;//1,GPIOB->BSRR=0x00030000;//0。

然后由于开和关间隔时间过短,所以还需要一个delay函数。在SYSTEM-delay.c文件中可以找到delay.h,然后进入delay.h头文件,可看到void delay_init(u8 SYSCLK);void delay_ms(u16 nms);void delay_us(u32 nus);三个函数。使用delay首先进行初始化delay_init(216);其中216是stm32f767的系统时钟。然后就可以调用delay_ms(500);

最终的代码为:

#include "sys.h"
#include "delay.h"
#include "usart.h"int main(void)
{HAL_Init();Stm32_Clock_Init(431,25,2,9);delay_init(216);RCC->AHB1ENR |= 1<<1;GPIOB->MODER=0x05;GPIOB->OTYPER=0x00;GPIOB->OSPEEDR=0x0f;GPIOB->PUPDR=0x05;while(1){GPIOB->BSRR=0x00000003;//1delay_ms(500);GPIOB->BSRR=0x00030000;//0delay_ms(500);}
}

5.使用HAL库

优点:方便在各个stm32平台移植。

HAL库很好的解决了程序移植的问题,不同型号的stm32芯片它的标准库是不一样的,例如在F4上开发的程序移植到F3上是不能通用的,而使用HAL库,只要使用的是相通的外设,程序基本可以完全复制粘贴,注意是相同外设。

在HALLIB文件夹下可以找到stm32f7xx_hal_gpio.c文件,然后找到stm32f7xx_hal_gpio.h文件,可以看到里面有几个函数:

/* Initialization and de-initialization functions *****************************/
void  HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init);//初始化函数
void  HAL_GPIO_DeInit(GPIO_TypeDef  *GPIOx, uint32_t GPIO_Pin);
/**
/* IO operation functions *****************************************************/
GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);//读取输入电平函数
void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState);//设置输出电平函数
void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);//电平翻转函数
HAL_StatusTypeDef HAL_GPIO_LockPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);//引脚电平锁定函数
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin);//外部中断函数
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin);
  • void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init);

    初始化一个或多个IO口(同一组)的工作模式,输出类型,速度以及上下拉方式。也就是一组IO口的4个配置寄存器。MODER、OSPEEDR、OTYPER、PUPDR。在stm32f7xx_hal_gpio.h找到HAL_GPIO_Init函数,然后go to definition,可以找到HAL_GPIO_Init的实现,可以发现他主要操作的是MODER、OSPEEDR、OTYPER、PUPDR寄存器。

        GPIO_Initure.Pin=GPIO_PIN_0|GPIO_PIN_1; //PB1,0GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP;  //推挽输出GPIO_Initure.Pull=GPIO_PULLUP;          //上拉GPIO_Initure.Speed=GPIO_SPEED_HIGH;     //高速HAL_GPIO_Init(GPIOB,&GPIO_Initure);     //一个参数是设置哪个组,另一个参数是初始化结构体
    
  • GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

    读取某个GPIO的输入电平,实际操作是GPIOx_IDR寄存器

  • void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState);

    设置引脚输出电平

    HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_SET);	//PB0置1
    HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_SET);	//PB1置1
    
  • void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

    输出电平翻转函数

6.配置HAL库操作IO口步骤

  • 初始化HAL库 HAL_Init()

  • 初始化系统时钟 Stm32_Clock_Init()

  • 使能IO口时钟 _HAL_RCC_GPIOB_CLK_ENABLE() ;配置IO口时钟使能寄存器:RCC->AHB1ENR

    在HALLIB文件-stm32f7xx_hal_rcc.ex.h文件中可以找到:__HAL_RCC_GPIOA_CLK_ENABLE(),这个函数实际上操作的是RCC->AHB1ENR

    #define __HAL_RCC_GPIOA_CLK_ENABLE()   do { \__IO uint32_t tmpreg; \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(0)
    
  • 初始化IO口模式HAL_GPIO_Init() ;操作寄存器:GPIOx_MODER OTYPER OSPEEDR PUPDR

    在stm32f7xx_hal_gpio.c文件找到HAL_GPIO_Init(),然后观察它的参数GPIO_TypeDef *GPIOx和GPIO_InitTypeDef *GPIO_Init,现在要弄明白GPIOx是什么,在下面的代码中找到对GPIOx的操作。 assert_param(IS_GPIO_ALL_INSTANCE(GPIOx));然后选择IS_GPIO_ALL_INSTANCE点击go to definition找到:

    #define IS_GPIO_ALL_INSTANCE(__INSTANCE__) (((__INSTANCE__) == GPIOA) || \((__INSTANCE__) == GPIOB) || \((__INSTANCE__) == GPIOC) || \((__INSTANCE__) == GPIOD) || \((__INSTANCE__) == GPIOE) || \((__INSTANCE__) == GPIOF) || \((__INSTANCE__) == GPIOG) || \((__INSTANCE__) == GPIOH) || \((__INSTANCE__) == GPIOI) || \((__INSTANCE__) == GPIOJ) || \((__INSTANCE__) == GPIOK))
    

    可以发现该函数是对GPIOA之类的进行操作,要对GPIOB进行操作,所以GPIO_TypeDef *GPIOx参数设置成GPIOB。

    下面要设置GPIO_InitTypeDef *GPIO_Init参数,可以发现这个是结构体类型。

    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;
    

    然后对这个结构体成员进行设置,首先要判断MODE要怎么设置,找到

    assert_param(IS_GPIO_PIN(GPIO_Init->Pin));
    assert_param(IS_GPIO_MODE(GPIO_Init->Mode));
    assert_param(IS_GPIO_PULL(GPIO_Init->Pull));

    go to definition一下IS_GPIO_MODE,可以发现mode可以设置成很多个类型,我们这里设置成推挽输出,所以设置成GPIO_MODE_OUTPUT_PP。

    #define IS_GPIO_PIN_ACTION(ACTION) (((ACTION) == GPIO_PIN_RESET) || ((ACTION) == GPIO_PIN_SET))
    #define IS_GPIO_PIN(PIN)           (((PIN) & GPIO_PIN_MASK ) != (uint32_t)0x00)
    #define IS_GPIO_MODE(MODE) (((MODE) == GPIO_MODE_INPUT)              ||\((MODE) == GPIO_MODE_OUTPUT_PP)          ||\((MODE) == GPIO_MODE_OUTPUT_OD)          ||\((MODE) == GPIO_MODE_AF_PP)              ||\((MODE) == GPIO_MODE_AF_OD)              ||\((MODE) == GPIO_MODE_IT_RISING)          ||\((MODE) == GPIO_MODE_IT_FALLING)         ||\((MODE) == GPIO_MODE_IT_RISING_FALLING)  ||\((MODE) == GPIO_MODE_EVT_RISING)         ||\((MODE) == GPIO_MODE_EVT_FALLING)        ||\((MODE) == GPIO_MODE_EVT_RISING_FALLING) ||\((MODE) == GPIO_MODE_ANALOG))
    #define IS_GPIO_SPEED(SPEED) (((SPEED) == GPIO_SPEED_LOW)  || ((SPEED) == GPIO_SPEED_MEDIUM) || \((SPEED) == GPIO_SPEED_FAST) || ((SPEED) == GPIO_SPEED_HIGH))
    #define IS_GPIO_PULL(PULL) (((PULL) == GPIO_NOPULL) || ((PULL) == GPIO_PULLUP) || \((PULL) == GPIO_PULLDOWN))
    

    然后找到pin的设置,发现里面有个GPIO_PIN_MASK,选中GPIO_PIN_MASK点击go to definition。可以发现下面这些代码,所以这个可以设置成GPIO_PIN_0。

    #define GPIO_PIN_0                 ((uint16_t)0x0001U)  /* Pin 0 selected    */
    #define GPIO_PIN_1                 ((uint16_t)0x0002U)  /* Pin 1 selected    */
    #define GPIO_PIN_2                 ((uint16_t)0x0004U)  /* Pin 2 selected    */
    #define GPIO_PIN_3                 ((uint16_t)0x0008U)  /* Pin 3 selected    */
    #define GPIO_PIN_4                 ((uint16_t)0x0010U)  /* Pin 4 selected    */
    #define GPIO_PIN_5                 ((uint16_t)0x0020U)  /* Pin 5 selected    */
    #define GPIO_PIN_6                 ((uint16_t)0x0040U)  /* Pin 6 selected    */
    #define GPIO_PIN_7                 ((uint16_t)0x0080U)  /* Pin 7 selected    */
    #define GPIO_PIN_8                 ((uint16_t)0x0100U)  /* Pin 8 selected    */
    #define GPIO_PIN_9                 ((uint16_t)0x0200U)  /* Pin 9 selected    */
    #define GPIO_PIN_10                ((uint16_t)0x0400U)  /* Pin 10 selected   */
    #define GPIO_PIN_11                ((uint16_t)0x0800U)  /* Pin 11 selected   */
    #define GPIO_PIN_12                ((uint16_t)0x1000U)  /* Pin 12 selected   */
    #define GPIO_PIN_13                ((uint16_t)0x2000U)  /* Pin 13 selected   */
    #define GPIO_PIN_14                ((uint16_t)0x4000U)  /* Pin 14 selected   */
    #define GPIO_PIN_15                ((uint16_t)0x8000U)  /* Pin 15 selected   */
    #define GPIO_PIN_All               ((uint16_t)0xFFFFU)  /* All pins selected */#define GPIO_PIN_MASK              ((uint32_t)0x0000FFFFU) /* PIN mask for assert test */
    

    同理设置pull和speed

  • 操作IO口,输出高低电平 HAL_GPIO_WritePin(); 配置寄存器GPIOX_ODR或者GPIOx_BSRR

    最后在main中写:

int main()
{GPIO_InitTypeDef GPIO_InitStructure;HAL_Init();delay_init(216);Stm32_Clock_Init(431,25,2,9);__HAL_RCC_GPIOB_CLK_ENABLE();//使能PB时钟GPIO_InitStructure.Mode=GPIO_MODE_OUTPUT_PP;//推挽输出GPIO_InitStructure.Pin=GPIO_PIN_0 | GPIO_PIN_1;GPIO_InitStructure.Pull=GPIO_PULLUP;//上拉GPIO_InitStructure.Speed=GPIO_SPEED_FREQ_VERY_HIGH;//高速HAL_GPIO_Init(GPIOB,&GPIO_InitStructure);while(1){HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_RESET);//PB0=0HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET);//PB1=0delay_ms(500);HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_RESET);//PB0=1HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET);//PB1=1delay_ms(500);}}

二、stm32按键实验

1.电路连接图

在这里插入图片描述

由图可知,KEY0、KEY1、KEY2需要设置成上拉,这样的话,如果按键KEY0、KEY1、KEY2按下,那么将得到低电平信号,如果KEY0、KEY1、KEY2没有按下,将是高电平信号。WK_UP设置为下拉,KEY_UP按下的话,将检测到高电平信号,没有按下将是低电平信号。KEY0->PH3上拉输入、KEY1->PH2上拉输入、KEY2->PC13上拉输入、WK_UP->PA0下拉输入。

2.步骤

1.使能按键对应IO口时钟

__HAL_RCC_GPIOx_CLK_ENABLE();

由电路连接图,可以发现,涉及到的是A、C、H口:

	__HAL_RCC_GPIOA_CLK_ENABLE();           //开启GPIOA时钟__HAL_RCC_GPIOC_CLK_ENABLE();           //开启GPIOC时钟__HAL_RCC_GPIOH_CLK_ENABLE();           //开启GPIOH时钟

2.初始化IO模式:上拉/下拉输入

HAL_GPIO_Init

根据KEY0->PH3上拉输入、KEY1->PH2上拉输入、KEY2->PC13上拉输入、WK_UP->PA0下拉输入。

 	GPIO_InitTypeDef GPIO_Initure; 	GPIO_Initure.Pin=GPIO_PIN_0;            //PA0GPIO_Initure.Mode=GPIO_MODE_INPUT;      //输入GPIO_Initure.Pull=GPIO_PULLDOWN;        //下拉GPIO_Initure.Speed=GPIO_SPEED_HIGH;     //高速HAL_GPIO_Init(GPIOA,&GPIO_Initure);GPIO_Initure.Pin=GPIO_PIN_13;           //PC13GPIO_Initure.Mode=GPIO_MODE_INPUT;      //输入GPIO_Initure.Pull=GPIO_PULLUP;          //上拉GPIO_Initure.Speed=GPIO_SPEED_HIGH;     //高速HAL_GPIO_Init(GPIOC,&GPIO_Initure);GPIO_Initure.Pin=GPIO_PIN_2|GPIO_PIN_3; //PH2,3HAL_GPIO_Init(GPIOH,&GPIO_Initure);

3.扫描IO口电平(库函数/寄存器)

HAL库函数:GPIO_PinState HAL_GPIO_ReadPin();

寄存器:GPIOx_IDR

4.编写按键扫描逻辑

按键支持连按,举个遥控器的例子,一直按,一直换台。如果不支持连按,就说明这个遥控器按下一次换台,如果后面也一直按,它不会换台。如果不支持连按的话,需要一个变量记录上一次的状态,如果是上一次是未按下,那么这一次检测到按下,说明这个按键算是按下了。如果检测上一次按下了,那么这次按不算,也就是说,按键按下了,没有松开,只能算一次。

按键扫描支持连按的思路:

u8 KEY_Scan(void){if(KEY按下){delay_ms(10);//延时防抖if(KEY确实按下){return KEY_Value;}return 无效值;}
}

每次调用getValue函数之后,返回值是多少

int getValue()
{static int flag = 0;flag++;return flag;
}

每一次调用,第一次返回1,第二次返回2…因为static变量存在,static修饰的变量只被初始化一次,并且保持最近的值,哪怕创建它的函数已经结束,这个变量也不会被释放,下次调用是同一个地址,所以里面的值是上次的。static定义的变量有记忆的作用。所以不支持连按的思路:注意key_up一定记录的是上一次的状态。

u8 KEY_SCAN(void)
{static u8 key_up = 1;//没有按下if(key_up && KEY按下)//上一次松开,这次按下{delay_ms(10);key_up=0;if(KEY确实按下){return KEY_VALUE;}}else if(KEY没有按下)key_up=1;
}

3.代码

//按键初始化函数
void KEY_Init(void)
{GPIO_InitTypeDef GPIO_Initure;__HAL_RCC_GPIOA_CLK_ENABLE();           //开启GPIOA时钟__HAL_RCC_GPIOC_CLK_ENABLE();           //开启GPIOC时钟__HAL_RCC_GPIOH_CLK_ENABLE();           //开启GPIOH时钟GPIO_Initure.Pin=GPIO_PIN_0;            //PA0GPIO_Initure.Mode=GPIO_MODE_INPUT;      //输入GPIO_Initure.Pull=GPIO_PULLDOWN;        //下拉GPIO_Initure.Speed=GPIO_SPEED_HIGH;     //高速HAL_GPIO_Init(GPIOA,&GPIO_Initure);GPIO_Initure.Pin=GPIO_PIN_13;           //PC13GPIO_Initure.Mode=GPIO_MODE_INPUT;      //输入GPIO_Initure.Pull=GPIO_PULLUP;          //上拉GPIO_Initure.Speed=GPIO_SPEED_HIGH;     //高速HAL_GPIO_Init(GPIOC,&GPIO_Initure);GPIO_Initure.Pin=GPIO_PIN_2|GPIO_PIN_3; //PH2,3HAL_GPIO_Init(GPIOH,&GPIO_Initure);
}//按键处理函数
//返回按键值
//mode:0,不支持连续按;1,支持连续按;
//0,没有任何按键按下
//1,WKUP按下 WK_UP
//注意此函数有响应优先级,KEY0>KEY1>KEY2>WK_UP!!
u8 KEY_Scan(u8 mode)
{static u8 key_up=1;     //按键松开标志if(mode==1)key_up=1;    //支持连按if(key_up&&(KEY0==0||KEY1==0||KEY2==0||WK_UP==1)){delay_ms(10);key_up=0;if(KEY0==0)       return KEY0_PRES;else if(KEY1==0)  return KEY1_PRES;else if(KEY2==0)  return KEY2_PRES;else if(WK_UP==1) return WKUP_PRES;          }else if(KEY0==1&&KEY1==1&&KEY2==1&&WK_UP==0)key_up=1;return 0;   //无按键按下
}int main(void)
{u8 key;u8 led0sta=1,led1sta=1;		    //LED0,LED1的当前状态Cache_Enable();                 //打开L1-CacheHAL_Init();				        //初始化HAL库Stm32_Clock_Init(432,25,2,9);   //设置时钟,216Mhz delay_init(216);                //延时初始化uart_init(115200);		        //串口初始化LED_Init();                     //初始化LEDKEY_Init();                     //按键初始化while(1){key=KEY_Scan(0); 		    //得到键值if(key){						   switch(key){				 case WKUP_PRES:	//控制LED0,LED1互斥点亮led1sta=!led1sta;led0sta=!led1sta;break;case KEY2_PRES:	//控制LED0翻转led0sta=!led0sta;break;case KEY1_PRES:	//控制LED1翻转	 led1sta=!led1sta;break;case KEY0_PRES:	//同时控制LED0,LED1翻转 led0sta=!led0sta;led1sta=!led1sta;break;}LED0(led0sta);		//控制LED0状态LED1(led1sta);		//控制LED1状态}else delay_ms(10);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/560289.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

STM32-时钟-时钟树-时钟初始化配置

1.STM32时钟 STM32有5个时钟源&#xff1a;HSI、HSE、LSI、LSE、PLL HSI是高速内部时钟&#xff0c;RC振荡器&#xff0c;频率为16MHz&#xff0c;精度不高。可以直接作为系统时钟或者用作PLL时钟输入。 HSE是高速外部时钟&#xff0c;可接石英/陶瓷谐振器&#xff0c;或者接…

pe系统 服务器维护,FirPE 维护系统

前言年更项目更新啦~~~&#xff0c;这次修复了一些BUG&#xff0c;此外还增加了亿点特性。首次使用WinRE注册表&#xff0c;使得WinPE速度更快了。系统简介FirPE 是一款系统预安装环境(Windows PE)&#xff0c;它具有简约、易操作等特点&#xff0c;使用起来十分人性化。以U盘作…

STM32-Systick滴答定时器-延时函数

1.STM32-Systick滴答定时器 Systick定时器&#xff0c;是一个简单的定时器&#xff0c;对于ST的CM3,CM4,CM7内核芯片&#xff0c;都有Systick定时器。 Systick定时器常用来做延时&#xff0c;或者实时系统的心跳时钟。这样可以节省MCU资源&#xff0c;不用浪费一个定时器。比如…

华为服务器更改从系统盘启动不了,华为服务器设置启动项

华为服务器设置启动项 内容精选换一换对于已安装Cloud-Init/Cloudbase-Init的云服务器&#xff0c;首次执行切换/故障切换操作&#xff0c;或者创建容灾演练后&#xff0c;系统第一次启动时会运行Cloud-Init/Cloudbase-Init&#xff0c;为云服务器重新注入初始化数据&#xff0…

STM32-IO引脚复用-原理和使用

1.什么是端口复用&#xff1f; STM32有很多的内置外设(把一些功能ADC\看门狗…集中到芯片里面)&#xff0c;这些外设的外部引脚都是与GPIO复用的。也就是说&#xff0c;一个GPIO如果可以复用为内置外设的功能引脚&#xff0c;那么当这个GPIO作为内置外设使用的时候&#xff0c…

泰拉瑞亚服务器config修改,泰拉瑞亚配置怎么修改_泰拉瑞亚配置修改方法介绍...

泰拉瑞亚配置怎么修改呢&#xff0c;还有很多玩家还不知道的&#xff0c;下面小编为玩家们带来了泰拉瑞亚配置修改方法介绍&#xff0c;一起来看看吧。泰拉瑞亚配置修改方法介绍Config文件(1)Wiki链接http://terraria.gamepedia.com/Config.json_settings(2)关于Configconfig文…

STM32-UART-串口通信框图-波特率计算

1.处理器与外部设备通信的两种方式&#xff1a; 并行通信 传输原理&#xff1a;数据各个位同时传输。优点&#xff1a;速度快缺点&#xff1a;占用引脚资源多 串行通信 传输原理&#xff1a;数据按位顺序传输。优点&#xff1a;占用引脚资源少缺点&#xff1a;速度相对较慢…

STM32-串口发送数据-过程与配置

串口发送过程配置流程 HAL库中串口寄存器定义文件&#xff1a; stm32f429xx.h F429芯片 stm32f767xx.h F767芯片 stm32f103xx.h F103芯片 stm32fnnnx.x.h 其他芯片 可以在其中找到USART_TypeDef&#xff1a;最终会映射到寄存器的地址。 typedef struct {__IO uint32_t CR1;…

STM32 串口接收流程-串口接收中断

串口接收 串口接收流程 编程USARTx_CR1的M位来定义字长。编程USARTx_CR2的STOP位来定义停止位位数。编程USARTx_BRR寄存器确定波特率。使能USARTx_CR1的UE位使能USARTx。如果进行多缓冲通信&#xff0c;配置USARTx_CR3的DMA使能&#xff08;DMAT)。使能USARTx_CR1的RE位为1使…

STM32-外部中断配置-外部中断实验

STM32-外部中断原理与配置 IO口外部中断原理概述 STM32控制器支持的外部中断/事件请求 中断线M3M4M7EXTI线0~15:对应外部IO口的输入中断。√√√EXTI线16&#xff1a;连接到PVD输出。√√√EXTI线17&#xff1a;连接到RTC闹钟事件。√√√EXTI线18&#xff1a;连接到USB OTG…

STM32-独立看门狗原理-实验

独立看门狗原理概述 为什么要看门狗&#xff1a; 在由单片机构成的微型计算机系统中&#xff0c;由于单片机的工作常常会受到来自外界电磁场的干扰&#xff0c;造成程序的跑飞&#xff0c;而陷入死循环&#xff0c;程序的正常运行被打断&#xff0c;由单片机控制的系统无法继…

STM32-窗口看门狗-工作过程-配置-实验

窗口看门狗 窗口看门狗概述 之所以称为窗口就是因为其喂狗时间是一个有上下限的范围内(窗口&#xff09;&#xff0c;你可以通过设定相关寄存器&#xff0c;设定其上限时间&#xff08;下限固定&#xff09;。喂狗的时间不能过早也不能过晚。 而独立看门狗限制喂狗时间在0-x内…

音频和视频的基础知识

目录PCM-脉冲编码调制录音的原理记录声音-音频数字化音频数字化-PCM采样采样采样率采样定理量化位深度编码声道&#xff08;Channel&#xff09;比特率信噪比编码&#xff08;Encode&#xff09;解码&#xff08;Decode&#xff09;常见的音频编码和文件格式无损Monkeys AudioF…

神经网络算法-论证单层感知器的局限性

神经网络算法-论证单层感知器的局限性 今天课上学习了一个思路 将真值表转换到平面直角坐标系中 来论证线性可分还是不可分&#xff0c;挺有意思记录一下。 简单感知器模型实际上仍然是MP模型的结构&#xff0c;但是它通过采用监督学习来逐步增强模式划分的能力&#xff0c;达…

python 通过pip安装库 pycharm里面使用第三方库

安装所需的模块&#xff08;python&#xff09; pip 是 Python 包管理工具&#xff0c;该工具提供了对Python 包的查找、下载、安装、卸载的功能。 首先命令行输入pip3 --version查看是否有pip C:\Users\jym>pip3 --version pip 19.2.3 from d:\program files\python38\l…

python 实现TCP socket通信和 HTTP服务器、服务器和客户端通信python实例

python 实现TCP socket通信和 HTTP服务器、服务器和客户端通信实例socket是什么&#xff1f;服务器和客户端通信的流程python 实现TCP socket通信例子关于Host和PORT的设置socket函数socket编程思路基于TCP socket的HTTP服务器分析HTTP服务器代码服务器的response文本客户端的r…

判断ROS版本号-ubuntu20.04安装ROS_noetic-修改镜像源

判断ROS版本号-ROS安装-修改镜像源 linux版本:ubuntu20.04 ROS版本:noetic判断ROS版本号安装ROS改镜像源判断ROS版本号 机器人用的是树莓派、stm32、ros。 首先要配置网络&#xff0c;把机器人上的sd卡插到树莓派开发板上&#xff0c;然后配置连接的热点。 接下来&#xff0…

ROS基本概念 文件系统 创建ROS软件包 ROS中的一些命令

ROS基本概念 文件系统 创建ROS软件包 ROS中的一些命令ROS是什么ROS文件系统文件系统工具&#xff1a;rospack、roscd、rosls创建ROS 软件包catkin是什么创建和构建一个catkin工作空间&#xff1a;catkin软件包的结构catkin工作空间中存放软件包的结构创建catkin软件包-catkin_c…

ubuntu 中 ROS 一些报错的解决

1. Unable to load type jymubuntu:~/catkin_ws$ rosservice args /add_two_ints ERROR: Unable to load type [beginner_tutorials/AddTwoInts]. Have you typed make in [beginner_tutorials]?由于当前使用的终端的工作环境没有更新导致无法找到自定义的消息。使用下面的命…

ROS 创建msg和srv 编写发布者和订阅者节点 编写服务端和客户端节点(python版本)

ROS 创建msg和srv 编写发布者和订阅者节点 编写服务端和客户端节点-python版本rosedmsg和srv创建msg使用rosmsg创建srv使用rossrv重新make一下软件包编写发布者节点发布者节点代码解析编写订阅者节点订阅者节点代码解析构建节点运行发布者和订阅者节点编写服务节点编写客户端节…