4. 使用const关键字将常量放入Flash
任务目标
在嵌入式C语言开发中,默认情况下字符串字面量和未加限定符的全局变量会占用RAM。本案例通过将只读数据定义为const,使其在编译后存放于Flash(代码存储区),从而减少RAM占用。完成本案例后,你将能够通过Map文件或链接脚本确认常量已从RAM迁移至Flash。
环境与依赖
- 硬件:任意ARM Cortex-M系列开发板(如STM32F103C8T6)
- 工具链:arm-none-eabi-gcc + Makefile(或使用STM32CubeIDE)
- 调试器:ST-Link / J-Link(用于查看内存分布)
关键完整代码
/* main.c - 演示const数据存放于Flash而非RAM */
#include <stdio.h>
#include <stdint.h>
/* 非const全局变量:存放在RAM(.data段) */
uint32_t g_ram_counter = 100;
/* const全局变量:存放在Flash(.rodata段) */
const uint32_t g_flash_version = 0x01020304;
/* const字符串字面量:存放在Flash */
const char g_welcome_msg[] = "Hello from Flash!";
/* const数组:存放在Flash */
const uint8_t g_sine_table[16] = {
0, 25, 50, 74, 98, 120, 141, 160,
177, 191, 203, 212, 219, 223, 225, 225
};
int main(void)
{
uint32_t sum = 0;
/* 读取Flash中的常量 */
sum += g_flash_version & 0xFF;
sum += g_welcome_msg[0];
sum += g_sine_table[7];
/* 使用g_ram_counter避免编译器优化移除 */
g_ram_counter += sum;
while (1) {
/* 空循环,保持程序运行 */
}
return 0;
}
编译与链接配置
使用以下链接脚本片段(或修改现有.ld文件),确保Flash区域包含只读数据段:
/* 在链接脚本中确认.rodata被放入Flash */
SECTIONS
{
.text :
{
*(.text*)
*(.rodata*)
*(.rodata.*)
} > FLASH
.data :
{
*(.data*)
} > RAM AT> FLASH
.bss :
{
*(.bss*)
} > RAM
}
运行与验证方法
- 编译:使用
arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -Tlink.ld main.c -o test.elf编译工程(根据实际芯片调整-mcpu参数)。 - 检查Map文件:编译时添加
-Wl,-Map=output.map生成Map文件,然后执行:
grep -E "g_flash_version|g_welcome_msg|g_sine_table|g_ram_counter" output.map
预期输出(示例,地址因芯片而异):
.g_ram_counter 0x20000000 0x4
.g_flash_version 0x08000050 0x4
.g_welcome_msg 0x08000054 0x10
.g_sine_table 0x08000064 0x10
观察地址:g_ram_counter位于0x20000000(RAM起始),而三个const变量均位于0x08000000(Flash起始),证明常量已存入Flash。
- 查看编译输出:在编译日志中确认
.rodata段大小。执行arm-none-eabi-size test.elf,观察text(含rodata)与data段大小差异。 - 通过条件:Map文件中所有
const修饰的全局变量地址落在Flash区域(如0x08000000~0x0801FFFF),且g_ram_counter地址在RAM区域(如0x20000000~0x2000FFFF),即判定通过。
常见问题
- const变量仍出现在RAM中:检查是否在函数内部定义了非静态
const局部变量——这类变量若未初始化会存放在栈上;若初始化则可能被编译器复制到RAM。确保使用static const或文件作用域const。 - 编译器优化掉未使用的const:若const变量未被引用,链接器可能将其丢弃。本案例中通过
sum累加引用了所有常量,避免被优化。 - 字符串字面量:直接使用
"string"字面量时,其存储位置由编译器决定(通常已在Flash)。显式定义为const char[]可确保存放于.rodata段。