zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    我爱Java系列之---【SpringBoot打成war包部署】
    279. Perfect Squares
    矩阵dfs--走回路
    112. Path Sum
    542. 01 Matrix
    106. Construct Binary Tree from Inorder and Postorder Traversal
    105. Construct Binary Tree from Preorder and Inorder Traversal
    Invert Binary Tree
    563 Binary Tree Tilt
    145 Binary Tree Postorder Traversal
  • 原文地址:https://www.cnblogs.com/sz189981/p/7722095.html
Copyright © 2011-2022 走看看