摘要:介绍了为满足智能手机的快速发展产生的LCM的MIPI-DSI协议的优点,描述了DSI协议的操作方式,分析了DSI协议的定义中的数据包格式及其传输方式。还完成了硬件电路的设计,详细阐述了Android下LCM驱动程序结构的介绍及具体的参数配置。详细分析调试过程中产生问题的原理及解决的思路。
关键词:DSI协议;硬件;Android;LCM驱动设计;TE;
0 引言
随着智能手机的高速发展,手机内部含有越来越多不同接口的设备,同时又要满足低功耗、高分辨率以及小体积的显示模块的发展,本文就将探讨新型显示接口技术MIPI-DSI命令模式的LCM驱动设计与调试。一方面,DSI兼容DBI(显示总线接口)、DPI(显示像素接口)、和DCS(显示命令集)。另一方面DSI让手机设备的接口在降低成本、复杂度、功耗以及EMI的同时,也能够增加带宽,从而极大的提高了手机性能。
本文在联发科的MT6572平台进行LCM(LCD module)驱动的设计,在联发科技提供的Android4.1和内核Linux 3.10下进行开发。MTK6572是一款28制程的双核基带芯片,单核的主频可达1.2Ghz,内核Cortex-A7基于ARMv7架构,同时内部有Mali-400图形处理器。同时,MT6572最高支持分辨率720P级的屏幕。LCM是台湾奇景光电股份有限公司的3.5寸液晶屏的TFT LCD模组HX8357d。
1 DSI协议
1.1DSI的连接
DSI协议属于接口通信协议,它是位于显示模组和处理器之间的一种可扩展通道的高速串行接口,其支持一条时钟通道和1-4条数据通道,图1是DSI的物理连接图。DSI根据其兼容的外设的不同架构,具有视频模式(video mode)和命令模式(command mode)这两种操作模式。
命令模式要求LCM有显示缓存,主机通过命令间接控制外设。LCM芯片有一个FMARK脚,用来与主控同步,当主机端接收到LCD发出的TE信号,才会开始往FrameBuffer写数据;当LCD接收到来自主控的刷新命令,LCD才开始从显示缓存中读数据。视频模式是指在主机和外设之间用实时像素流的方式传输,并且只能工作在高速传输模式,不需要有显示缓存。本文采用命令模式进行操作。图2命令模式的示意图。
1.2DSI协议数据的结构
DSI协议下数据包的传输如图3示,该协议规定了短数据包和长数据包这两种数据包。图中LPS(low powerstate)表示低功耗模式,SOT为传输开始,SP为短数据包,EOT为传输结束,LP为长数据包,数据包的前后配置了数据低功耗还是高速、长包还是短包的标志位。
如图4是只包含4字节数据包头的短数据包结构。分别包含ID(数据标识符),有效数据和ECC(错误检查和纠正)。短数据包可以操作寄存器、传送命令。图5是6-6554lB 大小的长数据包结构,首先其拥有4B的数据包头:ID、WC(传输数据的大小)和ECC;然后是有效数据和数据包页脚。长数据包可以传输控制命令及大量数据,结合不同的参数和命令可以配置不同长短的长数据包。
1.3DSI协议数据包的传输方式
DSI协议数据包的传输以同步方式在主机和LCD端进行数据传输,其传输模式有LP(Low Power)和HS(High Speed)这两种。其中LP采用数据速率小于10Mbps的单端信号进行传输,HS模式下采用数据速率80M~1Gbps的低压差分信号。MIPI总线有大量数据传输时使用HS传输,其他时候使用LP传输,两者结合能够让手机降低功耗的同时降低EMI。本文程序就是设计两者结合,在dsi_drv.c的DSI_TXRX_Control函数的定义中,设置tmp_reg.HSTX_CKLP_EN = 1,从而让在LCM每一帧画面之间工作在LP模式。
2硬件电路设计
MT6572可支持3条数据通道的DSI接口,本设计使用一条通道。硬件原理图如图6所示,有时钟差分信号DSI_CLK+和DSI_CLK-,双向数据通道DSI_D0+和DSI-D0-。FMARK引脚用来发送TE信号给主机,通知主机往frame buffer写数据。IC_ID引脚用来读取LCM的硬件ID,从而在兼容多个LCM时候根据ID选择正确的驱动程序。
3 LCM驱动的设计
LCM驱动要完成lk和kernel这两部分的设计。而在turkey版本的Android系统中,lk阶段的的LCM驱动程序是到kernel中程序的链接,这样只需要设置kernel中的LCM驱动就可以。系统中需要设置的LCM驱动有关的文件目录如下:
3.1配置文件
mediatek\config\y25\ProjectConfig.mk是配置文件,主要配置如下:
/*分别选择lk、kernel阶段的LCM驱动*/
CUSTOM_LK_LCM= hx8357d_hvga_dsi_cmd
CUSTOM_KERNEL_LCM= hx8357d_hvga_dsi_cmd
BOOT_LOGO=hvga 选择需要使用的logo
其中,hx8357d_hvga_dsi_cmd是添加的驱动所在文件夹的文件名。若想兼容两个不同的LCM,则在上述驱动名称后面加一个空格,再添加另一个文件名,系统即可根据读取的LCM的ID自动选择对应的驱动程序。
3.2LCM驱动的声明
mediatek\custom\common\kernel\lcm\mt65xx_lcm_list.c用于在lcm_driver_list结构体指针数组中声明hx8357d_hvga_dsi_cmd_lcm_drv结构体指针。Lcm_driver_list用于将LCM封装的信息传递给DSI控制器层的驱动。
3.3LCM驱动程序的设计
mediatek\custom\common\kernel\lcm\hx8357d_hvga_dsi_cmd\hx8357d_hvga_dsi_cmd.c
此为LCM的驱动程序,因为不同的LCM有不同的的分辨率、初始化序列、时序等,因此为了使程序有更好的可移植性和可读性,把这些部分分立出来。对于接口相同的LCM,就可以根据不同的型号进行不同的参数配置,这也是驱动程序分层思想的优点。LCM的配置主要是使用封装好的三个结构体。按照设置顺序,首先,LCM的初始化序列中的命令参数和初始化参数的配置是利用结构体LCM_setting_table进行封装,然后lcm_init()函数通过DSI的写函dsi_set_cmdq_V3将参数写入LCM;其次,LCM_PARAMS结构体对DSI控制器的初始化参数进行封装,需要注意的是命令模式下不需要设置VSPW、VBPD、VFPD及HSPW、HBPD、HFPD;最后LCM_DRIVER结构体对LCM的底层调用接口函数指针和名称进行封装,DSI控制器层的驱动会调用这些函数对LCM和DSI控制器进行初始化。其主要部分程序如下设置:
struct LCM_setting_table {
unsigned char cmd; //将要下的命令
unsigned char count;//命令的参数个数
unsigned char para_list[64];//参数列表
};
static struct LCM_setting_tablelcm_initialization_setting[] = {
{
/*设置LCM刷新频率55HZ*/
{0xB0,2,{0x55,0x01}},
};
static void lcm_init(void)
{
dsi_set_cmdq_V3(lcm_initialization_setting,sizeof(lcm_initialization_setting) / sizeof(struct LCM_setting_table), 1);
}
Typedef struct
{
LCM_TYPE type;
Unsigned int width; //LCM的宽
Unsigned int height; //LCM的高
/*初始化DSI控制器参数的结构体*/
LCM_DSI_PARAMS dsi;
} LCM_PARAMS;
static void lcm_get_params(LCM_PARAMS*params)
{
/*设置LCM工作在DSI模式*/
params->type= LCM_TYPE_DSI; params->width = 320;
params->height = 480;
/*设置dbi (dsi兼容dbi)在TE工作模式*/
params->dbi.te_mode =
LCM_DBI_TE_MODE_VSYNC_ONLY;
/*设置LCM工作在命令模式*/
params->dsi.mode = CMD_MODE;
/*设置LCM使用一条数据通道传输*/
params->dsi.LANE_NUM = LCM_ONE_LANE;
/*配置DSI的传输频率*/
params->dsi.PLL_CLOCK = 111;
}
LCM_DRIVER hx8357d_hvga_dsi_cmd_lcm_drv =
{
/*驱动的名称*/
.name= "hx8357d_hvga_dsi_cmd",
/*初始化DSI控制器的参数配置*/
.get_params = lcm_get_params,
.init = lcm_init, //LCM的初始化
/*用于多个LCM兼容时候读取硬件ID*/
.compare_id = lcm_compare_id, };
4 LCM驱动的调试
(1)命令模式会有画面刷新有残留(tearing effect,即TE)的问题。根本原因是主控往FrameBuffer写图像数据的速度与LCM从FrameBuffer读数据(刷屏)的速度不一致造成的,解决此问题必须设置主机端在两个TE周期内刷新完一帧数据。首先,配置LCD的刷新频率略小于主机端往FrameBuffer发送数据频率的2倍,其次通过设置FMARK迟滞几个HSYNC周期再输出TE信号来延迟主控开始写数据到FrameBuffer的时间。通过以上配置,避免FrameBuffer的读写数据产生交叉,从而避免由于刷新频率不一致产生的TearingEffect。本例中在拍照模式时候就会有TE问题,在确认开启TE SYNC功能之后,抓取图7所示的TE信号波形图,检测到TE信号正常送出。由于拍照时候数据传输的特殊性,此时camera相当于是主机端,刷新数据的流程相当于camera—FrameBuffer—LCM。而是camera刷新频率是30,LCM刷新频率设置的是60,图7是TE信号波形图,可以看出LCM刷新频率过大,在程序中设置LCM刷新频率为55Hz,最终解决了问题。
(2)在lk阶段读不到LCM的硬件ID的问题。从硬件角度读不到ID有两种可能。一个是BTA function没有成功,导致IC没有送出ID,通过图8可以看到BTA已经完成,总线控制权成功交给了IC;另一个是IC没有正确送出ID,导致读ID失败,图9 可以看到BTA后,IC有将ID=0x99正确送出。
通过上述排查,可以确认是软件由软件产生的问题,分析程序的日志如下:
DISP/DSI+000c : 0x80000001,//准备好读数据
/*1c表示 PacketType ,02表示数据包长度,短数据包的值000c*/
DISP/DSI+0074 : 0x0c00021c,
/*读取的长数据包的值0x99*/
DISP/DSI+0078 : 0x3efd0099,
/*等待软件接收到读到的数据的信号*/
DISP/DSI+0160 : 0x00010400,
/*0xD0寄存器已经发出去读到的数据*/
DISP/DSI_CMD+0000(0x1400c180): 0x00d00604,
分析日志可以发现:从寄存器看,CMDQ里面已经读到了数据,但是软件没有收到数据。最终可以知道,lk读不到id问题在于软件延时处理慢,通过增大polling次数解决。在mediatek\platform\mt6572\lk\dsi_drv.c文件中修改函数DSI_dcs_read_lcm_reg_v2的定义,设置read_timeout_ms= 200; 把polling的次数由20改为200,从而保证读到正确数据,最终解决了问题。
5 结语
本文描述了Android系统下的基于MIPI-DSI协议的LCM的驱动设计,最终完成了MT6572平台上的LCM驱动的设计,并且让屏幕能够正常的流畅显示画面。通过以上的设计过程的分析,一方面可以明显感觉到Android系统下的驱动开发的清晰的层次结构与简便的程序设计,Android的短周期的开发速度更是不断推进智能手机的迅速发展,另一方面也能感觉到DSI协议的极大优越性,并且相信以后DSI协议也会有进一步的发展。