SylixOS的LCD驱动移植
1. 原理概述
1.1 显示原理
如图 1-1所示,LCD驱动会申请一块FB内存,应用程序向FB内存中搬运显示的数据,屏幕上就会有对应的显示。
图 1-1显示原理
1.2 时序Timing
每一个LCD屏幕都会有自定义的时序,这些数据一般都会在屏幕的数据手册中提供,LCD控制器需要根据这些时序产生对应的行场,屏幕在获得正确的行场后才能正常显示。
LCD显示的时序如图 1-2所示,其中屏幕可见区域大小为xres * yres。
图 1-2 LCD显示时序
显示时序图中有相关概念,如表 1-1所示。
表 1-1时序使用的相关概念
| 名词 | 对应缩写 | 说明 |
| left_margin | HBP(Horizontal Back Porch) | 每行开始时需要插入的像素时钟周期数 |
| right_margin | HFP(Horizontal Front Porch) | 每行结束时需要插入的像素时钟周期数 |
| upper_margin | VBP(Vertical Back Porch) | 垂直同步周期之后帧开头时的无效行数 |
| lower_margin | VFP(Vertical Front Porch) | 本帧结束到下一帧垂直同步周期开始之 前的无效行数 |
| hsync_len | HPW (HSYNC plus width) | 表示水平同步信号的宽度(单位:像素时钟周期) |
| vsync_len | VPW (VSYNC width) | 表示垂直同步脉冲的宽度(单位:显示一行的时间) |
如表 1-2和表 1-3所示,E50A2V1屏幕的数据手册中有水平timing和竖直timing的定义,根据数据手册中的参数设置对应的LCD控制器寄存器即可。
此外需要配置LCD控制器总时钟DCLK,E50A2V1为30MHz。
表 1-2 Horizontal timing
| Parameter | Symbol | Min. Spec. | Typ. Spec. | Max. Spec. | Unit |
| Horizontal Display Area | thd | 800 | DCLK | ||
| DCLK frequency | fclk | - | 30 | 50 | MHz |
| One Horizontal Line | th | 889 | 928 | 1143 | DCLK |
| HS pulse width | thpw | 1 | 48 | 255 | DCLK |
| HS Back Porch(Blanking) | thb | 88 | DCLK | ||
| HS Front Porch | thfp | 1 | 40 | 255 | DCLK |
| DE mode Blanking | th-thd | 85 | 128 | 512 | DCLK |
表 1-3 Vertical timing
| Parameter | Symbol | Min. Spec. | Typ. Spec. | Max. Spec. | Unit |
| Vertical Display Area | tvd | 480 | TH | ||
| VS period time | tv | 513 | 525 | 768 | TH |
| VS pulse width | tvpw | 3 | 3 | 255 | TH |
| VS Back Porch(Blanking) | tvb | 32 | TH | ||
| VS Front Porch | tvfp | 1 | 13 | 255 | TH |
| DE mode Blanking | tv-tvd | 4 | 45 | 255 | TH |
1.3 RGB和灰度
1.3.1 RGB888和RGB565
对于彩色图像,每个像素通常用三个分量表示,即R(Red)、G(Green)、B(Blue)三个分量,每个分量用一个字节表示,因此每个分量的取值范围从0到255。
RGB888每个像素由3个字节组成,R、G、B各占8bit;
RGB565 每个像素由2个字节组成,R占5bit,G占6bit,B占5bit。
1.3.2 灰度
灰度表示一个像素最多能显示的颜色数量。
对于RGB888来说,一共可以显示256 * 256 * 256 = 16777216种色彩,那么灰度就是16777216。
对于RGB565来说,一共可以显示32 * 64 * 32 = 65536种色彩,那么灰度就是65536。
2. 技术实现
2.1 LCD驱动框架
lcd驱动实现基本功能,只要实现如图 2-1中的四个函数即可。
图 2-1 LCD驱动需要关注的四个函数
2.1.1 函数__lcdOpen
这个函数需要向系统申请一块用于FB的内存,并且根据timing初始化行场信号,最后开启背光。
2.1.2 函数__lcdClose
这个函数是lcdOpen的逆向过程,主要操作就是关闭背光。
2.1.3 函数__lcdGetVarInfo
这个函数用于获取和屏幕相关的一些参数,比如屏幕的高度和宽度、RGB格式和灰度等。
2.1.4 函数__lcdGetScrInfo
这个函数用于获取和FB相关的一些参数,比如FB的大小。
2.2 调试过程
调试的过程首先要确认屏幕的背光是否开启,如果背光不亮屏幕上是不会有任何显示的。在确认背光开启之后再进行后面几步的测试。
2.2.1 通过示波器确认行场信号
1. 确认DCLK
通过示波器测量DCLK引脚,正常显示的时候应该会产生30MHz的波形。
2. 确认HSYNC
通过示波器测量HSYNC引脚,正常显示的时候应该会产生波形。
3. 确认VSYNC
通过示波器测量VSYNC引脚,正常显示的时候应该会产生波形。
2.2.2 编写测试程序
可以编写一个绘制BMP图形的测试用例,另外利用PhotoShop或其他制图工具,参考实际的分辨率大小制作一张对应的BMP图片。
例如:E50A2V1的分辨率为800 x 480,RGB格式为RGB565,制作这样的一张图片。接着如代码清单 1所示,在代码中打开BMP图片后跳过头部信息,将实际内容拷贝到FB中,那么在屏幕上就应该有对应的图形显示了。
代码清单 1测试程序基本流程
/** 打开位图*/pFp = fopen(pBmpfile, "rb");……/** 读取位图文件头*/iRc = fread(&FileHead, sizeof(BITMAPFILEHEADER), 1, pFp);……/** 读取文件信息头*/iRc = fread((char*)&InfoHead, sizeof(BITMAPINFOHEADER), 1, pFp);……/** 跳过文件头*/fseek(pFp, (INT)charToLong(FileHead.cfoffBits, 4), SEEK_SET);……/** 逐行、逐像素绘制图片*/while (!feof(pFp)){iRc = fread((CHAR *)&pix, 1, sizeof(UINT16), pFp);if (iRc != sizeof(UINT16)){break;}lLocation = iLineX * iBitsPerPixel /8+(iLineY)* iXres * iBitsPerPixel /8;usTmp = pix.blue <<0| pix.green <<5| pix.red <<11;*((UINT16 *)(__GpcFb + lLocation))= usTmp;iLineX++;if(iLineX == iWidth){iLineX =0;iLineY++;if(iLineY == iHeight +1){break;}}}
3. 代码实现
3.1 驱动代码
3.1.1 __lcdOpen的具体实现
__lcdOpen调用了API_VmmPhyAllocAlign向系统申请了一块FB的内存,具体实现如代码清单 2所示。
代码清单 2 __lcdOpen的具体实现
static INT __lcdOpen (PLW_GM_DEVICE pgmdev, INT iFlag, INT iMode)
{if (GpvFbMemBase == LW_NULL) {GpvFbMemBase = API_VmmPhyAllocAlign(800 * 480 * 3, /* 最大800 * 400分辨率 */4 * 1024 * 1024, /* 4M字节对齐 */LW_ZONE_ATTR_DMA); if (GpvFbMemBase == LW_NULL) {__lcdDisable();printk(KERN_ERR "__lcdOpen() low vmm memory!\n");return (PX_ERROR);}__lcdInit();}__lcdEnable();return (ERROR_NONE);
}
3.1.2 __lcdGetVarInfo的具体实现
返回了屏幕的实际高度、灰度等数值,具体实现如代码清单 3所示。
代码清单 3 __lcdGetVarInfo的实现
static INT __lcdGetVarInfo (PLW_GM_DEVICE pgmdev, PLW_GM_VARINFO pgmvi)
{if (pgmvi) {pgmvi->GMVI_ulXRes = curDisplayDev.uiDevWidth; /* 屏幕实际宽度 */ pgmvi->GMVI_ulYRes = curDisplayDev.uiDevHeight; /* 屏幕实际高度 */ pgmvi->GMVI_ulXResVirtual = curDisplayDev.uiDevWidth; /* 屏幕虚拟区域宽度 */pgmvi->GMVI_ulYResVirtual = curDisplayDev.uiDevHeight; /* 屏幕虚拟区域高度 */pgmvi->GMVI_ulXOffset = 0; /* 屏幕x坐标偏移 */ pgmvi->GMVI_ulYOffset = 0; /* 屏幕y坐标偏移 *//* * BPP和掩码设置*/ switch (curDisplayDev.uiStreamColorFormat) {……case FORMAT_RGB565:pgmvi->GMVI_ulBitsPerPixel = 16;pgmvi->GMVI_ulBytesPerPixel = 2;pgmvi->GMVI_ulRedMask = 0xF800;pgmvi->GMVI_ulGreenMask = 0x07E0;pgmvi->GMVI_ulBlueMask = 0x001F;break;……}/* * 灰度设置*/switch (curDisplayDev.uiColorsType) {case COLORTYPE_4K:pgmvi->GMVI_ulGrayscale = 4096;break; …… }pgmvi->GMVI_ulTransMask = 0;pgmvi->GMVI_bHardwareAccelerate = LW_FALSE;pgmvi->GMVI_ulMode = LW_GM_SET_MODE;pgmvi->GMVI_ulStatus = 0;}return (ERROR_NONE);
}
3.1.3 __lcdGetScrInfo的具体实现
__lcdGetScrInfo返回FB相关的FB名称、FB大小等参数,具体实现如代码清单 4所示。代码清单 4 __lcdGetScrInfo的具体实现
static INT __lcdGetScrInfo (PLW_GM_DEVICE pgmdev, PLW_GM_SCRINFO pgmsi)
{UINT32 uiDevWidth = curDisplayDev.uiDevWidth;UINT32 uiDevHeight = curDisplayDev.uiDevHeight;if (pgmsi) {pgmsi->GMSI_pcName = "/dev/fb0";pgmsi->GMSI_ulId = 0;/* * 内存区域大小设置*/switch (curDisplayDev.uiStreamColorFormat) {……case FORMAT_RGB565:pgmsi->GMSI_stMemSize = uiDevWidth * uiDevHeight * 2;pgmsi->GMSI_stMemSizePerLine = uiDevWidth * 2;break;……}pgmsi->GMSI_pcMem = (caddr_t)GpvFbMemBase;}return (ERROR_NONE);
} 3.2 timing配置
为了方便timing的配置,我们定义了__LCD_DEV_PANEL结构体,如图 32所示。这样如果以后更换了其他分辨率的屏幕,理论上只需要根据这个结构体填充对应的timing数据即可。 代码清单 5 屏参相关的结构体struct dev_panel_t {CHAR cPanelName[32]; UINT32 uiPanelId;
#define COLORTYPE_4K (0)
#define COLORTYPE_64K (1)
#define COLORTYPE_256K (2)
#define COLORTYPE_16M (3) UINT32 uiColorsType;
#define FORMAT_YUV422 (0)
#define FORMAT_YCBCR422 (1)
#define FORMAT_RGB888 (2)
#define FORMAT_RGB666 (3)
#define FORMAT_RGB565 (4)
#define FORMAT_RGB444_L (5)
#define FORMAT_RGB332 (6)
#define FORMAT_RGB444_H (7)UINT32 uiStreamColorFormat; UINT32 uiDevWidth; UINT32 uiDevHeight; UINT32 uiPixelClock; UINT32 uiHFrontPorch;UINT32 uiHBackPorch; UINT32 uiHSyncWidth; UINT32 uiVFrontPorch; UINT32 uiVBackPorch;UINT32 uiVSyncWidth;
#define DATAWIDTH8or9 (0)
#define DATAWIDTH16or18 (1)UINT32 uiDataBusWidth;
#define MAPNORMAL (0)
#define MAPJEIDA (1)UINT32 uiMappingMode; UINT32 uiDataInterlaced; UINT32 uiSyncInterlaced; UINT32 uiVerticalPol; UINT32 uiHorizontalPol; UINT32 uiDEPol;
#define DEVICE_SYNC_YUV422 (0)
#define DEVICE_SYNC_YUV444 (1)
#define DEVICE_SYNC_UNIPAC (4)
#define DEVICE_SYNC_EPSON (5)
#define DEVICE_SYNC_HIGHCOLOR (6)
#define DEVICE_MPU (7)UINT32 uiDevType; UINT32 uiGpioBlkEn;
};
typedef struct dev_panel_t __LCD_DEV_PANEL;
typedef struct dev_panel_t* __PLCD_DEV_PANEL; 根据E50A2V1的timing数据填充的结构体如代码清单 6所示。
代码清单 6 E50A2V1的屏参
#define E50A2V1 (0)
static __LCD_DEV_PANEL DEF_LCD_E50A2V1_800x480 = {"E50A2V1", E50A2V1, COLORTYPE_16M, FORMAT_RGB565, 800, 480, 3000000, 88, 40, 48, 32, 13, 3, DATAWIDTH16or18, MAPNORMAL, 0, 0, 0, 0, 0, DEVICE_SYNC_HIGHCOLOR, NUC970_GPIO_NUMR(__BANK_G, 3)
};
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
