日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當(dāng)前位置:首頁(yè) > 嵌入式 > 嵌入式云IOT技術(shù)圈
[導(dǎo)讀]嵌入式開(kāi)源項(xiàng)目精選專欄 本專欄由Mculover666創(chuàng)建,主要內(nèi)容為尋找嵌入式領(lǐng)域內(nèi)的優(yōu)質(zhì)開(kāi)源項(xiàng)目,一是幫助開(kāi)發(fā)者使用開(kāi)源項(xiàng)目實(shí)現(xiàn)更多的功能,二是通過(guò)這些開(kāi)源項(xiàng)目,學(xué)習(xí)大佬的代碼及背后的實(shí)現(xiàn)思想,提升自己的代碼水平,和其它專欄相比,本專欄的優(yōu)勢(shì)在于:

嵌入式開(kāi)源項(xiàng)目精選專欄

本專欄由Mculover666創(chuàng)建,主要內(nèi)容為尋找嵌入式領(lǐng)域內(nèi)的優(yōu)質(zhì)開(kāi)源項(xiàng)目,一是幫助開(kāi)發(fā)者使用開(kāi)源項(xiàng)目實(shí)現(xiàn)更多的功能,二是通過(guò)這些開(kāi)源項(xiàng)目,學(xué)習(xí)大佬的代碼及背后的實(shí)現(xiàn)思想,提升自己的代碼水平,和其它專欄相比,本專欄的優(yōu)勢(shì)在于:

不會(huì)單純的介紹分享項(xiàng)目,還會(huì)包含作者親自實(shí)踐的過(guò)程分享,甚至還會(huì)有對(duì)它背后的設(shè)計(jì)思想解讀。

目前本專欄包含的開(kāi)源項(xiàng)目有:

  • SFUD | 一個(gè)簡(jiǎn)潔實(shí)用的開(kāi)源項(xiàng)目,幫你輕松搞定SPI Flash
  • cJSON | 一個(gè)輕量級(jí)C語(yǔ)言JSON解析器
  • paho | 支持10種語(yǔ)言編寫(xiě)mqtt客戶端,總有一款適合你!

如果您自己編寫(xiě)或者發(fā)現(xiàn)的開(kāi)源項(xiàng)目不錯(cuò),歡迎留言或者私信投稿到本專欄,分享獲得雙倍的快樂(lè)!

1. MultiButton

本期給大家?guī)?lái)的開(kāi)源項(xiàng)目是 MultiButton,一個(gè)小巧簡(jiǎn)單易用的事件驅(qū)動(dòng)型按鍵驅(qū)動(dòng)模塊,作者 0x1abin,目前收獲 222 個(gè)star,遵循 MIT 開(kāi)源許可。

這個(gè)項(xiàng)目非常精簡(jiǎn),只有兩個(gè)文件,可無(wú)限量擴(kuò)展按鍵,按鍵事件的回調(diào)異步處理方式可以簡(jiǎn)化程序結(jié)構(gòu),去除冗余的按鍵處理硬編碼,讓你的按鍵業(yè)務(wù)邏輯更清晰。MuliButton 支持如下的按鈕事件:

事件 說(shuō)明
PRESS_DOWN 按鍵按下,每次按下都觸發(fā)
PRESS_UP 按鍵彈起,每次松開(kāi)都觸發(fā)
PRESS_REPEAT 重復(fù)按下觸發(fā),變量repeat計(jì)數(shù)連擊次數(shù)
SINGLE_CLICK 單擊按鍵事件
DOUBLE_CLICK 雙擊按鍵事件
LONG_RRESS_START 達(dá)到長(zhǎng)按時(shí)間閾值時(shí)觸發(fā)一次
LONG_PRESS_HOLD 長(zhǎng)按期間一直觸發(fā)

GIthub地址:https://github.com/0x1abin/MultiButton

2. 使用MultiButton

2.1. 準(zhǔn)備一份裸機(jī)工程

需要掌握使用HAL庫(kù)讀取GPIO輸入的函數(shù)、串口的使用、printf重定向、以及systick的使用:

  • STM32CubeMX | 04-使用GPIO進(jìn)行按鍵檢測(cè)
  • STM32CubeMX | 06-使用USART發(fā)送和接收數(shù)據(jù)(查詢模式)
  • STM32CubeMX | 09-重定向printf函數(shù)到串口輸出的多種方法
  • STM32CubeMX | 27-系統(tǒng)滴答定時(shí)器Systick的使用

本文中我使用小熊派IoT開(kāi)發(fā)板,主控為STM32L431RCT6:配置外部時(shí)鐘:按鍵GPIO配置:打印串口配置:時(shí)鐘配置:

配置工程,生成代碼,重定向printf,printf可以正常打印后進(jìn)行下面的步驟。

2.2. 移植MultiButton

① 復(fù)制MultiButton源碼到裸機(jī)工程中:② 添加MultiButton源碼到項(xiàng)目中:此時(shí)編譯沒(méi)有問(wèn)題。

2.3. 編寫(xiě)MultiButton應(yīng)用代碼

在main.c文件中編寫(xiě)以下代碼。

① 包含頭文件

/* USER CODE BEGIN Includes */

#include <stdio.h> //要使用printf
#include "multi_button.h"

/* USER CODE END Includes */

② 定義一個(gè)按鍵結(jié)構(gòu)(按鍵對(duì)象)

/* USER CODE BEGIN PV */

//申請(qǐng)一個(gè)按鍵結(jié)構(gòu)
struct Button button1;

/* USER CODE END PV */

③ 初始化按鍵對(duì)象

初始化按鍵對(duì)象使用的API為:

  • 第一個(gè)參數(shù)為剛剛創(chuàng)建的按鍵對(duì)象的指針;
  • 第二個(gè)參數(shù)為綁定按鍵的GPIO電平讀取接口;
  • 第三個(gè)參數(shù)為設(shè)置有效觸發(fā)電平;

首先在main函數(shù)之前實(shí)現(xiàn)一個(gè)GPIO電平讀取接口:

/* USER CODE BEGIN 0 */

//按鍵狀態(tài)讀取接口
uint8_t read_button1_GPIO()
{
return HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin);
}

/* USER CODE END 0 */

初始化按鍵對(duì)象的代碼在main函數(shù)中,while(1)之前編寫(xiě),如下:

/* USER CODE BEGIN 2 */
printf("MultiButton Test...\r\n");

//初始化按鍵對(duì)象
button_init(&button1, read_button1_GPIO, 0);

/* USER CODE END 2 */

④ 注冊(cè)按鍵事件

注冊(cè)按鈕事件的API如下:

  • 第一個(gè)參數(shù)為按鈕對(duì)象指針;
  • 第二個(gè)參數(shù)為MultiButton支持的按鈕事件;
  • 第三個(gè)參數(shù)為要注冊(cè)的該事件回調(diào)函數(shù);

MultiButton支持的按鈕事件枚舉如下:首先在main函數(shù)之前定義這兩個(gè)事件的回調(diào)函數(shù),回調(diào)函數(shù)有兩種寫(xiě)法。

第一種適合于按鍵事件較少的情況

//按鍵1按下事件回調(diào)函數(shù)
void btn1_press_down_Handler(void* btn)
{
printf("---> key1 press down! <---\r\n");
}

//按鍵1松開(kāi)事件回調(diào)函數(shù)
void btn1_press_up_Handler(void* btn)
{
printf("***> key1 press up! <***\r\n");
}

在main函數(shù)中,while(1)之前注冊(cè)這兩個(gè)回調(diào)函數(shù):

//注冊(cè)按鈕事件回調(diào)函數(shù)
button_attach(&button1, PRESS_DOWN, btn1_press_down_Handler);
button_attach(&button1, PRESS_UP, btn1_press_up_Handler);

第二種適合于按鍵事件較多的情況,如果每個(gè)按鍵都要寫(xiě) 7 個(gè)回調(diào)函數(shù),那么代碼量會(huì)非常的大,所以可以將這 7 個(gè)回調(diào)函數(shù)寫(xiě)在一起,一次性全部注冊(cè),回調(diào)函數(shù)如下:

void button_callback(void *button)
{
uint32_t btn_event_val;

btn_event_val = get_button_event((struct Button *)button);

switch(btn_event_val)
{
case PRESS_DOWN:
printf("---> key1 press down! <---\r\n");
break;

case PRESS_UP:
printf("***> key1 press up! <***\r\n");
break;

case PRESS_REPEAT:
printf("---> key1 press repeat! <---\r\n");
break;

case SINGLE_CLICK:
printf("---> key1 single click! <---\r\n");
break;

case DOUBLE_CLICK:
printf("***> key1 double click! <***\r\n");
break;

case LONG_RRESS_START:
printf("---> key1 long press start! <---\r\n");
break;

case LONG_PRESS_HOLD:
printf("***> key1 long press hold! <***\r\n");
break;
}
}

使用這種回調(diào)函數(shù)的時(shí)候需要在MultiButton的源碼中添加一行代碼:注冊(cè)回調(diào)函數(shù)的代碼如下:

//注冊(cè)按鈕事件回調(diào)函數(shù)

button_attach(&button1, PRESS_DOWN, button_callback);
button_attach(&button1, PRESS_UP, button_callback);
//button_attach(&button1, PRESS_REPEAT, button_callback);
//button_attach(&button1, SINGLE_CLICK, button_callback);
//button_attach(&button1, DOUBLE_CLICK, button_callback);
//button_attach(&button1, LONG_RRESS_START, button_callback);
//button_attach(&button1, LONG_PRESS_HOLD, button_callback);

⑤ 啟動(dòng)按鍵 啟動(dòng)按鍵的API如下:接著在main函數(shù)中,while(1)之前編寫(xiě)代碼,啟動(dòng)按鍵:

//啟動(dòng)按鍵
button_start(&button1);

⑥ 設(shè)置一個(gè)5ms間隔的定時(shí)器循環(huán)調(diào)用后臺(tái)處理函數(shù)

這里就要用到systick了,在main函數(shù)的while(1)循環(huán)中編寫(xiě)如下代碼:

  /* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */
//每隔5ms調(diào)用一次后臺(tái)處理函數(shù)
button_ticks();
HAL_Delay(5);
}
/* USER CODE END 3 */

2.4. 實(shí)驗(yàn)現(xiàn)象

編譯、下載之后,每次按下Key1時(shí)打印按下提示,松開(kāi)Key1時(shí)打印松開(kāi)提示:

2.5. 擴(kuò)展實(shí)驗(yàn)

在注冊(cè)回調(diào)函數(shù)時(shí)將這按下和松開(kāi)屏蔽,將單擊和雙擊打開(kāi)進(jìn)行測(cè)試:

//注冊(cè)按鈕事件回調(diào)函數(shù)
//button_attach(&button1, PRESS_DOWN, button_callback);
//button_attach(&button1, PRESS_UP, button_callback);
//button_attach(&button1, PRESS_REPEAT, button_callback);
button_attach(&button1, SINGLE_CLICK, button_callback);
button_attach(&button1, DOUBLE_CLICK, button_callback);
//button_attach(&button1, LONG_RRESS_START, button_callback);
//button_attach(&button1, LONG_PRESS_HOLD, button_callback);

再測(cè)試長(zhǎng)按:

	//注冊(cè)按鈕事件回調(diào)函數(shù)
//button_attach(&button1, PRESS_DOWN, button_callback);
//button_attach(&button1, PRESS_UP, button_callback);
//button_attach(&button1, PRESS_REPEAT, button_callback);
//button_attach(&button1, SINGLE_CLICK, button_callback);
//button_attach(&button1, DOUBLE_CLICK, button_callback);
button_attach(&button1, LONG_RRESS_START, button_callback);
button_attach(&button1, LONG_PRESS_HOLD, button_callback);

3. MultiButton設(shè)計(jì)思想解讀

3.1. 面向?qū)ο笏枷?/span>

MultiButton中每個(gè)按鍵都抽象為了一個(gè)按鍵對(duì)象,每個(gè)按鍵對(duì)象是獨(dú)立的,系統(tǒng)中所有的按鍵對(duì)象使用單鏈表串起來(lái),結(jié)構(gòu)如下:其中在變量后面跟冒號(hào)的語(yǔ)法稱為位域,使用位域的優(yōu)勢(shì)是節(jié)省內(nèi)存

比如在這個(gè)結(jié)構(gòu)體中,本來(lái) 6 個(gè)uint8_t 類(lèi)型的變量需要占用 6 個(gè)字節(jié),但使用位域語(yǔ)法后,這6個(gè)變量只占用兩個(gè)字節(jié)

3.2. 按鍵對(duì)象單鏈表

MultiButton自己定義了一個(gè)頭指針

//button handle list head.
static struct Button* head_handle = NULL;

用戶插入一個(gè)按鍵對(duì)象的代碼如下:

//啟動(dòng)按鍵
button_start(&button1);

那么,button_start插入新的按鍵對(duì)象之后,單鏈表長(zhǎng)啥樣呢?

理解了 button_start 的源碼就很好知道答案了:第一次插入時(shí),因?yàn)閔ead_hanler 為 NULL,所以只需要執(zhí)行while之后的代碼,按照它的插入于原理,如果再插入一個(gè)buuton2按鍵對(duì)象,結(jié)果是不是可以猜出來(lái)了呢?

沒(méi)錯(cuò),它長(zhǎng)這樣:這樣做是不是有點(diǎn)不符合常理?后插入Button2竟然在button1前面,憑什么?

這又不是排隊(duì)搶雞蛋,在前在后沒(méi)什么關(guān)系的。只是這樣的插入方法在代碼算法上會(huì)非常簡(jiǎn)潔,兩行代碼完成插入。

3.3. 狀態(tài)機(jī)處理思想

MultiButton中使用狀態(tài)機(jī)來(lái)處理每個(gè)按鍵對(duì)象(的狀態(tài)),比如在上述應(yīng)用中根據(jù)Systick提供的時(shí)基信號(hào),每隔5ms調(diào)用一次 button_tick(),該函數(shù)會(huì)依次調(diào)用狀態(tài)機(jī)對(duì)單鏈表上的所有按鍵對(duì)象進(jìn)行遍歷處理:根據(jù)上一節(jié)的單鏈表講解,系統(tǒng)中定義的鏈表頭指針 head_handle 永遠(yuǎn)指向最后一個(gè)插入的按鍵對(duì)象,所以無(wú)需任何參數(shù)即可遍歷整個(gè)單鏈表上的對(duì)象,非常之牛逼。

使用 button_handler 來(lái)對(duì)按鍵對(duì)象的狀態(tài)進(jìn)行處理,該函數(shù)源碼如下:

(讀源碼的時(shí)候只需要記住該函數(shù)每隔5ms進(jìn)入一次就很好分析了)

① 讀取當(dāng)前引腳狀態(tài)

調(diào)用該按鍵對(duì)象注冊(cè)的讀取狀態(tài)函數(shù)進(jìn)行讀?。孩?讀取之后,判斷當(dāng)前狀態(tài)機(jī)的狀態(tài),如果有功能正在執(zhí)行(state不為0),則按鍵對(duì)象的tick值加1(后續(xù)一切功能的基礎(chǔ)):③ 按鍵消抖(連續(xù)讀取3次,15ms,如果引腳狀態(tài)一直與之前不同,則改變按鍵對(duì)象中的引腳狀態(tài)):④ 狀態(tài)機(jī)(整個(gè)設(shè)計(jì)的靈魂所在)

4. 項(xiàng)目工程源碼獲取和問(wèn)題交流

目前我將MultiButton源碼、我移植到小熊派STM32L431RCT6開(kāi)發(fā)板的工程、移植到STM32Nucleo-STM32G071RB開(kāi)發(fā)板的工程源碼上傳到了QQ群里(包含好幾份HAL庫(kù),QQ相對(duì)速度快點(diǎn)),可以在QQ群里下載,移植有問(wèn)題也可以在群里交流,當(dāng)然也歡迎大家分享出來(lái)自己移植的工程到QQ群里


放上QQ群二維碼:


接收更多精彩文章及資源推送,歡迎訂閱我的微信公眾號(hào):『mculover666』。


免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

LED驅(qū)動(dòng)電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: 驅(qū)動(dòng)電源

在工業(yè)自動(dòng)化蓬勃發(fā)展的當(dāng)下,工業(yè)電機(jī)作為核心動(dòng)力設(shè)備,其驅(qū)動(dòng)電源的性能直接關(guān)系到整個(gè)系統(tǒng)的穩(wěn)定性和可靠性。其中,反電動(dòng)勢(shì)抑制與過(guò)流保護(hù)是驅(qū)動(dòng)電源設(shè)計(jì)中至關(guān)重要的兩個(gè)環(huán)節(jié),集成化方案的設(shè)計(jì)成為提升電機(jī)驅(qū)動(dòng)性能的關(guān)鍵。

關(guān)鍵字: 工業(yè)電機(jī) 驅(qū)動(dòng)電源

LED 驅(qū)動(dòng)電源作為 LED 照明系統(tǒng)的 “心臟”,其穩(wěn)定性直接決定了整個(gè)照明設(shè)備的使用壽命。然而,在實(shí)際應(yīng)用中,LED 驅(qū)動(dòng)電源易損壞的問(wèn)題卻十分常見(jiàn),不僅增加了維護(hù)成本,還影響了用戶體驗(yàn)。要解決這一問(wèn)題,需從設(shè)計(jì)、生...

關(guān)鍵字: 驅(qū)動(dòng)電源 照明系統(tǒng) 散熱

根據(jù)LED驅(qū)動(dòng)電源的公式,電感內(nèi)電流波動(dòng)大小和電感值成反比,輸出紋波和輸出電容值成反比。所以加大電感值和輸出電容值可以減小紋波。

關(guān)鍵字: LED 設(shè)計(jì) 驅(qū)動(dòng)電源

電動(dòng)汽車(chē)(EV)作為新能源汽車(chē)的重要代表,正逐漸成為全球汽車(chē)產(chǎn)業(yè)的重要發(fā)展方向。電動(dòng)汽車(chē)的核心技術(shù)之一是電機(jī)驅(qū)動(dòng)控制系統(tǒng),而絕緣柵雙極型晶體管(IGBT)作為電機(jī)驅(qū)動(dòng)系統(tǒng)中的關(guān)鍵元件,其性能直接影響到電動(dòng)汽車(chē)的動(dòng)力性能和...

關(guān)鍵字: 電動(dòng)汽車(chē) 新能源 驅(qū)動(dòng)電源

在現(xiàn)代城市建設(shè)中,街道及停車(chē)場(chǎng)照明作為基礎(chǔ)設(shè)施的重要組成部分,其質(zhì)量和效率直接關(guān)系到城市的公共安全、居民生活質(zhì)量和能源利用效率。隨著科技的進(jìn)步,高亮度白光發(fā)光二極管(LED)因其獨(dú)特的優(yōu)勢(shì)逐漸取代傳統(tǒng)光源,成為大功率區(qū)域...

關(guān)鍵字: 發(fā)光二極管 驅(qū)動(dòng)電源 LED

LED通用照明設(shè)計(jì)工程師會(huì)遇到許多挑戰(zhàn),如功率密度、功率因數(shù)校正(PFC)、空間受限和可靠性等。

關(guān)鍵字: LED 驅(qū)動(dòng)電源 功率因數(shù)校正

在LED照明技術(shù)日益普及的今天,LED驅(qū)動(dòng)電源的電磁干擾(EMI)問(wèn)題成為了一個(gè)不可忽視的挑戰(zhàn)。電磁干擾不僅會(huì)影響LED燈具的正常工作,還可能對(duì)周?chē)娮釉O(shè)備造成不利影響,甚至引發(fā)系統(tǒng)故障。因此,采取有效的硬件措施來(lái)解決L...

關(guān)鍵字: LED照明技術(shù) 電磁干擾 驅(qū)動(dòng)電源

開(kāi)關(guān)電源具有效率高的特性,而且開(kāi)關(guān)電源的變壓器體積比串聯(lián)穩(wěn)壓型電源的要小得多,電源電路比較整潔,整機(jī)重量也有所下降,所以,現(xiàn)在的LED驅(qū)動(dòng)電源

關(guān)鍵字: LED 驅(qū)動(dòng)電源 開(kāi)關(guān)電源

LED驅(qū)動(dòng)電源是把電源供應(yīng)轉(zhuǎn)換為特定的電壓電流以驅(qū)動(dòng)LED發(fā)光的電壓轉(zhuǎn)換器,通常情況下:LED驅(qū)動(dòng)電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: LED 隧道燈 驅(qū)動(dòng)電源
關(guān)閉