概述

​ 项目地址:GitHub

​ 基于 GEC 6818 开发板,使用 Linux 下 C 编程实现的智能家居控制系统。可实现实时采集并显示周围环境的数据,当数据高于或低于某一阈值时,执行不同的动作,如报警、亮灯。

​ 代码文件结构:

​ 说明:

  • lcd.c — 打开/关闭屏幕、在屏幕上画点和画图
  • bmp.c — 在屏幕上显示 bmp 图片
  • word.c — 在屏幕上显示文本
  • touch.c — 获取在屏幕上点按的坐标和手指滑动的方向
  • uart.c — 获取并解析传感器数据、控制家具

环境准备

交叉编译

​ 在一个环境下编译生成,适用于另一个环境下运行的可执行文件的过程。

​ 由于 GEC6818 使用的是运行在 ARM 架构上的 Linux 操作系统,因此代码需要通过交叉编译才能正常运行在开发板上。

​ 指令:arm-linux-gcc *.c -o main.out -lm -pthread 。由于项目代码中使用了数学函数和线程函数,因此在编译时需要链接这两个库,即 -lmpthread

设置共享文件夹

​ 实现 Windows 和虚拟机的文件互通。在 Windows 中编辑代码,在 Linux 中编译代码。

​ 首先在 Windows 下创建项目工程文件夹(如 smart_home),把项目的代码都放入该文件夹中。

​ 虚拟机设置 –> 选项 –> 共享文件夹 –> 总是启用 –> 添加 –> 选择 smart_home 作为共享文件夹 –> 确定。

​ Linux 中共享文件夹的路径: /mnt/hgfs/<共享文件夹名字> ,如 /mnt/hgfs/share 。共享文件夹的名字可在上一步中自由命名。

​ cd 进入共享文件夹路径后,交叉编译即可。

其他

​ 使用 SecureCRT 连接 6818 开发板的操作系统。

​ 烧录文件:rx <filename>

​ 赋予 main.out 可执行权限:chmod +x main.outchmod 0777 main.out(超级权限)。

​ 在 Linux 中查看函数的说明文档:man 命令。

  • man -f 函数名 — 查看页码及对应页的内容概要
  • man 页码 函数名 — 查看指定页的详细内容
  • 键入 q 退出文档。

LCD显示屏

显色原理

​ 6818 开发板 LCD 屏幕的分辨率:800*480 。

​ 分辨率:单位面积内的像素点的个数。所以 800*480 表示一行有 800 个像素点, 总共有 480 行。

​ 像素点(pixel) 是能够显示某种颜色的点。在屏幕上显示一个颜色,就是给对应的像素点一个颜色值。

​ 各种各样的颜色都由三种基本颜色组成:Red Green Blue ,并用量化表示颜色的程度。

​ 量化:数量化,数值大小表示程度,每种颜色的分量占 1 个字节,0255( 0x000xFF )。

​ Red Green Blue
​ 0xFF 0x00 0x00 —> 0xFF0000 (正红色)

​ LCD 屏幕的每一个像素点占 4 个字节(a r g b),a 表示透明度。在后续的画点、画图、显示图片、显示字符中,对 LCD 屏幕的每一个像素点写入的颜色数据不必一定占满 4 个字节,如只写入 0xFF0000(3个字节),这种不指定透明度的情况下,默认透明度最大,即实际为 0xFF0000FF(4个字节)。

操作系统的作用

​ 提供接口。在内存中开辟一块缓冲区,用来保存屏幕上每一个像素点的颜色值。应用程序只需要把要显示的颜色值写到这个缓冲区中即可,操作系统将根据写入的内容渲染屏幕。

​ 这个缓冲区在 Linux 操作系统中称为帧缓冲(Frame Buffer)。帧缓冲设备是对具体图像硬件的一种抽象,它让上层应用不必关心底层硬件的实现细节。

​ 6818 开发板屏幕对应的设备文件路径名为 /dev/fb0 ,只需要把颜色值写入到 /dev/fb0 文件中,底层驱动就会把屏幕上对应的像素点显示对应的颜色。

文件IO

open

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
#include <sys/types.h>      
#include <sys/stat.h>
#include <fcntl.h>

int open(const char *pathname, int flags);

功能: 打开或者创建一个文件

参数:
pathname:指定要打开的文件的路径名(不带路径则默认当前路径)
flags:打开文件的方式
- O_RDONLY 只读
- O_WRONLY 只写
- O_RDWR 读写

返回值:
int类型 文件描述符fd
如果一个文件被成功打开,那么就可以用一个int类型的整数来表示这个文件
后续对于该文件的所有操作,都是去操作这个整数
失败 返回-1

例子:
int fd = open( "1.txt" , O_RDWR );
if( fd == -1 ){
perror("open 1.txt error ");
return -1;
}

write

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <unistd.h>

ssize_t write(int fd, const void *buf, size_t count);

功能: 往一个文件描述符中写入数据

参数:
fd:指定要写入的文件的文件描述符
buf:void * 通用指针
指定的空间,保存要写入的数据
count: 指定要写入的字节数

返回值:
成功,返回实际写入的字节数
失败,返回-1

read

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);

功能:从一个文件中读取数据
参数:
fd:指定要读取的文件的描述符
buf:指向的空间用来保存读取到的数据
count:指定要读取多少个字节的数据

返回值:
成功,返回实际读取到的字节数 >0
= 0 表示读到文件末尾了
失败,返回-1

lseek

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <sys/types.h>
#include <unistd.h>

off_t lseek(int fd, off_t offset, int whence);

功能:重定位文件读写的指针

参数:
fd:指定要操作的文件的描述符
offset:偏移量
whence:定位方式
- SEEK_SET 文件开头位置
新的光标位置 = 文件开头位置 + 偏移量(>=0
- SEEK_CUR 当前光标位置
新的光标位置 = 当前光标位置 + 偏移量(可正可负)
- SEEK_END 文件末尾位置
新的光标位置 = 当前光标位置 + 偏移量(可正可负)

返回值:
成功,返回当前的光标距离文件开头的偏移量
失败,返回-1

close

1
2
3
4
5
6
7
8
9
10
11
12
#include <unistd.h>

int close(int fd);

功能:关闭一个文件描述符

参数:
fd:指定要关闭的文件的文件描述符

返回值:
成功,返回0
失败,返回-1

内存映射

​ 使用 write 函数向 /dev/fb0 中写入颜色值的效率不高,因为:

  1. 系统拷贝缓冲区的数据到内存需要耗时间。
  2. write 是系统调用的函数,函数调用本身会有开销。
  3. 系统状态的切换(用户态和内核态)也有开销。

​ Frame Buffer 本身就是一块内存,在 C 语言中只要知道一个内存的地址,就可以通过指针去操作这个内存。如果能够获取到帧缓冲的首地址,那么每个像素点的位置只需要换算成相对首地址的偏移量即可。

​ 如果 int *plcd 指向帧缓冲的首地址,那么 *plcd = 0xFF0000 就表示对第 0 行 0 列的像素点写入正红色,*(plcd+800) = 0xFF0000 表示对第 1 行第 1 列的像素点写入正红色。

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
#include <sys/mman.h>

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

功能:映射一个文件或者设备到内存

参数:
addr:地址,指定要映射到内存的哪个地址上去
一般填NULL,表示让系统自行分配
length:指定要映射多大的内存空间(单位:字节)
对于6818开发板:800*480*4
prot: 指定映射区的权限
- PROT_EXEC 可执行
- PROT_READ 可读
- PROT_WRITE 可写
- PROT_NONE 没有权限
读写: PROT_READ | PROT_WRITE
flags: 映射方式
- MAP_SHARED 共享映射,对映射区的操作会立即反馈到文件中
- MAP_PRIVATE 私有映射,对映射区的操作仅对代码可见
fd:指定要映射的文件的描述符
offset:偏移量,指定从文件的哪个位置开始映射
一般为0,表示从文件开头的位置映射

返回值:
成功,返回映射区的首地址
失败,返回 MAP_FAILED

//

int munmap(void *addr, size_t length);

功能:解除映射

参数:
addr: 指定要解除映射区的首地址,即mmap的返回值
length:指定要解除映射的空间的大小(6818开发板:800*480*4

返回值:
成功,返回0
失败,返回-1

实现开关屏幕 画点 画图

lcd.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#ifndef __LCD_H__
#define __LCD_H__

//打开LCD
void lcd_init();
//关闭LCD
void lcd_close();
//在屏幕上画一个点
void display_point(int x, int y, int color);
//在屏幕上铺满纯色
void show_picture(int color);
//在屏幕上画一个纯色矩形
void display_rectangle(int x1, int x2, int y1, int y2, int color);
//在屏幕上画一个纯色圆形
void draw_circle(int a, int b, int r, int color);

#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
#include "lcd.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <math.h>

int fd = -1;
int *plcd = NULL;
const int row = 480;
const int col = 800;

void lcd_init()
{
fd = open("/dev/fb0", O_RDWR);
if(fd==-1){
perror("/dev/fb0 open error");
exit(-1);
}
plcd = mmap(NULL, row*col*4, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if(plcd==MAP_FAILED){
perror("mmap function error");
exit(-1);
}
}

//屏幕的宽对应 x ,长对应 y ,下同
void display_point(int x, int y, int color)
{
if(x>=0&&x<row && y>=0&&y<col){
*(plcd+x*col+y) = color;
}
}

void display_rectangle(int x1, int x2, int y1, int y2, int color)
{
for(int i=x1; i<=x2; i++){
for(int j=y1; j<=y2; j++){
display_point(i, j, color);
}
}
}

void draw_circle(int a, int b, int r, int color)
{
for(int i=0; i<row; i++){
for(int j=0; j<col; j++){
if(pow((i-a), 2)+pow((j-b), 2)<=pow(r, 2)){
display_point(i, j, color);
}
}
}
}

void show_picture(int color)
{
for(int i=0; i<row; i++){
for(int j=0; j<col; j++){
display_point(i, j, color);
}
}
}

void lcd_close()
{
if(munmap(plcd, row*col*4)==-1){
perror("munmap function error");
exit(-1);
}
if(close(fd)==-1){
perror("close /dev/fb0 error");
exit(-1);
}
}

bmp图片

简介

​ bitmap 位图文件,是由 Microsoft 发明的一种无压缩的图片文件格式,每一个像素点的原始数据都保存图片文件中。

​ 32 位 bmp 图片的像素点数据组成:a r g b

​ 24 位 bmp 图片像素点数据组成:r g b

​ 任何格式的图片都是由一个个的像素点组成的。在屏幕上显示图片,就是把图片的像素点的颜色数据解析出来,再将这些数据依次写入到屏幕上像素点即可。

文件格式

BITMAP文件头

​ 保存文件的魔数、大小等数据,固定占 14 个字节。

DIB头

​ 保存图片的宽、高、色深等数据,固定占 40 个字节。

  • 宽度 width :偏移量 0x12 ,占 4 个字节。

    1
    2
    3
    int width = 0;
    lseek( fd, 0x12, SEEK_SET );
    read( fd, &width, 4 );

    width>0 表示在 bmp 图片中每一行的像素点数据从左至右存放
    width<0 表示每一行的像素点数据从右至左存放

  • 高度 height :偏移量 0x16 ,占 4 个字节。

    height>0 从下至上保存每一行的像素点数据
    height<0 从上至下保存每一行的像素点数据

  • 色深 depth :每个像素点所占的 bit 位数,偏移量 0x1C ,占 2 个字节。

    depth=24 24位 bmp 图片
    depth=32 32位 bmp 图片

像素数组

​ 图像的数据区域,保存每一个像素点的颜色分量值 (bgr/bgra)。

  • depth=24 –> bgr
  • depth=32 –> bgra

​ 偏移量:0x36 。

​ 默认情况下, width>0 height>0 。排布顺序从下至上,从左至右。按行存放每一个像素点的颜色分量值
,但是每一行的字节数的必须是 4 的整数倍。

​ 假设 width=10 depth=24 ,则一行有效的字节数为 abs(width)*(depth/8)=30 。但实际的字节数为 30+2 ,因为规定每一行的字节数的必须是 4 的整数倍,需要填充 2 个字节的无效数据 “赖子”。

实现在屏幕上显示bmp图片

bmp.h

1
2
3
4
5
6
7
#ifndef __BMP_H__
#define __BMP_H__

//在屏幕上显示bmp图片
void draw_picture(int x, int y, char* filename);

#endif

bmp.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
#include "bmp.h"
#include "lcd.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

//屏幕的宽对应 x ,长对应 y
void draw_picture(int x, int y, char *filename)
{
/*打开bmp文件*/
int fd = -1;
fd = open(filename, O_RDWR);
if(fd==-1){
perror("bmp file open error");
exit(-1);
}
/*获取bmp图片的数据*/
int width, height;
short depth;
if(lseek(fd, 0x12, SEEK_SET)==-1){
perror("lseek error");
exit(-1);
}
if(read(fd, &width, 4)==-1){
perror("read function error");
exit(-1);
}
if(lseek(fd, 0x16, SEEK_SET)==-1){
perror("lseek error");
exit(-1);
}
if(read(fd, &height, 4)==-1){
perror("read function error");
exit(-1);
}
if(lseek(fd, 0x1C, SEEK_SET)==-1){
perror("lseek error");
exit(-1);
}
if(read(fd, &depth, 2)==-1){
perror("read function error");
exit(-1);
}
int laizi = 0; //赖子
int line_size = 0; //每行实际字节数
laizi = 4-(abs(width)*(depth/8))%4;
if(laizi==4){
laizi = 0;
}
//每行实际的字节数 = 有效字节数 + 赖子
line_size = abs(width)*(depth/8)+laizi;
char buf[abs(height)*line_size];
if(lseek(fd, 0x36, SEEK_SET)==-1){
perror("lseek error");
exit(-1);
}
if(read(fd, buf, abs(height)*line_size)==-1){
perror("read function error");
exit(-1);
}
/*在屏幕上显示图片*/
int color;
int b, g, r;
short a = 0;
int i, j;
int num = 0;
for(i=0; i<abs(height); i++){
for(j=0; j<abs(width); j++){
//合成颜色数据
b = buf[num++];
g = buf[num++];
r = buf[num++];
if(depth==32){
a = buf[num++];
}
color = (a<<24)|(r<<16)|(g<<8)|b;
display_point(height>0 ? x+abs(height)-1-i : x+i, width>0 ? y+j : y+abs(width)-1-j, color);
}
num = num+laizi; //跳过无效的数据
}
/*关闭bmp文件*/
if(close(fd)==-1){
perror("close bmp file error");
exit(-1);
}
}

字符取模

​ 为了在屏幕上显示字符,需要先将字符用点阵表示,然后对点阵进行颜色渲染即可显示字符。

​ 在网上找个工具生成,这里用的工具讲的原理我没看懂😅。不影响,照着它说的步骤着色就是了。

实现在屏幕上显示字符

word.h

1
2
3
4
5
6
7
8
9
10
11
#ifndef __WORD_H__
#define __WORD_H__

//显示单个字符
void show_word(int x, int y, int w, int h, char word[][w/8], int color);
//实现显示倒计时功能
void count_number(int x, int y, int number);
//显示数字文本
void show_number(int x, int y, int number);

#endif

word.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
#include "lcd.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

//定义三维数组表示0-9,第一维指向单个数字的字符点阵
char num[10][46][3] = {
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xFE,
0x00,0x07,0xFF,0x00,0x0F,0xFF,0x80,0x1F,0x9F,0xC0,0x1F,0x07,0xC0,0x3E,0x07,0xE0,
0x3E,0x03,0xE0,0x3C,0x03,0xE0,0x7C,0x03,0xE0,0x7C,0x01,0xF0,0x7C,0x01,0xF0,0x7C,
0x01,0xF0,0x7C,0x01,0xF0,0x7C,0x01,0xF0,0x7C,0x01,0xF0,0x7C,0x01,0xF0,0x7C,0x01,
0xF0,0x7C,0x01,0xF0,0x7C,0x01,0xE0,0x7C,0x03,0xE0,0x7C,0x03,0xE0,0x3E,0x03,0xE0,
0x3E,0x07,0xC0,0x1F,0x0F,0xC0,0x1F,0xFF,0x80,0x0F,0xFF,0x00,0x07,0xFE,0x00,0x00,
0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3C,
0x00,0x00,0x7C,0x00,0x01,0xFC,0x00,0x0F,0xFC,0x00,0x1F,0xFC,0x00,0x1F,0xFC,0x00,
0x1E,0x7C,0x00,0x18,0x7C,0x00,0x00,0x7C,0x00,0x00,0x7C,0x00,0x00,0x7C,0x00,0x00,
0x7C,0x00,0x00,0x7C,0x00,0x00,0x7C,0x00,0x00,0x7C,0x00,0x00,0x7C,0x00,0x00,0x7C,
0x00,0x00,0x7C,0x00,0x00,0x7C,0x00,0x00,0x7C,0x00,0x00,0x7C,0x00,0x00,0x7C,0x00,
0x00,0x7C,0x00,0x00,0x7C,0x00,0x00,0x7C,0x00,0x00,0x7C,0x00,0x00,0x7C,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xFC,
0x00,0x0F,0xFF,0x00,0x1F,0xFF,0x80,0x3F,0x1F,0xC0,0x3C,0x07,0xC0,0x38,0x07,0xC0,
0x00,0x03,0xE0,0x00,0x03,0xE0,0x00,0x03,0xE0,0x00,0x03,0xC0,0x00,0x07,0xC0,0x00,
0x0F,0xC0,0x00,0x0F,0x80,0x00,0x3F,0x00,0x00,0x7E,0x00,0x00,0xFC,0x00,0x03,0xF8,
0x00,0x07,0xF0,0x00,0x0F,0xC0,0x00,0x1F,0x80,0x00,0x1F,0x00,0x00,0x3E,0x00,0x00,
0x3C,0x00,0x00,0x7C,0x00,0x00,0x7F,0xFF,0xE0,0x7F,0xFF,0xE0,0x7F,0xFF,0xE0,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xFC,
0x00,0x1F,0xFE,0x00,0x1F,0xFF,0x00,0x1E,0x1F,0x80,0x18,0x0F,0x80,0x00,0x07,0xC0,
0x00,0x07,0xC0,0x00,0x07,0xC0,0x00,0x07,0xC0,0x00,0x0F,0x80,0x00,0x1F,0x80,0x00,
0xFF,0x00,0x0F,0xFE,0x00,0x0F,0xFE,0x00,0x0F,0xFF,0x00,0x00,0x1F,0x80,0x00,0x0F,
0xC0,0x00,0x07,0xC0,0x00,0x03,0xE0,0x00,0x03,0xE0,0x00,0x03,0xE0,0x00,0x07,0xC0,
0x00,0x07,0xC0,0x38,0x0F,0xC0,0x3F,0xFF,0x80,0x3F,0xFF,0x00,0x3F,0xFE,0x00,0x07,
0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,
0x00,0x00,0x1F,0x80,0x00,0x1F,0x80,0x00,0x3F,0x80,0x00,0x7F,0x80,0x00,0x7F,0x80,
0x00,0xFF,0x80,0x00,0xFF,0x80,0x01,0xFF,0x80,0x03,0xEF,0x80,0x03,0xCF,0x80,0x07,
0xCF,0x80,0x0F,0x8F,0x80,0x1F,0x0F,0x80,0x1F,0x0F,0x80,0x3E,0x0F,0x80,0x7C,0x0F,
0x80,0xF8,0x0F,0x80,0xFF,0xFF,0xF8,0xFF,0xFF,0xF8,0xFF,0xFF,0xF8,0x00,0x0F,0x80,
0x00,0x0F,0x80,0x00,0x0F,0x80,0x00,0x0F,0x80,0x00,0x0F,0x80,0x00,0x0F,0x80,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,
0x80,0x0F,0xFF,0x80,0x1F,0xFF,0x80,0x1F,0xFF,0x80,0x1F,0x00,0x00,0x1F,0x00,0x00,
0x1F,0x00,0x00,0x1F,0x00,0x00,0x1F,0x00,0x00,0x1E,0x00,0x00,0x1E,0xC0,0x00,0x1F,
0xFC,0x00,0x1F,0xFF,0x00,0x1F,0xFF,0x80,0x00,0x1F,0xC0,0x00,0x0F,0xC0,0x00,0x07,
0xC0,0x00,0x03,0xE0,0x00,0x03,0xE0,0x00,0x03,0xE0,0x00,0x03,0xE0,0x00,0x07,0xC0,
0x00,0x07,0xC0,0x38,0x1F,0xC0,0x3F,0xFF,0x80,0x3F,0xFF,0x00,0x3F,0xFE,0x00,0x07,
0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,
0xC0,0x01,0xFF,0xC0,0x03,0xFF,0xC0,0x07,0xE1,0xC0,0x0F,0xC0,0x00,0x1F,0x80,0x00,
0x1F,0x00,0x00,0x1E,0x00,0x00,0x3E,0x00,0x00,0x3E,0x00,0x00,0x3C,0x7E,0x00,0x3D,
0xFF,0x80,0x7F,0xFF,0xC0,0x7F,0xFF,0xC0,0x7F,0x07,0xE0,0x7E,0x03,0xE0,0x7E,0x01,
0xF0,0x7C,0x01,0xF0,0x7C,0x01,0xF0,0x3C,0x01,0xF0,0x3E,0x01,0xF0,0x3E,0x03,0xE0,
0x1F,0x03,0xE0,0x1F,0x87,0xE0,0x0F,0xFF,0xC0,0x07,0xFF,0x80,0x03,0xFF,0x00,0x00,
0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0xFF,
0xE0,0x7F,0xFF,0xE0,0x7F,0xFF,0xE0,0x7F,0xFF,0xE0,0x00,0x03,0xC0,0x00,0x07,0xC0,
0x00,0x07,0x80,0x00,0x0F,0x80,0x00,0x0F,0x00,0x00,0x1F,0x00,0x00,0x1E,0x00,0x00,
0x3E,0x00,0x00,0x3C,0x00,0x00,0x7C,0x00,0x00,0x7C,0x00,0x00,0xF8,0x00,0x00,0xF8,
0x00,0x00,0xF0,0x00,0x01,0xF0,0x00,0x01,0xF0,0x00,0x03,0xE0,0x00,0x03,0xE0,0x00,
0x03,0xE0,0x00,0x03,0xE0,0x00,0x07,0xC0,0x00,0x07,0xC0,0x00,0x07,0xC0,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xFC,
0x00,0x07,0xFF,0x00,0x0F,0xFF,0x80,0x1F,0x8F,0xC0,0x3F,0x07,0xC0,0x3E,0x03,0xC0,
0x3E,0x03,0xC0,0x3E,0x03,0xC0,0x3E,0x03,0xC0,0x1E,0x07,0xC0,0x1F,0x8F,0x80,0x0F,
0xFF,0x00,0x07,0xFE,0x00,0x0F,0xFF,0x80,0x1F,0xFF,0xC0,0x3F,0x07,0xC0,0x3E,0x03,
0xE0,0x7C,0x01,0xE0,0x7C,0x01,0xF0,0x7C,0x01,0xF0,0x7C,0x01,0xF0,0x7C,0x01,0xE0,
0x7E,0x03,0xE0,0x3F,0x07,0xE0,0x1F,0xFF,0xC0,0x0F,0xFF,0x80,0x07,0xFF,0x00,0x00,
0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xFC,
0x00,0x0F,0xFF,0x00,0x1F,0xFF,0x80,0x1F,0x9F,0x80,0x3E,0x07,0xC0,0x3E,0x07,0xC0,
0x7C,0x03,0xE0,0x7C,0x03,0xE0,0x7C,0x03,0xE0,0x7C,0x03,0xE0,0x7C,0x03,0xE0,0x7C,
0x03,0xE0,0x3E,0x07,0xE0,0x3F,0x0F,0xE0,0x1F,0xFF,0xE0,0x0F,0xFF,0xE0,0x07,0xFF,
0xE0,0x00,0xE3,0xE0,0x00,0x03,0xE0,0x00,0x03,0xE0,0x00,0x07,0xC0,0x00,0x07,0xC0,
0x00,0x0F,0x80,0x30,0x1F,0x80,0x3F,0xFF,0x00,0x3F,0xFE,0x00,0x3F,0xF8,0x00,0x07,
0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}
};

//显示单个字符(取模工具提供的方法)
//屏幕的长对应 x ,宽对应 y ,下同
void show_word(int x, int y, int w, int h, char word[][w/8], int color)
{
int i, j, k;
for( i=0; i<h; i++ ){
for( j=0; j<w/8; j++ ){
//解析 word[i][j] 的8个bits位,为1就显示颜色,为0就不显示
for( k=7; k>=0; k-- ){
if( word[i][j] & (1<<k) ){
display_point( i+y, j*8+(7-k)+x, color );
}
}
}
}
}

//倒计时
void count_number(int x, int y, int number)
{
while(number>=-1){
int n = number;
int offset = 0; //偏移量,每一位数字的间距
//从低位到高位显示每一位的数字
while(n){
show_word(x-offset, y, 24, 46, num[n%10], 0x000000);
offset += 20;
n /= 10;
}
number--;
sleep(1);
show_picture(0xFFFFFF);
}
}

//显示数字文本
void show_number(int x, int y, int number)
{
int offset = 0;
while(number>0){
show_word(x-offset, y, 24, 46, num[number%10], 0xFF34D3);
offset += 20;
number /= 10;
}
}

触摸屏

原理

​ 输入设备:键盘、鼠标、触摸屏、…

​ Linux 内核需要监听这些输入设备上的事件(event),然后做出相应的动作。

​ 当我们去触摸屏幕、点击鼠标、按下按键等操作输入设备时,就会产生相应的输入事件,并写入设备对应的文件中。

​ 输入设备对应的设备文件名为 /dev/input/eventX (X=0,1,2,...) 。在 6818 开发板上,触摸屏对应的设备文件名为 /dev/input/event0

​ 不同的输入设备对应输入事件是不同的,但是 Linux 用了一个标准的事件结构体来描述输入事件。

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
#include <linux/input.h>            // 路径:/usr/include/linux/input.h

struct input_event
{
struct timeval time; //事件发送的时间

__u16 type; //事件的类型
#define EV_SYN 0x00 同步事件
#define EV_KEY 0x01 按键事件
#define EV_REL 0x02 相对事件(鼠标事件)
#define EV_ABS 0x03 绝对事件(触摸事件)

__u16 code; //事件的编码,根据type的不同而有不同的含义
当 type == EV_ABS 时, code表示坐标轴
code == ABS_X //x轴 #define ABS_X 0x00
code == ABS_Y //y轴 #define ABS_Y 0x01
code == ABS_PRESSURE //触摸屏压力事件 #define ABS_PRESSURE 0x18
当 type == EV_KEY 时, code表示键值
KEY_A
KEY_B
...
BTN_TOUCH //把整个屏幕当作是一个按键来使用

__s32 value; //事件的值, 根据type的不同 而有不同的含义
当 type == EV_ABS 时, value坐标轴的值
code == ABS_X //value=x轴的坐标
code == ABS_Y //value=y轴的坐标
code == ABS_PRESSURE //压力值
value=0 触摸屏弹起
value>0 触摸屏按下
当 type == EV_KEY 时,value表示按键的状态
value=1 按键按下
value=0 按键松开

};

获取手指触摸的坐标

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
//1. open() 打开触摸屏  

//2. 不停地去读取
int x = -1, y = -1;
struct input_event ev;
while(1)
{
read( ) //读入 struct input_event 中
//解析坐标
if( ev.type == EV_ABS ) //触摸事件
{
if( ) //x轴
{
x = ev.value;
}
else if( ) //y轴
{

}
//退出的两种情况
else if( ) //压力事件 压力为0 就结束循环
{
//退出
}
}
else if( ev.type == EV_KEY ) //按键事件 把整个屏幕当作是一个按键来使用 按键松开 就结束循环
{
//退出
}
}

//3. 打印坐标

//4. close() 关闭触摸屏

实现获取点按坐标和滑动方向

touch.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#ifndef __TOUCH_H__
#define __TOUCH_H__

//点按坐标结构体
typedef struct pear
{
int x;
int y;
} pear;

//获取点按坐标
pear get_touch();
//获取手指滑动的方向
int get_direction();
//实现模拟手机相册滑动切图的功能
void electric_album();

#endif

touch.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
#include "lcd.h"
#include "touch.h"
#include "word.h"
#include "bmp.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <linux/input.h>

#define UP 1
#define DOWN 2
#define LEFT 3
#define RIGHT 4

typedef struct input_event input_event;

//触摸屏的长对应 x ,宽对应 y ,下同
pear get_touch()
{
int x, y;
input_event ev;
int fd = open("/dev/input/event0", O_RDWR);
if(fd==-1){
perror("/dev/input/event0 open error");
exit(-1);
}
while(1){
if(read(fd, &ev, sizeof(input_event))==-1){
perror("input_event read error");
exit(-1);
}
if(ev.type==EV_ABS){
if(ev.code==ABS_X){
x = ev.value;
}
else if(ev.code==ABS_Y){
y = ev.value;
}
//退出循环,即没有点按的情况有以下两种
else if(ev.code==ABS_PRESSURE&&ev.value==0){
break;
}
}
else if(ev.type==EV_KEY&&ev.code==BTN_TOUCH&&ev.value==0){
break;
}
}
if(close(fd)==-1){
perror("/dev/input/event0 close error");
}
pear posit;
posit.x = x;
posit.y = y;
return posit;
}

int get_direction()
{
input_event ev;
int fd = open("/dev/input/event0", O_RDWR);
if(fd==-1){
perror("/dev/input/event0 open error");
exit(-1);
}
int x0, y0, x1, y1;
x0 = x1 = y0 = y1 = -1;
while(1){
if(read(fd, &ev, sizeof(input_event))==-1){
perror("input_event read error");
exit(-1);
}
if(ev.type==EV_ABS){
if(ev.code==ABS_X){
if(x0==-1){ //滑动起点x坐标赋值
x0 = ev.value;
}
else{
x1 = ev.value;
}
}
else if(ev.code==ABS_Y){ //滑动起点y坐标赋值
if(y0==-1){
y0 = ev.value;
}
else{
y1 = ev.value;
}
}
//退出循环,即没有点按的情况有以下两种
else if(ev.code==ABS_PRESSURE&&ev.value==0){
break;
}
}
else if(ev.type==EV_KEY&&ev.code==BTN_TOUCH&&ev.value==0){
break;
}
}
if(close(fd)==-1){
perror("/dev/input/event0 close error");
}
int dx, dy;
dx = x1-x0;
dy = y1-y0;
if(abs(dx)>abs(dy)){
if(dx>0)
return 3;
else if(dx<0)
return 4;
}
else if(abs(dx)<abs(dy)){
if(dy>0)
return 1;
else if(dy<0)
return 2;
}
}

void electric_album()
{
char picture[5][32] = {"../bmp/c1.bmp", "../bmp/c2.bmp", "../bmp/c3.bmp", "../bmp/c4.bmp", "../bmp/c5.bmp"};
int i = 0;
int dir = -1;
draw_picture(0, 0, picture[0]);
while(1){
dir = get_direction();
if(dir==LEFT){
if(i-1<0){
continue;
}
draw_picture(0, 0, picture[--i]);
}
else if(dir==RIGHT){
if(i+1>4){
continue;
}
draw_picture(0, 0, picture[++i]);
}
}
}

GY39传感器

简介

​ 简介原理及使用说明参考官方手册。

​ 传感器和开发板使用串口进行通信。6818 开发板的串口:/dev/ttySAC1/dev/ttySAC2/dev/ttySAC3

获取传感器采集的数据

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
//串口初始化 
int init_serial(const char *file, int baudrate)
{
int fd;
fd = open(file, O_RDWR);
if (fd == -1)
{
perror("open device error:");
return -1;
}

struct termios myserial;
//清空结构体
memset(&myserial, 0, sizeof (myserial));
//O_RDWR
myserial.c_cflag |= (CLOCAL | CREAD);
//设置控制模式状态:本地连接,接受使能
//设置数据位
myserial.c_cflag &= ~CSIZE; //清空数据位
myserial.c_cflag &= ~CRTSCTS; //无硬件流控制
myserial.c_cflag |= CS8; //数据位:8

myserial.c_cflag &= ~CSTOPB;// //1位停止位
myserial.c_cflag &= ~PARENB; //不要校验
//myserial.c_iflag |= IGNPAR; //不要校验
//myserial.c_oflag = 0; //输入模式
//myserial.c_lflag = 0; //不激活终端模式

switch (baudrate) //设置波特率
{
case 9600:
cfsetospeed(&myserial, B9600);
cfsetispeed(&myserial, B9600);
break;
case 115200:
cfsetospeed(&myserial, B115200);
cfsetispeed(&myserial, B115200);
break;
case 19200:
cfsetospeed(&myserial, B19200);
cfsetispeed(&myserial, B19200);
break;
}

//刷新输出队列,清除正接受的数据
tcflush(fd, TCIFLUSH);

//改变配置
tcsetattr(fd, TCSANOW, &myserial);

return fd;
}

//解析数据
void parse_data( unsigned char buf[], int n )
{
if( buf[2] == 0x15 )
{
}
else if( buf[2] == 0x45 )
{
}
}

//获取GY39传感器数据
void get_gy39()
{
//1.打开串口并初始化
int fd = init_serial( COM2, 9600 );
if( fd == -1 )
{
}

//2.发送指令
unsigned char cmd[3] = { 0xA5, 0x83, 0x28 };
write( fd, cmd, 3 );
unsigned char buf[15] = {0};
unsigned char ch;
while( 1 )
{
//3.接收数据
//先读取第一个0x5A
do
{
read( fd, &ch, 1 );
} while( ch != 0x5A );

//读取第二个0x5A
read( fd, &ch, 1 );
if( ch != 0x5A )
{
continue;
}

int i = 0;
buf[0] = 0x5A;
buf[1] = 0x5A;

//读取 数据类型
read( fd, &buf[2], 1 );

//读取 数据长度
read( fd, &buf[3], 1 );

//读取 传感器数据内容
int len = buf[3];
for( i=0; i<len; i++ )
{
read( fd, &buf[4+i], 1 );
}

//读取 校验和
read( fd, &buf[ 4+len ], 1 );

//4.解析数据并显示
//根据手册解析buf数组里面的数据
}

//5.关闭串口

}

家具控制

蜂鸣器和led灯

​ 把 kobject_led.ko 驱动文件下载到开发板上,然后输入指令 insmod kobject_led.ko 加载驱动。

​ 一旦加载成功,在系统的 /sys/kernel/gec_ctrl/ 目录中会生成 led_d7 、led_d8 、led_d9 、led_d10 、led_all 、beep 几个文件。

​ 操作方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#define LED7 "/sys/kernel/gec_ctrl/led_d7" 
#define LED8 "/sys/kernel/gec_ctrl/led_d8"

int led_beep_ctrl( char * filename , int state )
{
//1.打开设备文件
open( filename , O_RDWR );

//2.操作
//写1 点亮
//写0 熄灭
write( fd, &state, sizeof(state) );

//3.关闭文件

}

播放MP3

madplay 命令用来播放指定的 MP3 音乐。

​ 语法:

1
2
3
4
5
6
7
madplay -Q  xxx.mp3         //播放 
madplay -Q xxx.mp3 & //后台播放

killall -STOP madplay //暂停
killall -CONT madplay //继续

killall madplay //关闭

​ system() 函数用来执行命令:

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

int system(const char *command);

例子:
system("ls");
system("madplay -Q 01.mp3 &");

实现获取解析显示传感器数据 控制家具

uart.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#ifndef __UART_H__
#define __UART_H__

//串口初始化
int init_serial(const char *file, int baudrate);
//获取传感器数据
//测试用的函数名
//void get_gy39();
//由于需要在主函数创建线程,实现在后台实时获取传感器数据,因此改为适配线程函数的格式
void *get_gy39(void *arg);
//解析传感器数据
void parse_date(unsigned char buf[], int n);
//控制家居:温度报警 智能亮灯
void led_beep_ctrl(char device);
//手动开关BGM
void mp3_ctl(char * filename);

#endif

uart.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
#include <sys/types.h>
#include <termios.h>
#include <netdb.h>
#include <string.h>
#include <errno.h>
#include "lcd.h"
#include "touch.h"
#include "word.h"
#include "bmp.h"
#include "uart.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <linux/input.h>

#define COM2 "/dev/ttySAC1"
#define COM3 "/dev/ttySAC2"
#define COM4 "/dev/ttySAC3"

int state_mp3 = 0;
int state_led = 0;
int state_curtain = 0;

int init_serial(const char *file, int baudrate)
{
int fd;

fd = open(file, O_RDWR);
if (fd == -1)
{
perror("open device error:");
exit(-1);
}

struct termios myserial;
//清空结构体
memset(&myserial, 0, sizeof (myserial));
//O_RDWR
myserial.c_cflag |= (CLOCAL | CREAD);
//设置控制模式状态,本地连接,接受使能
//设置 数据位
myserial.c_cflag &= ~CSIZE; //清空数据位
myserial.c_cflag &= ~CRTSCTS; //无硬件流控制
myserial.c_cflag |= CS8; //数据位:8

myserial.c_cflag &= ~CSTOPB;// //1位停止位
myserial.c_cflag &= ~PARENB; //不要校验
//myserial.c_iflag |= IGNPAR; //不要校验
//myserial.c_oflag = 0; //输入模式
//myserial.c_lflag = 0; //不激活终端模式

switch (baudrate)
{
case 9600:
cfsetospeed(&myserial, B9600); //设置波特率
cfsetispeed(&myserial, B9600);
break;
case 115200:
cfsetospeed(&myserial, B115200); //设置波特率
cfsetispeed(&myserial, B115200);
break;
case 19200:
cfsetospeed(&myserial, B19200); //设置波特率
cfsetispeed(&myserial, B19200);
break;
}

/* 刷新输出队列,清除正接受的数据 */
tcflush(fd, TCIFLUSH);

/* 改变配置 */
tcsetattr(fd, TCSANOW, &myserial);

return fd;
}

//解析数据
void parse_date(unsigned char buf[], int n)
{
int t, p, hum, h, lux;
if(buf[2]==0x15&&n==9){
lux = (buf[4]<<24)|(buf[5]<<16)|(buf[6]<<8)|buf[7];
lux /= 100;
//printf("lux: %d\n", lux);
//画白色矩形,擦除上一次获取的数据
display_rectangle(390, 450, 660, 720, 0xFFFFFF); //光强
show_number(700, 400, lux);
if(lux<50){
state_led = 0;
led_beep_ctrl('l');
//提示已开灯
draw_picture(135, 260, "../bmp/window.bmp");
}
else if(lux>=50){
state_led = 1;
led_beep_ctrl('l');
//提示已关灯
draw_picture(135, 260, "../bmp/window2.bmp");
}
}
else if(buf[2]==0x45&&n==15){
t = (buf[4]<<8)|buf[5];
t /= 100;
//printf("t: %d\n", t);
p = (buf[6]<<24) | (buf[7]<<16) | (buf[8]<<8) | buf[9];
p /= 100000;
//printf("p: %d\n", p);
hum = (buf[10]<<8)|buf[11];
hum /= 100;
//printf("hum: %d\n", hum);
h = (buf[12]<<8)|buf[13];
//printf("h: %d\n", h);
//画白色矩形,擦除上一次获取的数据
display_rectangle(390, 450, 140, 222, 0xFFFFFF); //气压
display_rectangle(280, 355, 155, 222, 0xFFFFFF); //温度
display_rectangle(280, 355, 420, 480, 0xFFFFFF); //湿度
display_rectangle(390, 450, 420, 480, 0xFFFFFF); //海拔
show_number(200, 310, t);
show_number(200, 400, p);
show_number(455, 310, hum);
show_number(455, 400, h);
if(t>30){
state_curtain = 0;
led_beep_ctrl('c');
}
else if(t<=30){
state_curtain = 1;
led_beep_ctrl('c');
}
}
sleep(1); //每10秒读一次
}

//获取GY39传感器数据
void *get_gy39(void *arg)
{
//1.打开串口并初始化
int fd = init_serial( COM2, 9600 );
if( fd == -1 )
{
perror("open device error:");
exit(-1);
}

//2.发送指令
unsigned char cmd[3] = { 0xA5, 0x83, 0x28 };
write( fd, cmd, 3 );

unsigned char buf[15] = {0};
unsigned char ch;

while( 1 )
{
//3.接收数据
//先读取第一个0x5A
do
{
read( fd, &ch, 1 );
}while( ch != 0x5A );

//读取第二个0x5A
read( fd, &ch, 1 );
if( ch != 0x5A )
{
continue;
}

int i = 0;
buf[0] = 0x5A;
buf[1] = 0x5A;

//读取 “数据类型”
read( fd, &buf[2], 1 );

//读取 “数据长度”
read( fd, &buf[3], 1 );

//读取 传感器数据内容
int len = buf[3];
for( i=0; i<len; i++ )
{
read( fd, &buf[4+i], 1 );
}

//读取 校验和
read( fd, &buf[ 4+len ], 1 );

//4.解析数据,并显示
//根据手册解析buf数组里面的数据
parse_date(buf, 4+len+1);
}

//5.关闭串口
if(close(fd)==-1){
perror("close device error:");
exit(-1);
}
}

void led_beep_ctrl(char device)
{
int fd = -1;
if(device=='l'){
state_led++;
fd = open("/sys/kernel/gec_ctrl/led_all", O_RDWR);
if(fd==-1){
perror("led_all open error");
exit(-1);
}
state_led %= 2;
write(fd, &state_led, sizeof(state_led));
if(state_led==1){
draw_picture(30, 675, "../bmp/on.bmp");
}
else if(state_led==0){
draw_picture(30, 675, "../bmp/off.bmp");
}
printf("led:%d\n", state_led);
}
else if(device=='c'){
state_curtain++;
fd = open("/sys/kernel/gec_ctrl/beep", O_RDWR);
if(fd==-1){
perror("beep open error");
exit(-1);
}
state_curtain %= 2;
write(fd, &state_curtain, sizeof(state_curtain));
if(state_curtain==1){
draw_picture(110, 675, "../bmp/on.bmp");
}
else if(state_curtain==0){
draw_picture(110, 675, "../bmp/off.bmp");
}
printf("beep:%d\n", state_curtain);
}
if(close(fd)==-1){
perror("close device error");
exit(-1);
}
}

void mp3_ctl(char * filename)
{
char command[1024] = {0};
if (state_mp3==0){
sprintf(command, "madplay -Q %s &", filename);
draw_picture(190, 675, "../bmp/on.bmp");
state_mp3 = 1;
}
else if(state_mp3==1){
printf("close audio\n");
sprintf(command, "killall madplay", filename);
draw_picture(190, 675, "../bmp/off.bmp");
state_mp3 = 0;
}
system(command);
}

功能整合

说明

​ 开机显示主界面。

​ 后台实时采集环境的参数并显示。

​ 设定阈值,进行智能控制家具。

​ 可以手动控制家具。

​ 可以手动开关 BGM 。

线程函数

​ 后台实时采集显示环境的参数并智能控制 和 手动控制家具,分别对应一个线程。

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

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

功能:创建一个新线程

参数:
thread:地址,指向的空间用来保存新线程的id
attr:指定新线程的属性
一般为 NULL ,表示默认属性
start_routine:函数指针
它指向返回值为 void* 且带有一个 void* 参数的函数, 即线程函数
线程函数的类型应该为:
void * xxx( void * arg )
{
}
arg: 线程函数的参数

返回值:
成功,返回0
失败,返回一个错误的值

线程一旦创建成功,线程函数就会立即被指向

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
#include "lcd.h"
#include "bmp.h"
#include "word.h"
#include "touch.h"
#include "uart.h"
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>

//LCD屏幕像素与触摸屏坐标的换算比例
//x 对应屏幕的长,y 对应屏幕的宽
const double dx = 1.28;
const double dy = 1.25;

int main()
{
//打开屏幕
lcd_init();
//显示主页面
draw_picture(0, 0, "../bmp/mind.bmp");
draw_picture(30, 675, "../bmp/off.bmp");
draw_picture(110, 675, "../bmp/off.bmp");
draw_picture(190, 675, "../bmp/off.bmp");
//创建线程,获取GY39传感器数据并显示
pthread_t tid = 1;
if(pthread_create( &tid, NULL, get_gy39, NULL )!=0){
perror("thread create error:");
exit(-1);
}
//加入手动控制家居功能
int dir = -1;
pear posit;
while(1){
posit = get_touch();
if(posit.x>675*dx&&posit.x<775*dx&&posit.y>30*dy&&posit.y<80*dy){
//led灯
led_beep_ctrl('l');
}
else if(posit.x>675*dx&&posit.x<775*dx&&posit.y>110*dy&&posit.y<160*dy){
//蜂鸣器
led_beep_ctrl('c');
}
else if(posit.x>675*dx&&posit.x<775*dx&&posit.y>190*dy&&posit.y<240*dy){
mp3_ctl("../mp3/pgydyd.mp3");
}
}

return 0;
}