一、LTDC简介 (注:本章介绍的是通过LDTC控制RGB接口的屏幕,即屏幕是不带控制器的,与带控制芯片的8080系列接口的屏幕不同) LTDC框图如下,最高24条RGB数据线(RGB888),两个可以显示图层,可以进行图像层叠加处理。 RGB时序图如下,RGB接口的屏幕是通过MCU不断发送显示数据到屏幕,逐行扫描显示。 VSYNC: 帧同步信号,表示扫描1帧的开始,一帧也就是LCD显示的一个画面。 HSYNC: 行同步信号,表示扫描1行的开始。 VDEN:数据使能信号。 MCU会用一部分内存来存储显示的数据,即显存,LDTC控制器不断将显存内的数据发送到屏幕扫描显示。屏幕是通过MCU扫描显示的,要改变显示的图片,只需要改变显存的数据即可。而显存的数据存储的格式和图像的格式有关,例如ARGB8888,RGB888,RGB565等,其中A为透明度,R表示红色,G表示绿色,B表示蓝色。例如RGB565格式的图形,一个像素点只需两个字节存储。ARGB8888一个像素点要四个字节存储。 二、DMA2D简介 Chrom-Art Accelerator™ (DMA2D) 是专用于图像处理的专业 DMA。由前面介绍可知,显示的图形是通过一定格式存储在内存中,要改变显示的内容,只需将新的数据存储在对应的显存中即可,可以通过DMA传输数据。DMA2D和普通的DMA通道不一样是,它是专用于图像处理的专用DMA,他可以执行下列操作。
三、显示图片实验。 实验中用SDRAM作为屏幕的显存, 复制上一章SDRAM的工程,修改文件夹名。击STM32F746I.ioc打开STM32cubeMX的工程文件重新配置,LTDC接口类型选择RGB888(24 bits)。启动DMA2D。 配置引脚如下, 其中PA3是PWM的背光,这里简单的做为GPIO_OUT进行初始化,设置用户标签为BL。 注意:由于CubeMX会默认帮你选择好相关的引脚,但有时和你实际的硬件有冲突(它认为它是最优的,但有时会觉得这功能很烦),这时需要强制配置某些引脚的功能(比较花时间的一步,需要对着原理图仔细检查是否有错误) 配置时钟,系统时钟为216MHz,LCD-TFT为32MHz,如果LCD时钟过高会刷新不过来出现花屏,如果频率过低显示的图像会出现闪烁。 DMA2D主要配置Color Mode 和 DMA2D Input Color Mode。Color Mode为显存的存储格式,此处为RGB565。DMA2D Input Color Mode为源图像的格式,此处配置为RGB565。如果要显示的图形为RGB888格式,则设置为RGB888,DMA2D会转为显存的格式(此处为RGB565)再存储到显存中。 LTDC参数设置,实验LCD分辨率是1024X600,所以Active Width和Active Height 分别为1024和600。 关于Synchronization for Width和Synchronizaion for Height里剩余的6个参数为SYNC的时序值,需要按你的LCD数据手册进行调整。实际很多LCD供应商都没有提供像样数据手册,但没数据手册也没关系, 如果你使用下面的数据,发现显示不是全屏,其实只要在下面参数的基础上进行微调就可以了。多改几次参数,看一下现像就懂了,不多解释。使用下面的参数,显示效果基本都在可接受的范围内。 简单科普一下: 一般的LCD都有DE和SYNC模式。如果使用的是DE模式(一般LCD模块默认是DE模式),对下面SYNC的参数就没有具体要求(想怎么填就怎么填),如果使用SYNC模式,那就乖乖的调吧。 可能有人会问,怎么知道自己的LCD模块是什么模式,以上面的LCD的数据手册结合原理图可知: R22 = NC ,R23 = 10K时为SYNC模式 R22 = 10K,R23 = NC 时为DE模式 (不要问我怎么有数据手册的,地上捡的!) 下面详细说明,各个选项的配置: Windows Position栏设置显示窗口的位置大小,此处设置图层1,图层2大小为512x300,(0,0)位置开始。显存显示显示格式为RGB565。 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刷新不过来等问题。 在CORTEX_M7 Configuration中,把CPU ICache和CPU DCache使能,其它保持默认。使用ICache和DCache可以大幅度提高程序的运行速度。 在DMA2里配置一个从内存到内存的DMA通道,其它保持默认 在Pin Configuration里,把背光默认设置为输出低电平,否则屏幕背光不亮不显示图像。 生成报告以及初始化代码,编译程序。若程序没有出错,下载这个两个头文件添加进工程目录Inc文件夹里面。 这个两头文件保存的是图像信息,是通过STemWin中的BmpCvt.exe软件将图片转换C语言的。 此软件可在固件库里面找到:STM32Cube_FW_F7_V1.3.0\Middlewares\ST\STemWin\Software 下面简单讲解一下如何将图片转为C语言。以.jpg格式的图片为例。 先用Windows自带的画图工具打开图片,调整图片的像素大小,然后另存为BMP 图片。 用BmpCvt.exe软件打开BMP图片,选择File->Save as ... 另存为,保存类型选择位图。 图片格式选择为RGB565格式,红蓝交换。 添加头文件,将刚才的图片包含进工程 /* 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(&hdma2d, (uint32_t)image1, (uint32_t)0xD0000000, 512, 300) != HAL_OK) //(uint32_t)SDRAM_DEVICE_ADDR { } HAL_Delay(500); if(HAL_DMA2D_Start_IT(&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(&hltdc, (tobuttom*4), 100, 0); /* reconfigure the layer2 position */ HAL_LTDC_SetWindowPosition(&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(&hltdc, (720 - (totop*4)), 100, 0); /* reconfigure the layer2 position */ HAL_LTDC_SetWindowPosition(&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,单层显示。 生成报告以及初始化代码,编译程序。若程序没有出错,下载下面的应用程序解压并添加进工程中。 打开原来的工程目录,添加一个BSP和一个Fonts文件夹 BSP文件夹中包含sdram驱动文件和lcd驱动文件,Fonts文件夹中包含各种大小的字体。 将这两个目录中的路径添加到工程中,添加stm32746_lcd.c文件。 在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(&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*)" www.waveshare.net "); BSP_LCD_DisplayStringAtLine(6, (uint8_t*)" 7inch 1024x600 LCD"); /* USER CODE END 2 */
编译程序并下载到开发板上可以看到 LCD屏幕上显示对应的字符。 五、滚动显示字符。 复制刚才的的工程修改文件夹名,打开工程keil 工程。 下载上面应用程序解压到工程目录下,Log包含下面三个文件,把lcd_log.c添加到工程中,并添加log文件夹路径到工程中。 在main文件中添加 lcd_log.h头文件。 /* USER CODE BEGIN Includes */ #include "stm32746g_sdram.h" #include "stm32746g_LCD.h" #include "lcd_log.h" /* USER CODE END Includes */ 代码中以及重构了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(&huart1, (uint8_t *)&ch, 1, 0xFFFF); // return ch; //} /* USER CODE END 1 */ /* 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 */ /* USER CODE BEGIN 1 */ int i =0; /* USER CODE END 1 */ |