STM32CubeMX系列教程22:LCD-TFT控制器(LDTC)

来自丢石头百科


一、LTDC简介

        LCD-TFT(液晶显示器 —— 薄膜晶体管)显示器控制器提供并行数字 RGB(红色、绿色、蓝色)以及水平同步、垂直同步、像素时钟和数据使能信号,这些信号直接输出到不同 LCD和 TFT 面板的接口。本章通过Open746I-C开发板控制微雪7inch Capacitive Touch LCD (F)型LCD。其原理图如下: (注:本章介绍的是通过LDTC控制RGB接口的屏幕,即屏幕是不带控制器的,与带控制芯片的8080系列接口的屏幕不同)



<a class="attach" href="portal.php?mod=attachment&id=549" target="_blank">7inch-Capacitive-Touch-LCD-F-Schematic.pdf


LTDC框图如下,最高24条RGB数据线(RGB888),两个可以显示图层,可以进行图像层叠加处理。


143502ah5fpi5oi6zhpiy2.png


143503v370fc5l4r4f3p7l.png


143503tc4447u1bxccq113.png



RGB时序图如下,RGB接口的屏幕是通过MCU不断发送显示数据到屏幕,逐行扫描显示。

VSYNC: 帧同步信号,表示扫描1帧的开始,一帧也就是LCD显示的一个画面。 HSYNC: 行同步信号,表示扫描1行的开始。VDEN:数据使能信号。



143503olraouprnajpljed.png


143504acu1rc1q007iq7u7.png


        MCU会用一部分内存来存储显示的数据,即显存,LDTC控制器不断将显存内的数据发送到屏幕扫描显示。屏幕是通过MCU扫描显示的,要改变显示的图片,只需要改变显存的数据即可。而显存的数据存储的格式和图像的格式有关,例如ARGB8888,RGB888,RGB565等,其中A为透明度,R表示红色,G表示绿色,B表示蓝色。例如RGB565格式的图形,一个像素点只需两个字节存储。ARGB8888一个像素点要四个字节存储。


143505thxr9heh1f7rf9ol.png


二、DMA2D简介

        Chrom-Art Accelerator™ (DMA2D) 是专用于图像处理的专业 DMA。由前面介绍可知,显示的图形是通过一定格式存储在内存中,要改变显示的内容,只需将新的数据存储在对应的显存中即可,可以通过DMA传输数据。DMA2D和普通的DMA通道不一样是,它是专用于图像处理的专用DMA,他可以执行下列操作。


  • 用特定颜色填充目标图像的一部分或全部* 将源图像的一部分(或全部)复制到目标图像的一部分(或全部)中* 通过像素格式转换将源图像的一部分或全部复制到目标图像的一部分或全部中* 将像素格式不同的两个源图像部分和 / 或全部混合,再将结果复制到颜色格式不同的部分或整个目标图像中。


三、显示图片实验。


       实验中用SDRAM作为屏幕的显存, 复制上一章SDRAM的工程,修改文件夹名。击STM32F746I.ioc打开STM32cubeMX的工程文件重新配置,LTDC接口类型选择RGB888(24 bits)。启动DMA2D。


143505az6q6oqqkt599hph.png


143505o2lh36pipe0ybzib.png

配置引脚如下, 其中PA3是PWM的背光,这里简单的做为GPIO_OUT进行初始化,设置用户标签为BL。 注意:由于CubeMX会默认帮你选择好相关的引脚,但有时和你实际的硬件有冲突(它认为它是最优的,但有时会觉得这功能很烦),这时需要强制配置某些引脚的功能(比较花时间的一步,需要对着原理图仔细检查是否有错误)


143505rshbz9b7iz5fz5bb.png


配置时钟,系统时钟为216MHz,LCD-TFT为32MHz,如果LCD时钟过高会刷新不过来出现花屏,如果频率过低显示的图像会出现闪烁。


143506zwxtyzc02yc1002x.png



DMA2D主要配置Color Mode 和 DMA2D Input Color Mode。Color Mode为显存的存储格式,此处为RGB565。DMA2D Input Color Mode为源图像的格式,此处配置为RGB565。如果要显示的图形为RGB888格式,则设置为RGB888,DMA2D会转为显存的格式(此处为RGB565)再存储到显存中。



143507g1o22dkdxdo2omkf.png



LTDC参数设置,实验LCD分辨率是1024X600,所以Active Width和Active Height 分别为1024和600。 关于Synchronization for Width和Synchronizaion for Height里剩余的6个参数为SYNC的时序值,需要按你的LCD数据手册进行调整。实际很多LCD供应商都没有提供像样数据手册,但没数据手册也没关系, 如果你使用下面的数据,发现显示不是全屏,其实只要在下面参数的基础上进行微调就可以了。多改几次参数,看一下现像就懂了,不多解释。使用下面的参数,显示效果基本都在可接受的范围内。


143507n5x5ne2l5sos6pk6.png



简单科普一下: 一般的LCD都有DE和SYNC模式。如果使用的是DE模式(一般LCD模块默认是DE模式),对下面SYNC的参数就没有具体要求(想怎么填就怎么填),如果使用SYNC模式,那就乖乖的调吧。


可能有人会问,怎么知道自己的LCD模块是什么模式,以上面的LCD的数据手册结合原理图可知:


R22 = NC ,R23 = 10K时为SYNC模式 R22 = 10K,R23 = NC 时为DE模式


(不要问我怎么有数据手册的,地上捡的!)


143507kq2m6b72m2dphkdm.png

文件:34185A9A45F642A6AB506ACED0C47D9F


143508ijfz550zphh0n366.png



143508rg6rg6rf5jy56wrj.png


好像讲的太远了,回来回来。。。。 图层配置如下:



143509u8pnbnqob8d2dxp8.png


下面详细说明,各个选项的配置:


Windows Position栏设置显示窗口的位置大小,此处设置图层1,图层2大小为512x300,(0,0)位置开始。显存显示显示格式为RGB565。


143509a332zhhw88hbdhhh.png



Alpha constant for blending相当于设置层的透明度,0xff表示透明度为100%,0x00表示透明度为0(不显示)。其中layer0为底层,layer1为顶层,顶层如果有显示,就会遮盖底层,即如果把顶层的透明度设置为0xff,那就可以100%遮盖底层,此处设置为0x7F,两个图层叠加显示。(有用过photoshop之类的软件,应该比较容易理解层的概念)


其它实际也没什么好说的,注意一下分配显存的地址空间,此处使用SDRAM作为显存,由上一章可知Open746I-C开发板SDRAM接到区域2,起始地址为0xD000 0000。由于有两个层,注意内存地址间要有一定的间距。


BackGroun Color为背景颜色,此处为默认黑色。


注意:LDTC输出管脚设置GPIO Settings需要把所有LCD管脚的最大输出速度(Maximum output speed)设置为高速(High),否则会导致LCD刷新不过来等问题。


143509z3lcq8log8olzt23.png



在CORTEX_M7 Configuration中,把CPU ICache和CPU DCache使能,其它保持默认。使用ICache和DCache可以大幅度提高程序的运行速度。


143510he9m84cr9hxq97mm.png


在DMA2里配置一个从内存到内存的DMA通道,其它保持默认


143510f11usx6iyb6aw5b9.png


在Pin Configuration里,把背光默认设置为输出低电平,否则屏幕背光不亮不显示图像。


143511tgamrj8tr9ba1aag.png



生成报告以及初始化代码,编译程序。若程序没有出错,下载这个两个头文件添加进工程目录Inc文件夹里面。



<a class="attach" href="portal.php?mod=attachment&id=551" target="_blank">image1.h <a class="attach" href="portal.php?mod=attachment&id=552" target="_blank">image2.h


这个两头文件保存的是图像信息,是通过STemWin中的BmpCvt.exe软件将图片转换C语言的。 此软件可在固件库里面找到:STM32Cube_FW_F7_V1.3.0\Middlewares\ST\STemWin\Software


143511f7ve56arrapz3v18.png


下面简单讲解一下如何将图片转为C语言。以.jpg格式的图片为例。 先用Windows自带的画图工具打开图片,调整图片的像素大小,然后另存为BMP 图片。


143511cvz4vtatkzzkzk92.png


用BmpCvt.exe软件打开BMP图片,选择File->Save as ... 另存为,保存类型选择位图。


143512jgibimitylur56qn.png


图片格式选择为RGB565格式,红蓝交换。


143512s0mhhcpcci7dktj0.png


添加头文件,将刚才的图片包含进工程


/* USER CODE BEGIN Includes */
#include "stm32746g_sdram.h"
#include "image1.h"
#include "image2.h"
/* USER CODE END Includes */


再main函数里面,while循环前面添加应用程序。程序中先初始化SDRAM,然后通过DMA2D将要显示的图片传输到显存中,图片的大小为512 x 300。上面的设置Layer 0显存起始地址为0xD0000 0000,Layer 1显存起始地址为0xD0100 0000。



/* USER CODE BEGIN 2 */
printf("\r\n LDTC DMA2D example !!!\r\n");
/* Program the SDRAM external device */
/*##-1- Configure the SDRAM device #########################################*/
/* SDRAM device configuration */
BSP_SDRAM_Init();
 
/*##-2- Start DMA2D transfer ###############################################*/  
if(HAL_DMA2D_Start_IT(&amp;hdma2d, (uint32_t)image1, (uint32_t)0xD0000000, 512, 300) != HAL_OK)  //(uint32_t)SDRAM_DEVICE_ADDR
{
}  
HAL_Delay(500);
 
if(HAL_DMA2D_Start_IT(&amp;hdma2d, (uint32_t)image2, (uint32_t)0xD0100000, 512, 300) != HAL_OK)  //(uint32_t)SDRAM_DEVICE_ADDR
{
}  
/* USER CODE END 2 */

编译程序并下载到开发板中,接上屏幕,可以看到右上角显示两张图片层叠。 上面显示的为静态图片,下载添加程序可以让图片动起来。在while循环中添加如下程序。



/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
 
/* USER CODE BEGIN 3 */
    for (tobuttom = 1; tobuttom < 181; tobuttom++)
    {
        /* move the picture */
        /* reconfigure the layer1 position */
        HAL_LTDC_SetWindowPosition(&amp;hltdc, (tobuttom*4), 100, 0); 
        /* reconfigure the layer2 position */
        HAL_LTDC_SetWindowPosition(&amp;hltdc, (720 - (tobuttom*4)), 200, 1); 
        HAL_Delay(50);
    }
    HAL_Delay(500);
    for (totop = 1; totop < 181; totop++)
    {
        /* move the picture */
        /* reconfigure the layer1 position */
        HAL_LTDC_SetWindowPosition(&amp;hltdc, (720 - (totop*4)), 100, 0); 
        /* reconfigure the layer2 position */
        HAL_LTDC_SetWindowPosition(&amp;hltdc, (totop*4), 200, 1); 
        HAL_Delay(50);
    }
    HAL_Delay(500);
}
/* USER CODE END 3 */


程序中调用HAL_LTDC_SetWindowPosition函数设置图片显示的位置。通过不断这是图片的显示位置可以让图片实现左右移动的效果。最后添加声明变量。



/* USER CODE BEGIN 1 */
uint32_t tobuttom = 0;
uint32_t totop = 0;
/* USER CODE END 1 */


重新编译程序并下载到开发板中,可以看到两个张图片左右移动,并且可以层叠显示。


四、显示字符程序             复制刚才的的工程修改文件夹名。打开工程重新配置LDTC,显示窗口设置为1024x600,单层显示。



143512z6ikdyeyd1gbkveb.png



143512vt29hh4thguh4guv.png


生成报告以及初始化代码,编译程序。若程序没有出错,下载下面的应用程序解压并添加进工程中。



<a class="attach" href="portal.php?mod=attachment&id=550" target="_blank">746_LCD.zip


打开原来的工程目录,添加一个BSP和一个Fonts文件夹


143513ztcyo97cupp0hi94.png


BSP文件夹中包含sdram驱动文件和lcd驱动文件,Fonts文件夹中包含各种大小的字体。


143513glmrvvrtrorlyrqh.png

143513knvzup3339zp9ii1.png


将这两个目录中的路径添加到工程中,添加stm32746_lcd.c文件。


143513u78z1lihh1f2t88t.png

在stm32746g_lcd.h头文件中可以看到LCD的控制操作函数,包括显示字符,画图等操作。 删除前面添加的应用程序,在main.c中添加头文件导入stm32746_lcd驱动文件。



/* USER CODE BEGIN Includes */
#include "stm32746g_sdram.h"
#include "stm32746g_LCD.h"
/* USER CODE END Includes */


在main函数中添加应用程序,程序首先初始化SDRAM,然后初始化LCD。设置单层显示,红色背景,蓝色字体,最后在屏幕上显示字符串。



/* USER CODE BEGIN 2 */
BSP_SDRAM_Init();   
BSP_LCD_Init();
 
BSP_LCD_SelectLayer(1);
BSP_LCD_SetLayerVisible(1, DISABLE);
 
/* Set Foreground Layer */
BSP_LCD_SelectLayer(0);
BSP_LCD_SetBackColor(LCD_COLOR_RED);
BSP_LCD_SetTextColor(LCD_COLOR_BLUE);
BSP_LCD_Clear(LCD_COLOR_RED);
 
BSP_LCD_SetFont(&amp;Font24);
BSP_LCD_DisplayStringAtLine(1, (uint8_t*)" WaveShare Open7XXI-C  Board"); 
BSP_LCD_DisplayStringAtLine(3, (uint8_t*)" www.waveshare.com  "); 
BSP_LCD_DisplayStringAtLine(4, (uint8_t*)" {{SERVERNAME}}  "); 
BSP_LCD_DisplayStringAtLine(6, (uint8_t*)" 7inch 1024x600 LCD"); 
/* USER CODE END 2 */

编译程序并下载到开发板上可以看到 LCD屏幕上显示对应的字符。


五、滚动显示字符。

             复制刚才的的工程修改文件夹名,打开工程keil 工程。


<a class="attach" href="portal.php?mod=attachment&id=586" target="_blank">Log.zip        


下载上面应用程序解压到工程目录下,Log包含下面三个文件,把lcd_log.c添加到工程中,并添加log文件夹路径到工程中。


143514nar36uyulglftbaa.png


在main文件中添加 lcd_log.h头文件。



/* USER CODE BEGIN Includes */
#include "stm32746g_sdram.h"
#include "stm32746g_LCD.h"
#include "lcd_log.h"
/* USER CODE END Includes */

打开 lcd_log_comf.h头文件可以看到如下代码。修改宏定义可以设置字体大小和颜色以及显示行数。


154201xthpdt6sish3tsdu.png

代码中以及重构了fputc函数,所以使用printf函数输出时,实际上是输出到显示屏上显示,不在是通过串口发送到电脑上显示。故把usart.c中的函数重构注释删掉或者注释掉。

/* USER CODE BEGIN 1 */
//#ifdef __GNUC__
//  /* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
//     set to 'Yes') calls __io_putchar() */
//  #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
//#else
//  #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
//#endif /* __GNUC__ */
///**
//  * @brief  Retargets the C library printf function to the USART.
//  * @param  None
//  * @retval None
//  */
//PUTCHAR_PROTOTYPE
//{
//  /* Place your implementation of fputc here */
//  /* e.g. write a character to the EVAL_COM1 and Loop until the end of transmission */
//  HAL_UART_Transmit(&amp;huart1, (uint8_t *)&amp;ch, 1, 0xFFFF);
 
//  return ch;
//}
/* USER CODE END 1 */

在main函数中添加以下应用程序,程序先初始化SDRAM以及LCD,设置单层显示。

/* USER CODE BEGIN 2 */
/* Initialize the SDRAM */
BSP_SDRAM_Init(); 
/* Initialize the LCD */  
BSP_LCD_Init();
 
BSP_LCD_SetLayerVisible(1, DISABLE);
BSP_LCD_SelectLayer(0);
 
/* Initialize LCD Log module */
LCD_LOG_Init();
 
/* Show Header and Footer texts */
LCD_LOG_SetHeader((uint8_t *)"Waveshare Electronics");
LCD_LOG_SetFooter((uint8_t *)"WaveShare Open7XXI-C board");
/* USER CODE END 2 */
 
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
 
/* USER CODE BEGIN 3 */
    LCD_UsrLog ("  Hello World ... %d\n",i);
    LCD_ErrLog ("  Hello World ... %d\n",i);
    LCD_DbgLog ("  Hello World ... %d\n",i++);
    HAL_Delay(1000);
}
/* USER CODE END 3 */

最后添加变量 i。

  /* USER CODE BEGIN 1 */
    int i =0;
  /* USER CODE END 1 */

编译程序并下载到开发板上可以看到 LCD屏幕滚动显示字符。