舟山市建设工程造价管理协会网站,徐州建站推广,做电商要不要公司网站,做巧克力的网站#x1f941;作者#xff1a; 华丞臧. #x1f4d5;专栏#xff1a;【项目经验】 各位读者老爷如果觉得博主写的不错#xff0c;请诸位多多支持(点赞收藏关注)。如果有错误的地方#xff0c;欢迎在评论区指出。 推荐一款刷题网站 #x1f449; LeetCode刷题网站… 作者 华丞臧. 专栏【项目经验】 各位读者老爷如果觉得博主写的不错请诸位多多支持(点赞收藏关注)。如果有错误的地方欢迎在评论区指出。 推荐一款刷题网站 LeetCode刷题网站 文章目录一、智能家居1.1 项目介绍1.2 开发平台1.2.1 硬件1.2.2 软件1.3 项目功能二、Linux的知识点2.1 Linux2.1.1 LINUX 下有两种编译器2.1.2 LINUX文件操作LINUX下的文件系统交叉开发2.1.3 文件IO【打开文件】open【关闭文件】close【读文件】read【写文件】write【改变文件指针】lseek三、项目模块3.1 项目接口3.1.1 全局数据3.2 图片显示和字符显示3.2.1 像素点3.2.2 图片显示解析像素组初始化屏幕和映射关闭屏幕和解映射3.2.4 字符显示3.3 触摸屏3.3.1 获取触摸坐标3.4 音乐播放器3.6 传感器模块3.6.1 传感器初始化3.6.2 读取传感器数据3.6.3 LED灯3.7 界面切换3.8 模块合并3.8.1 线程创建一个新的线程获取当前进程id--pthread_self线程分离--pthread_detach3.8.2 模块合并四、功能测试五、项目源码一、智能家居
1.1 项目介绍 智能家居又称智能住宅是以住宅为平台利用先进的计算机、嵌入式系统和网络通讯技术将家中各种设备包括照明、环境控制系统、网络家电等通过家庭网络连接到一起构建高效的住宅设施与家庭日程事务的管理系统。与普通的家居相比智能家居既具有传统的居住功能又提升了家居安全性、便利性、舒适性、艺术性保证人们在任何一个有网络的地方就可以掌控家里的一切因此成为当前人们家装所关注的热点。 1.2 开发平台
1.2.1 硬件 GEC6818 开发平台核心板采用 10 层板工艺设计确保稳定可靠可以批量用于平板电脑车机学习机POS 机游戏机行业监控等多种领域。该平台搭载三星 Cortex-A53 系列高性能八核处理器 S5P6818最高主频高达 1.4GHz可应用于嵌入式 Linux 和 Android 等操作系统的驱动、应用开发。开发板留有丰富的外设支持千兆以太网、板载 LVDS 接口、MIPI 接口、USB 接口等。 1.2.2 软件
VS2019 本次项目我使用的是VS2019用来编写代码。 VMwareubantu Linux操作系统用于编译代码并且生成可以在开发板上运行的可执行程序 secureCRT 把Linux编译生成的目标文件下载开发板上 secureCRT连接开发板的步骤如下
选择Serial协议 配置串行端口数据 点击完成。 点击连接 出现了绿色√那就说明连接成功了。 将可执行文件烧录到开发板上。
1.3 项目功能
把基本的传感器的数据正确的显示在开发板上。通过触控控制灯的亮灭。通过触控控制音乐播放器。显示传感器的测量数据并通过光照强度来控制灯的亮灭。
二、Linux的知识点
2.1 Linux
虚拟机和ubantu
虚拟机可以让我们在一台电脑中运行不同的操作系统 “双系统”ubantu是一个基于Linux内核的带图形化的一个Linux的操作系统使用的是ubantu18.04
2.1.1 LINUX 下有两种编译器
本地编译器 gcc
gcc xxx.c - a.out 默认的可执行文件
gcc xxx.c -o xxx - xxx是你命名的可执行文件交叉编译器arm-linux-gcc 在一个环境下编译生成适应于另外一个环境的可执行文件 linux - arm arm-linux-gcc xxx.c -o xxx - xxx是你命名的可执行文件 注意arm-linux-gcc生成的可执行文件只能在arm板上运行
如果我们的代码是在windows上面但是编译是在Linux上我们又如何将我们代码放到Linux下面去编译运行我们虚拟机提供的一个功能共享文件夹 虚拟文件夹的设置
虚拟机设置 → 选项 → 共享文件夹→ 总是启用 → 添加到共享文件夹中
2.1.2 LINUX文件操作
LINUX下的文件系统 文件系统是一套用来管理文件的系统 文件系统的结构 linux系统下只有目录,通过目录来管理文件,所有的文件都是以根目录“/”开头通过文件的路径可以找到对应的文件。 绝对路径以根目录开头的路径相对路径不以根目录开头的路径当前路径名相对路径名 绝对路径名
特殊目录 . 当前目录 .. 上一层目录 ... 家目录
交叉开发
交叉开发的目的是让我们编写的代码能在目标板上跑起来。
2.1.3 文件IO
即对文件的输入和输出的操作。 Linux下应用程序设计的哲学:
在Linux下所有一切皆文件;所有一切包括设备.
【打开文件】open
打开文件所使用的的函数其函数原型如下 int open(const char *pathname, int flags); pathname:你要打开的这个文件的设备路径flags:打开文件的方式三种方式 O_RDONLY:read only 以只读方式打开O_WRONLY:write only 以只写方式打开O_RDWR: 以读写方式打开 返回值:
成功0的整数这个整数就是文件描述符所有打开的文件都是通过文件描述符来引用的文件描述符在Linux下唯一表示一个打开的文件后面对文件所有的操作都是文件描述符。失败-1 错误码文件不存在。
【关闭文件】close
关闭文件其函数原型 int close(int fd); 【读文件】read
从一个文件描述符里面去读,其函数原型如下 size_t read(int fd, void *buf, size_t count); fd:文件描述符buf:用来保存从文件中读取到的内容count:你想要从文件当中读取多少个字节 函数返回值
成功返回我们实际读到的字节数失败返回0表示这个文件已经读到结尾了没有内容可读。
【写文件】write
将我们的内容写到文件中去,其函数原型如下 size_t write(int fd, const void *buf, size_t count); fd:文件描述符buf保存我们要写入文件里面去的内容count:你想要写入的字节数 函数返回值
写入成功返回实际写入文件的字节数写入失败返回 -1
【改变文件指针】lseek
用来移动你的光标也就是你的文件的偏移量其函数原型如下 off_t lseek(int fd, off_t offset, int whence); offset:文件的偏移量whence:从文件的哪里开始偏移 SEEK_SET: 定位到文件的开头 新光标的位置 文件开头offset让光标处于开头lseek(fd, 0, SEEK_SET); SEEK_CUR: 定位当前的位置 新光标的位置 当前的位置offset SEEK_END: 定位到文件末尾 新光标的位置 末尾offset 返回值
成功返回的是文件开头到新光标的位置失败返回-1。
三、项目模块 3.1 项目接口
#ifndef _LCD_H_
#define _LCD_H_#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include stdio.h
#include stdlib.h
#include unistd.h
#include sys/mman.h
#include linux/input.h
#include pthread.h
#include termios.h
#include netdb.h
#include string.h
#include errno.h//图片显示
int display_bmp(int x0, int y0);//初始化屏幕和映射
int init_lcd();//关闭屏幕和解映射
int uninit_lcd();//映射
void lcd_draw_point(int i, int j, int color);//触摸屏
int get_xy();//读取图片数据
int read_data();//字模
void draw_word(int x0, int y0, int w, int h, int color, char s1[]);//图片转换
void bmp_switch();//led控制函数
void led_ctrl(char* led_id, int on_or_off);//音乐
int music_play();//数字显示
void lcd_number(int x0, int y0, double lf);初始化gy39串口
void init_tty(int fd);//测量
void gy39();
#endif3.1.1 全局数据
#include IHSys.h//
#define N 3
#define LED_D7 /sys/kernel/gec_ctrl/led_d7
#define LED_D8 /sys/kernel/gec_ctrl/led_d8
#define LED_D9 /sys/kernel/gec_ctrl/led_d9
#define LED_D10 /sys/kernel/gec_ctrl/led_d10
#define LED_ALL /sys/kernel/gec_ctrl/led_all
#define BEEP /sys/kernel/gec_ctrl/beep#define COM2 /dev/ttySAC1
#define COM3 /dev/ttySAC2
#define COM4 /dev/ttySAC3int music_start 0; //标记madplay是否占用int lcd_fd; //灯文件
int* plcd; //像数文件
int bmp_fd; //图片文件
int led_fd; //显示屏文件int width; //图片宽度
int height;//图片高度
int depth;//图片深度char* p; //像素数组int lack; //像数组每行缺少字节数
int total_bytes;//像素字节总数int read_x -1, read_y -1; //x横y纵
int ret_x, ret_y;//x横y纵int flag_cont_music 1; //music状态
int music_count 0; //MP3下标unsigned char rbuf1[9]; //获取光照数据
unsigned char rbuf2[15]; //获取温湿度大气压海拔数据
double LUX, T, P, HUM, H; //光照、温度、压强、湿度、海拔int count 2; //歌曲计数
int start_all 1; //进入主界面标记
int flag_light 0; //进入灯界面标记
int flag_mp3 0; //进入音乐界面标记
int flag_dc 0; //进入测量面标记
int flag_on_off 1; //灯的状态
int flag_light_sys 1; //灯驱动的状态char* mp3[7] { /music/music_list/01.mp3,/music/music_list/02.mp3,/music/music_list/03.mp3 ,/music/music_list/04.mp3 ,/music/music_list/05.mp3 ,/music/music_list/06.mp3 ,/music/music_list/07.mp3 }; //音乐所在路径开发板上char number[][24 * 48 / 8] {{/*-- 文字: 0 --*/
/*-- 宋体36; 此字体下对应的点阵为宽x高24x48 --*/
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x01,0xFF,0x80,0x03,0xC3,
0xC0,0x07,0x81,0xE0,0x0F,0x81,0xF0,0x0F,0x00,0xF0,0x1F,0x00,0xF8,0x1F,0x00,0xF8,
0x1E,0x00,0x78,0x3E,0x00,0x7C,0x3E,0x00,0x7C,0x3E,0x00,0x7C,0x3E,0x00,0x7C,0x3E,
0x00,0x7C,0x3E,0x00,0x7C,0x3E,0x00,0x7C,0x3E,0x00,0x7C,0x3E,0x00,0x7C,0x3E,0x00,
0x7C,0x3E,0x00,0x7C,0x3E,0x00,0x7C,0x3E,0x00,0x7C,0x3E,0x00,0x7C,0x3E,0x00,0x7C,
0x3E,0x00,0x78,0x1F,0x00,0xF8,0x1F,0x00,0xF8,0x0F,0x00,0xF0,0x0F,0x81,0xF0,0x07,
0x81,0xE0,0x03,0xC3,0xC0,0x01,0xFF,0x80,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},{ /*-- 文字: 1 --*/
/*-- 宋体36; 此字体下对应的点阵为宽x高24x48 --*/
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0C,0x00,0x00,0x1C,0x00,0x00,0x7C,
0x00,0x07,0xFC,0x00,0x07,0xFC,0x00,0x00,0x3C,0x00,0x00,0x3C,0x00,0x00,0x3C,0x00,
0x00,0x3C,0x00,0x00,0x3C,0x00,0x00,0x3C,0x00,0x00,0x3C,0x00,0x00,0x3C,0x00,0x00,
0x3C,0x00,0x00,0x3C,0x00,0x00,0x3C,0x00,0x00,0x3C,0x00,0x00,0x3C,0x00,0x00,0x3C,
0x00,0x00,0x3C,0x00,0x00,0x3C,0x00,0x00,0x3C,0x00,0x00,0x3C,0x00,0x00,0x3C,0x00,
0x00,0x3C,0x00,0x00,0x3C,0x00,0x00,0x3C,0x00,0x00,0x3C,0x00,0x00,0x3C,0x00,0x00,
0x3E,0x00,0x00,0x3E,0x00,0x07,0xFF,0xE0,0x07,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },{ /*-- 文字: 2 --*/
/*-- 宋体36; 此字体下对应的点阵为宽x高24x48 --*/
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,0xFF,0x80,0x03,0xFF,0xC0,0x0F,0x83,
0xE0,0x0E,0x01,0xF0,0x1E,0x00,0xF8,0x1E,0x00,0xF8,0x3E,0x00,0x78,0x3E,0x00,0x78,
0x3F,0x00,0x78,0x3F,0x00,0x78,0x1F,0x00,0xF8,0x00,0x00,0xF8,0x00,0x00,0xF0,0x00,
0x01,0xF0,0x00,0x01,0xE0,0x00,0x03,0xC0,0x00,0x07,0xC0,0x00,0x0F,0x80,0x00,0x0F,
0x00,0x00,0x1E,0x00,0x00,0x3C,0x00,0x00,0x78,0x00,0x00,0xF0,0x00,0x01,0xE0,0x00,
0x03,0xC0,0x00,0x07,0x80,0x1C,0x0F,0x00,0x1C,0x0E,0x00,0x38,0x1C,0x00,0x38,0x3C,
0x00,0x78,0x3F,0xFF,0xF8,0x3F,0xFF,0xF8,0x3F,0xFF,0xF8,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },{/*-- 文字: 3 --*/
/*-- 宋体36; 此字体下对应的点阵为宽x高24x48 --*/
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,0xFF,0x00,0x07,0xFF,0x80,0x0F,0x07,
0xC0,0x1E,0x03,0xE0,0x1E,0x01,0xF0,0x1E,0x01,0xF0,0x1F,0x00,0xF0,0x1F,0x00,0xF0,
0x1E,0x00,0xF0,0x00,0x00,0xF0,0x00,0x01,0xF0,0x00,0x01,0xF0,0x00,0x01,0xE0,0x00,
0x07,0xC0,0x00,0x1F,0x80,0x00,0xFE,0x00,0x00,0x7F,0x80,0x00,0x03,0xE0,0x00,0x01,
0xF0,0x00,0x00,0xF0,0x00,0x00,0xF8,0x00,0x00,0x78,0x00,0x00,0x7C,0x00,0x00,0x7C,
0x1E,0x00,0x7C,0x3F,0x00,0x7C,0x3F,0x00,0x78,0x3F,0x00,0xF8,0x3E,0x00,0xF8,0x1E,
0x01,0xF0,0x0F,0x03,0xE0,0x07,0xFF,0xC0,0x01,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },{/*-- 文字: 4 --*/
/*-- 宋体36; 此字体下对应的点阵为宽x高24x48 --*/
0x00,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,0xC0,0x00,0x03,0xC0,0x00,0x07,
0xC0,0x00,0x0F,0xC0,0x00,0x0F,0xC0,0x00,0x1F,0xC0,0x00,0x1F,0xC0,0x00,0x3F,0xC0,
0x00,0x77,0xC0,0x00,0x77,0xC0,0x00,0xE7,0xC0,0x01,0xE7,0xC0,0x01,0xC7,0xC0,0x03,
0x87,0xC0,0x03,0x87,0xC0,0x07,0x07,0xC0,0x0E,0x07,0xC0,0x0E,0x07,0xC0,0x1C,0x07,
0xC0,0x3C,0x07,0xC0,0x38,0x07,0xC0,0x7F,0xFF,0xFC,0x7F,0xFF,0xFE,0x3F,0xFF,0xFC,
0x00,0x07,0xC0,0x00,0x07,0xC0,0x00,0x07,0xC0,0x00,0x07,0xC0,0x00,0x07,0xC0,0x00,
0x07,0xC0,0x00,0x07,0xC0,0x00,0x7F,0xFC,0x00,0x7F,0xFE,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },{/*-- 文字: 5 --*/
/*-- 宋体36; 此字体下对应的点阵为宽x高24x48 --*/
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,0xF8,0x0F,0xFF,0xF8,0x0F,0xFF,
0xF8,0x0E,0x00,0x00,0x0E,0x00,0x00,0x0E,0x00,0x00,0x0E,0x00,0x00,0x0E,0x00,0x00,
0x0E,0x00,0x00,0x0E,0x00,0x00,0x0E,0x00,0x00,0x0E,0x3E,0x00,0x0F,0xFF,0xC0,0x0F,
0xFF,0xE0,0x0F,0x83,0xF0,0x1F,0x01,0xF0,0x1E,0x00,0xF8,0x0C,0x00,0xF8,0x00,0x00,
0x78,0x00,0x00,0x7C,0x00,0x00,0x7C,0x00,0x00,0x7C,0x00,0x00,0x7C,0x1E,0x00,0x7C,
0x3F,0x00,0x7C,0x3F,0x00,0x78,0x3F,0x00,0x78,0x3E,0x00,0xF8,0x1E,0x00,0xF0,0x1E,
0x01,0xF0,0x0F,0x03,0xE0,0x07,0xFF,0xC0,0x01,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },{/*-- 文字: 6 --*/
/*-- 宋体36; 此字体下对应的点阵为宽x高24x48 --*/
0x00,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,0x00,0xFF,0xE0,0x03,0xE1,
0xF0,0x03,0xC1,0xF8,0x07,0x81,0xF8,0x0F,0x01,0xF0,0x0F,0x00,0xE0,0x1E,0x00,0x00,
0x1E,0x00,0x00,0x3E,0x00,0x00,0x3E,0x00,0x00,0x3E,0x00,0x00,0x3E,0x3F,0x80,0x3E,
0xFF,0xE0,0x3F,0xF3,0xF0,0x3F,0xC1,0xF8,0x3F,0x80,0xF8,0x3F,0x00,0x7C,0x3E,0x00,
0x7C,0x3E,0x00,0x7C,0x3E,0x00,0x3C,0x3E,0x00,0x3C,0x3E,0x00,0x3C,0x3E,0x00,0x3C,
0x3E,0x00,0x3C,0x3E,0x00,0x7C,0x1F,0x00,0x7C,0x1F,0x00,0x78,0x0F,0x80,0x78,0x0F,
0x80,0xF0,0x07,0xE1,0xE0,0x03,0xFF,0xC0,0x00,0xFF,0x80,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },{ /*-- 文字: 7 --*/
/*-- 宋体36; 此字体下对应的点阵为宽x高24x48 --*/
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0xFF,0xFC,0x1F,0xFF,0xFC,0x1F,0xFF,
0xF8,0x1F,0x00,0x78,0x1C,0x00,0x70,0x1C,0x00,0xF0,0x38,0x00,0xE0,0x38,0x01,0xE0,
0x00,0x01,0xC0,0x00,0x03,0xC0,0x00,0x03,0x80,0x00,0x07,0x80,0x00,0x07,0x00,0x00,
0x0F,0x00,0x00,0x0E,0x00,0x00,0x1E,0x00,0x00,0x1E,0x00,0x00,0x1C,0x00,0x00,0x3C,
0x00,0x00,0x3C,0x00,0x00,0x7C,0x00,0x00,0x7C,0x00,0x00,0x78,0x00,0x00,0x78,0x00,
0x00,0xF8,0x00,0x00,0xF8,0x00,0x00,0xF8,0x00,0x00,0xFC,0x00,0x00,0xFC,0x00,0x00,
0xFC,0x00,0x00,0xF8,0x00,0x00,0xF8,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},{ /*-- 文字: 8 --*/
/*-- 宋体36; 此字体下对应的点阵为宽x高24x48 --*/
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,0xFF,0x80,0x07,0xFF,0xC0,0x0F,0x81,
0xE0,0x1F,0x00,0xF0,0x1E,0x00,0x78,0x3C,0x00,0x78,0x3C,0x00,0x78,0x3C,0x00,0x7C,
0x3C,0x00,0x7C,0x3E,0x00,0x78,0x1F,0x00,0x78,0x1F,0x80,0x70,0x0F,0xC0,0xF0,0x07,
0xF1,0xE0,0x03,0xFF,0xC0,0x01,0xFF,0x00,0x03,0xFF,0xC0,0x07,0x9F,0xE0,0x0F,0x07,
0xF0,0x1E,0x03,0xF0,0x3C,0x01,0xF8,0x3C,0x00,0xF8,0x3C,0x00,0x7C,0x7C,0x00,0x7C,
0x78,0x00,0x3C,0x78,0x00,0x3C,0x3C,0x00,0x3C,0x3C,0x00,0x78,0x3C,0x00,0x78,0x1E,
0x00,0xF0,0x0F,0x81,0xE0,0x07,0xFF,0xC0,0x01,0xFF,0x80,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },{ /*-- 文字: 9 --*/
/*-- 宋体36; 此字体下对应的点阵为宽x高24x48 --*/
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,0xFF,0x00,0x07,0xFF,0x80,0x0F,0x83,
0xC0,0x1F,0x01,0xE0,0x1E,0x00,0xF0,0x3E,0x00,0xF0,0x3C,0x00,0x78,0x3C,0x00,0x78,
0x7C,0x00,0x78,0x7C,0x00,0x7C,0x7C,0x00,0x7C,0x7C,0x00,0x7C,0x7C,0x00,0x7C,0x3C,
0x00,0x7C,0x3E,0x00,0xFC,0x3E,0x00,0xFC,0x3E,0x01,0xFC,0x1F,0x07,0xFC,0x0F,0xFF,
0x7C,0x07,0xFE,0x7C,0x01,0xF8,0x7C,0x00,0x00,0xF8,0x00,0x00,0xF8,0x00,0x00,0xF8,
0x00,0x00,0xF8,0x00,0x00,0xF0,0x0E,0x01,0xF0,0x1F,0x01,0xE0,0x1F,0x03,0xE0,0x1F,
0x07,0xC0,0x1F,0x8F,0x80,0x0F,0xFF,0x00,0x03,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },{ /*-- 文字: . --*/
/*-- 宋体36; 此字体下对应的点阵为宽x高24x48 --*/
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,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,0x3F,
0xC0,0x00,0x3F,0xC0,0x00,0x1F,0x80,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }
};3.2 图片显示和字符显示
首先从智能家居系统图片显示开始也就是说我们需要在屏幕上显示图片并且能够进行图片切换的功能在GEC6818开发板上配置了一块LCD屏幕其分辨率为800*480即表示屏幕上有480行每一行有800个像素点。
3.2.1 像素点
像素点是可以显示某种颜色的点在开发板上显示一个颜色就是给对应的像素点赋值在LInux操作系统中一切皆文件LCD屏幕在OS内核中的一个结构体指向的一个文件这个文件描述了LCD的相关属性因此对LCD屏的操作就可以转化为对文件的操作。让LCD屏显示某种颜色就是将该颜色对应的值写入到LCD屏对应的文件中。那么颜色如何描述的呢 一个像素点的颜色由三种基色组成这三种基色分别是红绿蓝三种基色通过量化可以表示出不同的颜色量化即不同程度的红绿蓝数量化我们把一个像素点的颜色看做四个字节的大小的整型ARGB其中A表示透明度了解R表示红色G表示绿色B表示蓝色在这4个字节中从低到高位分别代表B、G、R、A一般使用的都是低三个字节本项目中也没有使用到透明度。 总结
每一种基色占据1个字节0~255。每一种颜色由四字节表示。
#include stdio.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include unistd.h#define RED 0XFF0000
#define GREEN 0X00FF00
#define BULE 0X0000FFint main()
{//打开屏幕int lcd_fd open(/dev/fb0, O_RDWR); //打开LCD屏幕文件if(-1 lcd_fd){perror(open lcd_fd failed: );return -1;}unsigned int color;int i,j;//写颜色for(i 0; i 480; i){for(j 0; j 800; j){if(i 0 i 160){color RED;write(lcd_fd, color, 4); //写入颜色}else if(i 160 i 320){color GREEN;write(lcd_fd, color, 4); //写入颜色}else{color BULE;write(lcd_fd, color, 4); //写入颜色}}}//关闭文件close(lcd_fd);return 0;
}在ubantu上运行代码并将可执行文件下载到开发板上运行结果如下
3.2.2 图片显示
图片显示我选择使用bmp格式的图片并且也是800*480的分辨率如果图片不是bmp的可以使用电脑自带的画图将图片格式和分辨率改成合适的格式即可步骤如下 bmp图片的优势bmp是位图文件是一种无压缩的图片文件格式无压缩在其中保存了每个像素点ARGB颜色的分量可以直接读取图片中的像素点。 得到bmp格式的图片只是第一步接下来需要将图片的像素点读取并且保存在内存中并且写入到LCD屏内核文件中首先来了解bmp图片文件中的内容如下图 bmp格式的文件分为4部分其中最值得我们关注的DIB头和像数组在DIB头中我们可以读取图片的高度和宽度即分辨率而在像数组中我们可以读取图片的每一个像素点并且将其保存下来调色板是一种采用索引的压缩算法目的是为了节省存储空间只有图片的颜色小于256或等于色的时候才采用对于像素深度高于16位的图像不使用调色板对于24位和32位色深的bmp图片是不需要调色板的。
//通过如下的代码可以读取bmp图片的宽度、高度、色深
//读取图片数据
//位图宽度每一行所占的像素点的个数
int read_data(int i)
{bmp_fd 0;char pch3[8] { 0 };sprintf(pch3, /bmp/p%d.bmp, i);printf(%s\n, pch3);bmp_fd open(pch3, O_RDWR);if (-1 bmp_fd){perror(open bmp failed:);return 0;}//宽度width 0;lseek(bmp_fd, 0x12, SEEK_SET);int ret read(bmp_fd, width, 4);if (-1 ret){printf(read width error.\n);return -1;}printf(width %d\n, width);//但是我们读取到的width有可能是一个负值//width 0从左到右来保存每一个像素点//width 0从右往左来保存每一个像素点//位图高度这张图片有多少行的像素点height 0;lseek(bmp_fd, 0x16, SEEK_SET);ret read(bmp_fd, height, 4);if (-1 ret){printf(read height error.\n);return -1;}printf(height %d\n, height);//height 0:从下到上保存像素点//height 0 :从上到下保存像素点//位图的深度色深每个像素点所占的位数depth 0;lseek(bmp_fd, 0x1c, SEEK_SET);ret read(bmp_fd, depth, 2);if (-1 ret){printf(read depth error.\n);return -1;}printf(depth %d\n, depth);//如果depth 324个字节,则是ARGB//如果depth 243个字节则是RGBA取默认值0if (abs(width) * (depth / 8) % 4){lack 4 - abs(width) * (depth / 8) % 4;}int line_bytes lack abs(width) * (depth / 8);total_bytes line_bytes * abs(height); //图片总字节数p (char*)realloc(p, total_bytes);if (p NULL){perror(realloc fail);return 0;}//读像素组lseek(bmp_fd, 0x36, SEEK_SET);int res read(bmp_fd, p, total_bytes);if (-1 res){perror(read fail);return 0;}close(bmp_fd);return 1;
}内存分配单位是4字节,位图中每行像素数据是连续的下一行和上一行不能共1个分配单元(4字节)。每行像素数据长度必须是4字节的倍数 字节数 % 4 不等于0时 后续字节用0补齐。因此对于每一行的像素我们都需要计算其是否是4的整数而每一行像素都是一样的所以只需要计算一行的却少数即可。
一行像素点的个数abs(width)每一个像素点占多少个字节depth/8一行有多少个字节abs(width)*(depth/8)为了保证每一行的字节数就是4的倍数我们就需要在末尾添上几个空白的字节。每一行需要多少个字节
int lack 0;
if(abs(width)*(depth/8)%4)
{lack 4 - abs(width)*(depth/8)%4;
}一行总字节数line_bytes lack abs(width)*(depth/8);多少行bs(height)整个像素数组的大小total_bytes line_bytes * abs(height)定义一个同样大小的数组去保存这个图片的所有像素点。
解析像素组
计算机操作系统字节序有大小端之分
大端模式存储器的低地址存放的是数据的高字节。小端模式存储器的低地址存放的是数据的低字节。图片的本质是磁盘上的一个文件文件中保存了图片的属性和数据而bmp图片的像素点一般是24位大小或者32位大小所以像素点有大小端之分。 bmp图片的像素点一般都是24位也有32位的如果是32位的像素点A就有相应的值而如果是24位的就给A设置为0以32位来说我们可以用一个整型来保存一个像素点的大小然后通过按位或来设置ARGB并将其映射到LCD屏上。 //图片显示
int display_bmp(int x0, int y0) //x0和y0表示映射的起始位置
{unsigned char a, r, g, b;int x, y;//x行y列int i 0;int color 0;if (abs(width) * (depth / 8) % 4){lack 4 - abs(width) * (depth / 8) % 4;}for (y 0; y abs(height); y){for (x 0; x abs(width); x){b p[i];g p[i];r p[i];if (depth 32){a p[i]; //像素为4字节}else if (depth 24){a 0; //像素为3字节}color ((a 24) | (r 16) | (g 8) | b); //小端lcd_draw_point(width 0 ? x x0 : abs(width) - 1 - x x0, height 0 ? abs(height) - 1 - y y0 : y y0, color);}i lack;}
}//映射
void lcd_draw_point(int i, int j, int color)
{if (i 0 i 800 j 0 j 480){*(plcd j * 800 i) color; //plcd从0开始映射每个点都可以通过plcd偏移量来找到}
}初始化屏幕和映射
不同的显示屏硬件数据线以及时序及硬件操作是不一样的从应用开发的角度所有的显示屏只有一个作用帮我们在正确的地方来显示颜色显示图片。在Linux下LCD屏幕就是一个文件OS通过数据结构来管理该文件那么当OS需要使用LCD资源时该文件会被加载到内存中因此我们可以把LCD屏幕看做内存中的一片空间所以只要我们可以将bmp图片的像数组映射到LCD在内核中的空间就可以在LCD屏上显示图片了。
//mmap一种内存映射文件的方法
//mmap将一个文件或者其它对象映射进内存。文件被映射到多个页上如果文件的大小不是所有页的大小之和最后一个页不被使用的空间将会清零。
//mmap在用户空间映射调用系统中作用很大。
#include sys/mman.h
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
//addr:如果addr为NULL则内核选择创建映射的地址;这是创建新地图最方便的方法平。
//如果addr不为NULL那么内核会把它作为映射位置的提示;在Linux上映射将在附近的页面边界。新映射的地址作为调用的结果返回。
//length:映射空间总大小
//prot描述了映射所需的内存保护
//flags确定映射的更新对映射同一区域的其他进程是否可见以及更新是否可见传递到底层文件。
//返回值当映射成功后会返回一个指向文件描述符所对应文件的指针//初始化屏幕和映射
//lcd_fd和plcd是全局变量
int init_lcd()
{//打开屏幕lcd_fd open(/dev/fb0, O_RDWR); ///dev/fb0是开发板上屏幕文件的路径if (-1 lcd_fd){perror(open fail);return 0;}//映射plcd mmap(NULL, 480 * 800 * 4, PROT_READ | PROT_WRITE, MAP_SHARED, lcd_fd, 0);if (NULL plcd){perror(mmap fail);return 0;}return 1;
}关闭屏幕和解映射
//解映射
int munmap(void *addr, size_t length);//关闭屏幕和解映射
int uninit_lcd()
{free(p);p NULL;int n munmap(plcd, 480 * 800 * 4);close(lcd_fd);return 1;
}图片显示结果如下
3.2.4 字符显示
图片显示和字符显示较为相似在LCD屏幕上我们可以将字符看做一个一个的像素点组成的因此我们只需要这个字符的像素点按照其位置一一映射即可实现字符显示原理就是把屏幕上相对应的像素点点亮。我们可以通过取模软件把字符按照一定的规律变成了16进制的数。 软件链接取模软件 提取码为r4ci。
取模软件操作如下
配置取模方式取消勾选直接倒序和保留 选择字模的字体、字形以及字体大小 取模。
在该项目中除了传感器模块测量的数据需要动态更新外其他的字符都是静态的因此对于静态的字符都采用图片的方式显示即可对于动态的字符使用字模的方式显示。为了很好的表示传感器测量的数据需要保留两位有效数据因此还需要对.进行取模将0~9和.取模转换的数组存放在一个二维字符数组方便使用。
//字模
void draw_word(int x0, int y0, int w, int h, int color, char s[])
{// 遍历数组的每个元素1个字节 代表每行的8个像素素点的显示信息int i, j;int arrary_num w / 8 * h; for (i 0; i arrary_num; i){// 每一个数组元素的每一位代表了一个像素点 从高到低// 遍历它的每一位for (j 7; j 0; j--){/*判断该位是不是1s[i]j 1 是否 01000 0000 7 0000 00010000 0001 0000 00010000 0001 0*/if (*(s i) j 1){//在屏幕对应的位置显示即可lcd_draw_point(i % (w / 8) * 8 7 - j y0, i / (w / 8) x0, color);}}}
}//数字显示
void lcd_number(int x0, int y0, double lf) //(x0,y0)表示起始坐标
{char* number_string (char*)calloc(sizeof(char), 50);if (NULL number_string){perror(malloc fail);return;}sprintf(number_string, %.2lf, lf);printf(number_string %s\n, number_string);char* cur number_string;int n 0; //每个字模对应的下标int displayce 10; //偏移量while (*cur){if (*cur ! .){n *cur - 0;}else{n 10;}displayce 20;draw_word(y0, x0 displayce, 24, 48, 0x050505, number[n]);cur;}free(number_string);number_string NULL;
}3.3 触摸屏
3.3.1 获取触摸坐标
触摸屏在Linux下同样是一个文件其在开发板的路径为/dev/input/event0与打开屏幕是相似的。重要的是Linux是如何描述触摸事件的怎么保存对应的数据? 操作系统管理触摸事件一定是先描述再组织因此首先是用一个结构体来描述输入事件该结构体定义在linux/input.h头文件当中。 输入事件的结构体input_event struct input_event
{struct timeval time;//该输入事件发生的事件可以精确到微秒 _u16 type;//事件的类型。如下_u16 code;//要跟据type的不同code表示的含义也不同_u16 value;//要根据typecode的痛而有不同的含义
}说明
type type EV_KEY 表示这是一个按键事件。type EV_REL 表示这是一个鼠标事件。type EV_ABS 表示这是一个触摸屏事件。type EV_SYN 事件的分割标志。 code要跟据type的不同code表示的含义也不同如type EV_ABS code BTN_TOUCH 表示触摸点击。code ABS_X 表示的是x轴的坐标。code ABS_Y 表示的是y轴的坐标。code ABS_PRESSURE 表示的是给屏幕压力。 value要根据typecode的值而有不同的含义。 type EV_ABS且 code ABS_Xvalue 表示的是x轴的坐标。type EV_ABS且code ABS_Yvalue 表示的是y轴的坐标。type EV_KEY且code BTN_TOUCHvalue 1 表示手指按下value 0 表示手指松开。
注意6818开发板现在触摸屏的坐标分成两种 800480、1024600如果板子是1024*600的话那就要进行等比例缩小。
//触摸屏
int get_xy()
{read_x -1; //初始化read_y -1; //初始化struct input_event ts;int touch_fd open(/dev/input/event0, O_RDONLY);while (1){//sleep(1);read(touch_fd, ts, sizeof(struct input_event));if (ts.type EV_KEY ts.code BTN_TOUCH){if (ts.value 1){printf(press\n);}else{printf(release\n);}}if (ts.type EV_ABS){if (ts.code ABS_X){read_x ts.value * 800 / 1024;ret_x read_x;}if (ts.code ABS_Y){read_y ts.value * 480 / 600;ret_y read_y;break;}}}close(touch_fd);
}3.4 音乐播放器
首先来认识一个Linux上的软件–madplaymadplay是一个Linux下的音乐播放器如果你想要在开发板上播放你的音乐首先开发板上需要有对应MP3的音乐所以需要拷贝音乐文件到开发板上路径自己选择。
播放方式
播放某一首歌一次madplay /lixiang/music/1.mp3播放某一首歌单曲循环madplay /lixiang/music/1.mp3 -r播放我现在这个目录下的全部MP3文件一次madplay /lixiang/music/*.mp3播放我现在这个目录下的全部MP3文件(列表循环)madplay /lixiang/music/*.mp3 -r播放我现在这个目录下的全部MP3文件(随机播放)madplay /lixiang/music/*.mp3 -z
程序控制madplay播放暂停播放恢复播放停止播放关闭播放器
后台播放目录下所有的.mp3文件system(“madplay /lixiang/music/*.mp3 ”)列表循环播放system(“madplay /lixiang/music/*.mp3 -r ”)暂停播放system(“kill -STOP madplay ”)恢复播放system(“kill -CONT madplay ”)停止播放system(kill -9 madplay )注意为后台播放如果不加则为前台播放将无法对其进行暂停恢复操作
对于一个音乐播放器需要有暂停、下一首、上一首等功能首先在音乐界面需要有这三个按钮而通过触摸屏我们可以得到按钮在图片上的位置所以根据在触摸屏按下的不同位置我们可以进行暂停、下一首、上一首等功能如下图
//system 执行一个shell命令
#include stdlib.h
int system(const char *command);//音乐
int music_play()
{int input 0;char cmd[250] { 0 }; //存放指令//上一首 下一首 暂停播放 继续播放 停止播放if (flag_mp3 1) //进入音乐界面{if (read_x 220 read_x 260 read_y 380 read_y 440) // 上一首{if (music_start 1) {system(killall -9 madplay );//停止当前正在播放的音乐}printf(music_start %d\n, music_start);if (music_count 0){music_count 6;}else{music_count--;}printf(music_count %d\n, music_count);read_data(7); //读取图片像素组display_bmp(0, 0); //显示图片sprintf(cmd, madplay %s -r , mp3[music_count]);music_start 1; //标记音乐开始播放system(cmd); //播放音乐flag_cont_music 1; //音乐正在播放}else if (read_x 540 read_x 580 read_y 380 read_y 430) //下一首{if (music_start 1) {system(killall -9 madplay );//停止当前正在播放的音乐}printf(music_start %d\n, music_start);if (music_count 6){music_count 0;}else{music_count;}printf(music_count %d\n, music_count);read_data(7);display_bmp(0, 0);sprintf(cmd, madplay %s -r , mp3[music_count]);music_start 1;system(cmd);flag_cont_music 1;}else if (read_x 350 read_x 450 read_y 370 read_y 480) //暂停{if (music_start 0){read_data(7);display_bmp(0, 0);sprintf(cmd, madplay %s -r , mp3[music_count]);system(cmd);music_start 1;}else if (music_start 1 flag_cont_music 1){read_data(3);display_bmp(0, 0);system(killall -STOP madplay );flag_cont_music -flag_cont_music;}else if (music_start 1 flag_cont_music -1){read_data(7);display_bmp(0, 0);system(killall -CONT madplay );flag_cont_music -flag_cont_music;}}}
}3.6 传感器模块
GY-39 是一款低成本气压温湿度光强度传感器模块。工作电压 3-5v功耗小安装方便。其工作原理是 MCU 收集各种传感器数据统一处理直接输出计算后的结果此模块有两种方式读取数据即串口 UART TTL 电平或者 IIC 2 线。串口的波特率有 9600bps 与 115200bps可配置有连续询问输出两种方式可掉电保存设置。可适应不同的工作环境与单片机及电脑连接。模块另外可以设置单独传感器芯片工作模式作为简单传感器模块 MCU 不参与数据处理工作。提供 arduino 51 stm32 单片机通讯程序不提供原理图及内部单片机源码。此 GY39 模块另外赠送安卓手机软件 app 查看数据且支持 wifi 局域内网连接手机及电脑同时显示数据。 gy39模块
1.接线方式TTL电平 3.3v~5v:1 0v:0VCC接电源 3.3v~5vGND接地CT 发送数据线DR 接收数据线串口通信全双工串行的通信协议ARM(6818) GY39VCC-------------VCCGND-------------GNDTX-------------DRRX-------------CT3.6.1 传感器初始化
//gy39传感器的初始化函数
void init_tty(int fd)
{//声明设置串口的结构体struct termios termios_new;//先清空该结构体bzero(termios_new, sizeof(termios_new));// cfmakeraw()设置终端属性就是设置termios结构中的各个参数。cfmakeraw(termios_new);//设置波特率//termios_new.c_cflag(B9600);cfsetispeed(termios_new, B9600);cfsetospeed(termios_new, B9600);//CLOCAL和CREAD分别用于本地连接和接受使能因此首先要通过位掩码的方式激活这两个选项。 termios_new.c_cflag | CLOCAL | CREAD;//通过掩码设置数据位为8位termios_new.c_cflag ~CSIZE;termios_new.c_cflag | CS8;//设置无奇偶校验termios_new.c_cflag ~PARENB;//一位停止位termios_new.c_cflag ~CSTOPB;tcflush(fd, TCIFLUSH);// 可设置接收字符和等待时间无特殊要求可以将其设置为0termios_new.c_cc[VTIME] 10;termios_new.c_cc[VMIN] 1;// 用于清空输入/输出缓冲区tcflush(fd, TCIFLUSH);//完成配置后可以使用以下函数激活串口设置if (tcsetattr(fd, TCSANOW, termios_new))printf(Setting the serial1 failed!\n);}3.6.2 读取传感器数据
GY39有两种工作模式 只获取光照强度只获取温湿度大气压强海拔 模块输出每帧包含8-13个字节 只输出光照强度9个字节只输出温湿度压强海拔13个字节 更加详细的gy39使用方法参考下面的资料其中详细介绍了gy39传感器的属性以及如何使用命令字节切换传感器模式、如何读取GY39的测量数据数据。 链接GY39传感器模块提取码1234。
数据的显示很简单得到的数据应该是浮点型的数据将这个浮点数的每一位都取出来转换成字模的形式打印出来即可打印时需要注意每一位数字都需要加上一个偏移量不然就会打印在同一个屏幕位置上。
void gy39()
{//打开串口int gy39_fd open(/dev/ttySAC1, O_RDWR);//初始化串口init_tty(gy39_fd);int tmp 0;unsigned char cmd1[3] { 0xa5, 0x81, 0x26 }; // 命令字节获取光照强度命令//write(fd, cmd, 3);//1000 0010 0x82unsigned char cmd2[3] { 0xa5, 0x82, 0x27 }; // 命令字节获取温湿度大气压强海拔//1000 0011 》0x83unsigned char cmd3[3] { 0xa5, 0x83, 0x28 }; // 命令字节获取温湿度大气压强海拔光照强度int ret write(gy39_fd, cmd1, 3);if (ret ! 3){perror(write fail);//sleep(1);}ret read(gy39_fd, rbuf1, 9);if (ret ! 9){perror(rbuf1 read fail);return;}//获取光照强度if (rbuf1[0] 0x5a rbuf1[1] 0x5a rbuf1[2] 0x15) //rbuf1[2] 0x15 表示只获取光照强度{tmp rbuf1[4] 24 | rbuf1[5] 16 | rbuf1[6] 8 | rbuf1[7];LUX tmp / 100.0;printf(LUX %.2lf\n, LUX);}//光照强度大于100熄灭灯if (LUX 100){led_ctrl(LED_ALL, 0);if (flag_light 1){read_data(4);display_bmp(0, 0);flag_on_off 1;}else if (flag_light 0){flag_on_off 1;}}//切换模式 获取温湿度大气压强海拔ret write(gy39_fd, cmd2, 3);if (ret ! 3){perror(write fail);//sleep(1);}ret read(gy39_fd, rbuf2, 15);if (ret ! 15){perror(rbuf2 read fail);//return;}//获取温度if (rbuf2[0] 0x5a rbuf2[1] 0x5a rbuf2[2] 0x45)//rbuf2[2] 0x45 表示只获取温湿度大气压强海拔{//温度tmp rbuf2[4] 8 | rbuf2[5];T tmp / 100.0;printf(T %.2lf\n, T);//气压tmp rbuf2[6] 24 | rbuf2[7] 16 | rbuf2[8] 8 | rbuf2[9];P tmp / 100.0;printf(P %.2lf\n, P);//湿度tmp rbuf2[10] 8 | rbuf2[11];HUM tmp / 100.0;printf(HUM %.2lf\n, HUM);//海拔tmp rbuf2[12] 8 | rbuf2[13];H tmp;printf(H %.2lf\n, H);}printf(\n);if (flag_dc 1){read_data(6);display_bmp(0, 0);lcd_number(150, 350, LUX); //光照强度lcd_number(560, 70, T); //温度lcd_number(140, 210, P); //压强lcd_number(150, 70, HUM); //湿度lcd_number(560, 215, H); //海拔sleep(1);}close(gy39_fd);return;
}3.6.3 LED灯
控制LED灯同样是打开一个开发板上LED灯的文件将1高电平或者0低电平写入即可操作灯的亮灭。
写1亮。写0 灭。
注意加载led的内核驱动把kobject_led.ko下载到开发板上然后输入命令kobject_led.ko文件LED驱动文件提取码1234
//开发板上以命令行模式输入每次开启开发板都需要设置
insmod kobject_led.ko//system 执行一个shell命令
system(insmod kobject_led.ko); //led控制函数
#define LED_D7 /sys/kernel/gec/ctrl/led_d7
#define LED_D8 /sys/kernel/gec/ctrl/led_d8
#define LED_D9 /sys/kernel/gec/ctrl/led_d9
#define LED_D10 /sys/kernel/gec/ctrl/led_d10
#define LED_ALL /sys/kernel/gec/ctrl/led_all
#define BEEP /sys/kernel/gec/ctrl/beepvoid led_ctrl(char* led_id, int on_or_off)
{printf(%d\n, on_or_off);led_fd open(led_id, O_RDWR);write(led_fd, on_or_off, 4);close(led_fd);
}3.7 界面切换
智能家居系统包含灯光控制、音乐播放、环境测量三个功能系统启动后首先是一个启动界面在屏幕上任意点击后进入系统主界面主界面给用户提供三个按钮分别是灯光控制、音乐播放、查看环境测量数据用户点击对应的功能按钮进入对应的功能界面每个界面中触摸屏的点击作用是不相同的如在灯光控制界面点击灯就是控制灯的亮灭而在音乐界面点击的作用就是播放、上一首、下一首等所以不同的界面必须有不同的标记在该项目中我使用的是全局变量来标记触摸屏当前所在的界面。
//
int start_all 1; //进入主界面标记
int flag_light 0; //进入灯界面标记
int flag_mp3 0; //进入音乐界面标记
int flag_dc 0; //进入测量面标记
int flag_on_off 1; //灯的状态
int flag_light_sys 1; //灯驱动的状态
int flag_cont_music 1;//music状态
int music_count 0;//MP3下标void bmp_switch()
{if (read_x ! -1 read_y ! -1 start_all 1) //进入主界面{start_all 0;read_data(2);display_bmp(0, 0);}else if (start_all 0) //{//mp3if (read_x 300 read_x 450 read_y 170 read_y 310 start_all 0 flag_light ! 1 flag_mp3 0 flag_dc ! 1){flag_mp3 1;if (music_start 1 flag_cont_music 1){read_data(7);display_bmp(0, 0);}else{read_data(3);display_bmp(0, 0);}}// mp3返回主界面关闭后台播放if (read_x 760 read_x 800 read_y 0 read_y 60 start_all 0 flag_light ! 1 flag_mp3 1 flag_dc ! 1){system(killall -9 madplay );flag_mp3 0;flag_cont_music -1;music_start 0;read_data(2);display_bmp(0, 0);}//灯if (read_x 60 read_x 200 read_y 170 read_y 310 start_all 0 flag_light 0 flag_mp3 ! 1 flag_dc ! 1){if (flag_light_sys 1){system(insmod kobject_led.ko);flag_light_sys -flag_light_sys;}flag_light 1;if (flag_on_off -1){read_data(5);display_bmp(0, 0);}else{read_data(4);display_bmp(0, 0);}}if (read_x 300 read_x 450 read_y 170 read_y 310 start_all 0 flag_light 1 flag_mp3 ! 1 flag_dc ! 1){//printf(%d\n,flag_on_off);if (flag_on_off 1){read_data(5);display_bmp(0, 0);led_ctrl(LED_ALL, 1);}else if (flag_on_off -1){read_data(4);display_bmp(0, 0);led_ctrl(LED_ALL, 0);}flag_on_off -flag_on_off;}//温湿度if (read_x 550 read_x 700 read_y 170 read_y 310 start_all 0 flag_light ! 1 flag_mp3 ! 1 flag_dc 0){flag_dc 1;read_data(6);display_bmp(0, 0);}// 返回主界面功能模块后台运行if (read_x 0 read_x 40 read_y 0 read_y 60){if (start_all 0 flag_light ! 1 flag_mp3 1 flag_dc ! 1){flag_mp3 0;read_data(2);display_bmp(0, 0);}else if (start_all 0 flag_light 1 flag_mp3 ! 1 flag_dc ! 1){flag_light 0;read_data(2);display_bmp(0, 0);}else if (start_all 0 flag_light ! 1 flag_mp3 ! 1 flag_dc 1){flag_dc 0;read_data(2);display_bmp(0, 0);}}}
}3.8 模块合并
3.8.1 线程
Linux认为没有进程没有线程在概念上区分只有一个叫做执行流。在一个程序里的一个执行路线就叫线程thread更准确的定义是线程是一个进程内部的控制序列一切进程至少都有一个执行线程。线程在进程内部运行本质是在进程地址空间内运行在Linux系统中在CPU眼中看到的PCB都要比传统的进程更加轻量化透过进程虚拟地址空间可以看到进程的大部分资源将进程资源合理分配给每个执行流就形成了线程执行流。
线程是真正的基本调度单位线程是在进程中的执行流在Linux当中线程是在进程的地址空间内运行的地址空间是进程看待资源的统一的视角多个执行流将进程的资源划分。 重新理解进程在学习Linux中进程概念时我们理解进程为内核数据结构加上进程对应的代码和数据那么在学习了线程后从内核视角来看 进程是承担分配系统资源的基本实体(进程的基座属性)即向系统申请资源的基本单元。 单执行流进程 内部只有一个执行流的进程多执行流进程内部有多个执行流的进程 线程总结
在进程内部运行的执行流线程比进程粒度更细调度成本更低线程是CPU调度的基本单元进程内部的线程共享主线程的地址空间每个进程可以占有进程的一部分资源执行进程的一部分代码
创建一个新的线程
功能创建一个新的线程
原型int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg);
参数thread:返回线程IDattr:设置线程的属性attr为NULL表示使用默认属性start_routine:是个函数地址线程启动后要执行的函数arg:传给线程启动函数的参数
返回值成功返回0失败返回错误码获取当前进程id–pthread_self
pthread_ create函数会产生一个线程ID存放在第一个参数指向的地址中。该线程ID和前面说的线程ID不是一回事。前面讲的线程ID属于进程调度的范畴。因为线程是轻量级进程是操作系统调度器的最小单位所以需要一个数值来唯一表示该线程。pthread_ create函数第一个参数指向一个虚拟内存单元该内存单元的地址即为新创建线程的线程ID属于NPTL线程库的范畴。线程库的后续操作就是根据该线程ID来操作线程的。线程库NPTL提供了pthread_ self函数可以获得线程自身的ID
pthread_t pthread_self(void);线程分离–pthread_detach
默认情况下新创建的线程是joinable的线程退出后需要对其进行pthread_join操作否则无法释放资源从而造成系统泄漏。如果不关心线程的返回值join是一种负担这个时候我们可以告诉系统当线程退出时自动释放线程资源。新线程分离但是主线程先退出那么该进程就会退出对应的所有线程也就会全部退出一般我们分离线程对应的main thread一般不要退出常驻内存。
int pthread_detach(pthread_t thread);可以是线程组内其他线程对目标线程进行分离也可以是线程自己分离;
pthread_detach(pthread_self());joinable和分离是冲突的一个线程不能既是joinable又是分离的。
3.8.2 模块合并
智能家居系统包括图片显示、触摸屏、LED灯、音乐播放、传感器测量等模块其中的一些模块是相互关联的点击屏幕不仅仅需要进行图片切换还需要操作某些硬件来完成相应的任务如进入LED灯的界面可以控制灯的开关进入音乐界面可以播放、暂停、切换音乐也就是说在触摸屏幕的同时需要进行其他的操作而线程能够增加多个执行流很好地实现模块的合并。
#include IHSys.h//获取坐标
void* get_user_touch()
{pthread_detach(pthread_self()); //线程分离while (1){get_xy();//printf(x %d y %d\n, ret_x, ret_y);}
}//测量
void* get_gy39()
{pthread_detach(pthread_self()); //线程分离while (1){gy39();}
}//音乐
void* get_music()
{pthread_detach(pthread_self()); //线程分离while (1){music_play();}
}//判断触摸点
void* get_bmp_switch()
{pthread_detach(pthread_self()); //线程分离while (1){bmp_switch();}
}int main()
{//初始化屏幕和映射init_lcd();//读取图片数据read_data(1); //系统启动界面display_bmp(0, 0);pthread_t id[4];pthread_create(id[0], NULL, get_user_touch, (void*)NULL);pthread_create(id[1], NULL, get_gy39, (void*)NULL);pthread_create(id[2], NULL, get_music, (void*)NULL);pthread_create(id[3], NULL, get_bmp_switch, (void*)NULL);while (1){;}//关闭屏幕和解映射uninit_lcd();return 0;
}四、功能测试 视频观看 智能家居系统
五、项目源码
Gitee智能家居系统