I.MX6ULL 裸机开发:LCD Framebuffer 与 RGB888 学习笔记

最近在 I.MX6ULL 裸机开发中学习 LCD 屏幕驱动,搞懂了 Framebuffer 显存原理、RGB 色彩格式,并且完成了实操代码:配置 LCD 引脚、时钟,初始化 LCDIF 控制器,最后接入 LM75 温度传感器,在屏幕上实时显示采集到的温度数据。

一、基础概念:像素、分辨率与 RGB888

屏幕画面本质是由大量像素点 构成的矩阵。我们这块 LCD 分辨率为800*480,代表横向 800 个像素,纵向 480 个像素。 分辨率是屏幕横向像素数量 × 纵向像素数量。分辨率相同,屏幕尺寸越小,PPI 越高,画面越细腻;尺寸相同,分辨率越高,图像效果越好。

每个像素点依靠 RGB 三原色混合出不同颜色,常见色彩格式有 RGB888、RGB565、ARGB8888:

  • RGB888:R、G、B 三个通道各占 8bit(0~255),一个像素占用 3 字节,可以表达 1670 万种颜色,色彩丰富;
  • RGB565:R 占 5bit、G 占 6bit、B 占 5bit,单个像素 2 字节,色彩较少,但占用内存更小;
  • ARGB8888:在 RGB 基础上增加 Alpha 透明通道,单个像素 4 字节,也就是我们代码里使用的格式。

我们代码中使用unsigned int保存颜色,就是 ARGB 格式,A 通道 0,后面 24bit 存放 RGB。

二、Framebuffer(显存)核心原理

Framebuffer,也就是显存,是内存中专门存放图像像素数据的一块空间。 显存起始地址固定,0x89000000,像素坐标(x,y)对应的显存地址计算公式:

复制代码
*(p + 800 * y + x) = color;
  • p:显存首地址
  • y:纵向行号,800是每行像素个数
  • x:横向列号

简单理解:CPU 不用直接和 LCD 屏幕打交道,只需要往显存内存地址写入像素颜色值 。硬件 LCDIF 控制器会自动读取显存里的数据,按照时序源源不断把像素送到屏幕上显示。 这就是裸机 LCD 绘图的核心:写内存 = 画屏幕。 我们写的lcd_drawpoint画点函数,底层就是这个公式。

三、LCD 时序信号

LCD 屏幕正常工作,依靠 4 根关键时序信号线:

  1. PCLK(像素时钟):Pixel Clock。同步每一个像素点发送的时间间隔,每来一个时钟,传输 1 个像素;
  2. HSYNC(行同步信号):Horizontal Sync。同步每一行图像,一行像素扫描完成,产生行同步脉冲,切换到下一行;
  3. VSYNC(帧同步信号):Vertical Sync。同步整幅图像,一整屏图像扫描完毕,触发帧同步,刷新下一帧;
  4. DE(Data Enable):数据使能信号。DE 为高的时候,当前传输的数据是有效的像素图像数据。

在 LCD 初始化代码中,我们配置的 hspw/hfp/hbp、vspw/vfp/vbp 这些参数,就是行场同步的前后肩、同步脉冲宽度,必须和屏幕手册匹配,否则屏幕会花屏、无法点亮。

四、代码实现讲解

1. LCD 硬件初始化

复制代码
void lcd_pin_init(void)

使用IOMUXC_SetPinMux把 24 根 LCD 数据线、PCLK、HSYNC、VSYNC、DE 等引脚复用为 LCDIF 外设功能;IOMUXC_SetPinConfig配置引脚电气属性,同时配置背光 GPIO 引脚。

复制代码
void lcd_clock_init(void)

配置 LCD 视频 PLL 时钟,为 LCDIF 控制器提供像素时钟 PCLK,是屏幕正常输出时序的基础。

复制代码
void lcd_init(void)

LCD 总初始化函数:引脚初始化→时钟初始化→LCDIF 复位→填写屏幕时序参数,指定 framebuffer 显存地址,最后开启 LCDIF,硬件开始输出图像。

2. 绘图基础函数

复制代码
// 画像素点
void lcd_drawpoint(int x, int y, unsigned int col)
{
    unsigned int *p = (unsigned int *)lcd_dev.frame_buffer;
    if (x > lcd_dev.width || y > lcd_dev.height)
    {
        return;
    }
    *(p + lcd_dev.width * y + x) = col;
}

这个函数就是显存公式的直接实现,给对应坐标的显存地址写入颜色,屏幕对应坐标就会点亮。lcd_clear_screen清屏函数,就是循环遍历全部显存,统一填充同一个颜色。

3. 项目实战:LCD 显示 LM75 实时温度

主循环代码:

复制代码
while (1)
{
    float f = get_lm75_temp();
    int k = f * 10;
    int m = k / 10;
    int n = k % 10;
    sprintf(buffer, "%d.%d", m, n);
    lcd_show_string(400, 240, 16 * strlen(buffer), 32, 32, buffer);
}

逻辑:I2C 读取 LM75 温度传感器浮点温度,放大十倍拆分整数、小数部分,用 sprintf 拼接字符串,调用lcd_show_string在屏幕坐标(400,240)位置打印温度。

当前代码存在小问题:每次刷新文字没有擦除旧文字,温度变化时会出现文字重叠拖影。解决方案:刷新前,在文字区域填充背景色矩形,覆盖上一次的文字。也可以增加delay_ms降低刷新频率,减少 CPU 占用。

五、学习小结

这次学习打通了 I.MX6ULL 裸机 LCD 驱动的完整流程:从 RGB 像素基础、显存 Framebuffer 原理,到 LCD 行场时序,再到寄存器配置、代码绘图,最后结合 I2C 温度传感器做了一个完整的小项目。 Framebuffer 模型非常关键,后续 Linux 系统下的 LCD 显示、Qt 界面,底层也沿用这套显存思想,裸机的学习相当于打好了底层基础。

相关推荐
数字新视界1 小时前
国产动环系统技术原理与应用实践的深入剖析
嵌入式硬件·物联网·系统架构·机房管理·动力与环境监控系统
一个低调的青年1 小时前
电池健康状态估计(一)
经验分享·笔记·其他·算法·模型
影寂ldy1 小时前
C# TCP转串口Modbus网关终极完整版笔记(队列防抖+一问一答+帧解析+双客户端轮询)
笔记·tcp/ip·c#
宁静致远20211 小时前
在VScode中使用MATLAB
vscode·matlab
恶魔泡泡糖2 小时前
stm32F103C8T6标准库串口接收之控制LED灯3
stm32·单片机·嵌入式硬件
新思维软件2 小时前
基于无线传输的地衡系统:三节点 LoRa 组网与 MQTT 上云的分布式称重方案
分布式·stm32·单片机·嵌入式硬件·物联网·物联网开发
芯祥联2 小时前
STM32 上跑 MQTT 怎么选?嵌入式 MQTT 客户端 C 语言实现对比
c语言·stm32·单片机·嵌入式硬件·网络协议·开源·broker
Orange_sparkle2 小时前
langgraph快速入门
学习·ai·typescript·deepseek·claude code
差不多的周周10 小时前
《骨架上下文:基于零镜头骨架的动作识别的骨架侧上下文提示学习》论文核心部分详解
学习