1-9、ARM开发步步深入之LCD入门

实验目的:通过串口选择实现LCD画线、画圆等操作借此掌握S3C2410的LCD控制器使用。

实验环境及说明:恒颐S3C2410开发板H2410。H24X0E扩展板外接夏普3.5英寸LQ035Q7DB02 LCD屏。

实验思路:开发板上电启动后,自动将NandFlash开始的4K数据复制到SRAM中,然后跳转到0地址开始执行。关闭看门狗、初始化SDRAM及NandFlash控制器、设置MPLL来改变FCLK、HCLK、PCLK的值,设置堆栈,复制4KB后的16KB数据到SDRAM,之后进入main函数中进行LCD各种画线、画圆等的操作测试。

知识掌握:LCD控制器。
★LCD(Liquid Crystal Display,液晶显示器)概述:可分为依驱动方式之静态驱动(Static)、单纯矩阵驱动(Simple Matrix)以及主动矩阵驱动(Active Matrix)三种。而其中单纯矩阵型又是俗称的被动式(Passive),可分为扭转向列型(Twisted Nematic,简称TN)和超扭转式向列型(Super Twisted Nematic,简称STN)两种;而主动矩阵型则以薄膜式晶体管型(Thin Film Transistor,简称TFT)为目前主流。
★S3C2410内置LCD控制器详解:一块LCD屏显示图像,不但需要LCD驱动器,还需要有相应的LCD控制器。通常LCD驱动器会以COF/COG的形式与LCD玻璃基板制做在一起,而LCD控制器则有外部电路来实现。而S3C2410内部已经集成了LCD控制器,因此可以很方便地去控制各种类型的LCD屏,例如:STN和TFT屏。由于TFT屏将是今后应用的主流,因此重点介绍TFT屏。
★S3C2410内置LCD控制器的特性:提供了驱动STN/TFT LCD所需的所有信号。
●对STN屏---支持3种扫描方式:4bit单扫、4位双扫和8位单扫;---支持单色、4级灰度和16级灰度屏;---支持256色和4096色彩色STN屏(CSTN);---支持分辩率为640*480、320*240、160*160以及其它规格的多种LCD。
●对TFT屏---支持单色、4级灰度、256色的调色板显示模式;---支持64K和16M色非调色板显示模式;---支持分辩率为640*480,320*240及其它多种规格的LCD;对于控制TFT屏来说,除了要给它送视频资料(VD[23:0])以外,还有以下一些信号是必不可少的,VSYNC(VFRAME)---帧同步信号;HSYNC(VLINE)---行同步信号;VCLK---像数时钟信号;VDEN(VM)---数据有效标志信号。

★LCD控制器的寄存器:LCD控制器逻辑示意图如下所示


●REGBANK---LCD控制器的寄存器组,含17个寄存器及一块256x16的调色板内存,用来设置各项参数。LCD控制寄存器:LCDCON1~LCDCON5(LCDCON1用于选择LCD类型、设置像素时钟、使能LCD信号的输出等;LCDCON2用于设置垂直方向各信号的时间参数;LCDCON3用于设置水平方向各信号的时间参数;LCDCON4对TFT只用来设置HSYNC信号的脉冲宽度;LCDCON5用于设置各个控制信号的极性,并可从中读到一些状态信息);帧内存地址寄存器:LCDSADDR1~LCDSADDR3(帧内存可以很大,而真正要显示的区域被称为视口(View Point),这3个寄存器用于确定帧内存的起始地址,定位视口在帧内存中的位置);临时调色板寄存器:TPAL(对输出一帧单色图像,可以在TPAL寄存器中设定这个颜色值,然后使能TPAL寄存器,这种方法可以避免修改整个调色板或帧缓冲区);其他寄存器(LCD中断、专用STN LCD及专用SEC(Samsung Electronics Company) TFT LCD)。
●LCDCDMA---LCD控制器专用的DMA信道。可自动从系统总线(System Bus)上取到图像数据,使得显示图像时不需要CPU的干涉(VIDPRCS将LCDDMA中的数据组合成特定的格式,然后从VD[23:0]发送给LCD屏;同时TIMEGEN和LPC3600(三星TFT专用)负责产生LCD屏所需要的控制时序)。LCDCDMA中含有两个FIFO:FIFOH容量为16个字,FIFOL容量为12个字,双扫方式两者分别对应上/下半屏数据,单扫方式只用到FIFOH。FIFO为空或其中数据减少到设定的阈值时LCDDMA自动发起DMA传输从内存中获得图像数据。

★LCD控制器可以支持单色(1BPP)、4级灰度(2BPP)、16级灰度(4BPP)、256色(8BPP)的调色板显示模式、64K(16BPP)和16M(24BPP)非调色板显示模式。图像数据的存储格式:
●16M(24BPP)色---使用24位的数据来表示一个像素的颜色,每种原色使用8位。LCD控制器从内存中获得某个像素的24位颜色值后,直接通过VD[23:0]数据线发送给LCD,为了方便DMA传输,内存中使用4字节来表示一个像素(其中3字节从高到低分别表示红、绿、蓝,剩余的1字节无效,此字节为最低字节还是最高字节是可以由BPP24BL值选择的)。
●64K(16BPP)色---使用16位的数据来表示一个像素的颜色,数据格式分5:6:5(高5位表示红色,中间6位表示绿色,最低5位表示蓝色)和5:5:5:1(从高到低依次为红:绿:蓝:透明度,故又称RGBA)两种,4个字节可以表示2个16BPP的像素,高2字节还是低2字节表示第一像素可以通过HWSWP值决定。
●256(8BPP)色---使用8位的数据来表示一个像素的颜色,直接用其表示对应的3原色的值时显示能力太弱,故引进调色板(Palette,就是一块内存,可以对每个索引值设置颜色,可以使用16BPP或24BPP),用这8位数据表示其在调色板中的索引值,S3C2410/S3C2440中的调色板是一块256x16的内存,使用16BPP的格式表示256色(8BPP)显示模式下各个索引值的颜色,这样即使使用256色的显示模式,最终显示在LCD数据总线上的仍是16BPP的数据。一个4字节可以表示4个8BPP的像素,字节与像素的对应顺序可通过BSWP值设置。


关键代码解析:
★head.S头文件来初始化,设置SDRAM,将程序复制到SDRAM,然后跳到SDRAM继续执行
.equ        MEM_CTL_BASE,       0x48000000
.text
.global _start
_start:
@中断向量表处理函数,只给出复位和普通中断模式的处理函数,其它异常未使用
    b   Reset
...
    b   HandleIRQ
@0x1c: 快中断模式的向量地址
HandleFIQ:
    b   HandleFIQ

Reset:                   @复位处理
    bl  disable_watch_dog                                    @关门喂狗
    bl  mem_control_setup                                   @设置存储控制器
    ldr sp, =4096                                                     @设置栈指针,以下C函数调用前需要设好栈
    bl init_clock                                                        @设置MPLL,改变FCLK、HCLK、PCLK
    bl init_nand                                                        @初始化NandFlash
                                                                                  @将NandFlash中地址4096开始的代码复制到SDRAM中
    ldr  r0, =0x30000000                                        @目标地址=0x30000000,SDRAM起始地址
    mov  r1, #4096                                                  @源地址=4096,连接时代码在4096开始处
    mov  r2, #16*1024                                           @复制长度=16K,对于本实验足够
    bl  read_nand                                                   @调用C函数read_nand
    bl  clean_bss                                                     @清除bss段
    msr cpsr_c, #0xd2                                           @进入中断模式
    ldr sp, =0x31000000                                       @设置中断模式栈指针
    msr cpsr_c, #0xdf                                            @进入系统模式
    ldr sp, =0x34000000                                      @设置系统模式栈指针
    ldr lr, =ret_initirq                                              @设置返回地址
    ldr pc, =init_irq                                                  @初始化中断
ret_initirq:
    msr cpsr_c, #0x5f                                           @设置I-bit=0,开IRQ中断
    ldr lr, =halt_loop                                              @设置返回地址
    ldr pc, =main                                                   @调用main函数
halt_loop:
    b   halt_loop

★main.c文件实现实现串口选择不同模式的LCD显示操作,主要代码:
#include
......
int main()
{
    char c;    
    init_uart0();   // 波特率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");
        printf("[3] TFT640480 8Bit\n\r");
        printf("[4] TFT640480 16Bit\n\r");
        printf("Enter your selection: ");
        c = getc();
        printf("%c\n\r", c);
        switch (c)
        {
            case '1':
            {
                Test_Lcd_Tft_8Bit_240320();
                break;
            }
......
}

★lcdlib.c 实现TFT LCD的测试函数,240x320,8bpp的显示模式主要代码:
...
void Test_Lcd_Tft_8Bit_240320(void)
{
    Lcd_Port_Init();                     // 设置LCD引脚
    Tft_Lcd_Init(MODE_TFT_8BIT_240320);  // 初始化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();
    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);

    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);
}
......
}

★lcddrv.c提供操作LCD控制器、调色板等的底层函数。以240x320,8bpp的显示模式主要代码:
#include
......
/*
* 初始化用于LCD的引脚
*/
void Lcd_Port_Init(void)
{
......
}

/*
* 初始化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)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:
         *    设置显示模式为16BPP时,调色板中的数据格式: 5:6:5
         *    设置HSYNC、VSYNC脉冲的极性(这需要参考具体LCD的接口信号): 反转
         *    字节交换使能
         */
        LCDCON1 = (CLKVAL_TFT_240320<<8) | (LCDTYPE_TFT<<5) | \
                  (BPPMODE_8BPP<<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) | \
                  (BSWP<<1);
......
/*
* 设置调色板
*/
void Lcd_Palette8Bit_Init(void)
{
......
}

/*
* 改变调色板为一种颜色
* 输入参数:
*     color: 颜色值,格式为0xRRGGBB
*/
void ChangePalette(UINT32 color)
{
......
    }
}

/*
* 设置是否输出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
    else
        LCDCON1 &= 0x3fffe; // ENVID Off
}    

/*
* 使用临时调色板寄存器输出单色图像
* 输入参数:
*     color: 颜色值,格式为0xRRGGBB
*/
void ClearScrWithTmpPlt(UINT32 color)
{
    TPAL = (1<<24)|((color & 0xffffff)<<0);
}

/*
* 停止使用临时调色板寄存器
*/
void DisableTmpPlt(void)
{
    TPAL = 0;
}

★framebuffer.c实现在framebuffer上画点、画线、画同心圆、清屏的函数。主要代码:
#include
......

/*
* 画点
* 输入参数:
*     x、y : 象素坐标
*     color: 颜色值
*         对于16BPP: color的格式为0xAARRGGBB (AA = 透明度),
*     需要转换为5:6:5格式
*         对于8BPP: color为调色板中的索引值,
*     其颜色取决于调色板中的数值
*/
void PutPixel(UINT32 x, UINT32 y, UINT32 color)
{
......
}

/*
* 画线
* 输入参数:
*     x1、y1 : 起点坐标
*     x2、y2 : 终点坐标
*     color  : 颜色值
*         对于16BPP: color的格式为0xAARRGGBB (AA = 透明度),
*     需要转换为5:6:5格式
*         对于8BPP: color为调色板中的索引值,
*     其颜色取决于调色板中的数值
*/
void DrawLine(int x1,int y1,int x2,int y2,int color)
{
......
}

/*
* 绘制同心圆
*/
void Mire(void)
{
......
}

/*
* 将屏幕清成单色
* 输入参数:
*     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);}

转载于:https://www.cnblogs.com/stardream/articles/1758303.html

你可能感兴趣的:(1-9、ARM开发步步深入之LCD入门)