一、前言

数码相册项目是嵌入式系统课程的综合实验项目;总体来说,难度适中,是一个相对不错的练手项目。

其中涉及的知识点有:搭建交叉编译工具链,编译内核和加载驱动,需要掌握链表数据结构的使用,以及一些Linux系统编程相关的技能(IO操作,input子系统,多线程等)。

二、项目介绍

项目设计

文件下载:综合设计-数码相册-验收标准.pdf

工程结构:

    .---welcome.bmp  // 欢迎界面---login.bmp    // 密码登陆---picture      // 图片库---bgm.mp3      // 自动播放图片的音乐---frame        // 可执行文件---ko           // 相关驱动

数码相册设计:

    1. 程序运行,显示欢迎界面2. 点击后进入登陆界面,输入密码验证验证失败时,led闪烁;验证通过,进入电子相册3. 可自动播放相册和音乐,点击暂停/继续播放4. 暂停时可以划屏操作, 上滑 到第一张图,下滑 到最后一张同时也支持按键操作,暂停/播放/切换图片5. 多线程:检测按键和检测触摸屏事件线程,与处理播放线程

基础功能

下面我将会将各部分基础功能一一介绍,最后由读者自己对代码进行整合,编写数码相册项目的代码。

1)LCD显示一帧bmp图片

LCD的分辨率800x480,所以准备的图片的分辨率和格式要符合要求。

bmp文件头54字节,后面跟着的是rgb24格式的像素信息;而LCD对应的/dev/fb0(framebuffer),格式是bgra32,所以我们在绘制前需要转换像素信息。

还有一点要注意的是LCD需从底到顶进行绘制。

void play_one_frame(int fd, int w, int h) {char color_buf[w*h*4];char bmp_buf[w*h*3];int i, j; //定位到RGB数据的起始位置(偏移文件头部54个字节)lseek(fd, 54, SEEK_SET);  //读取所有的RGB数据read(fd, bmp_buf, sizeof(bmp_buf));// 画一次图像有点问题,强制画2次for (int n = 0; n < 2; n++) {//定位到显示的起始位置(偏移文件头部0个字节)lseek(fd_lcd, 0, SEEK_SET);//向LCD设备写入要显示的数据for(i=0,j=0; i<w*h*4; i+=4,j+=3) {color_buf[i]   = bmp_buf[j];      //bcolor_buf[i+1]  = bmp_buf[j+1];       //g color_buf[i+2]     = bmp_buf[j+2];       //rcolor_buf[i+3]  = 0;               //a }for(i=h-1; i>=0; i--) // 从底到顶一行行绘制write(fd_lcd, &color_buf[w*i*4], w*4);}
}

有点小问题,应该将像素点转换的部分抽出去,只需转换一次。

2)触摸屏输入事件处理

输入事件的结构在input.h文件中定义

struct input_event {struct timeval time;     // 事件发生时间__u16 type;        // 事件的类型__u16 code;     // 事件代码__s32 value; // 事件的值
};

常见的事件类型有:

EV_KEY,键事件,键盘键,鼠标左右键等;

EV_REL,相对坐标,主要用于鼠标移动事件;

EV_ABS,绝对坐标,主要用于触摸屏移动事件.

value 事件的值,当事件类型代码是EV_KEY时,按键操作值为1,释放操作值为0。

触摸屏事件检测线程:

typedef struct {int x;int y;
} coord_t;typedef struct {coord_t press;      // 按下时的坐标coord_t release;    // 松开时的坐标
} slide_t;// 静态全局变量
static int touch_type = TOUCH_TYPE_NONE;void *touch_event_thread(void *p) {struct input_event buf;slide_t s;int start = 1;coord_t coord;int x_ready = 0, y_ready = 0;while(1){//读取触摸屏设备的数据read(fd_ts, &buf, sizeof(buf));//如果是绝对坐标值的事件类型if(buf.type == EV_ABS) {if(buf.code == ABS_X) {coord.x = buf.value;x_ready = 1;}if(buf.code == ABS_Y) {coord.y = buf.value;y_ready = 1;}          } else if(buf.type == EV_KEY && buf.code == BTN_TOUCH \&& buf.value == 0) { // 松开, 压力值为0pthread_mutex_lock(&mut);touch_type = get_touch_type(s);memset(&s, 0, sizeof(s));pthread_cond_broadcast(&cond);   // 广播touch_type变化pthread_mutex_unlock(&mut);start = 1;}if (x_ready && y_ready) {if (start) {  // 记录第一个坐标s.press.x = coord.x;s.press.y = coord.y;start = 0;} else {      // 更新松开前的坐标 s.release.x = coord.x;s.release.y = coord.y;}x_ready = 0; y_ready = 0;}}
}

事件分类:

点击事件 --》用于登陆界面时的密码验证,以及图片播放/暂停。

划动事件 --》用于切换浏览图片

int get_touch_type(slide_t s) {int res = TOUCH_TYPE_NONE;if (s.release.x == 0 && s.release.y == 0) {res = get_click_type(s.press); printf("touch pos:(%d, %d)\n", s.press.x, s.press.y);} else {res = get_slide_type(s);    printf("slide (%d, %d) --> (%d, %d)\n", s.press.x, s.press.y, s.release.x, s.release.y);}return res;
}

在密码验证时,我们需要根据具体的login.bmp图片和触摸位置,计算出输入的数字。

// 密码验证相关,省略0~9数字的定义
#define TOUCH_TYPE_C_DELETE   10
#define TOUCH_TYPE_C_ENTER    12
#define TOUCH_TYPE_C_OTHRE    13/* 参见:login.bmp* simulate keyboard:* number area [w:360,h:300]* start at (240,120), each uint[w:120,h:75]** 1 2 3* 4 5 6* 7 8 9* d 0 e    // 映射10 11 12*/
int get_click_type(coord_t c) {static coord_t start = {.x = 240, .y = 120,};static int width = 360, height = 300;int res = TOUCH_TYPE_C_OTHRE;if (c.x > start.x && c.x < start.x + width \&& c.y > start.y && c.y < start.y + height) {int dx = c.x - start.x, dy = c.y - start.y;int x_no = dx / 120 + (dx % 120 != 0);int y_no = dy / 75 + (dy % 75 != 0);res = x_no + (y_no-1)*3;}return res == 11 ? 0:res;
}

将login.bmp图片放到画图软件上,方便测量分辨率和规划数字区域

3)密码登陆线程(具体代码请自行补充)

#define PASSWD "012345"#define TOUCH_TYPE_NONE     -1
#define TOUCH_TYPE_CLICK    97      // 点击事件
#define TOUCH_TYPE_SLIDE    98      // 划动事件int touch_atrribute(int type) {if (type >= 0 && type <= 13 && type != 11) return TOUCH_TYPE_CLICK;else if (type >= 20 && type <= 23) return TOUCH_TYPE_SLIDE;else return TOUCH_TYPE_NONE;
}void *login_thread(void *p) {int fd = *(int *)p; // login picture int wake_up = 0, check_ok = 0;// 当前还是欢迎界面,需要点击事件唤醒,进入密码验证// 6位数字密码验证过程// ....
}

4)相册管理

① 定义带头指针和尾指针的双向链表

typedef int elem_t;
struct node {elem_t fd;struct node *prev;struct node *next;
};typedef struct {int size; struct node *head;struct node *tail;
} list_t;struct node *cur_frame;

② 链表操作

list_t *create_list(void) {list_t *me = malloc(sizeof(*me));me->head = NULL;me->tail = NULL;me->size = 0;return me;
}// 添加节点到链表尾
void list_add_node(list_t *L, elem_t e) {struct node *new = malloc(sizeof(*new));new->fd = e;new->prev = L->tail;new->next = NULL;if (L->head == NULL) {L->head = new;} else {L->tail->next = new;}L->tail = new;L->size++;
}void list_destroy(list_t *L) {struct node *cur = L->head, *save;while (cur != NULL) {close(cur->fd);save = cur->next;free(cur);cur = save;}free(L);
}

使用链表管理相册

list_t *analysis_imgpath(const char *dirpath) {DIR *dir = opendir(dirpath);if (dir == NULL) {printf("failed to open %s\n", dirpath);return NULL;}// 解析目录下的所有文件list_t *L = create_list();  // 创建链表struct dirent *ptr = NULL;while ( (ptr = readdir(dir)) != NULL) {// 跳过文件"."和".."if (strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0) {continue;} else {  // common file   char path[128];sprintf(path, "%s/%s", dirpath, ptr->d_name);int fd = open(path, O_RDONLY); if (fd < 0) {printf("open %s failed\n", path);list_destroy(L);return NULL;}list_add_node(L, fd);}}return L;
}

5)播放相册线程,根据触摸屏事件或按键事件进行处理。

  1. 可自动播放相册和音乐,点击暂停/继续播放
  2. 暂停时可以划屏操作, 上滑 到第一张图,下滑 到最后一张
    同时也支持按键操作,暂停/播放/切换图片
#define TOUCH_TYPE_S_LEFT    20
#define TOUCH_TYPE_S_RIGHT   21
#define TOUCH_TYPE_S_UP      22
#define TOUCH_TYPE_S_DOWN    23
#define PLAY_STOP       0
#define PLAY_AUTO       1
static int play_state = PLAY_STOP;int get_slide_type(slide_t s) {int res = TOUCH_TYPE_NONE;int allow_err = 32, bound = 192; if (abs(s.release.y - s.press.y) < allow_err || \abs(s.release.x - s.press.x) > bound) {if (s.release.x > s.press.x) { res = TOUCH_TYPE_S_RIGHT; } else {res = TOUCH_TYPE_S_LEFT; }} else if (abs(s.release.x - s.press.x) < allow_err || \abs(s.release.x - s.press.x) > bound) {if (s.release.y < s.press.y) {res = TOUCH_TYPE_S_UP; } else {res = TOUCH_TYPE_S_DOWN; }} return res;
}void *play_thread(void *p) {list_t *L = (list_t *)p;// 播放相册中的第一张图片while (1) {pthread_mutex_lock(&mut);// 暂停时需要等待事件,播放时不会阻塞while (play_state == PLAY_STOP && touch_type == TOUCH_TYPE_NONE)pthread_cond_wait(&cond, &mut);if (touch_atrribute(touch_type) == TOUCH_TYPE_CLICK) {// 点击事件,暂停/继续播放// 同时暂停/播放音乐} else if (play_state == PLAY_STOP) {// 暂停状态处理划屏事件switch (touch_type) { // ...case TOUCH_TYPE_S_DOWN:cur_frame = L->tail; break;  // 最后一张图片}play_one_frame(cur_frame->fd, WIDTH, HEIGHT);}touch_type = TOUCH_TYPE_NONE;pthread_mutex_unlock(&mut);if (play_state == PLAY_AUTO) {// 自动播放}}
}

开源代码地址:https://gitee.com/zhoujiabo/imx-apps/tree/master/geclab%E6%95%B0%E7%A0%81%E7%9B%B8%E5%86%8C/digital_frames

三、问题 & 优化

1. madplay是后台播放,如果在播放时ctrl+c退出,音乐会继续播放
参考思路:添加一个SIGINT的信号处理,在捕获到信号时调用killall命令关闭播放2. 密码验证时,通过串口反馈信息了解当前输入情况,不方便。
参考思路:a.添加数字边框特效,在点击触屏时,在对应数字区域显示白色边框,提示数字已按下b.添加一行输入信息框,直接将密码串在LCD上显示3. 相册管理,密码验证,事件检测,与事件处理都放到一个文件了,随着功能进一步添加,代码量增多,浏览和编写会变得不方便。
参考思路:解耦,进一步封装成库

有问题欢迎私信或留言讨论,如果觉得内容还可以的话麻烦点赞支持一下

数码相册项目(带密码登陆,代码已开源)相关推荐

  1. java编程石头剪刀布图片_石头、剪刀、布!10分钟带你打开深度学习大门,代码已开源...

    原标题:石头.剪刀.布!10分钟带你打开深度学习大门,代码已开源 沉沉 发自 宇宙中心 量子位 出品 | 公众号 QbitAI 深度学习技术的不断普及,越来越多的语言可以用来进行深度学习项目的开发,即 ...

  2. 国科大港中文提出带视觉语言验证和迭代推理的Visual Grounding框架,性能SOTA,代码已开源!(CVPR2022)...

    关注公众号,发现CV技术之美 本文分享 CVPR 2022 的一篇论文『Improving features Visual Grounding with Visual-Linguistic Verifi ...

  3. 实现自动带密码登陆远程机执行shell命令(linux)

    在centos下使用expect,实现带密码登陆远程机并执行shell命令. 第一步:安装expect sudo yum install expect 第二步:建立测试脚本 #!/usr/bin/ex ...

  4. 声称代码已开源却迟迟没更新,网友等了好几个月,最终一怒之下把作者挂网上...

    丰色 发自 凹非寺 量子位 报道 | 公众号 QbitAI "代码拖更"的经历,不知你遇到过没? 就是你看上了一篇论文或者项目,作者声称代码会开源或者已开源,但你左等右等,每天查查 ...

  5. VLM:Meta AI CMU提出任务无关视频语言模型视频理解预训练VLM,代码已开源!(ACL 2021)...

    关注公众号,发现CV技术之美 本文分享 ACL 2021 论文『VLM: Task-agnostic Video-Language Model Pre-training for Video Under ...

  6. Align and Prompt:SalesforceANU提出ALPRO,进行细粒度的视频文本对齐!代码已开源!...

    关注公众号,发现CV技术之美 本文分享论文『Align and Prompt: Video-and-Language Pre-training with Entity Prompts』,由 Sales ...

  7. McGill微软将卷积操作加入到ViT中,捕获更详细的局部信息!准确率达87.7%!代码已开源!...

    关注公众号,发现CV技术之美 本文分享论文CvT : Introducing Convolutions to Vision Transformers,由McGill&微软联合提出<CvT ...

  8. CVPR21小样本检测:蒸馏上下文助力小样本检测(代码已开源)

    计算机视觉研究院专栏 作者:Edison_G 目标检测现在的框架越来越多,我们"计算机视觉研究院"最近也分享了众多的目标检测框架!今天我们继续分享一个最新的检测框架--YOLOR. ...

  9. 声称代码已开源却迟迟没更新,网友等了好几个月,最终一怒之下把作者挂网上

    丰色发自凹非寺 "代码拖更"的经历,不知你遇到过没? 就是你看上了一篇论文或者项目,作者声称代码会开源或者已开源,但你左等右等,每天查查 GitHub,代码就是一直没发布-- re ...

  10. ECCV2022 | 人大提出轻量级基于注意力的特征融合机制,在多个公开数据集上有效!代码已开源!

    ECCV2022 | 人大提出轻量级基于注意力的特征融合机制,在多个公开数据集上有效!代码已开源! [写在前面] 本文在文本到视频检索的新背景下,作者重新探讨了特征融合这一古老的课题.与以往的研究只考 ...

最新文章

  1. elasticsearch 集群在线水平扩容收缩
  2. [转载自简书] ASPNetCore上传大文件碰到的一些问题总结
  3. 快速安装Docker图形化管理平台ShipYard
  4. 正则表达式python_Python正则表达式
  5. 高通Audio缩写(不断更新中...)
  6. LINUX搭建EtherPad环境
  7. 教师资格证考试攻略(高中信息技术)
  8. 信息系统项目管理师必背知识点(完整版)
  9. UVA 123 Searching Quickly
  10. 田金龙老师讲解杨式太极拳的起势
  11. arctan4怎么用计算机,计算器arctan怎么按
  12. 2022P气瓶充装考试试题及在线模拟考试
  13. DSN: Deep Subspace Clustering Networks
  14. Linux 单独编译驱动模块(ko文件)
  15. 薄盒 | 方文山原创数字潮玩《庞克猫史汀》系列正在展出中
  16. RabbitMQ 入门介绍
  17. 业精于勤,荒于嬉。行成于思,毁于随。
  18. 计算机考研难度档,计算机考研院校报考难度排行[文].pdf
  19. 卡迪夫大数据专业排名_数据科学与大数据技术专业哪家强?考取难度排名前100院校出炉!...
  20. 用计算机按出童话,电脑争功童话故事作文

热门文章

  1. ABBYY最新官方免费序列号激活码序列号密钥下载分享
  2. 浅谈form标签与table标签
  3. 保存的视频怎么去除水印
  4. 《众妙之门——用户体验设计的秘密》一第2章 设计“好脾气”的网页2.1 巴赫和他的十二平均律...
  5. 实战用Python+Pygame+Kivy(Buildozer)+Ubuntu开发安卓android手机端apk游戏及踩坑分享
  6. 上海法院驳回深圳唯冠停止销售苹果iPad禁令
  7. word选择性粘贴没有HTML选项,Word选择性粘贴如何使用?Word选择性粘贴的快捷键是什么?...
  8. 国外各国统计机构网址
  9. 你的代码会说话吗?(上)
  10. 天猫精灵服务器修改密码,天猫精灵怎么解绑 天猫精灵解绑账号方法