很多时候,一份Keil工程代码可能需要满足多个不同的应用场景。可以通过逻辑判断,将多个不同的点集成在一份代码之中,但是嵌入式往往特别关注RAM空间,集成过多的逻辑判断,RAM空间可能就不够用了。针对这种情况Keil提供了配置Target,通过宏定义来分隔不同的功能,编译生成不同的bin文件。
但是有时,一个Target项目中,另外又有几个子场景,子场景也需要用预定义宏来区分开。但是Keil并没有提供这样的功能。
另外,Keil工程有非常多的Target,一次修改,必须不停选择不同Target来进行编译,这种编译方式非常不方便。
Keil提供了命令行,可以编译工程。另外,Keil使用的ArmCC和Armlink来编译和链接,这样的话,便可以使用makefile来编译Keil工程代码。
UV4 [command] [projectfile]
command可以是下述列表之一。如果command没有指定,则只是打开Keil工程。
UV4 -b PROJECT1.uvprojx
UV4 -c PROJECT1.uvprojx
UV4 -cr PROJECT1.uvprojx
UV4 -d PROJECT1.uvprojx
UV4 -f PROJECT1.uvprojx -t"MCB2100 Board"
UV4 -r PROJECT1.uvprojx
UV4 -r PROJECT1.uvprojx -t"Simulator"
UV4 -5 myoldproject.uvproj -l log.txt
下面的选项是可选项。
UV4 MyProject.uvprojx -n Device123
UV4 -r PROJECT1.uvprojx -o"output.log"
UV4 -r PROJECT1.uvprojx -t"MCB2100 Board"
UV4 -b PROJECT1.uvproj -z
Kiel编译完成后的错误码
ERRORLEVEL | 描述 |
---|---|
0 | 无错误无敬告 |
1 | 仅有敬告 |
2 | 错误 |
3 | 重大错误 |
11 | 不能打开工程 |
12 | 给定的设备不存在 |
13 | 写工程文件出错 |
15 | 读XML文件出错 |
20 | 转换工程出错 |
为一个target指定多个预定义宏,此处使用shell脚本编写,需要git-bash或cygwin来编译。
添加一个define.h宏,Keil工程引用此宏达到控制一个Target中有不同的预编译宏。
# Obtain Keil project
prj_name=$(find *.uvproj)
# Obtain all target name of uvproj
mapfile -t target_array < <(cat "${prj_name}" | grep "TargetName" | awk -F '>' '{print $2}' | awk -F '<' '{print $1}')
# Macro
L85C_define=(__L85C1_ __L85C2__)
# Build all target
for name in "${target_array[@]}"; do
if [[ "$name" == "L85C" ]]; then
for define in "${L85C_define[@]}"; do
echo "#define" $define>define.h
# Code中include "define.h"
"C:\Keil\UV4\UV4.exe" -r 5081Scan.uvproj -t"$name" -o "$name""$define""Build.txt" -j0
done
else
"C:\Keil\UV4\UV4.exe" -r 5081Scan.uvproj -t"$name" -o "$name""Build.txt" -j0
fi
# If build error, stop build.
if (( $? > 0 )); then
echo Build error.
break
fi
done
Keil使用ArmCC编译源文件,使用Armlink链接目录文件生成efl文件,然后调用fromelf转换为bin文件。
那么ArmCC和Armlink如何使用呢?Keil提供一种方法来展示如何使用ArmCC和Armlink。如下图,勾选Create Batch File,会将ArmCC和Armlink编译链接的过程输出到相应的文件中。
编译当前工程指定的Test,编译连接的命令过程生成在test.bat文件中,如下图:
ArmAsm --cpu Cortex-M0 -g --apcs=interwork --pd “__MICROLIB SETA 1” -I C:\Keil\ARM\RV31\INC
-I C:\Keil\ARM\CMSIS\Include --list .\list\startup_m0.lst --xref -o .\obj\startup_m0.o --depend .\obj\startup_m0.d “startup_M0.s”
ArmCC -c --cpu Cortex-M0 -D__MICROLIB -g -O2 -Otime --apcs=interwork -I C:\Keil\ARM\RV31\INC -I C:\Keil\ARM\CMSIS\Include -o .\obj\main.o --omf_browse .\obj\main.crf --depend .\obj\main.d “Src\main.c”
ArmLink --cpu Cortex-M0".\obj\startup_m0.o" “.\obj\scan.o” “.\obj\main.o” --library_type=microlib --strict --scatter “.\Obj\scan.sct” --summary_stderr --info summarysizes --map --xref --callgraph --symbols --info sizes --info totals --info unused --info veneers --list “.\List\scan.map” -o .\Obj\scan.axf
APP ?= .\output\scan.afx
MACRO ?=invlid_macro
OUTPUTCHAN ?= SEMIHOSTED
SHELL=$(windir)\system32\cmd.exe
RM_FILES = $(foreach file,$(1),if exist $(file) del /q $(file))
RM_DIRS = $(foreach dir,$(1),if exist $(dir) rmdir /s /q $(dir)$(EOL))
ifeq ($(QUIET),@)
PROGRESS = @echo Compiling $<...
endif
SRC_DIR = Src
ASM_DIR = .
INC_DIR = include
OBJ_DIR = obj
LINK = armlink
OUTPUT_DIR=output
INCLUDES := -I$(INC_DIR)
INCLUDES += -I C:\Keil\ARM\RV31\INC
INCLUDES += -I C:\Keil\ARM\CMSIS\Include
ARCH := Cortex-M0
CPU := ARM
LINK_GCC := link.ld
GCC_LINKER_FILE := -T link/$(LINK_GCC)
GCC_TOOLCHAIN := C:\Keil\ARM\ARMCC\bin
ASM := $(GCC_TOOLCHAIN)\ArmAsm
CC := $(GCC_TOOLCHAIN)\ArmCC
LINKER_CSRC := $(GCC_TOOLCHAIN)\armlink
FROMELF := $(GCC_TOOLCHAIN)\fromelf
# GCC options
ASM_OPTS := --cpu Cortex-M0 -g --apcs=interwork --pd "__MICROLIB SETA 1"
CC_OPTS := --cpu Cortex-M0 -D__MICROLIB -g -O2 -Otime --apcs=interwork -D$(MACRO)
LINKER_FILE := --cpu Cortex-M0 --library_type=microlib --strict --scatter ".\scan.sct" --info summarysizes
APP_C_SRC := $(wildcard $(SRC_DIR)/*.c)
APP_S_SRC := $(wildcard $(ASM_DIR)/*.s)
OBJ_FILES := $(APP_C_SRC:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o)
OBJ_FILES += $(APP_S_SRC:$(ASM_DIR)/%.s=$(OBJ_DIR)/%.o)
DIRS= $(OUTPUT_DIR) $(OBJ_DIR)
.phony: all clean
all: FORCE $(APP)
FORCE:
@echo being
$(APP): $(DIRS) $(OBJ_FILES)
@echo Linking $@
$(LINKER_CSRC) $(LINKER_FILE) $(OBJ_FILES) -o $@
$(FROMELF) $@ --i32combined --output ".\output\scan.hex"
$(FROMELF) $@ --bin --output ".\output\scan.bin"
@echo Done.
clean:
$(call RM_DIRS,$(OBJ_DIR))
$(call RM_DIRS,$(OUTPUT_DIR))
$(call RM_FILES,$(APP))
$(DIRS):
mkdir $@
@echo $(OBJ_FILES)
$(OBJ_DIR)/%.o : $(SRC_DIR)/%.c
@echo begin to comple c source
$(CC) -c $(CC_OPTS) -o $@ $<
$(OBJ_DIR)/%.o : $(ASM_DIR)/%.s
@echo begin to comple asm
$(ASM) -c $(ASM_OPTS) -o $@ $<
# Make sure everything is rebuilt if this makefile is changed
$(OBJ_FILES) $(APP): makefile
利用批处理,添加MACRO参数控制预编译宏。
echo off
set list=__AAA_ __BBB__ __CCCC__ __DDDD__
for %%n in (%list%) do (
make clean
make MACRO=%%n )
命令行的方式使用简单,但是灵活度不够,例如一个Target不能指定宏。Makefile比较灵活,但是前期构建工程会比较复杂,但是如果makefile构建完成之后,使用也非常方便。