這個問題比較有意思,而且具有一定的普遍性,寫出來和大家一起分享。
最近做了一個物聯網項目,目的是為原有的一個只能通過UART接口控制的設備添加藍牙功能。
采用的主體結構是把CC2540模塊和設備通過UART連接,然后在CC2540上實現相應的Profile,手機通過Profile和CC2540通訊,而CC2540通過UART接口和設備通訊。這樣子經過CC2540的轉換,就可以實現用手機APP對設備進行控制了。
cc2540是TI的 藍牙4.0(BLE)SOC芯片,特性如下:
- 256KB FALSH
- 16KB RAM
- 32MHz的單周期增強型51 Core
- 藍牙協議棧和用戶的應用程序可以在單芯片上運行
- 極低的功耗
其實整個項目都很普通,沒有什么難度。除了一個地方,就是用CC2540捕捉UART數據。這里的設備數據包大小不定,最大不超過1KB。
接收UART數據不是很簡單嘛?單片機都能做,為嘛這個地方會有問題呢?
因為CC2540是一顆SOC芯片,也就是個System on chip。CC2540除了運行用戶的應用程序以外,還運行藍牙4.0的協議棧,收發無線數據包相關的事件都是由協議棧自動處理。
好的,問題來了。根據TI的資料,每次處理數據包相關事件可能需要占用CPU長達幾個毫秒的時間,並且在這個時間中是完全關閉中斷的。
而平時我們從UART接收數據,最常用就是通過中斷。配置好設備的寄存器,使得每次UART收到數據就產生一次中斷,然后在中斷中對數據進行讀取。
115200bps的速率下1秒大約有 115200/8=14400Byte的數據發送,1ms也就是14Byte。如果通過中斷的方式來接收數據,假設CPU處理運行協議棧花了1ms時間,而這時恰好有數據過來。就會丟失14Byte,這肯定是沒法用的。so,此路不通。
幸好還有更好的方法,也就是通過DMA接收數據
DMA(Direct Memory Access,直接內存存取) 是所有現代電腦的重要特色,它允許不同速度的硬件裝置來溝通,而不需要依於 CPU 的大量中斷負載。否則,CPU 需要從來源把每一片段的資料復制到暫存器,然后把它們再次寫回到新的地方。在這個時間中,CPU 對於其他的工作來說就無法使用。
DMA的特色就是可以不經過CPU來傳輸數據,正適合當前這種CPU分身乏術的情況。經查DataSheet,CC2540一共有4個DMA通道,無線協議棧用掉了一個。
通過把DMA的觸發源設置為UART接收到數據
,傳輸源地址設置為UART數據寄存器,傳輸目的地址設置為一片Buf,並設置為每次傳輸源地址不變,目的地址自增。就可以在DMA實現DMA自動搬運UART數據了。
不過不管開辟多大的DMA接收Buf,也總有滿的時候。當DMA Buf滿了,或者需要對DMA Buf中數據進行處理的時候,就需要暫停DMA傳輸。而這時如果有數據進入,就會丟失。
為了解決這個問題,這里使用經典的Ping-Pang DMA操作方法。
Ping-Pang通過開啟雙DMA通道,實現任意時刻都有一個DMA通道在接受數據中,不會丟數據。而且通過Ping-Pang切換,DMA Buf不會滿,也就可以持續接收。
這里具體實現上先開辟兩次要(a,b)一主要(x)三個緩沖區,並建立兩個DMA通道,分別把DMA數據傳輸到兩個次要緩沖區a和b中。通過一個定時器在兩個DMA通道之間來進行切換。每次切換到新的次要緩沖區之后,就把舊的次要緩沖區中的數據導出到主要接收緩沖x中。這樣就可以在x中對數據進行處理,並且過程中不會丟失數據。
不過這里還有一個坑,那就是怎樣獲得DMA傳輸數據的長度
使用Ping-Pang 方法操作DMA時,需要不斷導出已經保存在DMA Buf中的數據。這時就需要知道DMA Buf中數據的長度。有些芯片的DMA控制器有相關接口,可以直接從寄存器中讀取到傳輸計數值。不過CC2540沒有這個功能。。。TI啊TI,你這樣做事情做一半,真的好么-_-#
這里顯然不可以通過把DMA接收緩沖初始化為0,然后判斷非0數據的長度來實現,因為UART接收到的數據可能是0。
后來想了個方法來解決。思路就是開始每一輪DMA傳輸之前把DMA接收緩沖區的數據初始化為固定值,比如0x00。然后把DMA控制器配置為每次傳輸雙字節數據[1]。通過查詢DataSheet,UART控制器的數據寄存器后面一字節是波特率寄存器的一部分,這是一個固定值而且不為零。這樣DMA每次傳輸會傳輸1字節UART接收的數據+1字節固定數據到接收Buf中。通過分析收到固定數據的長度,就可以知道DMA收到數據的長度。
這里一共有三個地方需要注意的:
- 通過DMA接收數據
- DMA Ping-Pong操作
- 通過給數據添加"尾巴"來實現DMA傳輸計數
通過以上方法,就完美的解決了UART接收數據的問題。現在帶無線協議棧的Soc芯片被廣泛使用,這一類芯片的接收數據問題,都可以通過這樣的思路來解決。
AlexaZhou.xyz版權所有,轉載請注明
DMA控制器一般可以配置為觸發源每次觸發,傳輸單字節,雙字節,四字節數據。 ↩︎