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

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

stm32 SPI、FLASH

發(fā)布時(shí)間:2025/3/12 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 stm32 SPI、FLASH 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

main.c

FLASH:掉電后數(shù)據(jù)不丟失,U 盤(pán)、SD 卡、SSD 固態(tài)硬盤(pán)、STM32 芯片內(nèi)部用于存儲(chǔ)程序的設(shè)備,都是 FLASH 類(lèi)型的存儲(chǔ)器。FLASH芯片(W25Q64)是一種使用 SPI 通訊協(xié)議的 NOR FLASH 存儲(chǔ)器。 STM32 的 NSS 引腳是一個(gè)普通的 GPIO,程序中要使用軟件控制的方式。

結(jié)果:

里面的FlashID是Flash芯片ID,就是W25Q64的廠商號(hào)和FLASH型號(hào)。通過(guò)Flash型號(hào)對(duì)比來(lái)看看W25Q64是否連接正常。

然后擦除FLASH扇區(qū),往FLASH里面寫(xiě)數(shù)據(jù),讀FLASH里面的數(shù)據(jù)。檢查寫(xiě)入的數(shù)據(jù)與讀出的數(shù)據(jù)是否相等,相等的話亮綠燈。

#include "stm32f10x.h" #include "./usart/bsp_usart.h" #include "./led/bsp_led.h" #include "./flash/bsp_spi_flash.h"typedef enum { FAILED = 0, PASSED = !FAILED} TestStatus;/* 獲取緩沖區(qū)的長(zhǎng)度 */ #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/* 發(fā)送緩沖區(qū)初始化 */ uint8_t Tx_Buffer[] = "jymjymjym123456789\r\n"; uint8_t Rx_Buffer[BufferSize];__IO uint32_t DeviceID = 0; __IO uint32_t FlashID = 0; __IO TestStatus TransferStatus1 = FAILED;// 函數(shù)原型聲明 void Delay(__IO uint32_t nCount); TestStatus Buffercmp(uint8_t* pBuffer1,uint8_t* pBuffer2, uint16_t BufferLength);/** 函數(shù)名:main* 描述 :主函數(shù)* 輸入 :無(wú)* 輸出 :無(wú)*/ int main(void) { LED_GPIO_Config();LED_BLUE;/* 配置串口為:115200 8-N-1 */USART_Config();printf("\r\n 這是一個(gè)8Mbyte串行flash(W25Q64)實(shí)驗(yàn) \r\n");/* 8M串行flash W25Q64初始化 */SPI_FLASH_Init();/* 獲取 Flash Device ID */DeviceID = SPI_FLASH_ReadDeviceID(); Delay( 200 );/* 獲取 SPI Flash ID */FlashID = SPI_FLASH_ReadID(); printf("\r\n FlashID is 0x%X,\Manufacturer Device ID is 0x%X\r\n", FlashID, DeviceID);/* 檢驗(yàn) SPI Flash ID */if (FlashID == sFLASH_ID){ printf("\r\n 檢測(cè)到串行flash W25Q64 !\r\n");/* 擦除將要寫(xiě)入的 SPI FLASH 扇區(qū),FLASH寫(xiě)入前要先擦除 */// 這里擦除4K,即一個(gè)扇區(qū),擦除的最小單位是扇區(qū)SPI_FLASH_SectorErase(FLASH_SectorToErase); /* 將發(fā)送緩沖區(qū)的數(shù)據(jù)寫(xiě)到flash中 */// 這里寫(xiě)一頁(yè),一頁(yè)的大小為256個(gè)字節(jié)SPI_FLASH_BufferWrite(Tx_Buffer, FLASH_WriteAddress, BufferSize); printf("\r\n 寫(xiě)入的數(shù)據(jù)為:%s \r\t", Tx_Buffer);/* 將剛剛寫(xiě)入的數(shù)據(jù)讀出來(lái)放到接收緩沖區(qū)中 */SPI_FLASH_BufferRead(Rx_Buffer, FLASH_ReadAddress, BufferSize);printf("\r\n 讀出的數(shù)據(jù)為:%s \r\n", Rx_Buffer);/* 檢查寫(xiě)入的數(shù)據(jù)與讀出的數(shù)據(jù)是否相等 */TransferStatus1 = Buffercmp(Tx_Buffer, Rx_Buffer, BufferSize);if( PASSED == TransferStatus1 ){ LED_GREEN;printf("\r\n 8M串行flash(W25Q64)測(cè)試成功!\n\r");}else{ LED_RED;printf("\r\n 8M串行flash(W25Q64)測(cè)試失敗!\n\r");}}// if (FlashID == sFLASH_ID)else// if (FlashID == sFLASH_ID){ LED_RED;printf("\r\n 獲取不到 W25Q64 ID!\n\r");}while(1); }/** 函數(shù)名:Buffercmp* 描述 :比較兩個(gè)緩沖區(qū)中的數(shù)據(jù)是否相等* 輸入 :-pBuffer1 src緩沖區(qū)指針* -pBuffer2 dst緩沖區(qū)指針* -BufferLength 緩沖區(qū)長(zhǎng)度* 輸出 :無(wú)* 返回 :-PASSED pBuffer1 等于 pBuffer2* -FAILED pBuffer1 不同于 pBuffer2*/ 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--); } /*********************************************END OF FILE**********************/

spi_flash.h

里面寫(xiě)了SPI 硬件相關(guān)的宏定義。根據(jù)硬件連接,把FLASH用的SPI號(hào)、GPIO用宏封裝。定義控制 CS(NSS)引腳輸出電平的宏,用來(lái)控制產(chǎn)生起始和停止信號(hào)。

里面還定義了 FLASH 指令編碼表。

#ifndef __SPI_FLASH_H #define __SPI_FLASH_H#include "stm32f10x.h" #include <stdio.h>//#define sFLASH_ID 0xEF3015 //W25X16 //#define sFLASH_ID 0xEF4015 //W25Q16 //#define sFLASH_ID 0XEF4018 //W25Q128 #define sFLASH_ID 0XEF4017 //W25Q64#define SPI_FLASH_PageSize 256 #define SPI_FLASH_PerWritePageSize 256/*命令定義-開(kāi)頭*******************************/ #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)標(biāo)志,FLASH內(nèi)部正在寫(xiě)入 */ #define WIP_Flag 0x01 #define Dummy_Byte 0xFF /*命令定義-結(jié)尾*******************************//*SPI接口定義-開(kāi)頭****************************/ #define FLASH_SPIx SPI1 #define FLASH_SPI_APBxClock_FUN RCC_APB2PeriphClockCmd #define FLASH_SPI_CLK RCC_APB2Periph_SPI1//CS(NSS)引腳 片選選普通GPIO即可 #define FLASH_SPI_CS_APBxClock_FUN RCC_APB2PeriphClockCmd #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_APBxClock_FUN RCC_APB2PeriphClockCmd #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_APBxClock_FUN RCC_APB2PeriphClockCmd #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_APBxClock_FUN RCC_APB2PeriphClockCmd #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 )/*SPI接口定義-結(jié)尾****************************//*等待超時(shí)時(shí)間*/ #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(u32 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 /* __SPI_FLASH_H */

spi_flash.c

下面這個(gè)函數(shù),用于初始化 SPI 的 GPIO(配置 SPI 使用的引腳);配置 SPI 的模式。

void SPI_FLASH_Init(void)

下面這兩個(gè)函數(shù)分別是使用SPI發(fā)送、接收一個(gè)字節(jié)的數(shù)據(jù)。函數(shù)中不包含 SPI 起始和停止信號(hào),只是收發(fā)的主要過(guò)程。主要就是通過(guò)SPI_I2S_GetFlagStatus函數(shù)檢測(cè)事件。

首先檢測(cè) TXE 標(biāo)志,獲取發(fā)送緩沖區(qū)的狀態(tài)。等待至發(fā)送緩沖區(qū)為空后,通過(guò)SPI_I2S_SendData函數(shù)把要寫(xiě)入的數(shù)據(jù)寫(xiě)入數(shù)據(jù)寄存器DR。

然后檢測(cè) RXNE 標(biāo)志,獲取接收緩沖區(qū)的狀態(tài)。等待至接收緩沖區(qū)為非空,通過(guò)SPI_I2S_ReceiveData函數(shù)讀取 SPI 的數(shù)據(jù)寄存器 DR。

u8 SPI_FLASH_SendByte(u8 byte) u8 SPI_FLASH_ReadByte(void)

對(duì) FLASH 芯片進(jìn)行操作:控制 STM32 利用 SPI 總線向 FLASH 芯片發(fā)送指令,FLASH 芯片收到指令后就會(huì)執(zhí)行相應(yīng)的操作。

W25Q64定義的各種指令的功能及指令格式如下。根據(jù)這些指令的格式要求, 使用通訊協(xié)議向設(shè)備發(fā)送指令,從而控制設(shè)備。

定義FLASH 指令編碼表:可以把 FLASH 芯片的常用指令編碼使用宏來(lái)封裝起來(lái),可以在發(fā)送指令編碼時(shí)直接使用這些宏,FLASH 指令編碼表在上面的spi_flash.h里面寫(xiě)了。


讀取Flash芯片ID:

對(duì)于W25Q64芯片,廠商號(hào)(M7-M0):EF h ;FLASH 型號(hào)(ID15-ID0):4017 h。

根據(jù)下面的時(shí)序可以編寫(xiě)函數(shù):這里面發(fā)送JEDEC指令用的是SPI_FLASH_SendByte函數(shù),這個(gè)函數(shù)就是根據(jù)SPI通信協(xié)議編寫(xiě)的,那么這個(gè)指令就能夠傳到W25Q64芯片里面(相當(dāng)于使用通訊協(xié)議向設(shè)備發(fā)送指令)。由于SPI_FLASH_SendByte返回接收到的數(shù)據(jù),那么繼續(xù)調(diào)用它,相當(dāng)于接收W25Q64芯片對(duì)JEDEC指令的響應(yīng)。

最后可以把該返回值與定義的宏sFLASH_ID對(duì)比,判斷 FLASH 芯片是否正常。

#define sFLASH_ID 0XEF4017 //W25Q64 u32 SPI_FLASH_ReadID(void) {u32 Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;/* 開(kāi)始通訊:CS低電平 */SPI_FLASH_CS_LOW();/* 發(fā)送JEDEC指令,讀取ID */SPI_FLASH_SendByte(W25X_JedecDeviceID);/* 讀取一個(gè)字節(jié)數(shù)據(jù) */Temp0 = SPI_FLASH_SendByte(Dummy_Byte);/* 讀取一個(gè)字節(jié)數(shù)據(jù) */Temp1 = SPI_FLASH_SendByte(Dummy_Byte);/* 讀取一個(gè)字節(jié)數(shù)據(jù) */Temp2 = SPI_FLASH_SendByte(Dummy_Byte);/* 停止通訊:CS高電平 */SPI_FLASH_CS_HIGH();/*把數(shù)據(jù)組合起來(lái),作為函數(shù)的返回值*/Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;return Temp; }


FLASH寫(xiě)使能:下面這個(gè)函數(shù)實(shí)現(xiàn)FLASH寫(xiě)使能,也就是,使用SPI協(xié)議向FLASH里面發(fā)了一個(gè)WriteEnable指令。

向Flash芯片存儲(chǔ)矩陣寫(xiě)入數(shù)據(jù)之前,要進(jìn)行寫(xiě)使能。

void SPI_FLASH_WriteEnable(void)

讀取FLASH當(dāng)前狀態(tài):FLASH內(nèi)部有一個(gè)狀態(tài)寄存器,這個(gè)狀態(tài)寄存器第0位(busy位)為1,說(shuō)明FLASH芯片可能正在對(duì)內(nèi)部的存儲(chǔ)矩陣進(jìn)行擦除或數(shù)據(jù)寫(xiě)入操作。在寫(xiě)操作后需要確認(rèn) FLASH 芯片空閑時(shí)才能進(jìn)行再次寫(xiě)入。

下面這個(gè)函數(shù)實(shí)現(xiàn)了讀取FLASH當(dāng)前狀態(tài)功能。發(fā)了一個(gè)ReadStatusReg指令來(lái)讀FLASH的狀態(tài)寄存器,在while 循環(huán)里持續(xù)獲得狀態(tài)寄存器的內(nèi)容并檢驗(yàn)它的busy位,一直等待到busy位為0(FLASH芯片已經(jīng)寫(xiě)入完畢),才退出循環(huán)。

void SPI_FLASH_WaitForWriteEnd(void)

FLASH扇區(qū)擦除:向FLASH寫(xiě)入數(shù)據(jù)前,要對(duì)目標(biāo)存儲(chǔ)區(qū)進(jìn)行擦除操作,這是因?yàn)镕LASH存儲(chǔ)器只能把1數(shù)據(jù)位改成0,那么如果要存儲(chǔ)1,但如果目標(biāo)存儲(chǔ)區(qū)的數(shù)據(jù)位是0,那就沒(méi)法存儲(chǔ)1了。所以擦除操作,要對(duì)目標(biāo)存儲(chǔ)區(qū)中的數(shù)據(jù)位全部擦成1。

FLASH扇區(qū)擦除函數(shù)如下,其實(shí)也就是對(duì)FLASH發(fā)送幾個(gè)指令,指令后邊要加上SPI_FLASH_WaitForWriteEnd函數(shù),用來(lái)等待扇區(qū)操作完成。發(fā)送發(fā)送擦除地址時(shí)高位在前;調(diào)用扇區(qū)擦除指令時(shí)輸入的地址要對(duì)齊到 4KB。下圖是FLASH芯片的扇區(qū)(Sector 4KB)和塊(Block 64KB)的存儲(chǔ)結(jié)構(gòu)。

/*** @brief 擦除FLASH扇區(qū)* @param SectorAddr:要擦除的扇區(qū)地址* @retval 無(wú)*/ void SPI_FLASH_SectorErase(u32 SectorAddr) {/* 發(fā)送FLASH寫(xiě)使能命令 */SPI_FLASH_WriteEnable();SPI_FLASH_WaitForWriteEnd();/* 擦除扇區(qū) *//* 選擇FLASH: CS低電平 */SPI_FLASH_CS_LOW();/* 發(fā)送扇區(qū)擦除指令*/SPI_FLASH_SendByte(W25X_SectorErase);/*發(fā)送擦除扇區(qū)地址的高位*/SPI_FLASH_SendByte((SectorAddr & 0xFF0000) >> 16);/* 發(fā)送擦除扇區(qū)地址的中位 */SPI_FLASH_SendByte((SectorAddr & 0xFF00) >> 8);/* 發(fā)送擦除扇區(qū)地址的低位 */SPI_FLASH_SendByte(SectorAddr & 0xFF);/* 停止信號(hào) FLASH: CS 高電平 */SPI_FLASH_CS_HIGH();/* 等待擦除完畢*/SPI_FLASH_WaitForWriteEnd(); }

FLASH頁(yè)寫(xiě)入:

FLASH芯片有頁(yè)寫(xiě)入命令,使用頁(yè)寫(xiě)入命令最多可以一次向FLASH傳輸 256 個(gè)字節(jié)的數(shù)據(jù)(這個(gè)單位為頁(yè)大小)。時(shí)序圖如下。

由圖可知,發(fā)送完寫(xiě)指令,再發(fā)送寫(xiě)地址(從高位到低位發(fā)),再發(fā)要寫(xiě)入的內(nèi)容(一個(gè)字節(jié)一個(gè)字節(jié)的發(fā),最多256個(gè)字節(jié))。發(fā)送完,結(jié)束通信,然后等待Flash內(nèi)部寫(xiě)入結(jié)束。下面這個(gè)是頁(yè)寫(xiě)入函數(shù)。(調(diào)用這個(gè)函數(shù)寫(xiě)入數(shù)據(jù)前需要先擦除扇區(qū))

/*** @brief 對(duì)FLASH按頁(yè)寫(xiě)入數(shù)據(jù),調(diào)用本函數(shù)寫(xiě)入數(shù)據(jù)前需要先擦除扇區(qū)* @param pBuffer,要寫(xiě)入數(shù)據(jù)的指針* @param WriteAddr,寫(xiě)入地址* @param NumByteToWrite,寫(xiě)入數(shù)據(jù)長(zhǎng)度,必須小于等于SPI_FLASH_PerWritePageSize* @retval 無(wú)*/ void SPI_FLASH_PageWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)

下面這個(gè)函數(shù)是不定量數(shù)據(jù)寫(xiě)入。

/*** @brief 對(duì)FLASH寫(xiě)入數(shù)據(jù),調(diào)用本函數(shù)寫(xiě)入數(shù)據(jù)前需要先擦除扇區(qū)* @param pBuffer,要寫(xiě)入數(shù)據(jù)的指針* @param WriteAddr,寫(xiě)入地址* @param NumByteToWrite,寫(xiě)入數(shù)據(jù)長(zhǎng)度* @retval 無(wú)*/ void SPI_FLASH_BufferWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)

從Flash讀數(shù)據(jù):使用讀取指令ReadData。

/*** @brief 讀取FLASH數(shù)據(jù)* @param pBuffer,存儲(chǔ)讀出數(shù)據(jù)的指針* @param ReadAddr,讀取地址* @param NumByteToRead,讀取數(shù)據(jù)長(zhǎng)度* @retval 無(wú)*/ void SPI_FLASH_BufferRead(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead)

函數(shù)首先發(fā)送讀指令,再發(fā)送讀地址的高位到地位,再讀數(shù)據(jù),讀完就發(fā)送停止信號(hào)。


全部代碼:

#include "./flash/bsp_spi_flash.h"static __IO uint32_t SPITimeout = SPIT_LONG_TIMEOUT; static uint16_t SPI_TIMEOUT_UserCallback(uint8_t errorCode);/*** @brief SPI_FLASH初始化* @param 無(wú)* @retval 無(wú)*/ void SPI_FLASH_Init(void) {SPI_InitTypeDef SPI_InitStructure;GPIO_InitTypeDef GPIO_InitStructure;/* 使能SPI時(shí)鐘 */FLASH_SPI_APBxClock_FUN ( FLASH_SPI_CLK, ENABLE );/* 使能SPI引腳相關(guān)的時(shí)鐘 */FLASH_SPI_CS_APBxClock_FUN ( FLASH_SPI_CS_CLK|FLASH_SPI_SCK_CLK|FLASH_SPI_MISO_PIN|FLASH_SPI_MOSI_PIN, ENABLE );/* 配置SPI的 CS引腳,普通IO即可 */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);/* 配置SPI的 SCK引腳*/GPIO_InitStructure.GPIO_Pin = FLASH_SPI_SCK_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_Init(FLASH_SPI_SCK_PORT, &GPIO_InitStructure);/* 配置SPI的 MISO引腳*/GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MISO_PIN;GPIO_Init(FLASH_SPI_MISO_PORT, &GPIO_InitStructure);/* 配置SPI的 MOSI引腳*/GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MOSI_PIN;GPIO_Init(FLASH_SPI_MOSI_PORT, &GPIO_InitStructure);/* 停止信號(hào) FLASH: CS引腳高電平*/SPI_FLASH_CS_HIGH();/* SPI 模式配置 */// FLASH芯片 支持SPI模式0及模式3,據(jù)此設(shè)置CPOL CPHASPI_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 */SPI_Cmd(FLASH_SPIx , ENABLE);}/*** @brief 擦除FLASH扇區(qū)* @param SectorAddr:要擦除的扇區(qū)地址* @retval 無(wú)*/ void SPI_FLASH_SectorErase(u32 SectorAddr) {/* 發(fā)送FLASH寫(xiě)使能命令 */SPI_FLASH_WriteEnable();SPI_FLASH_WaitForWriteEnd();/* 擦除扇區(qū) *//* 選擇FLASH: CS低電平 */SPI_FLASH_CS_LOW();/* 發(fā)送扇區(qū)擦除指令*/SPI_FLASH_SendByte(W25X_SectorErase);/*發(fā)送擦除扇區(qū)地址的高位*/SPI_FLASH_SendByte((SectorAddr & 0xFF0000) >> 16);/* 發(fā)送擦除扇區(qū)地址的中位 */SPI_FLASH_SendByte((SectorAddr & 0xFF00) >> 8);/* 發(fā)送擦除扇區(qū)地址的低位 */SPI_FLASH_SendByte(SectorAddr & 0xFF);/* 停止信號(hào) FLASH: CS 高電平 */SPI_FLASH_CS_HIGH();/* 等待擦除完畢*/SPI_FLASH_WaitForWriteEnd(); }/*** @brief 擦除FLASH扇區(qū),整片擦除* @param 無(wú)* @retval 無(wú)*/ void SPI_FLASH_BulkErase(void) {/* 發(fā)送FLASH寫(xiě)使能命令 */SPI_FLASH_WriteEnable();/* 整塊 Erase *//* 選擇FLASH: CS低電平 */SPI_FLASH_CS_LOW();/* 發(fā)送整塊擦除指令*/SPI_FLASH_SendByte(W25X_ChipErase);/* 停止信號(hào) FLASH: CS 高電平 */SPI_FLASH_CS_HIGH();/* 等待擦除完畢*/SPI_FLASH_WaitForWriteEnd(); }/*** @brief 對(duì)FLASH按頁(yè)寫(xiě)入數(shù)據(jù),調(diào)用本函數(shù)寫(xiě)入數(shù)據(jù)前需要先擦除扇區(qū)* @param pBuffer,要寫(xiě)入數(shù)據(jù)的指針* @param WriteAddr,寫(xiě)入地址* @param NumByteToWrite,寫(xiě)入數(shù)據(jù)長(zhǎng)度,必須小于等于SPI_FLASH_PerWritePageSize* @retval 無(wú)*/ void SPI_FLASH_PageWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite) {/* 發(fā)送FLASH寫(xiě)使能命令 */SPI_FLASH_WriteEnable();/* 選擇FLASH: CS低電平 */SPI_FLASH_CS_LOW();/* 寫(xiě)頁(yè)寫(xiě)指令*/SPI_FLASH_SendByte(W25X_PageProgram);/*發(fā)送寫(xiě)地址的高位*/SPI_FLASH_SendByte((WriteAddr & 0xFF0000) >> 16);/*發(fā)送寫(xiě)地址的中位*/SPI_FLASH_SendByte((WriteAddr & 0xFF00) >> 8);/*發(fā)送寫(xiě)地址的低位*/SPI_FLASH_SendByte(WriteAddr & 0xFF);if(NumByteToWrite > SPI_FLASH_PerWritePageSize){NumByteToWrite = SPI_FLASH_PerWritePageSize;FLASH_ERROR("SPI_FLASH_PageWrite too large!"); }/* 寫(xiě)入數(shù)據(jù)*/while (NumByteToWrite--){/* 發(fā)送當(dāng)前要寫(xiě)入的字節(jié)數(shù)據(jù) */SPI_FLASH_SendByte(*pBuffer);/* 指向下一字節(jié)數(shù)據(jù) */pBuffer++;}/* 停止信號(hào) FLASH: CS 高電平 */SPI_FLASH_CS_HIGH();/* 等待寫(xiě)入完畢*/SPI_FLASH_WaitForWriteEnd(); }/*** @brief 對(duì)FLASH寫(xiě)入數(shù)據(jù),調(diào)用本函數(shù)寫(xiě)入數(shù)據(jù)前需要先擦除扇區(qū)* @param pBuffer,要寫(xiě)入數(shù)據(jù)的指針* @param WriteAddr,寫(xiě)入地址* @param NumByteToWrite,寫(xiě)入數(shù)據(jù)長(zhǎng)度* @retval 無(wú)*/ void SPI_FLASH_BufferWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite) {u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;/*mod運(yùn)算求余,若writeAddr是SPI_FLASH_PageSize整數(shù)倍,運(yùn)算結(jié)果Addr值為0*/Addr = WriteAddr % SPI_FLASH_PageSize;/*差count個(gè)數(shù)據(jù)值,剛好可以對(duì)齊到頁(yè)地址*/count = SPI_FLASH_PageSize - Addr;/*計(jì)算出要寫(xiě)多少整數(shù)頁(yè)*/NumOfPage = NumByteToWrite / SPI_FLASH_PageSize;/*mod運(yùn)算求余,計(jì)算出剩余不滿(mǎn)一頁(yè)的字節(jié)數(shù)*/NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;/* Addr=0,則WriteAddr 剛好按頁(yè)對(duì)齊 aligned */if (Addr == 0){/* NumByteToWrite < SPI_FLASH_PageSize */if (NumOfPage == 0) {SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);}else /* NumByteToWrite > SPI_FLASH_PageSize */{ /*先把整數(shù)頁(yè)都寫(xiě)了*/while (NumOfPage--){SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);WriteAddr += SPI_FLASH_PageSize;pBuffer += SPI_FLASH_PageSize;}/*若有多余的不滿(mǎn)一頁(yè)的數(shù)據(jù),把它寫(xiě)完*/SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);}}/* 若地址與 SPI_FLASH_PageSize 不對(duì)齊 */else {/* NumByteToWrite < SPI_FLASH_PageSize */if (NumOfPage == 0){/*當(dāng)前頁(yè)剩余的count個(gè)位置比NumOfSingle小,一頁(yè)寫(xiě)不完*/if (NumOfSingle > count) {temp = NumOfSingle - count;/*先寫(xiě)滿(mǎn)當(dāng)前頁(yè)*/SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);WriteAddr += count;pBuffer += count;/*再寫(xiě)剩余的數(shù)據(jù)*/SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp);}else /*當(dāng)前頁(yè)剩余的count個(gè)位置能寫(xiě)完NumOfSingle個(gè)數(shù)據(jù)*/{SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);}}else /* NumByteToWrite > SPI_FLASH_PageSize */{/*地址不對(duì)齊多出的count分開(kāi)處理,不加入這個(gè)運(yùn)算*/NumByteToWrite -= count;NumOfPage = NumByteToWrite / SPI_FLASH_PageSize;NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;/* 先寫(xiě)完count個(gè)數(shù)據(jù),為的是讓下一次要寫(xiě)的地址對(duì)齊 */SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);/* 接下來(lái)就重復(fù)地址對(duì)齊的情況 */WriteAddr += count;pBuffer += count;/*把整數(shù)頁(yè)都寫(xiě)了*/while (NumOfPage--){SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);WriteAddr += SPI_FLASH_PageSize;pBuffer += SPI_FLASH_PageSize;}/*若有多余的不滿(mǎn)一頁(yè)的數(shù)據(jù),把它寫(xiě)完*/if (NumOfSingle != 0){SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);}}} }/*** @brief 讀取FLASH數(shù)據(jù)* @param pBuffer,存儲(chǔ)讀出數(shù)據(jù)的指針* @param ReadAddr,讀取地址* @param NumByteToRead,讀取數(shù)據(jù)長(zhǎng)度* @retval 無(wú)*/ void SPI_FLASH_BufferRead(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead) {/* 選擇FLASH: CS低電平 */SPI_FLASH_CS_LOW();/* 發(fā)送 讀 指令 */SPI_FLASH_SendByte(W25X_ReadData);/* 發(fā)送 讀 地址高位 */SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);/* 發(fā)送 讀 地址中位 */SPI_FLASH_SendByte((ReadAddr& 0xFF00) >> 8);/* 發(fā)送 讀 地址低位 */SPI_FLASH_SendByte(ReadAddr & 0xFF);/* 讀取數(shù)據(jù) */while (NumByteToRead--) /* while there is data to be read */{/* 讀取一個(gè)字節(jié)*/*pBuffer = SPI_FLASH_SendByte(Dummy_Byte);/* 指向下一個(gè)字節(jié)緩沖區(qū) */pBuffer++;}/* 停止信號(hào) FLASH: CS 高電平 */SPI_FLASH_CS_HIGH(); }/*** @brief 讀取FLASH ID* @param 無(wú)* @retval FLASH ID*/ u32 SPI_FLASH_ReadID(void) {u32 Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;/* 開(kāi)始通訊:CS低電平 */SPI_FLASH_CS_LOW();/* 發(fā)送JEDEC指令,讀取ID */SPI_FLASH_SendByte(W25X_JedecDeviceID);/* 讀取一個(gè)字節(jié)數(shù)據(jù) */Temp0 = SPI_FLASH_SendByte(Dummy_Byte);/* 讀取一個(gè)字節(jié)數(shù)據(jù) */Temp1 = SPI_FLASH_SendByte(Dummy_Byte);/* 讀取一個(gè)字節(jié)數(shù)據(jù) */Temp2 = SPI_FLASH_SendByte(Dummy_Byte);/* 停止通訊:CS高電平 */SPI_FLASH_CS_HIGH();/*把數(shù)據(jù)組合起來(lái),作為函數(shù)的返回值*/Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;return Temp; }/*** @brief 讀取FLASH Device ID* @param 無(wú)* @retval FLASH Device ID*/ u32 SPI_FLASH_ReadDeviceID(void) {u32 Temp = 0;/* Select the FLASH: Chip Select low */SPI_FLASH_CS_LOW();/* Send "RDID " instruction */SPI_FLASH_SendByte(W25X_DeviceID);SPI_FLASH_SendByte(Dummy_Byte);SPI_FLASH_SendByte(Dummy_Byte);SPI_FLASH_SendByte(Dummy_Byte);/* Read a byte from the FLASH */Temp = SPI_FLASH_SendByte(Dummy_Byte);/* Deselect the FLASH: Chip Select high */SPI_FLASH_CS_HIGH();return Temp; } /******************************************************************************* * Function Name : SPI_FLASH_StartReadSequence * Description : Initiates a read data byte (READ) sequence from the Flash. * This is done by driving the /CS line low to select the device, * then the READ instruction is transmitted followed by 3 bytes * address. This function exit and keep the /CS line low, so the * Flash still being selected. With this technique the whole * content of the Flash is read with a single READ instruction. * Input : - ReadAddr : FLASH's internal address to read from. * Output : None * Return : None *******************************************************************************/ void SPI_FLASH_StartReadSequence(u32 ReadAddr) {/* Select the FLASH: Chip Select low */SPI_FLASH_CS_LOW();/* Send "Read from Memory " instruction */SPI_FLASH_SendByte(W25X_ReadData);/* Send the 24-bit address of the address to read from -----------------------*//* Send ReadAddr high nibble address byte */SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);/* Send ReadAddr medium nibble address byte */SPI_FLASH_SendByte((ReadAddr& 0xFF00) >> 8);/* Send ReadAddr low nibble address byte */SPI_FLASH_SendByte(ReadAddr & 0xFF); }/*** @brief 使用SPI讀取一個(gè)字節(jié)的數(shù)據(jù)* @param 無(wú)* @retval 返回接收到的數(shù)據(jù)*/ u8 SPI_FLASH_ReadByte(void) {return (SPI_FLASH_SendByte(Dummy_Byte)); }/*** @brief 使用SPI發(fā)送一個(gè)字節(jié)的數(shù)據(jù)* @param byte:要發(fā)送的數(shù)據(jù)* @retval 返回接收到的數(shù)據(jù)*/ u8 SPI_FLASH_SendByte(u8 byte) {SPITimeout = SPIT_FLAG_TIMEOUT;/* 等待發(fā)送緩沖區(qū)為空,TXE事件 */while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_TXE) == RESET){if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(0);}/* 寫(xiě)入數(shù)據(jù)寄存器,把要寫(xiě)入的數(shù)據(jù)寫(xiě)入發(fā)送緩沖區(qū) */SPI_I2S_SendData(FLASH_SPIx , byte);SPITimeout = SPIT_FLAG_TIMEOUT;/* 等待接收緩沖區(qū)非空,RXNE事件 */while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_RXNE) == RESET){if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(1);}/* 讀取數(shù)據(jù)寄存器,獲取接收緩沖區(qū)數(shù)據(jù) */return SPI_I2S_ReceiveData(FLASH_SPIx ); }/*** @brief 使用SPI發(fā)送兩個(gè)字節(jié)的數(shù)據(jù)* @param byte:要發(fā)送的數(shù)據(jù)* @retval 返回接收到的數(shù)據(jù)*/ u16 SPI_FLASH_SendHalfWord(u16 HalfWord) {SPITimeout = SPIT_FLAG_TIMEOUT;/* 等待發(fā)送緩沖區(qū)為空,TXE事件 */while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_TXE) == RESET){if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(2);}/* 寫(xiě)入數(shù)據(jù)寄存器,把要寫(xiě)入的數(shù)據(jù)寫(xiě)入發(fā)送緩沖區(qū) */SPI_I2S_SendData(FLASH_SPIx , HalfWord);SPITimeout = SPIT_FLAG_TIMEOUT;/* 等待接收緩沖區(qū)非空,RXNE事件 */while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_RXNE) == RESET){if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(3);}/* 讀取數(shù)據(jù)寄存器,獲取接收緩沖區(qū)數(shù)據(jù) */return SPI_I2S_ReceiveData(FLASH_SPIx ); }/*** @brief 向FLASH發(fā)送 寫(xiě)使能 命令* @param none* @retval none*/ void SPI_FLASH_WriteEnable(void) {/* 通訊開(kāi)始:CS低 */SPI_FLASH_CS_LOW();/* 發(fā)送寫(xiě)使能命令*/SPI_FLASH_SendByte(W25X_WriteEnable);/*通訊結(jié)束:CS高 */SPI_FLASH_CS_HIGH(); }/* WIP(busy)標(biāo)志,FLASH內(nèi)部正在寫(xiě)入 */ #define WIP_Flag 0x01/*** @brief 等待WIP(BUSY)標(biāo)志被置0,即等待到FLASH內(nèi)部數(shù)據(jù)寫(xiě)入完畢* @param none* @retval none*/ void SPI_FLASH_WaitForWriteEnd(void) {u8 FLASH_Status = 0;/* 選擇 FLASH: CS 低 */SPI_FLASH_CS_LOW();/* 發(fā)送 讀狀態(tài)寄存器 命令 */SPI_FLASH_SendByte(W25X_ReadStatusReg);/* 若FLASH忙碌,則等待 */do{/* 讀取FLASH芯片的狀態(tài)寄存器 */FLASH_Status = SPI_FLASH_SendByte(Dummy_Byte); }while ((FLASH_Status & WIP_Flag) == SET); /* 正在寫(xiě)入標(biāo)志 *//* 停止信號(hào) FLASH: CS 高 */SPI_FLASH_CS_HIGH(); }//進(jìn)入掉電模式 void SPI_Flash_PowerDown(void) { /* 通訊開(kāi)始:CS低 */SPI_FLASH_CS_LOW();/* 發(fā)送 掉電 命令 */SPI_FLASH_SendByte(W25X_PowerDown);/*通訊結(jié)束:CS高 */SPI_FLASH_CS_HIGH(); } //喚醒 void SPI_Flash_WAKEUP(void) {/*選擇 FLASH: CS 低 */SPI_FLASH_CS_LOW();/* 發(fā)送 上電 命令 */SPI_FLASH_SendByte(W25X_ReleasePowerDown);/* 停止信號(hào) FLASH: CS 高 */SPI_FLASH_CS_HIGH(); } /*** @brief 等待超時(shí)回調(diào)函數(shù)* @param None.* @retval None.*/ static uint16_t SPI_TIMEOUT_UserCallback(uint8_t errorCode) {/* 等待超時(shí)后的處理,輸出錯(cuò)誤信息 */FLASH_ERROR("SPI 等待超時(shí)!errorCode = %d",errorCode);return 0; }/*********************************************END OF FILE**********************/

總結(jié)

以上是生活随笔為你收集整理的stm32 SPI、FLASH的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。