• 正文
  • 相关推荐
申请入驻 产业图谱

如何在MCU指定Flash地址存入特定数据?

01/06 15:47
434
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

部分终端产品在出厂前需要在MCU Flash特定的地址处写入特定的数据,该数据可能是产品序列号、版本号或特定的配置参数等信息。比如我们希望在Flash 0x08030000地址处写入4个字节的数据0x11、0x22、0x33、0x44,如何实现该功能呢?

方法一、直接写Flash

按照MCU写Flash的方式,程序中先擦除地址对应的页,然后按字写入对应的内容即可。此方法需要增加一段操作flash的代码,不太方便,不推荐使用。

方法二、直接改写hex文件或者bin文件

hex文件需要熟悉其文件格式,bin文件也要计算文件大小,理论上可以实现,但是也比较麻烦,不推荐使用。

方法三、离线烧录器配置

如果使用离线烧录器的话,比如创芯工坊,可以在上位机的序列号配置栏中进行配置,注意序列号增量要改为0。

这种方式相比前两种方式要简单很多,但是它只能配置一个参数,如果有多个参数需要写入那就无法实现了。

方法四、分散加载文件实现

通过修改分散加载文件的方法,可以实现该功能。以常用的KEIL MDK为例。

假设默认的sct文件如下:

; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************
LR_IROM1 0x08000000 0x00040000 { ; load region size_region
  ER_IROM1 0x08000000 0x00040000 { ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
   .ANY (+XO)
}
  RW_IRAM1 0x20000000 0x00007000 { ; RW data
.ANY (+RW +ZI)
}
}

修改如下:

LR_IROM1 0x08000000 0x00030000 { ; load region size_region
  ER_IROM1 0x08000000 0x00030000 { ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
   .ANY (+XO)
}
  RW_IRAM1 0x20000000 0x00007000 { ; RW data
.ANY (+RW +ZI)
}
}

LR_IROM2 0x08030000 0x00010000 { ; load region size_region
  ER_IROM2 0x08030000 0x00010000 { ; user data
   *(myflash)
}
}

main.c文件中增加如下语句即可:const uint8_t my_flash_table[] __attribute__((used,section("myflash"))) = { 0x11,0x22,0x33,0x44 };

这样hex文件里就写入了相应的值:

原始hex文件如下:

注意一定要写上used,用 used 防止被链接器剔除。如果这么写:const uint8_t my_flash_table[] __attribute__((section("myflash"))) = { 0x11,0x22,0x33,0x44 };

编译就会报如下warning,实际是没有生效。

方法五、直接绝对定位实现

这是最直接的方法,如果用的是Arm Compiler 5 (AC5),可以直接使用

attribute((at(address)))或者 attribute((section(".ARM.__at_address")))。如果你的项目已经迁移到Arm Compiler 6 (AC6),则必须使用 attribute((section(".ARM.__at_address"))),因为AC6不再支持原始的 at属性。

根据使用的是AC5或AC6,main.c文件中增加如下语句即可:

const uint32_t gflashdata __attribute__((at(0x08030000))) = 0x44332211; 

或者

const uint32_t gflashdata __attribute__((section(".ARM.__AT_0x8030000"))) = 0x44332211;

hex文件变为如下:

和方法四不同的是0x08030000之前其他未使用的Flash地址会被填充为0x00,这是因为链接器在生成输出镜像时的行为,它会把映像从最小地址扩展到最高已定义地址;中间没有明确内容的“空洞”会被填充为 0x00。

可为什么地址0x08030004-0x08030007也被修改了呢?这四个字节的数据是0x00、0x24、0xF4、0x00。 从map文件可以看到,是因为链接器在该地址放置了 .data 的初始内容。

对比不加入const uint32_t gflashdata attribute((at(0x08030000))) = 0x44332211; 原始的map文件:

引入这句话改变了Load base。在Keil MDK的map文件中,RW_IRAM1的 Load base(加载基地址)指的是存储在Flash中的初始数据镜像的起始地址。这些数据(主要是已初始化的全局变量)在程序上电后,需要被复制到RAM中的执行地址(Exec Addr)才能被正确使用。

 

其实0x00、0x24、0xF4、0x00 对应的代码中SystemCoreClock这个全局变量的值。

以上介绍的内容,希望对大家有所帮助。

关注我们,扫码加入嵌入式交流群:

相关推荐

登录即可解锁
  • 海量技术文章
  • 设计资源下载
  • 产业链客户资源
  • 写文章/发需求
立即登录