WoodData 发表于 2025-6-11 23:55

【STM32N6570-DK】+ LCD绘图和字体显示

上期通过STM32CubeMX配置LTDC外设实现了lcd屏幕显示,本次继续深入实现lcd绘图和字体显示测试。

由于STM32N6有很大的RAM空间且连续的,这次直接使用LCD整屏RAM buffer显存显示。

LCD整屏显存需要800X*480X*2=750K。AXISRAM3\4区域各自只有448K,合起来共896K。

具体地址使用AXISRAM3\4区域RAM。由于AXISRAM3\4区域默认是无效的,需要使能时钟和电源。

!(data/attachment/forum/202506/11/233244e7jb5gn4p7p8n77m.png "image.png")

首先配置一下RAMCFG:

!(data/attachment/forum/202506/11/233549uj5jt7q73n3lagi9.png "image.png")

添加额外代码使能代码如下:

!(data/attachment/forum/202506/11/233628ifnga2ksktcvwgx2.png "image.png")

```
/* SRAM3 and SRAM4 memories clock enable */
LL_MEM_EnableClock(LL_MEM_AXISRAM3);
LL_MEM_EnableClock(LL_MEM_AXISRAM4);
LL_MEM_EnableClock(LL_MEM_AXISRAM4);
LL_MEM_EnableClock(LL_MEM_AXISRAM4);

/* Power On AXSRAM3 and AXISRAM4 */
hramcfg_SRAM3.Instance = RAMCFG_SRAM3_AXI;
HAL_RAMCFG_EnableAXISRAM(&hramcfg_SRAM3);

hramcfg_SRAM4.Instance = RAMCFG_SRAM4_AXI;
HAL_RAMCFG_EnableAXISRAM(&hramcfg_SRAM4);

hramcfg_SRAM5.Instance = RAMCFG_SRAM5_AXI;
HAL_RAMCFG_EnableAXISRAM(&hramcfg_SRAM5);

hramcfg_SRAM6.Instance = RAMCFG_SRAM6_AXI;
HAL_RAMCFG_EnableAXISRAM(&hramcfg_SRAM6);
```

设置LCD显存起始地址在

#define LCD_LAYER_0_ADDRESS               0x34200000U /* SRAM3 */

可以在LTDC初始化中添加显存地址,或者在LTDC初始化之后添加调用如下函数设置显存地址。

HAL_LTDC_SetAddress(&hltdc,(uint32_t )LCD_LAYER_0_ADDRESS,0);

然后打开CACHE和MPU设置,这里要设置显存区域为非CACHE区。否则打开CACHE后显示会不正常。

!(data/attachment/forum/202506/11/234101vubw32s9iprw2yb9.png "image.png")

!(data/attachment/forum/202506/11/234117z0ldfi3x06z632pq.png "image.png")

生成代码之后,添加绘图函数。

```
/*
* Copyright (c) 2024, 流水源 Water Source (WoodData).
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date             Author            Notes
* 2024-06          WoodData          the first version
*/
#include "lcd_buffer.h"


/**
* @briefInitialize the st7789h2 LCD component.
* @retval Component status.
*/
uint32_t lcd_buffer_init(void *args)
{

    return 0;
}


uint32_t lcd_buffer_display_on(uint8_t light)
{
    if(light)
    {
      /* Enable backlight */
      //HAL_GPIO_WritePin(LCD_BACKLIGHT_GPIO_PORT, LCD_BACKLIGHT_GPIO_PIN, GPIO_PIN_SET);
    }else
    {
      /* Deactivate backlight */
      //HAL_GPIO_WritePin(LCD_BACKLIGHT_GPIO_PORT, LCD_BACKLIGHT_GPIO_PIN, GPIO_PIN_RESET);
    }
    return 0;
}


uint32_t lcd_buffer_pixel (int16_t x,int16_t y,lcd_color_t color)
{
    if((x >= LCD_WIDTH) || (y >= LCD_HEIGHT) || (x<0) || (y<0))    return 1;
    /* Write data value to RAM memory */
    *(__IO uint16_t*) (LCD_LAYER_0_ADDRESS + (2U*((y*LCD_WIDTH) + x))) = (uint16_t)color;
    return 0;
}


uint32_t lcd_buffer_draw_bitmap (uint8_t mode, int16_t x,int16_t y,uint16_t w, uint16_t h, const uint8_t *bmp)
{
    uint16_t x0,y0;
    uint16_t *ptr;
    ptr = (uint16_t *) bmp;
    for(y0=0;y0<h;y0++)
    {
      for(x0=0;x0<w;x0++)
      {
            lcd_buffer_pixel(x+x0,y+y0,*ptr++);
      }
    }
    return 0;
}

uint32_t lcd_buffer_fill (int16_t x,int16_t y,uint16_t w, uint16_t h, lcd_color_t color)
{
    uint16_t x0,y0;

    for(y0=0;y0<h;y0++)
    {
      for(x0=0;x0<w;x0++)
      {
            lcd_buffer_pixel(x+x0,y+y0,color);
      }
    }
    return 0;
}


uint32_t lcd_buffer_draw_char_dot(uint8_t mode, int16_t x,int16_t y,uint16_t w,uint16_t h,const uint8_t *buff,lcd_color_t f_color,lcd_color_t b_color)
{
    uint32_t i, j;
    uint8_t *ptr= (uint8_t *)buff;

    if((x >= LCD_WIDTH) || (y >= LCD_HEIGHT) || ((x+w)<=0) || ((y+h)<=0))    return 1;
    switch(mode)
    {
      case    0:
            for(i=0;i<h;i++)
            {
                for(j=0;j<w;)
                {
                  if(*ptr & (0x80>>(j&0x07)))
                  {
                        lcd_buffer_pixel(x+j,y+i,f_color);
                  }else
                  {
                        lcd_buffer_pixel(x+j,y+i,b_color);
                  }
                  j++;
                  if(!(j&0x07))
                  {
                        ptr++;
                  }
                }
                if(w & 0x07)    ptr++;
            }
      
            break;
      case    1:
            for(i=0;i<h;i++)
            {
                for(j=0;j<w;)
                {
                  if(*ptr & (0x01<<(j&0x07)))
                  {
                        lcd_buffer_pixel(x+j,y+i,f_color);
                  }else
                  {
                        lcd_buffer_pixel(x+j,y+i,b_color);
                  }
                  j++;
                  if(!(j&0x07))
                  {
                        ptr++;
                  }
                }
                if(w & 0x07)    ptr++;
            }
            break;
      case    2:
            break;
      case    3:
            break;
    }
    return 0;

}


lcd_adapter_t   lcd_buffer_drv =
{
    .info.sx = 0,
    .info.sy = 0,
    .info.width = LCD_WIDTH,
    .info.height = LCD_HEIGHT,
    .info.color_bit = 16,
    .info.color_type = LCD_RGB565_16BIT,

    .func.init = lcd_buffer_init,
    .func.flush = NULL,
    .func.get_backlight = NULL,
    .func.set_backlight = lcd_buffer_display_on,

    .draw.get_point = NULL,
    .draw.set_point = lcd_buffer_pixel,
    .draw.clear= NULL,
    .draw.h_line = NULL,
    .draw.v_line = NULL,
    .draw.line = NULL,
    .draw.fill = lcd_buffer_fill,
    .draw.draw_bmp = lcd_buffer_draw_bitmap,
    .draw.draw_char_bmp = lcd_buffer_draw_char_dot, //
};



```

主函数测试代码如下:

```
int main(void)
{
uint32_t i;
/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

/* Enable the CPU Cache */

/* Enable I-Cache---------------------------------------------------------*/
SCB_EnableICache();

/* Enable D-Cache---------------------------------------------------------*/
SCB_EnableDCache();

/* MCU Configuration--------------------------------------------------------*/

/* MPU Configuration--------------------------------------------------------*/
MPU_Config();
HAL_Init();

/* USER CODE BEGIN Init */

/* USER CODE END Init */

/* Configure the system clock */
SystemClock_Config();

/* Configure the peripherals common clocks */
PeriphCommonClock_Config();

/* USER CODE BEGIN SysInit */

/* USER CODE END SysInit */

/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_RAMCFG_Init();
MX_I2C1_Init();
MX_I2C2_Init();
MX_LTDC_Init();
MX_USART1_UART_Init();
MX_XSPI1_Init();
SystemIsolation_Config();
/* USER CODE BEGIN 2 */


/* USER CODE END 2 */

/* Infinite loop */
/* USER CODE BEGIN WHILE */

HAL_LTDC_SetAddress(&hltdc,(uint32_t )LCD_LAYER_0_ADDRESS,0);
lcd_display_init(&lcd_buffer_drv);

lcd_clear(LCD_WHITE);
HAL_Delay(300);
lcd_clear(LCD_BLACK);
HAL_Delay(300);
lcd_clear(LCD_RED);
HAL_Delay(300);
lcd_clear(LCD_GREEN);
HAL_Delay(300);
lcd_clear(LCD_BLUE);
HAL_Delay(300);

for(i=0;i<10;i++){ lcd_draw_line(10+i*50,0,10+i*50,480,LCD_RED);HAL_Delay(200);}
for(i=0;i<10;i++){lcd_draw_line(0,10+i*40,800,10+i*40,LCD_GREEN);HAL_Delay(200);}
for(i=0;i<10;i++){lcd_draw_circle(400,240,10+i*20,LCD_YELLOW);HAL_Delay(200);}
for(i=0;i<10;i++){lcd_draw_rect(400-i*20,240-i*20,i*40,i*40,LCD_MAGENTA);HAL_Delay(200);}

lcd_fill_rect(80,30,80,80,LCD_RED);
lcd_draw_image(30,80,80,80,(uint8_t *)c_bmp_muzhu_RGB565);

lcd_set_font_window(0,0,lcd_get_width(),lcd_get_height());
lcd_set_font(&Font_ASCII16X8_default);
lcd_disp_str_at(200,200,"STM32N6-DK");
lcd_disp_str_at(250,250,"WoodData");

lcd_set_font(&Font_ASCII24x40);
lcd_disp_str_at(20,20,"Font_ASCII24x40");
HAL_Delay(5000);
while (1)
{
      lcd_clear(LCD_RED);
      HAL_Delay(1000);
      lcd_clear(LCD_GREEN);
      HAL_Delay(1000);
      lcd_clear(LCD_BLUE);
      HAL_Delay(1000);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
```

下面编译烧录查看显示效果:

!(data/attachment/forum/202506/11/235309sphndnnosojnhpb9.png "image.png")

!(data/attachment/forum/202506/11/235401nob35ao8j88pa330.png "1749657207725.png")

!(data/attachment/forum/202506/11/235411odedgg3wwjewtjji.png "1749657230352.png")

!(data/attachment/forum/202506/11/235422csbdj39ow4j4b4nw.png "1749657223803.png")

[!(/source/plugin/zhanmishu_markdown/template/editor/images/upload.svg) 附件:n6_lcd.zip](forum.php?mod=attachment&aid=2413212 "attachment")

minzisc 发表于 2025-6-15 21:44

利用LTDC和DMA2D提升显示效率,双缓冲避免闪烁。

timfordlare 发表于 2025-6-16 09:36

RGB 接口数据线需等长布线,避免信号延迟差异

elsaflower 发表于 2025-6-16 10:00

如果使用自定义字体库,需要将字体数据加载到显存中,并编写相应的显示函数。

wengh2016 发表于 2025-6-16 10:44

针对支持DMA的LCD接口,使用DMA加速数据传输。

51xlf 发表于 2025-6-16 11:23

STM32芯片本身没有直接支持汉字显示的功能,因此需要借助外部字库。

jtracy3 发表于 2025-6-16 14:44

绘图操作(如画圆、填充)需临时缓冲区,避免覆盖帧缓冲区,建议使用栈或堆

mmbs 发表于 2025-6-16 15:05

提前建立显示缓冲区,避免频繁操作LCD。

ingramward 发表于 2025-6-16 15:51

选择一个适合STM32的图形库,如u8g2、lvgl、TouchGFX等

maqianqu 发表于 2025-6-16 16:18

如果需要显示复杂的图形或动画,可以考虑使用硬件加速(如DMA2D或GPU)来提高性能。

modesty3jonah 发表于 2025-6-16 17:59

使用前缓冲(显示)和后缓冲(绘制),避免画面撕裂。切换缓冲时通过 LCD_WriteReg() 写入新的帧缓冲地址。

yeates333 发表于 2025-6-16 18:50

合理配置驱动参数,优化绘图与字体渲染逻辑,控制资源占用。

loutin 发表于 2025-6-16 19:15

使用GB2312编码索引,可建立结构体数组。

yorkbarney 发表于 2025-6-16 19:34

使用合适的LCD驱动程序,根据LCD控制器的特性和数据手册进行配置。

51xlf 发表于 2025-6-16 20:41

优化绘图和字体显示算法,减少不必要的计算和内存访问。

pixhw 发表于 2025-6-16 21:18

启用双缓冲或调整刷新率            

yeates333 发表于 2025-6-16 21:52

通过去耦电容、差分信号等措施抑制干扰

loutin 发表于 2025-6-16 22:18

开启缓存(CACHE)和内存保护单元(MPU),并确保显存区域设置为非缓存区,以避免显示异常。

uytyu 发表于 2025-6-17 10:25

LCD的刷新率足够高,以避免闪烁和拖影。

fengm 发表于 2025-6-17 10:57

可简化文本渲染和其他图形元素的创建。
页: [1] 2 3
查看完整版本: 【STM32N6570-DK】+ LCD绘图和字体显示