日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

STM32F1笔记(十三)SPI

發布時間:2023/12/1 编程问答 50 豆豆
生活随笔 收集整理的這篇文章主要介紹了 STM32F1笔记(十三)SPI 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

SPI:Serial Peripheral interface,串行外圍設備接口。

SPI接口一般使用4條線通信:

MISO主設備數據輸入,從設備數據輸出。

MOSI主設備數據輸出,從設備數據輸入。

SCLK時鐘信號,由主設備產生。

從圖中可以看出,主機和從機都有一個串行移位寄存器,主機通過向它的SPI串行寄存器寫入一個字節來發起一次傳輸。寄存器通過MOSI信號線將字節傳送給從機,從機也將自己的移位寄存器中的內容通過MISO信號線返回給主機。這樣,兩個移位寄存器中的內容就被交換。外設的寫操作和讀操作是同步完成的。如果只進行寫操作,主機只需忽略接收到的字節;反之,若主機要讀取從機的一個字節,就必須發送一個空字節來引發從機的傳輸,這個空字節通常稱為dummy_byte。

SPI總線四種工作方式是為了和不同外設進行數據交換,其輸出串行同步時鐘極性和相位可以進行配置。

時鐘極性CPOL=0,串行同步時鐘的空閑狀態為低電平;如果CPOL=1,串行同步時鐘的空閑狀態為高電平。

時鐘相位CPHA=0,在串行同步時鐘的第一個跳變沿(上升或下降)數據被采樣;如果CPHA=1,在串行同步時鐘的第二個跳變沿(上升或下降)數據被采樣。

SPI主從設備的時鐘相位和極性應該一致。

?

STM32F1的SPI時鐘最多可以到18Mhz。可用DMA。

SPI配置示例

void SPI2_Init(void) {GPIO_InitTypeDef GPIO_InitStructure;SPI_InitTypeDef SPI_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_SetBits(GPIOB, GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_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_High;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); }

SPI_Direction:選擇半雙工、全雙工。

SPI_Mode:選擇主機模式、從機模式。

SPI_DataSize:選擇8位還是16位幀格式傳輸。

SPI_CPOL:設置時鐘極性為高電平還是低電平。

SPI_CPHA:設置時鐘相位,選擇在時鐘的第幾個跳變沿數據被采樣。

SPI_NSS:設置NSS信號由硬件(NSS管腳)還是軟件控制。

SPI_BaudRatePrescaler:設置SPI波特率預分頻值。示例選擇256即本次SPI傳輸速度=36M/256=140.625KHz。

SPI_FirstBit:設置數據傳輸順序是MSB位在前還是LSB位在前。大部分應用是MSB在前。

SPI_CRCPolynomial:設置CRC校驗多項式,提高通信可靠性,大于1即可。

?

標準庫里SPI發送一個字節函數為

SPI_I2S_SendData(SPI2, data);

接受一個字節函數為

data =?SPI_I2S_ReceiveData(SPI2);

因此需要我們再封裝幾個函數,SPI寫任意字節,讀任意字節,讀寫任意字節。

?

SPI最常見的應用場景是讀寫FLASH。

正點原子戰艦板子上的FLASH為W25Q128。該款FLASH的容量為128Mb也就是16M字節。將16M的容量分為256個塊(Block),每個塊大小為64K字節,每個塊又分為16個扇區(Sector),每個扇區4K字節。W25Q128最小擦除單位為一個扇區,也就是每次必須擦除4K字節。這樣就需要開辟一個至少4K的緩存區,即單片機需要有4K以上的SRAM才能很好操作。W25Q128最大SPI時鐘能達80Mhz。

?

FLASH拿到手通常先測試能否到它的ID

從W25QXX datasheet ?里看到,主機給它發0x90,0x00,0x00,0x00,即可讀取16位ID

u16 W25QXX_ReadID(void) {u16 Temp = 0;W25QXX_CS = 0;SPI2_ReadWriteByte(0x90);SPI2_ReadWriteByte(0x00);SPI2_ReadWriteByte(0x00);SPI2_ReadWriteByte(0x00);Temp |= SPI2_ReadWriteByte(0xFF) << 8;Temp |= SPI2_ReadWriteByte(0xFF);W25QXX_CS = 1;return Temp; }

這里要注意讀ID時MOSI發送的是0xFF,這個就是DUMMY_BYTE,大多DUMMY_BYTE是0xFF,少部分設備會有特殊要求

FLASH讀取數據,先發0x03和讀取數據的地址起點,然后即可讀取。

代碼如下

void W25QXX_Read(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead) { u16 i; W25QXX_CS = 0;SPI2_ReadWriteByte(0x03);SPI2_ReadWriteByte((u8) ((ReadAddr) >> 16));SPI2_ReadWriteByte((u8) ((ReadAddr) >> 8)); SPI2_ReadWriteByte((u8) ReadAddr);for(i = 0; i < NumByteToRead; i++){ pBuffer[i] = SPI2_ReadWriteByte(0XFF);}W25QXX_CS = 1; }

?

W25Q128寫使能,要給flash寫數據,必須先寫使能。

從W25QXX datasheet ?里看到,只要發0x06即可寫使能。

void W25QXX_Write_Enable(void) {W25QXX_CS=0;SPI2_ReadWriteByte(0x06);W25QXX_CS=1; }

?

寫使能后還不能立即往FLASH里面寫數據,需要將寫入數據的FLASH地址擦除。

void W25QXX_Erase_Sector(u32 Dst_Addr) { Dst_Addr *= 4096;W25QXX_Write_Enable();W25QXX_Wait_Busy();W25QXX_CS = 0;SPI2_ReadWriteByte(0x20);SPI2_ReadWriteByte((u8) ((Dst_Addr) >> 16)); SPI2_ReadWriteByte((u8) ((Dst_Addr) >> 8)); SPI2_ReadWriteByte((u8) Dst_Addr);W25QXX_CS = 1;W25QXX_Wait_Busy(); }

?

FLASH寫數據需要考慮跨扇區寫入的問題,因為FLASH只提供了往一個頁page寫數據的命令0x02。

void W25QXX_Write_Page(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite) {u16 i;W25QXX_Write_Enable();W25QXX_CS = 0;SPI2_ReadWriteByte(0x02);SPI2_ReadWriteByte((u8) ((WriteAddr) >> 16));SPI2_ReadWriteByte((u8) ((WriteAddr) >> 8));SPI2_ReadWriteByte((u8) WriteAddr);for(i = 0; i < NumByteToWrite; i++){SPI2_ReadWriteByte(pBuffer[i]);}W25QXX_CS = 1;W25QXX_Wait_Busy(); } void W25QXX_Write_NoCheck(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite) { u16 pageremain;pageremain = 256 - WriteAddr % 256;if(NumByteToWrite <= pageremain){pageremain = NumByteToWrite;}while(1){W25QXX_Write_Page(pBuffer , WriteAddr, pageremain);if(NumByteToWrite == pageremain){ break;}else{pBuffer += pageremain;WriteAddr += pageremain; NumByteToWrite -= pageremain;if(NumByteToWrite > 256){pageremain = 256;}else{pageremain = NumByteToWrite;}}}; } 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; secoff = WriteAddr % 4096;secremain = 4096 - secoff; if(NumByteToWrite <= secremain){secremain = NumByteToWrite;}while(1) { W25QXX_Read(W25QXX_BUF, secpos * 4096, 4096);for(i = 0; i < secremain; i++){if(W25QXX_BUF[secoff + i] != 0XFF){break;}}if(i < secremain){W25QXX_Erase_Sector(secpos);for(i = 0; i < secremain; i++){W25QXX_BUF[i + secoff] = pBuffer[i]; }W25QXX_Write_NoCheck(W25QXX_BUF, secpos * 4096, 4096);}else{W25QXX_Write_NoCheck(pBuffer, WriteAddr, secremain);}if(NumByteToWrite == secremain){break;}else{secpos++;secoff = 0;pBuffer += secremain;WriteAddr += secremain;NumByteToWrite -= secremain;if(NumByteToWrite > 4096){secremain = 4096;}else{secremain = NumByteToWrite;}} } }

?

總結

以上是生活随笔為你收集整理的STM32F1笔记(十三)SPI的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。