嵌入式軟件開發人員需要掌握的一項基本技能是理解如何編寫驅動程序。在嵌入式系統中,通常有兩種類型的驅動程序:微控制器外圍驅動程序和通過I2C、SPI或UART等接口連接的外部設備驅動程序。在今天的許多情況下,微控制器供應商為他們的芯片提供了示例驅動程序,這些驅動程序可以按原樣使用,或者可能需要修改以用于生產。外部驅動程序可能包括偽代碼,但嵌入式開發人員幾乎總是自己負責編寫驅動程序。
重要的是要認識到,編寫驅動程序的方法不止一種,而且它的編寫方式會極大地影響系統性能、能耗以及我們在開發產品時喜歡跟蹤的許多其他因素。在本文中,我們將研究幾種常見的驅動程序設計模式,以及它們如何影響應用程序代碼。我們將從基本模式開始,并致力于更復雜的模式。
技術1:輪詢驅動程序
第一種技術,也是最基本的技術,是開發一個驅動程序,輪詢外圍設備(或外部設備),查看它是否準備好發送或接收信息。輪詢驅動程序非常容易實現,因為它們通常只輪詢一個標志。例如,模數轉換器(ADC)驅動程序可能啟動轉換序列,然后簡單地阻止處理器執行,并不斷檢查ADC完成標志。此代碼類似于以下內容:
首先,當我們有一個使用輪詢的驅動程序時,在大多數實現中,該驅動程序將是一個阻塞驅動程序。這意味著一旦我們調用驅動程序,它將不會從驅動程序返回,直到我們得到所需的結果。還有其他一些實現,我們可以讓驅動程序檢查一次結果,然后返回。在這種情況下,應用程序負責輪詢驅動程序,我們將考慮驅動程序是非阻塞的。從設計的角度來看,應該由嵌入式開發人員來決定輪詢應該在哪里進行。在驅動程序中,可以減少應用程序不得不這樣做,但如果應用程序這樣做,則可以靈活地執行其他活動并以較低的速率輪詢驅動程序。
其次,一般來說,輪詢非常容易實現。通常,開發人員所需要做的就是觀察寄存器中的一些位并監視它們,以決定何時與設備交互。最后,雖然輪詢很容易實現,但通常被認為效率低下。其他技術,例如使用中斷,可以在需要執行某些操作時通知CPU,這使得輪詢效率相當低。
這給我們帶來了一個更高效但稍微復雜的驅動程序實現,即使用中斷。
技術:2–中斷驅動程序
在驅動程序中使用中斷非常棒,因為它可以極大地提高代碼的執行效率。中斷告訴處理器驅動程序現在準備好了,我們跳起來處理中斷,而不是不斷地檢查是否該做某事。通常,我們可以使用兩種類型的中斷驅動機制:事件驅動和調度。當外設中發生需要處理的事件時,事件驅動驅動程序將觸發中斷。例如,我們可能有一個UART驅動程序,當緩沖區中接收到一個新字符時,該驅動程序將觸發一個中斷。另一方面,我們可能有一個ADC驅動程序,它使用定時器來安排訪問,以開始采樣或處理接收到的數據。
使用中斷驅動驅動程序雖然效率更高,但會給設計增加額外的實現復雜性。首先,嵌入式開發人員需要啟用適當的中斷,以便在驅動程序中使用,例如接收、傳輸和緩沖區已滿。通常發現,由于現代中斷控制器的復雜性,開發人員很難讓中斷正常工作。它們通常需要在通用寄存器的外圍級別設置中斷,有時甚至需要配置優先級和其他設置。
接下來,中斷的使用可以引入遵循一整套額外的最佳實踐的需要。例如,最好的做法是:
—縮短中斷時間
—將共享變量聲明為volatile
—處理高優先級項目,然后卸載到應用程序進行處理
當事件發生時,你不希望在驅動程序中有數千行代碼被執行的中斷。相反,你希望處理關鍵任務,比如從UART緩沖區中提取一個字符,并將其放入應用程序的循環緩沖區中。
最后,我們還需要擔心諸如中斷被禁用、中斷時間和運行速率、優先級以及中斷是否可能丟失等問題。雖然其中一些項目看起來額外的復雜性可能不值得付出努力,但對執行時間的改進可能是巨大的。例如,電池供電的設備可能進入深度睡眠模式,只在醒來時將字符存儲在緩沖區中,然后返回睡眠狀態。這樣做可以節省大量能源。
在某些情況下,在驅動程序中使用中斷實際上是處理外圍事件的最佳方式。例如,你可以編寫輪詢的I2C驅動程序,但編寫一個中斷ack、nack等傳輸序列中發生的不同事件的I2C驅動程序會產生更干凈、更小和更高效的驅動程序。
技術3:DMA驅動的驅動程序
有些驅動程序在系統中移動大量數據,如I2S和SDIO。管理這些類型接口上的緩沖區可能需要CPU的持續操作。如果CPU落后或必須處理另一個系統事件,數據可能會丟失或延遲,這可能會給用戶造成明顯的問題,例如音頻跳過。與吞吐量相關的嵌入式開發人員可以使用DMA控制器在CPU的微控制器周圍移動數據。
這些驅動程序背后的想法是DMA控制器可以通過以下方式在微控制器周圍移動數據:
—外圍存儲器
—內存對內存
—外設存儲器
使用DMA的優點是,當DMA通道為驅動程序移動數據時,CPU可以停止做其他事情,基本上一次完成兩件事情。
雖然在驅動程序中使用DMA控制器以減少CPU執行的需要是非常理想的,但大多數微控制器的可用DMA通道數量有限。因此,不能將每個驅動程序都寫入DMA。相反,開發人員需要選擇帶寬受限且能從DMA中獲益匪淺的外圍設備,如外部存儲器、ADC和通信通道的接口。
在沒有I2S或SDIO的應用程序中,開發人員可以使用DMA將傳入的UART字符移動到循環緩沖區中,一旦設置了某個限制,就會對該緩沖區進行處理。可以通過輪詢應用程序結構或通過DMA控制器設置中斷來監控此限制。可以想象,DMA驅動程序是驅動程序最有效的實現方式,但根據開發人員的技能水平以及他們以前是否使用過DMA,它們的實現也可能很復雜。這不應該阻止開發者嘗試在他們的驅動程序中使用DMA。
在本文中,我們研究了嵌入式開發人員可以用來為微控制器外圍設備和外部設備編寫驅動程序的三種主要技術。一般來說,開發人員在默認情況下應該在輪詢實現上使用中斷驅動程序實現,除非使用的外圍設備速度很快,即數Mbps。DMA可以用于任何驅動程序,但我通常為需要高吞吐量的接口(如外部內存或通信接口)保留DMA通道。你選擇的選項將高度依賴于最終應用程序。