目录
[一. 环境搭建](#一. 环境搭建)
[1. 播放器插件](#1. 播放器插件)
[2. 图形化工具](#2. 图形化工具)
截止到目前(信号)Linux相关学习暂告一段,后续会根据学习计划调整进行短期的MCU相关学习。实战可以检测系统编程相关知识的掌握程度,本节基于MP3经典案例论述。
一. 环境搭建
1. 播放器插件
做项目之前先把环境调好,首先使用以下命令需要在ubuntu系统上安装mpg123播放器插件

2. 图形化工具
LVGL是一个开源的嵌入式图形库,用于创建图形界面应用程序。它提供了丰富的图形元素和用户界面控件,能够在各种嵌入式平台上运行,包括单片机和微处理器。
Gui和LVGL是两个相关的概念。GUI是指图形用户界面,是用户与计算机进行交互的界面。LVGL是一个开源的GUI库,可以用于创建嵌入式系统中的图形用户界面。
lvgl提供了一套丰富的图形绘制和交互功能,可以在嵌入式设备上创建漂亮、交互式的用户界面。它支持多种显示控制器和输入设备,并具有高度可定制性。因此,Gui和LVGL的关系是,LVGL是一种用于创建Gui的库或工具。使用LVGL,开发人员可以更容易地实现各种功能的图形界面,从而提升用户体验。
安装所需库的环境:sudo apt-get install libsdl2-dev;注意:出现暂时不能解析域名 ,检查Ubuntu是否联网
安装GUI guider:sudo dpkg -i Gui-Guider-Setup-1.2.1-GA.deb
安装时有错误发生(依赖不存在) :使用该指令:sudo apt-get install -f
当你运行sudo apt-get install -f时,apt-get会自动检查并安装缺失的依赖包,以确保系统中的软件包及其依赖关系完整和一致,再次安装,安装好之后 从应用程序中搜索gui。

二、代码实现
我们就设计最简单的MP3项目即可,能够实现上一曲、播放/暂停、下一曲以及歌单显示和功能菜单的显示。可以利用父子进程来同步播放歌曲和显示功能菜单歌单,子进程用来播放歌曲,父进程来显示。(注意提前准备几首歌曲)
- 使用fork函数创建子进程
- 使用glob函数将目录下的歌曲遍历出来(歌曲都以.mp3为后缀)
- 使用execlp函数向终端发送指令播放歌曲
- 播放暂停时需要父进程给子进程发送18 19信号
- 通过17信号检测歌曲是手动切换还是播放完之后进行切换
- 切歌的核心是杀死当前子进程,创建新的子进程
- 自定义按键实现切歌和播放暂停功能
c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <glob.h>
int i = 0;
glob_t gl;
int main()
{
chdir("mp3file");
glob("*.mp3", 0, NULL, &gl);
pid_t pid = fork();
if (pid > 0)
{
while (1)
{
int wstatus;
pid_t antopid = waitpid(pid, &wstatus, WNOHANG);
if (antopid == pid)
{
printf("检测到子进程退出!antopid=%d, pid=%d\n", antopid, pid); // 调试打印
if (WIFEXITED(wstatus))
{
i++;
if (i == gl.gl_pathc)
{
i = 0;
}
pid = fork();
if (pid == 0)
{
execlp("mpg123", "mpg123", gl.gl_pathv[i], NULL);
}
}
}
printf("\t\t歌\t单\t\n");
for (int j = 0; j < gl.gl_pathc; j++)
{
printf("\t%s\t\n", gl.gl_pathv[j]);
}
printf("\n===== 功能菜单 =====\n");
printf("1: 暂停播放\n");
printf("2: 继续播放\n");
printf("4: 上一曲\n");
printf("6: 下一曲\n");
printf("0:退出程序\n");
printf("\n");
int x = 0;
scanf("%d", &x);
switch (x)
{
case 1:
kill(pid, 19);
break; //停止播放
case 2:
kill(pid, 18);
break; //继续播放
case 4: //切换上一首
i--;
if (i < 0)
{
i = gl.gl_pathc - 1;
}
kill(pid, 9);
pid = fork();
if (pid == 0)
{
execlp("mpg123", "mpg123", gl.gl_pathv[i], NULL);
}
break;
case 6: //切换下一首
i++;
if (i == gl.gl_pathc)
{
i = 0;
}
kill(pid, 9);
pid = fork();
if (pid == 0)
{
execlp("mpg123", "mpg123", gl.gl_pathv[i], NULL);
}
break;
case 0:
kill(pid, 9);
waitpid(pid, NULL, 0);
printf("程序已退出!\n");
return 0;
}
}
}
else if (pid == 0)
{
execlp("mpg123", "mpg123", gl.gl_pathv[i], NULL);
}
return 0;
}
以上整段代码可以实现MP3的基本功能,注意目录以及歌曲文件提前准备,


可以看到歌单,功能菜单正常显示并且歌曲正常播放这里播放的时候会显示很多字符串,可以通过调整execlp的参数进行隐藏,加入-q即可隐藏字符串。
c
execlp("mpg123", "mpg123","-q" gl.gl_pathv[i], NULL);
三、界面设计
打开Gui软件,为工程文件命名MP3_Project,尺寸自定义为1280*720便于后续加背景图,点击CREATE。

进入页面,点击这里把语言切换成中文。

点击图片,将准备添加的背景图模组拖拽到雪花屏里

这里调节参数,将坐标默认设置为0,0,尺寸调成我们设置好的1280*720铺满背景板,然后添加背景图片

提前准备一张图片,这里索引到位置选中(有绿色框提示)点击确定即可。

接下来添加按钮模组,MP3按钮有播放/暂停、上一曲、下一曲,可以提前去网上找一些按钮图片备好使用。

这里切歌的时候按钮图片是不用切换的,因此切歌时的四个图片都设一张图片即可

播放/暂停会有图片变化反馈,我们默认歌曲是播放状态,按下按钮不变释放后的图片改为暂停的图片即可。

设置好按钮之后这里来添加事件,触发方式选中按下触发,动作C code,我们后续通过C语言代码进行实现,这里先做好标记将整个框架生成,具体的功能后续添加,播放/暂停和下一曲作同样处理。

点击生成代码或者运行模拟器都可以,此时我们的界面框架代码就完成了。

去预先设置好的目录下找到工程文件夹打开,这里有很多文件,我们将代码写到custom.c里面,这个custom.c文件可以认为就是平常我们工程文件的main.c文件,可以把之前的代码进行移植实现界面控制歌曲的功能。(这里使用vs打开工程可能会报错不用管大概率是编译器的问题代码无错)
由于通过按钮控制歌曲的切换以及播放,所以这里可以直接把父进程里的控制相关语句全部删除
c
#include <stdio.h>
#include "lvgl/lvgl.h"
#include "custom.h"
#include <unistd.h>
#include <glob.h>
#include <wait.h>
#include <stdlib.h>
void fun(int sig);
void clean();
int i = 0;
pid_t pid;
glob_t gl;
void custom_init(lv_ui *ui)
{
signal(17,fun);
atexit(clean); //点击"X"即可结束进程
glob("*.mp3", 0, NULL, &gl);
pid = fork();
if (pid == 0)
{
printf("正在播放%s\n", gl.gl_pathv[i]);
execlp("mpg123", "mpg123", gl.gl_pathv[i], NULL);
}
}
void fun(int sig)
{
int wstatus;
int res = waitpid(pid, &wstatus, WNOHANG);
if (res == pid)
{
if (WIFEXITED(wstatus))
{
i++;
if (i == gl.gl_pathc)
{
i = 0;
}
}
pid = fork();
if (pid == 0)
{
printf("正在播放%s\n", gl.gl_pathv[i]);
execlp("mpg123", "mpg123", gl.gl_pathv[i], NULL);
}
}
}
void clean()
{
signal(17,SIG_IGN);
kill(pid,9);
system("stty echo");
}
接下来我们直接在工程里分步实现各个功能,由于之前我们添加切歌按钮的时候留下了标识符可以直接搜索。

这一页代码往下就是播放以及下一曲的写代码地方,我们一步一步移植;注意原代码尽量别动,我们只管添加自己的代码即可。这里切歌代码没什么问题,主要是播放/暂停的时候需要定义一个变量来记录点击次数,奇数为播放偶数为暂停。
c
#include "events_init.h"
#include <stdio.h>
#include "lvgl/lvgl.h"
#include <glob.h>
#include <signal.h>
#include <sys/types.h>
extern int i;
extern glob_t gl;
extern pid_t pid;
int button = 0;
void events_init(lv_ui *ui)
{
}
static void screen_imgbtn_1event_handler(lv_obj_t * obj, lv_event_t event)
{
switch (event)
{
case LV_EVENT_RELEASED:
{
i--;
if(i<0)
{
i=gl.gl_pathc-1;
}
kill(pid,9);
}
break;
default:
break;
}
}
static void screen_imgbtn_2event_handler(lv_obj_t * obj, lv_event_t event)
{
switch (event)
{
case LV_EVENT_PRESSED:
{
if(button)
{
button = 0;
kill(pid,18);
}
else
{
button = 1;
kill(pid,19);
}
}
break;
default:
break;
}
}
static void screen_imgbtn_3event_handler(lv_obj_t * obj, lv_event_t event)
{
switch (event)
{
case LV_EVENT_PRESSED:
{
i++;
if(i<0)
{
i=0;
}
kill(pid,9);
}
break;
default:
break;
}
}
void events_init_screen(lv_ui *ui)
{
lv_obj_set_event_cb(ui->screen_imgbtn_1, screen_imgbtn_1event_handler);
lv_obj_set_event_cb(ui->screen_imgbtn_2, screen_imgbtn_2event_handler);
lv_obj_set_event_cb(ui->screen_imgbtn_3, screen_imgbtn_3event_handler);
}
通过以上步骤便可实现MP3项目~~...~~