Keil使用命令行附加预定义宏编译
Posted -飞鹤-
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Keil使用命令行附加预定义宏编译相关的知识,希望对你有一定的参考价值。
1. 前言
很多时候,一份Keil工程代码可能需要满足多个不同的应用场景。可以通过逻辑判断,将多个不同的点集成在一份代码之中,但是嵌入式往往特别关注RAM空间,集成过多的逻辑判断,RAM空间可能就不够用了。针对这种情况Keil提供了配置Target,通过宏定义来分隔不同的功能,编译生成不同的bin文件。
但是有时,一个Target项目中,另外又有几个子场景,子场景也需要用预定义宏来区分开。但是Keil并没有提供这样的功能。
另外,Keil工程有非常多的Target,一次修改,必须不停选择不同Target来进行编译,这种编译方式非常不方便。
2. 方法
Keil提供了命令行,可以编译工程。另外,Keil使用的ArmCC和Armlink来编译和链接,这样的话,便可以使用makefile来编译Keil工程代码。
2.1. 命令行
2.1.1 用法
UV4 [command] [projectfile]
2.1.1.1. command
command可以是下述列表之一。如果command没有指定,则只是打开Keil工程。
- -b, 编译指定的Keil工程默认的Target。
UV4 -b PROJECT1.uvprojx
- -c, 清除Keil工程中所有Target的临时文件。
UV4 -c PROJECT1.uvprojx
- -cr, 清除所有Target,然后重新编译所有Target。
UV4 -cr PROJECT1.uvprojx
- -d, 启动Keil的调试模式。
UV4 -d PROJECT1.uvprojx
- -f, 下载程序到flash上,下载完成之后退出。
UV4 -f PROJECT1.uvprojx -t"MCB2100 Board"
- -r, 重新编译Keil工程的默认Target,或用-t指定Target。
UV4 -r PROJECT1.uvprojx
UV4 -r PROJECT1.uvprojx -t"Simulator"
- -5, 转换Keil4工程为Keil5工程。
UV4 -5 myoldproject.uvproj -l log.txt
- -et, 导出工程的Target的配置。
- -ep, 导出工程所有Target配置。
- -X, 生成预处理符号文件。
- -X1, 为所有Target生成预处理符号文件。
2.1.3. Option
下面的选项是可选项。
- -j0, 隐藏Keil的UI。
- -i, 创建一个新的工程,或通过XML文件更新存在的工程。
- -l logfile,保存命令生成的输出到logfile中。
- -n device_name, 创建一个指定device_name的工程。
UV4 MyProject.uvprojx -n Device123
- -np device_name, 如果工程不存在,则指定device_name创建工程。如果工程存在,则更新所有target的device_name。
- -o outputfile, 指定输出的Log文件。
UV4 -r PROJECT1.uvprojx -o"output.log"
- -q, 重新编译多工程指定的Target。
- -t, 指定工程的target。
UV4 -r PROJECT1.uvprojx -t"MCB2100 Board"
- -x, 配合Debug模式命令-d使用,返回完整的命令输出。
- -y, 配合Debug模式命令-d使用,返回通用的配置。
- -z, 重新编译工程的所有Target。
UV4 -b PROJECT1.uvproj -z
2.1.4. ERRORLEVEL
Kiel编译完成后的错误码
ERRORLEVEL | 描述 |
---|---|
0 | 无错误无敬告 |
1 | 仅有敬告 |
2 | 错误 |
3 | 重大错误 |
11 | 不能打开工程 |
12 | 给定的设备不存在 |
13 | 写工程文件出错 |
15 | 读XML文件出错 |
20 | 转换工程出错 |
2.1.5. 指定多预定义宏
为一个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
2.2. makefile
2.2.1. Keil编译过程
Keil使用ArmCC编译源文件,使用Armlink链接目录文件生成efl文件,然后调用fromelf转换为bin文件。
那么ArmCC和Armlink如何使用呢?Keil提供一种方法来展示如何使用ArmCC和Armlink。如下图,勾选Create Batch File,会将ArmCC和Armlink编译链接的过程输出到相应的文件中。
编译当前工程指定的Test,编译连接的命令过程生成在test.bat文件中,如下图:
- startup_mo_ia文件主要是编译汇编文件,内容为:
实际的调用命令为:
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”
- main._ip,这个主要是生成汇编文件,一般情况可以不使用。
- main._i文件主要是编译.c文件,内容为:
实际调用的过程为:
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”
- scan.lnp,主要是描述连接的过程。
实际的连接过程为:
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
2.2.2. makefile内容
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
2.2.3. 多预编译宏
利用批处理,添加MACRO参数控制预编译宏。
echo off
set list=__AAA_ __BBB__ __CCCC__ __DDDD__
for %%n in (%list%) do (
make clean
make MACRO=%%n )
3. 其他
3.1. 总结
命令行的方式使用简单,但是灵活度不够,例如一个Target不能指定宏。Makefile比较灵活,但是前期构建工程会比较复杂,但是如果makefile构建完成之后,使用也非常方便。
3.2. 参考
以上是关于Keil使用命令行附加预定义宏编译的主要内容,如果未能解决你的问题,请参考以下文章