SPI通信原理---STM32F4--HAL
SPI接口原理
SPI是一種高速全雙工同步通信,在芯片管腳上占用四根線,主要應(yīng)用在EEPROM、FLASH、實時時鐘、AD轉(zhuǎn)換器,還有數(shù)字信號處理器和數(shù)字信號解碼器之間。
SPI接口使用4根線通信。
- MISO:主設(shè)備數(shù)據(jù)輸入,從設(shè)備數(shù)據(jù)輸出
- MOSI:主設(shè)備數(shù)據(jù)輸出,從設(shè)備數(shù)據(jù)輸入
- SCLK:時鐘信號,由主設(shè)備產(chǎn)生
- CS:片選信號,由主設(shè)備控制
工作原理
時鐘信號的相位
SPI_CR寄存器的CPOL和CPHA位,能夠組合成四種可能的時序關(guān)系,如果CPOL位為0,SCK引腳在空閑狀態(tài)保持低電平,如果CPOL=1 ,SCK引腳在空閑狀態(tài)下保持高電平。
如果CPHA=0,在串行同步時鐘的第一個跳變沿(上升或下降)數(shù)據(jù)被采樣;如果CPHA=1,在串行同步時鐘的第二個跳變沿(上升或下降)數(shù)據(jù)被采樣
數(shù)據(jù)幀格式
根據(jù)SPI_CR1寄存器中的LSBFIRST位,輸出數(shù)據(jù)時可以MSB優(yōu)先,也可以LSB優(yōu)先
根據(jù)SPI_CR1寄存器的DFF位,每個數(shù)據(jù)幀可以是8位或是16位
程序配置過程
我們使用SPI和w25Q256通信,硬件連接為
具體代碼實現(xiàn)
SPI_HandleTypeDef SPI5_Handler; //SPI句柄//以下是SPI模塊的初始化代碼,配置成主機模式 //SPI口初始化 //這里針是對SPI5的初始化 void SPI5_Init(void) {SPI5_Handler.Instance=SPI5; //SP5SPI5_Handler.Init.Mode=SPI_MODE_MASTER; //設(shè)置SPI工作模式,設(shè)置為主模式SPI5_Handler.Init.Direction=SPI_DIRECTION_2LINES; //設(shè)置SPI單向或者雙向的數(shù)據(jù)模式:SPI設(shè)置為雙線模式SPI5_Handler.Init.DataSize=SPI_DATASIZE_8BIT; //設(shè)置SPI的數(shù)據(jù)大小:SPI發(fā)送接收8位幀結(jié)構(gòu)SPI5_Handler.Init.CLKPolarity=SPI_POLARITY_HIGH; //串行同步時鐘的空閑狀態(tài)為高電平SPI5_Handler.Init.CLKPhase=SPI_PHASE_2EDGE; //串行同步時鐘的第二個跳變沿(上升或下降)數(shù)據(jù)被采樣SPI5_Handler.Init.NSS=SPI_NSS_SOFT; //NSS信號由硬件(NSS管腳)還是軟件(使用SSI位)管理:內(nèi)部NSS信號有SSI位控制SPI5_Handler.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_256;//定義波特率預(yù)分頻的值:波特率預(yù)分頻值為256SPI5_Handler.Init.FirstBit=SPI_FIRSTBIT_MSB; //指定數(shù)據(jù)傳輸從MSB位還是LSB位開始:數(shù)據(jù)傳輸從MSB位開始SPI5_Handler.Init.TIMode=SPI_TIMODE_DISABLE; //關(guān)閉TI模式SPI5_Handler.Init.CRCCalculation=SPI_CRCCALCULATION_DISABLE;//關(guān)閉硬件CRC校驗SPI5_Handler.Init.CRCPolynomial=7; //CRC值計算的多項式HAL_SPI_Init(&SPI5_Handler);//初始化__HAL_SPI_ENABLE(&SPI5_Handler); //使能SPI5SPI5_ReadWriteByte(0Xff); //啟動傳輸 }//SPI5底層驅(qū)動,時鐘使能,引腳配置 //此函數(shù)會被HAL_SPI_Init()調(diào)用 //hspi:SPI句柄 void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi) {GPIO_InitTypeDef GPIO_Initure;__HAL_RCC_GPIOF_CLK_ENABLE(); //使能GPIOF時鐘__HAL_RCC_SPI5_CLK_ENABLE(); //使能SPI5時鐘//PF7,8,9GPIO_Initure.Pin=GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9;GPIO_Initure.Mode=GPIO_MODE_AF_PP; //復(fù)用推挽輸出GPIO_Initure.Pull=GPIO_PULLUP; //上拉GPIO_Initure.Speed=GPIO_SPEED_FAST; //快速 GPIO_Initure.Alternate=GPIO_AF5_SPI5; //復(fù)用為SPI5HAL_GPIO_Init(GPIOF,&GPIO_Initure); }先使用HAL_SPI_Init函數(shù)對SPI進(jìn)行初始化,注意我們只初始化PF7、PF8、PF9,也就是SPI的SCK線,MISO線和MOSI線,CS線還沒有初始化,HAL_SPI_MspInit是HAL_SPI_Init的回調(diào)函數(shù),我們在這里初始化GPIO以及使能。上面的代碼完成了第1到4步。接下來我們就可以進(jìn)行數(shù)據(jù)傳輸了。
W25Q256
W25Q256是容量為32M字節(jié)的串行Flash芯片,它將32M的容量分為512塊(Block),每個塊大小為64K字節(jié),每個塊又分為16個扇區(qū)(sector),每個扇區(qū)4K字節(jié),W25Q256最小擦除單位為一個扇區(qū),也就是每次必須擦除4K個字節(jié)。
W25QXX_Write函數(shù)思路
具體代碼實現(xiàn)
//寫SPI FLASH //在指定地址開始寫入指定長度的數(shù)據(jù) //該函數(shù)帶擦除操作! //pBuffer:數(shù)據(jù)存儲區(qū) //WriteAddr:開始寫入的地址(24bit) //NumByteToWrite:要寫入的字節(jié)數(shù)(最大65535) u8 W25QXX_BUFFER[4096]; void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite) { u32 secpos;u16 secoff;u16 secremain; u16 i; u8 * W25QXX_BUF; W25QXX_BUF=W25QXX_BUFFER; secpos=WriteAddr/4096;//扇區(qū)地址 secoff=WriteAddr%4096;//在扇區(qū)內(nèi)的偏移secremain=4096-secoff;//扇區(qū)剩余空間大小 //printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//測試用if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096個字節(jié)while(1) { W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//讀出整個扇區(qū)的內(nèi)容for(i=0;i<secremain;i++)//校驗數(shù)據(jù){if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除 }if(i<secremain)//需要擦除{W25QXX_Erase_Sector(secpos);//擦除這個扇區(qū)for(i=0;i<secremain;i++) //復(fù)制{W25QXX_BUF[i+secoff]=pBuffer[i]; }W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//寫入整個扇區(qū) }else W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//寫已經(jīng)擦除了的,直接寫入扇區(qū)剩余區(qū)間. if(NumByteToWrite==secremain)break;//寫入結(jié)束了else//寫入未結(jié)束{secpos++;//扇區(qū)地址增1secoff=0;//偏移位置為0 pBuffer+=secremain; //指針偏移WriteAddr+=secremain;//寫地址偏移 NumByteToWrite-=secremain; //字節(jié)數(shù)遞減if(NumByteToWrite>4096)secremain=4096; //下一個扇區(qū)還是寫不完else secremain=NumByteToWrite; //下一個扇區(qū)可以寫完了} }; }每個扇區(qū)的大小是4K字節(jié),也就是4094,除以4096就得到扇區(qū)的地址,模4096就得到在扇區(qū)里面開始寫的地址。
secremain是扇區(qū)剩余空間大小,NumByteToWrite是要寫入的字節(jié)數(shù),如果NumByteToWrite<=secremain就不需要跨扇區(qū)。所以在后面有:
if(NumByteToWrite==secremain)break;//寫入結(jié)束了不需要跨扇區(qū),寫入結(jié)束,否則的話,就需要跨扇區(qū),扇區(qū)號要加1,扇區(qū)偏移地址為0
secpos++;//扇區(qū)地址增1 secoff=0;//偏移位置為0W25QXX_Read先保存扇區(qū)數(shù)據(jù)到W25QXX_BUF中,然后遍歷剩余扇區(qū)數(shù)據(jù)有無不等于0xFF的,如果有,則調(diào)用W25QXX_Erase_Sector擦除整個扇區(qū),然后將要寫的數(shù)據(jù)線寫到W25QXX_BUF中,最后一次性把W25QXX_BUF緩沖寫到扇區(qū)中。
如果需要跨扇區(qū)寫數(shù)據(jù)
else//寫入未結(jié)束{secpos++;//扇區(qū)地址增1secoff=0;//偏移位置為0 pBuffer+=secremain; //指針偏移WriteAddr+=secremain;//寫地址偏移 NumByteToWrite-=secremain; //字節(jié)數(shù)遞減if(NumByteToWrite>4096)secremain=4096; //下一個扇區(qū)還是寫不完else secremain=NumByteToWrite; //下一個扇區(qū)可以寫完了}while會一直循環(huán),直到寫入結(jié)束了
if(NumByteToWrite==secremain)break;//寫入結(jié)束了W25QXX_Write就是在指定地址連續(xù)寫入NumByteToWrite個字節(jié)數(shù)據(jù)
讀取FLASH數(shù)據(jù)
//讀取SPI FLASH //在指定地址開始讀取指定長度的數(shù)據(jù) //pBuffer:數(shù)據(jù)存儲區(qū) //ReadAddr:開始讀取的地址(24bit) //NumByteToRead:要讀取的字節(jié)數(shù)(最大65535) void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead) { u16 i; W25QXX_CS=0; //使能器件 SPI5_ReadWriteByte(W25X_ReadData); //發(fā)送讀取命令 if(W25QXX_TYPE==W25Q256) //如果是W25Q256的話地址為4字節(jié)的,要發(fā)送最高8位{SPI5_ReadWriteByte((u8)((ReadAddr)>>24)); }SPI5_ReadWriteByte((u8)((ReadAddr)>>16)); //發(fā)送24bit地址 SPI5_ReadWriteByte((u8)((ReadAddr)>>8)); SPI5_ReadWriteByte((u8)ReadAddr); for(i=0;i<NumByteToRead;i++){ pBuffer[i]=SPI5_ReadWriteByte(0XFF); //循環(huán)讀數(shù) }W25QXX_CS=1; }W25QXX_Read從ReadAddr地址連續(xù)讀取NumByteToRead個字節(jié)數(shù)據(jù)
main函數(shù)
我們寫入數(shù)據(jù)到FALSH中,然后讀取出來在LCD上顯示
//要寫入到W25Q16的字符串?dāng)?shù)組 const u8 TEXT_Buffer[]={"Apollo STM32F4 SPI TEST"}; #define SIZE sizeof(TEXT_Buffer) int main(void) {u8 key;u16 i=0;u8 datatemp[SIZE];u32 FLASH_SIZE;HAL_Init(); //初始化HAL庫 Stm32_Clock_Init(360,25,2,8); //設(shè)置時鐘,180Mhzdelay_init(180); //初始化延時函數(shù)uart_init(115200); //初始化USARTLED_Init(); //初始化LED KEY_Init(); //初始化按鍵SDRAM_Init(); //初始化SDRAMLCD_Init(); //初始化LCDW25QXX_Init(); //W25QXX初始化POINT_COLOR=RED;LCD_ShowString(30,50,200,16,16,"Apollo STM32F4/F7"); LCD_ShowString(30,70,200,16,16,"SPI TEST"); LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");LCD_ShowString(30,110,200,16,16,"2016/1/16"); LCD_ShowString(30,130,200,16,16,"KEY1:Write KEY0:Read"); //顯示提示信息 while(W25QXX_ReadID()!=W25Q256) //檢測不到W25Q256{LCD_ShowString(30,150,200,16,16,"W25Q256 Check Failed!");delay_ms(500);LCD_ShowString(30,150,200,16,16,"Please Check! ");delay_ms(500);LED0=!LED0; //DS0閃爍}LCD_ShowString(30,150,200,16,16,"W25Q256 Ready!"); FLASH_SIZE=32*1024*1024; //FLASH 大小為32M字節(jié)POINT_COLOR=BLUE; //設(shè)置字體為藍(lán)色 while(1){key=KEY_Scan(0);if(key==KEY1_PRES)//KEY1按下,寫入W25Q128{LCD_Fill(0,170,239,319,WHITE);//清除半屏 LCD_ShowString(30,170,200,16,16,"Start Write W25Q256....");W25QXX_Write((u8*)TEXT_Buffer,FLASH_SIZE-100,SIZE); //從倒數(shù)第100個地址處開始,寫入SIZE長度的數(shù)據(jù)LCD_ShowString(30,170,200,16,16,"W25Q256 Write Finished!"); //提示傳送完成}if(key==KEY0_PRES)//KEY0按下,讀取字符串并顯示{LCD_ShowString(30,170,200,16,16,"Start Read W25Q256.... ");W25QXX_Read(datatemp,FLASH_SIZE-100,SIZE); //從倒數(shù)第100個地址處開始,讀出SIZE個字節(jié)LCD_ShowString(30,170,200,16,16,"The Data Readed Is: "); //提示傳送完成LCD_ShowString(30,190,200,16,16,datatemp); //顯示讀到的字符串} i++;delay_ms(10);if(i==20){LED0=!LED0;//提示系統(tǒng)正在運行 i=0;} } }總結(jié)
以上是生活随笔為你收集整理的SPI通信原理---STM32F4--HAL的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 奥图码投影机4K哪个行号家用好
- 下一篇: “岂但避霜雪”上一句是什么