title: Linux LCD驱动开发 tags: [embedded, linux, driver, lcd, framebuffer] created: 2026-09-17 updated: 2026-09-17
📖 章节说明:本节 LCD 原理与 RGB 时序内容对应《指南》第二十四章《RGBLCD显示实验》(原书第五十九章仅引用之),属相邻章节补充。
LCD(Liquid Crystal Display)即液晶显示器,通过控制液晶分子的排列来实现显示。LCD面板由背光源、偏光片、液晶层、彩色滤光片和驱动电路组成。每个像素由红(R)、绿(G)、蓝(B)三个子像素组成,通过控制各子像素的透光量来混合出不同颜色。
LCD工作时,背光源提供均匀的白色光源,液晶分子在电场作用下旋转,改变光线的偏振方向。偏光片只允许特定方向的光线通过,从而控制每个像素的亮暗程度。TFT(Thin Film Transistor)LCD为每个像素配备独立的薄膜晶体管,实现像素级的精确控制。
RGB接口是并行数据传输接口,通过同步信号(HSYNC、VSYNC)和数据信号(RGB数据线)传输图像数据。关键时序参数:
| 参数 | 符号 | 说明 | 典型值(1024×600) |
|---|---|---|---|
| 水平同步脉宽 | hsync-len (HSPW) | HSYNC高电平持续时钟周期 | 20 |
| 水平后肩 | hback-porch (HBP) | HSYNC下降沿到有效数据的时钟数 | 140 |
| 水平有效像素 | hactive | 每行有效像素个数 | 1024 |
| 水平前肩 | hfront-porch (HFP) | 有效数据结束到下一个HSYNC上升沿 | 160 |
| 垂直同步脉宽 | vsync-len (VSPW) | VSYNC高电平持续行数 | 3 |
| 垂直后肩 | vback-porch (VBP) | VSYNC下降沿到有效行的行数 | 20 |
| 垂直有效行 | vactive | 每帧有效行数 | 600 |
| 垂直前肩 | vfront-porch (VFP) | 有效行结束到下一个VSYNC上升沿 | 12 |
| 格式 | 位宽 | R | G | B | 总线宽度 | 应用场景 |
|---|---|---|---|---|---|---|
| RGB565 | 16bit | 5bit | 6bit | 5bit | 16bit | 低成本嵌入式 |
| RGB666 | 18bit | 6bit | 6bit | 6bit | 18bit | 中端设备 |
| RGB888 | 24bit | 8bit | 8bit | 8bit | 24bit | 高质量显示 |
| ARGB8888 | 32bit | 8bit | 8bit | 8bit+Alpha | 32bit | GUI合成 |
flowchart TB
accTitle: Linux LCD Subsystem Architecture
accDescr: Linux LCD子系统从用户空间到硬件的层次架构
subgraph 用户空间
app["🖥️ 应用程序"]
fbset["⚙️ fbset工具"]
end
subgraph 内核空间
vfs["📁 VFS层"]
fbmem["📄 fbmem.c"]
fops["🔧 fb_ops操作集"]
driver["🖥️ LCD控制器驱动"]
dt["📋 设备树"]
end
subgraph 硬件层
lcdif["⚡ eLCDIF控制器"]
panel["🖥️ LCD面板"]
end
app -->|"open/write/ioctl/mmap"| vfs
fbset -->|"ioctl"| fbmem
vfs --> fbmem
fbmem --> fops
fops --> driver
dt -->|"解析屏幕参数"| driver
driver --> lcdif
lcdif -->|"RGB数据+同步信号"| panel
Framebuffer(帧缓冲)是Linux内核对显示设备的抽象。它将显示硬件虚拟为一个字符设备/dev/fbX,用户空间程序通过标准文件操作(open/read/write/ioctl/mmap)即可访问显示设备。
fb设备的核心操作集定义在drivers/video/fbdev/core/fbmem.c:
static const struct file_operations fb_fops = {
.owner = THIS_MODULE,
.read = fb_read,
.write = fb_write,
.unlocked_ioctl = fb_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = fb_compat_ioctl,
#endif
.mmap = fb_mmap,
.open = fb_open,
.release = fb_release,
#ifdef HAVE_ARCH_FB_UNMAPPED_AREA
.get_unmapped_area = get_fb_unmapped_area,
#endif
#ifdef CONFIG_FB_DEFERRED_IO
.fsync = fb_deferred_io_fsync,
#endif
.llseek = default_llseek,
};
Linux内核将每个Framebuffer设备抽象为fb_info结构体,定义在include/linux/fb.h:
struct fb_info {
atomic_t count; /* 引用计数 */
int node; /* 设备节点号 */
int flags; /* 标志 */
struct mutex lock; /* 互斥锁 */
struct mutex mm_lock; /* 互斥锁,用于fb_mmap和smem_*域 */
struct fb_var_screeninfo var; /* 当前可变参数(分辨率、色深等) */
struct fb_fix_screeninfo fix; /* 当前固定参数(物理地址、行宽等) */
struct fb_monspecs monspecs; /* 显示器特性 */
struct work_struct queue; /* 帧缓冲事件队列 */
struct fb_pixmap pixmap; /* 图像硬件映射 */
struct fb_pixmap sprite; /* 光标硬件映射 */
struct fb_cmap cmap; /* 当前调色板 */
struct list_head modelist; /* 当前模式列表 */
struct fb_videomode *mode; /* 当前视频模式 */
#ifdef CONFIG_FB_BACKLIGHT
struct backlight_device *bl_dev; /* 背光设备 */
struct mutex bl_curve_mutex; /* 背光曲线互斥锁 */
u8 bl_curve[FB_BACKLIGHT_LEVELS]; /* 背光曲线 */
#endif
struct fb_ops *fbops; /* 帧缓冲操作函数集 */
struct device *device; /* 父设备 */
struct device *dev; /* 当前fb设备 */
int class_flag; /* 私有sysfs标志 */
char __iomem *screen_base; /* 虚拟内存基地址(显存) */
unsigned long screen_size; /* 虚拟内存大小(显存大小) */
void *pseudo_palette; /* 伪16位调色板 */
};
原书第五十九章重点关注的成员为
var、fix、fbops、screen_base、screen_size、pseudo_palette。⚠️ 来源说明:下面的
fb_var_screeninfo、fb_fix_screeninfo结构体定义取自内核include/linux/fb.h,原书第五十九章并未逐字段列出,此处为扩展补充。
fb_var_screeninfo存储可变的显示参数:
struct fb_var_screeninfo {
__u32 xres; /* 可见分辨率-水平 */
__u32 yres; /* 可见分辨率-垂直 */
__u32 xres_virtual; /* 虚拟分辨率-水平 */
__u32 yres_virtual; /* 虚拟分辨率-垂直 */
__u32 bits_per_pixel; /* 每像素位数 */
__u32 grayscale; /* 0=彩色, 1=灰度 */
struct fb_bitfield red; /* 红色位域 */
struct fb_bitfield green; /* 绿色位域 */
struct fb_bitfield blue; /* 蓝色位域 */
struct fb_bitfield transp; /* 透明度位域 */
__u32 width; /* 画面宽度(mm) */
__u32 height; /* 画面高度(mm) */
};
fb_fix_screeninfo存储固定的显示参数:
struct fb_fix_screeninfo {
char id[16]; /* 标识符 */
unsigned long smem_start; /* 显存物理起始地址 */
__u32 smem_len; /* 显存长度 */
__u32 type; /* FB类型 */
__u32 visual; /* 视觉类型 */
__u32 line_length; /* 每行字节数 */
unsigned long mmio_start; /* MMIO起始地址 */
__u32 mmio_len; /* MMIO长度 */
};
flowchart LR
accTitle: fb_info Data Structure Relationships
accDescr: fb_info及其子结构体的关联关系
fi["📦 fb_info"]
var["📊 var (可变参数)"]
fix["🔒 fix (固定参数)"]
ops["🔧 fb_ops (操作集)"]
screen["💾 screen_base (显存)"]
fi --> var
fi --> fix
fi --> ops
fi --> screen
var --> res["xres × yres × bpp"]
fix --> mem["smem_start + smem_len"]
ops --> fbfops["fb_check_var / fb_set_par / fb_mmap"]
LCD屏幕参数通过设备树的display-timings节点配置,核心属性:
| 属性 | 说明 | 示例值 |
|---|---|---|
clock-frequency |
像素时钟频率(Hz) | 51200000 (51.2MHz) |
hactive |
水平有效像素数 | 1024 |
vactive |
垂直有效行数 | 600 |
hfront-porch |
水平前肩(HFP) | 160 |
hback-porch |
水平后肩(HBP) | 140 |
hsync-len |
水平同步脉宽(HSPW) | 20 |
vfront-porch |
垂直前肩(VFP) | 12 |
vback-porch |
垂直后肩(VBP) | 20 |
vsync-len |
垂直同步脉宽(VSPW) | 3 |
hsync-active |
HSYNC极性(0/1) | 0 |
vsync-active |
VSYNC极性(0/1) | 0 |
de-active |
DE极性(0/1) | 1 |
pixelclk-active |
像素时钟极性(0/1) | 0 |
LCD需要配置数据线和控制线两组引脚。以I.MX6ULL 24位RGB888为例:
pinctrl_lcdif_dat: lcdifdatgrp {
fsl,pins = <
MX6UL_PAD_LCD_DATA00__LCDIF_DATA00 0x49
MX6UL_PAD_LCD_DATA01__LCDIF_DATA01 0x49
MX6UL_PAD_LCD_DATA02__LCDIF_DATA02 0x49
MX6UL_PAD_LCD_DATA03__LCDIF_DATA03 0x49
MX6UL_PAD_LCD_DATA04__LCDIF_DATA04 0x49
MX6UL_PAD_LCD_DATA05__LCDIF_DATA05 0x49
MX6UL_PAD_LCD_DATA06__LCDIF_DATA06 0x49
MX6UL_PAD_LCD_DATA07__LCDIF_DATA07 0x49
MX6UL_PAD_LCD_DATA08__LCDIF_DATA08 0x49
MX6UL_PAD_LCD_DATA09__LCDIF_DATA09 0x49
MX6UL_PAD_LCD_DATA10__LCDIF_DATA10 0x49
MX6UL_PAD_LCD_DATA11__LCDIF_DATA11 0x49
MX6UL_PAD_LCD_DATA12__LCDIF_DATA12 0x49
MX6UL_PAD_LCD_DATA13__LCDIF_DATA13 0x49
MX6UL_PAD_LCD_DATA14__LCDIF_DATA14 0x49
MX6UL_PAD_LCD_DATA15__LCDIF_DATA15 0x49
MX6UL_PAD_LCD_DATA16__LCDIF_DATA16 0x49
MX6UL_PAD_LCD_DATA17__LCDIF_DATA17 0x49
MX6UL_PAD_LCD_DATA18__LCDIF_DATA18 0x49
MX6UL_PAD_LCD_DATA19__LCDIF_DATA19 0x49
MX6UL_PAD_LCD_DATA20__LCDIF_DATA20 0x49
MX6UL_PAD_LCD_DATA21__LCDIF_DATA21 0x49
MX6UL_PAD_LCD_DATA22__LCDIF_DATA22 0x49
MX6UL_PAD_LCD_DATA23__LCDIF_DATA23 0x49
>;
};
pinctrl_lcdif_ctrl: lcdifctrlgrp {
fsl,pins = <
MX6UL_PAD_LCD_CLK__LCDIF_CLK 0x49
MX6UL_PAD_LCD_ENABLE__LCDIF_ENABLE 0x49
MX6UL_PAD_LCD_HSYNC__LCDIF_HSYNC 0x49
MX6UL_PAD_LCD_VSYNC__LCDIF_VSYNC 0x49
>;
};
注意:电气属性值
0x49表示降低驱动能力至R0/1,用于避免模拟开关影响信号完整性。
由芯片级 imx6ull.dtsi 提供,所有 I.MX6ULL 板子共有,compatible 决定其由 mxsfb.c 驱动:
lcdif: lcdif@021c8000 {
compatible = "fsl,imx6ul-lcdif", "fsl,imx28-lcdif";
reg = <0x021c8000 0x4000>;
interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6UL_CLK_LCDIF_PIX>,
<&clks IMX6UL_CLK_LCDIF_APB>,
<&clks IMX6UL_CLK_DUMMY>;
clock-names = "pix", "axi", "disp_axi";
status = "disabled";
};
说明:原书 NXP 官方默认节点为 4.3寸 480×272 屏(
clock-frequency=<9200000>、hfront-porch=<8>、hback-porch=<4>、hsync-len=<41>、vback-porch=<2>、vfront-porch=<4>、vsync-len=<10>,bits-per-pixel=<16>),本教程需替换为 ATK7016(7寸 1024×600)参数。
&lcdif {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_lcdif_dat
&pinctrl_lcdif_ctrl>;
display = <&display0>;
status = "okay";
display0: display {
bits-per-pixel = <24>; /* RGB888模式 */
bus-width = <24>; /* 总线宽度 */
display-timings {
native-mode = <&timing0>;
timing0: timing0 {
clock-frequency = <51200000>; /* 51.2MHz像素时钟 */
hactive = <1024>; /* 水平有效像素 */
vactive = <600>; /* 垂直有效行 */
hfront-porch = <160>; /* HFP */
hback-porch = <140>; /* HBP */
hsync-len = <20>; /* HSPW */
vback-porch = <20>; /* VBP */
vfront-porch = <12>; /* VFP */
vsync-len = <3>; /* VSPW */
hsync-active = <0>; /* HSYNC低有效 */
vsync-active = <0>; /* VSYNC低有效 */
de-active = <1>; /* DE高有效 */
pixelclk-active = <0>; /* CLK上升沿采样 */
};
};
};
};
背光控制引脚为 GPIO1_IO08,复用为 PWM1_OUT,需要三处配置:引脚 pinctrl、&pwm1 节点、backlight 节点。
/* 1. GPIO1_IO08 引脚复用为 PWM1_OUT */
pinctrl_pwm1: pwm1grp {
fsl,pins = <
MX6UL_PAD_GPIO1_IO08__PWM1_OUT 0x110b0
>;
};
/* 2. 使能 pwm1 */
&pwm1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_pwm1>;
status = "okay";
};
/* 3. 将 PWM1 与 LCD 背光关联 */
backlight {
compatible = "pwm-backlight";
pwms = <&pwm1 0 5000000>; /* PWM1, 周期5ms(200Hz) */
brightness-levels = <0 4 8 16 32 64 128 255>;
default-brightness-level = <6>;
status = "okay";
};
原书说明:
brightness-levels共 8 级(0~7),对应占空比 0%、1.57%、3.13%、6.27%、12.55%、25.1%、50.19%、100%;default-brightness-level = <6>即 50.19% 亮度。PWM 背光驱动文件为drivers/video/backlight/pwm_bl.c,设备树绑定参考Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt。
register_framebuffer()向内核注册Framebuffer设备:
int register_framebuffer(struct fb_info *fb_info);
| 参数 | 说明 |
|---|---|
fb_info |
已初始化的fb_info结构体指针 |
| 返回值 | 0=成功, 负值=失败码 |
配套的注销函数:
int unregister_framebuffer(struct fb_info *fb_info);
LCD驱动需要实现fb_ops结构体中的关键操作函数:
static struct fb_ops lcd_fb_ops = {
.owner = THIS_MODULE,
.fb_check_var = lcd_check_var, /* 检查显示参数 */
.fb_set_par = lcd_set_par, /* 设置显示参数 */
.fb_setcolreg = lcd_setcolreg, /* 设置颜色寄存器 */
.fb_blank = lcd_blank, /* 屏幕空白控制 */
.fb_pan_display = lcd_pan_display, /* 平移显示(双缓冲) */
.fb_mmap = lcd_mmap, /* 显存映射 */
.fb_fillrect = cfb_fillrect, /* 矩形填充 */
.fb_copyarea = cfb_copyarea, /* 区域拷贝 */
.fb_imageblit = cfb_imageblit, /* 图像绘制 */
};
在应用设置新参数前调用,校验参数合法性:
static int lcd_check_var(struct fb_var_screeninfo *var,
struct fb_info *info)
{
/* 检查像素时钟是否超出硬件范围 */
if (var->pixclock < LCD_MIN_PIXCLOCK)
return -EINVAL;
/* 检查分辨率是否超出LCD控制器支持范围 */
if (var->xres > LCD_MAX_XRES || var->yres > LCD_MAX_YRES)
return -EINVAL;
/* 检查色深支持 */
if (var->bits_per_pixel != 16 && var->bits_per_pixel != 24)
return -EINVAL;
return 0;
}
将校验后的参数写入LCD控制器寄存器:
static int lcd_set_par(struct fb_info *info)
{
struct lcd_controller *ctrl = info->par;
/* 设置扫描方向 */
/* 设置像素格式(RGB565/RGB888) */
/* 配置时序参数寄存器 */
/* 使能LCD控制器 */
return 0;
}
控制LCD的电源和背光状态:
static int lcd_blank(int blank_mode, struct fb_info *info)
{
switch (blank_mode) {
case FB_BLANK_UNBLANK: /* 正常显示 */
/* 开启背光, 使能LCD */
break;
case FB_BLANK_NORMAL: /* 待机(低功耗) */
case FB_BLANK_VSYNC_SUSPEND:
case FB_BLANK_HSYNC_SUSPEND:
case FB_BLANK_POWERDOWN: /* 关闭背光, 关闭LCD */
break;
}
return 0;
}
用于实现双缓冲或滚动显示:
static int lcd_pan_display(struct fb_var_screeninfo *var,
struct fb_info *info)
{
/* 设置显存起始地址寄存器 */
/* 切换显示缓冲区 */
return 0;
}
I.MX6ULL的LCD驱动文件位于drivers/video/fbdev/mxsfb.c,这是一个标准的platform驱动。
platform驱动注册:
static const struct of_device_id mxsfb_dt_ids[] = {
{ .compatible = "fsl,imx23-lcdif", .data = &mxsfb_devtype[0], },
{ .compatible = "fsl,imx28-lcdif", .data = &mxsfb_devtype[1], },
{ /* sentinel */ }
};
static struct platform_driver mxsfb_driver = {
.probe = mxsfb_probe,
.remove = mxsfb_remove,
.shutdown = mxsfb_shutdown,
.id_table = mxsfb_devtype,
.driver = {
.name = DRIVER_NAME,
.of_match_table = mxsfb_dt_ids,
.pm = &mxsfb_pm_ops,
},
};
module_platform_driver(mxsfb_driver);
mxsfb_probe核心流程:
flowchart TB
accTitle: mxsfb_probe Initialization Flow
accDescr: LCD驱动probe函数的初始化流程
start["🚀 probe入口"] --> get_res["📥 获取资源"]
get_res --> alloc_host["💾 分配host结构体"]
alloc_host --> alloc_fb["📦 分配fb_info"]
alloc_fb --> request_irq["⚡ 申请中断"]
request_irq --> ioremap["🗺️ 寄存器映射"]
ioremap --> init_fbinfo["📋 初始化fb_info"]
init_fbinfo --> init_ctrl["⚙️ 初始化控制器"]
init_ctrl --> register["✅ 注册framebuffer"]
register --> end["🎉 probe完成"]
probe函数关键代码解析:
static int mxsfb_probe(struct platform_device *pdev)
{
const struct of_device_id *of_id =
of_match_device(mxsfb_dt_ids, &pdev->dev);
struct resource *res;
struct mxsfb_info *host;
struct fb_info *fb_info;
struct pinctrl *pinctrl;
int irq = platform_get_irq(pdev, 0);
int gpio, ret;
/* 1. 获取寄存器资源首地址(0x021C8000) */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
/* 2. 分配mxsfb_info结构体 */
host = devm_kzalloc(&pdev->dev, sizeof(struct mxsfb_info), GFP_KERNEL);
if (!host)
return -ENOMEM;
/* 3. 分配fb_info结构体 */
fb_info = framebuffer_alloc(sizeof(struct fb_info), &pdev->dev);
if (!fb_info) {
devm_kfree(&pdev->dev, host);
return -ENOMEM;
}
host->fb_info = fb_info; /* host->fb_info */
fb_info->par = host; /* fb_info->par 关联host */
/* 4. 申请中断 */
ret = devm_request_irq(&pdev->dev, irq, mxsfb_irq_handler, 0,
dev_name(&pdev->dev), host);
if (ret)
goto fb_release;
/* 5. 寄存器地址映射 */
host->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(host->base)) {
ret = PTR_ERR(host->base);
goto fb_release;
}
/* 6. 分配pseudo_palette调色板 */
fb_info->pseudo_palette = devm_kzalloc(&pdev->dev,
sizeof(u32) * 16, GFP_KERNEL);
if (!fb_info->pseudo_palette) {
ret = -ENOMEM;
goto fb_release;
}
INIT_LIST_HEAD(&fb_info->modelist);
pm_runtime_enable(&host->pdev->dev);
/* 7. 初始化fb_info (从设备树获取参数,并申请显存) */
ret = mxsfb_init_fbinfo(host);
if (ret != 0)
goto fb_pm_runtime_disable;
mxsfb_dispdrv_init(pdev, fb_info);
/* 8. 配置pinctrl引脚 */
if (!host->dispdrv) {
pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
if (IS_ERR(pinctrl)) {
ret = PTR_ERR(pinctrl);
goto fb_pm_runtime_disable;
}
}
/* 9. 初始化LCD控制器寄存器 */
if (!host->enabled) {
writel(0, host->base + LCDC_CTRL);
mxsfb_set_par(fb_info);
mxsfb_enable_controller(fb_info);
pm_runtime_get_sync(&host->pdev->dev);
}
/* 10. 注册framebuffer设备 */
ret = register_framebuffer(fb_info);
if (ret != 0)
goto fb_destroy;
return ret;
}
eLCDIF寄存器偏移定义:
#define LCDC_CTRL 0x00
#define LCDC_CTRL1 0x10
#define LCDC_V4_CTRL2 0x20
#define LCDC_V3_TRANSFER_COUNT 0x20
#define LCDC_V4_TRANSFER_COUNT 0x30
#define LCDC_V4_CUR_BUF 0x40
#define LCDC_V4_NEXT_BUF 0x44
#define LCDC_V4_DTDMA 0x180
#define LCDC_V4_DEBUG0 0x1d0
#define LCDC_V3_DEBUG0 0x1f0
原书提示:可对照《I.MX6ULL 参考手册》eLCDIF 章节核对这些偏移值。
mxsfb_ops操作集:
static struct fb_ops mxsfb_ops = {
.owner = THIS_MODULE,
.fb_check_var = mxsfb_check_var,
.fb_set_par = mxsfb_set_par,
.fb_setcolreg = mxsfb_setcolreg,
.fb_ioctl = mxsfb_ioctl,
.fb_blank = mxsfb_blank,
.fb_pan_display = mxsfb_pan_display,
.fb_mmap = mxsfb_mmap,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
};
sequenceDiagram
accTitle: LCD Driver Initialization Sequence
accDescr: LCD驱动从设备探测到显示就绪的调用时序
participant P as platform总线
participant D as mxsfb_driver
participant F as fb_info
participant C as eLCDIF控制器
P->>D: 设备树匹配(mxsfb_dt_ids)
D->>D: mxsfb_probe()
D->>F: framebuffer_alloc()
D->>D: devm_ioremap_resource()
D->>F: mxsfb_init_fbinfo()
Note over F: 从设备树解析时序参数
D->>C: mxsfb_set_par()
Note over C: 配置时序/像素格式/时钟
D->>C: mxsfb_enable_controller()
Note over C: 使能LCD控制器
D->>D: register_framebuffer()
Note over D: 创建/dev/fb0
⚠️ 来源说明:原书第五十九章使用
make dtbs、Linux logo、/dev/tty1、背光 sysfs 等方式验证;本节中的fbset命令不属于原书内容,为扩展知识。
fbset工具用于查看和修改Framebuffer设备参数:
# 查看当前fb0参数
fbset -fb /dev/fb0
# 查看详细信息
fbset -fb /dev/fb0 -i
# 修改分辨率(需要驱动支持)
fbset -fb /dev/fb0 -g 1024 600 1024 600 24
1. 显示Linux Logo
内核启动时自动显示小企鹅logo,配置选项:
Device Drivers → Graphics support → Bootup logo
├── Standard black and white Linux logo
├── Standard 16-color Linux logo
└── Standard 224-color Linux logo
2. 输出测试字符到LCD终端
# 将字符串输出到LCD终端
echo "Hello Linux LCD!" > /dev/tty1
# 设置LCD为控制台
setenv bootargs 'console=tty1 console=ttymxc0,115200 ...'
# 修改/etc/inittab添加LCD终端
tty1::askfirst:-/bin/sh
3. 背光调节
# 查看当前背光等级
cat /sys/devices/platform/backlight/backlight/backlight/brightness
# 设置背光等级(0~7)
echo 7 > /sys/devices/platform/backlight/backlight/backlight/brightness
# 关闭背光(熄屏)
echo 0 > /sys/devices/platform/backlight/backlight/backlight/brightness
4. 禁用自动熄屏
修改内核源码drivers/tty/vt/vt.c中的blankinterval变量:
static int blankinterval = 0; /* 原值为10*60(10分钟), 改为0禁用 */
或编写应用程序禁用:
#include <fcntl.h>
#include <stdio.h>
int main(void)
{
int fd = open("/dev/tty1", O_RDWR);
write(fd, "\033[9;0]", 8); /* 禁用屏幕保护 */
close(fd);
return 0;
}
⚠️ 来源说明:本节不属于《I.MX6U嵌入式Linux驱动开发指南》内容,为扩展知识。
| 特性 | I.MX6ULL | STM32F7 | RK3568 |
|---|---|---|---|
| LCD控制器 | eLCDIF | LTDC | VOP |
| 最大分辨率 | 1366×768 | 1024×1024 | 4K×2K |
| 数据接口 | RGB888/RGB565 | RGB888/RGB565 | RGB/MIPI-DSI/eDP |
| 显存管理 | 内核CMA | 外部SDRAM | 内核CMA |
| 驱动文件 | mxsfb.c |
stm32-ltdc.c |
rockchip_vop.c |
| 设备树节点 | lcdif@021c8000 |
ltdc@40016800 |
vop@fe040000 |
| 时钟配置 | LCDIF_PIX + LCDIF_APB | LTDC_CLK | DCLK_VOP |
| 背光控制 | PWM + backlight节点 | PWM + backlight节点 | PWM + backlight节点 |
mxsfb.c⚠️ 来源说明:本节不属于《I.MX6U嵌入式Linux驱动开发指南》内容,为扩展知识。
答:Framebuffer是Linux内核对显示设备的字符设备抽象,设备节点为/dev/fbX。用户空间通过标准文件操作访问:
open():打开fb设备ioctl():获取/设置显示参数(如FBIOPUT_VSCREENINFO)mmap():将显存映射到用户空间,直接读写像素数据read()/write():读写显存内容核心是fb_info结构体,包含可变参数(var)、固定参数(fix)、操作集(fbops)和显存指针(screen_base)。
答:
像素时钟计算公式:
Pixel_Clock = (HBP + Hactive + HFP) × (VBP + Vactive + VFP) × Refresh_Rate
例如1024×600@60Hz:(140 + 1024 + 160) × (20 + 600 + 12) × 60 ≈ 51.2MHz
答:
驱动在fb_check_var()中校验var参数,在fb_set_par()中将参数写入硬件寄存器。
答:
fb_info->par和host->fb_info相互关联答:Linux内核默认10分钟后熄屏(blankinterval = 10*60)。原书给出三种解决方案:
drivers/tty/vt/vt.c中blankinterval改为0,重新编译内核/dev/tty1写入\033[9;0]转义序列禁用屏幕保护,并加入/etc/init.d/rcS实现开机自启动补充(扩展):也可通过
make menuconfig关闭 DPMS(Display Power Management Signaling)。原书推荐方案3,无需重新编译内核、部署灵活。
内容来源: 《I.MX6U嵌入式Linux驱动开发指南》第59章 Linux LCD驱动实验(可参考第24章 RGBLCD显示实验)