1. 程式人生 > >踏踏實實搞清 stm32 SPI匯流排 基礎和程式設計

踏踏實實搞清 stm32 SPI匯流排 基礎和程式設計

SPI應用是相當的廣了,同步序列匯流排,同步同步當然需要時鐘訊號來統一了,這樣通訊雙方通訊時就比較默契沒那麼延遲(呆呆)了。應用於中低速場合。

學起來從哪些地方入手呢?如下:

SPI概念與特點   關鍵的時序接受    程式設計時重點     STM32內部的SPI使用(介紹、功能、中斷)

SPI概念與特點:不多說,序列,需要同步訊號   主從結構的   CS(片選)  SCK   SDI  SDO

               全雙工  一主控多從   8或16位資料通訊

其它特點:8個主模式波特率預分頻係數  fpcll/2

         主模式和從模式下快速通行  並支援切換

         程式設計MSB 或LSB在前

         專用傳送和接受標誌可促發中斷

         有SPI忙標誌位

         支援硬體CRC校驗,傳送模式下crc值作為最後一個位元組被髮送,接受模式下最後一位元組自動CRC校驗

         支援錯誤中斷標誌,支援DMA功能的1位元組傳送和接受緩衝器:產生髮送和接受請求。

SPI匯流排時序介紹:

看下下圖應該就會了


使用stm32  spi需要以下的步驟

1.管腳因為複用的,故先配置好管腳、並開啟spi時鐘

2.設定spi的工作模式

通過 SPI1_CR1 來設定,設定SPI1主機模式,設定資料格式8位,然後通過 CPOL 和 CPHA 位來設定 SCK時鐘極性及取樣方式。並設定 SPI1 的時鐘頻率(最大18Mhz),以及資料的格式(MSB 在前還是 LSB在前)。
3.使能SPI.

SPI韌體庫函式

下面是初始化,必須得結構體原型

typedef struct
{
  uint16_t SPI_Direction;//設定方向     (2線全雙工、2線只接受、一線傳送、一線接受)

  uint16_tSPI_Mode;     //模式         (從或主裝置)

  uint16_t SPI_DataSize; //寬度         (8或16位)

  uint16_tSPI_CPOL;     //時鐘極性     (低或高)

  uint16_tSPI_CPHA;     //時鐘相位     (第一個或第二個跳變沿)

  uint16_tSPI_NSS;      //片選方式     (硬體或軟體方式)

  uint16_t SPI_BaudRatePrescaler; //波特率預分頻    (從2---256分頻)  

  uint16_tSPI_FirstBit;  //最先發送的位            (最低位,還是最高位在先)        

  uint16_tSPI_CRCPolynomial; //設定crc多項式        (數字)如7

}SPI_InitTypeDef;

下面是例項,對SPI2進行的初始化

void SPI2_Init(void) 
{
 SPI_InitTypeDef  SPI_InitStructure;
 GPIO_InitTypeDef GPIO_InitStructure;
 
 //配置SPI2管腳
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOB, ENABLE);
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 |GPIO_Pin_14| GPIO_Pin_15;
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //複用推輓輸出
 GPIO_Init(GPIOB, &GPIO_InitStructure);
 
 //SPI2配置選項
 RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2 ,ENABLE);
   
 SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
 SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
 SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
 SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
 SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
 SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
 SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;
 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
 SPI_InitStructure.SPI_CRCPolynomial = 7;
 SPI_Init(SPI2, &SPI_InitStructure);

 //使能SPI2
 SPI_Cmd(SPI2, ENABLE);  
}

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

以上是,初始化的一些函式,發現韌體庫還有很多函式沒用上,下面就要談到應用了

STM32的SPI序列外圍匯流排介面,本程式,是將STM32的SPI配置為全雙工模式,且NSS使用的軟體模式。在使用SPI前,下面的這個過程我們必須理解,即STM32作為主機發送一個位元組資料時,必然能接收到一個數據,至於資料是否處理,由程式操作。

● 全雙工模式(BIDIMODE=0並且RXONLY=0) 

─  當寫入資料到SPI_DR暫存器(傳送緩衝器)後,傳輸開始; 

─  在傳送第一位資料的同時,資料被並行地從傳送緩衝器傳送到8位的移位暫存器中,

然後按順序被序列地移位送到MOSI引腳上; 

─  與此同時,在MISO引腳上接收到的資料,按順序被序列地移位進入8位的移位暫存器

中,然後被並行地傳送到SPI_DR暫存器(接收緩衝器)中。 

注意:也就是說,在主機模式下,傳送和接收是同時進行的,所以我們傳送了一個數據,也就能接收到一個數據。而STM32內部硬體是這個過程的支撐!

讀一個位元組,往裡面傳送0,外設就返回一個數據了,傳送的0外設不處理(需要先寫入命令生效)

#define SPI_ReadByte(SPIx) SPI_WriteByte(SPIx,0)

寫一個位元組就直接傳送相應的位元組,外設就返回一個數據了

u8 SPI_WriteByte(SPI_TypeDef* SPIx,u8byte);

//spi 寫一個位元組...................................................................
u8 SPI_WriteByte(SPI_TypeDef* SPIx,u8 Byte)
{
 while((SPIx->SR&SPI_I2S_FLAG_TXE)==RESET);  //等待發送區空  
 SPIx->DR=Byte;   //傳送一個byte  
 while((SPIx->SR&SPI_I2S_FLAG_RXNE)==RESET);//等待接收完一個byte 
 returnSPIx->DR;                //返回收到的資料   
}


void M25P16_Write_Enable(void)
{
 Select_Flash(); 
 SPI_Flash_Write(WRITE_ENABLE); 
 NotSelect_Flash();
}

void M25P16_Read_Id(u8 * id)
{
 u8 i;
 
 Select_Flash(); 
 SPI_Flash_Write(READ_ID); 

 for(i=0;i<20;i++)
 {
  id[i] = SPI_Flash_Read(); 
 }
 
 NotSelect_Flash();
}

void M25P16_Write_Status_Reg(u8 reg)
{
 Select_Flash(); 
 SPI_Flash_Write(WRITE_STAUS_REG); 
 SPI_Flash_Write(reg);
 NotSelect_Flash();
}

void M25P16_Read_Data(u32 addr,u32 len,u8*buf)
{
 u32 i;
 Select_Flash(); 
 SPI_Flash_Write(READ_DATA); 
 SPI_Flash_Write((addr>>16) & 0xff);
 SPI_Flash_Write((addr>>8) & 0xff);
 SPI_Flash_Write(addr & 0xff);
 for(i=0;i<len;i++)
 {
  buf[i]=SPI_Flash_Read();
 }
 NotSelect_Flash();
}

//頁程式設計函式,頁程式設計前一定要進行頁擦除!!!
void M25P16_Page_Program(u32 addr,u16 len,u8 *buf)
{
 u32 i;
 
 M25P16_Write_Enable();
 Select_Flash(); 
 SPI_Flash_Write(PAGE_PROGRAM); 
 SPI_Flash_Write((addr>>16) & 0xff);
 SPI_Flash_Write((addr>>8) & 0xff);
 SPI_Flash_Write(addr & 0xff);

 for(i=0;i<len;i++)
  SPI_Flash_Write(buf[i]);

 NotSelect_Flash();

 while(M25P16_Read_Status_Reg()&0x01); 
}

以上函式搞懂了,特別市紅色部分標註的為重點,對於spi也就基本清楚了,這些也算是最底層的函數了,提供基本的API供以後檔案系統或其它地方使用。

----------------------------------------------------------------------------------------------

最後補上一些檔案框架的說明

首先我們把最底層的SPI初始化寫上,函式如下:


可見有晶片自帶SPI模組  有給mp3晶片的,有給flash的,還有給無線網路的,還有給軟體模擬spi時序供給觸控式螢幕的控制器的,然後我們把這個.c檔案配套的.h檔案給下面具體的函式包含,就能正確的選取和使用了,當這些具體功能的函式寫好了後,對應得.h函式就又繼續給更高級別的應用層使用。

今天先就介紹下spi_flash模組了,等天補上其它3個模組的講解,畢竟現在是基礎哦。