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 集成在一块芯片中,为客户提供:
该按键驱动通过状态机实现了丰富的按键事件检测,包括单击、双击(多击)、长按、超长按、组合按键等。使用定时器进行防抖和超时检测,通过任务队列处理事件,避免在中断中处理复杂逻辑。
该按键驱动采用分层状态机 + 定时器调度架构,实现以下功能:
基于状态机的按键驱动系统,采用分层设计:
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; // 连击次数
};
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):
(2) 刚按下状态(BUTTON_WORKING_STATE_JUST_PRESSED):
(3) 已按下状态(BUTTON_WORKING_STATE_PRESSED):
(4) 等待连击状态(BUTTON_WORKING_STATE_WAIT_MULTI):
(5) 长按状态(BUTTON_WORKING_STATE_LONG_PRESSED):
(6) 超长按状态(BUTTON_WORKING_STATE_LONG_LONG_PRESSED):
(7) 组合刚按下状态(BUTTON_WORKING_STATE_COMB_JUST_PRESSED):
(8) 组合键已按下状态(BUTTON_WORKING_STATE_COMB_PRESSED):
(9) 组合键长按状态(BUTTON_WORKING_STATE_COMB_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事件 |
定时器 | 类型 | 作用 | 默认时间 | 触发机制 |
---|---|---|---|---|
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);
button.h
#ifndef _BUTTON_H
#define _BUTTON_H
#include
// 事件类型
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
#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);
}
user_task.h
#ifndef _USER_TASK_H
#define _USER_TASK_H
enum 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
#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 sleep
system_sleep_disable();
#else
if(__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");
}
#endif
gap_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 0
gap_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
下载:单击 双击 长按 超长按和组件按键模块参考源码.rar