【STemWin】STM32H743VI单片机通过LTDC驱动4.3寸800×480高分辨率的40-pin FPC彩屏并裸机移植STemWin图形库

本程序使用的单片机为STM32H743VI,晶振大小为25MHz。程序利用LTDC驱动如图所示的RGB888接口的4.3寸高分辨率屏。

【STemWin】STM32H743VI单片机通过LTDC驱动4.3寸800×480高分辨率的40-pin FPC彩屏并裸机移植STemWin图形库_第1张图片 【STemWin】STM32H743VI单片机通过LTDC驱动4.3寸800×480高分辨率的40-pin FPC彩屏并裸机移植STemWin图形库_第2张图片 【STemWin】STM32H743VI单片机通过LTDC驱动4.3寸800×480高分辨率的40-pin FPC彩屏并裸机移植STemWin图形库_第3张图片

 【STemWin】STM32H743VI单片机通过LTDC驱动4.3寸800×480高分辨率的40-pin FPC彩屏并裸机移植STemWin图形库_第4张图片 【STemWin】STM32H743VI单片机通过LTDC驱动4.3寸800×480高分辨率的40-pin FPC彩屏并裸机移植STemWin图形库_第5张图片

彩屏的连接口是40-pin FPC接口,需要FPC座来连接。请注意FPC座的正反面。下面的绿色板子是一个FPC座转直插排针的板子。

【STemWin】STM32H743VI单片机通过LTDC驱动4.3寸800×480高分辨率的40-pin FPC彩屏并裸机移植STemWin图形库_第6张图片

插的时候注意黑色那一面是不导电的,要朝外插进去,里面金属那一面才是导电的。一定要根据FPC座的情况确定1脚的方向,不要搞反了。

  

此液晶屏的1脚和2脚连接21V的背光电源,只有连了背光,显示屏才会显示图像。在只连接了背光引脚的情况下,显示屏是黑的。背光正常时,可以从彩屏底部的小孔处看到白色的光亮。

电源只有3.3V的情况下,可以用KA2707升压芯片升压到21V给背光供电,电路图如下图所示。背光是由几个LED灯串联而成,所以需要的电压较高。B/L EN接单片机的I/O口,可以用PWM来控制液晶屏的亮度。单片机上还可以接一个光敏电阻,根据外界环境光线强度自动调节彩屏的显示亮度。LED-和LED+分别接彩屏的1脚和2脚。

【STemWin】STM32H743VI单片机通过LTDC驱动4.3寸800×480高分辨率的40-pin FPC彩屏并裸机移植STemWin图形库_第7张图片

以下是FPC排线和单片机LTDC的连接方式。STM32H743VI是100脚的芯片,LTDC只支持RGB565和RGB666,不支持RGB888,本程序采用的是RGB565,因此颜色线只接R3~7、G2~7和B3~7。剩余不用的R0~2、G0~1和B0~2可以悬空或者接地。DCLK接LTDC_CLK,HS接LTDC_HSYNC,VS接LTDC_VSYNC,DE接LTDC_DE。

【STemWin】STM32H743VI单片机通过LTDC驱动4.3寸800×480高分辨率的40-pin FPC彩屏并裸机移植STemWin图形库_第8张图片

【不含STemWin图形库的程序】

程序下载地址:https://pan.baidu.com/s/10qowWoZhaH-cMXmtRJI2Pg(提取码:9crm)

下面的程序初始化完LTDC后,直接显示保存在单片机内部Flash中的一幅花的图片。

#include 
#include 
#include 
#include "common.h"
#include "images.h"

#define SCREEN_WIDTH 800
#define SCREEN_HEIGHT 480

#define SCREEN_HSW 1
#define SCREEN_VSW 1
#define SCREEN_HBP 46
#define SCREEN_VBP 23
#define SCREEN_HFP 210
#define SCREEN_VFP 22

LTDC_HandleTypeDef hltdc;

static void ltdc_init(void)
{
  GPIO_InitTypeDef gpio;
  LTDC_LayerCfgTypeDef layer;
  PLL3_ClocksTypeDef clock_result;
  RCC_PeriphCLKInitTypeDef clock = {0};
  
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOE_CLK_ENABLE();
  
  // PA3: LTDC_B5, PA4: LTDC_VSYNC, PA5: LTDC_R4 ,PA6: LTDC_G2
  gpio.Alternate = GPIO_AF14_LTDC;
  gpio.Mode = GPIO_MODE_AF_PP;
  gpio.Pin = GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6;
  gpio.Pull = GPIO_NOPULL;
  gpio.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(GPIOA, &gpio);
  
  // PB0: LTDC_R3, PB1: LTDC_R6
  gpio.Alternate = GPIO_AF9_LTDC;
  gpio.Pin = GPIO_PIN_0 | GPIO_PIN_1;
  HAL_GPIO_Init(GPIOB, &gpio);
  
  // PB8~9: LTDC_B6~7, PB10~11: LTDC_G4~5
  gpio.Alternate = GPIO_AF14_LTDC;
  gpio.Pin = GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11;
  HAL_GPIO_Init(GPIOB, &gpio);
  
  // PC0: LTDC_R5, PC6: LTDC_HSYNC, PC7: LTDC_G6
  gpio.Pin = GPIO_PIN_0 | GPIO_PIN_6 | GPIO_PIN_7;
  HAL_GPIO_Init(GPIOC, &gpio);
  
  // PD3: LTDC_G7, PD10: LTDC_B3
  gpio.Pin = GPIO_PIN_3 | GPIO_PIN_10;
  HAL_GPIO_Init(GPIOD, &gpio);
  
  // PE11: LTDC_G3, PE12: LTDC_B4, PE13: LTDC_DE, PE14: LTDC_CLK, PE15: LTDC_R7
  gpio.Pin = GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;
  HAL_GPIO_Init(GPIOE, &gpio);
  
  // LTDC时钟频率: 30MHz
  clock.PeriphClockSelection = RCC_PERIPHCLK_LTDC;
  clock.PLL3.PLL3FRACN = 0;
  clock.PLL3.PLL3M = 25;
  clock.PLL3.PLL3N = 150;
  clock.PLL3.PLL3P = 2;
  clock.PLL3.PLL3Q = 2;
  clock.PLL3.PLL3R = 5;
  clock.PLL3.PLL3RGE = RCC_PLL3VCIRANGE_3;
  clock.PLL3.PLL3VCOSEL = RCC_PLL3VCOWIDE;
  HAL_RCCEx_PeriphCLKConfig(&clock);
  
  HAL_RCCEx_GetPLL3ClockFreq(&clock_result);
  printf("LTDC clock: %.1fMHz\n", clock_result.PLL3_R_Frequency / 1000000.0f);
  
  __HAL_RCC_LTDC_CLK_ENABLE();
  hltdc.Instance = LTDC;
  hltdc.Init.HSPolarity = LTDC_HSPOLARITY_AL;
  hltdc.Init.VSPolarity = LTDC_VSPOLARITY_AL;
  hltdc.Init.DEPolarity = LTDC_DEPOLARITY_AL;
  hltdc.Init.PCPolarity = LTDC_PCPOLARITY_IPC;
  hltdc.Init.HorizontalSync = SCREEN_HSW - 1;
  hltdc.Init.VerticalSync = SCREEN_VSW - 1;
  hltdc.Init.AccumulatedHBP = SCREEN_HSW + SCREEN_HBP - 1;
  hltdc.Init.AccumulatedVBP = SCREEN_VSW + SCREEN_VBP - 1;
  hltdc.Init.AccumulatedActiveW = SCREEN_HSW + SCREEN_HBP + SCREEN_WIDTH - 1;
  hltdc.Init.AccumulatedActiveH = SCREEN_VSW + SCREEN_VBP + SCREEN_HEIGHT - 1;
  hltdc.Init.TotalWidth = SCREEN_HSW + SCREEN_HBP + SCREEN_WIDTH + SCREEN_HFP - 1;
  hltdc.Init.TotalHeigh = SCREEN_VSW + SCREEN_VBP + SCREEN_HEIGHT + SCREEN_VFP - 1;
  hltdc.Init.Backcolor.Red = 0;
  hltdc.Init.Backcolor.Green = 0;
  hltdc.Init.Backcolor.Blue = 0;
  HAL_LTDC_Init(&hltdc);
  
  layer.WindowX0 = 0;
  layer.WindowX1 = IMAGE_FLOWER_WIDTH;
  layer.WindowY0 = 0;
  layer.WindowY1 = IMAGE_FLOWER_WIDTH;
  layer.PixelFormat = LTDC_PIXEL_FORMAT_RGB565;
  layer.Alpha = 255; // 整体(窗口区域+非窗口区域)半透明度(0为透明, 255为不透明), 窗口区域不会叠加上非窗口区域的颜色
  layer.Alpha0 = 0; // 非窗口区域半透明度
  layer.BlendingFactor1 = LTDC_BLENDING_FACTOR1_PAxCA;
  layer.BlendingFactor2 = LTDC_BLENDING_FACTOR2_PAxCA;
  layer.FBStartAdress = (uint32_t)image_flower; // 窗口区域显示内容
  layer.ImageWidth = IMAGE_FLOWER_WIDTH;
  layer.ImageHeight = IMAGE_FLOWER_WIDTH;
  layer.Backcolor.Red = 0; // 非窗口区域显示颜色
  layer.Backcolor.Green = 0;
  layer.Backcolor.Blue = 0;
  HAL_LTDC_ConfigLayer(&hltdc, &layer, LTDC_LAYER_1);
  
  // 为了消除开机时屏幕上随机黑线, 必须控制背光引脚
  // 默认情况下背光是关闭的, 当LTDC打开后延时几百毫秒再开背光
}

int main(void)
{
  uint8_t data;
  
  HAL_Init();
  
  clock_init();
  usart_init(115200);
  printf("STM32H743VI LTDC\n");
  printf("SystemCoreClock=%u\n", SystemCoreClock);
  
  ltdc_init();
  while (1)
  {
    if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE) != RESET)
    {
      HAL_UART_Receive(&huart1, &data, 1, HAL_MAX_DELAY);
      printf("Data: 0x%02x\n", data);
    }
  }
}

【含有STemWin图形库的程序】

程序下载地址:https://pan.baidu.com/s/1Ote5LTHgaMoc4iO9hV1DiQ(提取码:s3b4)

STM32H743VI单片机的内部SRAM分为4个区域:IRAM1、IRAM2、RAM1和RAM2,如下图所示。

【STemWin】STM32H743VI单片机通过LTDC驱动4.3寸800×480高分辨率的40-pin FPC彩屏并裸机移植STemWin图形库_第9张图片

若想要将显存放在内部SRAM中,则只能放在IRAM2中,因为LTDC只支持从0x24000000这个地址块上读取图像数据。从H7的手册中可以看到,LTDC只连接了AXI SRAM(也就是0x24000000那块SRAM),和SRAM1~4和Backup RAM都没有连接上。

【STemWin】STM32H743VI单片机通过LTDC驱动4.3寸800×480高分辨率的40-pin FPC彩屏并裸机移植STemWin图形库_第10张图片

因此,在Keil中定义screen_buffer数组的时候,需要使用__attribute__((at(0x24000000)))语句:

uint16_t screen_buffer[DISPLAY_HEIGHT][DISPLAY_WIDTH] __attribute__((at(0x24000000))); // 必须在项目属性中取消IRAM2 default的勾选才能编译成功

而且还要注意项目属性中IRAM2的default复选框不能勾选,否则编译不通过。

由于这块SRAM的大小有限,只有512KB,所以无法保存完整个屏幕800×480的区域,我们只选择保存并显示546×480大小的图像,其余区域显示为黑色。

#ifndef _SCREEN_H
#define _SCREEN_H

#define SCREEN_WIDTH 800
#define SCREEN_HEIGHT 480
#define DISPLAY_WIDTH 546
#define DISPLAY_HEIGHT SCREEN_HEIGHT

#define SCREEN_HSW 1
#define SCREEN_VSW 1
#define SCREEN_HBP 46
#define SCREEN_VBP 23
#define SCREEN_HFP 210
#define SCREEN_VFP 22

extern uint16_t screen_buffer[DISPLAY_HEIGHT][DISPLAY_WIDTH];

typedef void (*Screen_Function)(void);

void Screen_CopyBuffer(int layer, int src, int dest);
void Screen_CopyRect(int layer, int x0, int y0, int x1, int y1, int xsize, int ysize);
void Screen_CopyRectFromMemdev(void *dest, const void *src, int xsize, int ysize, int dest_linebytes, int src_linebytes);
void Screen_FillRect(int layer, int x0, int y0, int x1, int y1, uint32_t color);
void Screen_Init(void);

#endif

修改后的程序:

#include 
#include 
#include 
#include "Screen.h"

DMA2D_HandleTypeDef hdma2d;
LTDC_HandleTypeDef hltdc;
uint16_t screen_buffer[DISPLAY_HEIGHT][DISPLAY_WIDTH] __attribute__((at(0x24000000))); // 必须在项目属性中取消IRAM2 default的勾选才能编译成功

void Screen_CopyBuffer(int layer, int src, int dest)
{
  memcpy(screen_buffer[dest], screen_buffer[src], DISPLAY_WIDTH * DISPLAY_HEIGHT * sizeof(uint16_t));
}

void Screen_CopyRect(int layer, int x0, int y0, int x1, int y1, int xsize, int ysize)
{
  hdma2d.Init.ColorMode = DMA2D_OUTPUT_RGB565;
  hdma2d.Init.Mode = DMA2D_M2M;
  hdma2d.Init.OutputOffset = DISPLAY_WIDTH - xsize;
  HAL_DMA2D_Init(&hdma2d);
  
  hdma2d.LayerCfg[DMA2D_FOREGROUND_LAYER].InputColorMode = DMA2D_INPUT_RGB565;
  hdma2d.LayerCfg[DMA2D_FOREGROUND_LAYER].InputOffset = DISPLAY_WIDTH - xsize;
  HAL_DMA2D_ConfigLayer(&hdma2d, DMA2D_FOREGROUND_LAYER);
  
  HAL_DMA2D_Start(&hdma2d, (uint32_t)&screen_buffer[y0][x0], (uint32_t)&screen_buffer[y1][x1], xsize, ysize);
  HAL_DMA2D_PollForTransfer(&hdma2d, HAL_MAX_DELAY);
}

void Screen_CopyRectFromMemdev(void *dest, const void *src, int xsize, int ysize, int dest_linebytes, int src_linebytes)
{
  hdma2d.Init.ColorMode = DMA2D_OUTPUT_RGB565;
  hdma2d.Init.Mode = DMA2D_M2M;
  hdma2d.Init.OutputOffset = dest_linebytes / 2 - xsize;
  HAL_DMA2D_Init(&hdma2d);
  
  hdma2d.LayerCfg[DMA2D_FOREGROUND_LAYER].InputColorMode = DMA2D_INPUT_RGB565;
  hdma2d.LayerCfg[DMA2D_FOREGROUND_LAYER].InputOffset = src_linebytes / 2 - xsize;
  HAL_DMA2D_ConfigLayer(&hdma2d, DMA2D_FOREGROUND_LAYER);
  
  HAL_DMA2D_Start(&hdma2d, (uint32_t)src, (uint32_t)dest, xsize, ysize);
  HAL_DMA2D_PollForTransfer(&hdma2d, HAL_MAX_DELAY);
}

void Screen_FillRect(int layer, int x0, int y0, int x1, int y1, uint32_t color)
{
  int height = y1 - y0 + 1;
  int width = x1 - x0 + 1;
  
  hdma2d.Init.ColorMode = DMA2D_OUTPUT_RGB565;
  hdma2d.Init.Mode = DMA2D_R2M;
  hdma2d.Init.OutputOffset = DISPLAY_WIDTH - width;
  HAL_DMA2D_Init(&hdma2d);
  
  color = ((color & 0xf800) << 8) | ((color & 0x7e0) << 5) | ((color & 0x1f) << 3); // RGB565转RGB888
  HAL_DMA2D_Start(&hdma2d, color, (uint32_t)&screen_buffer[y0][x0], width, height);
  HAL_DMA2D_PollForTransfer(&hdma2d, HAL_MAX_DELAY);
}

void Screen_Init(void)
{
  GPIO_InitTypeDef gpio;
  LTDC_LayerCfgTypeDef layer;
  PLL3_ClocksTypeDef clock_result;
  RCC_PeriphCLKInitTypeDef clock = {0};
  
  __HAL_RCC_CRC_CLK_ENABLE(); // STemWin GUI_Init前必须打开CRC
  __HAL_RCC_DMA2D_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOE_CLK_ENABLE();
  
  // PA3: LTDC_B5, PA4: LTDC_VSYNC, PA5: LTDC_R4 ,PA6: LTDC_G2
  gpio.Alternate = GPIO_AF14_LTDC;
  gpio.Mode = GPIO_MODE_AF_PP;
  gpio.Pin = GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6;
  gpio.Pull = GPIO_NOPULL;
  gpio.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(GPIOA, &gpio);
  
  // PB0: LTDC_R3, PB1: LTDC_R6
  gpio.Alternate = GPIO_AF9_LTDC;
  gpio.Pin = GPIO_PIN_0 | GPIO_PIN_1;
  HAL_GPIO_Init(GPIOB, &gpio);
  
  // PB8~9: LTDC_B6~7, PB10~11: LTDC_G4~5
  gpio.Alternate = GPIO_AF14_LTDC;
  gpio.Pin = GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11;
  HAL_GPIO_Init(GPIOB, &gpio);
  
  // PC0: LTDC_R5, PC6: LTDC_HSYNC, PC7: LTDC_G6
  gpio.Pin = GPIO_PIN_0 | GPIO_PIN_6 | GPIO_PIN_7;
  HAL_GPIO_Init(GPIOC, &gpio);
  
  // PD3: LTDC_G7, PD10: LTDC_B3
  gpio.Pin = GPIO_PIN_3 | GPIO_PIN_10;
  HAL_GPIO_Init(GPIOD, &gpio);
  
  // PE11: LTDC_G3, PE12: LTDC_B4, PE13: LTDC_DE, PE14: LTDC_CLK, PE15: LTDC_R7
  gpio.Pin = GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;
  HAL_GPIO_Init(GPIOE, &gpio);
  
  // LTDC时钟频率: 30MHz
  clock.PeriphClockSelection = RCC_PERIPHCLK_LTDC;
  clock.PLL3.PLL3FRACN = 0;
  clock.PLL3.PLL3M = 25;
  clock.PLL3.PLL3N = 150;
  clock.PLL3.PLL3P = 2;
  clock.PLL3.PLL3Q = 2;
  clock.PLL3.PLL3R = 5;
  clock.PLL3.PLL3RGE = RCC_PLL3VCIRANGE_3;
  clock.PLL3.PLL3VCOSEL = RCC_PLL3VCOWIDE;
  HAL_RCCEx_PeriphCLKConfig(&clock);
  
  HAL_RCCEx_GetPLL3ClockFreq(&clock_result);
  printf("LTDC clock: %.1fMHz\n", clock_result.PLL3_R_Frequency / 1000000.0f);
  
  __HAL_RCC_LTDC_CLK_ENABLE();
  __HAL_RCC_DMA2D_CLK_ENABLE();
  hltdc.Instance = LTDC;
  hltdc.Init.HSPolarity = LTDC_HSPOLARITY_AL;
  hltdc.Init.VSPolarity = LTDC_VSPOLARITY_AL;
  hltdc.Init.DEPolarity = LTDC_DEPOLARITY_AL;
  hltdc.Init.PCPolarity = LTDC_PCPOLARITY_IPC;
  hltdc.Init.HorizontalSync = SCREEN_HSW - 1;
  hltdc.Init.VerticalSync = SCREEN_VSW - 1;
  hltdc.Init.AccumulatedHBP = SCREEN_HSW + SCREEN_HBP - 1;
  hltdc.Init.AccumulatedVBP = SCREEN_VSW + SCREEN_VBP - 1;
  hltdc.Init.AccumulatedActiveW = SCREEN_HSW + SCREEN_HBP + SCREEN_WIDTH - 1;
  hltdc.Init.AccumulatedActiveH = SCREEN_VSW + SCREEN_VBP + SCREEN_HEIGHT - 1;
  hltdc.Init.TotalWidth = SCREEN_HSW + SCREEN_HBP + SCREEN_WIDTH + SCREEN_HFP - 1;
  hltdc.Init.TotalHeigh = SCREEN_VSW + SCREEN_VBP + SCREEN_HEIGHT + SCREEN_VFP - 1;
  hltdc.Init.Backcolor.Red = 0;
  hltdc.Init.Backcolor.Green = 0;
  hltdc.Init.Backcolor.Blue = 0;
  HAL_LTDC_Init(&hltdc);
  
  layer.WindowX0 = (SCREEN_WIDTH - DISPLAY_WIDTH) / 2;
  layer.WindowX1 = layer.WindowX0 + DISPLAY_WIDTH;
  layer.WindowY0 = (SCREEN_HEIGHT - DISPLAY_HEIGHT) / 2;
  layer.WindowY1 = layer.WindowY0 + DISPLAY_HEIGHT;
  layer.PixelFormat = LTDC_PIXEL_FORMAT_RGB565;
  layer.Alpha = 255; // 整体(窗口区域+非窗口区域)半透明度(0为透明, 255为不透明), 窗口区域不会叠加上非窗口区域的颜色
  layer.Alpha0 = 0; // 非窗口区域半透明度
  layer.BlendingFactor1 = LTDC_BLENDING_FACTOR1_PAxCA;
  layer.BlendingFactor2 = LTDC_BLENDING_FACTOR2_PAxCA;
  layer.FBStartAdress = (uint32_t)screen_buffer; // 窗口区域显示内容
  layer.ImageWidth = DISPLAY_WIDTH;
  layer.ImageHeight = DISPLAY_HEIGHT;
  layer.Backcolor.Red = 0; // 非窗口区域显示颜色
  layer.Backcolor.Green = 0;
  layer.Backcolor.Blue = 0;
  HAL_LTDC_ConfigLayer(&hltdc, &layer, LTDC_LAYER_1);
  // 为了消除开机时屏幕上随机黑线, 必须控制背光引脚
  // 默认情况下背光是关闭的, 当LTDC打开后延时几百毫秒再开背光
  
  hdma2d.Instance = DMA2D;
}

主程序:

#include 
#include 
#include 
#include "common.h"
#include "Screen.h"

int main(void)
{
  char str[30];
  GUI_MEMDEV_Handle memdev;
  GUI_RECT rect;
  
  HAL_Init();
  
  clock_init();
  usart_init(115200);
  printf("STM32H743VI LTDC\n");
  printf("SystemCoreClock=%u\n", SystemCoreClock);
  
  Screen_Init();
  GUI_Init();
  GUI_SetBkColor(GUI_LIGHTBLUE);
  GUI_Clear();
  
  GUI_SetFont(GUI_FONT_32B_ASCII);
  GUI_DispString("Hello World");
  GUI_SetBkColor(GUI_LIGHTRED);
  GUI_ClearRect(50, 50, 100, 100);
  GUI_InvertRect(80, 80, 120, 120);
  
  GUI_CopyRect(0, 0, 300, 300, 200, 40);
  memdev = GUI_MEMDEV_Create(300, 300, 200, 40);
  GUI_MEMDEV_CopyFromLCD(memdev);
  GUI_MEMDEV_Select(memdev);
  GUI_InvertRect(300, 300, 500, 340);
  GUI_MEMDEV_CopyToLCD(memdev);
  GUI_SelectLCD();
  GUI_MEMDEV_Delete(memdev);
  
  rect.x0 = 30;
  rect.y0 = 140;
  rect.x1 = 500;
  rect.y1 = 190;
  memdev = GUI_MEMDEV_Create(rect.x0, rect.y0, rect.x1 - rect.x0 + 1, rect.y1 - rect.y0 + 1);
  GUI_MEMDEV_Select(memdev);
  GUI_SetBkColor(GUI_LIGHTYELLOW);
  GUI_SetColor(GUI_DARKRED);
  while (1)
  {
    snprintf(str, sizeof(str), "Ticks: %u", HAL_GetTick() / 1000);
    GUI_ClearRectEx(&rect);
    GUI_DispStringInRect(str, &rect, GUI_TA_HCENTER | GUI_TA_VCENTER);
    GUI_MEMDEV_CopyToLCD(memdev);
    GUI_Delay(1000);
  }
}

程序使用STemWin图形库绘图。接下来说明一下STemWin是怎么移植的。

在ST官网下载的STM32H7 CubeMX库的压缩包(en.stm32cubeh7.zip)中,Middlewares的ST文件夹下有一个STemWin文件夹,这就是STemWin图形库。我们需要将其中的Lib/STemWin_CM7_wc16.a、OS/GUI_X.c、Config/GUIConf.c和h、Config/LCDConf_Lin_Template.c和h以及inc文件夹下的所有文件复制到工程中,将LCDConf_Lin_Template.c/h重命名为LCDConf_Lin.c/h。

然后将STemWin头文件所在的文件夹路径添加到项目属性中:

【STemWin】STM32H743VI单片机通过LTDC驱动4.3寸800×480高分辨率的40-pin FPC彩屏并裸机移植STemWin图形库_第11张图片

将GUI_X.c、STemWin_CM7_wc16.a、GUIConf.c和LCDConf_Lin.c添加到工程中

【STemWin】STM32H743VI单片机通过LTDC驱动4.3寸800×480高分辨率的40-pin FPC彩屏并裸机移植STemWin图形库_第12张图片

STemWin_CM7_wc16.a的文件类型需要修改为Library file:

【STemWin】STM32H743VI单片机通过LTDC驱动4.3寸800×480高分辨率的40-pin FPC彩屏并裸机移植STemWin图形库_第13张图片

现在需要修改GUIConf.c、GUI_X.c和LCDConf_Lin.c这三个文件。

GUIConf.c中主要要修改STemWin使用的内存大小和地址:

#define GUI_NUMBYTES  0x48000
static U32 aMemory[GUI_NUMBYTES / 4] __attribute__((at(0x30000000)));

我们将整个RAM1(0x30000000)都分配给STemWin用作图形处理。

GUI_X.c中主要是要实现GUI_Delay延时函数,我们将该函数和HAL库的HAL_Delay绑定就行:

GUI_TIMER_TIME GUI_X_GetTime(void) { 
  return HAL_GetTick(); 
}

void GUI_X_Delay(int ms) { 
  HAL_Delay(ms);
}

在LCDConf_Lin.c中,我们需要指定绘图区域的大小(546×480),以及颜色格式(RGB565):

//
// Physical display size
//
#define XSIZE_PHYS DISPLAY_WIDTH
#define YSIZE_PHYS DISPLAY_HEIGHT

//
// Color conversion
//
#define COLOR_CONVERSION GUICC_M565

//
// Display driver
//
#define DISPLAY_DRIVER GUIDRV_LIN_16

还有显存地址:

#ifndef   VRAM_ADDR
  #define VRAM_ADDR screen_buffer // TBD by customer: This has to be the frame buffer start address
#endif

在LCD_X_Config中,可以将一些绘图函数和DMA2D图形加速器绑定:

  //
  // Set custom functions for several operations to optimize native processes
  //
  LCD_SetDevFunc(0, LCD_DEVFUNC_COPYBUFFER, (Screen_Function)Screen_CopyBuffer);
  LCD_SetDevFunc(0, LCD_DEVFUNC_COPYRECT, (Screen_Function)Screen_CopyRect);
  //LCD_SetDevFunc(0, LCD_DEVFUNC_FILLRECT, (Screen_Function)Screen_FillRect);
  //LCD_SetDevFunc(0, LCD_DEVFUNC_DRAWBMP_8BPP, (void(*)(void))CUSTOM_LCD_DrawBitmap8bpp); 
  //LCD_SetDevFunc(0, LCD_DEVFUNC_DRAWBMP_16BPP, (void(*)(void))CUSTOM_LCD_DrawBitmap16bpp);
  
  GUI_MEMDEV_SetDrawMemdev16bppFunc(Screen_CopyRectFromMemdev);

笔者发现绑定LCD_DEVFUNC_FILLRECT后,GUI_InvertRect函数无法正常工作,于是就注释掉了那句话,取消绑定了。

到此,STemWin就移植完成了,可以运行程序查看效果:

【关于触控】

这个彩屏不带触控功能,上面的37~40脚只是4个焊点:YU XL YD XR,分别代表Y up、X left、Y down、X right,并没有连接任何触控器件。如果需要触控功能可以加装一个如下图所示的四线电阻屏或电容屏膜,接到焊点上之后通过FPC的37~40脚引出,再接一个XPT2046触控检测芯片,芯片通过SPI接口和单片机连接,就可以检测触控了。

【STemWin】STM32H743VI单片机通过LTDC驱动4.3寸800×480高分辨率的40-pin FPC彩屏并裸机移植STemWin图形库_第14张图片【STemWin】STM32H743VI单片机通过LTDC驱动4.3寸800×480高分辨率的40-pin FPC彩屏并裸机移植STemWin图形库_第15张图片

淘宝上一般的液晶屏都是用XPT2046芯片检测触控的。

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