--- title: 裸机实验总结 tags: [裸机开发, IMX6ULL, 实验总结, 嵌入式Linux] created: 2026-09-16 updated: 2026-09-17 pdf_ref: - "第八章 汇编LED灯实验.pdf" - "第十章 C语言版LED灯实验.pdf" - "第十四章 蜂鸣器实验.pdf" - "第十五章 按键输入实验.pdf" - "第二十章 高精度延时实验.pdf" - "第二十三章 DDR3实验.pdf" - "第二十四章 RGBLCD显示实验.pdf" - "第二十五章 RTC实时时钟实验.pdf" - "第二十六章 I2C实验.pdf" - "第二十七章 SPI实验.pdf" --- # 裸机实验总结 > **关联知识**: [[01-Cortex-A7架构精讲]] | [[02-交叉编译工具链]] | [[03-IMX6ULL启动流程]] > **本章目标**: 系统梳理10个核心裸机实验,建立从汇编到C语言、从GPIO到复杂外设的完整认知链路 --- ## 一、裸机开发概述 ### 1.1 裸机 vs RTOS vs Linux | 维度 | 裸机 (Bare Metal) | RTOS | Linux | | -------------- | ------------------ | ---------------- | ----------------- | | **调度机制** | 无,main循环轮询 | 优先级抢占调度 | 多进程/多线程调度 | | **内存管理** | 物理地址直接访问 | 堆+静态分区 | 虚拟内存+MMU | | **外设访问** | 直接操作寄存器 | HAL/驱动封装 | 子系统框架 | | **中断处理** | 直接注册ISR | 有限中断嵌套 | 上下半部机制 | | **适用场景** | 简单控制、启动加载 | 实时控制、物联网 | 复杂应用、多媒体 | | **开发复杂度** | 低 | 中 | 高 | | **资源占用** | 最小(KB级) | 中等(几十KB) | 大(MB级) | ### 1.2 裸机开发应用场景 - **Boot ROM/SPL**: 芯片上电后的第一段代码,初始化DDR、时钟等 - **简单传感器采集**: 无需OS的低功耗设备 - **产品原型验证**: 快速验证硬件功能 - **Bootloader开发**: U-Boot等引导加载程序 - **安全启动链**: 信任根验证、加密启动 ### 1.3 I.MX6ULL裸机开发流程 ``` 1. 查阅芯片手册 → 定位外设寄存器地址 2. 编写汇编启动文件 → 初始化C运行环境(栈、BSS段) 3. 编写C语言业务代码 → 外设初始化、功能实现 4. 编写链接脚本(.lds) → 定义内存布局 5. 编写Makefile → 编译、链接、生成.bin 6. 烧录验证 → 通过SD卡/串口下载运行 ``` ### 1.4 I.MX6U GPIO体系架构 I.MX6ULL的GPIO系统由5组控制器组成: | GPIO组 | IO数量 | 基地址 | 说明 | | ------ | ------ | ---------- | --------------------------- | | GPIO1 | 32个 | 0x0209C000 | 仅GPIO1_IO00~IO09为专用GPIO | | GPIO2 | 22个 | 0x020A0000 | 其余IO需通过IOMUXC复用 | | GPIO3 | 29个 | 0x020A4000 | | | GPIO4 | 29个 | 0x020A8000 | | | GPIO5 | 12个 | 0x020AC000 | 包含SNVS域IO | **GPIO初始化三步曲**: 1. **时钟使能**: 设置CCGR寄存器对应位 2. **引足复用**: 配置IOMUXC_SW_MUX_CTL_PAD_XXX(MUX_MODE位选择ALT0~ALT8) 3. **电气属性**: 配置IOMUXC_SW_PAD_CTL_PAD_XXX(上拉/下拉/驱动强度/压摆率) --- ## 二、实验1: 汇编LED灯实验 ### 2.1 实验目的 - 掌握ARM汇编语言基本语法 - 理解I.MX6ULL GPIO寄存器配置方法 - 建立"查手册→写代码"的裸机开发思维 ### 2.2 硬件连接 LED0连接到GPIO1_IO03,通过三极管驱动,低电平点亮。 ### 2.3 关键代码 ```assembly .global _start _start: @ 使能GPIO1时钟 ldr r0, =0x020C4068 @ CCM_CCGR1寄存器地址 ldr r1, [r0] orr r1, r1, #(3 << 26) @ 设置CG14位[27:26]=11 str r1, [r0] @ 配置GPIO1_IO03复用为GPIO ldr r0, =0x020E0068 @ IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 mov r1, #5 @ ALT5 = GPIO模式 str r1, [r0] @ 配置GPIO1_IO03电气属性 ldr r0, =0x020E02F4 @ IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03 ldr r1, =0x10B0 str r1, [r0] @ 设置GPIO1_IO03为输出 ldr r0, =0x0209C004 @ GPIO1_GDIR寄存器 ldr r1, [r0] orr r1, r1, #(1 << 3) str r1, [r0] @ 点亮LED(输出低电平) ldr r0, =0x0209C000 @ GPIO1_DR寄存器 ldr r1, [r0] bic r1, r1, #(1 << 3) str r1, [r0] loop: b loop ``` ### 2.4 运行效果 LED0常亮,程序死循环保持状态。 --- ## 三、实验2: C语言LED灯实验 ### 3.1 实验目的 - 掌握汇编到C语言环境的跳转机制 - 理解C运行环境初始化(SVC模式、栈指针) - 学会模块化驱动编程 ### 3.2 关键代码 **启动文件 start.S**: ```assembly .global _start _start: @ 进入SVC模式 mrs r0, cpsr bic r0, r0, #0x1f orr r0, r0, #0x13 msr cpsr, r0 ldr sp, =0x80200000 @ 设置栈指针 b main @ 跳转到main函数 ``` **C语言主函数 main.c**: ```c #include "main.h" int main(void) { /* 使能GPIO1时钟 */ unsigned int *CCM_CCGR1 = (unsigned int *)0x020C4068; *CCM_CCGR1 |= (3 << 26); /* 配置GPIO1_IO03复用 */ unsigned int *SW_MUX = (unsigned int *)0x020E0068; *SW_MUX = 5; /* 配置电气属性 */ unsigned int *SW_PAD = (unsigned int *)0x020E02F4; *SW_PAD = 0x10B0; /* 设置为输出 */ unsigned int *GPIO1_GDIR = (unsigned int *)0x0209C004; *GPIO1_GDIR |= (1 << 3); /* 输出低电平点亮LED */ unsigned int *GPIO1_DR = (unsigned int *)0x0209C000; *GPIO1_DR &= ~(1 << 3); while(1); } ``` ### 3.3 从汇编到C的演进 - 汇编文件仅负责设置处理器模式(SVC)、初始化栈指针、跳转到main - DDR由Boot ROM读取DCD数据自动初始化,无需在代码中手动配置 - C代码通过指针直接操作寄存器,可读性和可维护性大幅提升 --- ## 四、实验3: 蜂鸣器实验 ### 4.1 实验目的 - 巩固GPIO输出控制 - 理解三极管驱动电路原理 - 掌握模块化驱动开发(bsp_beep.c/h) ### 4.2 硬件连接 蜂鸣器通过PNP三极管8550驱动,SNVS_TAMPER1(GPIO5_IO01)控制: - 输出低电平 → 三极管导通 → 蜂鸣器鸣叫 - 输出高电平 → 三极管截止 → 蜂鸣器静音 ### 4.3 关键代码 ```c void beep_init(void) { /* 1、复用为GPIO5_IO01 */ IOMUXC_SetPinMux(IOMUXC_SNVS_SNVS_TAMPER1_GPIO5_IO01, 0); /* 2、配置电气属性 */ IOMUXC_SetPinConfig(IOMUXC_SNVS_SNVS_TAMPER1_GPIO5_IO01, 0X10B0); /* 3、设置为输出 */ GPIO5->GDIR |= (1 << 1); /* 4、默认高电平,关闭蜂鸣器 */ GPIO5->DR |= (1 << 1); } void beep_switch(int status) { if(status == ON) GPIO5->DR &= ~(1 << 1); /* 打开蜂鸣器 */ else if(status == OFF) GPIO5->DR |= (1 << 1); /* 关闭蜂鸣器 */ } ``` ### 4.4 运行效果 LED0与蜂鸣器同步闪烁,周期约500ms。 --- ## 五、实验4: 按键输入实验 ### 5.1 实验目的 - 掌握GPIO输入配置方法 - 理解按键电平检测原理 - 建立通用GPIO驱动框架 ### 5.2 硬件连接 KEY0连接到UART1_CTS(GPIO1_IO18),外接10K上拉电阻: - 未按下:高电平 - 按下:低电平 ### 5.3 关键代码 **通用GPIO驱动**: ```c typedef enum _gpio_pin_direction { kGPIO_DigitalInput = 0U, kGPIO_DigitalOutput = 1U, } gpio_pin_direction_t; void gpio_init(GPIO_Type *base, int pin, gpio_pin_config_t *config) { if(config->direction == kGPIO_DigitalInput) base->GDIR &= ~(1 << pin); else { base->GDIR |= (1 << pin); gpio_pinwrite(base, pin, config->outputLogic); } } int gpio_pinread(GPIO_Type *base, int pin) { return (((base->DR) >> pin) & 0x1); } ``` **按键检测逻辑**: ```c int main(void) { int value = 0; gpio_pin_config_t key_config; /* 初始化KEY0为输入 */ key_config.direction = kGPIO_DigitalInput; gpio_init(GPIO1, 18, &key_config); while(1) { value = gpio_pinread(GPIO1, 18); /* 读取按键状态 */ if(value == 0) { /* 按下 */ delay(10); /* 消抖 */ if(gpio_pinread(GPIO1, 18) == 0) { beep_switch(!beep_status); /* 翻转蜂鸣器状态 */ } while(!gpio_pinread(GPIO1, 18)); /* 等待松开 */ } } } ``` ### 5.4 运行效果 按下KEY0切换蜂鸣器开关状态,实现按键控制。 --- ## 六、实验5: 高精度延时实验 ### 6.1 实验目的 - 掌握GPT定时器配置方法 - 实现微秒级精确延时 - 理解硬件定时器工作原理 ### 6.2 GPT定时器简介 GPT(General Purpose Timer)是32位向上计数器,特性: - 5个可选时钟源:ipg_clk(66MHz)、ipg_clk_32k、ipg_clk_24M等 - 12位分频器(1~4096分频) - 3路输出比较通道 - 2路输入捕获通道 - 两种工作模式:restart(重启)和free-run(自由运行) ### 6.3 关键代码 ```c void delay_init(void) { GPT1->CR = 0; /* 先关闭GPT */ GPT1->CR = (1 << 15); /* 软复位GPT */ while(GPT1->CR & (1 << 15)); /* 等待复位完成 */ GPT1->CR = (1 << 1) | (1 << 6); /* 使能+ipg_clk时钟源 */ GPT1->PR = 65; /* 66分频,1MHz */ GPT1->CR |= (1 << 0); /* 使能GPT计数器 */ } void delay_us(unsigned int us) { unsigned int cnt_start = GPT1->CNT; unsigned int cnt_end = cnt_start + us; /* 处理溢出情况 */ if(cnt_end >= cnt_start) { while(GPT1->CNT < cnt_end); } else { while(GPT1->CNT >= cnt_start); /* 等待溢出 */ while(GPT1->CNT < cnt_end); } } void delay_ms(unsigned int ms) { unsigned int i; for(i = 0; i < ms; i++) delay_us(1000); } ``` ### 6.4 运行效果 延时精度达到1μs级别,比循环延时准确得多。 ### 6.5 延时精度对比 | 延时方式 | 精度 | 原理 | 适用场景 | | ---------- | ---- | ---------------- | -------------- | | 循环延时 | ms级 | 编译器优化影响大 | 仅用于简单等待 | | SysTick | μs级 | Cortex-M专用 | STM32系列 | | GPT定时器 | 1μs | 硬件计数 | I.MX6U通用 | | EPIT定时器 | 1μs | 单次定时+中断 | 需要中断触发 | --- ## 七、实验6: DDR3实验 ### 7.1 实验目的 - 了解SRAM/SDRAM/DDR3发展历史 - 掌握DDR3初始化配置(通过DCD) - 理解DDR3寻址方式和时序参数 ### 7.2 DDR内存发展历程 | 类型 | 全称 | 特点 | 代表芯片 | | ----- | ---------------- | ------------------------ | ----------- | | SRAM | Static RAM | 不需刷新,速度快,成本高 | IS62WV51216 | | SDRAM | Synchronous DRAM | 同步时钟,需刷新,成本低 | W9825G6KH | | DDR | Double Data Rate | 双沿采样,带宽翻倍 | — | | DDR2 | DDR2 SDRAM | 4bit预取,1.8V | — | | DDR3 | DDR3 SDRAM | 8bit预取,1.5V,自动校准 | MT41K256M16 | | DDR4 | DDR4 SDRAM | 16bit预取,1.2V | — | ### 7.3 I.MX6U的DCD机制 I.MX6U Boot ROM会读取IVT中的DCD(Data Configuration Data)表,自动完成DDR3初始化: - DCD表存储在SD卡/eMMC的特定位置 - 包含DDR控制器寄存器配置和时序参数 - 用户无需手动编写DDR初始化代码 ### 7.4 DDR3初始化步骤(参考) ```c /* DDR3初始化主要参数 */ 1. 设置CCM_CSCMR1选择DDR时钟源(528MHz) 2. 配置CCM_CACRR设置DDR时钟分频 3. 设置MMDC类型寄存器(DDR3类型、位宽) 4. 配置时序参数(tRCD, tRP, tRAS等) 5. 配置校准寄存器(MPWLDECTRL, MPRDDLCTL等) 6. 使能MMDC控制器 7. 执行ZQ校准 ``` ### 7.5 运行效果 DDR3初始化成功后,程序可使用0x80000000~0xA0000000地址范围(512MB)。 --- ## 八、实验7: RGB LCD显示实验 ### 8.1 实验目的 - 掌握RGB LCD接口时序 - 实现像素点绘制和字符显示 - 理解eLCDIF控制器配置 ### 8.2 LCD核心概念 **分辨率**: 1080P=1920×1080,2K=2560×1440 **像素格式**: | 格式 | 位宽 | 字节 | 说明 | | -------- | ----- | ---- | ----------------------- | | RGB565 | 16bit | 2B | R5+G6+B5,STM32常用 | | RGB888 | 24bit | 3B | R8+G8+B8 | | ARGB8888 | 32bit | 4B | A8+R8+G8+B8,本实验使用 | **RGB LCD信号线**: | 信号线 | 说明 | | -------------------- | ---------- | | R[7:0]/G[7:0]/B[7:0] | 24根数据线 | | DE | 数据使能 | | VSYNC | 垂直同步 | | HSYNC | 水平同步 | | PCLK | 像素时钟 | ### 8.3 关键代码 ```c /* LCD初始化 */ void lcd_init(void) { /* 1、初始化eLCDIF控制器 */ /* 2、配置LCD时序参数 */ /* 3、设置帧缓冲地址 */ /* 4、使能LCD控制器 */ } /* 绘制像素点 */ void lcd_drawpoint(unsigned int x, unsigned int y, unsigned int color) { unsigned int *fb = (unsigned int *)FRAME_BUFFER_ADDR; fb[y * LCD_WIDTH + x] = color; } /* 填充矩形 */ void lcd_fill(unsigned int x0, unsigned int y0, unsigned int x1, unsigned int y1, unsigned int color) { unsigned int x, y; for(y = y0; y < y1; y++) { for(x = x0; x < x1; x++) { lcd_drawpoint(x, y, color); } } } ``` ### 8.4 运行效果 LCD屏幕显示彩色测试图案和字符信息。 --- ## 九、实验8: RTC实时时钟实验 ### 9.1 实验目的 - 掌握I.MX6U SNVS模块配置 - 实现年/月/日/时/分/秒时间读写 - 理解RTC后备电池供电机制 ### 9.2 SNVS架构 I.MX6U的RTC位于SNVS(Secure Non-Volatile Storage)模块内: - **SNVS_HP**: 高功耗域,主电源供电 - **SNVS_LP**: 低功耗域,后备电池供电,包含SRTC - SRTC本质是一个32位秒计数器,外接32.768KHz晶振 - 以1970-01-01 00:00:00为基准时间 ### 9.3 关键代码 ```c void rtc_init(void) { /* 1、使能SNVS时钟 */ CCM->CCGR4 |= (3 << 20); /* 2、配置SNVS_LP SRTC */ SNVS->LPCR &= ~(1 << 0); /* 先关闭SRTC */ while(SNVS->LPCR & (1 << 0)); /* 等待关闭 */ /* 3、设置初始时间(2026-09-17 12:00:00) */ rtc_settime(2026, 9, 17, 12, 0, 0); /* 4、使能SRTC */ SNVS->LPCR |= (1 << 0); while(!(SNVS->LPCR & (1 << 0))); /* 等待使能 */ } void rtc_settime(int year, int month, int day, int hour, int min, int sec) { /* 将日期时间转换为秒数 */ unsigned int seconds = timestamp_from_ymd(year, month, day, hour, min, sec); /* SRTC计数器是32位(非47位) */ SNVS->LPSRTCMR = (seconds >> 17) & 0x7FFF; /* 高15位 */ SNVS->LPSRTCLR = (seconds << 15) & 0xFFFF8000; /* 低17位 */ } ``` ### 9.4 寄存器注意事项 NXP参考手册中SNVS_SRTCMR和SNVS_SRTCLR的描述有误: - SRTC计数器实际是**32位**(非47位) - SNVS_SRTCMR的bit14:0是高15位 - SNVS_SRTCLR的bit31:bit15是低17位 ### 9.5 运行效果 LCD显示实时时间,秒数持续递增。 --- ## 十、实验9: I2C实验 ### 10.1 实验目的 - 掌握I2C总线协议时序 - 驱动AP3216C距离/光线/红外传感器 - 理解I2C读写操作流程 ### 10.2 I2C协议要点 | 要素 | 说明 | | ---- | --------------------------------- | | 总线 | SCL(时钟) + SDA(数据),需4.7K上拉 | | 速率 | 标准模式100Kb/s,快速模式400Kb/s | | 寻址 | 7位地址,支持多从机 | | 时序 | 起始位→设备地址→ACK→数据→停止位 | **写时序**: START → 设备地址+W → ACK → 寄存器地址 → ACK → 数据 → ACK → STOP **读时序**: START → 设备地址+W → ACK → 寄存器地址 → ACK → 重复START → 设备地址+R → ACK → 数据 → NACK → STOP ### 10.3 关键代码 ```c void i2c_write_reg(unsigned char addr, unsigned char reg, unsigned char data) { /* 1、发送起始信号 */ i2c_start(); /* 2、发送设备地址 + 写位 */ i2c_send_byte((addr << 1) | 0); i2c_wait_ack(); /* 3、发送寄存器地址 */ i2c_send_byte(reg); i2c_wait_ack(); /* 4、发送数据 */ i2c_send_byte(data); i2c_wait_ack(); /* 5、发送停止信号 */ i2c_stop(); } unsigned char i2c_read_reg(unsigned char addr, unsigned char reg) { unsigned char data; i2c_start(); i2c_send_byte((addr << 1) | 0); /* 写 */ i2c_wait_ack(); i2c_send_byte(reg); i2c_wait_ack(); i2c_start(); /* 重复起始 */ i2c_send_byte((addr << 1) | 1); /* 读 */ i2c_wait_ack(); data = i2c_read_byte(0); /* NACK + STOP */ i2c_stop(); return data; } ``` ### 10.4 运行效果 读取AP3216C的光强度、距离、红外数据,通过LCD显示。 --- ## 十一、实验10: SPI实验 ### 11.1 实验目的 - 掌握SPI总线协议(四线全双工) - 驱动ICM-20608六轴传感器 - 理解SPI四种工作模式 ### 11.2 SPI协议要点 | 要素 | 说明 | | ---- | ---------------------------------------------- | | 总线 | CS(片选) + SCK(时钟) + MOSI(主出) + MISO(主入) | | 速率 | 最高可达几十MHz,远超I2C | | 模式 | CPOL+CPHA组合,4种模式 | | 通信 | 全双工,收发同时进行 | | 寻址 | 通过CS片选,无需发送设备地址 | **SPI四种模式**: | 模式 | CPOL | CPHA | 空闲电平 | 采样沿 | | ------ | ---- | ---- | -------- | ------ | | Mode 0 | 0 | 0 | 低 | 上升沿 | | Mode 1 | 0 | 1 | 低 | 下降沿 | | Mode 2 | 1 | 0 | 高 | 下降沿 | | Mode 3 | 1 | 1 | 高 | 上升沿 | ### 11.3 关键代码 ```c void spi_init(void) { /* 1、配置ECSPI3为主模式 */ ECSPI3->CONREG = 0; /* 先关闭 */ ECSPI3->CONREG |= (7 << 24); /* 突发长度=8bit */ ECSPI3->CONREG |= (0 << 18); /* 通道0 */ ECSPI3->CONREG |= (0 << 16); /* 不关心SPI_RDY */ ECSPI3->CONREG |= (0 << 12); /* 预分频=1 */ ECSPI3->CONREG |= (0 << 8); /* 分频=1 */ ECSPI3->CONREG |= (1 << 4); /* 通道0主模式 */ ECSPI3->CONREG |= (0 << 3); /* SMC=0 */ ECSPI3->CONREG |= (1 << 2); /* XCH=1,开始突发 */ ECSPI3->CONREG |= (1 << 0); /* 使能SPI */ /* 2、配置SPI模式(CPOL=1, CPHA=1 → Mode 3) */ ECSPI3->CONFIGREG = 0; ECSPI3->CONFIGREG |= (3 << 4); /* 模式3 */ } unsigned char spi_transfer(unsigned char data) { unsigned int dummy; while(!(ECSPI3->STATUSREG & (1 << 0))); /* 等待TXFIFO空 */ ECSPI3->TXDATA = data; /* 发送数据 */ while(!(ECSPI3->STATUSREG & (1 << 3))); /* 等待RXFIFO满 */ dummy = ECSPI3->RXDATA; /* 读取数据 */ return (unsigned char)dummy; } ``` ### 11.4 运行效果 读取ICM-20608的加速度、陀螺仪、温度数据,通过LCD显示。 --- ## 十二、裸机开发技巧 ### 12.1 调试方法 | 方法 | 工具 | 适用场景 | | ---------- | ----------- | ------------------ | | LED指示 | GPIO输出 | 最基础的状态指示 | | 串口打印 | UART printf | 信息输出最常用 | | JTAG/SWD | 调试器+IDE | 单步调试、变量查看 | | 寄存器查看 | Memory窗口 | 验证配置是否正确 | | 逻辑分析仪 | Saleae等 | 时序分析 | ### 12.2 代码规范 ```c /* 1. 寄存器操作使用位域宏 */ #define BIT(n) (1 << (n)) #define SET_BIT(reg, n) ((reg) |= BIT(n)) #define CLR_BIT(reg, n) ((reg) &= ~BIT(n)) #define GET_BIT(reg, n) (((reg) >> (n)) & 1) /* 2. 寄存器地址使用宏定义 */ #define GPIO1_DR (*(volatile unsigned int *)0x0209C000) /* 3. 模块化驱动结构 */ /* bsp_xxx.h → 函数声明、类型定义 */ /* bsp_xxx.c → 函数实现 */ /* main.c → 业务逻辑调用 */ /* 4. 头文件保护 */ #ifndef __BSP_XXX_H #define __BSP_XXX_H /* ... */ #endif ``` ### 12.3 性能优化 - **避免函数调用开销**: 关键路径使用内联函数 - **减少内存访问**: 使用寄存器缓存变量 - **DMA传输**: 大数据量外设使用DMA - **编译优化**: 使用-O2/-O3优化选项 - **Cache使能**: ARM Cortex-A7支持I-Cache和D-Cache ### 12.4 链接脚本模板 ```ld SECTIONS { . = 0x87800000; /* 链接地址 */ .text : { *(.text) /* 代码段 */ *(.rodata) /* 只读数据 */ } .data : { *(.data) /* 初始化数据 */ } .bss : { __bss_start = .; *(.bss) /* 未初始化数据 */ __bss_end = .; } } ``` --- ## 十三、跨平台对比 ### 13.1 I.MX6ULL vs STM32 vs RK3568裸机差异 | 特性 | I.MX6ULL | STM32F103 | RK3568 | | -------------- | ---------------- | -------------------- | ---------------- | | **内核架构** | Cortex-A7 | Cortex-M3 | Cortex-A55 | | **主频** | 528MHz | 72MHz | 1.8GHz | | **内存管理** | MMU(可选) | 无MMU | MMU | | **GPIO命名** | GPIO1_IO00~IO31 | PA0~15 | GPIO0_A0~D7 | | **寄存器操作** | 直接访问 | HAL库封装 | 直接访问 | | **启动方式** | Boot ROM→IVT→DCD | 向量表→Reset_Handler | Boot ROM→TPL→SPL | | **DDR配置** | DCD表自动初始化 | 无需(片内SRAM) | TPL/SPL初始化 | | **GPIO数量** | 124个 | 51个 | 156个 | | **时钟配置** | CCM+PLL | RCC | CRU | | **中断控制器** | GIC | NVIC | GIC-500 | | **开发难度** | 中等 | 简单 | 较高 | ### 13.2 寄存器操作对比 **STM32 (HAL库)**: ```c HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET); ``` **I.MX6ULL (寄存器)**: ```c GPIO1->DR |= (1 << 3); /* 输出高电平 */ GPIO1->DR &= ~(1 << 3); /* 输出低电平 */ ``` **RK3568 (寄存器)**: ```c GPIO0->PORT_DR |= BIT(0); /* 输出高电平 */ GPIO0->PORT_DR &= ~BIT(0); /* 输出低电平 */ ``` ### 13.3 关键差异总结 - **I.MX6U**: GPIO以功能命名,需配置IOMUXC,Boot ROM提供DCD自动初始化DDR - **STM32**: GPIO以端口+引脚命名,HAL库封装好,NVIC中断控制器简单 - **RK3568**: 更复杂,支持Linux启动,裸机开发较少使用,通常用于启动Linux内核 --- ## 十四、面试精选 ### Q1: 请解释I.MX6U的GPIO初始化流程,与STM32有何不同? **答案**: I.MX6U GPIO初始化需要4步: 1. **时钟使能**: 配置CCM_CCGR寄存器对应位(STM32是RCC->APB2ENR等) 2. **引足复用**: 配置IOMUXC_SW_MUX_CTL_PAD_XXX的MUX_MODE位,选择ALT0~ALT8(STM32是GPIOx->AFRL/AFRH) 3. **电气属性**: 配置IOMUXC_SW_PAD_CTL_PAD_XXX,设置上拉/下拉/驱动强度/压摆率(STM32是GPIOx->OSPEEDR/PUPDR等) 4. **功能配置**: 配置GPIOx_GDIR(方向)和GPIOx_DR(数据) 关键差异:I.MX6U的GPIO复用是通过IOMUXC控制器集中管理的,而STM32是通过AFR寄存器分散管理的。 --- ### Q2: 裸机开发中如何实现精确延时?请对比不同方法的优缺点。 **答案**: | 方法 | 原理 | 优点 | 缺点 | | -------- | ---------------------- | --------------------- | ------------------------ | | 循环延时 | 空指令消耗CPU周期 | 实现简单 | 受编译器优化影响,精度差 | | SysTick | Cortex-M专用24位计数器 | 精度高(1μs) | 仅Cortex-M可用 | | EPIT | 32位定时器+中断 | 精度高,可触发中断 | 中断开销 | | GPT | 32位定时器轮询 | 精度高(1μs),无需中断 | CPU忙等 | I.MX6U推荐使用GPT定时器,配置ipg_clk(66MHz)为时钟源,66分频得到1MHz,计数器每加1表示1μs。 --- ### Q3: I2C和SPI有什么区别?各适用于什么场景? **答案**: | 特性 | I2C | SPI | | ------ | ---------------- | --------------------- | | 线数 | 2根(SCL+SDA) | 4根(CS+SCK+MOSI+MISO) | | 速率 | 最高400Kb/s | 最高几十MHz | | 模式 | 半双工 | 全双工 | | 寻址 | 软件发送设备地址 | 硬件CS片选 | | 多设备 | 通过地址区分 | 通过CS片选 | | 复杂度 | 时序复杂 | 时序简单 | **适用场景**: - I2C: 低速传感器(温湿度、加速度计)、EEPROM、配置寄存器 - SPI: 高速设备(Flash、LCD控制器、高速ADC)、需要全双工通信 --- ### Q4: DDR3初始化为什么可以不在代码中手动配置? **答案**: I.MX6U的Boot ROM在启动时会读取IVT(Image Vector Table)中的DCD(Data Configuration Data)表。DCD表存储在SD卡/eMMC的特定位置,包含了DDR控制器的所有寄存器配置和时序参数。Boot ROM读取DCD表后自动完成DDR初始化,因此用户代码中无需手动编写DDR初始化代码。 但需要注意: 1. DCD表必须正确配置,否则DDR无法使用 2. 在SPL阶段可能需要重新校准DDR参数 3. 如果不使用Boot ROM启动(如JTAG调试),则需要手动初始化DDR --- ### Q5: 解释I.MX6U的SNVS模块架构,为什么RTC需要后备电池? **答案**: I.MX6U的SNVS(Secure Non-Volatile Storage)分为两个子模块: - **SNVS_HP**(高功耗域): 由系统主电源供电,主电源掉电后关闭 - **SNVS_LP**(低功耗域): 由后备电池(VDD_SNVS_IN)供电,主电源掉电后继续运行 RTC(SRTC)位于SNVS_LP中,需要32.768KHz晶振提供时钟。即使系统主电源关闭,SNVS_LP仍由纽扣电池供电继续计时。这保证了: 1. 系统断电后时间不丢失 2. 不需要每次开机都设置时间 3. 支持低功耗唤醒功能 --- ## 十五、实验总结 ### 15.1 实验知识图谱 ``` 汇编LED → C语言LED → 蜂鸣器 → 按键输入 ↓ ↓ ↓ ↓ GPIO输出 模块化驱动 三极管驱动 GPIO输入 ↓ 高精度延时 ← GPT定时器 ↓ DDR3 → LCD → RTC → I2C → SPI ↓ ↓ ↓ ↓ ↓ 内存 显示 时间 传感器 传感器 ``` ### 15.2 学习路径建议 | 阶段 | 实验 | 目标 | | -------- | ---------------------------------- | ------------------ | | **入门** | 汇编LED → C语言LED → 蜂鸣器 → 按键 | 掌握GPIO和基础驱动 | | **进阶** | 高精度延时 → DDR3 → LCD | 理解定时器和显示 | | **综合** | RTC → I2C → SPI | 掌握通信协议 | | **实战** | 串联所有实验,构建完整系统 | 集成能力 | ### 15.3 下一步学习方向 1. **中断系统**: GIC中断控制器、GPIO中断、定时器中断 2. **UART串口**: 串口通信、printf重定向 3. **DMA传输**: 高效数据搬运 4. **触摸屏**: I2C触摸 + LCD显示 5. **PWM**: 背光控制、电机驱动 6. **ADC**: 模拟信号采集 --- > **参考文献**: > > - 《I.MX6ULL参考手册》第32章 IOMUX Controller > - 《I.MX6ULL参考手册》第14章 GPT Timer > - 《I.MX6UL参考手册》第46章 SNVS > - 《I.MX6ULL参考手册》第30章 ECSPI