富芮坤FR800X系列之按键检测模块设计

news2024/11/15 12:40:11

FR800X系列按键检测模块


读者对象:
本文档主要适用以下工程师:
嵌入式系统工程师
单片机软件工程师
IOT固件工程师
BLE固件工程师

文章目录

  • 1.概要
  • 2.用户如何设计按键检测模块
    • 2.1 GPIO初始化
    • 2.2按键模块初始化
    • 2.3设计中断函数:
    • 2.4循环检测按键和输出事件
    • 2.5按键任务处理
    • 2.6 事件处理函数
  • 3.按键模块调试细节
  • 4.小结

1.概要

FR800X系列软件开发中基于官方SDK中如何设计按键检测模块。按键检测需满足两个要求分别为消除抖动干扰和快速响应。要想消除抖动干扰,就需要做滤波处理,比如间隔10ms持续检测3(2-5)次为低电平为按键按下有效。快速响应就需要中断触发,中断触发后开启定时器循环检测按键。那用户怎样设计按键检测模块呢?
设计按键模块化好处:
灵活性:模块化的按键设计能够提供更多的选择和灵活性。不同的按键模块可以根据需求进行组合和更换,从而满足不同的功能需求。

易于维护:按键模块化可以使维护更加简单。当一个按键出现故障时,只需替换该模块,而无需更换整个设备。这节省了时间和资源,并减少了停机时间。

可扩展性:按键模块化允许在需要时添加更多的按键。这种扩展性使得设备能够适应不断变化的需求和功能。

互换性:按键模块化使不同厂商的按键能够互相兼容。这意味着用户可以更容易地找到合适的按键模块,并使用它们来替换原始设备中的按键。

降低成本:按键模块化可以降低成本,因为只需购买需要的按键模块,而无需购买整个设备。此外,模块化还可以减少对专门技术的需求,从而降低维修和维护的成本。
在这里插入图片描述
按键模块框图
本文主要讲述:①用户如何设置GOIO中断唤醒、IO中断怎样处理;②如何设计软件检测多个独立按键的;③用户发送按键事件,短按、长按、超长按;④然后统一处理按键事件,在按键事件中定义业务功能。

2.用户如何设计按键检测模块

首先建立键模块化文件app_buttons.c和app_buttons.h。app_buttons.c存放程序内容,app_buttons.h存放相关接口。实现了正常模式和低功耗模式下的按键检测,代码如下:

2.1 GPIO初始化

// 按键 PD7 PC2 PC3 exti_enable
    system_set_port_mux(GPIO_PORT_D, GPIO_BIT_7, PORTD7_FUNC_D7);
    system_set_port_mux(GPIO_PORT_D, GPIO_BIT_6, PORTD6_FUNC_D6);
    system_set_port_mux(GPIO_PORT_C, GPIO_BIT_2, PORTC2_FUNC_C2);
    system_set_port_mux(GPIO_PORT_C, GPIO_BIT_3, PORTC3_FUNC_C3);
    system_set_port_pull(GPIO_PD7, GPIO_PULL_UP, true);
    system_set_port_pull(GPIO_PD6, GPIO_PULL_UP, true);
    system_set_port_pull(GPIO_PA4, GPIO_PULL_UP, true);
    system_set_port_pull(GPIO_PA5, GPIO_PULL_UP, true);
    exti_enable(GPIO_PD7|GPIO_PD6|GPIO_PC3|GPIO_PC2);
    NVIC_EnableIRQ(GPIO_IRQn);

	// 按键PD7 PC2 PC3 wakeup
	pmu_set_pin_pull(GPIO_PORT_D, GPIO_PIN_7, GPIO_PULL_UP);
    pmu_set_pin_pull(GPIO_PORT_D, GPIO_PIN_6, GPIO_PULL_UP);
	pmu_set_pin_pull(GPIO_PORT_C, GPIO_PIN_2, GPIO_PULL_UP);
    pmu_set_pin_pull(GPIO_PORT_C, GPIO_PIN_3, GPIO_PULL_UP);
    pmu_port_wakeup_func_set(GPIO_PORT_D, GPIO_PIN_7);
    pmu_port_wakeup_func_set(GPIO_PORT_D, GPIO_PIN_6);
    pmu_port_wakeup_func_set(GPIO_PORT_C, GPIO_PIN_2);
    pmu_port_wakeup_func_set(GPIO_PORT_C, GPIO_PIN_3);
    button_init(GPIO_PD7|GPIO_PD6|GPIO_PC3|GPIO_PC2);
    NVIC_EnableIRQ(PMU_IRQn);

2.2按键模块初始化

    uart_printf("buttons_init,%x\r\n",enable_io);
    /*  清除RAM */
    memset(&button_curr_list,0,sizeof(button_curr_list));
    memset(&button_last_list,0,sizeof(button_last_list));
     /*  检测的IO mask */
    button_io_mask = enable_io;
    /*  检测变量初始化 */
    for(uint8_t i=0; i<GPIO_DIO_MAX; i++)
    {
        if(enable_io & (0x01 <<i))
        {
            DEBUG_PRINTF("DIO num : %d\r\n",i);
            button_curr_list[i].io_enable = 1;
            button_curr_list[i].count     = 0;
            button_curr_list[i].flag      = 1;
            button_curr_list[i].io_mask   = (0x01 << i);

            button_last_list[i].io_enable = 1;
            button_last_list[i].count     = 0;
            button_last_list[i].flag      = 1;
            button_last_list[i].io_mask   = (0x01 << i);
        }
    }
    // 按键检测定时器初始化
    os_timer_init(&button_check_timer, button_timeout_handler, NULL);

2.3设计中断函数:

/*
 * @fn      pmu_gpio_isr_ram
 *
 * @brief   pmu_gpio_isr
 *
 * @param   None.
 *
 * @return  None.
 */
__attribute__((section("ram_code"))) void pmu_gpio_isr_ram(void)
{
    uint32_t gpio_value = ool_read32(PMU_REG_PORTA_LAST_STATUS);
    button_toggle_detected(gpio_value);
    ool_write32(PMU_REG_PORTA_LAST_STATUS, gpio_value);
}

2.4循环检测按键和输出事件

/*  中断检查IO状态  */
void BUTTON_CheckHandler(void)
{
    static uint8_t released_type=0;
    static uint32_t curr_button=0;
    static uint32_t button_check_sleep=0;

    //DEBUG_PRINTF("but: %04X \r\n",ool_read32(PMU_REG_PORTA_LAST_STATUS));

    curr_button = ool_read32(PMU_REG_PORTA_LAST_STATUS);

    for(uint8_t index=0; index<GPIO_DIO_MAX; index++)
    {
        if(button_curr_list[index].io_enable == 0) continue;

        //DEBUG_PRINTF("[%d]\r\n",index);
        button_curr_list[index].flag = (curr_button & (0x01<<index)) ? 1 : 0;

        //DEBUG_PRINTF("%d[%d][%d]\r\n",index,button_curr_list[index].flag,button_last_list[index].flag);

        if(button_curr_list[index].flag == button_last_list[index].flag)
        {
            //button_check_timer_begin |= button_curr_list[index].io_mask;
            //DEBUG_PRINTF("Count[%d]\r\n",button_curr_list[index].count);

            button_curr_list[index].count ++;
            if(button_curr_list[index].count > BUTTON_LONG_LONG_TIME)            // 超长按 5s
            {
                button_curr_list[index].count = BUTTON_LONG_LONG_TIME;

                button_check_sleep &= ~button_curr_list[index].io_mask;         // 清除进入 循环检查
            }
            else if(button_curr_list[index].count >= BUTTON_LONG_LONG_TIME)      // 超长按 5s
            {
                if(button_curr_list[index].flag == 0)
                {
                    button_send_event(BUTTON_LONG_LONG_PRESSED,index);
                    button_curr_list[index].out_type = BUTTON_LONG_LONG_PRESSED;
                }
            }else if(button_curr_list[index].count == BUTTON_LONG_TIME)         // 长按 1s
            {
                //DEBUG_PRINTF("BUTTON_LONG_TIME\r\n");
                if(button_curr_list[index].flag == 0)
                {
                    button_send_event(BUTTON_LONG_PRESSED,index);
                    button_curr_list[index].out_type = BUTTON_LONG_PRESSED;
                }else
                {
                    if(released_type == BUTTON_NOP)
                    {
                        // 无按下按键或者按下时间不够
                        button_send_event(BUTTON_NOP,index);
                    }
                }

            }else if(button_curr_list[index].count == BUTTON_SHORT_TIME)   // 短按 50ms
            {
                if(button_curr_list[index].flag == 0)
                {
                    button_send_event(BUTTON_PRESSED,index);
                    button_curr_list[index].out_type = BUTTON_PRESSED;
                    released_type = BUTTON_PRESSED;
                }else
                {
                    if(button_curr_list[index].out_type == BUTTON_PRESSED)
                    {
                        button_send_event(BUTTON_RELEASED,index);
                    }else if(button_curr_list[index].out_type == BUTTON_LONG_PRESSED)
                    {
                        button_send_event(BUTTON_LONG_RELEASED,index);
                    }else if(button_curr_list[index].out_type == BUTTON_LONG_LONG_PRESSED)
                    {
                        button_send_event(BUTTON_LONG_LONG_RELEASED,index);
                    }

                    button_check_sleep &= ~button_curr_list[index].io_mask;         // 清除进入 循环检查

                    //DEBUG_PRINTF("button_check %x %x\r\n",button_check_sleep,~button_curr_list[index].io_mask);
                }
            }
        }else
        {
            button_curr_list[index].count = 0;
            released_type = BUTTON_NOP;
            button_check_sleep |= button_curr_list[index].io_mask;
            button_last_list[index].flag = button_curr_list[index].flag;
        }
    }

    //DEBUG_PRINTF("button_check %x %x\r\n",button_check_sleep,button_io_mask);

    if((button_check_sleep & button_io_mask ) == 0)
    {
        DEBUG_PRINTF("button os_timer_stop\r\n");
        os_timer_stop(&button_check_timer);
    }
}

2.5按键任务处理

如何处理按键事件?
这里需要用到消息任务,如下:发送一个按键事件

void button_send_event(button_type_t type, uint8_t but_code)
{
    os_event_t button_event;
    struct button_msg_t msg;

    msg.button_index = (0x01<<but_code);
    msg.button_type  = type;
    msg.button_cnt   = 0;

    button_event.event_id = USER_EVT_BUTTON;
    button_event.src_task_id = user_task_id;
    button_event.param = (void *)&msg;
    button_event.param_len = sizeof(msg);
    DEBUG_PRINTF("button_send_event %d\r\n",type);
    os_msg_post(user_task_id, &button_event);
}

2.6 事件处理函数

最后在用户任务中,处理相关按键事件任务

static int user_task_func(os_event_t *param)
{
    switch(param->event_id)
    {
        case USER_EVT_AT_COMMAND:
            //app_at_cmd_recv_handler(param->param, param->param_len);
            break;

        case USER_EVT_BUTTON:
            {
                struct button_msg_t *button_msg;
                const char *button_type_str[] = {
                                                    "BUTTON_PRESSED",
                                                    "BUTTON_RELEASED",
                                                    "BUTTON_SHORT_RELEASED",
                                                    "BUTTON_MULTI_PRESSED",
                                                    "BUTTON_LONG_PRESSED",
                                                    "BUTTON_LONG_PRESSING",
                                                    "BUTTON_LONG_RELEASED",
                                                    "BUTTON_LONG_LONG_PRESSED",
                                                    "BUTTON_LONG_LONG_RELEASED",
                                                    "BUTTON_COMB_PRESSED",
                                                    "BUTTON_COMB_RELEASED",
                                                    "BUTTON_COMB_SHORT_PRESSED",
                                                    "BUTTON_COMB_LONG_PRESSED",
                                                    "BUTTON_COMB_LONG_PRESSING",
                                                    "BUTTON_COMB_LONG_RELEASED",
                                                    "BUTTON_COMB_LONG_LONG_PRESSED",
                                                    "BUTTON_COMB_LONG_LONG_RELEASED",
                                                };

                button_msg = (struct button_msg_t *)param->param;

                co_printf("%x TYPE:%s.\r\n", button_msg->button_index,button_type_str[button_msg->button_type]);

                switch( button_msg->button_type )
                {
                    case BUTTON_PRESSED:
                         ProcessPress(button_msg->button_index);
                         break;
                    case BUTTON_LONG_PRESSED:
                         ProcessLongPress(button_msg->button_index);
                         break;
                    case BUTTON_LONG_LONG_PRESSED:
                         ProcessLongLongPress(button_msg->button_index);
                         break;

                    case BUTTON_SHORT_RELEASED:
                         ProcessRelease(button_msg->button_index);
                         break;
                    case BUTTON_LONG_RELEASED:
                         ProcessLongRelease(button_msg->button_index);
                         break;
                    case BUTTON_LONG_LONG_RELEASED:
                         ProcessLongLongRelease(button_msg->button_index);
                         break;
                    default:
                        break;
                }
            }
            break;
        case DSP_EVT_COMMAND:
            break;
        default:
            break;
    }

    return EVT_CONSUMED;
}

3.按键模块调试细节

1)BLE工作在低功耗模式和正常工作模式下,按键皆可以正常使用
2)GPIO中断函数回调函数要处理好,放入相关的中断回调的接口
3)启动定时器轮询检测按键,检测超时关闭定时器,减少功耗

4.小结

按键检测处理方法有很多,这里采用中断+定时器检测并发送事件+事件处理的方法,好处有①可以实现按键的模块化;②中断按键响应速度快;③按键触发稳定,定时间隔滤波防止误处理。④支持32个独立按键检测。按键检测是软件开发必备知识,相信还有很多中方法;作者常用这种方法来开发按键检测,不知您还有哪种更好按键检测的方法,欢迎评论,谢谢!。

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

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

相关文章

【Python面试题收录】Python编程基础练习题①(数据类型+函数+文件操作)

本文所有代码打包在Gitee仓库中https://gitee.com/wx114/Python-Interview-Questions 一、数据类型 第一题&#xff08;str&#xff09; 请编写一个Python程序&#xff0c;完成以下任务&#xff1a; 去除字符串开头和结尾的空格。使用逗号&#xff08;","&#…

【数据库】Quartz2.3 框架 数据库设计说明书

1、 Quartz表说明 2、 quartz 的触发时间的配置 1、 cron 方式&#xff1a;采用cronExpression表达式配置时间。 2、 simple 方式&#xff1a;和JavaTimer差不多&#xff0c;可以指定一个开始时间和结束时间外加一个循环时间。 3、 calendars 方式&#xff1a;可以和cron配合使…

Java-----栈

目录 1.栈&#xff08;Stack&#xff09; 1.1概念 1.2栈的使用 1.3栈的模拟实现 1.4栈的应用场景 1.5栈、虚拟机栈、栈帧有什么区别呢 1.栈&#xff08;Stack&#xff09; 1.1概念 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操…

Centos8 yum 更换源以及安装内核头文件

文章目录 一、简介二、yum 更换源三、安装内核头文件 一、简介 CentOS 是一个开源项目&#xff0c;发布了两个不同的 Linux 发行版——CentOS Stream 和 CentOS Linux 。 CentOS Stream 是即将发布的红帽企业 Linux 产品的上游开发平台。 CentOS 项目将于 2024 年 6 月 30 日…

场外期权如何报价?名义本金是什么?

今天带你了解场外期权如何报价&#xff1f;名义本金是什么&#xff1f;投资者首先需要挑选自己想要进行期权交易的沪深上市公司股票。选出股票后&#xff0c;需要将股票信息、预期的操作时间&#xff08;如期限&#xff09;、看涨或看跌的选择以及预计的交易金额等信息报给场外…

商家虚假发货行为频发,电商平台如何通过物流轨迹来监管?(内附视频号、抖音、京东的发货规则)

近年来&#xff0c;“虚假发货”问题在电商行业中日益凸显。某投诉平台数据显示&#xff0c;截至2024年7月&#xff0c;搜索“虚假发货”显示的投诉高达19万条&#xff0c;如何有效监控卖家发货的合规性与及时性、打击虚假发货行为成为电商平台的重要议题。 为了维护消费者权益…

剧透:巴黎奥运会用上了AI转播

** AI增强技术&#xff0c;让比赛画面变成电影特效。 ** 巴黎奥运会即将开幕&#xff01; 阿里云在奥运转播中应用的AI增强技术 将让比赛画面变成电影特效&#xff01; 剧透如下 &#x1f447;&#x1f3fb; 阿里云为奥运转播提供的高自由度回放“子弹时间”&#xff0c;是…

[Mysql-DDL数据操作语句]

目录 DDL语句操作数据库 库&#xff1a; 查看&#xff1a;show 创建&#xff1a;creat 删除&#xff1a;drop 使用(切换)&#xff1a;use 表&#xff1a; 查看&#xff1a;desc show 创建&#xff1a;create 表结构修改 rename as add drop modify change rename as …

cesium海洋到站提示

项目地址:Every Admin: 用于快速搭建后台管理和其他页面的项目,组件化开发,以及大屏展示. <template> <div class"topbox"> xx海洋管理 </div> <div class"selectbox"> <div class"title"> 航线列表 </div>…

了解Java虚拟机(JVM)

前言&#x1f440;~ 上一章我们介绍网络原理相关的知识点&#xff0c;今天我们浅浅来了解一下java虚拟机JVM JVM&#xff08; Java Virtual Machine &#xff09; JVM内存区域划分 方法区/元数据区&#xff08;线程共享&#xff09; 堆&#xff08;线程共享&#xff09; 虚…

Nginx 配置与优化:常见问题全面解析

文章目录 Nginx 配置与优化:常见问题全面解析一、Nginx 安装与配置问题1.1 Nginx 安装失败问题描述解决方法1.2 Nginx 配置文件语法错误问题描述解决方法二、Nginx 服务启动与停止问题2.1 Nginx 无法启动问题描述解决方法2.2 Nginx 服务无法停止问题描述解决方法三、Nginx 性能…

尚硅谷vue全家桶(vue2+vue3)笔记

Vue2 一、Vue核心 01_简介 1.特点 采用组件化模式&#xff0c;提高代码复用率、且让代码更好维护。声明式编码&#xff0c;让编程人员无需直接操作DOM&#xff08;命令式编码&#xff09;&#xff0c;提高开发效率。使用虚拟DOM优秀的Diff算法&#xff0c;尽量复用DOM节点。…

【日常记录】【JS】JS中查询参数处理工具URLSearchParams

文章目录 1. 引言2. URLSearchParams2.1 URLSearchParams 的构造函数2.2 append() 方法2.3 delete() 方法2.4 entries() 方法2.5 forEach() 方法2.6 get() 方法2.7 getAll() 方法2.8 has() 方法2.9 keys() 方法2.10 set() 方法2.11 toString() 方法2.12 values() 方法 参考链接…

Pytorch深度学习实践(5)逻辑回归

逻辑回归 逻辑回归主要是解决分类问题 回归任务&#xff1a;结果是一个连续的实数分类任务&#xff1a;结果是一个离散的值 分类任务不能直接使用回归去预测&#xff0c;比如在手写识别中&#xff08;识别手写 0 − − 9 0 -- 9 0−−9&#xff09;&#xff0c;因为各个类别…

python毕业设计选题协同过滤算法在音乐推荐系统

✌网站介绍&#xff1a;✌10年项目辅导经验、专注于计算机技术领域学生项目实战辅导。 ✌服务范围&#xff1a;Java(SpringBoo/SSM)、Python、PHP、Nodejs、爬虫、数据可视化、小程序、安卓app、大数据等设计与开发。 ✌服务内容&#xff1a;免费功能设计、免费提供开题答辩P…

【进程检测】使用pywin32捕获window进程信息

需求 检测win系统依赖服务进程的运行情况&#xff0c;版本信息&#xff08;进程检测器&#xff09;检测内外网连接情况 实现 进程检测 # 使用pywin32获取进程版本信息 def get_version_info(path):try:info GetFileVersionInfo(path, \\)ms info[FileVersionMS]ls info[…

基于单片机控制的气动机械手设计

摘 要&#xff1a; 机械手拥有灵活的运动结构&#xff0c;可以在控制系统控制下完成复杂的运动&#xff0c;从而实现高效率的自动化生产方式&#xff0c;因而成为发展工业生产技术的重要方向。气动技术和单片机技术已相当成熟&#xff0c;工业应用广泛&#xff0c;该文将基于单…

使用 useRequestURL 组合函数访问请求URL

title: 使用 useRequestURL 组合函数访问请求URL date: 2024/7/26 updated: 2024/7/26 author: cmdragon excerpt: 摘要&#xff1a;本文介绍了Nuxt 3中的useRequestURL组合函数&#xff0c;用于在服务器端和客户端环境中获取当前页面的URL信息。通过示例展示了如何在页面中…

html+css 实现水波纹按钮

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享htmlcss 绚丽效果&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 文…

Vue 3 实现左侧列表点击跳转滚动到右侧对应区域的功能

使用 Vue 3 实现左侧列表点击跳转到右侧对应区域的功能 1. 引言 在这篇博客中&#xff0c;我们将展示如何使用 Vue 3 实现一个简单的页面布局&#xff0c;其中左侧是一个列表&#xff0c;点击列表项时&#xff0c;右侧会平滑滚动到对应的内容区域。这种布局在很多应用场景中都…