LCD

LCD分类和特点

LCD背光是白色的,白光的像素值是255,255,255,通过白光可以显示任何其他颜色的光,LCD分很多种:

  • TN面板:最早的面板,响应性不够强,有拖尾现象
  • STN面板:解决了拖尾现象
  • TFT面板:超薄
  • -

其他显示技术

  • CRT阴极摄像管显示器,寿命短,体积较大,、
  • 等离子显示器,未成 主流
  • OLED,可以实现柔性 显示
  • LED,户外大屏幕,照明

LCD接口

LCD的数据量比较大,本质上LCD都是TTL接口,+5V表示逻辑1,0V或者GND表示逻辑0,SoC的LCD控制器的硬件接口是TTL电平,LCD硬件接口也是TTL电平,所以是可以直接对接通信的,用软排线连接

TTL不能传递太远的距离,如果LCD和控制器距离过远,则不能直接使用TTL连接,需要进行转换,从主机控制器转换成别的接口(VGA,HDMI等),然后由别的接口在转换成TTL电平到LCD

RGB接口

  • VD[0-23]:24根数据线,用于传输图像信息,所以LCD是24根线的并行接口,这样才能快速显示
  • HSYNC&VSYNC:时序信号线,控制时序
  • VCLK:像素时钟,LCD控制器工作需要主板控制器给他一个工作时钟,从而驱动LCD工作
  • VDEN:数据有效标志,时序信号,和HSYNC&VSYNC结合使用
  • LEND:行结束标示,时序信号

LCD图像显示

像素

组成图像的最基本的元素称为像素,在显示中可以被控制最小单位,整个图像就是由很多歌像素一个一个组成的,每个像素可以被单独控制

扫描

扫描是一个动作,扫描依次将颜色数值放入屏幕中所有像素里的一个过程,像素以2D平面的方式摆放在屏幕中,扫描就一行一行的从左到右,从上到下将像素数据依次放入到每一个像素中

驱动器&控制器

LCD驱动器产生复杂的模拟信号,驱动LCD中的液晶分子旋转,从而产生图像,LCD控制器通过接口传递数字信号到驱动器,从而告诉驱动器要产生什么样的图像,控制器和驱动器之间使用RGB接口进行通信,驱动器负责数字信号转换为模拟信号的过程,Soc从内存中将像素数据给到LCD控制器。控制器将数据传送到驱动器从而驱动屏幕显示图像

显示内存

Soc在内存中挑选一段满足要求的内存,通过配置将LCD控制器和这段内存连接起来,构成映射关系,LCD控制器就会从这段显存中读取像素数据传输给LCD驱动器来显示,显示体系建立起来后CPU就不用再管驱动器,控制器以及面板的事情了,只需要把图像数据放到显存中去就可显示了

LCD主要时序参数

LCD显示的单位为帧(Frame),表示显示器上一整个画面的内容,显示器工作时一帧一帧的在显示,一帧分为多行,每一行有多个像素,因此一帧图像就是多个像素组成的方形的矩阵,这些数据称为帧外数据,一个视频是由多个帧组成的,在播放的时候逐个播放各个帧,LCD控制器和驱动器一次只能传递一个像素点,一帧图像在屏幕上以串行的方式显示到屏幕上
主要的时序参数有:

  • HSPW:HSYNC脉冲持续时间的长短
  • HBPD:水平前肩,在水平开始的时候的等待时间
  • HFPD:水平后肩,在水平结束的时候的等待时间
  • VSPW:VSYNC脉冲持续时间的长短
  • VBPD:水平前肩,在水平开始的时候的等待时间
  • VFPD:水平后肩,在水平结束的时候的等待时间

每一行的开始,先发送一个HSYNC的高电平脉冲,宽度为HSPW,脉冲告诉驱动器下面的数据是一行信息,信息包含三部分:HBPD+有效信息+HFPD,其中HBPD和HFPD属于时序信息,有效信息就是横向分辨率

帧图像信号分为四部分,VSPW+VBPD+帧有效数据+VFPD,VSPW是帧同步脉冲信号宽度,VBPD和VFPD是帧垂直同步信号的前后肩,帧有效数据

LCD显示概念

像素间距

分为纵向和横向间距,一般像素都是矩形的,所以横向和纵向间距是一样的,像素间距影响最佳观看距离

屏幕分辨率

屏幕横向和纵向的像素个数叫做屏幕的分辨率,分辨率和屏幕尺寸无关

像素深度

bit per pixel,每个像素的数据大小,计算机中使用二进制位表示像素数据,像素位的数据越多,像素值更丰富,颜色更细腻

颜色在计算机中的表示

颜色的本质

颜色是主观存在的,是自然光在眼睛和大脑中产生的映像,颜色的本质在于光,取决于光的波长,光的波长是连续的,所以自然光的颜色也是连续的

颜色的组成

所有的颜色都由红绿蓝三原色组成,来模拟自然界中的颜色,但是受限于计算机中的二进制位数限制,所以颜色分的不够细,有点失真

计算机中的颜色

计算机中的颜色是有限的,类似于模拟信号和数字信号的区别,计算机中需要使用有限的颜色来表示自然界中的颜色,这种情况的缺点是不够真实,会漏掉一部分颜色,所以计算机中能表达的颜色没有自然界丰富,计算机能表达的颜色种类个数,叫做像素深度

常见的像素深度

  • 1bit:用1个二进制位来表示颜色,单色显示
  • 8bit:能表示256种颜色,但是不能表示彩色,只能以灰度显示
  • 16bit:能表示25536种颜色,此时就可以显示颜色了,一般是RGB565颜色分布,用5bit表示红色,6bit表示绿色,剩余5bit表示蓝色,
  • 24bit:可以表示2的24次方种颜色,使用RGB888颜色分布,这时颜色显示比较丰富和细腻,图像看起来就比较真实,最起码人眼看起来和真实颜色相差无几了,我们称这种颜色分布为真彩色
  • 32bit:使用24bit表示RGB三原色,剩下8bit表示透明度,这种颜色我们成为ARGB8888,颜色的整体个数没变,增加了8bit的透明显示

LCD控制器

S5PV210中的LCD控制器叫做FIMD,与AHB总线相连接,外部提供RGB,Y80,YUV接口,实际使用的是RGB

虚拟屏幕叠加

平时屏幕上看到的场景实际上是多个屏幕显示叠加在一起的效果,S5PV210中有Window0-4总共5个虚拟屏幕,对应的是内存中的不同显存区域,都被映射到一个真实的显示屏上边,所以将来真实的显示效果实际上是这几个虚拟屏幕显示内容的叠加,上面一层会覆盖下面一层,虚拟屏幕可以保证不污染原图像,处理起来比较简单,减少屏幕的刷新,减少CPU的工作量,提高效率

虚拟显示

在内存中显示一块比屏幕大的区域,LCD可以显示其中的一部分,LCD可以在这个大内存区域中移动来显示其中的哪一部分,适合于图片放大等场景

LCD编程

LCD控制器初始化

LCD控制器初始化用于建立显存和LCD之间的映射关系,LCD工作时需要和显存之间建立一个映射,需要使用CPU初始化LCD控制器,代码如下:

#include "main.h"

#define GPF0CON         (*(volatile unsigned long *)0xE0200120)
#define GPF1CON         (*(volatile unsigned long *)0xE0200140)
#define GPF2CON         (*(volatile unsigned long *)0xE0200160)
#define GPF3CON         (*(volatile unsigned long *)0xE0200180)

#define GPD0CON         (*(volatile unsigned long *)0xE02000A0)
#define GPD0DAT         (*(volatile unsigned long *)0xE02000A4)

#define CLK_SRC1        (*(volatile unsigned long *)0xe0100204)
#define CLK_DIV1        (*(volatile unsigned long *)0xe0100304)
#define DISPLAY_CONTROL (*(volatile unsigned long *)0xe0107008)

#define VIDCON0         (*(volatile unsigned long *)0xF8000000)
#define VIDCON1         (*(volatile unsigned long *)0xF8000004)
#define VIDTCON2        (*(volatile unsigned long *)0xF8000018)
#define WINCON0         (*(volatile unsigned long *)0xF8000020)
#define WINCON2         (*(volatile unsigned long *)0xF8000028)
#define SHADOWCON       (*(volatile unsigned long *)0xF8000034)
#define VIDOSD0A        (*(volatile unsigned long *)0xF8000040)
#define VIDOSD0B        (*(volatile unsigned long *)0xF8000044)
#define VIDOSD0C        (*(volatile unsigned long *)0xF8000048)

#define VIDW00ADD0B0    (*(volatile unsigned long *)0xF80000A0)
#define VIDW00ADD1B0    (*(volatile unsigned long *)0xF80000D0)

#define VIDTCON0        (*(volatile unsigned long *)0xF8000010)
#define VIDTCON1        (*(volatile unsigned long *)0xF8000014)

#define HSPW            (40)                // 1~40 DCLK
#define HBPD            (10 - 1)            // 46
#define HFPD            (240 - 1)           // 16 210 354
#define VSPW            (20)                // 1~20 DCLK
#define VBPD            (10 - 1)            // 23
#define VFPD            (30 - 1)            // 7 22 147



// FB地址
#define FB_ADDR         (0x23000000)
#define ROW             (480)
#define COL             (800)
#define HOZVAL          (COL-1)
#define LINEVAL         (ROW-1)

#define XSIZE           COL
#define YSIZE           ROW


// 初始化LCD
void lcd_init(void)
{
    // 配置引脚用于LCD功能
    GPF0CON = 0x22222222;
    GPF1CON = 0x22222222;
    GPF2CON = 0x22222222;
    GPF3CON = 0x22222222;

    // 打开背光 GPD0_0(PWMTOUT0)
    GPD0CON &= ~(0xf<<0);
    GPD0CON |= (1<<0);          // output mode
    GPD0DAT &= ~(1<<0);            // output 0 to enable backlight

    // 10: RGB=FIMD I80=FIMD ITU=FIMD
    DISPLAY_CONTROL = 2<<0;

    // bit[26~28]:使用RGB接口
    // bit[18]:RGB 并行
    // bit[2]:选择时钟源为HCLK_DSYS=166MHz
    VIDCON0 &= ~( (3<<26)|(1<<18)|(1<<2) );

    // bit[1]:使能lcd控制器
    // bit[0]:当前帧结束后使能lcd控制器
    VIDCON0 |= ( (1<<0)|(1<<1) );

    // bit[6]:选择需要分频
    // bit[6~13]:分频系数为5,即VCLK = 166M/(4+1) = 33M
    VIDCON0 |= 4<<6 | 1<<4;


    // H43-HSD043I9W1.pdf(p13) 时序图:VSYNC和HSYNC都是低脉冲
    // s5pv210芯片手册(p1207) 时序图:VSYNC和HSYNC都是高脉冲有效,所以需要反转
    VIDCON1 |= 1<<5 | 1<<6;

    // 设置时序
    VIDTCON0 = VBPD<<16 | VFPD<<8 | VSPW<<0;
    VIDTCON1 = HBPD<<16 | HFPD<<8 | HSPW<<0;
    // 设置长宽(物理屏幕)
    VIDTCON2 = (LINEVAL << 11) | (HOZVAL << 0);

    // 设置window0
    // bit[0]:使能
    // bit[2~5]:24bpp(RGB888)
    WINCON0 |= 1<<0;
    WINCON0 &= ~(0xf << 2);
    WINCON0 |= (0xB<<2) | (1<<15);

#define LeftTopX     0
#define LeftTopY     0
#define RightBotX   799
#define RightBotY   479

    // 设置window0的上下左右
    // 设置的是显存空间的大小
    VIDOSD0A = (LeftTopX<<11) | (LeftTopY << 0);
    VIDOSD0B = (RightBotX<<11) | (RightBotY << 0);
    VIDOSD0C = (LINEVAL + 1) * (HOZVAL + 1);


    // 设置fb的地址
    VIDW00ADD0B0 = FB_ADDR;
    VIDW00ADD1B0 = (((HOZVAL + 1)*4 + 0) * (LINEVAL + 1)) & (0xffffff);

    // 使能channel 0传输数据
    SHADOWCON = 0x1;
}

初始化GPIO

根据用户手册,将GPF0-3全部设置为0x22222222

打开背光

将PWMTOUT0设置为波形输出或者输出0来常量,所以设置GPD0CON寄存器

设置显示路径

DISPLAY_CONTROL对应显示路径,有RGB-I80-ITU,这里要设置为ITU就是FIMD路径

配置LCD控制器

配置VIDCON0寄存器:

  • bit[26~28]用于设置输出接口
  • bit[18]用于RGB并行
  • bit[2]设置要使用的时钟源
  • bit[1]设置使能或者禁止LCD控制器
  • bit[0]设置当前帧结束后是否马上使能LCD控制器
  • bit[6]设置是否需要分频
  • bit[6~13]设置分频系数
  • bit[4]设置是否使能分频

配置VIDCON1寄存器:

  • bit[5-6]设置信号极性,说明HSYNC和VSYNC是否反转

配置VIDTCON0和VIDTCON1:

  • bit[0]配置xSPW
  • bit[8]配置xFPD
  • bit[16]配置xBPD

配置VIDTCON2:

  • bit[0]配置HOZVAL
  • bit[11]配置LINEVAL

配置第一个window-WINCON0:

  • bit[0]配置是否使能该window
  • bit[2~5]配置颜色深度

下面是设置虚拟显示,控制显存空间

  • VIDOSD0A的bit[0]设置左上角的X坐标,bit[11]设置左上角Y坐标
  • VIDOSD0B的bit[0]设置右下角角的X坐标,bit[11]设置右下角Y坐标
  • VIDOSD0C设置 横向和纵向尺寸
  • VIDW00ADD0B0设置为显存空间的地址,最好对齐使用
  • VIDW00ADD1B0设置屏幕大小

配置SHADOWCON寄存器

  • 使能window0通过channel0传输数据

绘制简单元素

// 把整个屏幕填充为纯色
void lcd_draw_background(u32 color) {
    u32 i,j;
    for ( j=0; j < ROW; j++) {
        for ( i = 0; i < COL; i++) {
            lcd_draw_pixel(i,j,color);
        }
    }
}

void lcd_draw_pixel(u32 x,u32 y,u32 color) {
    // 基地址加上偏移量得到目标地址,设置颜色
    *(pfb + COL * y + x) = color;
}

//绘制一个横线,x变化y不变
void lcd_draw_hline(u32 x1,u32 x2,u32 y,u32 color) {
    u32 x;
    for (x = x1; x < x2; x++) {
        lcd_draw_pixel(x,y,color);
    }
}

//绘制一个竖线,y变化x不变
void lcd_draw_vline(u32 x,u32 y1,u32 y2,u32 color) {
    u32 y;
    for (y = y1; y < y2; y++) {
        lcd_draw_pixel(x,y,color);
    }
}

//绘制一个斜线,x变化y变化,需要使用到算法
// glib库中的画线函数,可以画斜线,线两端分别是(x1, y1)和(x2, y2)
void glib_line(u32 x1, u32 y1, u32 x2, u32 y2, u32 color) {
    int dx,dy,e;
    dx=x2-x1; 
    dy=y2-y1;

    if(dx>=0)
    {
        if(dy >= 0) // dy>=0
        {
            if(dx>=dy) // 1/8 octant
            {
                e=dy-dx/2;  
                while(x1<=x2)
                {
                    lcd_draw_pixel(x1,y1,color);
                    if(e>0){y1+=1;e-=dx;}   
                    x1+=1;
                    e+=dy;
                }
            }
            else        // 2/8 octant
            {
                e=dx-dy/2;
                while(y1<=y2)
                {
                    lcd_draw_pixel(x1,y1,color);
                    if(e>0){x1+=1;e-=dy;}   
                    y1+=1;
                    e+=dx;
                }
            }
        }
        else           // dy<0
        {
            dy=-dy;   // dy=abs(dy)

            if(dx>=dy) // 8/8 octant
            {
                e=dy-dx/2;
                while(x1<=x2)
                {
                    lcd_draw_pixel(x1,y1,color);
                    if(e>0){y1-=1;e-=dx;}   
                    x1+=1;
                    e+=dy;
                }
            }
            else     // 7/8 octant
            {
                e=dx-dy/2;
                while(y1>=y2)
                {
                    lcd_draw_pixel(x1,y1,color);
                    if(e>0){x1+=1;e-=dy;}   
                    y1-=1;
                    e+=dx;
                }
            }
        }   
    }
    else //dx<0
    {
        dx=-dx;     //dx=abs(dx)
        if(dy >= 0) // dy>=0
        {
            if(dx>=dy) // 4/8 octant
            {
                e=dy-dx/2;
                while(x1>=x2)
                {
                    lcd_draw_pixel(x1,y1,color);
                    if(e>0){y1+=1;e-=dx;}   
                    x1-=1;
                    e+=dy;
                }
            }
            else        // 3/8 octant
            {
                e=dx-dy/2;
                while(y1<=y2)
                {
                    lcd_draw_pixel(x1,y1,color);
                    if(e>0){x1-=1;e-=dy;}   
                    y1+=1;
                    e+=dx;
                }
            }
        }
        else           // dy<0
        {
            dy=-dy;   // dy=abs(dy)

            if(dx>=dy) // 5/8 octant
            {
                e=dy-dx/2;
                while(x1>=x2)
                {
                    lcd_draw_pixel(x1,y1,color);
                    if(e>0){y1-=1;e-=dx;}   
                    x1-=1;
                    e+=dy;
                }
            }
            else        // 6/8 octant
            {
                e=dx-dy/2;
                while(y1>=y2)
                {
                    lcd_draw_pixel(x1,y1,color);
                    if(e>0){x1-=1;e-=dy;}   
                    y1-=1;
                    e+=dx;
                }
            }
        }   
    }
}

//画圆函数,圆心坐标是(centerX, centerY),半径是radius,圆的颜色是color
void draw_circular(u32 centerX, u32 centerY, u32 radius, u32 color) {
    int x,y ;
    int tempX,tempY;;
    int SquareOfR = radius*radius;

    for(y=0; y<XSIZE; y++)
    {
        for(x=0; x<YSIZE; x++)
        {
            if(y<=centerY && x<=centerX)
            {
                tempY=centerY-y;
                tempX=centerX-x;                        
            }
            else if(y<=centerY&& x>=centerX)
            {
                tempY=centerY-y;
                tempX=x-centerX;                        
            }
            else if(y>=centerY&& x<=centerX)
            {
                tempY=y-centerY;
                tempX=centerX-x;                        
            }
            else
            {
                tempY = y-centerY;
                tempX = x-centerX;
            }
            if ((tempY*tempY+tempX*tempX)<=SquareOfR)
                lcd_draw_pixel(x, y, color);
        }
    }
}

// 写字的左上角坐标(x, y),字的颜色是color,字的字模信息存储在data中
void show_8_16(u32 x, u32 y, u32 color, unsigned char *data)  {  
// count记录当前正在绘制的像素的次序
    int i, j, count = 0;  

    for (j=y; j<(y+16); j++)  
    {  
        for (i=x; i<(x+8); i++)  
        {  
            if (i<XSIZE && j<YSIZE)  
            {  
            // 在坐标(i, j)这个像素处判断是0还是1,如果是1写color;如果是0直接跳过
                if (data[count/8] & (1<<(count%8)))   
                    lcd_draw_pixel(i, j, color);
            }  
            count++;  
        }  
    }  
}

// 写字符串
// 字符串起始坐标左上角为(x, y),字符串文字颜色是color,字符串内容为str
void draw_ascii_ok(u32 x, u32 y, u32 color, unsigned char *str) {
    int i;  
    unsigned char *ch;
    for (i=0; str[i]!='\0'; i++)  
    {  
        ch = (unsigned char *)ascii_8_16[(unsigned char)str[i]-0x20];
        show_8_16(x, y, color, ch); 

        x += 8;
        if (x >= XSIZE)
        {
            x -= XSIZE;         // 回车
            y += 16;            // 换行
        }
    }  
}

绘制图片

绘制图片本质上也是绘制像素点,所以循环图片数据并绘制到像素点上即可显示图片

// 绘制图片数据
void lcd_draw_pic(const unsigned char *pimg_data) {
    u32 x,y,color, p = 0;
    for(y = 0; y < ROW; y++) {
        for (x = 0; x < COL; x++) {
            // 计算颜色
            color = (pimg_data[p + 0] << 0) | (pimg_data[p + 1] << 8) | (pimg_data[p + 2] << 16);
            // 计算坐标点并填充颜色
            lcd_draw_pixel(x, y, color);
            p += 3;
        }
    }
}
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值