目录
一、LCD 基本概念
1.1 LCD 基本构成
1.2 数据传输模式
1.3 LCD 时序详解
1.3.1 水平时序
1.3.2 垂直时序
1.3.3 时钟频率计算
二、PWM 背光控制原理
三、LCD 驱动实现
3.1 引脚配置
3.2 时钟配置
3.3 LCDIF 模块初始化
3.4 显示功能实现
四、PWM 驱动实现
4.1 引脚配置与模块初始化
4.2 占空比调节
4.3 应用层控制
五、总结
一、LCD 基本概念
1.1 LCD 基本构成
LCD(Liquid Crystal Display)是一种非自发光型显示屏,需要背光支持。其核心由以下部分组成:
- 像素阵列:每个像素由 R(红)、G(绿)、B(蓝)三原色子像素构成。
- 驱动电路:逐行扫描,每行点亮后快速切换至下一行。
- 控制器:负责发送数据、同步信号和时钟。
在 IMU6ULL 系统中,使用的是TFT-LCD 屏幕,分辨率为800×480,采用RGB888 格式(24位真彩色),即每个像素占用 3 字节(R:8bit, G:8bit, B:8bit)。
1.2 数据传输模式
IMX6ULL 通过eLCDIF(Enhanced LCD Interface)模块连接到 LCD 模块,采用并行同步单工通信方式:
| 信号类型 | 数量 | 功能 |
|---|---|---|
| 数据线 | 24根 | RGB888 数据传输(8r+8g+8b) |
| 控制线 | 4根 | DE(Data Enable)、HSYNC、VSYNC、PCLK |
| 地址/控制总线 | 32位 | 用于 DMA 写入帧缓冲区 |
| 片选 / 使能 | 若干 | 控制 LCD 是否响应 |
关键点:所有信号都由主控芯片产生,LCD 只需接收并解析这些信号即可刷新画面。
1.3 LCD 时序详解
1.3.1 水平时序
针对 “一行像素” 的传输周期。水平方向上,一个完整的行周期分为以下几个阶段:
- HFP(行前沿,40):位于行同步脉冲之前的空闲周期。为LCD准备下一行的显示。
- HSYNC(行同步脉冲,48):实际的行同步信号。触发LCD开始新的一行扫描。
- HBP(行后沿,88):位于行同步脉冲之后的空闲周期。为下一行扫描做准备。
- 有效数据段(800):传输当前行的有效像素数据。
总水平周期 = 40 + 48 + 88 + 800 =976
1.3.2 垂直时序
针对 “一整幅画面” 的传输周期。垂直方向上,一帧图像包含多个行周期,其划分如下:
- VFP(场前沿,13):位于场同步脉冲之前的空闲行数。为新的一帧图像显示做准备。
- VSYNC(场同步脉冲,3):实际的场同步信号。触发LCD开始新的一帧图像。
- VBP(场后沿,32):位于场同步脉冲之后的空闲行数。为下一帧图像显示做准备。
- 有效行段(480):传输一帧的所有有效行数据。
总垂直周期 = 13 + 3 + 32 + 480=528
1.3.3 时钟频率计算
- 像素时钟 PCLK 频率决定了刷新速度。
- 对于 800×480,60Hz,所需 PCLK = (976 * 528) * 60 ≈ 30.92 MHz
二、PWM 背光控制原理
PWM(Pulse Width Modulation,脉冲宽度调制)是控制 LCD 背光亮度的核心技术:
- 工作原理:通过调整高电平持续时间(占空比)来控制平均功率
- 占空比:高电平时间 / 周期时间,0% 表示完全关闭,100% 表示全亮
- 频率选择:通常选择 1-2kHz,高于人眼视觉暂留频率(约60Hz),避免可见闪烁
三、LCD 驱动实现
LCD 驱动的核心是硬件配置(引脚 + 时钟)+ 时序参数初始化 + 帧缓存管理。
3.1 引脚配置
IMX6ULL 的 GPIO 引脚支持多功能复用,LCD 驱动需将指定引脚复用为 LCDIF(LCD 接口)功能,并配置电气特性(驱动能力、上下拉等)。
- 代码实现:
// 引脚复用:将24根数据线+4根控制信号复用为LCDIF功能 IOMUXC_SetPinMux(IOMUXC_LCD_DATA00_LCDIF_DATA00, 0); // 数据引脚0 ... IOMUXC_SetPinMux(IOMUXC_LCD_DATA23_LCDIF_DATA23, 0); // 数据引脚23 IOMUXC_SetPinMux(IOMUXC_LCD_CLK_LCDIF_CLK, 0); // LCD像素时钟 IOMUXC_SetPinMux(IOMUXC_LCD_HSYNC_LCDIF_HSYNC, 0); // 水平同步信号 IOMUXC_SetPinMux(IOMUXC_LCD_VSYNC_LCDIF_VSYNC, 0); // 垂直同步信号 IOMUXC_SetPinMux(IOMUXC_LCD_ENABLE_LCDIF_ENABLE, 0); // 显示使能信号 // 电气特性配置:0xB9对应高速驱动、上拉、100MHz速率 IOMUXC_SetPinConfig(IOMUXC_LCD_DATA00_LCDIF_DATA00, 0xB9); ... IOMUXC_SetPinConfig(IOMUXC_LCD_DATA23_LCDIF_DATA23, 0xB9); IOMUXC_SetPinConfig(IOMUXC_LCD_CLK_LCDIF_CLK, 0xB9); IOMUXC_SetPinConfig(IOMUXC_LCD_HSYNC_LCDIF_HSYNC, 0xB9); IOMUXC_SetPinConfig(IOMUXC_LCD_VSYNC_LCDIF_VSYNC, 0xB9); IOMUXC_SetPinConfig(IOMUXC_LCD_ENABLE_LCDIF_ENABLE, 0xB9); IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO08_GPIO1_IO08, 0xB9); // 将背光引脚输出高电平 GPIO1->GDIR |= (1 << 8); GPIO1->DR |= (1 << 8);3.2 时钟配置
LCD 的像素时钟(PCLK)直接影响显示稳定性,IMX6ULL 通过 PLL5(VIDEO PLL)生成 LCDIF 所需时钟。
- 代码实现:
// 配置PLL5:输入时钟1MHz,倍频31倍,输出31MHz CCM_ANALOG->PLL_VIDEO_NUM = 0; // 分子为0(默认1) CCM_ANALOG->PLL_VIDEO_DENOM = 1; // 分母为1 unsigned int t = CCM_ANALOG->PLL_VIDEO; t &= ~(3 << 19); t |= (2 << 19); // 倍频系数31 t &= ~(0x7F << 0); t |= (31 << 0); // 参考分频系数 CCM_ANALOG->PLL_VIDEO = t; CCM_ANALOG->PLL_VIDEO |= (1 << 13); // 使能PLL5 // 配置LCDIF时钟路径:PLL5 -> 预分频 -> 分频 -> LCDIF CCM->CSCDR2 |= (2 << 15); // 选择PLL5为时钟源 CCM->CSCDR2 |= (3 << 12); // 预分频系数4(3+1) CCM->CBCMR |= (5 << 23); // 后分频系数6(5+1),最终PCLK=31MHz/(4*6)≈1.3MHz(适配800×480刷新率)3.3 LCDIF 模块初始化
LCDIF(LCD 接口控制器)是 IMX6ULL 专门用于 LCD 驱动的外设,需配置时序参数、帧缓存地址、像素格式等。
- 代码实现:
// LCD设备参数初始化(与硬件时序一致) lcd_dev.width = 800; // 屏幕宽度 lcd_dev.height = 480; // 屏幕高度 lcd_dev.pix_size = 4; // 像素字节数(ARGB8888) lcd_dev.hspw = 48; // HSYNC脉冲宽度(对应硬件参数) lcd_dev.hbp = 88; // 行后沿(HSYNC后到有效数据的时间) lcd_dev.hfp = 40; // 行前沿(有效数据后到HSYNC的时间) lcd_dev.vspw = 3; // VSYNC脉冲宽度 lcd_dev.vbp = 32; // 场后沿 lcd_dev.vfp = 13; // 场前沿 lcd_dev.frame_addr = _FRAME_RAM_ADDRESS; // 帧缓存地址(SDRAM分配) // 配置LCDIF控制寄存器 LCDIF->CTRL |= (1 << 19) | (1 << 17) | (3 << 10) | (3 << 8) | (1 << 5); // CTRL1:像素格式配置(ARGB8888) LCDIF->CTRL1 = (0x07 << 16); // 传输尺寸(高度+宽度) LCDIF->TRANSFER_COUNT = (lcd_dev.height << 16) | (lcd_dev.width << 0); // 帧缓存地址(当前+下一个,单缓存模式) LCDIF->CUR_BUF = lcd_dev.frame_addr; LCDIF->NEXT_BUF = lcd_dev.frame_addr; // 垂直时序配置 LCDIF->VDCTRL0 = (1 << 28) | (1 << 24) | (1 << 21) | (1 << 20) | (lcd_dev.vspw << 0); LCDIF->VDCTRL1 = lcd_dev.height + lcd_dev.vspw + lcd_dev.vbp + lcd_dev.vfp; // 垂直总周期 LCDIF->VDCTRL2 = lcd_dev.hspw << 18 | (lcd_dev.width + lcd_dev.hspw + lcd_dev.hbp + lcd_dev.hfp); // 水平总周期 LCDIF->CTRL |= (1 << 0); // 使能LCDIF screen_clear(lcd_dev.back_color); // 清屏(填充背景色)3.4 显示功能实现
基于帧缓存机制,通过操作内存实现绘图功能:
// 清屏(填充整个屏幕为指定颜色) void screen_clear(unsigned int color) { int j = 0, i = 0; for (j = 0; j < lcd_dev.height; j++) for (i = 0; i < lcd_dev.width; i++) lcd_drawpoint(i, j, color); } // 绘制单个像素点 void lcd_drawpoint(int x, int y, unsigned int color) { unsigned int *p = (unsigned int *)lcd_dev.frame_addr; if (x >= lcd_dev.width || y >= lcd_dev.height) return; // 边界检查 *(p + lcd_dev.width * y + x) = color; // 直接操作帧缓存 } // 绘制横线 void screen_h_line(int x, int y, int len, unsigned int color) { int i = 0; for (i = x; i < x + len; i++) lcd_drawpoint(i, y, color); }四、PWM 驱动实现
PWM(脉冲宽度调制)的核心是通过改变高电平占空比控制外设(如 LCD 背光亮度)。基于 IMX6ULL 的 PWM1 模块,实现占空比 0~100% 循环调节。
4.1 引脚配置与模块初始化
- 代码实现:
// 引脚复用:GPIO1_IO08 -> PWM1_OUT IOMUXC_SetPinMux(IOMUXC_GPIO1_IO08_PWM1_OUT, 0); // 电气特性配置(与LCD引脚一致,适配高速输出) IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO08_PWM1_OUT, 0xB9); // PWM模块配置 PWM1->PWMCR |= (1 << 3); // 软件复位PWM while ((PWM1->PWMCR & (1 << 3)) != 0); // 等待复位完成 PWM1->PWMCR |= (1 << 26); // 使能自动重载 PWM1->PWMCR |= (1 << 16); // 中断使能(周期结束触发) PWM1->PWMCR |= (65 << 4); // 预分频系数66(65+1) PWM1->PWMCR |= (3 << 1); // 时钟源选择IPG_CLK(66MHz) PWM1->PWMIR |= (1 << 0); // 使能周期中断 // PWM周期设置:PWMPR = 周期计数-2 PWM1->PWMPR = 1000 - 2; // 中断配置:注册中断服务函数,使能GIC中断 system_interrupt_register(PWM1_IRQn, pwm_interrupt_handler); GIC_EnableIRQ(PWM1_IRQn); GIC_SetPriority(PWM1_IRQn, 0); // 最高优先级 set_ratio(0.5); // 初始占空比50% PWM1->PWMCR |= (1 << 0); // 使能PWM输出4.2 占空比调节
通过中断服务函数实时更新占空比,核心是修改 PWMSAR(PWM 输出比较寄存器):
static float g_dc = 0.5; // 全局占空比变量 // 设置占空比(dc范围0~1) void set_ratio(float dc) { unsigned short temp = PWM1->PWMPR; int i = 0; for (i = 0; i < 4; i++) // 多次写入确保稳定性 PWM1->PWMSAR = temp * dc; // 占空比=dc,高电平计数=周期×dc } // PWM中断服务函数(周期结束触发) void pwm_interrupt_handler(void) { if ((PWM1->PWMSR & (1 << 3)) != 0) // 检查周期中断标志 { set_ratio(g_dc); // 更新占空比 PWM1->PWMSR |= (1 << 3); // 清除中断标志 } }4.3 应用层控制
main 函数中循环修改 g_dc 变量,实现占空比 0~100% 循环:
float dc = 0.5; while (1) { delay_ms(1000); set_g_dc(dc); // 更新全局占空比 dc += 0.1; // 每次增加10% if (dc > 1.0) // 超过100%则重置为0 dc = 0.0; }五、总结
核心实现要点:
- LCD 驱动:关键是引脚复用、时钟稳定性、时序参数匹配硬件规格,帧缓存机制是绘图功能的基础;
- PWM 驱动:核心是时钟源选择、周期配置、中断驱动的占空比更新,确保信号平滑无抖动;
- 联动设计:利用 PWM 调节 LCD 背光,是嵌入式系统中 “显示 + 控制” 的典型组合。