返回列表 发新帖我要提问本帖赏金: 50.00元(功能说明)

[STM32L4] 基于STM32L4R5 LCD刷新率优化

[复制链接]
 楼主| 怀揣少年梦 发表于 2024-5-9 13:16 | 显示全部楼层 |阅读模式
<
本帖最后由 怀揣少年梦 于 2024-5-13 12:37 编辑

申请原创;申请原创;申请原创;@21ic小跑堂 @21ic小跑堂 @21ic小跑堂
本文章主要是记录一下优化LCD刷新率的过程。
一般情况,嵌入式工程师大部分使用的是LCD屏,其中LCD也分好几种,就拿常用的LCD_TFT来说,与普通液晶显示器相比,TFT LCD能提供非常清晰的图像/文字,响应时间更短,非常省电、高亮度也做得非常不错,那么本次测试将使用这个屏幕,进行刷新率的优化。我将通过三种方法,优化刷新率。
一、硬件
1、NUCLEO-L4R5 DEMO板
2、2.5寸 TFT-LCD 显示屏(ILI9341驱动芯片)模块3、8根杜邦线(主要用于与LCD屏连接)
4、Type-A的USB线
二、LCD屏模块显示图像原理
TFT-LCD内部驱动原理主要是使用FET场效应管产生驱动电压,进而使得点亮像素点。e而TFT-LCD显示屏一共包含有240*320的像素点,而每个像素有红绿蓝3个子像素点,混合这3种颜色即可显示各种颜色。只需要像素点亮起来就会显示相应的图案和颜色。并且显示屏模块内部已集成DDRAM,也就是显存。如图,要使LCD显示内容,只需要通过SPI把显示的内容放到这个显存就可以了。
DDRAM.png
因此优化SPI传输数据的速率就可以优化LCD的刷新率。
三、驱动编写
对于这部分的驱动,商家已经提供了DEMO,并且网上也有很多大佬出了很多教程,那么就直接移植相关代码即可。
驱动部分代码如下:
  1. /* @attention
  2.   *
  3.   * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
  4.   * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
  5.   * TIME. AS A RESULT, QD electronic SHALL NOT BE HELD LIABLE FOR ANY
  6.   * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
  7.   * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
  8.   * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
  9. **************************************************************************************************/   
  10. #include "lcd.h"
  11. #include "spi.h"
  12. #include <stdio.h>
  13. #
  14. //管理LCD重要参数
  15. //默认为竖屏
  16. _lcd_dev lcddev;
  17. extern DMA_HandleTypeDef hdma_spi1_tx;
  18. //画笔颜色,背景颜色
  19. uint16_t POINT_COLOR = 0x0000,BACK_COLOR = 0xFFFF;
  20. uint16_t DeviceCode;

  21. uint8_t LCD_SPI_WriteByte(uint8_t TxData) {
  22.     uint8_t RxData = 0X00;


  23.     if(HAL_SPI_TransmitReceive(LCD_SPI_Handle, &TxData, &RxData, 1, 100) != HAL_OK)
  24.     {
  25.         RxData = 0XFF;
  26.     }
  27.     return RxData;
  28. }
  29. /*****************************************************************************
  30. * [url=home.php?mod=space&uid=139335]@name[/url]       :void LCD_WR_REG(uint8_t data)
  31. * [url=home.php?mod=space&uid=212281]@date[/url]       :2018-08-09
  32. * [url=home.php?mod=space&uid=42490]@function[/url]   :Write an 8-bit command to the LCD screen
  33. * [url=home.php?mod=space&uid=2814924]@parameters[/url] :data:Command value to be written
  34. * @retvalue   :None
  35. ******************************************************************************/
  36. void LCD_WR_REG(uint8_t data)
  37. {
  38.    LCD_CS_CLR;     
  39.    LCD_RS_CLR;
  40.    LCD_SPI_WriteByte(data);
  41.    LCD_CS_SET;   
  42. }

  43. /*****************************************************************************
  44. * [url=home.php?mod=space&uid=139335]@name[/url]       :void LCD_WR_DATA(uint8_t data)
  45. * [url=home.php?mod=space&uid=212281]@date[/url]       :2018-08-09
  46. * [url=home.php?mod=space&uid=42490]@function[/url]   :Write an 8-bit data to the LCD screen
  47. * [url=home.php?mod=space&uid=2814924]@parameters[/url] :data:data value to be written
  48. * @retvalue   :None
  49. ******************************************************************************/
  50. void LCD_WR_DATA(uint8_t data)
  51. {
  52.    LCD_CS_CLR;
  53.    LCD_RS_SET;
  54.    LCD_SPI_WriteByte(data);
  55.    LCD_CS_SET;
  56. }

  57. /*****************************************************************************
  58. * [url=home.php?mod=space&uid=139335]@name[/url]       :void LCD_WriteReg(uint8_t LCD_Reg, uint16_t LCD_RegValue)
  59. * [url=home.php?mod=space&uid=212281]@date[/url]       :2018-08-09
  60. * [url=home.php?mod=space&uid=42490]@function[/url]   :Write data into registers
  61. * [url=home.php?mod=space&uid=2814924]@parameters[/url] :LCD_Reg:Register address
  62.                 LCD_RegValue:Data to be written
  63. * @retvalue   :None
  64. ******************************************************************************/
  65. void LCD_WriteReg(uint8_t LCD_Reg, uint16_t LCD_RegValue)
  66. {   
  67.     LCD_WR_REG(LCD_Reg);  
  68.     LCD_WR_DATA(LCD_RegValue);                 
  69. }      

  70. /*****************************************************************************
  71. * [url=home.php?mod=space&uid=139335]@name[/url]       :void LCD_WriteRAM_Prepare(void)
  72. * [url=home.php?mod=space&uid=212281]@date[/url]       :2018-08-09
  73. * [url=home.php?mod=space&uid=42490]@function[/url]   :Write GRAM
  74. * [url=home.php?mod=space&uid=2814924]@parameters[/url] :None
  75. * @retvalue   :None
  76. ******************************************************************************/     
  77. void LCD_WriteRAM_Prepare(void)
  78. {
  79.     LCD_WR_REG(lcddev.wramcmd);
  80. }     

  81. /*****************************************************************************
  82. * [url=home.php?mod=space&uid=139335]@name[/url]       :void Lcd_WriteData_16Bit(uint16_t Data)
  83. * [url=home.php?mod=space&uid=212281]@date[/url]       :2018-08-09
  84. * [url=home.php?mod=space&uid=42490]@function[/url]   :Write an 16-bit command to the LCD screen
  85. * [url=home.php?mod=space&uid=2814924]@parameters[/url] :Data:Data to be written
  86. * @retvalue   :None
  87. ******************************************************************************/     
  88. void Lcd_WriteData_16Bit(uint16_t Data)
  89. {   
  90.    LCD_CS_CLR;
  91.    LCD_RS_SET;  
  92.    LCD_SPI_WriteByte(Data>>8);
  93.    LCD_SPI_WriteByte(Data);
  94.    LCD_CS_SET;
  95. }

  96. /*****************************************************************************
  97. * [url=home.php?mod=space&uid=139335]@name[/url]       :void LCD_DrawPoint(uint16_t x,uint16_t y)
  98. * [url=home.php?mod=space&uid=212281]@date[/url]       :2018-08-09
  99. * [url=home.php?mod=space&uid=42490]@function[/url]   :Write a pixel data at a specified location
  100. * [url=home.php?mod=space&uid=2814924]@parameters[/url] :x:the x coordinate of the pixel
  101.                 y:the y coordinate of the pixel
  102. * @retvalue   :None
  103. ******************************************************************************/   
  104. void LCD_DrawPoint(uint16_t x,uint16_t y)
  105. {
  106.     LCD_SetCursor(x,y);//设置光标位置
  107.     Lcd_WriteData_16Bit(POINT_COLOR);
  108. }

  109. /*****************************************************************************
  110. * [url=home.php?mod=space&uid=139335]@name[/url]       :void LCD_Clear(uint16_t Color)
  111. * [url=home.php?mod=space&uid=212281]@date[/url]       :2018-08-09
  112. * [url=home.php?mod=space&uid=42490]@function[/url]   :Full screen filled LCD screen
  113. * [url=home.php?mod=space&uid=2814924]@parameters[/url] :color:Filled color
  114. * @retvalue   :None
  115. ******************************************************************************/   
  116. void LCD_Clear(uint16_t Color)
  117. {
  118.   unsigned int i,m;  
  119.     LCD_SetWindows(0,0,lcddev.width-1,lcddev.height-1);   
  120.     LCD_CS_CLR;
  121.     LCD_RS_SET;
  122.     for(i=0;i<lcddev.height;i++)
  123.     {
  124.     for(m=0;m<lcddev.width;m++)
  125.     {   
  126.             Lcd_WriteData_16Bit(Color);
  127.         }
  128.     }
  129.      LCD_CS_SET;
  130. }

  131. /*****************************************************************************
  132. * [url=home.php?mod=space&uid=139335]@name[/url]       :void LCD_RESET(void)
  133. * [url=home.php?mod=space&uid=212281]@date[/url]       :2018-08-09
  134. * [url=home.php?mod=space&uid=42490]@function[/url]   :Reset LCD screen
  135. * [url=home.php?mod=space&uid=2814924]@parameters[/url] :None
  136. * @retvalue   :None
  137. ******************************************************************************/   
  138. void LCD_RESET(void)
  139. {
  140.     LCD_RST_CLR;
  141.     HAL_Delay(100);
  142.     LCD_RST_SET;
  143.     HAL_Delay(50);
  144. }

  145. /*****************************************************************************
  146. * @name       :void LCD_RESET(void)
  147. * @date       :2018-08-09
  148. * @function   :Initialization LCD screen
  149. * @parameters :None
  150. * @retvalue   :None
  151. ******************************************************************************/         
  152. void LCD_Init(void)
  153. {  
  154.      LCD_RESET(); //LCD 复位
  155. //*************3.2inch ILI9341初始化**********//
  156.     LCD_WR_REG(0xCF);
  157.     LCD_WR_DATA(0x00);
  158.     LCD_WR_DATA(0xD9); //C1
  159.     LCD_WR_DATA(0X30);
  160.     LCD_WR_REG(0xED);  
  161.     LCD_WR_DATA(0x64);
  162.     LCD_WR_DATA(0x03);
  163.     LCD_WR_DATA(0X12);
  164.     LCD_WR_DATA(0X81);
  165.     LCD_WR_REG(0xE8);  
  166.     LCD_WR_DATA(0x85);
  167.     LCD_WR_DATA(0x10);
  168.     LCD_WR_DATA(0x7A);
  169.     LCD_WR_REG(0xCB);  
  170.     LCD_WR_DATA(0x39);
  171.     LCD_WR_DATA(0x2C);
  172.     LCD_WR_DATA(0x00);
  173.     LCD_WR_DATA(0x34);
  174.     LCD_WR_DATA(0x02);
  175.     LCD_WR_REG(0xF7);  
  176.     LCD_WR_DATA(0x20);
  177.     LCD_WR_REG(0xEA);  
  178.     LCD_WR_DATA(0x00);
  179.     LCD_WR_DATA(0x00);
  180.     LCD_WR_REG(0xC0);    //Power control
  181.     LCD_WR_DATA(0x1B);   //VRH[5:0]
  182.     LCD_WR_REG(0xC1);    //P0
  183.     LCD_WR_DATA(0x12);   //SAP[2:0];BT[3:0] //0x01
  184.     LCD_WR_REG(0xC5);    //VCM control
  185.     LCD_WR_DATA(0x26);      //3F
  186.     LCD_WR_DATA(0x26);      //3C
  187.     LCD_WR_REG(0xC7);    //VCM control2
  188.     LCD_WR_DATA(0XB0);
  189.     LCD_WR_REG(0x36);    // Memory Access Control
  190.     LCD_WR_DATA(0x08);
  191.     LCD_WR_REG(0x3A);   
  192.     LCD_WR_DATA(0x55);
  193.     LCD_WR_REG(0xB1);   
  194.     LCD_WR_DATA(0x00);   
  195.     LCD_WR_DATA(0x1A);
  196.     LCD_WR_REG(0xB6);    // Display Function Control
  197.     LCD_WR_DATA(0x0A);
  198.     LCD_WR_DATA(0xA2);
  199.     LCD_WR_REG(0xF2);    // 3Gamma Function Disable
  200.     LCD_WR_DATA(0x00);
  201.     LCD_WR_REG(0x26);    //Gamma curve selected
  202.     LCD_WR_DATA(0x01);
  203.     LCD_WR_REG(0xE0); //Set Gamma
  204.     LCD_WR_DATA(0x1F);
  205.     LCD_WR_DATA(0x24);
  206.     LCD_WR_DATA(0x24);
  207.     LCD_WR_DATA(0x0D);
  208.     LCD_WR_DATA(0x12);
  209.     LCD_WR_DATA(0x09);
  210.     LCD_WR_DATA(0x52);
  211.     LCD_WR_DATA(0xB7);
  212.     LCD_WR_DATA(0x3F);
  213.     LCD_WR_DATA(0x0C);
  214.     LCD_WR_DATA(0x15);
  215.     LCD_WR_DATA(0x06);
  216.     LCD_WR_DATA(0x0E);
  217.     LCD_WR_DATA(0x08);
  218.     LCD_WR_DATA(0x00);
  219.     LCD_WR_REG(0XE1); //Set Gamma
  220.     LCD_WR_DATA(0x00);
  221.     LCD_WR_DATA(0x1B);
  222.     LCD_WR_DATA(0x1B);
  223.     LCD_WR_DATA(0x02);
  224.     LCD_WR_DATA(0x0E);
  225.     LCD_WR_DATA(0x06);
  226.     LCD_WR_DATA(0x2E);
  227.     LCD_WR_DATA(0x48);
  228.     LCD_WR_DATA(0x3F);
  229.     LCD_WR_DATA(0x03);
  230.     LCD_WR_DATA(0x0A);
  231.     LCD_WR_DATA(0x09);
  232.     LCD_WR_DATA(0x31);
  233.     LCD_WR_DATA(0x37);
  234.     LCD_WR_DATA(0x1F);

  235.     LCD_WR_REG(0x2B);
  236.     LCD_WR_DATA(0x00);
  237.     LCD_WR_DATA(0x00);
  238.     LCD_WR_DATA(0x01);
  239.     LCD_WR_DATA(0x3f);
  240.     LCD_WR_REG(0x2A);
  241.     LCD_WR_DATA(0x00);
  242.     LCD_WR_DATA(0x00);
  243.     LCD_WR_DATA(0x00);
  244.     LCD_WR_DATA(0xef);     
  245.     LCD_WR_REG(0x11); //Exit Sleep

  246.     HAL_Delay(120);
  247.     LCD_WR_REG(0x29); //display on        

  248.   LCD_direction(USE_HORIZONTAL);//设置LCD显示方向
  249.     LCD_BL_ON;//点亮背光
  250.     LCD_Clear(GREEN);//清全屏白色
  251. }

  252. /*****************************************************************************
  253. * @name       :void LCD_SetWindows(uint16_t xStar, uint16_t yStar,uint16_t xEnd,uint16_t yEnd)
  254. * @date       :2018-08-09
  255. * @function   :Setting LCD display window
  256. * @parameters :xStar:the bebinning x coordinate of the LCD display window
  257.                                 yStar:the bebinning y coordinate of the LCD display window
  258.                                 xEnd:the endning x coordinate of the LCD display window
  259.                                 yEnd:the endning y coordinate of the LCD display window
  260. * @retvalue   :None
  261. ******************************************************************************/
  262. void LCD_SetWindows(uint16_t xStar, uint16_t yStar,uint16_t xEnd,uint16_t yEnd)
  263. {   
  264.     LCD_WR_REG(lcddev.setxcmd);   
  265.     LCD_WR_DATA(xStar>>8);
  266.     LCD_WR_DATA(0x00FF&xStar);        
  267.     LCD_WR_DATA(xEnd>>8);
  268.     LCD_WR_DATA(0x00FF&xEnd);

  269.     LCD_WR_REG(lcddev.setycmd);   
  270.     LCD_WR_DATA(yStar>>8);
  271.     LCD_WR_DATA(0x00FF&yStar);        
  272.     LCD_WR_DATA(yEnd>>8);
  273.     LCD_WR_DATA(0x00FF&yEnd);

  274.     LCD_WriteRAM_Prepare();    //开始写入GRAM
  275. }   

  276. /*****************************************************************************
  277. * @name       :void LCD_SetCursor(uint16_t Xpos, uint16_t Ypos)
  278. * @date       :2018-08-09
  279. * @function   :Set coordinate value
  280. * @parameters :Xpos:the  x coordinate of the pixel
  281.                                 Ypos:the  y coordinate of the pixel
  282. * @retvalue   :None
  283. ******************************************************************************/
  284. void LCD_SetCursor(uint16_t Xpos, uint16_t Ypos)
  285. {                          
  286.     LCD_SetWindows(Xpos,Ypos,Xpos,Ypos);   
  287. }

  288. /*****************************************************************************
  289. * @name       :void LCD_direction(uint8_t direction)
  290. * @date       :2018-08-09
  291. * @function   :Setting the display direction of LCD screen
  292. * @parameters :direction:0-0 degree
  293.                           1-90 degree
  294.                                                     2-180 degree
  295.                                                     3-270 degree
  296. * @retvalue   :None
  297. ******************************************************************************/
  298. void LCD_direction(uint8_t direction)
  299. {
  300.             lcddev.setxcmd=0x2A;
  301.             lcddev.setycmd=0x2B;
  302.             lcddev.wramcmd=0x2C;
  303.     switch(direction){         
  304.         case 0:                                      
  305.             lcddev.width=LCD_W;
  306.             lcddev.height=LCD_H;        
  307.             LCD_WriteReg(0x36,(1<<3)|(0<<6)|(0<<7));//BGR==1,MY==0,MX==0,MV==0
  308.         break;
  309.         case 1:
  310.             lcddev.width=LCD_H;
  311.             lcddev.height=LCD_W;
  312.             LCD_WriteReg(0x36,(1<<3)|(0<<7)|(1<<6)|(1<<5));//BGR==1,MY==1,MX==0,MV==1
  313.         break;
  314.         case 2:                                      
  315.             lcddev.width=LCD_W;
  316.             lcddev.height=LCD_H;   
  317.             LCD_WriteReg(0x36,(1<<3)|(1<<6)|(1<<7));//BGR==1,MY==0,MX==0,MV==0
  318.         break;
  319.         case 3:
  320.             lcddev.width=LCD_H;
  321.             lcddev.height=LCD_W;
  322.             LCD_WriteReg(0x36,(1<<3)|(1<<7)|(1<<5));//BGR==1,MY==1,MX==0,MV==1
  323.         break;   
  324.         default:break;
  325.     }        
  326. }


  327. void SPI_DMATransmitCplt(DMA_HandleTypeDef *hdma) {
  328. //    if (hdma == hdma_spi1_tx) {
  329.     LCD_CS_SET;
  330.     LCD_RS_SET;

  331. //    }
  332. }

四、如何优化SPI的传输速率
一般情况下,直接提升SPI传输速率的方法是,提高SPI的工作频率。在数据手册中,也没有看到最大工作频率;那么就只能看MCU支持多大的工作频率;
SPI.png
除了以上方法,还有以下几种方法:
——SPI传输使用DMA方式;
——提高IDE的优化等级;
——使用RAM存储LCD显示数据。

下面就实测以上几种方法的刷新率;
1、先来看看正常配置后的刷新率;2s一张图;如下视频
DMA0.gif
2、设置编译器优化等级;刷新率快了一点;1s一张图;如下
DMA1.gif
等级优化设置:
等级优化.png

3、设置SPI为DMA传输;也是2s一张图;1s一张图,如下视频
0.gif
  1.     LCD_CS_CLR;
  2.     HAL_SPI_Transmit_DMA(LCD_SPI_Handle, &TxData, 1);
  3.     while((HAL_SPI_GetState(&hspi1)) != HAL_SPI_STATE_READY);
  4.     LCD_CS_SET;
4、设置SPI为DMA传输,并且优化等级为fast;没有什么变化;2s一张图,如下视频
1.gif
5、把SPI的数据缓存放在RAM中;使用DMA传输;刷新率大幅提高;见如下视频;
4l.gif

具体操作如下:
  1. /* 在STM32L4R5ZITX_RAM.ld 中声明一个段,这个段在RAM中;*/
  2.   /*add user section*/
  3.   .lcdbuffer   :
  4.   {
  5.     . = ALIGN(4);
  6.     . = ALIGN(4);
  7.   } >RAM
  8.   
  9. /*  最后在头文件中设置LCD显示数据数组在声明的段中*/
  10. __attribute__((section(".lcdbuffer"))) unsigned short LCD_BUFFER[320][240] ;

编译后可以看到LCD_BUFFER运行地址是在RAM中;
段设置.png
此前DMA是传输一个字节,传输大量数据就会重复进行传输过程。
而直接使用RAM中的缓存,则可以把数据通过DMA把半屏数据传输到LCD的DDRAM中。




  

打赏榜单

21小跑堂 打赏了 50.00 元 2024-05-30
理由:恭喜通过原创审核!期待您更多的原创作品~

评论

[url=home.php?mod=space&uid=760190]@21小跑堂[/url] :点赞;DMA只适用于大量数据搬运比较有效果;仅仅用于SPI对刷新率没有益处  发表于 2024-5-30 22:36
使用多种方法优化LCD刷新速率,找出合适的方法,二姨认为,SPI使用DMA无法优化刷新率是很正常的,SPI的传输速率相对于DMA的搬运速度来说差距很大,即使DMA能够很快的将数据搬运到SPI的发送寄存器,但是SPI的发送速率摆在那里,并不能有效的体现刷新率的优化。但是DMA可以节省出MCU搬运数据的时间,这个时间太过于短暂,导致实际效果无感。  发表于 2024-5-30 15:04
xuanhuanzi 发表于 2024-6-9 18:35 | 显示全部楼层
这部分空间最大多少?
 楼主| 怀揣少年梦 发表于 2024-6-11 08:50 | 显示全部楼层
xuanhuanzi 发表于 2024-6-9 18:35
这部分空间最大多少?

空间最大是指什么
laocuo1142 发表于 2024-6-12 08:04 | 显示全部楼层
主频够快的话,IO模拟反而感觉比硬件SPI要更快
daichaodai 发表于 2024-6-12 08:40 来自手机 | 显示全部楼层
OLED才更省电吧
 楼主| 怀揣少年梦 发表于 2024-6-15 08:37 | 显示全部楼层
laocuo1142 发表于 2024-6-12 08:04
主频够快的话,IO模拟反而感觉比硬件SPI要更快

大佬
 楼主| 怀揣少年梦 发表于 2024-6-15 08:38 | 显示全部楼层

OLED成本高一点
EmmaTT 发表于 2024-6-18 21:41 | 显示全部楼层
这个性能怎么样啊
t60yz 发表于 2024-6-30 20:17 | 显示全部楼层
直接移植相关代码即可



suncat0504 发表于 2024-6-30 22:38 | 显示全部楼层
提高刷新率的话,还是用并行接口的吧。
suncat0504 发表于 2024-6-30 22:38 | 显示全部楼层
SPI接口和I2C接口的,感觉不太适合做动画处理方面的工作。
 楼主| 怀揣少年梦 发表于 2024-7-1 09:41 | 显示全部楼层
suncat0504 发表于 2024-6-30 22:38
SPI接口和I2C接口的,感觉不太适合做动画处理方面的工作。

对UI流畅度要求高的场合,不适合
而服务器人 发表于 2024-7-26 15:34 | 显示全部楼层
SPI传输使用DMA方式;
提高IDE的优化等级;
使用RAM存储LCD显示数据。

您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:一切皆有可能

45

主题

473

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部
个人签名:一切皆有可能

45

主题

473

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部