本人是剛入行的嵌入式,之前也沒有多少項目經驗,故在公司的這幾個月里,可謂是如履薄冰,對於公司不同項目使用的不同的設備之多,數據手冊之繁雜,讓我不禁望洋興嘆,故而不願意放棄周末這大好的自我提升時間,努力耕耘,特開此園,與諸君共論(咳咳,有點羞恥,算了就這樣吧,不改了)。
現階段我比較注重各種協議,所以今后幾個月內會不間斷的更新各種簡單常用的協議,這也算給自己立一個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); }