一、前言
最近在调试 i.MX6ULL 的 RGB LCD 驱动,从最基础的引脚复用开始,一步步配到时钟和 eLCDIF 寄存器。整个过程踩了不少坑,也把 IOMUXC 和 CCM 的机制搞清楚了。这篇文章把整个流程整理出来,希望能帮到同样在调试 LCD 的朋友。
二、整体流程
LCD 驱动初始化分四大步:
- 第 1 步:引脚复用 + 电气特性(IOMUXC) ✅
- 第 2 步:PCLK 时钟配置(CCM) ✅
- 第 3 步:eLCDIF 寄存器配置 ⬜
- 第 4 步:刷屏 / 图形库 ⬜
本文重点讲前两步。
三、第一步:引脚复用 + 电气特性
3.1 要配哪些引脚
RGB888 接口的引脚是固定的:
| 类别 | 引脚 | 数量 |
|---|---|---|
| 数据线 | LCD_DATA00 ~ LCD_DATA23 | 24 |
| 控制线 | LCD_CLK / LCD_ENABLE / LCD_HSYNC / LCD_VSYNC | 4 |
| 背光 | GPIO1_IO08 | 1 |
合计 29 个引脚。
3.2 怎么从原理图找到 Pad 名
看开发板原理图,左边一列是芯片 Pad 名,右边框是板级网络名:
| Pad 名(芯片) | 网络名(板子) | 用途 |
|---|---|---|
| LCD_DATA00 | LCD1_DATA00 | 数据 bit0 |
| ... | ... | ... |
| LCD_DATA23 | LCD1_DATA23 | 数据 bit23 |
| LCD_CLK | LCD1_CLK | 像素时钟 |
| LCD_ENABLE | LCD1_ENABLE | 数据使能 DE |
| LCD_HSYNC | LCD1_HSYNC | 水平同步 |
| LCD_VSYNC | LCD1_VSYNC | 垂直同步 |
| GPIO1_IO08 | LCD_BL | 背光 |
3.3 在头文件里找宏
在 fsl_iomuxc.h 里搜 Pad 名,找到对应功能的宏。宏名结构:
c
IOMUXC_<Pad名>_<功能名>
比如:
c
IOMUXC_LCD_DATA00_LCDIF_DATA00 // Pad LCD_DATA00 复用为 LCDIF_DATA00
IOMUXC_GPIO1_IO08_GPIO1_IO08 // Pad GPIO1_IO08 复用为 GPIO
宏展开后是 5 个值:
c
#define IOMUXC_LCD_DATA00_LCDIF_DATA00 0x020E0118U, 0x0U, 0x00000000U, 0x0U, 0x020E03A4U
| 顺序 | 值 | 含义 |
|---|---|---|
| 1 | 0x020E0118 | SW_MUX_CTL 地址 |
| 2 | 0x0U | MUX_MODE(ALT 编号) |
| 3 | 0x00000000 | 输入选择寄存器 |
| 4 | 0x0U | 输入 daisy |
| 5 | 0x020E03A4 | SW_PAD_CTL 地址 |
3.4 引脚复用(SW_MUX_CTL)
寄存器位域:
| 位 | 名称 | 含义 |
|---|---|---|
| 4 | SION | 软件输入使能 |
| 3:0 | MUX_MODE | 复用模式(ALT 编号) |
代码:
c
IOMUXC_SetPinMux(IOMUXC_LCD_DATA00_LCDIF_DATA00, 0);
- 宏里的
0x0U= MUX_MODE = 0 = ALT0 - 第二个参数
0= SION 位(不是 ALT)
注意:第二个参数传 0 是 SION 位,ALT 藏在宏里。
3.5 电气特性(SW_PAD_CTL)
寄存器位域:
| 位 | 名称 | 含义 |
|---|---|---|
| 7:6 | SPEED | 速度 |
| 5:3 | DSE | 驱动强度 |
| 12 | PKE | 保持器使能 |
| 13 | PUE | 上下拉/保持器选择 |
| 15:14 | PUS | 上下拉选择 |
| 0 | SRE | 压摆率 |
0xB9 的含义:
c
0xB9 = 1011 1001b
| 位 | 名称 | 值 | 含义 |
|---|---|---|---|
| 7:6 | SPEED | 10 | 中等速度(100MHz) |
| 5:3 | DSE | 111 | 最大驱动(R0/7) |
| 0 | SRE | 1 | 快速压摆率 |
代码:
c
IOMUXC_SetPinConfig(IOMUXC_LCD_DATA00_LCDIF_DATA00, 0xB9);
3.6 背光 GPIO 配置
背光是独立 GPIO,除了 IOMUXC 还要配 GPIO 模块:
c
// 1. 引脚复用:配成 GPIO(ALT5)
IOMUXC_SetPinMux(IOMUXC_GPIO1_IO08_GPIO1_IO08, 0);
// 2. 电气特性
IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO08_GPIO1_IO08, 0xB9);
// 3. 方向:输出(1=输出,0=输入)
GPIO1->GDIR |= (1 << 8);
// 4. 电平:高(背光亮)
GPIO1->DR |= (1 << 8);
GDIR 和 DR 是 GPIO 模块的寄存器:
| 寄存器 | 偏移 | GPIO1 地址 | 作用 |
|---|---|---|---|
| DR | +0x00 | 0x0209C000 | 数据(电平) |
| GDIR | +0x04 | 0x0209C004 | 方向(0=输入,1=输出) |
四、第二步:PCLK 时钟配置
4.1 时钟路径
c
24MHz 晶振
↓
PLL5 (PLL_VIDEO) → 744MHz
↓
VIDEO_DIV → 744MHz
↓
LCDIF1_PRE_CLK_SEL → 选 PLL5
↓
LCDIF1_PRED → ÷4 = 186MHz
↓
LCDIF1_PODF → ÷6 = 31MHz
↓
PCLK
4.2 PCLK 目标频率怎么算
c
PCLK = 刷新率 × (HFP + HSPW + HBP + width) × (VFP + VSPW + VBP + height)
代入 800×480@60Hz 的参数:
c
PCLK = 60 × (40 + 48 + 88 + 800) × (13 + 3 + 32 + 480)
= 60 × 976 × 528
≈ 30.9 MHz ≈ 31MHz
4.3 PLL5 配置
PLL 公式:
c
PLL 输出 = 参考时钟 × (DIV_SELECT + NUM/DENOM)
目标 744MHz:
c
744MHz = 24MHz × 31
所以 DIV_SELECT = 31,NUM = 0,DENOM = 1。
代码:
c
// 关闭小数分频
CCM_ANALOG->PLL_VIDEO_NUM = 0;
CCM_ANALOG->PLL_VIDEO_DENOM = 1;
// 选择 24MHz 参考时钟
unsigned int r = CCM_ANALOG->PLL_VIDEO;
r &= ~(3 << 19);
r |= (2 << 19);
// DIV_SELECT = 31
r &= ~(0x7F << 0);
r |= (31 << 0);
CCM_ANALOG->PLL_VIDEO = r;
// 使能 PLL
CCM_ANALOG->PLL_VIDEO |= (1 << 13);
4.4 VIDEO_DIV
c
CCM_ANALOG->MISC2 &= ~(3 << 30); // VIDEO_DIV = 0,分频 = 1
4.5 LCDIF 时钟树
c
cr = CCM->CSCDR2;
r |= (2 << 15); // LCDIF1_PRE_CLK_SEL = 2(选 PLL5)
r &= ~(7 << 12);
r |= (3 << 12); // LCDIF1_PRED = 3(÷4)
r &= ~(7 << 9); // LCDIF1_CLK_SEL = 0(选 PRED 后)
CCM->CSCDR2 = r;
CCM->CBCMR &= ~(7 << 23);
CCM->CBCMR |= (5 << 23); // LCDIF1_PODF = 5(÷6)
4.6 完整计算
c
24MHz × 31 = 744MHz (PLL5)
744MHz / 1 = 744MHz (VIDEO_DIV)
744MHz / 4 = 186MHz (PRED)
186MHz / 6 = 31MHz (PODF)
最终 PCLK = 31MHz ✅
五、踩过的坑
坑 1:HSYNC/VSYNC 宏名拼写错误
c
IOMUXC_LCD_HSYNS_LCDIF_HSYNC // ❌ 多了一个 S
IOMUXC_LCD_HSYNC_LCDIF_HSYNC // ✅ 正确
坑 2:以为第二个参数是 ALT
c
IOMUXC_SetPinMux(IOMUXC_LCD_DATA00_LCDIF_DATA00, 0);
这里的 0 是 SION 位,不是 ALT。ALT 藏在宏里。
坑 3:GDIR 的 0/1 记反
i.MX6ULL 手册明确写:
- 0 = INPUT(输入)
- 1 = OUTPUT(输出)
坑 4:PLL 锁定等待被注释
c
//while ((CCM_ANALOG->PLL_VIDEO & (1 << 31)) == 0);
建议恢复,否则 PLL 没稳定就继续配置,可能出错。