STM32使用HAL库实现按键的单击、双击、长按

Wesley13
• 阅读 1706

STM32使用HAL库实现按键的单击、双击、长按

目录

  • STM32使用HAL库实现按键的单击、双击、长按
    • 前言
    • 具体思路
    • 工程配置
    • 代码实现
    • 实验效果

前言

编程开发环境:STM32CubeIDE

~~~~~~~~ 按键的单击、双击、长按等在MCU编程中是比较常见且常用的事件,本文章基于STM32来实现,具体思路用在其他MCU也是如此。

具体思路

  • 初始化一个全局标记
  • 按键中断事件发生后置位标记
  • while死循环中一直检测这个标记,如果被置位那么进行消抖,然后再次检测连接KEY的IO是否处于按下状态,如是则认为本次按键有效
  • 第一次按键事件有效后,启动定时器定时300ms,在此定时期间内如果有二次按下那么就是双击,如果没有按下,等到300ms定时时间到后读取IO电平,如果处于松开状态那么本次就是单击事件,如果还是按下状态那么就再次启动700定时器,700ms过后再次读取IO电平是否处于按下状态,如是那么就是长按。

工程配置

我是使用STM32CUBEMX配置生成的工程,配置按键:

STM32使用HAL库实现按键的单击、双击、长按
STM32的定时器有向上、向下计数模式,向上计数就是从0计数到N然后触发中断,向下计数模式就是倒计时到0,由于我们需要二次启动定时器来检测长按,所以再次配置为向下计数模式,分频为7199,计数3000就是定时300ms:
STM32使用HAL库实现按键的单击、双击、长按
中断配置:
STM32使用HAL库实现按键的单击、双击、长按

代码实现

/*
 * key_state位说明:
 *
 * bit[15]:按键事件完成标志位
 * bit[14]:按键中断触发标志位
 * bit[13]:长按事件触发需要启动第二次定时器标志位
 * bit[12~0]: 按键计数,=1:单击  =2:双击  =3:长按
 */
__IO uint16_t key_state = 0;

int __io_putchar(int ch)
{
   
     
    HAL_UART_Transmit(&huart1, (uint8_t*) &ch, 1, 100);
    return ch;
}

void TIM4_Stop(void)
{
   
     
    HAL_TIM_Base_Stop_IT(&htim4);
}

void TIM4_Start(uint16_t timeout)
{
   
     
    __HAL_TIM_SET_COUNTER(&htim4, timeout);
    HAL_TIM_Base_Start_IT(&htim4);
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
   
     
    if(htim->Instance == htim4.Instance)
    {
   
     
        TIM4_Stop();
        if(key_state & 0X2000)    // 如果是700ms的超时
        {
   
     
            // 再次检测按键如果是按下状态,那么就是长按事件,否则事件无效
            if(HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == GPIO_PIN_RESET)
            {
   
     
                key_state = 0X8003;
            }else
            {
   
     
                key_state = 0X8000;
            }
        }
        else  // 如果是300ms的超时
        {
   
     
             // 300ms时间到再次检测按键状态如果仍处于按下状态,那么就启动700ms的超时,
             // 700ms时间到后再次检测按键如果是按下状态,那么就是长按事件了。
            if(HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == GPIO_PIN_RESET)  // 按键处于按下状态
            {
   
     
                key_state |= 0X2000;  // 标记启动了700ms的超时定时
                TIM4_Start(7000);     // 启动700ms超时
            }
            else  // 按键处于松开状态
            {
   
     
                if((key_state & 0X1FFF) == 1)        // 按键按下计数=1,是单击事件
                {
   
     
                    key_state = 0X8001;
                }else if((key_state & 0X1FFF) >= 2)  // 按键按下计数>=2,是双击事件
                {
   
     
                    key_state = 0X8002;
                }else                                // 按键按下计数为其他值,本次按键事件无效
                {
   
     
                    key_state = 0;
                }
            }
        }
    }
}

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
   
     
    switch(GPIO_Pin)
    {
   
     
    case KEY_Pin:
        if((key_state & 0X8000) == 0)
        {
   
     
            key_state |= 0X4000;    // 标记按键中断触发,在while死循环中进行消抖和的启动定时器
        }
        break;
    default:break;
    }
}

int main(void)
{
   
     
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_USART1_UART_Init();
    MX_TIM4_Init();
    __HAL_TIM_CLEAR_IT(&htim4, TIM_IT_UPDATE);    // STM32的定时器初始化完毕之后如果不清理中断标记为会有直接进入中断的问题

    while (1)
    {
   
     
        if(key_state & 0X4000)     // 按键中断触发
        {
   
     
            key_state &= ~0x4000;  // 清除事件
            HAL_Delay(60);         // 消抖延时

            // 消抖完毕之后再次检测按键是否为按下状态,如是则本次按键有效,否则视为无效并清除标识。
            if(HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == GPIO_PIN_RESET)
            {
   
     
                if((key_state & 0X1FFF) == 0)  // 首次按下时启动300ms超时定时器
                {
   
     
                    TIM4_Start(3000);
                }
                key_state++;                    // 按键按下计数增加
            }
        }

        if(key_state & 0X8000)  // 按键事件发成了
        {
   
     
            switch(key_state)
            {
   
     
            case 0X8001:
                printf("single\r\n");
                break;
            case 0X8002:
                printf("double\r\n");
                break;
            case 0X8003:
                printf("hold\r\n");
                break;
            default:break;
            }
            key_state = 0;  // 清除事件
        }
    }
}

实验效果

按键单击、双击、长按实验效果如下:

STM32使用HAL库实现按键的单击、双击、长按

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
2年前
STM32开发,定时器和状态机实现不一样的跑马灯
STM32开发,定时器和状态机实现不一样的跑马灯1概述1.1资源概述1.2代码移植1.3实现功能2软件实现2.1工程修改2.2main函数代码3实验结果1概述1.1资源概述开发板:正点原子STM32F10
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
2年前
STM32触摸按键
本文将使用STM32F207定时器12的捕获通道1实现触摸按键功能,将运用到输入捕获功能,具体请看之前的文章《STM32输入捕获功能(https://www.oschina.net/action/GoToLink?urlhttp%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzIxNTg1NzQwMQ%3D%3D%26
Easter79 Easter79
2年前
stm32 库函数(按键控制led灯闪烁)最简单易懂的使用方法
STM32使用库函数(按键控制led不同的闪烁效果)下面是main.cinclude"stm32f10x.h"include"led.h"include"key.h"intmain(void){while(1){
Wesley13 Wesley13
2年前
UIWebView长按保存图片和识别图片二维码的实现方案(使用缓存)
0x00需求:长按识别UIWebView中的二维码,如下图长按识别二维码0x01方案1:给UIWebView增加一个长按手势,激活长按手势时获取当前UIWebView的截图,分析是否包含二维码。核心代码:略优点:流程简单,可以快速实现。不足:无法实现保存UIWebView中图片,如果当前We
Wesley13 Wesley13
2年前
STM32使用HAL库DMA+空闲中断实现串口不定长数据接收
STM32使用HAL库DMA空闲中断实现串口不定长数据接收环境:STM32CubeIDESTM32F103RBWIN10HAL库V1.8首先配置串口:!在这里插入图片描述(https://img
Stella981 Stella981
2年前
STM32 实现内部Flash的读写(HAL库版)
  Flash中文名字叫闪存,是一种长寿命的非易失性(断电数据不丢失)的存储器。可以对称为块的存储器单元块进行擦写和再编程,在进行写入操作之前必须先执行擦除。一个NandFlash由多个块(Block)组成,每个块里面又包含很多页(page)。每个页对应一个空闲区域/冗余区域(sparearea),这个区域不是用来存储数据的,用于放置数据的校验值检测和
Stella981 Stella981
2年前
Android So动态加载 优雅实现与原理分析
背景:漫品Android客户端集成适配转换功能(基于目标识别(So库35M)和人脸识别库(5M)),导致apk体积50M左右,为优化客户端体验,决定实现So文件动态加载.!(https://oscimg.oschina.net/oscnet/00d1ff90e4b34869664fef59e3ec3fdd20b.png)点击上方“蓝字”关注我
Easter79 Easter79
2年前
STM32 实现内部Flash的读写(HAL库版)
  Flash中文名字叫闪存,是一种长寿命的非易失性(断电数据不丢失)的存储器。可以对称为块的存储器单元块进行擦写和再编程,在进行写入操作之前必须先执行擦除。一个NandFlash由多个块(Block)组成,每个块里面又包含很多页(page)。每个页对应一个空闲区域/冗余区域(sparearea),这个区域不是用来存储数据的,用于放置数据的校验值检测和