開發板:STM32F103CBT6 開發環境:keil 4
一、STM32FLASH簡介
不同的STM32它的FLASH大小也是不一樣的,分為大、中、小容量,容量由16K到1024K不等。這次實驗用的開發板FLASH容量大小為128K。
STM32的閃存模塊由:主存儲器、信息塊和閃存存儲器接口寄存器三部分組成。
主存儲器:該部分主要是用來存放代碼和數據常數,被划分為128頁,每頁1K字節(小容量產品也是每頁1K字節,大容量為每頁2K字節)。主存儲器的起始地址就是0X08000000, B0、B1都接GND的時候,就是從0X08000000開始運行代碼的。
信息塊:該部分分為2個小部分,其中啟動程序代碼,是用來存儲ST自帶的啟動程序,用於串口下載代碼,當B0接V3.3,B1接GND的時候,運行的就是這部分代碼。用戶選擇字節,則一般用於配置寫保護、讀保護等功能。
閃存存儲器接口寄存器:該部分用於控制閃存讀寫等,是整個閃存模塊的控制機構。
對主存儲器和信息塊的寫入由內嵌的閃存編程/擦除控制器(FPEC)管理;編程與擦除的高電壓由內部產生。
在執行閃存寫操作時,任何對閃存的讀操作都會鎖住總線,在寫操作完成后讀操作才能正確地進行;既在進行寫或擦除操作時,不能進行代碼或數據的讀取操作
閃存的讀取
內置閃存模塊可以在通用地址空間直接尋址,任何32位數據的讀操作都能訪問閃存模塊的內容並得到相應的數據。讀接口在閃存端包含一個讀控制器,還包含一個AHB接口與CPU銜接。這個接口的主要工作是產生讀閃存的控制信號並預取CPU要求的指令塊,預取指令塊僅用於在I-Code總線上的取指操作,數據常量是通過D-Code總線訪問的。這兩條總線的訪問目標是相同的閃存模塊,訪問D-Code將比預取指令優先級高。
這里要特別留意一個閃存等待時間,因為CPU運行速度比FLASH快得多,STM32F103的FLASH最快訪問速度≤24Mhz,如果CPU頻率超過這個速度,那么必須加入等待時間,比如我們一般使用72Mhz的主頻,那么FLASH等待周期就必須設置為2,該設置通過FLASH_ACR寄存器設置。
閃存的編程和擦除
編程過程:
·檢查FLASH_CR的LOCK是否解鎖,如果沒有則先解鎖
·檢查FLASH_SR寄存器的BSY位,以確認沒有其他正在進行的編程操作
·設置FLASH_CR寄存器的PG位為’1’
·在指定的地址寫入要編程的半字
·等待BSY位變為’0’
·讀出寫入的地址並驗證數據
擦除過程(頁擦除)
·檢查FLASH_CR的LOCK是否解鎖,如果沒有則先解鎖
·檢查FLASH_SR寄存器的BSY位,以確認沒有其他正在進行的閃存操作
·設置FLASH_CR寄存器的PER位為’1’
·用FLASH_AR寄存器選擇要擦除的頁
·設置FLASH_CR寄存器的STRT位為’1’
·等待BSY位變為’0’
·讀出被擦除的頁並做驗證
二、軟件實現
flash.c文件
#include "flash.h" #include "delay.h" #include "usart.h" /***flash解鎖*****/ void STMFLASH_Unlock(void) { FLASH->KEYR=FLASH_KEY1; //寫入解鎖序列 FLASH->KEYR=FLASH_KEY2; } //flash上鎖 void STMFLASH_Lock(void) { FLASH->CR|=1<<7; //上鎖 } //得到FLASH狀態 u8 STMFLASH_GetStatus(void) { u32 res; res=FLASH->SR; if(res&(1<<0))return 1; //忙 else if(res&(1<<2))return 2; //編程錯誤 else if(res&(1<<4))return 3; //寫保護錯誤 return 0; //操作完成 } //等待操作完成 //time:延時長短 //返回值:狀態. u8 STMFLASH_WaitDone(u16 time) { u8 res; do { res=STMFLASH_GetStatus(); if(res!=1)break; //非忙,無需等待,直接退出. Delay_us(1); time--; }while(time); if(time==0)res=0xff; //TIMEOUT return res; } //擦除頁 //paddr:頁地址 //返回值:執行情況 u8 STMFLASH_ErasePage(u32 paddr) { u8 res=0; res=STMFLASH_WaitDone(0X5FFF); //等待上次操作結束,>20ms if(res==0) { FLASH->CR|=1<<1; //頁擦除 FLASH->AR=paddr; //設置頁地址 FLASH->CR|=1<<6; //開始擦除 res=STMFLASH_WaitDone(0X5FFF); //等待操作結束,>20ms if(res!=1) //非忙 { FLASH->CR&=~(1<<1); //清除頁擦除標志. } } return res; } //讀出指定地址的半字(16位數據) //faddr:讀地址(此地址必須為2的倍數!!) //返回值:對應數據. u16 STMFLASH_ReadHalfWord(u32 faddr) { return *(vu16*)faddr; } #if STM32_FLASH_WREN //如果使能了寫 //不檢查的寫入 //WriteAddr:起始地址 //pBuffer:數據指針 //NumToWrite:半字(16位)數 void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite) { u16 i; for(i=0;i<NumToWrite;i++) { FLASH_ProgramHalfWord(WriteAddr,pBuffer[i]); WriteAddr+=2; //地址增加2. } } //從指定地址開始寫入指定長度的數據 //WriteAddr:起始地址(此地址必須為2的倍數!!) //pBuffer:數據指針 //NumToWrite:半字(16位)數(就是要寫入的16位數據的個數.) #if STM32_FLASH_SIZE<256 #define STM_SECTOR_SIZE 1024 //字節 #else #define STM_SECTOR_SIZE 2048 #endif u16 STMFLASH_BUF[STM_SECTOR_SIZE/2]; //最多是2K字節 void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite) { u32 secpos; //扇區地址 u16 secoff; //扇區內偏移地址(16位字計算) u16 secremain; //扇區內剩余地址(16位字計算) u16 i; u32 offaddr; //去掉0X08000000后的地址 if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))return;//非法地址 FLASH_Unlock(); //解鎖 offaddr=WriteAddr-STM32_FLASH_BASE; //實際偏移地址. secpos=offaddr/STM_SECTOR_SIZE; //扇區地址 0~127 for STM32F103RBT6 secoff=(offaddr%STM_SECTOR_SIZE)/2; //在扇區內的偏移(2個字節為基本單位.) secremain=STM_SECTOR_SIZE/2-secoff; //扇區剩余空間大小 if(NumToWrite<=secremain)secremain=NumToWrite; //不大於該扇區范圍 while(1) { STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//讀出整個扇區的內容 for(i=0;i<secremain;i++) //校驗數據 { if(STMFLASH_BUF[secoff+i]!=0XFFFF)break; //需要擦除 } if(i<secremain) //需要擦除 { FLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE); //擦除這個扇區 for(i=0;i<secremain;i++) //復制 { STMFLASH_BUF[i+secoff]=pBuffer[i]; } STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//寫入整個扇區 }else STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain); //寫已經擦除了的,直接寫入扇區剩余區間. if(NumToWrite==secremain)break; //寫入結束了 else //寫入未結束 { secpos++; //扇區地址增1 secoff=0; //偏移位置為0 pBuffer+=secremain; //指針偏移 WriteAddr+=secremain; //寫地址偏移 NumToWrite-=secremain; //字節(16位)數遞減 if(NumToWrite>(STM_SECTOR_SIZE/2))secremain=STM_SECTOR_SIZE/2;//下一個扇區還是寫不完 else secremain=NumToWrite; //下一個扇區可以寫完了 } }; FLASH_Lock(); //上鎖 } #endif //從指定地址開始讀取指定長度的數據 //ReadAddr:起始地址 //pBuffer:數據指針 //NumToWrite:半字(16位)數 void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead) { u16 i; for(i=0;i<NumToRead;i++) { pBuffer[i]=STMFLASH_ReadHalfWord(ReadAddr); //讀取2個字節. ReadAddr+=2; //偏移2個字節 } } ////////////////////////////////////////////////////////////////////////////////////////////////////// //WriteAddr:起始地址 //WriteData:要寫入的數據 void Test_Write(u32 WriteAddr,u16 WriteData) { STMFLASH_Write(WriteAddr,&WriteData,1); //寫入一個字 }
flash.h文件
#ifndef __FLASH_H__ #define __FLASH_H__ #include <stm32f10x.h> #define FLASH_KEY1 0X45670123 #define FLASH_KEY2 0XCDEF89AB #define STM32_FLASH_SIZE 128 //所選STM32的FLASH容量大小(單位為K) #define STM32_FLASH_WREN 1 //使能FLASH寫入(0,不使能;1,使能) ////////////////////////////////////////////////////////////////////////////////////////////////////// //FLASH起始地址 #define STM32_FLASH_BASE 0x08000000 //STM32 FLASH的起始地址 void STMFLASH_Unlock(void); //解鎖 void STMFLASH_Lock(void); //上鎖 u8 STMFLASH_GetStatus(void); //獲得狀態 u8 STMFLASH_WaitDone(u16 time); //等待操作結束 u8 STMFLASH_ErasePage(u32 paddr); //擦除頁 u16 STMFLASH_ReadHalfWord(u32 faddr); //讀出半字 void STMFLASH_WriteLenByte(u32 WriteAddr,u32 DataToWrite,u16 Len); //指定地址開始寫入指定長度的數據 u32 STMFLASH_ReadLenByte(u32 ReadAddr,u16 Len); //指定地址開始讀取指定長度的數據 void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite); //從指定地址開始寫入指定長度的數據 void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead); //從指定地址開始讀取指定長度的數據 //測試寫入 void Test_Write(u32 WriteAddr,u16 WriteData); #endif
main.c文件
#include "flash.h" #define SIZE sizeof(TEXT_Buffer) ///數組長度 #define FLASH_SAVE_ADDR 0X08070000 //設置FLASH 保存地址(必須為偶數,且其值要大於本代碼所占用FLASH的大小+0X08000000) const u8 TEXT_Buffer[]={"STM32 FLASH TEST"}; u8 datatemp[SIZE]; int main(void) { while(1) { if(掉電) { STMFLASH_Write(FLASH_SAVE_ADDR,(u16*)TEXT_Buffer,SIZE);//flash寫函數 } STMFLASH_Read(FLASH_SAVE_ADDR,(u16*)datatemp,SIZE); //flash讀函數 } }
以上就是flash在掉電的時候進行讀寫的程序,項目用到了就記錄一下,方便以后查閱學習。其中的大部分代碼在庫函數中都能夠找到參考。
