diff --git a/HAL/include/CONFIG.h b/HAL/include/CONFIG.h index 672d892..4d10b1d 100644 --- a/HAL/include/CONFIG.h +++ b/HAL/include/CONFIG.h @@ -124,7 +124,7 @@ #define CLK_OSC32K 1 // 该项请勿在此修改,必须在工程配置里的预处理中修改,如包含主机角色必须使用外部32K #endif #ifndef BLE_MEMHEAP_SIZE -#define BLE_MEMHEAP_SIZE (1024*6) +#define BLE_MEMHEAP_SIZE (1024*7) #endif #ifndef BLE_BUFF_MAX_LEN #define BLE_BUFF_MAX_LEN 251 diff --git a/common/FlexibleButton-2.0.1/README.md b/common/FlexibleButton-2.0.1/README.md new file mode 100644 index 0000000..cebc90c --- /dev/null +++ b/common/FlexibleButton-2.0.1/README.md @@ -0,0 +1,325 @@ +# FlexibleButton + +FlexibleButton 鏄竴涓熀浜庢爣鍑 C 璇█鐨勫皬宸х伒娲荤殑鎸夐敭澶勭悊搴擄紝鏀寔鍗曞嚮銆佽繛鍑汇佺煭鎸夈侀暱鎸夈佽嚜鍔ㄦ秷鎶栵紝鍙互鑷敱璁剧疆缁勫悎鎸夐敭锛屽彲鐢ㄤ簬涓柇鍜屼綆鍔熻楀満鏅 + +璇ユ寜閿簱瑙h︿簡鍏蜂綋鐨勬寜閿‖浠剁粨鏋勶紝鐞嗚涓婃敮鎸佽交瑙︽寜閿笌鑷攣鎸夐敭锛屽苟鍙互鏃犻檺鎵╁睍鎸夐敭鏁伴噺銆傚彟澶栵紝FlexibleButton 浣跨敤鎵弿鐨勬柟寮忎竴娆℃ц鍙栨墍鏈夋墍鏈夌殑鎸夐敭鐘舵侊紝鐒跺悗閫氳繃浜嬩欢鍥炶皟鏈哄埗涓婃姤鎸夐敭浜嬩欢銆傛牳蹇冪殑鎸夐敭鎵弿浠g爜浠呮湁涓夎锛屾病閿欙紝灏辨槸缁忓吀鐨 **涓夎鎸夐敭鎵弿绠楁硶**銆備娇鐢 C 璇█鏍囧噯搴 API 缂栧啓锛屼篃浣垮緱璇ユ寜閿簱鍙互鏃犵紳鍏煎浠绘剰鐨勫鐞嗗櫒骞冲彴锛屽苟涓旀敮鎸佷换鎰 OS 鍜 non-OS锛堣8鏈虹紪绋嬶級銆 + +## 鑾峰彇 + +### Git 鏂瑰紡 + +```SHELL +git clone https://github.com/murphyzhao/FlexibleButton.git +``` + +### RT-Thread menuconfig 鏂瑰紡 + +``` +RT-Thread online packages ---> + miscellaneous packages ---> + [*] FlexibleButton: Small and flexible button driver ---> + [*] Enable flexible button demo + version (latest) ---> +``` + +閰嶇疆瀹屾垚鍚庯紝杈撳叆 `pkgs --update` 涓嬭浇杞欢鍖呫 + +## 璧勬簮缁熻 + +ARMCC -O0 浼樺寲鐨勬儏鍐典笅锛孎lexibleButton 璧勬簮鍗犵敤濡備笅锛 + +- CODE锛798 瀛楄妭 +- RO DATA锛0 +- RW DATA锛13 瀛楄妭 +- ZI DATA锛0 + +## 蹇熶綋楠 + +FlexibleButton 搴撲腑鎻愪緵浜嗕竴涓祴璇曚緥绋 [`./examples/demo_rtt_iotboard.c`](./examples/demo_rtt_iotboard.c)锛岃渚嬬▼鍩轰簬 RT-Thread OS 杩涜娴嬭瘯锛岀‖浠跺钩鍙伴夋嫨浜 *RT-Thread IoT Board Pandora v2.51* 寮鍙戞澘銆傚綋鐒朵綘鍙互閫夋嫨浣跨敤鍏朵粬鐨 OS锛屾垨鑰呬娇鐢ㄨ8鏈烘祴璇曪紝鍙渶瑕佺Щ闄 OS 鐩稿叧鐨勭壒鎬у嵆鍙 + +濡傛灉浣犱娇鐢ㄨ嚜宸辩殑纭欢骞冲彴锛屽彧闇瑕佸皢 FlexibleButton 搴撴簮鐮佸拰渚嬬▼鍔犲叆浣犳棦鏈夌殑宸ョ▼涓嬪嵆鍙 + +## DEMO 绋嬪簭璇存槑 + +璇ョず渚嬬▼搴忓彲浠ョ洿鎺ュ湪 RT-Thread [`stm32l475-atk-pandora`](https://github.com/RT-Thread/rt-thread/tree/master/bsp/stm32/stm32l475-atk-pandora) BSP 涓繍琛岋紝鍙互鍦ㄨ BSP 鐩綍涓嬶紝浣跨敤 menuconfig 鑾峰彇鏈蒋浠跺寘銆 + +### 纭畾鐢ㄦ埛鎸夐敭 + +```C +typedef enum +{ + USER_BUTTON_0 = 0, // 瀵瑰簲 IoT Board 寮鍙戞澘鐨 PIN_KEY0 + USER_BUTTON_1, // 瀵瑰簲 IoT Board 寮鍙戞澘鐨 PIN_KEY1 + USER_BUTTON_2, // 瀵瑰簲 IoT Board 寮鍙戞澘鐨 PIN_KEY2 + USER_BUTTON_3, // 瀵瑰簲 IoT Board 寮鍙戞澘鐨 PIN_WK_UP + USER_BUTTON_MAX +} user_button_t; + +static flex_button_t user_button[USER_BUTTON_MAX]; +``` + +涓婅堪浠g爜瀹氫箟浜 4 涓寜閿紝鏁版嵁缁撴瀯瀛樺偍鍦 `user_button` 鏁扮粍涓 + +### 绋嬪簭鍏ュ彛 + +```C +int flex_button_main(void) +{ + rt_thread_t tid = RT_NULL; + user_button_init(); + /* 鍒涘缓鎸夐敭鎵弿绾跨▼ flex_btn锛岀嚎绋嬫爤 1024 byte锛屼紭鍏堢骇 10 */ + tid = rt_thread_create("flex_btn", button_scan, RT_NULL, 1024, 10, 10); + if(tid != RT_NULL) + { + rt_thread_startup(tid); + } + return 0; +} +/* 浣跨敤 RT-Thread 鐨勮嚜鍔ㄥ垵濮嬪寲 */ +INIT_APP_EXPORT(flex_button_main); +``` + +濡備笂浠g爜鎵绀猴紝棣栧厛浣跨敤 `user_button_init();` 鍒濆鍖栫敤鎴锋寜閿‖浠讹紝璇ユ楠ゅ皢鐢ㄦ埛鎸夐敭缁戝畾鍒 FlexibleButton 搴撱傜劧鍚庯紝浣跨敤 RT-Thread 鐨 `INIT_APP_EXPORT` 鎺ュ彛瀵煎嚭涓轰笂鐢佃嚜鍔ㄥ垵濮嬪寲锛屽垱寤轰簡涓涓 鈥渇lex_btn鈥 鍚嶅瓧鐨勬寜閿壂鎻忕嚎绋嬶紝绾跨▼閲屾壂鎻忔鏌ユ寜閿簨浠躲 + +### 鎸夐敭鍒濆鍖栦唬鐮 + +`user_button_init();` 鍒濆鍖栦唬鐮佸涓嬫墍绀猴細 + +``` +static void user_button_init(void) +{ + int i; + + /* 鍒濆鍖栨寜閿暟鎹粨鏋 */ + rt_memset(&user_button[0], 0x0, sizeof(user_button)); + + /* 鍒濆鍖 IoT Board 鎸夐敭寮曡剼锛屼娇鐢 rt-thread PIN 璁惧妗嗘灦 */ + rt_pin_mode(PIN_KEY0, PIN_MODE_INPUT_PULLUP); /* 璁剧疆 GPIO 涓轰笂鎷夎緭鍏ユā寮 */ + rt_pin_mode(PIN_KEY1, PIN_MODE_INPUT_PULLUP); /* 璁剧疆 GPIO 涓轰笂鎷夎緭鍏ユā寮 */ + rt_pin_mode(PIN_KEY2, PIN_MODE_INPUT_PULLUP); /* 璁剧疆 GPIO 涓轰笂鎷夎緭鍏ユā寮 */ + rt_pin_mode(PIN_WK_UP, PIN_MODE_INPUT_PULLDOWN); /* 璁剧疆 GPIO 涓轰笅鎷夎緭鍏ユā寮 */ + + for (i = 0; i < USER_BUTTON_MAX; i ++) + { + user_button[i].id = i; + user_button[i].usr_button_read = common_btn_read; + user_button[i].cb = common_btn_evt_cb; + 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); + + if (i == USER_BUTTON_3) + { + user_button[USER_BUTTON_3].pressed_logic_level = 1; + } + + flex_button_register(&user_button[i]); + } +} +``` + +鏍稿績鐨勯厤缃涓嬶細 + +|閰嶇疆椤箌璇存槑| +| :---- | :----| +| 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 瀹忚浆鍖栦负鎵弿娆℃暟 | + +娉ㄦ剰锛宻hort_press_start_tick銆乴ong_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 鍚庢寜閿緷鏃ф槸鎸変笅鐘舵佺殑璇濓紝灏辨柇瀹氫负鐭寜寮濮嬨 + +### 浜嬩欢澶勭悊浠g爜 + +```C +static void common_btn_evt_cb(void *arg) +{ + flex_button_t *btn = (flex_button_t *)arg; + + rt_kprintf("id: [%d - %s] event: [%d - %30s] repeat: %d\n", + btn->id, enum_btn_id_string[btn->id], + btn->event, enum_event_string[btn->event], + btn->click_cnt); + + if (flex_button_event_read(&user_button[USER_BUTTON_0]) == flex_button_event_read(&user_button[USER_BUTTON_1]) == FLEX_BTN_PRESS_CLICK) + { + rt_kprintf("[combination]: button 0 and button 1\n"); + } +} +``` + +绀轰緥浠g爜涓紝灏嗘墍鏈夌殑鎸夐敭浜嬩欢鍥炶皟鍧囩粦瀹氬埌 `common_btn_evt_cb` 鍑芥暟锛屽湪璇ュ嚱鏁颁腑鎵撳嵃浜嗘寜閿 ID 鍜屾寜閿簨浠讹紝浠ュ強鎸夐敭杩炲嚮娆℃暟锛屽苟婕旂ず浜嗗浣曚娇鐢ㄧ粍鍚堟寜閿 + +## FlexibleButton 浠g爜璇存槑 + +### 鎸夐敭浜嬩欢瀹氫箟 + +鎸夐敭浜嬩欢鐨勫畾涔夊苟娌℃湁浣跨敤 Windows 椹卞姩涓婄殑瀹氫箟锛屼富瑕佹槸鏂逛究宓屽叆寮忚澶囦腑鐨勫簲鐢ㄥ満鏅紙涔熷彲鑳芥槸鎴戠悊瑙g殑鍋忓樊锛夛紝鎸夐敭浜嬩欢瀹氫箟濡備笅锛 + +```C +typedef enum +{ + FLEX_BTN_PRESS_DOWN = 0, // 鎸変笅浜嬩欢 + FLEX_BTN_PRESS_CLICK, // 鍗曞嚮浜嬩欢 + FLEX_BTN_PRESS_DOUBLE_CLICK, // 鍙屽嚮浜嬩欢 + FLEX_BTN_PRESS_REPEAT_CLICK, // 杩炲嚮浜嬩欢锛屼娇鐢 flex_button_t 涓殑 click_cnt 鏂畾杩炲嚮娆℃暟 + 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_BTN_PRESS_LONG_HOLD` 浜嬩欢鍙互鐢ㄦ潵瀹炵幇闀挎寜绱姞鐨勫簲鐢ㄥ満鏅 + +### 鎸夐敭鏁版嵁缁撴瀯 + +``` +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; +``` + +| 搴忓彿 | 鏁版嵁鎴愬憳 | 鏄惁闇瑕佺敤鎴峰垵濮嬪寲 | 璇存槑 | +| :----: | :---- | :----: | :---- | +| 1 | next | 鍚 | 鎸夐敭搴撲娇鐢ㄥ崟鍚戦摼琛ㄤ覆璧锋墍鏈夌殑鎸夐敭 | +| 2 | usr_button_read | 鏄 | 鐢ㄦ埛璁惧鐨勬寜閿紩鑴氱數骞宠鍙栧嚱鏁帮紝**閲嶈** | +| 3 | cb | 鏄 | 璁剧疆鎸夐敭浜嬩欢鍥炶皟锛岀敤浜庡簲鐢ㄥ眰瀵规寜閿簨浠剁殑鍒嗙被澶勭悊 | +| 4 | scan_cnt | 鍚 | 鐢ㄤ簬璁板綍鎵弿娆℃暟锛屾寜閿寜涓嬫槸寮濮嬩粠闆惰鏁 | +| 5 | click_cnt | 鍚 | 璁板綍鍗曞嚮娆℃暟锛岀敤浜庡垽瀹氬崟鍑汇佽繛鍑 | +| 6 | max_multiple_clicks_interval | 鏄 | 杩炲嚮闂撮殭锛岀敤浜庡垽瀹氭槸鍚︾粨鏉熻繛鍑昏鏁帮紝鏈夐粯璁ゅ `MAX_MULTIPLE_CLICKS_INTERVAL` | +| 7 | debounce_tick | 鍚 | 娑堟姈鏃堕棿锛屾殏鏈娇鐢紝渚濋潬鎵弿闂撮殭杩涜娑堟姈 | +| 8 | short_press_start_tick | 鏄 | 璁剧疆鐭寜浜嬩欢瑙﹀彂鐨勮捣濮 tick | +| 9 | long_press_start_tick | 鏄 | 璁剧疆闀挎寜浜嬩欢瑙﹀彂鐨勮捣濮 tick | +| 10 | long_hold_start_tick | 鏄 | 璁剧疆闀挎寜淇濇寔浜嬩欢瑙﹀彂鐨勮捣濮 tick | +| 11 | id | 鏄 | 褰撳涓寜閿娇鐢ㄥ悓涓涓洖璋冨嚱鏁版椂锛岀敤浜庢柇瀹氬睘浜庡摢涓寜閿 | +| 12 | pressed_logic_level | 鏄 | 璁剧疆鎸夐敭鎸変笅鐨勯昏緫鐢靛钩銆1锛氭爣璇嗘寜閿寜涓嬬殑鏃跺欎负楂樼數骞筹紱0锛氭爣璇嗘寜閿寜涓嬬殑鏃跺欐湭浣庣數骞筹紝**閲嶈** | +| 13 | event | 鍚 | 鐢ㄤ簬璁板綍褰撳墠鎸夐敭浜嬩欢 | +| 14 | status | 鍚 | 鐢ㄤ簬璁板綍褰撳墠鎸夐敭鐨勭姸鎬侊紝鐢ㄤ簬鍐呴儴鐘舵佹満 | + +娉ㄦ剰锛屽湪浣跨敤 `max_multiple_clicks_interval`銆乣debounce_tick`銆乣short_press_start_tick`銆乣long_press_start_tick`銆乣long_hold_start_tick` 鐨勬椂鍊欙紝娉ㄦ剰闇瑕佷娇鐢ㄥ畯 `**FLEX_MS_TO_SCAN_CNT(ms)**` 灏嗘绉掑艰浆鎹负鎵弿娆℃暟銆傚洜涓烘寜閿簱鍩轰簬鎵弿娆℃暟杩愯浆銆傜ず渚嬪涓嬶細 + +``` +user_button[1].short_press_start_tick = FLEX_MS_TO_SCAN_CNT(1500); // 1500 姣 +``` + +涓婅堪浠g爜琛ㄧず锛氳〃绀烘寜閿寜涓嬪悗寮濮嬭鏃讹紝1500ms 鐨勬椂鍊欙紝鎸夐敭渚濇棫鎸変笅锛屽垯鏂畾涓虹煭鎸夊紑濮嬶紝骞朵笂鎶 `FLEX_BTN_PRESS_SHORT_START` 浜嬩欢銆 + +### 鎸夐敭娉ㄥ唽鎺ュ彛 + +浣跨敤璇ユ帴鍙f敞鍐屼竴涓敤鎴锋寜閿紝鍏ュ弬涓轰竴涓 flex_button_t 缁撴瀯浣撳疄渚嬬殑鍦板潃銆 + +```C +int8_t flex_button_register(flex_button_t *button); +``` + +### 鎸夐敭浜嬩欢璇诲彇鎺ュ彛 + +浣跨敤璇ユ帴鍙h幏鍙栨寚瀹氭寜閿殑浜嬩欢銆 + +```C +flex_button_event_t flex_button_event_read(flex_button_t* button); +```` + +### 鎸夐敭鎵弿鎺ュ彛 + +鎸夐敭鎵弿鐨勬牳蹇冨嚱鏁帮紝闇瑕佹斁鍒板簲鐢ㄧ▼搴忎腑瀹氭椂鎵弿锛屾壂鎻忛棿闅斿缓璁 20 姣銆 + +```C +void flex_button_scan(void); +``` + +## 娉ㄦ剰浜嬮」 + +- 闃诲闂 + + 鍥犱负鎸夐敭浜嬩欢鍥炶皟鍑芥暟浠ュ強鎸夐敭閿艰鍙栧嚱鏁版槸鍦ㄦ寜閿壂鎻忕殑杩囩▼涓墽琛岀殑锛屽洜姝よ涓嶈鍦ㄨ繖绫诲嚱鏁颁腑浣跨敤闃诲鎺ュ彛锛屼笉瑕佽繘琛屽欢鏃舵搷浣溿 + +- 鎸夐敭鎵弿鍑芥暟鏍堥渶姹 + + 鎸夐敭鎵弿鍑芥暟鏈韩瀵规爤鐨勯渶姹傚皬浜 300 瀛楄妭锛屼絾鏄寜閿簨浠跺洖璋冨嚱鏁板拰鎸夐敭閿艰鍙栧嚱鏁伴兘鏄湪鎸夐敭鎵弿鍑芥暟鐨勪笂涓嬫枃涓墽琛岀殑锛岃鏍煎鍏冲績鎸夐敭浜嬩欢鍥炶皟鍑芥暟涓庢寜閿敭鍊艰鍙栧嚱鏁板鏍堢┖闂寸殑闇姹傘 + +## 鍏跺畠 + +### 鍏充簬浣庡姛鑰 + +鏈寜閿簱鏄氳繃涓嶉棿鏂壂鎻忕殑鏂瑰紡鏉ユ鏌ユ寜閿姸鎬侊紝鍥犳浼氫竴鐩村崰鐢 CPU 璧勬簮锛岃繖瀵逛綆鍔熻楀簲鐢ㄥ満鏅槸涓嶅弸濂界殑銆備负浜嗛檷浣庢甯稿伐浣滄ā寮忎笅鐨勫姛鑰楋紝寤鸿鍚堢悊閰嶇疆鎵弿鍛ㄦ湡锛5ms - 20ms锛夛紝鎵弿闂撮殭閲 CPU 鍙互杩涘叆杞诲害鐫$湢銆 + +璇ユ寜閿簱涓嶅湪搴曞眰瀹炵幇浣庡姛鑰楀鐞嗭紝搴旂敤灞傚彲浠ユ牴鎹嚜宸辩殑鍔熻楁ā寮忕伒娲诲鐞嗭紝閫氬父浼氭湁浠ヤ笅涓ょ鏂瑰紡锛 + +1. 杩涘叆浣庡姛鑰楀墠锛屾寕璧锋寜閿壂鎻忕嚎绋嬶紱閫鍑轰綆鍔熻楀悗锛屽敜閱掓寜閿壂鎻忋 +2. 澧炲姞鎸夐敭涓柇妯″紡锛屾墍鏈夌殑鎸夐敭涓柇鏉ワ紝灏辫Е鍙戜竴娆℃寜閿壂鎻忥紝浠ョ‘璁ゆ墍鏈夌殑鎸夐敭鐘舵併 + +> 浣庡姛鑰楃浉鍏崇殑鎺㈣鍙傝 [issue 1](https://github.com/murphyzhao/FlexibleButton/issues/1) 涓殑璁ㄨ銆 + +### 鍏充簬鎸夐敭涓柇妯″紡 + +鐢变簬璇ユ寜閿簱涓娆℃壂鎻忓彲浠ョ‘瀹氭墍鏈夌殑鎸夐敭鐘舵侊紝鍥犳鍙互灏嗘墍鏈夌殑鎸夐敭涓柇閫氳繃 鈥**鎴**鈥 鐨勬柟寮忚浆鍖栦负涓涓腑鏂紝鐒跺悗鍦ㄤ腑鏂鐞嗗嚱鏁颁腑鎵ц涓娆℃寜閿壂鎻忋 + +涓柇 鈥**鎴**鈥 鐨勬柟寮忓彲浠ラ氳繃纭欢鏉ュ畬鎴愶紝涔熷彲浠ラ氳繃杞欢鏉ュ畬鎴愩 + +纭欢鏂瑰紡锛岄渶瑕佷娇鐢ㄤ竴涓 **鎴栭棬** 鑺墖锛屽涓緭鍏ユ潯浠惰浆鍖栦负涓涓緭鍑烘潯浠讹紝鐒跺悗閫氳繃涓涓閮ㄤ腑鏂嵆鍙畬鎴愭墍鏈夋寜閿殑涓柇鏂瑰紡妫娴嬨 + +杞欢鏂瑰紡锛岄渶瑕佷负姣忎竴涓寜閿厤缃负涓柇瑙﹀彂妯″紡锛岀劧鍚庡湪姣忎竴涓寜閿腑鏂殑涓柇澶勭悊鍑芥暟涓墽琛屾寜閿壂鎻忋 + +涓轰簡鍦ㄩ檷浣庝腑鏂鐞嗗嚱鏁颁腑鎵ц鎸夐敭鎵弿甯︽潵鐨勬椂寤讹紝鍙互閫氳繃淇″彿閲忕殑鏂瑰紡鏉ュ紓姝ュ鐞嗭紝浠呭湪涓柇澶勭悊鍑芥暟涓噴鏀句竴涓寜閿壂鎻忕殑淇″彿閲忥紝鐒跺悗鍦ㄦ寜閿壂鎻忕嚎绋嬩腑鐩戞祴璇ヤ俊鍙烽噺銆 + +### 鍏充簬缁勫悎鎸夐敭 + +璇ユ寜閿簱浠呭仛浜嗗簳灞傜殑鎸夐敭鎵弿澶勭悊锛屼竴娆℃壂鎻忓彲浠ョ‘瀹氭墍鏈夌殑鎸夐敭鐘舵侊紝骞朵笂鎶ュ搴旂殑鎸夐敭浜嬩欢锛屽鏋滈渶瑕佹敮鎸佺粍鍚堟寜閿紝璇峰啀灏佷竴灞傦紝鏍规嵁鎸夐敭搴撹繑鍥炵殑浜嬩欢灏佽闇瑕佺殑缁勫悎鎸夐敭銆俒绀轰緥绋嬪簭](./examples/demo_rtt_iotboard.c)鎻愪緵浜嗙畝鍗曠殑瀹炵幇銆 + +### 鍏充簬鐭╅樀閿洏 + +涓嶇浣犵殑鐭╅樀閿洏鏄氳繃浠涔堥氫俊鏂瑰紡鑾峰彇鎸夐敭鐘舵佺殑锛屽彧瑕佷綘灏嗚鍙栨寜閿姸鎬佺殑鍑芥暟瀵规帴鍒 Flexible_button 鏁版嵁缁撴瀯涓殑 `uint8_t (*usr_button_read)(void*);` 鍑芥暟涓婂嵆鍙 + +> 鍙傝 [issue 2](https://github.com/murphyzhao/FlexibleButton/issues/2) 涓殑璁ㄨ銆 + +## 闂鍜屽缓璁 + +濡傛灉鏈変粈涔堥棶棰樻垨鑰呭缓璁杩庢彁浜 [Issue](https://github.com/murphyzhao/FlexibleButton/issues) 杩涜璁ㄨ銆 + +## 缁存姢 + +- [MurphyZhao](https://github.com/murphyzhao) + +## 鎰熻阿 + +鎰熻阿鎵鏈変竴璧锋帰璁ㄧ殑鏈嬪弸锛屾劅璋㈡墍鏈変娇鐢 flexible_button 鐨勬湅鍙嬶紝鎰熻阿浣犱滑鐨 Star 鍜 Fork锛岃阿璋綘浠殑鏀寔銆 + +- 鎰熻阿 [BOBBOM](https://github.com/BOBBOM) 鍙戠幇 flex_button_register 鍑芥暟涓殑閫昏緫闂 +- 鎰熻阿 [BOBBOM](https://github.com/BOBBOM) 瑙i櫎 flexible_button 涓鎸夐敭鏁伴噺鐨勯檺鍒 +- 鎰熻阿 [**rt-thread**](https://mp.weixin.qq.com/s/HJEcSXhykBq1T5Hx0TdjMw) 鐨勬敮鎸 +- 鎰熻阿 [**鐢靛瓙鍙戠儳鍙**](https://mp.weixin.qq.com/s/mQFyrPAvz_TSktQLrSqQfA) 鐨勬敮鎸 +- 鎰熻阿 [**濞侀┌鐢靛瓙**](https://mp.weixin.qq.com/s/oAwFXPostMFBtb2EGxTdig) 鐨勬敮鎸 + +## 鍙嬫儏閾炬帴 + +- RT-Thread [IoT Board](https://github.com/RT-Thread/IoT_Board) 寮鍙戞澘 diff --git a/common/FlexibleButton-2.0.1/flexible_button.c b/common/FlexibleButton-2.0.1/flexible_button.c new file mode 100644 index 0000000..7cbcdd8 --- /dev/null +++ b/common/FlexibleButton-2.0.1/flexible_button.c @@ -0,0 +1,317 @@ +/** + * @File: flexible_button.c + * @Author: MurphyZhao + * @Date: 2018-09-29 + * + * Copyright (c) 2018-2019 MurphyZhao + * https://github.com/murphyzhao + * All rights reserved. + * License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); 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 "AS IS" 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 "flexible_button.h" + +#ifndef NULL +#define NULL 0 +#endif + +#define EVENT_SET_AND_EXEC_CB(btn, evt) \ + do \ + { \ + btn->event = evt; \ + if(btn->cb) \ + btn->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 & (1 << 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 +*/ +int8_t flex_button_register(flex_button_t *button) +{ + flex_button_t *curr = btn_head; + + if (!button || (button_cnt > sizeof(btn_type_t) * 8)) + { + return -1; + } + + while (curr) + { + if(curr == button) + { + return -1; /* already exist. */ + } + curr = curr->next; + } + + /** + * First registered button is at the end of the 'linked list'. + * btn_head points to the head of the 'linked list'. + */ + button->next = btn_head; + button->status = FLEX_BTN_STAGE_DEFAULT; + button->event = FLEX_BTN_PRESS_NONE; + button->scan_cnt = 0; + button->click_cnt = 0; + button->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->pressed_logic_level << 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) && (target->usr_button_read != NULL); + target = target->next, i--) + { + raw_data = raw_data | ((target->usr_button_read)(target) << i); + } + + g_btn_status_reg = (~raw_data) ^ g_logic_level; +} + +/** + * @brief Handle all key events in one scan cycle. + * Must be used after 'flex_button_read' API + * + * @param void + * @return none +*/ +static void flex_button_process(void) +{ + uint8_t i; + flex_button_t* target; + + for (target = btn_head, i = 0; target != NULL; target = target->next, i ++) + { + if (target->status > FLEX_BTN_STAGE_DEFAULT) + { + target->scan_cnt ++; + if (target->scan_cnt >= ((1 << (sizeof(target->scan_cnt) * 8)) - 1)) + { + target->scan_cnt = target->long_hold_start_tick; + } + } + + switch (target->status) + { + case FLEX_BTN_STAGE_DEFAULT: /* stage: default(button up) */ + if (BTN_IS_PRESSED(i)) /* is pressed */ + { + target->scan_cnt = 0; + target->click_cnt = 0; + + EVENT_SET_AND_EXEC_CB(target, FLEX_BTN_PRESS_DOWN); + + /* swtich to button down stage */ + target->status = FLEX_BTN_STAGE_DOWN; + } + else + { + target->event = FLEX_BTN_PRESS_NONE; + } + break; + + case FLEX_BTN_STAGE_DOWN: /* stage: button down */ + if (BTN_IS_PRESSED(i)) /* is pressed */ + { + if (target->click_cnt > 0) /* multiple click */ + { + if (target->scan_cnt > target->max_multiple_clicks_interval) + { + EVENT_SET_AND_EXEC_CB(target, + target->click_cnt < FLEX_BTN_PRESS_REPEAT_CLICK ? + target->click_cnt : + FLEX_BTN_PRESS_REPEAT_CLICK); + + /* swtich to button down stage */ + target->status = FLEX_BTN_STAGE_DOWN; + target->scan_cnt = 0; + target->click_cnt = 0; + } + } + else if (target->scan_cnt >= target->long_hold_start_tick) + { + if (target->event != FLEX_BTN_PRESS_LONG_HOLD) + { + EVENT_SET_AND_EXEC_CB(target, FLEX_BTN_PRESS_LONG_HOLD); + } + } + else if (target->scan_cnt >= target->long_press_start_tick) + { + if (target->event != FLEX_BTN_PRESS_LONG_START) + { + EVENT_SET_AND_EXEC_CB(target, FLEX_BTN_PRESS_LONG_START); + } + } + else if (target->scan_cnt >= target->short_press_start_tick) + { + if (target->event != FLEX_BTN_PRESS_SHORT_START) + { + EVENT_SET_AND_EXEC_CB(target, FLEX_BTN_PRESS_SHORT_START); + } + } + } + else /* button up */ + { + if (target->scan_cnt >= target->long_hold_start_tick) + { + EVENT_SET_AND_EXEC_CB(target, FLEX_BTN_PRESS_LONG_HOLD_UP); + target->status = FLEX_BTN_STAGE_DEFAULT; + } + else if (target->scan_cnt >= target->long_press_start_tick) + { + EVENT_SET_AND_EXEC_CB(target, FLEX_BTN_PRESS_LONG_UP); + target->status = FLEX_BTN_STAGE_DEFAULT; + } + else if (target->scan_cnt >= target->short_press_start_tick) + { + EVENT_SET_AND_EXEC_CB(target, FLEX_BTN_PRESS_SHORT_UP); + target->status = FLEX_BTN_STAGE_DEFAULT; + } + else + { + /* swtich to multiple click stage */ + target->status = FLEX_BTN_STAGE_MULTIPLE_CLICK; + target->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->status = FLEX_BTN_STAGE_DOWN; + target->scan_cnt = 0; + } + else + { + if (target->scan_cnt > target->max_multiple_clicks_interval) + { + EVENT_SET_AND_EXEC_CB(target, + target->click_cnt < FLEX_BTN_PRESS_REPEAT_CLICK ? + target->click_cnt : + FLEX_BTN_PRESS_REPEAT_CLICK); + + /* swtich to default stage */ + target->status = FLEX_BTN_STAGE_DEFAULT; + } + } + break; + } + } +} + +/** + * 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->event); +} + +/** + * flex_button_scan + * + * @brief Start key scan. + * Need to be called cyclically within the specified period. + * Sample cycle: 5 - 20ms + * + * @param void + * @return none +*/ +void flex_button_scan(void) +{ + flex_button_read(); + flex_button_process(); +} diff --git a/common/FlexibleButton-2.0.1/flexible_button.h b/common/FlexibleButton-2.0.1/flexible_button.h new file mode 100644 index 0000000..2456610 --- /dev/null +++ b/common/FlexibleButton-2.0.1/flexible_button.h @@ -0,0 +1,164 @@ +/* + * @Author : stark1898y 1658608470@qq.com + * @Date : 2024-07-04 16:13:57 + * @LastEditors : stark1898y 1658608470@qq.com + * @LastEditTime : 2024-12-14 15:56:11 + * @FilePath : \BLE_TYQ_CH592F\common\FlexibleButton-2.0.1\flexible_button.h + * @Description : + * + * Copyright (c) 2024 by yzy, All Rights Reserved. + */ +/** + * @File: flexible_button.h + * @Author: MurphyZhao + * @Date: 2018-09-29 + * + * Copyright (c) 2018-2019 MurphyZhao + * https://github.com/murphyzhao + * All rights reserved. + * License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); 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 "AS IS" 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 __FLEXIBLE_BUTTON_H__ +#define __FLEXIBLE_BUTTON_H__ + +#include "stdint.h" + +#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)) + +typedef void (*flex_button_response_callback)(void*); + +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 'MAX_MULTIPLE_CLICKS_INTERVAL'. + * 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 'flex_button_event_read' 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 "C" { +#endif + +int8_t flex_button_register(flex_button_t *button); +flex_button_event_t flex_button_event_read(flex_button_t* button); +void flex_button_scan(void); + +#ifdef __cplusplus +} +#endif +#endif /* __FLEXIBLE_BUTTON_H__ */ diff --git a/common/MultiButton/LICENSE b/common/MultiButton/LICENSE new file mode 100644 index 0000000..1603075 --- /dev/null +++ b/common/MultiButton/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Zibin Zheng + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/common/MultiButton/README.md b/common/MultiButton/README.md new file mode 100644 index 0000000..41ee54d --- /dev/null +++ b/common/MultiButton/README.md @@ -0,0 +1,131 @@ +# MultiButton + +## 绠浠 +MultiButton 鏄竴涓皬宸х畝鍗曟槗鐢ㄧ殑浜嬩欢椹卞姩鍨嬫寜閿┍鍔ㄦā鍧楋紝鍙棤闄愰噺鎵╁睍鎸夐敭锛屾寜閿簨浠剁殑鍥炶皟寮傛澶勭悊鏂瑰紡鍙互绠鍖栦綘鐨勭▼搴忕粨鏋勶紝鍘婚櫎鍐椾綑鐨勬寜閿鐞嗙‖缂栫爜锛岃浣犵殑鎸夐敭涓氬姟閫昏緫鏇存竻鏅般 + +## 浣跨敤鏂规硶 +1.鍏堢敵璇蜂竴涓寜閿粨鏋 + +```c +struct Button button1; +``` +2.鍒濆鍖栨寜閿璞★紝缁戝畾鎸夐敭鐨凣PIO鐢靛钩璇诲彇鎺ュ彛**read_button_pin()** 锛屽悗涓涓弬鏁拌缃湁鏁堣Е鍙戠數骞 + +```c +button_init(&button1, read_button_pin, 0, 0); +``` +3.娉ㄥ唽鎸夐敭浜嬩欢 + +```c +button_attach(&button1, SINGLE_CLICK, Callback_SINGLE_CLICK_Handler); +button_attach(&button1, DOUBLE_CLICK, Callback_DOUBLE_Click_Handler); +... +``` +4.鍚姩鎸夐敭 + +```c +button_start(&button1); +``` +5.璁剧疆涓涓5ms闂撮殧鐨勫畾鏃跺櫒寰幆璋冪敤鍚庡彴澶勭悊鍑芥暟 + +```c +while(1) { + ... + if(timer_ticks == 5) { + timer_ticks = 0; + + button_ticks(); + } +} +``` + +## 鐗规 + +MultiButton 浣跨敤C璇█瀹炵幇锛屽熀浜庨潰鍚戝璞℃柟寮忚璁℃濊矾锛屾瘡涓寜閿璞″崟鐙敤涓浠芥暟鎹粨鏋勭鐞嗭細 + +```c +struct Button { + uint16_t ticks; + uint8_t repeat: 4; + uint8_t event : 4; + uint8_t state : 3; + uint8_t debounce_cnt : 3; + uint8_t active_level : 1; + uint8_t button_level : 1; + uint8_t button_id; + uint8_t (*hal_button_Level)(uint8_t button_id_); + BtnCallback cb[number_of_event]; + struct Button* next; +}; +``` +杩欐牱姣忎釜鎸夐敭浣跨敤鍗曞悜閾捐〃鐩歌繛锛屼緷娆¤繘鍏 button_handler(struct Button* handle) 鐘舵佹満澶勭悊锛屾墍浠ユ瘡涓寜閿殑鐘舵佸郊姝ょ嫭绔嬨 + + +## 鎸夐敭浜嬩欢 + +浜嬩欢 | 璇存槑 +---|--- +PRESS_DOWN | 鎸夐敭鎸変笅锛屾瘡娆℃寜涓嬮兘瑙﹀彂 +PRESS_UP | 鎸夐敭寮硅捣锛屾瘡娆℃澗寮閮借Е鍙 +PRESS_REPEAT | 閲嶅鎸変笅瑙﹀彂锛屽彉閲弐epeat璁℃暟杩炲嚮娆℃暟 +SINGLE_CLICK | 鍗曞嚮鎸夐敭浜嬩欢 +DOUBLE_CLICK | 鍙屽嚮鎸夐敭浜嬩欢 +LONG_PRESS_START | 杈惧埌闀挎寜鏃堕棿闃堝兼椂瑙﹀彂涓娆 +LONG_PRESS_HOLD | 闀挎寜鏈熼棿涓鐩磋Е鍙 + + +## Examples + +```c +#include "button.h" + +unit8_t btn1_id = 0; + +struct Button btn1; + +uint8_t read_button_GPIO(uint8_t button_id) +{ + // you can share the GPIO read function with multiple Buttons + switch(button_id) + { + case btn1_id: + return HAL_GPIO_ReadPin(B1_GPIO_Port, B1_Pin); + break; + + default: + return 0; + break; + } +} +void BTN1_PRESS_DOWN_Handler(void* btn) +{ + //do something... +} + +void BTN1_PRESS_UP_Handler(void* btn) +{ + //do something... +} + +... + +int main() +{ + button_init(&btn1, read_button_GPIO, 0, btn1_id); + button_attach(&btn1, PRESS_DOWN, BTN1_PRESS_DOWN_Handler); + button_attach(&btn1, PRESS_UP, BTN1_PRESS_UP_Handler); + button_attach(&btn1, PRESS_REPEAT, BTN1_PRESS_REPEAT_Handler); + button_attach(&btn1, SINGLE_CLICK, BTN1_SINGLE_Click_Handler); + button_attach(&btn1, DOUBLE_CLICK, BTN1_DOUBLE_Click_Handler); + button_attach(&btn1, LONG_PRESS_START, BTN1_LONG_PRESS_START_Handler); + button_attach(&btn1, LONG_PRESS_HOLD, BTN1_LONG_PRESS_HOLD_Handler); + button_start(&btn1); + + //make the timer invoking the button_ticks() interval 5ms. + //This function is implemented by yourself. + __timer_start(button_ticks, 0, 5); + + while(1) + {} +} +``` diff --git a/common/MultiButton/multi_button.c b/common/MultiButton/multi_button.c new file mode 100644 index 0000000..66d1020 --- /dev/null +++ b/common/MultiButton/multi_button.c @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2016 Zibin Zheng + * All rights reserved + */ + +#include "multi_button.h" + +#define EVENT_CB(ev) if(handle->cb[ev])handle->cb[ev]((void*)handle) +#define PRESS_REPEAT_MAX_NUM 15 /*!< The maximum value of the repeat counter */ + +//button handle list head. +static struct Button* head_handle = NULL; + +static void button_handler(struct Button* handle); + +/** + * @brief Initializes the button struct handle. + * @param handle: the button handle struct. + * @param pin_level: read the HAL GPIO of the connected button level. + * @param active_level: pressed GPIO level. + * @param button_id: the button id. + * @retval None + */ +void button_init(struct Button* handle, uint8_t(*pin_level)(uint8_t), uint8_t active_level, uint8_t button_id) +{ + memset(handle, 0, sizeof(struct Button)); + handle->event = (uint8_t)NONE_PRESS; + handle->hal_button_Level = pin_level; + handle->button_level = !active_level; + handle->active_level = active_level; + handle->button_id = button_id; +} + +/** + * @brief Attach the button event callback function. + * @param handle: the button handle struct. + * @param event: trigger event type. + * @param cb: callback function. + * @retval None + */ +void button_attach(struct Button* handle, PressEvent event, BtnCallback cb) +{ + handle->cb[event] = cb; +} + +/** + * @brief Inquire the button event happen. + * @param handle: the button handle struct. + * @retval button event. + */ +PressEvent get_button_event(struct Button* handle) +{ + return (PressEvent)(handle->event); +} + +/** + * @brief Button driver core function, driver state machine. + * @param handle: the button handle struct. + * @retval None + */ +static void button_handler(struct Button* handle) +{ + uint8_t read_gpio_level = handle->hal_button_Level(handle->button_id); + + //ticks counter working.. + if((handle->state) > 0) handle->ticks++; + + /*------------button debounce handle---------------*/ + if(read_gpio_level != handle->button_level) { //not equal to prev one + //continue read 3 times same new level change + if(++(handle->debounce_cnt) >= DEBOUNCE_TICKS) { + handle->button_level = read_gpio_level; + handle->debounce_cnt = 0; + } + } else { //level not change ,counter reset. + handle->debounce_cnt = 0; + } + + /*-----------------State machine-------------------*/ + switch (handle->state) { + case 0: + if(handle->button_level == handle->active_level) { //start press down + handle->event = (uint8_t)PRESS_DOWN; + EVENT_CB(PRESS_DOWN); + handle->ticks = 0; + handle->repeat = 1; + handle->state = 1; + } else { + handle->event = (uint8_t)NONE_PRESS; + } + break; + + case 1: + if(handle->button_level != handle->active_level) { //released press up + handle->event = (uint8_t)PRESS_UP; + EVENT_CB(PRESS_UP); + handle->ticks = 0; + handle->state = 2; + } else if(handle->ticks > LONG_TICKS) { + handle->event = (uint8_t)LONG_PRESS_START; + EVENT_CB(LONG_PRESS_START); + handle->state = 5; + } + break; + + case 2: + if(handle->button_level == handle->active_level) { //press down again + handle->event = (uint8_t)PRESS_DOWN; + EVENT_CB(PRESS_DOWN); + if(handle->repeat != PRESS_REPEAT_MAX_NUM) { + handle->repeat++; + } + EVENT_CB(PRESS_REPEAT); // repeat hit + handle->ticks = 0; + handle->state = 3; + } else if(handle->ticks > SHORT_TICKS) { //released timeout + if(handle->repeat == 1) { + handle->event = (uint8_t)SINGLE_CLICK; + EVENT_CB(SINGLE_CLICK); + } else if(handle->repeat == 2) { + handle->event = (uint8_t)DOUBLE_CLICK; + EVENT_CB(DOUBLE_CLICK); // repeat hit + } + handle->state = 0; + } + break; + + case 3: + if(handle->button_level != handle->active_level) { //released press up + handle->event = (uint8_t)PRESS_UP; + EVENT_CB(PRESS_UP); + if(handle->ticks < SHORT_TICKS) { + handle->ticks = 0; + handle->state = 2; //repeat press + } else { + handle->state = 0; + } + } else if(handle->ticks > SHORT_TICKS) { // SHORT_TICKS < press down hold time < LONG_TICKS + handle->state = 1; + } + break; + + case 5: + if(handle->button_level == handle->active_level) { + //continue hold trigger + handle->event = (uint8_t)LONG_PRESS_HOLD; + EVENT_CB(LONG_PRESS_HOLD); + } else { //released + handle->event = (uint8_t)PRESS_UP; + EVENT_CB(PRESS_UP); + handle->state = 0; //reset + } + break; + default: + handle->state = 0; //reset + break; + } +} + +/** + * @brief Start the button work, add the handle into work list. + * @param handle: target handle struct. + * @retval 0: succeed. -1: already exist. + */ +int button_start(struct Button* handle) +{ + struct Button* target = head_handle; + while(target) { + if(target == handle) return -1; //already exist. + target = target->next; + } + handle->next = head_handle; + head_handle = handle; + return 0; +} + +/** + * @brief Stop the button work, remove the handle off work list. + * @param handle: target handle struct. + * @retval None + */ +void button_stop(struct Button* handle) +{ + struct Button** curr; + for(curr = &head_handle; *curr; ) { + struct Button* entry = *curr; + if(entry == handle) { + *curr = entry->next; +// free(entry); + return;//glacier add 2021-8-18 + } else { + curr = &entry->next; + } + } +} + +/** + * @brief background ticks, timer repeat invoking interval 5ms. + * @param None. + * @retval None + */ +void button_ticks(void) +{ + struct Button* target; + for(target=head_handle; target; target=target->next) { + button_handler(target); + } +} diff --git a/common/MultiButton/multi_button.h b/common/MultiButton/multi_button.h new file mode 100644 index 0000000..839e903 --- /dev/null +++ b/common/MultiButton/multi_button.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2016 Zibin Zheng + * All rights reserved + */ + +#ifndef _MULTI_BUTTON_H_ +#define _MULTI_BUTTON_H_ + +#include +#include + +//According to your need to modify the constants. +#define TICKS_INTERVAL 5 //ms +#define DEBOUNCE_TICKS 3 //MAX 7 (0 ~ 7) +#define SHORT_TICKS (400 /TICKS_INTERVAL) +#define LONG_TICKS (1500 /TICKS_INTERVAL) + + +typedef void (*BtnCallback)(void*); + +typedef enum { + PRESS_DOWN = 0, + PRESS_UP, + PRESS_REPEAT, + SINGLE_CLICK, + DOUBLE_CLICK, + LONG_PRESS_START, + LONG_PRESS_HOLD, + number_of_event, + NONE_PRESS +}PressEvent; + +typedef struct Button { + uint16_t ticks; + uint8_t repeat : 4; + uint8_t event : 4; + uint8_t state : 3; + uint8_t debounce_cnt : 3; + uint8_t active_level : 1; + uint8_t button_level : 1; + uint8_t button_id; + uint8_t (*hal_button_Level)(uint8_t button_id_); + BtnCallback cb[number_of_event]; + struct Button* next; +}Button; + +#ifdef __cplusplus +extern "C" { +#endif + +void button_init(struct Button* handle, uint8_t(*pin_level)(uint8_t), uint8_t active_level, uint8_t button_id); +void button_attach(struct Button* handle, PressEvent event, BtnCallback cb); +PressEvent get_button_event(struct Button* handle); +int button_start(struct Button* handle); +void button_stop(struct Button* handle); +void button_ticks(void); + +#ifdef __cplusplus +} +#endif + +#endif