IMX6ULL 裸机开发学习(二):C 语言点亮 LED
在汇编实验基础上,用 C 语言完成 IMX6ULL 裸机 LED 闪烁:start.S 启动文件(SVC模式+栈指针)、led.h/led.c 寄存器封装、链接脚本与 Makefile 编译,对比汇编方案体会工程化开发方式。
IMX6ULL 裸机开发学习(二):C 语言点亮 LED
一、项目背景
上一篇用汇编点亮了 LED,但真实项目不可能全部用汇编开发。C 语言才是嵌入式裸机与 Linux 驱动开发的主流语言。本篇文章在汇编实验基础上进行工程化升级:用 C 语言实现同样的 LED 闪烁功能,并引入启动文件、头文件封装、链接脚本与 Makefile 的完整工程范式。
二、技术方案:C 语言裸机工程结构
C 语言要运行起来,必须先由汇编准备好运行环境。整个工程分为三部分:
| 文件 | 作用 |
|---|---|
| start.S | 启动文件:配置 CPU 进入 SVC 模式、设置栈指针、跳转 main |
| led.h | 寄存器地址定义与 LED 操作宏封装 |
| led.c | 时钟使能、GPIO 初始化、延时与 main 主逻辑 |
| imxu.lds | 链接脚本:指定起始地址与段布局 |
| Makefile | 编译、链接、烧写一体化 |
三、系统架构:启动文件 start.S
C 语言入口 main 需要两个前提:处理器处于特权模式、栈指针有效。start.S 完成这两件事:
/*******************
*文件名: start.S
*描述 :裸机实验测试2 C语言点亮led
********************/
.global _start @全局标号
_start:
/***** 1.配置CPU 进入SVC模式 *****/
mrs r0, cpsr @ 拷贝CPSR状态寄存器到R0
bic r0, r0, #0x1f @ 清除CPU模式位BIT4-BIT0
orr r0, r0, #0x13 @ 配置BIT4-0为SVC模式
msr cpsr, r0 @ 写回CPSR
/***** 2.设置堆栈指针并跳转C语言main函数 *****/
ldr sp, =0x80200000 @ 设置堆栈 0x80200000 起点,2M空间
b main @ 跳转到C语言main函数
要点:
- SVC 模式:ARM 处理器复位后处于 SVC(管理模式),但显式配置可保证后续状态一致;
cpsr的 BIT4-0 为模式位,0x13 即 SVC 模式; - 栈指针:指向 DDR 高地址 0x80200000(IMX6ULL DDR 范围 0x80000000 起,2MB 栈空间足够裸机使用),避免覆盖代码段。
四、实施过程:C 代码与工程化构建
4.1 寄存器封装 led.h
/*******************
*文件名: led.h
*描述 :led灯程序头文件,存放寄存器指针
********************/
#ifndef _H_LED_H
#define _H_LED_H
/* CCM时钟相关寄存器地址定义 */
#define CCM_CCGR0 *((volatile unsigned int *)0x020c4068)
#define CCM_CCGR1 *((volatile unsigned int *)0x020c406C)
#define CCM_CCGR2 *((volatile unsigned int *)0x020c4070)
#define CCM_CCGR3 *((volatile unsigned int *)0x020c4074)
#define CCM_CCGR4 *((volatile unsigned int *)0x020c4078)
#define CCM_CCGR5 *((volatile unsigned int *)0x020c407C)
#define CCM_CCGR6 *((volatile unsigned int *)0x020c4080)
/* IOMUX复用相关寄存器地址定义 */
#define SW_MUX_GPIO5_IO3 *((volatile unsigned int *)0x02290014)
#define SW_PAD_GPIO5_IO3 *((volatile unsigned int *)0x02290058)
/* GPIO5相关寄存器地址定义 */
#define GPIO5_DR *((volatile unsigned int *)0x020AC000)
#define GPIO5_GDIR *((volatile unsigned int *)0x020AC004)
/* GPIO5_IO3 开漏输出控制宏 */
#define LED_ON() (GPIO5_DR &= ~(1<<3))
#define LED_OFF() (GPIO5_DR |= (1<<3))
#endif
使用 volatile 防止编译器优化掉寄存器读写,是操作物理寄存器的标准写法。
4.2 主程序 led.c
#include "led.h"
/* 使能所有外设控制时钟 */
void clk_enable(void)
{
CCM_CCGR0 = 0xffffffff;
CCM_CCGR1 = 0xffffffff;
CCM_CCGR2 = 0xffffffff;
CCM_CCGR3 = 0xffffffff;
CCM_CCGR4 = 0xffffffff;
CCM_CCGR5 = 0xffffffff;
CCM_CCGR6 = 0xffffffff;
}
/* LED初始化:复用GPIO + 电气属性 + 方向输出 */
void led_Init(void)
{
SW_MUX_GPIO5_IO3 = 0x5; // ALT5 复用为GPIO
SW_PAD_GPIO5_IO3 = 0x08B0; // 电气属性配置
GPIO5_GDIR = 0x08; // BIT3输出
LED_ON();
}
/* 约1ms单位延时 */
void delay_ms(volatile unsigned int nms)
{
unsigned int nus;
while (nms--)
{
nus = 0x7ff; // 1ms延时
while (nus--) { }
}
}
int main(void)
{
clk_enable(); // 使能时钟
led_Init(); // 初始化led
while (1) // LED闪烁循环
{
LED_OFF();
delay_ms(500);
LED_ON();
delay_ms(500);
}
return 0;
}
4.3 链接脚本 imxu.lds
SECTIONS{
. = 0x87800000;
.text :
{
start.o /* 启动文件必须放最前面 */
led.o
*(.text)
}
.rodata ALIGN(4) : { *(.rodata*) }
.data ALIGN(4) : { *(.data) }
__bss_start = .;
.bss ALIGN(4) : { *(.bss) *(COMMON) }
__bss_end = .;
}
链接脚本保证 start.S 的 _start 位于镜像最前面(0x87800000 起始),CPU 复位后第一条指令就是启动代码。
4.4 Makefile
objs := start.o led.o
led.bin : $(objs)
arm-linux-gnueabihf-ld -Timxu.lds -o led.elf $^
arm-linux-gnueabihf-objcopy -O binary -S led.elf $@
%.o : %.s
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<
%.o : %.S
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<
%.o : %.c
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<
clean:
rm -rf *.o led.bin led.elf led.dis *.imx
dump:
arm-linux-gnueabihf-objdump -D -m arm led.elf > led.dis
dowmload:
./imxdownload led.bin /dev/sdb
编译输出:
$ make
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o start.o start.S
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o led.o led.c
arm-linux-gnueabihf-ld -Timxu.lds -o led.elf start.o led.o
arm-linux-gnueabihf-objcopy -O binary -S led.elf led.bin
五、应用价值
- 工程范式:建立"启动文件 + 头文件封装 + 链接脚本 + Makefile"的标准裸机工程模板,可直接迁移到按键、串口、LCD 等外设实验;
- 可读性:寄存器地址集中在 led.h,业务代码只调用
LED_ON()/LED_OFF(),逻辑清晰; - 为 Linux 驱动铺路:寄存器操作方式与内核 GPIO 驱动、pinctrl 子系统的思路一脉相承,理解裸机后再学设备树与驱动事半功倍。
六、SEO关键词
IMX6ULL、裸机开发、C语言点亮LED、start.S、链接脚本、arm-linux-gnueabihf、野火开发板、GPIO驱动、嵌入式开发、Makefile
推荐阅读
更多工业 AI、智能制造与技术实践文章
TRIO-Basic 从入门到精通(五):直线与圆弧插补指令的使用
本文讲解 TRIO-Basic 三大运动指令:MOVE 相对移动、MOVEABS 绝对移动与 MOVECIRC 平面圆弧插补,给出各自的语法格式、完整可运行的代码示例(含轴参数段与矩形圆角轮廓插补示例),并说明增益参数的使用前提,帮助开发者快速上手多轴轨迹编程。
TRIO-Basic 从入门到精通(二十):Trio通过空间圆弧三点计算圆弧中心和半径
本文用TRIO-Basic手写实现空间圆弧三点求圆心与半径算法:由三点确定圆弧平面法向量,求两条中垂线交点得到圆心并输出半径,与Trio原装SPHERE_CENTRE指令计算结果对比一致,适用于空间圆弧插补与轨迹规划。
C#调用Intel NPU实现YOLOv26极速推理:OpenVINO工业视觉加速实践
通过C# + OpenVINO Runtime调用Intel Core Ultra NPU运行YOLOv26模型,实现低功耗、毫秒级的工业边缘AI视觉推理,涵盖模型导出、环境配置、推理代码与FP16/INT8/异步推理等性能优化实践。
C#工业视觉平台结合YOLOv26与OpenVINO
从数据标注、自动训练到Intel NPU边缘推理的完整工业AI方案:C#工业软件平台 + YOLOv26训练框架 + OpenVINO模型转换 + Intel NPU边缘推理,实现图像采集、自动标注、模型训练到现场推理的完整AI视觉闭环。
MY-BASIC 应用(一):轻量级嵌入式 BASIC 解析器介绍
MY-BASIC 是一个用标准 C 语言编写的轻量级 BASIC 解释器:不足 128KB 内存占用、可嵌入可扩展、支持原型编程与 Lambda 函数式编程、Unicode、列表/字典集合,适合嵌入式设备脚本化控制。
Linux系统篇(七)——工具篇(二):一篇搞懂 C 语言程序从代码到可执行文件的完整旅程
拆解 C 程序预处理、编译、汇编、链接四步变身流程,讲透条件编译与静态、动态链接差异,附分步实操与报错排查。
