/* * EEZ Modular Firmware * Copyright (C) 2021-present, Envox d.o.o. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #ifdef EEZ_PLATFORM_STM32F469I_DISCO #include "stm32469i_discovery_lcd.h" extern "C" LTDC_HandleTypeDef hltdc_eval; extern "C" DMA2D_HandleTypeDef hdma2d_eval; #define hltdc hltdc_eval #define hdma2d hdma2d_eval #endif #include #include #include #include #include #include #include void HAL_LTDC_LineEventCallback(LTDC_HandleTypeDef *phltdc) { using namespace eez::gui; using namespace eez::gui::display; LTDC_LAYER(phltdc, 0)->CFBAR = (uint32_t)g_syncedBuffer; __HAL_LTDC_RELOAD_IMMEDIATE_CONFIG(phltdc); sendMessageToGuiThread(GUI_QUEUE_MESSAGE_TYPE_DISPLAY_VSYNC, 0, 0); } namespace eez { namespace gui { namespace display { #define DMA2D_WAIT if (g_waitDMA) { while (HAL_DMA2D_PollForTransfer(&hdma2d, 1000) != HAL_OK); g_waitDMA = false; } static bool g_waitDMA; void initDriver() { __HAL_RCC_DMA2D_CLK_ENABLE(); } void syncBuffer() { DMA2D_WAIT; static const uint32_t LINE_INTERRUPT_POSITION = (LTDC->AWCR & 0x7FF) - 1; HAL_LTDC_ProgramLineEvent(&hltdc, LINE_INTERRUPT_POSITION); } static void bitBltRGB888(uint16_t *src, uint8_t *dst, int x, int y, int width, int height); void copySyncedBufferToScreenshotBuffer() { bitBltRGB888(g_syncedBuffer, SCREENSHOOT_BUFFER_START_ADDRESS, 0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT); DMA2D_WAIT; } //////////////////////////////////////////////////////////////////////////////// inline uint32_t vramOffset(uint16_t *vram, int x, int y) { return (uint32_t)(vram + y * DISPLAY_WIDTH + x); } inline uint32_t vramOffsetRGB888(uint8_t *vram, int x, int y) { return (uint32_t)(vram + (y * DISPLAY_WIDTH + x) * 3); } inline uint32_t vramOffset(uint32_t *vram, int x, int y) { return (uint32_t)(vram + y * DISPLAY_WIDTH + x); } void fillRect(uint16_t *dst, int x, int y, int width, int height, uint16_t color) { if (g_opacity == 255) { hdma2d.Init.Mode = DMA2D_R2M; hdma2d.Init.ColorMode = DMA2D_OUTPUT_RGB565; hdma2d.Init.OutputOffset = DISPLAY_WIDTH - width; uint32_t colorBGRA; uint8_t *pcolorBGRA = (uint8_t *)&colorBGRA; pcolorBGRA[0] = COLOR_TO_B(color); pcolorBGRA[1] = COLOR_TO_G(color); pcolorBGRA[2] = COLOR_TO_R(color); pcolorBGRA[3] = 255; DMA2D_WAIT; HAL_DMA2D_Init(&hdma2d); HAL_DMA2D_Start(&hdma2d, colorBGRA, vramOffset(dst, x, y), width, height); g_waitDMA = true; } else { // fill aux. buffer with BGRA color uint32_t colorBGRA; uint8_t *pcolorBGRA = (uint8_t *)&colorBGRA; pcolorBGRA[0] = COLOR_TO_B(color); pcolorBGRA[1] = COLOR_TO_G(color); pcolorBGRA[2] = COLOR_TO_R(color); pcolorBGRA[3] = g_opacity; // blend aux. buffer with dst buffer hdma2d.Init.Mode = DMA2D_M2M_BLEND; hdma2d.Init.ColorMode = DMA2D_OUTPUT_RGB565; hdma2d.Init.OutputOffset = DISPLAY_WIDTH - width; hdma2d.LayerCfg[0].InputOffset = DISPLAY_WIDTH - width; hdma2d.LayerCfg[0].InputColorMode = DMA2D_INPUT_RGB565; hdma2d.LayerCfg[0].AlphaMode = DMA2D_NO_MODIF_ALPHA; hdma2d.LayerCfg[0].InputAlpha = 0; hdma2d.LayerCfg[1].InputOffset = DISPLAY_WIDTH - width; hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_A8; hdma2d.LayerCfg[1].AlphaMode = DMA2D_REPLACE_ALPHA; hdma2d.LayerCfg[1].InputAlpha = colorBGRA; auto dstOffset = vramOffset(dst, x, y); DMA2D_WAIT; HAL_DMA2D_Init(&hdma2d); HAL_DMA2D_ConfigLayer(&hdma2d, 1); HAL_DMA2D_ConfigLayer(&hdma2d, 0); HAL_DMA2D_BlendingStart(&hdma2d, dstOffset, dstOffset, dstOffset, width, height); g_waitDMA = true; } } void fillRect(void *dst, int x1, int y1, int x2, int y2) { fillRect((uint16_t *)dst, x1, y1, x2 - x1 + 1, y2 - y1 + 1, g_fc); setDirty(); } void bitBlt(void *src, int srcBpp, uint32_t srcLineOffset, uint16_t *dst, int x, int y, int width, int height) { hdma2d.Init.ColorMode = DMA2D_OUTPUT_RGB565; hdma2d.Init.OutputOffset = DISPLAY_WIDTH - width; if (srcBpp == 32) { hdma2d.Init.Mode = DMA2D_M2M_BLEND; hdma2d.LayerCfg[0].InputOffset = DISPLAY_WIDTH - width; hdma2d.LayerCfg[0].InputColorMode = DMA2D_INPUT_RGB565; hdma2d.LayerCfg[0].AlphaMode = DMA2D_NO_MODIF_ALPHA; hdma2d.LayerCfg[0].InputAlpha = 0; hdma2d.LayerCfg[1].InputOffset = srcLineOffset; hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_ARGB8888; hdma2d.LayerCfg[1].AlphaMode = DMA2D_NO_MODIF_ALPHA; hdma2d.LayerCfg[1].InputAlpha = 0; } else if (srcBpp == 24) { hdma2d.Init.Mode = DMA2D_M2M_PFC; #ifndef EEZ_PLATFORM_STM32F469I_DISCO hdma2d.Init.RedBlueSwap = DMA2D_RB_SWAP; #endif hdma2d.LayerCfg[1].InputOffset = srcLineOffset; hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_RGB888; hdma2d.LayerCfg[1].AlphaMode = DMA2D_NO_MODIF_ALPHA; hdma2d.LayerCfg[1].InputAlpha = 0; } else { hdma2d.Init.Mode = DMA2D_M2M; hdma2d.LayerCfg[1].InputOffset = srcLineOffset; hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_RGB565; hdma2d.LayerCfg[1].AlphaMode = DMA2D_NO_MODIF_ALPHA; hdma2d.LayerCfg[1].InputAlpha = 0; } auto dstOffset = vramOffset(dst, x, y); DMA2D_WAIT; HAL_DMA2D_Init(&hdma2d); HAL_DMA2D_ConfigLayer(&hdma2d, 1); if (srcBpp == 32) { HAL_DMA2D_ConfigLayer(&hdma2d, 0); HAL_DMA2D_BlendingStart(&hdma2d, (uint32_t)src, dstOffset, dstOffset, width, height); } else { HAL_DMA2D_Start(&hdma2d, (uint32_t)src, dstOffset, width, height); } g_waitDMA = true; if (srcBpp == 24) { #ifndef EEZ_PLATFORM_STM32F469I_DISCO hdma2d.Init.RedBlueSwap = DMA2D_RB_REGULAR; #endif } } void bitBlt(void *src, int x1, int y1, int x2, int y2) { bitBlt(src, g_renderBuffer, x1, y1, x2, y2); setDirty(); } void bitBlt(uint16_t *src, uint16_t *dst, int x, int y, int width, int height) { hdma2d.Init.Mode = DMA2D_M2M; hdma2d.Init.ColorMode = DMA2D_OUTPUT_RGB565; hdma2d.Init.OutputOffset = DISPLAY_WIDTH - width; hdma2d.LayerCfg[1].InputOffset = DISPLAY_WIDTH - width; hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_RGB565; hdma2d.LayerCfg[1].AlphaMode = DMA2D_NO_MODIF_ALPHA; hdma2d.LayerCfg[1].InputAlpha = 0; DMA2D_WAIT; HAL_DMA2D_Init(&hdma2d); HAL_DMA2D_ConfigLayer(&hdma2d, 1); HAL_DMA2D_Start(&hdma2d, vramOffset(src, x, y), vramOffset(dst, x, y), width, height); g_waitDMA = true; } static void bitBltRGB888(uint16_t *src, uint8_t *dst, int x, int y, int width, int height) { hdma2d.Init.Mode = DMA2D_M2M_PFC; hdma2d.Init.ColorMode = DMA2D_OUTPUT_RGB888; hdma2d.Init.OutputOffset = DISPLAY_WIDTH - width; #ifndef EEZ_PLATFORM_STM32F469I_DISCO hdma2d.Init.RedBlueSwap = DMA2D_RB_SWAP; #endif hdma2d.LayerCfg[1].InputOffset = DISPLAY_WIDTH - width; hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_RGB565; hdma2d.LayerCfg[1].AlphaMode = DMA2D_NO_MODIF_ALPHA; hdma2d.LayerCfg[1].InputAlpha = 0; DMA2D_WAIT; HAL_DMA2D_Init(&hdma2d); HAL_DMA2D_ConfigLayer(&hdma2d, 1); HAL_DMA2D_Start(&hdma2d, vramOffset(src, x, y), vramOffsetRGB888(dst, x, y), width, height); g_waitDMA = true; #ifndef EEZ_PLATFORM_STM32F469I_DISCO hdma2d.Init.RedBlueSwap = DMA2D_RB_REGULAR; #endif } void bitBlt(void *src, void *dst, int x1, int y1, int x2, int y2) { bitBlt((uint16_t *)src, (uint16_t *)dst, x1, y1, x2 - x1 + 1, y2 - y1 + 1); setDirty(); } void bitBlt(uint16_t *src, uint16_t *dst, int x, int y, int width, int height, int dstx, int dsty) { hdma2d.Init.Mode = DMA2D_M2M; hdma2d.Init.ColorMode = DMA2D_OUTPUT_RGB565; hdma2d.Init.OutputOffset = DISPLAY_WIDTH - width; hdma2d.LayerCfg[1].InputOffset = DISPLAY_WIDTH - width; hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_RGB565; hdma2d.LayerCfg[1].AlphaMode = DMA2D_NO_MODIF_ALPHA; hdma2d.LayerCfg[1].InputAlpha = 0; DMA2D_WAIT; HAL_DMA2D_Init(&hdma2d); HAL_DMA2D_ConfigLayer(&hdma2d, 1); HAL_DMA2D_Start(&hdma2d, vramOffset(src, x, y), vramOffset(dst, dstx, dsty), width, height); g_waitDMA = true; } void bitBlt(void *src, void *dst, int sx, int sy, int sw, int sh, int dx, int dy, uint8_t opacity) { if (dst == nullptr) { dst = g_renderBuffer; } auto srcOffset = vramOffset((uint16_t *)src, sx, sy); auto dstOffset = vramOffset((uint16_t *)dst, dx, dy); if (opacity == 255) { hdma2d.Init.Mode = DMA2D_M2M; hdma2d.Init.ColorMode = DMA2D_OUTPUT_RGB565; hdma2d.Init.OutputOffset = DISPLAY_WIDTH - sw; hdma2d.LayerCfg[1].InputOffset = DISPLAY_WIDTH - sw; hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_RGB565; hdma2d.LayerCfg[1].AlphaMode = DMA2D_NO_MODIF_ALPHA; hdma2d.LayerCfg[1].InputAlpha = 0; DMA2D_WAIT; HAL_DMA2D_Init(&hdma2d); HAL_DMA2D_ConfigLayer(&hdma2d, 1); HAL_DMA2D_Start(&hdma2d, srcOffset, dstOffset, sw, sh); g_waitDMA = true; } else { hdma2d.Init.Mode = DMA2D_M2M_BLEND; hdma2d.Init.ColorMode = DMA2D_OUTPUT_RGB565; hdma2d.Init.OutputOffset = DISPLAY_WIDTH - sw; hdma2d.LayerCfg[0].InputOffset = DISPLAY_WIDTH - sw; hdma2d.LayerCfg[0].InputColorMode = DMA2D_INPUT_RGB565; hdma2d.LayerCfg[0].AlphaMode = DMA2D_COMBINE_ALPHA; hdma2d.LayerCfg[0].InputAlpha = 0xFF; hdma2d.LayerCfg[1].InputOffset = DISPLAY_WIDTH - sw; hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_RGB565; hdma2d.LayerCfg[1].AlphaMode = DMA2D_COMBINE_ALPHA; hdma2d.LayerCfg[1].InputAlpha = opacity; DMA2D_WAIT; HAL_DMA2D_Init(&hdma2d); HAL_DMA2D_ConfigLayer(&hdma2d, 1); HAL_DMA2D_ConfigLayer(&hdma2d, 0); HAL_DMA2D_BlendingStart(&hdma2d, srcOffset, dstOffset, dstOffset, sw, sh); g_waitDMA = true; } } //////////////////////////////////////////////////////////////////////////////// void startPixelsDraw() { DMA2D_WAIT; } void drawPixel(int x, int y) { *(g_renderBuffer + y * DISPLAY_WIDTH + x) = g_fc; } void drawPixel(int x, int y, uint8_t opacity) { auto dest = g_renderBuffer + y * DISPLAY_WIDTH + x; *dest = color32to16( blendColor( color16to32(g_fc, opacity), color16to32(*dest, 255 - opacity) ) ); } void endPixelsDraw() { setDirty(); } void fillRect(int x1, int y1, int x2, int y2) { fillRect(g_renderBuffer, x1, y1, x2 - x1 + 1, y2 - y1 + 1, g_fc); setDirty(); } void bitBlt(int x1, int y1, int x2, int y2, int dstx, int dsty) { bitBlt(g_renderBuffer, g_renderBuffer, x1, y1, x2-x1+1, y2-y1+1, dstx, dsty); setDirty(); } void drawBitmap(Image *image, int x, int y) { bitBlt(image->pixels, image->bpp, image->lineOffset, g_renderBuffer, x, y, image->width, image->height); setDirty(); } void drawStrInit() { auto color = g_fc; // initialize everything except lineOffset hdma2d.Init.Mode = DMA2D_M2M_BLEND; hdma2d.Init.ColorMode = DMA2D_OUTPUT_RGB565; hdma2d.Init.OutputOffset = 0; // background hdma2d.LayerCfg[0].InputColorMode = DMA2D_INPUT_RGB565; hdma2d.LayerCfg[0].AlphaMode = DMA2D_NO_MODIF_ALPHA; hdma2d.LayerCfg[0].InputAlpha = 0; hdma2d.LayerCfg[0].InputOffset = 0; // foreground uint32_t colorBGRA; uint8_t *pcolorBGRA = (uint8_t *)&colorBGRA; pcolorBGRA[0] = COLOR_TO_B(color); pcolorBGRA[1] = COLOR_TO_G(color); pcolorBGRA[2] = COLOR_TO_R(color); pcolorBGRA[3] = 255; hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_A8; hdma2d.LayerCfg[1].AlphaMode = DMA2D_NO_MODIF_ALPHA; hdma2d.LayerCfg[1].InputAlpha = colorBGRA; hdma2d.LayerCfg[1].InputOffset = 0; DMA2D_WAIT; HAL_DMA2D_Init(&hdma2d); HAL_DMA2D_ConfigLayer(&hdma2d, 0); HAL_DMA2D_ConfigLayer(&hdma2d, 1); g_waitDMA = true; } void drawGlyph(const uint8_t *src, uint32_t srcLineOffset, int x, int y, int width, int height) { uint32_t lineOffset = DISPLAY_WIDTH - width; uint32_t dst = vramOffset(g_renderBuffer, x, y); DMA2D_WAIT; // initialize lineOffset WRITE_REG(hdma2d.Instance->OOR, lineOffset); WRITE_REG(hdma2d.Instance->BGOR, lineOffset); WRITE_REG(hdma2d.Instance->FGOR, srcLineOffset); HAL_DMA2D_BlendingStart(&hdma2d, (uint32_t)src, dst, dst, width, height); g_waitDMA = true; } } // namespace display } // namespace gui } // namespace eez