目录
3.2、修改\libraries\STM32F4xx_HAL\SConscript
3.4、新增\board\CubeMX_Config\SConscript
4.2 完善 STM32TouchController.cpp
前言
在移植之前,需要对rt-thread比较熟悉并且会使用rtthread的ENV工具
一、新建BSP
rtthread针对STM32系列有一个详细的BSP制作教程,可以根据自己的板卡制作出自己的BSP,就不再这里赘述了,可以参考官网的文档说明
二、使用CubeMX创建TouchGFX工程
在创建好的BSP中,双击 ..\board\CubeMX_Config.ioc 打开CubeMX,配置相关外设并生成初始化代码,这些初始化代码在后面的touchgfx的示例文件中会用到
1、配置FMC(SDRAM)
一定要注意配置的GPIO要跟自己的硬件要一致!!!因为SDRAM有的引脚可以在多个GPIO上复用。还有就是生成的工程中有没有SDRAM的初始化程序
2、配置DMA2D,打开DMA2D中断
3、配置LTDC,打开LTDC中断
一定要注意配置的GPIO要跟自己的硬件要一致!!!
4、开启CRC
5、配置TouchGFX
打开TouchGFX软件包
配置TouchGFX
6、执行TouchGFX Designer
使用CubeMX生成工程后,先不要打开IAR工程,还需要执行Touch Designer来设计UI部分,完善工程。在Src目录下有个 ApplicationTemplate.touchgfx.part,点击这个链接就可以打开TouchGFX Designer。
6.1、TouchGFX Designer界面
6.2、添加GUI应用
如果之前已经使用TouchGFX Designer设计过UI,把相关的文件直接拷贝过来就可以了,就不必再按照我下面的步骤来设计UI了,点击Generate Code生成代码后,把下面的文件拷贝过来即可
第一步:创建screen1,放置一个Box控件和Button控件,设置Box的坐标为(0,0)大小为800x480,设置Button的坐标和按下释放时的背景图片,图片可以从阿里巴巴矢量图标库下载,然后放到…\Src\assets\images目录下;添加Interaction,实现通过点击按钮切换到screen2的功能
第二步:创建screen2,步骤跟第一步类似,只是多了7个Line控件用来显示7中颜色,实现了点击按钮切换到screen3的功能
第三步:创建screen3,主要添加了Animated Image控件,用于动态的定时切换图片,点击按钮又回到screen1
6.3、生成代码
点击Generate Code就可以生成代码,同时在Src目录下也会多出一个”xxxx.touchgfx“的文件,以后直接打开这个文件就可以继续使用TouchGFX Designer设计UI了
三、将TouchGFX移植到RT-Thread中
1、修改操作系统接口文件
拷贝OSWrappers.cpp,重命名为OSWrappers_rtt.cpp,更改代码
#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>
#include <touchgfx/hal/GPIO.hpp>
#include <touchgfx/hal/HAL.hpp>
#include <touchgfx/hal/OSWrappers.hpp>
using namespace touchgfx;
static rt_sem_t frame_buffer_sem;
static rt_mq_t vsync_q = 0;
using namespace touchgfx;
// Just a dummy value to insert in the VSYNC queue.
static uint8_t dummy = 0x5a;
/*
* Initialize frame buffer semaphore and queue/mutex for VSYNC signal.
*/
void OSWrappers::initialize()
{
frame_buffer_sem = rt_sem_create("gfx_sem", 1, RT_IPC_FLAG_PRIO);
// Create a queue of length 1
vsync_q = rt_mq_create("gfx_mq", 1, 1, RT_IPC_FLAG_PRIO);
}
/*
* Take the frame buffer semaphore. Blocks until semaphore is available.
*/
void OSWrappers::takeFrameBufferSemaphore()
{
rt_sem_take(frame_buffer_sem, RT_WAITING_FOREVER);
}
/*
* Release the frame buffer semaphore.
*/
void OSWrappers::giveFrameBufferSemaphore()
{
rt_sem_release(frame_buffer_sem);
}
/*
* Attempt to obtain the frame buffer semaphore. If semaphore is not available, do
* nothing.
*
* Note must return immediately! This function does not care who has the taken the semaphore,
* it only serves to make sure that the semaphore is taken by someone.
*/
void OSWrappers::tryTakeFrameBufferSemaphore()
{
rt_sem_trytake(frame_buffer_sem);
}
/*
* Release the frame buffer semaphore in a way that is safe in interrupt context. Called
* from ISR.
*
* Release the frame buffer semaphore in a way that is safe in interrupt context.
* Called from ISR.
*/
void OSWrappers::giveFrameBufferSemaphoreFromISR()
{
// Since this is called from an interrupt, FreeRTOS requires special handling to trigger a
// re-scheduling. May be applicable for other OSes as well.
rt_sem_release(frame_buffer_sem);
}
/*
* Signal that a VSYNC has occurred. Should make the vsync queue/mutex available.
*
* Note This function is called from an ISR, and should (depending on OS) trigger a
* scheduling.
*/
void OSWrappers::signalVSync()
{
if (vsync_q)
{
rt_mq_send(vsync_q, &dummy, 1);
}
}
/*
* This function blocks until a VSYNC occurs.
*
* Note This function must first clear the mutex/queue and then wait for the next one to
* occur.
*/
void OSWrappers::waitForVSync()
{
// First make sure the queue is empty, by trying to remove an element with 0 timeout.
rt_mq_recv(vsync_q, &dummy, 1, 0);
// Then, wait for next VSYNC to occur.
rt_mq_recv(vsync_q, &dummy, 1, RT_WAITING_FOREVER);
}
/*
* A function that causes executing task to sleep for a number of milliseconds.
*
* A function that causes executing task to sleep for a number of milliseconds.
* This function is OPTIONAL. It is only used by the TouchGFX in the case of
* a specific frame refresh strategy (REFRESH_STRATEGY_OPTIM_SINGLE_BUFFER_TFT_CTRL).
* Due to backwards compatibility, in order for this function to be useable by the HAL
* the function must be explicitly registered:
* hal.registerTaskDelayFunction(&OSWrappers::taskDelay)
*
* see HAL::setFrameRefreshStrategy(FrameRefreshStrategy s)
* see HAL::registerTaskDelayFunction(void (*delayF)(uint16_t))
*/
void OSWrappers::taskDelay(uint16_t ms)
{
rt_thread_mdelay(ms);
}
static rt_base_t IdleTaskHook(void* p)
{
if ((int)p) //idle task sched out
{
touchgfx::HAL::getInstance()->setMCUActive(true);
}
else //idle task sched in
{
touchgfx::HAL::getInstance()->setMCUActive(false);
}
return RT_TRUE;
}
2、新建touchgfx的应用示例文件
此文件用于初始化TouchGFX,并创建一个任务执行TouchGFX
3、向工程添加文件
3.1、修改\board\KConfig
在文件中添加 BSP_USING_LCD 、BSP_USING_LTDC、 BSP_USING_SDRAM、RT_TOUCHGFX的配置项,这样可以通过menuconfig来选择TouchGFX是否开启了
menu "Onboard Peripheral Drivers"
...
config BSP_USING_LCD
bool "Enable RGB-LCD"
select BSP_USING_LTDC
select BSP_USING_SDRAM
default n
endmenu
menu "On-chip Peripheral Drivers"
...
config BSP_USING_LTDC
bool
default n
endmenu
menu "Board extended module Drivers"
menuconfig RT_STEMWIN
bool "Enable STemWin"
default n
if RT_STEMWIN
config RT_STEMWIN_DEMO
bool "Enable STemWin Demo"
default n
endif
menuconfig RT_TOUCHGFX
bool "Enable TouchGFX"
default n
endmenu
在开启RT_TOUCHGFX之前,需要先开启BSP_USING_LCD
开启RT_TOUCHGFX
3.2、修改\libraries\STM32F4xx_HAL\SConscript
在文件中添加一下如下内容,这样在定义了BSP_USING_LTDC 后就能将HAL驱动的相关文件添加到工程中
if GetDepend(['BSP_USING_LTDC']):
src += ['STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_ltdc.c']
src += ['STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_ltdc_ex.c']
src += ['STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma2d.c']
src += ['STM32F4xx_HAL_Driver/Src/stm32f4xx_ll_dma2d.c']
src += ['STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dsi.c']
src += ['STM32F4xx_HAL_Driver/Src/stm32f4xx_ltdc.c']
3.3、修改\board\SConscript
在文件末尾添加如下内容,这样如果开启了RT_TOUCHGFX,则会包含\board\CubeMX_Config目录下的SConscript文件
if GetDepend(['RT_TOUCHGFX']):
group = group + SConscript(cwd + '/CubeMX_Config/SConscript')
3.4、新增\board\CubeMX_Config\SConscript
向工程中添加TouchGFX相关的分组,并包含相关的源文件和头文件
import os
import rtconfig
from building import *
cwd = GetCurrentDir()
# add general drivers
src = Split('''
Src/touchgfx_sample.c
Src/OSWrappers-rtt.cpp
Src/STM32DMA.cpp
Src/STM32TouchController.cpp
Src/TouchGFXGPIO.cpp
Src/TouchGFXConfiguration.cpp
Src/TouchGFXGeneratedHAL.cpp
Src/TouchGFXHAL.cpp
Src/app_touchgfx.c
''')
path = [cwd + '/Src']
path += [cwd + '/Middlewares/ST/touchgfx/framework/include']
if rtconfig.CROSS_TOOL == 'gcc':
src += [cwd + '/Middlewares/ST/touchgfx/lib/core/cortex_m4f/gcc/libtouchgfx.a']
elif rtconfig.CROSS_TOOL == 'keil':
src += [cwd + '/Middlewares/ST/touchgfx/lib/core/cortex_m4f/Keil/touchgfx_core.lib']
elif rtconfig.CROSS_TOOL == 'iar':
src += [cwd + '/Middlewares/ST/touchgfx/lib/core/cortex_m4f/IAR8.x/touchgfx_core.a']
group = DefineGroup('TouchGFX_app', src, depend = [''], CPPPATH = path)
# add TouchGFX generated
genSrc = Glob('./Src/generated/fonts/src/*.cpp')
genSrc += Glob('./Src/generated/gui_generated/src/*/*.cpp')
genSrc += Glob('./Src/generated/images/src/*.cpp')
genSrc += Glob('./Src/generated/texts/src/*.cpp')
genPath = [cwd + '/Src/generated/fonts/include']
genPath += [cwd + '/Src/generated/gui_generated/include']
genPath += [cwd + '/Src/generated/images/include']
genPath += [cwd + '/Src/generated/texts/include']
group = group + DefineGroup('TouchGFX_generated', genSrc, depend = [''], CPPPATH = genPath)
# add TouchGFX resource
resSrc = Glob('./Src/generated/images/src/*/*.cpp')
group = group + DefineGroup('TouchGFX_resource', resSrc, depend = [''])
# add TouchGFX gui
guiSrc = Glob('./Src/gui/src/*/*.cpp')
guiPath = [cwd + '/Src/gui/include']
group = group + DefineGroup('TouchGFX_gui', guiSrc, depend = [''], CPPPATH = guiPath)
Return('group')
最后生成的工程如下:
4、添加触摸驱动
在完成以上配置后,LCD液晶屏就可以点亮了,但是触摸屏还不能使用,还需要添加触摸驱动;触摸驱动的添加也比较简单,首先根据自己板卡的触摸芯片添加驱动代码
4.1 添加TOUCH驱动文件
新建drv_touch.c/h驱动文件;修改..\board\Kconfig添加BSP_USING_TOUCH的配置项
menu "Onboard Peripheral Drivers"
...
config BSP_USING_TOUCH
bool "Enable TOUCH Driver(TSC2046)"
default n
endmenu
修改..\libraries\HAL_Drivers\SConscript,添加drv_touch.c,将其自动添加进工程
if GetDepend(['BSP_USING_TOUCH']):
src += ['drv_touch.c']
4.2 完善 STM32TouchController.cpp
添加完touch驱动文件后,完善下 STM32TouchController.cpp 中的两个函数就可以了。注意!这里获取的坐标是(0,0)到(800,480)的逻辑坐标
四、将图片放置到片外SPI FLASH
1、制作STM32F429的SPI FLASH下载算法
通过该算法,可以将工程中生成的图片数据和字体数据存储到外部FLASH
https://blog.csdn.net/sinat_31039061/article/details/107220994
https://blog.csdn.net/Ningjianwen/article/details/100151158
2、修改工程模版template.uvprojx
3、修改链接文件
链接器选择位图的地址。默认情况下,TouchGFX中的所有位图都放入ExtFlashSection中,修改后就能将图片链接到片外FLASH了
LR_IROM1 0x08000000 0x00100000 { ; load region size_region
ER_IROM1 0x08000000 0x00100000 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
}
RW_IRAM1 0x20000000 0x00020000 { ; RW data
.ANY (+RW +ZI)
}
}
LR_EROM1 0x90000000 0x00800000 { ; load region size_region
ER_EROM1 0x90000000 0x00800000 { ; load address = execution address
*.o (ExtFlashSection)
}
}
4、将位图数据从外部FLASH拷贝到缓存
4.1、开启模拟SPI
4.2、开启SFUD组件
4.3、设备初初始化
在spi_flash_init.c中添加如下内容,注册softspi1总线,注册softspi10设备并挂载到softspi1总线上;使能SFUD驱动W25Q64块设备
#include <rtthread.h>
#include "spi_flash.h"
#include "spi_flash_sfud.h"
#include "drv_soft_spi.h"
#if defined(BSP_USING_SPI_FLASH)
static int rt_hw_spi_flash_init(void)
{
__HAL_RCC_GPIOG_CLK_ENABLE();
rt_soft_spi_device_attach("softspi1", "softspi10", GPIOG, GPIO_PIN_10);
if (RT_NULL == rt_sfud_flash_probe("W25Q64", "softspi10"))
{
return -RT_ERROR;
}
return RT_EOK;
}
INIT_COMPONENT_EXPORT(rt_hw_spi_flash_init);
#endif
4.4、选中FAL软件包
在ENV中开启FAL软件包,用于操作外部SPI FLASH
配置fal_cfg.h。tgfx分区用于touchgfx的资源存储
#ifndef _FAL_CFG_H_
#define _FAL_CFG_H_
#include <rtthread.h>
#include <board.h>
#define FLASH_SIZE_GRANULARITY_16K (4 * 16 * 1024)
#define FLASH_SIZE_GRANULARITY_64K (64 * 1024)
#define FLASH_SIZE_GRANULARITY_128K (7 * 128 * 1024)
#define STM32_FLASH_START_ADRESS_16K STM32_FLASH_START_ADRESS
#define STM32_FLASH_START_ADRESS_64K (STM32_FLASH_START_ADRESS_16K + FLASH_SIZE_GRANULARITY_16K)
#define STM32_FLASH_START_ADRESS_128K (STM32_FLASH_START_ADRESS_64K + FLASH_SIZE_GRANULARITY_64K)
extern const struct fal_flash_dev stm32_onchip_flash_16k;
extern const struct fal_flash_dev stm32_onchip_flash_64k;
extern const struct fal_flash_dev stm32_onchip_flash_128k;
extern struct fal_flash_dev nor_flash0;
/* flash device table */
#define FAL_FLASH_DEV_TABLE \
{ \
&stm32_onchip_flash_16k, \
&stm32_onchip_flash_64k, \
&stm32_onchip_flash_128k, \
&nor_flash0, \
}
/* ====================== Partition Configuration ========================== */
#ifdef FAL_PART_HAS_TABLE_CFG
/* partition table */
#define FAL_PART_TABLE \
{ \
{FAL_PART_MAGIC_WROD, "bootloader", "onchip_flash_16k", 0 , FLASH_SIZE_GRANULARITY_16K , 0}, \
{FAL_PART_MAGIC_WROD, "param", "onchip_flash_64k", 0 , FLASH_SIZE_GRANULARITY_64K , 0}, \
{FAL_PART_MAGIC_WROD, "app", "onchip_flash_128k", 0 , FLASH_SIZE_GRANULARITY_128K, 0}, \
{FAL_PART_MAGIC_WROD, "tgfx", FAL_USING_NOR_FLASH_DEV_NAME, 0 , 4 * 1024 * 1024, 0}, \
}
#endif /* FAL_PART_HAS_TABLE_CFG */
#endif /* _FAL_CFG_H_ */
初始化fal
#include "fal.h"
int fs_init(void)
{
/* partition initialized */
fal_init();
return 0;
}
INIT_COMPONENT_EXPORT(fs_init);
4.5、修改TouchGFX驱动
在缓存位图时,TouchGFX会将像素从原始位置复制到位图缓存中。这个复制是通过调用HAL类中的方法完成的,所以如果是将图片等数据缓存在外部的FLASH时,需要修改此函数来支持外部FLASH的读取。该函数在TouchGFXHAL.cpp中定义
#include <fal.h>
bool TouchGFXHAL::blockCopy(void* RESTRICT dest, const void* RESTRICT src, uint32_t numBytes)
{
uint32_t dataOffset = (uint32_t)src;
if (dataOffset >= 0x90000000 && dataOffset < 0x92000000)
{
const struct fal_partition *part;
part = fal_partition_find("tgfx");
dataOffset = dataOffset - 0x90000000;
// for copying data from there.
if (part != RT_NULL)
{
fal_partition_read(part, dataOffset, (uint8_t *)dest, numBytes);
}
return true;
}
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 HAL::blockCopy(dest, src, numBytes);
}
}
修改完 blockCopy 函数后,只是能从外部SPI FLASH读取图片数据了,但是读取之后要放在哪呢?也就是还需要设置图片的缓存地址,用来存储读取到的图片数据!!!!! 我这里设置到SDRAM中
void touchgfx_init()
{
...
Bitmap::setCache((uint16_t *)0xC0300000,0x400000,128);
Bitmap::cacheAll();
}
四、将字体放置到片外SPI FLASH
4.14版本的TouchGFX还没解决
4.15版本的TouchGFX官网有文档说明,应该是可以,没有验证
五、移植时的注意事项
1、编译报大量错误时,检查下IAR是否开启了C++编译
2、SDRAM和LTDC的引脚配置一定要跟硬件对应上,非常关键!!!