一、Flash簡介
快閃存儲器(flash memory),是一種電子式可清除程序化只讀存儲器的形式,允許在操作中被多次擦或?qū)懙拇鎯ζ?。它是一種非易失性存儲器,即斷電數(shù)據(jù)也不會丟失。
二、STM32F1的Flash
STM32F103ZET6的Flash大小為512KB,屬于大容量產(chǎn)品。在中文參考手冊中給出了大容量產(chǎn)品的Flash模塊組織結(jié)構(gòu)圖
大容量產(chǎn)品Flsh模塊組織結(jié)構(gòu)圖
? 主存儲器 主存儲器用來存儲我們的代碼和定義的一些常量數(shù)據(jù)。當(dāng)Boot0和Boot1都接GND時,芯片從主存儲器的起始地址0x0800 0000開始運行代碼。
? 信息塊
系統(tǒng)存儲器中存儲的是啟動程序代碼。啟動程序就是串口下載的代碼。當(dāng)Boot0接VCC,Boot1接GND時,運行的就是系統(tǒng)存儲器中的代碼。系統(tǒng)存儲器中存儲的啟動代碼,是ST公司在芯片出廠時就已經(jīng)下載好的,用戶無法修改。選擇字節(jié)是用來配置寫保護和杜保護功能。
? 閃存存儲器接口寄存器 閃存存儲器接口寄存器,是整個閃存的控制機構(gòu),里面包含了很多的閃存的控制寄存器和狀態(tài)寄存器。
在執(zhí)行閃存寫操作時,任何對閃存的讀操作都會被鎖住。只有對閃存的寫操作結(jié)束后,讀操作才能夠正常執(zhí)行。也就是說,在對閃存進行寫操作或者擦除操作時,無法對閃存進行讀操作。
三、Flash操作步驟
? 解鎖和鎖定
? 寫/擦除操作
? 獲取Flash狀態(tài)
? 等待操作完成
? 讀取Flash指定地址數(shù)據(jù)
四、程序設(shè)計
操作內(nèi)部Flash時,最小單位是半字(16位)。
44.1 讀取數(shù)據(jù)
讀取數(shù)據(jù)用的是指針的方式,在之前博主的文章中有關(guān)于如何利用指針在指定地址讀寫數(shù)據(jù)的操作。 ```c /*
*============================================================================== *函數(shù)名稱:Med_Flash_ReadHalfWord *函數(shù)功能:讀取指定地址的半字(16位數(shù)據(jù)) *輸入?yún)?shù):faddr:讀取地址 *返回值:對應(yīng)讀取地址數(shù)據(jù) *備 注:對內(nèi)部Flash的操作是以半字為單位,所以讀寫地址必須是2的倍數(shù) *============================================================================== */ vu16 Med_Flash_ReadHalfWord (u32 faddr) { return (vu16)faddr; }
```c
/*
*==============================================================================
*函數(shù)名稱:Med_Flash_Read
*函數(shù)功能:從指定地址開始讀出指定長度的數(shù)據(jù)
*輸入?yún)?shù):ReadAddr:讀取起始地址;pBuffer:數(shù)據(jù)指針;
NumToRead:讀?。ò胱郑?shù)
*返回值:無
*備 注:對內(nèi)部Flash的操作是以半字為單位,所以讀寫地址必須是2的倍數(shù)
*==============================================================================
*/
void Med_Flash_Read (u32 ReadAddr,u16 *pBuffer,u16 NumToRead)
{
u16 i;
for(i = 0;i < NumToRead;i ++)
{
pBuffer[i] = Med_Flash_ReadHalfWord(ReadAddr); // 讀取2個字節(jié).
ReadAddr += 2; // 偏移2個字節(jié).
}
}
4.2 寫入數(shù)據(jù)(不檢查)
這里的不檢查,是指在寫入之前,不檢查寫入地址是否可寫。
/*
*==============================================================================
*函數(shù)名稱:Med_Flash_Write_NoCheck
*函數(shù)功能:不檢查的寫入
*輸入?yún)?shù):WriteAddr:寫入起始地址;pBuffer:數(shù)據(jù)指針;
NumToWrite:寫入(半字)數(shù)
*返回值:無
*備 注:對內(nèi)部Flash的操作是以半字為單位,所以讀寫地址必須是2的倍數(shù)
*==============================================================================
*/
void Med_Flash_Write_NoCheck (u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)
{
u16 i;
for(i = 0;i < NumToWrite;i ++)
{
FLASH_ProgramHalfWord(WriteAddr,pBuffer[i]);
WriteAddr += 2; // 地址增加2.
}
}
4.3 寫入數(shù)據(jù)(檢查)
/*
*==============================================================================
*函數(shù)名稱:Med_Flash_Read
*函數(shù)功能:從指定地址開始寫入指定長度的數(shù)據(jù)
*輸入?yún)?shù):WriteAddr:寫入起始地址;pBuffer:數(shù)據(jù)指針;
NumToRead:寫入(半字)數(shù)
*返回值:無
*備 注:對內(nèi)部Flash的操作是以半字為單位,所以讀寫地址必須是2的倍數(shù)
*==============================================================================
*/
// 根據(jù)中文參考手冊,大容量產(chǎn)品的每一頁是2K字節(jié)
#if STM32_FLASH_SIZE < 256
#define STM32_SECTOR_SIZE 1024 // 字節(jié)
#else
#define STM32_SECTOR_SIZE 2048
#endif
// 一個扇區(qū)的內(nèi)存
u16 STM32_FLASH_BUF[STM32_SECTOR_SIZE / 2];
void Med_Flash_Write (u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)
{
u32 secpos; // 扇區(qū)地址
u16 secoff; // 扇區(qū)內(nèi)偏移地址(16位字計算)
u16 secremain; // 扇區(qū)內(nèi)剩余地址(16位計算)
u16 i;
u32 offaddr; // 去掉0X08000000后的地址
// 判斷寫入地址是否在合法范圍內(nèi)
if (WriteAddr < STM32_FLASH_BASE || (WriteAddr >= (STM32_FLASH_BASE + 1024 * STM32_FLASH_SIZE)))
{
return; // 非法地址
}
FLASH_Unlock(); // 解鎖
offaddr = WriteAddr - STM32_FLASH_BASE; // 實際偏移地址
secpos = offaddr / STM32_SECTOR_SIZE; // 扇區(qū)地址
secoff = (offaddr % STM32_SECTOR_SIZE) / 2; // 在扇區(qū)內(nèi)的偏移(2個字節(jié)為基本單位)
secremain = STM32_SECTOR_SIZE / 2 - secoff; // 扇區(qū)剩余空間大小
if (NumToWrite <= secremain)
{
secremain = NumToWrite; // 不大于該扇區(qū)范圍
}
while (1)
{
// 讀出整個扇區(qū)的內(nèi)容
Med_Flash_Read(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE,STM32_FLASH_BUF,STM32_SECTOR_SIZE / 2);
// 校驗數(shù)據(jù)
for (i = 0;i < secremain;i ++)
{
// 需要擦除
if (STM32_FLASH_BUF[secoff + i] != 0XFFFF)
{
break;
}
}
// 需要擦除
if (i < secremain)
{
FLASH_ErasePage(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE); // 擦除這個扇區(qū)
// 復(fù)制
for (i = 0;i < secremain;i ++)
{
STM32_FLASH_BUF[i + secoff] = pBuffer[i];
}
// 寫入整個扇區(qū)
Med_Flash_Write_NoCheck(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE,STM32_FLASH_BUF,STM32_SECTOR_SIZE / 2);
}
else
{
// 寫已經(jīng)擦除了的,直接寫入扇區(qū)剩余區(qū)間
Med_Flash_Write_NoCheck(WriteAddr,pBuffer,secremain);
}
if (NumToWrite == secremain)
{
break; // 寫入結(jié)束了
}
// 寫入未結(jié)束
else
{
secpos ++; // 扇區(qū)地址增1
secoff=0; // 偏移位置為0
pBuffer+=secremain; // 指針偏移
WriteAddr+=secremain; // 寫地址偏移
NumToWrite-=secremain; // 字節(jié)(16位)數(shù)遞減
if (NumToWrite >(STM32_SECTOR_SIZE/2))
{
secremain=STM32_SECTOR_SIZE/2; // 下一個扇區(qū)還是寫不完
}
else
{
secremain=NumToWrite; // 下一個扇區(qū)可以寫完了
}
}
}
FLASH_Lock(); // 上鎖
}
宏定義如下
// STM32的Flash容量,單位為KB
#define STM32_FLASH_SIZE 512
// FLASH主存儲塊起始地址
#define STM32_FLASH_BASE 0x08000000
上面的讀取數(shù)據(jù)和不檢查的寫入都比較簡單,因此并沒有再做分析。這里分析一下帶檢查的寫入的程序設(shè)計思路。
? 首先用一小段條件編譯來區(qū)分一下大容量產(chǎn)品和其他產(chǎn)品。因為大容量產(chǎn)品的一頁(一個扇區(qū))是2K字節(jié),中小容量產(chǎn)品的一頁是1K字節(jié)。定一個了一個數(shù)組,數(shù)組大小是一個扇區(qū)的大小。
// 根據(jù)中文參考手冊,大容量產(chǎn)品的每一頁是2K字節(jié)
#if STM32_FLASH_SIZE < 256
#define STM32_SECTOR_SIZE 1024 // 字節(jié)
#else
#define STM32_SECTOR_SIZE 2048
#endif
// 一個扇區(qū)的內(nèi)存
u16 STM32_FLASH_BUF[STM32_SECTOR_SIZE / 2];
大容量產(chǎn)品,一個扇區(qū)2K字節(jié),除以2是因為在對內(nèi)部Flash操作時,最小單位是半字。
? 接下來,判斷要寫入的地址是否合法,也就是是否在主存儲塊地址范圍內(nèi)。
// 判斷寫入地址是否在合法范圍內(nèi)
if (WriteAddr < STM32_FLASH_BASE || (WriteAddr >= (STM32_FLASH_BASE + 1024 * STM32_FLASH_SIZE)))
{
return; // 非法地址
}
? 如果要寫入的地址合法,那么解鎖后計算一些參數(shù)值。
offaddr = WriteAddr - STM32_FLASH_BASE; // 實際偏移地址
實際偏移地址 ,指的是要寫入的地址與主存儲塊基地址(0x0800 0000)的差值。
secpos = offaddr / STM32_SECTOR_SIZE; // 扇區(qū)地址
扇區(qū)地址指的是要寫入的地址所在扇區(qū)前面的扇區(qū)數(shù)。由于所有的參數(shù)都不是浮點型,因此在做除法時,小數(shù)位都是0。最終除出來的結(jié)果就是當(dāng)前扇區(qū)前面的扇區(qū)數(shù)。
secoff = (offaddr % STM32_SECTOR_SIZE) / 2; // 在扇區(qū)內(nèi)的偏移(2個字節(jié)為基本單位)
在扇區(qū)內(nèi)的偏移指的是要寫入的地址與其所在扇區(qū)首地址的差值。用要寫入的地址取余每一個扇區(qū)的字節(jié)數(shù),余數(shù)就是偏移地址。但是由于操作內(nèi)部Flash時的最小單位是半字,因此要除以2。
secremain = STM32_SECTOR_SIZE / 2 - secoff; // 扇區(qū)剩余空間大小
扇區(qū)內(nèi)剩余空間大小只需要用扇區(qū)總的空間大小減去偏移地址即可得到。但是需要注意的是,單位都是半字。這里的剩余空間大小,并不是真正的剩余空間大小。而是指寫入地址后面的扇區(qū)大小。這里不太好理解,畫一個圖表示一下。
扇區(qū)內(nèi)剩余空間大小示意圖
正是因為這里的扇區(qū)剩余空間大小并不是指真正的剩余空間大小。在剩余空間內(nèi),也可能存在已經(jīng)寫入數(shù)據(jù)的地址。所以后面需要進行判斷,來確定是否需要擦除。
? 判斷在寫入地址所在扇區(qū)能否將寫入內(nèi)容全部寫入完成
if (NumToWrite <= secremain)
{
secremain = NumToWrite; // 不大于該扇區(qū)范圍
}
如果可以,直接將要寫入的半字數(shù)賦值給當(dāng)前扇區(qū)剩余空間大小。如果當(dāng)前扇區(qū)剩余空間大小可以容納要寫入的半字數(shù),那么只需要寫入一次即可,在后續(xù)判斷是否寫完時,直接通過,while循環(huán)只執(zhí)行一次。
? 讀出整個扇區(qū)內(nèi)容,判斷是否需要擦除
// 讀出整個扇區(qū)的內(nèi)容
Med_Flash_Read(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE,STM32_FLASH_BUF,STM32_SECTOR_SIZE / 2);
// 校驗數(shù)據(jù)
for (i = 0;i < secremain;i ++)
{
// 需要擦除
if (STM32_FLASH_BUF[secoff + i] != 0XFFFF)
{
break;
}
上一篇:STM32基礎(chǔ)知識:串口通信-輪詢方式
下一篇:STM32CubeMx計數(shù)器基本使用方法
推薦閱讀最新更新時間:2025-06-24 06:10
- 熱門資源推薦
- 熱門放大器推薦
設(shè)計資源 培訓(xùn) 開發(fā)板 精華推薦
- Microchip 升級數(shù)字信號控制器(DSC)產(chǎn)品線 推出PWM 分辨率和 ADC 速度業(yè)界領(lǐng)先的新器件
- 意法半導(dǎo)體STM32MP23x:突破成本限制的工業(yè)AI應(yīng)用核心
- 意法半導(dǎo)體推出用于匹配遠距離無線微控制器STM32WL33的集成的匹配濾波芯片
- ESP32開發(fā)板連接TFT顯示屏ST7789跳坑記
- 如何讓ESP32支持analogWrite函數(shù)
- LGVL配合FreeType為可變字體設(shè)置字重-ESP32篇
- 使用樹莓派進行 ESP32 Jtag 調(diào)試
- ESP32怎么在SPIFFS里面存儲html,css,js文件,以及網(wǎng)頁和arduino的通訊
- ESP32 freeRTOS使用測試
- 基于霍爾傳感器的自行車測速
- LT3470AIDDB 12V 降壓轉(zhuǎn)換器的典型應(yīng)用電路
- OP295GSZ 4.5V、低壓差運算放大器基準(zhǔn)的典型應(yīng)用
- STEVAL-ISA098V1,使用 L7985A 的演示板,HSOP8 封裝中的 2 A 降壓開關(guān)
- NCP2811 NOCAP高級立體聲耳機放大器典型應(yīng)用電路
- STM3210C-EVAL,基于 STM32F107VCT STM32 ARM Cortex-M3(256KB 閃存)連接線 MCU 的評估板
- 使用 ROHM Semiconductor 的 BD45321 的參考設(shè)計
- EVAL40WFLYP7950VTOBO1、40W 適配器評估板在無緩沖器反激中使用 950V CoolMOS P7 和 ICE2QS03G QR 反激控制器以提高效率
- L296P大電流開關(guān)穩(wěn)壓器典型應(yīng)用電路
- 用于便攜式應(yīng)用的 RT9524 線性單節(jié)鋰離子電池充電器 IC 的典型應(yīng)用電路
- 蘋果被判侵犯3G專利,需向西班牙公司TOT賠償1.1億美元
- 從設(shè)計概念到 FPGA 原型僅需數(shù)分鐘,印度 InCore 完成 SoC Generator 平臺硅驗證
- 消息稱因難尋客戶,三星推遲美國芯片工廠的完工時間
- BOE(京東方)聯(lián)合榮耀打造榮耀Magic V5 以領(lǐng)先LTPO技術(shù)打造行業(yè)新標(biāo)桿
- 華為ADS 4發(fā)布:多傳感器融合,提升自動駕駛安全性
- 曉鶯說:線控制動變革風(fēng)云
- 大眾商用車推出AirConsole 將其信息娛樂系統(tǒng)擴展為游戲機
- 福州大學(xué)發(fā)明新機器視覺傳感器 可使機器人對極端光照做出超快反應(yīng)
- 蘋果獲沉浸式虛擬顯示器相關(guān)的專利
- 英特爾汽車“折戟”,十年布局一夜歸零
- 中微公司定增募資82.1億元:大基金二期獲25億元
- 字節(jié)跳動取消了大小周
- 龍騰股份MOSFET售價下滑,低研發(fā)投入布局IGBT
- 集微指數(shù)下跌1.62% 洲明科技調(diào)漲LED顯示產(chǎn)品價格
- COF封裝需求旺盛, 南茂獲多家手機品牌OLED DDI訂單
- NASA唯一機器人登月任務(wù)被取消 科學(xué)家感到憤怒
- 機器人索菲亞:不會嘗試統(tǒng)治世界
- 以后你的外賣都是機器人來送 硅谷已經(jīng)開始了
- 磐霖資本A輪主投行業(yè)級無人機企業(yè)傲勢科技
- 優(yōu)地科技完成數(shù)千萬元B輪融資 發(fā)力室外機器人場景
- 【廣州招聘】嵌入式軟件工程師(雙休 包工作餐 住宿)
- 通過MSP430F1232最小系統(tǒng)測試單片機AD10程序
- 數(shù)字通信中的卷積碼編解碼的FPGA設(shè)計
- who can tell me what the mean of /*lint -esym(715,param) */?
- 關(guān)于LPC2478在IAR環(huán)境下,從外部flash和外部sdram中的問題
- 如何調(diào)節(jié)低電壓非隔離式電源
- 什么材料可以阻擋,或者吸收磁鐵的磁場,除了磁鐵。
- 想通過做點實際的東西來入門嵌入式,幫忙給點創(chuàng)意
- CycloneV DQ管腳編譯錯誤
- 傳感器原理及其應(yīng)用