第12课-LCD

注:以下内容学习于韦东山老师arm裸机第一期视频教程

参考文章http://www.cnblogs.com/shangdawei/p/4760933.html


一.LCD硬件原理

    1.1 LCD可以看作是一个电子枪一边移动一边发出颜色

        
        1.1.1 电子枪如何移动?

            LCD上有一条CLK信号线,每来一个CLK电子枪就会移动一个像素

        
        1.1.2 颜色如何确定?    

            颜色有RGB三组信号线来确定

        
        1.1.3 电子枪如何得知应跳到下一行?

            HSYNC信号线

            
        1.1.4 电子枪扫描完最后一行后如何得知回到原点?

            VSYNC信号线

        
        1.1.5 RGB线上的数据从哪里来?

            来自于一段内存(frame buffer)

            

    1.2 JZ2440LCD接口原理图如下

        

        其中VLINE信号线代表了HSYNC信号

            VFRAME信号线代表了VSYNC信号

            RGB信号线来传输数据(R0-R5,G0-G6,B0-B5,565格式,即一个像素使用16位来表示,16bpp(bit per pixel))

            注意LCD上有8根R,8根G,8根B数据线,可以表示24bpp,但是只接到开发板上16条线

    

    1.3 LCD显示配置图如下

        

            HFP->一行中最后一个信号发出之后需要经过多久才会接受HSYNC信号,即代表右边的黑框

            HBP->HSYNC信号接受之后需要经过多久才会发出一行中第一个像素的信号,即代表左边的黑框

            VFP->最后一行中的最后一个信号发出之后需要经过多久才会接受VSYNC信号,即代表下边的黑框

            VBP->VSYNC信号接受之后需要经过多久才会发出第一行的第一个像素的信号,即上边的黑框


二.LCD控制器

    2.1 LCD控制器的作用

        2.1.1 从内存(frame buffer)中取出像素的数据,(我们需要将framebuffer的地址告诉控制器,bpp,分辨率)
        2.1.2 配合其他信号一起将数据发送给LCD(将LCD的时序设置给LCD控制器,设置引脚的极性)

        

    2.2 LCD控制器结构图如下

        
        LCDDMA会从内存中取出数据,然后发送到VD[23:0]引脚上去

        我们同时需要设置寄存器来控制LCD控制器来让他发出合适的时序

        因此,我们只需要将数据填写到frame buffer中去即可,硬件会自动帮助我们将数据刷到LCD中去

    

    2.3 frambuffer数据格式的设置

        如下图:
            

        对于16bpp的数据格式

        当BSWP = 0, HWSWP = 0,0地址的高16位存放第一个数据,低16位存放第二个数据,4地址的高16位存放第三个数据,低16位存放第四个数据

        当BSWP = 0, HWSWP = 1,0地址的低16位存放第一个数据,高16位存放第二个数据,4地址的低16位存放第三个数据,高16位存放第四个数据

        显然第二种情况更容易操作,我们使用第2种情况

    

    2.4 设置数据的传输引脚(16位数据通过VD[23:0]的哪几条来传输数据)

        如下图:

        

        对于16位数据,565或者5551

        在画原理图时需要根据这个图来进行线的连接

        对于565格式:8条红数据,8条绿数据,8条蓝数据,但是分别只用了高5条,高6条,高5条

        

    2.5 2440时序图如下:

        

        我们需要根据LCD的数据手册来设置这个时序图
        

三.调色板的概念

        当使用16条数据线与LCD连接的时候,每个像素在frame buffer应该占据16位

        如果想要节省空间,每个像素只占据8位如何做?

        

        8bit的数据需要得到16bit的数据,因此引入调色板,调色板一共有256项,里面存放16bit的数据

        LCD控制器会根据得到的8bit数据作为索引在调色板中取出16bit的数据,然后再发给LCD

        

        注意:调色板也在内存中,或者在LCD控制器中

        如下图:    

        

        第一个数据存放在0X4D000400地址中,第二个数据存放在0X4D000404地址中

        

四.临时调色板    

    当想要将LCD设置为一个颜色

    对于16bpp,需要去填充frame buffer里面的数据设置为一种值

    对于8bpp,可以设置frame buffer的值设置为同一个,也可以设置调色板中的值都设置为颜色值

    

    对于2440提供了一种临时调色板,如下图:

    

    可以设置TPAL寄存器的bit[23:0]来设置某一种颜色值,一旦设置bit24使能之后,对于LCD控制器无论是多少bpp,都会从调色板中取出数据发送给LCD

    

五.LCD编程(以面向对象的思想)

    5.1 明确目的

       a.在LCD上画线,画圆(核心都是画点)---->genmetry.c(纯软件,可以在LCD3.5寸,4.3寸上运行)
        b.画点---->framebuffer.c
        c.写字---->font.c
       d.LCD相关--->LCD3.5.c LCD4.3.c--------->抽象出一个结构体(lcd.c来管理各种型号的lcd)
   e.LCD控制器--->S3C2440_LCD_CONTROLLER.c----->抽象出结构体(lcd_conrtoller.c来管理各种控制器)

             f.提供测试菜单--->lcd_test.c

            测试菜单会调用lcd.c中的函数来初始化LCD,初始化控制器,开启电源等,然后调用genmetry.c,font.c中的函数来进行测试


    5.2 重要结构体的抽象

        a. LCD参数结构体的抽象(这些值提供给LCD控制器用来初始化LCD控制器)

            #ifndef _LCD_H
            #define _LCD_H


            enum {
                NORMAL = 0,
                INVERT = 1,
            };

            /* NORMAL : 正常极性
             * INVERT : 反转极性
             */
            typedef struct pins_polarity {
                int vclk;  /* normal: 在下降沿获取数据 */
                int rgb;   /* normal: 高电平表示1 */
                int hsync; /* normal: 高脉冲 */
                int vsync; /* normal: 高脉冲 */
            }pins_polarity, *p_pins_polarity;

            typedef struct time_sequence {
                /* 垂直方向 */
                int tvp; /* vysnc脉冲宽度 */
                int tvb; /* 上边黑框, Vertical Back porch */
                int tvf; /* 下边黑框, Vertical Front porch */

                /* 水平方向 */
                int thp; /* hsync脉冲宽度 */
                int thb; /* 左边黑框, Horizontal Back porch */
                int thf; /* 右边黑框, Horizontal Front porch */

                int vclk;
            }time_sequence, *p_time_sequence;


            typedef struct lcd_params {
                /* 引脚极性 */
                pins_polarity pins_pol;
                
                /* 时序 */
                time_sequence time_seq;
                
                /* 分辨率, bpp */
                int xres;
                int yres;
                int bpp;
                
                /* framebuffer的地址 */
                unsigned int fb_base;
            }lcd_params, *p_lcd_params;

            #endif /* _LCD_H *



        b. LCD控制器结构体的抽象

        /* lcd_constroller.h */
        #ifndef _LCD_CONTROLLER_H
        #define _LCD_CONTROLLER_H

        #include "lcd.h"

        typedef struct lcd_controller {
            void (*init)(p_lcd_params plcdparams);
            void (*enable)(void);
            void (*disable)(void);
            void (*palette_init)(void)
        }lcd_controller, *p_lcd_controller;

        #endif /* _LCD_CONTROLLER_H */
 

lcd控制器结构体,提供LCD控制器初始化函数,使能函数,禁止使能函数和调色板初始化函数.

        LCD控制器初始化函数根据上层LCD传入的参数来进行初始化
        

  

 5.3 LCD程序的完善

       

       /* framebuffer.c, 提供画点函数 */
        #include "lcd.h"

        /* 实现画点 */

        /* 获得LCD参数 */
        static unsigned int fb_base;
        static int xres, yres, bpp;

        void fb_get_lcd_params(void)
        {
            get_lcd_params(&fb_base, &xres, &yres, &bpp);
        }

        /* rgb: 0x00RRGGBB */
        unsigned short convert32bppto16bpp(unsigned int rgb)
        {
            int r = (rgb >> 16)& 0xff;
            int g = (rgb >> 8) & 0xff;
            int b = rgb & 0xff;

            /* rgb565 */
            r = r >> 3;
            g = g >> 2;
            b = b >> 3;

            return ((r<<11) | (g<<5) | (b));
        }


        /* color : 32bit, 0x00RRGGBB
         *
         */
        void fb_put_pixel(int x, int y, unsigned int color)
        {
            unsigned char  *pc;  /* 8bpp */
            unsigned short *pw;  /* 16bpp */
            unsigned int   *pdw; /* 32bpp */

            unsigned int pixel_base = fb_base + (xres * bpp / 8) * y + x * bpp / 8;

            switch (bpp)
            {
                case 8:
                    pc = (unsigned char *) pixel_base;
                    *pc = color;
                    break;
                case 16:
                    pw = (unsigned short *) pixel_base;
                    *pw = convert32bppto16bpp(color);
                    break;
                case 32:
                    pdw = (unsigned int *) pixel_base;
                    *pdw = color;
                    break;
            }
        }

      /* lcd.c,对各种型号的LCD进行管理,同时向最上层提供初始化函数 */
        #include "lcd.h"
        #include "lcd_controller.h"

        #define LCD_NUM 10

        static p_lcd_params p_array_lcd[LCD_NUM];
        static p_lcd_params g_p_lcd_selected;

        int register_lcd(p_lcd_params plcd)
        {
            int i;
            for (i = 0; i < LCD_NUM; i++)
            {
                if (!p_array_lcd[i])
                {
                    p_array_lcd[i] = plcd;
                    return i;
                }
            }
            return -1;        
        }

        int select_lcd(char *name)
        {
            int i;
            for (i = 0; i < LCD_NUM; i++)
            {
                if (p_array_lcd[i] && !strcmp(p_array_lcd[i]->name, name))
                {
                    g_p_lcd_selected = p_array_lcd[i];
                    return i;
                }
            }
            return -1;        
        }

        void get_lcd_params(unsigned int *fb_base, int *xres, int *yres, int *bpp)
        {
            *fb_base = g_p_lcd_selected->fb_base;
            *xres = g_p_lcd_selected->xres;
            *yres = g_p_lcd_selected->yres;
            *bpp = g_p_lcd_selected->bpp;
        }

        void lcd_enable(void)
        {
            lcd_controller_enable();
        }

        void lcd_disable(void)
        {
            lcd_controller_disable();
        }

        int lcd_init(void)
        {
            /* 注册LCD */
            lcd_4_3_add();

            /* 注册LCD控制器 */
            lcd_contoller_add();
            
            /* 选择某款LCD */
            select_lcd("lcd_4.3");

            /* 选择某款LCD控制器 */
            select_lcd_controller("s3c2440");

            /* 使用LCD的参数, 初始化LCD控制器 */
            lcd_controller_init(g_p_lcd_selected);
        }

      /* lcd4.3.c (根据芯片手册构造了lcd_4_3_params结构体)*/
        
        #include "lcd.h"

        #define LCD_FB_BASE 0x33c00000

        lcd_params lcd_4_3_params = {
            .name = "lcd_4.3",
            .pins_pol = {
                .de    = NORMAL,    /* normal: 高电平时可以传输数据 */
                .pwren = NORMAL,    /* normal: 高电平有效 */
                .vclk  = NORMAL,    /* normal: 在下降沿获取数据 */
                .rgb   = NORMAL,    /* normal: 高电平表示1 */
                .hsync = INVERT,    /* normal: 高脉冲 */
                .vsync = INVERT,     /* normal: 高脉冲 */
            },
            .time_seq = {
                /* 垂直方向 */
                .tvp=    10, /* vysnc脉冲宽度 */
                .tvb=    2,  /* 上边黑框, Vertical Back porch */
                .tvf=    2,  /* 下边黑框, Vertical Front porch */

                /* 水平方向 */
                .thp=    41, /* hsync脉冲宽度 */
                .thb=    2,  /* 左边黑框, Horizontal Back porch */
                .thf=    2,  /* 右边黑框, Horizontal Front porch */

                .vclk=    9,  /* MHz */
            },
            .xres = 480,
            .yres = 272,
            .bpp  = 8,  /* 16, no 24bpp */
            .fb_base = LCD_FB_BASE,
        };


        void lcd_4_3_add(void)
        {
            register_lcd(&lcd_4_3_params);
        }

       /* lcd_constroller.c(对底层的各种LCD控制器进行管理的函数,同时向上层提供选择控制器的函数) */
        static p_lcd_controller p_array_lcd_controller[LCD_CONTROLLER_NUM];
        static p_lcd_controller g_p_lcd_controller_selected;

        int register_lcd_controller(p_lcd_controller plcdcon)
        {
            int i;
            for (i = 0; i < LCD_CONTROLLER_NUM; i++)
            {
                if (!p_array_lcd_controller[i])
                {
                    p_array_lcd_controller[i] = plcdcon;
                    return i;
                }
            }
            return -1;        
        }

        int select_lcd_controller(char *name)
        {
            int i;
            for (i = 0; i < LCD_CONTROLLER_NUM; i++)
            {
                if (p_array_lcd_controller[i] && !strcmp(p_array_lcd_controller[i]->name, name))
                {
                    g_p_lcd_controller_selected = p_array_lcd_controller[i];
                    return i;
                }
            }
            return -1;        
        }


        /* 向上: 接收不同LCD的参数
         * 向下: 使用这些参数设置对应的LCD控制器
         */

        int lcd_controller_init(p_lcd_params plcdparams)
        {
            /* 调用所选择的LCD控制器的初始化函数 */
            if (g_p_lcd_controller_selected)
            {
                g_p_lcd_controller_selected->init(plcdparams);
                g_p_lcd_controller_selected->init_palette();
                return 0;
            }
            return -1;
        }

        void lcd_controller_enable(void)
        {
            if (g_p_lcd_controller_selected)
            {
                g_p_lcd_controller_selected->enable();
            }
        }

        void lcd_controller_disable(void)
        {
            if (g_p_lcd_controller_selected)
            {
                g_p_lcd_controller_selected->disable();
            }
        }


        void lcd_contoller_add(void)
        {
            s3c2440_lcd_contoller_add();
        }

        /* s3c2440_lcd_controller.c,会构造一个s3c2440_lcd_controller结构体,根据LCD参数来初始化 */
        /* 最后会向上提供一个注册函数,即将所构造的结构体放入一个lcd_controller结构体数组中,调用的时候通过名字来选择不同的控制器 */
        #include "lcd.h"
        #include "lcd_controller.h"
        #include "../s3c2440_soc.h"

        #define HCLK 100

        void jz2440_lcd_pin_init(void)
        {
            /* 初始化引脚 : 背光引脚 */
            GPBCON &= ~0x3;
            GPBCON |= 0x01;

            /* LCD专用引脚 */
            GPCCON = 0xaaaaaaaa;
            GPDCON = 0xaaaaaaaa;

            /* PWREN */
            GPGCON |= (3<<8);
        }


        /* 根据传入的LCD参数设置LCD控制器 */
        void s3c2440_lcd_controller_init(p_lcd_params plcdparams)
        {
            int pixelplace;
            unsigned int addr;

            jz2440_lcd_pin_init();
            
            /* [17:8]: clkval, vclk = HCLK / [(CLKVAL+1) x 2]
             *                   9   = 100M /[(CLKVAL+1) x 2], clkval = 4.5 = 5
             *                 CLKVAL = 100/vclk/2-1
             * [6:5]: 0b11, tft lcd
             * [4:1]: bpp mode
             * [0]  : LCD video output and the logic enable/disable
             */
            int clkval = (float)HCLK/plcdparams->time_seq.vclk/2-1+0.5;
            //int clkval = 5;
            int bppmode = plcdparams->bpp == 8  ? 0xb :\
                          plcdparams->bpp == 16 ? 0xc :\
                          0xd;  /* 0xd: 24,32bpp */
            LCDCON1 = (clkval<<8) | (3<<5) | (bppmode<<1) ;

            /* [31:24] : VBPD    = tvb - 1
             * [23:14] : LINEVAL = line - 1
             * [13:6]  : VFPD    = tvf - 1
             * [5:0]   : VSPW    = tvp - 1
             */
            LCDCON2 =     ((plcdparams->time_seq.tvb - 1)<<24) | \
                        ((plcdparams->yres - 1)<<14)         | \
                        ((plcdparams->time_seq.tvf - 1)<<6)  | \
                        ((plcdparams->time_seq.tvp - 1)<<0);

            /* [25:19] : HBPD     = thb - 1
             * [18:8]  : HOZVAL  = 列 - 1
             * [7:0]   : HFPD     = thf - 1
             */
            LCDCON3 =    ((plcdparams->time_seq.thb - 1)<<19) | \
                        ((plcdparams->xres - 1)<<8)              | \
                        ((plcdparams->time_seq.thf - 1)<<0);

            /*
             * [7:0]   : HSPW     = thp - 1
             */
            LCDCON4 =    ((plcdparams->time_seq.thp - 1)<<0);

            /* 用来设置引脚极性, 设置16bpp, 设置内存中象素存放的格式
             * [12] : BPP24BL
             * [11] : FRM565, 1-565
             * [10] : INVVCLK, 0 = The video data is fetched at VCLK falling edge
             * [9]  : HSYNC是否反转
             * [8]  : VSYNC是否反转
             * [7]  : INVVD, rgb是否反转
             * [6]  : INVVDEN
             * [5]  : INVPWREN
             * [4]  : INVLEND
             * [3]  : PWREN, LCD_PWREN output signal enable/disable
             * [2]  : ENLEND
             * [1]  : BSWP
             * [0]  : HWSWP
             */

            pixelplace = plcdparams->bpp == 32 ? (0) : \
                         plcdparams->bpp == 16 ? (1) : \
                         (1<<1);  /* 8bpp */
            
            LCDCON5 = (plcdparams->pins_pol.vclk<<10) |\
                      (plcdparams->pins_pol.rgb<<7)   |\
                      (plcdparams->pins_pol.hsync<<9) |\
                      (plcdparams->pins_pol.vsync<<8) |\
                      (plcdparams->pins_pol.de<<6)    |\
                      (plcdparams->pins_pol.pwren<<5) |\
                      (1<<11) | pixelplace;

            /* framebuffer地址 */
            /*
             * [29:21] : LCDBANK, A[30:22] of fb
             * [20:0]  : LCDBASEU, A[21:1] of fb
             */
            addr = plcdparams->fb_base & ~(1<<31);
            LCDSADDR1 = (addr >> 1);

            /*
             * [20:0] : LCDBASEL, A[21:1] of end addr
             */
            addr = plcdparams->fb_base + plcdparams->xres*plcdparams->yres*plcdparams->bpp/8;
            addr >>=1;
            addr &= 0x1fffff;
            LCDSADDR2 = addr;//    
        }

        void s3c2440_lcd_controller_enalbe(void)
        {
            /* 背光引脚 : GPB0 */
            GPBDAT |= (1<<0);
            
            /* pwren    : 给LCD提供AVDD  */
            LCDCON5 |= (1<<3);
            
            /* LCDCON1'BIT 0 : 设置LCD控制器是否输出信号 */
            LCDCON1 |= (1<<0);
        }

        void s3c2440_lcd_controller_disable(void)
        {
            /* 背光引脚 : GPB0 */
            GPBDAT &= ~(1<<0);

            /* pwren    : 给LCD提供AVDD  */
            LCDCON5 &= ~(1<<3);

            /* LCDCON1'BIT 0 : 设置LCD控制器是否输出信号 */
            LCDCON1 &= ~(1<<0);
        }


        /* 设置调色板之前, 先关闭lcd_controller */
        void s3c2440_lcd_controller_init_palette(void)
        {
            volatile unsigned int *palette_base =  (volatile unsigned int *)0x4D000400;
            int i;

            int bit = LCDCON1 & (1<<0);

            /* LCDCON1'BIT 0 : 设置LCD控制器是否输出信号 */
            if (bit)
                LCDCON1 &= ~(1<<0);

            for (i = 0; i < 256; i++)
            {
                /* 低16位 : rgb565 */    
                *palette_base++ = i;
            }

            if (bit)
                LCDCON1 |= (1<<0);
        }

        struct lcd_controller s3c2440_lcd_controller = {
            .name    = "s3c2440",
            .init    = s3c2440_lcd_controller_init,
            .enable  = s3c2440_lcd_controller_enalbe,
            .disable = s3c2440_lcd_controller_disable,
            .init_palette = s3c2440_lcd_controller_init_palette,
        };


        void s3c2440_lcd_contoller_add(void)
        {
            register_lcd_controller(&s3c2440_lcd_controller);
        }

 其余的font.c, genmetry.c都是基于画点函数的一些算法,这里就不一一列出了.

    


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值