LCD驱动详解
参考文档:《液晶屏.pdf》《S3C2440用户手册》《JZ2440-V3原理图》
frame buffer: 显存,用于存放LCD显示数据;frame buffer通过LCD控制器和LCD Panel建立一一映射关系;
LCD控制器: 参考LCD用户手册,配置LCD控制器,用于发出LCD控制信号,驱动LCD显示;
扫描方向: 如图①所示,由start到end的扫描方向是:从左到右,从上到下(扫描方向的一种);
HSYNC: 行同步信号,用于行切换,一行扫描结束,需要扫描新行时,需要先发送行同步信号;
VSYNC: 列同步信号,用于列切换,一帧扫描结束,需要扫描新的一帧时,需要先发送列同步信号;
时钟信号: 每来一个时钟,扫描的点移位一;
原理图——管脚说明
硬件操作配置
①配置LCD控制引脚;
②根据LCD手册,配置LCD控制器;
③分配Frame buffer,并映射到LCD Panel;
《液晶屏.pdf》
Block Diagram
Interface Timing
Driver Timing
Timing Chart
a、
b、
《S3C2440用户手册》
LCD CONTROLLER SPECIAL REGISTERS
MEMORY DATA FORMAT (TFT)
驱动程序
1 /*
2 * 参考内核自带的lcd驱动程序:
3 * C:\Users\liang\Desktop\linux-2.6.22.6\drivers\video\s3c2410fb.c
4 * 《液晶屏.pdf》、《s3c2440 用户手册》
5 */
6 #include <linux/module.h>
7 #include <linux/kernel.h>
8 #include <linux/errno.h>
9 #include <linux/string.h>
10 #include <linux/mm.h>
11 #include <linux/slab.h>
12 #include <linux/delay.h>
13 #include <linux/fb.h>
14 #include <linux/init.h>
15 #include <linux/dma-mapping.h>
16 #include <linux/interrupt.h>
17 #include <linux/workqueue.h>
18 #include <linux/wait.h>
19 #include <linux/platform_device.h>
20 #include <linux/clk.h>
21
22 #include <asm/io.h>
23 #include <asm/uaccess.h>
24 #include <asm/div64.h>
25
26 #include <asm/mach/map.h>
27 #include <asm/arch/regs-lcd.h>
28 #include <asm/arch/regs-gpio.h>
29 #include <asm/arch/fb.h>
30
31 struct lcd_regs {
32 unsigned long lcdcon1;
33 unsigned long lcdcon2;
34 unsigned long lcdcon3;
35 unsigned long lcdcon4;
36 unsigned long lcdcon5;
37
unsigned long lcdsaddr1;
38
unsigned long lcdsaddr2;
39
unsigned long lcdsaddr3;
40
unsigned long redlut;
41
unsigned long greenlut;
42
unsigned long bluelut;
43
unsigned long reserved[9];
44
unsigned long dithmode;
45
unsigned long tpal;
46
unsigned long lcdintpnd;
47
unsigned long lcdsrcpnd;
48
unsigned long lcdintmsk;
49
unsigned long lpcsel;
50 };
51
52
53 static volatile unsigned long *gpb_con;
54 static volatile unsigned long *gpb_dat;
55
56 static volatile unsigned long *gpc_con;
57 static volatile unsigned long *gpd_con;
58 static volatile unsigned long *gpg_con;
59
60 static volatile struct lcd_regs* lcd_regs;
61
62 static u32 pseudo_palette[16];//假的调色板
63
64 static struct fb_info *lcd_info;
65
66 static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
67 {
68 chan &= 0xffff;
69 chan >>= 16 - bf->length;
70 return chan << bf->offset;
71 }
72
73 static int lcdfb_setcolreg(unsigned int regno, unsigned int red,
74 unsigned int green, unsigned int blue,
75 unsigned int transp, struct fb_info *info)
76 {
77 unsigned int val;
78
79 if (regno > 16)
80 {
81 return -1;
82 }
83
84 //用三原色构造出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_palette[regno] = val; //调好颜色,放回调色板
90
91 return 0;
92 }
93
94
95 static struct fb_ops lcd_fbops = {
96 .owner = THIS_MODULE,
97 .fb_setcolreg = lcdfb_setcolreg,//假的调色板的调色函数
98 .fb_fillrect = cfb_fillrect, //
99 .fb_copyarea = cfb_copyarea,
100 .fb_imageblit = cfb_imageblit,
101 };
102
103 /* 1、出入口函数 */
104 static int lcd_init(void)
105 {
106 /* 2、分配一个fb_info结构体 */
107 lcd_info = framebuffer_alloc(0, NULL);
108 /******** 2 end ********/
109
110 /* 3、设置 */
111 /* 设置固定的参数:lcd_info->fix */
112 strcpy(lcd_info->fix.id, "mylcd"); //名字
113 lcd_info->fix.smem_len = 240*320*16/8; //framebuffer长度(240*320 dots,lrgb565: 16bit/dots)
114 lcd_info->fix.type = FB_TYPE_PACKED_PIXELS;
115 lcd_info->fix.visual = FB_VISUAL_TRUECOLOR; //颜色深度(tft-lcd设置为真彩)
116 lcd_info->fix.line_length = 240*16/8; //framebuffer中每一行(line)占据的字节数;240*2(2:16bit/8)
117
118 /* 设置可变的参数 */
119 lcd_info->var.xres = 240; //x方向的分辨率
120 lcd_info->var.yres = 320; //y方向的分辨率
121 lcd_info->var.xres_virtual = 240; //x方向的虚拟分辨率
122 lcd_info->var.yres_virtual = 320; //y方向的虚拟分辨率
123 lcd_info->var.bits_per_pixel= 16; //每个像素点16位(rgb565)
124 lcd_info->var.activate = FB_ACTIVATE_NOW;
125
126 // 颜色数据的位分配 rgb:565
127 lcd_info->var.red.offset = 11; //(5)bit11 - bit15
128 lcd_info->var.red.length = 5;
129 lcd_info->var.green.offset = 5; //(6)bit5 - bit10
130 lcd_info->var.green.length = 6;
131 lcd_info->var.blue.offset = 0; //(5)bit0 - bit4
132 lcd_info->var.blue.length = 5;
133
134 /* 设置操作函数 */
135 lcd_info->fbops = &lcd_fbops;
136
137 /* 其他设置 */
138 lcd_info->pseudo_palette = pseudo_palette; //假的调色板
139 lcd_info->screen_size = 240*320*16/8; //
140 /******** 3 end ********/
141
142 /* 4、硬件相关设置 */
143 /* 配置gpio用于lcd */
144 gpc_con = ioremap(0x56000020, 4);
145 gpd_con = ioremap(0x56000030, 4);
146 gpg_con = ioremap(0x56000060, 4);
147 gpb_con = ioremap(0x56000010, 8);
148 gpb_dat = gpb_con + 1;
149
150 //GPIO管脚用于VD[7:0],LCDVF[2:0],VM,VFRAME,VLINE,VCLK,LEND
151 *gpc_con = 0xaaaaaaaa;
152
153 //GPIO管脚用于VD[23:8]
154 *gpd_con = 0xaaaaaaaa;
155
156 //GPB0设置为输出引脚
157 *gpb_con &= ~(3);
158 *gpb_con |= 1;
159 *gpb_dat &= ~1; //输出低电平
160
161 //GPG4用作LCD_PWREN
162 *gpg_con |= (3<<8);
163
164 /* 根据lcd手册设置lcd控制器 */
165 lcd_regs = ioremap(0x4D000000, sizeof(struct lcd_regs));
166 lcd_regs->lcdcon1 = (4<<8) | (3<<5) | (0x0c<<1);
167 lcd_regs->lcdcon2 = (3<<24) | (319<<14) | (1<<6) | (0<<0);
168 lcd_regs->lcdcon3 = (16<<19) | (239<<8) | (10<<0);
169 lcd_regs->lcdcon4 = 4;
170 lcd_regs->lcdcon5 = (1<<11) | (0<<10) | (1<<9) | (1<<8) | (1<<0);
171
172 /* 分配显存(framebuffer),并把地址告诉lcd控制器 */
173 // 虚拟地址 大小 物理地址
174 lcd_info->screen_base = dma_alloc_writecombine(NULL, lcd_info->fix.smem_len, &lcd_info->fix.smem_start, GFP_KERNEL);
175
176 lcd_regs->lcdsaddr1 = (lcd_info->fix.smem_start >> 1) & ~(3<<30);
177 lcd_regs->lcdsaddr2 = ((lcd_info->fix.smem_start + lcd_info->fix.smem_len) >> 1) & 0x1fffff;
178 lcd_regs->lcdsaddr3 = (240*16/16); /* 一行的长度(单位: 2字节) */
179
180 /* 启动LCD */
181 lcd_regs->lcdcon1 |= (1<<0); //使能LCD本身
182 lcd_regs->lcdcon5 |= (1<<3);
183 *gpb_dat |= 1; //输出高电平, 使能背光
184 /******** 4 end ********/
185
186 /* 5、注册 */
187 register_framebuffer(lcd_info);
188 /******** 5 end ********/
189
190 return 0;
191 }
192
193 static void lcd_exit(void)
194 {
195 dma_free_writecombine(NULL, lcd_info->fix.smem_len, lcd_info->screen_base, lcd_info->fix.smem_start);
196 unregister_framebuffer(lcd_info);
197 /* 关闭LCD */
198 lcd_regs->lcdcon1 &= ~(1<<0);
199 lcd_regs->lcdcon5 &= ~(1<<3);
200 *gpb_dat &= ~(1<<0);
201
202 iounmap(lcd_regs);
203 iounmap(gpc_con);
204 iounmap(gpd_con);
205 iounmap(gpg_con);
206 iounmap(gpb_con);
207
208 framebuffer_release(lcd_info);
209 return;
210 }
211
212 module_init(lcd_init);
213 module_exit(lcd_exit);
214 MODULE_LICENSE("GPL");
215 /******** 1 end ********/
附:
1 /**
2 * framebuffer_alloc - creates a new frame buffer info structure
3 *
4 * @size: size of driver private data, can be zero
5 * @dev: pointer to the device for this fb, this can be NULL
6 *
7 * Creates a new frame buffer info structure. Also reserves @size bytes
8 * for driver private data (info->par). info->par (if any) will be
9 * aligned to sizeof(long).
10 *
11 * Returns the new structure, or NULL if an error occured.
12 *
13 */
14 struct fb_info *framebuffer_alloc(size_t size, struct device *dev);
调试
pc-linux:
cd /work/system/linux-2.6.22.6/
make menuconfig
(lcd驱动以模块方式编译)
make uImage
make modules
cp /work/system/linux-2.6.22.6/drivers/video/cfbcopyarea.ko /work/nfs_root
cp /work/system/linux-2.6.22.6/drivers/video/cfbfillrect.ko /work/nfs_root
cp /work/system/linux-2.6.22.6/drivers/video/cfbimgblt.ko /work/nfs_root
cp /work/system/linux-2.6.22.6/arch/arm/boot/uImage /work/nfs_root/uImage_nolcd
board-u-boot:
nfs 30000000 192.168.0.103:/work/nfs_root/uImage_nolcd
bootm 30000000
board-linux:
mount -t nfs -o nolock,vers=2 192.168.0.103:/work/nfs_root /mnt
cd /mnt
insmod cfbcopyarea.ko
insmod cfbfillrect.ko
insmod cfbimgblt.ko
insmod lcd.ko
echo hello world! 2019/10/18 > /dev/tty1
cat test.bmp > /dev/fb0
vi /etc/inittab
#+++
tty1::askfirst:-/bin/sh
reboot
insmod buttons.ko
insmod cfbcopyarea.ko
insmod cfbfillrect.ko
insmod cfbimgblt.ko
insmod lcd.ko
<input way:key>
KEY_L KEY_S KEY_ENTER(ls'\n')