JZ2440开发板-TFT LCD实验

本实验CPU:s3c2440

       LCD: 4.3寸  分辨率(Resolution)  480*272  TFT-LCD  型号AT043TN24 V.1

s3c2440 LCD控制器支持:

1/2/4/8bpp调色板显示模式,16bpp/24bpp非调色板显示模式

当选定了LCD型号后,硬件工程师做出电路板后,LCD模块接线确定,显示模式就确定了,如本实验使用8bpp调色板显示模式和16bpp非调色板显示模式,两种模式均为565像素格式

NOTE:

24bpp表示24bit per pixel  即每像素用24位表示,正好对应RRGGBB颜色值

24bpp内存数据格式为:

JZ2440开发板-TFT LCD实验_第1张图片

P1是位于内存地址000H的高位或地位的颜色值X,lcd控制器取像素值往数据线VD[0-23]发,其中RR->VD[16-23], GG->VD[8-15],BB->VD[0-7],然后可以第一个像素显示此颜色值X

16bpp

JZ2440开发板-TFT LCD实验_第2张图片

      显示器上像素由三部分红(R)/绿(G)/蓝(B)组成,称为三原色,每个颜色由8位表示,即每个颜色有256级,可以由此三原色构成其它的颜色,比如RGB(255,255,255)合成白色, color(AARRGGBB(AA表示透明度))为UINT32型,因此需要取出16位构成像素颜色值(如何取代码有介绍),然后将16位颜色值存到Framebuffer中

8bpp调色板模式

JZ2440开发板-TFT LCD实验_第3张图片

JZ2440开发板-TFT LCD实验_第4张图片

其中P1表示索引值index,范围为00H-FFH(15-4表也可看出),

16bpp/8bpp程序 结构:

JZ2440开发板-TFT LCD实验_第5张图片

Ⅰ.main.c提供菜单选择8bpp或16bpp显示模式:

main.c:

int main()
{
    char c;
    
    uart0_init();   // 波特率115200,8N1(8个数据位,无校验位,1个停止位)
    
    while (1)
    {
        printf("\r\n##### Test TFT LCD #####\r\n");
        printf("[1] TFT240320 8Bit\n\r");
        printf("[2] TFT240320 16Bit\n\r");
	/*
	*	author:xyc 添加支持480272 8Bit/16Bit
	*/
        printf("[3] TFT480272 8Bit\n\r");
        printf("[4] TFT480272 16Bit\n\r");
		
        printf("Enter your selection: ");

        c = getc();
        printf("%c\n\r", c);
        switch (c)
        {
            case '1':
            {
                Test_Lcd_Tft_8Bit_240320();
                break;
            }
            
            case '2':
            {
                Test_Lcd_Tft_16Bit_240320();
                break;
            }

            case '3':
            {
                Test_Lcd_Tft_8Bit_480272();
                break;
            }
            
            case '4':
            {
                Test_Lcd_Tft_16Bit_480272();
                break;
            }
            default: 
                break;
        }
    }
    
    return 0;
}

Ⅱ.lcdlib.c调用lcddrv.c中的 Lcd_Port_Init()
Tft_Lcd_Init(MODE_TFT_8BIT_480272)
Lcd_PowerEnable(0, 1)
Lcd_EnvidOnOff(1)
ClearScr(0x0)
DrawLine(0  , 0  , 479, 0  , 0)
ClearScr(128)
ClearScrWithTmpPlt(0x0000ff)
等等用于初始化LCD引脚,初始化LCD控制器,打开电源,视频数据输出使能和背光等

lcdlib.c:

16bpp:

/* 
 *	author:xyc  添加480272屏幕16bpp支持
 *     以480x272,16bpp的显示模式测试TFT LCD
 */
void Test_Lcd_Tft_16Bit_480272(void)
{
    Lcd_Port_Init();                     // 设置LCD引脚
    Tft_Lcd_Init(MODE_TFT_16BIT_480272); // 初始化LCD控制器
    Lcd_PowerEnable(0, 1);               // 设置LCD_PWREN有效,它用于打开LCD的电源
    Lcd_EnvidOnOff(1);                   // 使能LCD控制器输出信号

    ClearScr(0x0);  // 清屏,黑色
    printf("[TFT 64K COLOR(16bpp) LCD TEST]\n");

    printf("1. Press any key to draw line\n");
    getc();
    DrawLine(0  , 0  , 479, 0  , 0xff0000);     // 红色
    DrawLine(0  , 0  , 0  , 271, 0x00ff00);     // 绿色
    DrawLine(479, 0  , 479, 271, 0x0000ff);     // 蓝色
    DrawLine(0  , 271, 479, 271, 0xffffff);     // 白色
    DrawLine(0  , 0  , 479, 271, 0xffff00);     // 黄色
    DrawLine(479, 0  , 0  , 271, 0x8000ff);     // 紫色
    DrawLine(240, 0  , 240, 319, 0xe6e8fa);     // 银色
    DrawLine(0  , 136, 479, 136, 0xcd7f32);     // 金色

    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(0xff0000);             // 红色

    printf("4. Press any key to fill the screem by temporary palette\n");
    getc();
    ClearScrWithTmpPlt(0x0000ff);   // 蓝色

    printf("5. Press any key stop the testing\n");
    getc();
    Lcd_EnvidOnOff(0);
}

8bpp:

/* 
 *	author:xyc 添加480272屏幕支持
 * 以480x272,8bpp的显示模式测试TFT LCD
 */
void Test_Lcd_Tft_8Bit_480272(void)
{
    Lcd_Port_Init();                     // 设置LCD引脚
    Tft_Lcd_Init(MODE_TFT_8BIT_480272);  // 初始化LCD控制器
    Lcd_PowerEnable(0, 1);               // 设置LCD_PWREN有效,它用于打开LCD的电源
    Lcd_EnvidOnOff(1);                   // 使能LCD控制器输出信号,背光打开

    Lcd_Palette8Bit_Init();     // 初始化调色板
    ClearScr(0x0);              // 清屏
    printf("[TFT 64K COLOR(16bpp) LCD TEST]\n");

    printf("1. Press any key to draw line\n");
    getc();
	#if 0
    DrawLine(0  , 0  , 239, 0  , 0);    // 颜色为DEMO256pal[0]
    DrawLine(0  , 0  , 0  , 319, 1);    // 颜色为DEMO256pal[1]
    DrawLine(239, 0  , 239, 319, 2);    // ……
    DrawLine(0  , 319, 239, 319, 4);
    DrawLine(0  , 0  , 239, 319, 8);
    DrawLine(239, 0  , 0  , 319, 16);
    DrawLine(120, 0  , 120, 319, 32);
    DrawLine(0  , 160, 239, 160, 64);
	#endif
	
    DrawLine(0  , 0  , 479, 0  , 0);     // 颜色为DEMO256pal[0]
    DrawLine(0  , 0  , 0  , 271, 1);     // 颜色为DEMO256pal[1]
    DrawLine(479, 0  , 479, 271, 2);     
    DrawLine(0  , 271, 479, 271, 4);     
    DrawLine(0  , 0  , 479, 271, 8);     
    DrawLine(479, 0  , 0  , 271, 16);     
    DrawLine(240, 0  , 240, 319, 32);    
    DrawLine(0  , 136, 479, 136, 64);   

    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(128);  //  输出单色图像,颜色为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 stop the testing\n");
    getc();
    Lcd_EnvidOnOff(0);
}

1.1 lcddrv.c包含Lcd_Port_Init(),Tft_Lcd_Init(),Lcd_PowerEnable(),Lcd_EnvidOnOff(),Lcd_Palette8Bit_Init()函数的定义实现:

1.1.1 Lcd_Port_Init()设置GPC GPD端口为LCD控制信号和LCD视频数据等端口:

/*
 * 初始化用于LCD的引脚
 */
void Lcd_Port_Init(void)
{
    GPCUP   = 0xffffffff;   // 禁止内部上拉
    GPCCON  = 0xaaaaaaaa;   // GPIO管脚用于VD[7:0],LCDVF[2:0],VM,VFRAME,VLINE,VCLK,LEND 
    GPDUP   = 0xffffffff;   // 禁止内部上拉
    GPDCON  = 0xaaaaaaaa;   // GPIO管脚用于VD[23:8]
    /*author:xyc 背光关闭GPB0端口为LED背光口,而不是电源,所以注释有误*/
  	GPBCON &= ~(GPB0_MSK);  // Power enable pin  
  	GPBCON |= GPB0_out;
    GPBDAT &= ~(1<<0);			// Power off
    printf("Initializing GPIO ports..........\n");
}

1.1.2 Tft_Lcd_Init()设置LCD控制器时序,帧内存地址等等:

/*
 * 初始化LCD控制器
 * 输入参数:
 * type: 显示模式
 *      MODE_TFT_8BIT_240320  : 240*320 8bpp的TFT LCD
 *      MODE_TFT_16BIT_240320 : 240*320 16bpp的TFT LCD
 *      MODE_TFT_8BIT_480272  : 480*272 8bpp的TFT LCD
 *      MODE_TFT_16BIT_480272 : 480*272 16bpp的TFT LCD
 */
void Tft_Lcd_Init(int type)
{
    switch(type)
    {
		/*
		*	author: xyc 添加480x272屏幕8Bpp支持
		*/
    case MODE_TFT_8BIT_480272:
        /* 
         * 设置LCD控制器的控制寄存器LCDCON1~5
         * 1. LCDCON1:
         *    设置VCLK的频率:VCLK(Hz) = HCLK/[(CLKVAL+1)x2]
         *    选择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 = (4<<8) | (LCDTYPE_TFT<<5) | \
                  (BPPMODE_8BPP<<1) | (ENVID_DISABLE<<0);
        LCDCON2 = (1<<24) | (271<<14) | \
                  (1<<6) | (1);
        LCDCON3 = (1<<19) | (479<<8) | (1);
        LCDCON4 = 40;
        LCDCON5 = (FORMAT8BPP_565<<11) | (HSYNC_INV<<9) | (VSYNC_INV<<8) | \
                  (1<<1);

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

        /* 禁止临时调色板寄存器 */
        TPAL = 0;

        fb_base_addr = LCDFRAMEBUFFER;
        bpp = 8;
        xsize = 480;
        ysize = 272;
        
        break;

    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
         * 3. LCDCON5:
         *    设置显示模式为16BPP时的数据格式: 5:6:5
         *    设置HSYNC、VSYNC脉冲的极性(这需要参考具体LCD的接口信号): 反转
         *    半字(2字节)交换使能
         */
         #if 0
        LCDCON1 = (CLKVAL_TFT_240320<<8) | (LCDTYPE_TFT<<5) | \
                  (BPPMODE_16BPP<<1) | (ENVID_DISABLE<<0);
        LCDCON2 = (VBPD_240320<<24) | (LINEVAL_TFT_240320<<14) | \
                  (VFPD_240320<<6) | (VSPW_240320);
        LCDCON3 = (HBPD_240320<<19) | (HOZVAL_TFT_240320<<8) | (HFPD_240320);
        LCDCON4 = HSPW_240320;
        LCDCON5 = (FORMAT8BPP_565<<11) | (HSYNC_INV<<9) | (VSYNC_INV<<8) | \
                  (HWSWP<<1);/*这里有疑问*/
	#endif
	
        LCDCON1 = (4<<8) | (LCDTYPE_TFT<<5) | \
                  (BPPMODE_16BPP<<1) | (ENVID_DISABLE<<0);
        LCDCON2 = (1<<24) | (271<<14) | \
                  (1<<6) | (1);
        LCDCON3 = (1<<19) | (479<<8) | (1);
        LCDCON4 = 40;
        LCDCON5 = (FORMAT8BPP_565<<11) | (HSYNC_INV<<9) | (VSYNC_INV<<8) | \
                  (1<<0);
        /*
         * 设置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等于(240*2/2)
         */
        LCDSADDR1 = ((LCDFRAMEBUFFER>>22)<<21) | LOWER21BITS(LCDFRAMEBUFFER>>1);
        LCDSADDR2 = LOWER21BITS((LCDFRAMEBUFFER+ \
                    272*480*2)>>1);
        LCDSADDR3 = (0<<11) | (480*2/2);

        /* 禁止临时调色板寄存器 */
        TPAL = 0;

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

        break;

    default:
        break;
    }   
}

  1.1.3 Lcd_PowerEnable(0, 1),LCD电源打开,电压为正常极性:

/*
 * 设置是否输出LCD电源开关信号LCD_PWREN
 * 输入参数:
 *     invpwren: 0 - LCD_PWREN有效时为正常极性
 *               1 - LCD_PWREN有效时为反转极性
 *     pwren:    0 - LCD_PWREN输出有效
 *               1 - LCD_PWREN输出无效
 */
void Lcd_PowerEnable(int invpwren, int pwren)
{
    GPGCON = (GPGCON & (~(3<<8))) | (3<<8);   // GPG4用作LCD_PWREN
    GPGUP  = (GPGUP & (~(1<<4))) | (1<<4);    // 禁止内部上拉    
        
    LCDCON5 = (LCDCON5 & (~(1<<5))) | (invpwren<<5);  // 设置LCD_PWREN的极性: 正常/反转
    LCDCON5 = (LCDCON5 & (~(1<<3))) | (pwren<<3);     // 设置是否输出LCD_PWREN
}    

1.1.4 Lcd_EnvidOnOff(1),视频数据输出打开,背光打开:

/*
 * 设置LCD控制器是否输出信号
 * 输入参数:
 * onoff: 
 *      0 : 关闭
 *      1 : 打开
 */
void Lcd_EnvidOnOff(int onoff)
{
    if (onoff == 1)
    {
        LCDCON1 |= 1;         // ENVID ON
        /*author:xyc 背光打开,注释有误*/
		GPBDAT |= (1<<0);			// Power on
    }
    else
    {
        LCDCON1 &= 0x3fffe;  // ENVID Off
	    GPBDAT &= ~(1<<0);	 // Power off
    }
}    

1.1.5 Lcd_Palette8Bit_Init()初始化调色板

对于8bpp显示模式需要设置调色板,步骤如下:

①先定义含256个元素的16bpp全局数组;

static const unsigned short DEMO256pal[255]={0x001f,....};

初始化调色板( *palette++ = DEMO256pal[i],由表格15-4得出),由Lcd_Palette8Bit_Init()函数实现:

/*
 * 设置调色板内存
 */
void Lcd_Palette8Bit_Init(void)
{
    int i;  
    volatile unsigned int *palette;

	LCDCON1 &= ~0x01;	// stop lcd controller
    
    LCDCON5 |= (FORMAT8BPP_565<<11); // 设置调色板中数据格式为5:6:5

    palette = (volatile unsigned int *)PALETTE;
    for (i = 0; i < 256; i++)
	/*
	*	author:xyc 这里 DEMO256pal[i]数据为2字节短整形,而palette指向整形
	*	是这样吧((unsigned short *)palettle)++ =  DEMO256pal[i];  
	*	我理解有误,详见韦东山完全开发手册P220表格13.3
	*/
        *palette++ = DEMO256pal[i];  

	LCDCON1 |= 0x01;	// re-enable lcd controller
}

1.1.6 ClearScr(0x0)清屏/划线/画圆等

画线或清屏时:

 DrawLine(0  , 0  , 239, 0  , 0);    // 颜色为DEMO256pal[0]

ClearScr(128);  //  输出单色图像,颜色为DEMO256pal[128] ,128为索引值

 Framebuffer.c:  

/* 
 * 将屏幕清成单色
 * 输入参数:
 *     color: 颜色值
 *         对于16BPP: color的格式为0xAARRGGBB (AA = 透明度),
 *     需要转换为5:6:5格式
 *         对于8BPP: color为调色板中的索引值,
 *     其颜色取决于调色板中的数值
 */
void ClearScr(UINT32 color)
{   
    UINT32 x,y;
    
    for (y = 0; y < ysize; y++)
        for (x = 0; x < xsize; x++)
            PutPixel(x, y, color);
}

对于8bpp,ClearScr()循环调用PutPixel()函数写480*272个索引到基址为fb_base_addr的framebuffer内存中:

对于16bpp,ClearScr()循环调用PutPixel()函数写480*272个16bit像素颜色值(5:6:5格式)到基址为fb_base_addr的framebuffer内存中:

void PutPixel(UINT32 x, UINT32 y, UINT32 color)
{
    UINT8 red,green,blue;

    switch (bpp){
        case 16:
        {
            UINT16 *addr = (UINT16 *)fb_base_addr + (y * xsize + x);
            red   = (color >> 19) & 0x1f;
            green = (color >> 10) & 0x3f;
            blue  = (color >>  3) & 0x1f;
            color = (red << 11) | (green << 5) | blue; // 格式5:6:5
            *addr = (UINT16) color;
            break;
        }
        
        case 8:
        {
            UINT8 *addr = (UINT8 *)fb_base_addr + (y * xsize + x);
            *addr = (UINT8) color;
            break;
        }

        default:
            break;
    }
}

若case 8这个选项 ,这3行表示将索引写入FrameBuffer内存处,FrameBuffer基地址为 ((UINT8 *)fb_base_addr),LCD发VD[3-7]/VD[10-15]/VD[19-23]视频数据时根据索引index找到调色板内存对应像素颜色值(16位)发个数据线VD


调试时发现:单色清屏时,1帧数据拖影严重,可以打开Icache或者提高CPU频率(HCLK不变)

你可能感兴趣的:(嵌入式系统)