本文在蓝牙从机例程上添加tmos任务来实现一些按键操作,如短按,长按和双击。

源文件:

/*
 * button_tmos.c
 *
 *  Created on: 2025年9月30日
 *      Author: 14646
 */
#include "button_tmos.h"

// 全局变量
uint8_t tmos_TaskID = INVALID_TASK_ID;       // TMOS任务ID
uint16_t press_duration = 0;                 // 按键按下持续时间(ms)
uint8_t click_count = 0;                     // 短按计数(0:无点击,1:单击待确认,2:双击)
uint8_t is_pressed = 0;                      // 按键当前状态(0:未按下,1:按下)

// TMOS事件处理函数
uint16_t tmos_ProcessEvent(uint8_t task_id, uint16_t events)
{
    // 系统消息事件处理(必须保留,TMOS框架要求)
    if(events & SYS_EVENT_MSG)
    {
        uint8_t *pMsg;
        if((pMsg = tmos_msg_receive(tmos_TaskID)) != NULL)
        {
            tmos_msg_deallocate(pMsg);  // 释放消息内存
        }
        return (events ^ SYS_EVENT_MSG);  // 清除已处理事件
    }

    // 按键扫描事件:周期性检测按键状态变化(按下/松开)
    if(events & BUTTON_SCAN_EVT)
    {
        uint32_t current_state = GPIOB_ReadPortPin(BUTTON_KEY);  // 32位状态变量

        // 检测到按键按下(从"未按下"变为"按下")
        if(current_state == 0 && !is_pressed)
        {
            is_pressed = 1;               // 标记为按下状态
            press_duration = 0;           // 重置按下时长计数
            // 启动计时事件,持续累加按下时间
            tmos_start_task(tmos_TaskID, BUTTON_HOLD_EVT, SCAN_INTERVAL);
        }

        // 重新启动扫描事件,保证周期性检测
        tmos_start_task(tmos_TaskID, BUTTON_SCAN_EVT, SCAN_INTERVAL);
        return (events ^ BUTTON_SCAN_EVT);  // 清除已处理事件
    }

    // 按键按住计时事件:累加按下时长,直到按键松开
    if(events & BUTTON_HOLD_EVT)
    {
        uint32_t current_state = GPIOB_ReadPortPin(BUTTON_KEY);  // 32位状态变量

        // 按键仍处于按下状态:继续累加时长
        if(current_state == 0 && is_pressed)
        {
            press_duration += SCAN_INTERVAL;  // 累加按下时间(每次间隔SCAN_INTERVAL)
            // 继续启动计时事件,保持计数
            tmos_start_task(tmos_TaskID, BUTTON_HOLD_EVT, SCAN_INTERVAL);
        }
        // 按键已松开(从"按下"变为"松开"):此时判定长按/短按
        else if(current_state != 0 && is_pressed)
        {
            is_pressed = 0;  // 标记为松开状态

            // 判定为长按:松开时按下时长≥长按阈值
            if(press_duration >= LONG_PRESS_MIN)
            {
                PRINT("long press (released)\n");  // 长按松开后触发
                click_count = 0;  // 长按后重置点击计数,避免干扰双击逻辑
            }
            // 判定为短按:松开时按下时长<短按阈值(参与双击计数)
            else if(press_duration < SHORT_PRESS_MAX)
            {
                click_count++;  // 短按计数+1
                if(click_count == 1)
                {
                    // 第一次短按:启动双击超时检测,等待第二次短按
                    tmos_start_task(tmos_TaskID, DOUBLE_CLICK_EVT, DOUBLE_CLICK_MAX);
                }
                else if(click_count == 2)
                {
                    // 第二次短按(在超时前):判定为双击
                    PRINT("double click\n");
                    click_count = 0;  // 重置计数
                    tmos_stop_task(tmos_TaskID, DOUBLE_CLICK_EVT);  // 停止超时检测
                }
            }
            // 中间状态(按下时长在SHORT_PRESS_MAX~LONG_PRESS_MIN之间):视为无效操作
            else
            {
                click_count = 0;  // 重置计数,避免干扰后续判断
            }
        }
        return (events ^ BUTTON_HOLD_EVT);  // 清除已处理事件
    }

    // 双击超时事件:超过最大间隔未检测到第二次短按,判定为单击
    if(events & DOUBLE_CLICK_EVT)
    {
        if(click_count == 1)
        {
            PRINT("single click\n");  // 输出单击
        }
        click_count = 0;  // 重置计数
        return (events ^ DOUBLE_CLICK_EVT);  // 清除已处理事件
    }

    // 未处理的事件(返回0表示全部处理完毕)
    return 0;
}

// 按键GPIO初始化
void button_gpio_init()
{
    // 配置按键引脚为上拉输入(根据硬件实际情况调整,此处为示例)
    GPIOB_ModeCfg(BUTTON_KEY, GPIO_ModeIN_PU);
}

// 按键TMOS任务初始化
void button_tmos_init()
{
    // 注册事件处理函数,获取任务ID
    tmos_TaskID = TMOS_ProcessEventRegister(tmos_ProcessEvent);
    // 启动按键扫描事件(立即开始,间隔SCAN_INTERVAL)
    tmos_start_task(tmos_TaskID, BUTTON_SCAN_EVT, SCAN_INTERVAL);
    PRINT("button_tmos_init complete\n");
}

头文件:

/*
 * button_tmos.h
 *
 *  Created on: 2025年9月30日
 *      Author: 14646
 */
#include "CONFIG.h"
#ifndef BUTTON_TMOS_H_
#define BUTTON_TMOS_H_
// 按键引脚定义
#define BUTTON_KEY    GPIO_Pin_22

// 事件定义
#define BUTTON_SCAN_EVT  0x0001   // 按键扫描事件(周期性检测按键状态)
#define BUTTON_HOLD_EVT  0x0002   // 按键按住计时事件(累加按下时长)
#define DOUBLE_CLICK_EVT 0x0004   // 双击超时检测事件(判断是否为双击)

// 时间参数配置(单位:ms,可根据实际需求调整)
#define SCAN_INTERVAL    MS1_TO_SYSTEM_TIME(20)       // 按键扫描间隔(20ms一次,平衡响应速度和资源占用)
#define SHORT_PRESS_MAX  MS1_TO_SYSTEM_TIME(500)      // 短按最大时长(超过此值不算短按,不参与双击)
#define LONG_PRESS_MIN   MS1_TO_SYSTEM_TIME(1000)     // 长按最小时长(松开后判定为长按的阈值)
#define DOUBLE_CLICK_MAX MS1_TO_SYSTEM_TIME(300)      // 双击最大间隔(两次短按松开的间隔超过此值则判定为单击)

extern void button_gpio_init();

extern void button_tmos_init();

#endif /* BUTTON_TMOS_H_ */

在主函数中只要调用button_gpio_init();和button_tmos_init();即可,button_tmos_init();需在蓝牙协议栈初始化后调用。

int main(void)
{
#if(defined(DCDC_ENABLE)) && (DCDC_ENABLE == TRUE)
    PWR_DCDCCfg(ENABLE);
#endif
    SetSysClock(CLK_SOURCE_PLL_60MHz);
#if(defined(HAL_SLEEP)) && (HAL_SLEEP == TRUE)
    GPIOA_ModeCfg(GPIO_Pin_All, GPIO_ModeIN_PU);
    GPIOB_ModeCfg(GPIO_Pin_All, GPIO_ModeIN_PU);
#endif
#ifdef DEBUG
    GPIOA_SetBits(bTXD1);
    GPIOA_ModeCfg(bTXD1, GPIO_ModeOut_PP_5mA);
    UART1_DefInit();
#endif
    button_gpio_init();

    PRINT("%s\n", VER_LIB);
    CH59x_BLEInit();
    HAL_Init();
    GAPRole_PeripheralInit();
    Peripheral_Init();
    button_tmos_init();
    Main_Circulation();
}

打印输出结果:

wechat_2025-09-30_172605_337

 

posted on 2025-09-30 17:27  WCH蓝牙应用分享  阅读(453)  评论(1)    收藏  举报