【i.MX6ULL】驱动开发11——LCD驱动实践

1 知识点
1.1 Framebuffer
Framebuffer直译即帧缓冲,简称 fb,它是Linux将系统中所有跟显示有关的硬件以及软件集合起来,将底层的LCD虚拟抽象出一 个/dev/fbX设备,应用程序可以通过操作/dev/fbX来实现对屏幕的显示控制。
NXP官方Linux内核已默认开启了LCD驱动,在dev/目录下可以看到fb0这样一个设备

Framebuffer在内核中的表现就是fb_info结构体:

完整的结构体定义如下:
注意结构体中的fb_fops这一项,/dev/fb0 是个字符设备,fb_fops就是它的文件操作结构体,它的file_operations操作集在drivers/video/fbdev/core/fbmem.c 文件中:

可以看到有熟悉的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中

该函数的实现如下:
其中,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节点内容:

其中021c8000 这个地址,可以从参考手册中找到对应的介绍:

1.3.2 屏幕IO配置
打开 imx6ull-myboard.dts 文件,在 iomuxc 节点中找到如下内容:

具体为:
这里有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),其参数为:

修改后的lcdif 节点如下:
1.3.4 屏幕背光配置
通过PWM信号来控制LCD屏幕背光的亮度
LCD 背光要用到PWM1,因此也要设置 PWM1 节点,在imx6ull.dtsi 文件中找到如下内容:

这个节点信息不用修改,使用默认的配置即可。如果要修改的话,也不要修改这里,可以通过imx6ull-myboard.dts文件中进行修改。
imx6ull-myboard.dts中的pwm1节点:
imx6ull-myboard.dts中的backlight节点:
2 实验测试
2.1使能Linux logo显示
uboot启动的时候,LCD左上角上会显示NXP的图标,而Linux内核启动的时候,LCD左上角上会显示一个小企鹅。因此,可以通过小企鹅logo的显示来验证LCD 驱动是否正常。
默认情况下是已经开启logo显示的,可以再确认一下。
在Linux内核源码目录,输入以下指令打开内核的图形化配置:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
Linux内核配置界面:

然后按下路径找到对应的配置项:
最终到达这个界面:

这三个选项分别对应黑白、16 位、24 位色彩格式的 logo。
2.2 编译设备树
修改设备树中的lcdif节点后(主要是修改屏幕的参数),在Linux内核源码目录执行下面的命令,重新编译设备树并拷贝到网络启动位置。
然后重启开发板,就可以在Linux内核驱动的时候看到屏幕上的企鹅图标了:

2.3 设置LCD作为终端控制台
之前一直使用串口来显示板子的启动和调试信息,实际上可以设置 LCD 作为终端进行同步显示:
2.3.1 设置uboot的bootargs
重启开发板,在倒计时时按回充进入ubout,可以先看下之前的bootargs配置:

只需要在原来的基础上再添加console=tty1
即可:
然后重启开发板,在Linux内核驱动的时候就可以在屏幕上看到输出信息了:

对比一下串口输出的信息,可以看出屏幕输出到Freeing unused kernel memory: 400K (8090e000 - 80972000)
这句后就没有了,没有出现按下回车键继续的提示,也没有显示开启自启动的hello word测试程序的打印,这是因为某些设置还未完成。

2.3.2 修改/etc/inittab文件
该修改用于设置屏幕作为终端进行交互。
打开根文件系统中的/etc/inittab 文件,加入下面这一行:

保存后重启板子,并在板子的USB接口插上键盘,就可以通过键盘和板子交互了:

现在通过板子插入键盘,也可以在屏幕上操作板子了。
注意,之前设置的开机启动的hello word程序的打印没有出现在屏幕上,是因为printf的输入没有设置的LCD中,我们可以通过将输出指向 /dev/tty1 来实现LCD屏幕的打印,比如测试屏幕输出hello linux:

2.4 其它问题
2.4.1 自动熄屏的问题
当没有操作LCD屏幕一段时间后,屏幕会自动黑屏,这时可以通过接入键盘按下回车键进行唤醒(也可以通过板子的ON/OFF按键进行唤醒,因为该按键也被赋予了回车键的功能)。
这个时间是在Linux源码的 drivers/tty/vt/vt.c中设置的,默认是10分钟(10*60秒)。

如果想让屏幕一直亮着,可以将改值设为0,并重新编辑Linux内核得到zImage,然后用新的zImage启动开发板。
如果不想修改zImage,另外一种方式可以创建一个开机启动的应用程序来控制屏幕不熄灭, lcd_always_on.c
的内容为:
在ubuntu中编译该程序,然后将可执行程序拷贝到板子的根文件系统中:

然后,/etc/init.d/rcS中设置该程序开机自启动即可。

保存后,重启开发板,屏幕就不会自动熄屏了。
2.4.2 屏幕亮度调节
屏幕的亮度也是可以调节的,设备树中背光节点设置了8 个等级,可以在 0~7范围内进行亮度调节,进入下面的目录,可以查看当前屏幕的亮度:
通过下面的指令可以实时修改屏幕的亮度,比如修改亮度为1:

总结
本篇介绍了LCD屏幕驱动相关知识并进行了实验,因为NXP官方的板子和我这个板子的LCD引脚一样,因此主要的修改就是将设备树中的lcdif 节点的屏幕参数进行修改即可。
通过实验,可以将企鹅logo显示出来,并将板子的输出信息定向到了LCD屏幕显示,通过接入键盘可实现与Linux板子的交互。最后,还测试了屏幕熄屏和亮度调节功能。