纸翼 · 加载中
5930 words
30 minutes
i.mx6ull裸机C语言程序开发流程&相关寄存器使用

i.MX6ULL 裸机开发启动流程笔记#

一、整体启动流程概述#

在 i.MX6ULL 这类 ARM Cortex-A7 处理器的裸机开发中,程序启动分为三个关键阶段:

  1. 汇编初始化:完成处理器模式切换和栈指针设置。
  2. 硬件寄存器定义:通过头文件映射硬件寄存器地址。
  3. C 语言执行:跳转到 main 函数,开始业务逻辑开发。

二、汇编启动代码分析(start.s#

.global __start
_start:
/* 设置处理器进入 SVC 模式 */
mrs r0, cpsr /* 读取当前状态寄存器 CPSR 到 r0 */
bic r0, r0, #0x1f /* 清除 CPSR 的低 5 位(模式位 M[4:0])*/
orr r0, r0, #0x13 /* 将模式位设置为 0x13,对应 SVC 管理模式 */
msr cpsr, r0 /* 将修改后的值写回 CPSR,切换处理器模式 */
/* 设置 SP 指针 */
ldr sp, =0x80200000 /* 将栈指针指向 DDR 内存的 0x80200000 地址 */
/* 跳转到 C 语言 main 函数 */
b main /* 无条件跳转,进入 C 语言执行阶段 */

关键知识点#

  • SVC 模式:这是 ARM 处理器的特权模式,用于操作系统内核或裸机程序,拥有所有硬件访问权限。
  • 栈指针 SP:C 语言函数调用依赖栈来保存局部变量和返回地址。此处栈指向外部 DDR 内存,大小为 2MB,栈向下增长。
  • b 指令:这是无条件跳转指令,直接跳转到 main 函数,标志着程序从汇编进入 C 语言阶段。

三、硬件寄存器定义(main.h#

这个头文件的核心是将芯片物理寄存器地址映射为 C 语言可访问的变量,以实现硬件控制。

#ifndef __MAIN_H
#define __MAIN_H
/* 定义时钟控制寄存器(CCM) */
#define CCM_CCGR0 (*((volatile unsigned long*)0X020C4068))
#define CCM_CCGR1 (*((volatile unsigned int*)0X020C406C))
// ... 其他 CCGR 寄存器
/* 定义 IOMUX 配置寄存器 */
#define SW_MUX_GPIO1_IO03 (*((volatile unsigned int*)0X020E0068))
#define SW_PAD_GPIO1_IO03 (*((volatile unsigned int*)0X020E02F4))
/* 定义 GPIO1 控制寄存器 */
#define GPIO1_DR (*((volatile unsigned int*)0X0209C000))
#define GPIO1_GDIR (*((volatile unsigned int*)0X0209C004))
// ... 其他 GPIO 寄存器
#endif

关键知识点#

  • volatile 关键字:防止编译器对寄存器读写进行优化,确保每次访问都是真实的硬件操作。
  • 寄存器映射原理:通过指针直接访问物理内存地址,实现对硬件寄存器的直接读写。
  • 功能分类
    • CCM 寄存器:用于开启/关闭外设时钟,例如给 GPIO1 模块提供时钟。
    • IOMUX 寄存器:用于配置引脚的功能和电气属性(如上下拉、速度)。
    • GPIO 寄存器:用于控制引脚的输入输出方向和电平状态。
    • 可以模仿stm32使用结构体访问寄存器
    /* led初始化 */
    void led_init(void)
    {
    IOMUXC_SetPinMux(IOMUXC_SNVS_SNVS_TAMPER1_GPIO5_IO01, 0); /*复用为GPIO*/
    IOMUXC_SetPinConfig(IOMUXC_SNVS_SNVS_TAMPER1_GPIO5_IO01, 0X10B0);
    }

四、从汇编到 C 语言的衔接#

  1. 汇编初始化是基础:如果没有正确设置处理器模式和栈指针,C 语言程序会直接崩溃。
  2. 头文件是硬件接口main.h 是 C 语言与硬件之间的桥梁,所有硬件操作都依赖这些寄存器定义。
  3. 启动流程的意义:这个流程是所有 ARM 裸机开发的通用范式,掌握它可以快速适配其他 Cortex-A 系列处理器。

五、常见问题与注意事项#

  • 栈溢出风险:栈大小设置要合理,避免局部变量过多导致栈溢出。
  • 寄存器地址错误:必须严格参考芯片手册,错误的地址会导致硬件无响应或系统崩溃。
  • 模式切换错误:如果未进入 SVC 模式,后续硬件操作可能因权限不足而失败。

i.MX6ULL 裸机开发:链接脚本与 Makefile 联动笔记#


一、核心作用概述#

这两份文件是 i.MX6ULL 裸机程序编译链接的核心配置

  • imx6ul.lds:链接脚本,定义程序在内存中的布局,规定代码、数据段的存放位置与地址。
  • Makefile:编译构建脚本,自动化完成编译、链接、二进制转换、反汇编等流程。

二、链接脚本(imx6ul.lds)详解#

SECTIONS
{
. = 0x87800000; /* 整个程序的起始地址(DDR 范围内) */
.text:
{
start.o /* 启动代码必须放在最前面,保证执行入口 */
*(.text) /* 所有文件的代码段 */
}
.rodata ALIGN(4) : {*(.rodata*)} /* 只读数据段(4字节对齐) */
.data ALIGN(4) : {*(.data)} /* 已初始化数据段(4字节对齐) */
bss_start = .; /* BSS 段起始地址符号 */
.bss ALIGN(4) : {*(.bss) *(COMMON)} /* 未初始化数据段 */
bss_end = .; /* BSS 段结束地址符号 */
}

关键要点#

  1. 地址定位0x87800000 是程序加载地址,必须在 DDR 内存范围(0x80000000~0xFFFFFFFF)内,且与栈指针(0x80200000)无地址冲突。
  2. 段布局顺序:代码段 → 只读数据段 → 初始化数据段 → 未初始化数据段(BSS),符合 ARM 裸机程序的标准内存布局。
  3. BSS 段管理bss_startbss_end 用于在启动代码中对 BSS 段清零,保证未初始化全局变量默认值为 0。
  4. 入口保证start.o 优先链接,确保处理器上电后先执行汇编初始化代码。

三、Makefile 详解#

objs = start.o main.o
ledc.bin : $(objs)
# 使用链接脚本链接,生成 ELF 文件
arm-linux-gnueabihf-ld -Timx6ul.lds $^ -o ledc.elf
# 转换为纯二进制文件(用于烧录)
arm-linux-gnueabihf-objcopy -O binary -S ledc.elf $@
# 生成反汇编文件(用于调试)
arm-linux-gnueabihf-objdump -D -m arm ledc.elf > ledc.dis
# 编译 .c 文件
%.o : %.c
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -o $@ $<
# 编译 .S 汇编文件
%.o : %.S
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -o $@ $<
clean:
rm -rf *.o ledc.bin ledc.elf ledc.dis

关键要点#

  1. 链接阶段-Timx6ul.lds 指定使用自定义链接脚本,替代 -Ttext 选项,实现更精细的内存布局控制。
  2. 编译选项
    • -Wall:开启所有编译警告,便于排查代码问题。
    • -nostdlib:不链接标准 C 库(裸机环境无操作系统支持)。
    • -O2:开启二级优化,减小代码体积、提升执行效率。
    • -c:只编译不链接,生成 .o 目标文件。
  3. 二进制转换objcopy 将 ELF 格式转为 .bin 纯二进制文件,用于烧录到开发板。
  4. 反汇编objdump 生成 .dis 反汇编文件,方便调试和查看指令执行流程。
  5. 清理规则clean 目标删除所有编译生成的中间文件,便于重新构建。

四、两者联动逻辑#

  1. 编译阶段:Makefile 先将 start.Smain.c 编译为 start.omain.o
  2. 链接阶段ld 工具根据 imx6ul.lds 的布局规则,将 .o 文件链接为 ledc.elf,确保代码和数据段按指定地址存放。
  3. 输出阶段objcopy 生成可烧录的 ledc.binobjdump 生成调试用的反汇编文件。
  4. 调试支持:链接脚本定义的 bss_start/bss_end 可在启动代码中使用,配合 Makefile 构建出符合 C 语言规范的可执行程序。

五、关键注意事项#

  • 地址一致性:链接脚本的起始地址 0x87800000 必须与 DDR 内存范围、栈指针地址无冲突。
  • 入口文件顺序start.o 必须在链接脚本中优先链接,否则程序无法从正确的初始化入口执行。
  • BSS 段清零:需在启动汇编中添加清零 BSS 段的代码,利用 bss_startbss_end 符号完成初始化。
  • 工具链版本arm-linux-gnueabihf 交叉编译工具链需与 i.MX6ULL 架构匹配,避免编译错误。

CCM 寄存器核心解析(i.MX6ULL 裸机开发必备)#

CCM 是 Clock Control Module 的缩写,直译是时钟控制模块,它是 i.MX6ULL 芯片的「时钟总控中心」,核心作用是为芯片内部所有外设(GPIO、UART、SPI、I2C 等)和核心模块分配、开启/关闭、配置时钟频率,是裸机开发中操作硬件的第一步必配寄存器(外设时钟未开启时,无论怎么配置寄存器,硬件都无响应)。

一、CCM 寄存器的核心作用#

i.MX6ULL 芯片上电后,内部时钟不会默认给所有外设供电,原因有二:

  1. 节能:未使用的外设关闭时钟,减少芯片功耗;
  2. 稳定:按需开启时钟,避免无用时钟信号干扰硬件工作。

而 CCM 寄存器就是用来精准控制每个外设的时钟开关和频率,简单说:操作任何外设前,必须先通过 CCM 寄存器给该外设「打开时钟」(比如操作 GPIO1 之前,要先开启 GPIO1 对应的时钟)。

二、i.MX6ULL 中 CCM 的关键寄存器:CCM_CCGRx 系列#

你在之前的 main.h 中看到的 CCM_CCGR0CCM_CCGR1CCM_CCGR2 等是 CCM 最常用的子寄存器,全称是 Clock Control Gate Register(时钟控制门寄存器),也是裸机开发中唯一高频使用的 CCM 寄存器(其他 CCM 寄存器用于配置系统时钟频率,入门阶段无需修改)。

1. CCGRx 寄存器的工作原理#

每个 CCM_CCGRx 都是 32 位的寄存器,每 2 个二进制位对应一个外设的时钟控制位,通过配置这 2 位的取值,决定对应外设的时钟状态,核心配置规则(通用):

2 位取值时钟状态说明
00关闭外设无时钟,完全休眠,无法操作
01运行模式开启芯片运行时开启时钟,休眠时关闭
10保留芯片厂商预留,开发中禁止使用
11始终开启无论芯片运行/休眠,时钟一直开启(裸机开发首选配置,简单直接)

2. 入门核心操作:一键开启所有外设时钟#

裸机开发(比如 LED 点灯、UART 打印)的入门阶段,无需精准配置单个外设的时钟,最简便的方式是直接将所有 CCGRx 寄存器赋值为 0XFFFFFFFF(32 位全 1),实现「所有外设时钟始终开启」,避免逐个配置的麻烦,代码如下(C 语言):

// 引用之前定义的 CCM 寄存器
CCM_CCGR0 = 0XFFFFFFFF;
CCM_CCGR1 = 0XFFFFFFFF;
CCM_CCGR2 = 0XFFFFFFFF;
CCM_CCGR3 = 0XFFFFFFFF;
CCM_CCGR4 = 0XFFFFFFFF;
CCM_CCGR5 = 0XFFFFFFFF;
CCM_CCGR6 = 0XFFFFFFFF;

这行代码是 i.MX6ULL 裸机程序 main 函数的第一行必写代码,写完再配置 GPIO、UART 等外设。

三、CCM 寄存器在裸机开发中的使用流程(以 GPIO1 点灯为例)#

结合之前的知识,完整的硬件操作流程会包含 CCM 配置,步骤如下:

  1. 汇编初始化:设置 SVC 模式、初始化栈指针、清零 BSS 段,跳转到 C 语言 main 函数;
  2. 开启时钟:在 main 函数开头,通过 CCM_CCGRx 寄存器开启 GPIO1 时钟(或全外设时钟);
  3. 配置引脚:通过 IOMUX 寄存器将 GPIO1_IO03 配置为 GPIO 功能;
  4. 配置 GPIO:通过 GPIO1_GDIR 设为输出模式,GPIO1_DR 控制电平高低;

四、关键注意点#

  1. 地址固定:i.MX6ULL 的 CCM 寄存器基地址为 0x020C4000,你之前定义的 CCM_CCGR0 = 0X020C4068 就是该基地址下的偏移地址,必须严格参考芯片手册,地址错误会导致时钟配置无效
  2. 先时钟后外设:这是裸机开发的「铁律」,如果跳过 CCM 配置直接操作外设,无论寄存器配置多正确,硬件都不会有任何响应;
  3. 入门无需深入:CCM 模块还有时钟分频、倍频、源时钟选择等复杂功能(对应其他寄存器),入门阶段(点灯、串口)只需掌握「开启时钟」这一个操作即可,后续开发复杂外设时再深入。

总结#

  1. CCM 是芯片的时钟总控中心,核心负责外设时钟的开启/关闭和频率配置;
  2. 裸机开发中最常用的是 CCM_CCGRx 系列寄存器,入门直接赋值 0XFFFFFFFF 全开启即可;
  3. 操作任何外设前,必须先通过 CCM 开启对应时钟,这是硬件操作的前提;
  4. CCM 寄存器地址是固定的,需严格对照 i.MX6ULL 芯片手册。

用于BSP工程管理的makefile文件#

这份Makefile是ARM裸机开发标准化编译脚本,适配模块化BSP工程结构,实现多目录汇编/.c文件的自动化编译、链接、镜像生成,同时提供调试、清理功能。笔记将按代码结构+核心语法+关键规则拆解,兼顾记忆和实际使用,所有语法均标注适用场景和作用。

一、文件整体概述#

核心功能#

  1. 自动检索分散在不同目录的.S(汇编)、.c(C语言)源文件;
  2. 统一将编译后的.o目标文件存至obj目录,保持工程整洁;
  3. 结合链接脚本imx6u.lds链接生成elf格式文件;
  4. 转换elf为可直接烧录的bin二进制镜像,生成dis反汇编文件(调试用);
  5. 提供clean(清理编译产物)、print(打印变量调试)辅助功能。

适用场景#

ARM裸机开发(如IMX6U开发板)、交叉编译环境、模块化BSP工程(驱动/主程序/头文件分离)。

核心语法体系#

Makefile基础变量、内置函数、自动变量、静态模式规则、伪目标,是嵌入式Makefile的通用核心语法,可直接复用至其他ARM裸机项目。

二、分模块笔记(代码+语法+释义)#

模块1:交叉编译工具链定义(基础变量)#

# 变量定义(?= 若未定义则赋值,:= 直接赋值)
CROSS_COMPILE ?= arm-linux-gnueabihf-
TARGET ?= ledc
CC := $(CROSS_COMPILE)gcc
LD := $(CROSS_COMPILE)ld
OBJCOPY := $(CROSS_COMPILE)objcopy
OBJDUMP := $(CROSS_COMPILE)objdump

核心语法#

  1. 变量赋值符(Makefile核心,区别于普通编程)
    • ?=条件赋值,仅当变量未被定义时才赋值(适合预留可外部修改的变量,如工具链前缀、目标名);
    • :=直接赋值(立即展开),赋值后变量值固定,后续修改原变量不影响此变量(嵌入式开发首选,避免变量嵌套歧义); 补充:=延迟赋值(使用时才展开),易出嵌套问题,裸机开发极少用。
  2. 变量引用$(变量名),如$(CC)会展开为arm-linux-gnueabihf-gcc,是Makefile引用变量的唯一标准方式

代码释义#

  • CROSS_COMPILE:交叉编译工具链前缀,ARM架构专用,可外部修改;
  • TARGET:最终编译目标名,所有生成文件(ledc.elf/ledc.bin)均基于此;
  • CC/LD/OBJCOPY/OBJDUMP:封装交叉编译工具,分别为C编译器、链接器、格式转换工具、反汇编工具。

模块2:工程目录定义(纯变量,为后续检索做准备)#

# 头文件目录(续行符\:实现多行变量定义)
INCUDIRS := imx6u \
bsp/clk \
bsp/led \
bsp/delay
# 源文件目录
SRCDIRS := project \
bsp/clk \
bsp/led \
bsp/delay

核心语法#

  • 续行符\:Makefile中一行写不完时,用\结尾表示续行,注意\后无空格(否则会报错);
  • 多目录定义:按功能拆分目录(头文件/源文件、芯片级/板级驱动),适配BSP模块化思想。

代码释义#

  • INCUDIRS:所有头文件(.h)所在目录,后续编译器会通过此目录搜索头文件;
  • SRCDIRS:所有源文件(.S/.c)所在目录,后续会自动检索这些目录下的源文件。

模块3:源文件/头文件自动检索(内置函数核心)#

# 生成编译器头文件搜索参数:-I 目录1 -I 目录2
INCLUDE := $(patsubst %, -I %, $(INCUDIRS))
# 检索所有.S(汇编)、.c(C)源文件
SFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.S))
CFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c))
# 去除文件路径,仅保留文件名(如bsp/clk/clk.c → clk.c)
SFILENDIR := $(notdir $(SFILES))
CFILENDIR := $(notdir $(CFILES))
# 生成obj目录下的目标文件(如clk.S → obj/clk.o)
SOBJS := $(patsubst %, obj/%, $(SFILENDIR:.S=.o))
COBJS := $(patsubst %, obj/%, $(CFILENDIR:.c=.o))
OBJS := $(SOBJS)$(COBJS)
# 设置源文件搜索路径,Make自动在这些目录找源文件
VPATH := $(SRCDIRS)

核心语法(Makefile内置函数,嵌入式开发高频使用)#

所有函数格式:$(函数名 参数1, 参数2, ...),参数间用逗号+空格分隔,是Makefile批量处理的核心。

  1. patsubst 匹配模式, 替换模式, 处理对象模式替换函数,按规则批量替换字符串;
    • 例:$(patsubst %, -I %, $(INCUDIRS)) → 给INCUDIRS中每个目录加-I(编译器头文件搜索参数);
    • 例:$(SFILENDIR:.S=.o) → 把所有.S后缀替换为.o(简写形式,等价于patsubst %.S, %.o, $(SFILENDIR))。
  2. foreach 循环变量, 遍历集合, 执行操作循环函数,遍历集合中每个元素并执行操作;
    • 例:$(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.S)) → 遍历SRCDIRS每个目录,检索其中所有.S文件。
  3. wildcard 匹配规则通配符函数,检索指定目录下符合规则的文件,替代系统通配符*(Makefile中直接用*易失效);
    • 例:$(wildcard $(dir)/*.c) → 检索dir目录下所有.c文件。
  4. notdir 文件路径路径处理函数,去除文件的绝对/相对路径,仅保留文件名,方便统一存放目标文件。
  5. VPATH 路径集合:Makefile内置搜索路径变量,指定后Make会自动在这些目录中检索源文件(无需写完整路径),解决源文件分散问题。

代码释义#

  • INCLUDE:最终传给编译器的头文件参数,如-I imx6u -I bsp/clk
  • SFILES/CFILES:所有汇编/C源文件的完整路径,如bsp/led/led.c
  • SFILENDIR/CFILENDIR:仅保留源文件文件名,如led.c
  • SOBJS/COBJSobj目录下的目标文件,是最终编译的中间产物;
  • OBJS:所有目标文件集合,后续链接时直接使用。

模块4:伪目标声明#

# 声明clean为伪目标,避免与同名文件冲突
.PHONY:clean

核心语法#

  • .PHONY: 目标名声明伪目标,伪目标不是实际的文件,而是一个「命令标识」;
    • 作用:避免工程中存在与目标名(如clean)同名的文件时,Make误判目标已完成,导致命令不执行;
    • 规则:所有非文件生成类目标(如clean、print)都要声明为伪目标,嵌入式开发必加。

模块5:核心编译规则(静态模式规则+自动变量)#

Makefile的核心是「规则」,规则格式:目标: 依赖 \n 制表符 命令命令前必须用制表符\t,不能用空格,嵌入式开发高频报错点)。

规则1:最终镜像生成规则(主规则)#

# 目标:ledc.bin 依赖:所有.o文件(OBJS)
$(TARGET).bin : $(OBJS)
$(LD) -Timx6u.lds -o $(TARGET).elf $^ # 链接生成elf文件
$(OBJCOPY) -O binary -S $(TARGET).elf $@ # 转换为bin文件
$(OBJDUMP) -D -m arm $(TARGET).elf > $(TARGET).dis # 生成反汇编文件

规则2:汇编文件编译规则#

# 静态模式规则:为SOBJS中每个obj/%.o匹配对应的%.S源文件
$(SOBJS) : obj/%.o : %.S
$(CC) -Wall -nostdlib -c -O2 $(INCLUDE) -o $@ $<

规则3:C文件编译规则#

# 静态模式规则:为COBJS中每个obj/%.o匹配对应的%.c源文件
$(COBJS) : obj/%.o : %.c
$(CC) -Wall -nostdlib -c -O2 $(INCLUDE) -o $@ $<

核心语法1:静态模式规则(嵌入式裸机开发最核心规则#

格式:目标集合 : 目标模式 : 依赖模式,专门用于批量处理同类型文件(如所有汇编/所有C文件),替代逐行写规则的冗余操作。

  • 目标集合:要处理的所有目标文件(如$(SOBJS)所有汇编目标文件);
  • 目标模式:匹配目标的通用格式,用%作为通配符(代表任意长度字符串,区别于*,仅在模式中生效);
  • 依赖模式:根据%自动推导目标对应的依赖文件,实现「目标-依赖」的批量匹配。
  • 例:$(SOBJS) : obj/%.o : %.S → 遍历SOBJS中的obj/start.o,匹配obj/%.o得到%=start,自动推导依赖为start.S

核心语法2:自动变量(规则中专用,批量编译的关键)#

Makefile自动将「目标/依赖」赋值给内置变量,无需手动写文件名,仅在规则的命令行中生效,嵌入式开发高频使用3个:

自动变量含义适用场景
$@代表当前规则的目标文件(完整名)所有规则的输出文件指定
$<代表当前规则的第一个依赖文件单依赖规则(如编译.o文件)
$^代表当前规则的所有依赖文件(去重)多依赖规则(如链接所有.o文件)

编译器参数释义(嵌入式裸机专用)#

  • -Wall:开启所有编译警告,便于排查代码问题;
  • -nostdlib:不使用标准C库(裸机开发无操作系统,无标准库,必加);
  • -c只编译不链接,生成.o目标文件(编译阶段核心参数);
  • -O2:二级代码优化,平衡编译效率和运行效率;
  • -T:指定链接脚本(如imx6u.lds),裸机开发必须通过链接脚本指定程序运行地址;
  • -O binary:将elf文件转换为二进制格式;
  • -S:去掉符号信息,减小bin文件体积;
  • -D -m arm:反汇编时指定架构为ARM,生成可读的反汇编代码。

模块6:辅助规则(清理+调试)#

# 清理规则:删除所有编译产物
clean:
rm -rf $(TARGET).elf $(TARGET).bin $(TARGET).dis $(OBJS)
# 调试规则:打印变量值,排查Makefile语法问题
print:
@echo INCLUDE = $(INCLUDE)
@echo SFILES = $(SFILES)
@echo CFILES = $(CFILES)
# 其余变量打印略

核心语法#

  1. rm -rf:Linux命令,-r递归删除,-f强制删除,用于清理所有编译生成的文件(elf/bin/dis/obj/*.o);
  2. @echo@表示不打印命令本身,仅打印输出结果,若无@,Make会先打印echo 变量=值,再打印变量内容,影响调试可读性;

用法#

  • 清理:终端执行make clean,一键删除所有编译产物;
  • 调试:终端执行make print,查看所有变量的实际展开值,排查变量定义/函数使用的错误(如源文件检索失败、路径错误)。

三、关键执行流程(Makefile运行逻辑)#

Makefile会从主目标开始,自动推导依赖并执行规则,本文件的执行流程为:

  1. 终端执行make,Make默认执行第一个非变量/非伪目标的规则(即$(TARGET).bin : $(OBJS));
  2. 检查依赖$(OBJS)(所有.o文件)是否存在,若不存在,执行对应的编译规则(汇编/C文件编译);
  3. 编译规则通过静态模式规则+自动变量,批量将.S/.c编译为obj/%.o;
  4. 所有.o文件生成后,执行链接命令生成$(TARGET).elf
  5. 转换elf为bin镜像,生成dis反汇编文件;
  6. 最终在工程根目录生成ledc.elf/ledc.bin/ledc.disobj目录生成所有.o文件。

四、高频易错点&避坑技巧#

  1. 命令前必须用制表符\t:Makefile的语法要求,若用空格,会报*** missing separator. Stop.错误,是最高频的报错;
  2. 续行符\后无空格\结尾后若有空格,会导致变量定义错误,检索不到文件;
  3. VPATH必须指定源文件目录:否则Make无法找到分散在不同目录的源文件,报No rule to make target错误;
  4. 伪目标必须声明.PHONY:否则工程中若有同名文件(如clean.txt),make clean会失效;
  5. 裸机开发必加-nostdlib:若无此参数,编译器会尝试链接标准C库,裸机环境无标准库,会报undefined reference to main等链接错误;
  6. **变量引用必须用()Makefile中直接写变量名不会展开,如CC不会变成armlinuxgnueabihfgcc,必须写( )**:Makefile中直接写变量名不会展开,如`CC`不会变成`arm-linux-gnueabihf-gcc`,必须写`(CC)`。

五、核心语法速查(记忆版)#

1. 变量赋值#

  • ?=:条件赋值(未定义则赋值)→ 预留可外部修改的变量;
  • :=:直接赋值(立即展开)→ 嵌入式开发首选;

2. 内置函数(高频)#

  • $(patsubst 匹配, 替换, 对象):模式替换;
  • $(foreach 变量, 集合, 操作):循环遍历;
  • $(wildcard 规则):检索文件;
  • $(notdir 路径):去除路径;

3. 自动变量(必记)#

  • $@:当前目标文件;$<:第一个依赖文件;$^:所有依赖文件;

4. 核心规则#

  • 静态模式规则:目标集合 : 目标模式 : 依赖模式 → 批量编译;
  • 伪目标:.PHONY: 目标名 → 非文件类目标必加;

5. 关键标记#

  • \:续行;@:不打印命令本身;%:模式通配符;$( ):变量/函数引用。
i.mx6ull裸机C语言程序开发流程&相关寄存器使用
https://blog.huangzy.xyz/posts/imx6ull裸机c语言程序/
Author
纸翼
Published at
2026-02-04
License
CC BY-NC-SA 4.0

Some information may be outdated