嵌入式的划水项目

起因:学校安排了实训,遗憾的是出不去校园,只能校外培训机构的进来喽。说是经费不足,我等低端人员也只能默默。。。培训的是嵌入式开发,由于以后不走那个方向所以哈哈哈哈。。

1.项目
web远程控制MP3

2.环境
①.macOS
②.win7 --用来使用串口调试开发板
③.Ubuntu --16.04用来交叉编译
④.开发板FriendlyARM mini2440
3.大致流程图
流程图
4..具体步骤

1.安装交叉编译环境

2.移植boa服务器

1.下载boa源码(ps:www.boa.org挂了,得通过别的渠道下,本人直接科学上网Google下载)
2.生成Makefile:将boa压缩包解压,进入src目录运行./configure生成Makefile
3.修改生成Makefile文件,

CC = arm-linux-gcc -static
CPP = arm-linux-gcc –E -static

4.修改boa.c源文件

	大约211行附近注释掉下面部分

   /*  if (passwdbuf == NULL) {

            DIE("getpwuid");
        }
        if (initgroups(passwdbuf->pw_name, passwdbuf->pw_gid) == -1) {
            DIE("initgroups");
        }*/

 /*if (setuid(0) != -1) {
            DIE("icky Linux kernel bug!");
        }*/

       
5.修改文件compat.h

#define TIMEZONE_OFFSET(foo) foo##->tm_gmtoff
修改成
#define TIMEZONE_OFFSET(foo) (foo)->tm_gmtoff

6.执行make命令

make
arm-linux-strip boa//缩小体积

7.成功了就跳过此步骤,失败就看看,然后在执行步骤6(反正我失败了,😿)

sudo apt-get install bison flex

否则会出现如下错误

make: yacc:命令未找到
       make: *** [y.tab.c] 错误 127

make: lex:命令未找到
       make: *** [lex.yy.c] 错误 127

8.创建boa配置文件boa.conf

Port 8080//访问端口号
User root//使用用户权限
Group root//用户组权限
ErrorLog /var/log/boa/error_log//错误日志
AccessLog /var/log/boa/access_log//通过日志
ServerName 192.168.1.230//服务器ip即开发板ip
DocumentRoot /www//客户端访问的根目录,放置html文件的地方
UserDir public_html
DirectoryIndex index.html
DirectoryMaker /usr/lib/boa/boa_indexer
KeepAliveMax 1000
KeepAliveTimeout 10
MimeTypes /etc/mime.types
DefaultType text/plain
CGIPath /bin:/usr/bin:/usr/local/bin
Alias /doc /usr/doc
ScriptAlias /cgi-bin/ /www/cgi-bin/  //c放置cgi文件地方

可以和我一样哈哈哈

9.拷贝文件
关键:将mime.types、inittab和passwd文件从任何一个linux系统中的/etc目录下拷贝到开发板文件系统的/etc目录下,创建CGI脚本所在目录/var/www/cgi-bin/。mime.types文件用来指明不同文件扩展名对应的MIME类型,一般可以直接从Linux主机上拷贝一个,大部分也都是在主机的/etc目录下。

10.最后把在src下生成的boa文件和自己写的boa.conf拷贝到开发板的/etc/boa/目录下,我还复制了一份到/bin目录下,然后它从我第一次手动开机后,就会开机自启,暂时没去抠这个问题。如果产生了端口占用的话可能是开机自启了。所以修改配置文件后,要嘛吧boa kill 要嘛重启一下,让它重新加载配置文件,如果启动没有啥问题的话,就成功了!!!

3.移植madplay

1.准备好5个压缩包
zlib-1.1.4.tar.gz,
libid3tag-0.15.1b.tar.gz,
libmad-0.15.1b.tar.gz,
mad-0.10.1b.tar.gz,
madplay-0.15.2b.tar.gz

以上是本人使用的包,包不同可能会出现奇怪的东西,所以建议版本相同哈

2.解压源代码包
3.编译madplay及其所依赖的库文件

(1)编译zlib,因为libid3tag依赖于这个库文件
进入zlib包目录:

# ./configure --prefix=/opt/madplay/target-x86
# make
# make install

编译好之后就可以在上面prefix指定目录下的lib目录下找到libz.a这个库。
如果失败了,并且提示文件不存在就自己手动创建,这些命令最好用root账户运行否则权限不够。
(2)编译libid3tag
进入libid3tag-0.15.1b包:

# ./configure --prefix=/opt/madplay/target-x86 --disable-debugging --disable-shared --enable-static CPPFLAGS=-I/opt/madplay/target-x86/include LDFLAGS=-L/opt/madplay/target-x86/lib
# make
# make install

其中,--disable-shared --enable-static是指定为静态编译,不过这样并不能够进行静态编译。至于为什么,有待于继续研究。

(3)编译libmad
进入libmad-0.15.1b包目录

# ./configure --prefix=/opt/madplay/target-x86 --disable-debugging --disable-shared --enable-static CPPFLAGS=-I/opt/madplay/target-x86/include LDFLAGS=-L/opt/madplay/target-x86/lib

如果此时make,make install就会报错,说是"-fforce-mem"参数不能识别。打开当前目录下的Makefile文件,去掉里面出现的"-fforce-mem",然后再make,make install就没用问题了。

(4)编译madplay
进入madplay-0.15.2b包目录

# ./configure --prefix=/opt/madplay/target-x86 --disable-debugging --disable-shared --enable-static CPPFLAGS=-I/opt/madplay/target-x86/include LDFLAGS=-L/opt/madplay/target-x86/lib
# make
# make install

然后把包中生成的madplay文件拷贝到开发板中就可以了。之后自己试看看是否可以run,使用命令./madplay /a.mp3,自己也可以去百度madplay的命令

4.代码实现

1.通过监听管道来控制mp3,代码非原创,从博主一叶知秋处copy,更正了两处小错
MP3.c代码

/*
 *     SD卡mp3播放器控制程序
 *	   功能:
 			 k1:播放、暂停
 			 k2:停止播放
 			 k3:上一首
 			 k4:下一首
 *     附加:歌曲自动循环播放SD卡/sdcard/song目录中mp3歌曲
 */
 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

/*共享内存申请标记*/
#define PERM S_IRUSR|S_IWUSR													

/*双向循环列表:存放歌曲名*/
struct song				
{
	char songname[20];
	struct song *prev;
	struct song *next;
};

/*孙子进程id号*/
pid_t gradchild;

/*子进程id号*/
pid_t pid;

/*共享内存描述标记*/
int shmid;
char *p_addr;
/*共享内存内容格式*/
/*|gradchild(孙子进程PID) |+ |空一个字节|+ currentsong(当前播放列表的节点指针)|*/

/*播放标记*/
int first_key=1;
int play_flag=0;

/*************************************************
Function name: play
Parameter    : struct song *
Description	 : 播放函数
Return		 : void
Argument     : void
**************************************************/
void play(struct song *currentsong)
{
	pid_t fd;
	char *c_addr;
	char *p;
	int len;
	char my_song[30]="/sdcard/song/"; 
	while(currentsong)
	{
		/*创建子进程,即孙子进程*/
		fd = fork();
		if(fd == -1)
		{	
			perror("fork");
			exit(1);
		}
		else if(fd == 0) 
		{
			/*把歌曲名加上根路径*/
			strcat(my_song,currentsong->songname); 
			p = my_song;
			len = strlen(p);
			/*去掉文件名最后的'\n'*/
			my_song[len-1]='\0';

			printf("THIS SONG IS %s\n",my_song);
                      /*运行madplay播放器,播放MP3*/
                     //system("printf(" THIS SONG IS %s\n", $my_song) >> /tmp/songname");
		
			
			execl("/bin/madplay","madplay",my_song,NULL);
			printf("\n\n\n");
		}
		else 
		{
			/*内存映射*/
			
			c_addr = shmat(shmid,0,0);

			
			memcpy(c_addr,&fd,sizeof(pid_t));
			memcpy(c_addr + sizeof(pid_t)+1,¤tsong,4);

			/*使用wait阻塞子进程,直到孙子进程播放完才能被唤醒;
			  当被唤醒时,表示播放MP3期间没有按键按下,则继续顺序播放下一首MP3*/
			
			if(fd == wait(NULL))
			{
				currentsong = currentsong->next;
				printf("THE NEXT SONG IS %s\n",currentsong->songname);
			}
		}
	}
}

/*************************************************
Function name: creat_song_list
Parameter    : void
Description	 : 创建歌曲名的双向循环链表
Return		 : struct song *
Argument     : void
**************************************************/
struct song *creat_song_list(void)
{	
	FILE *fd;
	size_t size;
	size_t len;
	char *line = NULL;
	struct song *head;
	struct song *p1;
	struct song *p2;
	system("ls /sdcard/song >song_list");
	fd = fopen("song_list","r");

	p1 = (struct song *)malloc(sizeof(struct song));

	printf("==================================song list=====================================\n");
	system("ls /sdcard/song");	
	printf("\n");
	printf("================================================================================\n");
	size = getline(&line,&len,fd);

	strncpy(p1->songname,line,strlen(line));
	head = p1;
	while((size = getline(&line,&len,fd)) != -1) //从文件中读取一行,直到出错或者到文件尾EOF返回-1
	{	
		p2 = p1;
		p1 = (struct song *)malloc(sizeof(struct song));
		strncpy(p1->songname,line,strlen(line));
		p2->next = p1;
		p1->prev = p2;	
	}
	//此时到了文件尾,若是循环列表,则下一首为head,即第一首 
	p1->next = head;
	head->prev = p1;
	p1 = NULL;
	p2 = NULL;
	system("rm -rf song_list");
	return head;
}
/*************************************************
Function name: startplay
Parameter    : pid_t *,struct song *
Description	 : 开始播放函数
Return		 : void
Argument     : void
**************************************************/
void startplay(pid_t *childpid,struct song *my_song)
{
	pid_t pid;
	int ret;
	/*创建子进程*/
	pid = fork();

	if(pid > 0) //父进程
	{
		*childpid = pid; //子进程PID初始化
		play_flag = 1;
		sleep(1);
		/*读取共享内存保存的pid,初始化孙子进程的pid*/
		memcpy(&gradchild,p_addr,sizeof(pid_t));
		
	}
	else if(0 == pid) //子进程
	{	
		/*子进程播放MP3函数*/
		play(my_song);
	}
}
/*************************************************
Function name: my_pause
Parameter    : pid_t
Description	 : 暂停函数
Return		 : void
Argument     : void
**************************************************/
void my_pause(pid_t pid)
{
	printf("=======================PAUSE!PRESS K1 TO CONTINUE===================\n");
	kill(pid,SIGSTOP); //对孙子进程发送SIGSTOP信号
	play_flag = 0;
}

/*************************************************
Function name: my_pause
Parameter    : pid_t
Description	 : 停止播放函数
Return		 : void
Argument     : void
Autor & date : Hanson  11,04, 05
**************************************************/
void my_stop(pid_t g_pid)
{

	printf("=======================STOP!PRESS K1 TO START PLAY===================\n");
	kill(g_pid,SIGKILL); //对孙子进程发送SIGKILL信号
	kill(pid,SIGKILL);   //对子进程发送SIGKILL信号
	first_key=1;

}

/*************************************************
Function name: conti_play
Parameter    : pid_t
Description	 : 继续函数
Return		 : void
Argument     : void
**************************************************/
void conti_play(pid_t pid)
{
	printf("===============================CONTINUE=============================\n");
	kill(pid,SIGCONT); //对孙子进程发送SIGCONT信号
	play_flag=1;
}

/*************************************************
Function name: next
Parameter    : pid_t
Description	 : 下一首函数
Return		 : void
Argument     : void
**************************************************/
void next(pid_t next_pid)
{
	struct song *nextsong;

	printf("===============================NEXT MP3=============================\n");
	/*从共享内存获得孙子进程播放歌曲的节点指针*/
	memcpy(&nextsong,p_addr + sizeof(pid_t)+1,4);
	/*指向下首歌曲的节点*/
	nextsong = nextsong->next;
	/*杀死当前歌曲播放的子进程,孙子进程*/
	kill(pid,SIGKILL);
	kill(next_pid,SIGKILL);
	wait(NULL);
	startplay(&pid,nextsong);
}

/*************************************************
Function name: prev
Parameter    : pid_t
Description	 : 上一首函数
Return		 : void
Argument     : void
**************************************************/
void prev(pid_t prev_pid)
{
	struct song *prevsong;
	/*从共享内存获得孙子进程播放歌曲的节点指针*/
	printf("===============================PRIOR MP3=============================\n");
	memcpy(&prevsong,p_addr + sizeof(pid_t)+1,4);
	/*指向上首歌曲的节点*/
	prevsong = prevsong->prev;
	/*杀死当前歌曲播放的子进程,孙子进程*/
	kill(pid,SIGKILL);
	kill(prev_pid,SIGKILL);
	wait(NULL);
	startplay(&pid,prevsong);
}

/*************************************************
Function name: main
Parameter    : void
Description	 : 主函数
Return		 : int
Argument     : void
**************************************************/
int main(void)
{
//	int buttons_fd;
    int mp3_control_pipe;
	int mp3_control_pipe_write;
//	int key_value;
    int playnum;
//  char playnum;
	struct song *head;
	/*打开设备文件*/
//	buttons_fd = open("/dev/key", 0);
//	if (buttons_fd < 0) {
//		perror("open device buttons");
//		exit(1);
//	}

   /*创建命名管道mp3-control*/
	unlink("/tmp/mp3_control");
	mkfifo("/tmp/mp3_control",0666);
	
	mp3_control_pipe=open("/tmp/mp3_control",O_RDONLY|O_NONBLOCK);
	mp3_control_pipe_write=open("/tmp/mp3_control",O_WRONLY|O_NONBLOCK);


	if(mp3_control_pipe<0)
	{
       perror("open control pipe for read");  
	    }

	printf("**********基于mini2440的网页控制MP3播放器*********** \n");
	printf("********************XXX制作 ************************ \n");
	printf("**************嵌入式开发项目************************ \n");
	
  /*创建播放列表*/
	head = creat_song_list();
	
	printf("===================================FUNTION======================================\n\n");
	printf("        K1:播放、暂停     K2:停止播放   K3:下一歌曲      K4:上一歌曲  \n\n");
	printf("================================================================================\n");


  /*共享内存:用于存放子进程ID,播放列表位置*/
	if((shmid = shmget(IPC_PRIVATE,5,PERM))== -1)
		exit(1);
	p_addr = shmat(shmid,0,0);
	memset(p_addr,'\0',1024);
	
	
	while(1) 
	{
		fd_set rds;/*先声明一个 fd_set 集合来保存我们要检测的 buttons_fd句柄*/
		int ret1;

		FD_ZERO(&rds);/*用select函数之前将rds清零*/
		FD_SET(mp3_control_pipe, &rds);/*将检测到的句柄放到集合rds里*/

		/*监听获取键值*/
		//struct timeval timeout = {30,0};
		ret1 = select(mp3_control_pipe + 1, &rds, NULL, NULL,NULL);
		//printf ("%s %d %d\n", __FUNCTION__, __LINE__, ret1);
		if (ret1 < 0) 
		{
			perror("select");
			exit(1);
		}
		if (ret1 == 0) 
			printf("Timeout.\n");
		else if (FD_ISSET(mp3_control_pipe, &rds))
		{
            static char buffer[10];
			int ret = read(mp3_control_pipe, &buffer, 2);
//			printf ("%s %d %d\n", __FUNCTION__, __LINE__, ret);
			if (ret != 2) 
			{
				if (errno != EAGAIN) {
					perror("read buttons\n");
					continue;
				}
			} 
			else
			{   
			    int playnum1;
			    if(sscanf(buffer,"%d",&playnum1)==1)
			    	{
                     playnum=playnum1;
			    	}
				memset(buffer,0,sizeof(buffer));

				printf("buttons_value: %d\n", playnum);
				
				/*首次播放,必须是按键1*/
				if(first_key){
					switch(playnum)
					{	
					case 0:
						startplay(&pid,head);
						first_key=0;
						break;
					case 1:
					case 2:
					case 3:
						printf("=======================PRESS K1 TO START PLAY===================\n");
						break;
				    default:
						printf("=======================PRESS K1 TO START PLAY===================\n");
						break;
					} //end switch
				}//end if(first_key)
				/*若不是首次播放,则根据不同键值处理*/
				else if(!first_key){
				    switch(playnum)
					{
					case 0:
						//printf("play_flag:%d\n",play_flag);
						if(play_flag)
							my_pause(gradchild);
						else
							conti_play(gradchild);
						break;
					case 1:
						my_stop(gradchild);
						break;
					case 2:
						next(gradchild);
						break;
					case 3:
						prev(gradchild);
						break;
					} //end switch
			 }//end if(!first_key)

			}
				
		}
	}
	close(mp3_control_pipe);
	return 0;
}

2.mp3_control.cgi代码,用shell写的

#!/bin/sh
 
type=0
 
 
case $QUERY_STRING in
	*play*)
		type=0
		;;
	*stop*)
		type=1
		;;
	*priv*)
		type=2
		;;
	*next*)
		type=3
		;;
esac
 
 
 
/bin/echo $type > /tmp/mp3_control
 
echo "Content-type: text/html; charset=utf-8"
echo
/bin/cat mp3-result.template
 
exit 0

3.web端的html界面,此处代码几乎原创,不过还是丑,没用框架,我的审美也不怎么样,😿




网页控制MP3

MP3 Player

选择播放

  播放/暂停

停止播放

上一歌曲

下一歌曲


参考

1.https://blog.csdn.net/zqixiao_09/article/details/50285931
2.https://blog.csdn.net/sdkdlwk/article/details/80744472
3.http://blog.chinaunix.net/uid-20539088-id-115750.html
点赞
  1. 隔壁老傅说道:

    超级无敌宇宙中最华丽界面,要吗 :huaji:

发表评论

电子邮件地址不会被公开。必填项已用 * 标注