• JZ2440 裸机驱动 第13章 LCD控制器(2)


    13.2 TFT LCD显示实例
    13.2.1 程序设计
        本实例的目的是从串口输出一个菜单,从中选择各种方法进行测试,比如画线、
    画圆、显示单色、使用调色板等。
    13.2.2代码详解
        本实例源码在/work/hardware/lcd目录下,与LCD相关的代码有3个文件:lcddrv.c、
    framebuffer.c和lcdlib.c(及相应的头文件)。
        (1)lcddrv.c封装了对LCD控制器、调色板的访问函数,可以设置LCD的显示模式、
    开启/关闭LCD、设置调色板等。
        (2)framebuffer.c直接操作帧缓冲区,实现画点、画线、画同心圆、清屏等函数。
        (3)lcdlib.c调用前两个文件提供的函数在LCD上进行各种操作。
        程序的结构如图13.8所示。
    1.main.c
    main.c的代码很简单,其主体如下:
     1 c = getc();
     2 printf("%c
    
    ", c);
     3 switch(c)
     4 {
     5     case '1':
     6     {
     7         Test_Lcd_Tft_8Bit_240320();
     8         break;
     9     }
    10 
    11     case '2':
    12     {
    13         Test_Lcd_Tft_16Bit_240320();
    14         break;
    15     }
    16         
    17     case '3':
    18     {
    19         Test_Lcd_Tft_8Bit_640480();
    20         break;
    21     }
    22         
    23     case '4':
    24     {
    25         Test_Lcd_Tft_16Bit_640480();
    26         break;
    27     }
    28 }
    main.c主体代码
        它根据串口的输入选择是以哪种显示模式操作LCD,所调用的4个函数都在lcdlib.c中实现。
    2.lcdlib.c
        8BPP模式将用到调色板,其操作比16BPP模式稍复杂,但大部分仍相似。下面以
    Test_Lcd_Tft_8Bit_240320为例进行说明。
     1 行号
     2 11行/*
     3 12行 *以240x320、8BPP的显示模式测试TFT LCD
     4 13行 */
     5 14行void Test_Lcd_Tft_8Bit_240320(void)
     6 15行{
     7 16行    Lcd_Port_Init();                    //设置LCD引脚
     8 17行    Tft_Lcd_Init(MODE_TFT_8BIT_240320); //初始化LCD控制器
     9 18行    Lcd_PowerEnable(0, 1);              //设置LCD_PWREN有效,它用于打开LCD的电源
    10 19行    Lcd_EnvidOnOff(1);                  //使能LCD控制器输出信号
    lcdlib.c->Test_Lcd_Tft_8bit_240320()_1
        第16行设置所涉及的GPIO引脚用于LCD功能。
        第17行调用Tft_Lcd_Init函数初始化LCD控制器,即设置各个控制信号的时间特性、
    LCD显示模式、帧缓冲区的地址等,它是lcddrv.c中最复杂的函数,在后面会详细分析这个
    函数。
        进行第16、17行的初始化之后,只要打开lcd,帧缓冲区中的数据就会被LCD控制器
    自动地发送到LCD上去显示。打开操作由18、19行完成。
        第18行发出LCD_PWREN信号。对于有电源开关控制引脚的LCD,可以使用其打开过关
    闭LCD。LCD_PWREN信号的极性可以设置。
        第19行使能LCD控制器输出信号。这时,帧缓冲区中数据就开始在LCD上显示出来了。
        接下来就是按照设定的流程进行各类操作了,比如画线、清屏等,代码如下:
     1 Lcd_Palette8Bit_Init();    //初始化调色板
     2 ClearScr(0x0);             //清屏
     3 printf("[TFT 64K COLOR(16bpp) LCD TEST]
    ");
     4 
     5 printf("1. Press any key to draw line
    ");
     6 get();
     7 DrawLine(0  , 0  , 239, 0  , 0);    //颜色为DEMO256pal[0]
     8 DrawLine(0  , 0  , 0  , 319, 1);    //颜色为DEM0256pal[1]
     9 DrawLine(239, 0  , 239, 319, 2);    //... 
    10 DrawLine(0  , 319, 239, 319, 4); 
    11 DrawLine(0  , 0  , 239, 319, 8); 
    12 DrawLine(239, 0  , 0  , 319, 16); 
    13 DrawLine(120, 0  , 120, 319, 32); 
    14 DrawLine(0  , 160, 239, 160, 64); 
    15 
    16 printf("2. Press any key to draw circles
    ");
    17 getc();
    18 Mire();
    19 
    20 printf("3. Press any key to fill the screem with one color
    ");
    21 getc();
    22 ClearScr(128);    //输出单色图像,颜色值等于DEMO256pal[128]
    23 
    24 printf("4. Press any key to fill the screem by temporary palette
    ");
    25 getc();
    26 ClearScrWithTmpPlt(0x0000ff);        //输出单色图像,颜色为蓝色
    27 
    28 printf("5. Press any key to fill the screem by palette
    ");
    29 getc();
    30 DisableTmpPlt();                     //关闭临时调色板寄存器
    31 ChangePalette(0xffff00);             //改变整个调色板为黄色,输出单色图像
    32 
    33 printf("6. Press any key to stop the testing
    ");
    34 getc();
    35 Lcd_EnvidOnOff(0);
    36 }
    lcdlib.c->Test_Lcd_Tft_8bit_240320()_2
        将上面的函数分成3类:
    (1)清屏函数ClearScr、画线函数DrawLine,都是通过framebuffer.c中的PutPixel函数
    来设置帧缓冲区的数据,以像素为单位修改颜色来实现的。
    (2)Lcd_Palette8Bit_Init函数:设置调色板,ChangePalette函数:通过设置调色板来
    实现清屏功能,不涉及帧缓冲区,它在lcddrv.c中实现。
     (3)ClearScrWithTmpPlt函数:通过临时调色板寄存器来快速地输出单色的图像,也
    不涉及帧缓冲区,它在lcddrv.c中实现
        lcddrv.c、framebuffer.c文件中各个函数才是本实例的关键。可以认为lcddrv.c是对操作
    各寄存器的封装,framebuffer.c则是对操作图像数据的封装。先看lcddrv.c文件
    3.lcddrv.c
        这个文件中函数的重点在于Tft_Lcd_Init、Lcd_Palette8Bit_Init。
    (1)Lcd_Port_Init函数。
        设置所涉及的GPIO引脚用于LCD功能。
    (2)Tft_Lcd_Init函数。
        初始化LCD控制器,即设置各个控制信号的时间特性、LCD的显示模式、帧缓冲区的地址等。
        首先是对5个控制寄存器LCDCON1~5的设置,代码如下:
     1 /*
     2  *初始化LCD控制器
     3  *输入参数:
     4  *type:显示模式
     5  *   MODE_TFT_8BIT_240320:240*320  8bpp的TFT LCD
     6  *   MODE_TFT_16BIT_240320:240*320 16bpp的TFT LCD
     7  *   MODE_TFT_8BIT_640480:640*480  8bpp的TFT LCD
     8  *   MODE_TFT_16BIT_640480:640*480 16bpp的TFT LCD
     9  */
    10 void Tft_Lcd_Init(int type)
    11 {
    12     switch(type)
    13     {
    14         case MODE_TFT_8BIT_240320:
    15             /*
    16             *设置LCD控制器的控制寄存器LCDCON1~5
    17             *1.LCDCON1
    18             *    设置VCLK的频率:VCLK(Hz) = HCLK/[(CLKVAL+1) x 2]
    19             *    选择LCD类型:TFT LCD
    20             *    设置显示模式:8BPP
    21             *    先禁止LCD信号输出
    22             *2.LCDCON2/3/4
    23             *    设置控制信号的时间参数
    24             *    设置分辨率,即行数和列数
    25             *现在,可以根据公式算出显示器的分辨率
    26             *当HCLK = 100MHz时,
    27             *Frame Rate = 1/[{(VSPW+1) + (VBPD+1) + (LIINEVAL+1) + (VFPD+1)} x
    28             *                {(HSPW+1) + (HBPD+1) + (HFPD+1) + (HOZVAL+1)} x
    29             *                {(2x(CLKVAL+1)/(HCLK))}]
    30             *           = 60Hz
    31             *3.LCDCON5
    32             *    设置显示模式为8BPP时,调色板中的数据格式为5:6:5
    33             *    设置HSYNC、VSYNC脉冲的极性(这需要参考具体的LCD的接口信号):反转字节交换使能
    34             */
    35     LCDCON1 = (CLKVAL_TFT_240320 << 8) | (LCDTYPE_TFT << 5) | 
    36               (BPPMODE_8BPP << 1) | (ENVID_DISABLE << 0);
    37     LCDCON2 = (VBPD_240320 << 24) | (LINEVAL_TFT_240320 << 14) | 
    38               (VFPD_240320 << 6) | (VSPW_240320);
    39     LCDCON3 = (HBPD_240320 << 19) | (HOZVAL_TFT_240320 << 8) | (HFPD_240320);
    40     LCDCON4 = HSPW_240320;
    41     LCDCON5 = (FORMAT8BPP_565 << 11) | (HSYNC_INV << 9) | (VSYNC_INV << 8) | 
    42               (BSWP << 1);
    lcddrv.c->Tft_Lcd_Init()
        时间参数VSPW、VBPD、VFPD、HSPW、HBPD、HFPD、CLKVAL的设置可以
    从LCD数据手册了解到,或使用经验值,或自行调整,并根据上面的公式确认显示频
    率在60Hz左右或之上。
        接下来是地址寄存器LCDSADDR1~3的设置,请参考图13.7帧内存与视图的位置关
    系。在本程序中,帧内存与视图吻合,即图中的OFFSIZE为0,LCDBANK、LCDBASEU
    指向同一个地址(它们是同一个地址的不同位)。
        需要注意的是,8BPP的显示模式要用到调色板,帧缓冲区中的数据不是颜色值,而
    是调色板中的索引值,真正的颜色值在调色板中。
     1 行号
     2 78行 /* 
     3 79行  *设置LCD控制器的地址寄存器:LCDSADDR1~3
     4 80行  *帧内存与视口(view point)完全吻合
     5 81行  *图像数据格式如下(8BPP时,帧缓冲区中的数据为调色板中的索引值):
     6 82行  *                |--------- PAGEWIDTH ----------|
     7 83行  *      y/x  0    1    2       239
     8 84行  *       0   idx  idx  idx ... idx
     9 85行  *       1   idx  idx  idx ... idx
    10 86行  *1.LCDSADDR1
    11 87行  *    设置LCDBANK、LCDBASEU
    12 88行  *2.LCDSADDR2
    13 89行  *    设置LCDBASEL:帧缓冲区的结束地址A[21:1]
    14 90行  *3.LCDSADDR3
    15 91行  *    OFFSIZE等于0,PAGEWIDTH等于(240/2)
    16 92行  */
    17 93行    LCDSADDR1 = ((LCDFRAMEBUFFER >> 22) << 21) | LOWER21BITS (LCDFRAMEBUFFER >> 1);
    18 94行    LCDSADDR2 = LOWER21BITS((LCDFRAMEBUFFER+ 
    19 95行                (LINEVAL_TFT_240320 + 1) x (HOZVAL_TFT_240320 + 1) x 1) >> 1);
    20 96行    LCDSADDR3 = (0 << 11) | (LCD_XSIZE_TFT_240320/2);
    21 97行
    设置LCD控制器的地址寄存器
        第93行将帧缓冲区的开始地址写入LCDSADDR1寄存器。
        第94行先计算帧缓冲区的结束地址,再取其位[21:1]存入LCDSADDR2中。这个地址值
    在本实例中即是“LCDFRAMEBUFFER+320x240x1”,其中的“x1”表示在8BPP中一个像素
    使用1个字节表示(对于16BPP,就是“x2”)。
        在设置寄存器的最后,禁止临时调色板寄存器,现在还没用到它。
    行号
    98行 /*禁止临时调色板寄存器*/
    99行 TPAL = 0;
    100行
     最后,将显示模式的主要参数记录下来,在framebuffer.c中需要用到。 
    101行 fb_base_addr = LCDFRAMEBUFFER;
    102行 bpp = 8;
    103行 xsize = 240;
    104行 ysize = 320;
    105行
        其他显示模式的寄存器设置非常相似,不再赘述。
        需要说明的是,显示模式为8BPP时,LCDCON5中BSWAP位设为1,表示“字节交换
    使能”,这时帧缓冲区中的数据与屏幕上的像素位置关系如图13.6所示;
        显示模式为16BPP时,LCDCON5中HWSWAP位设为1,表示“半字交换使能”,这时
    帧缓冲区中的数据与屏幕上的像素位置关系如图13.5所示。它们都是“低地址的数据”对
    应“位置靠前”的像素。
    (3)Lcd_Palette8Bit_Init函数。
        设置调色板上的数据:调色板大小为256x16,而8BPP模式中每个像素的索引值占据8
    位,刚好有256个索引值。代码如下:
     1 行号
     2 296行 /*
     3 297行  *设置调色板
     4 298行  */
     5 299行 void Lcd_Palette8Bit_Init(void)
     6 300行 {
     7 301行    int i;
     8 302行    volatile unsigned int *palette;
     9 303行
    10 304行    LCDCON5 |= (FORMAT8BPP_565 << 11);        //设置调色板中数据格式为:5:6:5
    11 305行
    12 306行    palette = (volatile unsigned int *)PALETTE;
    13 307行    for(i = 0; i < 256; i++)
    14 308行        *palette++ = DEMO256pal[i];
    15 309行 }
    16 310行 
    Lcd_Palette8Bit_Init()
        调色板中用16BPP的格式表示颜色。
        第307、308行将数组DEMO256pal中数据写入调色板。这个数组中的数据没有
    什么特别之处,读者可以自行构造。
    (4)ChangePalette函数。
        以给定的颜色值填充整个调色板,代码如下:
     1 行号
     2 311行 /*
     3 312行  *改变调色板为一种颜色
     4 313行  *输入参数:
     5 314行  *    color:颜色值,格式为0xRRGGBB
     6 315行  */
     7 316行 void ChangePalette(UINT32 color)
     8 317行 {
     9 318行     int i;
    10 319行     unsigned char red, green, blue;
    11 320行     UINT32 *palette;
    12 321行 
    13 322行     palette = (UINT32 *)PALETTE;
    14 323行     for(i = 0; i < 256; i++)
    15 324行     {
    16 325行         red   = (color >> 19) & 0xff;
    17 326行         green = (color >> 10) & 0xff; 
    18 327行         blue  = (color >> 3)  & 0xff;
    19 328行         color = (red << 11) | (green << 5) | blue;    //格式:5:6:5
    20 329行 
    21 330行         while((LCDCON5 >> 16) == 2);                  //等待直到VSTATUS不为“有效”
    22 331行         *palette++ = color;
    23 332行     }
    24 333行 }
    25 334行 
    ChangePalette()
        第330行检测当前VSYNC信号的状态,如果它处于有效的状态,则等待。前面说过,
    读写调色板时,VSTATUS、HSTATUS不能处于有效状态。这里当VSTATUS不是“有效”
    状态时,HSTATUS也不可能是“有效”状态。
    (5)Lcd_PowerEnable函数。
        用于控制是否发出LCD_PWREN信号。对于有电源开关控制引脚的LCD,可以使用
    LCD_PWREN来打开或关闭LCD。LCD_PWREN信号的极性可以设置。代码如下:
     1 /*
     2  *设置是否输出LCD电源开关信号LCD_PWREN
     3  *输入参数:
     4  *    invpwren:0表示LCD_PWREN有效时为正常极性
     5  *             1表示................反转极性
     6  *    pwren   :0表示LCD_PWREN输出有效
     7  *             1表示LCD_PWREN输出无效
     8  */
     9 void Lcd_PowerEnable(int invpwren, int pwren)
    10 {
    11     GPGCON  = (GPGCON  & (~(3 << 8))) | (3 << 8);    //GPG4用于LCD_PWREN
    12     GPGUP   = (GPGUP   & (~(1 << 4))) | (1 << 4);    //禁止内部上拉
    13     
    14     LCDCON5 = (LCDCON5 & (~(1 << 5))) | (invpwren << 5);    //设置LCD_PWREN的极性:正常/反转
    15     LCDCON5 = (LCDCON5 & (~(1 << 3))) | (pwren    << 3);    //设置是否输出LCD_PWREN
    16 }
    Lcd_PowerEnable()
    (6)Lcd_EnvidOnOff函数
        用于控制是否使能LCD控制器输出各个LCD信号,当设置如控制寄存器、地址寄存器
    之后,即可调用此函数输出各个LCD信号,这样,帧缓冲区中的数据即发送给LCD。代码如下:
     1 /*
     2  *设置LCD控制器是否输出信号
     3  *输入参数:
     4  *onoff:
     5  *    0:关闭
     6  *    1:打开
     7  */
     8 void Lcd_EnvidOnOff(int onoff)
     9 {
    10     if(onoff == 1)
    11         LCDCON1 |= 1;          //ENVID ON
    12     else
    13         LCDCON1 &= 0x3fffe;    //ENVID OFF
    14 }
    Lcd_EnvidOnOff
    (7)ClearScrWithTmpPlt、DisableTmpPlt函数。
        参考13.13TPAL寄存器格式,ClearScrWithTmpPlt函数设置颜色值并使能TPAL寄
    存器,这使得LCD上显示单一颜色图像。DisableTmpPlt函数停止TPAL寄存器的功能,
    继续输出帧缓冲区的图像。它们的代码如下:
     1 /*
     2  *使用临时调色板寄存器输出单色图像
     3  *输入参数:
     4  *    color:颜色值,格式为0xRRGGBB
     5  */
     6 void ClearScrWithTmpPlt(UINT32 color)
     7 {
     8     TPAL = (1 << 24) | ((color & 0xffffff) << 0);
     9 }
    10 
    11 /*
    12  *停止使用临时调色板寄存器
    13  */
    14 void DisableTmpPlt(void)
    15 {
    16     TPAL = 0;
    17 }
    ClearScrWithTmpPlt()和DisableTmpPlt()
    4.framebuffer.c
        此文件有4个函数:画点PutPixel、画线DrawLine、绘制同心圆Mire、清屏ClearScr,
    后3个函数都是基于PutPixel函数实现的。画点函数时framebuffer.c文件的核心,它在
    帧缓冲区中找到给定坐标的像素的内存,然后修改它的值,代码如下:
     1 行号
     2 8行  extern unsigned int fb_base_addr;
     3 9行  extern unsigned int bpp;
     4 10行 extern unsigned int xsize;
     5 11行 extern unsigned int ysize;
     6 12行 
     7 13行 /*
     8 14行  *画点
     9 15行  *输入参数:
    10 16行  *    x、y:像素坐标
    11 17行  *    color:颜色值
    12 18行  *    对于16BPP:color的格式为0xAARRGGBB(AA = 透明度),
    13 19行  *    需要转换为5:6:5格式
    14 20行  *    对于8BPP:color为调色板中索引值,
    15 21行  *    其颜色取决于调色板中的数值
    16 22行  */
    17 23行 void PutPixel(UINT32 x, UINT32 y, UINT32 color)
    18 24行 {
    19 25行     UINT8 red, green, blue;
    20 26行     
    21 27行     switch(bpp){
    22 28行          case 16:
    23 29行             {
    24 30行             UINT16 *addr = (UINT16 *)fb_base_addr + (y * xsize + x);
    25 31行             red   = (color >> 19) & 0xff;
    26 32行             green = (color >> 10) & 0xff; 
    27 33行             blue  = (color >> 3)  & 0xff;
    28 34行             color = (red << 11) | (green << 5) | blue;    //格式:5:6:5
    29 35行             *addr = (UINT16)color;
    30 36行             break;
    31 37行              }
    32 38行 
    33 39行          case 8:
    34 40行             {
    35 41行             UINT8 *addr = (UINT8 *)fb_base_addr + (y * xsize + x);
    36 42行             *addr = (UINT8)color;
    37 43行             break;
    38 44行             }
    39 45行 
    40 46行          default:
    41 47行             break;
    42 48行     }
    43 49行 }
    44 50行 
    PutPixel
        第8~11行的4个变量在lcddrv.c中的Tft_Lcd_Init函数中设置,PutPixel函数根据它们
    来确定给定坐标的像素在帧缓冲区中的地址。
        对于16BPP模式,每个像素占2字节;对于8BPP模式,每个像素占1字节。
        对于16BPP模式,第31~34行从0xAARRGGBB格式的color变量中,提取8位红色值
    的高5位、8位绿色值的高6位、8位蓝色值的高5位组成5:6:5格式的16BPP颜色值。
        最后,第35、42行将颜色值(对于8BPP模式,为调色板的索引值)写入帧缓冲区中,
    这样,下一次显示时,新颜色即可显示出来。
    13.2.3 实例测试
        本程序在main函数中通过串口输出一个菜单,用于选择LCD的显示模式进行测试。
    实验步骤如下:
        (1)使用串口连接开发板和PC,打开PC上串口工具并设置为115200、8N1.
        (2)在LCD目录下执行make命令生成lcd可执行程序,烧入NAND Flash后运行。
        (3)在PC串口工具上,可以看到如下菜单:
                #### Test TFT LCD ####
                [1] TFT240320 8Bit
                [2] TFT240320 16Bit
                [3] TFT640480 8Bit
                [4] TFT640480 16Bit
                Enter your selection:
        (4)可以输入1、2、3或4,然后按照提示输入任意键可一步一步地观察到LCD中图像
    的变化。
        (5)最后又会出现第(3)步骤的菜单,可以再次选择。
    附:代码:
    链接: https://pan.baidu.com/s/1kV24a9L 密码: tfab
  • 相关阅读:
    Xah Lee Web 李杀网
    About Unixstickers
    Amazon.com: NEW VI AND VIM EDITOR KEYBOARD STICKER: Office Products
    Company Story | Vistaprint
    8月30号周五香港接单ING~~化妆品只加10元!!!!!!
    贝佳斯绿泥多久用一次?_百度知道
    贝佳斯绿泥_百度百科
    [PHP]利用MetaWeblog API实现XMLRPC功能
    The tempfile module
    20.23. xmlrpclib — XML-RPC client access — Python v2.7.5 documentation
  • 原文地址:https://www.cnblogs.com/sz189981/p/7722095.html
Copyright © 2020-2023  润新知