0.前言
初学单片机的时候通常使用IDE(通常是keil),IDE的好处是上手快捷;但是IDE将很多东西屏蔽了,例如什么是编译、链接、加载文件等。编译、链接功能均是由一个个命令实现的,如果把这些命令比作工地上干活的工人,那么makefile就是指导工人干活的图纸,make命令这是负责解读makefile这张图纸的工程师。出于好奇心驱使决定尝试使用makefile方式编译stm32的程序。
先介绍总体思路,makefile只是一个工具,makefile也需要调用IDE背后的命令工具,所以要想实现用makefile编译STM32 程序需要分两步走:
- 搞懂keil怎么将一个个源码程序转换为可以下载到芯片中的文件。
- 用makefile的方式将IDE做的工作实现。
1.解密keil
1.1 IDE的bat文件
在keil下新建一个工程,
如图2 所示,在output->Create Batch File,编译后在工程目录下找到了led.BAT文件,
SETPATH=F:\keil\ARM\BIN40\;C:\Python27\Lib\sitepackages\PyQt4;C:\PROGRAMDATA\ORACLE\JAVA\JAVAPATH;C:\Windows\SYSTEM32;C:\Windows;C:\Windows\SYSTEM32\WBEM;C:\Windows\SYSTEM32\WINDOWSPOWERSHELL\V1.0\;D:\FLASH MAGIC;D:\PROGRAM FILES (X86)\MATLAB\RUNTIME\WIN64;D:\PROGRAM FILES (X86)\MATLAB\BIN;D:\PROGRAM FILES (X86)\MATLAB\POLYSPACE\BIN;D:\PROGRAM FILES (X86)\FLASH MAGIC;D:\software\Bluetooth Software\;D:\software\Bluetooth Software\syswow64;D:\Program Files\common;C:\Program Files (x86)\Microsoft SQL Server\90\Tools\binn\;C:\Program Files\CMake\bin;C:\Python27;F:\Program Files (x86);F:\keil\ARM\BIN40
SET ARMCC41INC=F:\keil\ARM\RV31\INC
SET ARMCC41LIB=F:\keil\ARM\RV31\LIB
SET CPU_TYPE=STM32F103C8
SET CPU_VENDOR=STMicroelectronics
SET UV2_TARGET=led
SET CPU_CLOCK=0x00000000
"F:\keil\ARM\BIN40\ArmCC" --Via "main.__i"
"F:\keil\ARM\BIN40\ArmCC" --Via "stm32f10x_it.__i"
"F:\keil\ARM\BIN40\ArmAsm" --Via ".\startup_stm32f10x_md._ia"
"F:\keil\ARM\BIN40\ArmCC" --Via "stm32f10x_gpio.__i"
"F:\keil\ARM\BIN40\ArmCC" --Via "stm32f10x_rcc.__i"
"F:\keil\ARM\BIN40\ArmCC" --Via "core_cm3.__i"
"F:\keil\ARM\BIN40\ArmCC" --Via "system_stm32f10x.__i"
"F:\keil\ARM\BIN40\ArmLink" --Via "led.lnp"
F:\keil\ARM\BIN40\fromelf.exe "led.axf" --i32combined --output "led.hex"
神奇的是,运行该bat文件(PS:bat是windows下的一种批处理文件,类似于linux下的shell脚本),竟然就可以生成想要的hex文件。看来keil的奥秘就藏在这个bat文件中了。bat文件分为两段,第一段是 几个SET命令,设置了一些变量;第二段是ArmCC,ArmAsm,ArmLink,fromelf.exe命令构成,但是 --Via “main.__i” 这种是什么鬼就不得而知。
1.2 编译
查看keil的帮助文档:
--via=filename*
This option instructs the compiler to read additional command-line options
from a specified file. The options read from the file are added to the current
command line. Via commands can be nested within via files.
Syntax
--via=*filename
Where:
*filename
is the name of a via file containing options to be included on the command line.
Example
Given a source file main.c, a via file apcs.txt containing the line:
--apcs=/rwpi --no_lower_rwpi --via=L_apcs.txt
and a second via file L_apcs.txt containing the line:
-L--rwpi -L--callgraph
compiling main.c with the command line:
armcc main.c -L-o”main.axf" --via=apcs.txt
compiles main.c using the command line:
armcc --no_lower_rwpi --apcs=/rwpi -L--rwpi -L--callgraph -L-o"main.axf" main.c
现在清楚了,编译一个文件需要指定各种编译选项,这些选项可以直接写在命令行中,同时可以将这些命令选项写在一个文件中,然后使用“--via”指明该文件。
打开生成的main.__i文件,内容如下:
-c
--cpu Cortex-M3
-g
-O0
--apcs=interwork
-IE:\src\stm32\CMSIS -IE:\src\stm32\StartUp -IE:\src\stm32\StdPeriph_Lib\inc -IE:\src\stm32\led -I "F:\keil\ARM\INC" -I "F:\keil\ARM\INC\ST\STM32F10x"
-DSTM32F10X_MD -DUSE_STDPERIPH_DRIVER
-o "main.o"
--omf_browse "main.crf" --depend "main.d" "E:\src\stm32\led\main.c"
这些都是执行armcc命令带的参数,-c,-I -g是比较通用,--cpu显然是指明CPU的类型,编写makefile的时候可以直接借用。
1.3 链接
执行ArmLink命令时候的使用的命令参数在文件led.lnp中,其中内容如下:
--cpu Cortex-M3 "main.o" "stm32f10x_it.o" ".\startup_stm32f10x_md.o" "stm32f10x_gpio.o" "stm32f10x_rcc.o" "core_cm3.o" "system_stm32f10x.o" --strict --scatter "led.sct"
--autoat --summary_stderr --info summarysizes --map --xref --callgraph --symbols
--info sizes --info totals --info unused --info veneers
--list ".\led.map" -o "led.axf"
注意其中的--scatter "led.sct",该选项表示链接器ArmLink使用的链接文件led.sct; -o "led.axf",表示链接生成的目标文件。
知识点:hex,axf,bin,elf格式文件文件
- bin文件:最纯粹的二进制文件,就是汇编程序直接汇编成为二进制代码,在裸机
条件下运行的是bin文件。可以说bin文件是没有其他几种格式文件复杂的格式。
文件本身不包含任何地址信息,在将bin文件烧写到flash中必须要指定地址。 - hex文件:intel格式文件,它里面除包含精华的bin部分还有其他的一些信息,
hex文件常常被用做很多ISP工具的文件格式。 - axf文件:arm调试文件格式,同样这个文件格式的文件除包含bin文件部分,还包含
了一些调试相关的信息。 - elf文件:是一种目标文件格式,在有操作系统条件下例如linux中的目标文件就是
采用elf文件格式,这种文件需要在有OS的支持下才可以运行。如果将elf文件放到
裸机中运行,肯定会出错。 - 联系 这几种文件格式中bin文件是最为基本的,bin文件是没有任何的
“杂质”的机器码文件,其他的几种格式的文件都是在这种文件的基础上加入其他相关的信息形成的。
1.4 生成hex文件
fromelf.exe "led.axf" --i32combined --output "led.hex" 命令是讲生成的axf文件转换为hex文件,isp下载的使用的就是hex文件。
2.编写makefile
由之前的分析,知道了,怎么用命令将源文件转换为hex文件,下面就用makefile将这些命令组合起来。
2.1 安装windows版make工具
下载一个windows版的GNU make工具 ,安装到本地。http://www.gnu.org/software/make/manual/make.html
2.2 编写makefile
编写一个点亮LED的程序,程序本身由startup_stm32f10x_md.s和main.c两个文件组成。整个编译makefile由三个文件组成,分别是common_compile.mk,common_config.mk,Makefile
common_compile.mk的内容如下
%.o:%.c
$(ARMCC) $(CFLAGS) $(INC) $(CMACRO) $< -o $@
%.o:%.s
$(ARMASM) $(ASMFLAGS) $(INC) $(CMACRO) $< -o $@
all:$(OBJS)
$(ARMLINK) --libpath "$(KEIL_PATH)\RV31\LIB" $(LINKFLAGS) $(MAP) $(INFO) $^ -o $(TARGET).axf
$(FROMELF) --bin -o $(TARGET).bin $(TARGET).axf
$(FROMELF) --i32 -o $(TARGET).hex $(TARGET).axf
del $(OBJHTM) $(OBJAXF) $(OBJS)
# 若只是生成LIB库,只需要以下一条命令就可以了
# $(ARMAR) $(APPNAME).lib -r $(OBJS)
.PHONY : clean
clean:
-del $(OBJS) *.map *.htm
common_config.mk内容如下
SHELL=cmd.exe
KEIL_PATH = F:\Keil\ARM
ARMCC = $(KEIL_PATH)\BIN40\armcc
ARMASM =$(KEIL_PATH)\BIN40\armasm
ARMAR = $(KEIL_PATH)\BIN40\armar
ARMLINK =$(KEIL_PATH)\BIN40\armlink
FROMELF = $(KEIL_PATH)\BIN40\fromelf
TARGET =.\output\stm32
OBJMAP := .\stm32.map
OBJHTM := .\output\*.htm
OBJAXF := .\output\*.axf
CFLAGS := -c --cpu Cortex-M3 -D__MICROLIB -g -O0 --apcs=interwork
CMACRO :=
ASMFLAGS := --cpu Cortex-M3 -g --apcs=interwork --pd "__MICROLIB SETA 1"
LINKFLAGS := --cpu Cortex-M3 --library_type=microlib --scatter=led.sct --strict
MAP := --autoat --summary_stderr --info summarysizes --map --xref --callgraph --symbols
INFO := --info sizes --info totals --info unused --info veneers
Makefile内容如下
include ./common_config.mk
OBJS = .\main.o .\startup_stm32f10x_md.o
INC += -I$(KEIL_PATH)\INC\St\STM32F10x
INC += -I$(KEIL_PATH)\RV31\INC
include ./common_compile.mk
命令行中执行make命令,成功编译输出bin,hex文件。