富芮坤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://xiahunao.cn/news/3268060.html

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

相关文章

深入探索PHP框架:Symfony框架全面解析

1. 引言 在现代Web开发领域&#xff0c;PHP作为一种广泛使用的服务器端脚本语言&#xff0c;其框架的选择对于项目的成功至关重要。PHP框架不仅能够提高开发效率&#xff0c;还能确保代码的质量和可维护性。本文将深入探讨Symfony框架&#xff0c;这是一个功能强大且灵活的PHP…

Python学习笔记45:游戏篇之外星人入侵(六)

前言 飞船模块的功能基本已经完成。今天继续完成子弹模块的功能。 子弹模块 子弹和飞船模块&#xff0c;在游戏逻辑中有一种生成与被生成的表面关系&#xff0c;因为子弹在游戏中是由飞船发射的。但是在我们实际抽象的过程中&#xff0c;飞船与子弹并不是is的关系&#xff0…

UML通信图建模技术及应用例

新书速览|《UML 2.5基础、建模与设计实践》 在对系统的动态行为进行建模时&#xff0c;通信图常被用于按组织结构对控制流进行建模。与顺序图一样&#xff0c;一个单独的通信图只能显示一个控制流。 使用通信图建模时可以遵循如下策略&#xff1a; &#xff08;1&#xff09…

普通人这一生逆袭的唯一机会

普通人这一生逆袭的唯一机会 在人生的长河中&#xff0c;每个普通人心中都藏着一个逆袭的梦想。梦想着从平凡走向卓越&#xff0c;从底层攀至顶峰。但梦想与现实之间&#xff0c;究竟有多远的距离&#xff1f;今天&#xff0c;让我们一起探索那些看似遥不可及&#xff0c;却又…

Unity UGUI 之 自动布局组件

本文仅作学习笔记与交流&#xff0c;不作任何商业用途 本文包括但不限于unity官方手册&#xff0c;唐老狮&#xff0c;麦扣教程知识&#xff0c;引用会标记&#xff0c;如有不足还请斧正 本文在发布时间选用unity 2022.3.8稳定版本&#xff0c;请注意分别 1.什么是自动布局组件…

微服务注册中心

目录 1.微服务的注册中心 1.1 注册中⼼的主要作⽤ 1.2 常⻅的注册中⼼ 2.nacos简介 2.1 nacos实战⼊⻔ 2.2.1 搭建nacos环境 2.2.2 将商品微服务注册到nacos 3.服务调⽤Ribbon⼊⻔ 3.1 Ribbon概述 3.1.1 什么是Ribbon 3.1.2 Ribbon的主要作⽤ 3.2.2 ⼯程改造 4.服务…

openmv学习笔记(24电赛备赛笔记)

#openmv简介 openmv一种小型&#xff0c;可编程机器视觉摄像头&#xff0c;设计应用嵌入式应用和计算边缘&#xff0c;是图传模块&#xff0c;或者认为是一种&#xff0c;具有图像处理功能的单片机&#xff0c;提供多种接口&#xff08;I2C SPI UART CAN ADC DAC &#xff0…

204、【动态规划】牛客网 ——DP3 跳台阶扩展问题(Python版本)

题目描述 原题链接&#xff1a;DP3 跳台阶扩展问题 解题思路 一个DP问题&#xff0c;相比于普通爬楼&#xff08;只能爬一层或者两层&#xff09;对应的状态函数为 d p [ i ] d p [ i − 1 ] d p [ i − 2 ] dp[i] dp[i - 1] dp[i - 2] dp[i]dp[i−1]dp[i−2]。本题的dp…

vue3+g2plot实现词云图

词云图 效果预览: 核心代码: import {WordCloud } from @antv/g2plot;fetch(https://gw.alipayobjects.com/os/antfincdn/jPKbal7r9r/mock.json).then((res) => res.json()).then((data) => {const wordCloud = new WordCloud(container, {data,wordField: x,weigh…

秒懂Linux之权限

目录 一.Linux用户 二.文件权限 2.1 权限属性 chmod命令 chown与chgrp命令 2.2 文件类型 file指令 常见类型 2.3 常见权限问题 问题一&#xff1a; 问题二&#xff1a; 问题三&#xff1a; 一.Linux用户 Linux 下有两种用户&#xff1a;超级用户&#xff08; root …

kettle从入门到精通 第八十课 ETL之kettle kettle中的json对象字段写入postgresql中的json字段

场景&#xff1a;源数据库表为mysql的其中有json字段&#xff0c;通过kettle 查询出来 插入到目标数据库 postgresql中&#xff0c;对应的表中也有json字段。。但是报错&#xff0c;提示kettle查询出来是varchar的的字段&#xff0c;无法插入到目标数据库中。 1、创建测试表。 …

VBA技术资料MF180:将某个文件夹中的某类图片导入Word

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。“VBA语言専攻”提供的教程一共九套&#xff0c;分为初级、中级、高级三大部分&#xff0c;教程是对VBA的系统讲解&#…

【C++进阶学习】第九弹——哈希的原理与实现——开放寻址法的讲解

前言&#xff1a; 在前面&#xff0c;我们已经学习了很多存储机构&#xff0c;包括线性存储、树性存储等&#xff0c;并学习了多种拓展结构&#xff0c;效率也越来越高&#xff0c;但是是否有一种存储结构可以在大部分问题中都一次找到目标值呢&#xff1f;哈希可能能实现 目录…

【C++】C++应用案例-翻转数组

翻转数组&#xff0c;就是要把数组中元素的顺序全部反过来。比如一个数组{1,2,3,4,5,6,7,8}&#xff0c;翻转之后就是{8,7,6,5,4,3,2,1}。 &#xff08;1&#xff09;另外创建数组&#xff0c;反向填入元素 数组是将元素按照顺序依次存放的&#xff0c;长度固定。所以如果想要…

C++ | Leetcode C++题解之第283题移动零

题目&#xff1a; 题解&#xff1a; class Solution { public:void moveZeroes(vector<int>& nums) {int n nums.size(), left 0, right 0;while (right < n) {if (nums[right]) {swap(nums[left], nums[right]);left;}right;}} };

Go语言编程 学习笔记整理 第2章 顺序编程 前半部分

前言&#xff1a;《Go语言编程》编著 许式伟 吕桂华 等 1.1 变量 var v1 int var v2 string var v3 [10]int // 数组 var v4 []int // 数组切片 var v5 struct { f int } var v6 *int // 指针 var v7 map[string]int // map&#xff0c;key为string类型&#xff0c;value为in…

shell脚本(fifteen day)

一、shell概述 1、shell概念 &#xff08;1&#xff09;shell 英文翻译过来是外壳的意思&#xff0c;作为计算机语言来理解可以认为它是操作系统的外壳。可以通过shell 命令来操作和控制操作系统&#xff0c;比如Linux中的shell命令就包括 ls、cd、pwd 等等。 &#xff08;2&a…

postgres启动错误

说明&#xff1a;记录一次在Linux上启动postgres数据错误&#xff1b; 问题&#xff1a;安装好postgres数据库后&#xff0c;我使用systemctl启动数据库&#xff0c;报下面的错误 ● postgresql-15.service - PostgreSQL 15 database serverLoaded: loaded (/usr/lib/systemd…

【研路导航】保研英语面试高分攻略,助你一路过关斩将

面试攻略之 千锤百炼英语口语 写在前面 在保研面试中&#xff0c;英语口语往往是让许多同学感到头疼的一部分。如何在面试中展现出自信和流利的英语表达能力&#xff0c;是我们今天要探讨的主题。以下是一些有效的英语口语练习方法和常见题型解析&#xff0c;帮助你在保研面试…

使用PyTorch导出JIT模型:C++ API与libtorch实战

PyTorch导出JIT模型并用C API libtorch调用 本文将介绍如何将一个 PyTorch 模型导出为 JIT 模型并用 PyTorch 的 CAPI libtorch运行这个模型。 Step1&#xff1a;导出模型 首先我们进行第一步&#xff0c;用 Python API 来导出模型&#xff0c;由于本文的重点是在后面的部署…