1、FIMD结构框图

(1)Samsung的s5pv210的LCD控制器叫做FIMD(也叫显示控制器)。Display controller(显示控制器)包括用于将图像数据从相机接口控制器的本

地总线或位于系统存储器(例如:显存)中的视频缓冲器传送到外部LCD驱动器接口的逻辑。 LCD驱动接口支持三种接口,即RGB接口,I80接口和YUV

接口。显示控制器使用多达五个覆盖图像窗口(也就是虚拟窗口win0-win4),其支持各种颜色格式,如RGB、YUV。

FIMD在内部与AHB总线等相连接,在外部提供RGB接口、I80接口、YUV接口与外部相连接,我们实际使用的是RGB接口。这个接口就是我在上一篇博文中说的

LCD控制器的数据输出接口。RGB接口传输的是RGB编码的颜色数据,同理YUV接口传输的就是YUV编码的颜色数据,至于I80我不了解。本文讨论也是RGB接口。

(2)RGB接口信号

LCD_HSYNC :水平同步信号

LCD_VSYNC:垂直同步信号

LCD_VCLK:像素时钟,LCD工作时需要主板控制器给LCD模组一个工作时钟信号,就是VCLK

LCD_VDEN:数据有效标志,时序信号,和HSYNC、VSYNC结合使用。

LCD_VD[23:0]:24根数据线,用来传输图像信息。可见LCD是并行接口,速率才够快。

 

2、LCD显示一帧图像的过程

(1)LCD显示单位:帧(frame)

显示器上一整个画面的内容成为一个帧(frame),整个显示器工作时是一帧一帧的在显示。

帧内数据:一帧分为多行,一行分为多像素,因此一帧图像其实就是多个像素组成的矩阵。

帧外数据:整个视频由很多个帧构成,最终播放视频时逐个播放各个图像帧即可。

(2)显示一帧图像

首先把帧分为行,然后再把行分为像素,然后逐个像素去显示。(显示像素:其实就是LCD驱动器按照接收到的LCD控制器给的显示数据,驱动一个像素的液晶分子旋转,让

这个像素显示出相应的颜色值的过程)。

关键点:LCD控制器和驱动器之间一次只能传一个像素点的显示数据。所以一帧图像在屏幕上其实是串行的依次被显示上去的,不是同一时间显示出来的。

(3)为了向前兼容出现的六个时序参数

HSPW 水平同步信号脉宽

HBPD 水平同步信号前肩
HFPD 水平同步信号后肩
VSPW 垂直同步信号脉宽
VBPD 垂直同步信号前肩
VFPD 垂直同步信号后肩

 

 

(3.1)一行的通信过程是这样的:LCD控制器先发送一个HSYNC高电平脉冲(脉冲宽度是HSPW
),脉冲告诉驱动器下面的信息是一行信息。然后开始这一行信息,这一行信息包括3部
分:HBPD+有效行信息+HFPD。其中前肩和后肩都属于时序信息(和LCD屏幕具体有关),
有效行信息就是横向分辨率。所以你可以认为一行总共包含4部分:HSPW+HBPD+有效行信
息+HFPD。
(3.2)一帧图像其实就是一列,一列图像由多个行组成,每行都是上面讲的这个时序。
(3.3)一帧图像的通信过程是这样的:整个帧图像信号分为4部分:VSPW+VBPD+帧有效信号
+VFPD。VSPW是帧同步信号宽度,用来告诉驱动器一帧图像要开始了;VBPD和VFPD分别是
垂直同步信号前后肩。
(3.4)必须说明:这6个参数对于LCD显示器其实本来是没用的,这些信号其实是老式的CRT
显示器才需要的,LCD本身不需要,但是出于历史兼容性要求,LCD选择了兼容CRT显示器
的这些时序要求,所以理解LCD显示器时序和编程时,用CRT的方式来理解不会错。
(3.5)要注意,这几个时序参数本身是LCD屏幕本身的参数,与LCD控制器无关。所以同一个
主板如果接的屏幕不一样则时序参数设置也会不同。这些参数的来源一般是:第一,厂
家会直接给出,一般以实例代码的形式给出;第二,来自于LCD的数据手册。

 

2、虚拟屏幕叠加

(1)虚拟屏幕的意思是,我们平时看到的屏幕上显示出来的场景实际是很多个屏幕显示叠加在一起的效果(譬如新闻图像、电视台台标、下方飘动的字幕新闻)。

(2)像S5PV210的LCD控制器中有5个虚拟屏幕Window0到Window4,虚拟屏幕不存在于真实而存在于内存中。(之前讲过,LCd显示时实际是显示的是对应的内存

中的显存区域的数值)虚拟屏幕其实就是一个内存中的显存区域,有几个显存区域就有几个虚拟屏幕,但是这些虚拟屏幕都被映射到一个真实的显示屏上面,所以将

来真实的现实效果实际是这几个虚拟屏幕的显示内容的叠加。(叠加时要注意上面一层会覆盖下面一层,所以要注意谁在前谁在后,设置寄存器时有这个选项)。

(3)使用虚拟屏幕而不是整个LCD使用一个显存是有一定好处的:第一,可以保证不污染源图像,方便程序处理;第二,可以减少屏幕刷新,提高显示效率,减少CPU工作量。

 

3、虚拟显示

(1)如何实现在小分辨率的屏幕上(真实)显示大分辨率的图像?

细节上,我们需要屏幕上看到不同图像时,需要对显存区域进行刷新。即使我们只需要屏幕显示移动一点点,整个屏幕对应的显存空间也需要整个重新刷新,工作量和完全

重新显示一幅图像是一样的。这个显然不好,这样CPU刷新屏幕的工作量太大了,效率很低。

如何能够在显示一个大图片的不同区域时让CPU刷新屏幕工作量减少?

有,方法就是虚拟显示。具体做法就是在内存中建立显示缓存的时候实际建立一个很大的区域,然后让LCD去对应其中的一部分区域作为有效的显示区域。将来要显示大图像

时,直接将大图像全部一次性加载入显示缓存区,然后通过移动有效显示区域就可以显示大图像的不同区域了。

 

4、LCD控制器的主要寄存器

LCD相关寄存器寄存器介绍:因为这里面的寄存器实在太多了,我们的一个原则就是能够 

让LCD工作就行了,所以很多寄存器我们都没有涉及到,同一个寄存器中有些位也是没有
涉及到的,所以需要用到的位我们就设置,不需要我们暂且不管,如果以后做方面相关
的时候再去好好研究。

(1)VIDCON0:

bit[0]: 当前帧结束后是否使能视频输出控制器,也就是一帧画面显示结束后是否使能
LCD控制器,1使能 0禁止
bit[1]: 使能视频输出控制器(指的是当前) 1使能 0禁止
bit[2]: 时钟源选择 0=HCLK_DSYS 1=SCLK_FIMD
bit[13:6]: 时钟源分频器分频系数设置 本LCd控制器时钟不超过100MHZ,最终的时钟频
率是需要考虑LCD控制器和LCD驱动器两个方面,要低于两个中的最小值。
bit[4]: 时钟控制,使用分频器设置后的时钟还是源时钟,0是源时钟 1是分频器设置
后 的时钟。
bit[18]: 选择显示的模式,0是RGB并行模式 1是RGB串行模式
bit[28:26]: 选择视频输出格式,000=RGB接口 010=I80接口 011=I80接口...

 

(2)VIDCON1:
bit[4]: VSYNC信号反转控制,要跟LCd驱动器的电平要一致
bit[5]: HSYNC信号反转控制,要跟LCD驱动器的电平要一致

 

(3)VIDTCON0:
bit[7:0]: VSPW设置,意思在上面已经说过
bit[15:8]: VFPD设置
bit[23:16]: VBPD设置

 

(4)VIDTCON1:
bit[7:0]: HSPW设置
bit[15:8]: HFPD设置
bit[23:16]: HBPD设置

 

(5)VIDTCON2:
bit[10:0]: 显示水平大小设置,水平分辨率
bit[21:11]: 显示垂直大小设置,垂直分辨率

 

(6)WINCON0,WINCON1,WINCON2,WINCON3,WINCON4:
分别设置5个虚拟显示,对应分配的5个显存
bit[0]: 使能位,0禁止当前显存 1使能当前显存
bit[5:2]: 选择像素深度(bpp)
0000=1bpp 0001=2bpp 0010=4bpp.....1011=24bpp(R:8 G:8 B:8)
bit[15]: 像素交换使能控制位,像素交换就是 24位数据怎么分布的问题,是RGB还是
BGR
0禁止 1使能交换

 

(7)VIDOSD0A,VIDOSD1A,VIDOSD2A,VIDOSD3A....
用来设置虚拟显存的位置相关的
bit[10:0]: 指定左上像素的垂直坐标
bit[21:11]: 指定左上像素的水平坐标

 

(8)VIDOSD0B................
bit[10:0]: 指定右下角像素的垂直坐标
bit[21:11]: 指定右下角像素的水平坐标

 

(9)VIDOSD0C....... 设置显存的大小、注意显存的大小可以大于LCD实际的一帧数据的大
小的,这是需要理解的。
bit[23:0]: 注意这里设置其实是以像素为单位的,也就是说这里设置的是我们的显存

 

(10)VIDW00ADD0B0(分为5组)分别对应我们的5个虚拟显存
bit[31:0]: 显存的内存地址设置 , 将地址值写入这个寄存器

 

VIDW00ADD1B0(分为5组)分别对应我们的5个虚拟显存
bit[31:0]: 显存的大小,以字节为单位

 

SHADOWCON:用来设置我们的5个虚拟显存使能控制位和叠加方式寄存器,如果我们显示
的图片是需要叠加的话是需要好好看看这里的。
bit[0]: win0显存使能控制开关 0禁止 1使能 所以由此可知我们的每一个虚拟显存
的使能开关有两个一个就是在各自的寄存器上,另一个就是在这里集中的控制
,当然我们还有总的视频控制输出开关,之前说过了。
....

/*************************************************************************************/

附上裸机下操作LCD控制器的代码:(基于:S5PV210平台)

  1 #define HSPW             (40)                // 1~40 DCLK
  2 #define HBPD            (10 - 1)            // 46
  3 #define HFPD             (240 - 1)            // 16 210 354
  4 #define VSPW            (20)                // 1~20 DCLK
  5 #define VBPD             (10 - 1)            // 23
  6 #define VFPD             (30 - 1)            // 7 22 147
  7 
  8 #define FB_ADDR            (0x23000000)
  9 #define ROW                (600)
 10 #define COL                (1024)
 11 #define HOZVAL            (COL-1)
 12 #define LINEVAL            (ROW-1)
 13 
 14 #define XSIZE            COL
 15 #define YSIZE            ROW
 16 
 17 #define LeftTopX     0
 18 #define LeftTopY     0
 19 #define RightBotX   1023
 20 #define RightBotY   599
 21 
 22 #define  GPF0CON   (*(volatile unsigned int *)0xE0200120)
 23 #define  GPF1CON   (*(volatile unsigned int *)0xE0200140)
 24 #define  GPF2CON   (*(volatile unsigned int *)0xE0200160)
 25 #define  GPF3CON   (*(volatile unsigned int *)0xE0200180)
 26 
 27 #define  GPD0CON   (*(volatile unsigned int *)0xE02000A0)
 28 #define  GPD0DAT   (*(volatile unsigned int *)0xE02000A4)
 29 
 30 #define  DISPLAY_CONTROL   (*(volatile unsigned int *)0xE0107008)
 31 
 32 #define  VIDCON0   (*(volatile unsigned int *)0xF8000000) 
 33 #define  VIDCON1   (*(volatile unsigned int *)0xF8000004)
 34 
 35 #define  VIDTCON0  (*(volatile unsigned int *)0xF8000010)
 36 #define  VIDTCON1  (*(volatile unsigned int *)0xF8000014)
 37 #define  VIDTCON2  (*(volatile unsigned int *)0xF8000018)
 38 #define  VIDOSD0A  (*(volatile unsigned long *)0xF8000040)
 39 #define  VIDOSD0B  (*(volatile unsigned long *)0xF8000044)
 40 #define  VIDOSD0C  (*(volatile unsigned long *)0xF8000048)
 41 #define  VIDW00ADD0B0     (*(volatile unsigned long *)0xF80000A0)
 42 #define  VIDW00ADD1B0     (*(volatile unsigned long *)0xF80000D0)
 43 #define  WINCON0         (*(volatile unsigned long *)0xF8000020)
 44 #define  SHADOWCON         (*(volatile unsigned long *)0xF8000034)
 45 
 46 
 47 typedef unsigned int u32;
 48 typedef unsigned short u16;
 49 
 50 
 51 /*填充像素点*/
 52 static inline void lcd_draw_pixel(u32 x, u32 y, u32 color)
 53 {
 54     *(u32 *)(FB_ADDR + (COL*x + y)*4) = color;
 55 }
 56 
 57 /*填充LCD背景*/
 58 void lcd_draw_background(const u32 color)
 59 {
 60      u32 i = 0;
 61      u32 j = 0;
 62      
 63      for (i = 1; i <= ROW; ++i)
 64      {
 65          for (j = 0; j <= COL; ++j)
 66              lcd_draw_pixel(i, j, color);
 67      }
 68 } 
 69 
 70 /*在LCd上绘制水平线*/
 71 void lcd_draw_lline(const u32 x, const u32 y, const u32 length,
 72                      const u32 width, const u32 color)              
 73 {
 74     volatile u32 i = 0;
 75     volatile u32 j = 0;
 76     
 77     for (i = x; i < width+x; i++)
 78     {
 79         for (j = y; j < length+y; j++)
 80         {
 81             lcd_draw_pixel(i, j, color);
 82         }
 83     }    
 84 }
 85 
 86 /*在LCd上绘制垂直线*/
 87 void lcd_draw_vline(const u32 x, const u32 y, const u32 length,
 88                      const u32 width, const u32 color)
 89 {
 90     volatile u32 i = 0;
 91     volatile u32 j = 0;
 92     
 93     for (i = x; i < length+x; i++)
 94     {
 95         for (j = y; j < width+y; j++)
 96         {
 97             lcd_draw_pixel(i, j, color);
 98         }
 99     }
100 }
101 
102 // glib库中的画线函数,可以画斜线,线两端分别是(x1, y1)和(x2, y2)
103 void glib_line(unsigned int x1, unsigned int y1, 
104                   unsigned int x2, unsigned int y2, unsigned int color)
105 {
106     int dx,dy,e;
107     dx=x2-x1; 
108     dy=y2-y1;
109     
110     if(dx>=0)
111     {
112         if(dy >= 0) // dy>=0
113         {
114             if(dx>=dy) // 1/8 octant
115             {
116                 e=dy-dx/2;  
117                 while(x1<=x2)
118                 {
119                     lcd_draw_pixel(x1,y1,color);
120                     if(e>0){y1+=1;e-=dx;}    
121                     x1+=1;
122                     e+=dy;
123                 }
124             }
125             else        // 2/8 octant
126             {
127                 e=dx-dy/2;
128                 while(y1<=y2)
129                 {
130                     lcd_draw_pixel(x1,y1,color);
131                     if(e>0){x1+=1;e-=dy;}    
132                     y1+=1;
133                     e+=dx;
134                 }
135             }
136         }
137         else           // dy<0
138         {
139             dy=-dy;   // dy=abs(dy)
140 
141             if(dx>=dy) // 8/8 octant
142             {
143                 e=dy-dx/2;
144                 while(x1<=x2)
145                 {
146                     lcd_draw_pixel(x1,y1,color);
147                     if(e>0){y1-=1;e-=dx;}    
148                     x1+=1;
149                     e+=dy;
150                 }
151             }
152             else     // 7/8 octant
153             {
154                 e=dx-dy/2;
155                 while(y1>=y2)
156                 {
157                     lcd_draw_pixel(x1,y1,color);
158                     if(e>0){x1+=1;e-=dy;}    
159                     y1-=1;
160                     e+=dx;
161                 }
162             }
163         }    
164     }
165     else //dx<0
166     {
167         dx=-dx;        //dx=abs(dx)
168         if(dy >= 0) // dy>=0
169         {
170             if(dx>=dy) // 4/8 octant
171             {
172                 e=dy-dx/2;
173                 while(x1>=x2)
174                 {
175                     lcd_draw_pixel(x1,y1,color);
176                     if(e>0){y1+=1;e-=dx;}    
177                     x1-=1;
178                     e+=dy;
179                 }
180             }
181             else        // 3/8 octant
182             {
183                 e=dx-dy/2;
184                 while(y1<=y2)
185                 {
186                     lcd_draw_pixel(x1,y1,color);
187                     if(e>0){x1-=1;e-=dy;}    
188                     y1+=1;
189                     e+=dx;
190                 }
191             }
192         }
193         else           // dy<0
194         {
195             dy=-dy;   // dy=abs(dy)
196 
197             if(dx>=dy) // 5/8 octant
198             {
199                 e=dy-dx/2;
200                 while(x1>=x2)
201                 {
202                     lcd_draw_pixel(x1,y1,color);
203                     if(e>0){y1-=1;e-=dx;}    
204                     x1-=1;
205                     e+=dy;
206                 }
207             }
208             else        // 6/8 octant
209             {
210                 e=dx-dy/2;
211                 while(y1>=y2)
212                 {
213                     lcd_draw_pixel(x1,y1,color);
214                     if(e>0){x1-=1;e-=dy;}    
215                     y1-=1;
216                     e+=dx;
217                 }
218             }
219         }    
220     }
221 }
222 
223 //画圆函数,圆心坐标是(centerX, centerY),半径是radius,圆的颜色是color
224 void draw_circular(unsigned int centerX, unsigned int centerY, 
225                        unsigned int radius, unsigned int color)
226 {
227     int x,y ;
228     int tempX,tempY;;
229     int SquareOfR = radius*radius;
230 
231     for(y=0; y<XSIZE; y++)
232     {
233         for(x=0; x<YSIZE; x++)
234         {
235             if(y<=centerY && x<=centerX)
236             {
237                 tempY=centerY-y;
238                 tempX=centerX-x;                        
239             }
240             else if(y<=centerY&& x>=centerX)
241             {
242                 tempY=centerY-y;
243                 tempX=x-centerX;                        
244             }
245             else if(y>=centerY&& x<=centerX)
246             {
247                 tempY=y-centerY;
248                 tempX=centerX-x;                        
249             }
250             else
251             {
252                 tempY = y-centerY;
253                 tempX = x-centerX;
254             }
255             if ((tempY*tempY+tempX*tempX)<=SquareOfR)
256                 lcd_draw_pixel(x, y, color);
257         }
258     }
259 }
260 
261 
262 void lcd_draw_Chinese(const u32 x, const u32 y, 
263                      const u32 color, const char *Chinese)
264 {
265     u32 j = 0;
266     u32 count = 0;
267     u32 g = 0;
268     u32 y1 = y;
269     const unsigned char *ptr = (unsigned char *)0;
270     
271     if (*Chinese == 'd')
272         ptr = deng;
273     else
274         ptr = tao;
275     
276     for (g = x; g < x+97; g++)
277     {
278         u32 i = 0;
279         u32 k = 0;
280         
281         for (i = count; i < count +12; i++)
282         {
283             for (j = 0, k = y1; j <= 7; j++, k++)
284             {
285                 if (((1 << j) & ptr[count]) == 1)
286                     lcd_draw_pixel(x, k, color);                    
287             }
288             
289             y1 = k;    //更新y值
290         }
291         
292         y1 = y;
293         
294         count = i;        //更新count计数
295     }
296 }                     
297 
298 
299 void lcd_init(void)
300 {
301     /*GPIO初始化*/
302     GPF0CON = 0x22222222;
303     GPF1CON = 0x22222222;
304     GPF2CON = 0x22222222;
305     GPF3CON = 0x332222;
306     
307     /*打开LCD背光*/
308     GPD0CON &= ~(0xf << 0); 
309     GPD0CON |= (1 << 0);     //配置为输出模式
310     GPD0DAT &= ~(1 << 0);    //输出为低电平
311     
312     /*设置RGB格式由FIMD输出 其实就是指定GRB格式由FIMD输出*/  // Display path selection
313     DISPLAY_CONTROL = 2 << 0;                // RGB=FIMD I80=FIMD ITU=FIMD
314     
315     /*CON0寄存器配置*/
316     VIDCON0 &= ~((1 << 18) | (7 << 26));     //选择显示模式和输出格式
317     VIDCON0 &= ~(1 << 2);                   //选择时钟源HCLK
318     VIDCON0 &= ~(0xff << 6);               
319     VIDCON0 |= ((4 << 6) | (1 << 4));       //设置分频器系数和使能分频器  
320     VIDCON0 |= ((1 << 0)|(1 << 1)) ;        //使能视频输出和使能帧结束视频输出    
321     
322     /*CON1寄存器配置*/
323     VIDCON1 |= ((1<<5) | (1<<6));           //VSYNC和HSYNC信号反转,是否反转与LCd显示器相关
324     
325     /*关于时序的6个参数的设置*/
326     VIDTCON0 &= ~(0xffffff << 0);
327     VIDTCON0 |= (VSPW << 0) | (VFPD << 8) | (VBPD << 16);
328     VIDTCON1 &= ~(0xffffff << 0);
329     VIDTCON1 |= (HSPW << 0) | (HFPD << 8) | (HBPD << 16);
330     
331     /*设置LCD像素,对应实际的LCd像素*/
332     VIDTCON2 &= ~(0x3fffff << 0);
333     VIDTCON2 |= (LINEVAL << 11) | (HOZVAL << 0);
334     
335     /*设置window0的像素空间范围和空间大小*/
336     VIDOSD0A &= ~(0x3fffff << 0);
337     VIDOSD0A |= (LeftTopX << 11) | (LeftTopY << 0);
338     VIDOSD0B &= ~(0x3fffff << 0);
339     VIDOSD0B |= (RightBotX << 11) | (RightBotY << 0);
340     VIDOSD0C &= ~(0xffffffff << 0);
341     VIDOSD0C |= (LINEVAL + 1) * (HOZVAL + 1);
342     
343     /*设置window0显存的内存地址*/
344     VIDW00ADD0B0 = FB_ADDR;     //内存地址
345     VIDW00ADD1B0 = (HOZVAL + 1) * (LINEVAL + 1) * 4;    //显存字节大小,这个空间可以做很大的
346 
347     /*使能window0*/
348     WINCON0 &= ~(0xf << 2);
349     WINCON0 |= (0xb << 2);             //选择像素深度 = 24bpp(RGB888)
350     WINCON0 |= (1 << 0) | (1 << 15); //使能显存和使能交换
351     
352     // 使能channel 0传输数据 对应window0
353     SHADOWCON = 0x1;
354     
355 }
View Code

 

 

参考:《朱友鹏嵌入式Linux开发\1.ARM裸机全集\1.14.ARM裸机第十四部分-LCD显示器》

          http://blog.chinaunix.net/uid-27411029-id-3302040.html

          http://blog.csdn.net/xubin341719/article/details/9177085