有时我们需要在主控芯片的代码里访问二进制文件的数据,这个二进制文件可能是校验数据、索引数据表、FPGA程序镜像或者其他由外部工具生成的数据。
这和我们在PC上可以使用数据库或者直接打开这个文件访问不同,需要在源代码级别上直接访问这些数据。
那么,在Keil MDK-ARM上如何实现这个功能呢?
下面介绍一种方法,在汇编语言中使用INCBIN指令,直接将二进制数据文件包含到汇编源代码文件中。
这就类似于我们在C语言源文件中,使用include预处理指令包含头文件。
首先我们打开Keil,创建一个uVision project,选一款芯片,比如STM32L4。
在Manage Run-Time Environment中,添加相应的startup.s文件和CMSIS core,具体可参见本博主文章“ARM KEIL: MDK5 Software Packs”。
然后还要添加C源文件,来提供main函数的定义,否则编译时会提示下面错误:
Build started: Project: TestKeil
*** Using Compiler 'V6.14.1', folder: 'C:\Keil_v5\ARM\ARMCLANG\Bin'
Build target 'Target 1'
assembling startup_stm32l4r9xx.s...
compiling system_stm32l4xx.c...
linking...
.\Objects\TestKeil.axf: Error: L6218E: Undefined symbol main (referred from entry9a.o).
Not enough information to list image symbols.
Not enough information to list load addresses in the image map.
Finished: 2 information, 0 warning and 1 error messages.
".\Objects\TestKeil.axf" - 1 Error(s), 0 Warning(s).
Target not created.
Build Time Elapsed: 00:00:01
所以我们创建一个简单main.c文件。
先在工程中创建一个group,名为src,然后右键点击src,选择新创建一个文件或添加已有文件均可。
文件内容如下:
#include <stdio.h>
int main()
{
printf("Hello world!");
return 0;
}
将main.c文件添加到工程中,注意添加时默认时image file类型,要改成c source file类型。
如果是添加已有文件要注意文件类型,如果是使用新创建文件的话,类型在创建时就正确指定了。
如果文件格式没有设置正确,会出现编译错误:
Build started: Project: TestKeil
*** Using Compiler 'V6.14.1', folder: 'C:\Keil_v5\ARM\ARMCLANG\Bin'
Build target 'Target 1'
FCARM - Output Name not specified, please check 'Options for Target - Utilities'
Target not created.
Build Time Elapsed: 00:00:00
菜单栏里选择Project -> Build Target (F7),编译成功:
Build started: Project: TestKeil
*** Using Compiler 'V6.14.1', folder: 'C:\Keil_v5\ARM\ARMCLANG\Bin'
Build target 'Target 1'
compiling main.c...
assembling startup_stm32l4r9xx.s...
compiling system_stm32l4xx.c...
linking...
Program Size: Code=3280 RO-data=488 RW-data=12 ZI-data=1892
".\TestKeil.axf" - 0 Error(s), 0 Warning(s).
Build Time Elapsed: 00:00:01
然后我们找到一个bin文件,比如MyBinFile1.bin,将其放在工程根目录下。
接着再创建一个汇编文件,并添加到工程中。
如果文件类型不正确,会有提示:
在binaryData.s中,添加如下代码,注意格式,开头没有空格的行是label定义,有空格或tab键的行表示此行是指令。
AREA MyBinFile1_Section, DATA, READONLY
EXPORT MyBinaryImage1
; Includes the binary file MyBinFile1.bin from the current source folder
MyBinaryImage1
INCBIN MyBinFile1.bin
MyBinaryImage1_End
; Use a relative or absolute path to other folders if necessary
; INCBIN c:\project\MyBinFile1.bin
; Add further binary files to merge them if necessary
; INCBIN MyBinFile2.bin
; define a constant which contains the size of the image above
MyBinaryImage1_length
DCD MyBinaryImage1_End - MyBinaryImage1
EXPORT MyBinaryImage1_length
END
上面代码的含义,
首先是AREA指令,用来在汇编中定义一个section用来存放下面的数据或代码,这里还指定了只读属性。可以选择:DATA / CODE , READONLY / READWRITE。
如果没有上面这行,此汇编文件编译不过去的,提示:
*** Using Compiler 'V6.14.1', folder: 'C:\Keil_v5\ARM\ARMCLANG\Bin'
"binaryData.s", line 5: Fatal error: A1355U: A Label was found which was in no AREA
5 00000000 MyBinaryImage1
1 Error, 0 Warnings
ArmClang: error: armasm command failed with exit code 8 (use -v to see invocation)
assembling binaryData.s...
"binaryData.s" - 1 Error(s), 0 Warning(s).
section是由链接器生成的独立的命名块,包含代码或数据。
EXPORT指令是声明object文件或库文件中一个符号,可以被链接器使用,用来解析符号引用。
符号名是大小写区分的。
MyBinaryImage1和MyBinaryImage1_End是两个label,表示索引地址。
中间的INCBIN指令包含一个二进制文件的内容。
这个文件不用包含在工程中,可以使用相对路径或绝对路径,上例即使用的相对路径,MyBinFile1.bin和binaryData.s文件在同一目录下。
接下来定义了二进制数据长度,定义了lable - MyBinaryImage1_length。
DCD指令定义了一个常数,分配了4个字节。
MyBinaryImage1_length这个label再导出符号,就是表示二进制文件的长度。
END最后一句表示整个汇编程序的结束。
修改main.c文件,声明binaryData.s里面导出的变量符号,然后使用变量数据,如果不使用变量数据,编译时就不会将这些数据生成到最后的输出目标文件里。
// these variables are defined in the assembler source file MyBinFile1.s and
// represent the data from MyBinFile1.bin
extern const unsigned char MyBinaryImage1[];
extern const unsigned long MyBinaryImage1_length;
#include <stdio.h>
int main()
{
printf("Hello world!:%d", MyBinaryImage1[0]);
return 0;
}
F7编译:
Rebuild started: Project: TestKeil
*** Using Compiler 'V6.14.1', folder: 'C:\Keil_v5\ARM\ARMCLANG\Bin'
Rebuild target 'Target 1'
compiling system_stm32l4xx.c...
assembling binaryData.s...
assembling startup_stm32l4r9xx.s...
compiling main.c...
linking...
Program Size: Code=3660 RO-data=66028 RW-data=12 ZI-data=1892
".\TestKeil.axf" - 0 Error(s), 0 Warning(s).
Build Time Elapsed: 00:00:01
注意对比前面编译成功的结果:
Program Size: Code=3280 RO-data=488 RW-data=12 ZI-data=1892
和当前的:
Program Size: Code=3660 RO-data=66028 RW-data=12 ZI-data=1892
我们看到RO-data变大了很多,
本例中MyBinFile1.bin大小为64KB,65536 + 488 + 4 = 66028。
默认输出文件格式为axf文件,TestKeil.axf。
需要hex文件格式:
需要bin文件格式:
After Buid/Rebuid 选项下的Run#1 打上勾,在后一格添加一行代码,此代码的功能为将Objects下的TestKeil.axf文件转换为project.bin文件输出至Objects文件夹下。其中.axf文件为keil编译过程产生的文件。
fromelf --bin --output Objects\project.bin Objects\TestKeil.axf
注意生成axf文件的路径,需要设置objects目录位置:
将生成的project.bin文件和我们包含的MyBinFile1.bin文件相比,发现MyBinFile1.bin里面的内容是被全部包含进project.bin里面的。
上面使用的fromelf 是keil安装目录下的可执行程序。
使用Everything搜索,发现两个版本。
我们来确认下我们用的是哪个,打开项目选项设置,查看我们当前使用的ARM编译器是Compiler version 6,而汇编器是armclang。
打开Keil uVision的帮助文档,在Help -> uVision Help。
上面介绍Arm Compiler 6是基于LLVM架构的编译器工具链,用来编译ARM裸片程序(区分于Linux下)。
包含了armclang编译器、armlink链接器等。
其中fromelf,是一个将Arm ELF镜像转换为二进制文件的工具。
所以我们用的fromelf是ARMCLANG版本的。
ARM Compiler 5 (and earlier versions) use the armcc compiler. ARM Compiler 6 replaces armcc with armclang。
具体的fromelf的命令使用方式,命令行参数详情请参阅帮助文档。
参考:
Documentation – Arm Developer
https://developer.arm.com/documentation/ka002916/latest
————————————————
版权声明:本文为CSDN博主「夜流冰」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/guoqx/article/details/120282118