2026/4/6 4:03:46
网站建设
项目流程
平原网站建设价格,怎么做营销,企业邮箱手机怎么登录,黄骅港天气预报一周7天i.MX6ULL嵌入式系统代码详解#xff08;从零开始#xff09;
一、系统启动与初始化#xff08;start.S#xff09;
1. 异常向量表
.global _start_start:ldr pc, _reset_handler ; 复位异常#xff08;系统启动#xff09;ldr pc, _undef_handler ; 未定义指令异…i.MX6ULL嵌入式系统代码详解从零开始一、系统启动与初始化start.S1. 异常向量表.global _start _start: 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 ; IRQ中断 ldr pc, _fiq_handler ; FIQ中断解释这是ARM处理器的异常向量表位于内存起始位置0x87800000。处理器发生异常时会根据异常类型自动跳转到对应地址。每个向量占用4字节第7个向量为IRQ中断入口。2. 复位处理程序系统初始化_reset_handler: cpsid i ; 禁用IRQ中断保证初始化不被中断 ; 配置CP15系统控制寄存器 mrc p15, 0, r0, c1, c0, 0 ; 读取控制寄存器 bic r0, r0, #(1 13) ; 清除V位使用VBAR重映射异常向量表 orr r0, r0, #(1 12) ; 设置I位启用指令缓存ICache mcr p15, 0, r0, c1, c0, 0 ; 写回控制寄存器 ; 设置IRQ模式栈指针 cps #0x12 ; 切换到IRQ模式模式编码0x12 ldr sp, 0x82000000 ; IRQ栈起始地址 ; 设置系统模式栈指针 cps #0x1F ; 切换到系统模式0x1F ldr sp, 0x84000000 ; 系统栈起始地址 cpsie i ; 启用IRQ中断 bl _bss_init ; 初始化BSS段清零 b main ; 跳转到C语言main函数关键点cpsid i/cpsie i中断禁用/使能指令不同模式有不同的栈指针避免模式切换时数据破坏BSS段存放未初始化的全局/静态变量启动时需要清零3. IRQ中断处理程序_irq_handler: sub lr, lr, #4 ; 调整返回地址ARM中断返回特性 stmfd sp!, {r0-r12, lr} ; 保存现场寄存器入栈 ; 获取GIC中断控制器基地址 mrc p15, 4, r1, c15, c0, 0 ; 从CP15读取GIC基地址 add r1, r1, #0x2000 ; GIC CPU接口偏移 ldr r0, [r1, #0xC] ; 读取中断IDICC_IAR ; 保存中断ID并调用C中断处理函数 stmfd sp!, {r0, r1} cps #0x1F ; 切换到系统模式 stmfd sp!, {lr} bl system_interrupt_handler ; C语言中断分发函数 ldmfd sp!, {lr} cps #0x12 ; 切换回IRQ模式 ; 中断处理完成发送EOI ldmfd sp!, {r0, r1} str r0, [r1, #0x10] ; 写ICC_EOIR中断结束 ldmfd sp!, {r0-r12, pc}^ ; 恢复现场并返回^表示恢复CPSR二、时钟系统初始化clock.c1. 主要时钟源配置void clock_init(void) { // 1. ARM内核时钟配置 CCM-CCSR ~(1 8); // 清除pll1_sw_clk_sel位 CCM-CCSR | (1 2); // step_clk选择24MHz晶振 // 设置ARM时钟分频 CCM-CACRR ~(7 0); // 清除分频系数 CCM-CACRR | (1 0); // 设置2分频ARM_PODF1 // 配置PLL1ARM PLL unsigned int t CCM_ANALOG-PLL_ARM; t ~(3 14); // 清除旁路控制位 t | (1 13); // 使能PLL输出 t ~(0x7F 0); // 清除倍频系数 t | (88 0); // 设置倍频系数N88 CCM_ANALOG-PLL_ARM t; // 24MHz × 88 2112MHz CCM-CCSR ~(1 2); // step_clk切换回PLL1 // 2. 配置528 PLL的PFD通道 t CCM_ANALOG-PFD_528; t ~((0x3F 0) | (0x3F 8) | (0x3F 16) | (0x3F 24)); t | ((27 0) | (16 8) | (24 16) | (32 24)); CCM_ANALOG-PFD_528 t; /* PFD0: 528×18/27 352MHz PFD1: 528×18/16 594MHz PFD2: 528×18/24 396MHz PFD3: 528×18/32 297MHz */ // 3. 配置480 PLL的PFD通道 t CCM_ANALOG-PFD_480; t ~((0x3F 0) | (0x3F 8) | (0x3F 16) | (0x3F 24)); t | ((12 0) | (16 8) | (17 16) | (19 24)); CCM_ANALOG-PFD_480 t; /* PFD0: 480×18/12 720MHz PFD1: 480×18/16 540MHz PFD2: 480×18/17 ≈ 508MHz PFD3: 480×18/19 ≈ 454MHz */ // 4. 配置AHB总线时钟132MHz t CCM-CBCMR; t ~(3 18); // 清除PRE_PERIPH_CLK_SEL t | (1 18); // 选择PLL2 PFD2396MHz CCM-CBCMR t; t CCM-CBCDR; t ~(1 25); // PERIPH_CLK_SEL0选择PRE_PERIPH t ~(7 10); // 清除AHB_PODF t | (2 10); // AHB_PODF23分频396MHz÷3132MHz // 5. 配置IPG时钟66MHz t ~(3 8); // 清除IPG_PODF t | (1 8); // IPG_PODF12分频132MHz÷266MHz CCM-CBCDR t; // 6. 配置PERCLK时钟66MHz t CCM-CSCMR1; t ~(1 6); // PERCLK_CLK_SEL0选择IPG时钟 t ~(0x3F 0); // PERCLK_PODF01分频 CCM-CSCMR1 t; clock_cg_init(); // 使能所有外设时钟 }2. 外设时钟使能void clock_cg_init(void) { CCM-CCGR0 0XFFFFFFFF; // 使能CCGR0控制的所有外设时钟 CCM-CCGR1 0XFFFFFFFF; // GPIO1-5等 CCM-CCGR2 0XFFFFFFFF; // GPT、EPIT等定时器 CCM-CCGR3 0XFFFFFFFF; // UART、I2C等 CCM-CCGR4 0XFFFFFFFF; // PWM、ADC等 CCM-CCGR5 0XFFFFFFFF; // 视频相关 CCM-CCGR6 0XFFFFFFFF; // 以太网等 }时钟门控每个CCGR寄存器控制一组外设的时钟写入1使能时钟0禁用时钟低功耗。三、中断系统interrupt.c1. 中断向量表irq_handler_t Vector_table[160] {NULL}; // 定义中断向量表最多支持160个中断源 // irq_handler_t是函数指针类型typedef void (*irq_handler_t)(void);2. 中断系统初始化void system_interrupt_init(void) { __set_VBAR(0x87800000); // 设置异常向量表基地址到0x87800000 GIC_Init(); // 初始化通用中断控制器GIC }VBARVector Base Address RegisterARMv7异常向量表基址寄存器。3. 中断注册与管理// 中断服务函数注册 int system_interrupt_register(IRQn_Type irq, irq_handler_t handler) { if (irq PMU_IRQ2_IRQn || irq IOMUXC_IRQn) return -1; // 中断号范围检查 if (handler NULL) return -2; // 空指针检查 Vector_table[irq] handler; // 注册中断处理函数 return 0; } // 中断分发函数由汇编_irq_handler调用 void system_interrupt_handler(IRQn_Type irq) { if (Vector_table[irq] ! NULL){ Vector_table[irq](); // 调用注册的中断服务函数 } }四、GPIO外设驱动1. LED驱动led.cvoid led_init(void) { // 1. 引脚复用配置 IOMUXC_SetPinMux(IOMUXC_GPIO1_IO03_GPIO1_IO03, 0); // 将GPIO1_IO03配置为GPIO功能ALT5模式 // 2. 电气特性配置 IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO03_GPIO1_IO03, 0x10B0); // 0x10B0配置含义 // - 驱动强度R0/6普通驱动 // - 速度100MHz // - 上拉电阻47K上拉 // - 滞回器使能 // - 开漏输出禁用 // 3. 方向配置输出模式 GPIO1-GDIR | (1 3); // 设置GPIO1_IO03为输出 led_off(); // 初始状态关闭 } void led_on(void) { GPIO1-DR ~(1 3); } // 低电平点亮 void led_off(void) { GPIO1-DR | (1 3); } // 高电平熄灭 void led_nor(void) { GPIO1-DR ^ (1 3); } // 电平翻转2. 蜂鸣器驱动beep.cvoid beep_init(void) { // SNVS域GPIO低功耗域 IOMUXC_SetPinMux(IOMUXC_SNVS_SNVS_TAMPER1_GPIO5_IO01, 0); IOMUXC_SetPinConfig(IOMUXC_SNVS_SNVS_TAMPER1_GPIO5_IO01, 0x10B0); GPIO5-GDIR | (1 1); // GPIO5_IO01设为输出 beep_off(); }SNVS域始终供电域系统休眠时仍可工作。3. 按键驱动key.c与中断配置void key_irq_handler(void) { // 检查GPIO1_IO18中断状态 if ((GPIO1-ISR (1 18)) ! 0) { led_nor(); // 翻转LED beep_nor(); // 翻转蜂鸣器 GPIO1-ISR | (1 18); // 清除中断标志 } } void key_init(void) { // 1. 引脚配置 IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0); IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0xF0B0); GPIO1-GDIR ~(1 18); // 设为输入模式 // 2. 中断触发方式配置 GPIO1-ICR2 | (3 4); // 设置GPIO1_IO18双边沿触发 /* ICR2寄存器位说明 - bits[5:4]ICR18配置位 - 00低电平触发 01高电平触发 - 10上升沿触发 11双边沿触发 */ // 3. 中断使能 GPIO1-IMR | (1 18); // 使能GPIO1_IO18中断屏蔽 // 4. GIC中断配置 GIC_EnableIRQ(GPIO1_Combined_16_31_IRQn); // 使能GIC中断 GIC_SetPriority(GPIO1_Combined_16_31_IRQn, 0); // 设置最高优先级 // 5. 注册中断处理函数 system_interrupt_register(GPIO1_Combined_16_31_IRQn, key_irq_handler); }五、主程序main.cint main(void) { system_interrupt_init(); // 1. 中断系统初始化 clock_init(); // 2. 时钟系统初始化 led_init(); // 3. LED初始化 beep_init(); // 4. 蜂鸣器初始化 key_init(); // 5. 按键中断初始化 while(1) // 主循环 { led_nor(); // 翻转LED非中断方式 g_delay(0x7FFFF); // 软件延时 } return 0; } // 简单软件延时函数 void g_delay(unsigned int t) { while(t--); // 空循环消耗时间 }六、系统工作流程总结1. 启动流程上电复位 → 执行_start汇编 → 初始化CPU → 设置栈指针 → 初始化BSS段 → 跳转到main() → 外设初始化 → 进入主循环2. 中断响应流程按键按下 → GPIO检测边沿 → 置位中断标志 → GIC接收中断 → CPU响应IRQ → 执行_irq_handler汇编 → 保存现场 → 调用system_interrupt_handler → 执行key_irq_handler → 处理中断翻转LED/蜂鸣器 → 清除中断标志 → 恢复现场返回3. 时钟树概要24MHz晶振 → PLL1(2112MHz) → 2分频 → ARM内核(1056MHz) → PLL2(528MHz) → PFD2(396MHz) → 3分频 → AHB(132MHz) → 2分频 → IPG(66MHz) → PLL3(480MHz) → 各PFD通道 → 各外设时钟4. 硬件连接关系GPIO1_IO03 → LED低电平点亮 GPIO5_IO01 → 蜂鸣器低电平发声 GPIO1_IO18 → 按键按下为低电平七、关键概念解析1. 内存地址映射0x87800000异常向量表起始地址由VBAR指定 0x82000000IRQ模式栈空间 0x84000000系统模式栈空间 0x0209C000GPIO1寄存器基地址 0x020A8000CCM时钟控制器基地址2. 中断优先级GIC优先级0为最高数值越大优先级越低嵌套中断高优先级中断可打断低优先级中断处理中断屏蔽通过CPSID/CPSIE指令全局控制3. GPIO寄存器关键位GDIR方向寄存器1输出0输入DR数据寄存器读写引脚电平ICR1/2中断配置寄存器触发方式IMR中断屏蔽寄存器1使能中断ISR中断状态寄存器1中断发生写1清除4. 时钟配置安全原则先降频后升频配置PLL前切换到低频时钟分频保护设置倍频前先配置输出分频等待锁定PLL配置后需等待LOCK标志顺序切换时钟源切换按标准流程操作