韦东山数码相框任务需求分析

2023-10-12 07:59

本文主要是介绍韦东山数码相框任务需求分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 前言
  • 需求界面
  • 抽象流程
    • 界面分解
    • Page 结构体
      • static void (*Display)();
      • static void (*GetInputEvent)();
    • 第一界面:主界面
    • 第二界面:选择界面
    • 第三界面:浏览界面
    • 第四界面:连播界面
    • 第五界面:设置界面
    • 第六界面:间隔界面
    • 其他过程抽象
      • 显示接口
      • 读取图片/图标
        • GetPicFmts(Icon 图片信息):
      • 输入接口
      • 调试输出接口
    • 优化接口
      • 显示内存管理
    • 后续想法
      • 阅读界面

前言

只是简单分析了下各个结构体的由来,意淫编程

整体框架参考了韦东山数码相框修正调整,另类介绍程序代码结构

需求界面

整个需求如下图
在这里插入图片描述

抽象流程

理解为是各个界面,通过不同的按钮相关切换,所以将界面抽象出来
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

总共分解成六个小界面,针对每个界面,这时可以想到的操作有:

  1. 显示界面内容 ==》显示数据准备
  2. 响应界面上的触摸事件 =》按键位置判断为哪个按钮

针对界面,则有管理问题,是数组,还是链表?

这里所能想到的对应结构体基本结构应为:

Page {char *name;           	        // 页面名字 void (*Display)();              // 页面的运行函数int (*GetInputEvent)();         // 获得输入数据的函数 Page *ptNext;                   // 链表管理
}

界面分解

每个界面又分为类似如下几个图标:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

针对这些图标,想到的可能属性有:

  1. 位置
  2. 使用哪里图片

对应结构体:

Icon{iTopLeftX                       // 左上角坐标 iTopLeftYiBotRightX                      // 右下角坐标 iBotRightYstrIconName                     // 图片位置
}

而图标数目这种明显跟界面强相关,需要保存在界面 Page 结构体中

Page {char *name;           	        // 页面名字 void (*Display)();              // 页面的运行函数int (*GetInputEvent)();         // 获得输入数据的函数 Page *ptNext;                   // 链表管理Icon[]                          // 所有包含的图标
}

再梳理流程:

Main -> Browser -> manual ||---> Auto||--->Setting --> interval

这个时候需要一个更高层次的来调用组织 Page,暂时叫 App 结构体吧,但是抽象了发现,切换到哪个界面跟只有界面自己知道,这个逻辑最简单,高内聚,每个程序管好自己内部就行了,切出去时自己指定切到谁。这里发现就需要在 Page 内部做逻辑切换。为此在 Page 内部增加一个 Run() 函数

Page {char *name;           	        // 页面名字 void (*Run)();                  // 页面运行函数	            int (*GetInputEvent)();         // 获得输入数据的函数 void (*Display)();              // 页面显示函数Page *ptNext;                   // 链表管理Icon[]                          // 所有包含的图标
}

梳理下 Run() 流程大致如下:

Run()Display();			// 显示主界面for(;;)GetInputEvent();				// 获取哪个按钮被点击switch()PageSelect("目的界面")->Run(); 

这里发现 Display() 跟 GetInputEvent() 似乎不会被其他模块调用,属于 Private 内部就好了

Page {char *name;           	        // 页面名字 void (*Run)();                  // 页面运行函数static int (*GetInputEvent)();         // 获得输入数据的函数 static void (*Display)();              // 页面显示函数Page *ptNext;                   // 链表管理Icon[]                          // 所有包含的图标
}

到这里,大的切换框架已经可以实现了,整体程序暂时框架为:

main()PageInit();											// 注册所有界面到链表 PageList 中管理PageSelect("Main")->Run();							// 选择主界面运行

剩下再细究先研究 Page 结构体对应函数功能, 然后再针对每个界面不同重载的特殊处理。

Page 结构体

Page {char *name;           	        // 页面名字 void (*Run)();                  // 页面运行函数static int (*GetInputEvent)();         // 获得输入数据的函数 static void (*Display)();              // 页面显示函数Page *ptNext;                   // 链表管理Icon[]                          // 所有包含的图标
}

static void (*Display)();

在这里插入图片描述
比如像这种怎么显示到界面上

Display() 流程:1. 获取一块内存2. 填充内存2.1 读取图片2.2 加载到显示内存指定位置3. 刷新到显示中

static void (*GetInputEvent)();

static int (*GetInputEvent)();         // 获得输入数据的函数 1. 获取报点2. 判断点是否有在某个图标位置内部,有返回数组下标

第一界面:主界面

在这里插入图片描述
都是些界面跳转,最简单的一页,流程不需要特别改

Page {char *name;           	            // 页面名字 void (*Run)();                      // 页面运行函数Display();			                // 显示主界面for(;;)GetInputEvent();				// 获取哪个按钮被点击switch()case: 浏览按钮PageSelect("选择界面")->Run(); case: 连播按钮PageSelect("连播界面")->Run(); case: 设置按钮PageSelect("设置界面")->Run(); static int (*GetInputEvent)();         // 获得输入数据的函数 1. 获取报点2. 判断点是否有在某个图标位置内部,有返回数组下标static void (*Display)();              // 页面显示函数1. 获取一块内存2. 填充内存2.1 读取图片2.2 加载到显示内存指定位置3. 刷新到显示中Page *ptNext;                       // 链表管理Icon[]                              // 所有包含的图标
}

第二界面:选择界面

在这里插入图片描述
涉及到目录切换与图标显示,而且实际图标只有四个,剩下 9 个图标是可变的

Page {char *name;           	            // 页面名字 void (*Run)();                      // 页面运行函数Display(“当前根目录”,1);			                // 显示主界面1 打开目录2 遍历目录,保存 文件名+是否文件, 目录名+是否目录到缓存中for(;;)GetInputEvent();				// 获取哪个按钮被点击判断是这 13 个按钮哪个被按到,是文件还是目录文件或目录的话,则保存名称及类型switch()case: 向上:当前目录缩短一段1 打开目录2 遍历目录,保存 文件名+是否文件, 目录名+是否目录到缓存中Display(“当前目录”,缓存[index 第几个 9]);case: 选择:目录:当前目录增加一段1 打开目录2 遍历目录,保存 文件名+是否文件, 目录名+是否目录到缓存中Display(“当前目录”,缓存[index 第几个 9]);文件:目前仅支持图片当前路径增加一段PageSelect("浏览界面")->Run("当前图片路径");  # Run() 需要增加参数case: 上一页:index++Display(“当前目录”,缓存[index 第几个 9]);case: 下一页 index--Display(“当前目录”,缓存[index 第几个 9]);static int (*GetInputEvent)();         // 获得输入数据的函数 1. 获取报点2. 判断点是否有在某个图标位置内部,有返回数组下标static void (*Display)("目录路径",缓存[index 第几个 9]);              // 页面显示函数1. 获取一块内存2. 填充内存2.1 根据 缓存[index 第几个 9] 更新下九个图标的名称2.1 读取图标2.2 刷新到显示中3. 刷新到显示中Page *ptNext;                       // 链表管理Icon[]                              // 所有包含的图标
}

第三界面:浏览界面

在这里插入图片描述
Page {
char *name; // 页面名字

    void (*Run)("当前图片路径");        // 页面运行函数Display();			                // 显示主界面1 当前图片路径2 遍历目录,保存 文件名 缓存中for(;;)GetInputEvent();				// 获取哪个按钮被点击switch()case: 返回退出当前 Run()case: 缩小缩放标志--Display();case: 放大缩放标志++Display();case: 上一张 更新当前图片路径为上一张图片Display();case: 下一张 更新当前图片路径为下一张图片Display();case: 连播 PageSelect("连播界面")->Run("当前图片路径");static int (*GetInputEvent)();         // 获得输入数据的函数 1. 获取报点2. 判断点是否有在某个图标位置内部,有返回数组下标static void (*Display)();              // 页面显示函数1. 获取一块内存2. 填充内存2.1 读取图标2.2 读取当前图片路径 + 缩放标志2.2 加载到显示内存指定位置3. 刷新到显示中Page *ptNext;                       // 链表管理Icon[]                              // 所有包含的图标
}

第四界面:连播界面

在这里插入图片描述
Page {
char *name; // 页面名字

    void (*Run)("当前图片路径");        // 页面运行函数Display();			                // 显示主界面1 当前图片路径2 遍历目录,保存 文件名 缓存中for(;;)GetInputEvent();				// 获取哪个按钮被点击switch()case: 返回退出当前 Run()延时指定时间间隔更新当前图片路径为下一张 Display()static int (*GetInputEvent)();         // 获得输入数据的函数 1. 获取报点2. 判断点是否有在某个图标位置内部,有返回数组下标static void (*Display)();              // 页面显示函数1. 获取一块内存2. 填充内存2.1 读取图标2.2 读取当前图片路径 + 缩放标志2.3 加载到显示内存指定位置3. 刷新到显示中Page *ptNext;                       // 链表管理Icon[]                              // 所有包含的图标
}

第五界面:设置界面

在这里插入图片描述
Page {
char *name; // 页面名字

    void (*Run)("当前图片路径");        // 页面运行函数Display();			                // 显示主界面当前图片路径 = 根目录for(;;)GetInputEvent();				// 获取哪个按钮被点击switch()case: 选择目录PageSelect("选择界面")->Run("当前图片路径");case: 设置间隔PageSelect("连播界面")->Run("当前图片路径");static int (*GetInputEvent)();         // 获得输入数据的函数 1. 获取报点2. 判断点是否有在某个图标位置内部,有返回数组下标static void (*Display)();              // 页面显示函数1. 获取一块内存2. 填充内存2.1 读取图标2.2 加载到显示内存指定位置3. 刷新到显示中Page *ptNext;                       // 链表管理Icon[]                              // 所有包含的图标
}

第六界面:间隔界面

在这里插入图片描述
这里发现需要在添加一个时间间隔全局变量

Page {char *name;           	            // 页面名字 void (*Run)("当前图片路径");        // 页面运行函数Display();			                // 显示主界面当前图片路径 = 根目录for(;;)GetInputEvent();				// 获取哪个按钮被点击switch()case: 增加时间间隔++Display()case: 减小时间间隔--Display()static int (*GetInputEvent)();         // 获得输入数据的函数 1. 获取报点2. 判断点是否有在某个图标位置内部,有返回数组下标static void (*Display)();              // 页面显示函数1. 获取一块内存2. 填充内存            2.1 根据 时间间隔 选择中间图标用哪张图2.2 读取图标2.3 加载到显示内存指定位置3. 刷新到显示中Page *ptNext;                       // 链表管理Icon[]                              // 所有包含的图标
}

到这里,感觉程序整体框架已经搭完了,下面思考下各个页面中使用到的模块抽象

其他过程抽象

显示接口

显示在哪里使用呢?在 Display() 流程有使用

Display() 流程:1. 获取一块内存2. 填充内存2.1 读取图片2.2 加载到显示内存指定位置3. 刷新到显示中

针对 Linux 的话,显示就是将显存映射为一块内存,然后往内存里面填东西就能显示

// 显示接口
Display{char *name;			// 显示接口名称void (*Init)();     // 显示接口初始化流程,比如打开,映射显示设备void (*Flush)(“包含显示的缓存”);    // 刷新显示     
}

这些都是根据上面流程想到的比较直接的接口定义

读取图片/图标

  1. 图片图标使用位置

     Display() 流程:1. 获取一块内存2. 填充内存2.1 读取图片2.2 加载到显示内存指定位置3. 刷新到显示中
    
  2. 图片,图标那肯定有不同的格式的,所以会需要不同的格式解析模块

  3. 不同图片格式那也是需要管理的,链表吧,就用 g_PicFmtsList

     PicFmt{char *name;								        // 图片解析模块名称,比如 Bmp, Pngvoid (*Read)(显示缓存,Icon 图片信息);			// 读取图片到显示缓存中指定位置ptNext		                                    // 下一个模块}
    

这里还有个问题,程序还需要判断这是什么图片类型后,才好调用具体的 PicFmts 格式处理的,所以 PicFmts 还需要有个判断本模块是否支持的功能

PicFmt{char *name;								        // 图片解析模块名称void (*Read)(显示缓存,Icon 图片信息);		    // 读取图片到显示缓存中指定位置1. 针对 Icon 所有图片,打开图片2. 解析图片内容放进显示缓存指定位置void (*isSupport)(Icon 图片信息)1. 打开图片2. 判断格式是否是本模块支持的ptNext		                                    // 下一个模块
}

这样针对每个传入的 Icon 图片,需要先遍历链表 g_PicFmtsList 通过 isSupport() 找到对应模块,再调用读入函数

GetPicFmts(Icon 图片信息):

需要先遍历链表 g_PicFmtsList 通过 isSupport() 找到对应模块,再调用读入函数

1. 遍历 g_PicFmtsList 链表,调用 PicFmt->isSupport() 判断是否有模块支持 
2. 返回支持的 PicFmt 结构体

所以 Display 流程会更新类似如下:

	Display() 流程:1. 获取一块内存2. 填充内存2.1 遍历当前页面 Icon[]2.2 GetPicFmts(Icon):获取对应处理格式模块2.3 PicFmt->Read(显示缓存,Icon ):读入显存中3. 刷新到显示中 	

输入接口

输入接口使用位置:

static int (*GetInputEvent)();         // 获得输入数据的函数 1. 获取报点2. 判断点是否有在某个图标位置内部,有返回数组下标

在第一步获取报点处使用,输入接口相对于六个界面是独立存在的,所以可以用个独立的循环线程存在

Input{char *name;                 // 输入模块名称void (*Init)()              // 输入设备初始化1. 打开设备,创建线程轮询等待事件上报2. 在线程中,有数据上报就唤醒 GetInputData() 上的睡眠进程void (*GetInputData)()      // 获取输入数据等待输入事件并上报
}

当然感觉输入设备不应该只有触摸屏,想以后也可以响应按键,响应网络,响应终端等输入设备,所以这个结构体还需要再改改
需要用链表管理

Input{char *name;                 // 输入模块名称void (*Init)()              // 输入设备初始化1. 打开设备,创建线程轮询等待事件上报2. 在线程中,有数据上报就唤醒 GetInputData() 上的睡眠进程void (*GetInputData)()      // 获取输入数据检查是否有事件上报,有则上报,无则睡眠ptNext                      // 下一个模块 
}

也需要修改界面的 GetInputEvent() 函数,以及 Run() 函数因为每个界面响应的按键方式可能不一定

static int (*GetInputEvent)();         // 获得输入数据的函数 1. 获取报点2. 判断点是否有在某个图标位置内部,有返回数组下标3. 获取按键void (*Run)("当前图片路径");        // 页面运行函数Display();			                // 显示主界面当前图片路径 = 根目录for(;;)GetInputEvent();				// 获取哪个按钮被点击switch()case: 选择目录PageSelect("选择界面")->Run("当前图片路径");case: 设置间隔PageSelect("连播界面")->Run("当前图片路径");################添加按键等响应处理

调试输出接口

调试输出接口嘛,就是支持各种 log 输出,可以从 文件输出、标准输出、网络输出、串口输出, 屏上输出等等

Debug{char *name;             // 输出名称void (*Init)()          // 调试模块初始化 1. 打开输出模块2. 创建线程,等待 DebugPrint() 函数输入,再转发输出模块输出void (*DebugPrint)(格式化字符串)    // 输出函数唤醒线程,让其通过特定模块输出 logptNext;                 // 下一个模块 
}

优化接口

显示内存管理

显示过程中发现还有获取一块内存的操作,像这种也可以使用缓冲池管理

Display() 流程:1. 获取一块内存2. 填充内存2.1 遍历当前页面 Icon[]2.2 GetPicFmts(Icon):获取对应处理格式模块2.3 PicFmt->Read(显示缓存,Icon ):读入显存中3. 刷新到显示中 		

可用如下结构体管理:

VideoMem{int count;void (*Init)(内存大小,内存块数)         // 建池void (*Get)()                            // 从缓冲池获取数据void (*Set)()                            // 释放到缓冲池中
}

后续想法

阅读界面

比如想在浏览界面,支持文本阅读,那要怎么实现呢?
阅读界面逻辑如下:

Page {char *name;           	            // 页面名字 void (*Run)("当前文件路径");        // 页面运行函数Display();			                // 显示主界面for(;;)GetInputEvent();				// 获取哪个按钮被点击switch()case: 上一页根据屏大小,及字体大小,更新上一页开始文件中位置Display()case: 下一页根据屏大小,及字体大小,更新下一页开始文件中位置Display()static int (*GetInputEvent)();         // 获得输入数据的函数 1. 获取报点2. 判断点位置,左半屏上翻,右半屏下翻static void (*Display)();              // 页面显示函数1. 获取一块内存2. 填充内存2.1 读取文件2.2 加载到显示内存指定位置3. 刷新到显示中Page *ptNext;                       // 链表管理Icon[]                              // 所有包含的图标
}

根据之前学习过程可知道

	怎样在 LCD 上显示文件:1. 根据文件获得字符编码 {ASCII, GBK【这一行是大陆用户默认的】UTF-8,UTF16LE,UTF16BE,}2. 根据编码从字体文件中得到 字体数据【包括点阵图】{ASCII字体数组,HZK16,GBK字体文件,freetype,}3. 把 点阵图 在 LCD 上显示出来

字库:主要是将不同编码的字符,转换成对应的点阵图,所以
一个字库可以支持多种编码方式

freetype: 支持 ASCII/GBK/UTF-8/UTF16LE/UTF16BE
HZK16: 支持 ASCII/GBK
ASCII: 支持 ASCII /UTF-16LE/UTF-16BE/UTF-8 			# ? 有这么多?参考程序提取

针对浏览界面读取 txt 场景,处理流程大致如下:

static void (*Display)();              // 页面显示函数1. 获取一块内存2. 填充内存2.1 读取文件,判断文本字符编码2.2 根据字符字符获取让对应模块处理,获取其点阵图2.3 将获取的点阵图显示在显示缓存合适位置3. 刷新到显示中

故可以针对上面的字符编码,字库进行抽象如下:

Encoding{char *name;                     // 编码名称 void (*isSupport)(文件路径)     // 是否是某字符文件1. 打开文件,读取到内存中2. 判断是否支持此种编码文件void (*DisplayTxt)(显示缓存,文件路径,文件内部位置,字体大小)1. 打开文件,2. 根据屏幕尺寸及字体大小,更新可从指定位置读取多少字符3. 将读入的字符通过支持此编码的字库的 GetBmpData() 获得位图4. 将获得的位图填充到显示缓存中ptNext                          // 链表管理 ptNextFont						// 会有个绑定支持字符操作,放到这个链表中
}Fonts{char *name;                     // 字库名称void (*Init)()                  // 字库初始化void (*GetBmpData)(字符,返回位图)        // 根据字符,返回对应的位图ptNext                          // 指向下一个字库}

更新显示场景逻辑:

static void (*Display)();              // 页面显示函数1. 获取一块内存2. 填充内存2.1 遍历编码表, 调用 isSupport() 判断是否有支持的2.2 对应编码的 DisplayTxt() 显示字符3. 刷新到显示中

这篇关于韦东山数码相框任务需求分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/194433

相关文章

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者

MOLE 2.5 分析分子通道和孔隙

软件介绍 生物大分子通道和孔隙在生物学中发挥着重要作用,例如在分子识别和酶底物特异性方面。 我们介绍了一种名为 MOLE 2.5 的高级软件工具,该工具旨在分析分子通道和孔隙。 与其他可用软件工具的基准测试表明,MOLE 2.5 相比更快、更强大、功能更丰富。作为一项新功能,MOLE 2.5 可以估算已识别通道的物理化学性质。 软件下载 https://pan.quark.cn/s/57

衡石分析平台使用手册-单机安装及启动

单机安装及启动​ 本文讲述如何在单机环境下进行 HENGSHI SENSE 安装的操作过程。 在安装前请确认网络环境,如果是隔离环境,无法连接互联网时,请先按照 离线环境安装依赖的指导进行依赖包的安装,然后按照本文的指导继续操作。如果网络环境可以连接互联网,请直接按照本文的指导进行安装。 准备工作​ 请参考安装环境文档准备安装环境。 配置用户与安装目录。 在操作前请检查您是否有 sud

线性因子模型 - 独立分量分析(ICA)篇

序言 线性因子模型是数据分析与机器学习中的一类重要模型,它们通过引入潜变量( latent variables \text{latent variables} latent variables)来更好地表征数据。其中,独立分量分析( ICA \text{ICA} ICA)作为线性因子模型的一种,以其独特的视角和广泛的应用领域而备受关注。 ICA \text{ICA} ICA旨在将观察到的复杂信号

【软考】希尔排序算法分析

目录 1. c代码2. 运行截图3. 运行解析 1. c代码 #include <stdio.h>#include <stdlib.h> void shellSort(int data[], int n){// 划分的数组,例如8个数则为[4, 2, 1]int *delta;int k;// i控制delta的轮次int i;// 临时变量,换值int temp;in

三相直流无刷电机(BLDC)控制算法实现:BLDC有感启动算法思路分析

一枚从事路径规划算法、运动控制算法、BLDC/FOC电机控制算法、工控、物联网工程师,爱吃土豆。如有需要技术交流或者需要方案帮助、需求:以下为联系方式—V 方案1:通过霍尔传感器IO中断触发换相 1.1 整体执行思路 霍尔传感器U、V、W三相通过IO+EXIT中断的方式进行霍尔传感器数据的读取。将IO口配置为上升沿+下降沿中断触发的方式。当霍尔传感器信号发生发生信号的变化就会触发中断在中断

kubelet组件的启动流程源码分析

概述 摘要: 本文将总结kubelet的作用以及原理,在有一定基础认识的前提下,通过阅读kubelet源码,对kubelet组件的启动流程进行分析。 正文 kubelet的作用 这里对kubelet的作用做一个简单总结。 节点管理 节点的注册 节点状态更新 容器管理(pod生命周期管理) 监听apiserver的容器事件 容器的创建、删除(CRI) 容器的网络的创建与删除

PostgreSQL核心功能特性与使用领域及场景分析

PostgreSQL有什么优点? 开源和免费 PostgreSQL是一个开源的数据库管理系统,可以免费使用和修改。这降低了企业的成本,并为开发者提供了一个活跃的社区和丰富的资源。 高度兼容 PostgreSQL支持多种操作系统(如Linux、Windows、macOS等)和编程语言(如C、C++、Java、Python、Ruby等),并提供了多种接口(如JDBC、ODBC、ADO.NET等