ARM9的LCD

2019-07-13 05:51发布

实验前须知:
关于LCD的知识,你可以参考《嵌入式Linux应用开发完全手册》的第13章,只要你静下心来看,里边的知识点完全可以看懂。
待你看懂后,LCD的知识你就了解差不多了。接下来就从代码中去解读LCD的实际操作了。

操作LCD的大体流程如下:
1>配置GPIO为相应的功能管脚
2>配置寄存器,其中LCDCON1-LCDCON5和LCDSADDR1-LCDSADDR3可能相对难配置一点。待会在问题总结给大家解释。
3>LCD的实际操作(从代码中去理解吧)

实验的目的:

操作LCD,让其显示不同的画面。

实验的源程序:
lcd.rar


实验的问题总结:
I. 关于LCD比较关键的一点的就是Tft_Lcd_Init()这个函数。我们进入Tft_Lcd_Init(MODE_TFT_16BIT_480272)看一下吧。
在里边我们可以看到,主要是配置LCDCON1-LCDCON5和LCDSADDR1-LCDSADDR3这几个寄存器。刚开始看,我也没大整明白
为何要这样设置。现在我就给大伙讲讲这样配置的原因。
摘录其中一段源码如下:
case MODE_TFT_16BIT_480272:
        /* 
         * 设置LCD控制器的控制寄存器LCDCON1~5
         * 1. LCDCON1:
         *    设置VCLK的频率:VCLK(Hz) = HCLK/[(CLKVAL+1)x2]
         *    选择LCD类型: TFT LCD   
         *    设置显示模式: 16BPP
         *    先禁止LCD信号输出
         * 2. LCDCON2/3/4:
         *    设置控制信号的时间参数
         *    设置分辨率,即行数及列数
         * 现在,可以根据公式计算出显示器的频率:
         * 当HCLK=100MHz时,
         * Frame Rate = 1/[{(VSPW+1)+(VBPD+1)+(LIINEVAL+1)+(VFPD+1)}x
         *              {(HSPW+1)+(HBPD+1)+(HFPD+1)+(HOZVAL+1)}x
         *              {2x(CLKVAL+1)/(HCLK)}]
         *            = 60Hz
* Frame Rate = 60Hz 是根据LCD手册来的。 /* LCD手册表示Dclk=9MHz~15MHz, HCLK=100MHz, Dclk=VCLK=HCLK/[(CLKVAL+1)x2]=100/((4+1)*2)=10MHz */ 从而就确定CLKVAL的值。
         * 3. LCDCON5:
         *    设置显示模式为16BPP时的数据格式: 5:6:5
         *    设置HSYNC、VSYNC脉冲的极性(这需要参考具体LCD的接口信号): 反转
         *    半字(2字节)交换使能
         */
        LCDCON1 = (CLKVAL_TFT_480272<<8) | (LCDTYPE_TFT<<5) |
                  (BPPMODE_16BPP<<1) | (ENVID_DISABLE<<0);
        LCDCON2 = (VBPD_480272<<24) | (LINEVAL_TFT_480272<<14) |
                  (VFPD_480272<<6) | (VSPW_480272);
        LCDCON3 = (HBPD_480272<<19) | (HOZVAL_TFT_480272<<8) | (HFPD_480272);
        LCDCON4 = HSPW_480272;
        LCDCON5 = (FORMAT8BPP_565<<11) | (HSYNC_INV<<9) | (VSYNC_INV<<8) |
                  (HWSWP<<1);

        /*
         * 设置LCD控制器的地址寄存器LCDSADDR1~3
         * 帧内存与视口(view point)完全吻合,
         * 图像数据格式如下:
         *         |----PAGEWIDTH----|
         *    y/x  0   1   2       479
         *     0   rgb rgb rgb ... rgb
         *     1   rgb rgb rgb ... rgb
         * 1. LCDSADDR1:
         *    设置LCDBANK、LCDBASEU
         * 2. LCDSADDR2:
         *    设置LCDBASEL: 帧缓冲区的结束地址A[21:1]
         * 3. LCDSADDR3:
         *    OFFSIZE等于0,PAGEWIDTH等于(480*2/2)
         */
        LCDSADDR1 = ((LCDFRAMEBUFFER>>22)<<21) | LOWER21BITS(LCDFRAMEBUFFER>>1);
        LCDSADDR2 = LOWER21BITS((LCDFRAMEBUFFER+
                    (LINEVAL_TFT_480272+1)*(HOZVAL_TFT_480272+1)*2)>>1);
        LCDSADDR3 = (0<<11) | (LCD_XSIZE_TFT_480272*2/2);


        /* 禁止临时调 {MOD}板寄存器 */
        TPAL = 0;


        fb_base_addr = LCDFRAMEBUFFER;
        bpp = 16;
        xsize = 480;
        ysize = 272;


        break;

1>LCDCON1寄存器的配置
主要讲一下:CLKVAL_TFT_480272<<8这条语句。查看其宏可以知道:#define CLKVAL_TFT_480272 (4)  /* LCD手册表示Dclk=9MHz~15MHz, HCLK=100MHz, Dclk=VCLK=HCLK/[(CLKVAL+1)x2]=100/((4+1)*2)=10MHz */
在其注释部分可以很清楚地知道, Dclk是根绝LCD手册来的。HCLK=100MHz是根据你在程序中设置的系统时钟来的。
这样我们就可以确定
CLKVAL的值了。

2>LCDCON2寄存器的配置
在代码中,我们可以看到这样一条语句: LCDCON2 = (VBPD_480272<<24) | (LINEVAL_TFT_480272<<14) |
                  (VFPD_480272<<6) | (VSPW_480272);
然后查看这些相关的宏之后我们得知:
#define VBPD_480272 ((2-1)&0xff)     /* tvb=2 */
#define VFPD_480272 ((2-1)&0xff)     /* tvf=2 */
#define VSPW_480272 ((10-1) &0x3f)   /* tvp=10 */
#define LINEVAL_TFT_480272 (LCD_YSIZE_TFT_480272-1) ----> #define LCD_YSIZE_TFT_480272 (272)
这些参数的设置是根据什么来的呢?
查看s3c2440手册,我们知道:

查看LCD手册,我们知道:



对照着这三幅图中,我们可以知道:
VSPW+1 =tvp ;
VBPD+1 = tvb;
VFPD+1 = tvf;
HSPW+1 = thp;
HBPD+1 = thb;
HFPD+1 = thf;
而查看Typ及Min这两列,我们便可以得知:
tvp = 10;
tvb = 2;
tvf = 2;
thp = 41;
thb = 2;
thf = 2;
所以
VSPW = 9;
VBPD = 1;
VFPD = 1;
HSPW = 40;
HBPD  = 1;
HFPD = 1;
这也就是为什么下面这些宏如此设置的原因。
#define VBPD_480272 ((2-1)&0xff)     /* tvb=2 */
#define VFPD_480272 ((2-1)&0xff)     /* tvf=2 */
#define VSPW_480272 ((10-1) &0x3f)   /* tvp=10 */
LCD_YSIZE_TFT_480272 设置为272是根据LCD的分辨率来的 (480 X 272)

注:我们一般情况下选择其Typ列对应的值,若没有,再选择考虑其Min列的值。从Typ字面意思去理解的话,
Typ列的值为其比较常用且典型的值。

3>LCDCON3和LCDCON4寄存器的配置
由于我们在步骤2中已经知道这些参数的设置方法,所以对于LCDCON3和LCDCON4寄存器的配置就不赘述了。

4>LCDCON5寄存器的配置
在这里主要讲一下关于极性的配置,这个之后我才了解到,默认极性是根据s3c2440的时序图来的。需不需要反转,这还得参考
LCD的各信号在什么电平期间有效和3c2440的是否一致。若一致,则不需要反转,若不一致,则需要反转。
可参考博客:http://blog.chinaunix.net/uid-25871104-id-3462676.html中的”判断HSYNC,VSYNC,VCLK,VDEN信号是否需要反转“

II.接下来我们看一下如何操作LCD,就拿 " ClearScr(0x0);  // 清屏,黑 {MOD} " 这个来分析吧
代码如下:
/* 
 * 将屏幕清成单 {MOD}
 * 输入参数:
 *     color: 颜 {MOD}值
 *         对于16BPP: color的格式为0xAARRGGBB (AA = 透明度),
 *     需要转换为5:6:5格式
 *         对于8BPP: color为调 {MOD}板中的索引值,
 *     其颜 {MOD}取决于调 {MOD}板中的数值
 */
void ClearScr(UINT32 color)
{   
    UINT32 x,y;
    
    for (y = 0; y < ysize; y++)
        for (x = 0; x < xsize; x++)
            PutPixel(x, y, color);
}

进入到PutPixel(x, y, color)函数看一下:

case 16:
        {
            UINT16 *addr = (UINT16 *)fb_base_addr + (y * xsize + x);   //X,Y是从0开始的。
            red   = (color >> 19) & 0x1f; // 5 BIT            //color是你传进来的时候对应的一个数值,代表某种颜 {MOD}。
            green = (color >> 10) & 0x3f; // 6 bit
            blue  = (color >>  3) & 0x1f; // 5 bit
            color = (red << 11) | (green << 5) | blue; // 格式5:6:5
            *addr = (UINT16) color;  //往adddr这个地址上写值为color;
            break;
        }
我们可以很清晰地看到:
我们只需要往对应地址赋颜 {MOD}对应的值即可。那么在显示屏上就可以看到我们在某个位置对应的颜 {MOD}了,简单吧!上述这段代码没有用到
调 {MOD}板。关于调 {MOD}板的如何使用,可自行再去分析其中的代码,原理相似。



关注微信公众号获取更多资讯