OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 787|回复: 6

使用DMA,让LCD_Fill函数速度提升62%

[复制链接]

13

主题

166

帖子

0

精华

高级会员

Rank: 4

积分
791
金钱
791
注册时间
2018-12-19
在线时间
163 小时
发表于 2024-4-26 22:46:09 | 显示全部楼层 |阅读模式
本帖最后由 854278507 于 2024-4-26 22:49 编辑

正点原子提供的LCD_Fill函数是使用CPU运算的方式,整屏填充需要10.375ms,算下来就是48.131帧,虽然已经很快了,但是如果使用DMA,还可以更快
下面是我修改后的LCD_Fill函数
  1. // 在指定区域内填充单个颜色
  2. //(sx,sy),(ex,ey):填充矩形对角坐标,区域大小为:(ex-sx+1)*(ey-sy+1)
  3. // color:要填充的颜色
  4. void LCD_Fill(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint16_t color)
  5. {

  6.     uint16_t i, j;
  7.     uint16_t xlen = 0;
  8.     uint16_t temp;


  9.     if ((lcddev.id == 0x6804) && (lcddev.dir == 1)) // 6804横屏的时候特殊处理
  10.     {
  11.         temp = sx;
  12.         sx = sy;
  13.         sy = lcddev.width - ex - 1;
  14.         ex = ey;
  15.         ey = lcddev.width - temp - 1;
  16.         lcddev.dir = 0;
  17.         lcddev.setxcmd = 0x2A;
  18.         lcddev.setycmd = 0x2B;
  19.         LCD_Fill(sx, sy, ex, ey, color);
  20.         lcddev.dir = 1;
  21.         lcddev.setxcmd = 0x2B;
  22.         lcddev.setycmd = 0x2A;
  23.     }
  24.     else
  25.     {
  26. #if 1
  27.         uint16_t width;
  28.         uint16_t height;
  29.         
  30.         width = ex - sx + 1;
  31.         height = ey - sy + 1;
  32.         
  33.         
  34.         
  35.         LCD_Set_Window(sx, sy, width, height);
  36.         LCD_WriteRAM_Prepare();
  37.         
  38.         uint32_t lcdsize = width * height;
  39.         uint32_t dmatransfered = 0;
  40.         
  41.         uint16_t color_buf[2];
  42.         
  43.         color_buf[0] = color;
  44.         color_buf[1] = color;
  45.         

  46.         DMA_InitTypeDef DMA_InitStruct;
  47.         
  48.         lcdsize /= 2; /* 使用32位宽数据传输,数据量减少一半 */
  49.         
  50.         DMA_InitStruct.DMA_PeripheralBaseAddr  = (uint32_t)&LCD->LCD_RAM;   /* 外设地址为LCD_RAM */   
  51.         DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)color_buf;            /* 内存地址为显示缓存 */
  52.         DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralDST;                     /* DMA方向为内存到外设 */   
  53.         DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;       /* 外设地址固定 */
  54.         DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Disable;               /* 内存地址固定 */
  55.         DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;/* 外设数据宽度32位 */
  56.         DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;        /* 内存数据宽度32位 */
  57.         DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;                          /* 普通模式 */
  58.         DMA_InitStruct.DMA_Priority = DMA_Priority_High;                    /* DMA优先级为高 */
  59.         DMA_InitStruct.DMA_M2M = DMA_M2M_Enable;                            /* 内存到内存模式(不需要外部触发) */   
  60.         
  61.         while (lcdsize)
  62.         {
  63.             if (lcdsize >= 65535)
  64.             {
  65.                 DMA_InitStruct.DMA_BufferSize = 65535; /* 单次数据传输量不能超过65535 */
  66.                 lcdsize -= 65535;
  67.                 dmatransfered += 65535;
  68.             }
  69.             else
  70.             {
  71.                 DMA_InitStruct.DMA_BufferSize = lcdsize;
  72.                 lcdsize = 0;
  73.             }
  74.             
  75.             DMA_Init(DMA2_Channel5, &DMA_InitStruct);
  76.             
  77.             DMA_Cmd(DMA2_Channel5, ENABLE);
  78.             
  79.             while (DMA_GetFlagStatus(DMA2_FLAG_TC5) == RESET)
  80.             {
  81.                
  82.             }            
  83.             
  84.             DMA_ClearFlag(DMA2_FLAG_TC5);

  85.         }
  86.         DMA_Cmd(DMA2_Channel5, DISABLE);
  87.         
  88.         LCD_Set_Window(0, 0, lcddev.width, lcddev.height);
  89. //        LCD_WriteRAM_Prepare();
  90.         
  91. #else
  92.         
  93.         xlen = ex - sx + 1;
  94.         for (i = sy; i <= ey; i++)
  95.         {
  96.             LCD_SetCursor(sx, i);      // 设置光标位置
  97.             LCD_WriteRAM_Prepare();    // 开始写入GRAM
  98.             for (j = 0; j < xlen; j++) // 显示颜色
  99.             {
  100.                 LCD->LCD_RAM = color;
  101.             }
  102.         }
  103.         
  104. #endif /* 0 */
  105.     }
  106. }
复制代码
可能会有人有疑问,LCD是16位的8080 16位的接口,怎么可以使用32位的数据传输,实际上是可以的,也可以改成16位传输,这样速度会慢一点,因为 240 * 320 的屏用16位传输需要写 240 * 320 = 76800次,而STM32F103的DMA一次最多只能传输65535个数据,需要分两次传输,如果用32位传输就只需要 240 * 320 / 2 = 38400次,DMA一次就可以传输完成。
由于是单色填充,需要先定义一个数组,填充好要用的颜色数值,然后把这地址设置为DMA的内存地址,初始化DMA的时候要设置内存地址不自增,这样就可以快速整屏填充了。

下面是没有使用DMA刷屏速度截图
Snipaste_2024-04-26_22-29-12.png
从上面的图片可以看出没有使用DMA的时候刷一屏需要10.375ms,刷屏速度48.131帧。

下面是使用DMA刷屏速度截图
Snipaste_2024-04-26_22-29-15.png
从上面的图片可以看出使用DMA后刷一屏只需要6.411ms,刷屏速度77.99帧。

下面放上测试刷屏速度的代码

  1. void lcd_test_play(void)
  2. {
  3. #if 1

  4.     const uint16_t color_table[] =
  5.     {
  6.         //画笔颜色
  7.         WHITE             ,
  8.         BLACK             ,
  9.         BLUE              ,
  10.         BRED              ,
  11.         GRED                           ,
  12.         GBLUE                          ,
  13.         RED               ,
  14.         MAGENTA           ,
  15.         GREEN             ,
  16.         CYAN              ,
  17.         YELLOW            ,
  18.         BROWN                           ,
  19.         BRRED                           ,
  20.         GRAY                            ,
  21.         DARKBLUE          ,
  22.         LIGHTBLUE         ,
  23.         GRAYBLUE          ,
  24.         LIGHTGREEN        ,
  25.         LGRAY                           ,
  26.         LGRAYBLUE         ,
  27.         LBBLUE            ,
  28.     };
  29.       
  30.     uint8_t i;

  31.    

  32.     spb_delete();
  33.         printf("\r\n[%s][%s][%04d]LCD刷屏 %d",
  34.                __FILE__, __func__, __LINE__, system_task_return);
  35.     system_task_return = 0;
  36.    
  37.     while (1)
  38.     {
  39.         for (i = 0; i < sizeof(color_table) / 2; i++)
  40.         {
  41.             LCD_Fill(0, 0, lcddev.width - 1, lcddev.height - 1, color_table[i]);   
  42.             LED_1_Turn();
  43.         }
  44.         
  45.         if (system_task_return == 1)
  46.         {
  47.             if (TPAD_Scan(1))
  48.             {
  49.                 break;
  50.             }
  51.             else
  52.             {
  53.                 system_task_return = 0;
  54.             }
  55.         }
  56.     }
  57.     LED_1_Off();  
  58.    
  59. #else


  60.     uint16_t r;
  61.     uint16_t g;
  62.     uint16_t b;
  63.    

  64.     spb_delete();
  65.         printf("\r\n[%s][%s][%04d]LCD刷屏 %d",
  66.                __FILE__, __func__, __LINE__, system_task_return);
  67.     system_task_return = 0;
  68.    
  69.     while (1)
  70.     {
  71. #define LCD_DELAY 20

  72.         for (r = 0; r < (1 << 5); r++)
  73.         {
  74.             for (g = 0; g < (1 << 6); g++)
  75.             {
  76.                 for (b = 0; b < (1 << 5); b++)
  77.                 {
  78.                     LCD_Fill(0,
  79.                     0,
  80.                     lcddev.width - 1,
  81.                     lcddev.height - 1,
  82.                     (r << 11) | (g << 5) | b);
  83.                     LED_1_Turn();
  84.                     OSTimeDly(LCD_DELAY);
  85.                 }
  86.                 for (b = (1 << 5); b > 0; b--)
  87.                 {
  88.                     LCD_Fill(0,
  89.                     0,
  90.                     lcddev.width - 1,
  91.                     lcddev.height - 1,
  92.                     (r << 11) | (g << 5) | b);
  93.                     LED_1_Turn();
  94.                     OSTimeDly(LCD_DELAY);
  95.                 }               
  96.             }
  97.             for (g = (1 << 6); g > 0; g--)
  98.             {
  99.                 for (b = 0; b < (1 << 5); b++)
  100.                 {
  101.                     LCD_Fill(0,
  102.                     0,
  103.                     lcddev.width - 1,
  104.                     lcddev.height - 1,
  105.                     (r << 11) | (g << 5) | b);
  106.                     LED_1_Turn();
  107.                     OSTimeDly(LCD_DELAY);
  108.                 }
  109.                 for (b = (1 << 5); b > 0; b--)
  110.                 {
  111.                     LCD_Fill(0,
  112.                     0,
  113.                     lcddev.width - 1,
  114.                     lcddev.height - 1,
  115.                     (r << 11) | (g << 5) | b);
  116.                     LED_1_Turn();
  117.                     OSTimeDly(LCD_DELAY);
  118.                 }               
  119.             }            
  120.         }
  121.         
  122.         for (r = (1 << 5); r > 0; r--)
  123.         {
  124.             for (g = 0; g < (1 << 6); g++)
  125.             {
  126.                 for (b = 0; b < (1 << 5); b++)
  127.                 {
  128.                     LCD_Fill(0,
  129.                     0,
  130.                     lcddev.width - 1,
  131.                     lcddev.height - 1,
  132.                     (r << 11) | (g << 5) | b);
  133.                     LED_1_Turn();
  134.                     OSTimeDly(LCD_DELAY);
  135.                 }
  136.                 for (b = (1 << 5); b > 0; b--)
  137.                 {
  138.                     LCD_Fill(0,
  139.                     0,
  140.                     lcddev.width - 1,
  141.                     lcddev.height - 1,
  142.                     (r << 11) | (g << 5) | b);
  143.                     LED_1_Turn();
  144.                     OSTimeDly(LCD_DELAY);
  145.                 }               
  146.             }
  147.             for (g = (1 << 6); g > 0; g--)
  148.             {
  149.                 for (b = 0; b < (1 << 5); b++)
  150.                 {
  151.                     LCD_Fill(0,
  152.                     0,
  153.                     lcddev.width - 1,
  154.                     lcddev.height - 1,
  155.                     (r << 11) | (g << 5) | b);
  156.                     LED_1_Turn();
  157.                     OSTimeDly(LCD_DELAY);
  158.                 }
  159.                 for (b = (1 << 5); b > 0; b--)
  160.                 {
  161.                     LCD_Fill(0,
  162.                     0,
  163.                     lcddev.width - 1,
  164.                     lcddev.height - 1,
  165.                     (r << 11) | (g << 5) | b);
  166.                     LED_1_Turn();
  167.                     OSTimeDly(LCD_DELAY);
  168.                 }               
  169.             }            
  170.         }        
  171.         
  172.         if (system_task_return == 1)
  173.         {
  174.             if (TPAD_Scan(1))
  175.             {
  176.                 break;
  177.             }
  178.             else
  179.             {
  180.                 system_task_return = 0;
  181.             }
  182.         }
  183.     }
  184.     LED_1_Off();   
  185.    
  186. #endif /* 1 */
  187.    
  188.    
  189. }
复制代码


正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

9

主题

56

帖子

0

精华

高级会员

Rank: 4

积分
630
金钱
630
注册时间
2018-8-9
在线时间
148 小时
发表于 2024-5-6 22:44:20 | 显示全部楼层
16根数据线可以用DMA32位转输??可以上传个完整代码试一下吗?好奇想试试
回复 支持 反对

使用道具 举报

0

主题

2

帖子

0

精华

新手入门

积分
8
金钱
8
注册时间
2024-5-15
在线时间
1 小时
发表于 2024-5-15 18:58:10 来自手机 | 显示全部楼层
那个LCD_Color_Fill是不是也可以用类似的操作实现。我尝试了用dma将数据传到LCD->sram上,但一直没成功,不知道是我设置错了,还是这种方法不行
回复 支持 反对

使用道具 举报

13

主题

166

帖子

0

精华

高级会员

Rank: 4

积分
791
金钱
791
注册时间
2018-12-19
在线时间
163 小时
 楼主| 发表于 2024-5-17 12:38:09 | 显示全部楼层
DoubleDefiner 发表于 2024-5-15 18:58
那个LCD_Color_Fill是不是也可以用类似的操作实现。我尝试了用dma将数据传到LCD->sram上,但一直没成功, ...

可以呀,只是要把内存地址自增使能
回复 支持 反对

使用道具 举报

13

主题

166

帖子

0

精华

高级会员

Rank: 4

积分
791
金钱
791
注册时间
2018-12-19
在线时间
163 小时
 楼主| 发表于 2024-5-17 12:39:13 | 显示全部楼层
maidilong 发表于 2024-5-6 22:44
16根数据线可以用DMA32位转输??可以上传个完整代码试一下吗?好奇想试试

FSMC的配置不用改,只是DMA初始化设置为32位数据宽度
回复 支持 反对

使用道具 举报

0

主题

2

帖子

0

精华

新手入门

积分
8
金钱
8
注册时间
2024-5-15
在线时间
1 小时
发表于 2024-5-20 18:32:16 | 显示全部楼层
854278507 发表于 2024-5-17 12:39
FSMC的配置不用改,只是DMA初始化设置为32位数据宽度

我试过了,但是不行,每次只能传入二十个像素点左右,但是往同样挂载在fsmc上的sram上传数据,就能一次性全部传完。使用的是单次传输,没有用队列,不是1kB边界的问题。猜测是lcd屏幕的问题,可能是显存不支持高速写入?
回复 支持 反对

使用道具 举报

13

主题

166

帖子

0

精华

高级会员

Rank: 4

积分
791
金钱
791
注册时间
2018-12-19
在线时间
163 小时
 楼主| 发表于 2024-5-21 12:28:00 | 显示全部楼层
DoubleDefiner 发表于 2024-5-20 18:32
我试过了,但是不行,每次只能传入二十个像素点左右,但是往同样挂载在fsmc上的sram上传数据,就能一次性 ...

降低FSMC的速度试一下,我用的屏是9341和9488,两种都没问题,我感觉在逻辑上跟屏的驱动芯片关系不大,DMA会自动把32位的数据拆分成两个16位的数据。
回复 支持 反对

使用道具 举报

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

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-22 19:00

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表