zoukankan      html  css  js  c++  java
  • 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,},

    就解决了

  • 相关阅读:
    ios开发中超简单抽屉效果(MMDrawerController)的实现
    mysql的查询、子查询及连接查询
    iOS在线音乐播放SZKAVPlayer(基于AVPlayer的封装)
    iOS js oc相互调用(JavaScriptCore)(二)
    Linux下面如何安装Django
    国内外测绘遥感领域核心期刊
    MFC程序消息处理的顺序
    VC++中关于控件重绘函数/消息 OnPaint,OnDraw,OnDrawItem,DrawItem的区别
    C#中控件Control的Paint事件和OnPaint虚函数的区别
    Lambda表达式学习(2)
  • 原文地址:https://www.cnblogs.com/fah936861121/p/7151215.html
Copyright © 2011-2022 走看看