引言
Keil MDK是常用的嵌入式集成开发环境(Integrated Development Environment),使用IDE,很容易操作,点点鼠标就可完成:
- 添加文件
- 指定文件路径(头文件路径、库文件路径)
- 指定链接库
- 编译、链接
- 下载、调试
那么每次点击MDK后会执行什么命令?
1 MDK背后的命令
勾选生成批处理文件.bat
,使用文本编辑器打开:
SET PATH=E:\MDK\ARM\ARMCC\Bin // 所有系统环境变量,太长后面部分已截取
SET CPU_TYPE=STM32F103RC
SET CPU_VENDOR=STMicroelectronics
SET UV2_TARGET=led_c
SET CPU_CLOCK=0x007A1200
"E:\MDK\ARM\ARMCC\Bin\ArmCC" --Via ".\objects\main.__i"
"E:\MDK\ARM\ARMCC\Bin\ArmAsm" --Via ".\objects\start._ia"
"E:\MDK\ARM\ARMCC\Bin\ArmCC" --Via ".\objects\uart.__i"
"E:\MDK\ARM\ARMCC\Bin\ArmLink" --Via ".\Objects\led_c.lnp"
"E:\MDK\ARM\ARMCC\Bin\fromelf.exe" ".\Objects\led_c.axf" --i32combined --output ".\Objects\led_c.hex"
fromelf --bin --output=led.bin Objects\led_c.axf
fromelf --text -a -c --output=led.dis Objects\led_c.axf
"E:\MDK\ARM\ARMCC\Bin\ArmCC"
:armcc编译器,编译C文件。"E:\MDK\ARM\ARMCC\Bin\ArmAsm"
:armasm编译器,编译汇编文件。"E:\MDK\ARM\ARMCC\Bin\ArmLink.exe"
:链接目标文件"E:\MDK\ARM\ARMCC\Bin\fromelf.exe"
:生成axf、hex等可执行文件
最后两条是在user
中添加的生成bin文件和dis反汇编文件的命令:
注意:勾选了create batch file
后,不管文件是否修改,每次点击单编译都会全部编译。
E:\MDK\ARM\ARMCC\Bin
目录下可以看到这些编译工具:
git bash here
后输入./xxx.exe -h
可查看命令信息,如./armar.exe -h
:
关于各编译工具的命令使用参考:[转]keil编译链接过程以及ARMCC、ARMASM、FROMELF、ARMLINK、ARMAR的使用
可以将路径添加到系统环境变量,这样即可在任意路径下使用编译器工具。
1.1 编译
1.1.1 编译C文件
打开uart.__i
文件:
--c99 --gnu -c --cpu Cortex-M3 -g -O0 --apcs=interwork --split_sections -I .\drivers
-IE:\MDK\ARM-pack\Keil\STM32F1xx_DFP\2.2.0\Device\Include
-IE:\MDK\ARM\CMSIS\Include
-D__UVISION_VERSION="533" -DSTM32F10X_HD
-o .\objects\uart.o --omf_browse .\objects\uart.crf --depend .\objects\uart.d "drivers\uart.c"
使用"E:\MDK\ARM\ARMCC\Bin\ArmCC"
命令编译(需要整理成一行,中间用空格隔开):
"E:\MDK\ARM\ARMCC\Bin\ArmCC" --c99 --gnu -c --cpu Cortex-M3 -g -O0 --apcs=interwork --split_sections -I .\drivers -IE:\MDK\ARM-pack\Keil\STM32F1xx_DFP\2.2.0\Device\Include -IE:\MDK\ARM\CMSIS\Include -D__UVISION_VERSION="533" -DSTM32F10X_HD -o .\objects\uart.o --omf_browse .\objects\uart.crf --depend .\objects\uart.d "drivers\uart.c"
或者直接执行
"E:\MDK\ARM\ARMCC\Bin\ArmCC" --Via ".\objects\uart.__i"
使用cmd切换到工程所在文件夹或用git bash here
编译:
这些命令也可以在C/C++中看到:
--c99 --gnu -c --cpu Cortex-M3 -g -O0 --apcs=interwork --split_sections -I ./drivers
-IE:/MDK/ARM-pack/Keil/STM32F1xx_DFP/2.2.0/Device/Include
-IE:/MDK/ARM/CMSIS/Include
-D__UVISION_VERSION="533" -DSTM32F10X_HD
-o .\Objects\*.o --omf_browse .\Objects\*.crf --depend .\Objects\*.d
将其中的*.
通配符替换生成需要编译源文件的名称,然后在末尾追加文件相对路径,如:"drivers\uart.c"
,然后在前面加上"E:\MDK\ARM\ARMCC\Bin\ArmCC"
命令编译即可(依然需要整理成一行,中间用空格隔开)。
使用git编译main
函数出现以下错误:
"E:\MDK\ARM\ARMCC\Bin\ArmCC" --Via ".\objects\main.__i"
1.1.2 编译汇编文件
打开start._ia
文件:
--cpu Cortex-M3 -g --apcs=interwork
-IE:\MDK\ARM-pack\Keil\STM32F1xx_DFP\2.2.0\Device\Include
-IE:\MDK\ARM\CMSIS\Include
--pd "__UVISION_VERSION SETA 533" --pd "STM32F10X_HD SETA 1"
--list .\listings\start.lst --xref -o .\objects\start.o --depend .\objects\start.d "start.s"
使用"E:\MDK\ARM\ARMCC\Bin\ArmAsm"
命令编译:
"E:\MDK\ARM\ARMCC\Bin\ArmAsm" --cpu Cortex-M3 -g --apcs=interwork -IE:\MDK\ARM-pack\Keil\STM32F1xx_DFP\2.2.0\Device\Include -IE:\MDK\ARM\CMSIS\Include --pd "__UVISION_VERSION SETA 533" --pd "STM32F10X_HD SETA 1" --list .\listings\start.lst --xref -o .\objects\start.o --depend .\objects\start.d "start.s"
或者直接执行
"E:\MDK\ARM\ARMCC\Bin\ArmAsm" --Via ".\objects\start._ia"
汇编文件编译时没有输出log信息。
这些命令也可以在Asm中看到:
--cpu Cortex-M3 -g --apcs=interwork
-I E:\MDK\ARM-pack\Keil\STM32F1xx_DFP\2.2.0\Device\Include
-I E:\MDK\ARM\CMSIS\Include
--pd "__UVISION_VERSION SETA 533" --pd "STM32F10X_HD SETA 1" --list ".\Listings\*.lst" --xref -o "*.o" --depend "*.d"
使用方法与编译C文件一样。如果汇编文件与工程在同一目录下则无需添加路径:
1.2 链接
打开led_c.lnp
文件:
--cpu Cortex-M3
".\objects\main.o"
".\objects\start.o"
".\objects\uart.o"
--strict --scatter ".\Objects\led_c.sct"
--entry Reset_Handler --summary_stderr --info summarysizes --map --load_addr_map_info --xref --callgraph --symbols
--info sizes --info totals --info unused --info veneers
--list ".\Listings\led_c.map" -o .\Objects\led_c.axf
使用"E:\MDK\ARM\ARMCC\Bin\ArmLink"
命令链接:
"E:\MDK\ARM\ARMCC\Bin\ArmLink" --cpu Cortex-M3 ".\objects\main.o" ".\objects\start.o" ".\objects\uart.o" --strict --scatter ".\Objects\led_c.sct" --entry Reset_Handler --summary_stderr --info summarysizes --map --load_addr_map_info --xref --callgraph --symbols --info sizes --info totals --info unused --info veneers --list ".\Listings\led_c.map" -o .\Objects\led_c.axf
或者直接执行
"E:\MDK\ARM\ARMCC\Bin\ArmLink" --Via ".\Objects\led_c.lnp"
输出结果与MDK一致:
这些命令也可以在Linker中看到:
--cpu Cortex-M3 *.o
--strict --scatter ".\Objects\led_c.sct"
--entry Reset_Handler --summary_stderr --info summarysizes --map --xref --callgraph --symbols
--info sizes --info totals --info unused --info veneers
--list ".\Listings\led_c.map"
-o .\Objects\led_c.axf
1.3 生成可执行文件
"E:\MDK\ARM\ARMCC\Bin\ArmLink" --Via ".\Objects\led_c.lnp"
"E:\MDK\ARM\ARMCC\Bin\fromelf.exe" ".\Objects\led_c.axf" --i32combined --output ".\Objects\led_c.hex"
fromelf --bin --output=led.bin Objects\led_c.axf
fromelf --i32 --output=led.hex Objects\led_c.axf // intel hex
fromelf --text -a -c --output=led.dis Objects\led_c.axf
ArmLink
与fromelf.exe
需同时执行(否则失败),最后生成:
- led_c.axf:不仅包含代码数据,还包含了工程的各种信息
- led_c.hex:十六进制符号表示的代码记录,记录了代码应该存储到的flash哪个地址和二进制数据等信息
- led.bin:只存储机器码,文件大小 = ROM Size (Code + RO Data + RW Data)
- led.dis:反汇编文件
1.4 Hex文件分析
16进制文本表示
-
:
:使用冒号表示一条记录的开始 -
ll
:主体数据区(绿色区域)长度 -
aaaa
:每条记录中的内容应存放到Flash中的起始地址,相对flash起始地址的偏移值 -
tt
:表示这条记录的类型:- 00:绿色部分数据记录
dd
- 01:hex文件结束记录
- 02:扩展地址记录,与04类似
- 04:如上图第一条可以用绿色0800+橙色0000表示记录开始地址:
0x0800 0000
,即内部flash的起始地址 - 05:ARM芯片专用
- 00:绿色部分数据记录
-
dd
:表示一个字节的数据(16进制),一条记录含多个dd,即可以多个字节数据,而ll
则表示这条记录字节数 -
cc
:本条记录的校验和,即前面所有16进制(冒号除外)两个为一组的和对256取模运算结果的补码。如第一条记录:(0x02+0x00+0x00+0x04+0x08+0x00) % 256= 0x0e,反码为0xf1,补码即为0xf2。
hex与bin对比:
1.5 生成杂项文件
1.5.1 生成各节区详细信息
fromelf --text -v Objects/F103-Fwlib.axf > test.txt
// 或 fromelf --text -v --output=f.txt Objects/F103-Fwlib.axf
- size in file = Code + RO Data + RW Data
- size in memory = ZI Data
有时候编译器会做优化,实际可能更小些,如:开辟的数组很多空间没有初始化,则会被分配到bss段,烧录时不占用flash空间。
1.5.2 查看函数调用关系与最大栈调用深度.htm
编译器生成的xx.htm
中可查看:
静态最大调用栈为160b,调用关系:main ⇒ Usart_Init ⇒ USART_Init ⇒ RCC_GetClocksFreq
但是无法计算动态栈,即实际可能更大一些,如出现:
- 递归调用
- 函数指针调用函数
1.5.3 map文件分析
- Section Cross References:节区跨文件引用
- Removing Unused input sections from the image:从映像文件中移除未使用的输入节区
- Image Symbol Table:Value[运行地址]、Type[变量/指令类型]、Size[占用字节, 0表示未计算,而不是没有]、Object(Section) [符号所属的目标节区]
- Local Symbols
- Global Symbols
- Memory Map of the image:映像文件加载域和执行域信息(bss、stack、heap无加载地址)
- Image component sizes:映像组件大小,即每个Object文件各段大小信息
END