嵌入式电子钢琴游戏开发设计

运行环境要求:

硬件环境: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;
}

  • 6
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值