编写简单 fb 驱动

一步步编写 linux fb 驱动

Posted by Jerry Chen on August 6, 2020

接下来编写 lcd160160 的 linux fb 驱动;

了解

lcd160160 型号:UC1698u

管脚符号 状态 管脚定义
/CS I 片选信号,/CS=0 时选通模块;/CS=1 时模块接口被封锁
/RST I 复位信号,低电平复位;正常运行时,为高电平状态
A0 I 通道选择信号,当RS=0 时,选择指令通道;RS=1 时,选择数据通道
/WR I 当使用并行接口时,为写信号/WR,低电平有效;
/RD I 当使用并行接口时,为读信号/RD,低电平有效;
DB0 I/O/Z 并行接口数据总线
DB1 I/O/Z 并行接口数据总线
DB2 I/O/Z 并行接口数据总线
DB3 I/O/Z 并行接口数据总线
DB4 I/O/Z 并行接口数据总线
DB5 I/O/Z 并行接口数据总线
DB6 I/O/Z 并行接口数据总线
DB7 I/O/Z 并行接口数据总线

驱动框架

  • disp_gpio_lcd160160.c :显示驱动;
  • disp_gpio_lcd160160_fb.c :fb 框架,依赖于 disp_gpio_lcd160160.ko 模块;
  • disp_gpio_lcd160160.h :屏幕固定参数信息以及函数外部声明;

disp_gpio_lcd160160.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/* SPDX-License-Identifier: GPL-2.0 */
/*
 *    Filename: lcd160160.h
 *     Version: 0.1.0
 * Description: lcd160160 LCD driver header
 *
 *      Author: Copyright (C)
 *        Date: 2020-08-06
 */

#ifndef _LCD160160_H_
#define _LCD160160_H_

#define LCD160160_WIDTH 	160
#define LCD160160_HEIGHT 	160
#define LCD160160_SIZE		(LCD160160_WIDTH * LCD160160_HEIGHT / 8)

/*
 * The driver will blit this buffer to the LCD
 *
 * Its size is LCD160160_SIZE.
 */
extern unsigned char * lcd160160_buffer;

/*
 * Get the refresh rate of the LCD
 *
 * Returns the refresh rate (hertz).
 */
extern unsigned int lcd160160_getrate(void);

/*
 * Enable refreshing
 *
 * Returns 0 if successful (anyone was using it),
 * or != 0 if failed (someone is using it).
 */
extern unsigned char lcd160160_enable(void);

/*
 * Disable refreshing
 *
 * You should call this only when you finish using the LCD.
 */
extern void lcd160160_disable(void);

/*
 * Is enabled refreshing? (is anyone using the module?)
 *
 * Returns 0 if refreshing is not enabled (anyone is using it),
 * or != 0 if refreshing is enabled (someone is using it).
 *
 * Useful for buffer read-only modules.
 */
extern unsigned char lcd160160_isenabled(void);

/*
 * Is the module inited?
 */
extern unsigned char lcd160160_isinited(void);

#endif /* _LCD160160_H_ */

disp_gpio_lcd160160_fb.c

依赖 fb_sys_fops.c sysfillrect.c syscopyarea.c sysimgblt.c

请到 linux 内核 drivers/video 目录,编辑 Kconfig 文件让上述依赖默认生成模块;(如不知道对应的宏,看看 Makefile)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/fb.h>
#include <linux/mm.h>
#include "disp_gpio_lcd160160.h"

#define LCD160160FB_NAME "lcd160160_fb"

/* --- --- FB 的操作 --- --- */

//固定参数
static const struct fb_fix_screeninfo lcd160160fb_fix = {
	.id = "uc1698u",
	.smem_len = LCD160160_SIZE,
	.type = FB_TYPE_PACKED_PIXELS,
	.visual = FB_VISUAL_MONO10,
	.xpanstep = 0,
	.ypanstep = 0,
	.ywrapstep = 0,
	.line_length = LCD160160_WIDTH / 8,
	.accel = FB_ACCEL_NONE,
};

//可变参数
static const struct fb_var_screeninfo lcd160160fb_var = {
	.xres = LCD160160_WIDTH,
	.yres = LCD160160_HEIGHT,
	.xres_virtual = LCD160160_WIDTH,
	.yres_virtual = LCD160160_HEIGHT,
	.bits_per_pixel = 1,
	.red = { 0, 1, 0 },
	.green = { 0, 1, 0 },
	.blue = { 0, 1, 0 },
	.left_margin = 0,
	.right_margin = 0,
	.upper_margin = 0,
	.lower_margin = 0,
	.vmode = FB_VMODE_NONINTERLACED,
};

//地址映射,应用层对 fb 的 mmap 操作时会调用
static int lcd160160fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
{
	unsigned long page;
	unsigned long start = (unsigned long)vma->vm_start;
	unsigned long size = (unsigned long)(vma->vm_end - vma->vm_start);
	printk("size %d \n", (int)size);
	vma->vm_flags |= VM_IO | VM_SHARED;
	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
	//得到物理地址 注意这里边的vma->vm_pgoff,否则可能出现不正常的情况
	page = virt_to_phys(lcd160160_buffer+vma->vm_pgoff);
	//将用户空间的一个vma虚拟内存区映射到以page开始的一段连续物理页面上
	if(remap_pfn_range(vma,start,page>>PAGE_SHIFT,size,PAGE_SHARED)){//第三个参数是页帧号,由物理地址右移PAGE_SHIFT得到
		printk("remap_pfn_range failed\n");
		return -1;
	}
	return 0;
}

//FB 的操作函数(均用的系统函数,需要 ko 模块支持)
//fb_sys_fops.c
//sysfillrect.c syscopyarea.c sysimgblt.c
static const struct fb_ops lcd160160fb_ops = {
	.owner = THIS_MODULE,
	.fb_read = fb_sys_read,
	.fb_write = fb_sys_write,
	.fb_fillrect = sys_fillrect,
	.fb_copyarea = sys_copyarea,
	.fb_imageblit = sys_imageblit,
	.fb_mmap = lcd160160fb_mmap,
};

/* --- --- 匹配后的操作 --- --- */

static int lcd160160fb_probe(struct platform_device *device)
{
	int ret = -EINVAL;
	int i;
	struct fb_info *info = framebuffer_alloc(0, &device->dev);
	printk("lcd160160fb drv_func probe!\n");

	if (!info)
		goto none;

	info->screen_base = (char __iomem *) lcd160160_buffer;
	info->screen_size = LCD160160_SIZE;
	info->fbops = (struct fb_ops *)&lcd160160fb_ops;
	info->fix = lcd160160fb_fix;
	info->var = lcd160160fb_var;
	info->pseudo_palette = NULL;
	info->par = NULL;
	info->flags = FBINFO_FLAG_DEFAULT;

	if (register_framebuffer(info) < 0)
		goto fballoced;

	platform_set_drvdata(device, info);

	printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
        info->fix.id);
	
	//给 fb 赋初值
	for(i = 0; i<LCD160160_SIZE; i++){
        info->screen_base[i] = 0x0F;
    }

	return 0;

fballoced:
	framebuffer_release(info);

none:
	return ret;
}

static int lcd160160fb_remove(struct platform_device *device)
{
	struct fb_info *info = platform_get_drvdata(device);

	if (info) {
		unregister_framebuffer(info);
		framebuffer_release(info);
	}

	printk("lcd160160fb drv_func remove!\n");
	return 0;
}

/* --- --- 驱动和设备注册 --- --- */

static struct platform_device *lcd160160fb_device;
static struct platform_driver lcd160160fb_driver = {
	.probe	= lcd160160fb_probe,
	.remove = lcd160160fb_remove,
	.driver = {
		.name	= LCD160160FB_NAME,
	},
};

static int __init lcd160160fb_init(void){
	int ret = -EINVAL;
    printk("lcd160160fb module init!\n");

	// lcd160160_init() must be called first
	if (!lcd160160_isinited()) {
		printk(KERN_ERR LCD160160FB_NAME ": ERROR: "
			"lcd160160 is not initialized\n");
		goto none;
	}

	if (lcd160160_enable()) {
		printk(KERN_ERR LCD160160FB_NAME ": ERROR: "
			"can't enable lcd160160 refreshing (being used)\n");
		return -ENODEV;
	}

	ret = platform_driver_register(&lcd160160fb_driver);

	if (!ret) {
		lcd160160fb_device =
			platform_device_alloc(LCD160160FB_NAME, 0);

		if (lcd160160fb_device)
			ret = platform_device_add(lcd160160fb_device);
		else
			ret = -ENOMEM;

		if (ret) {
			platform_device_put(lcd160160fb_device);
			platform_driver_unregister(&lcd160160fb_driver);
		}
	}

none:
	return ret;
}

static void __exit lcd160160fb_exit(void){
	platform_device_unregister(lcd160160fb_device);
	platform_driver_unregister(&lcd160160fb_driver);
	printk("lcd160160fb module exit!\n");
}

module_init(lcd160160fb_init);
module_exit(lcd160160fb_exit);

MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("jerry");

初步测试去掉依赖的 fb 驱动

上述代码有使用 lcd160160_init()lcd160160_enable(),你可尝试去掉这两处依赖,以及更改 lcd160160_buffer 在本模块中定义,加载模块后会发现多出了一个设备节点 /dev/fb*,卸载模块后节点注销;

接着测试生成的 fb 节点,测试程序如下:

测试程序如下:

驱动中给空间初值均为 0x0F,这里获取第一个值;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>

int main (int argc, char *argv[]) {
    if(argc <= 1){
        printf("argc error!\n");
        printf("Try: ./{this} /dev/fb0\n");
        exit(1);
    }

    int fp = open (argv[1], O_RDWR);
    if (fp < 0){
        printf("Error : Can not open framebuffer device\n");
        exit(1);
    }

    struct fb_var_screeninfo vinfo;
    struct fb_fix_screeninfo finfo;

    if (ioctl(fp,FBIOGET_FSCREENINFO,&finfo)){
        printf("Error reading fixed information\n");
        exit(1);
    }

    if (ioctl(fp,FBIOGET_VSCREENINFO,&vinfo)){
        printf("Error reading variable information\n");
        exit(1);
    }

    long screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;
    /*这就是把fp所指的文件中从开始到screensize大小的内容给映射出来,得到一个指向这块空间的指针*/
    char *fbp =(char *) mmap (0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fp,0);
    if ((int) fbp == -1){
        printf ("Error: failed to map framebuffer device to memory.\n");
        exit (1);
    }

    printf("first is %d\n", *fbp);

    munmap (fbp, screensize); /*解除映射*/
    close (fp);    /*关闭文件*/
    return 0;
}

disp_gpio_lcd160160.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/bug.h>
#include <linux/platform_device.h>
#include <linux/fb.h>
#include <linux/mm.h>
#include <linux/stat.h>
#include <asm/param.h>
#include "disp_gpio_lcd160160.h"

#define LCD160160_NAME "lcd160160"

EXPORT_SYMBOL_GPL(lcd160160_isinited);
EXPORT_SYMBOL_GPL(lcd160160_buffer);
EXPORT_SYMBOL_GPL(lcd160160_getrate);
EXPORT_SYMBOL_GPL(lcd160160_enable);
EXPORT_SYMBOL_GPL(lcd160160_disable);
EXPORT_SYMBOL_GPL(lcd160160_isenabled);

/*
 * Is the module inited?
 */

static unsigned char lcd160160_inited;
unsigned char lcd160160_isinited(void)
{
	return lcd160160_inited;
}

/*
 * Module Parameters
 */

//下面指定 1s 执行函数 lcd160160_update 的次数
static unsigned int lcd160160_rate = 30;
module_param(lcd160160_rate, uint, S_IRUGO);
MODULE_PARM_DESC(lcd160160_rate, "Refresh rate (hertz)");

unsigned int lcd160160_getrate(void)
{
	return lcd160160_rate;
}

/*
 * Update work
 */

unsigned char *lcd160160_buffer;
static unsigned char *lcd160160_cache;
static DEFINE_MUTEX(lcd160160_mutex);
static unsigned char lcd160160_updating;
static void lcd160160_update(struct work_struct *delayed_work);
static struct workqueue_struct *lcd160160_workqueue;
static DECLARE_DELAYED_WORK(lcd160160_work, lcd160160_update);

unsigned char lcd160160_isenabled(void)
{
	return lcd160160_updating;
}

static void lcd160160_queue(void)
{
	queue_delayed_work(lcd160160_workqueue, &lcd160160_work,
		HZ / lcd160160_rate);
}

unsigned char lcd160160_enable(void)
{
	unsigned char ret;

	mutex_lock(&lcd160160_mutex);

	if (!lcd160160_updating) {
		lcd160160_updating = 1;
		lcd160160_queue();
		ret = 0;
	} else
		ret = 1;

	mutex_unlock(&lcd160160_mutex);

	return ret;
}

void lcd160160_disable(void)
{
	mutex_lock(&lcd160160_mutex);

	if (lcd160160_updating) {
		lcd160160_updating = 0;
		cancel_delayed_work(&lcd160160_work);
		flush_workqueue(lcd160160_workqueue);
	}

	mutex_unlock(&lcd160160_mutex);
}

static void lcd160160_update(struct work_struct *work)
{
	struct timeval tv;
	/*获取时间*/
	do_gettimeofday(&tv);
	printk(KERN_ALERT "now: %ld %ld\n", tv.tv_sec, tv.tv_usec);

	if (memcmp(lcd160160_cache, lcd160160_buffer, LCD160160_SIZE))
	{
		//将 lcd160160_buffer 数据写入到 LCD
		//for .. lcd160160_writebyte();
		memcpy(lcd160160_cache, lcd160160_buffer, LCD160160_SIZE);
	}

	if (lcd160160_updating)
		lcd160160_queue();
}

/* ---- ---- 入口出口函数 ---- ---- */

static int __init lcd160160_init(void)
{
	int ret = -EINVAL;
	BUILD_BUG_ON(PAGE_SIZE < LCD160160_SIZE);

	lcd160160_buffer = (unsigned char *) get_zeroed_page(GFP_KERNEL);
	if (lcd160160_buffer == NULL) {
		printk(KERN_ERR LCD160160_NAME ": ERROR: "
			"can't get a free page\n");
		ret = -ENOMEM;
		goto none;
	}

	lcd160160_cache = kmalloc(LCD160160_SIZE,
				   GFP_KERNEL);
	if (lcd160160_cache == NULL) {
		printk(KERN_ERR LCD160160_NAME ": ERROR: "
			"can't alloc cache buffer (%i bytes)\n",
			LCD160160_SIZE);
		ret = -ENOMEM;
		goto bufferalloced;
	}

	lcd160160_workqueue = create_singlethread_workqueue(LCD160160_NAME);
	if (lcd160160_workqueue == NULL)
		goto cachealloced;

	//lcd160160_clear();
	//lcd160160_on();

	lcd160160_inited = 1;
	return 0;

cachealloced:
	kfree(lcd160160_cache);

bufferalloced:
	free_page((unsigned long) lcd160160_buffer);

none:
	return ret;
}

static void __exit lcd160160_exit(void)
{
	lcd160160_disable();
	//lcd160160_off();
	destroy_workqueue(lcd160160_workqueue);
	kfree(lcd160160_cache);
	free_page((unsigned long) lcd160160_buffer);
}

module_init(lcd160160_init);
module_exit(lcd160160_exit);

MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("jerry");

初步测试没有 lcd 控制的 lcd 驱动

先加载 fb 依赖驱动,再加载 disp_gpio_lcd160160.ko,最后加载 disp_gpio_lcd160160_fb.ko,会发现内核 1s 打印 30 次时间戳,1s 执行 30 次 lcd160160_update 函数;

驱动完整版

disp_gpio_lcd160160.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/bug.h>
#include <linux/platform_device.h>
#include <linux/fb.h>
#include <linux/mm.h>
#include <linux/stat.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <asm/param.h>
#include "disp_gpio_lcd160160.h"
#include "gpio_dev.h"

/* -- lcd ctrl -------------- */

#define bit(n) (((unsigned char)1)<<(n))

//描述:片选,低电平有效
#define gpio_lcd_cs(x) 	gpio_set_value(PIN_LCD_CS, x)	
//描述:0命令通道,1数据通道
#define gpio_lcd_A0(x)	gpio_set_value(PIN_LCD_A0, x) 
//描述:0表示写有效,1无效
#define gpio_lcd_wr(x) gpio_set_value(PIN_LCD_WR, x)
//描述:0表示读有效,1无效
#define gpio_lcd_rd(x) gpio_set_value(PIN_LCD_RD, x)

static void gpio_lcd_wr_dat(unsigned char dat)
{
	if(dat & bit(0))
		gpio_set_value(PIN_LCD_D0, 1);
	else
		gpio_set_value(PIN_LCD_D0, 0);
	if(dat & bit(1))
		gpio_set_value(PIN_LCD_D1, 1);
	else
		gpio_set_value(PIN_LCD_D1, 0);
	if(dat & bit(2))
		gpio_set_value(PIN_LCD_D2, 1);
	else
		gpio_set_value(PIN_LCD_D2, 0);
	if(dat & bit(3))
		gpio_set_value(PIN_LCD_D3, 1);
	else
		gpio_set_value(PIN_LCD_D3, 0);
	if(dat & bit(4))
		gpio_set_value(PIN_LCD_D4, 1);
	else
		gpio_set_value(PIN_LCD_D4, 0);
	if(dat & bit(5))
		gpio_set_value(PIN_LCD_D5, 1);
	else
		gpio_set_value(PIN_LCD_D5, 0);
	if(dat & bit(6))
		gpio_set_value(PIN_LCD_D6, 1);
	else
		gpio_set_value(PIN_LCD_D6, 0);
	if(dat & bit(7))
		gpio_set_value(PIN_LCD_D7, 1);
	else
		gpio_set_value(PIN_LCD_D7, 0);
}

static void write_cmd(uint8_t cmd)
{
	gpio_lcd_A0(0);
	gpio_lcd_wr_dat(cmd);
	gpio_lcd_cs(0);
	gpio_lcd_wr(0);
	gpio_lcd_wr(1);
	gpio_lcd_cs(1);
}
static void write_data(uint8_t dat)
{
	gpio_lcd_A0(1);
	gpio_lcd_wr_dat(dat);
	gpio_lcd_cs(0);
	gpio_lcd_wr(0);
	gpio_lcd_wr(1);
	gpio_lcd_cs(1);
}
static void write_8dots_data(uint8_t _8dots)
{
	uint8_t temp = 0;

	if(_8dots & bit(0))
		temp=0xf0;
	if(_8dots & bit(1))
		temp|=0x0f;
	write_data(temp);

	temp=0;
	if(_8dots & bit(2))
		temp=0xf0;
	if(_8dots & bit(3))
		temp|=0x0f;
	write_data(temp);

	temp=0;
	if(_8dots & bit(4))
		temp=0xf0;
	if(_8dots & bit(5))
		temp|=0x0f;
	write_data(temp);

	temp=0;
	if(_8dots & bit(6))
		temp=0xf0;
	if(_8dots & bit(7))
		temp|=0x0f;
	write_data(temp);
}
static void lcd160160_ctl_clr(void){
	int i = 0;
	int j = 0;
	write_cmd(0xf4); write_cmd(0x25); //左边界
	write_cmd(0xf6); write_cmd(0x5a); //右边界
	write_cmd(0xf5); write_cmd(0x00); //上边界
	write_cmd(0xf7); write_cmd(0x9f); //下边界
	write_cmd(0xf8);  //inside mode
	for(i=0; i<160; i++)// 320*160 B/W picture for example
	{
		for(j=0; j<20; j++) 
		{  
			write_8dots_data(0xff);
		}
	    write_data(0x00);
	}
}
static void lcd160160_ctl_on(void){
	//开显示
	gpio_set_value(PIN_BL_CTRL, 1);

	//关键 GPIO 初始化
	gpio_direction_output(PIN_LCD_D0, 1);
	gpio_direction_output(PIN_LCD_D1, 1);
	gpio_direction_output(PIN_LCD_D2, 1);
	gpio_direction_output(PIN_LCD_D3, 1);
	gpio_direction_output(PIN_LCD_D4, 1);
	gpio_direction_output(PIN_LCD_D5, 1);
	gpio_direction_output(PIN_LCD_D6, 1);
	gpio_direction_output(PIN_LCD_D7, 1);

	gpio_direction_output(PIN_LCD_RD, 1);
	gpio_direction_output(PIN_LCD_WR, 1);
	gpio_direction_output(PIN_LCD_CS, 1);
	gpio_direction_output(PIN_LCD_A0, 1);

	gpio_lcd_rd(1);
	gpio_lcd_wr(1);
	gpio_lcd_A0(0);
	gpio_lcd_cs(1);

	mdelay(50);
}
static void lcd160160_ctl_init(void){
	//硬件复位
	gpio_set_value(PIN_LCD_RST, 0);
	mdelay(10);
	gpio_set_value(PIN_LCD_RST, 1);
	mdelay(200);
	//软件复位
	write_cmd(0xE2);
	mdelay(5);

	write_cmd(0x25); //设置温度补偿系数-0.05%/C
	write_cmd(0x2b); //内部DC-DC
	write_cmd(0xc4); // LCD 映像MY=1,MX=0,LC0=0
	write_cmd(0xa3); //设置行扫描频率 FR=263Hz
	write_cmd(0xd1); //彩色数据格式R-G-B
	write_cmd(0xd5); //设置数据位为12 位RRRR-GGGG-BBBB
	write_cmd(0xc8); write_cmd(0x00); //设置M 信号为帧翻转
	write_cmd(0xe9); //设置偏压比1/10
	write_cmd(0xa7); //负性显示
	write_cmd(0xa4); //正常显示
	write_cmd(0x81); write_cmd(0x96); //设置对比度
	write_cmd(0xd8); //设置扫描模式
	write_cmd(0xad); //开显示
	write_cmd(0x70); //set row MSB address
	write_cmd(0x60); //set row LSB address
	write_cmd(0x12); //set column MSB address
	write_cmd(0x05); //set column LSB address

	lcd160160_ctl_clr(); //清屏
	mdelay(50);
}
static void lcd160160_ctl_off(void){
	write_cmd(0xAE); //关显示
	gpio_set_value(PIN_BL_CTRL, 0); //关背光
}
/* -- lcd ctrl end ---------- */

#define LCD160160_NAME "lcd160160"

EXPORT_SYMBOL_GPL(lcd160160_isinited);
EXPORT_SYMBOL_GPL(lcd160160_buffer);
EXPORT_SYMBOL_GPL(lcd160160_getrate);
EXPORT_SYMBOL_GPL(lcd160160_enable);
EXPORT_SYMBOL_GPL(lcd160160_disable);
EXPORT_SYMBOL_GPL(lcd160160_isenabled);

/*
 * Is the module inited?
 */

static unsigned char lcd160160_inited;
unsigned char lcd160160_isinited(void)
{
	return lcd160160_inited;
}

/*
 * Module Parameters
 */

//下面指定 1s 执行函数 lcd160160_update 的次数
static unsigned int lcd160160_rate = 100;
module_param(lcd160160_rate, uint, S_IRUGO);
MODULE_PARM_DESC(lcd160160_rate, "Refresh rate (hertz)");

unsigned int lcd160160_getrate(void)
{
	return lcd160160_rate;
}

/*
 * Update work
 */

unsigned char *lcd160160_buffer;
static unsigned char *lcd160160_cache;
static DEFINE_MUTEX(lcd160160_mutex);
static unsigned char lcd160160_updating;
static void lcd160160_update(struct work_struct *delayed_work);
static struct workqueue_struct *lcd160160_workqueue;
static DECLARE_DELAYED_WORK(lcd160160_work, lcd160160_update);

unsigned char lcd160160_isenabled(void)
{
	return lcd160160_updating;
}

static void lcd160160_queue(void)
{
	queue_delayed_work(lcd160160_workqueue, &lcd160160_work,
		HZ / lcd160160_rate);
}

unsigned char lcd160160_enable(void)
{
	unsigned char ret;

	mutex_lock(&lcd160160_mutex);

	if (!lcd160160_updating) {
		lcd160160_updating = 1;
		lcd160160_queue();
		ret = 0;
	} else
		ret = 1;

	mutex_unlock(&lcd160160_mutex);

	return ret;
}

void lcd160160_disable(void)
{
	mutex_lock(&lcd160160_mutex);

	if (lcd160160_updating) {
		lcd160160_updating = 0;
		cancel_delayed_work(&lcd160160_work);
		flush_workqueue(lcd160160_workqueue);
	}

	mutex_unlock(&lcd160160_mutex);
}

static void lcd160160_update(struct work_struct *work)
{
	int k=0;
	int i,j;

	if (memcmp(lcd160160_cache, lcd160160_buffer, LCD160160_SIZE))
	{
		//将 lcd160160_buffer 数据写入到 LCD
		write_cmd(0xf4); write_cmd(0x25); //左边界
		write_cmd(0xf6); write_cmd(0x5a); //右边界
		write_cmd(0xf5); write_cmd(0x00); //上边界
		write_cmd(0xf7); write_cmd(0x9f); //下边界
		write_cmd(0xf8);  //inside mode

		for(i=0; i<160; i++)  // 160*160 dots
		{ 	
			for(j=0; j<20; j++) 
			{
				write_8dots_data(lcd160160_buffer[k++]);
			}
			write_data(0x00);
		}

		memcpy(lcd160160_cache, lcd160160_buffer, LCD160160_SIZE);
	}

	if (lcd160160_updating)
		lcd160160_queue();
}

/* ---- ---- 入口出口函数 ---- ---- */

static int __init lcd160160_init(void)
{
	int ret = -EINVAL;
	BUILD_BUG_ON(PAGE_SIZE < LCD160160_SIZE);

	lcd160160_buffer = (unsigned char *) get_zeroed_page(GFP_KERNEL);
	if (lcd160160_buffer == NULL) {
		printk(KERN_ERR LCD160160_NAME ": ERROR: "
			"can't get a free page\n");
		ret = -ENOMEM;
		goto none;
	}

	lcd160160_cache = kmalloc(LCD160160_SIZE,
				   GFP_KERNEL);
	if (lcd160160_cache == NULL) {
		printk(KERN_ERR LCD160160_NAME ": ERROR: "
			"can't alloc cache buffer (%i bytes)\n",
			LCD160160_SIZE);
		ret = -ENOMEM;
		goto bufferalloced;
	}

	lcd160160_workqueue = create_singlethread_workqueue(LCD160160_NAME);
	if (lcd160160_workqueue == NULL)
		goto cachealloced;

	lcd160160_ctl_on();
	lcd160160_ctl_init();

	lcd160160_inited = 1;
	return 0;

cachealloced:
	kfree(lcd160160_cache);

bufferalloced:
	free_page((unsigned long) lcd160160_buffer);

none:
	return ret;
}

static void __exit lcd160160_exit(void)
{
	lcd160160_disable();
	lcd160160_ctl_off();
	destroy_workqueue(lcd160160_workqueue);
	kfree(lcd160160_cache);
	free_page((unsigned long) lcd160160_buffer);
}

module_init(lcd160160_init);
module_exit(lcd160160_exit);

MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("jerry");

disp_gpio_lcd160160.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/* SPDX-License-Identifier: GPL-2.0 */
/*
 *    Filename: lcd160160.h
 *     Version: 0.1.0
 * Description: lcd160160 LCD driver header
 *
 *      Author: Copyright (C)
 *        Date: 2020-08-06
 */

#ifndef _LCD160160_H_
#define _LCD160160_H_

#define LCD160160_WIDTH 	160
#define LCD160160_HEIGHT 	160
#define LCD160160_SIZE		(LCD160160_WIDTH * LCD160160_HEIGHT / 8)

/*
 * The driver will blit this buffer to the LCD
 *
 * Its size is LCD160160_SIZE.
 */
extern unsigned char * lcd160160_buffer;

/*
 * Get the refresh rate of the LCD
 *
 * Returns the refresh rate (hertz).
 */
extern unsigned int lcd160160_getrate(void);

/*
 * Enable refreshing
 *
 * Returns 0 if successful (anyone was using it),
 * or != 0 if failed (someone is using it).
 */
extern unsigned char lcd160160_enable(void);

/*
 * Disable refreshing
 *
 * You should call this only when you finish using the LCD.
 */
extern void lcd160160_disable(void);

/*
 * Is enabled refreshing? (is anyone using the module?)
 *
 * Returns 0 if refreshing is not enabled (anyone is using it),
 * or != 0 if refreshing is enabled (someone is using it).
 *
 * Useful for buffer read-only modules.
 */
extern unsigned char lcd160160_isenabled(void);

/*
 * Is the module inited?
 */
extern unsigned char lcd160160_isinited(void);

#endif /* _LCD160160_H_ */

disp_gpio_lcd160160_fb.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/fb.h>
#include <linux/mm.h>
#include "disp_gpio_lcd160160.h"

#define LCD160160FB_NAME "lcd160160_fb"

/* --- --- FB 的操作 --- --- */

//固定参数
static const struct fb_fix_screeninfo lcd160160fb_fix = {
	.id = "uc1698u",
	.smem_len = LCD160160_SIZE,
	.type = FB_TYPE_PACKED_PIXELS,
	.visual = FB_VISUAL_MONO10,
	.xpanstep = 0,
	.ypanstep = 0,
	.ywrapstep = 0,
	.line_length = LCD160160_WIDTH / 8,
	.accel = FB_ACCEL_NONE,
};

//可变参数
static const struct fb_var_screeninfo lcd160160fb_var = {
	.xres = LCD160160_WIDTH,
	.yres = LCD160160_HEIGHT,
	.xres_virtual = LCD160160_WIDTH,
	.yres_virtual = LCD160160_HEIGHT,
	.bits_per_pixel = 1,
	.red = { 0, 1, 0 },
	.green = { 0, 1, 0 },
	.blue = { 0, 1, 0 },
	.left_margin = 0,
	.right_margin = 0,
	.upper_margin = 0,
	.lower_margin = 0,
	.vmode = FB_VMODE_NONINTERLACED,
};

//地址映射,应用层对 fb 的 mmap 操作时会调用
static int lcd160160fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
{
	unsigned long page;
	unsigned long start = (unsigned long)vma->vm_start;
	unsigned long size = (unsigned long)(vma->vm_end - vma->vm_start);
	printk("size %d \n", (int)size);
	vma->vm_flags |= VM_IO | VM_SHARED;
	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
	//得到物理地址 注意这里边的vma->vm_pgoff,否则可能出现不正常的情况
	page = virt_to_phys(lcd160160_buffer+vma->vm_pgoff);
	//将用户空间的一个vma虚拟内存区映射到以page开始的一段连续物理页面上
	if(remap_pfn_range(vma,start,page>>PAGE_SHIFT,size,PAGE_SHARED)){//第三个参数是页帧号,由物理地址右移PAGE_SHIFT得到
		printk("remap_pfn_range failed\n");
		return -1;
	}
	return 0;
}

//FB 的操作函数(均用的系统函数,需要 ko 模块支持)
//fb_sys_fops.c
//sysfillrect.c syscopyarea.c sysimgblt.c
static const struct fb_ops lcd160160fb_ops = {
	.owner = THIS_MODULE,
	.fb_read = fb_sys_read,
	.fb_write = fb_sys_write,
	.fb_fillrect = sys_fillrect,
	.fb_copyarea = sys_copyarea,
	.fb_imageblit = sys_imageblit,
	.fb_mmap = lcd160160fb_mmap,
};

/* --- --- 匹配后的操作 --- --- */

static int lcd160160fb_probe(struct platform_device *device)
{
	int ret = -EINVAL;
	int i;
	struct fb_info *info = framebuffer_alloc(0, &device->dev);
	printk("lcd160160fb drv_func probe!\n");

	if (!info)
		goto none;

	info->screen_base = (char __iomem *) lcd160160_buffer;
	info->screen_size = LCD160160_SIZE;
	info->fbops = (struct fb_ops *)&lcd160160fb_ops;
	info->fix = lcd160160fb_fix;
	info->var = lcd160160fb_var;
	info->pseudo_palette = NULL;
	info->par = NULL;
	info->flags = FBINFO_FLAG_DEFAULT;

	if (register_framebuffer(info) < 0)
		goto fballoced;

	platform_set_drvdata(device, info);

	printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
        info->fix.id);
	
	//给 fb 赋初值
	for(i = 0; i<LCD160160_SIZE; i++){
        info->screen_base[i] = 0x0F;
    }

	return 0;

fballoced:
	framebuffer_release(info);

none:
	return ret;
}

static int lcd160160fb_remove(struct platform_device *device)
{
	struct fb_info *info = platform_get_drvdata(device);

	if (info) {
		unregister_framebuffer(info);
		framebuffer_release(info);
	}

	printk("lcd160160fb drv_func remove!\n");
	return 0;
}

/* --- --- 驱动和设备注册 --- --- */

static struct platform_device *lcd160160fb_device;
static struct platform_driver lcd160160fb_driver = {
	.probe	= lcd160160fb_probe,
	.remove = lcd160160fb_remove,
	.driver = {
		.name	= LCD160160FB_NAME,
	},
};

static int __init lcd160160fb_init(void){
	int ret = -EINVAL;
    printk("lcd160160fb module init!\n");

	// lcd160160_init() must be called first
	if (!lcd160160_isinited()) {
		printk(KERN_ERR LCD160160FB_NAME ": ERROR: "
			"lcd160160 is not initialized\n");
		goto none;
	}

	if (lcd160160_enable()) {
		printk(KERN_ERR LCD160160FB_NAME ": ERROR: "
			"can't enable lcd160160 refreshing (being used)\n");
		return -ENODEV;
	}

	ret = platform_driver_register(&lcd160160fb_driver);

	if (!ret) {
		lcd160160fb_device =
			platform_device_alloc(LCD160160FB_NAME, 0);

		if (lcd160160fb_device)
			ret = platform_device_add(lcd160160fb_device);
		else
			ret = -ENOMEM;

		if (ret) {
			platform_device_put(lcd160160fb_device);
			platform_driver_unregister(&lcd160160fb_driver);
		}
	}

none:
	return ret;
}

static void __exit lcd160160fb_exit(void){
	lcd160160_disable();
	platform_device_unregister(lcd160160fb_device);
	platform_driver_unregister(&lcd160160fb_driver);
	printk("lcd160160fb module exit!\n");
}

module_init(lcd160160fb_init);
module_exit(lcd160160fb_exit);

MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("jerry");

gpio_dev.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
#ifndef GPIO_DEV_H
#define GPIO_DEV_H

#include <linux/ioctl.h>

#define GPIO_OUTPUT_LOW _IOW('g', 0, char)
#define GPIO_OUTPUT_HIGH _IOW('g', 1, char)
#define GPIO_SET_OUTPUT _IOW('g', 2, char)
#define GPIO_SET_INPUT _IOW('g', 3, char)
#define GPIO_SET_INPUT_PULLUP _IOW('g', 4, char)
#define GPIO_GET_INPUT _IOW('g', 5, char)

#define GPIO_PA0 (0x00 + 0)
#define GPIO_PA1 (0x00 + 1)
#define GPIO_PA2 (0x00 + 2)
#define GPIO_PA3 (0x00 + 3)
#define GPIO_PA4 (0x00 + 4)
#define GPIO_PA5 (0x00 + 5)
#define GPIO_PA6 (0x00 + 6)
#define GPIO_PA7 (0x00 + 7)
#define GPIO_PA8 (0x00 + 8)
#define GPIO_PA9 (0x00 + 9)
#define GPIO_PA10 (0x00 + 10)
#define GPIO_PA11 (0x00 + 11)
#define GPIO_PA12 (0x00 + 12)
#define GPIO_PA13 (0x00 + 13)
#define GPIO_PA14 (0x00 + 14)
#define GPIO_PA15 (0x00 + 15)
#define GPIO_PA16 (0x00 + 16)
#define GPIO_PA17 (0x00 + 17)
#define GPIO_PA18 (0x00 + 18)
#define GPIO_PA19 (0x00 + 19)
#define GPIO_PA20 (0x00 + 20)
#define GPIO_PA21 (0x00 + 21)
#define GPIO_PA22 (0x00 + 22)
#define GPIO_PA23 (0x00 + 23)
#define GPIO_PA24 (0x00 + 24)
#define GPIO_PA25 (0x00 + 25)
#define GPIO_PA26 (0x00 + 26)
#define GPIO_PA27 (0x00 + 27)
#define GPIO_PA28 (0x00 + 28)
#define GPIO_PA29 (0x00 + 29)
#define GPIO_PA30 (0x00 + 30)
#define GPIO_PA31 (0x00 + 31)

#define GPIO_PB0 (0x20 + 0)
#define GPIO_PB1 (0x20 + 1)
#define GPIO_PB2 (0x20 + 2)
#define GPIO_PB3 (0x20 + 3)
#define GPIO_PB4 (0x20 + 4)
#define GPIO_PB5 (0x20 + 5)
#define GPIO_PB6 (0x20 + 6)
#define GPIO_PB7 (0x20 + 7)
#define GPIO_PB8 (0x20 + 8)
#define GPIO_PB9 (0x20 + 9)
#define GPIO_PB10 (0x20 + 10)
#define GPIO_PB11 (0x20 + 11)
#define GPIO_PB12 (0x20 + 12)
#define GPIO_PB13 (0x20 + 13)
#define GPIO_PB14 (0x20 + 14)
#define GPIO_PB15 (0x20 + 15)
#define GPIO_PB16 (0x20 + 16)
#define GPIO_PB17 (0x20 + 17)
#define GPIO_PB18 (0x20 + 18)
#define GPIO_PB19 (0x20 + 19)
#define GPIO_PB20 (0x20 + 20)
#define GPIO_PB21 (0x20 + 21)
#define GPIO_PB22 (0x20 + 22)
#define GPIO_PB23 (0x20 + 23)
#define GPIO_PB24 (0x20 + 24)
#define GPIO_PB25 (0x20 + 25)
#define GPIO_PB26 (0x20 + 26)
#define GPIO_PB27 (0x20 + 27)
#define GPIO_PB28 (0x20 + 28)
#define GPIO_PB29 (0x20 + 29)
#define GPIO_PB30 (0x20 + 30)
#define GPIO_PB31 (0x20 + 31)

#define GPIO_PC0 (0x40 + 0)
#define GPIO_PC1 (0x40 + 1)
#define GPIO_PC2 (0x40 + 2)
#define GPIO_PC3 (0x40 + 3)
#define GPIO_PC4 (0x40 + 4)
#define GPIO_PC5 (0x40 + 5)
#define GPIO_PC6 (0x40 + 6)
#define GPIO_PC7 (0x40 + 7)
#define GPIO_PC8 (0x40 + 8)
#define GPIO_PC9 (0x40 + 9)
#define GPIO_PC10 (0x40 + 10)
#define GPIO_PC11 (0x40 + 11)
#define GPIO_PC12 (0x40 + 12)
#define GPIO_PC13 (0x40 + 13)
#define GPIO_PC14 (0x40 + 14)
#define GPIO_PC15 (0x40 + 15)
#define GPIO_PC16 (0x40 + 16)
#define GPIO_PC17 (0x40 + 17)
#define GPIO_PC18 (0x40 + 18)
#define GPIO_PC19 (0x40 + 19)
#define GPIO_PC20 (0x40 + 20)
#define GPIO_PC21 (0x40 + 21)
#define GPIO_PC22 (0x40 + 22)
#define GPIO_PC23 (0x40 + 23)
#define GPIO_PC24 (0x40 + 24)
#define GPIO_PC25 (0x40 + 25)
#define GPIO_PC26 (0x40 + 26)
#define GPIO_PC27 (0x40 + 27)
#define GPIO_PC28 (0x40 + 28)
#define GPIO_PC29 (0x40 + 29)
#define GPIO_PC30 (0x40 + 30)
#define GPIO_PC31 (0x40 + 31)

#define GPIO_PD0 (0x60 + 0)
#define GPIO_PD1 (0x60 + 1)
#define GPIO_PD2 (0x60 + 2)
#define GPIO_PD3 (0x60 + 3)
#define GPIO_PD4 (0x60 + 4)
#define GPIO_PD5 (0x60 + 5)
#define GPIO_PD6 (0x60 + 6)
#define GPIO_PD7 (0x60 + 7)
#define GPIO_PD8 (0x60 + 8)
#define GPIO_PD9 (0x60 + 9)
#define GPIO_PD10 (0x60 + 10)
#define GPIO_PD11 (0x60 + 11)
#define GPIO_PD12 (0x60 + 12)
#define GPIO_PD13 (0x60 + 13)
#define GPIO_PD14 (0x60 + 14)
#define GPIO_PD15 (0x60 + 15)
#define GPIO_PD16 (0x60 + 16)
#define GPIO_PD17 (0x60 + 17)
#define GPIO_PD18 (0x60 + 18)
#define GPIO_PD19 (0x60 + 19)
#define GPIO_PD20 (0x60 + 20)
#define GPIO_PD21 (0x60 + 21)
#define GPIO_PD22 (0x60 + 22)
#define GPIO_PD23 (0x60 + 23)
#define GPIO_PD24 (0x60 + 24)
#define GPIO_PD25 (0x60 + 25)
#define GPIO_PD26 (0x60 + 26)
#define GPIO_PD27 (0x60 + 27)
#define GPIO_PD28 (0x60 + 28)
#define GPIO_PD29 (0x60 + 29)
#define GPIO_PD30 (0x60 + 30)
#define GPIO_PD31 (0x60 + 31)

#define GPIO_PE0 (0x80 + 0)
#define GPIO_PE1 (0x80 + 1)
#define GPIO_PE2 (0x80 + 2)
#define GPIO_PE3 (0x80 + 3)
#define GPIO_PE4 (0x80 + 4)
#define GPIO_PE5 (0x80 + 5)
#define GPIO_PE6 (0x80 + 6)
#define GPIO_PE7 (0x80 + 7)
#define GPIO_PE8 (0x80 + 8)
#define GPIO_PE9 (0x80 + 9)
#define GPIO_PE10 (0x80 + 10)
#define GPIO_PE11 (0x80 + 11)
#define GPIO_PE12 (0x80 + 12)
#define GPIO_PE13 (0x80 + 13)
#define GPIO_PE14 (0x80 + 14)
#define GPIO_PE15 (0x80 + 15)
#define GPIO_PE16 (0x80 + 16)
#define GPIO_PE17 (0x80 + 17)
#define GPIO_PE18 (0x80 + 18)
#define GPIO_PE19 (0x80 + 19)
#define GPIO_PE20 (0x80 + 20)
#define GPIO_PE21 (0x80 + 21)
#define GPIO_PE22 (0x80 + 22)
#define GPIO_PE23 (0x80 + 23)
#define GPIO_PE24 (0x80 + 24)
#define GPIO_PE25 (0x80 + 25)
#define GPIO_PE26 (0x80 + 26)
#define GPIO_PE27 (0x80 + 27)
#define GPIO_PE28 (0x80 + 28)
#define GPIO_PE29 (0x80 + 29)
#define GPIO_PE30 (0x80 + 30)
#define GPIO_PE31 (0x80 + 31)

#define GPIO_PF0 (0xA0 + 0)
#define GPIO_PF1 (0xA0 + 1)
#define GPIO_PF2 (0xA0 + 2)
#define GPIO_PF3 (0xA0 + 3)
#define GPIO_PF4 (0xA0 + 4)
#define GPIO_PF5 (0xA0 + 5)
#define GPIO_PF6 (0xA0 + 6)
#define GPIO_PF7 (0xA0 + 7)
#define GPIO_PF8 (0xA0 + 8)
#define GPIO_PF9 (0xA0 + 9)
#define GPIO_PF10 (0xA0 + 10)
#define GPIO_PF11 (0xA0 + 11)
#define GPIO_PF12 (0xA0 + 12)
#define GPIO_PF13 (0xA0 + 13)
#define GPIO_PF14 (0xA0 + 14)
#define GPIO_PF15 (0xA0 + 15)
#define GPIO_PF16 (0xA0 + 16)
#define GPIO_PF17 (0xA0 + 17)
#define GPIO_PF18 (0xA0 + 18)
#define GPIO_PF19 (0xA0 + 19)
#define GPIO_PF20 (0xA0 + 20)
#define GPIO_PF21 (0xA0 + 21)
#define GPIO_PF22 (0xA0 + 22)
#define GPIO_PF23 (0xA0 + 23)
#define GPIO_PF24 (0xA0 + 24)
#define GPIO_PF25 (0xA0 + 25)
#define GPIO_PF26 (0xA0 + 26)
#define GPIO_PF27 (0xA0 + 27)
#define GPIO_PF28 (0xA0 + 28)
#define GPIO_PF29 (0xA0 + 29)
#define GPIO_PF30 (0xA0 + 30)
#define GPIO_PF31 (0xA0 + 31)

#define GPIO_PG0 (0xC0 + 0)
#define GPIO_PG1 (0xC0 + 1)
#define GPIO_PG2 (0xC0 + 2)
#define GPIO_PG3 (0xC0 + 3)
#define GPIO_PG4 (0xC0 + 4)
#define GPIO_PG5 (0xC0 + 5)
#define GPIO_PG6 (0xC0 + 6)
#define GPIO_PG7 (0xC0 + 7)
#define GPIO_PG8 (0xC0 + 8)
#define GPIO_PG9 (0xC0 + 9)
#define GPIO_PG10 (0xC0 + 10)
#define GPIO_PG11 (0xC0 + 11)
#define GPIO_PG12 (0xC0 + 12)
#define GPIO_PG13 (0xC0 + 13)
#define GPIO_PG14 (0xC0 + 14)
#define GPIO_PG15 (0xC0 + 15)
#define GPIO_PG16 (0xC0 + 16)
#define GPIO_PG17 (0xC0 + 17)
#define GPIO_PG18 (0xC0 + 18)
#define GPIO_PG19 (0xC0 + 19)
#define GPIO_PG20 (0xC0 + 20)
#define GPIO_PG21 (0xC0 + 21)
#define GPIO_PG22 (0xC0 + 22)
#define GPIO_PG23 (0xC0 + 23)
#define GPIO_PG24 (0xC0 + 24)
#define GPIO_PG25 (0xC0 + 25)
#define GPIO_PG26 (0xC0 + 26)
#define GPIO_PG27 (0xC0 + 27)
#define GPIO_PG28 (0xC0 + 28)
#define GPIO_PG29 (0xC0 + 29)
#define GPIO_PG30 (0xC0 + 30)
#define GPIO_PG31 (0xC0 + 31)

#define GPIO_PH0 (0xE0 + 0)
#define GPIO_PH1 (0xE0 + 1)
#define GPIO_PH2 (0xE0 + 2)
#define GPIO_PH3 (0xE0 + 3)
#define GPIO_PH4 (0xE0 + 4)
#define GPIO_PH5 (0xE0 + 5)
#define GPIO_PH6 (0xE0 + 6)
#define GPIO_PH7 (0xE0 + 7)
#define GPIO_PH8 (0xE0 + 8)
#define GPIO_PH9 (0xE0 + 9)
#define GPIO_PH10 (0xE0 + 10)
#define GPIO_PH11 (0xE0 + 11)
#define GPIO_PH12 (0xE0 + 12)
#define GPIO_PH13 (0xE0 + 13)
#define GPIO_PH14 (0xE0 + 14)
#define GPIO_PH15 (0xE0 + 15)
#define GPIO_PH16 (0xE0 + 16)
#define GPIO_PH17 (0xE0 + 17)
#define GPIO_PH18 (0xE0 + 18)
#define GPIO_PH19 (0xE0 + 19)
#define GPIO_PH20 (0xE0 + 20)
#define GPIO_PH21 (0xE0 + 21)
#define GPIO_PH22 (0xE0 + 22)
#define GPIO_PH23 (0xE0 + 23)
#define GPIO_PH24 (0xE0 + 24)
#define GPIO_PH25 (0xE0 + 25)
#define GPIO_PH26 (0xE0 + 26)
#define GPIO_PH27 (0xE0 + 27)
#define GPIO_PH28 (0xE0 + 28)
#define GPIO_PH29 (0xE0 + 29)
#define GPIO_PH30 (0xE0 + 30)
#define GPIO_PH31 (0xE0 + 31)

#define GPIO_PI0 (0x100 + 0)
#define GPIO_PI1 (0x100 + 1)
#define GPIO_PI2 (0x100 + 2)
#define GPIO_PI3 (0x100 + 3)
#define GPIO_PI4 (0x100 + 4)
#define GPIO_PI5 (0x100 + 5)
#define GPIO_PI6 (0x100 + 6)
#define GPIO_PI7 (0x100 + 7)
#define GPIO_PI8 (0x100 + 8)
#define GPIO_PI9 (0x100 + 9)
#define GPIO_PI10 (0x100 + 10)
#define GPIO_PI11 (0x100 + 11)
#define GPIO_PI12 (0x100 + 12)
#define GPIO_PI13 (0x100 + 13)
#define GPIO_PI14 (0x100 + 14)
#define GPIO_PI15 (0x100 + 15)
#define GPIO_PI16 (0x100 + 16)
#define GPIO_PI17 (0x100 + 17)
#define GPIO_PI18 (0x100 + 18)
#define GPIO_PI19 (0x100 + 19)
#define GPIO_PI20 (0x100 + 20)
#define GPIO_PI21 (0x100 + 21)
#define GPIO_PI22 (0x100 + 22)
#define GPIO_PI23 (0x100 + 23)
#define GPIO_PI24 (0x100 + 24)
#define GPIO_PI25 (0x100 + 25)
#define GPIO_PI26 (0x100 + 26)
#define GPIO_PI27 (0x100 + 27)
#define GPIO_PI28 (0x100 + 28)
#define GPIO_PI29 (0x100 + 29)
#define GPIO_PI30 (0x100 + 30)
#define GPIO_PI31 (0x100 + 31)

#define GPIO_PJ0 (0x120 + 0)
#define GPIO_PJ1 (0x120 + 1)
#define GPIO_PJ2 (0x120 + 2)
#define GPIO_PJ3 (0x120 + 3)
#define GPIO_PJ4 (0x120 + 4)
#define GPIO_PJ5 (0x120 + 5)
#define GPIO_PJ6 (0x120 + 6)
#define GPIO_PJ7 (0x120 + 7)
#define GPIO_PJ8 (0x120 + 8)
#define GPIO_PJ9 (0x120 + 9)
#define GPIO_PJ10 (0x120 + 10)
#define GPIO_PJ11 (0x120 + 11)
#define GPIO_PJ12 (0x120 + 12)
#define GPIO_PJ13 (0x120 + 13)
#define GPIO_PJ14 (0x120 + 14)
#define GPIO_PJ15 (0x120 + 15)
#define GPIO_PJ16 (0x120 + 16)
#define GPIO_PJ17 (0x120 + 17)
#define GPIO_PJ18 (0x120 + 18)
#define GPIO_PJ19 (0x120 + 19)
#define GPIO_PJ20 (0x120 + 20)
#define GPIO_PJ21 (0x120 + 21)
#define GPIO_PJ22 (0x120 + 22)
#define GPIO_PJ23 (0x120 + 23)
#define GPIO_PJ24 (0x120 + 24)
#define GPIO_PJ25 (0x120 + 25)
#define GPIO_PJ26 (0x120 + 26)
#define GPIO_PJ27 (0x120 + 27)
#define GPIO_PJ28 (0x120 + 28)
#define GPIO_PJ29 (0x120 + 29)
#define GPIO_PJ30 (0x120 + 30)
#define GPIO_PJ31 (0x120 + 31)

#define PIN_BL_CTRL GPIO_PB9  //背光控制
#define PIN_LCD_RST GPIO_PH2  //LCD复位

#define PIN_LCD_D0 GPIO_PD0
#define PIN_LCD_D1 GPIO_PD1
#define PIN_LCD_D2 GPIO_PD2
#define PIN_LCD_D3 GPIO_PD3
#define PIN_LCD_D4 GPIO_PD4
#define PIN_LCD_D5 GPIO_PD5
#define PIN_LCD_D6 GPIO_PD6
#define PIN_LCD_D7 GPIO_PD7
#define PIN_LCD_CS GPIO_PD24
#define PIN_LCD_A0 GPIO_PD25
#define PIN_LCD_WR GPIO_PD26
#define PIN_LCD_RD GPIO_PD27

#endif