基于粤嵌GEC6818开发板的Linux电子相册开发实战

基于粤嵌GEC6818开发板的Linux电子相册开发实战 1. 项目背景与开发板介绍第一次拿到粤嵌GEC6818开发板时我对着这块巴掌大的板子研究了半天。这块开发板虽然体积小但功能相当强大搭载了四核Cortex-A53处理器主频能达到1.3GHz完全能满足电子相册这种嵌入式应用的需求。开发板标配的7寸电容触摸屏分辨率是800×480显示效果在这个尺寸下已经相当不错了。选择这款开发板有几个原因首先是性价比高作为教学和入门开发都很合适其次是资料齐全粤嵌提供了完整的技术文档和示例代码最重要的是它运行的是Linux系统这对我们这些习惯Linux开发的程序员来说太友好了。我记得当时在淘宝上看到的价格是300多块钱比树莓派便宜不少但性能完全够用。2. 开发环境搭建在开始编码前得先把开发环境搭好。我用的是一台Ubuntu 20.04的笔记本作为开发主机。首先安装交叉编译工具链sudo apt-get install gcc-arm-linux-gnueabihf这个工具链会把我们的代码编译成能在ARM架构上运行的程序。然后需要配置NFS共享这样在开发板上就能直接访问主机上的文件省去了反复烧录的麻烦。在/etc/exports文件里添加/home/yourname/workspace *(rw,sync,no_root_squash,no_subtree_check)接着重启NFS服务sudo service nfs-kernel-server restart开发板那边也需要配置修改/etc/fstab添加挂载点192.168.1.100:/home/yourname/workspace /mnt nfs defaults 0 0这样每次开发板启动就会自动挂载我们的工作目录。我还建议安装minicom或者picocom作为串口终端工具调试的时候特别有用。3. 电子相册的核心功能实现3.1 图片显示原理电子相册最核心的功能当然是显示图片。GEC6818开发板的显示屏对应着/dev/fb0这个设备文件。显示图片的基本思路是先把图片数据读入内存然后通过内存映射的方式把数据写入framebuffer。这里用到了mmap系统调用它能把设备文件映射到进程地址空间。具体实现如下int lcd_fd open(/dev/fb0, O_RDWR); char *fbp (char *)mmap(NULL, 800*480*4, PROT_READ|PROT_WRITE, MAP_SHARED, lcd_fd, 0);800×480是屏幕分辨率4表示每个像素用4字节表示ARGB格式。映射成功后直接往fbp指向的内存写数据就能在屏幕上显示出来。3.2 BMP图片处理我选择支持BMP格式是因为它结构简单不需要额外的库就能解析。BMP文件头包含了很多信息最重要的是图片的宽度和高度。通过hexdump查看BMP文件结构hexdump -C test.bmp | head -n 5在代码中我们这样读取图片信息lseek(bmp_fd, 18, SEEK_SET); read(bmp_fd, width, 4); read(bmp_fd, height, 4);需要注意的是BMP的像素数据是从下往上存储的而framebuffer是从上往下所以显示时需要做垂直翻转。另外BMP的像素顺序是BGR而framebuffer通常是ARGB需要进行转换。3.3 触摸屏交互开发板的触摸屏对应/dev/input/event0设备文件。触摸事件通过input子系统上报我们需要读取struct input_event结构体struct input_event { struct timeval time; __u16 type; __u16 code; __s32 value; };处理触摸事件的关键是识别点击和滑动。我设置了一个150像素的阈值来判断是否是有效滑动if((ts_x old_x) (ts_x - old_x 150)) { // 从左向右滑动 show_next_image(); }对于点击事件我划分了屏幕右侧200像素宽的区域作为控制区分别对应上一张、下一张、自动播放和退出功能。4. 多线程设计与优化4.1 线程分工为了让界面响应更流畅我使用了三个线程主线程负责初始化和触摸事件监听显示线程处理图片切换和显示自动播放线程控制幻灯片自动播放使用pthread_create创建线程pthread_t display_thread; pthread_create(display_thread, NULL, display_func, NULL);4.2 线程同步多个线程访问共享资源时需要同步。我使用了互斥锁和条件变量pthread_mutex_t mutex PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond PTHREAD_COND_INITIALIZER;当触摸事件发生时主线程通过条件变量通知显示线程pthread_mutex_lock(mutex); flag 1; pthread_cond_signal(cond); pthread_mutex_unlock(mutex);显示线程则等待条件变量pthread_mutex_lock(mutex); while(flag 0) { pthread_cond_wait(cond, mutex); } // 处理事件 pthread_mutex_unlock(mutex);4.3 性能优化在实际测试中我发现频繁的内存分配和释放会影响性能。于是改用了内存池技术预先分配好所需内存#define BUF_SIZE (800*480*4) static char display_buf[BUF_SIZE];图片加载也做了优化提前把解码后的图片数据保存在内存中切换时直接拷贝避免了重复解码。5. 功能扩展与改进5.1 支持更多图片格式虽然BMP简单易用但文件体积太大。我后来增加了对JPEG的支持使用开源的libjpeg库进行解码。解码流程大致如下struct jpeg_decompress_struct cinfo; jpeg_create_decompress(cinfo); jpeg_stdio_src(cinfo, infile); jpeg_read_header(cinfo, TRUE); jpeg_start_decompress(cinfo);5.2 添加过渡动画简单的图片切换显得太生硬我实现了淡入淡出效果。原理是通过alpha混合在两帧之间插入过渡帧for(int i0; i256; i16) { blend_images(old_img, new_img, i); usleep(10000); }5.3 增加图片信息显示在照片底部添加了一个半透明的信息栏显示文件名和拍摄时间如果EXIF信息可用。这里用到了FreeType库来渲染文字FT_Init_FreeType(library); FT_New_Face(library, font_path, 0, face); FT_Set_Char_Size(face, 0, 16*64, 300, 300);6. 常见问题与调试技巧在开发过程中遇到了不少坑。比如刚开始触摸屏坐标不准确后来发现需要做坐标转换ts_x ts_x * 800 / 1024; ts_y ts_y * 480 / 600;还有一次图片显示出现花屏排查后发现是BMP文件头解析错误忘记考虑字节序问题。解决方法是在读取时加上__attribute__((packed))struct __attribute__((packed)) bmp_header { // 结构体定义 };调试时我经常用printf输出关键变量值后来改用syslog这样在系统日志里也能看到调试信息openlog(photo_frame, LOG_PID, LOG_USER); syslog(LOG_DEBUG, x%d, y%d, x, y);7. 项目部署与优化最后一步是把程序部署到开发板上。我制作了一个简单的启动脚本#!/bin/sh cd /mnt ./photo_frame 然后修改/etc/rc.local在启动时自动运行。为了节省资源我去掉了调试输出并优化了内存使用。实测下来程序运行时的内存占用不到20MB非常轻量。