我们在使用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