STM32 | 使用STM32內部Flash額外的空間來存儲數據

STM32 芯片內部的 FLASH 存儲器,主要用於存儲我們代碼。如果內部FLASH存儲完我們的代碼還有剩餘的空間,那麼這些剩餘的空間我們就可以利用起來,存儲一些需要掉電保存的數據。

本文以STM32103ZET6爲例。STM32103ZET6屬於大容量產品,其閃存模塊組織如下:

======001

其主存儲器大小爲512KB,分爲256頁,每頁大小都爲2KB。我們的程序一般默認燒寫到第0頁的起始地址(0x08000000)處。當BOOT0引腳和BOOT1引腳都接GND時,就是從這個地址開始運行代碼的。這個地址在keil中可以看到:

======002

假如我們要下載的程序大小爲4.05KB,則第0、1、2頁用於保存我們的程序,我們需要掉電保存的數據只能保存在第3頁至第255頁這一部分空間內。我們最終要下載的程序大小可在工程對應的.map文件中看到。.map文件可以雙擊工程的Target的名字快速打開,如:

======003

下面對STM32的內部FLASH進行簡單的讀寫測試:
STM32的內部FLASH讀寫測試

過程圖如下(省略異常情況,只考慮成功的情況):

======004

示例代碼:

本例的關鍵代碼如下:

/*******************************************************************************************************                                
*------------------------------------------STM32 Demo---------------------------------------------------
*
*       工程說明:STM32內部FLASH實驗
*    作    者:ZhengNian
*     博    客:zhengnianli.github.io
*    公 衆 號:嵌入式大雜燴
*
********************************************************************************************************/
#define MAIN_CONFIG
#include "config.h"

/* STM32F103ZET6有256頁,每一頁的大小都爲2KB */
#define ADDR_FLASH_PAGE_255     ((uint32_t)0x0807F800) /* Page255 2KB */

/* FLASH讀寫測試結果 */
#define  TEST_ERROR       -1    /* 錯誤(擦除、寫入錯誤) */
#define  TEST_SUCCESS  0    /* 成功 */
#define  TEST_FAILED   1    /* 失敗 */

/* Flash讀寫測試buf */
#define BufferSize 6
uint16_t usFlashWriteBuf[BufferSize] = {0x0101,0x0202,0x0303,0x0404,0x0505,0x0606};
uint16_t usFlashReadBuf[BufferSize] = {0};

/* 供本文件調用的函數聲明 */
static int FlashReadWriteTest(void);                       

/*******************************************************************************************************
** 函數: main
**------------------------------------------------------------------------------------------------------
** 參數: void
** 返回: 無
** 說明: 主函數
********************************************************************************************************/
int main(void)
{    
    /* 上電初始化 */
    SysInit();
    
    /* 內部Flash讀寫測試 */
    if (TEST_SUCCESS == FlashReadWriteTest())
    {
        printf("Flash test success!\n");
    }
    else
    {
        printf("Flash test failed!\n");
    }

    while (1)
    {}
}

/*******************************************************************************************************
** 函數: FlashReadWriteTest, 內部Flash讀寫測試函數
**------------------------------------------------------------------------------------------------------
** 參數: void
** 返回: TEST_ERROR:錯誤(擦除、寫入錯誤)  TEST_SUCCESS:成功   TEST_FAILED:失敗
** 說明: 無
********************************************************************************************************/
static int FlashReadWriteTest(void)
{
    uint32_t ucStartAddr;
    
    /* 解鎖 */
    FLASH_Unlock();    
    
    /* 擦除操作 */
    ucStartAddr = ADDR_FLASH_PAGE_255;
    if (FLASH_COMPLETE != FLASH_ErasePage(ucStartAddr))
    {
        printf("Erase Error!\n");
        return TEST_ERROR;
    }
    else
    {
        ucStartAddr = ADDR_FLASH_PAGE_255;
        printf("擦除成功,此時FLASH中值爲:\n");
        for (int i = 0; i < BufferSize; i++)
        {
            usFlashReadBuf[i] = *(uint32_t*)ucStartAddr;
            printf("ucFlashReadBuf[%d] = 0x%.4x\n", i, usFlashReadBuf[i]);
            ucStartAddr += 2;
        }
    }
    
    /* 寫入操作 */
    ucStartAddr = ADDR_FLASH_PAGE_255;
    printf("\n往FLASH中寫入的數據爲:\n");
    for (int i = 0; i < BufferSize; i++)
    {
        if (FLASH_COMPLETE != FLASH_ProgramHalfWord(ucStartAddr, usFlashWriteBuf[i]))
        {
            printf("Write Error!\n");
            return TEST_ERROR;
        }
        printf("ucFlashWriteBuf[%d] = 0x%.4x\n", i, usFlashWriteBuf[i]);
        ucStartAddr += 2;
    }
    
    /* 上鎖 */
    FLASH_Lock();
    
    /* 讀取操作 */
    ucStartAddr = ADDR_FLASH_PAGE_255;
    printf("\n從FLASH中讀出的數據爲:\n");
    for (int i = 0; i < BufferSize; i++)
    {
        usFlashReadBuf[i] = *(__IO uint16_t*)ucStartAddr;
        printf("ucFlashReadBuf[%d] = 0x%.4x\n", i, usFlashReadBuf[i]);
        ucStartAddr += 2;
    }
    
    /* 讀出的數據與寫入的數據做比較 */
    for (int i = 0; i < BufferSize; i++)
    {
        if (usFlashReadBuf[i] != usFlashWriteBuf[i])
        {
            return TEST_FAILED;
        }
    }
    
    return TEST_SUCCESS;
}


/*********************************************************************************************************
**                            End Of File
********************************************************************************************************/

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131

(1)進行解鎖操作

STM32 的閃存編程是由內嵌的閃存編程/擦除控制器(FPEC)管理 ,這個模塊包含的寄存器如下:

======005

STM32 復位後, FPEC 模塊是被保護的, 不能寫入 FLASH_CR 寄存器; 通過寫入特定的序列到 FLASH_KEYR 寄存器可以打開 FPEC 模塊(即寫入 KEY1 和KEY2) , 只有在寫保護被解除後, 我們才能操作相關寄存器。 固件庫中的函數爲:

void FLASH_Unlock(void);

    1

(2)擦除將要寫的頁

STM32 的 FLASH 在編程的時候,也必須要求其寫入地址的 FLASH 是被擦除了的(也就是其值必須是 0XFFFF),否則無法寫入,在 FLASH_SR 寄存器的 PGERR 位將得到一個警告。 STM32 的閃存擦除分爲兩種:頁擦除和整片擦除。 也就是其最小擦除單位爲1頁,儘管你只需往某頁裏寫10個字節數據或者更少的數據,你也必須先擦除該頁(2*1024個字節)。我們這裏使用按頁擦除,固件庫中按頁擦除的函數爲:

FLASH_Status FLASH_ErasePage(uint32_t Page_Address);

    1

其返回值爲枚舉:

typedef enum
{
  FLASH_BUSY = 1,    /* 忙 */
  FLASH_ERROR_PG,   /* 編程錯誤 */
  FLASH_ERROR_WRP,  /* 寫保護錯誤 */
  FLASH_COMPLETE,   /* 操作完成 */
  FLASH_TIMEOUT     /* 操作超時 */
}FLASH_Status;

    1
    2
    3
    4
    5
    6
    7
    8

(3)往上一步擦寫成功的頁寫入數據

STM32 閃存的編程每次必須寫入16 位。雖然固件庫中有如下三個寫操作的函數:

FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data);
FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);
FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data);

    1
    2
    3

分別爲按字(32bit)寫入、按半字(16bit)寫入、按字節(8bit)寫入函數。32 位字節寫入實際上是寫入的兩次 16 位數據,寫完第一次後地址+2,這與我們前面講解的 STM32 閃存的編程每次必須寫入 16 位並不矛盾。 寫入 8
位實際也是佔用的兩個地址了,跟寫入 16 位基本上沒啥區別。

(4)寫入操作完成後進行上鎖操作

對FLASH進行寫操作完成後要進行上鎖操作,對應的固件庫中函數爲:

void FLASH_Lock(void);

    1

(5)讀出數據

固件庫中並沒有與讀操作的函數。讀操作其實就是讀取FLASH某個地址的數據。

(6)對比寫入的數據與讀出的數據是否相等

最後對比我們寫入的數據與讀出的數據是否完全一致,若一致則表明讀寫測試成功,否則失敗。

程序執行結果:

======006

可見,讀出的數據與寫入的數據一致,表明讀寫測試成功。
————————————————
版權聲明:本文爲CSDN博主「嵌入式大雜燴」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/zhengnianli/article/details/102555964

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章