hud项目lcd调试过程的一些见解

1.帧缓冲(FrameBuffer)设备驱动
帧缓冲设备为标准的字符型设备,在Linux中主设备号29,定义在/include/uapi/linux/major.h中的FB_MAJOR,次设备号定义帧缓冲的个数,最大允许有32个FrameBuffer,定义在/include/linux/fb.h中的FB_MAX,对应于文件系统下/dev/fb%d设备文件

 

我们从上面这幅图看,帧缓冲设备在Linux中也可以看做是一个完整的子系统,大体由fbmem.c和xxxfb.c组成。向上给应用程序提供完善的设备文件操作接口(即对FrameBuffer设备进行read、write、ioctl等操作),接口在Linux提供的fbmem.c文件中实现;向下提供了硬件操作的接口,只是这些接口Linux并没有提供实现,因为这要根据具体的LCD控制器硬件进行设置,所以这就是我们要做的事情了(即xxxfb.c部分的实现), imx6dl的在kernel/drivers/video/mxc/mxc_ipuv3_fb.c中实现

 

参考sdk中rgb接口配置
硬件资源:设备树/imx6ul_sdk/linux-3.14.52/arch/arm/boot/dts/myimx6ek140-6g.dts和imx6ul_sdk/linux-3.14.52/arch/arm/boot/dts/imx6ul.dtsi
lcdif设备节点, ELCDIF(Enhanced LCD Interface)内存基地址为0x021c8000
lcdif数据接口为pinctrl_lcdif_dat, 信号控制接口为pinctrl_lcdif_ctrl
lcd屏硬件资源为display0设备节点

2.解析drivers/video/mxc/mxc_ipuv3_fb.c文件
2.1根据compatible = "fsl,mxc_sdc_fb", 匹配进入mxcfb_probe()
2.2of_alias_get_id()获取mxcfb硬件节点编号
2.3devm_kzalloc()申请内存给plat_data
2.4pdev->dev.platform_data = plat_data, 把申请好的地址传递给pdev->dev的自定义的私有数据platform_data
2.5mxcfb_get_of_property(pdev, plat_data), 利用pdev来匹配设备节点, 然后解析设备树上的私有数据(disp_dev, mode_str, interface_pix_fmt, default_bpp, init_clk, late_init, prefetch)赋值给plat_data
2.6mxcfb_init_fbinfo(), 初始化framebuffer(帧缓冲)数据结构, 即填充fb_info数据结构(主要填充fb_ops)
2.7mxcfb_option_setup(), 解析用户特定选项
2.8把已经初始化好的fb_info数据赋值给自定义的数据结构mxcfbi
2.9mxcfb_dispdrv_init()显示设备驱动初始化, 把刚刚从设备树解析出来的plat_data私有数据赋值给mxcfbi, 然后mxc_dispdrv_gethandle()从已经注册好的驱动中获取驱动(比如drv name = "lcd", 即名字为lcd的驱动)句柄, 如果有该驱动(即名为"lcd"驱动), 那么注册mxc显示驱动
2.10platform_get_resource()获取内存资源, 由于改成设备树了, 已解析完, 不是按原来的板级文件解析, 所以获取不到, res = NULL
2.11ipu_get_soc()获取ipu编号

3.解析drivers/video/mxc/mxc_lcdif.c文件(RGB接口lcd驱动)
3.1lcdif = devm_kzalloc(), 为lcdif申请内存
3.2plat_data = devm_kzalloc(), 为plat_data申请内存
3.3pdev->dev.platform_data = plat_data; 把plat_data地址赋值给pdev->dev的私有数据结构
3.4ret = lcd_get_of_property(pdev, plat_data); 解析设备树已经匹配好的设备节点(通过compatible = "fsl,lcd"进行匹配, 获取到lcd节点)的属性, 把解析好的数据(ipu_id, disp_id, default_ifmt)赋值给plat_data
3.5pinctrl = devm_pinctrl_get_select_default(&pdev->dev); 根据设备(pdev->dev)获取pin操作句柄
3.6lcdif->pdev = pdev; 把pdev赋值给私有的lcdif->pdev
3.7lcdif->disp_lcdif = mxc_dispdrv_register(&lcdif_drv); 把lcd硬件操作句柄lcdif_drv赋值给lcdif->disp_lcdif, 其中lcdif_drv操作接口有:lcdif_init, lcdif_deinit
对于lcdif_init: ipu_di_to_ctrc, 使用平台预先定义好的ipu/di(在设备树上lcd节点的disp_id, ipu_id属性)来设置ipu的时钟; fb_find_mode()用于解析有效的视频或图像模式(像素, 行切换延迟时间, 频率等)
注意:lcdif_modedb[]这里添加我们lcd屏的参数(像素, 行切换延迟, 频率等)
3.8mxc_dispdrv_setdata(lcdif->disp_lcdif, lcdif), 把lcdif数据添加到驱动链表中
3.9dev_set_drvdata(&pdev->dev, lcdif)

4.关于显示设备驱动(比如lcd驱动)和framebuffer驱动加载顺序

首先加载lcd驱动mxc_lcdif.c, lcd_get_of_property会解析lcd设备节点的default_ifmt, 比如"RGB24"

然后加载framebuffer驱动, 赋值好pdev->id, 这样才可以即系对应的uboot传过来的参数

然后在mxc_option_setup解析uboot传过来的参数, 即uboot传下来的参数是为了设置bpp, 像素格式ifmt和显示设备

然后又在mxcfb_dispdrv_init初始化显示驱动, 在mxc_dispdrv_gethandle()会对刚刚加载的lcd驱动进行lcdif_init, 即把mxc_option_setup解析的显示设备参数通过lcdif_init对显示设备进行设置(这回覆盖掉之前加载的lcd驱动的default_ifmt)

所以framebuffer驱动与显示驱动即分开又有联系, 且uboot的传参尤为重要, 它的优先级最高, 会覆盖掉设备设备树上的默认值比如(default_bpp, default_ifmt)

5.lcd驱动(RGB接口)内核配置
make menuconfig
然后搜索ipu, 可以看到加载lcd驱动需要依赖哪些驱动
framebuffer驱动, drivers/fbmem.c

6.初始化lcd屏mode
lcd屏参数:根据TFF9K2353-V1规格书p13
分辨率:480x240(屏幕一行有480个像素点, 一列有240个像素点), 实际项目中调试分辨率为:480x280
RGB666
VCLK(dotclk):7.97M(typ)
VBPD(vertical back porch):1-2(tH) ---> upper margin
VFBD(vertical front porch):1-8(tH) ---> lower margin
VSPW(vertical sync pulse width):5(tH) ---> vsync len
HBPD(horizontal back porch):2-40(dotclk) ---> left margin
HFPD(horizontal front porth):1-5(dotclk) ---> right margin
HSPW(horizontal sync pulse width):27(dotclk)---> hsync len

7.uboot配置:
The command line parameter takes the following form, use "off" for unused framebuffers:
"video=mxcfb<number>:dev=<Output>,<Mode Specifier>,if=<Output Format>,[bpp=<Framebuffer Depth>]"

e.g.:
video=mxcfb0:dev=lcd,EDT-WVGA,if=RGB24
video=mxcfb0:dev=hdmi,1920x1080M@60,if=RGB24
video=mxcfb1:off

8.lcd参数解释:
当前显示模式结构体:
/* include/linux/fb.h */
struct fb_videomode {
const char *name; /* 液晶屏名字, 可选optional */
u32 refresh; /* 刷新频率, 可选optional(内核中大多为60) */
u32 xres; /* 屏幕一行有多少个像素点 */
u32 yres; /* 屏幕一列有多少个像素点 */
u32 pixclock; /* 每个像素时钟周期的长度, 单位时ps(10的负12次方分之一秒), 比如像素频率为25M, 那么这个值就为(1/25*1000000)*(1000000000000)=40000*/
u32 left_margin; /*行切换,从同步到绘图之间的延迟*/
u32 right_margin; /*行切换,从绘图到同步之间的延迟*/
u32 upper_margin; /*帧切换,从同步到绘图之间的延迟*/
u32 lower_margin; /*帧切换,从绘图到同步之间的延迟*/
u32 hsync_len; /*水平同步的长度*/
u32 vsync_len; /*垂直同步的长度*/
u32 sync;
u32 vmode;
u32 flag;
};

9.当lcd显示图片是反向或镜像时,请参考echo 1 > /sys/class/graphics/fb0/rotate,具体参考连接:https://community.nxp.com/message/416223

10.lcd常用接口原理篇
TFT-lCD常用的接口,TTL(RGB)、LVDS、EDP、MIPI, 这篇我们大致说一下这些接口的信号组成已经基本原理.
10.1TTL
a.TTL接口概述
TTL接口属于并行方式传输数据的接口,采用这种接口时,不必在液晶显示器的驱动板端和液晶面板端使用专用的接口电路,而是由驱动板主控芯片输出的TTL数据信号经电缆线直接传送到液晶面板的输人接口。由于TTL接口信号电压高、连线多、传输电缆长,因此,电路的抗干扰能力比较差,而且容易产生电磁干扰(EMI)。在实际应用中,TTL接口电路多用来驱动小尺寸(15in以下)或低分辨率的液晶面板。TTL最高像素时钟只有28MHz。
TTL是信号时TFT-LCD唯一能识别的信号,早期的数字处理芯片都是TTL的,也就是RGB直接输出到TFT-LCD。
b.TTL接口的信号类型
驱动板TTL输出接口中一般包含RGB数据信号、时钟信号和控制信号这三大类信号
10.2LVDS
a.LVDS接口概述
LVDS,即Low Voltage Differential Signaling,是一种低压差分信号技术接口。克服以TTL电平方式传输宽带高码率数据时功耗大、EMI电磁干扰大等缺点而研制的一种数字视频信号传输方式。LVDS输出接口利用非常低的电压摆幅(约350mV)在两条PCB走线或一对平衡电缆上通过差分进行数据的传输,即低压差分信号传输。采用LVDS输出接口,可以使得信号在差分PCB线或平衡电缆上以几百Mbit/s的速率传输,由于采用低压和低电流驱动方式,因此,实现了低噪声和低功耗。
b.LVDS接口电路的组成
在液晶显示器中,LVDS接口电路包括两部分,即主板侧的LVDS输出接口电路(LVDS发送端)和液晶面板侧的LVDS输入接口电路(LVDS接收器)。LVDS发送端将TTL信号转换成LVDS信号,然后通过驱动板与液晶面板之间的柔性电缆(排线)将信号传送到液晶面板侧的LVDS接收端的LVDS解码IC中,LVDS接收器再将串行信号转换为TTL电平的并行信号,送往液晶屏时序控制与行列驱动电路。也就是其实TFT只识别TTL(RGB)信号。
c.LVDS接口的信号类型
LVDS信号有数据差分和时钟差分信号组成
10.3EDP
这个接口比较陌生,我接触到一个屏IPAD3的,用于高清屏,比如2048*1536,goole n10的分辨率2536* 也是用这个接口。
10.4MIPI接口
这个我们公司有产品用,不过是其他平台的,不是我们调试 ,我也没接触过。只是过一下。感觉这类接口非常类似:比如LVDS、EDP、HDMI、MIPI,都是差分信息+差分时钟。

11.如何阅读LCD规格书
首先我们调试LCD的时候要获得的一些参数,没必要把整个规格书通读一遍,我刚开始调试屏的时候拿到一个规格书不知道从何入手,也不知那些参数有用,比较模糊,其实只提取一些有用的信息就可以,下面这些对初学者也许有点用处。
一个LCD规格书要了解的也就这么多,调试软件就够用:
a.General Specification中可得到,尺寸、分辨率、位数、色彩、像素时钟频率、接口类型;
b.Timing Characteristics中可以得到一些具体的参数;
c.LCD Timing diagram信号时序图,可以看到一些信号的时序、极性等;

 

12.调试lcd驱动遇到的问题

a.lcd屏幕显示颜色不对

b.lcd显示的图像下篇40mm

解决方法:

a.lcd屏幕显示颜色不对,发现应该显示红色的显示为蓝色,应该显示蓝色的显示为红色,即RGB显示为BGR

这个说明配置的RGB位域有问题(即一个像素的组织格式不对,RGB位域,表示该结构描述每一个像素显示缓冲区的组织方式,假如为RGB565模式,R占5位=bit[11:15],G占6位=bit[10:5] B占5位=bit[4:0] ),之前像素格式ifmt=RGB565,lcd屏的硬件是RGB666,但是原理图中它是按照RGB888格式来接的,低两位接地,我的驱动也是用RGB565来解析,所以会有lcd颜色问题,后面改成ifmt=RGB24,颜色就正常了(低两位接地,会导致lcd表示的颜色没有那么丰富而已),所以在boot的参数中也要修改,设备树中也要修改

之前的boot参数mmcargs=setenv bootargs console=ttymxc0,115200  root=/dev/mmcblk3p5 rootwait rw video=mxcfb0:dev=lcd,HUD-WVGA,if=RGB565,bpp=16

改为mmcargs=setenv bootargs console=ttymxc0,115200  root=/dev/mmcblk3p5 rootwait rw video=mxcfb0:dev=lcd,HUD-WVGA,if=RGB24,bpp=24

之前设备树mxcfb3: fb@2 {
         compatible = "fsl,mxc_sdc_fb";
         disp_dev = "lcd";
         interface_pix_fmt = "RGB565";
         mode_str ="HUD-WVGA";
         default_bpp = <16>;
         int_clk = <0>;
         late_init = <0>;
         status = "disabled";

    }

   lcd@0{

    compatible = "fsl,lcd";
         ipu_id = <0>;
         disp_id = <0>;
         default_ifmt = "RGB565";
         pinctrl-names = "default";
         pinctrl-0 = <&pinctrl_ipu1>;
         status = "okay";
    }

改为mxcfb3: fb@2 {
         compatible = "fsl,mxc_sdc_fb";
         disp_dev = "lcd";
         interface_pix_fmt = "RGB24";
         mode_str ="HUD-WVGA";
         default_bpp = <24>;
         int_clk = <0>;
         late_init = <0>;
         status = "disabled";

    }

   lcd@0{

    compatible = "fsl,lcd";
         ipu_id = <0>;
         disp_id = <0>;
         default_ifmt = "RGB24";
         pinctrl-names = "default";
         pinctrl-0 = <&pinctrl_ipu1>;
         status = "okay";
    }

这样就显示正常了

b.lcd显示的图像下篇40mm,对于这个问题是由于调试的lcd行列同步的时间没调好,所以会导致延迟(可参考http://blog.csdn.net/wocao1226/article/details/21702601中的LCD图像错位问题解决方案)

本来lcd的屏幕分辨率时480x240,由于调试时发现下偏了40mm,所以把分辨率配置成480x280,这只是权宜之计,这相当于lcd显示的原点都下移了40mm,还是要从根本上解决问题,就调整了行列同步问题

之前调试的

/* 480x280 @ 60 Hz , pixel clk @ 9.2 MHz*/
+#if 0
     "HUD-WVGA", 60, 480, 280, 108696, 16, 4, 2, 5, 27, 5,
     FB_SYNC_CLK_LAT_FALL,
     FB_VMODE_NONINTERLACED,
     0,},
改为

 /* 480x240 @ 60 Hz , pixel clk @ 9.2 MHz*/
+    "HUD-WVGA", 60, 480, 240, 125470, 40, 5, 40, 8, 2, 2,
+    FB_SYNC_CLK_LAT_FALL,
+    FB_VMODE_NONINTERLACED,
+    0,},

就解决了

 

posted @ 2017-07-11 16:27  大海中的一粒沙  阅读(3279)  评论(1编辑  收藏  举报