上一章已经解释的很清楚了,如何将所要运行的user_bin程序定位到DRAM中,这一章要进行重定位到DRAM后运行LCD程序,实际上一章中BL2中程序可以不用改动,直接重写我们的USER目录下的程序即可,将USER目录下的LED灯闪烁程序用LCD程序替换就行,最后编译出的程序名字也叫user_bin.bin即可,这样也可以用上一章中的fast_fuse.sh进行烧写到SD卡运行。
Exynos4412的LCD控制器可以通过编程支持不同LCD屏的要求,例如行和列像素数,数据总线宽度,接口时序和刷新频率等。LCD控制器的主要作用,是将定位在系统存储器中的显示缓冲区中的LCD图像数据传送到外部LCD驱动器,并产生必要的控制信号,例如RGB_VSYNC,RGB_HSYNC, RGB_VCLK等。
图8-1、Exynos4412 LCD控制器框图
(下面的部分来自网络翻译,规格书中的描述)
如上图8-1所示,在Exynos4412规格书中截图,LCD控制器的构成主要由VSFR,VDMA,VPRCS , VTIME和视频时钟产生器几个模块组成:
(1)、VSFR由121个可编程控制器组,一套gammaLUT寄存器组(包括64个寄存器),一套i80命令寄存器组(包括12个寄存器)和5块256*32调色板存储器组成,主要用于对lcd控制器进行配置。
(2)、VDMA是LCD专用的DMA传输通道,可以自动从系统总线上获取视频数据传送到VPRCS,无需CPU干涉。
(3)、VPRCS收到数据后组成特定的格式(如16bpp或24bpp),然后通过数据接口(RGB_VD, VEN_VD, V656_VD or SYS_VD)传送到外部LCD屏上。
(4)、VTIME模块由可编程逻辑组成,负责不同lcd驱动器的接口时序控制需求。VTIME模块产生 RGB_VSYNC, RGB_HSYNC, RGB_VCLK, RGB_VDEN,VEN_VSYNC等信号。
Exynos4412的LCD主要特性:
(1)、支持4种接口类型:RGB/i80/ITU601(656)/YTU444
(2)、支持单色、4级灰度、16级灰度、256色的调色板显示模式
(3)、支持64K和16M色非调色板显示模式
(4)、支持多种规格和分辨率的LCD
(5)、虚拟屏幕最大可达16MB
(6)、5个256*32位调色板内存
(7)、支持透明叠加
LCD的FIMD显示控制器全部信号定义如下表8-1所示
表8-1、LCD接口信号表
Signal |
I/O |
Description |
LCD Type |
LCD_HSYNC |
O |
水平同步信号 |
RGB I/F |
LCD_VSYNC |
O |
垂直同步信号 |
|
LCD_VDEN |
O |
数据使能 |
|
LCD_VCLK |
O |
视频时钟 |
|
LCD_VD[23:0] |
O |
LCD像素数据输出 |
|
SYS_OE |
O |
输出使能 |
|
VSYNC_LDI |
O |
Indirect i80接口,垂直同步信号 |
i80 I/F |
SYS_CS0 |
O |
Indirect i80接口,片选LCD0 |
|
SYS_CS1 |
O |
Indirect i80接口,片选LCD1 |
|
SYS_RS |
O |
Indirect i80接口,寄存器选择信号 |
|
SYS_WE |
O |
Indirect i80接口,写使能信号 |
|
SYS_VD[23:0] |
IO |
Indirect i80接口,视频数据输入输出 |
|
SYS_OE |
O |
Indirect i80接口,输出使能信号 |
|
VEN_HSYNC |
O |
601接口水平同步信号 |
ITU 601/656 I/F |
VEN_VSYNC |
O |
601接口垂直同步信号 |
|
VEN_HREF |
O |
601接口数据使能 |
|
V601_CLK |
O |
601接口数据时钟 |
|
VEN_DATA[7:0] |
O |
601接口YUV422格式数据输出 |
|
V656_DATA[7:0] |
O |
656接口YUV422格式数据输出 |
|
V656_CLK |
O |
656接口数据时钟 |
|
VEN_FIELD |
O |
601接口域信号 |
其中主要的RGB接口信号:
(1)、LCD_HSYNC:行同步信号,表示一行数据的开始,LCD控制器在整个水平线(整行)数据移入LCD驱动器后,插入一个LCD_HSYNC信号;
(2)、LCD_VSYNC: 帧同步信号,表示一帧数据的开始,LCD控制器在一个完整帧显示完成后立即插入一个LCD_VSYNC信号,开始新一帧的显示;VSYNC信号出现的频率表示一秒钟内能显示多少帧图像,称为“显示器的频率”
(3)、LCD_VCLK:像素时钟信号,表示正在传输一个像素的数据;
(4)、LCD_VDEN:数据使能信号;
(5)、 LCD_VD[23:0]: LCD像素数据输出端口
如下图8-2是LCD的RGB接口工作时序图,图中各时钟延时参数的含义如下,这些配置可以在所使用的LCD规格书中查取:
(1)、相关参数说明
VBPD(vertical back porch):表示在一帧图像开始时,垂直同步信号以后的无效的行数。
VFBD(vertical front porch):表示在一帧图像结束后,垂直同步信号以前的无效的行数。VSPW(vertical sync pulse width):表示垂直同步脉冲的宽度,用行数计算。
HBPD(horizontal back porch):表示从水平同步信号开始到一行的有效数据开始之间的VCLK的个数。
HFPD(horizontal front porth):表示一行的有效数据结束到下一个水平同步信号开始之间的VCLK的个数。
HSPW(horizontal sync pulse width):表示水平同步信号的宽度,用VCLK计算。
(2)、帧的传输过程
VSYNC信号有效时,表示一帧数据的开始,信号宽度为(VSPW +1)个HSYNC信号周期,即(VSPW +1)个无效行;
VSYNC信号脉冲之后,总共还要经过(VBPD+ 1)个HSYNC信号周期,有效的行数据才出现; 所以,在VSYNC信号有效之后,还要经过(VSPW +1 + VBPD + 1)个无效的行;
随即发出(LINEVAL+ 1)行的有效数据;
最后是(VFPD + 1)个无效的行。
(3)、行中像素数据的传输过程
HSYNC信号有效时,表示一行数据的开始,信号宽度为(HSPW+ 1)个VCLK信号周期,即(HSPW +1)个无效像素;
HSYNC信号脉冲之后,还要经过(HBPD +1)个VCLK信号周期,有效的像素数据才出现;
随后发出(HOZVAL+1)个像素的有效数据;
最后是(HFPD +1)个无效的像素。
(4)、将VSYNC、HSYNC、VCLK等信号的时间参数设置好之后,并将帧内存的地址告诉LCD控制器,它即可自动地发起DMA传输从帧内存中得到图像数据,最终在上述信号的控制下RGB数据出现在数据总线VD[23:0]上。用户只需要把要显示的图像数据写入帧内存中。
其实现实的图像有像素点组成行、行组成场、场组成动画、动画叠加也就是3D的出现,也就是我们所说的“点动成线、线动成面、面动成体”。
图8-2、LCD的RGB工作时序图
图8-3、LCD的部分硬件电路图
由于我的Tiny4412所用屏幕是S07,是24bit(A888)显示模式,即用24位的数据来表示一个像素的颜色,每种颜色使用8位。 LCD控制器从内存中获得某个像素的24为颜色值后,直接通过VD[23:0]数据线发送给LCD;在内存中,使用4个字节(32位)来表示一个像素,其中的3个字节从高到低分别表示红、绿、蓝,剩余的1个字节无效;
以上内容是我从以前调试uboot中LCD显示查阅到的资料整合,其主要来自于网络,网络上还有各种介绍LCD的相交资料,有兴趣的朋友可以自己深究,也可以参考我即将整理的UBOOT相关文档。
下面对程序进行简要说明,这时我只对USER目录下的程序实现过程进行必要说明,其他相关细节,请自行对照手册来分析程序,这一章的程序我也进行必要的注释,
首先来看一下Makefile和sdram.lds start.S几个文件
上面三个程序的说明请参考上一章,其主要作用就是将user_bin.bin中的而start.s链接地址设置为0X43E00000让程序重定们DRAM中时,就执行这个程序,而start.s唯一做的事就是跳转到main.c中的main函数执行。
Mian函数很清楚,一开始调用lcd_init();然后调用lcd_clear_screen(0x000000)清屏,接着画一个十字,再画了几条水平线和一条垂直线,最后让LDE灯一直闪烁。
首先需要说明的一点时,这个程序我参考了网上很多人关于S5PV210的写法,但或多或少总存在一些问题,最后在FriendlyARM的BBS的论坛上一个ID号为“赵远远”提供的lcd_chinese_char程序,这个程序初始化流程可以很好的工作。所以,这个程序里的初始化部分主要取自于此!
3.1、寄存器设置方法
如下面两句话所示:
#define LCDBLK_CFG (*(volatile unsigned int *)0x10010210)
LCDBLK_CFG = a;
语句中0x10010210是寄存器LCDBLK_CFG的地址,(volatileunsigned int *)0x10010210是将此0x10010210值强制转换成一个指向unsigned int的指针,volatile作用是防止编译器优化,再在外面加一个*就是取0x10010210地址处的值了,所以用LCDBLK_CFG就可改写0x10010210处的数据,
下面来按着LCD的初始化顺序进行说明。
3.2、定义IO引脚功能为RGB接口。
由上图8-3所示,所用的GPIO口分别是GPIO的F口的0/1/2/3四组,查看手册可知要想将其设置为LCD的RGB接口,只需要将其设置为2即可。同时将其IO口设置成为内部上拉,且将其驱动能力设置为最强代码如下:
GPF0CON = 0x22222222;
GPF1CON = 0x22222222;
GPF2CON = 0x22222222;
GPF3CON = 0x00222222;
// Set pull-up,down disable
GPF0PUD = 0x0000FFFF;
GPF1PUD = 0x0000FFFF;
GPF2PUD = 0x0000FFFF;
GPF3PUD = 0x00000FFF;
//MAX drive strength---------//
GPF0DRV = 0x0000FFFF;
GPF1DRV = 0x0000FFFF;
GPF2DRV = 0x0000FFFF;
GPF3DRV = 0x00000FFF;
3.3、接着设置LCD相关时钟寄存器
这一步主要设置选择LCD时钟输入源为MPLL,且不对其进行分频,同时设置LCDBLK_CFG使其使用FIMD接口,且设置LCDBLK_CFG2使其PWM输出使能,其实,LCDBLK_CFG2可以不用设置。
3.4、清除Fram Buffer处的数据
这里调用一个Memset()函数,对地址0x54000000处,0X200000大小的DRAM进行初始化为统一值,一开始我设置为,后来为了调试,我将其设置为0xff00,即可以显示为绿色。
3.5、设置VIDCONx,设置接口类型,时钟分频,极性以及使能LCD控制器等
A、VIDCON0参考数据手册,这一个寄存器主要设置接口类型和时钟分频,这里仅仅设置了其时钟分频值。参考S07的手册,找到时像素时钟。由于我们的MPLL为800MHZ,所以这里设置值,根据手册进行计算,要得到33.3MHZ左右的像素时钟,所以寄存器第6~13bit设置为23.其他都保持为0即可。
代码如下:
VIDCON0 = (23 << 6);
此代码中也给出了如下代码风格的写法,后面很多寄存器配置我也均写好相类似风格的代码,但我要遗憾的说一声,这种风格的写法且总不能执行成功,LCD像是初始化成功,但且不能将图形给画出,这在此比对代码比对了很久,也没有找出原由来,所以这里代码中可运行的部分是受高手唾弃的一种写作风格,同时也给出了另一种写作风格的代码,希望细心的网友能发现其中那里我设置有差别,而让后一种风格的代码能正常运行。
// #define EXYNOS_VIDCON0_CLKVAL_F(x) (((x) & 0xff) << 6)
//VIDCON0 = EXYNOS_VIDCON0_CLKVAL_F(23); //not use this method ,it does not work???
B、VIDCON1主要设置像表时钟信号一直存在,且高电平有效,而IHSYNC=1,极性反转IVSYNC=1,极性反转,这是由于S07的时序图中VSYNC和HSYNC都是低脉冲有效,而Exynos4412芯片手册时序图,如上图8-2所示:VSYNC和HSYNC都是高脉冲有效,所以需要反转。
C、VIDTCONx用来设置时序和长宽等参数,这里就主要设置VBPD(vertical back porch)、 VFBD(vertical frontporch)、VSPW(vertical sync pulse width)、HBPD(horizontal backporch)、 HFPD(horizontal sync pulse width)等参数,参数的意义前面已有简要说明,这里简单用图片的方式说明如何取值。
如下图8.4所示是VIDTCON0取值过程,VIDTCON0设置帧同步时序。
图8-4、VIDTCON0寄存器取值参考图
VBPDE与RGB接口无关,不用关心,所以其代码设置如下:
VIDTCON0 = (22 << 16) | (21 << 8) | (0);
如下图8.5所示是VIDTCON1取值过程,VIDTCON1设置像素同步时序。
图8-5、VIDTCON1寄存器取值参考图
VFPDE与RGB接口无关,不用关心,所以其代码设置如下:
VIDTCON1 = (45 << 16) | (209 << 8) | (0);
如下图8-6所示,为寄存器VIDTCON2设置取值要点:
图8-6、VIDTCON2寄存器取值参考图
VIDTCON2 = (479 << 11) | 799;
D、设置WINCON0寄存器,即设置数据格式。
Exynos4412的LCD控制器有overlay功能,它支持5个window。这里只使用window0,设置其代码RGB模式为24bit(A888)且使能window0,其相关的代码设置如下:
WINCON0 = (11 << 2) | 1;
E、设置VID0SD0A/B/C,即设置Window0的坐标系。
相关代码如下:
VIDOSD0A = 0;
VIDOSD0B = (799 << 11) | 479;
VIDOSD0C = 480 * 800;
F、配置VIDW00ADD0B0和VIDW00ADD1B0,设置framebuffer的地址;
相关代码如下:
VIDW00ADD0B0 = LCD_VIDEO_ADDR;
VIDW00ADD1B0 = LCD_VIDEO_ADDR + VIDOSD0C * 4;
G、配置SHADOWCON和WINCHMAP2、选择使能DMA通道0。由于我们使用的是Window0,所以需要使能DMA通道0;
相关代码如下:
SHADOWCON |= 1;
WINCHMAP2 &= ~(7 << 16);
WINCHMAP2 |= 1 << 16;
WINCHMAP2 &= ~7;
WINCHMAP2 |= 1;
H、最后设置VIDCON0低两位使能LCD,代码如下:
VIDCON0 |= 3;
我们不需要使用很强大的功能,所有只需设置这几个寄存器即可。
经过lcd_init()初始化LCD控制器之后,我们就可以在LCD上描绘图形了,代码里的所有绘制图形的函数都是基于lcd_draw_pixel()这个函数,它的作用是在LCD上描绘一个点,通过描绘一个个的点最终会可以组成图形了。在LCD上描点的本质就是往Frame Buffer中写入颜色值而已。LCD控制器自己会去读所定义的Frame Buffer内存位置处读取数据,然后输出到LCD。
lcd_draw_pixel()这个函数以及后面几个画水平线、垂直线和十字的函数我完成COPY了《Linux平台下Mini210S裸机程序开发指南》里的代码,下面我们仅仅COPY说明一下此函数,其代码如下:
void lcd_draw_pixel(int row, int col, intcolor)
{
unsigned long * pixel = (unsigned long *)LCD_VIDEO_ADDR;
*(pixel + row * COL + col) = color;
}
其中LCD_VIDEO_ADDR = 0x54000000,即framebuffer的基地址,这个值只要是DRAM中,一块可分配16M大小的DRAM的一个地址即可。row和col用来决定偏移,color是颜色值,只要在framebuffer中对应的地址处写入颜色值就可以在LCD上描绘出该点。
后面其他函数很简单,这里就不费时间做过多说明。
已将SD卡插入电脑,假设linux识别了SD卡,其识别号为sdb。执行下面命令:
# chmod 777 –R 7_sdram_LCD
# cd 7_sdram_LCD
# make
# ./ fast_fuse /dev/sdb
将sd卡插入Tiny4412中,选择sd卡启动,和电脑能过串口0连接好,打开一个串口调试助手,然后上电,可以看到以下现象:
串口助手中会显示一些信息,LCD初始化完成后,LCD屏幕首先会显示一整幅的绿色背景。 一两秒钟后,会显示Mian()函数中所画的几条横线和一条竖线以及一个十字,板上的LED灯会一直闪烁。
前面说过,这一章中留有一点问题,就是两种写法,同样的设置,为什么第二种代码风格却不能运行,我自己用人眼对比了好多次,也没有发现代码那里有问题,没办法人眼排查,只能试着将串口重新初始化,然后打印出相关信息,找出原因吧。