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

本文指导如何在Keil MDK-ARM环境下,使用汇编语言的INCBIN指令将二进制文件内容直接集成到代码中,包括创建工程、配置C源文件、添加汇编文件并处理文件类型,以及如何在main.c中引用和使用这些二进制数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

有时我们需要在主控芯片的代码里访问二进制文件的数据,这个二进制文件可能是校验数据、索引数据表、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

### 解决方案 当遇到向项目添加模块时出现 `setSdk JavaSDK not registered in ProjectJdkTable` 错误的情况,通常是因为所选的 SDK 尚未在 IntelliJ IDEA 中正确配置或注册。以下是详细的解决方案: #### 配置全局和项目级别的 JDK 设置 确保已安装并配置了所需的 JDK 版本。通过设置全局和特定项目的 JDK 来解决问题。 1. 打开 **File | Settings** 对话框(对于 macOS 用户,则为 **IntelliJ IDEA | Preferences**),导航至 **Build, Execution, Deployment | Build Tools | Maven | Importing** 或者 **Project Structure | Project**。 2. 在此界面中确认选择了有效的 JDK 路径。如果列表为空或者显示错误路径,请点击右侧的按钮来指定新的 JDK 安装位置[^1]。 #### 添加缺失的 SDK 到 ProjectJdkTable 有时即使已经指定了正确的 JDK,在某些情况下仍然会报告该问题。此时可以尝试手动将缺少的 SDK 注册到 `ProjectJdkTable` 表中。 ```java // 获取当前项目中的所有模块 Module[] modules = ModuleManager.getInstance(project).getModules(); for (Module module : modules) { // 查找是否有任何模块设置了无效的 SDK Sdk sdk = ModuleRootManager.getInstance(module).getSdk(); if (sdk == null || !ProjectJdkTable.getInstance().getAllJdks().contains(sdk)) { // 如果发现有无效的 SDK,则重新分配有效的一个 final Sdk validSdk = selectValidSdk(); // 自定义方法用于选择合适的 SDK ModifiableRootModel model = ModuleRootManager.getInstance(module).getModifiableModel(); model.setSdk(validSdk); model.commit(); } } ``` 上述代码片段展示了如何遍历所有模块,并针对那些具有不合法 SDK 的情况更新它们以指向一个有效的 SDK 实例。 #### 清理缓存与重启 IDE 执行清理操作可以帮助消除由于临时文件损坏或其他原因引起的构建问题。关闭 IDE 后删除 `.idea` 文件夹以及整个工程目录下的 `target` 和其他编译输出文件夹;之后再次启动 IDE 并导入项目即可恢复正常工作状态。 #### 使用命令行工具验证环境变量 最后还可以利用命令提示符窗口输入如下指令测试 JAVA_HOME 是否被正确设定: ```bash echo %JAVA_HOME% javac -version ``` 这有助于排除因系统级别配置不当而导致的问题。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夜流冰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值