精华内容
下载资源
问答
  • 韦东山数码相框笔记

    2017-01-17 11:42:08
    韦东山数码相框笔记 韦东山数码相框笔记
  • 韦东山 数码相框之系统框架分析 拿到项目,先弄清需求 参考手机相册 可以想象我们的操作过程都需要用到哪些功能 上电,LCD显示一幅界面 根据配置文件,决定停留在当前界面还是自动切换显示下一幅图片 ...

    韦东山 数码相框之系统框架分析

    1. 拿到项目,先弄清需求

      • 参考手机相册

      • 可以想象我们的操作过程都需要用到哪些功能

        1. 上电,LCD显示一幅界面

        2. 根据配置文件,决定停留在当前界面还是自动切换显示下一幅图片

        3. 点击一下,出现菜单(对话框)

          • 手动显示

          • 自动显示

        4. 手动显示下,向左向右滑动切换显示不同图片

        5. 向上滑动 - 放大图片, 向下滑动 - 缩小图片

          • 或者可以两个手指操作放大缩小
        6. 左右移动非常快的时候,显示下下一幅图片

    2. 设计框架

      • 对软件各个性能了解
      • 设计文档,将项目、程序分成几部分
      • 各个部分的接口构造好
      • 我们应该具备设计框架的功力
      tslib
      no lib
      上报
      触摸屏
      封装
      按键
      ...
      输入进程
      时间
      类型
      数据
      点击
      移动
      位置
      方向/速度/幅度
      输入进程-封装事件

      注释:封装将触摸屏/按键等硬件操作封装成事件,选着不同硬件时只需修改封装的事件即可,提高可移植性

    socket
    输入进程
    显示进程
    进程数据传输
      graph TB
      
      ps3[显示进程]
      t1[接收socket]
      tm[主控]
      t2[当前显示]
      t3[左边图片]
      t4[右边图片]
      t5[上 放大]
      t6[下 缩小]
      
      ps3-->t1
      ps3-->t2
      ps3-->t3
      ps3-->t4
      ps3-->t5
      ps3-->t6
      ps3-->tm
      
      t2-->libjpeg
      t3-->|准备好左边图片| libjpeg
      t4-->|准备好右边图片| libjpeg
      t5-->|准备好放大的图片| libjpeg
      t6-->|准备好缩小的图片| libjpeg
      
      
      libjpeg-.mmap.-mem1[内存-当前]
      libjpeg-.-mem2[内存-左]
      libjpeg-.-mem3[内存-右]
      libjpeg-.-mem4[内存-放大]
      libjpeg-.-mem5[内存-缩小]
      
      mem1-->DMA
      mem2-->DMA
      mem3-->DMA
      mem4-->DMA
      mem5-->DMA
      
      tm-->ctr{要显示哪一幅图片?}
      ctr-->|当前| mem1
      ctr-->mem2
      ctr-->mem3
      ctr-->mem4
      ctr-->mem5
      
      DMA-->LCD
      
      subgraph 
      LCD[显存 LCD]
      end
    
    总结:
    
     1. 输入进程
        - 主控线程:得到上报的事件,用socket发出给显示进程
        - ts线程:使用tslib读触摸屏ts,封装事件,上报
        - 按键线程:读按键,封装按键事件,上报
     2. 显示进程
        - socket接收线程:接收socket数据
        - 放大线程:准备好当前图片的放大图片数据
        - 缩小线程:准备好当前图片的缩小数据
        - 上一幅线程:准备好上一幅图片
        - 下一幅线程:准备好下一幅图片
        - 当前图片线程:准备好当前图片
        - 主控线程:根据根据socket得到的事件,决定显示哪一幅
     3. 驱动程序
        - 触摸屏驱动
        - 按键驱动
        - LCD驱动
        - **分配5块内存、DMA操作、mmap等驱动**
    
      使用进程,为了保持输入和显示的独立;使用线程,为了保持不同线程间的独立并且可以共享一些资源
    
    1. 编写代码

    2. 测试功能

    两个方面
    专家
    系统
    对某个/某一类问题钻研的很深
    对系统的各个部分都有较深的理解,也称为系统架构师
    嵌入式工程师发展方向

    链接:韦东山_嵌入式Linux_第3期_Linux项目实战视频教程_免费试看版

    展开全文
  • 整体框架参考了韦东山数码相框修正调整,另类介绍程序代码结构 需求界面 整个需求如下图 抽象流程 理解为是各个界面,通过不同的按钮相关切换,所以将界面抽象出来 ‘ 总共分解成六个小...

    前言

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

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

    需求界面

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

    抽象流程

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

    在这里插入图片描述

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

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

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

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

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

    界面分解

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

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

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

    对应结构体:

    Icon{
        iTopLeftX                       // 左上角坐标 
        iTopLeftY
        iBotRightX                      // 右下角坐标 
        iBotRightY
        strIconName                     // 图片位置
    }
    

    而图标数目这种明显跟界面强相关,需要保存在界面 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, Png
       	void (*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)(格式化字符串)    // 输出函数
            唤醒线程,让其通过特定模块输出 log
        
        ptNext;                 // 下一个模块 
    }
    

    优化接口

    显示内存管理

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

    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://blog.csdn.net/tech_pro。若是侵权用于商业用途,请联系博主,否则将追究责任。 ...
    				转载地址:http://blog.csdn.net/tech_pro。若是侵权用于商业用途,请联系博主,否则将追究责任。					https://blog.csdn.net/TECH_PRO/article/details/73437735				</div>
    							            <link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-f57960eb32.css">
    					<div class="htmledit_views" id="content_views">
    

    一、freetype简介

    FreeType库是一个完全免费(开源)的、高质量的且可移植的字体引擎,它提供统一的接口来访问多种字体格式文件,可以非常方便我们开发字体显示相关的程序功能。它支持单色位图、反走样位图的渲染。FreeType库是高度模块化的程序库,虽然它是使用ANSI C开发,但是采用面向对象的思想,因此,FreeType的用户可以灵活地对它进行裁剪。关于freetype的详细信息可以参考freetype的官方网站:https://www.freetype.org/来获取更多相关的信息。

    二、基本开发环境

    PC机:Ubuntu9.10

    交叉工具版本 :gcc version 4.3.2

    开发板:JZ2440

    linux内核版本:Linux-3.4.10

    freetype版本:Freetype-2.4.10

    要想使用freetype矢量字体库来开发,必须先要下载这个矢量字体库,可以从官网:https://www.freetype.org/下载,也可以从我上传的资料点击这里下载。下载完成后将这个矢量字体库编译安装到交叉编译工具链和开发板的根文件系统当中(具体过程可以自己去搜索)。

    三、基本开发步骤

    1、打开LCD液晶设备

    为了使用freetype矢量字体库来显示文字,首先要把和硬件LCD液晶相关的显示字体操作的API接口实现。

    1.1 打开LCD设备,获取相关参数,映射显存到用户空间

    这部分具体代码实现如下:

    1. /* 以可读可写方式打开LCD设备驱动文件 */
    2. fd_fb = open("/dev/fb0", O_RDWR);
    3. if(fd_fb == -1)
    4. {
    5. printf("can't open /dev/fb0!\n");
    6. return -1;
    7. }
    8. /* 获取液晶屏设备的可变参数 */
    9. ret = ioctl(fd_fb, FBIOGET_VSCREENINFO, &var);
    10. if(ret == -1)
    11. {
    12. printf("can't ioctl for /dev/fb0!\n");
    13. return -1;
    14. }
    15. /* 获取液晶屏设备的固定参数 */
    16. ret = ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix);
    17. if(ret == -1)
    18. {
    19. printf("can't ioctl for /dev/fb0!\n");
    20. return -1;
    21. }
    22. /* 获取相关的显存信息 */
    23. screen_size = var.xres * var.yres * var.bits_per_pixel / 8;
    24. line_width = var.xres * var.bits_per_pixel / 8;
    25. pixel_width = var.bits_per_pixel / 8;
    26. /* 将液晶显存映射到用户空间 */
    27. fbmem = mmap(NULL, screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
    28. if(fbmem == (char *)-1)
    29. {
    30. printf("mmap for /dev/fb0 error!\n");
    31. return -1;
    32. }
    33. /* 将液晶屏清为黑色 */
    34. lcd_clear_screen(BLACK);

    1.2 实现像素显示和清屏接口

    像素显示是所有液晶屏显示的最基本的调用接口,通过它可以实现各种各样的显示技巧,那么这个函数的具体实现如下:
    1. /* 在液晶屏上面显示一个像素
    2. * x : 表示x坐标
    3. * y : 表示y坐标
    4. * color : 表示像素显示的颜色
    5. */
    6. void lcd_put_pixel(int x, int y, int color)
    7. {
    8. /* 获取像素点在显存中的位置 */
    9. unsigned char *pen8 = fbmem + y * line_width + x * pixel_width;
    10. unsigned short *pen16 = (unsigned short *)pen8;
    11. unsigned int *pen32 = (unsigned int *)pen32;
    12. int red, green, blue;
    13. /* 判断一个像素点所占的位数 */
    14. switch(var.bits_per_pixel)
    15. {
    16. case 16: // RGB = 565
    17. {
    18. red = (color >> 16) & 0xff;
    19. green = (color >> 8) & 0xff;
    20. blue = color & 0xff;
    21. *pen16 = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
    22. break;
    23. }
    24. case 32:
    25. {
    26. *pen32 = color;
    27. break;
    28. }
    29. default :
    30. {
    31. printf("don't support this size of pixel : %d\n", var.bits_per_pixel);
    32. break;
    33. }
    34. }
    35. }
    清屏函数的实现也是经常用到的,它的主要作用就是把屏幕清成同一种颜色,方便文字、图像等的显示,它的具体实现如下所示:
    1. /* 液晶屏清屏
    2. * color : 表示清屏颜色
    3. */
    4. void lcd_clear_screen(int color)
    5. {
    6. memset(fbmem, color, screen_size);
    7. }

    2、初始化库

    初始化库,会创建一个freetype库的实例,具体实现如下:

    1. /* 初始化freetype库 */
    2. error = FT_Init_FreeType( &library );
    3. if(error)
    4. {
    5. printf("FT_Init_FreeType error!\n");
    6. return -1;
    7. }

    3、加载字体文件

    加载字体,创建一个face对象,用它来描述加载的字体的类型,具体的实现如下:

    1. /* 打开加载的字体文件 */
    2. error = FT_New_Face( library, argv[1], 0, &face );
    3. if(error)
    4. {
    5. printf("FT_New_Face error!\n");
    6. return -1;
    7. }
    argv[1] : 传入加载的字体文件的名称。

    4、设置字体大小

    我们这里通过像素的形式来设置字体的大小,设置字体的大小为24*24,具体实现如下:

    1. /* 设置字符的像素的大小为24*24 */
    2. error = FT_Set_Pixel_Sizes(face, 24, 0);
    3. if(error)
    4. {
    5. printf("FT_Set_Pixel_Sizes error!\n");
    6. return -1;
    7. }

    5、根据字符的编码值,加载glyph

    1. FT_Set_Transform(face, 0, &pen); // 设置字体的起始坐标位置
    2. /* 装载字符编码,填充face的glyph slot成员 */
    3. error = FT_Load_Char( face, wcstr1[i], FT_LOAD_RENDER);
    4. if(error)
    5. {
    6. printf("FT_Load_Char error!\n");
    7. return -1;
    8. }

    6、获取字体的位图信息,通过液晶屏显示出来

    这个函数的具体实现如下:

    1. /* LCD显示矢量字体的位图信息
    2. * bitmap : 要显示的字体的矢量位图
    3. * x : 显示的x坐标
    4. * y : 显示的y坐标
    5. */
    6. void lcd_draw_bitmap( FT_Bitmap* bitmap, FT_Int x, FT_Int y)
    7. {
    8. FT_Int i, j, p, q;
    9. FT_Int x_max = x + bitmap->width;
    10. FT_Int y_max = y + bitmap->rows;
    11. /* 将位图信息循环打印到屏幕上 */
    12. for(i = x, p = 0; i < x_max; i++, p++)
    13. {
    14. for(j = y, q = 0; j < y_max; j++, q++)
    15. {
    16. if((i > x_max) || (j > y_max) || (i < 0) || (j < 0))
    17. continue;
    18. if(bitmap->buffer[q * bitmap->width + p] != 0)
    19. {
    20. lcd_put_pixel(i, j, WHITE);
    21. }
    22. else
    23. {
    24. lcd_put_pixel(i, j, BLACK);
    25. }
    26. }
    27. }
    28. }

    以上就简单的介绍了整个freetype的开发基本过程,完整的内容可以参考freetype的官方网站:https://www.freetype.org/,也可以参考这篇文章:https://wenku.baidu.com/view/2d24be10cc7931b765ce155b.html


    附录:完整代码实现请从以下链接下载

    http://download.csdn.net/download/tech_pro/9873843


    `
    展开全文
  • 韦东山第三期第一个项目数码相框项目学习笔记,搬运
  • 数码相框框架 两个进程,其下多个线程。

    数码相框框架

    两个进程,其下多个线程。

    图片有点大,可以复制下来到桌面看,在浏览器内不方便。

    在这里插入图片描述

    展开全文
  • 解析:int ShowOnePage(unsigned char *pucTextFileMemCurPos) 其中: iLen = g_ptEncodingOprForFile->GetCodeFrmBuf(pucBufStart, g_pucTextFileMemEnd, &dwCode); 这里得到一个字 但是得到的编码并不是...
  • 包括3个大项目和若干衍生项目,涵盖:数码相框、电子书、指针、链表、Makefile、网络编程Socket、USB摄像头、CMOS摄像头、视频监控、WIFI、3G网卡、ALSA声卡、便携式视频监控(局域网通信)和电源管理。 总体格调:...
  • 通用 Makefile-- 韦东山视频学习笔记

    千次阅读 2019-03-06 16:10:58
    相关源码可去直接参考韦东山三期数码相框第 7 课找 解释 3. 编写一个通用的Makefile 编译test_Makefile的方法: a. gcc -o test a.c b.c 对于a.c: 预处理、编译、汇编 对于b.c:预处理、编译、汇编 ...
  • 数码相框

    2019-06-24 22:00:23
    声明,文章的内容基本上来自于韦东山老师的F:\010_韦东山Linux_第3期视频_项目实战(适用任意Linux板,111节,6节免费,已完结)\3期视频(含相应的文档与源码)\项目1_文件浏览器_数码相框(33节, 2节免费)\视频,若有...
  • 注:本人已购买韦东山老师第三期项目视频,内容来源《数码相框项目视频》,只用于学习记录,如有侵权,请联系删除。 1.数码相框需求框架 数码相框项目需求的框架如下图所示: ① 开发板上电后,进入主界面(Main ...
  • 文章目录链接背景项目介绍1. 编译环境2. 注意事项 链接 JZ2440 数码相框项目 扩展项目(一) 多文件图标 (二) 显示png ...   学习到韦东山的第三期数码相框项目,看了一下框架的大体介绍,就直接去看后面...
  • 文件浏览器_数码相框项目总结 (上)

    千次阅读 2017-09-03 15:41:07
    让我们一起来做 韦东山老师 第三期项目:文件浏览器_数码相框 项目吧功能
  • 韦东山第三期视频中第一个项目-数码相框源代码的完全注释 韦东山注释过的代码写的很不详细,只是把各个函数的功能写了一下,具体实现偶尔写一下。 我从头到尾学习了一遍,把自己的理解全部加了进去,程序中有一两...
  • 基于韦东山三期视频第一个项目,在此基础上添加了 MP3 播放器,可以播放大多数 MP3 文件,播放页面有音量控制,进度条以及歌曲名等
  • 之前看了韦东山老师的数码相框项目,断断续续学完了,现在再整理回顾,做个笔记记录一下。 项目需求: 实现在开发板上显示、浏览图片文件,并能进行图片的放大、缩小、移动、连播等操作 项目的主体框架: 项目的...
  • 数码相框之框架理解

    2019-09-26 04:00:28
    【参考】韦东山 教学笔记 1.先是主函数中 /* 注册页面 */  PagesInit();2.PagesInit()函数中,进行各个页面的初始化 1 int PagesInit(void) 2 { int iError; 3 iError = MainPageInit(); 4 iError |= ...
  • 注:本人已购买韦东山第三期项目视频,内容来源《数码相框项目视频》,只用于学习记录,如有侵权,请联系删除。
  • 该项目为韦东山老师的第三期项目中的第一个数码相框项目,本人已经购买此套视频,该套博客的目的是记录自己在项目学习中遇到的种种问题,对自己是一个进步,对他人是一个分享,欢迎交流学习。 视频内容和时长 第1课...
  • 注:本人已购买韦东山老师第三期项目视频,内容来源《数码相框项目视频》,只用于学习记录,如有侵权,请联系删除。 1. 数码相框效果图 根据上一节的数码相框需求框架可得出的大致效果图如下图所示:     上图...
  • 注:本人已购买韦东山老师第三期项目视频,内容来源《数码相框项目视频》,只用于学习记录,如有侵权,请联系删除。 这一节我们继续修改电子书的源码,让电子书既能够通过标准输入打印,也能通过网络远程打印,新的...
  • 基于S3C2440数码相框

    2019-09-26 04:00:20
    【参考】韦东山 教学笔记 1. 程序框架1.1 触摸屏: 主按线程,通过socket发给显示进程 --------------------------- 封装事件:ts线程 按键线程 --------------------------- ...
  • 注:本人已购买韦东山第三期项目视频,内容来源《数码相框项目视频》,只用于学习记录,如有侵权,请联系删除。 这一节主要讲述如何使用触摸屏操作电子书,实现电子书的翻页。 1. 使用轮询方式输入 2. 使用select...
  • 注:本人已购买韦东山第三期项目视频,内容来源《数码相框项目视频》,只用于学习记录,如有侵权,请联系删除。 多线程的优点:CPU占用率低,非常灵活,适用范围广 参考:《Unix_Linux_Windows_OpenMP多线程编程....
  • 注:本人已购买韦东山第三期项目视频,内容来源《数码相框项目视频》,只用于学习记录,如有侵权,请联系删除。 在数码相框(六、在LCD上显示任意编码的文本文件)中,我们以面向对象的思想实现了在LCD上显示电子书,...
  • 注:本人已购买韦东山老师第三期项目视频,内容来源《数码相框项目视频》,只用于学习记录,如有侵权,请联系删除。 1. LCD 如何显示一张图片? 假如下图是是我们的 JZ2440 开发板,它有一个块显存、LCD控制器、LCD...
  • 注:本人已购买韦东山第三期项目视频,内容来源《数码相框项目视频》,只用于学习记录,如有侵权,请联系删除。 上一节我们使用了多线程的方法去获取标准输入与LCD触摸屏的输入事件,这一节将在此基础上通过在LCD...

空空如也

空空如也

1 2
收藏数 37
精华内容 14
关键字:

韦东山数码相框