【i.MX6ULL】驱动开发11——LCD驱动实践
发布时间 - 2025-07-19 00:00:00 点击率:次之前在linux系统移植时提到过lcd驱动,本篇来看下linux设备树如何配置lcd驱动。
1 知识点首先需要了解一个新的概念:Framebuffer
1.1 FramebufferFramebuffer直译即帧缓冲,简称 fb,它是Linux将系统中所有跟显示有关的硬件以及软件集合起来,将底层的LCD虚拟抽象出一 个/dev/fbX设备,应用程序可以通过操作/dev/fbX来实现对屏幕的显示控制。
NXP官方Linux内核已默认开启了LCD驱动,在dev/目录下可以看到fb0这样一个设备
Framebuffer在内核中的表现就是fb_info结构体:
完整的结构体定义如下:
代码语言:javascript代码运行次数:0运行复制struct fb_info {atomic_t count;int node;int flags;struct mutex lock;/* Lock for open/release/ioctl funcs */struct mutex mm_lock;/* Lock for fb_mmap and smem_* fields */struct fb_var_screeninfo var;/* 当前的可变参数 */struct fb_fix_screeninfo fix;/* 当前的固定参数 */struct fb_monspecs monspecs;/* Current Monitor specs */struct work_struct queue;/* Framebuffer event queue */struct fb_pixmap pixmap;/* Image hardware mapper */struct fb_pixmap sprite;/* Cursor hardware mapper */struct fb_cmap cmap;/* Current cmap */struct list_head modelist; /* mode list */struct fb_videomode *mode;/* current mode */#ifdef CONFIG_FB_BACKLIGHT/* assigned backlight device *//* set before framebuffer registration, remove after unregister */struct backlight_device *bl_dev;/* Backlight level curve */struct mutex bl_curve_mutex;u8 bl_curve[FB_BACKLIGHT_LEVELS];#endif#ifdef CONFIG_FB_DEFERRED_IOstruct delayed_work deferred_work;struct fb_deferred_io *fbdefio;#endifstruct fb_ops *fbops; /* 帧缓冲操作函数集 */struct device *device;/* This is the parent */struct device *dev; /* This is this fb device */int class_flag; /* private sysfs flags */#ifdef CONFIG_FB_TILEBLITTINGstruct fb_tile_ops *tileops; /* Tile Blitting */#endifchar __iomem *screen_base; /* 虚拟内存基地址(屏幕显存) */unsigned long screen_size; /* 虚拟内存大小(屏幕显存大小) */ void *pseudo_palette; /* 伪16位调色板 */ #define FBINFO_STATE_RUNNING0#define FBINFO_STATE_SUSPENDED1u32 state; /* Hardware state i.e suspend */void *fbcon_par; /* fbcon use-only private area *//* From here on everything is device dependent */void *par;/* we need the PCI or similar aperture base/size not smem_start/size as smem_start may just be an object allocated inside the aperture so may not actually overlap */struct apertures_struct {unsigned int count;struct aperture {resource_size_t base;resource_size_t size;} ranges[0];} *apertures;bool skip_vt_switch; /* no VT switch on suspend/resume required */};注意结构体中的fb_fops这一项,/dev/fb0 是个字符设备,fb_fops就是它的文件操作结构体,它的file_operations操作集在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,};可以看到有熟悉的open、release等函数接口。
因此,LCD驱动的重点就是初始化fb_info里面的各个成员。
fb_info结构体的成员变量很多,需要重点关注的是这几个:
var:当前的可变参数fix:当前的固定参数fbops:帧缓冲操作函数集screen_base:虚拟内存基地址(屏幕显存)screen_size:虚拟内存大小(屏幕显存大小)pseudo_palette:伪16位调色板初始化完成fb_info后,通过register_framebuffer函数向内核注册刚刚初始化的fb_info。
1.2 LCD驱动文件mxsfb介绍LCD的驱动文件为mxsfb.c,这是一种platform驱动框架,驱动和设备匹配之后,mxsfb_probe函数就会执行。
LCD的初始化通过mxsfb_probe函数来实现,该函数的主要功能有:
申请fb_info初始化fb_info结构体中的各个成员变量初始化eLCDIF控制器使用register_framebuffer函数向Linux内核注册初始化好的fb_info该函数位于:/drivers/video/fbdev/mxsfb.c中
该函数的实现如下:
代码语言:javascript代码运行次数:0运行复制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; //<-----NXP的fb_infostruct fb_info *fb_info; //<-----Linux的fb_infostruct pinctrl *pinctrl;int irq = platform_get_irq(pdev, 0);int gpio, ret;if (of_id)pdev->id_entry = of_id->data;gpio = of_get_named_gpio(pdev->dev.of_node, "enable-gpio", 0);if (gpio == -EPROBE_DEFER)return -EPROBE_DEFER;//省略...fb_info = framebuffer_alloc(sizeof(struct fb_info), &pdev->dev);//<--------申请fb_infoif (!fb_info) {dev_err(&pdev->dev, "Failed to allocate fbdev\n");devm_kfree(&pdev->dev, host);return -ENOMEM;} host->fb_info = fb_info; //<---将mxsfb_info与fb_info联系起来 fb_info->par = host;//省略...ret = mxsfb_init_fbinfo(host);if (ret != 0)goto fb_pm_runtime_disable;mxsfb_dispdrv_init(pdev, fb_info);//省略... ret = register_framebuffer(fb_info); //<------------注册if (ret != 0) {dev_err(&pdev->dev, "Failed to register framebuffer\n");goto fb_destroy;}console_lock();ret = fb_blank(fb_info, FB_BLANK_UNBLANK);console_unlock();if (ret < 0) {dev_err(&pdev->dev, "Failed to unblank framebuffer\n");goto fb_unregister;}dev_info(&pdev->dev, "initialized\n");}其中,register_framebuffer函数的原型如下:
函数参数和返回值含义:
fb_info:需上报的fb_info返回值:0-成功,负值-失败1.3 LCD 驱动程序编写6ULL的eLCDIF接口驱动程序 NXP 已经编 写好了,因此 LCD 驱动部分我们不需要去修改。我们需要做的就是按照所使用的 LCD 来修改设备树。
1.3.1 查看设备树1.3 先来看一下NXP官方编写的Linux下的 LCD 驱动。打开 imx6ull.dtsi,然后找到 lcdif节点内容:
0运行复制lcdif: lcdif@021c8000 { compatible = "fsl,imx6ul-lcdif", "fsl,imx28-lcdif"; reg = <0x021c8000 0x4000>; interrupts = ; clocks = <&clks IMX6UL_CLK_LCDIF_PIX>, <&clks IMX6UL_CLK_LCDIF_APB>, <&clks IMX6UL_CLK_DUMMY>; clock-names = "pix", "axi", "disp_axi"; status = "disabled";}; 其中021c8000 这个地址,可以从参考手册中找到对应的介绍:
打开 imx6ull-myboard.dts 文件,在 iomuxc 节点中找到如下内容:
具体为:
代码语言:javascript代码运行次数:0运行复制pinctrl_lcdif_dat: lcdifdatgrp {fsl,pins = ;};pinctrl_lcdif_ctrl: lcdifctrlgrp {fsl,pins = ;};pinctrl_pwm1: pwm1grp {fsl,pins = ;}; 这里有3个节点:
子节点**pinctrl_lcdif_dat **,为 RGB LCD 的 24根数据线配置项子节点 **pinctrl_lcdif_ctrl **,为RGB LCD 的 4根控制线配置项,包括 CLK、ENABLE、VSYNC 和 HSYNC子节点 **pinctrl_pwm1 **,为RGB LCD 的背光亮度配置项1.3.3 屏幕参数配置在imx6ull-myboard.dts 文件中找到lcdif 节点,根据自己使用的LCD,修改为对应的参数。
下面是NXP官方板子的参数:
我用的野火7寸屏(GT911,800x480),其参数为:
参数
值
width
800
height
480
HBP
46
HFP
22
VBP
23
VFP
22
HSPW
1
VSPW
1
修改后的lcdif 节点如下:
代码语言:javascript代码运行次数:0运行复制&lcdif {pinctrl-names = "default"; /* 使用到的 IO */ pinctrl-0 = <&pinctrl_lcdif_dat &pinctrl_lcdif_ctrl &pinctrl_lcdif_reset>;display = <&display0>;status = "okay";display0: display { /* LCD 属性信息 */ bits-per-pixel = <16>; /* 一个像素占用几个bit */ bus-width = <24>; /* 总线宽度 */ display-timings {native-mode = <&timing0>; /* 时序信息 */ timing0: timing0 {clock-frequency = <9200000>; /* LCD像素时钟,单位Hz */ hactive = <800>; /* LCD X轴像素个数 */ vactive = <480>; /* LCD Y轴像素个数 */ hfront-porch = <22>; /* LCD hfp 参数 */ hback-porch = <46>; /* LCD hbp 参数 */hsync-len = <23>; /* LCD hspw 参数 */ vback-porch = <22>; /* LCD vbp 参数 */vfront-porch = <4>; /* LCD vfp 参数 */ vsync-len = <1>; /* LCD vspw 参数 */ hsync-active = <0>; /* hsync 数据线极性 */ vsync-active = <0>; /* vsync 数据线极性 */ de-active = <1>; /* de 数据线极性 */ pixelclk-active = <0>; /* clk 数据线极性 */ };};};};1.3.4 屏幕背光配置通过PWM信号来控制LCD屏幕背光的亮度
代码语言:javascript代码运行次数:0运行复制pinctrl_pwm1: pwm1grp { fsl,pins = < MX6UL_PAD_GPIO1_IO08__PWM1_OUT 0x110b0 >;};LCD 背光要用到PWM1,因此也要设置 PWM1 节点,在imx6ull.dtsi 文件中找到如下内容:
这个节点信息不用修改,使用默认的配置即可。如果要修改的话,也不要修改这里,可以通过imx6ull-myboard.dts文件中进行修改。
imx6ull-myboard.dts中的pwm1节点:
代码语言:javascript代码运行次数:0运行复制&pwm1 {pinctrl-names = "default";pinctrl-0 = <&pinctrl_pwm1>;status = "okay";};imx6ull-myboard.dts中的backlight节点:
代码语言:javascript代码运行次数:0运行复制backlight { compatible = "pwm-backlight"; pwms = <&pwm1 0 5000000>; brightness-levels = <0 4 8 16 32 64 128 255>; default-brightness-level = <6>; status = "okay";};2 实验测试2.1使能Linux logo显示 uboot启动的时候,LCD左上角上会显示NXP的图标,而Linux内核启动的时候,LCD左上角上会显示一个小企鹅。因此,可以通过小企鹅logo的显示来验证LCD 驱动是否正常。
默认情况下是已经开启logo显示的,可以再确认一下。
在Linux内核源码目录,输入以下指令打开内核的图形化配置:
代码语言:javascript代码运行次数:0运行复制make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
Linux内核配置界面:
然后按下路径找到对应的配置项:
代码语言:javascript代码运行次数:0运行复制-> Device Drivers -> Graphics support -> Bootup logo (LOGO [=y])
最终到达这个界面:
这三个选项分别对应黑白、16 位、24 位色彩格式的 logo。
2.2 编译设备树修改设备树中的lcdif节点后(主要是修改屏幕的参数),在Linux内核源码目录执行下面的命令,重新编译设备树并拷贝到网络启动位置。
代码语言:javascript代码运行次数:0运行复制make imx6ull-myboard.dtbcp arch/arm/boot/dts/imx6ull-myboard.dtb ~/myTest/tftpboot/nxp/
然后重启开发板,就可以在Linux内核驱动的时候看到屏幕上的企鹅图标了:
之前一直使用串口来显示板子的启动和调试信息,实际上可以设置 LCD 作为终端进行同步显示:
2.3.1 设置uboot的bootargs重启开发板,在倒计时时按回充进入ubout,可以先看下之前的bootargs配置:
只需要在原来的基础上再添加console=tty1即可:
setenv bootargs 'console=tty1 console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.5.104:/home/xxpcb/myTest/nfs/rootfs,proto=tcp,nfsvers=4 rw ip=192.168.5.102:192.168.5.104:192.168.5.1:255.255.255.0::eth1:off'saveenv
然后重启开发板,在Linux内核驱动的时候就可以在屏幕上看到输出信息了:
对比一下串口输出的信息,可以看出屏幕输出到Freeing unused kernel memory: 400K (8090e000 - 80972000)这句后就没有了,没有出现按下回车键继续的提示,也没有显示开启自启动的hello word测试程序的打印,这是因为某些设置还未完成。
该修改用于设置屏幕作为终端进行交互。
打开根文件系统中的/etc/inittab 文件,加入下面这一行:
代码语言:javascript代码运行次数:0运行复制tty1::askfirst:-/bin/sh
保存后重启板子,并在板子的USB接口插上键盘,就可以通过键盘和板子交互了:
现在通过板子插入键盘,也可以在屏幕上操作板子了。
注意,之前设置的开机启动的hello word程序的打印没有出现在屏幕上,是因为printf的输入没有设置的LCD中,我们可以通过将输出指向 /dev/tty1 来实现LCD屏幕的打印,比如测试屏幕输出hello linux:
代码语言:javascript代码运行次数:0运行复制echo hello linux > /dev/tty1
当没有操作LCD屏幕一段时间后,屏幕会自动黑屏,这时可以通过接入键盘按下回车键进行唤醒(也可以通过板子的ON/OFF按键进行唤醒,因为该按键也被赋予了回车键的功能)。
这个时间是在Linux源码的 drivers/tty/vt/vt.c中设置的,默认是10分钟(10*60秒)。
如果想让屏幕一直亮着,可以将改值设为0,并重新编辑Linux内核得到zImage,然后用新的zImage启动开发板。
如果不想修改zImage,另外一种方式可以创建一个开机启动的应用程序来控制屏幕不熄灭, lcd_always_on.c的内容为:
#include#include #include int main(int argc, char *argv[]) { int fd; fd = open("/dev/tty1", O_RDWR); write(fd, "\033[9;0]", 8); close(fd); return 0; }
在ubuntu中编译该程序,然后将可执行程序拷贝到板子的根文件系统中:
代码语言:javascript代码运行次数:0运行复制arm-linux-gnueabihf-gcc lcd_always_on.c -o lcd_always_on cp lcd_always_on ~/myTest/nfs/rootfs/usr/bin/
然后,/etc/init.d/rcS中设置该程序开机自启动即可。
保存后,重启开发板,屏幕就不会自动熄屏了。
2.4.2 屏幕亮度调节屏幕的亮度也是可以调节的,设备树中背光节点设置了8 个等级,可以在 0~7范围内进行亮度调节,进入下面的目录,可以查看当前屏幕的亮度:
代码语言:javascript代码运行次数:0运行复制/sys/devices/platform/backlight/backlight/backlight
通过下面的指令可以实时修改屏幕的亮度,比如修改亮度为1:
代码语言:javascript代码运行次数:0运行复制echo 1 > brightness
本篇介绍了LCD屏幕驱动相关知识并进行了实验,因为NXP官方的板子和我这个板子的LCD引脚一样,因此主要的修改就是将设备树中的lcdif 节点的屏幕参数进行修改即可。
通过实验,可以将企鹅logo显示出来,并将板子的输出信息定向到了LCD屏幕显示,通过接入键盘可实现与Linux板子的交互。最后,还测试了屏幕熄屏和亮度调节功能。
# linux
# ai
# switch
# red
# JavaScript
# 成员变量
# printf
# 结构体
# 可变参数
# 接口
# var
# console
# ubuntu
# word
# 可以通过
# 重启
# 显存
# 回车键
# 开发板
# 按下
# 来实现
# 就可以
# 中找到
# 屏幕上
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
Laravel如何使用API Resources格式化JSON响应_Laravel数据资源封装与格式化输出
如何在IIS7中新建站点?详细步骤解析
如何在自有机房高效搭建专业网站?
JS经典正则表达式笔试题汇总
微博html5版本怎么弄发语音微博_语音录制入口及时长限制操作【教程】
Laravel如何发送邮件_Laravel Mailables构建与发送邮件的简明教程
高防服务器如何保障网站安全无虞?
如何快速生成凡客建站的专业级图册?
百度输入法ai组件怎么删除 百度输入法ai组件移除工具
Laravel N+1查询问题如何解决_Eloquent预加载(Eager Loading)优化数据库查询
Zeus浏览器网页版官网入口 宙斯浏览器官网在线通道
如何用已有域名快速搭建网站?
香港服务器网站推广:SEO优化与外贸独立站搭建策略
jimdo怎样用html5做选项卡_jimdo选项卡html5实现与切换效果【指南】
Python函数文档自动校验_规范解析【教程】
edge浏览器无法安装扩展 edge浏览器插件安装失败【解决方法】
浅谈Javascript中的Label语句
如何在HTML表单中获取用户输入并结合JavaScript动态控制复利计算循环
网站制作壁纸教程视频,电脑壁纸网站?
如何用手机制作网站和网页,手机移动端的网站能制作成中英双语的吗?
Laravel如何实现API速率限制?(Rate Limiting教程)
如何快速搭建高效WAP手机网站?
如何用AI帮你把自己的生活经历写成一个有趣的故事?
JavaScript 输出显示内容(document.write、alert、innerHTML、console.log)
北京网站制作的公司有哪些,北京白云观官方网站?
成都网站制作公司哪家好,四川省职工服务网是做什么用?
为什么php本地部署后css不生效_静态资源加载失败修复技巧【技巧】
如何快速打造个性化非模板自助建站?
如何解决hover在ie6中的兼容性问题
微信小程序 五星评分(包括半颗星评分)实例代码
如何在景安服务器上快速搭建个人网站?
Laravel队列由Redis驱动怎么配置_Laravel Redis队列使用教程
Laravel如何处理文件下载请求?(Response示例)
Python结构化数据采集_字段抽取解析【教程】
惠州网站建设制作推广,惠州市华视达文化传媒有限公司怎么样?
Laravel如何使用Socialite实现第三方登录?(微信/GitHub示例)
Laravel策略(Policy)如何控制权限_Laravel Gates与Policies实现用户授权
Laravel如何配置和使用缓存?(Redis代码示例)
Laravel如何实现数据导出到CSV文件_Laravel原生流式输出大数据量CSV【方案】
Laravel如何实现API版本控制_Laravel API版本化路由设计策略
如何在Windows环境下新建FTP站点并设置权限?
小视频制作网站有哪些,有什么看国内小视频的网站,求推荐?
如何用ChatGPT准备面试 模拟面试问答与职场话术练习教程
Laravel如何处理文件上传_Laravel Storage门面实现文件存储与管理
Laravel如何使用Livewire构建动态组件?(入门代码)
如何为不同团队 ID 动态生成多个独立按钮
手机软键盘弹出时影响布局的解决方法
怎么用AI帮你为初创公司进行市场定位分析?
Laravel怎么调用外部API_Laravel Http Client客户端使用
Laravel怎么使用Blade模板引擎_Laravel模板继承与Component组件复用【手册】

