当前位置: 首页 > news >正文

【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模块系统架构概览

基于状态机的按键驱动系统,采用分层设计:

  1. 硬件抽象层:GPIO中断和寄存器访问
  2. 驱动核心层:状态机引擎和定时器管理
  3. 应用接口层:事件消息传递机制

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 状态转移矩阵

当前状态触发事件下一状态执行动作
IDLESINGLE_PRESSEDJUST_PRESSED启动短按计时器(80ms),发送BUTTON_PRESSED事件
JUST_PRESSEDRELEASEDIDLE发送BUTTON_RELEASED事件
JUST_PRESSEDTIMEOUT(80ms)PRESSED启动长按计时器(1920ms)
PRESSEDRELEASEDWAIT_MULTI启动连击检测计时器(200ms),增加连击计数
PRESSEDTIMEOUT(2000ms)LONG_PRESSED启动超长按计时器(2000ms)和连发定时器(300ms),发送BUTTON_LONG_PRESSED事件
WAIT_MULTISINGLE_PRESSEDJUST_PRESSED启动短按计时器(80ms),发送BUTTON_PRESSED事件
WAIT_MULTITIMEOUT(200ms)IDLE发送BUTTON_SHORT_PRESSED或BUTTON_MULTI_PRESSED事件
LONG_PRESSEDRELEASEDIDLE停止所有定时器,发送BUTTON_LONG_RELEASED事件
LONG_PRESSEDTIMEOUT(4000ms)LONG_LONG_PRESSED发送BUTTON_LONG_LONG_PRESSED事件
LONG_LONG_PRESSEDRELEASEDIDLE发送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

http://www.dtcms.com/a/269617.html

相关文章:

  • JVM系列六:JVM性能调优实战指南
  • Java基础回顾(1)
  • 7 种简单方法将三星文件传输到电脑
  • 瞄准Win10难民,苹果正推出塑料外壳、手机CPU的MacBook
  • 用户生命周期与改进型RFM模型
  • C#读取modbus值,C#读写modbus,支持读写uint32值,Modbus TCP工具类
  • HTTPS工作原理
  • java获取文件的消息摘要APP进行文件完整性校验
  • JavaScript基础篇——第二章 类型转换与常见错误解析
  • 二分查找篇——搜索二维矩阵【LeetCode】遍历法
  • qt-C++笔记之setCentralWidget的使用
  • Visual Studio Code 中统一配置文件在团队协作中的应用
  • 论文略读:Prefix-Tuning: Optimizing Continuous Prompts for Generation
  • Git 安装避坑指南:从环境检查到高级配置的全流程解析
  • EXCEL转html,含图片
  • Linux下SPHinXsys源码编译安装及使用
  • Flutter基础(前端教程③-跳转)
  • Wend看源码-RAGFlow(上)
  • nvm npm nrm 使用教程
  • 台式电脑如何连wifi 快速连接方法
  • synchronized 的使用和特性
  • 算法学习笔记:11.冒泡排序——从原理到实战,涵盖 LeetCode 与考研 408 例题
  • VBA经典应用69例应用8:取消预设任务
  • (三)C#使用yolo
  • 在教育领域中,如何通过VRM分片错序对视频进行加密?
  • git学习:首次创建仓库
  • ubuntu 运行脚本打开WIFI adb
  • YOLO在自动驾驶交通标志识别中的应用与优化【附代码】
  • Qt:图片切割
  • 代码详细注释:演示如何使用dup()系统调用复制文件描述符