stm32-SPI讀取串行FLASH
阿新 • • 發佈:2018-06-27
stm32 支持 時鐘 檢測 ora 緩沖 sequence pass conf I2C 協議中通過設備地址來尋址、選中總線上的某個設備並與其進行通訊;而SPI 協議中沒有設備地址,它使用NSS 信號線來尋址,當主機要選擇從設備時,把該從設備的NSS 信號線設置為低電平,該從設備即被選中,即片選有效,接著主機開始與被選中的從設備進行SPI通訊。所以SPI通訊以NSS 線置低電平為開始信號,以NSS 線被拉高作為結束信號。
Slave Input):主設備輸出/從設備輸入引腳。主機的數據從這條信號線輸出,從機由這條信號線讀入主機發送的數據,即這條線上數據的方向為主機到從機。
CPOL 是指SPI 通訊設備處於空閑狀態時,SCK 信號線的電平信號(即SPI 通訊開始前、NSS 線為高電平時SCK 的狀態)。CPOL=0 時,SCK 在空閑狀態時為低電平,CPOL=1 時,則相反。時鐘相位CPHA 是指數據的采樣的時刻,當CPHA=0 時,MOSI 或MISO 數據線上的信號將會在SCK 時鐘線的“奇數邊沿”被采樣。當CPHA=1 時,數據線在SCK 的“偶數邊沿”采樣。
SPI協議:是由摩托羅拉公司提出的通訊協議(Serial Peripheral Interface),即串行外圍設備接口,是一種高速全雙工的通信總線。它被廣泛地使用在ADC、LCD 等設備與MCU 間,要求通訊速率較高的場合。
物理層:
S S ( Slave Select):從設備選擇信號線,常稱為片選信號線,也稱為NSS、CS,以下用NSS 表示。當有多個SPI 從設備與SPI 主機相連時,設備的其它信號線SCK、MOSI及MISO 同時並聯到相同的SPI 總線上,即無論有多少個從設備,都共同只使用這3 條總線;而每個從設備都有獨立的這一條NSS 信號線,本信號線獨占主機的一個引腳,即有多少個從設備,就有多少條片選信號線。
SCK (Serial Clock):時鐘信號線,用於通訊數據同步。它由通訊主機產生,決定了通訊的速率,不同的設備支持的最高時鐘頻率不一樣,如STM32 的SPI 時鐘頻率最大為fpclk/2,兩個設備之間通訊時,通訊速率受限於低速設備。
MOSI (Master Output,
MISO(Master Input,,Slave Output):主設備輸入/從設備輸出引腳。主機從這條信號線讀入數據,從機的數據由這條信號線輸出到主機,即在這條線上數據的方向為從機到主機,只有這條線的信號由從機產生。
SPI設備之間的常用連線方式如下:
協議層:
SPI 一共有四種通訊模式,它們的主要區別是總線空閑時SCK 的時鐘狀態以及數據采樣時刻。為方便說明,在此引入“時鐘極性CPOL”和“時鐘相位CPHA”的概念。時鐘極性
bsp_spi_flash.h文件:
#ifndef __SPI_FLASH_H #define __SPI_FLASH_H #include "stm32f10x.h" #include <stdio.h> #define sFLASH_ID 0XEF4017 #define SPI_FLASH_PageSize 256 #define SPI_FLASH_PerWritePageSize 256 //Flash命令定義 #define W25X_WriteEnable 0x06 #define W25X_WriteDisable 0x04 #define W25X_ReadStatusReg 0x05 #define W25X_WriteStatusReg 0x01 #define W25X_ReadData 0x03 #define W25X_FastReadData 0x0B #define W25X_FastReadDual 0x3B #define W25X_PageProgram 0x02 #define W25X_BlockErase 0xD8 #define W25X_SectorErase 0x20 #define W25X_ChipErase 0xC7 #define W25X_PowerDown 0xB9 #define W25X_ReleasePowerDown 0xAB #define W25X_DeviceID 0xAB #define W25X_ManufactDeviceID 0x90 #define W25X_JedecDeviceID 0x9F //WIP(busy) 標誌,Flash內部正在寫入 #define WIP_flag 0x01 #define Dummy_Byte 0xFF //SPI接口定義 #define FLASH_SPIx SPI1 #define FLASH_SPI_CLK RCC_APB2Periph_SPI1 //片選引腳 #define FLASH_SPI_CS_CLK RCC_APB2Periph_GPIOC #define FLASH_SPI_CS_PORT GPIOC #define FLASH_SPI_CS_PIN GPIO_Pin_0 //SCK引腳 #define FLASH_SPI_SCK_CLK RCC_APB2Periph_GPIOA #define FLASH_SPI_SCK_PORT GPIOA #define FLASH_SPI_SCK_PIN GPIO_Pin_5 //MISO引腳 #define FLASH_SPI_MISO_CLK RCC_APB2Periph_GPIOA #define FLASH_SPI_MISO_PORT GPIOA #define FLASH_SPI_MISO_PIN GPIO_Pin_6 //MOSI引腳 #define FLASH_SPI_MOSI_CLK RCC_APB2Periph_GPIOA #define FLASH_SPI_MOSI_PORT GPIOA #define FLASH_SPI_MOSI_PIN GPIO_Pin_7 #define SPI_FLASH_CS_LOW() GPIO_ResetBits(FLASH_SPI_CS_PORT, FLASH_SPI_CS_PIN) #define SPI_FLASH_CS_HIGH() GPIO_SetBits( FLASH_SPI_CS_PORT, FLASH_SPI_CS_PIN ) #define SPIT_FLAG_TIMEOUT ((uint32_t)0x1000) #define SPIT_LONG_TIMEOUT ((uint32_t)(10 * SPIT_FLAG_TIMEOUT)) #define FLASH_DEBUG_ON 1 #define FLASH_INFO(fmt,arg...) printf("<<-FLASH-INFO->> "fmt"\n",##arg) #define FLASH_ERROR(fmt,arg...) printf("<<-FLASH-ERROR->> "fmt"\n",##arg) #define FLASH_DEBUG(fmt,arg...) do{ if(FLASH_DEBUG_ON) printf("<<-FLASH-DEBUG->> [%d]"fmt"\n",__LINE__, ##arg); }while(0) void SPI_FLASH_Init(void); void SPI_FLASH_SectorErase(u32 SectorAddr); void SPI_FLASH_BulkErase(void); void SPI_FLASH_PageWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite); void SPI_FLASH_BufferWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite); void SPI_FLASH_BufferRead(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead); u32 SPI_FLASH_ReadID(void); u32 SPI_FLASH_ReadDeviceID(void); void SPI_FLASH_StartReadSequence(up32 ReadAddr); void SPI_Flash_PowerDown(void); void SPI_Flash_WAKEUP(void); u8 SPI_FLASH_ReadByte(void); u8 SPI_FLASH_SendByte(u8 byte); u16 SPI_FLASH_SendHalfWord(u16 HalfWord); void SPI_FLASH_WriteEnable(void); void SPI_FLASH_WaitForWriteEnd(void); #endif
bsp_spi_flash.c文件:
#include "bsp_spi_flash.h" static __IO uint32_t SPITimeout = SPIT_LONG_TIMEOUT; static uint16_t SPI_TIMEOUT_UserCallback(uint8_t errorCode); void SPI_FLASH_Init(void) { SPI_InitTypeDef SPI_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(FLASH_SPI_CLK, ENABLE ); RCC_APB2PeriphClockCmd(FLASH_SPI_CS_CLK|FLASH_SPI_SCK_CLK| FLASH_SPI_MISO_PIN|FLASH_SPI_MOSI_PIN, ENABLE ); GPIO_InitStructure.GPIO_Pin = FLASH_SPI_CS_PIN; //配置片選 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(FLASH_SPI_CS_PORT, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = FLASH_SPI_SCK_PIN; //配置SCK GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(FLASH_SPI_SCK_PORT, &GPIO_InitStructure); //配置MISO GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MISO_PIN; GPIO_Init(FLASH_SPI_MISO_PORT, &GPIO_InitStructure); //配置MOSI GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MOSI_PIN; GPIO_Init(FLASH_SPI_MOSI_PORT, &GPIO_InitStructure); //cs高電平,通訊停止 SPI_FLASH_CS_HIGH(); //SPI模式配置 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_4; SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; SPI_InitStructure.SPI_CRCPolynomial = 7; SPI_Init(FLASH_SPIx , &SPI_InitStructure); SPI_Cmd(FLASH_SPIx , ENABLE); } //擦除FLASH void SPI_FLASH_SectorErase(u32 SectorAddr) { SPI_FLASH_WriteEnable(); SPI_FLASH_WaitForWriteEnd(); SPI_FLASH_CS_LOW(); SPI_FLASH_SendByte(W25X_SectorErase); SPI_FLASH_SendByte((SectorAddr & 0xFF0000) >> 16); SPI_FLASH_SendByte((SectorAddr & 0xFF00) >> 8); SPI_FLASH_SendByte(SectorAddr & 0xFF); SPI_FLASH_CS_HIGH(); SPI_FLASH_WaitForWriteEnd(); } //擦除FLASH扇區 void SPI_FLASH_BulkErase(void) { SPI_FLASH_WriteEnable(); SPI_FLASH_CS_LOW(); SPI_FLASH_SendByte(W25X_ChipErase); SPI_FLASH_CS_HIGH(); SPI_FLASH_WaitForWriteEnd(); } //按頁寫入數據 void SPI_FLASH_PageWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite) { SPI_FLASH_WriteEnable(); SPI_FLASH_CS_LOW(); SPI_FLASH_SendByte(W25X_PageProgram); SPI_FLASH_SendByte((WriteAddr & 0xFF0000) >> 16); SPI_FLASH_SendByte((WriteAddr & 0xFF00) >> 8); SPI_FLASH_SendByte(WriteAddr & 0xFF); if(NumByteToWrite > SPI_FLASH_PerWritePageSize) { NumByteToWrite = SPI_FLASH_PerWritePageSize; FLASH_ERROR("SPI_FLASH_PageWrite too large!"); } while (NumByteToWrite--) { SPI_FLASH_SendByte(*pBuffer); pBuffer++; } SPI_FLASH_CS_HIGH(); SPI_FLASH_WaitForWriteEnd(); } //寫數據 void SPI_FLASH_BufferWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite) { u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0; Addr = WriteAddr % SPI_FLASH_PageSize; count = SPI_FLASH_PageSize - Addr; NumOfPage = NumByteToWrite / SPI_FLASH_PageSize; NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize; if (Addr == 0) { if (NumOfPage == 0) { SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite); } else { while (NumOfPage--) { SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize); WriteAddr += SPI_FLASH_PageSize; pBuffer += SPI_FLASH_PageSize; } SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle); } } else { if (NumOfPage == 0) { if (NumOfSingle > count) { temp = NumOfSingle - count; SPI_FLASH_PageWrite(pBuffer, WriteAddr, count); WriteAddr += count; pBuffer += count; SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp); } else { SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite); } } else { NumByteToWrite -= count; NumOfPage = NumByteToWrite / SPI_FLASH_PageSize; NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize; SPI_FLASH_PageWrite(pBuffer, WriteAddr, count); WriteAddr += count; pBuffer += count; while (NumOfPage--) { SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize); WriteAddr += SPI_FLASH_PageSize; pBuffer += SPI_FLASH_PageSize; } if (NumOfSingle != 0) { SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle); } } } } //讀數據 void SPI_FLASH_BufferRead(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead) { SPI_FLASH_CS_LOW(); SPI_FLASH_SendByte(W25X_ReadData); SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16); SPI_FLASH_SendByte((ReadAddr& 0xFF00) >> 8); SPI_FLASH_SendByte(ReadAddr & 0xFF); while (NumByteToRead--) { *pBuffer = SPI_FLASH_SendByte(Dummy_Byte); pBuffer++; } SPI_FLASH_CS_HIGH(); } //讀取ID u32 SPI_FLASH_ReadID(void) { u32 Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0; SPI_FLASH_CS_LOW(); SPI_FLASH_SendByte(W25X_JedecDeviceID); Temp0 = SPI_FLASH_SendByte(Dummy_Byte); Temp1 = SPI_FLASH_SendByte(Dummy_Byte); Temp2 = SPI_FLASH_SendByte(Dummy_Byte); SPI_FLASH_CS_HIGH(); Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2; return Temp; } //讀取DeviceID u32 SPI_FLASH_ReadDeviceID(void) { u32 Temp = 0; SPI_FLASH_CS_LOW(); SPI_FLASH_SendByte(W25X_DeviceID); SPI_FLASH_SendByte(Dummy_Byte); SPI_FLASH_SendByte(Dummy_Byte); SPI_FLASH_SendByte(Dummy_Byte); Temp = SPI_FLASH_SendByte(Dummy_Byte); SPI_FLASH_CS_HIGH(); return Temp; } void SPI_FLASH_StartReadSequence(u32 ReadAddr) { SPI_FLASH_CS_LOW(); SPI_FLASH_SendByte(W25X_ReadData); SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16); SPI_FLASH_SendByte((ReadAddr& 0xFF00) >> 8); SPI_FLASH_SendByte(ReadAddr & 0xFF); } u8 SPI_FLASH_ReadByte(void) { return (SPI_FLASH_SendByte(Dummy_Byte)); } u8 SPI_FLASH_SendByte(u8 byte) { SPITimeout = SPIT_FLAG_TIMEOUT; while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_TXE) == RESET) { if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(0); } SPI_I2S_SendData(FLASH_SPIx , byte); SPITimeout = SPIT_FLAG_TIMEOUT; while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_RXNE) == RESET) { if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(1); } return SPI_I2S_ReceiveData(FLASH_SPIx ); } u16 SPI_FLASH_SendHalfWord(u16 HalfWord) { SPITimeout = SPIT_FLAG_TIMEOUT; while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_TXE) == RESET) { if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(2); } SPI_I2S_SendData(FLASH_SPIx , HalfWord); SPITimeout = SPIT_FLAG_TIMEOUT; while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_RXNE) == RESET) { if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(3); } return SPI_I2S_ReceiveData(FLASH_SPIx ); } void SPI_FLASH_WriteEnable(void) { SPI_FLASH_CS_LOW(); SPI_FLASH_SendByte(W25X_WriteEnable); SPI_FLASH_CS_HIGH(); } void SPI_FLASH_WaitForWriteEnd(void) { u8 FLASH_Status = 0; SPI_FLASH_CS_LOW(); SPI_FLASH_SendByte(W25X_ReadStatusReg); do { FLASH_Status = SPI_FLASH_SendByte(Dummy_Byte); } while ((FLASH_Status & WIP_flag ) == SET); SPI_FLASH_CS_HIGH(); } void SPI_Flash_PowerDown(void) { SPI_FLASH_CS_LOW(); SPI_FLASH_SendByte(W25X_PowerDown); SPI_FLASH_CS_HIGH(); } void SPI_Flash_WAKEUP(void) { SPI_FLASH_CS_LOW(); SPI_FLASH_SendByte(W25X_ReleasePowerDown); SPI_FLASH_CS_HIGH(); } static uint16_t SPI_TIMEOUT_UserCallback(uint8_t errorCode) { FLASH_ERROR("SPI等待超時!errorCode = %d",errorCode); return 0; }
main.c文件:
#include "stm32f10x.h" #include "bsp_usart.h" #include "bsp_led.h" #include "bsp_spi_flash.h" typedef enum { FAILED = 0, PASSED = !FAILED} TestStatus; #define TxBufferSize1 (countof(TxBuffer1) - 1) #define RxBufferSize1 (countof(TxBuffer1) - 1) #define countof(a) (sizeof(a) / sizeof(*(a))) #define BufferSize (countof(Tx_Buffer)-1) #define FLASH_WriteAddress 0x00000 #define FLASH_ReadAddress FLASH_WriteAddress #define FLASH_SectorToErase FLASH_WriteAddress uint8_t Tx_Buffer[] = "hello stm32!!!\r\n"; uint8_t Rx_Buffer[BufferSize]; __IO uint32_t DeviceID = 0; __IO uint32_t FlashID = 0; __IO TestStatus TransferStatus1 = FAILED; void Delay(__IO uint32_t nCount); TestStatus Buffercmp(uint8_t* pBuffer1,uint8_t* pBuffer2, uint16_t BufferLength); int main(void) { LED_GPIO_Config(); USART_Config(); printf("\r\n 這是一個8M的串行flash W25Q64實驗!\r\n"); SPI_FLASH_Init(); DeviceID = SPI_FLASH_ReadDeviceID(); Delay( 200 ); FlashID = SPI_FLASH_ReadID(); printf("\r\n FlashID is 0x%X,\ Manufacturer Device ID is 0x%X\r\n", FlashID, DeviceID); if (FlashID == sFLASH_ID) { printf("\r\n檢測到串行flash W25Q64\r\n"); SPI_FLASH_SectorErase(FLASH_SectorToErase); SPI_FLASH_BufferWrite(Tx_Buffer, FLASH_WriteAddress, BufferSize); printf("\r\n 寫入的數據為:%s \r\t", Tx_Buffer); SPI_FLASH_BufferRead(Rx_Buffer, FLASH_ReadAddress, BufferSize); printf("\r\n 讀出的數據為\r\n", Rx_Buffer); TransferStatus1 = Buffercmp(Tx_Buffer, Rx_Buffer, BufferSize); if( PASSED == TransferStatus1 ) { green(ON); printf("\r\n 測試成功!\n\r"); } else { red(ON); printf("\r\n 測試失敗\n\r"); } }// if (FlashID == sFLASH_ID) else// if (FlashID == sFLASH_ID) { red(ON); printf("\r\n 獲取不到W25Q64 ID!\n\r"); } while(1); } //比較兩個緩沖區的內容是否一樣 TestStatus Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint16_t BufferLength) { while(BufferLength--) { if(*pBuffer1 != *pBuffer2) { return FAILED; } pBuffer1++; pBuffer2++; } return PASSED; } void Delay(__IO uint32_t nCount) { for(; nCount != 0; nCount--); }
stm32-SPI讀取串行FLASH