1 lcd硬件操作原理
Lcd显示的过程如下:
- 从显存中输出显示颜色的数据,在屏幕左上角的第一个点开始显示,每间隔一个像素时钟VCLK,向右移动一个点,当移到最右边时,会根据水平同步信号HSYNC跳到下一行的最左边;
- 又重复步骤1的操作,直到显示到右下角最后一个点为止,这时根据垂直同步信号YSYNC,又跳回到左上角第一个点开始下一帧图片的显示。
2 编写驱动
在上一章节结尾已经理出了lcd驱动程序的大致流程。首先来实现入口函数lcd_init函数。
- 分配一个fb_info空间;
- 对fb_info指向的结构体进行配置;
- 设置可变参数;
- 设置固定参数;
- 设置fops;
- 设置其他参数。
- 硬件相关的配置;
- 设置lcd相关的引脚;
- 设置lcd控制器的寄存器;
- 设置显存,并将显存的地址通知到lcd控制器;
- 开启lcd,lcd控制器以及背光。
- 注册framebuffer。
出口函数lcd_exit主要实现对资源的释放,具体操作如下:
- 卸载framebuffer;
- 关闭lcd控制器,背光;
- 释放显存空间;
- 解除所有io资源的映射;
- 释放申请的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 编译调试
- 去掉内核中原有的lcd驱动使用make menuconfig命令,对内核重新配置。
如上图所示,由->Device Drivers进入,选择->Graphics support,最终将sc2410 lcd framebuffer support设置成M,作为模块编译。因为在fb_ops结构体中,cfb_fillrect、cfb_copyarea、cfb_imageblit三个模块被调用了,所以需要将sc2410 lcd framebuffer作为模块进行编译,方便我们后面对这三个模块进行挂载。
- 对内核设置完成后,使用make uImage命令编译内核,使用make modules命令编译模块。编译结束后,将uImage和cfbfillrect.ko、cfbcopyarea.ko、cfbimageblit.ko分别拷贝到可被NFS挂接的目录下/work/nfs_root/...。
- 通过nfs服务,使用新的uImage启动系统。
- 装载cfbfillrect.ko、cfbcopyarea.ko、cfbimageblit.ko和lcd.ko驱动模块。
- 测试。使用echo hello > /dev/tty1命令,将hello输出到lcd终端。会发现lcd屏幕上打印出"hello"字符。
使用cat lcd.ko > /dev/fb0命令,将lcd.ko的内容显示到lcd上。这里显示的是花屏的效果。
视频中使用的是3.5寸的lcd屏,这里需要将对应的参数修改成4.3寸屏的,才能看到正确的实验现象。