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


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_enablesystem_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 wakeuppmu_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.mzph.cn/web/49359.shtml

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

相关文章

Spring循环依赖详解

一、什么是循环依赖 在Spring框架中&#xff0c;循环依赖是指两个或更多的Bean相互间直接或间接地依赖对方的情况。这种依赖关系可能导致Bean的实例化过程中出现问题&#xff0c;但Spring通过其内部的处理机制能够解决某些类型的循环依赖。 简单的说就是A依赖B&#xff0c;B依赖…

数据库安全综合治理方案(可编辑54页PPT)

引言&#xff1a;数据库安全综合治理方案是一个系统性的工作&#xff0c;需要从多个方面入手&#xff0c;综合运用各种技术和管理手段&#xff0c;确保数据库系统的安全稳定运行。 方案介绍&#xff1a; 数据库安全综合治理方案是一个综合性的策略&#xff0c;旨在确保数据库系…

photoshop学习笔记——选区3 选区的变化

选区制作完成之后可以对选区进行变化 主要有&#xff1a; 反选&#xff1a;shift ctrl I 变换 修改&#xff1a; 边界 设置选区的边界大小 类似 CSS中的borderSize平滑 对选区的角进行平滑扩展 向外扩展选区收缩 向内收缩选区羽化 让边界变得过渡透明度 设置…

MySQL作业四

1. 创建数据库mydb15_indexstu 2. 创建表student&#xff0c;course&#xff0c;sc 2.1 创建表student 2.2 创建表course 2.3 创建表sc 3. 处理表 3.1 修改表student中年龄&#xff08;sage&#xff09;字段属性&#xff0c;数据类型由int改变为smallint 3.2 为表course中cno…

【数据结构】栈(基于数组、链表实现 + GIF图解 + 原码)

Hi~&#xff01;这里是奋斗的明志&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f331;&#x1f331;个人主页&#xff1a;奋斗的明志 &#x1f331;&#x1f331;所属专栏&#xff1a;数据结构 &#x1f4da;本系列文章为个人学…

Golang | Leetcode Golang题解之第268题丢失的数字

题目&#xff1a; 题解&#xff1a; func missingNumber(nums []int) int {n : len(nums)total : n * (n 1) / 2arrSum : 0for _, num : range nums {arrSum num}return total - arrSum }

scrapy爬取城市天气数据

scrapy爬取城市天气数据 一、创建scrapy项目二、修改settings,设置UA,开启管道三、编写爬虫文件四、编写items.py五、在weather.py中导入WeatherSpiderItem类六、管道中存入数据,保存至csv文件七、完整代码一、创建scrapy项目 先来看一下爬取的字段情况: 本次爬取城市天…

【中项】系统集成项目管理工程师-第5章 软件工程-5.3软件设计

前言&#xff1a;系统集成项目管理工程师专业&#xff0c;现分享一些教材知识点。觉得文章还不错的喜欢点赞收藏的同时帮忙点点关注。 软考同样是国家人社部和工信部组织的国家级考试&#xff0c;全称为“全国计算机与软件专业技术资格&#xff08;水平&#xff09;考试”&…

一文解决 | Linux(Ubuntn)系统安装 | 硬盘挂载 | 用户创建 | 生信分析配置

原文链接&#xff1a;一文解决 | Linux&#xff08;Ubuntn&#xff09;系统安装 | 硬盘挂载 | 用户创建 | 生信分析配置 本期教程 获得本期教程文本文档&#xff0c;在后台回复&#xff1a;20240724。请大家看清楚回复关键词&#xff0c;每天都有很多人回复错误关键词&#xf…

Blackbox AI-跨时代AI产物,你的私人编程助手

1. 引言 随着人工智能技术的飞速发展&#xff0c;我们的生活方式正在经历前所未有的变革。从智能家居到自动驾驶&#xff0c;AI已经渗透到我们生活的方方面面。而在这场科技革命中&#xff0c;Blackbox 网站凭借其先进的技术和全面的功能&#xff0c;成为了众多AI产品中的佼佼者…

不同行情下算法的具体使用!

上一篇我们说到了不同公司算法交易的区分&#xff0c;有朋友提出了不同的行情下的算法交易应该怎么使用&#xff0c;小编今天就带大家了解下&#xff01;当然具体实际状况百出&#xff0c;这种可以实际为准&#xff08;韭菜修养全拼实际探讨交流&#xff09;&#xff01; 我们在…

Redis的使用场景、持久化方式和集群模式

1. Redis的使用场景 热点数据的缓存 热点数据&#xff1a;频繁读取的数据 限时任务的操作。比如短信验证码 完成session共享的问题。因为前后端分离 完成分布式锁 商品的销售量 2. Redis的持久化方式 2.1 什么是持久化 把内存中的数据存储到磁盘的过程。同时也可以把磁盘中…

AI绘画入门实践|Midjourney:使用 --no 去除不想要的物体

在 Midjourney 中&#xff0c;--no 作为反向提示词&#xff0c;告诉 MJ 在生成图像时&#xff0c;不要包含什么。 使用格式&#xff1a;--no 对应物体提示词&#xff08;多个物体之间使用","间隔&#xff09; 使用演示 a web banner, summer holiday --v 6.0 a web b…

【Git多人协作开发】同一分支下的多人协作开发模式

目录 0.前言场景 1.开发者1☞完成准备工作&协作开发 1.1创建dev分支开发 1.2拉取远程dev分支至本地 1.3查看分支情况和分支联系情况 1.4创建本地dev分支且与远程dev分支建立联系 1.5在本地dev分支上开发file.txt 1.6推送push至远程仓库 2.开发者2☞完成准备工作&…

C++第十弹 ---- vector的介绍及使用

目录 前言vector的介绍及使用1. vector的使用1.1 vector的定义1.2 iterator的使用1.3 vector空间增长问题1.4 vector增删查改 2. vector迭代器失效问题(重点) 总结 前言 本文介绍了C中的vector数据结构及其使用方法。 更多好文, 持续关注 ~ 酷酷学!!! 正文开始 vector的介绍…

GPT-4O 的实时语音对话功能在处理多语言客户时有哪些优势?

最强AI视频生成&#xff1a;小说文案智能分镜智能识别角色和场景批量Ai绘图自动配音添加音乐一键合成视频百万播放量 我瞄了一眼OpenAI春季发布会&#xff0c;这个发布会只有26分钟&#xff0c;你可以说它是一部科幻短片&#xff0c;也可以说它过于“夸夸其谈”&#xff01;关于…

Anconda 快速常用命令简洁版

目的&#xff1a;简单清楚的使用基本的conda 命令 可能需求 查看项目中的虚拟环境及依赖是否满足需求操作新环境来满足项目或者论文的实现 Anconda 常用命令 conda 查看基础命令1. 进入Anaconda 环境2. 查看版本3.查看有哪些虚拟环境4.激活虚拟环境5. 进入虚拟环境查看6. 退出…

如何在网站嵌入可填写的PDF表单:2024巴黎奥运会赛程

如何将可填写的 PDF 表单嵌入您的网页&#xff1f;访问者无需下载或注册即可查看并填写。 简单&#xff01;本文以2024巴黎奥运会赛程表单为例&#xff0c;演示如何将其嵌入网页中。您可以在 ONLYOFFICE 表单库免费获取该模板&#xff0c;有白色和紫色两种背景设计。 如何在网站…

六、抽象工厂模式

文章目录 1 基本介绍2 案例2.1 Drink 抽象类2.2 Tea 类2.3 Coffee 类2.4 DrinkFactory 接口2.5 TeaFactory 类2.6 CoffeeFactory 类2.7 Client 类2.8 Client 类运行结果2.9 总结 3 各角色之间的关系3.1 角色3.1.1 Product ( 抽象产品 )3.1.2 ConcreteProduct ( 具体产品 )3.1.3…

昇思25天学习打卡营第1天|快速入门-实现一个简单的深度学习模型

目录 实验环境 Jupyter云上开发环境使用 导包 处理数据集 网络构建 模型训练 评估模型性能 保存模型 加载模型 预测推理 实验环境 02-快速入门.ipynb (4) - JupyterLab (mindspore.cn) 规格&#xff1a;4u 16G 20G 镜像&#xff1a;py39-ms2.3.0rc1 特性&#xff1…