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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

《STM32从零开始学习历程》——SPI读写FLASH

發布時間:2024/1/1 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 《STM32从零开始学习历程》——SPI读写FLASH 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

《STM32從零開始學習歷程》@EnzoReventon

SPI讀寫FLASH

相關鏈接:
SPI物理層及FLASH芯片介紹
SPI協議層
SPI特性及架構

參考資料:
[野火EmbedFire]《STM32庫開發實戰指南——基于野火霸天虎開發板》
[正點原子]STM32F4開發指南-庫函數版本_V1.2
[ST]《STM32F4xx中文參考手冊》
SPI協議及總線協議介紹
W25Q128產品數據手冊

1 實現功能

  • 實現對FLASH一扇區的擦除
  • 對該FLASH扇區寫入數據,并讀取該數據,通過串口調試助手進行調試觀察
  • 寫入256個數據,并讀取寫入的數據以及其余未寫入的數據。
  • 對該扇區所有數據都寫入。

2 硬件設計

本文使用的外設為SPI1(正點原子F4探索者開發板)、FLASH以及USART1。
USART用來調試程序,我們還是使用USART1,因此將PB9,PB10與TX,RX相連接即可。

查閱正點原子F4探索者開發板硬件手冊,了解SPI引腳與GPIO的對應情況。


由上圖可以看出:SPI的SCK,MISO,MOSI分別與芯片的PB3,PB4,PB5連接,片選信號F_CS與PB14相連接,因此在后面程序配置的時候需要注意不能配置錯引腳。

3 軟件設計流程

① 使能SPIx和IO口時鐘
RCC_AHBxPeriphClockCmd() / RCC_APBxPeriphClockCmd();
② 初始化IO口為復用功能
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
③ 設置引腳復用映射:
GPIO_PinAFConfig();
② 初始化SPIx,設置SPIx工作模式
void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);
③ 使能SPIx
void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState);
④ 編寫字節發送函數:uint8_t SPI_FLASH_ByteWrite(uint8_t data)

發送數據(指令):void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);

接收返回的數據:uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx) ;

⑤ 編寫擦除扇區函數:void SPI_FLASH_Erase_Sector(uint32_t addr);

⑥ 編寫寫入數據函數:void SPI_FLASH_Page_Write(uint32_t addr, uint8_t *buf, uint32_t size);
⑦ 編寫讀取數據函數:void SPI_FLASH_Read_Buff(uint32_t addr, uint8_t *buf, uint32_t size);
⑧ 查看SPI傳輸狀態
SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE);

⑨ 編寫SPI發送函數
控制片選引腳:GPIO_ResetBits()、GPIO_SetBits();
⑩ 主函數調用,優化程序(超時函數、宏定義、等待空閑函數、寫使能函數)

4 代碼分析

  • 宏定義

這里大家可以按照自己的需求以及編程習慣進行定義,宏定義的好處在于便于程序的移植,方便改動程序。

/*命令定義-開頭*/ #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 /*SPI GPIO 接口*/ #define FLASH_SPI SPI1 #define FLASH_SPI_CLK RCC_APB2Periph_SPI1 #define FLASH_SPI_CLK_INIT RCC_APB2PeriphClockCmd#define FLASH_SPI_SCK_PIN GPIO_Pin_3 #define FLASH_SPI_SCK_GPIO_PORT GPIOB #define FLASH_SPI_SCK_GPIO_CLK RCC_AHB1Periph_GPIOB #define FLASH_SPI_SCK_SOURCE GPIO_PinSource3 #define FLASH_SPI_SCK_AF GPIO_AF_SPI1#define FLASH_SPI_MOSI_PIN GPIO_Pin_5 #define FLASH_SPI_MOSI_GPIO_PORT GPIOB #define FLASH_SPI_MOSI_GPIO_CLK RCC_AHB1Periph_GPIOB #define FLASH_SPI_MOSI_SOURCE GPIO_PinSource5 #define FLASH_SPI_MOSI_AF GPIO_AF_SPI1#define FLASH_SPI_MISO_PIN GPIO_Pin_4 #define FLASH_SPI_MISO_GPIO_PORT GPIOB #define FLASH_SPI_MISO_GPIO_CLK RCC_AHB1Periph_GPIOB #define FLASH_SPI_MISO_SOURCE GPIO_PinSource4 #define FLASH_SPI_MISO_AF GPIO_AF_SPI1#define FLASH_SPI_CS_PIN GPIO_Pin_14 #define FLASH_SPI_CS_GPIO_PORT GPIOB #define FLASH_SPI_CS_GPIO_CLK RCC_AHB1Periph_GPIOB#define FLASH_SPI_CS_LOW() GPIO_ResetBits(FLASH_SPI_CS_GPIO_PORT,FLASH_SPI_CS_PIN) #define FLASH_SPI_CS_HIGH() GPIO_SetBits(FLASH_SPI_CS_GPIO_PORT,FLASH_SPI_CS_PIN)/*等待超時時間*/ #define SPIT_FLAG_TIMEOUT ((uint32_t)0x1000) #define SPIT_LONG_TIMEOUT ((uint32_t)(10 * SPIT_FLAG_TIMEOUT))
  • 函數申明
/*函數申明*/static void SPI_GPIO_Config(void); //引腳初始化函數 void SPI_Mode_Config(void); //SPI模式初始化函數 void SPI_FLASH_Init(void); //SPI FLASH外設初始化調用函數 uint8_t SPI_FLASH_ByteWrite(uint8_t data); //字節寫入函數 uint8_t SPI_FLASH_Read_ID(void); //SPI讀取FLASH ID函數 void SPI_FLASH_Erase_Sector(uint32_t addr); //FLASH擦除函數 void SPI_FLASH_Write_Enable(void); //SPI寫使能函數 void SPI_FLASH_Wait_For_Standby(void); //SPI等待直到空閑函數 void SPI_FLASH_Read_Buff(uint32_t addr, uint8_t *buf, uint32_t size); //SPI連續讀函數 void SPI_FLASH_Write_Buff(uint32_t addr, uint8_t *buf, uint32_t size); //SPI連續寫函數 void SPI_FLASH_Page_Write(uint32_t addr, uint8_t *buf, uint32_t size); //SPI頁寫入函數

SPI子函數:

  • 靜態函數申明:
//申明超時變量 static uint32_t SPITimeOut = ((uint32_t)(10 * SPIT_FLAG_TIMEOUT));//申明錯誤代碼返回函數 static uint32_t SPI_TIMEOUT_UserCallback(uint8_t errorCode);
  • SPI GPIO初始化 復用函數
/* ================================================ SPI GPIO初始化 復用函數 ================================================ */ static void SPI_GPIO_Config(void) {GPIO_InitTypeDef GPIO_InitStructure; FLASH_SPI_CLK_INIT(FLASH_SPI_CLK, ENABLE); //使能SPI時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1)/* 使能 FLASH_SPI 及GPIO 時鐘 *//*!< SPI_FLASH_SPI_CS_GPIO, SPI_FLASH_SPI_MOSI_GPIO, SPI_FLASH_SPI_MISO_GPIO,SPI_FLASH_SPI_SCK_GPIO 時鐘使能 */RCC_AHB1PeriphClockCmd(FLASH_SPI_SCK_GPIO_CLK | FLASH_SPI_MOSI_GPIO_CLK|FLASH_SPI_MISO_GPIO_CLK|FLASH_SPI_CS_GPIO_CLK, ENABLE); //初始化GPIO時鐘//查看引腳,初始化SCK,MOSI,MISO,CS引腳GPIO//設置引腳復用GPIO_PinAFConfig(FLASH_SPI_SCK_GPIO_PORT, FLASH_SPI_SCK_SOURCE, FLASH_SPI_SCK_AF);GPIO_PinAFConfig(FLASH_SPI_MOSI_GPIO_PORT, FLASH_SPI_MOSI_SOURCE, FLASH_SPI_MOSI_AF); GPIO_PinAFConfig(FLASH_SPI_MISO_GPIO_PORT, FLASH_SPI_MISO_SOURCE, FLASH_SPI_MISO_AF);/*!< 配置 SPI_FLASH_SPI 引腳: SCK */GPIO_InitStructure.GPIO_Pin = FLASH_SPI_SCK_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;GPIO_Init(FLASH_SPI_SCK_GPIO_PORT, &GPIO_InitStructure);/*!< 配置 SPI_FLASH_SPI 引腳: MISO */GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MOSI_PIN;GPIO_Init(FLASH_SPI_MOSI_GPIO_PORT, &GPIO_InitStructure);/*!< 配置 SPI_FLASH_SPI 引腳: MOSI */ GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MISO_PIN;GPIO_Init(FLASH_SPI_MOSI_GPIO_PORT, &GPIO_InitStructure);/*!< 配置 SPI_FLASH_SPI 引腳: CS */GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;GPIO_Init(FLASH_SPI_CS_GPIO_PORT, &GPIO_InitStructure);}/* ================================================ SPI 初始化結構體初始化 ================================================ */ void SPI_Mode_Config(void) {/*!< 初始化SPI結構體函數 */SPI_InitTypeDef SPI_InitStructure;SPI_InitStructure.SPI_BaudRatePrescaler=SPI_BaudRatePrescaler_2;SPI_InitStructure.SPI_CPHA=SPI_CPHA_1Edge;SPI_InitStructure.SPI_CPOL=SPI_CPOL_Low;SPI_InitStructure.SPI_CRCPolynomial=7;SPI_InitStructure.SPI_DataSize=SPI_DataSize_8b;SPI_InitStructure.SPI_Direction=SPI_Direction_2Lines_FullDuplex;SPI_InitStructure.SPI_FirstBit=SPI_FirstBit_MSB;SPI_InitStructure.SPI_Mode=SPI_Mode_Master;SPI_InitStructure.SPI_NSS=SPI_NSS_Soft;SPI_Init(FLASH_SPI,&SPI_InitStructure);SPI_Cmd(FLASH_SPI,ENABLE);}
  • SPI FLASH 外設初始化
================================================ SPI FLASH 外設初始化 ================================================ */ void SPI_FLASH_Init(void) {SPI_GPIO_Config(); SPI_Mode_Config(); }
  • SPI字節寫入
/* ================================================ 通過SPI發送一個字節 參數:要寫入的數據 返回值:錯誤代碼 ================================================ */ uint8_t SPI_FLASH_ByteWrite(uint8_t data) {uint8_t re_data;//等待TXE標志SPITimeOut = SPIT_FLAG_TIMEOUT;while(SPI_I2S_GetFlagStatus (FLASH_SPI, SPI_I2S_FLAG_TXE) == RESET ){if((SPITimeOut--) == 0) return SPI_TIMEOUT_UserCallback(1);} SPI_I2S_SendData(FLASH_SPI, data);//等待RXNE標志 來確認發送完成,及準備讀取數據SPITimeOut = SPIT_FLAG_TIMEOUT;while(SPI_I2S_GetFlagStatus (FLASH_SPI, SPI_I2S_FLAG_RXNE) == RESET ){if((SPITimeOut--) == 0) return SPI_TIMEOUT_UserCallback(2);} re_data = SPI_I2S_ReceiveData(FLASH_SPI);return re_data; }
  • 讀取FLASH ID
/* ================================================ 讀ID0-ID7 ================================================ */uint8_t SPI_FLASH_Read_ID(void) { uint8_t id;//控制片選引腳FLASH_SPI_CS_LOW();//指令代碼SPI_FLASH_ByteWrite(W25X_ReleasePowerDown);SPI_FLASH_ByteWrite(0xFF);SPI_FLASH_ByteWrite(0xFF);SPI_FLASH_ByteWrite(0xFF);//接收讀取到的內容id = SPI_FLASH_ByteWrite(0xFF);FLASH_SPI_CS_HIGH();return id;}
  • 擦除FLASH扇區函數
/* ================================================ 擦除扇區 addr:必須對齊到要擦除的扇區的首地址 ================================================ */void SPI_FLASH_Erase_Sector(uint32_t addr) {//寫使能SPI_FLASH_Write_Enable();//控制片選引腳FLASH_SPI_CS_LOW();//指令代碼SPI_FLASH_ByteWrite(W25X_SectorErase);//發送要擦除的地址SPI_FLASH_ByteWrite((addr>>16) & 0xFF);SPI_FLASH_ByteWrite((addr>>8) & 0xFF);SPI_FLASH_ByteWrite(addr & 0xFF);FLASH_SPI_CS_HIGH(); //等待內部時序完成SPI_FLASH_Wait_For_Standby();}
  • 頁寫入函數
/* ================================================ 寫入數據 addr:要寫入數據的首地址, buf:存儲寫入的數據的指針 size:要寫入多少個數據 不超過256 ================================================ */void SPI_FLASH_Page_Write(uint32_t addr, uint8_t *buf, uint32_t size) {//寫使能SPI_FLASH_Write_Enable();//控制片選引腳FLASH_SPI_CS_LOW();//指令代碼SPI_FLASH_ByteWrite(W25X_PageProgram);//發送要寫入的地址SPI_FLASH_ByteWrite((addr>>16) & 0xFF);SPI_FLASH_ByteWrite((addr>>8) & 0xFF);SPI_FLASH_ByteWrite(addr & 0xFF);while(size--){SPI_FLASH_ByteWrite(*buf);buf++;}FLASH_SPI_CS_HIGH(); //等待內部時序完成SPI_FLASH_Wait_For_Standby();}
  • 讀取數據函數
/* ================================================ 讀取數據 addr:要讀取數據的首地址, buf:存儲讀取到的數據的指針 size:要讀取多少個數據 ================================================ */void SPI_FLASH_Read_Buff(uint32_t addr, uint8_t *buf, uint32_t size) {//控制片選引腳FLASH_SPI_CS_LOW();//指令代碼SPI_FLASH_ByteWrite(W25X_ReadData);//發送要讀取的地址SPI_FLASH_ByteWrite((addr>>16) & 0xFF);SPI_FLASH_ByteWrite((addr>>8) & 0xFF);SPI_FLASH_ByteWrite(addr & 0xFF); while(size--){*buf = SPI_FLASH_ByteWrite(0xFF);buf++;}FLASH_SPI_CS_HIGH(); }
  • 寫入數據,不受256數據限制
/* ================================================ 寫入數據 addr:要寫入數據的首地址, buf:要寫入的數據的指針 size:要寫入多少個數據 不超過256 ================================================ */void SPI_FLASH_Write_Buff(uint32_t addr, uint8_t *buf, uint32_t size) {uint32_t count=0;//計算循環次數while(size--){count++;//第一次執行,第257次,256*2+1,256*3+1,addr對齊到4096時if(count == 1 || (count%256) ==1 || (addr%4096)==0){//結束上一次的頁寫入指令FLASH_SPI_CS_HIGH(); //等待上一次頁寫入的完成SPI_FLASH_Wait_For_Standby();//寫使能,每次寫入前都必須調用SPI_FLASH_Write_Enable(); //控制片選引腳FLASH_SPI_CS_LOW();//指令代碼SPI_FLASH_ByteWrite(W25X_PageProgram);//發送要寫入的地址SPI_FLASH_ByteWrite((addr>>16) & 0xFF);SPI_FLASH_ByteWrite((addr>>8) & 0xFF);SPI_FLASH_ByteWrite(addr & 0xFF); } SPI_FLASH_ByteWrite(*buf);buf++;addr++; }FLASH_SPI_CS_HIGH(); //等待內部時序完成SPI_FLASH_Wait_For_Standby();}
  • 寫使能函數
/* ================================================ 寫使能 ================================================ */void SPI_FLASH_Write_Enable(void) {//控制片選引腳FLASH_SPI_CS_LOW();//指令代碼SPI_FLASH_ByteWrite(W25X_WriteEnable);FLASH_SPI_CS_HIGH(); }
  • 等待直到空閑函數
/* ================================================ 等待直到空閑狀態 ================================================ */void SPI_FLASH_Wait_For_Standby(void) {uint8_t status ;//控制片選引腳FLASH_SPI_CS_LOW(); //指令代碼 0x05 檢測讀取標志位SPI_FLASH_ByteWrite(W25X_ReadStatusReg);SPITimeOut = SPIT_LONG_TIMEOUT;while(1){status = SPI_FLASH_ByteWrite(0xFF);//如果條件成立,說明為空閑狀態if((status & 0x01) == 0) break;//若SPITimeout為0,表示已檢測SPITimeout次都仍為忙碌,跳出循環if((SPITimeOut--)==0){SPI_TIMEOUT_UserCallback(3);break;}} FLASH_SPI_CS_HIGH(); }
  • 錯誤代碼 返回函數
/* ================================================ 錯誤代碼返回函數 參數:寫如的錯誤代碼 返回值:錯誤代碼 ================================================ */ static uint32_t SPI_TIMEOUT_UserCallback(uint8_t errorCode) {/* Block communication and all processes */printf("\r\nSPI 等待超時!errorCode = %d\r\n",errorCode);return errorCode; }

主函數

uint8_t read_buff[4096] = {0};uint8_t write_buff[4096] = {0};int main(void) {int i = 0;NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //設置系統中斷優先級分組2delay_init(168); //延時初始化 uart_init(115200); //串口初始化波特率為115200LED_Init(); //初始化與LED連接的硬件接口 SPI_FLASH_Init();printf("\r\n=======================================\r\n");printf("this is id : 0x%x",SPI_FLASH_Read_ID());printf("\r\n擦除開始");//擦除測試SPI_FLASH_Erase_Sector(4096*0);//擦除測試SPI_FLASH_Erase_Sector(4096*1); printf("\r\n擦除完成");SPI_FLASH_Read_Buff(0,read_buff,4096);for(i=0;i<4096;i++){//若不等于0xFF,說明擦除不成功if(read_buff[i] != 0xFF){printf("\r\n擦除失敗");} }printf("\r\n擦除完成");//初始化要寫入的數據for(i=0;i<256;i++){write_buff[i] = i;}printf("\r\n開始寫入");SPI_FLASH_Write_Buff(0,write_buff,256); //寫入數據 0為扇區首地址 write_buff為數據地址,256為寫入數據數量printf("\r\n寫入完成");SPI_FLASH_Read_Buff(0,write_buff,4096);printf("\r\n讀取到的數據:\r\n");for(i=0;i<4096;i++) //循環打印輸出{printf("0x%02x ",write_buff[i]);}while (1){ } }

5 效果展示

  • 寫入256個數據。

  • 可以看到前256個數據已經被寫入為我們想要寫入的數據了,其余數據均為擦除后的0xFF。

  • 對整個扇區寫入數據。
    修改主函數部分參數。
  • //初始化要寫入的數據for(i=0;i<4096;i++){write_buff[i] = i;}printf("\r\n開始寫入");SPI_FLASH_Write_Buff(0,write_buff,4096);printf("\r\n寫入完成");SPI_FLASH_Read_Buff(0,write_buff,4096);printf("\r\n讀取到的數據:\r\n");for(i=0;i<4096;i++){printf("0x%02x ",write_buff[i]);}


    可以看出整個扇區的數據都被寫入。

    總結

    以上是生活随笔為你收集整理的《STM32从零开始学习历程》——SPI读写FLASH的全部內容,希望文章能夠幫你解決所遇到的問題。

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