数码相册——设置时间间隔界面的显存管理、页面规划、输入控制

  • 硬件平台:韦东山嵌入式Linxu开发板(S3C2440.v3)
  • 软件平台:运行于VMware Workstation 12 Player下UbuntuLTS16.04_x64 系统
  • 参考资料:《嵌入式Linux应用开发手册》、《嵌入式Linux应用开发手册第2版》
  • 开发环境:Linux 3.4.2内核、arm-linux-gcc 4.3.2工具链
  • 源码仓库:https://gitee.com/d_1254436976/Embedded-Linux-Phase-3

目录

  • 数码相册——设置时间间隔界面的显存管理、页面规划、输入控制
    • 一、前言
    • 二、interval_page页面的实现
      • 1、关键的结构体
      • 2、显存管理
        • 2.1 思想
        • 2.2 具体的实现
      • 3、页面规划与显示
        • 3.1 思想
        • 3.2 具体的实现
      • 4、触摸输入的设置
        • 4.1 思想
        • 4.2 具体实现
      • 5、页面的切换
    • 三、编译与运行
      • 1、编译
      • 2、运行

一、前言

在【1.10 数码相册—main_page主界面的显存管理、页面规划、输入控制】中,实现了main_page主界面的显示与按键控制,在这篇博文中,实现setting_page设置界面的测试,实现如下目的:

  1. 有效的管理内存,实现页面的快速刷新
  2. 合理的规划页面空间,达到较好的显示效果
  3. 有效的输入区域实现触控的效果

二、interval_page页面的实现

由于需要程序改动的地方过多,所以这篇博文会比较侧重于描述思想与方法

1、关键的结构体

/* 描述图片信息 */
typedef struct PixelDatas{int bpp;                   //图片的bppint width;                 //图片的宽度int height;                 //图片的高度int linebytes;              //图片每行占据的字节数int TotalBytes;                //图片总大小unsigned char *PixelDatas;   //存储图片具体像素数据的首地址
}T_PixelDatas, *PT_PixelDatas;/* 页面内存数据是否被使用 */
typedef enum {VMS_FREE = 0,               //空闲VMS_USED_FOR_PREPARE,     //子线程占用VMS_USED_FOR_CURMAIN,      //当前主线程占用
}E_VideoMemState;/* 页面内存数据是否已准备好 */
typedef enum {PS_BLANK = 0,               //数据空白PS_GENERATING,              //数据生成中PS_GENERATED,              //数据完毕
}E_PicState;/* 图标布局信息 */
typedef struct Layout {char *strIconName;            //图标的名字int TopLeftX;               //图标左上角x坐标int TopLeftY;                //图标左上角y坐标int BotRightX;               //图标右下角x坐标int BotRightY;               //图标右下角x坐标
}T_Layout, *PT_Layout;/* 页面图标信息 */
typedef struct PageLayout {int bpp;                  //显示页面的bppint MaxTotalBytes;           //最大图标的大小PT_Layout ptLayout;          //指向描述该页面图标数组的指针
}T_PageLayout, *PT_PageLayout;/* 描述页面内存块 */
typedef struct VideoMem {int id;                         //页面内存的idint isDevFB;                   //当前描述的内存是否为Framebuffer:1-是,2-否E_VideoMemState eVideoMemState; //描述该页面内存数据的使用状态E_PicState ePicState;         //描述该页面内存数据是否已准备好T_PixelDatas tPixelDatas;        //描述图片信息struct VideoMem *ptNext;       //指向下一结点的指针
}T_VideoMem, *PT_VideoMem;/* 描述页面行为 */
typedef struct PageAction {char *name;int (*Run)(void);int (*GetInputEvent)(PT_PageLayout ptPageLayout, PT_InputEvent ptInputEvent);int (*Prepare)(void);struct PageAction *ptNext;
}T_PageAction, *PT_PageAction;

2、显存管理

2.1 思想

对于页面的显示LCD与Framebuffer之间通过LCD控制器来进行像素信息的传递,当我们需要显示图片时,只需要在Framebuffer中写入颜色信息就可以在LCD上显示出来。
应用程序过大,会导致显示的很慢,此时需要优化措施

  1. 在内存中开辟一个与Framebuffer大小相同的内存空间
  2. 提前把需要显示的内容写到新开辟的内存空间中;(3、页面规划与显示中介绍)
  3. 需要显示时,直接把新开辟的内存空间memcpyFramebuffer。(3、页面规划与显示中介绍)

2.2 具体的实现

  • 步骤描述:
/*---------------------------显存管理----------------------------------------*/
页面管理内存 = 页面描述信息 + 显存int AllocVideoMem(int num)函数:1. 获得LCD显示设备的分辨率以及bpp,计算得到需要开辟内存空间的大小(单位:byte)以及行宽(单位:byte)2. 先把设备本身的Framebuffer放入链表2.1 开辟一个大小为sizeof(T_VideoMem)内存,用来存放页面描述信息,地址放在ptNew结构体指针中2.2 根据1中获取的信息设置该内存的描述信息,以及把Framebuffer的地址放入结构体变量中2.3 强制设置其eVideoMemState状态位为占用状态2.4 采用头插法插入链表3. 后根据num分配用于页面管理的内存并设置结构体3.1 开辟一个大小为sizeof(T_VideoMem) + VMSize内存,用来存放页面描述信息与显存,地址放在ptNew结构体指针中3.2 根据1获得的信息,设置该内存的描述信息与显存的地址3.3 采用头插法插入链表
  • 代码实现:
int AllocVideoMem(int num)
{int i;int bpp;int linebytes;int xres, yres;int VMSize;PT_VideoMem ptNew;PT_VideoMem ptTmp;bpp = yres = xres = 0;GetDispResolution(&xres, &yres, &bpp);VMSize    = xres * yres * bpp / 8;linebytes = xres * bpp / 8;/* 1、先把设备本身的Framebuffer放入链表 */ptNew = (PT_VideoMem)malloc(sizeof(T_VideoMem));if (ptNew == NULL) {DebugPrint(APP_ERR"Framebuffer set fail!\n");goto fail;}/* 设置该内存所描述页面的信息 */ptNew->id                       = 0;ptNew->isDevFB                  = 1;ptNew->eVideoMemState         = VMS_FREE;ptNew->ePicState              = PS_BLANK;ptNew->tPixelDatas.bpp        = bpp;ptNew->tPixelDatas.height     = yres;ptNew->tPixelDatas.width      = xres;ptNew->tPixelDatas.linebytes  = linebytes;       ptNew->tPixelDatas.PixelDatas = s_ptDefaultDispOpr->pDispMem;ptNew->tPixelDatas.TotalBytes = VMSize;/* 强制设置设备本身的Framebuffer状态为被占用 */if (num != 0)ptNew->eVideoMemState = VMS_USED_FOR_CURMAIN;/* 头插法插入链表 */ptNew->ptNext = s_ptVieoMemHead;s_ptVieoMemHead = ptNew;/* 2、后分配用于页面管理的内存并设置结构体,组成:页面描述 + 显存 */for (i = 0; i < num; i++) {ptNew = (PT_VideoMem)malloc(sizeof(T_VideoMem) + VMSize);if (ptNew == NULL) {DebugPrint(APP_ERR"ptNew malloc fail,already malloc num: %d!\n", i);goto fail;}/* 设置该内存所描述页面的信息 */ptNew->id                    = 0;ptNew->isDevFB                  = 0;ptNew->eVideoMemState         = VMS_FREE;ptNew->ePicState              = PS_BLANK;ptNew->tPixelDatas.bpp        = bpp;ptNew->tPixelDatas.height     = yres;ptNew->tPixelDatas.width      = xres;ptNew->tPixelDatas.linebytes  = linebytes;       ptNew->tPixelDatas.PixelDatas = (unsigned char *)(ptNew + 1);ptNew->tPixelDatas.TotalBytes = VMSize;/* 头插法插入链表 */ptNew->ptNext = s_ptVieoMemHead;s_ptVieoMemHead = ptNew;}return 0;fail:while (s_ptVieoMemHead) {ptTmp = s_ptVieoMemHead->ptNext;free(s_ptVieoMemHead);s_ptVieoMemHead = ptTmp;}return -1;
}

3、页面规划与显示

3.1 思想

  • 页面规划:
    为了适应多种尺寸的LCD设备以及良好的视觉感受,所以图标的显示如下,尺寸计算可以适当:(不通用于其他页面,每个页面都需要制定不同的方案
    注意:由于该页面的特殊性:需要在图标中的指定区域进行数字变换长按实现快速加减,所以会使用Freetype库的字符编码和timval结构体记录时间
  • 显示:
    对于多个页面的显示与切换,需要大致做到以下步骤:
    1、获得该页面的显存
    2、描画该页面内存的显示数据
    3、刷新到显示设备的Framebuffer

3.2 具体的实现

  • 步骤描述:
1.1 获得显存1.2 描画数据需要判断该页面的图标坐标信息是否已经计算完毕:若是则表示数据已经准备好,则直接进行步骤1.3否则进行计算各图标坐标信息如下措施:1.2.2 获得LCD显示设备的分辨率以及bpp1.2.3 根据事先约定的LCD上的页面规划,进行图标的尺寸计算1.2.4 绘制图标中的数字1.2.5 清空屏幕1.2.6 根据TotalBytes的数据来malloc分配存储LCD显示的图片数据的内存,把内存的首地址存储到页面内存的PixelDatas1.2.7 描绘该页面的需要显示的图标源bmp信息   1.2.7.1 调用GetPixelDatasForIcon(),获取每个图标的bmp像素信息1.2.7.2 调用PicZoom(),根据1.2.3中获得的LCD显示的图标信息与1.2.7.1获得的源bmp文件信息,采用近邻取样插值,对LCD显示的图标的像素信息进设置1.2.7.3 调用PicMerge(),根据1.2.7.2获得的LCD显示的图标信息,把数据整合到1.2.6中的页面内存的PixelDatas所指向的内存中1.2.7.4 调用FreePixelDatasForIcon(),释放1.2.7.1中所开辟的内存移动到下一个图标的在LCD中显示的位置1.2.8 释放1.2.6中所开辟的内存,并更新该页面的ePicState页面数据状态位为数据完毕位1.3 刷新设备并解放显存
  • 改进
    1、在结构体内部定义另一结构体指针方便管理该页面图标的信息
    2、对于每个页面需要使用到的公共部分定义为函数
    3、做了信息的判断防止每个页面界面进行切换时重复计算该页面图标的坐标
/* 定义结构体数组存储该页面的每个图标 */
static T_Layout s_tIntervalPageIconLayout[] = {{"inc.bmp",    0, 0, 0, 0},{"time.bmp",   0, 0, 0, 0},{"dec.bmp",    0, 0, 0, 0},{"ok.bmp",     0, 0, 0, 0},{"cancel.bmp", 0, 0, 0, 0},{NULL,          0, 0, 0, 0},  //结尾标志
};/* 定义一个新的结构体存储,成员变量指向该页面所有图标信息的结构体地址 */
static T_PageLayout s_tIntervalPageLayout = {.MaxTotalBytes = 0,.ptLayout       = s_tIntervalPageIconLayout,
};
  • 函数的实现
static void ShowIntervalPage(PT_PageLayout ptPageLayout)
{int error;PT_VideoMem  ptVideoMem;PT_Layout    ptLayout;ptLayout = ptPageLayout->ptLayout;/* 获得显存 */ptVideoMem = GetVideoMem(ID("Interval"), 1);if (ptVideoMem == NULL) {DebugPrint(APP_ERR"Can not get video mem for Interval_page!\n");return ;}/* 描画数据 *//* 如果还没有计算过各图标的坐标 */if (ptLayout[0].TopLeftX == 0)CalcIntervalPageLayout(ptPageLayout);/* 绘制图标中的数字 */error = GenerateIntervalPageSpecialIcon(s_IntervalSecond, ptVideoMem);if (error)DBG_PRINTF(APP_ERR"GenerateIntervalPageSpecialIcon error!\n");/* 页面数据的描绘 */error = GeneratePage(ptPageLayout, ptVideoMem);   /* 刷新/加载到设备 */FlushVideoMemToDev(ptVideoMem);/* 设置页面内存为空闲状态 */PutVideoMem(ptVideoMem);
}

4、触摸输入的设置

4.1 思想

对于页面中需要显示的图标,每个图标都有对应的有效触摸区域。当用户触摸到对应的区域时,进入不同的页面(未实现,下篇博客实现)且该图标的颜色变化
由于这里使用到输入系统,即【1.7 数码相册—电子书(5)—多线程支持多输入】中介绍的,会使用到触摸屏子线程与tslib库

4.2 具体实现

  • 步骤描述
2.1 IntervalPageGetInputEvent(),获得该页面的输入事件与对应图标的索引,由于调用到之前的输入事件子线程,所以在这里程序会处于休眠,有输入事件才唤醒2.2 若输入事件得到的压力值为0(松开) 且 pressured==1曾经按下,则调用ReleaseButton(),根据indexPressured,把图标颜色取反2.2.1 根据按键的索引进入不同的页面2.2.1.1 indexPressured == 0,inc按钮,对应计数值加1,把该数值显示在LCD对应图标上2.2.1.2 indexPressured == 2,dec按钮,对应计数值减1,把该数值显示在LCD对应图标上2.2.1.3 indexPressured == 3,ok按钮2.2.1.4 indexPressured == 4,cancel按钮2.2.1.5 其他则不操作2.3 若输入事件得到的压力值为1(按下) 且 已经获得了按键的index索引 且 pressured==0未按下,则表示按键按下未松开2.3.1 记录该事件的时间值到tInputEventPrePress.timeval结构体中,设置标志位并图标颜色取反2.3.2 比较tInputEventPrePress与tInputEvent两次事件中的timeval结构体的时间差,小于50ms则判断为长按,数值持续加或减并不断刷新该数值显示在LCD对应图标上
  • 代码实现
    while (1) {index = IntervalPageGetInputEvent(&s_tIntervalPageLayout, &tInputEvent);/* 松开和按下不在同一个图标范围内 */if (tInputEvent.pressure == 0) {/* 曾经有按键按下 */if (pressured) {ReleaseButton(&s_tIntervalPageIconLayout[indexPressured]);pressured = 0;/* 松开与按下的按键为同一个 */if (indexPressured == index) {switch (indexPressured) {case 0: /* inc按钮 */intervalSecond++;if (intervalSecond == 60)intervalSecond = 0;/* 在指定区域显示数字 */GenerateIntervalPageSpecialIcon(intervalSecond, ptDevVideoMem);break;case 2: /* dec按钮 */intervalSecond--;if (intervalSecond == -1)intervalSecond = 59;/* 在指定区域显示数字 */GenerateIntervalPageSpecialIcon(intervalSecond, ptDevVideoMem);break;case 3: /* ok按钮 */return 0;break;case 4: /* cancel按钮 */return 0;break;default:break;}}indexPressured = -1;}} else {/* 松开和按下都在同一个图标范围内 *//* 按下 */if (index != -1) {/* 之前未按下 */if (!pressured && index != -1) {pressured = 1;indexPressured = index;tInputEventPrePress = tInputEvent;  /* 记录下来 */PressButton(&s_tIntervalPageIconLayout[indexPressured]);}/* 如果按下的是"inc.bmp"或"dec.bmp" * 连按2秒后, 飞快的递增或减小: 每50ms变化一次*/if ((indexPressured == 0) || (indexPressured == 2)) {if (fast && (TimeMSBetween(tInputEventPrePress.time, tInputEvent.time) > 50)){intervalSecond = indexPressured ? (intervalSecond - 1) : (intervalSecond + 1);if (intervalSecond == 60)intervalSecond = 0;else if (intervalSecond == -1)intervalSecond = 59;/* 在指定区域显示数字 */GenerateIntervalPageSpecialIcon(intervalSecond, ptDevVideoMem);tInputEventPrePress = tInputEvent;}if (TimeMSBetween(tInputEventPrePress.time, tInputEvent.time) > 2000) {fast = 1;tInputEventPrePress = tInputEvent;}}}}}

5、页面的切换

main_page主页面、setting_page设置页面与interval_page设置时间间隔界面的切换,三者通过在对应页面的GetInputEvent()函数获取当前按下图标的索引值,根据索引值执行Page("xxx")->Run();进入到不同页面

  • main_page主页面:
    switch (indexPressured) {case 2:        /* setting_page *//* 设置页面 */Page("setting")->Run();/* 返回主页面 */ShowMainPage(&s_tMainPageLayout);break;default:break;}
  • setting_page设置页面:
    switch (indexPressured) {case 1: /* interval按钮 *//* 设置时间间隔页面 */Page("interval")->Run();/* 返回设置页面 */ShowSettingPage(&s_tSettingPageLayout);break;case 2:     /* 返回按键 */   return 0;default:break;}
  • interval_page设置时间间隔界面:
    switch (indexPressured) {case 0: /* inc按钮 */intervalSecond++;if (intervalSecond == 60)intervalSecond = 0;/* 在指定区域显示数字 */GenerateIntervalPageSpecialIcon(intervalSecond, ptDevVideoMem);break;case 2: /* dec按钮 */intervalSecond--;if (intervalSecond == -1)intervalSecond = 59;/* 在指定区域显示数字 */GenerateIntervalPageSpecialIcon(intervalSecond, ptDevVideoMem);break;case 3: /* ok按钮 */   return 0;break;case 4: /* cancel按钮 */  return 0;break;   default:break;}

三、编译与运行

1、编译

执行make生成可执行文件digitpic文件

2、运行

  1. 可执行文件digitpic放入到根文件系统
  2. icon目录下的图标文件放入到根文件系统的/etc/digitpic/icons(程序打开图标文件时需要用到)
  3. 执行./digitpic MSTH.TTF,得到以下的效果:
    1、程序一开始进入main_page主界面按下与松开3个图标的位置,图标的颜色会反转

    按下设置按钮时,进入setting_main设置界

    按下返回按键后可以返回main_page主页面

    2、在设置页面的时候按下与松开3个图标的位置,图标的颜色会反转
    当按下设置时间间隔按钮时,进入interval_main设置界面点击⬆或⬇按钮实现数字加减长按实现快速加减

第三阶段应用层——1.12 数码相册—interval_page设置时间间隔界面的显存管理、页面规划、输入控制相关推荐

  1. 第三阶段应用层——2.6 视频监控—CMOS摄像头的硬件原理

    视频监控-CMOS摄像头的硬件原理 硬件平台:韦东山嵌入式Linxu开发板(S3C2440.v3) 软件平台:运行于VMware Workstation 12 Player下UbuntuLTS16.0 ...

  2. 第三阶段应用层——2.7 视频监控—从零写CMOS摄像头驱动

    视频监控-从零写CMOS摄像头驱动 硬件平台:韦东山嵌入式Linxu开发板(S3C2440.v3),OV7740摄像头 软件平台:运行于VMware Workstation 12 Player下Ubu ...

  3. 第三阶段应用层——2.4 视频监控—从0写USB摄像头驱动(1)-描述符的分析与打印

    视频监控-从0写USB摄像头驱动(1)-描述符的分析与打印 硬件平台:韦东山嵌入式Linxu开发板(S3C2440.v3) 软件平台:运行于VMware Workstation 12 Player下U ...

  4. 中国石油大学(北京)-《计算机网络应用基础》第三阶段在线作业

    第三阶段在线作业 单选题 (共20道题) 收起 1.(2.5分) 以下关于VPN说法正确的是 A.VPN指的是用户自己租用线路,和公共网络物理上完全隔离的.安全的线路 B.VPN指的是用户通过公用网络 ...

  5. 中国石油大学《计算机应用基础#》第三阶段在线作业

    第三阶段在线作业 单选题 (共20道题) 收起 1.(2.5分) PowerPoint演示文稿的作者必须非常注意幻灯片集的两个要素是(). A.内容和设计 B.内容和模板 C.内容和视觉效果 D.问题 ...

  6. java 高并发第三阶段实战_JAVA多线程编程实战视频-第三阶段(共80节)

    高并发编程第三阶段01讲 AtomicInteger多线程下测试讲解 高并发编程第三阶段02讲 AtomicInteger API详解,以及CAS算法详细介绍 高并发编程第三阶段03讲 利用CAS构造 ...

  7. Linux运维 第三阶段 (二) DHCP

    Linux运维 第三阶段 (二) DHCP服务 dhcp(dynamic host configuration protocol) 前期bootp(无盘工作站)-->dhcp(引入租约lease ...

  8. java 高并发第三阶段实战_Java 高并发第三阶段实战---Java并发包深入解析与使用详解...

    第三阶段的课程主要围绕着Java并发包的使用,展开详细的介绍,主要内容有1.原子包源码剖析,2.并发包工具类详细介绍,3.线程服务以及Future和callable等详细介绍,4.高并发容器和阻塞容器 ...

  9. WEB_面试题_第三阶段

    第三阶段面试题 一.JavaScript高级 1. 判断以下程序的输出结果: var age=100; function test(){ this.age=50; return function(){ ...

  10. Linux运维 第三阶段 (十八) varnish

    Linux运维 第三阶段 (十八) varnish 数据: 结构化数据,RDBMS: 非结构化数据,FS,存海量小文件,NAS.SAN.DFS可提供较好的性能: web cache: 程序具有局部性( ...

最新文章

  1. Swagger增强神器:Knife4j!用它轻松实现接口搜索、Word下载、接口过滤...
  2. secureCrt个人操作手册
  3. Java黑皮书课后题第8章:*8.27(列排序)用下面的方法实现一个二维数组中的列排序。返回新数组,且原数组保持不变。编写一个测试程序,提示用户输入一个3*3的double型矩阵,显示一个排好的矩阵
  4. Java黑皮书课后题第1章:1.1(显示三条消息)编写程序,显示Welcome to Java、Welcome to Computer Science和Programming is fun
  5. UVA - 1606 Amphiphilic Carbon Molecules
  6. 2.15 更改所有者和所属组chown
  7. SAP应用followup transaction的错误讨论
  8. mybatis 使用in 查询时报错_MyBatis(四):mybatis中使用in查询时的注意事项
  9. JEECG ——11月份版本即将发布功能点
  10. 2021年最值得推荐的七款可视化工具,人人都能学会使用
  11. 从氨基酸到大分子(蛋白质、核酸)
  12. CORE网络数据包接收传递过程分析
  13. AWS宣布计划在加拿大开设第二个区域
  14. Git 版本控制系统的安装与使用
  15. 计算机配件出口单证,出口制单
  16. mac 10.11 brew php71,MAC OS X 10.11.4下载-OS X 10.11正式版下载 V10.11.6-PC6苹果网
  17. 计算机硬技术与基础在线测试,计算机硬件技术基础网上作业及答案
  18. R语言计算logistic回归C指数,最详细的基于R语言的Logistic Regression(Logistic回归)源码,包括拟合优度,Recall,Precision的计算...
  19. ​当我谈我的自行车时,我谈些什么
  20. 程序出现错误如何解决

热门文章

  1. Java十二平均律判断
  2. 涉及到各种场景-英语小记-最爱的一篇
  3. 计算机桌面壁纸在哪个文件夹,系统桌面背景在哪个文件夹
  4. Vue中如何引用富文本?富文本又是啥?
  5. html flv jquery 插件,基于js与flash实现的网站flv视频播放插件代码
  6. 轻型机械臂模块关节机械设计方案
  7. 利用 cookie,实现在html页面 记住我 功能
  8. 搭建Hadoop VM集群
  9. 1. 如何创建python环境
  10. 如果让你来制作一个访问量很高的大型网站,你会如何来管理所有CSS文件、JS与图片?