一、需求分析

智能家居系统是一种近年来兴起的智能化系统,在家庭场合基本都能看到它的身影,各种家居通过一个智能控制终端来进行人机交互。本课题基于粤嵌GEC-6818开发平台,定制的Linux系统,通过C语言的编程,经过交叉编译环境,让编译的文件能够直接适用于ARM开发板。用7寸LCD电容式触摸屏作为人机互交的方式,做到可以LED控制、液晶屏检测、报警控制、电子相册、音乐播放、家电控制的功能,显示相应的界面。具有音频输出的功能,获取屏幕触摸点的坐标,实现界面上相应的功能。
通过这次的设计,掌握了ARM的结构体系,了解了部分的Linux命令,对文件的读写操作等,初步掌握了交叉编译方式,加强了对嵌入式产品的认知。同时,在本课程设计时的调试、排错,让我们对C语言进行了一次查漏补缺,也巩固了C语言基础。让我们能够用团队的力量,对ARM系列的开发板进行开发,用固定的硬件设备实现多种功能,了解ARM开发的文件读取方式、交叉编译环境。扩大了我们的眼界,学会了无显示界面时使用Linux命令操作设备。

二、设计过程

1、嵌入式开发环境搭建

1
2
3
4
5
6
7
8
9
10
安装交叉编译器:
arm-linux-gnueabi-5.4.0.tar.xz
1)复制arm-linux-gnueabi-5.4.0.tar.xz到/usr/local/arm
2)解压命令arm-linux-gnueabi-5.4.0.tar.xz
3)然后配置下编译环境路径:
在/usr/local/arm下输入:vi /etc/profile
加上下面代码:
export PATH=/usr/local/arm/5.4.0/usr/bin:$PATH
4)重启
不重启:输入source /etc/profile使之生效

2、进行在液晶屏上显示单色–液晶屏检测的功能

打开液晶屏—open()

1
2
3
4
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);

参数说明:
const char *pathname —–文件的路径名。液晶屏:“/dev/fb0”
int flags —- 文件的访问属性:O_RDONLY, O_WRONLY, or O_RDWR2、写入颜色数据—write()
显示红色:

1
2
3
4
5
int lcd_buf[800*480];//数组的大小:800*480*4
for(int i=0; i<800*480; i++)
lcd_buf[i] = 0x00ff0000;
write(fd_lcd, lcd_buf, 800*480*4);
close(fd_lcd);

3、学会显存映射

液晶屏的驱动是在Linux内核中工作的,显存也是有Linux内核来管理的。应用程序操作显存的时候,就会有一个应用程序访问Linux内核的系统调用过程,这样如果数据量比较大、而且数据访问比较频繁,此时效率就比较低。这样可以使用内存映射的方式,将Linux内核中的显存映射到应用程序中,在应用程序中直接访问这个显存。
通常使用内存映射的硬件:液晶屏(显存)、声卡、摄像头
显存映射,在应用程序中得到显存的首地址—mmap()

1
2
3
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);

4、访问触摸屏,获取坐标的过程。

1)打开触摸屏设备文件
2)读取触摸屏的数据

1
2
3
#include <linux/input.h>
struct input_event ts_data;
read(fd_ts, &ts_data, sizeof(struct struct input_event));

3)从数据中获取坐标
分析struct inputevent的成员数据。从中得到触摸屏的坐标。

1
2
3
4
5
6
7
8
printf("type=%d, code=%d,value=%d\n", ts_data.type, ts_data.code,ts_data.value);
if(ts_data.type == EV_ABS )
{
if(ts_data.code == ABS_X)
x = ts_data.value;
else if(ts_data.code == ABS_Y)
y = ts_data.value;
}

4)关闭触摸屏

1
close(fd_ts);

5、使用液晶屏显示bmp图片

1)打开bmp图片

1
2
3
4
5
6
7
int fd_bmp;
fd_bmp = open(bmp_name, O_RDONLY);
if(fd_bmp == -1)
{
perror("open bmp");
return -1;
}

2)移动文件指针54B,移动到RGB数据区

1
lseek()

3)读取RGB数据,8004803

1
2
char lcd_buf[800*480*3];
read();

4)关闭文件

1
close();

5)打开液晶屏—open()
6)显存映射,得到显存的首地址—mmap()

1
2
3
4
5
6
7
int *lcd_base = NULL;
lcd_base = (int *)mmap(NULL, 800*480*4, PROT_READ|PROT_WRITE, MAP_SHARED, fd_lcd, 0);
if(lcd_base == NULL)
{
printf("lcd mmap error\n");
return -1;
}

7)通过显存首地址,直接bmp_buf[]写入显存

1
2
for(int i=0;i<800*480;i++)
*(lcd_base+i) = (bmp_buf[3*i+2]<<16) + (bmp_buf[3*i+1] <<8) + bmp_buf[3*i];

8)解除显存映射—-munmap()

1
munmap(lcd_base, 800*480*4);

9)关闭液晶屏—-close()

1
close();

6、学会多线程编程

1)创建线程

1
2
3
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);

Compile and link with -pthread.

2)子线程的退出

1
2
#include <pthread.h>
int pthread_cancel(pthread_t thread);

参数:
pthread_t thread —- 线程的ID

7、学会驱动设计的流程

驱动流程

a、设计一个模块(module)

在Linux内核中,驱动程序是以内核模块的形式存在,module相当于是一个盒子,里面放了一个驱动。驱动编译的时候,每个盒子独立编译,生成一个ko文件(驱动的安装文件),驱动可安装(#insmod led_drv.ko),可卸载(#rmmod led_drv.ko)。

1)module的入口—驱动的安装函数

完成资源的申请,如GPIO口;注册驱动到内核。

2)module的出口—驱动的卸载函数

安装函数的反函数,释放资源,注销驱动。

3)module的描述

可有可无,主要是描述module的作者,版本,协议、作用…….

b、申请硬件资源(GPIO)

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
#include <linux/gpio.h>

//1)申请GPIO资源
/* request GPIO, returning 0 or negative errno.
* non-null labels may be useful for diagnostics.
*/
int gpio_request(unsigned gpio, const char *label);
//资源只能申请一次。

//2)设置GPIO的方向
/* set as input or output, returning 0 or negative errno */
int gpio_direction_input(unsigned gpio); //按钮
int gpio_direction_output(unsigned gpio, int value); //灯

//3)设置GPIO的输出值和读取GPIO的输入值
/* GPIO INPUT: return zero or nonzero */
int gpio_get_value(unsigned gpio);

/* GPIO OUTPUT */
void gpio_set_value(unsigned gpio, int value);


//4)释放GPIO
/* release previously-claimed GPIO */
void gpio_free(unsigned gpio);
//5)GPIO号

S5P6818的GPIO分成了5个组(ABCDE),每个组有32个引脚(0~31)。在Linux内核中,给每个GPIO分了一个ID,这个ID是32无符号整数,用这个ID(unsigned gpio)来表示这个GPIO。

GPIO口号是和处理器相关的。

1
2
3
4
5
6
7
8
9
/*  gpio group pad start num. */
enum {
PAD_GPIO_A = (0 * 32),
PAD_GPIO_B = (1 * 32),
PAD_GPIO_C = (2 * 32),
PAD_GPIO_D = (3 * 32),
PAD_GPIO_E = (4 * 32),
PAD_GPIO_ALV = (5 * 32),
};

c、注册驱动

需要利用Linux内核的驱动模型,在安装驱动的时候,将设计的驱动注册到Linux内核中。简单的驱动模型是混杂设备驱动模型。

使用混杂设备驱动模型—miscdevice
#include <linux/miscdevice.h>

1)定义一个混杂设备

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct miscdevice  {
int minor;
const char *name;
const struct file_operations *fops;
struct list_head list;
struct device *parent;
struct device *this_device;
const char *nodename;
umode_t mode;
};
//例:
static struct miscdevice GEC6818_misc_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "led_drv", // /dev/led_drv
.fops = &gec6818_led_fops,
};
#### 2)混杂设备的注册
misc_register(struct miscdevice * misc);
#### 3)混杂设备注销
int misc_deregister(struct miscdevice *misc);

d、定义并初始化文件操作集

提供给应用程序的接口,open()\write()\close()
#include <linux/fs.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
struct file_operations {
struct module *owner;
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
int (*open) (struct inode *, struct file *);
int (*release) (struct inode *, struct file *);
};
//例:
int gec6818_led_open(struct inode inode, struct file filp)
{

}
ssize_t gec6818_led_write(struct file *filp, const char __user *buf,
size_t len, loff_t *off)
{

}
int gec6818_led_close(struct inode *inode, struct file *filp)
{

}
static struct file_operations gec6818_led_fops = {
.owner = THIS_MODULE,
.write = gec6818_led_write,
.open = gec6818_led_open,
.release = gec6818_led_close,
};

e、驱动程序的编译

编译驱动程序和编译应用程序完全不同。编译应用程序使用的是编译器中的库;编译驱动程序使用的是内核源码。
编译驱动程序之前,首先要编译内核源码。

1)使用的内核源码

1
6818GEC.tar.gz

2)将内核源码放在ubuntu中,注意不要放在共享目录下/mnt/hgfs

/home/gec/6818GEC.tar.gz

3)解压内核源码

1
tar -jxvf 6818GEC.tar.gz

4)编译uboot

1
2
3
4
gec@ubuntu:~$ cd 6818GEC/
gec@ubuntu:~/6818GEC$ ls
buildroot GEC6818uboot kernel linux mk out prebuilts prototype tools
gec@ubuntu:~/6818GEC$ ./mk -u

5)编译Linux内核

1
gec@ubuntu:~/6818GEC$ ./mk -k

6)编写一个驱动程序的Makefile

1
2
3
4
5
6
7
8
9
10
11
obj-m := led_drv.o
KERNELDIR:=/home/gec/6818GEC/kernel
CROSS_COMPILE:=/home/gec/6818GEC/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi-

PWD:=$(shell pwd)

default:
$(MAKE) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNELDIR) M=$(PWD) modules

clean:
rm -rf *.o *.order .*.cmd *.ko *.mod.c *.symvers *.tmp_versions

7)编译驱动

1
2
3
4
5
6
7
8
9
gec@ubuntu:/mnt/hgfs/上课笔记/day5/project/led_drv$ make
make ARCH=arm CROSS_COMPILE=/home/gec/6818GEC/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi- -C /home/gec/6818GEC/kernel M=/mnt/hgfs/上课笔记/day5/project/led_drv modules
make[1]: Entering directory '/home/gec/6818GEC/kernel'
CC [M] /mnt/hgfs/上课笔记/day5/project/led_drv/led_drv.o
Building modules, stage 2.
MODPOST 1 modules
CC /mnt/hgfs/上课笔记/day5/project/led_drv/led_drv.mod.o
LD [M] /mnt/hgfs/上课笔记/day5/project/led_drv/led_drv.ko
make[1]: Leaving directory '/home/gec/6818GEC/kernel'

8)通过串口将led_drv.ko下载到开发板

9)安装驱动

1
[root@GEC6818 /test]# insmod led_drv.ko

8、实现音乐播放

a、音乐播放器

音乐一般都是mp3格式的,需要解压和播放。播放器一般使用madplay,开源免费的播放器。
检查madplay是否安装:

1
2
[root@GEC6818 /mnt/udisk]#which madplay
/usr/bin/madplay

如何移植一个madplay。
去网上下载madplay的源码(多个软件包),经过配置、编译、布置才可以使用。

1
2
3
4
5
6
7
8
[root@GEC6818 /]#find ./ -name *mp3
[root@GEC6818 /]#find ./ -name *mp3
./IOT/mp3
./IOT/mp3/end.mp3
./IOT/mp3/left.mp3
./IOT/mp3/right.mp3
./IOT/mp3/test.mp3
./usr/local/Qt-Embedded-5.7.0/examples/svg/embedded/desktopservices/data/sax.mp3

播放:

1
madplay /IOT/test.mp3

b、编写程序,调用madplay播放器

1
2
#include <stdlib.h>
int system(const char *command);

播放音乐:

1
2
system(“madplay /IOT/test.mp3”);
system(“madplay /IOT/test.mp3 -r ”);

c、madplay的使用

创建一个新的进程,这个进程调用了madplay,用来播放mp3.
如何实现暂停、继续和停止。向madplay这个进程发送信号。
kill命令,发信号给进程的ID(PID)
killall命令,发信号给进程的名字
PID是会变的。

1
2
3
4
5
6
7
8
//播放音乐:
system(“madplay /IOT/test.mp3 &”); //madplay进程在后台运行
//暂停播放:
system(“killall -STOP madplay &”);
//继续播放:
system(“killall -CONT madplay &”);
//停止播放:
system(“killall -KILL madplay &”);

注意:
声卡只有一个,在一个时刻只能播放一个mp3。否则声卡会报busy做错。

9、学会多线程编程后的多线程设计

a、创建线程

1
2
3
4
5
6
#include <pthread.h>

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);

Compile and link with -pthread.

参数说明

1
2
3
4
pthread_t *thread ---新创建的线程的ID,也是一个正整数
const pthread_attr_t *attr ----线程的属性,一般为NULL
void *(*start_routine) (void *) -----创建的线程的执行函数
void *arg -----向子线程执行函数传递的参数

返回值:

1
2
3
RETURN VALUE
On success, pthread_create() returns 0; on error, it returns an error
number, and the contents of *thread are undefined.

注意:

1.编译多线程程序的时候,需要引用多线程库,需要加一个后缀 -lpthread
2.如果主线程退出,这个主线程常见的所有子线程都会自动退出。
3.子线程和主线程共享全局变量

b、子线程的退出

1)return 0,子线程直接退出
2)如果一个线程,让另一个线程退出,如何实现。
例:在主线程中,让子线程退出??

1
2
#include <pthread.h>
int pthread_cancel(pthread_t thread);

参数:
pthread_t thread —- 线程的ID

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
触屏的坐标 //全局变量
void color_thread() //子线程
{
while(1)//不会产生阻塞
{
显示红色
显示绿色
显示蓝色
}
}

main() //主线程
{
判断是否进入循环播放单色(液晶屏检测)
thread_create()创建一个子线程void color_thread() ,用来循环显示单色。
获取触摸屏坐标 //可能会产生阻塞。但是子线程可以持续运行
判断触摸屏坐标,否是退出循环播放单色,返回主控界面。
若是,pthread_cancel()退出子线程
}

10、进行各个功能的整合

编写各个单独功能函数后再编写Makefile来进行统一编译,生成最终开发板用的可执行文件main。
Makefile如下:

1
2
3
4
5
6
main:main.o lcd.o touch.o led.o beep.o
    arm-linux-gcc -o main  $^ -lpthread
%.o : %.c
    arm-linux-gcc -o $@  $< -c  
clean:
    rm *.o main

三、功能设计

1、LED控制功能
2、实现液晶屏显示界面,附带液晶屏检测功能
3、实现触摸屏功能
4、实现音乐播放
5、实现数码相册控制
6、蜂鸣器控制功能

四、运行结果及分析

1、LED控制功能
LED测试
LED测试
LED测试

程序运行效果如上,LED驱动功能正常,可以控制LED的亮灭。
2、实现液晶屏显示界面,附带液晶屏检测功能

LCD测试
LCD测试
LCD测试
LCD测试

程序运行效果如上,功能正常,屏幕内容完美显示。
3、实现触摸屏功能

触摸屏测试
触摸屏测试
触摸屏测试

程序运行效果如上,可以通过触摸屏来判断点击的方位,正常切换界面。
4、音乐播放功能
音乐播放
音乐播放功能正常,能够控制音乐的暂停和播放。
5、实现数码相册控制
数码相册
程序运行效果如上,数码相册功能正常。
6、蜂鸣器控制功能
蜂鸣器测试
蜂鸣器测试
驱动安装正常,程序能够正常控制蜂鸣器的开启和关闭。

五、总结

本次实训是做一个基于GEC-6818开发平台的智能家居控制系统,利用定制的Linux系统,通过C语言的编程,经过交叉编译环境,让编译的文件能够直接适用于ARM开发板。用7寸LCD电容式触摸屏作为人机互交的方式,可以切换相册、音乐、led蜂鸣器等功能,并显示相应的界面。
在过程中我收获了非常非常充足的嵌入式知识,包括驱动开发,应用开发,显存映射,掌握了ARM的结构体系,了解了部分的Linux命令,对文件的读写操作等,初步掌握了交叉编译方式,加强了对嵌入式产品的认知。同时,在本课程设计时的调试、排错,让我对C语言和linux环境下编程进行了一次查漏补缺,也巩固了C语言基础。让我对ARM系列的开发板进行开发,用固定的硬件设备实现多种功能,了解ARM开发的文件读取方式、交叉编译环境。也学会了无显示界面时使用Linux命令操作设备。这也让我对嵌入式开发产生的浓厚的兴趣。

附录:项目核心源程序

Main.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
#include <stdio.h>
#include "lcd.h"
#include "beep.h"
#include "led.h"
#include "touch.h"
#include "pthread.h"
#define MAIN 0
#define CTL 1//Control面板
#define LED 2
#define LCDt 3//LCD test
#define MUS 4//Music
#define BEEP 5
#define DP 6//数字相册
#define HA 7//home appliances


void *check_lcd(void *arg)
{
while(1)
{
lcd_show_color(RED);
sleep(1);
lcd_show_color(GREEN);
sleep(1);
lcd_show_color(BLUE);
sleep(1);
lcd_show_color(YELLOW);
sleep(1);
lcd_show_color(WHITE);
sleep(1);
lcd_show_color(BLACK);
sleep(1);
}
}

void *digital_photo(void *arg)
{
while (1)
{
lcd_show_bmp("/pic/xx.bmp");
usleep(50000);
ts_get_xy();//阻塞
lcd_show_bmp("/pic/jp.bmp");
usleep(50000);
ts_get_xy();
lcd_show_bmp("/pic/sl.bmp");
usleep(50000);
ts_get_xy();
}
}

int main(void)
{
int mode=0;
struct xy ts;
pthread_t tid_ts;
lcd_show_bmp("/pic/main"); //进入欢迎界面
while(1)
{
ts = ts_get_xy();
if(650<ts.x && ts.x<800 && 420<ts.y && ts.y<480)
{
lcd_show_bmp("/pic/control"); //进入主控界面
mode = CTL;
while(1)
{
ts = ts_get_xy();
if(!(ts.x==0 && ts.y==0))
{
printf("position:(%d,%d)\n",ts.x,ts.y);
}
if (ts.x > 700 && ts.x < 800 && ts.y > 0 && ts.y < 60) //在任何界面下都可以BACK
{
mode = CTL;
lcd_show_bmp("/pic/control"); //进入主控界面
printf("BACK\n");
ts.x=0;ts.y=0;
}
if (ts.x > 0 && ts.x < 100 && ts.y > 0 && ts.y < 60 && mode == CTL ) //返回MAIN界面
{
mode = MAIN;
lcd_show_bmp("/pic/main"); //进入main界面
printf("BACK MAIN\n");
ts.x=0;ts.y=0;
}
if((60<ts.x && ts.x<300 && 140<ts.y && ts.y<200 && mode == CTL) || mode == LED)//进入LED控制界面
{
mode = LED;
lcd_show_bmp("/pic/led_ctrl");
if (ts.x > 100 && ts.x < 250 && ts.y > 100 && ts.y < 160)//led1 on
{
led_ctrl(LED1, LED_ON);
printf("LED1 on\n");
ts.x=0;ts.y=0;
}
if (ts.x > 100 && ts.x < 250 && ts.y > 200 && ts.y < 260)//led1 off
{
led_ctrl(LED1, LED_OFF);
printf("LED1 off\n");
ts.x=0;ts.y=0;
}
if (ts.x > 100 && ts.x < 250 && ts.y > 300 && ts.y < 360)//led2 on
{
led_ctrl(LED2, LED_ON);
printf("LED2 on\n");
ts.x=0;ts.y=0;
}
if (ts.x > 100 && ts.x < 250 && ts.y > 400 && ts.y < 460)//led2 off
{
led_ctrl(LED2, LED_OFF);
printf("LED2 off\n");
ts.x=0;ts.y=0;
}
if (ts.x > 530 && ts.x < 680 && ts.y > 100 && ts.y < 160)//led3 on
{
led_ctrl(LED3, LED_ON);
printf("LED3 on\n");
ts.x=0;ts.y=0;
}
if (ts.x > 530 && ts.x < 680 && ts.y > 200 && ts.y < 260)//led3 off
{
led_ctrl(LED3, LED_OFF);
printf("LED3 off\n");
ts.x=0;ts.y=0;
}
if (ts.x > 530 && ts.x < 680 && ts.y > 300 && ts.y < 360)//led4 on
{
led_ctrl(LED4, LED_ON);
printf("LED4 on\n");
ts.x=0;ts.y=0;
}
if (ts.x > 530 && ts.x < 680 && ts.y > 400 && ts.y < 460)//led4 off
{
led_ctrl(LED4, LED_OFF);
printf("LED4 off\n");
ts.x=0;ts.y=0;
}
}
if (ts.x > 60 && ts.x < 300 && ts.y > 240 && ts.y < 300 && mode == CTL)//进入LCD测试界面
{
mode = LCDt;
//进入液晶屏检测功能
int ret;
ret = pthread_create(&tid_ts, NULL, check_lcd, NULL);
if(ret < 0 )
{
perror("create ts_thread error");
return -1;
}
while (1)
{
ts = ts_get_xy();
if (ts.x > 700 && ts.x < 800 && ts.y > 0 && ts.y < 60)
{
//退出液晶屏检测
pthread_cancel(tid_ts);
break;
}
}
mode=CTL;
ts.x=0;ts.y=0;
}
if ((ts.x > 60 && ts.x < 300 && ts.y > 350 && ts.y < 420 && mode == CTL) || mode == MUS)//进入Music界面
{
lcd_show_bmp("/pic/music.bmp");
mode = MUS;
if (ts.x > 30 && ts.x < 170 && ts.y > 210 && ts.y < 260)//Music界面:音乐播放
{
printf("Music begin\n");
system("madplay /IOT/mp3/test.mp3 &"); //madplay进程在后台运行
}
if (ts.x > 135 && ts.x < 273 && ts.y > 300 && ts.y < 400)//上一首
{
printf("Previous music \n");
}
if (ts.x > 515 && ts.x < 657 && ts.y > 300 && ts.y < 400)//下一首
{
printf("Next music \n");
}
if (ts.x > 230 && ts.x < 370 && ts.y > 210 && ts.y < 260)//Music界面:音乐暂停
{
printf("Music pause\n");
system("killall -STOP madplay &");
ts.x=0;ts.y=0;
}
if (ts.x > 420 && ts.x < 560 && ts.y > 210 && ts.y < 260)//Music界面:音乐继续
{
printf("Music continue\n");
system("killall -CONT madplay &");
ts.x=0;ts.y=0;
}
if (ts.x > 620 && ts.x < 760 && ts.y > 210 && ts.y < 260)//Music界面:音乐停止
{
printf("Music stop\n");
system("killall -KILL madplay &");
ts.x=0;ts.y=0;
}
}
if ((ts.x > 460 && ts.x < 700 && ts.y > 140 && ts.y < 200 && mode == CTL )|| mode == BEEP)//进入蜂鸣器控制
{
mode = BEEP;
lcd_show_bmp("/pic/beep_ctrl.bmp");
if (ts.x > 120 && ts.x < 310 && ts.y > 200 && ts.y < 322)//蜂鸣器响
{
beep_ctrl(BEEP_ON);
printf("Beep open \n");
ts.x=0;ts.y=0;
}
if (ts.x > 490 && ts.x < 670 && ts.y > 200 && ts.y < 322)//蜂鸣器停
{
beep_ctrl(BEEP_OFF);
printf("Beep stop \n");
ts.x=0;ts.y=0;
}
}
if (ts.x > 460 && ts.x < 700 && ts.y > 240 && ts.y < 300 && mode == CTL)//进入数码相册
{
mode = HA;
int ret;
ret = pthread_create(&tid_ts, NULL,digital_photo, NULL);
if(ret < 0 )
{
perror("create ts_thread error");
return -1;
}
while (1)
{
ts = ts_get_xy();
if (ts.x > 700 && ts.x < 800 && ts.y > 0 && ts.y < 60)
{
//退出电子相册
pthread_cancel(tid_ts);
break;
}
}
mode=CTL;
ts.x=0;ts.y=0;
}
if ((ts.x > 460 && ts.x < 700 && ts.y > 350 && ts.y < 420)&& mode == CTL || mode == HA)//进入家电控制
{
mode = HA;
lcd_show_bmp("/pic/key.bmp");
if (ts.x > 158 && ts.x < 298 && ts.y > 195 && ts.y < 255)//风扇开
{
printf("Fan:on \n");
ts.x=0;ts.y=0;
}
if (ts.x > 158 && ts.x < 298 && ts.y > 320 && ts.y < 372)//风扇关
{
printf("Fan:off \n");
ts.x=0;ts.y=0;
}
if (ts.x > 500 && ts.x < 640 && ts.y > 195 && ts.y < 255)//空调开
{
printf("Air-conditioning:on \n");
ts.x=0;ts.y=0;
}
if (ts.x > 500 && ts.x < 640 && ts.y > 320 && ts.y < 372)//空调关
{
printf("Air-conditioning:off \n");
ts.x=0;ts.y=0;
}
}
if (ts.x > 650 && ts.x < 800 && ts.y > 420 && ts.y < 480 && mode == MAIN)//start
{
lcd_show_bmp("/pic/control");
mode = CTL;
ts.x=0;ts.y=0;
}
}
ts.x=0;ts.y=0;
}
}
return 0;
}

touch.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
#include "touch.h"
struct xy ts_get_xy(void)
{
struct xy z;
int x,y;
int fd_ts;
fd_ts = open("/dev/input/event0", O_RDONLY);
if(fd_ts == -1)
{
perror("open ts");
exit(-1);
}

struct input_event ts_data;
for (int i = 0; i < 6; i++)
{
//2、读取触摸屏的数据
read(fd_ts, &ts_data, sizeof(struct input_event));
if(ts_data.type == EV_ABS)
{
if(ts_data.code == ABS_X)
x = ts_data.value*800/1024;
else if(ts_data.code == ABS_Y)
y = ts_data.value*480/600;
}
}
z.x=x;
z.y=y;

close(fd_ts);
return z;
}

Touch.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#ifndef __TOUCH_H_
#define __TOUCH_H_
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h>
#include <stdlib.h>
struct xy
{
int x;
int y;
};
struct xy ts_get_xy();

#endif

Lcd.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
#include <stdio.h>
#include "lcd.h"


int lcd_show_color(unsigned int color)
{
//1、打开液晶屏---open()
int lcd_fd;
lcd_fd = open("/dev/fb0",O_RDWR);
if(lcd_fd == -1)
{
perror("open lcd failed");
return -1;
}

//2、显存映射,得到显存的首地址---mmap()
int *lcd_base = NULL;
lcd_base = mmap(NULL, // 填NULL表示显存地址由系统自动分配
800*480*4, // 整块屏幕的显存大小
PROT_READ|PROT_WRITE, // 显存保护权限:可读可写
MAP_SHARED, // 多进程共享设置
lcd_fd, // LCD文件描述符
0); // 0表示不偏移
if(lcd_base == NULL)
{
printf("lcd mmap error\n");
return -1;
}

//3、通过显存首地址,直接向显存写入颜色数据
for(int i=0;i<800*480;i++)
*(lcd_base+i) = color;

//4、解除显存映射----munmap()
munmap(lcd_base, 800*480*4);

//5、关闭液晶屏----close()
close(lcd_fd);

return 0;
}


int lcd_show_flag(unsigned int state)
{
//1、打开液晶屏---open()
int lcd_fd;
int x,y;
int buf2[480][800];
lcd_fd = open("/dev/fb0",O_RDWR);
if(lcd_fd == -1)
{
perror("open lcd failed");
return -1;
}

//2、显存映射,得到显存的首地址---mmap()
int *lcd_base = NULL;
lcd_base = (int *)mmap(NULL, 800*480*4, PROT_READ|PROT_WRITE,
MAP_SHARED, lcd_fd, 0);
if(lcd_base == NULL)
{
printf("lcd mmap error\n");
return -1;
}

switch(state)
{
case JAPAN:
for(x=0; x<480; x++)
{
for(y=0; y<800; y++)
{
if(0.8*(x-240)*(x-240)+(y-400)*(y-400) <= 150*150)
{
buf2[x][y] = 0x00FF0000;
}
else
{
buf2[x][y] = 0x00FFFFFF;
}
}
}
for(int x=0; x<480; x++)
{
for(int y=0; y<800; y++)
*(lcd_base+x*800+y) = buf2[x][y];
}
break;
case RUS:
for(y=0; y<480/3; y++)
for(x=0; x<800; x++)
*(lcd_base+x+800*y) = WHITE;
for(y=480/3; y<480/3*2; y++)
for(x=0; x<800; x++)
*(lcd_base+x+800*y) = BLUE;
for(y=480/3*2; y<480; y++)
for(x=0; x<800; x++)
*(lcd_base+x+800*y) = RED;
break;
default:
break;
}

//4、解除显存映射----munmap()
munmap(lcd_base, 800*480*4);

//5、关闭液晶屏----close()
close(lcd_fd);
return 0;
}


int lcd_show_bmp(char *bmp_name)
{
int lcd_fd;

lcd_fd = open("/dev/fb0",O_RDWR);
if(lcd_fd == -1)
{
perror("open lcd failed");
return -1;
}
int *lcd_base = NULL;
lcd_base = (int *)mmap(NULL, 800*480*4, PROT_READ|PROT_WRITE,
MAP_SHARED, lcd_fd, 0);
if(lcd_base == NULL)
{
printf("lcd mmap error\n");
return -1;
}
char buf[800*480*3];
//重新定义一个数组存放800*480个像素点
int new_buf[800*480];
int x, y;
int bmp;
bmp = open(bmp_name,O_RDONLY);
if(bmp == -1)
{
printf("打开BMP图片失败\n");
return -1;
}
//剔除54字节
lseek(bmp,54,SEEK_SET);
read(bmp,buf,800*480*3);
close(bmp);
//把旧数组转移到新数组
//把旧数组转移到新数组
for (y = 0; y < 480; y++)
{
for (x = 0; x < 800; x++)
{
new_buf[800*(479-y)+x] = buf[3*(800*y+x)]<<0 | buf[3*(800*y+x)+1]<<8 | buf[3*(800*y+x)+2]<<16;
}
}
//把读取到的像素点(buf里面的像素点),写入lcd(fb0)
for(int i=0;i<800*480;i++)
*(lcd_base+i) = new_buf[i];
// for(int i=0;i<800*480;i++)
// *(FB+i) = (buf[3*i+2]<<16) + (buf[3*i+1] <<8) + buf[3*i];
//4、解除显存映射----munmap()
munmap(lcd_base, 800*480*4);

//5、关闭液晶屏----close()
close(lcd_fd);
}

Lcd.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
#ifndef _LCD_H

#define _LCD_H 1
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>

#define USSR 0
#define JAPAN 1
#define RUS 2
#define MAIN1 3

#define BLUE 0x000000FF // 纯白色16进制表示aRGB
#define RED 0x00FF0000 // 纯红色16进制表示aRGB
#define GREEN 0x0000FF00 // 纯蓝色16进制表示aRGB
#define WHITE 0x00FFFFFF // 白色16进制表示aRGB
#define YELLOW 0x00FFFF00 // 黄色16进制表示aRGB
#define BLACK 0x00000000 // 黑色16进制表示aRGB

int lcd_show_color(unsigned int color);
int lcd_show_flag(unsigned int state);
int lcd_show_bmp(char *bmp_name);

#endif /* !_LCD_H */

Led.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include "led.h"

int led_ctrl(unsigned char led_num, unsigned char led_state)
{
//1、打开LED驱动文件---open()
int led_fd;
// /dev/led_drv---LED设备驱动文件
led_fd = open("/dev/led_drv",O_RDWR);
if(led_fd == -1)
{
perror("open led failed");
return -1;
}
//2、向LED灯驱动写入灯号和灯的状态数据
char led_buf[2];
led_buf[1]=led_num;led_buf[0]=led_state;
write(led_fd, led_buf, 2);
//3、关闭LED灯----close()
close(led_fd);
return 0;
}

Led.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#ifndef __LED_H_
#define __LED_H_
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h>
#include <stdlib.h>
#define LED1 7
#define LED2 8
#define LED3 9
#define LED4 10
#define LED_ON 1
#define LED_OFF 2

int led_ctrl(unsigned char led_num, unsigned char led_state);

#endif /* __LED_H_ */

Beep.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include "beep.h"

int beep_ctrl(unsigned char beep_state)
{
//1、打开蜂鸣器驱动文件---open()
int beep_fd;
// /dev/beep_drv---蜂鸣器设备驱动文件
beep_fd = open("/dev/beep_drv",O_RDWR);
if(beep_fd == -1)
{
perror("open led failed");
return -1;
}
//2、向蜂鸣器驱动写入灯号和灯的状态数据
char beep_buf[1];
beep_buf[0]=beep_state;
write(beep_fd,beep_buf, 1);
//3、关闭蜂鸣器驱动
close(beep_fd);
return 0;
}

Beep.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#ifndef __BEEP_H_
#define __BEEP_H_
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h>
#include <stdlib.h>

#define BEEP_ON 1
#define BEEP_OFF 0
int beep_ctrl(unsigned char beep_state);

#endif /* __BEEP_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
/****** 头文件 ******/
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include "cfg_type.h"


/****** LED驱动的资源 *****
GPIOE13---D7 ------ PAD_GPIO_E + 13
GPIOC17---D8 ------ PAD_GPIO_C + 17
GPIOC8 ---D9 ------ PAD_GPIO_C + 8
GPIOC7 ---D10 ------ PAD_GPIO_C +7
*/
#define BEEP_GPIO PAD_GPIO_C + 14

#define BEEP_ON 1
#define BEEP_OFF 0


/******* 定义并初始化文件操作集 ********/
int gec6818_beep_open(struct inode *inode, struct file *filp)
{
printk(KERN_WARNING "gec6818 beep driver init\n");
return 0;
}

ssize_t gec6818_beep_write(struct file *filp, const char __user *buf,
size_t len, loff_t *off)
{
//接收应用程序write()写下来的数据,利用这两个字节的数据控制LED灯。
char beep_flag[1]; //用来接收应用程序写下来的数据
if(len != 1)
return -EINVAL;
if(copy_from_user(beep_flag, buf,len))
return -EFAULT;

switch(beep_flag[0]){//灯号
case BEEP_OFF:
gpio_set_value(BEEP_GPIO, 0);
break;
case BEEP_ON:
gpio_set_value(BEEP_GPIO, 1);
break;
default:
return -EINVAL;
}

return len;
}

int gec6818_beep_close(struct inode *inode, struct file *filp)
{
printk(KERN_WARNING "gec6818 beep driver close\n");
return 0;
}

static const struct file_operations gec6818_beep_fops = {
.owner = THIS_MODULE,
.write = gec6818_beep_write,
.open = gec6818_beep_open,
.release = gec6818_beep_close,
};


/******* 定义一个混杂设备驱动模型 ******/
static struct miscdevice gec6818_misc_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "beep_drv", // /dev/beep_drv
.fops = &gec6818_beep_fops, //文件操作集
};


/**** 驱动的安装函数
#insmod beep_drv.ko -->module_init()-->gec6818_beep_init()
******/
static int __init gec6818_beep_init(void)
{
int ret;
//完成资源的申请,如GPIO口;注册驱动到内核。
/******* 申请4个GPIO资源 *******/
gpio_free(BEEP_GPIO);
ret = gpio_request(BEEP_GPIO, "BEEP");
if(ret < 0)
{
printk(KERN_WARNING "gpio request error\n");
return -1;
}

gpio_direction_output(BEEP_GPIO, 0);

misc_register(&gec6818_misc_dev);

printk(KERN_WARNING "gec6818 beep driver init\n");
return 0;
}

/**** 驱动的卸载函数
#rmmod beep_drv.ko -->module_exit()-->gec6818_beep_exit()
******/
static void __exit gec6818_beep_exit(void)
{
//安装函数的反函数,释放资源,注销驱动。
/******* 释放4个GPIO资源 *******/
gpio_free(BEEP_GPIO);

misc_deregister(&gec6818_misc_dev);

printk(KERN_WARNING "gec6818 beep driver exit\n");
}


//驱动的入口。#insmod beep_drv.ko调用moudle_init()
module_init(gec6818_beep_init);
//驱动的出口。#rmsmod beep_drv.ko调用moudle_exit()
module_exit(gec6818_beep_exit);

/*** module的描述 #modinfo beep_drv.ko ***/
MODULE_AUTHOR("1175595406@qq.com");
MODULE_DESCRIPTION("BEEP driver for GEC6818)");
MODULE_LICENSE("GPL");
MODULE_VERSION("V1.0");

LED驱动源文件:

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
/****** 头文件 ******/
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include "cfg_type.h"


/****** LED驱动的资源 *****
GPIOE13---D7 ------ PAD_GPIO_E + 13
GPIOC17---D8 ------ PAD_GPIO_C + 17
GPIOC8 ---D9 ------ PAD_GPIO_C + 8
GPIOC7 ---D10 ------ PAD_GPIO_C +7
*/
#define LED1_GPIO PAD_GPIO_E + 13
#define LED2_GPIO PAD_GPIO_C + 17
#define LED3_GPIO PAD_GPIO_C + 8
#define LED4_GPIO PAD_GPIO_C + 7


#define LED1 7
#define LED2 8
#define LED3 9
#define LED4 10

#define LED_ON 1
#define LED_OFF 2


/******* 定义并初始化文件操作集 ********/
int gec6818_led_open(struct inode *inode, struct file *filp)
{
printk(KERN_WARNING "gec6818 led driver init\n");
return 0;
}

ssize_t gec6818_led_write(struct file *filp, const char __user *buf,
size_t len, loff_t *off)
{
//接收应用程序write()写下来的数据,利用这两个字节的数据控制LED灯。
char led_flag[2]; //用来接收应用程序写下来的数据
if(len != 2)
return -EINVAL;
if(copy_from_user(led_flag, buf,len))
return -EFAULT;

switch(led_flag[1]){//灯号
case LED1:
if(led_flag[0] == LED_ON)
gpio_set_value(LED1_GPIO, 0);
else if(led_flag[0] == LED_OFF)
gpio_set_value(LED1_GPIO, 1);
else
return -EINVAL;
break;

case LED2:
if(led_flag[0] == LED_ON)
gpio_set_value(LED2_GPIO, 0);
else if(led_flag[0] == LED_OFF)
gpio_set_value(LED2_GPIO, 1);
else
return -EINVAL;
break;

case LED3:
if(led_flag[0] == LED_ON)
gpio_set_value(LED3_GPIO, 0);
else if(led_flag[0] == LED_OFF)
gpio_set_value(LED3_GPIO, 1);
else
return -EINVAL;
break;

case LED4:
if(led_flag[0] == LED_ON)
gpio_set_value(LED4_GPIO, 0);
else if(led_flag[0] == LED_OFF)
gpio_set_value(LED4_GPIO, 1);
else
return -EINVAL;
break;
default:
return -EINVAL;
}

return len;
}

int gec6818_led_close(struct inode *inode, struct file *filp)
{
printk(KERN_WARNING "gec6818 led driver close\n");
return 0;
}

static const struct file_operations gec6818_led_fops = {
.owner = THIS_MODULE,
.write = gec6818_led_write,
.open = gec6818_led_open,
.release = gec6818_led_close,
};


/******* 定义一个混杂设备驱动模型 ******/
static struct miscdevice gec6818_misc_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "led_drv", // /dev/led_drv
.fops = &gec6818_led_fops, //文件操作集
};


/**** 驱动的安装函数
#insmod led_drv.ko -->module_init()-->gec6818_led_init()
******/
static int __init gec6818_led_init(void)
{
int ret;
//完成资源的申请,如GPIO口;注册驱动到内核。
/******* 申请4个GPIO资源 *******/
gpio_free(LED1_GPIO);
gpio_free(LED2_GPIO);
gpio_free(LED3_GPIO);
gpio_free(LED4_GPIO);
ret = gpio_request(LED1_GPIO, "led1");
if(ret < 0)
{
printk(KERN_WARNING "gpio request error\n");
return -1;
}
gpio_request(LED2_GPIO, "led2");
gpio_request(LED3_GPIO, "led3");
gpio_request(LED4_GPIO, "led4");

gpio_direction_output(LED1_GPIO, 1);
gpio_direction_output(LED2_GPIO, 1);
gpio_direction_output(LED3_GPIO, 1);
gpio_direction_output(LED4_GPIO, 1);

misc_register(&gec6818_misc_dev);

printk(KERN_WARNING "gec6818 led driver init\n");
return 0;
}

/**** 驱动的卸载函数
#rmmod led_drv.ko -->module_exit()-->gec6818_led_exit()
******/
static void __exit gec6818_led_exit(void)
{
//安装函数的反函数,释放资源,注销驱动。
/******* 释放4个GPIO资源 *******/
gpio_free(LED1_GPIO);
gpio_free(LED2_GPIO);
gpio_free(LED3_GPIO);
gpio_free(LED4_GPIO);

misc_deregister(&gec6818_misc_dev);

printk(KERN_WARNING "gec6818 led driver exit\n");
}


//驱动的入口。#insmod led_drv.ko调用moudle_init()
module_init(gec6818_led_init);
//驱动的出口。#rmsmod led_drv.ko调用moudle_exit()
module_exit(gec6818_led_exit);

/*** module的描述 #modinfo led_drv.ko ***/
MODULE_AUTHOR("1175595406@qq.com");
MODULE_DESCRIPTION("LED driver for GEC6818)");
MODULE_LICENSE("GPL");
MODULE_VERSION("V1.0");