最新教程下载:http://www.armbbs.cn/forum.php?mod=viewthread&tid=98429
第23章 ThreadX GUIX双缓冲的实现
本章节为大家讲解GUIX双缓冲的实现方法。
23.1初学者重要提示
23.2理解STM32H7的LTDC水平消隐和垂直消隐
23.3避免STM32H7的LTDC刷新撕裂感的解决办法
23.4 GUIX双缓冲实现框架
23.5 GUIX双缓冲实现方法
23.6 实验例程设计框架
23.7 实验例程
23.8 总结
23.1 初学者重要提示
- 本章节配套例子的实现效果和第22章是一样的,如果要学习此界面的实现效果,可以看第22章教程说明。
- 本章例子仅作了800*480分辨率大小的界面设计。
- GUIX双缓冲实现的关键借助了LTDC垂直消隐。
23.2 理解STM32H7的LTDC水平消隐和垂直消隐
正常情况下,LCD的刷新就是从左到右,从上到下进行逐个像素点刷新。但仅刷新有效的显示区是不够的,比如800*480分辨率,我们不仅仅要刷800*480这段有效区域,边界区也是要刷新的,即下图总宽度以内,有效区以外的区域也是要刷新的。
水平消隐就是LCD用户区一行结束到另一行开始的时间,这段消失的时间就是水平消隐,即HSYNC宽度 HBP HFP这段消失的时间。
垂直消隐就是LCD用户区最后一行结束到第一行开始的时间,这段消失的时间就是垂直消隐,即VSYNC宽度 VBP VFP这段消失的时间。
我们实际计算刷新率就是:
刷新率 = LTDC输出时钟 /((Width HSYNC_W HBP HFP )*(Height VSYNC_W VBP VFP ))
23.3 避免STM32H7的LTDC刷新撕裂感的解决办法
如果用户快速刷新颜色差异比较大两种界面效果,容易遇到这种撕裂问题。
出现这个问题的原因:
用户更新显存数据期间,LTDC(H7带的LCD控制器)也在不断的读取显存的数据到显示屏上,如果用户才更新了部分界面数据,后面部分还没有更新,LTDC刷新到显示屏的界面效果出现撕裂感,即下面这种现象:
解决这个问题的办法:
LTDC刷新还在垂直消隐期间就将整个界面刷新完成,而我们如何只知道LTDC在垂直消隐期,通过函数HAL_LTDC_ProgramLineEvent设置刷新到指定行时进入中断即可,一般设置为第0行进入中断,然后设置个标志即可。一旦检测到这个标志,就通过DMA2D快速将界面刷新好,这样就有效的避免了撕裂感。
23.4 GUIX双缓冲实现框架
为了方便大家理解GUIX双缓冲的实现思路,制作了个实现框图,此方法借助了前面说的垂直消隐。
核心就是一个显存地址的内容被LTDC刷新到显示屏时,GUIX画布的内容更新到另一个显存,从而实现双缓冲的效果。
23.5 GUIX双缓冲实现方法
23.5.1 第1步:开启LTDC行中断
代码如下:
代码语言:javascript复制/* 使能行中断 */
HAL_LTDC_ProgramLineEvent(&hLTDC, VSYNC_W VBP Height);
/* 使能LTDC中断,并配置其优先级 */
HAL_NVIC_SetPriority(LTDC_IRQn, 0x02, 0x00);
HAL_NVIC_EnableIRQ(LTDC_IRQn);
当前程序行中断设置的位置:
设置在这个位置,可以实现最大的垂直消隐时间。
23.5.2 第2步:创建信号量用于双缓冲同步
使用信号量实现任务同步,我们这里是通过LTDC中断发信号量给任务做同步。
代码语言:javascript复制/*
*********************************************************************************************************
* 函 数 名: AppObjCreate
* 功能说明: 创建任务通讯
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
static void AppObjCreate (void)
{
/* 创建互斥信号量 */
tx_mutex_create(&AppPrintfSemp,"AppPrintfSemp",TX_NO_INHERIT);
/* GUIX双缓冲信号量 */
tx_semaphore_create(&GuixSemaphore, "GUIX Semaphore", 1);
}
23.5.3 第3步:LTDC行中断处理
代码如下:
代码语言:javascript复制/*
*********************************************************************************************************
* 函 数 名: LTDC_IRQHandler
* 功能说明: LTDC中断服务程序
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
extern TX_SEMAPHORE GuixSemaphore;
extern __IO uint8_t g_ucGuixFlag;
void LTDC_IRQHandler(void)
{
LTDC->ICR = (uint32_t)LTDC_IER_LIE;
if(g_ucGuixFlag == 0)
{
g_ucGuixFlag = 1;
/* 更新LTDC寄存器 */
__HAL_LTDC_LAYER(&hLTDC, 0)->CFBAR = 0xC0000000;
__HAL_LTDC_RELOAD_IMMEDIATE_CONFIG(&hLTDC);
}
else
{
g_ucGuixFlag = 0;
/* 更新LTDC寄存器 */
__HAL_LTDC_LAYER(&hLTDC, 0)->CFBAR = 0xC0200000;
__HAL_LTDC_RELOAD_IMMEDIATE_CONFIG(&hLTDC);
}
tx_semaphore_put(&GuixSemaphore);
}
- LTDC->ICR = (uint32_t)LTDC_IER_LIE
清除行中断标志。
- __HAL_LTDC_LAYER(&hLTDC, 0)->CFBAR = 0xC0000000
- __HAL_LTDC_RELOAD_IMMEDIATE_CONFIG(&hLTDC)
这里是在垂直消隐期间设置的,设置新的显存地址,并立即生效。
- tx_semaphore_put(&GuixSemaphore)
LTDC中断里面发送同步信号量给任务。
23.5.4 第4步:双缓冲任务处理
代码如下:
代码语言:javascript复制/*
*********************************************************************************************************
* 函 数 名: AppTaskMsgPro
* 功能说明: 消息处理,这里用作GUIX双缓冲处理
* 形 参: thread_input 是在创建该任务时传递的形参
* 返 回 值: 无
优 先 级: 3
*********************************************************************************************************
*/
extern void DoubleBufferPro(void);
static void AppTaskMsgPro(ULONG thread_input)
{
(void)thread_input;
while(1)
{
DoubleBufferPro();
}
}
/*
*********************************************************************************************************
* 函 数 名: DoubleBufferPro
* 功能说明: 双缓冲处理
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void DoubleBufferPro(void)
{
tx_semaphore_get(&GuixSemaphore, TX_WAIT_FOREVER);
if(g_ucGuixFlag == 1)
{
DMA2D->OMAR = (uint32_t)0xC0200000;
}
else
{
DMA2D->OMAR = (uint32_t)0xC0000000;
}
DMA2D->CR = 0x00000000UL | (1 << 9);
DMA2D->FGMAR = (uint32_t)0xC0400000;
DMA2D->FGOR = 0;
DMA2D->OOR = 0;
/* 前景层和输出区域都采用RGB565颜色格式 */
DMA2D->FGPFCCR = LTDC_PIXEL_FORMAT_RGB565;
DMA2D->OPFCCR = LTDC_PIXEL_FORMAT_RGB565;
DMA2D->NLR = (uint32_t)(g_LcdWidth << 16) | (uint16_t)g_LcdHeight;
DMA2D->CR |= DMA2D_CR_START;
while (DMA2D->CR & DMA2D_CR_START) { tx_thread_sleep(1);}
}
- tx_semaphore_get(&GuixSemaphore, TX_WAIT_FOREVER)
等待LTDC中断的信号量发送。
- DMA2D->OMAR = (uint32_t)0xC0200000
- DMA2D->OMAR = (uint32_t)0xC0000000
LTDC中断里设置使用那块显存,这里设置使用另一块显存做DMA2D操作,实现双缓冲。
- DMA2D->FGMAR = (uint32_t)0xC0400000
设置的画布地址。通过DMA2D,将GUIX画布中的内容更新到显存中。
23.5.5 第5步:清空函数stm32h7_565rgb_buffer_toggle
代码如下,暂时用不上,将其置空即可:
代码语言:javascript复制/*
*********************************************************************************************************
* 函 数 名: stm32h7_565rgb_buffer_toggle
* 功能说明: 更新canvas内容到LCD显存
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void stm32h7_565rgb_buffer_toggle(GX_CANVAS *canvas, GX_RECTANGLE *dirty)
{
}
23.5.6 第6步:使用DTCM做主RAM
为实现最高性能,使用DTCM做主RAM:
23.5.7 第7步:合理设置任务优先级
三个涉及到GUIX的任务(数值越小优先级越高):
- GUIX System Thread
GUIX系统任务,优先级设置为16。
- App Msp Pro
GUIX双缓冲处理任务,优先级设置为17。
- App Task GUI
GUIX应用任务,优先级设置为18。
23.5.8 第8步:将触摸和GUIX放到一个任务
为了触摸效果更好,将触摸功能和GUIX应用功能都放到一个任务里面,程序代码如下:
代码语言:javascript复制/*
*********************************************************************************************************
* 函 数 名: MainTask
* 功能说明: GUI主函数
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void MainTask(void)
{
/* 省略未写 */
while(1)
{
TOUCH_Scan(); /* 电阻触摸屏和计数 */
TOUCH_CapScan(); /* 电容触摸屏 */
tx_thread_sleep(1);
}
}
23.6 实验例程设计框架
本章例程的重点是GUIX双缓冲的实现。
23.7 实验例程
(注,如果是电阻屏,需要做触摸校准,校准方法看本教程附件章节A)
配套例子:
本章节配套了如下例子供大家移植参考:
u V7-2028_Window Sliding(only 800x480)
GUIX Studio生成的代码在硬件平台实际运行的工程,含有MDK AC5和AC6两个版本工程。
实验目的:
- 本章主要学习GUIX双缓冲的实现。
实验内容:
- 共创建了如下几个任务,通过按下按键K1可以通过串口打印任务堆栈使用情况
App Task Start任务 :启动任务,这里用作BSP驱动包处理。
App Task MspPro任务 :消息处理,这里用作LED闪烁。
App Task UserIF任务 :按键消息处理。
App Task GUI任务 :GUI应用任务。
App Task STAT任务 :统计任务。
App Task IDLE任务 :空闲任务。
GUIX System Thread :GUI系统任务。
System Timer Thread任务:系统定时器任务。
实验效果:
GUIX Studio的界面设计如下:
串口打印任务执行情况:
MDK AC5和AC6工程可以串口打印任务执行情况:按开发板的按键K1可以打印,波特率 115200,数据位 8,奇偶校验位无,停止位 1:
23.8 总结
本章节主要为大家讲解了滑动效果的实现,推荐大家熟练掌握本章节的函数用法。