arm9 lcd controller

2440的lcd控制器可以驱动 STN LCD 和 TFT LCD 本文以较为常见的 TFT LCD 来讲解使用方法:

基础知识:

首先, 我们来看下2440的 lcd controller 组成框图:

arm9 lcd controller_第1张图片

REGBANK: LCD控制器的寄存器组, 含有17个寄存器及一块256x16的调色板

LCDCDMA: LCD控制器的专用DMA通道, 可以自动从系统总线上获取图像数据, 显示图像时不需要cpu core的参与

TIMEGEN / LPC3600: 产生控制时序, 如: VSYNC, HSYNC, VCLK, VDEN, 而这些信号又与REGBANK中的LCDCON1/2/3/4的配置密切相关, 通过不同的配置产生不同的控制信号. 然后从VIDEO MUX中传递给液晶屏(LPC3600为STN屏专用)

VIDPRCS:  接收LCDCDMA 的数据, 然后转换为合适的数据格式, 比如 4 bit单扫 / 8ibt单扫 /4bit双扫, 然后由 VD[23:0]来显示


其次, 我们来分析一下lcd controller的时序:

arm9 lcd controller_第2张图片

VSYNC/VFRAME/STV:垂直同步信号(TFT)/帧同步信号(STN)/SEC TFT信号

HSYNC/VLINE/CPV: 水平同步信号(TFT)/行同步脉冲信号(STN)/SEC TFT信号

VCLK/LCD_HCLK:   像素时钟信号(TFT/STN)/SEC TFT信号

VD[23:0]:        LCD像素数据输出端口(TFT/STN/SEC TFT)

VDEN/VM/TP:      数据使能信号(TFT)/LCD驱动交流偏置信号(STN)/SEC TFT 信号

LEND/STH:        行结束信号(TFT)/SEC TFT信号

LCD_LPCOE:       SEC TFT OE信号

LCD_LPCREV:      SEC TFT REV信号

LCD_LPCREVB:     SEC TFT REVB信号

所有显示器显示图像的原理都是从上到下,从左到右的。这是什么意思呢?这么说吧,一副图像可以看做是一个矩形,由很多排列整齐的点一行一行组成,这些点称之为像素。那么这幅图在LCD上的显示原理就是:

A:显示指针从矩形左上角的第一行第一个点开始,一个点一个点的在LCD上显示,在上面的时序图上表示为VCLK,我们称之为像素时钟信号

B:当显示指针一直显示到矩形的右边就结束这一行,那么这一行的动作在上面的时序图中就称之为1 Line

C:接下来显示指针又回到矩形的左边从第二行开始显示,注意,显示指针在从第一行的右边回到第二行的左边是需要一定的时间的,我们称之为行切换

D:如此类推,显示指针就这样一行一行的显示至矩形的右下角才把一副图显示完成。行的显示在时序图上看就是HSYNC

E:然而,LCD要显示多个图片就要一幅一幅的切换, 那么这每一幅图像就称之为帧,在时序图上就表示为1 Frame,因此从时序图上可以看出1 Line只是1 Frame中的一行

F:同样的,在帧与帧切换之间也是需要一定的时间的,我们称之为帧切换,那么LCD整个显示的过程在时间线上看,就可表示为时序图上的VSYNC

上面时序图上各时钟延时参数的含义如下:(这些参数的值,LCD产生厂商会提供相应的数据手册)

VBPD(vertical back porch):表示在一帧图像开始时,垂直同步信号以后的无效的行数,对应驱动中的upper_margin

VFBD(vertical front porch):表示在一帧图像结束后,垂直同步信号以前的无效的行数,对应驱动中的lower_margin

VSPW(vertical sync pulse width):表示垂直同步脉冲的宽度,用行数计算,对应驱动中的vsync_len

HBPD(horizontal back porch):表示从水平同步信号开始到一行的有效数据开始之间的VCLK的个数,对应驱动中的left_margin

HFPD(horizontal front porth):表示一行的有效数据结束到下一个水平同步信号开始之间的VCLK的个数,对应驱动中的right_margin

HSPW(horizontal sync pulse width):表示水平同步信号的宽度,用VCLK计算,对应驱动中的hsync_len

arm9 lcd controller_第3张图片

使用LCD的步骤:

    Lcd_Port_Init();                     // 设置LCD引脚
    Tft_Lcd_Init(MODE_TFT_16BIT_240320); // 初始化LCD控制器, 这里配置了液晶的显示模式, 如: 分辨率 240x320 颜色深度 16bit
    Lcd_PowerEnable(0, 1);               // 设置LCD_PWREN有效,它用于打开LCD的电源
    Lcd_EnvidOnOff(1);                   // 使能LCD控制器输出信号
    ClearScr(0x0);                       // 清屏,黑色

一 设置引脚

    GPCUP   = 0xffffffff;   // 禁止内部上拉
    GPCCON  = 0xaaaaaaaa;   // GPIO管脚用于VD[7:0],LCDVF[2:0],VM,VFRAME,VLINE,VCLK,LEND
    GPDUP   = 0xffffffff;   // 禁止内部上拉
    GPDCON  = 0xaaaaaaaa;   // GPIO管脚用于VD[23:8]
    GPBCON  &= ~(GPB0_MSK); // Power enable pin
    GPBCON  |= GPB0_out;    // GPB0控制液晶的背光ic输出使能
    GPBDAT  &= ~(1<<0);	    // Power off
    printf("Initializing GPIO ports..........\n");

二 设置LCD控制器  (以 分辨率: 640x480 颜色深度: 16bit 液晶类型: TFT-LCD 颜色格式: 565 等为例)

(1) LCD控制寄存器

LCDCON1:

    #define CLKVAL_TFT_640480 	(1)
    #define LCDTYPE_TFT         0x3
    #define BPPMODE_16BPP       0xC
    #define ENVID_DISABLE       0
    LCDCON1 = (CLKVAL_TFT_640480<<8) | (LCDTYPE_TFT<<5) | (BPPMODE_16BPP<<1) | (ENVID_DISABLE<<0);
/*  设置VCLK的频率:VCLK(Hz) = HCLK/[(CLKVAL+1)x2]
    选择LCD类型: TFT LCD
    设置显示模式: 16BPP
    先禁止LCD信号输出            */

arm9 lcd controller_第4张图片

LCDCON2:

    #define VBPD_640480		((33-1)&0xff)
    #define LINEVAL_TFT_640480	(LCD_YSIZE_TFT_640480-1)
    #define VFPD_640480		((10-1)&0xff)
    #define VSPW_640480    	((2-1) &0x3f)
    LCDCON2 = (VBPD_640480<<24) | (LINEVAL_TFT_640480<<14) | (VFPD_640480<<6) | (VSPW_640480);

arm9 lcd controller_第5张图片

LCDCON3:

    #define HBPD_640480		((48-1)&0x7f)
    #define HOZVAL_TFT_640480	(LCD_XSIZE_TFT_640480-1)
    #define HFPD_640480		((16-1)&0xff)
    LCDCON3 = (HBPD_640480<<19) | (HOZVAL_TFT_640480<<8) | (HFPD_640480);

arm9 lcd controller_第6张图片

LCDCON4:

    #define HSPW_640480		((96-1)&0xff)
    LCDCON4 = HSPW_640480;

arm9 lcd controller_第7张图片

LCDCON5:

    #define FORMAT8BPP_565  1  
    #define HSYNC_INV       1
    #define VSYNC_INV       1
    #define HWSWP           1
    LCDCON5 = (FORMAT8BPP_565<<11) | (HSYNC_INV<<9) | (VSYNC_INV<<8) | (HWSWP<<1);
/*  16bpp 565
    设置HSYNC、VSYNC脉冲的极性(这需要参考具体LCD的接口信号): 反转
    半字(2字节)交换使能      */

arm9 lcd controller_第8张图片

arm9 lcd controller_第9张图片

(2)帧内存寄存器

LCDSADDR1:

    #define LCDFRAMEBUFFER 0x30400000
    #define LOWER21BITS(n)  ((n) & 0x1fffff)
    LCDSADDR1 = ((LCDFRAMEBUFFER>>22)<<21) | LOWER21BITS(LCDFRAMEBUFFER>>1);
/*  0x30400000的[30:22]值为LCDSADDR1[29:21]的值, 所以0x30400000>>22之后再左移21位
    0x30400000的[21:1]值为LCDSADDR1[20:0]的值, 所以0x30400000>>1 为应该配置的值, 这个值只保留低21位, 所以 又与 0x1fffff相与    */

arm9 lcd controller_第10张图片

LCDSADDR2:

    #define HOZVAL_TFT_640480	(LCD_XSIZE_TFT_640480-1)
    #define LINEVAL_TFT_640480	(LCD_YSIZE_TFT_640480-1)
    LCDSADDR2 = LOWER21BITS((LCDFRAMEBUFFER+(LINEVAL_TFT_640480+1)*(HOZVAL_TFT_640480+1)*2)>>1);
/*  这里是计算帧缓冲的结束地址, 本例中占用缓冲区大小为:  (LINEVAL_TFT_640480+1) * 640 * 480 * 2, 这里 *2 是因为16bpp, 如果是8bpp则应 *1. 这个大小再加上起始地址LCDFRAMEBUFFER就得到了结束地址 */

arm9 lcd controller_第11张图片

LCDSADDR3:

    #define LCD_XSIZE_TFT_640480 	(640)	
    #define LCD_YSIZE_TFT_640480 	(480)
    LCDSADDR3 = (0<<11) | (LCD_XSIZE_TFT_640480*2/2);

arm9 lcd controller_第12张图片

如果需要禁止调色板:

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

帧地址:

    unsigned int fb_base_addr;
    unsigned int bpp;
    unsigned int xsize;
    unsigned int ysize;
    fb_base_addr = LCDFRAMEBUFFER;
    bpp = 16;
    xsize = 640;
    ysize = 480;

三 打开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
}

四 开启控制器输出

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

五 写点函数

屏幕上任何写操作都是由写一个个的点来组合完成的, 写点函数如下:

/* 
 * 画点
 * 输入参数:
 *     x、y : 象素坐标
 *     color: 颜色值
 *         对于16BPP: color的格式为0xAARRGGBB (AA = 透明度),
 *     需要转换为5:6:5格式
 *         对于8BPP: color为调色板中的索引值,
 *     其颜色取决于调色板中的数值
 */
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);
            //这里fb_base_addr已经指向了帧内存首地址, 还有帧内存与视图虽然类比为窗口一样的形状, 但是在内存里只有线性结构, 就如同二维数组照样是线性存储的. 所以这里可以这样寻址每个点的内存中的位置
            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;
    }
}

你可能感兴趣的:(controller,LCD,2440)