【框架】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通訊的停止信號。
  • 簡單時序圖

  • 模式時序圖

SPI四種模式 **

  • 四種模式由 CPOLCPHA 組合區分
  • 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設備驅動拆解
  • 自己先在寫出四種模式的讀寫時序,便會發現以下規律
  • 讀寫的邏輯差不多都一樣,只是 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 寫函數
/**
  * @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 讀函數
/**
  * @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 讀寫函數
/**
  * @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);
	}
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM