關(guān)鍵字:回調(diào)函數(shù),HAL庫(kù)
目錄預(yù)覽
1.回調(diào)函數(shù)
2.STM32固件庫(kù)里的回調(diào)函數(shù)
3.STM32庫(kù)函數(shù)里的回調(diào)機(jī)制及觸發(fā)事件
4.常見(jiàn)問(wèn)題
01 回調(diào)函數(shù)
有人對(duì)STM32固件庫(kù)里的回調(diào)函數(shù)有些好奇甚至糾結(jié),這里簡(jiǎn)單地介紹下,以供參考。其實(shí)從用法及功能上講他們并沒(méi)有什么特別的,跟其他函數(shù)一樣,也是實(shí)現(xiàn)特定功能的代碼段。一般來(lái)講,所謂回調(diào)函數(shù),泛指基于事件觸發(fā)而被調(diào)用執(zhí)行的函數(shù),簡(jiǎn)單點(diǎn)說(shuō),就是條件滿(mǎn)足了就調(diào)用的函數(shù),往往會(huì)跟函數(shù)指針結(jié)合起來(lái)通過(guò)函數(shù)指針實(shí)現(xiàn)調(diào)用。
經(jīng)常會(huì)有人基于類(lèi)似下面的代碼介紹回調(diào)函數(shù):
在上面代碼中,那四個(gè)有關(guān)加減乘除的函數(shù)可以看成回調(diào)函數(shù),具體何時(shí)被調(diào)用,根據(jù)函數(shù)Compute(float a,float b,float(*Action)(float a,float b))里的函數(shù)指針的賦值情況來(lái)定,被賦予哪個(gè)回調(diào)函數(shù)的地址就調(diào)用哪個(gè)回調(diào)函數(shù)。當(dāng)然,使用函數(shù)指針并不是回調(diào)函數(shù)的核心特征,因事件驅(qū)動(dòng)而被調(diào)用才是其核心特征。
生活中我們有時(shí)會(huì)對(duì)某人說(shuō),回頭再談、回頭再聊。潛臺(tái)詞往往就是等時(shí)機(jī)成熟了、條件滿(mǎn)足了再來(lái)具體交涉。這里就充滿(mǎn)著濃濃的回調(diào)意味。
回調(diào)函數(shù)可以理解為事件響應(yīng)函數(shù)或者說(shuō)事件驅(qū)動(dòng)函數(shù)。即使相同的事件、基于不同的場(chǎng)景可能會(huì)有不同應(yīng)對(duì)處理,從軟件代碼角度講就對(duì)應(yīng)不同的回調(diào)函數(shù)代碼。
我們不妨看個(gè)生活中的例子。生活中有人中了六合彩了,針對(duì)這一事件,中獎(jiǎng)人可能有下面諸多舉動(dòng)之一【這里簡(jiǎn)化下,多選一】。但這件事發(fā)生在不同人身上,右邊的選擇很可能不盡一樣。換言之,中獎(jiǎng)了,到底會(huì)選擇右邊哪一項(xiàng)還得結(jié)合具體的人來(lái)定。
圖1 中六合彩的可能后續(xù)行為
我們?cè)偾袚Q到STM32的嵌入式開(kāi)發(fā)中來(lái),以UART接收完成事件為例。針對(duì)這一事件,不同的應(yīng)用場(chǎng)景的應(yīng)對(duì)處理往往也是五花八門(mén)、五彩繽紛。
圖2 UART接收完成后可能后續(xù)動(dòng)作
顯然,特定的應(yīng)用場(chǎng)景對(duì)應(yīng)著特定的回調(diào)函數(shù),一般來(lái)講,沒(méi)法簡(jiǎn)單地僅僅基于事件就擬定一段既能適用于各種場(chǎng)景而又富有針對(duì)性的代碼。
結(jié)合上面的描述,稍微小結(jié)下?;卣{(diào)函數(shù)除了具有基于事件的觸發(fā)而被調(diào)用執(zhí)行的特征外,還具有相同事件因應(yīng)不同應(yīng)用場(chǎng)景可能需要不同的回調(diào)函數(shù)之特征,即基于特定應(yīng)用場(chǎng)景的回調(diào)函數(shù)其內(nèi)容具有特定性。
02
STM32固件庫(kù)里的回調(diào)函數(shù)
說(shuō)到這里,我們具體結(jié)合STM32外設(shè)固件庫(kù)里回調(diào)函數(shù)來(lái)聊聊。
首先,作為一個(gè)函數(shù)庫(kù),除了個(gè)別初始化函數(shù)外,里面不存在現(xiàn)存的完整的回調(diào)函數(shù)。結(jié)合前面的介紹,我們知道回調(diào)函數(shù)需要結(jié)合具體場(chǎng)景而擬定,作為函數(shù)庫(kù)根本做不到這一點(diǎn),它沒(méi)法事先知曉發(fā)生某個(gè)事件時(shí)不同的應(yīng)用會(huì)需要采取怎樣的操作。
其次,STM32庫(kù)函數(shù)的確采用了回調(diào)機(jī)制,并基于可能的各種事件為STM32開(kāi)發(fā)者預(yù)留了只有函數(shù)定義而無(wú)具體內(nèi)容的空回調(diào)函數(shù),或者是只定義了一些基于各類(lèi)事件的函數(shù)指針,具體的回調(diào)函數(shù)需要用戶(hù)完成并將函數(shù)地址賦給相應(yīng)的函數(shù)指針而被調(diào)用。簡(jiǎn)單點(diǎn)說(shuō),函數(shù)庫(kù)給我們事先預(yù)留了眾多的回調(diào)函數(shù)接口。
STM32固件庫(kù)里的回調(diào)函數(shù)采用了兩種調(diào)用方式:
第一種是legacy方式,傳統(tǒng)的回調(diào)方式,庫(kù)以weak方式定義了各種空的回調(diào)函數(shù),像下面這些。STM32庫(kù)里都給我們準(zhǔn)備好了?!鞠旅媸怯嘘P(guān)UART部分事件的弱回調(diào)函數(shù)體,內(nèi)容為空】
圖3 UART傳輸事件相關(guān)弱回調(diào)函數(shù)定義
具體開(kāi)發(fā)時(shí),我們根據(jù)事件和應(yīng)用場(chǎng)景基于類(lèi)似上面的weak函數(shù)進(jìn)行重寫(xiě),重寫(xiě)時(shí)拿掉weak,庫(kù)里預(yù)留的弱定義函數(shù)盡量不用動(dòng)它。比方像下面這些都是最終的用戶(hù)回調(diào)函數(shù)。
圖4 UART傳輸事件相關(guān)的用戶(hù)回調(diào)函數(shù)
另外一種就是指針?lè)绞?/span>,或稱(chēng)注冊(cè)方式。即函數(shù)庫(kù)里事先基于各類(lèi)事件定義好了各種回調(diào)函數(shù)指針,具體的回調(diào)函數(shù)由用戶(hù)基于不同事件和應(yīng)用需求撰寫(xiě),然后將函數(shù)地址賦給函數(shù)指針,這個(gè)動(dòng)作我們稱(chēng)之為回調(diào)函數(shù)進(jìn)行注冊(cè),之后回調(diào)函數(shù)就可以通過(guò)函數(shù)指針而被適時(shí)調(diào)用。
比方下面是UART外設(shè)里定義的一些函數(shù)指針:【星號(hào)所指的是與UART傳輸完成事件有關(guān)的回調(diào)函數(shù)所用的指針】
圖5 UART傳輸事件相關(guān)的回調(diào)函數(shù)指針
當(dāng)我們將回調(diào)函數(shù)寫(xiě)好后,將函數(shù)地址賦給函數(shù)指針即可在相應(yīng)事件發(fā)生時(shí)被調(diào)用。比方類(lèi)似下面的操作代碼。紅星標(biāo)所指代碼就是在做回調(diào)函數(shù)的注冊(cè)。
圖6 UART傳輸完成事件用戶(hù)回調(diào)函數(shù)及注冊(cè)
給函數(shù)指針賦地址可以直接賦地址或通過(guò)調(diào)用庫(kù)函數(shù)xxx_RegisterCallback完成【見(jiàn)上圖星標(biāo)代碼】。
這種指針?lè)绞叫枰覀儗?duì)C語(yǔ)言中的結(jié)構(gòu)體、函數(shù)指針有相應(yīng)的了解,庫(kù)只是給我們提供了相應(yīng)的函數(shù)指針,具體的用戶(hù)回調(diào)函數(shù)由用戶(hù)根據(jù)需要來(lái)編寫(xiě),將其地址賦給相應(yīng)的函數(shù)指針以供調(diào)用。
而前面介紹的傳統(tǒng)型回調(diào)函數(shù),庫(kù)則幫我們把可能涉及到的回調(diào)函數(shù)全部以弱定義的方式都準(zhǔn)備好了,我們按需針對(duì)性選用,去掉weak填空重寫(xiě)。使用起來(lái)相對(duì)更直觀些,無(wú)需我們對(duì)函數(shù)指針有太多了解。
目前STM32庫(kù)回調(diào)機(jī)制中,作為用戶(hù)到底使用上面的哪種回調(diào)方式呢?在每個(gè)系列的固件庫(kù)的配置頭文件中有針對(duì)各個(gè)外設(shè)事件回調(diào)函數(shù)使用方式的選擇,比方以STM32F4系列為例,這里有個(gè)stm32f4xx_hal_conf.h的頭文件,我們可以看到基于各個(gè)外設(shè)事件回調(diào)函數(shù)使用方式選擇的宏。
圖7 回調(diào)函數(shù)調(diào)用方式的選擇配置
若我們不對(duì)該頭文件的相應(yīng)外設(shè)事件的回調(diào)函數(shù)調(diào)用方式的宏定義做調(diào)整,則默認(rèn)傳統(tǒng)回調(diào)方式,即legacy方式,非指針?lè)绞健H魧⑾鄳?yīng)的宏值改為1,則該外設(shè)事件相關(guān)回調(diào)函數(shù)采用指針注冊(cè)方式。
03 STM32庫(kù)函數(shù)里的回調(diào)機(jī)制及觸發(fā)事件
整體上講,STM32外設(shè)庫(kù)里的API函數(shù)大體由三部分組成,分別是:
初始化函數(shù)
啟動(dòng)型執(zhí)行函數(shù)
回調(diào)函數(shù)【弱定義函數(shù)或回調(diào)函數(shù)指針,最終靠用戶(hù)具體完成編寫(xiě)】
這樣的安排,讓整個(gè)工程代碼結(jié)構(gòu)比較清晰,可以讓人快速了解庫(kù)結(jié)構(gòu),同時(shí)現(xiàn)存的API函數(shù)大大減少開(kāi)發(fā)工作量,預(yù)留的回調(diào)函數(shù)接口一方面給開(kāi)發(fā)者提供了便利,另一方面讓用戶(hù)基于不同應(yīng)用場(chǎng)景自由組織代碼而又不破壞整個(gè)軟件架構(gòu)。
對(duì)于回調(diào)函數(shù),可以由哪些事件觸發(fā)呢?大致分三類(lèi),分別是外設(shè)初始化操作、外設(shè)處理完成【中斷】事件、外設(shè)出錯(cuò)【中斷】事件。我們關(guān)注最多是外設(shè)處理完成中斷事件相關(guān)的回調(diào)函數(shù)。
圖8 回調(diào)函數(shù)觸發(fā)事件的分類(lèi)
04 常見(jiàn)問(wèn)題
4.1 STM32庫(kù)函數(shù)里的回調(diào)函數(shù)是什么,有何用?
回調(diào)函數(shù)終究乃用戶(hù)所編寫(xiě),是用戶(hù)基于特定事件和應(yīng)用需求而編寫(xiě)的功能模塊,與其他函數(shù)并無(wú)本質(zhì)區(qū)別。形式上講,STM32庫(kù)預(yù)先為用戶(hù)做了回調(diào)函數(shù)的弱定義或基于事件的函數(shù)指針的定義。因基于特定條件發(fā)生后被調(diào)用執(zhí)行而被冠以回調(diào)稱(chēng)號(hào)。
嚴(yán)格來(lái)講,庫(kù)函數(shù)里沒(méi)有完整的回調(diào)函數(shù),只有基于各類(lèi)事件的弱定義的不具備實(shí)際功能的空回調(diào)函數(shù),或者是針對(duì)各類(lèi)事件而定義的各種用于調(diào)用回調(diào)函數(shù)的函數(shù)指針。我們的程序監(jiān)測(cè)相應(yīng)條件或事件往往是有的放矢,當(dāng)相應(yīng)事件出現(xiàn)時(shí)我們需要做相應(yīng)的處理,這正是回調(diào)函數(shù)要實(shí)現(xiàn)的功能,也是其功用所在。
4.2 STM32工程里的回調(diào)函數(shù)與中斷函數(shù)有什么區(qū)別?
STM32外設(shè)庫(kù)里的回調(diào)函數(shù)的確多數(shù)時(shí)候跟中斷事件及中斷服務(wù)程序息息相關(guān),往往在中斷服務(wù)程序中基于特定事件調(diào)用相應(yīng)的用戶(hù)回調(diào)函數(shù)。很多時(shí)候,我們完全可以將用戶(hù)回調(diào)函數(shù)看成中斷函數(shù)的一個(gè)調(diào)用模塊或延伸。
一個(gè)中斷服務(wù)程序里可以因不同事件而調(diào)用不同的回調(diào)函數(shù),即一個(gè)中斷服務(wù)程序里可能包含多個(gè)不同的回調(diào)函數(shù)。比方,我們?cè)?a href="http://www.brongaenegriffin.com/tags/定時(shí)器/" target="_blank">定時(shí)器中斷服務(wù)程序里可以涉及多個(gè)事件及相應(yīng)的用戶(hù)回調(diào)函數(shù),定時(shí)器中斷服務(wù)程序可能涉及更新事件、不同通道的比較事件或捕獲事件,相應(yīng)的用戶(hù)回調(diào)函數(shù)往往因應(yīng)用場(chǎng)景而異。
當(dāng)然,回調(diào)函數(shù)的調(diào)用還可以是中斷事件以外的其他事件觸發(fā)調(diào)用,比方可以基于初始化操作來(lái)調(diào)用相應(yīng)初始化回調(diào)函數(shù)。當(dāng)然,在庫(kù)里對(duì)某個(gè)外設(shè)的初始化可能有些默認(rèn)操作,但這個(gè)默認(rèn)操作很難是放之四海而皆準(zhǔn)的操作,這時(shí)我們就得根據(jù)實(shí)際應(yīng)用針對(duì)性編寫(xiě)初始化代碼,即初始化型回調(diào)函數(shù)。
4.3 STM32庫(kù)函數(shù)里的回調(diào)函數(shù)是否可以不用?
STM32庫(kù)函數(shù)里的回調(diào)機(jī)制是庫(kù)設(shè)計(jì)者為了便于軟件框架清晰、減少開(kāi)發(fā)者工作量等因素事先準(zhǔn)備的函數(shù)聲明及接口,用戶(hù)使用時(shí)只需根據(jù)具體應(yīng)用編寫(xiě)相關(guān)函數(shù)體。當(dāng)然,你如果不想理睬這些回調(diào)函數(shù)聲明及定義也是可以的,你根據(jù)具體應(yīng)用自行組織代碼完全可行。
4.4 STM32庫(kù)函數(shù)里似乎存在著類(lèi)似半成品的庫(kù)回調(diào)函數(shù)?
STM32庫(kù)函數(shù)里的確準(zhǔn)備了一些包含用戶(hù)回調(diào)函數(shù)的由庫(kù)定義的回調(diào)函數(shù),是庫(kù)設(shè)計(jì)者基于各類(lèi)特定事件而準(zhǔn)備的回調(diào)函數(shù),它會(huì)針對(duì)特定事件做一些基本而必要的操作,比方狀態(tài)的檢查、標(biāo)志監(jiān)測(cè)及清除,但它沒(méi)有辦法徹底寫(xiě)完整,因?yàn)樗鼰o(wú)法知道該事件發(fā)生后用戶(hù)的真實(shí)需求是什么,該如何操作,所以它終究還是需要調(diào)用真正的用戶(hù)回調(diào)函數(shù)。這樣做的目的還是為了給開(kāi)發(fā)者減少開(kāi)發(fā)工作量、以及減少出錯(cuò)等。
我們不妨具體看個(gè)實(shí)例。下面的回調(diào)函數(shù)采樣的指針注冊(cè)方式,我們看看UART的DMA傳輸完成中斷里傳輸完成的回調(diào)函數(shù)的調(diào)用過(guò)程。
首先,在UART的DMA啟動(dòng)函數(shù)HAL_UART_Transmit_ DMA()里有這樣一部分內(nèi)容:
圖9 外設(shè)啟動(dòng)運(yùn)行代碼中庫(kù)回調(diào)函數(shù)的賦值
庫(kù)里就DMA傳輸事件準(zhǔn)備了幾個(gè)回調(diào)函數(shù)【傳輸完成、半完成、出錯(cuò)】,即上圖中紅線標(biāo)示出來(lái)的。其實(shí)這幾個(gè)回調(diào)函數(shù)還不算完整的用戶(hù)回調(diào)函數(shù),是庫(kù)定義的并會(huì)做一些在它看來(lái)用戶(hù)必定需要完成的一些操作,它事先幫助完成,之后才調(diào)用最終的用戶(hù)回調(diào)函數(shù)。我們以傳輸完成事件為例來(lái)看看,上圖星號(hào)所標(biāo)的函數(shù)。
圖10 庫(kù)回調(diào)函數(shù)進(jìn)一步調(diào)回用戶(hù)回調(diào)函數(shù)
在這個(gè)庫(kù)定義的UART_DMATransmitCplt()函數(shù)里,它對(duì)DMA的傳輸模式做了判斷,如果是Normal模式,就將UART的傳輸數(shù)據(jù)長(zhǎng)度設(shè)置為0,禁止DMA后續(xù)傳輸功能,使能UART傳輸完成中斷的使能。然后才來(lái)調(diào)用用戶(hù)回調(diào)函數(shù)【上圖中箭頭所指】。如果DMA工作在循環(huán)模式,代碼進(jìn)到UART_DMATransmitCplt()函數(shù)后就直接調(diào)用最終的用戶(hù)回調(diào)函數(shù)。也就說(shuō)這些庫(kù)定義的回調(diào)函數(shù)在用戶(hù)回調(diào)函數(shù)的基礎(chǔ)上做了些必要操作,用戶(hù)回調(diào)函數(shù)可以看成這類(lèi)庫(kù)回調(diào)函數(shù)的子集。
4.5 基于STM32庫(kù)來(lái)組織用戶(hù)回調(diào)函數(shù)要注意什么?
前面提過(guò)了,用戶(hù)回調(diào)函數(shù)主要基于初始化事件或中斷事件而組織的代碼。那些中斷事件的回調(diào)函數(shù)的調(diào)用基本都是在中斷服務(wù)程序里發(fā)生的。所以,我們?cè)诰帉?xiě)回調(diào)函數(shù)時(shí)要結(jié)合具體情況靈活地組織代碼。要考慮中斷優(yōu)先級(jí)、具體事件響應(yīng)的實(shí)時(shí)性等。具體點(diǎn)說(shuō),我們?cè)诮M織回調(diào)函數(shù)時(shí),要考慮是否一定要一股腦地全寫(xiě)在中斷服務(wù)程序里,會(huì)不會(huì)影響別的中斷響應(yīng)。對(duì)于有些不緊急而又耗時(shí)的事件響應(yīng)代碼,可以考慮只在回調(diào)函數(shù)里設(shè)置相應(yīng)標(biāo)志,真正的處理代碼放到主循環(huán)去完成。
還提醒一點(diǎn),STM32庫(kù)設(shè)計(jì)者主動(dòng)給我們準(zhǔn)備了弱定義回調(diào)函數(shù)或基于各個(gè)事件的回調(diào)函數(shù)指針,盡管很豐富了,但未必能包羅萬(wàn)象,必要時(shí)我們可能還得根據(jù)具體情況來(lái)額外組織些類(lèi)似回調(diào)函數(shù)的事件/中斷響應(yīng)代碼。
關(guān)于STM32 HAL庫(kù)里的回調(diào)函數(shù)就簡(jiǎn)單介紹到這里,希望能幫到一些STM32開(kāi)發(fā)者。
完整內(nèi)容請(qǐng)點(diǎn)擊“閱讀原文”下載原文檔。

關(guān)注STM32


▽點(diǎn)擊“閱讀原文”,可下載原文檔
原文標(biāo)題:應(yīng)用筆記 | 淺談STM32庫(kù)里的回調(diào)函數(shù)
文章出處:【微信公眾號(hào):STM32單片機(jī)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
-
單片機(jī)
+關(guān)注
關(guān)注
6067文章
44992瀏覽量
650543 -
STM32
+關(guān)注
關(guān)注
2293文章
11032瀏覽量
365017
原文標(biāo)題:應(yīng)用筆記 | 淺談STM32庫(kù)里的回調(diào)函數(shù)
文章出處:【微信號(hào):STM32_STM8_MCU,微信公眾號(hào):STM32單片機(jī)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
GPDV6624C應(yīng)用筆記1.0版
為什么中斷回調(diào)函數(shù)中不能使用接收中斷開(kāi)啟函數(shù)?
如何使用自定義設(shè)置回調(diào)函數(shù)?
cyusb3014的usbTouart的dma通道配置,請(qǐng)問(wèn)為什么回調(diào)函數(shù)無(wú)法觸發(fā)?
為什么中斷回調(diào)函數(shù)中不能使用接收中斷開(kāi)啟函數(shù)?
為什么不需要給回調(diào)函數(shù)傳遞參數(shù)
解鎖TSMaster fifo函數(shù):報(bào)文讀取的高效方法

STM32CUBUMX定時(shí)器1中斷回調(diào)函數(shù)就是進(jìn)不去怎么解決?
ES32VF2264應(yīng)用筆記

AT32F423 PWC應(yīng)用筆記

評(píng)論