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

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


1.1     __main()代码执行分析

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

 

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

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

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

 

分析__scatterload()函数

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

Map文件中的symbol

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

Region$$Table$$Limit               0x000003b4   Number         0 anon$$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这个函数来设置堆栈空间。


 

  • 11
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
函数声明只是函数的定义,需要实现该函数才能执行设备 GPIO 口的初始化。具体实现方法会根据使用的芯片和开发环境而有所不同,这里提供一个常见的方法,针对 STM32F4 系列芯片和使用 Keil MDK 开发环境的情况。 1. 首先需要在 Keil MDK 中创建一个工程,并将所需的文件添加到工程中,比如启动文件、CMSIS 库和 HAL 库等。 2. 在工程中找到 main 函数,在其中调用 GPIO_ActEquInit 函数,以执行设备 GPIO 口的初始化。可以选择在 main 函数的开头或结尾调用该函数,具体取决于初始化的先后顺序和需要的 GPIO 功能。 3. 在 GPIO_ActEquInit 函数中,需要使用 STM32F4 的 HAL 库函数来配置 GPIO 端口。以下是一个示例代码,用于初始化 PA5 和 PA6 两个 GPIO 端口为输出模式,并将它们的输出电平设置为高电平: ``` #include "stm32f4xx_hal.h" void GPIO_ActEquInit(void) { GPIO_InitTypeDef GPIO_InitStruct; __HAL_RCC_GPIOA_CLK_ENABLE(); // 使能 GPIOA 时钟 GPIO_InitStruct.Pin = GPIO_PIN_5 | GPIO_PIN_6; // 配置 PA5 和 PA6 两个引脚 GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 配置为推挽输出模式 GPIO_InitStruct.Pull = GPIO_NOPULL; // 关闭上下拉电阻 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; // 配置为高速输出 HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // 初始化 GPIOA 端口 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5 | GPIO_PIN_6, GPIO_PIN_SET); // 将 PA5 和 PA6 输出电平设置为高电平 } ``` 注意,以上代码仅供参考,具体的 GPIO 端口配置和初始化方法可能会因芯片型号、引脚定义等因素而有所不同。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值