RT-Thread ART-PI 学习笔记:TouchGFX通过文件系统读取图片

前言

TouchGFX界面中可能会存在占用空间比较大的图片资源,在默认情况下,图片资源会随着其他只读数据一起放入芯片的已经经过内存映射的闪存中,系统可以直接通过地址访问到图片

这个机制虽然对art-pi没什么影响,因为art-pi的只读段放在片外空间比较大的8M NOR FLASH里,空间还算大。而且这块FLASH因为是NOR FLASH,经过内存映射,也可以通过地址直接访问指令和数据,和片内ROM的访问方式一样了。这就是是常说的代码(.text段)在片外flash上运行

但是对于ROM空间比较小的单片机,还没有外挂flash,内部空间捉襟见肘,就需要考虑采用把图片放入sd卡等非内存映射的闪存中以节省空间

TouchGFX提供了一种位图缓存机制,允许把图片放入sd卡等非内存映射的缓存中,系统上电时将部分或全部图片放在位于RAM的位图缓存中

参考链接

【STM32H750】玩转ART-Pi(七)——TouchGFX从文件系统中读取图片

【STM32H750】玩转ART-Pi(三)——如何在ART-PI上创建TouchGFX工程

14.RT-thread项目实战–TouchGFx界面效果优化

上面两篇博客用的方法都是依据touchgfx官方文档的步骤,我觉得可以主要参考官方文档,上面两篇博客描述的步骤已经很详细了,但是还是有一点不足之处,这个我在后面会提到

使用非内存映射闪存存储图像

缓存位图

上面两篇是官方文档,教的过程已经比较详细了,实践时只用稍作改动,下面我的操作都是按照官方文档来的

代码下载

本文演示代码https://download.csdn.net/download/weixin_42487906/21110777?spm=1001.2014.3001.5503

一定要在sd卡或者16M nor flash里面有相应图片的二进制bin文件这个工程才可以正常运行,如何操作见我的这篇博客

前期准备

软件准备

  • RT-Thread Studio 2.1.1:一站式的 RT-Thread 开发工具,通过简单易用的图形化配置系统以及丰富的软件包和组件资源,让物联网开发变得简单和高效。官网链接
  • touchgfx designer 4.16:touchgfx 界面设计工具,去官网上下载就可以了,版本不能低于4.15 官网链接

硬件准备

  • RT-Thread 推出的ART-PI STM32H750开发板
  • 正点原子 800*480 4.3寸 RGBLCD屏,搭载GT9147触摸芯片

在进行本篇操作之前必须配置文件系统,wifi功能和ftp服务器,并且使能touchgfx库,这几个操作在我之前的学习笔记中都有讲

同时我使用1.10版本的BSP,最新的1.21版本在配置touchgfx后会报错不知道问题出在哪里

配置步骤

在配置完touchgfx图形库后,用touchgfx designer 打开art_pi.touchgfx

image-20210815182832479

image-20210815184154449

可以看到默认工程中有几张png格式的位图资源,下面我们就把他们转移到sd卡或者flash里面去

首先根据官方文档修改连接器脚本

默认情况下,TouchGFX中的所有位图都放入ExtFlashSection中,标准的链接描述文件(此处为GCC)将其他只读数据一起放入闪存。

下面我们把数据放在0x9A000000的ExtFlashSection中

image-20210815184100882

根据官方文档,打开link.lds连接脚本,添加一块名为USB的存储区域,可读可写可执行

image-20210815184306491 image-20210815184459354

不用担心这块存储区域会占用系统资源,因为虽然链接产生的elf文件中会多一个USB段,但是最后烧进板子里的是elf经过objcopy处理生成的bin文件,这个文件只会包含图中的ROM和RAM段

image-20210815184427238

image-20210815195956496

image-20210815200536137

虽然图片文件根据lds文件链接到elf中,但是并不会被烧写到板子中,程序运行时并没有操作系统,不会像电脑那样将elf文件自动解析并将各个段放到ram和rom的指定位置。而是根据上图,elf会经过objcopy文件将RAM,ROM等可存储区域提取到rtthread.bin这个二进制文件中去,由烧写算法烧写到板子的制定位置。而USB区域并不会被烧写到板子中,这一步结束后需要用objcopy将 ExtFlashSection 段提取到一个二进制文件中,在rtthread里面是rtthread.bin,再把这个二进制文件放到SD卡或者flash里面,供程序读取。

编译时的命令,从上图可以看到使用了objcopy

上面的操作完成后,用记事本等文本编辑器打开link.lds

根据touchgfx官网的文档,将以下代码添加到link.lds的__bss_start = .; 和 _end = .;之间,最终效果如第三张图

image-20210815184751840

1
2
3
4
5
ExtFlashSection :
{
KEEP(*(ExtFlashSection.*))
. = ALIGN(4);
} > USB
image-20210815185528288

重新编译链接工程,打开rtthread.map看看ExtFlashSection被链接到哪个地址了

image-20210815190141050 image-20210815190204654

image-20210815190502382

在rtthread.map文件中搜索ExtFlashSection可以发现几个图片文件被连接到0x9a000000的位置,总大小为0x68230=426544字节

在rtthread studio中配置如下命令将ExtFlashSection从elf文件中拷贝出来

相当于将生成的图片二进制文件从elf里整出来,好放到sdcard等位置

1
arm-none-eabi-objcopy -O binary -j ExtFlashSection "${BuildArtifactFileBaseName}.elf" "images.bin"

通过查阅objcopy指令可以得知上述指令是将ExtFlashSection这个段以二进制文件的方式复制出来

objcopy参考博客:

http://www.manongjc.com/detail/22-pohrelllskvguhd.html

image-20210815194454891

把这个命令添加到构建后步骤

image-20210815200728843 image-20210815200831266

重新编译可以看到新添加的命令被执行

image-20210815203432962

DEBUG目录下生成了images.bin二进制文件

在TouchGFXHAL.cpp中添加如下代码,改掉blockcopy函数

新的blockCopy函数我参考了上面的两篇博客,这两篇博客中提到他们改过的blockCopy方法可以在检查完sd卡有没有相应文件后再去flash中检查,但是在实际用的时候,并不能读到flash里面的文件,看下了发现是代码逻辑问题,于是我改正了一下,下面的代码可以实现若sd卡未挂载或未存在相应文件时去flash查找

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include "dfs_posix.h"
bool TouchGFXHAL::blockCopy(void* RESTRICT dest, const void* RESTRICT src, uint32_t numBytes)
{
uint32_t dataOffset = (uint32_t) src;
if (dataOffset >= 0x9A000000 && dataOffset < 0xA0000000)
{
int fd;
struct statfs buffer;
if (rt_device_find("sd0") != RT_NULL)
{
if ((dfs_statfs("/sdcard", &buffer) == RT_EOK) | (dfs_mount("sd0", "/sdcard", "elm", 0, 0) == RT_EOK))
{
fd = open("/sdcard/images.bin", O_RDONLY, 0);

if (fd < 0)
{
//rt_kprintf("open file from sdcard failed,find from /flash\n");
}
else{
dataOffset = dataOffset - 0x9A000000;
lseek(fd, dataOffset, SEEK_SET);
// for copying data from there.
read(fd, (uint8_t *) dest, numBytes);
close(fd);
return true;
}
}
}
if (dfs_statfs("/flash", &buffer) == RT_EOK)
{
fd = open("/flash/images.bin", O_RDONLY, 0);
if (fd < 0)
{
rt_kprintf("open file for read failed\n");
return false;
}
dataOffset = dataOffset - 0x9A000000;
lseek(fd, dataOffset, SEEK_SET);
// for copying data from there.
read(fd, (uint8_t *) dest, numBytes);
close(fd);
return true;
}
else
{
return false;
}
}
else
{
// For all other addresses, just use the default implementation.
// This is important, as blockCopy is also used for other things in the core framework.
return TouchGFXGeneratedHAL::blockCopy(dest, src, numBytes);
}
}

通过我列出的第二个touchgfx官方文档,将TouchGFXHAL.cpp中的TouchGFXHAL::initialize()改为下面的,启用和配置位图缓存,并且分配一块新的缓存区域设置位图缓存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void TouchGFXHAL::initialize()
{
// Calling parent implementation of initialize().
//
// To overwrite the generated implementation, omit call to parent function
// and implemented needed functionality here.
// Please note, HAL::initialize() must be called to initialize the framework.

TouchGFXGeneratedHAL::initialize();

uint16_t* cacheStartAddr =(uint16_t *)rt_malloc(0x1400000);
uint32_t cacheSize = 0x1400000;

Bitmap::removeCache();
Bitmap::setCache(cacheStartAddr,cacheSize,1024);
Bitmap::cacheAll();
}

通过读卡器将images.bin复制到SD卡中,也可以通过ftp复制到SD卡或flash中

image-20210815204415575

image-20210815210431030

编译烧写后,发现程序报错,touchgfx初始化有问题,查看代码后发现touchgfx采用INIT_APP_INIT自动初始化机制,这种机制不能准确定义个模块初始化的顺序,这个模块可能是初始化的太早了,于是注释掉INIT_APP_INIT,并且在mian函数中调用TouchGFXTask();

在TouchGFXTask();前我还加了一段延时,为了解决wifi和touchgfx同时使用导致开机时屏幕短暂花屏的现象

image-20210815220243651

记得在main函数开始处添加extern

1
extern void TouchGFXTask();

最后编译烧写进板子,发现一切正常image-20210815220055708

编译烧写后一切正常,屏幕运行正常

终端显示sdcard挂载失败是正常现象,因为在TouchGFXHAL::blockCopy已经挂载过sdcard了

但是这个bug看的我很难受,通过查rtthread官方文档发现sd卡挂载函数应该用INIT_ENV_EXPORT来初始化,但是改了之后还是会报sd卡挂载失败,可能是sd卡挂载本身需要一定时间造成的,最终还是blockcopy挂载sdcard的操作起了作用

image-20210816144919426 image-20210816144957127

Powered by Hexo and Hexo-theme-hiker

Copyright © 2013 - 2021 张竞豪的小岛 All Rights Reserved.

UV : | PV :