【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功能框图

【FR801xH】富芮坤FR801xH之全功能按键案例_第1张图片

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 非组合按键状态图

【FR801xH】富芮坤FR801xH之全功能按键案例_第2张图片

参考一:

状态机流程

状态机有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. 事件处理流程

【FR801xH】富芮坤FR801xH之全功能按键案例_第3张图片

10. 程序实现参考源码

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);
}

11. 测试示例

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

12. 附录

下载:单击 双击 长按 超长按和组件按键模块参考源码.rar

你可能感兴趣的:(物联网专栏,富芮坤,FR801xH,按键,单击,双击,长按,超长按)