引语

记录学习路程,抛砖引玉。如有更好的算法或者出现错误,欢迎指点。

内容介绍

1、理解触摸屏的工作原理
2、掌握触摸屏获取坐标的方法
3、理解多线程的原理。
4、掌握多线程的编程方法。
5、通过触摸屏实现智能家居控制系统人机界面交互
6、实现MP3音乐播放、暂停、继续和退出

案例展示

原理

一、多线程原理
1、线程的创建
gec@ubuntu:/mnt/hgfs/project$ man 3 pthread_create
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
参数说明:
pthread_t *thread ---- 创建的子线程的ID
const pthread_attr_t *attr ---- 线程的属性
void *(*start_routine) (void *)—创建的线程的执行函数
void *arg ---->创建线程的时候,向线程的执行函数传递的参数。
返回值:
On success, pthread_create() returns 0; on error, it returns an error number, and the contents of *thread are undefined.
Compile and link with -pthread.
2、线程的等待
int pthread_join(pthread_t thread, void **retval);
如果在主线程中调用这个函数,则这个函数会等待子线程退出,如果子线程不退出,主线程不会退出。
3、让一个线程退出的函数
#include <pthread.h>
int pthread_cancel(pthread_t thread);
参数:
pthread_t thread —cancle线程的ID
4、线程自己退出
#include <pthread.h>
void pthread_exit(void *retval);

二、触摸屏的原理
1.触摸屏
电容式触摸屏:(0,0)(799,479)或者(0,0)(1023,599)
2.设备文件
在Linux中访问硬件设备都是通过设备文件来访问的,访问的时候使用的是Linux系统IO函数:open()/read()/write()/lseek()/close().
液晶屏:
[root@GEC6818 /]#ls /dev/fb0 -l
crw-rw---- 1 root root 29, 0 Jan 1 1970 /dev/fb0
触摸屏:
[root@GEC6818 /]#ls /dev/input/event0 -l
crw-rw---- 1 root root 13, 64 Jan 1 1970 /dev/input/event0
注意:
有些嵌入式平台,触摸屏的设备文件是/dev/event0
思考:
Linux中文件的类型(7种)----- #ls -l
1)字符设备文件
[root@GEC6818 /]#ls /dev/fb0 -l
crw-rw---- 1 root root 29, 0 Jan 1 1970 /dev/fb0
c—charactor
2)块设备文件
[root@GEC6818 /]#ls /dev/sda* -l
brw-rw-rw- 1 root root 8, 0 Jan 1 00:05 /dev/sda
brw-rw-rw- 1 root root 8, 1 Jan 1 00:05 /dev/sda1
b—block,一般大容量的存储设备
3)普通文件
[root@GEC6818 /]#ls /test/main.bmp -l
-rw-r–r-- 1 root root 1152054 Jan 1 2015 /test/main.bmp

  • ----普通文件
    4)目录文件
    drwxr-xr-x 2 root root 4096 Jan 1 2015 test
    d— directory 文件夹
    5)链接文件
    [root@GEC6818 /lib]#ls /lib -l
    total 6144
    -rwxrwxr-x 1 root root 169264 Dec 27 2016 ld-2.23.so
    lrwxrwxrwx 1 root root 10 Dec 27 2016 ld-linux.so.3 -> ld-2.2
    l----link 快捷方式
    6)管道文件
    进程通信的一种方式
    p— pipe
    7)socket文件/套接字文件
    s — socket
    3.查看触摸屏的文件
    查看系统中有哪些输入类型的设备----触摸屏也是输入类型的设备。
    [root@GEC6818 /lib]#cat /proc/bus/input/devices
    4、检测触摸屏是否ok
    [root@GEC6818 /]#cat /dev/input/event0

三、如何获取触摸屏的坐标
1、打开触摸屏
例:
int fd_ts;
fd_ts = open("/dev/input/event0", O_RDONLY);
if(fd_ts == -1)
{
perror(“open ts”);
return 0;
}
2、读入触摸屏的数据
char ts_buf[100];
read(fd_ts, ts_buf, 100); —错误的代码。
解决:
在Linux系统中,应用程序从输入类型的设备(鼠标、键盘、手写板、按钮、触摸屏等)中读出来的数据是有一个固定的格式的。这个固定的格式在<linux/input.h>中定义,是一个结构体struct input_event。
gec@ubuntu:~$ gedit /usr/include/linux/input.h
/*

  • The event structure itself
    */
    struct input_event {
    struct timeval time;
    __u16 type;
    __u16 code;
    __s32 value;
    };
    成员说明:
    struct timeval time; ----输入事件的时间
    __u16 type; ---- 输入事件的类型
    __u16 code; ----- 输入事件的编码
    __s32 value;----- 输入事件的值
    触摸屏X轴和Y轴的坐标就是包含在struct input_event 的结构体中。
    例:读取触摸屏的数据
    struct input_event ts_data;
    read(fd_ts, &ts_data, sizeof(struct input_event));
    注意:
    数组的名字是数组的地址,结构体的名字不是结构体的地址。
    根据type、code和value分析触摸屏的坐标。
    3、关闭触摸屏
    close(fd_ts);

四、触摸屏数据的分析
type=3,code=0,value=58 ----->触摸屏事件,ABS_X,坐标值
type=3,code=1,value=595 ----->触摸屏事件,ABS_Y,坐标值
type=1,code=330,value=1 ----->按键事件,code=330触摸屏,(触摸屏按下)
type=0,code=0,value=0 ----->同步事件
type=1,code=330,value=0 ----->按键事件(触摸屏松开)
type=0,code=0,value=0 ----->同步事件
注意:
1.每次点击触摸屏,应用程序循环读取六次数据。
2.在读取触摸屏数据的过程中,如果没有点击触摸屏,应用程序会产生阻塞,等待触摸屏按下。
数据分析
1、type ----- 输入设备的类型—>通过type可以知道读到的设备的类型。
#define EV_SYN 0x00 ---->同步事件,触摸屏按下和松开都会产生一次同步事件。
#define EV_KEY 0x01 ---->按键事件
#define EV_REL 0x02 ---->相对坐标事件(鼠标)
#define EV_ABS 0x03 ---->绝对坐标事件(触摸屏)
2、code ---- 输入事件的编码
code的是和type相关的,不用的type下面,code含义不同。
例:
type == 1(EV_KEY)
则code代表哪一个按钮(按钮的编号)
#define KEY_T 20
#define KEY_Y 21
#define KEY_U 22
#define KEY_I 23
#define BTN_TOUCH 0x14a
如果type等于3,则code代表的是触摸屏的坐标轴
#define ABS_X 0x00
#define ABS_Y 0x01
3、value ----- 输入事件的值
例:
如果type等于1,code == 20,value == 0
键盘上的T按钮松开。value:0–按钮松开,1–按键按下
如果type==3,code == 1,value == 100;
触摸屏的Y轴坐标是100.

五、触摸屏的校正
触摸屏的校正----让触摸屏的坐标和液晶屏的坐标保持一致。
两个方面的校正:
1.坐标的范围:
液晶屏:(0,0)~(799,479)
触摸屏:(0,0)(1023,599)—>按比例调整(0,0)(799,479)
2.坐标轴的调整
如果触摸屏和液晶屏的原点不一致,需要调整。
注意:液晶屏的原点(0,0)是在液晶屏的左上角。

六、多个远程源程序的编译
lcd.c lcd.h main.c touch.c touch.h ----->可执行程序main
#arm-linux-gcc lcd.c touch.c main.c -o main -std=c99

七、播放音乐
1.音乐播放器
[root@GEC6818 /]#which madplay
/usr/bin/madplay

2.mp3音乐
如何讲一个mp3文件下载到开发板
串口、U盘、网络下载(tftp)
1)使用网线将电脑直接连接开发板(GEC6818)。
2)电脑的设置
(1)设置静态IP:172.22.75.1
(2)关闭windows的防火墙
(3)最好关闭wifi
3)配置GEC6818的IP
4)开发板和电脑之间互ping一下
要可以互ping
5)打开一个tftp服务器,并设置下载目录
tftpd32.exe
6)在开发板端下载文件
[root@GEC6818 /]#tftp 172.22.75.1 -g -r music.mp3
-g ---- get
-r ---- remote
7)通过tftp将文件上传到PC机
[root@GEC6818 /]#tftp 172.22.75.1 -p -l music.mp3
-p ---- put
-l ---- local

3.使用命令行:
[root@GEC6818 /test]#madplay music.mp3
2.如何编写程序播放音乐
管理madplay的主程序,包括播放,暂停播放,恢复播放,停止播放
system(“madplay north.mp3 &”);//利用system函数调用madplay播放器播放*.mp3音乐
system(“madplay north.mp3 -r &”);//循环播放:参数-r
system(“killall -9 madplay”);//利用system函数调用killall命令将madplay终止掉
system(“killall -STOP madplay &”);//利用system函数调用killall命令将madplay暂停
system(“killall -CONT madplay &”);//利用system函数调用killall命令恢复madplay的播放

代码

1.显示图片,单色循环lcd.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int lcd_show_bmp(char *bmp_name)
{
	int fd_bmp;
	int fd_lcd; 
	int  *fb_base =NULL;
	char bmp_buf[800*480*3];
	int lcd_buf[800*480];
	int lcd_new[800*480]; //风险:每个进程的stack不能超过8MB
	fd_bmp = open(bmp_name, O_RDONLY);
	if(fd_bmp == -1)
	{
		perror("open bmp");
		return -1;
	}
	lseek(fd_bmp,54,SEEK_SET);
	read(fd_bmp,bmp_buf, 800*480*3);//RGB数据
	close(fd_bmp);
	for(int i=0;i<800*480;i++)
		lcd_buf[i] =(0x00<<24) + (bmp_buf[3*i+2]<<16) + (bmp_buf[3*i+1]<<8) + (bmp_buf[3*i+0]);
	for(int i=0;i<800;i++)
		for(int j=0;j<480;j++)
			lcd_new[i+(479-j)*800] = lcd_buf[i+j*800];
	fd_lcd= open("/dev/fb0", O_RDWR);
	if(fd_lcd == -1)
	{
		perror("open lcd"); //根据错误码,输出错误的原因
		return -1;
	}
	fb_base = mmap( NULL, 800*480*4, PROT_READ|PROT_WRITE, MAP_SHARED, fd_lcd, 0 );
	if(fb_base == NULL)
	{
		printf("mmap error\n");
		return 0;
	}
	for(int i =0; i<800*480; i++)  //arm-linux-gcc -o test test.c  -std=c99    
		*(fb_base+i)= lcd_new[i];
	munmap(fb_base, 800*480*4);
	close(fd_lcd);
	return 0;	
}
int lcd_show_color(unsigned int color)
{	int fd_lcd; 
	int  *fb_base =NULL;
	fd_lcd= open("/dev/fb0", O_RDWR);
	if(fd_lcd == -1)
	{
		perror("open lcd"); //根据错误码,输出错误的原因
		return -1;
	}
	fb_base = mmap( NULL, 800*480*4, PROT_READ|PROT_WRITE, MAP_SHARED, fd_lcd, 0 );
	if(fb_base == NULL)
	{
		printf("mmap error\n");
		return 0;
	}
	for(int i =0; i<800*480; i++)  //arm-linux-gcc -o test test.c  -std=c99    
		*(fb_base+i)= color;
	munmap(fb_base, 800*480*4);
	close(fd_lcd);
	return 0;	
}


2.触摸屏touch.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/input.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "touch.h"
struct ts_data get_ts_xy(void)
{//1、打开触摸屏
	int fd_ts,i;
	struct ts_data ts_temp;
	struct input_event ts_data;
	fd_ts=open("/dev/input/event0",O_RDONLY);
	if(fd_ts==-1)
	{
		perror("open ts");
		exit(0);
	}
	//读入触摸屏数据
	for(i=0;i<6;i++)
	{
		read(fd_ts,&ts_data,sizeof(struct input_event));
		if((ts_data.type == EV_ABS) && ts_data.code == ABS_X )
			ts_temp.x = ts_data.value*800/1024;
		else if((ts_data.type == EV_ABS) && ts_data.code == ABS_Y)
			ts_temp.y = ts_data.value*480/600;
	}
	close(fd_ts);
	return ts_temp;
}

3.音乐播放上一曲和下一曲和液晶屏检测

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/input.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "lcd.h"
#include <pthread.h>
#include "touch.h"
#include <stdlib.h>
#include <unistd.h>
struct music_ls
	{
		char *music_term;
		struct music_ls *next;
		struct music_ls *last;
	};
struct ts_data ts_xy;
int mos=1;
struct music_ls rc,lm,*p;
void music(void)
{	system("madplay lm.mp3 &");
	p=&lm;
	int u=1,c=1;
	while(1)
	{   if(mos==7 && ts_xy.x>700 && ts_xy.x<800 && ts_xy.y>0 && ts_xy.y<50)
		{	mos=2;
			lcd_show_bmp("control.bmp");
			system("killall -9 madplay");
			break;
		}
		if(mos==7 && ts_xy.x>10 && ts_xy.x<95 && ts_xy.y>10 && ts_xy.y<50 && u==1)
		{	if(p->last!=NULL)
			{	system("killall -9 madplay");
				p=p->last;
				usleep(100000);
				system(p->music_term);
				u=0;
			}	
		}
		if(mos==7 && ts_xy.x>10 && ts_xy.x<95 && ts_xy.y>93 && ts_xy.y<133 && u==0)
		{   if(p->next!=NULL)
			{	system("killall -9 madplay");
				p=p->next;
				usleep(100000);
				system(p->music_term);
				u=1;
				
			}
		}
		if(mos==7 && ts_xy.x>10 && ts_xy.x<95 && ts_xy.y>50 && ts_xy.y<93 && c==1)
		{	system("killall -STOP madplay &");
			c=0;
			ts_xy.x=0;ts_xy.y=0;
		}
		if(mos==7 && ts_xy.x>10 && ts_xy.x<95 && ts_xy.y>50 && ts_xy.y<93 && c==0)
		{	system("killall -CONT madplay &");
			c=1;
			ts_xy.x=0;ts_xy.y=0;
		}
	}
}	
void lcd_show_color_xh(void)//循环显示单色
{   while(1)
	{	lcd_show_color(0x00FF0000);
		sleep(2);
		if(mos==5 && ts_xy.x>700 && ts_xy.x<800 && ts_xy.y>0 && ts_xy.y<50)
		{	mos=2;
			lcd_show_bmp("control.bmp");
			break;
		}
		lcd_show_color(0x0000FF00);
		sleep(2);
		if(mos==5 && ts_xy.x>700 && ts_xy.x<800 && ts_xy.y>0 && ts_xy.y<50)
		{	mos=2;
			lcd_show_bmp("control.bmp");
			break;
		}
		lcd_show_color(0x000000FF);
		sleep(2);		
		if(mos==5 && ts_xy.x>700 && ts_xy.x<800 && ts_xy.y>0 && ts_xy.y<50)
		{	mos=2;
			lcd_show_bmp("control.bmp");
			break;
		}
	}	
}
void lcd_show_bmp_xh(void)
{   while(1)
	{   lcd_show_bmp("lx1.bmp");
		sleep(2);
		if(mos==6 && ts_xy.x>700 && ts_xy.x<800 && ts_xy.y>0 && ts_xy.y<50)
		{	mos=2;
			lcd_show_bmp("control.bmp");
			break;
		}
		lcd_show_bmp("lx2.bmp");
		sleep(2);
		if(mos==6 && ts_xy.x>700 && ts_xy.x<800 && ts_xy.y>0 && ts_xy.y<50)
		{	mos=2;
			lcd_show_bmp("control.bmp");
			break;
		}
		lcd_show_bmp("lx3.bmp");
		sleep(2);
		if(mos==6 && ts_xy.x>700 && ts_xy.x<800 && ts_xy.y>0 && ts_xy.y<50)
		{	mos=2;
			lcd_show_bmp("control.bmp");
			break;
		}
	}		
}
void *ts_thread(void *data) 
{    while(1)
	{	ts_xy=get_ts_xy();
		
	}
}
int main(void)
{
	int ret;
	pthread_t tid_ts;
	ret=pthread_create(&tid_ts,NULL,ts_thread,NULL);
	if(ret<0)
	{	perror("ts thread error");
		return -1;
	}
	//对音频文件的预处理
	rc.music_term="madplay rc.mp3 &";rc.next=&lm;rc.last=NULL;
	lm.music_term="madplay lm.mp3 &";lm.next=NULL;lm.last=&rc;
	//1、打开触摸屏
	int fd_ts;
struct input_event ts_data;
	fd_ts = open("/dev/input/event0", O_RDONLY);
	if(fd_ts == -1)
	{	perror("open ts");
		return 0;
	}
	lcd_show_bmp("main.bmp");
		while(1)
	{	if(mos==1)
		{	if(ts_xy.x>650 && ts_xy.x<800 && ts_xy.y>427 && ts_xy.y<473)
			{	mos=2;
				lcd_show_bmp("control.bmp");
			}
		}
		if(mos==2)
		{	if(ts_xy.x>60 && ts_xy.x<300 && ts_xy.y>140 && ts_xy.y<200)
			{	mos=3;
				lcd_show_bmp("led_ctrl.bmp");
			}
			if(ts_xy.x>460 && ts_xy.x<700 && ts_xy.y>140 && ts_xy.y<200)
			{	mos=4;
				lcd_show_bmp("lx.bmp");
			}
			if(ts_xy.x>60 && ts_xy.x<300 && ts_xy.y>245 && ts_xy.y<305)
			{	mos=5;
				lcd_show_color_xh();
			}
			if(ts_xy.x>460 && ts_xy.x<700 && ts_xy.y>245 && ts_xy.y<305)
			{
				mos=6;
				lcd_show_bmp_xh();
			}	
			if(ts_xy.x>60 && ts_xy.x<300 && ts_xy.y>350 && ts_xy.y<410)
			{   mos=7;
				lcd_show_bmp("rc1.bmp");
				music();
			}
			if(ts_xy.x>460 && ts_xy.x<700 && ts_xy.y>350 && ts_xy.y<410)
			{   mos=8;
				lcd_show_bmp("lx3.bmp");
			}
		}
		if(mos==3 || mos==4|| mos==8)
		{   if(ts_xy.x>700 && ts_xy.x<800 && ts_xy.y>0 && ts_xy.y<50)
			    {mos=2;
				lcd_show_bmp("control.bmp");
			    }
	    }
	}//3、关闭触摸屏
	close(fd_ts);
	return 0;
}



结果展示

在这里插入图片描述

在这里插入图片描述

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐