zl程序教程

您现在的位置是:首页 >  其他

当前栏目

Keil MDK-ARM: 将二进制文件包含到程序中(使用汇编语言指令INCBIN)

文件程序ARM二进制 指令 包含 汇编语言 Keil
2023-09-11 14:22:09 时间

有时我们需要在主控芯片的代码里访问二进制文件的数据,这个二进制文件可能是校验数据、索引数据表、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 Developericon-default.png?t=L892https://developer.arm.com/documentation/ka002916/latest