【方法】STemWin图形库在MULTIBUF多缓冲区模式下获取当前绘图使用的缓冲区编号(BufferID)

我们在使用STemWin图形库的时候,在MULTIBUF多缓冲区模式下,有时我们希望获取当前绘图使用的缓冲区号,以及屏幕上显示使用的缓冲区号。

我们在LCDConf_Lin.c中用DMA2D重写LCD_DEVFUNC_COPYRECT和LCD_DEVFUNC_FILLRECT函数时,也是需要获取绘图缓冲区的缓冲区号的。因为这两个函数传入的第一个参数是图层号,不是缓冲区号。图层(Layer)和缓冲区(Buffer)是两个不同的概念,不能错把图层号当做了缓冲区号,从而在MULTIBUF模式下GUI_Clear和GUI_FillRect等函数无法被GUI_MULTIBUF_Begin和GUI_MULTIBUF_End保护起来。
遗憾的是,STemWin的MULTIBUF并没有提供获取当前缓冲区号的API函数,而执不执行GUI_MULTIBUF_Begin,当前缓冲区号的值是不一样的。比如:

GUI_FillRect(0, 0, 100, 100); // 当前缓冲区号为0
GUI_MULTIBUF_Begin();
GUI_MULTIBUF_End();

但是

GUI_MULTIBUF_Begin();
GUI_FillRect(0, 0, 100, 100); // 当前缓冲区号为1
GUI_MULTIBUF_End();

那在MULTIBUF模式下我们怎么才能知道当前到底该往哪个缓冲区写数据呢?
在LCDConf_Lin.c的LCD_X_Config函数中,我们绑定了3个自定义函数:

typedef void (*ARKLCD7_Function)(void);
     
LCD_SetDevFunc(0, LCD_DEVFUNC_COPYBUFFER, (ARKLCD7_Function)ARKLCD7_CopyBuffer);
LCD_SetDevFunc(0, LCD_DEVFUNC_COPYRECT, (ARKLCD7_Function)ARKLCD7_CopyRect);
LCD_SetDevFunc(0, LCD_DEVFUNC_FILLRECT, (ARKLCD7_Function)ARKLCD7_FillRect);

这里ARKLCD7_CopyBuffer就是关键所在。我们在MULTIBUF模式下使用GUI_MULTIBUF_Begin和GUI_MULTIBUF_End这两个函数,都会调用我们自定义的ARKLCD7_CopyBuffer函数,此函数的第三个参数dest正是当前缓冲区号的值。
MULTIBUF的工作原理是,调用GUI_MULTIBUF_Begin函数时会将显示器上显示的内容复制到绘图用的缓冲区中,绘制完了之后又调用GUI_MULTIBUF_End函数将绘制好的图像内容复制回屏幕显示出来。缓冲区间复制图像正是调用的我们的ARKLCD7_CopyBuffer函数!
所以解决方案就出来了,我们定义一个名为screen_paint_index的全局变量,每次CopyBuffer的时候,都将dest参数的值保存到全局变量中:

void ARKLCD7_CopyBuffer(int layer, int src, int dest)
{
  screen_paint_index = dest; // GUI_MULTIBUF_Begin和GUI_MULTIBUF_End均会触发CopyBuffer函数调用, 可以根据dest参数记录当前绘图使用的缓冲区编号
  memcpy(screen_buffer[dest], screen_buffer[src], SCREEN_WIDTH * SCREEN_HEIGHT * sizeof(uint16_t));
}

这样我们就知道CopyRect和FillRect该往哪个缓冲区写数据了:

void ARKLCD7_CopyRect(int layer, int x0, int y0, int x1, int y1, int xsize, int ysize)
{
  //...
  HAL_DMA2D_Start(&hdma2d, (uint32_t)&screen_buffer[screen_paint_index][y0][x0], (uint32_t)&screen_buffer[screen_paint_index][y1][x1], xsize, ysize);
  //...
}
void ARKLCD7_FillRect(int layer, int x0, int y0, int x1, int y1, uint32_t color)
{
  //...
  HAL_DMA2D_Start(&hdma2d, color, (uint32_t)&screen_buffer[screen_paint_index][y0][x0], width, height);
  //...
}

在LCD_X_DisplayDriver函数中,我们还通过LCD_X_SHOWBUFFER绑定了ARKLCD7_ShowBuffer函数,该函数会在调用GUI_MULTIBUF_End时执行,用于切换屏幕显示的缓冲区。
因此我们还可以定义一个screen_display_index变量,在ARKLCD7_ShowBuffer的时候赋值,表示当前屏幕上显示的是哪个缓冲区。

void ARKLCD7_ShowBuffer(int index)
{
  screen_display_index = index;
  screen_pending_index = index;
  HAL_LTDC_SetAddress_NoReload(&hltdc, (uint32_t)screen_buffer[screen_pending_index], LTDC_LAYER_1);
  HAL_LTDC_Reload(&hltdc, LTDC_RELOAD_VERTICAL_BLANKING);
  while (screen_pending_index != -1);
}

void LTDC_IRQHandler(void)
{
  HAL_LTDC_IRQHandler(&hltdc);
}

void HAL_LTDC_ReloadEventCallback(LTDC_HandleTypeDef *hltdc)
{
  if (screen_pending_index != -1)
  {
    if (screen_pending_index >= 0)
      GUI_MULTIBUF_Confirm(screen_pending_index);
    screen_pending_index = -1;
  }
}

int型全局变量的默认值为0,所以我们在定义变量时不需要赋初值。因为STemWin在初始化完毕的时候,显示缓冲区号和绘图缓冲区号都是0。

int screen_display_index, screen_paint_index;

示例程序:https://blog.csdn.net/ZLK1214/article/details/104804410

你可能感兴趣的:(CubeMX,STM32)