news 2026/2/1 13:15:27

ARM架构——LCD 显示与 PWM 背光控制

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ARM架构——LCD 显示与 PWM 背光控制

目录

一、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 时序详解

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 背光,是嵌入式系统中 “显示 + 控制” 的典型组合。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/31 22:59:17

零基础玩转Qwen3语义搜索:手把手教你构建个性化知识库

零基础玩转Qwen3语义搜索&#xff1a;手把手教你构建个性化知识库 1. 什么是语义搜索&#xff1f;别再被“关键词”困住了 你有没有试过在文档里搜“怎么修电脑蓝屏”&#xff0c;结果返回一堆讲“Windows更新失败”的内容&#xff0c;却漏掉了那篇真正教你用安全模式进系统、…

作者头像 李华
网站建设 2026/2/1 5:48:42

SeqGPT-560M镜像免配置部署教程:3条命令启动NER服务并接入业务系统

SeqGPT-560M镜像免配置部署教程&#xff1a;3条命令启动NER服务并接入业务系统 1. 这不是另一个聊天机器人&#xff0c;而是一个“文本信息挖掘机” 你有没有遇到过这样的场景&#xff1a;每天收到几十份简历&#xff0c;要手动圈出姓名、公司、岗位、电话&#xff1b;法务团…

作者头像 李华
网站建设 2026/2/1 7:49:54

Fun-ASR麦克风权限问题解决全攻略,新手少走弯路

Fun-ASR麦克风权限问题解决全攻略&#xff0c;新手少走弯路 你是不是也遇到过这样的情况&#xff1a;点开Fun-ASR WebUI&#xff0c;兴致勃勃想试试实时语音识别&#xff0c;刚点下麦克风图标&#xff0c;浏览器却弹出“无法访问麦克风”提示&#xff1f;或者明明授权了&#…

作者头像 李华
网站建设 2026/1/31 2:32:51

多模态小模型新标杆:MinerU技术路线与部署价值分析

多模态小模型新标杆&#xff1a;MinerU技术路线与部署价值分析 1. 为什么我们需要一个“文档专用”的小模型&#xff1f; 你有没有遇到过这些场景&#xff1a; 手里有一张拍得歪歪扭扭的PDF截图&#xff0c;想快速提取其中的公式和表格&#xff0c;却卡在OCR识别不准、格式全…

作者头像 李华
网站建设 2026/2/1 5:44:35

跨语言播客制作:用SenseVoiceSmall同步处理多国语言素材

跨语言播客制作&#xff1a;用SenseVoiceSmall同步处理多国语言素材 你是否经历过这样的困扰&#xff1a;手头有一段日语访谈录音&#xff0c;一段粤语街头采访&#xff0c;还有一段韩语嘉宾对话&#xff0c;想快速整理成带情绪标注的双语播客文稿&#xff0c;却卡在语音识别这…

作者头像 李华