GEC6818音乐播放器制作

news/2024/12/2 19:32:34/

环境

# linux操作系统
uname -a
Linux incipe-virtual-machine 5.4.0-31-generic #35-Ubuntu SMP Thu May 7 20:20:34 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux# 交叉编译器
arm-linux-gcc -v
Using built-in specs.
Target: arm-none-linux-gnueabi
Configured with: /opt/FriendlyARM/mini2440/build-toolschain/working/src/gcc-4.4.3/configure --build=i386-build_redhat-linux-gnu --host=i386-build_redhat-linux-gnu --target=arm-none-linux-gnueabi --prefix=/opt/FriendlyARM/toolschain/4.4.3 --with-sysroot=/opt/FriendlyARM/toolschain/4.4.3/arm-none-linux-gnueabi//sys-root --enable-languages=c,c++ --disable-multilib --with-arch=armv4t --with-cpu=arm920t --with-tune=arm920t --with-float=soft --with-pkgversion=ctng-1.6.1 --disable-sjlj-exceptions --enable-__cxa_atexit --with-gmp=/opt/FriendlyARM/toolschain/4.4.3 --with-mpfr=/opt/FriendlyARM/toolschain/4.4.3 --with-ppl=/opt/FriendlyARM/toolschain/4.4.3 --with-cloog=/opt/FriendlyARM/toolschain/4.4.3 --with-mpc=/opt/FriendlyARM/toolschain/4.4.3 --with-local-prefix=/opt/FriendlyARM/toolschain/4.4.3/arm-none-linux-gnueabi//sys-root --disable-nls --enable-threads=posix --enable-symvers=gnu --enable-c99 --enable-long-long --enable-target-optspace
Thread model: posix
gcc version 4.4.3 (ctng-1.6.1) 

开发板 GEC6818 ,详情介绍

思路

Ⅰ. 总体框架

image-20200928220250419

Ⅱ. 目录结构

.
├── include
│   ├── get_touch.h
│   ├── lcd.h
│   ├── play_music.h
│   ├── show_bmp.h
│   └── ts_init.h
├── Makefile
├── README.md
└── sources├── lcd.c├── main.c├── play_music.c├── show_bmp.c└── ts_init.c

Ⅲ. 流程图

image-20200928220610937

Ⅳ. 需要用的知识

  1. Linux系统API,可以参考此文,你会linux系统API吗?
  2. Makefile文件编写,可以参考此文,简简单单学会写makefile
  3. 交叉编译开发
  4. 如果没有安装 madplay ,还要源码编译安装 madplay ,这个后面有空补上。
  5. markdown使用语法,可以参考,Markdown: 语法

代码

接下来详细讲解每个模块的具体功能与作用。

Ⅰ. LCD模块

LCD是GEC6818的显示屏,要想展示图片就必须打开LCD显示屏。

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>// lcd 文件描述符
int fd = 0;
// 共享映射区首地址
unsigned int *plcd = NULL;/***  打开lcd屏幕和共享映射区* */
void lcd_init() {fd = open("/dev/fb0", O_RDWR);if (fd == -1) {perror("lcd open error: ");exit(-1);}// 打开共享映射区plcd = (unsigned int *)mmap(NULL, 800 * 480 * 4, PROT_READ | PROT_WRITE,MAP_SHARED, fd, 0);if (plcd == MAP_FAILED) {perror("mmap error: ");exit(-1);}
}/***  关闭lcd屏幕和映射区* */
void lcd_uninit() {// 关闭共享映射区munmap(plcd, 800 * 480 * 4);// 关闭文件描述符close(fd);
}

打开映射区的目的是为了加快显示图片的速度,直接使用 write 函数也是可以的。

Ⅱ. 显示图片模块

在这之前先简单介绍下 bmp 图片的存储格式。

BMP文件通常是不压缩的,通常比同一幅图像的压缩图像文件格式要大很多。可以参考百度百科 ,这里只介绍存储格式。

BMP文件组成

  1. BMP文件头,14字节,BMP文件的类型、文件大小和位图起始位置等信息。
  2. 位头信息,40字节。
  3. 颜色表,可用索引来表示图像。
  4. 位图数据,即图像数据。

文件格式

图片来源:粤嵌课设老师

例如,偏移量从 0x02 ~ 0x05 表示图片大小,0x12 ~ 0x15 表示图片宽, 0x16 ~ 0x19 表示图片高, 0x1c ~ 0x1d 表示图片的位深。

把这按照小端拼接起来就可以得到图片大小,宽高和位深信息。

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>extern int fd;
extern unsigned int* plcd;/***  绘制图片* */
void lcd_drawpoint(int w, int h, unsigned int color) {//(w,h)显示了color色*(plcd + w + h * 800) = color;
}/***  读取bmp图片数据*  从x0,y0处开始显示一张宽w高h的图片* */
void show_bmp(int x0, int y0, int w, int h, const char* bmp_file) {int bmp = 0;bmp = open(bmp_file, O_RDONLY);if (-1 == bmp) {perror("open bmp error");exit(-1);}//读取BMP和DIB数据int ret = 0;// BMP头和DIB数据unsigned char ch[64] = {0};ret = read(bmp, ch, 54);if (-1 == ret) {perror("read bmp error");exit(-1);} else if (0 == ret) {printf("no read data or file end\n");exit(-1);}// 3.处理数据int file_size = 0;int width = 0, hight = 0, pix_bit = 0;unsigned int color = 0;// rgba位图unsigned char a, r, g, b;//存储图像的位图数据(各个像素点颜色值分量)unsigned char pix[800 * 480 * 4] = {0};file_size = ch[2] | ch[3] << 8 | ch[4] << 16 | ch[5] << 24;width = ch[0x12] | ch[0x13] << 8 | ch[0x14] << 16 | ch[0x15] << 24;hight = ch[0x16] | ch[0x17] << 8 | ch[0x18] << 16 | ch[0x19] << 24;pix_bit = ch[0x1c] | ch[0x1d] << 8;//读取位图数据read(bmp, pix, w * h * pix_bit / 8);int i = 0;for (int y = 0; y < h; y++) {for (int x = 0; x < w; x++) {b = pix[i++];g = pix[i++];r = pix[i++];a = (pix_bit == 32) ? pix[i++] : 0;color = a << 24 | r << 16 | g << 8 | b;lcd_drawpoint(x0 + x, y0 + ((h - 1) - y), color);}}close(bmp);
}

因为前54个字节,我们是不需要的,所以,文件要偏移54个字节,或者把这54个字节读取出来。

注:bmp 图片是没有透明度选项的,即 rgb 颜色标准。

Ⅲ. 打开触屏文件

打开触屏的主要目的是实现上一首下一首,播放暂停功能的实现。

#include <fcntl.h>
#include <linux/input.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>int ts = 0;
extern bool flag;/***  获取触摸的x,y坐标* */
void get_touch(int *x, int *y) {int ret;struct input_event ev;  //输入事件结构体变量,用来保存读取的输入事件// 1) 打开触摸屏文件ts = open("/dev/input/event0", O_RDWR);if (-1 == ts) {perror("open input error");exit(-1);}// 2) 读取触摸屏事件while (1) {if (flag) {break;}ret = read(ts, &ev, sizeof(ev));  //读取输入事件保存到结构体ev中if (ret == sizeof(ev)) {if (ev.type == EV_ABS && ev.code == ABS_X) {*x = ev.value * 0.8;  //此时的value是触摸点X轴的坐标}if (ev.type == EV_ABS && ev.code == ABS_Y) {*y = ev.value * 0.8;  //此时的value是触摸点Y轴的坐标}if (ev.type == EV_KEY && ev.code == BTN_TOUCH && ev.value == 0) {//手指从触摸屏 离开printf("(x = %d, y = %d)\n", *x, *y);break;}}}
}/***  关闭触屏板* */
void close_ts() { close(ts); }

Ⅳ. 实现音乐播放器功能

介绍下 madplay 的使用。

管理madplay的主程序,包括播放,暂停播放,恢复播放,停止播放
system("madplay 1.mp3 &"); // 利用system函数调用madplay播放器播放*.mp3音乐system("madplay 1.mp3 -r &"); // 循环播放:参数-rsystem("killall -9 madplay"); // 利用system函数调用killall命令将madplay终止掉 system("killall -STOP madplay &"); // 利用system函数调用killall命令将madplay暂停system("killall -CONT madplay &"); // 利用system函数调用killall命令恢复madplay的播放system("madplay 1.mp3 -a volume &");// 初始化播放音量,volume表示音量大小,范围是 -175 to +18 dB// 更多可以使用man命令查看
// man madplay

再介绍下信号:

kill -
-l  -- list signal names or numbers of specified signals
-n  -- specify signal number
-s  -- specify signal name
-ABRT    -BUS     -CONT    -HUP     -INT     -PIPE    -PROF    -QUIT    -STKFLT  -SYS     -TRAP    -TTIN    -URG     -USR2    -WINCH   -XFSZ                  
-ALRM    -CHLD    -FPE     -ILL     -KILL    -POLL    -PWR     -SEGV    -STOP    -TERM    -TSTP    -TTOU    -USR1    -VTALRM  -XCPU

代码:

#include <fcntl.h>
#include <linux/input.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>char music[7][6] = {"1.mp3", "2.mp3", "3.mp3", "4.mp3","5.mp3", "6.mp3", "7.mp3"};int order = 0;extern bool isFirst;
extern bool isPlay;
extern int vol;/***  播放音乐*  如果是第一次播放就开始播放*  如果不是,就继续播放* */
void play_music() {if (isFirst) {char command[100] = {0};sprintf(command, "madplay %s -a %d &", music[order], vol);printf("%s\n", command);system(command);} else {system("killall -CONT madplay &");}
}/***  暂停音乐* */
void stop_music() { system("killall -STOP madplay &"); }/***  下一首* */
void next_music() {system("killall -9 madplay");if (order == 6) {order = -1;}char command[100] = {0};sprintf(command, "madplay %s -a %d &", music[++order], vol);printf("%s\n", command);system(command);
}/***  上一首* */
void pre_music() {system("killall -9 madplay");if (order == 0) {order = 7;}char command[100] = {0};sprintf(command, "madplay %s -a %d &", music[--order], vol);printf("%s\n", command);system(command);
}

这里播放音乐有个逻辑,就是如果是第一次播放的话,就要开始播放音乐,如果不是的话,就要继续播放音乐。

另外,上一首下一首功能,要防止数组越界,更简单的直接取模也是可以的。

Ⅴ. 主函数逻辑功能

#include <fcntl.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>extern void lcd_init();
extern void lcd_uninit();
extern void get_touch(int *, int *);
extern void close_ts();
extern void show_bmp(int, int, int, int, const char *);
extern void play_music();
extern void stop_music();
extern void next_music();
extern void pre_music();bool isFirst = true;
bool isPlay = false;
bool flag = false;
int vol = 0;/***  处理信号函数* */
void my_handler(int sig) {// 最好不加printf("End of program, end of code: %d\n", sig);flag = true;
}int main(int argc, char *argv[]) {// argv[0] 文件名 argv[1] 音量大小if (argc != 2) {vol = 0;} else {switch (atoi(argv[1])) {case 0:vol = -175;break;case 1:vol = -15;break;case 2:vol = 0;break;case 3:vol = 10;break;}}signal(SIGINT, my_handler);const char *background_bmp = "./bmp/background.bmp";const char *next_bmp = "./bmp/next.bmp";const char *pre_bmp = "./bmp/pre.bmp";const char *pause_bmp = "./bmp/pause.bmp";const char *play_bmp = "./bmp/play.bmp";lcd_init();show_bmp(0, 0, 800, 480, background_bmp);show_bmp(44, 340, 100, 100, pre_bmp);show_bmp(375, 340, 100, 100, pause_bmp);show_bmp(639, 340, 100, 100, next_bmp);// 触屏得到的坐标int x = 0, y = 0;while (1) {if (flag) {// 不让程序自动处理ctrl + z/csystem("killall -9 madplay");show_bmp(375, 340, 100, 100, pause_bmp);flag = false;break;}get_touch(&x, &y);if (!flag) {if (375 < x && x < 475 && 340 < y && y < 440) {// 如果正在播放音乐,就停止播放音乐// 如果音乐没有播放,就开始播放音乐if (isPlay) {stop_music();show_bmp(375, 340, 100, 100, pause_bmp);isPlay = false;} else {play_music();show_bmp(375, 340, 100, 100, play_bmp);isPlay = true;isFirst = false;}}// 上一首音乐if (45 < x && x < 145 && 340 < y && y < 440) {pre_music();show_bmp(375, 340, 100, 100, play_bmp);isFirst = false;isPlay = true;}// 下一首音乐if (639 < x && x < 739 && 340 < y && y < 440) {next_music();show_bmp(375, 340, 100, 100, play_bmp);isFirst = false;isPlay = true;}}}close_ts();lcd_uninit();return 0;
}

主函数增加了一个 ctrl + c/z 信号处理,不想让程序帮我处理这个信号,我要自己处理,目的是为了解决直接使用 ctrl + c 结束程序,madplay 还在播放音乐的情况。

其次就是通过触摸屏得到的触摸点,进行相应的操作逻辑。

总结

代码经过编译,可以成功移植到 GEC1818 开发板上,具体操作,可见 README.md 文件。

源码地址,github

代码很大一部分是教课设的粤嵌老师造的轮子,自己的代码实际工作量不大。

不足之处

  1. 使用了较多的全局变量,这样会导致代码的耦合性降低,后期维护难度大;
  2. 功能还是有所欠缺;
  3. 代码还是有点冗余,不太精简。

http://www.ppmy.cn/news/410922.html

相关文章

debian 笔记本 准系统 RTL8822BE 8723 8192 8188 8168 8111 8107 8821-22 等PCIE 无线网卡蓝牙驱动安装

原装笔记本&#xff0c;迷你准系统 安装PCIE下列无线网卡蓝牙驱动 查看网卡 lspci|grep less sudo nano /etc/apt/sources.list (debian10)最后一行添加deb http://ftp.cn.debian.org/debian buster main non-freeee sudo apt-get update sudo apt-get install firmware-realt…

Spring常见问题

包含内容 单例bean线程是安全的吗&#xff1f; Spring框架中的bean是单例的吗&#xff1f; 是单例的 这个默认是单例的但是可以在Bean注解类文件使用Scope注解进行配置 singleton&#xff1a;bean在每个Spring IOC容器中只有一个实例prototype&#xff1a;一个bean的定义可以…

Adobe国际认证,PS软件向多图层图像中添加更多图像,官方教程

Adobe国际认证&#xff0c;Photoshop软件向多图层图像中添加更多图像&#xff0c;官方教程 Adobe国际认证&#xff0c;Photoshop软件向多图层图像中添加更多图像&#xff0c;官方教程 所学内容&#xff1a;向多图层图像中添加更多图像 选择“File”&#xff08;文件&#xff09…

《C和指针》读书笔记(第十一章 动态内存分配)

目录 0 简介1 为什么使用动态内存分配2 malloc和free3 calloc和realloc4 使用动态分配的内存5 常见的动态内存错误6 内存分配实例6.1 排序一列整型值6.2 复制字符串6.3 变体记录的创建与销毁 7 总结 0 简介 在实际开发中&#xff08;C语言&#xff09;&#xff0c;数组的元素存…

Adobe photoshop cc 2020,PS 2020问世啦!

千呼万唤始出来&#xff0c;犹抱琵琶半遮面&#xff01; Adobe官方依旧是很调皮&#xff0c;悄悄的将PS2020发布了&#xff01; PS2020究竟有啥功能&#xff1f;有啥特色&#xff1f;让我们一睹为快&#xff01; ps 2020 mac版新增功能 预设改进 我们对预设进行了重新设计。…

【MySQL入门】-- 数据库介绍

目录 1.为什么要使用数据库&#xff1f; 2.数据库相关概念 3.数据库与数据库管理系统的关系 4.常见数据库排名 5.常见数据库的简单介绍 6.MySQL介绍 7.MySQL版本的选择 8.关系型数据库和非关系型数据库 9.关系型数据库设计规则以及规范 10.表的关系&#xff08;一对…

计算机视觉算法——BEV Perception算法总结

计算机视觉算法——BEV Perception算法总结 计算机视觉算法——BEV Perception算法总结1. Homograph Based——3D LaneNet2. Depth Based——LSS3. MLP Based——PON4. Transformer Based——BEVFormer5. Transformer Based——Translating Image into Maps 计算机视觉算法——…

zkML零知识机器学习介绍

1. 引言 零知识证明技术的2大基石为&#xff1a; 1&#xff09;succinctness&#xff1a;相比于直接运行整个计算本身&#xff0c;验证该计算完整性证明要简单很多。2&#xff09;zero-knowledge&#xff1a;可在不泄露计算隐私的情况下&#xff0c;证明计算的完整性。 生成…