嵌入式开发--电子钢琴项目(Linux+C开发)附上源码

这篇具有很好参考价值的文章主要介绍了嵌入式开发--电子钢琴项目(Linux+C开发)附上源码。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

电子钢琴项目

前言

复盘一下之前做的一个小项目,温习一下c语言和Linux的知识,唔,就是这样子。

一、环境搭建

所用软件以及工具如下:

1、VMware-workstation-full搭配Ubuntu18.04的Linux操作系统,VMware是桌面虚拟计算机软件,提供用户可在单一的桌面上同时运行不同的操作系统和进行开发、测试 、部署新的应用程序。
2、Vsode代码编写软件,配置c语言开发环境

3、CH341/340和PL2303 串口驱动软件

4、SecureCRT串口调试软件

5、GEC6818开发板如下图

嵌入式开发--电子钢琴项目(Linux+C开发)附上源码

上述环境搭建百度一下就有很多很多教程,这里不在赘述。真的要说配置环境,那得拉老长的文章了。

二、开发板的使用与Linux文件IO

开发流程如下:

  1. 通过Vscode中进行代码编辑,进行开发后放入共享文件夹
  2. Ubantu中切换到共享目录,使用交叉编译器出可执行文件
  3. 打开SecureCRT将可执行文件上传开发板
  4. 在开发板上调试运行

联通开发板与电脑:

  1. 接通电源,插上开发板后,安装CH341/340和PL2303串口驱动,

  2. 在计算机右键点击计算机管理中的设备管理器查看对应端口

  3. 打开SecureCRT串口调试软件,点击快速连接

  4. 设置SSH为serial,端口为查看的端口,波特率115200,流控全部关闭,点击连接即可

文件上传开发板

  • 可执行文件(小):rx demo命令后点击传输选择文件上传,百k以下
  • 程序运行资源(大):如音频、图像文件等,通过U盘上传,在 /mnt/udisk目录下cp -r demo /就行;

Linux文件IO

嵌入式开发--电子钢琴项目(Linux+C开发)附上源码

  1. open函数打开文件
  2. 定义数据缓冲区,write函数写入数据
  3. lseek函数调整文件位置偏移量
  4. 定义数据缓冲区,read函数存放读到的数据
  5. 打印出读到的数据
  6. 关闭对应文件

小练习:德国国旗的显示

终于简单的归纳了一下部分内容到这里,可以开始做个小Demo练习一下熟练知识点啦!显示德国国旗在开发板上。(不要在意条条的颜色这些细节,问就是酱样紫)
嵌入式开发--电子钢琴项目(Linux+C开发)附上源码

在开发板上显示德国国旗代码如下:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int main()
{
	// 1.打开lcd驱动
	int lcd_fd;
	lcd_fd = open("/dev/fb0", O_RDWR);
	if(-1 == lcd_fd)
	{
		perror("open lcd failed!\n");
		return -1;
	}

	// 2.处理颜色数据
	// 定义颜色数据缓冲区
	int col_buf[800*480];
	int x,y;
	for(y=0; y<160; y++)
	{
		for(x=0; x<800; x++)
			col_buf[800*y+x] = 0x000000;
	}
	for(y=160; y<320; y++)
	{
		for(x=0; x<800; x++)
			col_buf[800*y+x] = 0xff0000;
	}
	for(y=320; y<480; y++)
	{
		for(x=0; x<800; x++)
			col_buf[800*y+x] = 0xffff00;
	}

	//	3.将颜色数据写入lcd
	write(lcd_fd, col_buf, sizeof(col_buf));

	// 4.关闭lcd
	close(lcd_fd);

	return 0;
}

三、项目界面显示

tips:猜猜界面中的背景图是谁鸭

最终电子钢琴界面效果如下图:

嵌入式开发--电子钢琴项目(Linux+C开发)附上源码

​ 不同的图片格式有着不同的压缩算法,所以需要将对应格式的图片文件压缩算法库引入项目,这里为了简单一点使用通用的24位图格式BMP文件存储图片,当然在GEC6818开发板的LCD显示屏却是32位图,为了在开发板的系统上正常显示需要进行原色移位即24转32位。

​ 开发板系统的图片像素点是A、R、G、B共4个字节32位,BMP格式的图片像素点是B、G、R共3个字节24位,以B为基准,一个字节是八位,所以将R左移16位,G左移8位,B不动,A可以左移24事实上不管也行默认就可以,然后通过C语言的按位或,配合左移,实现数据的拼接大功告成啦!

嵌入式开发--电子钢琴项目(Linux+C开发)附上源码

例         二进制:  1101 0101
                   1010 1101<<8 
                   1101 1010<<16
                   0000 0000<<24
      结果 0000 0000 1101 1010 1010 1101 1101 0101 
//int  4字节                   buf是char 1字节         透明度是0不管
bmp_buf[i] = buf[3*i+2]<<16 | buf[3*i+1]<<8 | buf[3*i]; //其实写成代码也就一行搞定     

注意:

  • 边界显示:bmp图片的宽所占的字节数如果不能被4整除,windows在保存的时候,会在每一行的后面添加垃圾数凑够4整除
  • 图像偏移:BMP图片有54字节的头文件存储图片的bmp格式图片的长度和宽度,色深,大小……,所以需要用lseek进行偏移
  • 图形翻转:因为BMP独特的编码方式:它的像素点的编码方式是上下颠倒的,我们在开发板的屏幕上显示需要从最下面一行开始向上进行显示,如下图方便理解,左图显示的开发板,右图缓冲区的图片

嵌入式开发--电子钢琴项目(Linux+C开发)附上源码

图片显示代码如下:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int main()
{
	// 1.打开lcd驱动
	int lcd_fd;
	lcd_fd = open("/dev/fb0", O_RDWR);
	if(-1 == lcd_fd)
	{
		perror("open lcd failed!\n");
		return -1;
	}
	// 2.打开图片
	int bmp_fd;
	bmp_fd = open("/LeiMu.bmp", O_RDONLY);
	if(-1 == bmp_fd)
	{
		perror("open bmp failed!\n");
		return -1;
	}
	char head[54] = {0};
	read(bmp_fd, head, 54);
	int w = *((int *)&head[18]);
	int h = *((int *)&head[22]);
	printf("w=%d, h=%d\n", w,h);
	// 补齐4字节
	int n_add; // 需要补的字节数
	int add_a; // 补齐后的字节数
	n_add = (4-w*3%4)%4;
	add_a = w*3+n_add;
	char bmp_add[add_a*h];
	lseek(bmp_fd, 54, SEEK_SET);
	read(bmp_fd, bmp_add, sizeof(bmp_add));

	// 3.读取图片像素数据
	char bmp_buf[w*h*3];
	for(int j=0; j<h; j++)
		memcpy(&bmp_buf[w*3*j], &bmp_add[add_a*j], w*3);
	// 3.1 24--->32
	int bmp_32[w*h];
	for(int i=0; i<w*h; i++)
	{//                b         g        r        a
		bmp_32[i] = bmp_buf[3*i+0]<<0 | bmp_buf[3*i+1]<<8 | bmp_buf[3*i+2]<<16 | 0x00<<24;
	}
	// 3.2 翻转
	int buffz[800*480];
	int x0=0, y0=0;
	for(int y=0; y<h; y++)
	{
		for(int x=0; x<w; x++)
		{
			buffz[800*(y+y0)+x+x0] = bmp_32[w*(h-1-y)+x];
		}
	}
	// 4.把图片像素数据写入lcd
	write(lcd_fd, buffz, sizeof(bmp_32));
	// 5.关闭lcd,关闭图片
	close(lcd_fd);
	close(bmp_fd);

	return 0;
}

图片显示效果如下:

嵌入式开发--电子钢琴项目(Linux+C开发)附上源码

​ 猜到背景板上的人物了吗?OK,现在背景搞定了,接下来就是把按键以及头部和底部添上去了,和之前操作差不多其实,无非就是改了改参数而已(正常开发中,这些界面参数都会由UI设计师给出),难道我们要copy这些代码一个个费劲儿的调参吗?不,这里可以封装一下显示的函数,将需要显示的图片长宽以及起始点位置x、y和图片文件路径传给它就好了。

​ 除了需要封装显示函数,这里我们采用效率更高的mmap映射的方式来实现用户空间和内核空间的数据直接交互从而省去了空间不同数据不通的繁琐过程。

mmap介绍

#include <sys/mman.h>

FB = mmap(NULL, 800*480*4, PROT_READ | PROT_WRITE, MAP_SHARED, lcd_fd, 0);

返回值为void型万能指针,可以和浮点型、整型等兼容。关键看定义的存储数据数组是什么类型就用什么类型;

共有6个参数:

  • 第一个 映射内存的起始地址,我们一般用NULL,系统会自动寻找一个合适的起始地址。
  • 第二个 映射内存的大小,就是我们要把一个多大的文件映射到内存中,mmap映射后,会返回 给我们一个内存映射的起始地址,这个len就是我们文件的大小,8004804。
  • 第三个 映射内存的保护权限,一般给可读可写就行。
  • 第四个 我们要选共享也就是map-shared。
  • 第五个 文件描述符,把lcd文件描述符给他就可以。
  • 第六个 文件映射的开始区域偏移量,那么在屏幕上来说,要从左上角,也就是0开始。
munmap(FB, 800*480*4);  //释放虚例内存函数
  • 第一个参数 释放内存地址
  • 第二个参数 释放内存长度

封装的显示图片函数代码如下:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>	
#include <signal.h>

#define LCD_PATH "/dev/fb0"

int lcd_fd;
int *FB;

//1,LCD初始化函数
void Lcd_Init(void)
{
	//1,打开LCD文件
	lcd_fd = open(LCD_PATH, O_RDWR);
	if(-1 == lcd_fd)
	{
		perror("open lcd failed");
		return;
	}
	
	//2,lcd映射到用户空间
	FB = mmap(NULL, 800*480*4, PROT_READ | PROT_WRITE, MAP_SHARED, lcd_fd, 0);
	if(MAP_FAILED == FB)
	{
		perror("mmap lcd failed");
		return;
	}
}

//2,LCD释放函数
void Lcd_Uninit(void)
{
	//4,解除映射,关闭文件
	munmap(FB, 800*480*4);
	close(lcd_fd);
}

//3,显示宽度为win,高度为high的bmp图片  在起点坐标为(x_s, y_s)这个点开始显示图片
void Show_Bmp(int win, int high, int x_s, int y_s, char *picname)
{
	int i, j;
	int tmp;
	char buf[win*high*3];		//存放图片的原始数据
	int bmp_buf[win*high];		//存放转码之后的ARGB数据
	
	//1,lcd初始化
	Lcd_Init();
	
	//2,读取图片数据,并且进行转码 RGB -> ARGB
		//打开图片
		FILE *fp = fopen(picname, "r");
		if(NULL == fp)
		{
			perror("fopen failed");
			return;
		}
		
		//读取图片像素点原始数据
		fseek(fp, 54, SEEK_SET);
		fread(buf, 3, win*high, fp);
		
		//将读取的数据进行转码 24-->32
		for(i=0; i<win*high; i++)
		{	
			//ARGB			R					G			B
			bmp_buf[i] = buf[3*i+2]<<16 | buf[3*i+1]<<8 | buf[3*i];
		}
		//将转码的数据进行倒转 把第i行,第j列的点跟第479-i行,第j列的点进行交换
		for(i=0; i<high/2; i++)	//0~239行
		{
			for(j=0; j<win; j++) 	//0~799列
			{
				//第i行,第j列的点跟第479-i行,第j列的点进行交换
				tmp = bmp_buf[win*i+j];
				bmp_buf[win*i+j] = bmp_buf[win*(high-1-i)+j];
				bmp_buf[win*(high-1-i)+j] = tmp;
			}
		}
	//3,将转码之后的数据写入LCD (写入到LCD的区域由 (0,0) --> (100, 20))
	for(i=y_s; i<high+y_s && i<480; i++)		// 0 ~ high-1行   20 ~ high+20-1
	{
		for(j=x_s; j<win+x_s && j<800; j++)	// 0~win-1列	  100 ~ win+100-1	
		{
			//FB[800*i+j] = bmp_buf[win*i+j];(图片的数组中第i行,第j列的点)
			FB[800*i+j] = bmp_buf[win*(i-y_s)+j-x_s];			
		}
	}		
	//4,lcd资源销毁,关闭图片
	fclose(fp);
	Lcd_Uninit();
}

int main()
{
    Show_Bmp(800,480,0,0, "LeiMu.bmp");
    Show_Bmp(800,80,0,0,"logo.bmp");//top部图片
    for (int i = 0; i < 12; i++)
        Show_Bmp(60,280,40+60*i,100,"key_off.bmp");
    Show_Bmp(800,80,0,400,"bar.bmp");//bootom部图片
    return 0;
}

到这里,以上代码就可以实现,钢琴界面的显示啦!效果见最开始的图哦。目前只是静态的显示,接下来要做的是获取点击的位置,判断区域,播放对应音乐,按键换图。

四、触摸屏应用

使用Linux中的输入子系统,可以轻松获取触摸屏的被触摸的坐标,引入#include <linux/input.h>即可开始使用,其实它是定义了一个结构体,如下所示

struct input_event
{
        struct timeval time;时间戳,精确到微秒
        _u16 type;输入事件类型
        _u16 code;具体事件描述,比如触摸屏的EV_ABS中的ABS_X和ABS_Y;
        _u16 value;具体动作描述
}
    EV_SYN:事件分割标志
    EV_ABS:发生了触摸屏事件,触摸屏坐标值ABS_X,ABS_Y;
    EV_REL:发生了鼠标事件
    EV_KEY:发生了键盘事件,设备的状态发生变化
    BTN_TOUCH:点击事件   
struct timeval{
        _time_t tv_sec;long int tv_usec;微秒
}

嵌入式开发--电子钢琴项目(Linux+C开发)附上源码

当用户点击时,只需要根据输入事件类型为触摸屏,就可以去具体事件描述里获取EV_ABS,这也就是触摸屏坐标值ABS_X,ABS_Y,废话不多说,直接上代码!

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>	
#include <signal.h>
#include <linux/input.h>	//输入子系统的头文件

#define LCD_PATH "/dev/fb0"
#define TS_PATH  "/dev/input/event0"

int lcd_fd;
int *FB;
int ts_x, ts_y;		//存放点击屏幕的横纵坐标


//获取一次点击触摸屏的坐标信息,存入ts_x,ts_y
int get_ts(void)
{
	//1,打开触摸屏文件
	int fd = open(TS_PATH, O_RDWR);
	if(-1 == fd)
	{
		perror("open ts failed");
		return -1;
	}
	
	//2,读取触摸屏文件数据
	struct input_event xy;		
	int flag = 0;		//记录当前获取坐标的信息
	
	while(1)
	{
		read(fd, &xy, sizeof(xy));
		if(xy.type == EV_ABS && xy.code == ABS_X && flag == 0)
		{
			ts_x = xy.value * 800 / 1024;		//获取点击的时候X轴坐标的值 (0~1024)--> (0~800)
			flag = 1;
		}
		if(xy.type == EV_ABS && xy.code == ABS_Y && flag == 1)
		{
			ts_y = xy.value * 480 / 600;		//获取点击的时候Y轴坐标的值 (0~600)-->(0~480)
			flag = 2;
		}
		//设置条件:每读取一次完整的坐标,就打印一次坐标
		if(flag == 2)
		{
			flag = 0;
			printf("(%d,%d)\n", ts_x, ts_y);
			break;	//获取一次坐标就跳出循环
		}
	}
	//3,关闭触摸屏文件
	close(fd);
}

这里qmy_lhl博主的一个比较好的地方,定义一个flag来控制获取函数的结束,同时需要注意获取点击时候坐标的值,注意一个细节,ts_x,ts_y是定义的全局变量便于拿到主函数里去用。

最后,再坚持一下下,这个项目接近实现啦!

现在我们有了用户按压了琴键,这时候需要播放对应的声音,我们借助Alsa(Advanced Linux Sound Architecture)库(官方去下载即可,搜一下就有)来实现播放,这里记得复制libasound.so.2加上混响并配置一下全局变量就好了

现在,我们在代码中执行下面的代码即可播放对应路径的声音!

execlp("aplay","aplay",./mp3/d1.wav”,NULL);

琴键那么多声音,难道我们要一个个手戳重复的代码来播放吗?不,这里封装一下下播放函数,只需要传一个编号就能播放一个MP3目录下对应的声音;

void play(int num){
    char str[32] = {0};
    printf("%d\n",num);
    sprintf(str,"./mp3/d%d.wav",num);//打印字符到str中
    execlp("aplay","aplay",str,NULL);
    return;
}

​ 秋豆麻袋,目前用户点击一下就会播放一次对应编号的声音,这是一个线程,多点多放对应着多线程的问题,下面是一个很形象的比喻:

  • 单进程单线程:一个人在一个桌子上吃菜。
  • 单进程多线程:多个人在同一个桌子上一起吃菜。
  • 多进程单线程:多个人每个人在自己的桌子上吃菜。

​ 多线程的问题是多个人同时吃一道菜的时候容易发生争抢,例如两个人同时夹一个菜,一个人刚伸出筷子,结果伸到的时候已经被夹走菜了。。。此时就必须等一个人夹一口之后,在还给另外一个人夹菜,也就是说资源共享就会发生冲突争抢。

1、对于 Windows 系统来说,【开桌子】的开销很大,因此 Windows 鼓励大家在一个桌子上吃菜。因此 Windows 多线程学习重点是要大量面对资源争抢与同步方面的问题。

2、对于 Linux 系统来说,【开桌子】的开销很小,因此 Linux 鼓励大家尽量每个人都开自己的桌子吃菜。这带来新的问题是:坐在两张不同的桌子上,说话不方便。因此,Linux 下的学习重点大家要学习进程间通讯的方法。

这里,我们用fork()函数执行子进程实现播放多个声音,解决这个问题。

下面放上完整代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>	
#include <signal.h>
#include <linux/input.h>	//输入子系统的头文件

#define LCD_PATH "/dev/fb0"
#define TS_PATH  "/dev/input/event0"

int lcd_fd;
int *FB;
int ts_x, ts_y;		//存放点击屏幕的横纵坐标

//1,LCD初始化函数
void Lcd_Init(void)
{
	//1,打开LCD文件
	lcd_fd = open(LCD_PATH, O_RDWR);
	if(-1 == lcd_fd)
	{
		perror("open lcd failed");
		return;
	}
	
	//2,lcd映射到用户空间
	FB = mmap(NULL, 800*480*4, PROT_READ | PROT_WRITE, MAP_SHARED, lcd_fd, 0);
	if(MAP_FAILED == FB)
	{
		perror("mmap lcd failed");
		return;
	}
}

//2,LCD释放函数
void Lcd_Uninit(void)
{
	//4,解除映射,关闭文件
	munmap(FB, 800*480*4);
	close(lcd_fd);
}

//3,显示宽度为win,高度为high的bmp图片  在起点坐标为(x_s, y_s)这个点开始显示图片
void Show_Bmp(int win, int high, int x_s, int y_s, char *picname)
{
	int i, j;
	int tmp;
	char buf[win*high*3];		//存放图片的原始数据
	int bmp_buf[win*high];		//存放转码之后的ARGB数据
	
	//1,lcd初始化
	Lcd_Init();
	
	//2,读取图片数据,并且进行转码 RGB -> ARGB
		//打开图片
		FILE *fp = fopen(picname, "r");
		if(NULL == fp)
		{
			perror("fopen failed");
			return;
		}
		
		//读取图片像素点原始数据
		fseek(fp, 54, SEEK_SET);
		fread(buf, 3, win*high, fp);
		
		//将读取的数据进行转码 24-->32
		for(i=0; i<win*high; i++)
		{	
			//ARGB			R					G			B
			bmp_buf[i] = buf[3*i+2]<<16 | buf[3*i+1]<<8 | buf[3*i];
		}
		//将转码的数据进行倒转 把第i行,第j列的点跟第479-i行,第j列的点进行交换
		for(i=0; i<high/2; i++)	//0~239行
		{
			for(j=0; j<win; j++) 	//0~799列
			{
				//第i行,第j列的点跟第479-i行,第j列的点进行交换
				tmp = bmp_buf[win*i+j];
				bmp_buf[win*i+j] = bmp_buf[win*(high-1-i)+j];
				bmp_buf[win*(high-1-i)+j] = tmp;
			}
		}
	//3,将转码之后的数据写入LCD (写入到LCD的区域由 (0,0) --> (100, 20))
	for(i=y_s; i<high+y_s && i<480; i++)		// 0 ~ high-1行   20 ~ high+20-1
	{
		for(j=x_s; j<win+x_s && j<800; j++)	// 0~win-1列	  100 ~ win+100-1	
		{
			//FB[800*i+j] = bmp_buf[win*i+j];(图片的数组中第i行,第j列的点)
			FB[800*i+j] = bmp_buf[win*(i-y_s)+j-x_s];			
		}
	}		
	//4,lcd资源销毁,关闭图片
	fclose(fp);
	Lcd_Uninit();
}

//4,获取一次点击触摸屏的坐标信息,存入ts_x,ts_y
int get_ts(void)
{
	//1,打开触摸屏文件
	int fd = open(TS_PATH, O_RDWR);
	if(-1 == fd)
	{
		perror("open ts failed");
		return -1;
	}
	
	//2,读取触摸屏文件数据
	struct input_event xy;		
	int flag = 0;		//记录当前获取坐标的信息
	
	while(1)
	{
		read(fd, &xy, sizeof(xy));
		if(xy.type == EV_ABS && xy.code == ABS_X && flag == 0)
		{
			ts_x = xy.value * 800 / 1024;		//获取点击的时候X轴坐标的值 (0~1024)--> (0~800)
			flag = 1;
		}
		if(xy.type == EV_ABS && xy.code == ABS_Y && flag == 1)
		{
			ts_y = xy.value * 480 / 600;		//获取点击的时候Y轴坐标的值 (0~600)-->(0~480)
			flag = 2;
		}
		//设置条件:每读取一次完整的坐标,就打印一次坐标
		if(flag == 2)
		{
			flag = 0;
			printf("(%d,%d)\n", ts_x, ts_y);
			break;	//获取一次坐标就跳出循环
		}
	}
	//3,关闭触摸屏文件
	close(fd);
}

void play(int num){
    char str[32] = {0};
    printf("%d\n",num);
    sprintf(str,"./mp3/d%d.wav",num);//打印字符到str中
    execlp("aplay","aplay",str,NULL);
    return;
}

int main()
{
    Show_Bmp(800,480,0,0, "LeiMu.bmp");
    Show_Bmp(800,80,0,0,"logo.bmp");//top
    for (int i = 0; i < 12; i++)
        Show_Bmp(60,280,40+60*i,100,"key_off.bmp");
    Show_Bmp(800,80,0,400,"bar.bmp");//bootom

    while (1)
    {
        get_ts();
        int num;long stime;
        num = (ts_x-40)/60; 
        if(ts_y > 100 && ts_y < 380 && ts_x > 40+num*60 && ts_x < 100+num*60)//如果点击对应区域就创建一条子进程播放音乐 
		{
            Show_Bmp(60,280,40+num*60,100,"key_on.bmp");//改变按下的键
            if(0 == fork()){             
                play(num+1);            
            } 
            stime = 200000;
            usleep(stime);
            Show_Bmp(60,280,40+num*60,100,"key_off.bmp");//恢复按下的键
        }          
    }
    return 0;
}

五、收工躺平

弄了个《小星星》代码播放的,毫无感情,全是技巧,播放即社死,千万别轻易尝试!

void Playstar(){
    int star[4][7]={{1,1,5,5,6,6,5} //一闪一闪亮晶晶
                ,{4,4,3,3,2,2,1}    //满天都是小星星
                ,{5,5,4,4,3,3,2}    //挂在天空放光明,
                ,{5,5,4,4,3,3,2}};  //好像许多小眼睛
    int stime;
    for(int i=0;i<4;i++)
    {
        for(int j=0;j<7;j++)
        {
            if(0 == fork())
            {
                play(star[i][j]);                
            }
            printf("%d\t",star[i][j]);
            stime = 200000;
            usleep(stime);
    	}
        stime = 500000;
        usleep(stime);
    }
}

文章中省略了一些细节,但基本上完整复盘了该项目过程,如有错漏,欢迎指出哦!

参考文章:

  • 交叉编译器arm-linux-gcc - 知乎 (zhihu.com)
  • 认真分析mmap:是什么 为什么 怎么用 - 胡潇 - 博客园 (cnblogs.com)
  • (28条消息) GEC6818开发板上音乐播放器_qmy_lhl的博客-CSDN博客_6818开发板
  • 多进程和多线程的概念 - fengMisaka - 博客园 (cnblogs.com)

一些GEC6818的小项目链接:文章来源地址https://www.toymoban.com/news/detail-472473.html

  • 《粤嵌开发板电子相册嵌入式课程设计》.doc (book118.com)
  • (28条消息) 基于Cortex-A53内核Linux系统粤嵌GEC6818开发板的电子乐队程序设计(四种模式:和弦模式、键盘模式、鼓点模式、编曲模式)_网易独家音乐人Mike Zhou的博客-CSDN博客_粤嵌6818开发板项目代码
  • ARM_钢琴游戏_LinuxC: 在GEC6818开发板中,实现一个模拟的钢琴游戏,通过手指弹奏发出相应的琴声。 (gitee.com)

到了这里,关于嵌入式开发--电子钢琴项目(Linux+C开发)附上源码的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 嵌入式linux项目之智能仓储(基于正点原子IMX6ULL开发板)

    提示:该项目根据华清远见智能仓储项目改版,将他的A9开发板换成了自己的IMX6ULL开发板。同时等我将该项目整个流程完成之后,会为大家附上适配正点原子linux开发板的全部代码 (代码资料和视频教程私聊) 视频教程 相应课程配套源码 本人其他项目链接基于linux的智能仓储项

    2024年01月16日
    浏览(89)
  • 基于粤嵌gec6818开发板嵌入式开发电子相册,音乐播放,视频播放,2048游戏

    实现功能:本系统需要使用粤嵌的GEC-6818开发板设计一款娱乐影音系统,其中包括图片显示(相册)、音乐播放、视频播放,游戏四个部分,在每个部分内部,具有操控各个部分的功能触摸按键。本系统还应具有蓝牙远程操控功能。 具体要求:对使用者具有良好的可视交互体

    2024年02月13日
    浏览(71)
  • 嵌入式Linux:如何进行嵌入式Linux开发?

    目录 1、裸机开发 2、SDK开发 3、驱动开发 3.1、字符设备驱动 3.2、块设备驱动 3.3、网络设备驱动 4、应用开发 嵌入式Linux开发主要有四种方式:裸机开发、SDK开发、驱动开发和应用开发。 裸机开发通常指在没有操作系统支持的环境中直接在硬件上运行程序的开发。这种开发方

    2024年01月25日
    浏览(80)
  • 关于嵌入式开发的一些信息汇总:嵌入式C开发人员、嵌入式系统Linux

    这篇文章是关于嵌入式开发的一些基本信息,供想入行的人参考。有一些作者本人的想法,以及来自外网的大拿的文章翻译而来,原文链接在此Learning Linux for embedded systems,再次感谢,支持原创。 普通C开发人员和嵌入式C开发人员之间的 基本区别在于 ,因为嵌入式C程序被设

    2024年02月03日
    浏览(71)
  • 嵌入式开发:单片机嵌入式Linux学习路径

    SOC(System on a Chip)的本质区别在于架构和功能。低端SOC如基于Cortex-M架构的芯片,如STM32和NXP LPC1xxx系列,不具备MMU(Memory Management Unit),适用于轻量级实时操作系统如uCOS和FreeRTOS。高端SOC如基于Cortex-A架构的芯片,如三星、高通、飞卡、全志和瑞芯微等,具备MMU,支持虚拟内

    2024年02月15日
    浏览(57)
  • Linux嵌入式项目-智能家居

    一、资料下载  二、框架知识  三、MQTT通信协议 1、上位机APP主要工作        1.wait for msg  / while(1)订阅等待消息        2.处理消息 客户端创建了两个线程,一个线程用于发布消息,一个线程用于监听订阅消息 (那我的仿真系统也可以啊,一个发送处理数据线程。一个监听

    2024年02月16日
    浏览(50)
  • 【嵌入式Linux项目】基于Linux的全志H616开发板智能家居项目(语音控制、人脸识别、安卓APP和PC端QT客户端远程操控)有视频功能展示

    目录 一、功能需求 二、开发环境 1、硬件: 2、软件: 3、引脚分配: 三、关键点 1、设计模式之工厂模式 2、线程 3、wiringPi库下的相关硬件操作函数调用 4、语音模块的串口通信 5、摄像头的实时监控和拍照功能 6、人脸识别 7、qt程序跨平台运行(编译成安卓APP) 四、编译和

    2024年02月15日
    浏览(61)
  • 嵌入式Linux开发教程汇总

    野火:https://doc.embedfire.com/products/link/zh/latest/linux/index.html 正点原子:http://47.111.11.73/docs/boards/arm-linux/index.html 百问网:http://download.100ask.net/books/Linux/ELADCM1/index.html 百问网:https://space.bilibili.com/275908810/channel/seriesdetail?sid=1714177 野火:https://space.bilibili.com/356820657/channel/seriesdeta

    2024年02月10日
    浏览(66)
  • 嵌入式Linux驱动开发之点灯

      使用驱动开发的方式点亮一个LED灯。看看两者有啥区别不? 首先查看原理图,看看我们的板子上的LED等接在哪一个IO口上面。 好了,看原理图我们知道LED灯接在芯片的GPIO1的第三个引脚上面,也就是GPIO1_IO03。 先掌握三个名词 CCM: Clock Controller Module (时钟控制模块) IOMUXC : I

    2024年02月01日
    浏览(101)
  • 嵌入式开发之linux内核移植

    目录  前言 一、下载内核源码 1.1 下载linux-3.0.1 1.2 解压源码文件 二、 内核添加yaffs2文件系统支持 2.1 下载yaffs2 2.2 内核添加yaffs2文件补丁 三、配置开发板 3.1 修改机器ID 3.2 添加开发板初始化文件 3.3 配置NandFalsh 3.3.1 添加NandFlash设备 3.3.2 添加NandFlash驱动 3.3 修改Kconfig(支持

    2024年02月07日
    浏览(104)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包