LCM调试入门
一. 概述
调试一款LCM,也就是我们平常所说的调屏是我们做驱动的的一项最基本的工作任务。
LCM是通常由屏厂已经封装好的一个模组,即我们见到的屏。它一般由几部分组成:
1. 玻璃。也就是我们看到的显示图像的部分,是LCM里最重要的器件。它内部含有液晶,电极等。
2. IC。也就是我们通常说的显示芯片。它是控制让玻璃能显示图像的器件,我们调屏其实就是让该显示芯片与我们的BB能配合起来工作。
3. 外围器件。包括PCB板,在PCB板上的电容电阻,背光等。
二. 调屏前的注意事项
在真正调屏之前,我们一般要做一些前期准备工作,这将有助于我们今后调屏时少走弯路。
先看原理图,确认该屏的信号控制线连接是否正确,看数据线连的是几位的,8位、16位、18位的数据传输方式是不一样的,程序中由不同的宏来控制。
大致看一下IC的spec,看一下里面的寄存器设置方式;寄存器中控制各个功能的分布情况,比如控制电压的,控制位置的,控制效果的等等;还要看看它的时序是否和我们的BB配合。
三. MTK接口定义
在我们的MTK平台里,它的接口函数都由一个结构来封装。该结构在文件lcd_if.h里:
typedef struct
{
void (*Init)(kal_uint32 background, void **buf_addr);
void (* PWRON)(kal_bool on);
void (* BrightLevel)(kal_uint8 level);
void (* SCREENON)(kal_bool on);
void (* BlockWrite)(kal_uint16startx,kal_uint16 starty,kal_uint16 endx,kal_uint16 endy);
void (* GetSize)(kal_uint16*out_LCD_width,kal_uint16 *out_LCD_height);
void (* SleepIn)(void);
void (* SleepOut)(void);
void (* PartialOn) (kal_uint16start_page,kal_uint16 end_page);
void (* PartialOff) (void);
kal_uint8(*partial_display_align) (void);
/*Engineering mode*/
kal_uint8 (* get_param_number)(lcd_func_typetype);
void (* set_bias)(kal_uint8 *bias);
void (* set_contrast)(kal_uint8 *contrast);
void (* set_linerate)(kal_uint8*linerate);
void (* set_temp_compensate)(kal_uint8*compensate);
#ifdef__LCD_ESD_RECOVERY__
kal_bool (* esd_check)(void);
#endif
}LCD_Funcs;
在lcd.c文件内有相应的LCD_Funcs实例:
LCD_Funcs LCD_func_HX8306A = {
LCD_Init_HX8306A,
LCD_PWRON_HX8306A,
LCD_SetContrast_HX8306A,
LCD_ON_HX8306A,
LCD_BlockWrite_HX8306A,
LCD_Size_HX8306A,
LCD_EnterSleep_HX8306A,
LCD_ExitSleep_HX8306A,
LCD_Partial_On_HX8306A,
LCD_Partial_Off_HX8306A,
LCD_Partial_line_HX8306A,
/*Engineering mode*/
LCD_GetParm_HX8306A,
LCD_SetBias_HX8306A,
LCD_Contrast_HX8306A,
LCD_LineRate_HX8306A,
LCD_Temp_Compensate_HX8306A
};
在lcd.c中实现了该结构的各个成员。下面一节将具体介绍跟我们调试一款LCM有关的函数,其他函数我们可以置空。
四. MTK各函数介绍
我们现在要真正进入到调试LCM了,下面以显示芯片HD66789为例说明。
一般LCM的正常启动(工作)流程是这样的:
l 上电
l 硬件复位
l 初始化
l 开始显示数据
LCM的电源都是电源芯片直接供应的,所以这部分我们可以不考虑。
硬件复位,我们在代码里可以看到。一般的IC使用之前都会需要一个硬件复位,显示IC的复位信号是低高,接下来在LCM的整个工作期间复位脚都要处在一个高电平上。在lcd.c的函数init_lcd_interface中,我们可以看到:
voidinit_lcd_interface(void)
{
… … … …
CLEAR_LCD_CTRL_RESET_PIN; //low level
… … … …
SET_LCD_CTRL_RESET_PIN; //high level
… … … …
}
这段代码产生的信号效果如下图所示:
|
在讲初始化之前,有必要强调一下如何设置时序。设置时序还是在函数init_lcd_interface中,有类似以下的结构:
SET_LCD_PARALLEL_CE2WR_SETUP_TIME((kal_uint32)0);…………..( tAS)(C2WS)
SET_LCD_PARALLEL_CE2WR_HOLD_TIME(0);………………...………(tAH)(C2WH)
SET_LCD_PARALLEL_CE2RD_SETUP_TIME(1);
SET_LCD_PARALLEL_WRITE_WAIT_STATE(2);………………...………(tAS+tAH+PWLW)(WST)
SET_LCD_PARALLEL_READ_LATENCY_TIME(2);
SET_LCD_ROI_CTRL_CMD_LATENCY(2);
这里的数值1对应实际时序的20ns。
参考HD66789 spec:
从该spec我们要关注这些数据:tAS,tAH,PWLW等数值,其中tAS,tAH分别对应结构的代码已经标出,spec中tAS和tAH分别是0ns和2ns,我们的代码中分别写了0,安全期间,也可以写1,表示延时20ns。代码中的WST+40ns=tAS+tAH+PWLW, spec中PWLW是30ns,我们代码里写了2,表示40ns。在实际bb输出时,该时序还会再加上40ns,所以总共有80ns,减去tAS,tAH(都是0ns)还有80ns(PWLW),大于30ns,这里我们把范围放得稍微有点宽,这样是可以的,因为spec上标注的是min的时间。在实际调试中,我们设定的数值不一定完全符合spec,总体的原则是宁可大一点。
下面是HD66789的时序图,供参考:
l LCD_Init_HX8306A
初始化函数。设完了时序之后,我们就可以写初始化程序了。初始化程序的作用是使屏能正常工作起来,屏在上电复位之后其寄存器都是清零的,不能马上工作,要对它的寄存器重新进行一些配置。
一般我们在拿到屏的时候,也会要求屏的供应商提供相应的初始化代码,我们的工作就是把它的初始化代码抄到我们的初始化函数中,使之符合我们的代码规范。
l LCD_EnterSleep_HX8306A,LCD_ExitSleep_HX8306A
进入和退出睡眠模式(sleep)。我们在长期不对手机进行操作的情况下,程序会调用进入sleep函数,使LCM进到省电模式;进到sleep之后,如果我们又对手机有操作了,这时候就要调用退出sleep函数使屏能够恢复正常显示。一般的显示芯片都会有一些寄存器控制进入和退出sleep,我们在函数LCD_EnterSleep_HX8306A, LCD_ExitSleep_HX8306A里分别对这些寄存器进行设置。
l LCD_SetContrast_HX8306A
设置对比度函数。LCM有好多中分类,根据显示原理有TFT,CSTN,OLED等等(与显示IC相关)。如果是TFT或OLED等LCM,那对比度是不需要设置的,可以把该函数置空;如果是CSTN的屏,IC中有相应设置对比度的寄存器,我们在该函数中设置。
l LCD_Size_HX8306A
设置屏幕尺寸大小的函数。在该函数里会要求对屏的大小进行设置,如果设错可能会造成满屏显示不了或者超出显示区域。
l LCD_BlockWrite_HX8306A
往屏输出数据的函数。我们在LCM上看到的各种缤纷图像,都是通过这个函数来实现的。想象一下这个函数要显示那么多东西,肯定很复杂吧?其实看了我下面的说明之后你就不会觉得它有多可怕了。
看函数原型,该函数是针对HD66789的函数,我在最右面标了有效行数:
voidLCD_BlockWrite_HX8306A(kal_uint16startx,kal_uint16 starty,kal_uint16 endx,kal_uint16 endy){ //1
kal_uint16 roi_width; //2
kal_uint16 x,y; //3
kal_uint16 LCD_x; //4
kal_uint16 LCD_y; //5
while (LCD_IS_RUNNING){}; //6
// ASSERT(lcd_assert_fail==KAL_FALSE);
lcd_assert_fail=KAL_TRUE; //7
#ifdef LCD_CMD_DMA_MODE //8
#if (defined(LCD_8BIT_MODE)) //9
SET_LCD_CMD_PARAMETER(0,LCD_CMD,0x00); //10
SET_LCD_CMD_PARAMETER(1,LCD_CMD,0x44); //11
SET_LCD_CMD_PARAMETER(2,LCD_DATA,endx); //12
SET_LCD_CMD_PARAMETER(3,LCD_DATA,startx); //13
SET_LCD_CMD_PARAMETER(4,LCD_CMD,0x00); //14
SET_LCD_CMD_PARAMETER(5,LCD_CMD,0x45); //15
SET_LCD_CMD_PARAMETER(6,LCD_DATA,endy); //16
SET_LCD_CMD_PARAMETER(7,LCD_DATA,starty); //17
SET_LCD_CMD_PARAMETER(8,LCD_CMD,0x00); //18
SET_LCD_CMD_PARAMETER(9,LCD_CMD,0x21); //19
SET_LCD_CMD_PARAMETER(10,LCD_DATA,starty); //20
SET_LCD_CMD_PARAMETER(11,LCD_DATA,startx); //21
//SET_LCD_CMD_PARAMETER(11,LCD_DATA,(startx&0xFFFC)); //22
SET_LCD_CMD_PARAMETER(12,LCD_CMD,0x00); //23
SET_LCD_CMD_PARAMETER(13,LCD_CMD,0x22); //24
#endif //25
ENABLE_LCD_TRANSFER_COMPLETE_INT; //26
ENABLE_LCD_ROI_CTRL_CMD_FIRST; //27
SET_LCD_ROI_CTRL_NUMBER_OF_CMD(14); //28
START_LCD_TRANSFER; //29
#endif /* MT6218B,MT6219 */ //30
lcd_assert_fail = KAL_FALSE; //31
} //32
第1行,请注意参数列表,它表示在屏上真正要显示区域的坐标,如下图所示。因为BB每次刷屏不是全屏刷的,而是有选择性地在要刷的地方刷新,其他地方保留原来的图像;第6行表示如果现在BB正在往屏上写东西,则等BB写完;第10到第22行就是把矩形显示区域写到IC的寄存器中,让IC知道接下来BB送来的数据从屏的哪里开始写,到哪一列要跳到下一行继续写,到哪里结束。第22、23行表示接下来BB开始要送数据了;第26行到结束是对BB设置,告诉它显示IC已经准备好接受数据了。以上讲的是对HD66789而言的刷屏方式,如果用其他IC会有所不同,但是原理都是一样的。
这个函数返回后,BB就开始往屏上输出图像数据。
其他函数我们目前都已不用或用处不大,大家可以置空。
http://wenku.baidu.com/view/8db8c49fb9f3f90f77c61b07.html?re=view