嵌入式軟件開發(fā)人員需要掌握的一項(xiàng)基本技能是了解如何編寫驅(qū)動程序。在嵌入式系統(tǒng)中,通常有兩種類型的驅(qū)動程序:微控制器外圍驅(qū)動程序和通過 I2C、SPI 或 UART 等接口連接的外部設(shè)備驅(qū)動程序。在當(dāng)今的許多情況下,微控制器供應(yīng)商為其芯片提供示例驅(qū)動程序,這些驅(qū)動程序可以按原樣使用,也可能需要修改以進(jìn)行生產(chǎn)。外部驅(qū)動程序可能包含偽代碼,但開發(fā)人員幾乎總是自己負(fù)責(zé)編寫驅(qū)動程序。
重要的是要意識到編寫驅(qū)動程序的方法不止一種,而且編寫驅(qū)動程序的方式會極大地影響系統(tǒng)性能、能耗以及我們在開發(fā)產(chǎn)品時(shí)喜歡跟蹤的許多其他因素。在這篇文章中,我們將研究幾種常見的驅(qū)動程序設(shè)計(jì)模式以及它們?nèi)绾斡绊憫?yīng)用程序代碼。我們將從基礎(chǔ)開始,向更復(fù)雜的模式努力。
技術(shù) #1 – 輪詢驅(qū)動程序
第一種技術(shù),也是最基本的技術(shù),是開發(fā)一個輪詢外圍設(shè)備(或外部設(shè)備)的驅(qū)動程序,以查看它是否準(zhǔn)備好發(fā)送或接收信息。輪詢驅(qū)動程序很容易實(shí)現(xiàn),因?yàn)樗鼈兺ǔV皇禽喸円粋€標(biāo)志。例如,模數(shù)轉(zhuǎn)換器 (ADC) 驅(qū)動程序可能會啟動轉(zhuǎn)換序列,然后簡單地阻止處理器執(zhí)行并不斷檢查 ADC 完成標(biāo)志。此代碼如下所示:
Adc_Start();
而(ADC_COMPLETE_FLAG == FALSE);
AdcResults = Adc_ReadAll();
返回 AdcResults;
如您所見,上面的代碼不斷地輪詢 ADC_COMPLETE_FLAG,大概是映射到一個硬件位,以便查看數(shù)據(jù)何時(shí)可用。雖然像這樣測試硬件位被稱為輪詢,但它會產(chǎn)生一些有用的特性來討論。
首先,當(dāng)我們有一個使用輪詢的驅(qū)動程序時(shí),在大多數(shù)實(shí)現(xiàn)中,該驅(qū)動程序?qū)⑹且粋€阻塞驅(qū)動程序。這意味著一旦我們調(diào)用驅(qū)動程序,它不會從驅(qū)動程序返回,直到我們得到我們需要的結(jié)果。還有其他實(shí)現(xiàn),我們可以讓驅(qū)動程序檢查一次結(jié)果然后返回。在這種情況下,應(yīng)用程序負(fù)責(zé)輪詢驅(qū)動程序,我們認(rèn)為驅(qū)動程序是非阻塞的。從設(shè)計(jì)的角度來看,由開發(fā)人員決定輪詢應(yīng)該在哪里進(jìn)行。在驅(qū)動程序中,應(yīng)用程序不必這樣做,但如果應(yīng)用程序這樣做,則可以靈活地執(zhí)行其他活動并以較低的速率輪詢驅(qū)動程序。
其次,總的來說,輪詢很容易實(shí)現(xiàn)。通常,開發(fā)人員需要做的就是觀察寄存器中的一些位并監(jiān)視它們以決定何時(shí)與設(shè)備交互。最后,雖然它很容易實(shí)現(xiàn),但輪詢通常被認(rèn)為是低效的。其他技術(shù)(例如使用中斷)可以在需要執(zhí)行某些操作時(shí)通知 CPU,這使得輪詢效率相當(dāng)?shù)汀?/font>我經(jīng)常將民意調(diào)查與長途旅行中的一個孩子聯(lián)系起來,他不斷地問“我們到了嗎?”。民意調(diào)查不斷地問“你準(zhǔn)備好了嗎?現(xiàn)在怎么樣?現(xiàn)在?”。
這給我們帶來了一個更高效但稍微復(fù)雜一點(diǎn)的驅(qū)動程序?qū)崿F(xiàn),即使用中斷。
技巧#2——中斷驅(qū)動的驅(qū)動程序
在驅(qū)動程序中使用中斷非常棒,因?yàn)樗梢燥@著提高代碼執(zhí)行效率。中斷不是不斷檢查是否該做某事,而是告訴處理器驅(qū)動程序現(xiàn)在準(zhǔn)備就緒,我們跳轉(zhuǎn)到處理中斷。一般來說,我們可以使用兩種類型的中斷驅(qū)動驅(qū)動機(jī)制:事件驅(qū)動和調(diào)度。當(dāng)外圍設(shè)備發(fā)生需要處理的事件時(shí),事件驅(qū)動驅(qū)動程序?qū)⒂|發(fā)中斷。例如,我們可能有一個 UART 驅(qū)動程序,當(dāng)緩沖區(qū)中接收到一個新字符時(shí),它會觸發(fā)一個中斷。另一方面,我們可能有一個 ADC 驅(qū)動程序,它使用計(jì)時(shí)器來安排訪問以開始采樣或處理接收到的數(shù)據(jù)。
使用中斷驅(qū)動的驅(qū)動程序雖然效率更高,但會為設(shè)計(jì)增加額外的實(shí)現(xiàn)復(fù)雜性。首先,開發(fā)人員需要啟用適當(dāng)?shù)闹袛嘁怨?qū)動程序使用,例如接收、發(fā)送和緩沖區(qū)滿。我通常發(fā)現(xiàn)由于現(xiàn)代中斷控制器的復(fù)雜性,開發(fā)人員很難讓中斷工作。它們通常需要在通用寄存器中、外設(shè)級別設(shè)置中斷,有時(shí)甚至需要配置優(yōu)先級和其他設(shè)置。幾年前,我整理了一份配置中斷的分步指南,可在此處下載。
接下來,使用中斷可能需要遵循一整套額外的最佳實(shí)踐。例如,最好的做法是:
- 保持中斷簡短
- 將共享變量聲明為 volatile
- 處理高優(yōu)先級項(xiàng)目,然后卸載到應(yīng)用程序進(jìn)行處理
您不希望在事件發(fā)生時(shí)執(zhí)行數(shù)千行代碼的驅(qū)動程序中斷。相反,您希望處理關(guān)鍵任務(wù),例如從 UART 緩沖區(qū)中取出一個字符并將其放入應(yīng)用程序的循環(huán)緩沖區(qū)中。
最后,我們還需要擔(dān)心中斷被禁用、中斷時(shí)序和運(yùn)行速率、優(yōu)先級以及是否有可能錯過中斷等問題。雖然其中一些項(xiàng)目看起來額外的復(fù)雜性可能不值得付出努力,但執(zhí)行時(shí)間的改進(jìn)可能是巨大的。例如,電池供電的設(shè)備可能會進(jìn)入深度睡眠模式,只有在將字符存儲在緩沖區(qū)中時(shí)才醒來,然后再重新進(jìn)入睡眠狀態(tài)。這樣做可以節(jié)省大量能源。
在某些情況下,在驅(qū)動程序中使用中斷確實(shí)是處理外圍事件的最佳方式。例如,您可以編寫一個輪詢 I2C 驅(qū)動程序,但編寫一個中斷 ack、nack 等傳輸序列中發(fā)生的不同事件的驅(qū)動程序會產(chǎn)生更清潔、更小和更高效的驅(qū)動程序。
我們將在下一篇文章中查看中斷驅(qū)動驅(qū)動程序的代碼。現(xiàn)在,讓我們看看我們可以用來編寫驅(qū)動程序的第三種技術(shù),它是利用直接內(nèi)存訪問 (DMA) 控制器。
技術(shù)#3 – DMA 驅(qū)動的驅(qū)動程序
有一些驅(qū)動程序會通過系統(tǒng)移動大量數(shù)據(jù),例如 I2S 和 SDIO。管理這些類型接口上的緩沖區(qū)可能需要 CPU 不斷采取行動。如果 CPU 落后或必須處理另一個系統(tǒng)事件,則數(shù)據(jù)可能會丟失或延遲,這可能會給用戶帶來明顯的問題,例如音頻跳過。關(guān)注吞吐量的開發(fā)人員可以改為使用 DMA 控制器在微控制器周圍為 CPU 移動數(shù)據(jù)。
這些驅(qū)動程序背后的想法是 DMA 控制器可以通過以下方式在微控制器周圍移動數(shù)據(jù):
- 內(nèi)存外圍
- 記憶到記憶
- 內(nèi)存到外設(shè)
使用 DMA 的優(yōu)點(diǎn)是 CPU 可以在 DMA 通道為驅(qū)動程序移動數(shù)據(jù)時(shí)停止做其他事情,基本上可以同時(shí)完成兩件事。
雖然非常希望在驅(qū)動程序中使用 DMA 控制器來減少 CPU 執(zhí)行的需要,但大多數(shù)微控制器的可用 DMA 通道數(shù)量有限。因此,不能編寫每個驅(qū)動程序來使用 DMA。相反,開發(fā)人員需要選擇將受到帶寬限制且將從 DMA 中受益匪淺的外設(shè),例如用于外部存儲器、ADC 和通信通道的接口。
在沒有 I2S 或 SDIO 的應(yīng)用程序中,開發(fā)人員可以使用 DMA 將傳入的 UART 字符移動到一個循環(huán)緩沖區(qū)中,一旦設(shè)置了某個限制,該緩沖區(qū)就會被處理。可以通過輪詢應(yīng)用程序結(jié)構(gòu)或通過 DMA 控制器設(shè)置中斷來監(jiān)控此限制。可以想象,DMA 驅(qū)動程序是驅(qū)動程序最有效的實(shí)現(xiàn),但根據(jù)開發(fā)人員的技能水平以及他們以前是否使用過 DMA,它們的實(shí)現(xiàn)也可能很復(fù)雜。這不應(yīng)該阻止開發(fā)人員嘗試在其驅(qū)動程序中使用 DMA。
結(jié)論
在這篇文章中,我們研究了嵌入式開發(fā)人員可以用來為其微控制器外設(shè)和外部設(shè)備編寫驅(qū)動程序的三種主要技術(shù)。為了比較總結(jié)這些技術(shù),下面的表 1 顯示了我們討論的每種技術(shù),以及實(shí)現(xiàn)的相對復(fù)雜性和由此產(chǎn)生的執(zhí)行效率。
表 1:驅(qū)動程序設(shè)計(jì)技術(shù)的相對復(fù)雜性和
效率。
技術(shù) | 復(fù)雜 | 效率 |
輪詢 | 低的 | 低的 |
打斷 | 中等的 | 中等的 |
DMA | 中等的 | 高的 |
一般而言,開發(fā)人員應(yīng)該默認(rèn)使用中斷驅(qū)動程序?qū)崿F(xiàn)而不是輪詢實(shí)現(xiàn),除非正在使用的外設(shè)速度很快,即幾個 Mbps。DMA 可用于任何驅(qū)動程序,但我通常為需要高吞吐量的接口(例如外部存儲器或通信接口)保留 DMA 通道。您選擇的選項(xiàng)將高度依賴于最終應(yīng)用程序。
在下一篇文章中,我們將通過研究如何為模數(shù)轉(zhuǎn)換器開發(fā)一個簡單的驅(qū)動程序來探索如何更深入地研究這些概念。
?
?
評論