ARM嵌入式开发代码实践——LED灯闪烁(C语言版)

嵌入式LED控制程序详解 - 从汇编启动到C语言控制

一、整体架构概览

这个项目是一个完整的嵌入式系统程序,包含:

  1. 汇编启动代码(start.S):系统初始化和异常向量表

  2. C语言主程序(main.c):硬件初始化和LED控制逻辑

  3. Makefile:自动化编译脚本

工作流程:

上电 → 汇编启动代码 → C语言main函数 → LED闪烁

二、汇编启动代码详解(start.S)

.global _start /* 声明_start为全局符号,链接器可识别 */ _start: /* ============================== */ /* ARM异常向量表(必须放在0x00000000) */ /* ============================== */ ldr pc, =_reset_handler /* 复位异常:系统复位或上电 */ ldr pc, =_undef_handler /* 未定义指令异常 */ ldr pc, =_software_handler /* 软件中断(SWI) */ ldr pc, =_prefect_handler /* 预取中止(指令获取失败)*/ ldr pc, =_data_abort_handler /* 数据中止(数据访问失败)*/ nop /* 保留 */ ldr pc, =_irq_handler /* 普通中断请求 */ ldr pc, =_fiq_handler /* 快速中断请求 */ /* ============================== */ /* 各个异常处理函数(简化版本) */ /* ============================== */ _undef_handler: b _undef_handler /* 无限循环,实际应处理异常 */ _software_handler: b _software_handler _prefect_handler: b _prefect_handler _data_abort_handler: b _data_abort_handler _irq_handler: b _irq_handler _fiq_handler: b _fiq_handler /* ============================== */ /* 复位处理函数(主启动代码) */ /* ============================== */ _reset_handler: /* 第一步:禁用所有中断 */ cpsid i /* 将CPSR的I位置1,禁用IRQ中断 */ /* cpsid = Change Processor State, Interrupt Disable */ /* 第二步:设置IRQ模式的栈指针 */ cps #0x12 /* 切换到IRQ模式(模式编号0x12)*/ /* 等价于传统写法: mrs r0, cpsr ; 读取CPSR到r0 bic r0, r0, #0x1F ; 清除模式位[4:0] orr r0, r0, #0x12 ; 设置IRQ模式(0x12) msr cpsr, r0 ; 写回CPSR */ ldr sp, =0x82000000 /* 设置IRQ模式栈指针 */ /* 栈从高地址向低地址增长 */ /* 第三步:设置SYS模式的栈指针并启用中断 */ cps #0x1F /* 切换到SYS模式(0x1F)*/ /* 等价于传统写法: mrs r0, cpsr bic r0, r0, #0x1F ; 清除模式位 bic r0, r0, #(1 << 7) ; 清除I位(使能IRQ中断) orr r0, r0, #0x1F ; 设置SYS模式(0x1F) msr cpsr, r0 */ ldr sp, =0x84000000 /* 设置SYS模式栈指针 */ cpsie i /* 使能IRQ中断 */ /* 第四步:跳转到C语言main函数 */ b main /* 程序控制权交给C代码 */ /* ============================== */ /* 程序结束处理 */ /* ============================== */ finish: b finish /* 死循环,程序结束 */

关键概念解释:

1. ARM异常向量表
  • 位置:必须放在内存地址0x00000000处

  • 作用:CPU发生异常时,自动跳转到对应地址

  • 8个异常向量

    1. 复位(Reset)

    2. 未定义指令(Undefined Instruction)

    3. 软件中断(SWI)

    4. 预取中止(Prefetch Abort)

    5. 数据中止(Data Abort)

    6. 保留

    7. IRQ中断

    8. FIQ中断

2. ARM处理器模式
#define MODE_USR 0x10 // 用户模式 #define MODE_FIQ 0x11 // 快速中断模式 #define MODE_IRQ 0x12 // 普通中断模式 #define MODE_SVC 0x13 // 管理模式 #define MODE_ABT 0x17 // 中止模式 #define MODE_UND 0x1B // 未定义模式 #define MODE_SYS 0x1F // 系统模式
3. CPSR寄存器
31 30 29 28 27 26 25 24 ... 8 7 6 5 4 3 2 1 0 N Z C V - - - - ... - I F T M4 M3 M2 M1 M0 ↑ ↑模式位 I位:1=禁用IRQ

三、C语言主程序详解(main.c)

1. 寄存器地址定义

/* ============================== */ /* i.MX6ULL寄存器地址定义 */ /* ============================== */ /* GPIO1_IO03引脚复用控制寄存器 */ /* 地址:0x020E0068,控制引脚功能(GPIO/UART等) */ #define IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 *((unsigned int *)0x020E0068) /* GPIO1_IO03引脚电气特性寄存器 */ /* 地址:0x020E02F4,控制驱动能力、上下拉等 */ #define IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03 *((unsigned int *)0x020E02F4) /* GPIO1方向寄存器 */ /* 地址:0x0209C004,控制每个引脚是输入还是输出 */ #define GPIO1_GDIR *((unsigned int *)0x0209C004) /* GPIO1数据寄存器 */ /* 地址:0x0209C000,写入数据控制引脚电平 */ #define GPIO1_DR *((unsigned int *)0x0209C000) /* ============================== */ /* 时钟控制寄存器(CCM) */ /* ============================== */ #define CCM_CCGR0 *((unsigned int *)0x020C4068) /* 外设时钟门控0 */ #define CCM_CCGR1 *((unsigned int *)0x020C406C) /* 外设时钟门控1 */ #define CCM_CCGR2 *((unsigned int *)0x020C4070) /* 外设时钟门控2 */ #define CCM_CCGR3 *((unsigned int *)0x020C4074) /* 外设时钟门控3 */ #define CCM_CCGR4 *((unsigned int *)0x020C4078) /* 外设时钟门控4 */ #define CCM_CCGR5 *((unsigned int *)0x020C407C) /* 外设时钟门控5 */ #define CCM_CCGR6 *((unsigned int *)0x020C4080) /* 外设时钟门控6 */

2. 外设时钟使能函数

void clock_cg_init(void) { /* 使能所有外设时钟 */ CCM_CCGR0 = 0xFFFFFFFF; /* 全写1,使能该组所有外设时钟 */ CCM_CCGR1 = 0xFFFFFFFF; CCM_CCGR2 = 0xFFFFFFFF; CCM_CCGR3 = 0xFFFFFFFF; CCM_CCGR4 = 0xFFFFFFFF; CCM_CCGR5 = 0xFFFFFFFF; CCM_CCGR6 = 0xFFFFFFFF; /* 原理:CCM(Clock Controller Module)控制各个外设的时钟 1 = 时钟开启,0 = 时钟关闭 开发阶段全部开启,实际产品应只开启需要的外设 */ }

3. LED初始化函数

void led_init(void) { /* 第一步:配置引脚复用功能 */ /* 将GPIO1_IO03设置为GPIO功能(ALT5) */ IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 = 0x05; /* 位域说明: 0x05 = 0101b,表示ALT5模式(GPIO功能) 其他模式: 0x00 = ALT0,0x01 = ALT1,以此类推 具体功能参考芯片手册的IOMUXC章节 */ /* 第二步:配置引脚电气特性 */ IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03 = 0x10B0; /* 位域说明(参考i.MX6ULL手册): bit16: HYS = 0(滞后关闭) bit15: 保留 bit14: PUS = 1(47K上拉) bit13: PUE = 0(保持器关闭) bit12: PKE = 1(上拉/下拉使能) bit11: ODE = 0(开漏输出关闭) bit10: SPEED = 2(中等速度) bit9: 保留 bit8: DSE = 6(驱动能力R0/6) bit7: 保留 bit6: SRE = 0(压摆率慢) */ /* 第三步:设置GPIO引脚为输出模式 */ GPIO1_GDIR |= (1 << 3); /* GPIO1_GDIR寄存器: bit3 = 1:GPIO1_IO03为输出模式 bit3 = 0:GPIO1_IO03为输入模式 使用|=操作符只设置第3位,不影响其他位 */ }

4. LED控制函数

/* LED点亮函数 */ void led_on(void) { /* 将GPIO1_IO03输出低电平(LED点亮)*/ GPIO1_DR &= ~(1 << 3); /* GPIO1_DR寄存器: bit3 = 0:输出低电平,LED亮(假设LED正极接3.3V,负极接GPIO) bit3 = 1:输出高电平,LED灭 操作分解: 1 << 3 = 00001000b (二进制) ~(1 << 3) = 11110111b (二进制) GPIO1_DR &= 11110111b:清除第3位(设为0) */ } /* LED熄灭函数 */ void led_off(void) /* 注意:原代码有拼写错误len_off(),应为led_off() */ { /* 将GPIO1_IO03输出高电平(LED熄灭)*/ GPIO1_DR |= (1 << 3); /* GPIO1_DR |= 00001000b:设置第3位(设为1) 其他位保持不变 */ } /* 简单延时函数 */ void led_delay(unsigned int t) { while(t--); /* 空循环,消耗CPU时间实现延时 */ /* 注意:这不是精确延时,实际时间取决于CPU频率 */ }

5. 主函数

int main(void) { /* 第一步:初始化时钟(使能外设工作)*/ clock_cg_init(); /* 第二步:初始化LED引脚 */ led_init(); /* 第三步:主循环 - LED闪烁 */ while(1) /* 无限循环 */ { led_on(); /* 点亮LED */ led_delay(0x7FFFF); /* 延时约524,287个循环 */ led_off(); /* 熄灭LED */ led_delay(0x7FFFF); /* 再次延时 */ /* 形成效果:亮→延时→灭→延时→亮... */ } return 0; /* 实际不会执行到这里 */ }

四、Makefile详解

# ============================================== # 编译器配置 # ============================================== COMPILER = arm-linux-gnueabihf- # 交叉编译器前缀 CC = $(COMPILER)gcc # C编译器 LD = $(COMPILER)ld # 链接器 OBJCOPY = $(COMPILER)objcopy # 二进制转换工具 OBJDUMP = $(COMPILER)objdump # 反汇编工具 # ============================================== # 文件列表 # ============================================== OBJS = start.o main.o led.o # 所有目标文件 TAGRT = led # 最终目标文件名 # ============================================== # 编译规则 # ============================================== # 规则1:汇编文件编译规则 # 格式:目标文件 : 依赖文件 %.o : %.S # 通配符规则:所有.S文件生成.o文件 $(CC) -c -g $^ -o $@ # 编译命令 # $^ : 所有依赖文件(这里是%.S) # $@ : 目标文件名(这里是%.o) # -c : 只编译不链接 # -g : 包含调试信息 # 规则2:C文件编译规则 %.o:%.c # 通配符规则:所有.c文件生成.o文件 $(CC) -c -g $^ -o $@ # 规则3:链接生成最终二进制文件 $(TAGRT).bin : $(OBJS) # 依赖所有.o文件 $(LD) -Ttext 0x87800000 $^ -o $(TAGRT).elf # 链接 # -Ttext 0x87800000 : 设置代码段起始地址 # $^ : 所有.o文件 # -o : 输出文件名 $(OBJCOPY) -O binary -S -g $(TAGRT).elf $@ # 生成二进制 # -O binary : 输出二进制格式 # -S : 移除符号信息 # -g : 移除调试信息 # $@ : 目标文件(led.bin) $(OBJDUMP) -D $(TAGRT).elf > $(TAGRT).dis # 生成反汇编 # -D : 反汇编所有段 # > : 输出重定向到文件 # ============================================== # 清理规则 # ============================================== clean: rm $(OBJS) $(TAGRT).elf $(TAGRT).bin $(TAGRT).dis -rf # 删除所有生成的文件 # -rf : 递归强制删除 # ============================================== # 烧写规则(针对i.MX6ULL开发板) # ============================================== load: ./imxdownload ./$(TAGRT).bin /dev/sdb # 使用imxdownload工具将bin文件烧写到SD卡 # /dev/sdb : SD卡设备文件(根据实际情况可能不同)

五、完整的工作流程

1. 编译流程

# 第一步:编译汇编文件 arm-linux-gnueabihf-gcc -c -g start.S -o start.o # 第二步:编译C文件 arm-linux-gnueabihf-gcc -c -g main.c -o main.o # 第三步:链接所有目标文件 arm-linux-gnueabihf-ld -Ttext 0x87800000 start.o main.o -o led.elf # 第四步:生成二进制文件 arm-linux-gnueabihf-objcopy -O binary -S led.elf led.bin # 第五步:生成反汇编文件(可选,用于调试) arm-linux-gnueabihf-objdump -D led.elf > led.dis

2. 内存布局

内存地址 内容 0x00000000 ┌─────────────┐ │ 异常向量表 │ ← CPU上电从这里开始执行 │ (start.S) │ 0x87800000 ├─────────────┤ │ 代码段 │ ← 链接地址,代码实际运行位置 │ (main.c等) │ 0x82000000 ├─────────────┤ │ IRQ模式栈 │ ← 中断处理时使用 0x84000000 ├─────────────┤ │ SYS模式栈 │ ← C语言函数调用使用 └─────────────┘

3. 实际烧写和执行

# 1. 编译 make # 2. 查看生成的文件 ls -lh led.* # 3. 烧写到SD卡 make load # 或者手动执行 sudo ./imxdownload ./led.bin /dev/sdb # 4. 插入SD卡到开发板,启动

六、重要概念总结

1. 嵌入式开发核心概念

  • 交叉编译:在PC上编译,在ARM开发板上运行

  • 寄存器编程:直接操作硬件寄存器控制外设

  • 地址映射:每个外设都有固定的内存地址

2. GPIO控制三要素

// 1. 引脚复用(选择GPIO功能) IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 = 0x05; // 2. 电气特性配置(上下拉、驱动能力等) IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03 = 0x10B0; // 3. 方向设置(输入/输出) GPIO1_GDIR |= (1 << 3); // 输出模式 // 4. 数据控制(高/低电平) GPIO1_DR &= ~(1 << 3); // 输出低电平

3. 启动代码关键步骤

  1. 设置异常向量表(CPU硬性要求)

  2. 初始化栈指针(每种模式都需要)

  3. 切换处理器模式

  4. 中断控制(先禁用,初始化后再开启)

  5. 跳转到C代码

4. 常见问题与调试

问题1:LED不亮
// 检查步骤: // 1. 确认时钟使能 clock_cg_init(); // 2. 确认引脚配置正确 // - 复用功能是否正确(0x05) // - 方向是否为输出(GPIO1_GDIR bit3=1) // 3. 确认电平控制 // - 亮:GPIO1_DR bit3=0 // - 灭:GPIO1_DR bit3=1 // 4. 确认硬件连接 // - LED正负极是否正确 // - 是否有限流电阻
问题2:程序不运行
# 检查方法: # 1. 查看反汇编文件 cat led.dis # 2. 检查链接地址是否正确 # 确保-Ttext与开发板内存匹配 # 3. 检查启动代码 # 向量表是否正确 # 栈指针是否设置

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1191101.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Qt的技巧笔记(二):ComboBox 下拉组合框组件

Qt中,ComboBox(组合框)是一种常用的用户界面控件, 它提供了一个下拉列表,允许用户从预设的选项中选择一个。该组件提供了一种比较的方便方式让用户从预定义的选项中进行选择,一般来说`ComboBox` 会以按钮的形式在界…

突破想象!AI应用架构师用科研AI智能体重塑金融学分析格局

突破想象&#xff01;AI应用架构师用科研AI智能体重塑金融学分析格局 一、引言&#xff1a;金融分析的“旧时代”与“新革命” 1. 一个让分析师崩溃的场景 凌晨3点&#xff0c;某顶级投行的资深分析师李阳还在办公室加班。他面前的电脑屏幕上&#xff0c;开着20多个窗口&#x…

stm32TIM输入捕获基本结构

1. GPIO- 功能&#xff1a;外部信号的“入口”&#xff0c;用来接收外部的脉冲、方波等电信号。2. 滤波器- 功能&#xff1a;给信号“降噪”&#xff0c;过滤掉高频干扰或抖动&#xff0c;保证后续检测的是真实的信号边沿。3. 边沿检测/极性选择- 功能&#xff1a;设置要捕捉的…

计算机大数据毕设实战-基于springboot+大数据技术旅游商品智能推荐管理系统【完整源码+LW+部署说明+演示视频,全bao一条龙等】

java毕业设计-基于springboot的(源码LW部署文档全bao远程调试代码讲解等) 博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、…

【深度解析x-algorithm】XAI-org开源的通用算法引擎核心技术与实践

文章目录前言一、项目背景&#xff1a;AI算法工程化的痛点与解决方案二、x-algorithm核心架构解析1. 基础核心层&#xff08;Core Layer&#xff09;2. 算法组件层&#xff08;Algorithm Component Layer&#xff09;3. 应用适配层&#xff08;Application Adapter Layer&#…

本地Python脚本是否存在命令注入风险

是的&#xff0c;本地Python脚本依然存在严重的命令注入风险&#xff01;核心观点命令注入风险与脚本是否是本地还是Web无关&#xff0c;而与输入来源的可信度有关。 只要脚本使用了不可信的用户输入来构造命令&#xff0c;就存在注入风险。风险来源分析1. 用户输入来源&#x…

收藏!2026年AI浪潮下,Java程序员的出路与三大黄金风口

最近和不少一线开发同行深入交流&#xff0c;发现一个极具普遍性的困惑&#xff1a;技术迭代日新月异&#xff0c;AI浪潮席卷各行各业&#xff0c;多数开发者既对新技术充满期待&#xff0c;又深陷方向迷茫的焦虑。尤其是Java领域的程序员&#xff0c;几乎都在追问同一个问题&a…

关于comfyui的comfyui-prompt-reader-node插件(import failed)和图片信息问题(metadata) - 实践

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

【Eino 】架构与核心特性解析

文章目录前言一、整体架构1. 核心层&#xff08;compose 模块&#xff09;2. 组件层&#xff08;components 模块&#xff09;3. 工具层&#xff08;flow/utils 模块&#xff09;二、核心特性1. 强类型的编排体系2. 灵活的编排能力3. 原生的流处理能力4. 标准化的组件生态5. 工…

α-Conotoxin SI ;Ile-Cys-Cys-Asn-Pro-Ala-Cys-Gly-Pro-Lys-Tyr-Ser-Cys-NH2

一、基础性质英文名称&#xff1a;α-Conotoxin SI&#xff1b;Conotoxin SI (Conus species)中文名称&#xff1a;α- 芋螺毒素 SI&#xff1b;SI 型 α- 芋螺毒素&#xff1b;α3β2 亚型 nAChRs 特异性拮抗肽多肽序列&#xff1a;H-Ile-Cys-Cys-Asn-Pro-Ala-Cys-Gly-Pro-Lys…

VirtualLab Fusion应用:导入材料数据

摘要要对光学系统进行精确建模&#xff0c;必须使用精确的材料特性。 对于薄层或更复杂的材料&#xff0c;实际折射率可能与文献中的数值不同。 因此&#xff0c;需要测量有关材料的复合折射率&#xff0c;并将数据导入 VirtualLab Fusion。 本文件介绍了导入复杂材料数据的工作…

最近搞了个硬核的工业自动化项目,主角是西门子S7-1500 PLC带着一群小弟玩协同作战。这个焊装系统里藏着不少值得说道的门道,咱们边拆边聊

西门子PLC1500大型程序fanuc机器人焊装 包括1台 西门子1500PLC程序&#xff0c;2台触摸屏TP1500程序 9个智能远程终端ET200SP Profinet连接 15个Festo气动智能模块Profinet通讯 10台Fanuc发那科机器人Profinet通讯 3台G120变频器Profinet通讯 2台智能电能管理仪表PAC3200 4个G…

VirtualLab Fusion应用:X射线掠入射聚焦反射镜

摘要掠入射反射光学元件在X射线光路中广泛使用&#xff0c;特别是Kirkpatrick-Baez&#xff08;KB&#xff09;椭圆反射镜系统。&#xff08;A. Verhoeven, et al., Journal of Synchrotron Radiation 27.5 (2020): 1307-1319&#xff09;聚焦是通过使用两个物理分离的椭圆反射…

VirtualLab Fusion应用:导入包含微结构高度数据的位图文件

摘要建模结果与测量数据的比较对于任何光学元件的设计过程都非常重要。因此&#xff0c;有必要将测量到的高度剖面&#xff08;例如微结构的高度剖面&#xff09;导入建模软件&#xff0c;以评估真实元件的性能。因此&#xff0c;在本文档中&#xff0c;我们将展示如何使用位图…

α-Conotoxin EI ;Arg-Asp-Hyp-Cys-Cys-Tyr-His-Pro-Thr-Cys-Asn-Met-Ser-Asn-Pro-Gln-Ile-Cys-NH2

一、基础性质英文名称&#xff1a;α-Conotoxin EI&#xff1b;Conotoxin EI (Conus species)中文名称&#xff1a;α- 芋螺毒素 EI&#xff1b;EI 型 α- 芋螺毒素&#xff1b;α4β2 亚型 nAChRs 特异性拮抗肽多肽序列&#xff1a;H-Arg-Asp-Hyp-Cys-Cys-Tyr-His-Pro-Thr-Cys…

【2026最新】修复工具Directx下载操作使用教程(附官网安装包+图文步骤)

DirectX修复工具是一款专门给 Windows 系统打补丁的小程序&#xff0c;可以把电脑里缺失或损坏的 DirectX 文件重新补全&#xff0c;让游戏、制图、视频软件能正常调用显卡、声卡。DirectX修复工具完全免费&#xff0c;也没有广告&#xff0c;软件只认 Windows&#xff0c;从老…

大数据毕设选题推荐:基于大数据技术旅游商品管理系统基于springboot+大数据技术旅游商品管理系统【附源码、mysql、文档、调试+代码讲解+全bao等】

java毕业设计-基于springboot的(源码LW部署文档全bao远程调试代码讲解等) 博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、…

肿瘤坏死因子超家族受体TWEAKR

肿瘤坏死因子受体超家族成员12A&#xff08;TNFRSF12A&#xff09;&#xff0c;又称为成纤维细胞生长因子诱导的早期反应蛋白14&#xff08;FN14&#xff09;&#xff0c;TWEAKR或CD266&#xff0c;是TNFSF12/TWEAK受体。是某些细胞类型的弱凋亡诱导剂。促进血管生成和内皮细胞…

微恢复(微信聊天记录恢复软件)

链接&#xff1a;https://pan.quark.cn/s/477474919484微恢复是一款专业且免费的聊天记录恢复软件&#xff0c;可以将删除聊天记录快速、完整的恢复回来&#xff0c;恢复内容包括图片&#xff0c;视频&#xff0c;语音&#xff0c;文件&#xff0c;无需要越狱&#xff0c;操作简…

重庆同传Gino小翻译认为,同传无非就是听懂发言人讲的话,用听众听得明白的话讲出来,哪有那么多狗屁理论,什么质量,鬼啊,神哦,都是唬人的。

重庆同传Gino小翻译认为&#xff0c;同传无非就是听懂发言人讲的话&#xff0c;用听众听得明白的话讲出来&#xff0c;哪有那么多狗屁理论&#xff0c;什么质量&#xff0c;鬼啊&#xff0c;神哦&#xff0c;都是唬人的。