stm32串行設備接口SPI控制max31865


  本人是剛入行的嵌入式,之前也沒有多少項目經驗,故在公司的這幾個月里,可謂是如履薄冰,對於公司不同項目使用的不同的設備之多,數據手冊之繁雜,讓我不禁望洋興嘆,故而不願意放棄周末這大好的自我提升時間,努力耕耘,特開此園,與諸君共論(咳咳,有點羞恥,算了就這樣吧,不改了)。

  現階段我比較注重各種協議,所以今后幾個月內會不間斷的更新各種簡單常用的協議,這也算給自己立一個flag吧,督促自己。

——————————————————————————————————————————分割線———————————————————————————————————

  本篇講述的是常用的工業級標准串行協議SPI,經常用於各種嵌入式系統,能夠將微處理器連接到各種片外傳感器、存儲器和控制設備等。

       SPI使用兩根數據線、一根時鍾線、一根控制線(片選線)實現串行通信:

MOSI 主設備數據輸出從設備數據輸入線
MISO 主設備數據輸出從設備數據輸入線
SCK 主設備輸出從設備輸入時鍾線(用於同步數據位)
NSS(CS) 主設備輸出從設備輸入片選線(低電平有效)

 

  SPI由於其時鍾SCK的極性和相性的不同,其工作模式一共有4種:

  在這里我要感謝我的高頻電子線路的老師,謝謝他教會我的許多模電知識(話說我模電課是在干嘛?)

  對於不知道什么是極性和相性的同學,可以先簡單的將這兩個名詞分別記為極性(波形的上下高低起伏)、相性(波形沿時間線的前后的變化)(實在不了解的就當作名詞使用)

  在SPI的SCK時鍾線極性為0時,SPI在空閑時SCK為低電平,工作時由低電平起跳到高電平

       SCK時鍾線極性為1時,SPI在空閑時SCK為高電平,工作時由高電平起跳到低電平

  在SPI的SCK時鍾線相性為0時,SPI會在工作時從第一個時鍾沿開始采集數據

               SCK時鍾線相性為1時,SPI會在工作時從第二個時鍾沿開始采集數據 

                                                                                                                                                                           

                                                                                                                                             

  但我一般不會去記這四種工作模式分別是什么極性什么相位,我只會記住極性為0是低電平跳到高電平,相性為0是第一個邊沿采集,反之亦然,這樣不管在遇到什么從設備時,你都能根據datasheet優雅的設置SPI工作模式。

       

       對於整個SPI通信協議來說,由數據的收發和對收發的控制兩部分組成,其收發數據的整個流程為:

主設備將數據copy到SPI發送緩存區——》主設備拉低要通信的從設備的NSS(CS)片選線電平——》SPI檢測到片選線被拉低后進入工作模式——》位移寄存器將發送緩存區的數據並串轉換發送出去(同時,在從設備中數據由位移寄存器串並轉換到接收緩存區中)

這里要注意:不管是主設備還是從設備的位移轉換器都在主設備的SCK作用下完成移位的,故從設備想要發送數據到主設備必須依賴主設備的時鍾,也就是主設備要空發一個沒用的數據,從設備利用此時主設備給的時鍾趁機將數據發給主設備(從設備好慘啊~~~)

      這是SPI的方框圖,對於收發控制方面感興趣的可以看一下(晚上照的,燈光不好,請見諒)

      圖中的NSS(CS)就是片選,低電平有效,可以使得主設備在與多個設備連接時,也能單獨與某一個從設備進行通信,而不受干擾(這就是渣男的夢想嗎?doge)

      圖中右下部分CR1這個寄存器中的SSM位是用於控制上面說講的NSS是否有效的,SSM為0時,此時NSS就有效;SSM為1時,此時NSS就無效,

      但此時NSS無效了我該怎么實現片選這個功能呢?

      此時就可以通過圖中邊上的那個CR1寄存器中的SSI位來實現,此位一般主設備設為1,當某個從設備設為0時,表示選中該設備了

 

      一般在實際項目的使用中都會用到多個從設備,所有的從設備都共用MOSI、MISO、SCK這三根線,但每個從設備都必須擁有獨屬於自己的NSS片選線,但我不可能有幾個從設備就直接從主設備連幾根NSS線,這太消耗資源了,此時一般都會使用多路復用器來控制。

     SPI寄存器一共有7個,分別為CR1(控制寄存器1)、CR2(控制寄存器2)、SR(狀態寄存器)、DR(數字寄存器)、CRCPR(CRC多項式寄存器)、RXCRCR(接收CRC寄存器)、TXCRCR(發送CRC寄存器);

     這些寄存器在STM32中都是被定義過的,可以不用管該寄存器的地址,我在這里就不一一列舉各個寄存器的使用了,感興趣的可以私下了解一番。

——————————————————————————分割線————————————————————————————————————-——————————————

以上就是對於SPI的理論知識,對於具體的代碼實現的話,由於大家所應用的環境不同,故我這里只能給出一份基於STM32F407GT6通過SPI控制MAX31865溫度采集的代碼示范:

 
//本來想寫點注釋的,但……懶癌犯了,直接復制應該沒有問題的
int main(void) { int temvalue = 0,RTDs = 0,i = 0; uint16_t data = 0; float temps = 0; uint16_t dtemp[2] = {0}; char tem_buff[30] = "0"; char *temptr = tem_buff; RCC_Configuration(); delay_init(); uart_init(115200); SPI2_Init(); MAX31865_Init(); LED_Init(); delay_ms(10); GPIO_ResetBits (GPIOB, GPIO_Pin_12); SPI2_ReadWriteByte(0x80); SPI2_ReadWriteByte(0xC1); GPIO_SetBits (GPIOB, GPIO_Pin_12); while(1) { dtemp[0] ='0'; dtemp[1] ='0'; data = 0; temps = 0; RTDs = 0; SPI_ReadWrite(dtemp); data=((dtemp[0]<<7) | dtemp[1]); temps=data; temps = (temps*402)/32768; RTDs = (int)temps; temvalue = 9e-10*(temps*temps*temps*temps)+4e-7*(temps*temps*temps)+0.0008*(temps*temps)+2.3828*temps-246.81; sprintf(temptr,"RTD:%d temp:%d,data:%d\r\n",RTDs,temvalue,data); for(i = 0;i<30;i++) { USART_SendData(UART4, temptr[i]); while(USART_GetFlagStatus(UART4,USART_FLAG_TC) ==RESET); } GPIO_ResetBits(GPIOC,GPIO_Pin_14); delay_ms(500); GPIO_SetBits(GPIOC,GPIO_Pin_14); delay_ms(500); } }
void MAX31865_Init(void)
{    
  GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(    RCC_APB2Periph_GPIOB, ENABLE );

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;  
     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
     GPIO_Init(GPIOB, &GPIO_InitStructure);
     GPIO_SetBits(GPIOB,GPIO_Pin_12);
 
    GPIO_SetBits (GPIOB, GPIO_Pin_12);  //cs_H
    SPI2_Init();               
    SPI2_SetSpeed(SPI_BaudRatePrescaler_256);
 

}  
void RCC_Configuration(void) 
{    
  ErrorStatus HSEStartUpStatus;
  RCC_DeInit(); 
  RCC_HSEConfig(RCC_HSE_ON); 
  HSEStartUpStatus = RCC_WaitForHSEStartUp(); 
  if(HSEStartUpStatus == SUCCESS) 
  { 
    FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); 
    FLASH_SetLatency(FLASH_Latency_2); 
    RCC_HCLKConfig(RCC_SYSCLK_Div1);  
    RCC_PCLK2Config(RCC_HCLK_Div1);  
    RCC_PCLK1Config(RCC_HCLK_Div4); 
    RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); 
    RCC_PLLCmd(ENABLE); 
    while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) { } 
    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); 
    while(RCC_GetSYSCLKSource() != 0x08) { } 
  } 
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE); 
 
 
} 
void SPI2_Init(void)
{
     GPIO_InitTypeDef GPIO_InitStructure;
  SPI_InitTypeDef  SPI_InitStructure;

    RCC_APB2PeriphClockCmd(    RCC_APB2Periph_GPIOB |RCC_APB2Periph_GPIOA, ENABLE );//PORTBʱÖÓʹÄÜ 
    
    RCC_APB1PeriphClockCmd(    RCC_APB1Periph_SPI2,  ENABLE );//SPI2ʱÖÓʹÄÜ     
 
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13  | GPIO_Pin_15;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //PB13/14/15¸´ÓÃÍÆÍìÊä³ö cs miso mosi
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);//³õʼ»¯GPIOB
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
     GPIO_SetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);  //PB13/14/15ÉÏÀ­
    
     
    
 
    

    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_Low;        
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;    
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;        
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;        
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;    
    SPI_InitStructure.SPI_CRCPolynomial = 7;    
    SPI_Init(SPI2, &SPI_InitStructure);  
 
    SPI_Cmd(SPI2, ENABLE); 
    
    SPI2_ReadWriteByte(0xff);     


} 
void SPI2_SetSpeed(u8 SPI_BaudRatePrescaler)
{
  assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));
    SPI2->CR1&=0XFFC7;
    SPI2->CR1|=SPI_BaudRatePrescaler;    
    SPI_Cmd(SPI2,ENABLE); 

} 
u8 SPI2_ReadWriteByte(u8 TxData)
{
    u8 retry=0;
                 
    while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) 
        {
        retry++;
        if(retry>200)
        {
            
            return 0;
        }
        }              
    SPI_I2S_SendData(SPI2, TxData); 
    retry=0;

    while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET) 
        {
        retry++;
        if(retry>200)
        {
           
            return 0;
        }
        }
     return SPI_I2S_ReceiveData(SPI2); 
                      
}

 


免責聲明!

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



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