s3c2440裸板驱动之LCD

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_22863733/article/details/78251540

        s3c2440片上集成了LCD控制器,用于驱动外接LCD屏,LCD屏的硬件特性是固定的,LCD裸板编程重点是根据外接LCD的硬件特性和用户需求配置LCD控制器的寄存器组。而配置寄存器的重点又是理解LCD的工作时序,本文重点分析了LCD的工作时序和与时序相关的寄存器的配置,也结合代码简单介绍LCD使用思路。

        LCD控制器提供了驱动外接LCD屏所需的所有控制信号,REGBANK是LCD控制器的寄存器组,含17个寄存器及一块256*16的调色板内存,用来设置各项参数,LCDCDMA是LCD控制器专用的DMA信道,可以自动的从系统总线上取到图像数据。将相关寄存器设置好后,并将帧内存(frame memory)的地址告诉LCD控制器,它即可自动的发起DMA传输,从帧内存中得到图像数据,最终在上述信号的控制下出现在数据总线,VD[23:0]上。用户只需要把要显示的图像数据写入帧内存中。

LCD相关名词解释:

        数据传输方式有4位单扫,4位双扫,8位单扫等

单扫:从上到下,从左到右,一个一个地发送数据。

双扫:整屏分上下两部分,每一部分单扫

4位、8位:多少位表示多少根数据线,4位双扫亦是8根数据线

BPP:bit per piexl,表示每个像素使用多少位来表示其颜色。

      1BPP:单色(黑白)

      2BPP:4级灰度(共四种颜色)

      4BPP:16级灰度(共16种颜色)

      8BPP:256色

      2BPP:4096色

      16BPP:64k色

调色板:颜色库,一块存着RGB颜色值的内存。调色板颜色值一般为16BPP,565格式或5551格式。一般8BPP的显示模式要用到调色板,此时帧缓冲区中的数据不是像素的颜色值,而是调色板中颜色值的索引值,真正的颜色值是调色板中的颜色值。这样8BPP显示模式的像素实际是16BPP的。

 TFT LCD Timing Example:

  

名词解释:

VSYNC:垂直同步信号

VSYNC频率:帧/秒,垂直频率或场频率,相当于显示器的频率

VDEN:数据有效使能

HSYNC:水平同步信号

VCLK:像素时钟

VD:数据信号

LEND:行结束信号(行有效数据结束信号)(这不是必须的)

VSPW (vertical sync pulse width):表示VSYNC信号脉冲宽度值为(VSPW+1)个HSYNC信号周期。(若放在一周期最后,则可形象理解为电子枪回扫所需的时间),即(VSPW+1)行,这(VSPW+1)行数据无效。

VBPD (vertical back porch):表示VSYNC脉冲信号结束以后还要经过(VBPD+1)个HSYNC信号周期,有效的行数据才出现。所以,在VSYNC信号刚刚开始之后,总共要经过(VSPW+1 +VBPD+1)个无效的行,第一个有效的行才出现。

LINEVAL:表示(LINEVAL+1)个有效行。

VFPD (vertical front porch):表示有效行结束后要经过(VFPD+1)个无效行。

HSPW (hertical sync pulse width):概念与VSPW相似,表示HSYNC信号脉冲宽度值为(HSPW+1)个VCLKC信号周期。(若放在一HSYNC周期最后,则可形象理解为电子枪行回扫所需的时间),即(HSPW+1)个像素,这(HSPW+1)个像素数据无效。

HBPD (hertical back porch):表示HSYNC脉冲信号结束以后还要经过(HBPD+1)个VCLK信号周期,有效的像素数据才出现。所以,在HSYNC信号刚刚开始之后,总共要经过(HSPW+1 +HBPD+1)个无效的像素,第一个有效的像素才出现。

HOZVAL:表示一行中的(HOZVAL+1)个有效像素。

HFPD (vertical front porch):表示有效像素结束后要经过(HFPD+1)个无效像素。

反映在屏幕上的关系示意图:

 

 具体结合2440开发板上日立液晶模组TX09D70VM1CBA的时序图分析:

 

 

        时序图原图中T5的相对长度与实际不符,我用红色部分把它投影到VSYNC周期里面,相对关系就非常清晰明了了。 

 

 解释其中四个单词:

Vertical Sync Start:有效行开始到下一个VSYNC开始的行数

Vertical Sync end:有效行开始到下一个VSYNC结束的行数

       则有 Vertical Sync end - Vertical Sync Start = 1个VSYNC信号脉冲宽度(VSPW+1 行)

Vertical Blank Time:总黑框行数,值为(VSPW+1+VBPD+1+VFPD+1)

Vertical Display End:有效行。

        对于s3c2440控制器,与时序相关的要配置的项是VBPD、LINEVAL、VFPD、VSPW、HBPD、HOZVAL、HFPD。

结合8.2时序图和表8.1数据分析得:

VSPW +1= T1=1,  VSPW=0

VBPD+1 = T0-T2-T1=327 - 322 - 1 = 4,  VBPD =3

VFPD+1 = T2-T5= 322 -320 = 2,  VFPD = 1

LINEVAL +1=T5 =320,  LINEVAL = 319

同理:

HSPW +1= T7=5,  HSPW=4

HBPD+1 = T6-T7-T8=273 - 5 - 251 = 17,  HBPD =16

HFPD+1 = T8-T11= 251 -240 = 11,  HFPD = 10

HOZVAL +1=T5 =240,  HOZVAL = 239

        其他寄存器的配置及寄存器的位操作技巧,由于内容多繁杂,就不展开讨论了。也不难,只是需要大家花时间细心去推敲。

 裸板LCD的使用步骤:

 1、配置引脚用于LCD,函数:Lcd_Port_Init()

2、根据显示模式,配置LCD控制寄存器组,函数:void Tft_Lcd_Init(int type)

3、打开LCD电源

4、使能LCD控制器输出信号

5、初始化调色板: 函数:Lcd_Palette8Bit_Init() 

6、清屏 ClearScr(0x0) 

7、向帧内存中写图像。

下面的代码参考了百问网韦东山老师提供的源码。

1、配置引脚用于LCD,函数:Lcd_Port_Init()


 
 
  1. /*
  2. * 初始化用于LCD的引脚
  3. */
  4. void Lcd_Port_Init(void)
  5. {
  6. GPCUP = 0xffffffff; // 禁止内部上拉
  7. GPCCON = 0xaaaaaaaa; // GPIO管脚用于VD[7:0],LCDVF[2:0],VM,VFRAME,VLINE,VCLK,LEND
  8. GPDUP = 0xffffffff; // 禁止内部上拉
  9. GPDCON = 0xaaaaaaaa; // GPIO管脚用于VD[23:8]
  10. GPBCON &= ~(GPB0_MSK); // Power enable pin
  11. GPBCON |= GPB0_out;
  12. GPBDAT &= ~( 1<< 0); // Power off
  13. printf( "Initializing GPIO ports..........\n");
  14. }

2、根据显示模式,配置LCD控制寄存器组,以MODE_TFT_8BIT_240320为例。

 函数:void Tft_Lcd_Init(int type):


 
 
  1. /*
  2. * 初始化LCD控制器
  3. * 输入参数:
  4. * type: 显示模式
  5. * MODE_TFT_8BIT_240320 : 240*320 8bpp的TFT LCD
  6. * MODE_TFT_16BIT_240320 : 240*320 16bpp的TFT LCD
  7. * MODE_TFT_8BIT_640480 : 640*480 8bpp的TFT LCD
  8. * MODE_TFT_16BIT_640480 : 640*480 16bpp的TFT LCD
  9. */
  10. void Tft_Lcd_Init(int type)
  11. {
  12. switch(type)
  13. {
  14. case MODE_TFT_8BIT_240320:
  15. /*
  16. * 设置LCD控制器的控制寄存器LCDCON1~5
  17. * 1. LCDCON1:
  18. * 设置VCLK的频率:VCLK(Hz) = HCLK/[(CLKVAL+1)x2]
  19. * 选择LCD类型: TFT LCD
  20. * 设置显示模式: 8BPP
  21. * 先禁止LCD信号输出
  22. * 2. LCDCON2/3/4:
  23. * 设置控制信号的时间参数
  24. * 设置分辨率,即行数及列数
  25. * 现在,可以根据公式计算出显示器的频率:
  26. * 当HCLK=100MHz时,
  27. * Frame Rate = 1/[{(VSPW+1)+(VBPD+1)+(LIINEVAL+1)+(VFPD+1)}x
  28. * {(HSPW+1)+(HBPD+1)+(HFPD+1)+(HOZVAL+1)}x
  29. * {2x(CLKVAL+1)/(HCLK)}]
  30. * = 60Hz
  31. * 3. LCDCON5:
  32. * 设置显示模式为8BPP时,调色板中的数据格式: 5:6:5
  33. * 设置HSYNC、VSYNC脉冲的极性(这需要参考具体LCD的接口信号): 反转
  34. * 字节交换使能
  35. */
  36. LCDCON1 = (CLKVAL_TFT_240320<< 8) | (LCDTYPE_TFT<< 5) | \
  37. (BPPMODE_8BPP<< 1) | (ENVID_DISABLE<< 0);
  38. LCDCON2 = (VBPD_240320<< 24) | (LINEVAL_TFT_240320<< 14) | \
  39. (VFPD_240320<< 6) | (VSPW_240320);
  40. LCDCON3 = (HBPD_240320<< 19) | (HOZVAL_TFT_240320<< 8) | (HFPD_240320);
  41. LCDCON4 = HSPW_240320;
  42. LCDCON5 = (FORMAT8BPP_565<< 11) | (HSYNC_INV<< 9) | (VSYNC_INV<< 8) | \
  43. (BSWP<< 1);
  44. /*
  45. * 设置LCD控制器的地址寄存器LCDSADDR1~3
  46. * 帧内存与视口(view point)完全吻合,
  47. * 图像数据格式如下(8BPP时,帧缓冲区中的数据为调色板中的索引值):
  48. * |----PAGEWIDTH----|
  49. * y/x 0 1 2 239
  50. * 0 idx idx idx ... idx
  51. * 1 idx idx idx ... idx
  52. * 1. LCDSADDR1:
  53. * 设置LCDBANK、LCDBASEU
  54. * 2. LCDSADDR2:
  55. * 设置LCDBASEL: 帧缓冲区的结束地址A[21:1]
  56. * 3. LCDSADDR3:
  57. * OFFSIZE等于0,PAGEWIDTH等于(240/2)
  58. */
  59. LCDSADDR1 = ((LCDFRAMEBUFFER>> 22)<< 21) | LOWER21BITS(LCDFRAMEBUFFER>> 1);
  60. LCDSADDR2 = LOWER21BITS((LCDFRAMEBUFFER+ \
  61. (LINEVAL_TFT_240320+ 1)*(HOZVAL_TFT_240320+ 1)* 1)>> 1);
  62. LCDSADDR3 = ( 0<< 11) | (LCD_XSIZE_TFT_240320/ 2);
  63. /* 禁止临时调色板寄存器 */
  64. TPAL = 0;
  65. fb_base_addr = LCDFRAMEBUFFER;
  66. bpp = 8;
  67. xsize = 240;
  68. ysize = 320;
  69. break;
  70. ......
  71. }

3、打开LCD电源。

函数:Lcd_PowerEnable(0, 1);       // 设置LCD_PWREN有效,它用于打开LCD的电源


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

4、使能LCD控制器输出信号

函数:Lcd_EnvidOnOff(1);          // 使能LCD控制器输出信号


 
 
  1. /*
  2. * 设置LCD控制器是否输出信号
  3. * 输入参数:
  4. * onoff:
  5. * 0 : 关闭
  6. * 1 : 打开
  7. */
  8. void Lcd_EnvidOnOff(int onoff)
  9. {
  10. if (onoff == 1)
  11. {
  12. LCDCON1 |= 1; // ENVID ON
  13. GPBDAT |= ( 1<< 0); // Power on
  14. }
  15. else
  16. {
  17. LCDCON1 &= 0x3fffe; // ENVID Off
  18. GPBDAT &= ~( 1<< 0); // Power off
  19. }
  20. }

5、初始化调色板:

 函数:Lcd_Palette8Bit_Init();     // 初始化调色板


 
 
  1. /*
  2. * 设置调色板
  3. */
  4. void Lcd_Palette8Bit_Init(void)
  5. {
  6. int i;
  7. volatile unsigned int *palette;
  8. LCDCON1 &= ~ 0x01; // stop lcd controller
  9. LCDCON5 |= (FORMAT8BPP_565<< 11); // 设置调色板中数据格式为5:6:5
  10. palette = ( volatile unsigned int *)PALETTE;
  11. for (i = 0; i < 256; i++)
  12. *palette++ = DEMO256pal[i];
  13. LCDCON1 |= 0x01; // re-enable lcd controller
  14. }、

6、清屏


 
 
  1. ClearScr( 0x0)
  2. {
  3. //往帧内存中写0,或者用临时调色板方法。
  4. }


 7、向帧内存中写图像。先在屏幕指定位置画一个点,再画一条线。

        函数:void PutPixel(UINT32 x, UINT32 y, UINT32 color)

      void DrawLine(int x1,int y1,int x2,int y2,int color)

指定位置画一点:


 
 
  1. /*
  2. * 画点
  3. * 输入参数:
  4. * x、y : 象素坐标
  5. * color: 颜色值
  6. * 对于16BPP: color的格式为0xAARRGGBB (AA = 透明度),
  7. * 需要转换为5:6:5格式
  8. * 对于8BPP: color为调色板中的索引值,
  9. * 其颜色取决于调色板中的数值
  10. */
  11. void PutPixel(UINT32 x, UINT32 y, UINT32 color)
  12. {
  13. UINT8 red,green,blue;
  14. switch (bpp){
  15. case 16:
  16. {
  17. UINT16 *addr = (UINT16 *)fb_base_addr + (y * xsize + x);
  18. red = (color >> 19) & 0x1f; // 5 BIT
  19. green = (color >> 10) & 0x3f; // 6 bit
  20. blue = (color >> 3) & 0x1f; // 5 bit
  21. color = (red << 11) | (green << 5) | blue; // 格式5:6:5
  22. *addr = (UINT16) color;
  23. break;
  24. }
  25. case 8:
  26. {
  27. UINT8 *addr = (UINT8 *)fb_base_addr + (y * xsize + x);
  28. *addr = (UINT8) color;
  29. break;
  30. }
  31. default:
  32. break;
  33. }
  34. }

需要注意的是PutPixel()传入的颜色值是32位的,格式是AARRGGBB,这里转为565格式,要取出R、G、B所在位的高5位,高6位,高5位,然后合并为565格式。
画任意一条线段: 


 
 
  1. /*
  2. * 画线
  3. * 输入参数:
  4. * x1、y1 : 起点坐标
  5. * x2、y2 : 终点坐标
  6. * color : 颜色值
  7. * 对于16BPP: color的格式为0xAARRGGBB (AA = 透明度),
  8. * 需要转换为5:6:5格式
  9. * 对于8BPP: color为调色板中的索引值,
  10. * 其颜色取决于调色板中的数值
  11. */
  12. void DrawLine(int x1,int y1,int x2,int y2,int color)
  13. {
  14. }
    实现方法大家可以参考东山老师源码,也可以参考bresenham算法,无非就是一条线段,取距离这条线段最近的离散的整数点,每取一个整数点PutPixel.
                </div>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值