上位机图像处理和嵌入式模块部署(f103 mcu中main入口函数误解)

本文主要是介绍上位机图像处理和嵌入式模块部署(f103 mcu中main入口函数误解),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】

        学习stm32代码的时候,关于汇编文件,大家一般都会参考官方给出的汇编文件。通常情况下,不会自己去写汇编文件。特别是汇编文件的最后一行,大家都会把__main看成是直接进入main函数。后面通过反汇编,发现情况并不是这样的。我们编写代码,除了keil工程中的内容,还有MicroLib库,这一点常常被我们忽视。

1、之前的汇编代码

; Reset handler
Reset_Handler   PROCEXPORT  Reset_Handler             [WEAK]IMPORT  __mainIMPORT  SystemInitLDR     R0, =SystemInitBLX     R0               LDR     R0, =__mainBX      R0ENDP

        汇编代码这部分呢,大家一般都不会陌生,我们也通常都认为是__main直接跳转到了自定义的main函数。但是实际情况,我们可以通过反汇编来确认。

2、如何生成反汇编文件

        在项目的User tab配置中,添加这样的命令,就可以在project下面生成dis文件,

fromelf --text -a -c --output=Fire_F103VE.dis ../Output/Fire_F103VE.axf

        这样在最后的axf生成的同时,也能生成dis文件。如果需要生成bin文件,也是类似的做法。

fromelf --bin --output Fire_F103VE.bin ../Output/Fire_F103VE.axf

3、找到Reset_Handler位置

        首先我们找到Reset_Handler位置,

    __Vectors0x08000000:    20000410    ...     DCD    5368719520x08000004:    08000145    E...    DCD    1342180530x08000008:    08000d81    ....    DCD    134221185

        一般向量的第二个选项就是reset入口,不过mcu很奇怪,入口地址都要减去1才是正确的地址。也就是说,这里的Reset_Handler其实是08000144,

    Reset_Handler0x08000144:    4806        .H      LDR      r0,[pc,#24] ; [0x8000160] = 0x8000e170x08000146:    4780        .G      BLX      r00x08000148:    4806        .H      LDR      r0,[pc,#24] ; [0x8000164] = 0x80001310x0800014a:    4700        .G      BX       r0

        我们发现了,这边pc最后跳转的地址是8000131,因为需要减去1,那就是8000130,

    _main_stk0x08000130:    f8dfd00c    ....    LDR      sp,__lit__00000000 ; [0x8000140] = 0x20000410.ARM.Collect$$$$00000004_main_scatterload0x08000134:    f000f82a    ..*.    BL       __scatterload ; 0x800018c

        这边执行不久,就跳转到了__scatterload,

    __scatterload__scatterload_rt20x0800018c:    4c06        .L      LDR      r4,[pc,#24] ; [0x80001a8] = 0x8000ec40x0800018e:    4d07        .M      LDR      r5,[pc,#28] ; [0x80001ac] = 0x8000ee40x08000190:    e006        ..      B        0x80001a0 ; __scatterload + 200x08000192:    68e0        .h      LDR      r0,[r4,#0xc]0x08000194:    f0400301    @...    ORR      r3,r0,#10x08000198:    e8940007    ....    LDM      r4,{r0-r2}0x0800019c:    4798        .G      BLX      r30x0800019e:    3410        .4      ADDS     r4,r4,#0x100x080001a0:    42ac        .B      CMP      r4,r50x080001a2:    d3f6        ..      BCC      0x8000192 ; __scatterload + 60x080001a4:    f7ffffc8    ....    BL       __main_after_scatterload ; 0x8000138

        快结束的时候,又跳转到了__main_after_scatterload,

    __main_after_scatterload_main_clock_main_cpp_init_main_init0x08000138:    4800        .H      LDR      r0,[pc,#0] ; [0x800013c] = 0x8000e750x0800013a:    4700        .G      BX       r0

        这边貌似要回到main函数了,看这里的地址是8000e75,那就是8000e74,

    i.mainmain0x08000e74:    bf00        ..      NOP      0x08000e76:    f7ffff9e    ....    BL       SystemClock_Config ; 0x8000db60x08000e7a:    f7ffff2b    ..+.    BL       LED_GPIO_Config ; 0x8000cd40x08000e7e:    e012        ..      B        0x8000ea6 ; main + 500x08000e80:    2200        ."      MOVS     r2,#00x08000e82:    f44f5100    O..Q    MOV      r1,#0x20000x08000e86:    4808        .H      LDR      r0,[pc,#32] ; [0x8000ea8] = 0x400110000x08000e88:    f7fffb4a    ..J.    BL       HAL_GPIO_WritePin ; 0x80005200x08000e8c:    f44f707a    O.zp    MOV      r0,#0x3e80x08000e90:    f7fff992    ....    BL       HAL_Delay ; 0x80001b80x08000e94:    2201        ."      MOVS     r2,#10x08000e96:    0351        Q.      LSLS     r1,r2,#130x08000e98:    4803        .H      LDR      r0,[pc,#12] ; [0x8000ea8] = 0x400110000x08000e9a:    f7fffb41    ..A.    BL       HAL_GPIO_WritePin ; 0x80005200x08000e9e:    f44f707a    O.zp    MOV      r0,#0x3e80x08000ea2:    f7fff989    ....    BL       HAL_Delay ; 0x80001b80x08000ea6:    e7eb        ..      B        0x8000e80 ; main + 12

        看到上面的代码,大约可以验证到我们的猜测了。

4、总结

        之前在做linux soc芯片汇编文件编写的时候,中断初始化、bss初始化、sp初始化、已初始化全局变量copy、main函数跳转,这些都是基本的内容。后面自己看mcu代码的时候,却没有发现这部分内容,当时觉得很诧异,直到后来自己看了axf文件的反汇编内容之后,才晓得ide和背后的编译器帮我们做了很多事情。刚才说的这一切,都在main函数调用之前准备好了。但恰恰这一点,对于我们debug调试、分析和boot 加载研究很有帮助。

这篇关于上位机图像处理和嵌入式模块部署(f103 mcu中main入口函数误解)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1015886

相关文章

postgresql使用UUID函数的方法

《postgresql使用UUID函数的方法》本文给大家介绍postgresql使用UUID函数的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录PostgreSQL有两种生成uuid的方法。可以先通过sql查看是否已安装扩展函数,和可以安装的扩展函数

MySQL字符串常用函数详解

《MySQL字符串常用函数详解》本文给大家介绍MySQL字符串常用函数,本文结合实例代码给大家介绍的非常详细,对大家学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录mysql字符串常用函数一、获取二、大小写转换三、拼接四、截取五、比较、反转、替换六、去空白、填充MySQL字符串常用函数一、

MySQL 主从复制部署及验证(示例详解)

《MySQL主从复制部署及验证(示例详解)》本文介绍MySQL主从复制部署步骤及学校管理数据库创建脚本,包含表结构设计、示例数据插入和查询语句,用于验证主从同步功能,感兴趣的朋友一起看看吧... 目录mysql 主从复制部署指南部署步骤1.环境准备2. 主服务器配置3. 创建复制用户4. 获取主服务器状态5

C++中assign函数的使用

《C++中assign函数的使用》在C++标准模板库中,std::list等容器都提供了assign成员函数,它比操作符更灵活,支持多种初始化方式,下面就来介绍一下assign的用法,具有一定的参考价... 目录​1.assign的基本功能​​语法​2. 具体用法示例​​​(1) 填充n个相同值​​(2)

MySql基本查询之表的增删查改+聚合函数案例详解

《MySql基本查询之表的增删查改+聚合函数案例详解》本文详解SQL的CURD操作INSERT用于数据插入(单行/多行及冲突处理),SELECT实现数据检索(列选择、条件过滤、排序分页),UPDATE... 目录一、Create1.1 单行数据 + 全列插入1.2 多行数据 + 指定列插入1.3 插入否则更

PostgreSQL中rank()窗口函数实用指南与示例

《PostgreSQL中rank()窗口函数实用指南与示例》在数据分析和数据库管理中,经常需要对数据进行排名操作,PostgreSQL提供了强大的窗口函数rank(),可以方便地对结果集中的行进行排名... 目录一、rank()函数简介二、基础示例:部门内员工薪资排名示例数据排名查询三、高级应用示例1. 每

全面掌握 SQL 中的 DATEDIFF函数及用法最佳实践

《全面掌握SQL中的DATEDIFF函数及用法最佳实践》本文解析DATEDIFF在不同数据库中的差异,强调其边界计算原理,探讨应用场景及陷阱,推荐根据需求选择TIMESTAMPDIFF或inte... 目录1. 核心概念:DATEDIFF 究竟在计算什么?2. 主流数据库中的 DATEDIFF 实现2.1

golang程序打包成脚本部署到Linux系统方式

《golang程序打包成脚本部署到Linux系统方式》Golang程序通过本地编译(设置GOOS为linux生成无后缀二进制文件),上传至Linux服务器后赋权执行,使用nohup命令实现后台运行,完... 目录本地编译golang程序上传Golang二进制文件到linux服务器总结本地编译Golang程序

MySQL中的LENGTH()函数用法详解与实例分析

《MySQL中的LENGTH()函数用法详解与实例分析》MySQLLENGTH()函数用于计算字符串的字节长度,区别于CHAR_LENGTH()的字符长度,适用于多字节字符集(如UTF-8)的数据验证... 目录1. LENGTH()函数的基本语法2. LENGTH()函数的返回值2.1 示例1:计算字符串

Python通用唯一标识符模块uuid使用案例详解

《Python通用唯一标识符模块uuid使用案例详解》Pythonuuid模块用于生成128位全局唯一标识符,支持UUID1-5版本,适用于分布式系统、数据库主键等场景,需注意隐私、碰撞概率及存储优... 目录简介核心功能1. UUID版本2. UUID属性3. 命名空间使用场景1. 生成唯一标识符2. 数