MDK __main()代码执行过程分析

本文详细解析了嵌入式程序从加载到运行的过程,包括__scatterload()函数如何处理RW/RO段的复制及ZI段的初始化,以及__rt_entry()函数如何进行堆栈初始化并最终跳转到main()函数。
AI助手已提取文章相关产品:

版权声明:原创作品,允许转载,转载时请务必以超链接形式标明文章原始出版、作者信息和本声明。http://blog.youkuaiyun.com/wfq0624


1.1__main()代码执行分析

以keyled程序为例说明,keyled代码请参考我的博客网址:http://my.youkuaiyun.com/wfq0624/code/detail/7645

程序经过汇编启动代码,执行到__main()后,可以看出有两个大的函数:

__scatterload():负责RW/RO输出段从装载域地址复制到运行域地址,并完成了ZI运行域的初始化工作。

__rt_entry():负责初始化堆栈,完成库函数的初始化,最后自动跳转向main()函数。

分析__scatterload()函数

执行到__main(),先跳转到_scatterload下图红框框中代码所示,执行完后,R10和R11就被赋给成了下面两个值。

Map文件中的symbol

Region$$Table$$Base 0x00000394 Number 0anon$$obj.o(Region$$Table)

Region$$Table$$Limit 0x000003b4 Number 0anon$$obj.o(Region$$Table)


然后执行_scatterload_null代码,将R10对应地址存放的的4个字copy到R0~R3中,可以看出

R0:0x1000表示的是keyled.o加载域起始地址

R1:0x30000100为keyled.o运行域地址

R2:0X160为copy的大小,keyled.o的大小从map文件中得知就是0x160 Byte

R3:0X1E4 是_scatterload_copy 代码的起始地址,实用BXR3 就能跳转到_scatterload_copy来复制代码。

跳到_scatterload_copy,开始copy,循环0x16次,每次搬移4个字(16Byte),共搬移0x16*0x10=0x160


复制完keyled.o代码后,进一步循环到_scatterload_null准备好,ZI段需要清零的地址和范围

执行完这个循环后

R1:0x30050000 为ZI段的起始地址

R2:0x618为ZI段大小,换成十进制是1560.从map文件得知ZI大小就是1560Byte

R3:0x20c 为_scatterload_zeroinit 的地址


执行下面红框框中循环体,共清零0x610Byte范围,然后再执行蓝框框中代码,清零8Byte,总共0x618

ZI段清零(0x30050000~0x30050618)


然后使用BX R14跳转到0x000001BC处,顺序执行到BL __rt_enty 指令

成功跳转到__rt_enty函数

分析__rt_entry()函数

先调用__user_setup_stackheap()函数来建立堆栈


可以看出在这个函数中,会执行到BL__user_initial_stackheap()函数,这样也就明白了,为什么使用分散加载文件,需要设置__user_initial_stackheap这个函数来设置堆栈空间。


您可能感兴趣的与本文相关内容

### ### MDK_SIMU 单片机仿真问题及解决方案 MDK(Keil µVision)的软件仿真功能在单片机开发中扮演重要角色,尤其在代码调试和硬件寄存器观察方面具有显著优势。通过软件仿真,可以避免频繁烧录芯片,从而延长单片机 Flash 的使用寿命。在使用 MDK_SIMU 进行仿真时,常见问题包括仿真配置不正确、无法查看寄存器状态、仿真速度慢以及调试信息输出受限等。 在开始仿真前,必须正确配置工程设置,包括选择正确的芯片型号和设置晶振频率。这一过程直接影响仿真精度和外设行为的模拟效果[^1]。例如,在 STM32 系列单片机中,若晶振频率设置错误,可能导致定时器、串口等模块的仿真结果与实际运行不一致。 对于调试信息输出,MDK 提供了两种机制:ITM 和 semihosting。ITM 机制通过 SWO 引脚将调试信息输出到调试器,适用于资源受限的项目;而 semihosting 则利用调试器与主机通信,实现标准输入输出功能,如 printf 的重定向。这两种方式在调试过程中各有优劣,需根据项目需求进行选择[^2]。 在实际仿真过程中,若遇到仿真速度缓慢的问题,可以尝试关闭不必要的寄存器窗口更新,或者使用断点代替单步执行,以提高仿真效率。此外,确保使用最新版本的 MDK 工具链,可以修复一些已知的仿真兼容性问题。 以下是一个简单的 STM32F103 仿真初始化代码示例,用于配置系统时钟和 GPIO: ```c #include "stm32f10x.h" void SystemInit(void) { // 配置系统时钟为 72MHz RCC->CR |= RCC_CR_HSEON; // 开启 HSE while(!(RCC->CR & RCC_CR_HSERDY)); // 等待 HSE 就绪 RCC->CFGR = RCC_CFGR_PLLSRC_HSE_PREDIV | // 选择 HSE 作为 PLL 输入 RCC_CFGR_PLLMULL9; // PLL 倍频为 9 RCC->CR |= RCC_CR_PLLON; // 开启 PLL while(!(RCC->CR & RCC_CR_PLLRDY)); // 等待 PLL 就绪 RCC->CFGR |= RCC_CFGR_SW_PLL; // 选择 PLL 作为系统时钟源 } int main(void) { // 初始化 GPIO RCC->APB2ENR |= RCC_APB2ENR_IOPCEN; // 开启 GPIOC 时钟 GPIOC->CRH &= ~GPIO_CRH_MODE13; // 设置 PC13 为输入模式 GPIOC->CRH |= GPIO_CRH_MODE13_0; // 设置 PC13 为推挽输出模式 GPIOC->ODR |= GPIO_ODR_ODR13; // 默认输出高电平 while(1) { // 主循环 } } ``` 在使用仿真器调试时,可以通过 Keil 的寄存器窗口观察 RCC、GPIO 等模块的寄存器值,从而验证配置是否正确。此外,利用断点和观察窗口可以动态调试程序执行流程和变量变化。 ###
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值