許多微控制器都帶有一個(gè)生態(tài)系統(tǒng),其中包括外圍驅(qū)動(dòng)程序、RTOS、中間件甚至示例應(yīng)用程序代碼。許多嵌入式開(kāi)發(fā)人員可以將大部分時(shí)間花在高級(jí)應(yīng)用程序代碼上,而忽略了滿足硬件的軟件。問(wèn)題是,雖然這個(gè)預(yù)構(gòu)建的生態(tài)系統(tǒng)可以加速開(kāi)發(fā),但這種加速通常是以時(shí)鐘周期和執(zhí)行效率為代價(jià)的。
在今天的文章中,我們將探討開(kāi)發(fā)人員可以應(yīng)用的幾個(gè)技巧,以幫助提高其中斷服務(wù)例程回調(diào)的效率,這些回調(diào)與許多微控制器軟件框架緊密集成。
先決條件 #1 – 測(cè)量 ISR 執(zhí)行時(shí)間
加速軟件執(zhí)行的第一步是停止并進(jìn)行一些測(cè)量。你如何知道你的中斷處理程序是使用過(guò)多的 CPU
時(shí)間還是運(yùn)行緩慢?你量一下!開(kāi)發(fā)人員可以利用幾種不同的選項(xiàng)來(lái)測(cè)量中斷執(zhí)行時(shí)間。
首先,只需切換 GPIO 線!我經(jīng)常將測(cè)試 GPIO 線初始化為高電平,然后當(dāng)我進(jìn)入 ISR 時(shí),我會(huì)將 GPIO
線切換為低電平,然后在退出 ISR 時(shí)再次將 GPIO 線切換為高電平。結(jié)果是一個(gè)低電平有效信號(hào),它近似代表 ISR
執(zhí)行時(shí)間。測(cè)量值是近似值的原因是它沒(méi)有考慮切換 GPIO
線的時(shí)間,我們假設(shè)它可以忽略不計(jì)(但如果你使用的是框架代碼,則可能不是!)。這種方法產(chǎn)生了一個(gè)簡(jiǎn)單且易于測(cè)量的波形,如下所示:
第二種方法,我將簡(jiǎn)要提及的是使用跟蹤軟件。如果你使用的是 RTOS,RTOS
通常會(huì)記錄系統(tǒng)中發(fā)生的事件,包括進(jìn)入和退出中斷服務(wù)程序。嵌入式開(kāi)發(fā)人員可以使用他們的跟蹤分析器來(lái)了解他們的中斷服務(wù)程序執(zhí)行了多長(zhǎng)時(shí)間。
現(xiàn)在乍一看,上面測(cè)得的 24.3 us 對(duì)于 ISR
來(lái)說(shuō)似乎并不算太糟糕。這實(shí)際上取決于應(yīng)用程序的好壞,但總的來(lái)說(shuō),我們希望 ISR
執(zhí)行時(shí)間盡可能短。在這個(gè)例子中,我設(shè)置了一個(gè)輸入捕捉外設(shè)來(lái)測(cè)量輸入信號(hào)的頻率。如果頻率只有區(qū)區(qū) 20 KHz,這個(gè) ISR 將占用大約 50% 的 CPU
周期!
技巧 #1 – 在 ISR 中調(diào)用的內(nèi)聯(lián)函數(shù)
首先,從 ISR
調(diào)用函數(shù)是個(gè)壞主意!函數(shù)調(diào)用開(kāi)銷(xiāo)會(huì)給中斷增加一大堆浪費(fèi)的時(shí)鐘周期,這將延遲返回到定期安排的代碼執(zhí)行。但問(wèn)題是許多現(xiàn)代框架都這樣做!例如,如果你查看
STM32CubeIDE 生成的定時(shí)器中斷,你會(huì)看到如下內(nèi)容:
現(xiàn)在,我添加了 GPIO HAL 調(diào)用,但你可以看到,默認(rèn)情況下,中斷會(huì)調(diào)用 HAL_TIM_IRQHandler,這是
STM32 上所有定時(shí)器的通用中斷處理程序。 (對(duì)于可重用和可移植的代碼來(lái)說(shuō),這是一個(gè)很棒的框架理念,但它可能對(duì)時(shí)間敏感的代碼有害)。 如果我們檢查
HAL_TIM_IRQHandler 的定義,我們會(huì)發(fā)現(xiàn)以下內(nèi)容:
這里沒(méi)有試圖告訴編譯器我們處于 ISR 中,因此編譯器可能會(huì)添加函數(shù)調(diào)用的代碼并向 ISR 添加無(wú)用的循環(huán)。
事實(shí)上,這個(gè)函數(shù)會(huì)有條件地檢查并調(diào)用幾個(gè)函數(shù),這可能會(huì)使事情變得更糟。 內(nèi)聯(lián)函數(shù)可能會(huì)減少執(zhí)行時(shí)間,但會(huì)以稍大的代碼大小為代價(jià)。 只需將 inline
關(guān)鍵字添加到函數(shù)定義中即可完成,如下所示:
進(jìn)行前后測(cè)量,在這種情況下,我發(fā)現(xiàn)我可以將中斷執(zhí)行時(shí)間縮短 0.2
us。不是很大,但在時(shí)間敏感的應(yīng)用程序中,它是一些東西。
提示 #2 – 自定義默認(rèn)中斷服務(wù)程序 (ISR)
預(yù)構(gòu)建的框架通常會(huì)將外圍類(lèi)型的中斷處理集中在一起。例如,我們剛剛看到的定時(shí)器中斷,它傳遞了一個(gè)定時(shí)器對(duì)象,然后有一堆條件語(yǔ)句來(lái)決定它應(yīng)該做什么。該框架是為重用而不是執(zhí)行速度而構(gòu)建的。如果我重寫(xiě)我的中斷以刪除所有這些通用函數(shù)調(diào)用,中斷執(zhí)行時(shí)間變?yōu)?
21.712,現(xiàn)在為我們節(jié)省了 2.5 us (10.3%)!對(duì)于我們正在查看的數(shù)字,它似乎并不多,但如果這是一個(gè)高頻中斷,那可能是大量的 CPU
使用率。
提示 #3 – 優(yōu)化中斷服務(wù)程序 (ISR) 回調(diào)函數(shù)
我經(jīng)常注意到,編寫(xiě)各種功能的示例代碼是為了向嵌入式開(kāi)發(fā)人員展示如何完成某事。例如,許多供應(yīng)商將提供輸入捕獲代碼,以顯示如何計(jì)算信號(hào)的占空比和頻率。這太棒了,除了代碼通常是在中斷服務(wù)程序中執(zhí)行的。這是次優(yōu)的。事實(shí)上,我在整個(gè)博客中展示的示例都與使用輸入捕獲計(jì)算頻率有關(guān)。當(dāng)你測(cè)量信號(hào)頻率時(shí),21.712
us 是中斷運(yùn)行的較長(zhǎng)時(shí)間。
示例代碼就是這樣,一個(gè)例子。算法通常是正確的,但它們不是以生產(chǎn)意圖的方式完成的。他們可能不會(huì)考慮重要的考慮因素,例如 CPU
負(fù)載和實(shí)時(shí)響應(yīng)。他們只是想向你展示他們的部分可以做你需要的事情,測(cè)量頻率或任何功能。
今天的嵌入式開(kāi)發(fā)人員擁有如此多的示例代碼和如此多的開(kāi)箱即用的框架供我們利用,這真是太棒了。需要注意的是,這段代碼可能不是為我們自己的目的而設(shè)計(jì)或?qū)崿F(xiàn)的。它通常被快速編寫(xiě)以展示一個(gè)特性或功能,而不是為生產(chǎn)而設(shè)計(jì)的。