Button press & debounce

本文探讨了在电子系统中处理按键输入时的消抖技术,通过分析原始代码流程,展示了如何在中断服务函数和事件处理函数中实现按钮按下事件的处理,确保稳定可靠的信号检测。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、先看下原始代码流程,按键1中断进入函数:

void usr_button1_cb(void)
{    ke_evt_set(1UL << EVENT_BUTTON1_PRESS_ID);
}
建立事件 EVENT_BUTTON1_PRESS_ID


而在事件初始化里定义了EVENT_BUTTON1_PRESS_ID 对应的函数是app_event_button1_press_handler:

    if(KE_EVENT_OK != ke_evt_callback_set(EVENT_BUTTON1_PRESS_ID, 
                                            app_event_button1_press_handler))
    {
        ASSERT_ERR(0);
    }

所以按键1的处理函数是app_event_button1_press_handler

void app_event_button1_press_handler(void)
{
#if ((QN_DEEP_SLEEP_EN) && (!QN_32K_RCO))
    if (sleep_env.deep_sleep)
    {
        sleep_env.deep_sleep = false;
        // start 32k xtal wakeup timer
        wakeup_32k_xtal_start_timer();
    }
#endif

    // delay 20ms to debounce
    ke_timer_set(APP_SYS_BUTTON_1_TIMER, TASK_APP, 2);
    ke_evt_clear(1UL << EVENT_BUTTON1_
这是我的button.h和button.c文件 /** * @File: flexible_button.h * @Author: MurphyZhao * @Date: 2018-09-29 * * Copyright (c) 2018-2019 MurphyZhao &lt;d2014zjt@163.com&gt; * https://github.com/murphyzhao * All rights reserved. * License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the &quot;License&quot;); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an &quot;AS IS&quot; BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Change logs: * Date Author Notes * 2018-09-29 MurphyZhao First add * 2019-08-02 MurphyZhao Migrate code to github.com/murphyzhao account * 2019-12-26 MurphyZhao Refactor code and implement multiple clicks * */ #ifndef __BUTTON_H__ #define __BUTTON_H__ #include &quot;stdint.h&quot; #include &quot;key_app.h&quot; #include &quot;KEY.h&quot; #define FLEX_BTN_SCAN_FREQ_HZ 50 // How often flex_button_scan () is called #define FLEX_MS_TO_SCAN_CNT(ms) (ms / (1000 / FLEX_BTN_SCAN_FREQ_HZ)) /* Multiple clicks interval, default 300ms */ #define MAX_MULTIPLE_CLICKS_INTERVAL (FLEX_MS_TO_SCAN_CNT(300)) /** * @brief 按钮事件枚举类型 * * 定义了各种按钮触发事件类型,包括: * - 按下(FLEX_BTN_PRESS_DOWN) * - 单击(FLEX_BTN_PRESS_CLICK) * - 双击(FLEX_BTN_PRESS_DOUBLE_CLICK) * - 重复点击(FLEX_BTN_PRESS_REPEAT_CLICK) * - 短按开始(FLEX_BTN_PRESS_SHORT_START) * - 短按抬起(FLEX_BTN_PRESS_SHORT_UP) * - 长按开始(FLEX_BTN_PRESS_LONG_START) * - 长按抬起(FLEX_BTN_PRESS_LONG_UP) * - 长按保持(FLEX_BTN_PRESS_LONG_HOLD) * - 长按保持后抬起(FLEX_BTN_PRESS_LONG_HOLD_UP) * - 最大值(FLEX_BTN_PRESS_MAX) * - 无事件(FLEX_BTN_PRESS_NONE) */ typedef enum { FLEX_BTN_PRESS_DOWN = 0, FLEX_BTN_PRESS_CLICK, FLEX_BTN_PRESS_DOUBLE_CLICK, FLEX_BTN_PRESS_REPEAT_CLICK, FLEX_BTN_PRESS_SHORT_START, FLEX_BTN_PRESS_SHORT_UP, FLEX_BTN_PRESS_LONG_START, FLEX_BTN_PRESS_LONG_UP, FLEX_BTN_PRESS_LONG_HOLD, FLEX_BTN_PRESS_LONG_HOLD_UP, FLEX_BTN_PRESS_MAX, FLEX_BTN_PRESS_NONE, } flex_button_event_t; /** * flex_button_t * * @brief Button data structure * Below are members that need to user init before scan. * * @member next * Internal use. * One-way linked list, pointing to the next button. * * @member usr_button_read * User function is used to read button vaule. * * @member cb * Button event callback function. * * @member scan_cnt * Internal use, user read-only. * Number of scans, counted when the button is pressed, plus one per scan cycle. * * @member click_cnt * Internal use, user read-only. * Number of button clicks * * @member max_multiple_clicks_interval * Multiple click interval. Default &#39;MAX_MULTIPLE_CLICKS_INTERVAL&#39;. * Need to use FLEX_MS_TO_SCAN_CNT to convert milliseconds into scan cnts. * * @member debounce_tick * Debounce. Not used yet. * Need to use FLEX_MS_TO_SCAN_CNT to convert milliseconds into scan cnts. * * @member short_press_start_tick * Short press start time. Requires user configuration. * Need to use FLEX_MS_TO_SCAN_CNT to convert milliseconds into scan cnts. * * @member long_press_start_tick * Long press start time. Requires user configuration. * Need to use FLEX_MS_TO_SCAN_CNT to convert milliseconds into scan cnts. * * @member long_hold_start_tick * Long hold press start time. Requires user configuration. * * @member id * Button id. Requires user configuration. * When multiple buttons use the same button callback function, * they are used to distinguish the buttons. * Each button id must be unique. * * @member pressed_logic_level * Requires user configuration. * The logic level of the button pressed, each bit represents a button. * * @member event * Internal use, users can call &#39;flex_button_event_read&#39; to get current button event. * Used to record the current button event. * * @member status * Internal use, user unavailable. * Used to record the current state of buttons. * */ typedef struct flex_button { struct flex_button* next; uint8_t (*usr_button_read)(void *); flex_button_response_callback cb; uint16_t scan_cnt; uint16_t click_cnt; uint16_t max_multiple_clicks_interval; uint16_t debounce_tick; uint16_t short_press_start_tick; uint16_t long_press_start_tick; uint16_t long_hold_start_tick; uint8_t id; uint8_t pressed_logic_level : 1; uint8_t event : 4; uint8_t status : 3; } flex_button_t; #ifdef __cplusplus extern &quot;C&quot; { #endif int32_t flex_button_register(flex_button_t *button); flex_button_event_t flex_button_event_read(flex_button_t* button); uint8_t flex_button_scan(void); void user_button_init(void); #ifdef __cplusplus } #endif #endif /* __FLEXIBLE_BUTTON_H__ */ /** * @File: flexible_button.c * @Author: MurphyZhao * @Date: 2018-09-29 * * Copyright (c) 2018-2019 MurphyZhao &lt;d2014zjt@163.com&gt; * https://github.com/murphyzhao * All rights reserved. * License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the &quot;License&quot;); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an &quot;AS IS&quot; BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Change logs: * Date Author Notes * 2018-09-29 MurphyZhao First add * 2019-08-02 MurphyZhao Migrate code to github.com/murphyzhao account * 2019-12-26 MurphyZhao Refactor code and implement multiple clicks * */ #include &quot;button.h&quot; #include &quot;KEY.h&quot; #include &quot;string.h&quot; #include &quot;key_app.h&quot; typedef enum { USER_BUTTON_UP = 0, // 对应 IoT Board 开发板的 PIN_KEY0 USER_BUTTON_LEFT, // 对应 IoT Board 开发板的 PIN_KEY1 USER_BUTTON_RIGHT, // 对应 IoT Board 开发板的 PIN_KEY2 USER_BUTTON_DOWN, USER_BUTTON_MID, // 对应 IoT Board 开发板的 PIN_WK_UP USER_BUTTON_MAX } user_button_t; static flex_button_t user_button[USER_BUTTON_MAX]; static uint8_t common_btn_read(void *arg) { uint8_t value = 0; flex_button_t *btn = (flex_button_t *)arg; switch (btn-&gt;id) { case USER_BUTTON_UP: value = key_scan().up; break; case USER_BUTTON_LEFT: value = key_scan().left; break; case USER_BUTTON_RIGHT: value =key_scan().right; break; case USER_BUTTON_DOWN: value = key_scan().down; break; case USER_BUTTON_MID: value = key_scan().mid; break; } return value; } // 配置项 说明 // id 按键编号 // usr_button_read 设置按键读值回调函数 // cb 设置按键事件回调函数 // pressed_logic_level 设置按键按下时的逻辑电平 // short_press_start_tick 短按起始 tick,使用 FLEX_MS_TO_SCAN_CNT 宏转化为扫描次数 // long_press_start_tick 长按起始 tick,使用 FLEX_MS_TO_SCAN_CNT 宏转化为扫描次数 // long_hold_start_tick 超长按起始 tick,使用 FLEX_MS_TO_SCAN_CNT 宏转化为扫描次数 // 注意,short_press_start_tick、long_press_start_tick 和 long_hold_start_tick 必须使用 FLEX_MS_TO_SCAN_CNT 将毫秒时间转化为扫描次数。 // user_button[i].short_press_start_tick = FLEX_MS_TO_SCAN_CNT(1500); 表示按键按下开始计时,1500 ms 后按键依旧是按下状态的话,就断定为短按开始。 //--------------------------------------------------改到这里了----------------------------- void user_button_init(void) { int i; memset(&amp;user_button[0], 0x0, sizeof(user_button)); user_button[USER_BUTTON_UP].cb = up_btn_evt_cb; user_button[USER_BUTTON_LEFT].cb = left_btn_evt_cb; user_button[USER_BUTTON_RIGHT].cb = right_btn_evt_cb; user_button[USER_BUTTON_DOWN].cb = down_btn_evt_cb; user_button[USER_BUTTON_MID].cb = mid_btn_evt_cb; for (i = 0; i &lt; USER_BUTTON_MAX; i ++) { user_button[i].id = i; user_button[i].usr_button_read = common_btn_read; user_button[i].pressed_logic_level = 0; user_button[i].short_press_start_tick = FLEX_MS_TO_SCAN_CNT(1500); user_button[i].long_press_start_tick = FLEX_MS_TO_SCAN_CNT(3000); user_button[i].long_hold_start_tick = FLEX_MS_TO_SCAN_CNT(4500); flex_button_register(&amp;user_button[i]); } } #ifndef NULL #define NULL 0 #endif #define EVENT_SET_AND_EXEC_CB(btn, evt) \ do \ { \ btn-&gt;event = evt; \ if(btn-&gt;cb) \ btn-&gt;cb((flex_button_t*)btn); \ } while(0) /** * BTN_IS_PRESSED * * 1: is pressed * 0: is not pressed */ #define BTN_IS_PRESSED(i) (g_btn_status_reg &amp; (1 &lt;&lt; i)) enum FLEX_BTN_STAGE { FLEX_BTN_STAGE_DEFAULT = 0, FLEX_BTN_STAGE_DOWN = 1, FLEX_BTN_STAGE_MULTIPLE_CLICK = 2 }; typedef uint32_t btn_type_t; static flex_button_t *btn_head = NULL; /** * g_logic_level * * The logic level of the button pressed, * Each bit represents a button. * * First registered button, the logic level of the button pressed is * at the low bit of g_logic_level. */ btn_type_t g_logic_level = (btn_type_t)0; /** * g_btn_status_reg * * The status register of all button, each bit records the pressing state of a button. * * First registered button, the pressing state of the button is * at the low bit of g_btn_status_reg. */ btn_type_t g_btn_status_reg = (btn_type_t)0; static uint8_t button_cnt = 0; /** * @brief Register a user button * * @param button: button structure instance * @return Number of keys that have been registered, or -1 when error */ int32_t flex_button_register(flex_button_t *button) { flex_button_t *curr = btn_head; if (!button || (button_cnt &gt; sizeof(btn_type_t) * 8)) { return -1; } while (curr) { if(curr == button) { return -1; /* already exist. */ } curr = curr-&gt;next; } /** * First registered button is at the end of the &#39;linked list&#39;. * btn_head points to the head of the &#39;linked list&#39;. */ button-&gt;next = btn_head; button-&gt;status = FLEX_BTN_STAGE_DEFAULT; button-&gt;event = FLEX_BTN_PRESS_NONE; button-&gt;scan_cnt = 0; button-&gt;click_cnt = 0; button-&gt;max_multiple_clicks_interval = MAX_MULTIPLE_CLICKS_INTERVAL; btn_head = button; /** * First registered button, the logic level of the button pressed is * at the low bit of g_logic_level. */ g_logic_level |= (button-&gt;pressed_logic_level &lt;&lt; button_cnt); button_cnt ++; return button_cnt; } /** * @brief Read all key values in one scan cycle * * @param void * @return none */ static void flex_button_read(void) { uint8_t i; flex_button_t* target; /* The button that was registered first, the button value is in the low position of raw_data */ btn_type_t raw_data = 0; for(target = btn_head, i = button_cnt - 1; (target != NULL) &amp;&amp; (target-&gt;usr_button_read != NULL); target = target-&gt;next, i--) { raw_data = raw_data | ((target-&gt;usr_button_read)(target) &lt;&lt; i); } g_btn_status_reg = (~raw_data) ^ g_logic_level; } /** * @brief Handle all key events in one scan cycle. * Must be used after &#39;flex_button_read&#39; API * * @param void * @return Activated button count */ static uint8_t flex_button_process(void) { uint8_t i; uint8_t active_btn_cnt = 0; flex_button_t* target; for (target = btn_head, i = button_cnt - 1; target != NULL; target = target-&gt;next, i--) { if (target-&gt;status &gt; FLEX_BTN_STAGE_DEFAULT) { target-&gt;scan_cnt ++; if (target-&gt;scan_cnt &gt;= ((1 &lt;&lt; (sizeof(target-&gt;scan_cnt) * 8)) - 1)) { target-&gt;scan_cnt = target-&gt;long_hold_start_tick; } } switch (target-&gt;status) { case FLEX_BTN_STAGE_DEFAULT: /* stage: default(button up) */ if (BTN_IS_PRESSED(i)) /* is pressed */ { target-&gt;scan_cnt = 0; target-&gt;click_cnt = 0; EVENT_SET_AND_EXEC_CB(target, FLEX_BTN_PRESS_DOWN); /* swtich to button down stage */ target-&gt;status = FLEX_BTN_STAGE_DOWN; } else { target-&gt;event = FLEX_BTN_PRESS_NONE; } break; case FLEX_BTN_STAGE_DOWN: /* stage: button down */ if (BTN_IS_PRESSED(i)) /* is pressed */ { if (target-&gt;click_cnt &gt; 0) /* multiple click */ { if (target-&gt;scan_cnt &gt; target-&gt;max_multiple_clicks_interval) { EVENT_SET_AND_EXEC_CB(target, target-&gt;click_cnt &lt; FLEX_BTN_PRESS_REPEAT_CLICK ? target-&gt;click_cnt : FLEX_BTN_PRESS_REPEAT_CLICK); /* swtich to button down stage */ target-&gt;status = FLEX_BTN_STAGE_DOWN; target-&gt;scan_cnt = 0; target-&gt;click_cnt = 0; } } else if (target-&gt;scan_cnt &gt;= target-&gt;long_hold_start_tick) { if (target-&gt;event != FLEX_BTN_PRESS_LONG_HOLD) { EVENT_SET_AND_EXEC_CB(target, FLEX_BTN_PRESS_LONG_HOLD); } } else if (target-&gt;scan_cnt &gt;= target-&gt;long_press_start_tick) { if (target-&gt;event != FLEX_BTN_PRESS_LONG_START) { EVENT_SET_AND_EXEC_CB(target, FLEX_BTN_PRESS_LONG_START); } } else if (target-&gt;scan_cnt &gt;= target-&gt;short_press_start_tick) { if (target-&gt;event != FLEX_BTN_PRESS_SHORT_START) { EVENT_SET_AND_EXEC_CB(target, FLEX_BTN_PRESS_SHORT_START); } } } else /* button up */ { if (target-&gt;scan_cnt &gt;= target-&gt;long_hold_start_tick) { EVENT_SET_AND_EXEC_CB(target, FLEX_BTN_PRESS_LONG_HOLD_UP); target-&gt;status = FLEX_BTN_STAGE_DEFAULT; } else if (target-&gt;scan_cnt &gt;= target-&gt;long_press_start_tick) { EVENT_SET_AND_EXEC_CB(target, FLEX_BTN_PRESS_LONG_UP); target-&gt;status = FLEX_BTN_STAGE_DEFAULT; } else if (target-&gt;scan_cnt &gt;= target-&gt;short_press_start_tick) { EVENT_SET_AND_EXEC_CB(target, FLEX_BTN_PRESS_SHORT_UP); target-&gt;status = FLEX_BTN_STAGE_DEFAULT; } else { /* swtich to multiple click stage */ target-&gt;status = FLEX_BTN_STAGE_MULTIPLE_CLICK; target-&gt;click_cnt ++; } } break; case FLEX_BTN_STAGE_MULTIPLE_CLICK: /* stage: multiple click */ if (BTN_IS_PRESSED(i)) /* is pressed */ { /* swtich to button down stage */ target-&gt;status = FLEX_BTN_STAGE_DOWN; target-&gt;scan_cnt = 0; } else { if (target-&gt;scan_cnt &gt; target-&gt;max_multiple_clicks_interval) { EVENT_SET_AND_EXEC_CB(target, target-&gt;click_cnt &lt; FLEX_BTN_PRESS_REPEAT_CLICK ? target-&gt;click_cnt : FLEX_BTN_PRESS_REPEAT_CLICK); /* swtich to default stage */ target-&gt;status = FLEX_BTN_STAGE_DEFAULT; } } break; } if (target-&gt;status &gt; FLEX_BTN_STAGE_DEFAULT) { active_btn_cnt ++; } } return active_btn_cnt; } /** * flex_button_event_read * * @brief Get the button event of the specified button. * * @param button: button structure instance * @return button event */ flex_button_event_t flex_button_event_read(flex_button_t* button) { return (flex_button_event_t)(button-&gt;event); } /** * flex_button_scan * * @brief Start key scan. * Need to be called cyclically within the specified period. * Sample cycle: 5 - 20ms * * @param void * @return Activated button count */ uint8_t flex_button_scan(void) { flex_button_read(); return flex_button_process(); }
07-13
void button_handler(struct Button* handle) { uint8_t read_gpio_level = handle-&gt;hal_button_Level(handle-&gt;button_id); //ticks counter working.. if((handle-&gt;state) &gt; 0) handle-&gt;ticks++; /*------------button debounce handle---------------*/ if(read_gpio_level != handle-&gt;button_level) { //not equal to prev one //continue read 3 times same new level change if(++(handle-&gt;debounce_cnt) &gt;= DEBOUNCE_TICKS) { handle-&gt;button_level = read_gpio_level; handle-&gt;debounce_cnt = 0; } } else { //leved not change ,counter reset. handle-&gt;debounce_cnt = 0; } /*-----------------State machine-------------------*/ switch (handle-&gt;state) { case 0: if(handle-&gt;button_level == handle-&gt;active_level) { //start press down handle-&gt;event = (uint8_t)PRESS_DOWN; EVENT_CB(PRESS_DOWN); handle-&gt;ticks = 0; handle-&gt;repeat = 1; handle-&gt;state = 1; } else { handle-&gt;event = (uint8_t)NONE_PRESS; } break; case 1: if(handle-&gt;button_level != handle-&gt;active_level) { //released press up handle-&gt;event = (uint8_t)PRESS_UP; EVENT_CB(PRESS_UP); handle-&gt;ticks = 0; handle-&gt;state = 2; } else if(handle-&gt;ticks &gt; LONG_TICKS) { handle-&gt;event = (uint8_t)LONG_PRESS_START; EVENT_CB(LONG_PRESS_START); handle-&gt;state = 5; } break; case 2: if(handle-&gt;button_level == handle-&gt;active_level) { //press down again handle-&gt;event = (uint8_t)PRESS_DOWN; EVENT_CB(PRESS_DOWN); handle-&gt;repeat++; EVENT_CB(PRESS_REPEAT); // repeat hit handle-&gt;ticks = 0; handle-&gt;state = 3; } else if(handle-&gt;ticks &gt; SHORT_TICKS) { //released timeout if(handle-&gt;repeat == 1) { handle-&gt;event = (uint8_t)SINGLE_CLICK; EVENT_CB(SINGLE_CLICK); } else if(handle-&gt;repeat == 2) { handle-&gt;event = (uint8_t)DOUBLe_CLICK; EVENT_CB(DOUBLe_CLICK); // repeat hit } handle-&gt;state = 0; } break; case 3: if(handle-&gt;button_level != handle-&gt;active_level) { //released press up handle-&gt;event = (uint8_t)PRESS_UP; EVENT_CB(PRESS_UP); if(handle-&gt;ticks &lt; SHORT_TICKS) { handle-&gt;ticks = 0; handle-&gt;state = 2; //repeat press } else { handle-&gt;state = 0; } }else if(handle-&gt;ticks &gt; SHORT_TICKS){ // long press up handle-&gt;state = 0; } break; case 5: if(handle-&gt;button_level == handle-&gt;active_level) { //continue hold trigger handle-&gt;event = (uint8_t)LONG_PRESS_HOLD; EVENT_CB(LONG_PRESS_HOLD); } else { //releasd handle-&gt;event = (uint8_t)PRESS_UP; EVENT_CB(PRESS_UP); handle-&gt;state = 0; //reset } break; default: handle-&gt;state = 0; //reset break; } }帮我详细注释这段代码
06-09
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值