CoX.SPI接口設計思想
1.1 CoX.SPI發展過程,歷史版本
CoX1.0版本下的CoX.SPI是隨著CoX第一版于2009年誕生而開始的,在第一版中,我們首先實現了SPI模塊的主從模式的選擇和工作狀態的配置,單個字符的發送和接收以及數據流的發送和接收等功能。同樣我們也采用面向對象的方式,定義了一個通用的SPI的結構體。 typedef struct { COX_Status (*Init) (uint8_t mode, uint32_t rate); uint32_t (*Write) (const void *wbuf, uint32_t wlen); uint32_t (*Read) (void *rbuf, uint32_t rlen); uint32_t (*ReadWrite) (uint32_t wdat); COX_Status (*Cfg) (uint8_t index, uint32_t arg, uint32_t *pre_arg); } COX_SPI_PI_Def; 然后再實現基于CoX.SPI的對應不同廠商 MCU的函接口數。 當我們在實際編程中需要某個功能的時候,只需調用對應的CoX接口函數就可以了,而且由于CoX上層的函數名稱的一致性,以利于系統將來的可移植性。有興趣的可以參考Coocox官網中NUC140-LB Board中的CoOS例程,鏈接地址: http://www.coocox.com/downloadfile/CoOS/Demo/NUC140_CoOS.zip。 然而,在后來的開發使用過程中,發現CoX1.0中SPI定義有幾個明顯的不足: 1. 實現過程中考慮情況不全面。比如在第一版中SPI讀取和發送數據的方式只有阻塞式,這樣在遇到系統不需要強制讀取和發送數據的時候就無能為力了,同時還缺乏一些其他特殊的功能,比如SSn管腳配置的功能。 2. 大量采用結構體的形式,使得代碼的可讀性大大降低,同時也降低了代碼的重用性,效率也不高。 3. CoX API封裝不全面,僅僅實現了SPI初始化、配置、發送與接收的功能,在實現一些其它基本的功能時,仍然需要結合對應的廠商庫來使用。 鑒于CoX.SPI以上的幾種缺陷,經常一年多時間的積累,在2011年開始推出了CoX 2.0版本,這個版本解決了上述所有的缺點的同時,還保留了CoX設計的初衷——那就是在 Cortex-M系列CPU上面的通用性。下面,詳細介紹2.0版本下的CoX.SPI接口。
2. CoX2.0的SPI接口定義 SPI通信模塊提供了用來處理器件和外圍設備的串行通信,可以配置成使用Motorola SPI 、Microwire或Texas Instrument同步串行接口的幀格式。數據幀的大小也可以配置。 SPI 模塊對接收到的外圍設備的數據執行串行-并行轉換,對發送給外圍設備的數據執行 并行-串行轉換,TX 和 RX 通路由內部 FIFO 進行緩沖。 SPI 模塊可以配置成一個主機或一個從機設備。作為一個從機設備,SPI 模塊還能配置 成禁止它的輸出,這就允許一個主機設備與多個從機設備相連。 SPI 模塊還包含一個可編程的位速率時鐘分頻器和預分頻器來產生輸出串行時鐘(從 SPI 模塊的輸入時鐘獲得)。產生的位速率取決于輸入時鐘和連接的外設支持的最大位速率。 對于包含一個 DMA 控制器的器件,SPI 模塊也提供一個 DMA 接口以便通過 DMA 來 實現數據傳輸。 在設計SPI的CoX接口的時候,我們按著SPI的功能劃分了幾個模塊,具體的設計思想如下: (1)SPI 工作狀態配置接口 主模式/從模式的選擇 數據流傳輸速率、數據寬度、數據移位方向的設置 SSn管腳狀態的設置 DMA模式的選擇 (2)SPI 中斷控制接口 使能/失能中斷 中斷服務程序 (3)SPI 數據發送與接收處理接口 啟動SPI數據發送和接收 SPI數據發送與接收處理 在設計CoX2.0-SPI的接口時,我們充分考慮了基于Cortex-M內核的MCU中SPI所具有的大部分功能和SPI通信協議的特性,最終采用了通用強制性接口、通用非強制性接口和廠商庫接口這三種類型的接口組合來共同完成SPI的接口定義,下面將分別對SPI的各個接口做介紹。
2.1通用強制接口
通用強制接口是提取的一套 ARM Cortex M0/M3所有廠商系列MCU都具有的功能接口。本篇將同樣以新唐M051為例講解CoX.SPI,其他系列大同小異。 SPI的APIs分組完成處理配置和狀態、處理數據和管理中斷幾大功能,CoX的宏定義的參數和APIs都是以' x '開頭的, 函數和形式參數都是x開頭,體現出CoX接口的特征。 配置對應的GPIO管腳線為SPI功能的函數:
- xSPinTypeSPIu SPI工作模式配置的函數:
- xSPIConfigSetl xSPIEnablel xSPIDisablel xSPIDMAEnablel xSPIDMADisableu
SSn管教狀態配置的函數: - xSPISSSetu 處理SPI中斷的APIs
- xSPIIntCallbackInitl xSPIIntEnable
- xSPIIntDisablel xSPIStatusGetu
獲取SPI工作狀態的APIs - xSPIBitLengthGetl xSPIIsBusy
- xSPIIsRxEmptyl xSPIIsTxEmpty
- xSPIIsRxFulll xSPIIsTxFullu
處理SPI的數據讀寫的APIs - xSPISingleDataReadWrite
- xSPIDataBufferRead
- xSPIDataBufferReadNonBlocking
- xSPIDataReadl xSPIDataBufferWrite
- xSPIDataBufferWriteNonBlocking
- xSPIDataWrite
2.2通用非強制接口
通用非強制接口是一類MCU通有的功能,而不是所有MCU都具有的功能接口:l xSPIErrorGetl xSPIErrorClear
2.3廠商庫特色接口
特色接口是包括了通用性接口,和MCU特有功能的接口。它并不是通用強制型或者通用非強制型,而是MCU特有的功能,就是在廠商庫特色接口這一組。相關的APIs接口如下: - SPIAutoSSEnablel SPIAutoSSDisable
- SPISSClearl SPISSConfig
- SPILevelTriggerStatusGet
- SPIByteReorderSetl SPIVariableClockSet
- SPIDivOneFunction
- SPI3WireFunctionl SPI3WireAbort
- SPI3WireStartIntEnable
- SPI3WireStartIntDisable
- SPI3WireStartIntFlagGet
- SPI3WireStartIntFlagClear
另外廠商庫接口也實現了MCU其他所有的功能,比如: SPIIntEnable(ulBase, ulIntFlags); 也實現了SPI中斷使能/失能模式的配置,這個在CoX接口的xSPIIntEnable也是這個功能。其實這個時候xSPIIntEnable的實現方式如下: #define xSPIIntEnable(ulBase, ulIntFlags) \ SPIIntEnable(ulBase, ulIntFlags) 進行了一次宏定義包裝罷了,對應的參數也是進行的一次宏定義比如: // //! End of transfer // #define xSPI_INT_EOT SPI_INT_EOT
3. 設計技巧簡介
SPI模塊的CoX接口在設計的過程中非常注重“上層應用簡潔 下層驅動通用”的核心思想, CoX.SPI組件提供了一套寄存器組件, 對所需要寫入寄存器的值進行了宏定義,這樣使得用戶在操作過程中不需深究寄存器的細節,使得配置更加直觀,同時也方便用戶調用寄存器接口定義自己的功能函數。下面以函數xSPIConfigSet(ulBase, ulBitRate, ulConfig)為例來說明:
比如將 MCU的SPI0模塊配置成Master Mode , polarity 0 , phase 0 , 200KHz, 8Bits Data width, SPI MSB First模式, 代碼如下: xSPIConfigSet(xSPI0_BASE, 200000, xSPI_MOTO_FORMAT_MODE_0 | xSPI_MODE_MASTER | xSPI_MSB_FIRST | xSPIConfigSet()函數的定義是 xSPIConfigSet(ulBase, ulBitRate, ulConfig) ,其中ulBase對應SPIn的基地址,可以取值xSPI0_BASE、xSPI1_BASE、……,ulBitRate是SPI傳輸和接收數據的速率,ulConfig是SPI的各種工作狀態的組合。該函數的功能是非常豐富的,在執行完該函數后,SPI就完成了傳輸速率、數據格式、工作狀態等配置。具體的實現過程如下: #define xSPIConfigSet(ulBase, ulBitRate, ulConfig) \ do \ { \ SPIConfig(ulBase, ulBitRate, ulConfig); \ SPISSConfig(ulBase, SPI_SS_LEVEL_TRIGGER, SPI_SS_ACTIVE_LOW_FALLING);\ } \ while(0) (1)向SPI控制寄存器中分別寫入數據格式、工作狀態、數據寬度等配置信息 (2)獲取SPI的系統時鐘,根據用戶設定的時鐘與獲取的SPI系統時鐘計算出分頻系數和分頻之后的值,然后將它們分別寫入對應寄存器中。 (3)配置SSn管腳的觸發狀態。 在這個功能函數中我們集成了兩個函數,其中SPIConfig(ulBase, ulBitRate, ulConfig)為廠商庫函數,這個接口剛好可以給CoX接口調用,做好相應的配置,不需要做特殊處理,這樣可以最大程度的重用代碼。同時為了方便用戶,CoX.SPI會在初始化時選取一些默認狀態配置,例如SPISSConfig(ulBase, SPI_SS_LEVEL_TRIGGER, SPI_SS_ACTIVE_LOW_FALLING);這里默認將SS腳配置成了低電平觸發, 符合MCU大部分工作狀態下SS管腳的配置,這樣不僅可以減少用戶的工作量,也使得代碼更簡潔,當然用戶也可以使用CoX.SPI中的xSPISSSet()來自定義SS腳狀態。 CoX.SPI另一個主要特色就是引入了阻塞式和非阻塞式的概念.我們在設計CoX.SPI接口的時候充分考慮實際編程的需求,相較于CoX1.0的數據處理,CoX2.0中特別增加了非阻塞式的數據發送和接收處理函數,可以滿足不同情況下的SPI數據發送與接收。具體的函數名稱如下: 非阻塞式發送:xSPIDataBufferWriteNonBlocking () 非阻塞式接收:xSPIDataBufferReadNonBlocking() 阻塞式發送:xSPIDataBufferWrite() 阻塞式接收:xSPIDataBufferRead() 比如在一些采用循環查詢SPI數據的系統中,MCU會不停的循環查詢SPI的數據,這時如果我們采用一般的阻塞式的SPI數據接收,當SPI沒有接收數據時,MCU會一直停在這等待,不斷的查詢SPI數據接收情況,而無法執行其他的任務,這顯然不符合我們的要求,CoX1.0與廠商庫都不能很好的解決這一問題,而在CoX2.0中,由于我們引入了非阻塞式數據接收與處理的概念,這個時候我們就可以采用非阻塞式的數據接收處理,讓 單片機在查詢SPI數據為空時,可以繼續執行下面的任務,等待下次來再來查詢SPI數據狀態,這樣就可以很好的解決等待SPI接收數據與運行其它任務之間的矛盾問題。
4. SPI接口使用示例與移植
下面給出一個CoX.SPI的示例,都是使用的通用強制型的接口,因此下面的例子在所有Cortex M0/M3上都是平滑移植的, 下面展示一個程序。
// // Users should to modify the following GPIO pins definition when transplanting the different //MCUs based on Cortex-M3/M0. // #define SPICLK_PIN PB7 #define SPIMOSI_PIN PB5 #define SPIMISO_PIN PB6 #define SPICS_PIN PB4 void SpiReceiveTransfer (void) { // // Enable Peripheral SPI0 // xSysCtlPeripheralEnable2(xGPIOSPinToPort(SPICLK_PIN)); xSysCtlPeripheralEnable2(xSPI0_BASE); // // Set SysClk 36MHz using Extern 12M oscillator // xSysCtlClockSet(12000000, xSYSCTL_OSC_MAIN | xSYSCTL_XTAL_12MHZ);
// // Configure Some GPIO pins as SPI Mode // xSPinTypeSPI(SPI0CLK, SPICLK _PIN); xSPinTypeSPI(SPI0MOSI, SPIMOSI _PIN); xSPinTypeSPI(SPI0MISO, SPIMISO _PIN); xSPinTypeSPI(SPI0CS, SPICS _PIN); xSPIConfigSet(xSPI0_BASE, 200000, xSPI_FORMAT_MODE_0 | xSPI_DATA_WIDTH32 | xSPI_MSB_FIRST | xSPI_MODE_SLAVE); while(1) { for(i = 0; i < 16; i++) { ulDestData = xSPISingleDataReadWrite(xSPI0_BASE, ulSourceData); } } }
|