You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
423 lines
14 KiB
423 lines
14 KiB
/* |
|
* 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 <http://www.gnu.org/licenses/>. |
|
*/ |
|
|
|
#include <assert.h> |
|
#include <math.h> |
|
#include <memory.h> |
|
#include <stdio.h> |
|
#include <string> |
|
|
|
#include <dma2d.h> |
|
#include <i2c.h> |
|
#include <ltdc.h> |
|
|
|
#ifdef EEZ_PLATFORM_STM32F469I_DISCO |
|
#include "stm32746g_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 <eez/core/util.h> |
|
#include <eez/core/memory.h> |
|
#include <eez/core/os.h> |
|
|
|
#include <eez/gui/gui.h> |
|
#include <eez/gui/thread.h> |
|
#include <eez/gui/display.h> |
|
#include <eez/gui/display-private.h> |
|
|
|
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
|
|
|