STM32 | 使用STM32内部Flash额外的空间来存储数据

06/17 11:49
阅读数 25

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

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部