0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

STM32速成笔记(3)—按键检测

冬至子 来源:二土电子 作者:二土电子 2023-10-23 17:31 次阅读

一、按键检测原理

按键检测原理比较简单,按键按下和不按下,其连接引脚的电平是不一样的,按键检测正是通过检测按键引脚的电平变化来实现的。比如按键未按下时引脚电平为高电平,按键按下后为低电平。我们在检测按键时只需要检测按键引脚是否变为低电平来确定按键是否按下。

二、硬件连接

按键的硬件连接决定了我们在配置按键IO时IO的状态。以我们使用的普中核心板为例,上面有三个按键

图片

普中核心板按键硬件电路图

其中K1一端接VCC,另一端接单片机。K2和K3一端接地,另一端接单片机。硬件电路不同,导致他们在进行按键检测时IO的配置不同。

针对K1这种按键电路,按键按下时,单片机的引脚接到VCC,因此在未按下的情况下该引脚的默认电平为低电平,也就是要把IO设置为输入下拉模式。同理,对于K2和K3这种连接方式,对应IO应该配置为输入上拉模式,使得按键未被按下时,引脚处于高电平状态。

三、程序设计

按键检测主要有以下步骤

  • • 初始化GPIO
  • • 检测按下按键
  • • 消抖(防误触,一般通过延时实现)
  • • 松手检测
  • • 执行按键功能

3.1 初始化GPIO

根据原理图,谱中的STM32核心板提供了三个按键,我们使用K1和K2来实现点亮和关闭LED的操作。K1对应的IO为PA0,K2对应的IO为PE4。

图片

按键对应GPIO

根据上一节了解的初始化GPIO程序,初始化按键GPIO。

/*
 *==============================================================================
 *函数名称:Drv_KeyGpio_Init 
 *函数功能:初始化KEY的GPIO
 *输入参数:无
 *返回值:无
 *备  注:根据硬件电路确定GPIO模式
 *==============================================================================
 */
void Drv_KeyGpio_Init (void)
{
    GPIO_InitTypeDef GPIO_InitStructure;   // 定义结构体
    // 开启时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOE,ENABLE);

    // 配置结构体 WK UP
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;   // 输入下拉
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    // 配置结构体 KEY0,KEY1
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;   // 输入上拉
    GPIO_Init(GPIOE, &GPIO_InitStructure);
}

3.2 按键扫描函数

按键扫描函数的功能是检测是否有按键按下,按下的按键是哪一个。检测方法上面已经叙述,通过检测按键引脚的电平。以WK UP按键为例。当WK UP被按下时,其对应的引脚PA0会变为高电平。

此时检测PA0的输入电平,如果确实是低电平,则说明WK UP可能被按下。说可能是因为PA0为低电平不一定是WK UP按下造成,也可能是抖动,所以这里就需要消抖操作。这里的消抖操作比较简单粗暴,直接延时10ms看该引脚是否依旧是低电平。如果延时10ms后依旧是高电平,则认为确实是由按键按下导致的电平变化,而不是机械抖动。

确定检测到按键按下后,需要等待按键被松开在执行按键功能。为什么需要进行松手检测?举个例子,比如设置阈值时,按键按下阈值加1,如果不进行松手检测,那么按下一次按键会加很多次,因为在不停地执行按键功能。

这里按键的松手检测也比较简单粗暴,用一个while死循环等待松手。比如WK UP被按下后,其引脚会一直保持高电平,也就是PAin(0)一直等于1,此时用一个while (PAin(1));来等待松手,做松手检测。

四、按键控制LED

这里做一个小练习,用普中核心板上的按键KEY0和KEY1来控制LED1的亮灭。步骤如下

  • • 初始化LED和KEY的GPIO
  • • 编写LED控制函数
  • • 编写按键检测函数(检测按键)
  • • 编写按键服务函数(实现按键功能)

4.1 初始化LED和KEY的GPIO

/*
 *==============================================================================
 *函数名称:Drv_LedGpio_Init
 *函数功能:初始化LED的GPIO
 *输入参数:无
 *返回值:无
 *备  注:无
 *==============================================================================
 */
void Drv_LedGpio_Init (void)
{
    GPIO_InitTypeDef GPIO_InitStructure;   // 定义结构体
    // 开启时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOE,ENABLE);

    // 配置结构体 LED0
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   // 推挽式输出
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    GPIO_SetBits(GPIOB,GPIO_Pin_5);   // 熄灭LED
    
    // 配置结构体 LED1
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   // 推挽式输出
    GPIO_Init(GPIOE, &GPIO_InitStructure);
    GPIO_SetBits(GPIOE,GPIO_Pin_5);   // 熄灭LED
}
/*
 *==============================================================================
 *函数名称:Drv_KeyGpio_Init 
 *函数功能:初始化KEY的GPIO
 *输入参数:无
 *返回值:无
 *备  注:根据硬件电路确定GPIO模式
 *==============================================================================
 */
void Drv_KeyGpio_Init (void)
{
    GPIO_InitTypeDef GPIO_InitStructure;   // 定义结构体
    // 开启时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOE,ENABLE);

    // 配置结构体 WK UP
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;   // 输入下拉
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    // 配置结构体 KEY0,KEY1
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;   // 输入上拉
    GPIO_Init(GPIOE, &GPIO_InitStructure);
}

4.2 编写按键扫描函数

```c
/*
 *==============================================================================
 *函数名称:Med_KeyScan
 *函数功能:检测按下按键
 *输入参数:无
 *返回值:按键键值 0:按键WK UP,1:KEY0,2:KEY1
 *备  注:无
 *==============================================================================
 */
u8 Med_KeyScan (void)
{
    // 按键WK UP
    if (KEY_UP == 1)
    {
        delay_ms (10);   // 延时10ms消抖
        if (KEY_UP == 1)
        {
            while (KEY_UP);   // 松手检测
            return 1;
        }
    }
    
    // 按键KEY0
    else if (KEY0 == 0)
    {
        delay_ms (10);   // 延时10ms消抖
        if (KEY0 == 0)
        {
            while (!KEY0);   // 松手检测
            return 2;
        }
    }
    
    // 按键KEY1
    else if (KEY1 == 0)
    {
        delay_ms (10);   // 延时10ms消抖
        if (KEY1 == 0)
        {
            while (!KEY1);   // 松手检测
            return 3;
        }
    }
    
    // 没有按键按下
    return 0xff;   // 用0xff表示没有按键按下
}

4.2 编写LED控制函数

/*
 *==============================================================================
 *函数名称:Med_Led_StateCtrl
 *函数功能:控制LED亮灭
 *输入参数:
           LEDx:可选择的LED(0~1)
                     State:LED亮灭状态(LED_ON,LED_OFF)
 *返回值:无
 *备  注:无
 *==============================================================================
 */
void Med_Led_StateCtrl (LED_TypeDef LEDx,u8 State)
{
    switch (LEDx)
    {
        case 0:
            PBout(5) = State;
        break;
        
        case 1:
            PEout(5) = State;
        break;
        
        default:
            break;
    }
}

下面是.h文件中的一些结构体和宏定义。

// 可选择的LED
typedef enum
{
    LED1 = 0,
    LED2
}LED_TypeDef;

// 亮灭电平需要根据硬件电路确定
#define LED_ON   0
#define LED_OFF  1

4.3 编写按键服务函数

这里没有再单独编写按键服务函数,直接在main函数中编写。KETY0按下点亮LED1,KEY1按下,熄灭LED1。

u8 gKeyValue = 0;   // 记录按键键值变量

int main(void)
{
    Med_Mcu_Iint();   // 系统初始化
    
    while(1)
  {
        gKeyValue = Med_KeyScan();   // 获取按键键值
        
        // 按键KEY0按下
        if (gKeyValue == 2)
        {
            Med_Led_StateCtrl(LED1,LED_ON);   // 点亮LED1
        }
        
        // 按键KEY1按下
        if (gKeyValue == 3)
        {
            Med_Led_StateCtrl(LED1,LED_OFF);   // 熄灭LED1
        }
    }
}

至此,实现了利用KEY0和KEY1控制LED的亮灭状态。

五、拓展

5.1 一个按键单独控制一个LED亮灭

单片机的IO资源是比较珍贵的,在实际用用时很少会用两个IO资源来控制一个外设的开关,这里介绍一下方法并给出例程。比如使用普中核心板上的WK UP按键来控制LED2的亮灭状态。基本思路是在上面学会按键检测的基础上,增加一个按键按下计次变量。按键按下一次,该变量加1。如果检测到变量为1,那么点亮LED,如果检测到变量为2,那么熄灭LED,同时将计数变量清零。程序设计如下

u8 gKeyValue = 0;   // 记录按键键值变量
u8 gKeyWkUpCunt = 0;   // WK UP按下次数计数变量

int main(void)
{
    Med_Mcu_Iint();   // 系统初始化
    
    while(1)
  {
        gKeyValue = Med_KeyScan();   // 获取按键键值
        
        // 按键WK UP按下
        if (gKeyValue == 1)
        {
            gKeyWkUpCunt = gKeyWkUpCunt + 1;   // 按键按下次数计数变量加1
            
            // 第一次被按下
            if (gKeyWkUpCunt <= 1)
            {
                Med_Led_StateCtrl(LED2,LED_ON);   // 点亮LED2
            }
            // 不是第一次被按下
            else if (gKeyWkUpCunt > 1)
            {
                gKeyWkUpCunt = 0;   // 清空计数变量
                Med_Led_StateCtrl(LED2,LED_OFF);   // 熄灭LED2
            }
        }
    }
}

5.2 按键长短按

除了上面介绍的一些常规操作外,有时还会用到一个按键分长按和短按。这里给出一种简单粗暴的实现思路。需要检测按键长短按时,修改一下松手检测的逻辑。延时10ms后如果按键IO依旧保持按下状态,那么确定不是机械抖动,此时在之前的松手检测while中进行粗略地计时。定义一个计数变量,每隔10ms加1。直到按键被松开。根据计数变量的值来判断按键被按下的时间,从而来区别长短按。

这里给出一个例程,KEY1短按功能为熄灭LED1,长按功能为LED1和LED2交替闪烁两轮后熄灭。按下持续时间在1s内,认为是短按,按下超过2s认为是长按。短按返回3,长按返回4。程序设计如下

u8 gKeyValue = 0;   // 记录按键键值变量
u16 gKey1TimeCunt = 0;   // 按键KEY1的计时变量
/*
 *==============================================================================
 *函数名称:Med_KeyScan
 *函数功能:检测按下按键
 *输入参数:无
 *返回值:按键键值 0:按键WK UP,1:KEY0,2:KEY1
 *备  注:无
 *==============================================================================
 */
u8 Med_KeyScan (void)
{
    // 按键WK UP
    if (KEY_UP == 1)
    {
        delay_ms (10);   // 延时10ms消抖
        if (KEY_UP == 1)
        {
            while (KEY_UP);   // 松手检测
            return 1;
        }
    }
    
    // 按键KEY0
    else if (KEY0 == 0)
    {
        delay_ms (10);   // 延时10ms消抖
        if (KEY0 == 0)
        {
            while (!KEY0);   // 松手检测
            return 2;
        }
    }
    
    // 按键KEY1
    // 按下1s内认为是短按,返回3
    // 按下超过2s认为是长按,返回4
    else if (KEY1 == 0)
    {
        delay_ms (10);   // 延时10ms消抖
        if (KEY1 == 0)
        {
            // 等待松手
            while (!KEY1)
            {
                delay_ms (10);
                gKey1TimeCunt = gKey1TimeCunt + 1;   // 计时变量加1
            }
        }
        
        // 判断长短按
        if (gKey1TimeCunt <= 99)   // 小于等于1s
        {
            gKey1TimeCunt = 0;   // 清零计时变量
            return 3;   // 短按
        }
        else if (gKey1TimeCunt >= 199)   // 大于1s,等于2s
        {
            gKey1TimeCunt = 0;   // 清零计时变量
            return 4;   // 长按
        }
    }
    
    // 没有按键按下
    return 0xff;   // 用0xff表示没有按键按下
}

main函数中添加下述程序

// 长按KEY1
        if (gKeyValue == 4)
        {
            Med_Led_StateCtrl(LED1,LED_ON);   // 点亮LED1
            delay_ms (500);
            Med_Led_StateCtrl(LED1,LED_OFF);   // 熄灭LED1
            Med_Led_StateCtrl(LED2,LED_ON);   // 点亮LED2
            delay_ms (500);
            Med_Led_StateCtrl(LED2,LED_OFF);   // 熄灭LED2
            Med_Led_StateCtrl(LED1,LED_ON);   // 点亮LED1
            delay_ms (500);
            Med_Led_StateCtrl(LED1,LED_OFF);   // 熄灭LED1
            Med_Led_StateCtrl(LED2,LED_ON);   // 点亮LED2
            delay_ms (500);
            Med_Led_StateCtrl(LED1,LED_OFF);   // 熄灭LED1
            Med_Led_StateCtrl(LED2,LED_OFF);   // 熄灭LED1
        }
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • STM32
    +关注

    关注

    2264

    文章

    10852

    浏览量

    354100
  • GPIO
    +关注

    关注

    16

    文章

    1186

    浏览量

    51795
  • LED控制器
    +关注

    关注

    0

    文章

    63

    浏览量

    20607
  • 按键电路
    +关注

    关注

    1

    文章

    35

    浏览量

    21739
  • 按键控制
    +关注

    关注

    1

    文章

    44

    浏览量

    8761
收藏 人收藏

    评论

    相关推荐

    STM32速成

    哟管STM32速成贴?求助
    发表于 01-23 14:34

    如何使用GPIO进行按键检测

    STM32F429I-Discovery学习笔记–(3)使用GPIO进行按键检测写在前面由于我们使用的是官方的开发板,所以在用
    发表于 02-16 06:06

    STM32按键控制小车代码

    好用的stm32小车代码,STM32按键控制小车代码,STM32按键控制小车代码,STM32
    发表于 06-08 16:28 49次下载

    STM32各模块学习笔记

    中断......................................................3 STM32笔记之三 时钟系统
    发表于 11-30 03:32 3124次阅读

    STM32单片机的按键检测程序设计

    STM32按键检测相对比较简单,首先按部就班的初始化连接的到的i/o,然后写一个按键扫描函数,这个和51单片机的差不多。
    发表于 10-13 16:28 1.2w次阅读
    <b class='flag-5'>STM32</b>单片机的<b class='flag-5'>按键</b><b class='flag-5'>检测</b>程序设计

    STM32 独立按键扫描功能大全-支持连击、组合连击、任意连击

    STM32 独立按键扫描功能大全-支持连击、组合连击、任意连击本人刚学习STM32开发,最近看了硬汉的按键检测程序,进行了架构的深度优化,所
    发表于 12-04 20:36 58次下载
    <b class='flag-5'>STM32</b> 独立<b class='flag-5'>按键</b>扫描功能大全-支持连击、组合连击、任意连击

    STM32G4系列MCU学习笔记按键模块

    第一次以写博客的方式记录自己在嵌入式学习过程中的学习经历和踩的那些坑~Day1 那么开始叭!STM32G4系列MCU学习笔记按键模块前言一、硬件操作1. 原理图2. 硬件分析3. 初
    发表于 12-05 20:51 10次下载
    <b class='flag-5'>STM32</b>G4系列MCU学习<b class='flag-5'>笔记</b>:<b class='flag-5'>按键</b>模块

    STM32F429I-Discovery学习笔记--(3)使用GPIO进行按键检测

    STM32F429I-Discovery学习笔记–(3)使用GPIO进行按键检测写在前面由于我们使用的是官方的开发板,所以在用
    发表于 12-20 18:59 8次下载
    <b class='flag-5'>STM32</b>F429I-Discovery学习<b class='flag-5'>笔记</b>--(<b class='flag-5'>3</b>)使用GPIO进行<b class='flag-5'>按键</b><b class='flag-5'>检测</b>

    STM32笔记按键KEY输入

    关于STM32学习分享第二章 按键输入控制文章目录前言二、代码1.key.c2.key.h3.main.c总结前言开始!开始!单片机的按键配置为输入模式获取控制信号。# 一、
    发表于 12-31 19:49 7次下载
    <b class='flag-5'>STM32</b><b class='flag-5'>笔记</b>之<b class='flag-5'>按键</b>KEY输入

    STM32按键实验学习笔记

    读取按键输入引脚的信号,然后识别高低电平来判断是否有按键触发。为什么去抖动?按键的输入引脚有低电平产生不代表一定是有按键按下,也许是干扰信号 , 因此,需要通过去抖动处理,将这些干扰信
    发表于 01-18 08:26 1次下载
    <b class='flag-5'>STM32</b><b class='flag-5'>按键</b>实验学习<b class='flag-5'>笔记</b>

    STM32学习笔记按键实验

    个人学习笔记按键实验一.所使用的函数1.时钟使能函数RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState
    发表于 01-18 08:52 1次下载
    <b class='flag-5'>STM32</b>学习<b class='flag-5'>笔记</b>:<b class='flag-5'>按键</b>实验

    AN5597_STM32WB3或5xxx和STM32WB1xxx应用笔记

    AN5597_STM32WB3或5xxx和STM32WB1xxx应用笔记
    发表于 11-21 08:11 0次下载
    AN5597_<b class='flag-5'>STM32WB3</b>或5xxx和<b class='flag-5'>STM32</b>WB1xxx应用<b class='flag-5'>笔记</b>

    STM32G0开发笔记:GPIO接按键的使用方式

    使用Platformio平台的libopencm3开发框架来开发STM32G0,下面为GPIO接按键的使用方式。
    的头像 发表于 01-17 10:48 1681次阅读

    【应用笔记】触摸按键设计要点

    【应用笔记】触摸按键设计要点
    的头像 发表于 10-19 17:58 1949次阅读
    【应用<b class='flag-5'>笔记</b>】触摸<b class='flag-5'>按键</b>设计要点

    STM32怎么实现按键开关机

    STM32按键开关机需要以下步骤: 1. 硬件连接。 2. 配置GPIO引脚。 3. 编程实现按键检测。 4. 编程实现开关机控制功能。 详
    的头像 发表于 12-07 15:17 2687次阅读