ARM交叉工具链各工具作用及IMX6ULL平台LED+蜂鸣器裸机程序实现

  1. ARM 交叉工具链各工具作用

基于你使用的 arm-linux-gnueabihf-xxx 工具链,各工具核心功能如下,均运行在 x86 主机上,生成 ARM 架构可执行程序:

(1)arm-linux-gnueabihf-gcc ------ 交叉 C 编译器
  • 核心作用:将 C 语言源文件(.c)、汇编源文件(.S)编译、汇编生成目标文件(.o),也可直接完成编译 + 链接。

  • 常用参数:

    • -c:只编译不链接,生成单个.o 目标文件

    • -I:指定头文件搜索路径

    • -g:加入调试信息,配合 JTAG 调试

    • -Wall:开启编译警告

    • -nostdlib:不链接系统标准 C 库,裸机开发必用

  • 裸机场景:把每个驱动、主函数源文件编译成独立的目标文件,后续交给链接器合并。

(2)arm-linux-gnueabihf-ld ------ 链接器
  • 核心作用:将多个.o 目标文件、库文件按照 \\ 链接脚本 (.lds)\\ 的规则,合并成一个完整的 ELF 格式可执行文件,完成地址重定位、符号解析、段排布。

  • 常用参数:

    • -T xxx.lds:指定自定义链接脚本,安排代码 / 数据在内存中的起始地址

    • -Ttext 0x87800000:直接指定代码段运行地址(简化写法)

  • 裸机场景:决定程序在 DDR / 内部 RAM 中的运行地址,指定程序入口点,是裸机程序地址分配的核心工具。

(3)arm-linux-gnueabihf-objcopy ------ 二进制格式转换工具
  • 核心作用:在 ELF、纯二进制 (.bin)、Intel HEX 等多种文件格式之间转换,同时可裁剪符号、调试信息。

  • 常用参数:

    • -O binary:输出纯二进制文件(去掉 ELF 的文件头、符号表等冗余信息)

    • -S:移除所有符号和调试信息,减小文件体积

  • 裸机场景:LD 生成的 ELF 文件不能直接烧录到 SD 卡 / NAND Flash 运行,必须用 objcopy 转换成.bin 纯二进制文件,才是芯片能直接加载执行的程序。

(4)arm-linux-gnueabihf-objdump ------ 反汇编与段分析工具
  • 核心作用:对 ELF 文件进行反汇编、查看段信息、符号表、地址分布,是调试裸机程序的核心工具。

  • 常用参数:

    • -D:反汇编所有段(代码 + 数据段)

    • -h:查看所有段的大小、地址、属性

    • -t:查看所有符号的地址和类型

  • 裸机场景:排查地址错误、段排布异常、程序跑飞、变量地址不对等问题,也可分析编译器生成的汇编指令优化效果。


2. 各内存段的区别

这些段是 ELF 文件的标准分段,对应程序运行时在 Flash 和 RAM 中的不同存储区域,核心区别如下表:

|----------|--------------------------|------|------------|--------------------------------|
| 段名 | 存储内容 | 读写属性 | 初始化状态 | 存储介质(裸机典型场景) |
| .text | 程序执行指令、中断向量表 | 只读 | 编译时确定,固定 | 存放于 Flash(ROM),运行时可在 Flash/RAM |
| .rodata | 只读常量:const 全局变量、字符串常量 | 只读 | 编译时确定,固定 | 存放于 Flash,与.text 段相邻 |
| .data | 已初始化的全局变量、已初始化静态局部变量 | 可读写 | 编译时给定初始值 | 初始值存在 Flash;运行时拷贝到 RAM |
| .bss | 未初始化的全局变量、未初始化静态局部变量 | 可读写 | 运行时自动清 0 | 不占 Flash 空间,运行时占用 RAM |
| common 段 | 多文件重复声明的未初始化全局变量(编译期临时段) | 可读写 | 链接后合并到.bss | 编译期临时标记,最终不存在于可执行文件 |

补充说明:

  1. common 段本质 :是编译阶段的临时标记。单个.c 文件中未初始化的全局变量,编译器不会直接分配地址,而是标记为 common 符号;链接阶段会把所有编译单元的同名 common 符号合并,最终统一分配到.bss 段。如果编译时加 -fno-common 参数,未初始化全局变量会直接放入.bss,不生成 common 段。

  2. 体积差异:.bss 段不占用 Flash 存储空间,只记录大小,程序启动时由启动代码把对应 RAM 区域清零,以此节省 Flash 空间。


3. LED + 蜂鸣器裸机程序实现(IMX6ULL 平台)

基于当前的工程结构(bsp/ 存放驱动文件、project/ 存放主程序和启动文件、imx6ull/ 存放芯片头文件),以下提供一套完整可运行的寄存器版代码。该代码适配主流 IMX6ULL 开发板(LED 连接 GPIO1_IO03,蜂鸣器连接 GPIO1_IO19,若使用其他开发板,请根据实际引脚定义进行相应修改)。

工程目录结构
复制代码
├── bsp/
│   ├── led.h
│   ├── led.c
│   ├── beep.h
│   └── beep.c
├── project/
│   ├── start.S
│   └── main.c
├── imx6ull/
│   └── MCIMX6Y2.h
├── imx6ull.lds
└── Makefile
(1)芯片寄存器头文件 imx6ull/MCIMX6Y2.h
c 复制代码
#ifndef __MCIMX6Y2_H
#define __MCIMX6Y2_H
/* 外设基地址 */
#define CCM_BASE        0x020C4000
#define IOMUXC_BASE     0x020E0000
#define GPIO1_BASE      0x0209C000
/* CCM时钟使能寄存器 /
#define CCM_CCGR1       ((volatile unsigned int *)(CCM_BASE + 0x6C))
/* IOMUXC引脚复用与电气属性寄存器 /
#define IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03  ((volatile unsigned int )(IOMUXC_BASE + 0x068))
#define IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03  ((volatile unsigned int )(IOMUXC_BASE + 0x2F4))
#define IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO19  ((volatile unsigned int )(IOMUXC_BASE + 0x0AC))
#define IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO19  ((volatile unsigned int *)(IOMUXC_BASE + 0x338))
/* GPIO寄存器 /
#define GPIO1_DR        ((volatile unsigned int )(GPIO1_BASE + 0x00))
#define GPIO1_GDIR      ((volatile unsigned int *)(GPIO1_BASE + 0x04))
#endif
(2)启动文件 project/start.S
assembly 复制代码
.global _start

_start:
	ldr pc, =_reset_handler	   
	ldr pc, =_undefine_handler	  
	ldr pc, =_svc_handler			  
	ldr pc, =_prefetch_aboart_handler
	ldr pc, =_data_abort_handler
	ldr pc, =_reserved_handler
	ldr pc, =_irq_handler
	ldr pc, =_fiq_handler

_fiq_handler:
	ldr pc, =_fiq_handler

_irq_handler:
	ldr pc, =_irq_handler

_reserved_handler:
	ldr pc, =_reserved_handler

_data_abort_handler:
	ldr pc, =_data_abort_handler

_prefetch_aboart_handler:
	ldr pc, =_prefetch_aboart_handler

_svc_handler:
    ldr pc, =_svc_handler
	
_undefine_handler:
	ldr pc, =_undefine_handler
	
_reset_handler:
    // 切换工作模式:IRQ
    mrs r0, cpsr
    bic r0, r0, #0x1F
    orr r0, r0, #0x12      
    msr cpsr, r0

    // 设置IRQ模式下的栈
    ldr sp, =0x86000000

    // 切换工作模式:System
    mrs r0, cpsr
    bic r0, r0, #0x1F
    orr r0, r0, #0x1F  
    msr cpsr, r0

    // 设置System模式下的栈
    ldr sp, =0x84000000

    // 清空bss段和common段
    bl _bss_clear

    // 跳转到main函数执行
    b main

    b finished

_bss_clear:
    ldr r0, =__bss_start   
    ldr r1, =__bss_end  

loop:
    mov r2, #0
    str r2, [r0]  
    add r0, r0, #4  
    cmp r0, r1 
    blt loop
    bx lr

finished:
	b finished	
(3)LED 驱动 bsp/led.h + bsp/led.c

led.h

c 复制代码
#ifndef _LED_H_
#define _LED_H_

extern void led_init(void);
extern void led_on(void);
extern void led_off(void);
extern void led_nor(void);

#endif

led.c

c 复制代码
#include "led.h"
#include "MCIMX6Y2.h"
#include "fsl_iomuxc.h"
void led_init(void)
{
    // 1. 引脚复用
    IOMUXC_SetPinMux(IOMUXC_GPIO1_IO03_GPIO1_IO03, 0);

    // 2. 电气属性
    IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO03_GPIO1_IO03, 0x10B0);

    // 3. GPIO方向:输出模式
    GPIO1->GDIR |= (1 << 3);
}

void led_on(void)
{
    GPIO1->DR &= ~(1 << 3);
}

void led_off(void)
{
   GPIO1->DR |= (1 << 3);
}

void led_nor(void)
{
   GPIO1->DR ^= (1 << 3);
}
(4)蜂鸣器驱动 bsp/beep.h + bsp/beep.c

beep.h

c 复制代码
#ifndef _BEEP_H_
#define _BEEP_H_

extern void beep_init(void);
extern void beep_on(void);
extern void beep_off(void);
extern void beep_nor(void);

#endif

beep.c

c 复制代码
#include "beep.h"
#include "MCIMX6Y2.h"
#include "fsl_iomuxc.h"

/**
 * @brief 蜂鸣器初始化函数
 * @note 该函数用于初始化蜂鸣器对应的GPIO引脚,配置其复用功能、电气属性和方向
 */
void beep_init(void)
{
    // 1. 复用功能配置
    // 将SNVS_TAMPER1引脚复用为GPIO5_IO01功能
    IOMUXC_SetPinMux(IOMUXC_SNVS_SNVS_TAMPER1_GPIO5_IO01, 0);

    // 2. 电气属性配置
    // 设置GPIO5_IO01引脚的电气属性,0x10B0表示适当的驱动能力和速度
    IOMUXC_SetPinConfig(IOMUXC_SNVS_SNVS_TAMPER1_GPIO5_IO01, 0x10B0);

    // 3. GPIO方向:输出模式
    GPIO5->GDIR |= (1 << 1);
}

void beep_on(void)
{
    GPIO5->DR &= ~(1 << 1);
}

void beep_off(void)
{
    GPIO5->DR |= (1 << 1);
}

void beep_nor(void)
{
    GPIO5->DR ^= (1 << 1);
}
(5)主函数 project/main.c
c 复制代码
include "MCIMX6Y2.h"
#include "fsl_iomuxc.h"
#include "led.h"
#include "beep.h"

void enable_clocks(void)
{
    CCM->CCGR0 = 0xFFFFFFFF;
    CCM->CCGR1 = 0xFFFFFFFF;
    CCM->CCGR2 = 0xFFFFFFFF;
    CCM->CCGR3 = 0xFFFFFFFF;   
    CCM->CCGR4 = 0xFFFFFFFF;
    CCM->CCGR5 = 0xFFFFFFFF;
    CCM->CCGR6 = 0xFFFFFFFF;         
}

void delay(unsigned int n)
{
    while (n--);
}
int main(void)
{
    // 使能外设时钟
    enable_clocks();
    // LED初始化
    led_init();
    // 蜂鸣器初始化
    beep_init();

    while (1)
    {
        led_nor();
        beep_nor();
        delay(0x7FFFF);
    }

    return 0;
}
(6)链接脚本 imx6ull.lds
ld 复制代码
SECTIONS
{
    . = 0x87800000;
    .text :
    {
       obj/start.o
       obj/led.o
       obj/main.o
       *(.text) 
    }

    .rodata ALIGN(4) : { *(.rodata*)}
    .data ALIGN(4) : { *(.data)}
    . = ALIGN(4);
    __bss_start = .;
    .bss ALIGN(4) : { *(.bss) *(COMMON)}
    __bss_end = .;
}   
(7)Makefile
makefile 复制代码
target = beep

cross_compiler = arm-linux-gnueabihf-

cc = $(cross_compiler)gcc
ld = $(cross_compiler)ld
objcopy = $(cross_compiler)objcopy
objdump = $(cross_compiler)objdump

incdirs = bsp imx6ull
srcdirs = bsp project

include = $(patsubst %, -I%, $(incdirs))

cfiles = $(foreach dir, $(srcdirs), $(wildcard $(dir)/*.c))
sfiles = $(foreach dir, $(srcdirs), $(wildcard $(dir)/*.S))

cfilenodir = $(notdir $(cfiles))
sfilenodir = $(notdir $(sfiles))

cobjs = $(patsubst %, obj/%, $(cfilenodir:.c=.o))
sobjs = $(patsubst %, obj/%, $(sfilenodir:.S=.o))

objs = $(cobjs) $(sobjs)

VPATH = $(srcdirs)

$(target).bin : $(objs)
	$(ld) -Timx6ull.lds -o$(target).elf $^
	$(objcopy) -O binary -S -g $(target).elf $@
	$(objdump) -D $(target).elf > $(target).dis

$(sobjs) : obj/%.o : %.S
	@mkdir -p obj
	$(cc) -Wall -nostdlib -c $(include) -o $@ $<

$(cobjs) : obj/%.o : %.c
	@mkdir -p obj		
	$(cc) -Wall -nostdlib -c $(include) -o $@ $<

.PHONY : clean
clean:
	rm -rf $(objs) $(target).elf $(target).bin $(target).dis
root: $(target).bin
	chmod +x ./imxdownload
	./imxdownload $(target).bin /dev/sdb
编译运行步骤
  1. 创建编译产物目录:执行命令 mkdir obj

  2. 执行编译:运行 make 命令,系统将自动调用交叉编译工具链完成编译、链接和格式转换。

  3. 将生成的 beep_led.bin 文件烧录到 SD 卡,插入开发板并上电。此时即可观察到 LED 闪烁,同时蜂鸣器同步鸣叫。

相关推荐
曾阿伦4 小时前
MongoDB 查询语句备忘手册
数据库·mongodb
一叶龙洲5 小时前
wslg打开Ubuntu24.04默认打开图形界面
linux·服务器·数据库·ubuntu
CCPC不拿奖不改名5 小时前
大模型推理架构与开源生态知识整理
数据库·windows·python·架构·langchain·开源·github
Marst Code5 小时前
(python)2026Plotly 库评估:交互式可视化到底值不值得引入?
开发语言·python
Miao121315 小时前
微服务 API 测试实践:海外某民宿平台如何构建模式驱动测试基础设施
java·开发语言
爬也要爬着前进6 小时前
redis主从搭建
数据库·redis·缓存
CRMEB系统商城6 小时前
开源自建还是SaaS订阅?算一笔3年经济账
java·大数据·开发语言·开源
@Mike@7 小时前
02-数据库学习笔记(SQL引擎)
数据库·笔记·学习
keyipatience8 小时前
线程栈与TLS和线程互斥
java·linux·服务器·开发语言·ubuntu