STM32裸机开发(6) — Keil-MDK下散列文件的分析
一、什么是散列文件
我们可以看到,在编译过程中有多个.o文件,而最后生成的只是一个文件,那么这些文件要怎么以什么方式生成一个文件呢?说的专业一点,这个过程就是链接,而在Keil-MDK下就是使用散列文件来指导链接的。
如图所示,将【Use Memory Layout from Target Dialog】勾选上
然后重新编译,我们就可以在【Objects】目录下得到一个.sct
文件如下所示
二、散列文件的格式
如下所示,一个散列文件由一个或多个Load region
组成,一个Load region
中含有一个或多个Execution region
,一个Execution region
中又含有一个或多个Input section
。
首先,LR_IROM1
是Load region的区域名,紧接着0x08000000
是其加载地址,然后0x00080000
是其区域最大容量;
然后,ER_IROM1
是Execution region的区域名,紧接着0x08000000
是其可执行地址,也可以叫链接地址,然后0x00080000
也是其区域最大容量;
接下来就是各种文件的链接方式了,解释如下
LR_IROM1 0x08000000 0x00080000 { ; load region size_region
ER_IROM1 0x08000000 0x00080000 { ; load address = execution address
*.o (RESET, +First) ;所有的.o文件里的RESET段抽取出来放在最开始的位置
*(InRoot$$Sections) ;所有的文件包括库,keil添加的可执行文件,看不到源码
.ANY (+RO) ;等同于*,优先级比*低,这里表示所有的只读数据段
.ANY (+XO) ;这里表示所有的只可执行段
}
RW_IRAM1 0x20000000 0x00010000 { ; RW data
.ANY (+RW +ZI) ;所有的可读可写数据段
}
}
三、分析散列文件
打开uart.dis
文件,可以看到有个__main,这就是keil添加的可执行代码,因为我们使用了main函数,所以这段代码被添加进去了,
我们将我们代码中的main改为mymain
,可以看到,这段代码没有了
我们在main.c中添加如下所示变量定义,
然后编译,打开uart.dis
文件,可以看到,myconst
变量被链接到0x08…开头的地址,即ER_IROM1 区域,而my
、mydate
和myzero
即被链接到0x20…开头的地址,即RW_IRAM1 区域;与之相对应的,ER_IROM1 区域为该STM32的Flash地址,RW_IRAM1 区域为该STM32的RAM地址。
然后我们再看一下,uart.dis
文件中,ER_IROM1 区域的最后一个位置0x080001ac
存放的即我们定义的只读变量myconst
但我们使用STM32Cubeprg打开uart.hex
文件可以看到,在0x080001ac后还有三个位置,而这三个位置存放的刚好就是我们定义的三个my
、mydate
和myzero
变量。
实际上,对于在STM32F103这类资源紧缺的单片机芯片中:
- 代码段保存在Flash上,直接在Flash上运行(当然也可以重定位到内存里)
- 数据段暂时先保存在Flash上,然后在使用前被复制到内存里(只读数据段不复制)
四、验证数据段的存放
修改main.c
如下所示
#include "uart.h"
#include "led.h"
int mydata = 0x12315;
const int myconst = 0x22315;
int myzero = 0x0;
int my;
int mymain(void)
{
uart_init();
led_init();
putstring("stm32f103zet6\r\n");
putstring("mydata\t:");
puthex((unsigned int)mydata);
putstring("\r\nmyconst\t:");
puthex((unsigned int)myconst);
putstring("\r\nmyzero\t:");
puthex((unsigned int)myzero);
putstring("\r\nmy\t:");
puthex((unsigned int)my);
putstring("\r\n");
while(1)
{
putstring("led on\r\n");
led_on();
delay(1000000);
putstring("led off\r\n");
led_off();
delay(1000000);
}
}
编译烧录运行,可以看到,只有myconst
变量值是正确的,其他三个变量值都是错误的,这是因为我们并没有将那三个变量的值从Flash上复制到内存里,所以读取出来的只是上电后该RAM地址的一个随机值。
然后再添加一下地址的打印
编译烧录运行,可以看到,确实只有myconst
变量值是保存在Flash的,其他三个变量值都是在内存里的
另外我们也可以顺便看一下栈地址,在mymain函数里定义一个变量并打印其地址
int val = 3;
putstring("val\t:");
puthex((unsigned int)val);
puthex((unsigned int)&val);
putstring("\r\n");
编译烧录运行,可以看到,其地址确实在我们设置的栈中
五、附录
上一篇:STM32裸机开发(5) — 在Keil-MDK下编写uart串口打印程序
下一篇:STM32裸机开发(7) — 复制data段和清除BSS段(ZI段)
代码存放:https://gitee.com/william_william/stm32f103_noos/tree/master/keil-mdk/03_links