【STM32H7】第23章 ThreadX GUIX双缓冲的实现

2021-03-02 15:59:16 浏览数 (1)

最新教程下载: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 初学者重要提示

  1. 本章节配套例子的实现效果和第22章是一样的,如果要学习此界面的实现效果,可以看第22章教程说明。
  2. 本章例子仅作了800*480分辨率大小的界面设计。
  3. 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两个版本工程。

实验目的:

  1. 本章主要学习GUIX双缓冲的实现。

实验内容:

  1. 共创建了如下几个任务,通过按下按键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 总结

本章节主要为大家讲解了滑动效果的实现,推荐大家熟练掌握本章节的函数用法。

0 人点赞