Linux|智能家居嵌入式系统之音乐播放
嵌入式实验引语内容介绍案例展示原理代码结果展示引语记录学习路程,抛砖引玉。如有更好的算法或者出现错误,欢迎指点。内容介绍1、理解触摸屏的工作原理2、掌握触摸屏获取坐标的方法3、理解多线程的原理。4、掌握多线程的编程方法。5、通过触摸屏实现智能家居控制系统人机界面交互6、实现MP3音乐播放、暂停、继续和退出案例展示原理一、多线程原理1、线程的创建gec@ubuntu:/mnt/hgfs/projec
引语
记录学习路程,抛砖引玉。如有更好的算法或者出现错误,欢迎指点。
内容介绍
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;
}
结果展示
更多推荐
所有评论(0)