1 lcd硬件操作原理

android的lcd驱动程序 设计lcd驱动程序_android的lcd驱动程序

Lcd显示的过程如下:

  1. 从显存中输出显示颜色的数据,在屏幕左上角的第一个点开始显示,每间隔一个像素时钟VCLK,向右移动一个点,当移到最右边时,会根据水平同步信号HSYNC跳到下一行的最左边;
  2. 又重复步骤1的操作,直到显示到右下角最后一个点为止,这时根据垂直同步信号YSYNC,又跳回到左上角第一个点开始下一帧图片的显示。

2 编写驱动

在上一章节结尾已经理出了lcd驱动程序的大致流程。首先来实现入口函数lcd_init函数。

  1. 分配一个fb_info空间;
  2. 对fb_info指向的结构体进行配置;
  1. 设置可变参数;
  2. 设置固定参数;
  3. 设置fops;
  1. 设置其他参数。
  2. 硬件相关的配置;
  1. 设置lcd相关的引脚;
  2. 设置lcd控制器的寄存器;
  3. 设置显存,并将显存的地址通知到lcd控制器;
  4. 开启lcd,lcd控制器以及背光。
  1. 注册framebuffer。

出口函数lcd_exit主要实现对资源的释放,具体操作如下:

  1. 卸载framebuffer;
  2. 关闭lcd控制器,背光;
  3. 释放显存空间;
  4. 解除所有io资源的映射;
  5. 释放申请的fb_info空间。

详细的代码如下所示。

1. #include <linux/module.h>  
2. #include <linux/kernel.h>  
3. #include <linux/errno.h>  
4. #include <linux/string.h>  
5. #include <linux/mm.h>  
6. #include <linux/slab.h>  
7. #include <linux/delay.h>  
8. #include <linux/fb.h>  
9. #include <linux/init.h>  
10. #include <linux/dma-mapping.h>  
11. #include <linux/interrupt.h>  
12. #include <linux/workqueue.h>  
13. #include <linux/wait.h>  
14. #include <linux/platform_device.h>  
15. #include <linux/clk.h>  
16.     
17. #include <asm/io.h>  
18. #include <asm/uaccess.h>  
19. #include <asm/div64.h>  
20.     
21. #include <asm/mach/map.h>  
22. #include <asm/arch/regs-lcd.h>  
23. #include <asm/arch/regs-gpio.h>  
24. #include <asm/arch/fb.h>  
25.     
26. staticvolatilelong *gpbcon;  
27. staticvolatilelong *gpbdat;  
28. staticvolatilelong *gpccon;  
29. staticvolatilelong *gpdcon;  
30. staticvolatilelong *gpgcon;  
31. static u32 pseudo_pal[16];  
32.     
33. struct lcd_regs{  
34. long lcdcon1;  
35. long lcdcon2;  
36. long lcdcon3;  
37. long lcdcon4;  
38. long lcdcon5;  
39. long lcdsaddr1;  
40. long lcdsaddr2;  
41. long lcdsaddr3;  
42. long redlut;  
43. long greenlut;  
44. long bluelut;  
45. long reserved[9];  
46. long dithmode;  
47. long tpal;  
48. long lcdintpnd;  
49. long lcdsrcpnd;  
50. long lcdintmask;  
51. long tconsel;  
52. };  
53. staticvolatilestruct lcd_regs *lcdregs;  
54.         
55. staticstruct fb_info      *s3cxx_lcd;  
56. staticint s3cfb_setcolreg(unsigned regno,  
57.                    unsigned red, unsigned green, unsigned blue,  
58. struct fb_info *info);  
59.     
60. staticstruct fb_ops s3c_lcdfb_ops = {  
61.     .owner      = THIS_MODULE,  
62.     .fb_setcolreg   = s3cfb_setcolreg,  
63.     .fb_fillrect    = cfb_fillrect,  
64.     .fb_copyarea    = cfb_copyarea,  
65.     .fb_imageblit   = cfb_imageblit,  
66. };  
67.     
68. staticinlineintintstruct fb_bitfield *bf)  
69. {  
70.     chan &= 0xffff;  
71.     chan >>= 16 - bf->length;  
72. return chan << bf->offset;  
73. }  
74.     
75. staticint s3cfb_setcolreg(unsigned regno,  
76.                    unsigned red, unsigned green, unsigned blue,  
77. struct fb_info *info)  
78. {  
79. int val;  
80.         
81. if(regno > 16)  
82. return 1;  
83.     
84. /* 用red green blue三原色构造出val */  
85.     val  = chan_to_field(red,   &info->var.red);  
86.     val |= chan_to_field(green, &info->var.green);  
87.     val |= chan_to_field(blue,  &info->var.blue);  
88.     
89.     pseudo_pal[regno] = val;  
90. return 0;     
91. }  
92.     
93. staticintvoid)  
94. {  
95. /* 1、分配一个fb_info空间  
96.     s3cxx_lcd = framebuffer_alloc(0, NULL);  
97.         
98. /* 2、配置  
99. /* 2.1、设置可变参数  
100.     s3cxx_lcd->var.xres          = 480;  
101.     s3cxx_lcd->var.yres          = 272;  
102.     s3cxx_lcd->var.xres_virtual  = 480;  
103.     s3cxx_lcd->var.yres_virtual  = 272;  
104.     s3cxx_lcd->var.bits_per_pixel    = 16;  
105. /* RGB:565 */  
106.     s3cxx_lcd->var.red.offset        = 11;  
107.     s3cxx_lcd->var.red.length        = 5;  
108.     s3cxx_lcd->var.green.offset      = 5;  
109.     s3cxx_lcd->var.green.length      = 6;  
110.     s3cxx_lcd->var.blue.offset       = 0;  
111.     s3cxx_lcd->var.blue.length       = 5;      
112.     s3cxx_lcd->var.activate          = FB_ACTIVATE_NOW;  
113.             
114. /* 2.2、设置固定参数  
115. "mylcd");  
116.     s3cxx_lcd->fix.smem_len      = 480*272*16/8;  
117.     s3cxx_lcd->fix.type          = FB_TYPE_PACKED_PIXELS;  
118.     s3cxx_lcd->fix.visual            = FB_VISUAL_TRUECOLOR;  
119.     s3cxx_lcd->fix.line_length       = 480*2;  
120.         
121. /* 2.3、设置fbops */  
122.     s3cxx_lcd->fbops             = &s3c_lcdfb_ops;  
123. /* 2.4、设置其他参数  
124.     s3cxx_lcd->pseudo_palette        = pseudo_pal;  
125.     s3cxx_lcd->screen_size           = 480*272*16/8;  
126.         
127. /* 3、硬件相关的操作  
128. /* 3.1、设置lcd的引脚  
129.     gpbcon = ioremap(0x56000010, 8);  
130.     gpbdat = gpbcon + 1;  
131.     gpccon = ioremap(0x56000020, 4);  
132.     gpdcon = ioremap(0x56000030, 4);  
133.     gpgcon = ioremap(0x56000060, 4);  
134.     
135. /* GPIOc管脚用于VD[7:0]、LCD_LPCREVB、LCD_LPCREV、 LCD_LPCOE、VM、VFRAME、VLINE、VCLK、LEND */  
136.     *gpccon = 0Xaaaaaaaa;  
137. /*GPIOd管脚用于VD[23:8] */  
138.     *gpdcon = 0Xaaaaaaaa;  
139. /* GPIOB0管脚设为输出引脚  
140.     *gpbcon &= ~0x03;  
141.     *gpbcon |= 0x01;  
142.     *gpbdat &= ~0x01;  
143. /*gpiog4管脚用于LCD_PWRDN */  
144.     *gpgcon |= (3<<8);  
145.         
146. /* 3.2、设置lcd控制器的寄存器  
147. sizeof(struct lcd_regs));  
148. /* lcdcon1 
149.  
150.  
151.  
152.  
153.  
154.  
155.   
156.     lcdregs->lcdcon1 = (4 << 8)| (3 << 5) | (0x0c << 1) ;  
157. /* 
158.      * lcdcon2 垂直方向的时间参数 
159.      * bit[31:24] VBPD VSYNC之后多久才能发出第一行数据 
160.      *            LCD手册上 
161.  
162.      * bit[23:14] LINEVAL 显示的行数 
163.  
164.      * bit[13:6]  VFPD 最有一行数据发出多久后,再发出VSYNC 
165.  
166.  
167.      * bit[5:0]   VSPW VSYNC信号的脉冲宽度 
168.  
169.  
170.   
171.     lcdregs->lcdcon2 = (1 << 24) | (271 << 14) | (1 << 6) |(9);  
172. /* 
173.      * lcdcon3 水平方向的时间参数 
174.      * bit[25:19] HBPD HSYNC之后多久才能发出第一行数据 
175.      *            LCD手册上 
176.  
177.      * bit[18:8]  HOZVAL 显示的列数 
178.  
179.      * bit[7:0]   HFPD 最后一行的最后一个数据发出多久后,再发出HSYNC 
180.  
181.  
182.   
183.      lcdregs->lcdcon3 = (1 << 19) | (479 << 8) | (1 << 0);  
184. /* 
185.      * lcdcon4 水平方向的时间参数 
186.      * bit[7:0]   HSPW HSYNC信号的脉冲宽度 
187.  
188.  
189.   
190.     lcdregs->lcdcon4 = (40 << 0);  
191. /* 
192.      * 信号的极性 
193.  
194.  
195.  
196.  
197.  
198.  
199.  
200.      * bit[0]  HWSWP = 1 参照2440手册 
201.   
202.     lcdregs->lcdcon5 = (1 << 11) | (0 << 10) | (1 << 9) | (1 << 8) | (1 << 0);  
203.         
204. /* 3.3、设置显存,并将显存的地址通知到lcd控制器*/  
205.     s3cxx_lcd->screen_base = dma_alloc_writecombine(NULL, s3cxx_lcd->fix.smem_len, &s3cxx_lcd->fix.smem_start, GFP_KERNEL);  
206.     lcdregs->lcdsaddr1 = (s3cxx_lcd->fix.smem_start >> 1) &~(3<<30);  
207.     lcdregs->lcdsaddr2 = ((s3cxx_lcd->fix.smem_start + s3cxx_lcd->fix.smem_len) >> 1) & 0x1fffff;  
208. /* 一行的长度(单位:2字节) */  
209.     
210. /* 使能LCD控制器  
211. /* 使能lcd */  
212. /*输出高电平,打开背光  
213.         
214. //s3cxx_lcd->fix.smem_start          /* 显存的物理地址  
215. /* 4、注册  
216.     register_framebuffer(s3cxx_lcd);  
217.         
218. return 0;  
219. }  
220.     
221.     
222. staticvoid lcd_exit()  
223. {  
224.     unregister_framebuffer(s3cxx_lcd);  
225.     lcdregs->lcdcon1 &= ~(1<<0);  
226.     *gpbdat &= ~1;  
227.     dma_free_writecombine(NULL, s3cxx_lcd->fix.smem_len, s3cxx_lcd->screen_base, s3cxx_lcd->fix.smem_start);  
228.         
229.     iounmap(lcdregs);  
230.     iounmap(gpbcon);  
231.     iounmap(gpccon);  
232.     iounmap(gpdcon);  
233.     iounmap(gpgcon);  
234.     framebuffer_release(s3cxx_lcd);  
235. }  
236.     
237.     
238. module_init(lcd_init);  
239. module_exit(lcd_exit);  
240.     
241. MODULE_LICENSE("GPL");

3 编译调试

  1. 去掉内核中原有的lcd驱动使用make menuconfig命令,对内核重新配置。

android的lcd驱动程序 设计lcd驱动程序_#include_02

android的lcd驱动程序 设计lcd驱动程序_#include_03

如上图所示,由->Device Drivers进入,选择->Graphics support,最终将sc2410 lcd framebuffer support设置成M,作为模块编译。因为在fb_ops结构体中,cfb_fillrect、cfb_copyarea、cfb_imageblit三个模块被调用了,所以需要将sc2410 lcd framebuffer作为模块进行编译,方便我们后面对这三个模块进行挂载。

android的lcd驱动程序 设计lcd驱动程序_3c_04

  1. 对内核设置完成后,使用make uImage命令编译内核,使用make modules命令编译模块。编译结束后,将uImage和cfbfillrect.ko、cfbcopyarea.ko、cfbimageblit.ko分别拷贝到可被NFS挂接的目录下/work/nfs_root/...。
  2. 通过nfs服务,使用新的uImage启动系统。
  3. 装载cfbfillrect.ko、cfbcopyarea.ko、cfbimageblit.ko和lcd.ko驱动模块。
  4. 测试。使用echo hello > /dev/tty1命令,将hello输出到lcd终端。会发现lcd屏幕上打印出"hello"字符。
    使用cat lcd.ko > /dev/fb0命令,将lcd.ko的内容显示到lcd上。这里显示的是花屏的效果。
     

视频中使用的是3.5寸的lcd屏,这里需要将对应的参数修改成4.3寸屏的,才能看到正确的实验现象。