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的代码很简单,其主体如下:
 c = getc();
printf("%c\n\r", c);
switch(c)
{
case '':
{
Test_Lcd_Tft_8Bit_240320();
break;
} case '':
{
Test_Lcd_Tft_16Bit_240320();
break;
} case '':
{
Test_Lcd_Tft_8Bit_640480();
break;
} case '':
{
Test_Lcd_Tft_16Bit_640480();
break;
}
}

main.c主体代码

    它根据串口的输入选择是以哪种显示模式操作LCD,所调用的4个函数都在lcdlib.c中实现。
2.lcdlib.c
    8BPP模式将用到调色板,其操作比16BPP模式稍复杂,但大部分仍相似。下面以
Test_Lcd_Tft_8Bit_240320为例进行说明。
 行号
11行/*
12行 *以240x320、8BPP的显示模式测试TFT LCD
13行 */
14行void Test_Lcd_Tft_8Bit_240320(void)
15行{
16行 Lcd_Port_Init(); //设置LCD引脚
17行 Tft_Lcd_Init(MODE_TFT_8BIT_240320); //初始化LCD控制器
18行 Lcd_PowerEnable(, ); //设置LCD_PWREN有效,它用于打开LCD的电源
19行 Lcd_EnvidOnOff(); //使能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上显示出来了。
    接下来就是按照设定的流程进行各类操作了,比如画线、清屏等,代码如下:
 Lcd_Palette8Bit_Init();    //初始化调色板
ClearScr(0x0); //清屏
printf("[TFT 64K COLOR(16bpp) LCD TEST]\n"); printf("1. Press any key to draw line\n");
get();
DrawLine( , , , , ); //颜色为DEMO256pal[0]
DrawLine( , , , , ); //颜色为DEM0256pal[1]
DrawLine(, , , , ); //...
DrawLine( , , , , );
DrawLine( , , , , );
DrawLine(, , , , );
DrawLine(, , , , );
DrawLine( , , , , ); printf("2. Press any key to draw circles\n");
getc();
Mire(); printf("3. Press any key to fill the screem with one color\n");
getc();
ClearScr(); //输出单色图像,颜色值等于DEMO256pal[128] printf("4. Press any key to fill the screem by temporary palette\n");
getc();
ClearScrWithTmpPlt(0x0000ff); //输出单色图像,颜色为蓝色 printf("5. Press any key to fill the screem by palette\n");
getc();
DisableTmpPlt(); //关闭临时调色板寄存器
ChangePalette(0xffff00); //改变整个调色板为黄色,输出单色图像 printf("6. Press any key to stop the testing\n");
getc();
Lcd_EnvidOnOff();
}

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的设置,代码如下:
 /*
*初始化LCD控制器
*输入参数:
*type:显示模式
* MODE_TFT_8BIT_240320:240*320 8bpp的TFT LCD
* MODE_TFT_16BIT_240320:240*320 16bpp的TFT LCD
* MODE_TFT_8BIT_640480:640*480 8bpp的TFT LCD
* MODE_TFT_16BIT_640480:640*480 16bpp的TFT LCD
*/
void Tft_Lcd_Init(int type)
{
switch(type)
{
case MODE_TFT_8BIT_240320:
/*
*设置LCD控制器的控制寄存器LCDCON1~5
*1.LCDCON1
* 设置VCLK的频率:VCLK(Hz) = HCLK/[(CLKVAL+1) x 2]
* 选择LCD类型:TFT LCD
* 设置显示模式:8BPP
* 先禁止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
*3.LCDCON5
* 设置显示模式为8BPP时,调色板中的数据格式为5:6:5
* 设置HSYNC、VSYNC脉冲的极性(这需要参考具体的LCD的接口信号):反转字节交换使能
*/
LCDCON1 = (CLKVAL_TFT_240320 << ) | (LCDTYPE_TFT << ) | \
(BPPMODE_8BPP << ) | (ENVID_DISABLE << );
LCDCON2 = (VBPD_240320 << ) | (LINEVAL_TFT_240320 << ) | \
(VFPD_240320 << ) | (VSPW_240320);
LCDCON3 = (HBPD_240320 << ) | (HOZVAL_TFT_240320 << ) | (HFPD_240320);
LCDCON4 = HSPW_240320;
LCDCON5 = (FORMAT8BPP_565 << ) | (HSYNC_INV << ) | (VSYNC_INV << ) | \
(BSWP << );

lcddrv.c->Tft_Lcd_Init()

    时间参数VSPW、VBPD、VFPD、HSPW、HBPD、HFPD、CLKVAL的设置可以
从LCD数据手册了解到,或使用经验值,或自行调整,并根据上面的公式确认显示频
率在60Hz左右或之上。
    接下来是地址寄存器LCDSADDR1~3的设置,请参考图13.7帧内存与视图的位置关
系。在本程序中,帧内存与视图吻合,即图中的OFFSIZE为0,LCDBANK、LCDBASEU
指向同一个地址(它们是同一个地址的不同位)。
    需要注意的是,8BPP的显示模式要用到调色板,帧缓冲区中的数据不是颜色值,而
是调色板中的索引值,真正的颜色值在调色板中。
 行号
78行 /*
79行 *设置LCD控制器的地址寄存器:LCDSADDR1~3
80行 *帧内存与视口(view point)完全吻合
81行 *图像数据格式如下(8BPP时,帧缓冲区中的数据为调色板中的索引值):
82行 * |--------- PAGEWIDTH ----------|
83行 * y/x 0 1 2 239
84行 * 0 idx idx idx ... idx
85行 * 1 idx idx idx ... idx
86行 *1.LCDSADDR1
87行 * 设置LCDBANK、LCDBASEU
88行 *2.LCDSADDR2
89行 * 设置LCDBASEL:帧缓冲区的结束地址A[21:1]
90行 *3.LCDSADDR3
91行 * OFFSIZE等于0,PAGEWIDTH等于(240/2)
92行 */
93行 LCDSADDR1 = ((LCDFRAMEBUFFER >> ) << ) | LOWER21BITS (LCDFRAMEBUFFER >> );
94行 LCDSADDR2 = LOWER21BITS((LCDFRAMEBUFFER+ \
95行 (LINEVAL_TFT_240320 + ) x (HOZVAL_TFT_240320 + ) x ) >> );
96行 LCDSADDR3 = ( << ) | (LCD_XSIZE_TFT_240320/);
97行

设置LCD控制器的地址寄存器

    第93行将帧缓冲区的开始地址写入LCDSADDR1寄存器。
    第94行先计算帧缓冲区的结束地址,再取其位[21:1]存入LCDSADDR2中。这个地址值
在本实例中即是“LCDFRAMEBUFFER+320x240x1”,其中的“x1”表示在8BPP中一个像素
使用1个字节表示(对于16BPP,就是“x2”)。
    在设置寄存器的最后,禁止临时调色板寄存器,现在还没用到它。
行号
98行 /*禁止临时调色板寄存器*/
99行 TPAL = ;
100行
 最后,将显示模式的主要参数记录下来,在framebuffer.c中需要用到。 
101行 fb_base_addr = LCDFRAMEBUFFER;
102行 bpp = ;
103行 xsize = ;
104行 ysize = ;
105行
    其他显示模式的寄存器设置非常相似,不再赘述。
    需要说明的是,显示模式为8BPP时,LCDCON5中BSWAP位设为1,表示“字节交换
使能”,这时帧缓冲区中的数据与屏幕上的像素位置关系如图13.6所示;
    显示模式为16BPP时,LCDCON5中HWSWAP位设为1,表示“半字交换使能”,这时
帧缓冲区中的数据与屏幕上的像素位置关系如图13.5所示。它们都是“低地址的数据”对
应“位置靠前”的像素。
(3)Lcd_Palette8Bit_Init函数。
    设置调色板上的数据:调色板大小为256x16,而8BPP模式中每个像素的索引值占据8
位,刚好有256个索引值。代码如下:
 行号
296行 /*
297行 *设置调色板
298行 */
299行 void Lcd_Palette8Bit_Init(void)
300行 {
301行 int i;
302行 volatile unsigned int *palette;
303行
304行 LCDCON5 |= (FORMAT8BPP_565 << ); //设置调色板中数据格式为:5:6:5
305行
306行 palette = (volatile unsigned int *)PALETTE;
307行 for(i = ; i < ; i++)
308行 *palette++ = DEMO256pal[i];
309行 }
310行

Lcd_Palette8Bit_Init()

    调色板中用16BPP的格式表示颜色。
    第307、308行将数组DEMO256pal中数据写入调色板。这个数组中的数据没有
什么特别之处,读者可以自行构造。

(4)ChangePalette函数。

    以给定的颜色值填充整个调色板,代码如下:
 行号
311行 /*
312行 *改变调色板为一种颜色
313行 *输入参数:
314行 * color:颜色值,格式为0xRRGGBB
315行 */
316行 void ChangePalette(UINT32 color)
317行 {
318行 int i;
319行 unsigned char red, green, blue;
320行 UINT32 *palette;
321行
322行 palette = (UINT32 *)PALETTE;
323行 for(i = ; i < ; i++)
324行 {
325行 red = (color >> ) & 0xff;
326行 green = (color >> ) & 0xff;
327行 blue = (color >> ) & 0xff;
328行 color = (red << ) | (green << ) | blue; //格式:5:6:5
329行
330行 while((LCDCON5 >> ) == ); //等待直到VSTATUS不为“有效”
331行 *palette++ = color;
332行 }
333行 }
334行

ChangePalette()

    第330行检测当前VSYNC信号的状态,如果它处于有效的状态,则等待。前面说过,
读写调色板时,VSTATUS、HSTATUS不能处于有效状态。这里当VSTATUS不是“有效”
状态时,HSTATUS也不可能是“有效”状态。
(5)Lcd_PowerEnable函数。
    用于控制是否发出LCD_PWREN信号。对于有电源开关控制引脚的LCD,可以使用
LCD_PWREN来打开或关闭LCD。LCD_PWREN信号的极性可以设置。代码如下:
 /*
*设置是否输出LCD电源开关信号LCD_PWREN
*输入参数:
* invpwren:0表示LCD_PWREN有效时为正常极性
* 1表示................反转极性
* pwren :0表示LCD_PWREN输出有效
* 1表示LCD_PWREN输出无效
*/
void Lcd_PowerEnable(int invpwren, int pwren)
{
GPGCON = (GPGCON & (~( << ))) | ( << ); //GPG4用于LCD_PWREN
GPGUP = (GPGUP & (~( << ))) | ( << ); //禁止内部上拉 LCDCON5 = (LCDCON5 & (~( << ))) | (invpwren << ); //设置LCD_PWREN的极性:正常/反转
LCDCON5 = (LCDCON5 & (~( << ))) | (pwren << ); //设置是否输出LCD_PWREN
}

Lcd_PowerEnable()

(6)Lcd_EnvidOnOff函数
    用于控制是否使能LCD控制器输出各个LCD信号,当设置如控制寄存器、地址寄存器
之后,即可调用此函数输出各个LCD信号,这样,帧缓冲区中的数据即发送给LCD。代码如下:
 /*
*设置LCD控制器是否输出信号
*输入参数:
*onoff:
* 0:关闭
* 1:打开
*/
void Lcd_EnvidOnOff(int onoff)
{
if(onoff == )
LCDCON1 |= ; //ENVID ON
else
LCDCON1 &= 0x3fffe; //ENVID OFF
}

Lcd_EnvidOnOff

(7)ClearScrWithTmpPlt、DisableTmpPlt函数。
    参考13.13TPAL寄存器格式,ClearScrWithTmpPlt函数设置颜色值并使能TPAL寄
存器,这使得LCD上显示单一颜色图像。DisableTmpPlt函数停止TPAL寄存器的功能,
继续输出帧缓冲区的图像。它们的代码如下:
 /*
*使用临时调色板寄存器输出单色图像
*输入参数:
* color:颜色值,格式为0xRRGGBB
*/
void ClearScrWithTmpPlt(UINT32 color)
{
TPAL = ( << ) | ((color & 0xffffff) << );
} /*
*停止使用临时调色板寄存器
*/
void DisableTmpPlt(void)
{
TPAL = ;
}

ClearScrWithTmpPlt()和DisableTmpPlt()

4.framebuffer.c
    此文件有4个函数:画点PutPixel、画线DrawLine、绘制同心圆Mire、清屏ClearScr,
后3个函数都是基于PutPixel函数实现的。画点函数时framebuffer.c文件的核心,它在
帧缓冲区中找到给定坐标的像素的内存,然后修改它的值,代码如下:
 行号
8行 extern unsigned int fb_base_addr;
9行 extern unsigned int bpp;
10行 extern unsigned int xsize;
11行 extern unsigned int ysize;
12行
13行 /*
14行 *画点
15行 *输入参数:
16行 * x、y:像素坐标
17行 * color:颜色值
18行 * 对于16BPP:color的格式为0xAARRGGBB(AA = 透明度),
19行 * 需要转换为5:6:5格式
20行 * 对于8BPP:color为调色板中索引值,
21行 * 其颜色取决于调色板中的数值
22行 */
23行 void PutPixel(UINT32 x, UINT32 y, UINT32 color)
24行 {
25行 UINT8 red, green, blue;
26行
27行 switch(bpp){
28行 case :
29行 {
30行 UINT16 *addr = (UINT16 *)fb_base_addr + (y * xsize + x);
31行 red = (color >> ) & 0xff;
32行 green = (color >> ) & 0xff;
33行 blue = (color >> ) & 0xff;
34行 color = (red << ) | (green << ) | blue; //格式:5:6:5
35行 *addr = (UINT16)color;
36行 break;
37行 }
38行
39行 case :
40行 {
41行 UINT8 *addr = (UINT8 *)fb_base_addr + (y * xsize + x);
42行 *addr = (UINT8)color;
43行 break;
44行 }
45行
46行 default:
47行 break;
48行 }
49行 }
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

JZ2440 裸机驱动 第13章 LCD控制器(2)的更多相关文章

  1. JZ2440 裸机驱动 第13章 LCD控制器(1)

    本章目标  了解LCD显示器的接口及时序: 掌握S3C2410/S3C2440 LCD控制器的使用方法: 了解帧缓冲区的概念,掌握如何设置帧缓冲区来显示图像: 13.1 LCD和LCD控制器 13.1 ...

  2. JZ2440 裸机驱动 第6章 存储控制器

    本章目标:     了解S3C2410/S3C2440地址空间的布局     掌握如何通过总线形式访问扩展的外设,比如内存.NOR Flash.网卡等 ························ ...

  3. JZ2440 裸机驱动 第10章 系统时钟和定时器

    本章目标      了解S3C2410/S3C2440的时钟体系结构     掌握通过设置MPLL改变系统时钟的方法     掌握在不同的频率下设置存储控制器的方法     掌握PWM定时器的用法   ...

  4. JZ2440 裸机驱动 第8章 NAND Flash控制器

    本章目标  了解NAND Flash 芯片的接口 掌握通过NAND Flash控制器访问NAND Flash的方法 8.1 NAND Flash介绍和NAND Flash控制器使用     NAND ...

  5. JZ2440 裸机驱动 第14章 ADC和触摸屏接口

    本章目标:     了解S3C2410/S3C2440和触摸屏的结构:     了解电阻触摸屏的工作原理和等效电路图:     了解S3C2410/S3C2440触摸屏控制器的多种工作模式:     ...

  6. JZ2440 裸机驱动 第12章 I2C接口

    本章目标: 了解I2C总线协议: 掌握S3C2410/S3C2440中I2C接口的使用方法: 12.1 I2C总线协议及硬件介绍 12.1.1 I2C总线协议 1 I2C总线的概念 2 I2C总线的信 ...

  7. JZ2440 裸机驱动 第7章 内存管理单元MMU

    本章目标:     了解虚拟地址和物理地址的关系:     掌握如何通过设置MMU来控制虚拟地址到物理地址的转化:     了解MMU的内存访问权限机制:     了解TLB.Cache.Write ...

  8. JZ2440 裸机驱动 第9章 中断体系结构

    本章目标:     了解ARM体系CPU的7种工作模式     了解S3C2410/S3C2440中断体系结构     掌握S3C2410/S3C2440的中断服务程序的编写方法 9.1 S3C241 ...

  9. JZ2440 裸机驱动 第5章 GPIO接口

    本章目标:     掌握嵌入式开发的步骤:编程.编译.烧写程序.运行     通过GPIO的操作了解软件如何控制硬件 5.1 GPIO硬件介绍     S3C2440A有130个多功能输入/输出口引脚 ...

随机推荐

  1. javascript--- hasOwnProperty、instanceof 、typeof的区别

    typeof 作用:用来判断变量的类型 返回值: string  只有以下几种:number.boolean.string.object.undefined.function[很容易漏掉这个] 形式: ...

  2. code format using astyle

    script context : #! /bin/bash for f in $(find . -name '*.c' -or -name '*.cpp' -or -name '*.h' -type ...

  3. sgu106.The equation 拓展欧几里得 难度:0

    106. The equation time limit per test: 0.25 sec. memory limit per test: 4096 KB There is an equation ...

  4. vue-router如何做历史返回提示?

    获取vue-router的上一个页面是否存在或者是否是自己需要返回的地址,可以使用vue-router的的声明周期函数,有三种模式: 第一种.使用全局函数beforeEach,直接来获取form.pa ...

  5. selenium(二)查找定位目标 ID XPATH CSS 操作目标

    简介: 我们只打开一个页面是没有什么意义的. 尽管你可以get很多次. selenium可以做的更多,比如找到百度的搜索框,输入我们要搜索的内容,再用鼠标点击搜索按钮,再把搜索结果提取出来…… 这篇文 ...

  6. 迁移HTML5移动项目到PhoneGap

    MyEclipse开年钜惠 在线购买低至75折!立即开抢>> [MyEclipse最新版下载] 一.创建一个新的PhoneGap应用程序项目 PhoneGap应用程序项目的结构与HTML5 ...

  7. 26个你不知道的Python技巧

    Python是目前世界上最流行的编程语言之一.因为: 1.它容易学习 2.它用途超广 3.它有非常多的开源支持(大量的模块和库) 不好意思,优达菌又啰嗦了. 本文作者 Peter Gleeson 是一 ...

  8. MySQL Performance Tuning: Tips, Scripts and Tools

    With MySQL, common configuration mistakes can cause serious performance problems. In fact, if you mi ...

  9. Eclipse SVN 使用教程

    Eclipse SVN 使用教程 做好以上的准备后打开Eclipse编译器,点击编译器右上角的Open Perspective 打开SVN资源库界面,新建一个资源库 选择资源库的位置,这里我们就用刚才 ...

  10. [转]一种让超大banner图片不拉伸、全屏宽、居中显示的方法

    现在很多网站的Banner图片都是全屏宽度的,这样的网站看起来显得很大气.这种Banner一般都是做一张很大的图片,然后在不同分辨率下都是显示图片的中间部分.实现方法如下: <html> ...