运行环境要求:
硬件环境:QT210开发板,采用三星s5pv210为芯片方案,该芯片内核为ARM Cortex-A8
软件环境:软件采用开源的linux操作系统;LCD显示技术,触摸屏技术,并发技术
技术要求:
(1)Linux环境搭建及使用、Linux基本命令的使用,
(2))shell基本语法的应用、shell解释器原理;
(3)Linux系统IO接口函数的应用,bmp图片信息存储格式,
设计要求
具体要求:在开发板的屏幕上显示出钢琴界面,手指按压不同的区域时,对应位置的琴键显示按压的效果,并且会发出相对应的声音。当松开手指的时候,琴键按压效果消失,但为了和更接近真实钢琴,其发出的声音不会马上消失,而时等待该声音播放完。当按下下一个按键的时候,便会马上结束上一个声音的播放。
功能结构
电子钢琴分为3个模块,即lcd显示模块,触摸屏模块,并发模块;再往下到具体功能模块就有,钢琴界面显示;按键显示和按压效果展示;曲谱显示和曲谱切换效果展示;音乐的播放和停止。首先显示钢琴界面,琴键以及曲谱就是lcd模块的功能。显示琴键按压效果和曲谱切换显示就是lcd模块和触摸屏模块共同作用的结果。而当我们按压琴键时,会发出声音并播放音乐,这就是并发模块的功能。图片显示和音乐播放的并发。
功能结构图如同所示:
系统流程图
lcd显示模块
再此模块中,首先是对屏幕进行初始化,具体包括,打开屏幕文件和打开映射。然后是定义画点函数,即lcd_display_point();作用是给每个点映射对应的颜色。最后是关闭屏幕文件和关闭映射。
触摸屏模块
在此模块中,先是要打开触摸屏文件,然后读取触摸屏的模式,将其存放在缓冲区类,在不同模式下,将读取到的值赋给x,y(横纵坐标)和p(按键)。个根据x,y,p的值,编写判断程序,不同条件就进入不同的功能函数。最后则是关闭触摸屏文件。
触屏模块程序流程图如同所示:
多线程模块
本模块用到的是多线程并发,要实现手指按压屏幕,按压效果显示和音乐播放并发,首先实在并发模块的主程序创建新的线程函数,新创建的线程函数内容是播放一个MP3文件,而在主程序中写显示按压效果图片。这样一来就实现了图片显示和音乐播放的并发。
并发模块程序流程图:
lcd模块测试
实验结果:在任意位置显示任意大小的图片
触摸屏模块测试
触摸屏模块实验结果:按压琴键会显示按压效果,点击屏幕左上角和右上角,分别向前和向后循环切换图片
按压钢琴键效果图:
按压左上角或右上角切换曲谱图:
并发模块测试
实验结果:当手指按下琴键的时候,屏幕上对应位置的琴键会展示出按压效果,同时会播放出音乐,当手指松开屏幕,按键按压效果消失,音乐并不会消失。当未按下下一个按键的时候,该音乐会播放到直至结束。当按下一个按键的时候,该位置的按键会显示按压效果,并结束上一个音乐的播放,播放本次按压对应位置的音频。
项目源码
//main.c
#include <stdio.h>
#include "bmp.h"
#include "lcd.h"
#include "chupin.h"
#include "pthread.h"
int main()
{
int r = lcd_init();
if(r == -1)
{
printf("lcd_init fail\n");
return 0;
}
lcd_display_bmp("piano.bmp",0,0);
chupin();
lcd_uninit();
}
Lcd.c //lcd显示模块
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/mman.h>
#include <stdio.h>
#include "lcd.h"
int lcd_init()
{
lcd_fd = open("/dev/fb0",O_RDWR);
if(lcd_fd == -1)
{
perror("open lcd error");
return -1;
}
p = mmap(NULL,480*800*4,PROT_READ|PROT_WRITE,MAP_SHARED,lcd_fd,0);
if(p == NULL)
{
perror("mmap error");
return -1;
}
return 0;
}
void lcd_uninit()
{
close(lcd_fd);
munmap(p,800*480*4);
}
/*
功能:在屏幕上显示一个点
i:这个点的行
j:这个点的列
color:这个点的颜色
*/
int lcd_display_point(int i,int j,int color)
{
if(i>=0 && i<480 && j>=0 && j<800)
*(p+(480-1-i)*800+j) = color;//lcdbuf[j+800*(480-1-j)]
else
printf("error\n");
}
bmp.c //图片模块,获取图片的宽高
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include"bmp.h"
/*
功能:在lcd屏幕上的任意位置显示一张任意大小的图片
bmpname:是你要显示的图片的路径名(带路径的文件名)
x0:代表这张图片从屏幕的第x0行开始显示
y0:代表这张图片从屏幕的第y0列开始显示
*/
void lcd_display_bmp(char * bmpname,int x0,int y0)
{
int fd = open(bmpname,O_RDWR);
if(fd == -1)
{
perror("open error");
return;
}
lseek(fd,0x12,SEEK_SET);//因为宽度的偏移量是12h
//char buf[4] ;
int w;//用来保存图片的宽度
read(fd,&w,4);
lseek(fd,0x16,SEEK_SET);//因为高度的偏移量是12h
//char buf[4] ;
int h;//用来保存图片的高度
read(fd,&h,4);
char buf[w*h*3];
lseek(fd,54,SEEK_SET);//跳过“文件头”,定位到“像素数组”
read(fd,buf,w*h*3);
int i,j;
int color;
int n = 0;//
for(i=0;i<h;i++)//因为有480行
{
for(j=0;j<w;j++)//因为一行有800个像素点
{
//*(p+800*i+j) = color; 0x0000 0000 // 0xff ff ff 0xff<<16 0xff0000 0xff<<8 0xff00
color = buf[3*n] | buf[3*n+1]<<8 | buf[3*n+2]<<16;
lcd_display_point(i+x0,j+y0,color);
n++;
}
}
close(fd);
}
chupin.c //触摸屏模块
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <strings.h>
#include <linux/input.h>
#include <sys/mman.h>
#include "chupin.h"
#include "pthread.h"
#include <stdlib.h>
int chupin()
{
//lcd_display_bmp("tiger.bmp",279,0);
int x,y,p;
int num = 1;
int flag = 1;
int n = 1;
int ts = open("/dev/input/event0",O_RDWR);
struct input_event buf;
bzero(&buf,sizeof(buf));
while(1)
{
read(ts,&buf,sizeof(buf));
if(buf.type == EV_SYN)
printf("-----------------EV_SYV------------------\n");
if (buf.type == EV_ABS)
{
if(buf.code == ABS_X )
{
x = buf.value;
//printf("x = %d\n",x);
}
if (buf.code == ABS_Y)
{
y = buf.value;
}
}
if(buf.type == EV_KEY && buf.code == BTN_TOUCH)
{
p = buf.value;
//printf("p = %d\n",p);
}
if (p == 1 && x>0 && x < 57 && flag == 1 && y>280 && y <479)
{
system("killall -9 madplay");
lcd_display_bmp("1.bmp",0,0);
create1("madplay -Q d1.mp3");
flag = 0;
}
if (p == 1 && x>57 && x < 125 && flag == 1 && y>280 && y <479)
{
system("killall -9 madplay");
lcd_display_bmp("2.bmp",0,60);
create1("madplay -Q d2.mp3");
flag = 0;
}
if (p == 1 && x>125 && x < 193 && flag == 1 && y>280 && y <479)
{
system("killall -9 madplay");
lcd_display_bmp("3.bmp",0,127);
create1("madplay -Q d3.mp3");
flag = 0;
}
if (p == 1 && x>193 && x < 261 && flag == 1 && y>280 && y <479)
{
system("killall -9 madplay");
lcd_display_bmp("4.bmp",0,195);
create1("madplay -Q d4.mp3");
flag = 0;
}
if (p == 1 && x>261 && x < 329 && flag == 1 && y>280 && y <479)
{
system("killall -9 madplay");
lcd_display_bmp("5.bmp",0,263);
create1("madplay -Q d5.mp3");
flag = 0;
}
if (p == 1 && x>329 && x < 399 && flag == 1 && y>280 && y <479)
{
system("killall -9 madplay");
lcd_display_bmp("6.bmp",0,330);
create1("madplay -Q d6.mp3");
flag = 0;
}
if (p == 1 && x>397 && x < 465 && flag == 1 && y>280 && y <479)
{
system("killall -9 madplay");
lcd_display_bmp("7.bmp",0,398);
create1("madplay -Q d7.mp3");
flag = 0;
}
if (p == 1 && x>465 && x < 533 && flag == 1 && y>280 && y <479)
{
system("killall -9 madplay");
lcd_display_bmp("8.bmp",0,465);
create1("madplay -Q d8.mp3");
flag = 0;
}
if (p == 1 && x>533 && x < 601 && flag == 1 && y>280 && y <479)
{
system("killall -9 madplay");
lcd_display_bmp("9.bmp",0,533);
create1("madplay -Q d9.mp3");
flag = 0;
}
if (p == 1 && x>601 && x < 669 && flag == 1 && y>280 && y <479)
{
system("killall -9 madplay");
lcd_display_bmp("10.bmp",0,601);
create1("madplay -Q d10.mp3");
flag = 0;
}
if (p == 1 && x>669 && x < 737 && flag == 1 && y>280 && y <479)
{
system("killall -9 madplay");
lcd_display_bmp("11.bmp",0,669);
create1("madplay -Q d11.mp3");
flag = 0;
}
if (p == 1 && x>737 && x < 799 && flag == 1 && y>280 && y <479)
{
system("killall -9 madplay");
lcd_display_bmp("12.bmp",0,731);
create1("madplay -Q d12.mp3");
flag = 0;
}
if(x>0 && x<100 && y>0 && y < 100 && p == 1 && flag == 1)
{
n = n*(-1);
flag = 0;
}
if(x<800 && x>700 && y>0 && y < 100 && p == 1 && flag == 1)
{
n = n*(-1);
flag = 0;
}
// printf("n = %d",n);
if (p == 0 && flag == 0)
{
lcd_display_bmp("piano.bmp",0,0);
//system("killall -9 madplay");
flag = 1;
}
if(n == 1 && p == 0 )
{
lcd_display_bmp("tiger.bmp",279,0);
}
if (n == -1 && p == 0)
{
lcd_display_bmp("star.bmp",279,0);
}
}
close(ts);
return 0;
}
pthread.c //多线程模块
#include<stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <stdlib.h>
/*
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
thread: 是一个地址,用来保存新建线程的id
attr:线程的属性,一般来说,这个参数为NULL,表示采取默认属性
start_routine:函数指针,具体解析在上面
应该要指向线程函数(也就是新线程要去执行的那个函数)
arg:就是线程函数的参数
如果线程函数不需要参数,那么可以为NULL
*/
//线程函数
void * start_routine(void * string)
{
pthread_detach(pthread_self());
system((char *)string);
pthread_exit(0) ;
}
int create1(char *string)
{
pthread_t tid;//线程id
pthread_create(&tid,NULL,start_routine,(void *)string);
return 0;
}