精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

STM32速成筆記(12)—Flash閃存

冬至子 ? 來源:二土電子 ? 作者:二土電子 ? 2023-10-24 15:19 ? 次閱讀

一、Flash簡介

快閃存儲器(flash memory),是一種電子式可清除程序化只讀存儲器的形式,允許在操作中被多次擦或寫的存儲器。它是一種非易失性存儲器,即斷電數據也不會丟失。

二、STM32F1的Flash

STM32F103ZET6的Flash大小為512KB,屬于大容量產品。在中文參考手冊中給出了大容量產品的Flash模塊組織結構圖

圖片

大容量產品Flsh模塊組織結構圖

  • ? 主存儲器 主存儲器用來存儲我們的代碼和定義的一些常量數據。當Boot0和Boot1都接GND時,芯片從主存儲器的起始地址0x0800 0000開始運行代碼。
  • ? 信息

系統存儲器中存儲的是啟動程序代碼。啟動程序就是串口下載的代碼。當Boot0接VCC,Boot1接GND時,運行的就是系統存儲器中的代碼。系統存儲器中存儲的啟動代碼,是ST公司在芯片出廠時就已經下載好的,用戶無法修改。選擇字節是用來配置寫保護和杜保護功能。

  • ? 閃存存儲器接口寄存器 閃存存儲器接口寄存器,是整個閃存的控制機構,里面包含了很多的閃存的控制寄存器和狀態寄存器。

在執行閃存寫操作時,任何對閃存的讀操作都會被鎖住。只有對閃存的寫操作結束后,讀操作才能夠正常執行。也就是說,在對閃存進行寫操作或者擦除操作時,無法對閃存進行讀操作。

三、Flash操作步驟

  • ? 解鎖和鎖定

  • ? 寫/擦除操作

  • ? 獲取Flash狀態

  • ? 等待操作完成

  • ? 讀取Flash指定地址數據

    四、程序設計

    操作內部Flash時,最小單位是半字(16位)。

    44.1 讀取數據

    讀取數據用的是指針的方式,在之前博主的文章中有關于如何利用指針在指定地址讀寫數據的操作。 ```c /*

*============================================================================== *函數名稱:Med_Flash_ReadHalfWord *函數功能:讀取指定地址的半字(16位數據) *輸入參數:faddr:讀取地址 *返回值:對應讀取地址數據 *備 注:對內部Flash的操作是以半字為單位,所以讀寫地址必須是2的倍數 *============================================================================== */ vu16 Med_Flash_ReadHalfWord (u32 faddr) { return (vu16)faddr; }

```c
/*
 *==============================================================================
 *函數名稱:Med_Flash_Read
 *函數功能:從指定地址開始讀出指定長度的數據
 *輸入參數:ReadAddr:讀取起始地址;pBuffer:數據指針;
                        NumToRead:讀取(半字)數
 *返回值:無
 *備  注:對內部Flash的操作是以半字為單位,所以讀寫地址必須是2的倍數
 *==============================================================================
 */
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個字節.
        ReadAddr += 2;   // 偏移2個字節. 
    }
}

4.2 寫入數據(不檢查)

這里的不檢查,是指在寫入之前,不檢查寫入地址是否可寫。

/*
 *==============================================================================
 *函數名稱:Med_Flash_Write_NoCheck
 *函數功能:不檢查的寫入
 *輸入參數:WriteAddr:寫入起始地址;pBuffer:數據指針;
                        NumToWrite:寫入(半字)數
 *返回值:無
 *備  注:對內部Flash的操作是以半字為單位,所以讀寫地址必須是2的倍數
 *==============================================================================
 */
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 寫入數據(檢查)

/*
 *==============================================================================
 *函數名稱:Med_Flash_Read
 *函數功能:從指定地址開始寫入指定長度的數據
 *輸入參數:WriteAddr:寫入起始地址;pBuffer:數據指針;
                        NumToRead:寫入(半字)數
 *返回值:無
 *備  注:對內部Flash的操作是以半字為單位,所以讀寫地址必須是2的倍數
 *==============================================================================
 */

// 根據中文參考手冊,大容量產品的每一頁是2K字節
#if STM32_FLASH_SIZE < 256
    #define STM32_SECTOR_SIZE   1024   // 字節
#else 
    #define STM32_SECTOR_SIZE   2048
#endif

// 一個扇區的內存
u16 STM32_FLASH_BUF[STM32_SECTOR_SIZE / 2];

void Med_Flash_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 / STM32_SECTOR_SIZE;   // 扇區地址
    secoff = (offaddr % STM32_SECTOR_SIZE) / 2;   // 在扇區內的偏移(2個字節為基本單位)
    secremain = STM32_SECTOR_SIZE / 2 - secoff;   // 扇區剩余空間大小
    
    if (NumToWrite <= secremain)
    {
        secremain = NumToWrite;   // 不大于該扇區范圍
    }
    while (1) 
    {
        // 讀出整個扇區的內容
        Med_Flash_Read(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE,STM32_FLASH_BUF,STM32_SECTOR_SIZE / 2);
        
        // 校驗數據
        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);   // 擦除這個扇區
            
            // 復制
            for (i = 0;i < secremain;i ++)
            {
                STM32_FLASH_BUF[i + secoff] = pBuffer[i];   
            }
            // 寫入整個扇區
            Med_Flash_Write_NoCheck(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE,STM32_FLASH_BUF,STM32_SECTOR_SIZE / 2);
        }
        else
        {
            // 寫已經擦除了的,直接寫入扇區剩余區間
            Med_Flash_Write_NoCheck(WriteAddr,pBuffer,secremain);
        }
        
        if (NumToWrite == secremain)
        {
            break;   // 寫入結束了
        }
        // 寫入未結束
        else
        {
            secpos ++;   // 扇區地址增1
            secoff=0;   // 偏移位置為0   
            pBuffer+=secremain;   // 指針偏移
            WriteAddr+=secremain;   // 寫地址偏移    
            NumToWrite-=secremain;   // 字節(16位)數遞減
            if (NumToWrite >(STM32_SECTOR_SIZE/2))
            {
                secremain=STM32_SECTOR_SIZE/2;   // 下一個扇區還是寫不完
            }
            else
            {
                secremain=NumToWrite;   // 下一個扇區可以寫完了
            }
        }  
    } 
    FLASH_Lock();   // 上鎖
}

宏定義如下

// STM32的Flash容量,單位為KB
#define STM32_FLASH_SIZE   512

// FLASH主存儲塊起始地址
#define STM32_FLASH_BASE   0x08000000

上面的讀取數據和不檢查的寫入都比較簡單,因此并沒有再做分析。這里分析一下帶檢查的寫入的程序設計思路。

  • ? 首先用一小段條件編譯來區分一下大容量產品和其他產品。因為大容量產品的一頁(一個扇區)是2K字節,中小容量產品的一頁是1K字節。定一個了一個數組,數組大小是一個扇區的大小。
// 根據中文參考手冊,大容量產品的每一頁是2K字節
#if STM32_FLASH_SIZE < 256
    #define STM32_SECTOR_SIZE   1024   // 字節
#else 
    #define STM32_SECTOR_SIZE   2048
#endif

// 一個扇區的內存
u16 STM32_FLASH_BUF[STM32_SECTOR_SIZE / 2];

大容量產品,一個扇區2K字節,除以2是因為在對內部Flash操作時,最小單位是半字。

  • ? 接下來,判斷要寫入的地址是否合法,也就是是否在主存儲塊地址范圍內。
// 判斷寫入地址是否在合法范圍內
    if (WriteAddr < STM32_FLASH_BASE || (WriteAddr >= (STM32_FLASH_BASE + 1024 * STM32_FLASH_SIZE)))
    {
        return;   // 非法地址
    }
  • ? 如果要寫入的地址合法,那么解鎖后計算一些參數值。
offaddr = WriteAddr - STM32_FLASH_BASE;   // 實際偏移地址

實際偏移地址 ,指的是要寫入的地址與主存儲塊基地址(0x0800 0000)的差值。

secpos = offaddr / STM32_SECTOR_SIZE;   // 扇區地址

扇區地址指的是要寫入的地址所在扇區前面的扇區數。由于所有的參數都不是浮點型,因此在做除法時,小數位都是0。最終除出來的結果就是當前扇區前面的扇區數。

secoff = (offaddr % STM32_SECTOR_SIZE) / 2;   // 在扇區內的偏移(2個字節為基本單位)

在扇區內的偏移指的是要寫入的地址與其所在扇區首地址的差值。用要寫入的地址取余每一個扇區的字節數,余數就是偏移地址。但是由于操作內部Flash時的最小單位是半字,因此要除以2。

secremain = STM32_SECTOR_SIZE / 2 - secoff;   // 扇區剩余空間大小

扇區內剩余空間大小只需要用扇區總的空間大小減去偏移地址即可得到。但是需要注意的是,單位都是半字。這里的剩余空間大小,并不是真正的剩余空間大小。而是指寫入地址后面的扇區大小。這里不太好理解,畫一個圖表示一下

圖片

扇區內剩余空間大小示意圖

正是因為這里的扇區剩余空間大小并不是指真正的剩余空間大小。在剩余空間內,也可能存在已經寫入數據的地址。所以后面需要進行判斷,來確定是否需要擦除。

  • ? 判斷在寫入地址所在扇區能否將寫入內容全部寫入完成
if (NumToWrite <= secremain)
    {
        secremain = NumToWrite;   // 不大于該扇區范圍
    }

如果可以,直接將要寫入的半字數賦值給當前扇區剩余空間大小。如果當前扇區剩余空間大小可以容納要寫入的半字數,那么只需要寫入一次即可,在后續判斷是否寫完時,直接通過,while循環只執行一次。

  • ? 讀出整個扇區內容,判斷是否需要擦除
// 讀出整個扇區的內容
        Med_Flash_Read(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE,STM32_FLASH_BUF,STM32_SECTOR_SIZE / 2);
        
        // 校驗數據
        for (i = 0;i < secremain;i ++)
        {
            // 需要擦除 
            if (STM32_FLASH_BUF[secoff + i] != 0XFFFF)
            {
                break; 
            }    
        }

要對內部Flash某個地址寫入數據時,需要確保該地址數值為0xFFFF。判斷方法就是從扇區內的偏移開始,利用for循環判斷讀出地扇區剩余空間內,是否存在已經被寫入內容的地址。for循環找到i的值,i加上在扇區內的偏移加1之后的空間,才是真正的扇區剩余空間大小。

for循環結束后,判斷是否需要進行擦除

// 需要擦除
        if (i < secremain)
        {
            FLASH_ErasePage(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE);   // 擦除這個扇區
            
            // 復制
            for (i = 0;i < secremain;i ++)
            {
                STM32_FLASH_BUF[i + secoff] = pBuffer[i];   
            }
            
            // 寫入整個扇區
            Med_Flash_Write_NoCheck(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE,STM32_FLASH_BUF,STM32_SECTOR_SIZE / 2);
        }
        else
        {
            // 寫已經擦除了的,直接寫入扇區剩余區間
            Med_Flash_Write_NoCheck(WriteAddr,pBuffer,secremain);
        }

擦除時,最小單元為一個扇區。在大容量產品中,也就是2048字節。

  • ? 最后,將需要寫入的數據,寫入到對應位置。如果是需要擦除的情況,寫入時是先將原來的內容提取出來,然后在后面填充上需要寫入的內容,擦除整個扇區之后再一起寫入。如果是不需要擦除的情況,直接寫入即可。

    五、注意事項

    在操作Flash時,注意不要對代碼區內容進行擦寫。如果擦寫的地址在代碼區,會導致程序運行異常。那么如何確保我們操作的地址不是在代碼區?這就需要我們知道我們的代碼所占的內存是多少。在Keil5編譯完成后,會顯示下面的內容

圖片

keil5編譯后提示

  • ? Code 程序所占用的內存大小(存放在Flash中)
  • ? RO-data 程序定義的常量所占內存大小(存放在Flash中)
  • ? RW-data 已被初始化的全局變量所占內存大小(在程序初始化的時候,RW-data會從FLASH中拷貝到RAM中)

ZI-data 未被初始化的全局變量所占內存大小(存放在RAM中)

最后,計算程序代碼所占Flash空間。flash = Code + RO-data + RW-data

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • RAM
    RAM
    +關注

    關注

    8

    文章

    1367

    瀏覽量

    114531
  • STM32
    +關注

    關注

    2266

    文章

    10871

    瀏覽量

    354806
  • FLASH閃存
    +關注

    關注

    0

    文章

    7

    瀏覽量

    7603
  • 快閃存儲器
    +關注

    關注

    0

    文章

    15

    瀏覽量

    11158
  • for循環
    +關注

    關注

    0

    文章

    61

    瀏覽量

    2493
收藏 人收藏

    評論

    相關推薦

    STM32入門學習筆記之外置FLASH讀寫實驗(上)

    Flash,全名叫做Flash EEPROM Memory,即平時所說的“閃存”,它結合了ROM和RAM的長處,不僅可以反復擦除,還可以快速讀取數據,STM32運行的程序其實就是存放在
    的頭像 發表于 02-16 14:10 ?8877次閱讀
    <b class='flag-5'>STM32</b>入門學習<b class='flag-5'>筆記</b>之外置<b class='flag-5'>FLASH</b>讀寫實驗(上)

    STM32入門學習筆記之外置FLASH讀寫實驗(下)

    Flash,全名叫做Flash EEPROM Memory,即平時所說的“閃存”,它結合了ROM和RAM的長處,不僅可以反復擦除,還可以快速讀取數據,STM32運行的程序其實就是存放在
    的頭像 發表于 02-16 14:13 ?1455次閱讀

    STM32學習筆記-Flash做為存儲器儲存數據

    的時候更容易操作。FPEC FPEC(FLASH Program/Erase controller 閃存編程/擦除控制器),STM32通過FPEC來擦除和編程FLASH。FPEC使用7
    發表于 10-07 15:55

    STM32速成

    喲管STM32速成貼?求助
    發表于 01-23 14:34

    STM32F0x HAL庫學習筆記分享

    在此聲明——本文摘自這里:【碼神島】STM32F0x HAL庫學習筆記(5)片內FLASH的讀寫操作本文開發環境MCU型號:STM32F103C8T6IDE環境: MDK 5.25代碼
    發表于 01-26 07:33

    flash_erase無法無錯誤地擦除某些閃存頁面怎么解決?

    擦除。現在的問題是,第0頁和第12頁無法無誤地擦除。代碼如下(與第 12 頁相同):閃存擦除(0,1);從 flash_erase() 到 -->HAL_
    發表于 12-06 06:39

    Flash閃存有哪些類型,Flash閃存分類

    Flash閃存有哪些類型 Flash閃存是非易失性存儲器,這是相對于SDRAM等存儲器所說的。即存儲器斷電后,內部的數據仍然可以保存。Flash
    發表于 03-25 16:26 ?1.2w次閱讀

    STM32F2技術培訓_片上閃存Flash

    STM32F2技術培訓_片上閃存Flash
    發表于 12-03 17:35 ?0次下載

    閃存(Flash)

    LPC1138閃存(Flash),有需要的下來看看。
    發表于 01-13 16:45 ?20次下載

    你真的了解Flash閃存嗎?Flash閃存具備哪些類型?

    對于Flash,大家應該并不陌生。但是請注意哦,這里談及的Flash不是動畫播放格式,這里的Flash指的是Flash閃存。在這篇文章中,小
    的頭像 發表于 11-06 17:36 ?7778次閱讀
    你真的了解<b class='flag-5'>Flash</b><b class='flag-5'>閃存</b>嗎?<b class='flag-5'>Flash</b><b class='flag-5'>閃存</b>具備哪些類型?

    STM32閃存編程手冊

    STM32閃存編程手冊(嵌入式開發和編程)-STM32閃存編程手冊?STM32開發必備技術文檔之一
    發表于 08-04 12:44 ?51次下載
    <b class='flag-5'>STM32</b><b class='flag-5'>閃存</b>編程手冊

    STM32學習心得三十三:FLASH閃存編程原理與實驗

    記錄一下,方便以后翻閱~主要內容:1) STM32 Flash操作介紹;2) 寄存器和庫函數介紹;3) 相關實驗代碼解讀。參考資料:《STM32F10xxx閃存編程參考手冊》實驗
    發表于 11-26 17:51 ?27次下載
    <b class='flag-5'>STM32</b>學習心得三十三:<b class='flag-5'>FLASH</b><b class='flag-5'>閃存</b>編程原理與實驗

    HAL庫之讀寫STM32F103內部的FLASH空間

    在此聲明——本文摘自這里:【碼神島】STM32F0x HAL庫學習筆記(5)片內FLASH的讀寫操作本文開發環境MCU型號:STM32F103C8T6IDE環境: MDK 5.25代碼
    發表于 12-01 20:51 ?23次下載
    HAL庫之讀寫<b class='flag-5'>STM32</b>F103內部的<b class='flag-5'>FLASH</b>空間

    STM32F103學習筆記——SPI讀寫Flash(三)

    ??此系列文章是小白學習STM32的一些學習筆記。小白第一次寫筆記文章,有不足或是錯誤之處,請多體諒和交流!目錄1.FLASH指令編碼表2.讀取FL
    發表于 12-22 19:29 ?11次下載
    <b class='flag-5'>STM32</b>F103學習<b class='flag-5'>筆記</b>——SPI讀寫<b class='flag-5'>Flash</b>(三)

    STM32F103學習筆記——SPI讀寫Flash(四)

    ??此系列文章是小白學習STM32的一些學習筆記。小白第一次寫筆記文章,有不足或是錯誤之處,請多體諒和交流!目錄1.main函數編寫2.下載驗證1.main函數編寫??編寫主函數,對FLASH
    發表于 12-22 19:32 ?6次下載
    <b class='flag-5'>STM32</b>F103學習<b class='flag-5'>筆記</b>——SPI讀寫<b class='flag-5'>Flash</b>(四)