【STM32】RTT-Studio中HAL库开发教程九:FLASH中的OPT

文章目录

  • 一、概要
  • 二、内部FLASH排布
  • 三、内部FLASH主要特色
  • 四、OTP函数介绍
  • 五、测试验证
  • 一、概要

      STM32系列是一款强大而灵活的微控制器,它的片内Flash存储器可以用来存储有关代码和数据,在实际应用中,我们也需要对这个存储器进行读写操作。

      STM32的FLASH主存储块按页组织,有的产品每页1KB,有的能到2KB,页面典型的用途就是用于按页擦除FLASH,STM32F407的FLASH页大一点,能到16K,我们也叫做扇区。


    二、内部FLASH排布

    1. 根据用途,STM32片内的FLASH分成两部分:主存储块、信息块。
    2. 主存储块:用于存储程序,我们写的程序一般存储在这里,用户还可以存储数据。信息块又分成两部分:系统存储器、OTP、选项字节。
    3. 系统存储器存储用于存放在系统存储器自举模式下的启动程序(BootLoader),当使用ISP方式加载程序时,就是由这个程序执行。这个区域由芯片厂写入BootLoader,然后锁死,用户是无法改变这个区域的。
    4. OTP(One Time Program)区域,指的是只能写入一次的存储区域,容量为528字节,写入后数据就无法再更改,OTP常用于存储应用程序的加密密钥。
    5. 选项字节存储芯片的配置信息及对主存储块的保护信息,主要有写保护字节,读保护字节等。

    STM32F407产品主存储块512KB, 每个扇区16KB~128K大小不等,一共有7个扇区



    三、内部FLASH主要特色

  • 容量大的芯片可以高达 1 MB 容量
  • 128 位宽数据读取
  • 字节、半字、字和双字数据写入
  • 扇区擦除与批量擦除
  • 存储器构成
  •   Flash 结构如下:
      — 主存储器块,含 4 个 16 KB 扇区、1 个 64 KB 扇区 和 7 个 128 KB 扇区。
      — 系统存储器,器件在系统存储器自举模式下从该存储器自举。此区域为意法半导体预留,其中包含自举程序,用以通过以下接口之一对 Flash 进行重新编程:USART1、USART3、CAN2、USB OTG FS 设备模式(DFU:设备固件升级)。自举程序由 ST 在器件制造期间编写,用于防止误写/误擦除操作。
      — 512 OTP(一次性可编程)字节,用于存储用户数据。OTP 区域包含 16 个附加字节,用于锁定相应的 OTP 数据块。
      — 选项字节:读写保护、BOR 级别、软件/硬件看门狗以及器件在待机或停机模式下的复位。


    四、OTP函数介绍

    1. HAL_StatusTypeDef HAL_FLASH_Unlock(void);
      作用:解锁FLASH控制寄存器访问
      返回值:写寄存器的状态

    2. HAL_StatusTypeDef HAL_FLASH_Lock(void);
      作用:锁定FLASH控制寄存器访问
      返回值:写寄存器的状态

    3. HAL_StatusTypeDef HAL_FLASH_OB_Unlock(void);
      作用:解锁FLASH选项控制寄存器访问
      返回值:写寄存器的状态

    4. HAL_StatusTypeDef HAL_FLASH_OB_Unlock(void);
      作用:锁定FLASH选项控制寄存器访问
      返回值:写寄存器的状态

    5. HAL_StatusTypeDef HAL_FLASH_OB_Launch(void)
      作用:启动选项字节加载
      返回值:写寄存器的状态

    6. HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint64_t Data)
      作用通过选定的字节类型进行数据写入
      返回值:写寄存器的状态

    7. OPT数据读写过程代码:由于OPT是用户数据的一次性写入,所有需要确定写入的数据无误,不然存储的数据一旦写入,就无法再进行更改,但是可以进行无数次的读写。

    // 定义OPT区域的起始地址和结束地址
    #define OPT_START_ADDR 0x1FFF7800
    #define OPT_END_ADDR   0x1FFF7A0F
    
    /**
     * @brief 写入数据到OPT区域
     * @param data:需要写的用户数据
     * @param size:数据个数
     */
    void write_to_opt(uint32_t *data, uint32_t size)
    {
        uint32_t addr = OPT_START_ADDR;
        HAL_StatusTypeDef status;
    
        // 检查数据大小是否超过OPT区域的大小
        if ((size * 4) > (OPT_END_ADDR - OPT_START_ADDR))
        {
            return;
        }
    
        HAL_FLASH_Unlock();             // 解锁FLASH
        status = HAL_FLASH_OB_Unlock(); // 解锁选项字节区域
        HAL_FLASH_OB_Launch();          // 生效设置
    
        // 写入OPT数据
        for (uint32_t i = 0; i < size; i++)
        {
            // 按字写入数据
            status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr, data[i]);
            if (status != HAL_OK)
            {
                rt_kprintf("opt data write error!!!\n");
                break;
            }
            addr += 4;
        }
    
        // 锁定FLASH
        HAL_FLASH_OB_Lock();
        HAL_FLASH_Lock();
    }
    
    // 读取OPT区域的数据
    void read_from_opt(uint32_t *data, uint32_t size)
    {
        uint32_t addr = OPT_START_ADDR;
    
        for (uint32_t i = 0; i < size; i++)
        {
            data[i] = *(uint32_t*) addr;
            addr += 4;
        }
    }
    
    /**
     * @brief 设置OPT数据
     */
    int write_opt_data(int argc, char **argv)
    {
        if (argc != 2 && argc != 3)
        {
            return -RT_ERROR;
        }
        else
        {
            if (strcmp(argv[0], "opt") == 0)
            {
                if (argc == 3)
                {
                    if (strcmp(argv[1], "w") == 0)
                    {
                        int size = atoi(argv[2]);
                        uint32_t time[50] = {0};
                        if (atoi(argv[2]) <= 50)
                        {
                            read_from_opt(time, size);
                            for (int i = 0; i < size; ++i)
                            {
                                rt_kprintf("time[%d]:%u\n", i, time[i]);
                            }
                        }
                    }
                }
                else
                {
                    uint32_t time = (uint32_t)strtol(argv[1], NULL, 16);
                    rt_kprintf("time:%u(0x%X)\n", time, time);
                    write_to_opt(&time, 1);
                }
            }
            else
            {
                return -RT_ERROR;
            }
        }
    
        return RT_TRUE;
    }
    MSH_CMD_EXPORT_ALIAS(write_opt_data, opt, write_opt_data);
    

    五、测试验证

      通过下面的测试数据可以看到,当第一次写入数据之后,可以正常读取导数据,读取的数据是写入的数据。当第二次重新写入数据的时候,读取到的数据还是之前写入的数据,没有改变,因此说明OPT数据只会被写入一次,然后无法再次写入。


    作者:0南城逆流0

    物联沃分享整理
    物联沃-IOTWORD物联网 » 【STM32】RTT-Studio中HAL库开发教程九:FLASH中的OPT

    发表回复