赞
踩
# 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 ,详情介绍

.
├── 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

接下来详细讲解每个模块的具体功能与作用。
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文件通常是不压缩的,通常比同一幅图像的压缩图像文件格式要大很多。可以参考百度百科 ,这里只介绍存储格式。

例如,偏移量从 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 &"); // 循环播放:参数-r
system("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/c system("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
代码很大一部分是教课设的粤嵌老师造的轮子,自己的代码实际工作量不大。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。