1. 程式人生 > >框架-SPI四種模式+通用裝置驅動實現

框架-SPI四種模式+通用裝置驅動實現

[toc] --- ### 前言 * **SPI 介紹**為蒐集百度資料+個人理解 * 其餘為原創(有誤請指正) * 集四種模式於一身 ### 筆錄草稿 ### SPI介紹 * SPI 協議簡介 * SPI 協議是由摩托羅拉公司提出的通訊協議(Serial Peripheral Interface),即序列外圍裝置介面,是一種高速**全雙工**的通訊匯流排。 * 是一個環形匯流排結構 * 由 ss(cs)、sck、sdi、sdo 構成 * 其時序主要是在 sck 的控制下,兩個雙向移位暫存器進行資料交換。 * 物理線說明 * SS * 從裝置選擇訊號線,常稱為片選訊號線,也稱為NSS、CS。 * 用於選擇從機。 * SCK (Serial Clock) * 時鐘訊號線 * 用於通訊資料同步。 * MOSI (Master Output, Slave Input) * 主裝置輸出/從裝置輸入引腳。 * 主機發出,從機接收。 * MISO (Master Input,,Slave Output) * 主裝置輸入/從裝置輸出引腳。 * 從機發出,主機接收。 * **SPI 四種模式** * ***請移步到下面章節學習*** * SPI的協議層 * SPI協議定義了通訊的起始和停止訊號、資料有效性、時鐘同步等環節。 * 基本通訊過程 * 圖解 1. 標號1:NSS訊號線由高變低,是SPI通訊的起始訊號。 2. 標號6:NSS訊號由低變高,是SPI通訊的停止訊號。 * 簡單時序圖![](https://img2020.cnblogs.com/blog/2085252/202010/2085252-20201031193956127-1618655976.png) * 模式時序圖![](https://img2020.cnblogs.com/blog/2085252/202010/2085252-20201031194011237-428911815.png) ### SPI四種模式 ** * 四種模式由 **CPOL** 和 **CPHA** 組合區分 * **CPOL** * 時鐘極性 * 是指SPI通訊裝置處於空閒狀態時,SCK訊號線的電平訊號 * 為 **0** 時 * SCK 空閒狀態為 低電平 * 為 **1** 時 * SCK 空閒狀態為 高電平 * **CPHA** * 時鐘相位 * 是指資料的取樣的時刻 * 為 **0** 時 * MOSI或MISO資料線上的訊號將會在SCK時鐘線的“奇數邊沿”被取樣。(即是第一個邊沿) * 這種模式適合那種從裝置一旦被片選後就輸出資料到MISO線上。 * 為 **1** 時 * 資料線在SCK的“偶數邊沿”取樣。(即是第二個邊沿) * 這種模式適合那種從裝置被片選後還需要一個時鐘才能 輸出資料到MISO線上。 * 四種模式(**CPOL, CPHA**) * 模式 0:(**0, 0**) * SCK空閒為 **低電平**,資料在SCK的 **上升沿** 被取樣 * 模式 1:(**0, 1**) * SCK空閒為 **低電平**,資料在SCK的 **下降沿** 被取樣 * 模式 2:(**1, 0**) * SCK空閒為 **高電平**,資料在SCK的 **下降沿** 被取樣 * 模式 3:(**1, 1**) * SCK空閒為 **高電平**,資料在SCK的 **上升沿** 被取樣 ### SPI 驅動框架 ** #### 框架 * 實現方法參考 [I2C裝置驅動拆解](https://www.cnblogs.com/lizhuming/p/13834535.html) * 自己先在寫出四種模式的讀寫時序,便會發現以下規律 * 讀寫的邏輯差不多都一樣,只是 **SCK** 訊號線出現的位置及高低電平會因不同模式而不同。(*這裡我就不分別寫出4種模式的單獨實現了,直接上規律表,然後實現統一的原始碼*) * |R/W|CPOL|CPHA|位置1-SCK|位置2-SCK|位置3-SCK|位置4-SCK| |:-:|:-:|:-:|:-:|:-:|:-:|:-:| |R|0|0|X|0|1|0| |R|0|1|X|1|0|0| |R|1|0|X|1|0|1| |R|1|1|X|0|1|1| |-|-|-|-|-|-|-| |W|0|0|X|0|1|0| |W|0|1|0|1|0|X| |W|1|0|X|1|0|1| |W|1|1|1|0|1|X| 由上規律得出 支援四種模式的 SPI 讀寫原始碼 * SPI 寫函式 ```c /** * @brief SPI 寫函式 * @param * @retval * @author lzm */ void spiWriteOneByte(eSPI_ID id, unsigned char data) { unsigned char i; const spi_t * spi = &spiDriverElem[id]; // 位置1 if(spi->CPHA){ spiOut(spi->sckGpiox, spi->sckPin, spi->CPOL); } for(i=0; i<8; i++) { // 位置2 spiOut(spi->sckGpiox, spi->sckPin, (spi->CPOL != spi->CPHA)); if(data & 0x80){ spiMosiOutHi(spi); } else{ spiMosiOutLo(spi); } data <<= 1; spi->delayUsFun(spi->readDelayUsCnt); // 位置3 spiOut(spi->sckGpiox, spi->sckPin, (spi->CPOL == spi->CPHA)); } // 位置4 if(!(spi->CPHA)){ spiOut(spi->sckGpiox, spi->sckPin, spi->CPOL); } } ``` * SPI 讀函式 ```c /** * @brief SPI 讀函式 * @param * @retval * @author lzm */ unsigned char spiReadOneByte(eSPI_ID id) { unsigned char i; unsigned char ret; const spi_t * spi = &spiDriverElem[id]; // 位置1 for(i=0; i<8; i++) { // 位置2 spiOut(spi->
sckGpiox, spi->sckPin, (spi->CPOL != spi->CPHA)); ret <<= 1; if(spiMisoIn(spi)) ret |= 0x01; else ret &= 0xfe; spi->delayUsFun(spi->readDelayUsCnt); // 位置3 spiOut(spi->sckGpiox, spi->sckPin, (spi->CPOL == spi->CPHA)); } // 位置4 spiOut(spi->sckGpiox, spi->sckPin, spi->CPOL); return ret; } ``` * SPI 讀寫函式 ```c /** * @brief SPI 讀寫一體函式 * @param * @retval * @author lzm */ unsigned char spiRWOneByte(eSPI_ID id, unsigned char data) { unsigned char i; unsigned char ret; const spi_t * spi = &spiDriverElem[id]; // 位置1 if(spi->CPHA){ spiOut(spi->sckGpiox, spi->sckPin, spi->CPOL); } for(i=0; i<8; i++) { // 位置2 spiOut(spi->
sckGpiox, spi->sckPin, (spi->CPOL != spi->CPHA)); if(data & 0x80){ spiMosiOutHi(spi); } else{ spiMosiOutLo(spi); } data <<= 1; spi->delayUsFun(spi->readDelayUsCnt); // 位置3 spiOut(spi->sckGpiox, spi->sckPin, (spi->CPOL == spi->CPHA)); ret <<= 1; if(spiMisoIn(spi)) ret |= 0x01; else ret &= 0xfe; spi->delayUsFun(spi->readDelayUsCnt); } // 位置4 if(!(spi->CPHA)){ spiOut(spi->sckGpiox, spi->sckPin, spi->CPOL);