【FR801xH】富芮坤FR801xH之全功能按键案例
00. 目录
文章目录
- 00. 目录
- 01. FR801xH概述
- 02. FR801xH功能框图
- 03.Button模块概述
- 04. Button模块核心设计思想
- 05. Button模块系统架构概览
- 06. Button模块数据结构详解
- 07. 状态机引擎解析
- 08. 定时器系统设计
- 09. 事件处理流程
- 10. 程序实现参考源码
- 11. 测试示例
- 12. 附录
01. FR801xH概述
FR801xH 系列芯片是面向SOC(片上系统),易于快速开发的低功耗蓝牙芯片。基于 Freqchip 的蓝牙智能固件和协议栈的支持,完全兼容蓝牙 V5.3(LE 模式)协议。同时用户可以基于芯片内置的 ARM CorteM3 嵌入式 32 位高性能单片机开发各种应用程序。
蓝牙智能固件包括 L2CAP 服务层协议、安全管理器 (SM)、属性协议(ATT)、通用属性配置文件 (GATT)和通用访问配置文件(GAP)。此外,还 支持应用程序配置文件,例如接近度、健康温度计、 心率、血压、血糖、人机界面设备(HID)和 SDK (包括驱动程序、OS-API 等)。SDK 还集成了用于网络应用程序的 SIG Mesh 协议。
采用 Freqchip 的创新技术,将 PMU(锂电池充电 器+LDO)、带 XIP 模式的 QSPI FLASH ROM、 I2C、UART、GPIO、ADC、PWM 集成在一块芯片中,为客户提供:
- 竞争力的功耗
- 稳定的蓝牙连接
- 极低的 BOM 成本
02. FR801xH功能框图
03.Button模块概述
该按键驱动通过状态机实现了丰富的按键事件检测,包括单击、双击(多击)、长按、超长按、组合按键等。使用定时器进行防抖和超时检测,通过任务队列处理事件,避免在中断中处理复杂逻辑。
04. Button模块核心设计思想
该按键驱动采用分层状态机 + 定时器调度架构,实现以下功能:
- 支持单按键/组合按键检测
- 支持单击、连击、短按、长按、超长按、长按自动连发
- 硬件防抖处理
- 事件驱动架构(通过消息队列传递事件)
05. Button模块系统架构概览
基于状态机的按键驱动系统,采用分层设计:
- 硬件抽象层:GPIO中断和寄存器访问
- 驱动核心层:状态机引擎和定时器管理
- 应用接口层:事件消息传递机制
06. Button模块数据结构详解
6.1 按键状态机状态定义(10种状态)
enum button_working_state_t {BUTTON_WORKING_STATE_IDLE, // 空闲状态BUTTON_WORKING_STATE_JUST_PRESSED, // 单键初始按下BUTTON_WORKING_STATE_PRESSED, // 单键持续按下BUTTON_WORKING_STATE_WAIT_MULTI, // 等待连击BUTTON_WORKING_STATE_LONG_PRESSED, // 单键长按BUTTON_WORKING_STATE_LONG_LONG_PRESSED, // 单键超长按BUTTON_WORKING_STATE_COMB_JUST_PRESSED, // 组合键初始按下BUTTON_WORKING_STATE_COMB_PRESSED, // 组合键持续按下BUTTON_WORKING_STATE_COMB_LONG_PRESSED, // 组合键长按BUTTON_WORKING_STATE_COMB_LONG_LONG_PRESSED // 组合键超长按
};
6.2 按键事件类型(17种事件)
enum button_type_t {BUTTON_PRESSED, // 按下事件BUTTON_RELEASED, // 释放事件BUTTON_SHORT_PRESSED, // 短按事件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 // 组合键超长按释放
};
6.3 button_msg_t类型
struct button_msg_t {uint32_t button_index; // 按键位图uint8_t button_type; // 事件类型uint8_t button_cnt; // 连击次数
};
07. 状态机引擎解析
7.1 非组合按键状态图
参考一:
状态机流程:
状态机有10个状态,每个状态对应一个处理函数。状态处理函数根据接收到的事件(释放、单键按下、组合键按下、超时)进行状态转移,并执行相应的动作(如发送事件、启动定时器等)。
单键场景:
- 初始状态:IDLE
- 按下事件(BUTTON_WORKING_EVENT_SINGLE_PRESSED):进入JUST_PRESSED状态,启动状态定时器(80ms),发送BUTTON_PRESSED事件。
- 在JUST_PRESSED状态下:
* 若释放:回到IDLE,发送BUTTON_RELEASED事件。
* 若超时(80ms):进入PRESSED状态,启动状态定时器(2000ms-80ms=1920ms,实际代码中计算为(BUTTON_LONG_DURING*10-BUTTON_SHORT_DURING)*10,注意单位是ms)。
- 在PRESSED状态下:
* 若释放:如果禁止连击,则发送短按事件并回到IDLE;否则进入WAIT_MULTI状态,启动定时器(200ms)等待连击。
* 若超时(2000ms):进入LONG_PRESSED状态,启动长按连发定时器(300ms)和状态定时器(4000ms-2000ms=2000ms?实际是(BUTTON_LONG_LONG_DURING-BUTTON_LONG_DURING)*10 * 10),发送长按事件。
- 在LONG_PRESSED状态下:
* 释放:回到IDLE,发送长按释放事件。
* 超时(4000ms):进入LONG_LONG_PRESSED状态,发送超长按事件。
连击场景(在WAIT_MULTI状态):
- 在200ms内再次按下同一个键:进入JUST_PRESSED状态,重置状态定时器(80ms),并发送按下事件(此时不发送短按事件,等待连击结束)。
- 若超时(200ms):根据pressed_cnt的值,发送短按(cnt=1)或连按事件(cnt>1),然后回到IDLE。
组合键场景:
- 组合键的状态转移与单键类似,但有独立的状态(如COMB_JUST_PRESSED, COMB_PRESSED等)。
参考二:
(1) 空闲状态(BUTTON_WORKING_STATE_IDLE):
- 单键按下事件:进入刚按下状态(JUST_PRESSED),启动短按定时器(80ms),发送BUTTON_PRESSED事件。
- 组合键按下事件:进入组合刚按下状态(COMB_JUST_PRESSED),启动短按定时器,发送BUTTON_COMB_PRESSED事件。
(2) 刚按下状态(BUTTON_WORKING_STATE_JUST_PRESSED):
- 释放事件:回到空闲状态,停止状态定时器,发送BUTTON_RELEASED事件。
- 组合键按下事件:转移到组合刚按下状态,并重新启动短按定时器,发送BUTTON_COMB_PRESSED事件(注意:这里可能是一个组合键按下的过程,比如先按一个键再按另一个键)。
- 短按定时器超时(80ms):进入已按下状态(PRESSED),并启动长按定时器(2000ms-80ms=1920ms)。
(3) 已按下状态(BUTTON_WORKING_STATE_PRESSED):
- 释放事件:
a) 如果该按键禁用了连击(代码中0表示未启用,因为条件为0),则回到空闲状态,发送短按事件(BUTTON_SHORT_PRESSED)。
b) 否则,进入等待连击状态(WAIT_MULTI),记录当前按键(button_to_be_send),连击计数加1,启动连击间隔定时器(200ms)。 - 组合键按下事件:进入组合刚按下状态,重新启动短按定时器,发送组合键按下事件(BUTTON_COMB_PRESSED)。
- 长按定时器超时:进入长按状态(LONG_PRESSED),启动超长按定时器(4000ms-2000ms=2000ms)和长按重复定时器(300ms),发送长按事件(BUTTON_LONG_PRESSED)。
(4) 等待连击状态(BUTTON_WORKING_STATE_WAIT_MULTI):
- 单键按下事件(可能是同一个按键或者不同按键):
- 如果是不同的按键:先发送之前记录的连击事件(如果计数>1则发送连击事件,否则发送短按事件),然后发送新按键的按下事件。然后进入刚按下状态,启动短按定时器。
- 如果是同一个按键?代码中判断current_pressed_button != button_to_be_send,即不同按键,则按上述处理。但如果是同一个按键,代码中没有这个分支,实际上同一个按键再次按下应该也是走这个分支(因为button_to_be_send是之前记录的按键,而current_pressed_button是当前按下的按键,如果是同一个按键,那么条件不成立,不会进入if,直接执行后面的代码。但是注意,同一个按键按下时,current_pressed_button应该等于button_to_be_send,所以不会进入if,因此不会发送连击事件,而是直接进入刚按下状态并发送新按下事件。这样设计可能有问题,因为同一个按键的连击应该被识别。
- 组合键按下事件:进入组合刚按下状态,启动短按定时器,发送组合键按下事件,并发送之前记录的按键的短按事件(BUTTON_SHORT_PRESSED)。
- 连击间隔超时:回到空闲状态,根据连击计数发送连击事件(计数>1)或短按事件(计数=1)。
(5) 长按状态(BUTTON_WORKING_STATE_LONG_PRESSED):
- 释放事件:回到空闲状态,停止两个定时器,发送长按释放事件(BUTTON_LONG_RELEASED)。
- 组合键按下事件:进入组合刚按下状态,停止长按重复定时器,发送组合键按下事件和长按释放事件。
- 超长按定时器超时(2000ms):进入超长按状态(LONG_LONG_PRESSED),发送超长按事件(BUTTON_LONG_LONG_PRESSED)。
(6) 超长按状态(BUTTON_WORKING_STATE_LONG_LONG_PRESSED):
- 释放事件:回到空闲状态,停止长按重复定时器(如果还在运行),发送超长按释放事件(BUTTON_LONG_LONG_RELEASED)。
- 组合键按下事件:进入组合刚按下状态,停止长按重复定时器,发送组合键按下事件和超长按释放事件。
(7) 组合刚按下状态(BUTTON_WORKING_STATE_COMB_JUST_PRESSED):
- 释放事件:回到空闲状态,停止状态定时器,发送组合键释放事件(BUTTON_COMB_RELEASED)。
- 单键按下事件:进入刚按下状态,启动短按定时器,发送组合键释放事件和新按键的按下事件。
- 组合键按下事件:这里指组合键中又按下一个键?进入刚按下状态(这里应该是组合键再次变化,但代码中进入JUST_PRESSED并发送组合键按下事件)。注意:这个状态本身就是组合键按下,再次组合键按下事件的处理似乎不太合理。
- 短按定时器超时:进入组合键已按下状态(COMB_PRESSED),启动长按定时器(2000ms-80ms=1920ms)。
(8) 组合键已按下状态(BUTTON_WORKING_STATE_COMB_PRESSED):
- 释放事件:回到空闲状态,停止状态定时器,发送组合键短按事件(BUTTON_COMB_SHORT_PRESSED)。
- 单键按下事件:进入刚按下状态,发送组合键短按事件和新按键按下事件,启动短按定时器。
- 组合键按下事件:进入组合刚按下状态,发送组合键短按事件和新的组合键按下事件,并启动短按定时器。
- 长按定时器超时:进入组合键长按状态(COMB_LONG_PRESSED),启动超长按定时器和长按重复定时器,发送组合键长按事件(BUTTON_COMB_LONG_PRESSED)。
(9) 组合键长按状态(BUTTON_WORKING_STATE_COMB_LONG_PRESSED):
- 释放事件:回到空闲状态,停止定时器,发送组合键长按释放事件。
- 单键按下事件:进入刚按下状态,停止长按重复定时器,发送组合键长按释放事件和新按键按下事件。
- 组合键按下事件:进入组合刚按下状态,停止长按重复定时器,发送组合键长按释放事件和新的组合键按下事件。
- 超长按定时器超时:进入组合键超长按状态(COMB_LONG_LONG_PRESSED),发送组合键超长按事件。
(10) 组合键超长按状态(BUTTON_WORKING_STATE_COMB_LONG_LONG_PRESSED):
- 释放事件:回到空闲状态,停止长按重复定时器,发送组合键超长按释放事件。
- 单键按下事件:进入刚按下状态,停止长按重复定时器,发送组合键超长按释放事件和新按键按下事件。
- 组合键按下事件:进入组合刚按下状态,停止长按重复定时器,发送组合键超长按释放事件和新的组合键按下事件。
7.2 状态转移矩阵
当前状态 | 触发事件 | 下一状态 | 执行动作 |
---|---|---|---|
IDLE | SINGLE_PRESSED | JUST_PRESSED | 启动短按计时器(80ms),发送BUTTON_PRESSED事件 |
JUST_PRESSED | RELEASED | IDLE | 发送BUTTON_RELEASED事件 |
JUST_PRESSED | TIMEOUT(80ms) | PRESSED | 启动长按计时器(1920ms) |
PRESSED | RELEASED | WAIT_MULTI | 启动连击检测计时器(200ms),增加连击计数 |
PRESSED | TIMEOUT(2000ms) | LONG_PRESSED | 启动超长按计时器(2000ms)和连发定时器(300ms),发送BUTTON_LONG_PRESSED事件 |
WAIT_MULTI | SINGLE_PRESSED | JUST_PRESSED | 启动短按计时器(80ms),发送BUTTON_PRESSED事件 |
WAIT_MULTI | TIMEOUT(200ms) | IDLE | 发送BUTTON_SHORT_PRESSED或BUTTON_MULTI_PRESSED事件 |
LONG_PRESSED | RELEASED | IDLE | 停止所有定时器,发送BUTTON_LONG_RELEASED事件 |
LONG_PRESSED | TIMEOUT(4000ms) | LONG_LONG_PRESSED | 发送BUTTON_LONG_LONG_PRESSED事件 |
LONG_LONG_PRESSED | RELEASED | IDLE | 发送BUTTON_LONG_LONG_RELEASED事件 |
08. 定时器系统设计
定时器 | 类型 | 作用 | 默认时间 | 触发机制 |
---|---|---|---|---|
button_anti_shake_timer | 一次性 | 硬件防抖 | 10ms | 电平变化时启动 |
button_state_timer | 一次性 | 状态超时控制 | 80-4000ms | 状态转移时启动 |
button_pressing_timer | 循环 | 长按连发间隔 | 300ms | 长按时周期触发 |
定时器使用模式:
// 启动一次性定时器
os_timer_start(&timer, duration, false); // 启动循环定时器
os_timer_start(&timer, interval, true);
09. 事件处理流程
10. 程序实现参考源码
button.h
#ifndef _BUTTON_H
#define _BUTTON_H#include <stdint.h>// 事件类型
enum button_event_t
{BUTTON_TOGGLE, //Button切换事件BUTTON_PRESSED_EVENT,BUTTON_TIMER_TO_TIMER,BUTTON_PRESSING_TO_TIMER,BUTTON_ANTI_SHAKE_TO_TIMER,
};// 按键类型
enum button_type_t
{BUTTON_PRESSED, // 按下事件BUTTON_RELEASED, // 释放事件BUTTON_SHORT_PRESSED, // 短按事件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, // 组合键超长按释放
};// 按键状态机状态
enum button_working_state_t
{BUTTON_WORKING_STATE_IDLE, // 空闲状态BUTTON_WORKING_STATE_JUST_PRESSED, // 单键初始按下BUTTON_WORKING_STATE_PRESSED, // 单键持续按下BUTTON_WORKING_STATE_WAIT_MULTI, // 等待连击BUTTON_WORKING_STATE_LONG_PRESSED, // 单键长按BUTTON_WORKING_STATE_LONG_LONG_PRESSED, // 单键超长按BUTTON_WORKING_STATE_COMB_JUST_PRESSED, // 组合键初始按下BUTTON_WORKING_STATE_COMB_PRESSED, // 组合键持续按下BUTTON_WORKING_STATE_COMB_LONG_PRESSED, // 组合键长按BUTTON_WORKING_STATE_COMB_LONG_LONG_PRESSED, // 组合键超长按BUTTON_WORKING_STATE_MAX,
};// 工作事件
enum button_working_event_t
{BUTTON_WORKING_EVENT_RELEASED,BUTTON_WORKING_EVENT_SINGLE_PRESSED,BUTTON_WORKING_EVENT_COMB_PRESSED,BUTTON_WORKING_EVENT_TIME_OUT,
};struct button_toggle_param_t
{uint32_t curr_button;uint32_t timestamp;
};struct button_msg_t
{uint32_t button_index; // 按键掩码(如0x01=KEY1)uint8_t button_type; // 事件类型uint8_t button_cnt; // only for multi click // 连击次数
};// 按键切换检测
void button_toggle_detected(uint32_t curr_button);// 按键中断
void button_int_isr(uint32_t changed_button);// 按键初始化
void button_init(uint32_t enable_io);#endif //_BUTTON_H
button.c
#include <stdint.h>#include "os_task.h"
#include "os_msg_q.h"
#include "os_timer.h"
#include "user_task.h"
#include "button.h"#include "driver_pmu.h"// 基于状态机的按键驱动系统// 最大按钮索引 但是代码中没有使用
#define BUTTON_IDX_MAX 1#define BUTTON_SHORT_DURING 0x08 // x10ms 80ms
#define BUTTON_LONG_DURING 0x14 // x100ms 2000ms
#define BUTTON_LONG_LONG_DURING 0x28 // x100ms 4000ms
#define BUTTON_MULTI_INTERVAL 0x14 // x10ms 200ms
#define BUTTON_LONG_PRESSING_INTERVAL 0x1e // x10ms 300ms// 当前按键工作状态
uint8_t current_state = BUTTON_WORKING_STATE_IDLE;
// 按键任务ID
uint16_t button_task_id;
// 防抖定时器
os_timer_t button_anti_shake_timer;
// 长按连发定时器
os_timer_t button_pressing_timer;
// 状态定时器
os_timer_t button_state_timer;// 使能了按键功能的IO掩码
/* which io is enabled for button function */
uint32_t button_io_mask = 0;
// 防抖前读取的按键状态
uint32_t curr_button_before_anti_shake = 0;
// 当前按下的按键(经过防抖后的稳定状态)
uint32_t current_pressed_button = 0;
// 上一次保存的按键状态(用于检测变化)
uint32_t last_saved_button = 0;
// 用于连击时保存要发送的按键(在多次按下之间传递)
uint32_t button_to_be_send = 0; // for multi click
// 连击计数
uint8_t pressed_cnt = 0; // for multi click// 当检测到按键电平变化时调用。将curr_button与button_io_mask进行与操作(只关心使能的IO),
// 然后启动防抖定时器(10ms后检查)。
void button_toggle_detected(uint32_t curr_button)
{if (button_io_mask != 0){curr_button_before_anti_shake = curr_button & button_io_mask;os_timer_start(&button_anti_shake_timer, 10, false);}
}// 按键中断服务函数。计算当前按键状态(current_pressed_button与changed_button异或),
// 然后向按键任务发送BUTTON_TOGGLE事件。
void button_int_isr(uint32_t changed_button)
{uint32_t curr_button;os_event_t toggle_event;curr_button = current_pressed_button ^ changed_button;toggle_event.event_id = BUTTON_TOGGLE;toggle_event.param = (void *)&curr_button;toggle_event.param_len = sizeof(uint32_t);os_msg_post(button_task_id, &toggle_event);
}// 构造一个按键消息(button_msg_t),然后以USER_EVT_BUTTON事件发送给user_task
void button_send_event(uint8_t event, uint32_t button, uint8_t cnt)
{os_event_t button_event;struct button_msg_t msg;msg.button_index = button;msg.button_type = event;msg.button_cnt = cnt;button_event.event_id = USER_EVT_BUTTON;button_event.src_task_id = button_task_id;button_event.param = (void *)&msg;button_event.param_len = sizeof(msg);os_msg_post(user_task_id, &button_event);pressed_cnt = 0;
}// 状态处理函数
static void button_idle(uint8_t event)
{if (event == BUTTON_WORKING_EVENT_SINGLE_PRESSED){current_state = BUTTON_WORKING_STATE_JUST_PRESSED;os_timer_start(&button_state_timer, BUTTON_SHORT_DURING * 10, false);button_send_event(BUTTON_PRESSED, current_pressed_button, pressed_cnt);}else if (event == BUTTON_WORKING_EVENT_COMB_PRESSED){current_state = BUTTON_WORKING_STATE_COMB_JUST_PRESSED;os_timer_start(&button_state_timer, BUTTON_SHORT_DURING * 10, false);button_send_event(BUTTON_COMB_PRESSED, current_pressed_button, pressed_cnt);}
}static void button_just_pressed(uint8_t event)
{if (event == BUTTON_WORKING_EVENT_RELEASED){current_state = BUTTON_WORKING_STATE_IDLE;os_timer_stop(&button_state_timer);button_send_event(BUTTON_RELEASED, last_saved_button, pressed_cnt);}else if (event == BUTTON_WORKING_EVENT_COMB_PRESSED){current_state = BUTTON_WORKING_STATE_COMB_JUST_PRESSED;os_timer_start(&button_state_timer, BUTTON_SHORT_DURING * 10, false);button_send_event(BUTTON_COMB_PRESSED, current_pressed_button, pressed_cnt);}else if (event == BUTTON_WORKING_EVENT_TIME_OUT){current_state = BUTTON_WORKING_STATE_PRESSED;os_timer_start(&button_state_timer, (BUTTON_LONG_DURING * 10 - BUTTON_SHORT_DURING) * 10, false);}
}static void button_pressed(uint8_t event)
{if (event == BUTTON_WORKING_EVENT_RELEASED){if (0 /*__jump_table.button_disable_multi_click*/ & last_saved_button){current_state = BUTTON_WORKING_STATE_IDLE;button_send_event(BUTTON_SHORT_PRESSED, last_saved_button, 0);}else{// TBD���Ƿ����������current_state = BUTTON_WORKING_STATE_WAIT_MULTI;button_to_be_send = last_saved_button;pressed_cnt++;os_timer_start(&button_state_timer, BUTTON_MULTI_INTERVAL * 10, false);}}else if (event == BUTTON_WORKING_EVENT_COMB_PRESSED){current_state = BUTTON_WORKING_STATE_COMB_JUST_PRESSED;os_timer_start(&button_state_timer, BUTTON_SHORT_DURING * 10, false);button_send_event(BUTTON_COMB_PRESSED, current_pressed_button, pressed_cnt);}else if (event == BUTTON_WORKING_EVENT_TIME_OUT){current_state = BUTTON_WORKING_STATE_LONG_PRESSED;os_timer_start(&button_state_timer, ((BUTTON_LONG_LONG_DURING - BUTTON_LONG_DURING) * 10) * 10, false);os_timer_start(&button_pressing_timer, BUTTON_LONG_PRESSING_INTERVAL * 10, false);button_send_event(BUTTON_LONG_PRESSED, current_pressed_button, pressed_cnt);}
}static void button_wait_multi(uint8_t event)
{if (event == BUTTON_WORKING_EVENT_SINGLE_PRESSED){if (current_pressed_button != button_to_be_send){if (pressed_cnt > 1){button_send_event(BUTTON_MULTI_PRESSED, button_to_be_send, pressed_cnt);}else{button_send_event(BUTTON_SHORT_PRESSED, button_to_be_send, pressed_cnt);}button_send_event(BUTTON_PRESSED, current_pressed_button, pressed_cnt);}current_state = BUTTON_WORKING_STATE_JUST_PRESSED;os_timer_start(&button_state_timer, BUTTON_SHORT_DURING * 10, false);}else if (event == BUTTON_WORKING_EVENT_COMB_PRESSED){current_state = BUTTON_WORKING_STATE_COMB_JUST_PRESSED;os_timer_start(&button_state_timer, BUTTON_SHORT_DURING * 10, false);button_send_event(BUTTON_COMB_PRESSED, current_pressed_button, pressed_cnt);button_send_event(BUTTON_SHORT_PRESSED, button_to_be_send, pressed_cnt);}else if (event == BUTTON_WORKING_EVENT_TIME_OUT){current_state = BUTTON_WORKING_STATE_IDLE;if (pressed_cnt > 1){button_send_event(BUTTON_MULTI_PRESSED, button_to_be_send, pressed_cnt);}else{button_send_event(BUTTON_SHORT_PRESSED, button_to_be_send, pressed_cnt);}}
}static void button_long_pressed(uint8_t event)
{if (event == BUTTON_WORKING_EVENT_RELEASED){current_state = BUTTON_WORKING_STATE_IDLE;os_timer_stop(&button_state_timer);os_timer_stop(&button_pressing_timer);button_send_event(BUTTON_LONG_RELEASED, last_saved_button, pressed_cnt);}else if (event == BUTTON_WORKING_EVENT_COMB_PRESSED){current_state = BUTTON_WORKING_STATE_COMB_JUST_PRESSED;os_timer_start(&button_state_timer, BUTTON_SHORT_DURING * 10, false);os_timer_stop(&button_pressing_timer);button_send_event(BUTTON_COMB_PRESSED, current_pressed_button, pressed_cnt);button_send_event(BUTTON_LONG_RELEASED, last_saved_button, pressed_cnt);}else if (event == BUTTON_WORKING_EVENT_TIME_OUT){current_state = BUTTON_WORKING_STATE_LONG_LONG_PRESSED;button_send_event(BUTTON_LONG_LONG_PRESSED, current_pressed_button, pressed_cnt);}
}static void button_long_long_pressed(uint8_t event)
{if (event == BUTTON_WORKING_EVENT_RELEASED){os_timer_stop(&button_pressing_timer);current_state = BUTTON_WORKING_STATE_IDLE;button_send_event(BUTTON_LONG_LONG_RELEASED, last_saved_button, pressed_cnt);}else if (event == BUTTON_WORKING_EVENT_COMB_PRESSED){current_state = BUTTON_WORKING_STATE_COMB_JUST_PRESSED;os_timer_start(&button_state_timer, BUTTON_SHORT_DURING * 10, false);os_timer_stop(&button_pressing_timer);button_send_event(BUTTON_COMB_PRESSED, current_pressed_button, pressed_cnt);button_send_event(BUTTON_LONG_LONG_RELEASED, last_saved_button, pressed_cnt);}
}static void button_comb_just_pressed(uint8_t event)
{if (event == BUTTON_WORKING_EVENT_RELEASED){current_state = BUTTON_WORKING_STATE_IDLE;os_timer_stop(&button_state_timer);button_send_event(BUTTON_COMB_RELEASED, last_saved_button, pressed_cnt);}else if (event == BUTTON_WORKING_EVENT_SINGLE_PRESSED){current_state = BUTTON_WORKING_STATE_JUST_PRESSED;os_timer_start(&button_state_timer, BUTTON_SHORT_DURING * 10, false);button_send_event(BUTTON_COMB_RELEASED, last_saved_button, pressed_cnt);button_send_event(BUTTON_PRESSED, current_pressed_button, pressed_cnt);}else if (event == BUTTON_WORKING_EVENT_COMB_PRESSED){current_state = BUTTON_WORKING_STATE_JUST_PRESSED;os_timer_start(&button_state_timer, BUTTON_SHORT_DURING * 10, false);button_send_event(BUTTON_COMB_PRESSED, current_pressed_button, pressed_cnt);}else if (event == BUTTON_WORKING_EVENT_TIME_OUT){current_state = BUTTON_WORKING_STATE_COMB_PRESSED;os_timer_start(&button_state_timer, (BUTTON_LONG_DURING * 10 - BUTTON_SHORT_DURING) * 10, false);}
}static void button_comb_pressed(uint8_t event)
{if (event == BUTTON_WORKING_EVENT_RELEASED){current_state = BUTTON_WORKING_STATE_IDLE;os_timer_stop(&button_state_timer);button_send_event(BUTTON_COMB_SHORT_PRESSED, last_saved_button, pressed_cnt);}else if (event == BUTTON_WORKING_EVENT_SINGLE_PRESSED){current_state = BUTTON_WORKING_STATE_JUST_PRESSED;os_timer_start(&button_state_timer, BUTTON_SHORT_DURING * 10, false);button_send_event(BUTTON_COMB_SHORT_PRESSED, last_saved_button, pressed_cnt);button_send_event(BUTTON_PRESSED, current_pressed_button, pressed_cnt);}else if (event == BUTTON_WORKING_EVENT_COMB_PRESSED){current_state = BUTTON_WORKING_STATE_COMB_JUST_PRESSED;os_timer_start(&button_state_timer, BUTTON_SHORT_DURING * 10, false);button_send_event(BUTTON_COMB_SHORT_PRESSED, last_saved_button, pressed_cnt);button_send_event(BUTTON_COMB_PRESSED, current_pressed_button, pressed_cnt);}else if (event == BUTTON_WORKING_EVENT_TIME_OUT){current_state = BUTTON_WORKING_STATE_COMB_LONG_PRESSED;os_timer_start(&button_state_timer, ((BUTTON_LONG_LONG_DURING - BUTTON_LONG_DURING) * 10) * 10, false);os_timer_start(&button_pressing_timer, BUTTON_LONG_PRESSING_INTERVAL * 10, false);button_send_event(BUTTON_COMB_LONG_PRESSED, current_pressed_button, pressed_cnt);}
}static void button_comb_long_pressed(uint8_t event)
{if (event == BUTTON_WORKING_EVENT_RELEASED){current_state = BUTTON_WORKING_STATE_IDLE;os_timer_stop(&button_state_timer);os_timer_stop(&button_pressing_timer);button_send_event(BUTTON_COMB_LONG_RELEASED, last_saved_button, pressed_cnt);}else if (event == BUTTON_WORKING_EVENT_SINGLE_PRESSED){current_state = BUTTON_WORKING_STATE_JUST_PRESSED;os_timer_start(&button_state_timer, BUTTON_SHORT_DURING * 10, false);os_timer_stop(&button_pressing_timer);button_send_event(BUTTON_COMB_LONG_RELEASED, last_saved_button, pressed_cnt);button_send_event(BUTTON_PRESSED, current_pressed_button, pressed_cnt);}else if (event == BUTTON_WORKING_EVENT_COMB_PRESSED){current_state = BUTTON_WORKING_STATE_COMB_JUST_PRESSED;os_timer_start(&button_state_timer, BUTTON_SHORT_DURING * 10, false);os_timer_stop(&button_pressing_timer);button_send_event(BUTTON_COMB_LONG_RELEASED, last_saved_button, pressed_cnt);button_send_event(BUTTON_COMB_PRESSED, current_pressed_button, pressed_cnt);}else if (event == BUTTON_WORKING_EVENT_TIME_OUT){current_state = BUTTON_WORKING_STATE_COMB_LONG_LONG_PRESSED;button_send_event(BUTTON_COMB_LONG_LONG_PRESSED, current_pressed_button, pressed_cnt);}
}static void button_comb_long_long_pressed(uint8_t event)
{if (event == BUTTON_WORKING_EVENT_RELEASED){current_state = BUTTON_WORKING_STATE_IDLE;os_timer_stop(&button_pressing_timer);button_send_event(BUTTON_COMB_LONG_LONG_RELEASED, last_saved_button, pressed_cnt);}else if (event == BUTTON_WORKING_EVENT_SINGLE_PRESSED){current_state = BUTTON_WORKING_STATE_JUST_PRESSED;os_timer_start(&button_state_timer, BUTTON_SHORT_DURING * 10, false);os_timer_stop(&button_pressing_timer);button_send_event(BUTTON_COMB_LONG_LONG_RELEASED, last_saved_button, pressed_cnt);button_send_event(BUTTON_PRESSED, current_pressed_button, pressed_cnt);}else if (event == BUTTON_WORKING_EVENT_COMB_PRESSED){current_state = BUTTON_WORKING_STATE_COMB_JUST_PRESSED;os_timer_start(&button_state_timer, BUTTON_SHORT_DURING * 10, false);os_timer_stop(&button_pressing_timer);button_send_event(BUTTON_COMB_LONG_LONG_RELEASED, last_saved_button, pressed_cnt);button_send_event(BUTTON_COMB_PRESSED, current_pressed_button, pressed_cnt);}
}// 状态机数组button_statemachines将状态枚举与对应的处理函数绑定
void (*const button_statemachines[BUTTON_WORKING_STATE_MAX])(uint8_t) ={button_idle,button_just_pressed,button_pressed,button_wait_multi,button_long_pressed,button_long_long_pressed,button_comb_just_pressed,button_comb_pressed,button_comb_long_pressed,button_comb_long_long_pressed,
};// 处理BUTTON_TOGGLE事件。它比较当前按键状态与上一次保存的状态,根据变化情况(释放、单键按下、组合键按下)产生事件,
// 并调用当前状态的处理函数。
// one or more button is released or pressed
static int button_toggle_handler(uint32_t curr_button)
{enum button_working_event_t event;current_pressed_button = curr_button;if (last_saved_button != current_pressed_button){if (current_pressed_button == 0){event = BUTTON_WORKING_EVENT_RELEASED;}else{if ((current_pressed_button & (current_pressed_button - 1)) == 0){event = BUTTON_WORKING_EVENT_SINGLE_PRESSED;}else{event = BUTTON_WORKING_EVENT_COMB_PRESSED;}}button_statemachines[current_state](event);last_saved_button = current_pressed_button;}return EVT_CONSUMED;
}// 状态定时器超时,调用当前状态的处理函数并传递超时事件(BUTTON_WORKING_EVENT_TIME_OUT)
static void button_timeout_handler(void *param)
{button_statemachines[current_state](BUTTON_WORKING_EVENT_TIME_OUT);
}// 长按连发定时器超时,发送长按连发事件(BUTTON_LONG_PRESSING或BUTTON_COMB_LONG_PRESSING),
// 并重启定时器(周期触发)。
static void button_pressing_timeout_handler(void *param)
{enum button_type_t event;if ((current_pressed_button & (current_pressed_button - 1)) == 0){event = BUTTON_LONG_PRESSING;}else{event = BUTTON_COMB_LONG_PRESSING;}button_send_event(event, current_pressed_button, pressed_cnt);os_timer_start(&button_pressing_timer, BUTTON_LONG_PRESSING_INTERVAL * 10, false);
}// 防抖定时器超时,读取硬件按键状态,如果与防抖前保存的状态一致,则发送BUTTON_TOGGLE事件(表示稳定状态)。
static void button_anti_shake_timeout_handler(void *param)
{uint32_t curr_button;os_event_t toggle_event;curr_button = ool_read32(PMU_REG_GPIOA_V);curr_button &= button_io_mask;if (curr_button == curr_button_before_anti_shake){curr_button ^= button_io_mask;toggle_event.event_id = BUTTON_TOGGLE;toggle_event.param = (void *)&curr_button;toggle_event.param_len = sizeof(uint32_t);os_msg_post(button_task_id, &toggle_event);}
}// 按键任务的处理函数,目前只处理BUTTON_TOGGLE事件,调用button_toggle_handler
static int button_task_func(os_event_t *event)
{switch (event->event_id){case BUTTON_TOGGLE:button_toggle_handler(*(uint32_t *)event->param);break;}return EVT_CONSUMED;
}// 按键初始化函数,设置使能的IO掩码,创建按键任务,初始化三个定时器。
void button_init(uint32_t enable_io)
{button_io_mask = enable_io;button_task_id = os_task_create(button_task_func);os_timer_init(&button_anti_shake_timer, button_anti_shake_timeout_handler, NULL);os_timer_init(&button_pressing_timer, button_pressing_timeout_handler, NULL);os_timer_init(&button_state_timer, button_timeout_handler, NULL);
}
11. 测试示例
user_task.h
#ifndef _USER_TASK_H
#define _USER_TASK_Henum user_event_t {USER_EVT_AT_COMMAND,USER_EVT_BUTTON,
};extern uint16_t user_task_id;void user_task_init(void);#endif // _USER_TASK_H
user_task.c
#include <stdint.h>#include "os_task.h"
#include "os_msg_q.h"#include "co_printf.h"
#include "user_task.h"
#include "button.h"
#include "driver_gpio.h"
#include "driver_iomux.h"
#include "driver_exti.h"
#include "driver_system.h"
#include "sys_utils.h"uint16_t user_task_id;static int user_task_func(os_event_t *param)
{switch (param->event_id){case USER_EVT_BUTTON:{struct button_msg_t *button_msg;const char *button_type_str[] = {"BUTTON_PRESSED","BUTTON_RELEASED","BUTTON_SHORT_PRESSED","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("KEY 0x%08x, TYPE %s\r\n", button_msg->button_index, button_type_str[button_msg->button_type]);}break;}return EVT_CONSUMED;
}void user_task_init(void)
{user_task_id = os_task_create(user_task_func);
}
proj_main.c文件中添加一下函数定义
__attribute__((section("ram_code"))) void pmu_gpio_isr_ram(void)
{uint32_t gpio_value = ool_read32(PMU_REG_GPIOA_V);button_toggle_detected(gpio_value);// co_printf(" %s gpio_value: %x\r\n", __func__, gpio_value);ool_write32(PMU_REG_PORTA_LAST, gpio_value);
}
proj_main.c文件中的user_entry_after_ble_init内进行初始化
void user_entry_after_ble_init(void)
{co_printf("user_entry_after_ble_init\r\n");#if 1
//stop sleepsystem_sleep_disable();
#elseif(__jump_table.system_option & SYSTEM_OPTION_SLEEP_ENABLE){co_printf("\r\na");co_delay_100us(10000); //must keep it, or pressing reset key may block .co_printf("\r\nb");co_delay_100us(10000);co_printf("\r\nc");co_delay_100us(10000);co_printf("\r\nd");}
#endifgap_adv_param_t adv_param;adv_param.adv_mode = GAP_ADV_MODE_UNDIRECT;adv_param.adv_addr_type = GAP_ADDR_TYPE_PUBLIC;adv_param.adv_chnl_map = GAP_ADV_CHAN_ALL;adv_param.adv_filt_policy = GAP_ADV_ALLOW_SCAN_ANY_CON_ANY;adv_param.adv_intv_min = 1600;adv_param.adv_intv_max = 1600;gap_set_advertising_param(&adv_param);uint8_t adv_data[]="\x09\x08\x46\x52\x38\x30\x31\x30\x48\x00";uint8_t rsp_data[]="\x09\xFF\x00\x60\x52\x57\x2D\x42\x4C\x45";gap_set_advertising_data(adv_data,sizeof(adv_data) -1 );gap_set_advertising_rsp_data(rsp_data,sizeof(rsp_data) -1 );#if 0gap_start_advertising(0);
#endif// 用户任务初始化user_task_init();// button初始化pmu_set_pin_pull(GPIO_PORT_C, (1 << GPIO_BIT_5), true);pmu_port_wakeup_func_set(GPIO_PC5);button_init(GPIO_PC5);}
测试结果
KEY 0x00200000, TYPE BUTTON_PRESSED
KEY 0x00200000, TYPE BUTTON_SHORT_PRESSED
KEY 0x00200000, TYPE BUTTON_PRESSED
KEY 0x00200000, TYPE BUTTON_MULTI_PRESSED
KEY 0x00200000, TYPE BUTTON_PRESSED
KEY 0x00200000, TYPE BUTTON_LONG_PRESSED
KEY 0x00200000, TYPE BUTTON_LONG_RELEASED
KEY 0x00200000, TYPE BUTTON_PRESSED
KEY 0x00200000, TYPE BUTTON_LONG_PRESSED
KEY 0x00200000, TYPE BUTTON_LONG_PRESSING
KEY 0x00200000, TYPE BUTTON_LONG_PRESSING
KEY 0x00200000, TYPE BUTTON_LONG_PRESSING
KEY 0x00200000, TYPE BUTTON_LONG_PRESSING
KEY 0x00200000, TYPE BUTTON_LONG_PRESSING
KEY 0x00200000, TYPE BUTTON_LONG_PRESSING
KEY 0x00200000, TYPE BUTTON_LONG_LONG_PRESSED
KEY 0x00200000, TYPE BUTTON_LONG_PRESSING
KEY 0x00200000, TYPE BUTTON_LONG_PRESSING
KEY 0x00200000, TYPE BUTTON_LONG_PRESSING
KEY 0x00200000, TYPE BUTTON_LONG_PRESSING
KEY 0x00200000, TYPE BUTTON_LONG_PRESSING
KEY 0x00200000, TYPE BUTTON_LONG_PRESSING
KEY 0x00200000, TYPE BUTTON_LONG_PRESSING
KEY 0x00200000, TYPE BUTTON_LONG_PRESSING
KEY 0x00200000, TYPE BUTTON_LONG_LONG_RELEASED
12. 附录
下载:单击 双击 长按 超长按和组件按键模块参考源码.rar