摘要:今天繼續來聊下回調函數。輸入型輸入型函數一般是用在不同文件不同層硬件層應用層之間傳遞信號和數據的,比如說按鍵檢測串口數據。最終就是把這個指針指向別的文件的函數,從而實現不同文件之間的數據傳遞,同時又能保持很好的可移植性相互獨立,互不干擾。
大家好,我是無際。
今天繼續來聊下回調函數。
之前寫過一篇受到了廣大老鐵們的認可。
最近有幾個新學員被回調函數搞得有點懵逼。
不理解為什么要搞這種繞來繞去、指針指來指去的函數。
先寫篇文章預熱一下,晚上再直播跟大家互動講解和答疑。
其實并不是我想把簡單的東西復雜化,而是如果你想寫出好的代碼架構,回調函數是必不可少的。
如果你去看那些大神寫的程序,你會發現他們都是這樣做的,比如說藍牙協議棧、實時操作系統、STM32固件庫等等。
每個人寫得風格可能不一樣,但是本質是一樣的。
我們先來理解一下回調函數的作用。
函數我一般喜歡分為輸出型和輸入型(個人理解)。
輸出型:
就是我們主動去調用的控制函數,比如說控制LED燈去亮和滅,控制蜂鳴器響和不響,控制LCD顯示,控制繼電器吸合和斷開。
簡單來說,就是我們知道什么時候該去調用這些函數,比如說滿足某些條件的時候,我們就會主動去調用這些函數。
這種函數,就是輸出型函數。
輸入型:
輸入型函數一般是用在不同.c文件/不同層(硬件層、應用層)之間傳遞信號和數據的,比如說按鍵檢測、串口數據。
我們不知道什么時候按鍵會被按下、什么時候串口會有數據過來對吧?
當然,我們可以寫一個帶返回值的函數,然后定時去檢測,比如說定時10ms去掃描一下按鍵。
Unsigned char ScanKey()
{
//按鍵檢測程序…
}
然后我們在主程序用:
while(1)
{
Unsigned char key;
If(10ms時間到)
{
Key = ScanKey();
}
?????? if(Key == 有效按鍵值)
?????? {
????????????? //執行按鍵功能程序
}
}
這樣不斷地去掃描按鍵,檢測按鍵是否被按下。
這種方式當然也是可以的,只是不夠專業,不夠好。
因為這個我需要一直在while循環里判斷Key的值,然后根據Key的值來判斷有沒有按鍵按下,在一定程度上,造成了cpu資源的浪費。
而且有些應用場景,這種方式不好實現,比如說串口數據,你不能一直在while循環里判斷是否有新的串口數據過來吧?
那我們理想的一種狀態是什么?
就是如果有按鍵按下了,或者有新的數據來了,再通知我。
這種通知方式一般叫事件觸發,就是觸發了按鍵這個事件,我才去處理。
所以,這個時候回調函數就能很好地解決這種需求。
我們還是拿按鍵來舉例。
前面我說每個人寫回調函數的風格可能都不一樣,STM32固件庫的那些中斷處理函數基本都是回調函數,但是跟我的編寫風格還是有些差異。
我們在寫回調函數的時候,需要以下幾步:
第一步:
自定義一個函數指針類型,類型名稱是KeyEvent_CallBack_t。
typedef void (*KeyEvent_CallBack_t)(KEY_VALUE_TYPEDEF keys);
還有這個一般是要自定義在頭文件,因為別的.c文件也會用到。
這是一個無返回值的,形參是KEY_VALUE_TYPEDEF枚舉類型的函數指針類型。
一般這個形參keys就是我們最終要通過回調函數傳遞到別的.c文件的信號/數據,如果是按鍵檢測的話也就是按鍵值,是哪個按鍵按下的。
我們來看下KEY_VALUE_TYPEDEF這個枚舉都有哪些值?
typedef enum
{
?????? KEY_IDLE_VAL,
?????? KEY1_CLICK,
?????? KEY1_CLICK_RELEASE,
?????? KEY1_LONG_PRESS,
?????? KEY1_LONG_PRESS_CONTINUOUS,
?????? KEY1_LONG_PRESS_RELEASE,?????????? //5
?????? KEY2_CLICK,?????????????????????????????????????????????????? //6
?????? KEY2_CLICK_RELEASE,
?????? KEY2_LONG_PRESS,
?????? KEY2_LONG_PRESS_CONTINUOUS,
?????? KEY2_LONG_PRESS_RELEASE,
?????? KEY3_CLICK,??????????????????????????????????????????? //11
?????? KEY3_CLICK_RELEASE,
?????? KEY3_LONG_PRESS,
?????? KEY3_LONG_PRESS_CONTINUOUS,
?????? KEY3_LONG_PRESS_RELEASE,
?????? KEY4_CLICK,???????????????????????????????????? //16
?????? KEY4_CLICK_RELEASE,
?????? KEY4_LONG_PRESS,
?????? KEY4_LONG_PRESS_CONTINUOUS,
?????? KEY4_LONG_PRESS_RELEASE,
?????? KEY5_CLICK,???????????????????????????????????? //21
?????? KEY5_CLICK_RELEASE,
?????? KEY5_LONG_PRESS,
?????? KEY5_LONG_PRESS_CONTINUOUS,
?????? KEY5_LONG_PRESS_RELEASE,
?????? KEY6_CLICK,???????????????????????????????????? //26
?????? KEY6_CLICK_RELEASE,
?????? KEY6_LONG_PRESS,
?????? KEY6_LONG_PRESS_CONTINUOUS,
?????? KEY6_LONG_PRESS_RELEASE,
}KEY_VALUE_TYPEDEF;
我們這個項目總共有6個按鍵,每個按鍵需要檢測短按、短按釋放、長按、長按釋放、連續長按這5個功能,所以總共有30個不同的枚舉值分別來對應不同按鍵的不同功能。
第二步:
自定義了函數指針類型以后,我們就可以通過KeyEvent_CallBack_t這個類型名稱,去定義我們的函數指針變量。
KeyEvent_CallBack_t KeyScanCBS;
那KeyScanCBS就是函數指針,所以它的返回值是void類型,形參是KEY_VALUE_TYPEDEF枚舉類型的。
最終就是把這個指針指向別的.c文件的函數,從而實現不同.c文件之間的數據傳遞,同時又能保持很好的可移植性(相互獨立,互不干擾)。
那怎么指向呢?我的方法是重新定義一個函數,專門來為這個指針指向,這樣方便別的.c文件調用,這個函數我稱為注冊函數。
比如以下函數:
void hal_KeyScanCBSRegister(KeyEvent_CallBack_t pCBS)
{
?????? if(KeyScanCBS == 0)
?????? {
???????????????????? KeyScanCBS = pCBS;
?????? }
}?????
這個函數的作用就是把我們前面定義的KeyScanCBS函數指針指向外部的函數地址(也就是要指向那個函數的函數名)。
當然,這個函數不是必須的,只是我的思維和代碼風格,你也可以不多帶帶寫這樣的函數,只要用之前把KeyScanCBS指向外部函數就可以了,否則等著程序死機吧哈哈哈。
第三步:
準備好這幾步以后,我們繼續來說下怎么去使用它。
我們哪里要用到按鍵的功能,就在那個.c文件那里重寫一個同樣的函數。
比如說app.c這個文件是產品功能代碼(應用層),我需要在應用層使用按鍵功能。
重寫函數的時候,返回值和形參要跟那個函數指針類型一樣。
如果你忘記了,那我們再來回顧下。
typedef void (*KeyEvent_CallBack_t)(KEY_VALUE_TYPEDEF keys);
無返回值,形參為KEY_VALUE_TYPEDEF類型。
只有這樣,你才能把這個函數的地址賦值給KeyScanCBS這個指針,才能正常傳遞數據。
重寫的這個函數就是通過形參來接收硬件層按鍵值的,如果是串口數據,也是同理,只是形參不一樣。
然后,我們在產品功能初始化的函數里直接調用剛剛hal_key.c的注冊函數。
把KeyEventHandle這個函數的地址賦值給hal_key.c的KeyScanCBS這個函數指針。
所以,最終KeyScanCBS可以理解成等同于KeyEventHandle函數。
我們在hal_key.c文件里,看按鍵檢測解析程序,最終就是執行KeyScanCBS把我們keys(按鍵值)傳遞到我們app.c文件的。
這樣,就能做到以事件去驅動,只有按鍵按下,并且真實有效,我才會調用KeyScanCBS,才會把按鍵值傳遞給應用層。
而中間,兩個文件之間沒有任何全局變量的依賴,也完全可以獨立,大家可以細品消化一下。
這里有個細節就是為什么我函數的形參要用枚舉類型。
如果你對接過一些模塊(WiFi、藍牙等)二次開發就知道了,模塊核心代碼都是封裝成lib這種庫給你的,你并看不到源代碼。
只能用他們的函數,如果不用枚舉,那你不知道形參可以傳入什么值對吧?
如果用枚舉,我把能用的值都列出來給你,并且起好名字,讓你一看就知道是啥意思,這是不是就很方便?
Ok,今天就寫到這里,大家下去可以做下實驗。
原創不易,盡量用最通俗的語言表達,如果對你有幫助,麻煩安排個三連吧^ ^。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/121232.html
摘要:異步通信與同步通信異步通信異步通信是指通信的發送與接收設備使用各自的時鐘控制數據的發送和接收過程。同步通信同步通信時要建立發送方時鐘對接收方時鐘的直接控制,使雙方達到完全同步。配置串口設置為異步通信基礎參數波特率為。 ...
摘要:那么問題來了,單片機和之間的串口通信屬于哪種通信制式呢答案是全雙工,從單片機上有和兩個口就可以知道最后要講的一個重要的概念叫波特率。 ????????對于剛剛接觸單片機的同學們來說,串口通信似乎是一個神秘感十足的東西,筆者在剛剛開始學習51單片機時,讀的是郭天祥先生的那本著名的《新概念51單...
摘要:在云計算剛進入中國的時候,成功地把握住了職業轉型的機會,在實踐中成長為優秀的架構師。技術人攻略在工作中遇到最大的挑戰是什么做云計算的難點在什么地方挑戰最大的是在工作的時候,要從頭到尾搭一套以為基礎的云計算平臺。 showImg(https://segmentfault.com/img/remote/1460000006889503); 導語:本期采訪對象李雨來@Blackte...
閱讀 1277·2021-09-27 13:35
閱讀 2576·2021-09-06 15:12
閱讀 3393·2019-08-30 15:55
閱讀 2846·2019-08-30 15:43
閱讀 444·2019-08-29 16:42
閱讀 3455·2019-08-29 15:39
閱讀 3074·2019-08-29 12:28
閱讀 1253·2019-08-29 11:11