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

发布时间:2024-03-05  

一、按键检测原理

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

二、硬件连接

按键的硬件连接决定了我们在配置按键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。程序设计如下

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

相关文章

    示波器测不到波形真的是坏了吗;示波器测不到信号可以从以下几点判断是不是出故障了 当你使用示波器的时候突然发现示波器测不到波形或者测到的波形和我们预期的不一致,是不是首先怀疑是不是示波器坏了,是不......
    泰克示波器测不到波形需要返厂维修吗?;当你使用示波器的时候突然发现示波器测不到波形或者测到的波形和我们预期的不一致,是不是首先怀疑是不是示波器坏了,是不是探头坏了,先不要急着怀疑,先做......
    低端检流电路比较简单,但有几种故障状态是低端检流电路检测不到的,这会使负载处于危险的情况,利用高端检流电路则可解决这些问题。 高端检流电路直接连到电源端,能够检测到后续回路的任何故障并采取相应的保护措施,特别......
    ,能解析出按键的不同状态,即按下、按住、弹起、为按下这四种状态,用以实现更丰富的功能。 但需注意两点:一是定时器的定时时间,不可过长也不可过短,过长容易检测不到按下,过短会占用大量时间资源。二是......
    )系统打造而成,将在英飞凌的“在线体验传感器”展会上展出。由于采用基于机器学习的算法,该系统能够识别驾驶员看不到,或车辆的ADAS传感器检测不到的应急车辆、常规车辆及其他交通参与者。机器......
    惯了电视机的遥控器,很多人无法接受电视盒子的遥控器了。左一个遥控器控制电视,右一个遥控器控制电视盒子,过于繁杂的设置以及密密麻麻的按键,不仅老年人嫌麻烦不会用,连上手快的年轻人都头疼。 尤其......
    , 110, 200, 16, 16, "KEY0:Read Sector 0", RED); while (sd_init()) /*检测不到SD卡 */ { lcd_show_string(30......
    在输出端接上了一个频率计和一个扬声器,进行频率检测。 //当按下按键时,输出频率可以轮番转换。 //图中把四种频率的检测结果都截图显示,可以看出,输出的频率十分理想。 //PC的扬声器也能听到声音,10kHz的频率,比较......
    过程中发现键盘扫描出了问题,时常有检测不到按键的现象。而回头看看自己写的程序,实现类似的键盘检测却运用了各种不同的方法,还都写在同一个函数中,就算不出错,自己也不想再看。真有种绝望的感觉,后来又将这段代码全部删除了,这是......
    小,那么开路故障就检测不到了。为了解决上述问题和避开相关噪声的影响,我们用电流来传输信号,因为电流对噪声并不敏感。4~20mA的电流环便是用4mA表示零信号,用20mA表示信号的满刻度,而低于4mA......

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>