前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >【嵌入式】嵌入式项目手动与自动化构建流程详解

【嵌入式】嵌入式项目手动与自动化构建流程详解

作者头像
LuckiBit
发布2025-03-06 08:58:22
发布2025-03-06 08:58:22
11700
代码可运行
举报
文章被收录于专栏:C语言C语言
运行总次数:0
代码可运行
摘要

在嵌入式开发过程中,从源代码编写到最终程序烧录涉及多个关键步骤,这些步骤在手动方式和自动化方式下有所不同。本文详细介绍了8个嵌入式项目处理流程:源代码编写、预处理、编译、汇编、链接、生成二进制和 HEX 文件、烧录以及清理。每个流程都有其独立的操作命令,并对不同的开发工具链进行详细讲解。

1. 嵌入式项目文件结构

代码语言:javascript
代码运行次数:0
复制
├── src/                # 源代码文件夹
│   ├── main.c          # 主程序
│   ├── drivers.c       # 驱动程序
│   ├── startup.s       # 启动文件
│   ├── utils.c         # 工具函数
│   └── common_defs.h   # 公共定义头文件
├── build/              # 编译输出文件夹
│   ├── main.i          # 预处理后的主程序
│   ├── drivers.i       # 预处理后的驱动程序
│   ├── startup.i       # 预处理后的启动文件
│   ├── main.s          # 汇编代码 - 主程序
│   ├── drivers.s       # 汇编代码 - 驱动程序
│   ├── startup.s       # 汇编代码 - 启动文件
│   ├── main.o          # 编译后的目标文件 - 主程序
│   ├── drivers.o       # 编译后的目标文件 - 驱动程序
│   ├── startup.o       # 编译后的目标文件 - 启动文件
│   ├── utils.o         # 编译后的目标文件 - 工具函数
│   ├── main.elf        # 可执行文件
│   ├── main.bin        # 烧录用二进制文件
│   └── main.hex        # 烧录用 HEX 文件
├── linker_script.ld    # 链接脚本
└── Makefile            # Makefile 构建系统(可选)
文件解释
  • src/:存放项目的源代码和头文件。
    • main.c:主程序代码,包含应用的主逻辑。
    • drivers.c:硬件相关驱动文件,例如 LED、按键等设备控制。
    • startup.s:启动代码,负责设置堆栈指针、跳转到主程序入口。
    • utils.c:辅助工具函数,如延时、其他常用功能。
    • common_defs.h:公共定义的头文件,包含宏定义、常量、外部函数声明等。
  • build/:编译过程中的中间文件和最终生成的文件都存放在这里。
    • .i 文件:预处理文件,包含头文件展开、宏替换等处理后的代码。
    • .s 文件:汇编代码文件。
    • .o 文件:目标文件,是编译过程中生成的机器码,尚未链接。
    • .elf 文件:最终的可执行文件,包含所有链接后的代码,可以用于调试。
    • .bin 文件:将 .elf 文件转换后的二进制文件,用于烧录到硬件中。
    • .hex 文件:可选的 HEX 格式文件,同样用于烧录。
  • linker_script.ld:链接脚本,定义内存布局和段的分配等。用于告诉链接器如何安排程序的代码和数据在内存中的位置。
  • Makefile(可选):构建系统脚本,描述如何编译源代码、链接目标文件,并生成最终的可执行文件。如果使用 Make 工具,可以使用该文件来自动化构建流程。

2. 手动编译流程

2.1 代码编写阶段

在此阶段,你编写并组织源代码文件和头文件。代码包括 .c 文件和 .h 文件,涉及的文件为 main.c, drivers.c, startup.s, 和 common_defs.h

2.2 预处理阶段

预处理操作会展开宏定义和包含的头文件,生成 .i 文件。可以通过以下命令生成:

代码语言:javascript
代码运行次数:0
复制
arm-none-eabi-gcc -E -o build/main.i src/main.c
arm-none-eabi-gcc -E -o build/drivers.i src/drivers.c
arm-none-eabi-gcc -E -o build/startup.i src/startup.s

生成的文件:

  • build/main.i:预处理后的主程序代码。
  • build/drivers.i:预处理后的驱动程序代码。
  • build/startup.i:预处理后的启动文件代码。
2.3 编译阶段

编译操作会将 .c.s 文件转化为汇编语言文件 .s。生成文件:

代码语言:javascript
代码运行次数:0
复制
arm-none-eabi-gcc -S -o build/main.s src/main.c
arm-none-eabi-gcc -S -o build/drivers.s src/drivers.c
arm-none-eabi-gcc -S -o build/startup.s src/startup.s
2.4 汇编阶段

汇编文件会生成目标文件 .o,目标文件是编译过程中的机器代码,准备链接成最终的可执行文件。

代码语言:javascript
代码运行次数:0
复制
arm-none-eabi-gcc -c -o build/main.o src/main.c
arm-none-eabi-gcc -c -o build/drivers.o src/drivers.c
arm-none-eabi-gcc -c -o build/startup.o src/startup.s
arm-none-eabi-gcc -c -o build/utils.o src/utils.c

生成的文件:

  • build/main.o
  • build/drivers.o
  • build/startup.o
  • build/utils.o
2.5 链接阶段

链接阶段将所有目标文件 .o 链接成一个可执行文件 .elf。你需要提供一个链接脚本 linker_script.ld,它会指定程序在内存中的位置。

代码语言:javascript
代码运行次数:0
复制
arm-none-eabi-ld -T linker_script.ld -o build/main.elf build/main.o build/drivers.o build/startup.o build/utils.o

生成的文件:

  • build/main.elf:最终的可执行文件。
2.6 格式转换阶段

将可执行文件 .elf 转换为 .bin.hex 格式,用于烧录到设备。

代码语言:javascript
代码运行次数:0
复制
arm-none-eabi-objcopy -O binary build/main.elf build/main.bin
arm-none-eabi-objcopy -O ihex build/main.elf build/main.hex

生成的文件:

  • build/main.bin
  • build/main.hex
2.7 烧录阶段

.bin 文件烧录到目标硬件中。可以使用烧录工具,如 st-flash

代码语言:javascript
代码运行次数:0
复制
st-flash write build/main.bin 0x8000000
2.8 调试阶段

在调试过程中,可以使用 GDB 等工具进行调试,加载 .elf 文件进行符号调试。

代码语言:javascript
代码运行次数:0
复制
arm-none-eabi-gdb build/main.elf -ex "target remote :3333"
2.9 总结

步骤

说明

生成文件类型

是否继续使用

1. 代码编写

编写源代码与配置文件

.c, .h, .s, .ld

2. 预处理

宏展开、头文件替换

.i

3. 编译

C 代码 → 汇编代码

.s

4. 汇编

汇编代码 → 机器码

.o

5. 链接

合并所有目标文件

.elf

6. 格式转换

可执行文件 → 烧录文件

.bin, .hex

7. 烧录

将程序写入 MCU

-

-

8. 调试

远程在线调试

.elf

以上就是嵌入式项目的完整手动编译流程,包含了详细的文件结构、文件生成步骤和命令。

3. 自动编译流程

3.1 Makefile 示例

接下来是如何通过 Makefile 来自动化构建和管理这个项目的过程。

代码语言:javascript
代码运行次数:0
复制
# 设置编译器、汇编器、链接器等工具
CC = arm-none-eabi-gcc
AS = arm-none-eabi-as
LD = arm-none-eabi-ld
OBJCOPY = arm-none-eabi-objcopy
OBJCPYFLAGS = -O binary
OBJCOPYHEXFLAGS = -O ihex

# 编译选项
CFLAGS = -g -Wall -mcpu=cortex-m3 -mthumb
AFLAGS = -g
LDFLAGS = -T linker_script.ld -nostartfiles -ffreestanding

# 源文件和目标文件
SRC = src/main.c src/drivers.c src/startup.s src/utils.c
OBJ = build/main.o build/drivers.o build/startup.o build/utils.o
DEPS = $(OBJ:.o=.d)

# 编译输出
OUTPUT = build/main.elf
BIN = build/main.bin
HEX = build/main.hex

# 默认目标
all: $(BIN)

# 编译源文件为目标文件
build/main.o: src/main.c
	$(CC) $(CFLAGS) -MMD -MF build/main.d -c $< -o $@

build/drivers.o: src/drivers.c
	$(CC) $(CFLAGS) -MMD -MF build/drivers.d -c $< -o $@

build/startup.o: src/startup.s
	$(AS) $(AFLAGS) -c $< -o $@

build/utils.o: src/utils.c
	$(CC) $(CFLAGS) -MMD -MF build/utils.d -c $< -o $@

# 生成可执行文件
$(OUTPUT): $(OBJ)
	$(LD) $(LDFLAGS) -o $@ $^

# 生成二进制文件
$(BIN): $(OUTPUT)
	$(OBJCOPY) $(OBJCPYFLAGS) $< $@

# 生成 HEX 文件
$(HEX): $(OUTPUT)
	$(OBJCOPY) $(OBJCOPYHEXFLAGS) $< $@

# 清理文件
clean:
	rm -f build/*.o build/*.d $(OUTPUT) $(BIN) $(HEX)

# 生成依赖文件
-include $(DEPS)
3.2 Makefile 详解
3.2.1 设置工具和变量
代码语言:javascript
代码运行次数:0
复制
CC = arm-none-eabi-gcc
AS = arm-none-eabi-as
LD = arm-none-eabi-ld
OBJCOPY = arm-none-eabi-objcopy
OBJCPYFLAGS = -O binary
OBJCOPYHEXFLAGS = -O ihex
  • CC:指定 C 编译器为 arm-none-eabi-gcc,适用于 ARM 架构的交叉编译器。
  • AS:指定汇编器为 arm-none-eabi-as
  • LD:指定链接器为 arm-none-eabi-ld
  • OBJCOPY:指定用于转换目标文件格式的工具,如生成二进制文件和 HEX 文件。
  • OBJCPYFLAGSOBJCOPYHEXFLAGS:为二进制文件和 HEX 文件转换设置标志。
3.2.2 编译选项
代码语言:javascript
代码运行次数:0
复制
CFLAGS = -g -Wall -mcpu=cortex-m3 -mthumb
AFLAGS = -g
LDFLAGS = -T linker_script.ld -nostartfiles -ffreestanding
  • CFLAGS :设置 C 编译器选项。
    • -g:生成调试信息。
    • -Wall:启用所有警告。
    • -mcpu=cortex-m3:指定目标 CPU 为 Cortex-M3。
    • -mthumb:启用 Thumb 模式,适合嵌入式编程。
  • AFLAGS:设置汇编编译选项(例如,启用调试信息)。
  • LDFLAGS :设置链接器选项。
    • -T linker_script.ld:指定链接脚本。
    • -nostartfiles:告诉链接器不要自动链接启动文件。
    • -ffreestanding:用于裸机编程,禁止标准库的调用。
3.2.3 源文件和目标文件
代码语言:javascript
代码运行次数:0
复制
SRC = src/main.c src/drivers.c src/startup.s src/utils.c
OBJ = build/main.o build/drivers.o build/startup.o build/utils.o
DEPS = $(OBJ:.o=.d)
  • SRC:源代码文件列表。
  • OBJ:目标文件列表,指定了每个源文件编译后生成的 .o 文件路径。
  • DEPS:自动生成的依赖文件列表,用于处理头文件的依赖。
3.2.4 生成可执行文件
代码语言:javascript
代码运行次数:0
复制
$(OUTPUT): $(OBJ)
	$(LD) $(LDFLAGS) -o $@ $^
  • $(OUTPUT):生成的最终可执行文件。
  • $(OBJ):依赖的目标文件列表。
  • (LD) (LDFLAGS) -o @ ^ :使用链接器生成可执行文件。 @ 是目标文件,即可执行文件。^ 是所有的依赖文件。
3.2.5 生成二进制文件和 HEX 文件
代码语言:javascript
代码运行次数:0
复制
$(BIN): $(OUTPUT)
	$(OBJCOPY) $(OBJCPYFLAGS) $< $@
  • $(BIN):二进制文件。
  • $(OBJCPYFLAGS):转换为二进制文件的标志。
  • $<:第一个依赖文件,即可执行文件。
  • $@:目标文件,即二进制文件。
代码语言:javascript
代码运行次数:0
复制
$(HEX): $(OUTPUT)
	$(OBJCOPY) $(OBJCOPYHEXFLAGS) $< $@
  • $(HEX):HEX 文件。
  • $(OBJCOPYHEXFLAGS):转换为 HEX 文件的标志。
3.2.6 清理文件
代码语言:javascript
代码运行次数:0
复制
clean:
	rm -f build/*.o build/*.d $(OUTPUT) $(BIN) $(HEX)
  • clean:删除生成的中间文件、目标文件、可执行文件、二进制文件和 HEX 文件。
3.2.7 自动生成依赖文件
代码语言:javascript
代码运行次数:0
复制
-include $(DEPS)
  • 使用 -include 使得 make 在找不到依赖文件时不会报错,而是跳过。

了解了 Makefile 的内容后,我们可以专注于 Makefile 中的 命令行 部分。接下来,我会逐步解释命令行中的各个命令,以及它们如何在嵌入式开发的构建过程中被使用。

3.3 基本的 make 命令

make 是一个自动化工具,它根据 Makefile 中的定义来编译和链接源代码。最常用的命令行是 makemake <target>

3.3.1 默认目标(make
代码语言:javascript
代码运行次数:0
复制
make
  • 执行 make 时,make 会查找 Makefile 并默认执行 Makefile 中的 第一个目标,一般是 all
  • 如果 Makefile 中没有定义 allmake 会选择第一个目标作为默认目标。
3.3.2 指定目标(make <target>
代码语言:javascript
代码运行次数:0
复制
make <target>
  • 你可以通过命令行指定一个特定的目标。make 会执行该目标的规则。
  • 例如,make clean 会执行 clean 目标的规则,清理所有编译生成的文件。
3.4 目标与命令行参数

Makefile 中定义的每个目标(如 all, build/main.o, clean)都可以通过命令行传递给 make 来执行。make 根据这些目标执行相关的规则。

目标 all
代码语言:javascript
代码运行次数:0
复制
all: $(BIN)
  • 当你执行 make 时,它默认执行 all 目标的规则。
  • all 目标依赖于 $(BIN),即 build/main.bin
目标 clean
代码语言:javascript
代码运行次数:0
复制
clean:
	rm -f build/*.o build/*.d $(OUTPUT) $(BIN) $(HEX)
  • make clean 会执行 clean 目标的规则,删除构建过程中生成的所有文件,包括 .o 文件、.d 文件、最终的可执行文件和二进制文件。
3.5 常用命令行选项

make 还支持一些常用的命令行选项,用来控制编译和构建过程。以下是一些常用的选项:

-f 选项:指定 Makefile 文件
代码语言:javascript
代码运行次数:0
复制
make -f Makefile.custom
  • -f 选项允许你指定一个自定义的 Makefile 文件。例如,make -f Makefile.custom 会使用 Makefile.custom 文件来构建项目,而不是默认的 Makefile
-j 选项:并行构建
代码语言:javascript
代码运行次数:0
复制
make -j4
  • -j 选项允许你指定并行构建的任务数。例如,make -j4 会启动 4 个并行进程来加速构建过程。
  • 这个选项在多核处理器上尤其有用,可以显著提高构建速度。
-n 选项:仅显示命令而不执行
代码语言:javascript
代码运行次数:0
复制
make -n
  • -n 选项会告诉 make 显示每个目标的执行命令,但不执行实际操作。这对于调试 Makefile 非常有用,可以查看 make 将会执行哪些命令。
-B 选项:强制重新构建
代码语言:javascript
代码运行次数:0
复制
make -B
  • -B 选项强制 make 即使没有检测到依赖文件的变化,也重新构建所有目标。这个选项通常用于清理旧的文件并强制重新构建。
-k 选项:忽略错误并继续构建
代码语言:javascript
代码运行次数:0
复制
make -k
  • -k 选项在构建过程中遇到错误时,继续执行剩余的目标。这对于构建多个目标时检查不同的错误非常有帮助。
3.6 如何使用 Makefile 中的命令行

根据 Makefile 的结构,你可以执行不同的目标。以下是如何使用命令行与 Makefile 配合工作的详细示例:

示例 1:构建默认目标

假设 Makefile 中有一个默认目标 all,当你在命令行中运行 make 时,它将自动执行该目标。

代码语言:javascript
代码运行次数:0
复制
make
  • 执行 make 命令时,make 会编译所有源文件,并生成 main.bin 文件。
示例 2:单独编译某个目标文件

如果你只想编译某个单独的源文件,而不是整个项目,可以执行相应的目标,如 build/main.o

代码语言:javascript
代码运行次数:0
复制
make build/main.o
  • 这会使用 make 编译 main.c 文件,并生成 main.o 文件。
示例 3:清理构建文件

运行 clean 目标来删除所有中间文件和最终生成的文件:

代码语言:javascript
代码运行次数:0
复制
make clean
  • 这会删除 build/ 目录下的 .o 文件、.d 文件、main.elfmain.binmain.hex 文件。
示例 4:并行构建

如果你的项目包含多个目标文件,并且你有一个多核处理器,使用 -j 选项可以加速构建过程:

代码语言:javascript
代码运行次数:0
复制
make -j4
  • 这会启动 4 个并行进程来执行构建任务,从而加速整个过程。
示例 5:仅显示命令不执行

在调试 Makefile 时,你可能只想查看 make 会执行哪些命令,而不实际执行它们:

代码语言:javascript
代码运行次数:0
复制
make -n
  • 这会列出所有的命令,但不会执行任何操作。
3.7 总结
  • make 是一个非常强大的工具,可以根据 Makefile 中的规则自动化构建过程。
  • 使用命令行来控制构建过程,可以通过指定不同的目标(如 clean)来实现不同的功能。
  • 使用选项(如 -j 来并行构建,-n 来仅显示命令)可以让构建过程更加灵活和高效。

如果你在使用 make 命令时遇到问题,查看 Makefile 里的规则和目标,确保你正在执行正确的目标,并理解每个目标的操作。

4. 嵌入式项目处理流程摘要

在嵌入式开发中,项目从源代码编写到最终烧录的过程涉及多个步骤,可以分为手动和自动两种方式。以下是8个主要的处理流程,概述了每个流程如何操作及其在整个开发过程中的角色。

4.1 源代码编写
  • 手动操作:开发人员编写 C、C++、汇编等源代码文件,包含项目的逻辑实现(如 main.cdrivers.c 等),并定义系统中各个模块的功能。
  • 自动化操作:无,完全由开发人员手动进行源代码编写。
4.2 预处理
  • 手动操作:开发人员通过命令手动编译源代码,执行预处理器来生成 .i 文件(如 main.i),这一步会展开宏定义、包含头文件等。
  • 自动化操作:在 Makefile 中配置相应的编译规则,运行 make 命令时自动进行预处理。
4.3 编译
  • 手动操作:开发人员手动运行编译器(如 gcc)将源代码(.c 文件)编译成目标文件(.o 文件),例如 main.o
  • 自动化操作:通过 Makefile 中的规则,使用命令如 (CC) (CFLAGS) -c 自动执行编译任务,无需手动输入每个命令。
4.4 汇编
  • 手动操作:对于汇编源代码(如 startup.s),开发人员手动运行汇编器(如 as)将汇编文件编译成目标文件(.o 文件)。
  • 自动化操作:通过 Makefile 的自动规则,使用 $(AS) 命令自动汇编汇编文件,生成 .o 文件。
4.5 链接
  • 手动操作:开发人员手动使用链接器(如 ld)将目标文件链接成可执行文件(.elf)。
  • 自动化操作Makefile 中定义了链接规则,运行 make 时,自动调用链接器将所有目标文件链接成最终的可执行文件。
4.6 生成二进制和 HEX 文件
  • 手动操作:开发人员手动运行工具(如 objcopy)将 .elf 文件转换为二进制文件(.bin)或 HEX 文件(.hex),供烧录到设备。
  • 自动化操作:通过 Makefile 的规则,运行 $(OBJCOPY) 自动生成二进制文件和 HEX 文件,准备烧录。
4.7 烧录
  • 手动操作:开发人员手动使用烧录工具将生成的 .bin.hex 文件烧录到目标嵌入式设备。
  • 自动化操作:可以在 Makefile 中添加自动烧录命令,通过连接设备并运行 make 完成烧录。
4.8 清理
  • 手动操作:开发人员手动删除临时生成的文件(如 .o.d.elf 文件等),以保持工作目录整洁。
  • 自动化操作:通过 make clean 命令,Makefile 自动清理所有临时文件和构建输出,减少手动干预。

5. 总结

嵌入式项目的处理流程可以通过手动或者自动化方式完成。使用 Makefile 可以实现大部分步骤的自动化,包括源代码编译、目标文件生成、链接、二进制和 HEX 文件生成等。通过自动化流程,能够显著提高开发效率并减少人为错误,尤其适用于较为复杂和重复的项目构建过程。

6. 结束语

  1. 本节内容已经全部介绍完毕,希望通过这篇文章,大家对嵌入式项目的处理流程有了更深入的理解和认识。
  2. 感谢各位的阅读和支持,如果觉得这篇文章对你有帮助,请不要吝惜你的点赞和评论,这对我们非常重要。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-03-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 嵌入式项目文件结构
    • 文件解释
  • 2. 手动编译流程
    • 2.1 代码编写阶段
    • 2.2 预处理阶段
    • 2.3 编译阶段
    • 2.4 汇编阶段
    • 2.5 链接阶段
    • 2.6 格式转换阶段
    • 2.7 烧录阶段
    • 2.8 调试阶段
    • 2.9 总结
  • 3. 自动编译流程
    • 3.1 Makefile 示例
    • 3.2 Makefile 详解
      • 3.2.1 设置工具和变量
      • 3.2.2 编译选项
      • 3.2.3 源文件和目标文件
      • 3.2.4 生成可执行文件
      • 3.2.5 生成二进制文件和 HEX 文件
      • 3.2.6 清理文件
      • 3.2.7 自动生成依赖文件
    • 3.3 基本的 make 命令
      • 3.3.1 默认目标(make)
      • 3.3.2 指定目标(make <target>)
    • 3.4 目标与命令行参数
      • 目标 all:
      • 目标 clean:
    • 3.5 常用命令行选项
      • -f 选项:指定 Makefile 文件
      • -j 选项:并行构建
      • -n 选项:仅显示命令而不执行
      • -B 选项:强制重新构建
      • -k 选项:忽略错误并继续构建
    • 3.6 如何使用 Makefile 中的命令行
      • 示例 1:构建默认目标
      • 示例 2:单独编译某个目标文件
      • 示例 3:清理构建文件
      • 示例 4:并行构建
      • 示例 5:仅显示命令不执行
    • 3.7 总结
  • 4. 嵌入式项目处理流程摘要
    • 4.1 源代码编写
    • 4.2 预处理
    • 4.3 编译
    • 4.4 汇编
    • 4.5 链接
    • 4.6 生成二进制和 HEX 文件
    • 4.7 烧录
    • 4.8 清理
  • 5. 总结
  • 6. 结束语
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档