首页 > 其他分享 >【RTT-Studio】详细使用教程六:按键检测实现

【RTT-Studio】详细使用教程六:按键检测实现

时间:2024-07-29 16:58:46浏览次数:9  
标签:count 教程 button RTT state Studio 按键 pButton RCC

文章目录

一、简介

本文主要介绍在RT-Thread Studio中,如何对按键进行检测,分为两种方式进行检测:

  • 状态机方式:可以检测按键的单击、双击和长按,并且可以设置三个状态的检测时间长短,主要分为五个状态:未按、按下结束、按下抖动、松开抖动和长按。

  • 常用的方式:通过检测按键的按下状态,并且还有长按的标志,可以长按,也可以不启用长按。


二、RTT时钟配置

由于使用RTT生成的工程默认使用的是系统内部时钟,便于我们对时间的控制,所以通常会使用外部时钟,因此需要对工程中的时钟进行更改,更改内容如下:

  • 打开RT-Thread Studio软件新建基于芯片的项目,并使用外部时钟系统。
  • 在drv_clk.c文件中添加时钟配置函数,并且注释内部时钟的调用。
/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

    /** Configure the main internal regulator output voltage
     */
    __HAL_RCC_PWR_CLK_ENABLE();
    __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

    /** Initializes the CPU, AHB and APB busses clocks
     */
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    RCC_OscInitStruct.PLL.PLLM = 4;
    RCC_OscInitStruct.PLL.PLLN = 168;
    RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
    RCC_OscInitStruct.PLL.PLLQ = 4;
    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
    {
        Error_Handler();
    }

    /** Initializes the CPU, AHB and APB busses clocks
     */
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
            | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
    {
        Error_Handler();
    }
}

void clk_init(char *clk_source, int source_freq, int target_freq)
{
//    system_clock_config(target_freq);
    SystemClock_Config();
}

三、初始化配置

1.常用的方式

  • 实现的功能:主要是检测按键是否按下,以及是否使用长按的功能
  • 使用延时函数:主要是用来去除抖动,一般去抖动的时间为:10-20ms
  • 长按功能:如果需要使用长按,则是将mode参数设置为1即可,如果设置为0则是不支持长按
/**
 * @brief 选择和启停按键处理函数
 * @param mode: 0,不支持连续按;1,支持连续按;
 * @return 返回按键值
 */
uint8_t KEY_MODE_and_ON_Scan(uint8_t mode)
{
    static uint8_t key_up = 1; //按键按松开标志

    if (mode)
    {
        key_up = 1;  //支持连按
    }

    if (key_up && (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_11) == 0 || HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3) == 0))
    {
        Delay_ms(20);  //去抖动
        key_up = 0;
        if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_11) == 0)
        {
            return 2;
        }
        else if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3) == 0)
        {
            return 1;
        }
    }
    else if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_11) == 1 && HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3) == 1)
    {
        key_up = 1;
    }
    return 0;  // 无按键按下
}

2.状态机方式:
(1)按键数据清零函数

/**
 * @brief 按键数据清零
 * @param pButton 按键结构体
 */
static void clear_State(Laser_Button *pButton)
{
    pButton->first_count = 0;
    pButton->wait_count = 0;
    pButton->singleclick_count = 0;
    pButton->longclick_flag = 0;
    pButton->button_flg = 0;
    pButton->last_state = 0;
    pButton->current_state = 0;
}

(2)按键检测函数:主要分为四个状态,分别为:未按和按下结束、按下抖动期、松开抖动期、长按。

  • 实现的功能:主要是检测按键的单击、双击和长按。并且三个状态的检测事件可以自定义
  • 返回值:主要是返回检测到按键的状态,包括单击、双击、长按和长按结束
/**
 * @brief  按键检测函数
 * @param  pButton 按键结构体
 * @return 1:单击   2:双击   3:长按   4:单击和双击结束   5:长按结束
 */
static rt_uint8_t button_scan(Laser_Button *pButton)
{
    rt_uint16_t button_state;              //按键状态标志位

    // 按键按下
    if (READ_BUTTON_STATE == 1)
    {
        pButton->first_count++;
        pButton->last_state = pButton->current_state;
        pButton->current_state = 1;
    }
    else
    {
        pButton->last_state = pButton->current_state;
        pButton->current_state = 0;
    }

    button_state = (pButton->last_state << 4) + pButton->current_state;

    switch (button_state)
    {
        // 未按、按下结束
        case 0x00:
        {
            // 单击
            if (pButton->singleclick_count == 1)
            {
                pButton->wait_count++;
                pButton->button_flg = 1;

                if (pButton->wait_count > pButton->doubleClick_time)
                {
                    pButton->button_flg = 1;
                    clear_State(pButton);

                    return KEY_SINGLE_CLICK;
                }
            }

            // 双击
            else if (pButton->singleclick_count == 2)
            {
                clear_State(pButton);

                return KEY_DOUBLE_CLICK;
            }

            // 长按结束
            else if (pButton->longclick_flag)
            {
                pButton->longclick_flag = 0;
                clear_State(pButton);
                if (pButton->button_flg == 1)
                {
                    pButton->button_flg = 0;
                    return KEY_STRIKE_CANCEL;
                }

                return KEY_LONG_CLICK_CANCEL;
            }

            break;
        }

        // 处于按下抖动期
        case 0x01:
        {
            pButton->wait_count = 0;
            break;
        }

        // 处于松开抖动期
        case 0x10:
        {
            // 不是长按和其他
            if (pButton->first_count > pButton->singleClick_time && pButton->longclick_flag == 0)
            {
                pButton->singleclick_count++;
            }
            else
            {
                pButton->singleclick_count = 0;
            }

            pButton->first_count = 0;

            break;
        }

        // 处于长按状态
        case 0x11:
        {
            if (pButton->first_count > pButton->longClick_time)
            {
                pButton->longclick_flag = 1;
                pButton->singleclick_count = 0;
                if (pButton->button_flg == 1)
                {
                    return KEY_STRIKE_CANCEL;
                }

                return KEY_LONG_CLICK;
            }

            break;
        }
    }

    return 0;
}

(3)使用方式:直接通过调用该函数既可。如果需要检测不同的按键,只需要将检测函数的宏定义进行更改,或者需要添加多个按键检测,只需增加标志位,代表是哪个按键按下的既可。

(4)检测时间:检测时间通过宏定义对自定义的结构体进行初始化,具体时间如下:

Laser_Button  g_button = {3*20, 15*20, 20*20, 0, 0, 0, 0, 0, 0, 0}; // 初始按键信息, x * 20ms

四、完整代码

1.button.c文件

#include "button.h"
#include "control.h"

Laser_Button  g_button = {3*20, 15*20, 20*20, 0, 0, 0, 0, 0, 0, 0}; // 初始按键信息, x * 20ms

/*======================================================### 静态函数调用 ###==================================================*/
/**
 * @brief 按键数据清零
 * @param pButton 按键结构体
 */
static void clear_State(Laser_Button *pButton)
{
    pButton->first_count = 0;
    pButton->wait_count = 0;
    pButton->singleclick_count = 0;
    pButton->longclick_flag = 0;
    pButton->button_flg = 0;
    pButton->last_state = 0;
    pButton->current_state = 0;
}

/**
 * @brief  按键检测函数
 * @param  pButton 按键结构体
 * @return 1:单击   2:双击   3:长按   4:单击和双击结束   5:长按结束
 */
static rt_uint8_t button_scan(Laser_Button *pButton)
{
    rt_uint16_t button_state;              //按键状态标志位

    // 按键按下
    if (READ_BUTTON_STATE == 1)
    {
        pButton->first_count++;
        pButton->last_state = pButton->current_state;
        pButton->current_state = 1;
    }
    else
    {
        pButton->last_state = pButton->current_state;
        pButton->current_state = 0;
    }

    button_state = (pButton->last_state << 4) + pButton->current_state;

    switch (button_state)
    {
        // 未按、按下结束
        case 0x00:
        {
            // 单击
            if (pButton->singleclick_count == 1)
            {
                pButton->wait_count++;
                pButton->button_flg = 1;

                if (pButton->wait_count > pButton->doubleClick_time)
                {
                    pButton->button_flg = 1;
                    clear_State(pButton);

                    return KEY_SINGLE_CLICK;
                }
            }

            // 双击
            else if (pButton->singleclick_count == 2)
            {
                clear_State(pButton);

                return KEY_DOUBLE_CLICK;
            }

            // 长按结束
            else if (pButton->longclick_flag)
            {
                pButton->longclick_flag = 0;
                clear_State(pButton);
                if (pButton->button_flg == 1)
                {
                    pButton->button_flg = 0;
                    return KEY_STRIKE_CANCEL;
                }

                return KEY_LONG_CLICK_CANCEL;
            }

            break;
        }

        // 处于按下抖动期
        case 0x01:
        {
            pButton->wait_count = 0;
            break;
        }

        // 处于松开抖动期
        case 0x10:
        {
            // 不是长按和其他
            if (pButton->first_count > pButton->singleClick_time && pButton->longclick_flag == 0)
            {
                pButton->singleclick_count++;
            }
            else
            {
                pButton->singleclick_count = 0;
            }

            pButton->first_count = 0;

            break;
        }

        // 处于长按状态
        case 0x11:
        {
            if (pButton->first_count > pButton->longClick_time)
            {
                pButton->longclick_flag = 1;
                pButton->singleclick_count = 0;
                if (pButton->button_flg == 1)
                {
                    return KEY_STRIKE_CANCEL;
                }

                return KEY_LONG_CLICK;
            }

            break;
        }
    }

    return 0;
}

/**
 * @brief 电源按键检测
 * @return 0:未按下   1:按下
 */
rt_uint8_t Power_Key_Scan(void)
{
    if (READ_POWER_KEY_STATE == 0)
    {
        rt_thread_delay(20);  //去抖动
        if (READ_POWER_KEY_STATE == 0)
        {
            return 1;
        }
    }

    return 0;  // 无按键按下
}
/*=====================================================#######  END  #######=================================================*/

2.button.h文件

#include <rtthread.h>
#include <drv_common.h>

/**=====================================================###### 宏定义 ######==================================================*/
#define KEY_SINGLE_CLICK         1               // 单击
#define KEY_DOUBLE_CLICK         2               // 双击
#define KEY_LONG_CLICK           3               // 长按
#define KEY_STRIKE_CANCEL        4               // 单击和双击结束
#define KEY_LONG_CLICK_CANCEL    5               // 长按结束

#define KEY_PRESS_DOEN_TIME      3000            // 按键按下时间

#define READ_BUTTON_STATE       rt_pin_read(SW_LOCK)  // 读取扳机按键状态
#define READ_POWER_KEY_STATE    rt_pin_read(PWR_KEY)  // 读取开机按键状态

typedef struct Laser_Button
{
    rt_uint16_t singleClick_time;                // 单击按钮时间
    rt_uint16_t doubleClick_time;                // 双击时两次单击最大间隔时间
    rt_uint16_t longClick_time;                  // 长按时间

    rt_uint8_t  last_state;                      // 上一次按键的状态
    rt_uint8_t  current_state;                   // 当前按键状态

    rt_uint16_t first_count;                     // 第一次计数
    rt_uint16_t wait_count;                      // 等待计数
    rt_uint8_t singleclick_count;                // 单击计数
    rt_uint8_t longclick_flag;                   // 双击标志
    rt_uint8_t button_flg;                       // 按键标志

} Laser_Button, *PLaser_Button;
/**====================================================#######  END  #######=================================================*/

五、测试验证

通过使用串口助手进行打印验证,观察按键的检测状况,然后通过更改检测的时间,来检测实际的使用效果,通过使用效果来优化检测的时间。测试效果如下:
在这里插入图片描述

标签:count,教程,button,RTT,state,Studio,按键,pButton,RCC
From: https://blog.csdn.net/Hei_se_meng_yan/article/details/140775084

相关文章

  • Hadoop伪分布式/分布式平台搭建教程以及安装过程中遇到部分问题的解决经验
    Hadoop伪分布式/分布式平台搭建教程声明:本搭建教程参考XMU的数据库实验室林子雨老师的搭建文档,附带了一点我们在安装时候遇到的问题和解决经验。XMU安装指导文档网址:https://dblab.xmu.edu.cn/blog/2544/目录文章目录Hadoop伪分布式/分布式平台搭建教程目录1.Linux......
  • java 教程
    Java基础廖雪峰Java教程阿里巴巴Java开发手册 下载pdf[书单]Java从入门到高级书籍推荐 SpringBootspringboot最全,最完整,最适合小白教程(基础篇) SpringCloudSpringCloud最佳实践方案(2021版本) MyBatis官网SpringBoot集成Mybatis保姆级教程MyBatis-Plus快速入门......
  • 记录|Visual Studio如何设置仅Tab键为自动补全
    目录前言一、ctrl+Alt+空格键,补全切换更新时间前言参考文章:【VS2022】如何仅通过Tab提交自动补全的候选内容本来用VS2022的补全功能还是很舒服的,但是后来写到某些变量名和类名只是名字的首字母大小写不同时,特别容易出现优先补全类名。【例如下图】一、ctrl......
  • 怎么在虚拟机跑PCDN项目教程
    Windows启用Hyper-V​Hyper-V是微软推出的免费的虚拟化服务,从Windows8开始,其企业版.专业版.教育版系统都可以免费开启Hyper-V服务。注意:家庭版系统不支持Hyper-V,需要升级为专业版。注意:如果已经开启过Hyper-V,请跳过本步骤。下边我们以Win10为例,开启Hyper-V服务。​方法......
  • Python全套教程,小白零基础入门必备!
    Python是一种语法简单、功能强大的编程语言,它注重的是如何高效解决问题。【教程领取方式在文末!!】正是这种简单实用的特性,让Python成为近年来最热门的编程语言之一。Python具有广泛的应用场景,包括:Web开发、数据分析、机器学习、网络爬虫、人工智能、量化交易等众多领......
  • GLSL教程 第11章:性能优化和调试
    目录11.1GLSL着色器的性能考量11.1.1减少计算复杂度避免不必要的计算使用适当的数据类型优化数学操作11.1.2减少内存访问减少纹理采样次数使用纹理缓存11.1.3优化数据传输减少数据传输量批处理(Batching)11.1.4使用高级渲染技术LevelofDetail(LOD)延迟渲染......
  • 如何在 Visual Studio Code 中降级终端中的 python?
    我有一段代码需要运行,但它仅与tensorflow1.3、1.4和1.5兼容。我想我需要降级python才能运行它,但是终端有python3.10我尝试创建虚拟环境失败,但终端仍然说它正在使用python3.10,所以我仍然无法运行代码有什么我可以做的吗?是对的,创建虚拟环境是运行不同Python版本代......
  • 如何使用Visual Studio代码调试django
    我是django开发领域的新人,使用Xcode和相关IDE进行桌面/移动应用程序开发。我必须使用Django,我想知道是否有一个高效的使用VisualStudioCode(或Atom)进行调试的方法。与DjangoIDE相关的任何帮助也会有所帮助。使用VisualStu......
  • 软件著作权申请教程(超详细)(2024新版)软著申请
      目录一、注册账号与实名登记二、材料准备三、申请步骤1.办理身份2.软件申请信息3.软件开发信息4.软件功能与特点5.填报完成一、注册账号与实名登记    首先我们需要在官网里面注册一个账号,并且完成实名认证,一般是注册【个人】的身份。中国版权保护中心......
  • 【全过程】windows GPU训练大模型的前期准备教程
    CUDA下载及安装下载显卡驱动点这里进入之后点画圈的地方,然后打开下载的文件,会帮你自动下载和安装适配你显卡的驱动程序(这里不是特别重要,就简单带过)点击桌面左下角小箭头,出现花圈的标志,也就是英伟达的logo,说明驱动安装成功安装CUDA接下来到了重头戏,cuda的安装查看本机......