前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >STM32CubeMX |42-使用DMA2D加速显存数据传输

STM32CubeMX |42-使用DMA2D加速显存数据传输

作者头像
Mculover666
发布2021-07-23 15:19:49
2.7K2
发布2021-07-23 15:19:49
举报
文章被收录于专栏:TencentOS-tiny

一、使用CPU搬运数据到显存

在上一篇文章中讲述了如何配置 LTDC 驱动 RGB 屏幕:

STM32CubeMX | 41-使用LTDC驱动TFT-LCD屏幕(RGB屏)

本节中我们接着上一节的实验,讲述如何使用 DMA2D 实现打点、画线、填充等函数,只需要单层全屏即可,修改LTDC层配置如下:

1. 编写lcd驱动头文件

创建lcd_rgb_ltdc_drv.h文件,存放关于操作LCD屏幕的一些宏定义配置和函数定义:

代码语言:javascript
复制
#ifndef _LCD_RGB_LTDC_DRV_H_
#define _LCD_RGB_LTDC_DRV_H_

#include "ltdc.h"

/**
 * @brief   Windows size on lcd.
*/
#define LCD_WIDTH       1024
#define LCD_HEIGHT      600

/**
 * @brief   Backlight control pin of lcd.
*/
#define LCD_BL_GPIO_PORT    GPIOB
#define LCD_BL_GPIO_PIN     GPIO_PIN_5

/**
 * @brief   start address of lcd framebuffer.
*/
#define LCD_FRAME_BUFFER    0xc0000000

/**
 * @brief   color
 * @note    rgb565   
*/
#define BLACK   0x0000
#define BLUE    0x001F
#define GREEN   0x07E0
#define GBLUE   0X07FF
#define GRAY    0X8430
#define BROWN   0XBC40
#define RED     0xF800
#define PINK    0XF81F
#define BRRED   0XFC07
#define YELLOW  0xFFE0
#define WHITE   0xFFFF

/**
 * @brief       Control the lcd backlight.
 * @param[in]   brightness  the value of lcd backlight.
 * @return      None
*/
void lcd_backlight_control(uint8_t bightness);

/**
 * @brief       LCD initialization.
 * @param       None
 * @return      None
*/
void lcd_init(void);

/**
 * @brief       Clear lcd.
 * @param[in]   color   rgb565.
 * @return      None
*/
void lcd_clear(uint16_t color);

#endif /* _LCD_RGB_LTDC_DRV_H_ */

2. lcd驱动实现

创建lcd_rgb_ltdc_drv.c文件,存放关于操作LCD屏幕的函数实现。

首先是背光控制实现,应该使用pwm实现背光调节,本文中为了方便直接使用GPIO控制:

代码语言:javascript
复制
void lcd_backlight_control(uint8_t bightness)
{
    // todo: use pwm to control backlight

    if (bightness) {    
        // turn on the backlight
        HAL_GPIO_WritePin(LCD_BL_GPIO_PORT, LCD_BL_GPIO_PIN, GPIO_PIN_SET);
    } else {            
        // turn off the backlight
        HAL_GPIO_WritePin(LCD_BL_GPIO_PORT, LCD_BL_GPIO_PIN, GPIO_PIN_RESET);
    }
}

接着实现lcd清屏函数,使用CPU(循环)搬运数据到显存中

代码语言:javascript
复制
void lcd_clear(uint16_t color)
{
    uint16_t *ptr = (uint16_t*)LCD_FRAME_BUFFER;
    uint32_t i = 0;

    while (i++ < LCD_WIDTH*LCD_HEIGHT) {
        *(ptr+i) = color;
    }
}

最后实现lcd初始化函数,先刷显存,然后开背光,防止设备上电时屏幕闪烁:

代码语言:javascript
复制
void lcd_init()
{
    lcd_clear(BLACK);
    lcd_backlight_control(255);
}

3. lcd单次清屏时间测试

在main.c中包含驱动头文件:

代码语言:javascript
复制
#include "lcd_rgb_ltdc_drv.h"

在main函数中的sdram初始化函数之后,添加lcd初始化函数,并使用HAL库自带的systick时间戳测量一次清屏的时间:

代码语言:javascript
复制
/* USER CODE BEGIN 2 */
printf("sdram test by mculover666\r\n");
SDRAM_Init();
printf("sdram init success\r\n");

lcd_init();

start_time = HAL_GetTick();
lcd_clear(PINK);
end_time = HAL_GetTick();

printf("lcd clear spend time:%ld ms\r\n", end_time - start_time);
/* USER CODE END 2 */

编译、运行,在串口助手可以看到使用CPU搬运数据到显存中,在-Og优化等级下单次清屏需要 155 ms左右,在-O0优化等级下单次清屏需要321ms左右:

二、使用DMA2D加速显存数据搬运

1. DMA2D

在STM32中,DMA2D外设专门用来给LCD显示加速,有LTDC外设的型号中,通常也会配套有DMA2D。

DMA2D外设主要提供了两个功能:

  • DMA数据搬运:常用从寄存器到存储器、存储器到存储器两种模式,快速高效,并且不占用cpu资源
  • 2D图形加速:支持快速格式转换和混合;

本文中主要使用到DMA2D外设的数据搬运功能,使用起来也是比较简单。

2. 开启DMA2D

重新生成工程,cubemx会自动生成并调用dma2d初始化函数,完成dma2d外设时钟使能以及dma2d传输模式配置。

3. 使用DMA2D实现lcd清屏函数

在lcd驱动头文件中添加一个宏定义,用于控制是否使能DMA2D:

代码语言:javascript
复制
/**
 * @brief   whether use dma2d to transfer data to lcd framebuffer.
*/
#define USE_DMA2D_EN        1

接着对DMA2D传输操作进行封装,编写一个DMA2D传输函数:

代码语言:javascript
复制
static void dma2d_transfer_data_r2m(uint32_t *addr, uint32_t xSize, uint32_t ySize, uint32_t offsetLine, uint16_t color)
{
    DMA2D->CR = DMA2D_R2M;   // dma2d mode: register to memory.
    DMA2D->OPFCCR = DMA2D_OUTPUT_RGB565;

    DMA2D->OCOLR = color;
    DMA2D->OMAR = (uint32_t)addr;
    DMA2D->OOR = offsetLine;
    DMA2D->NLR = (uint32_t)(xSize << 16) | (uint16_t)ySize;
    
    DMA2D->CR |= DMA2D_CR_START;
    while (DMA2D->CR & DMA2D_CR_START);
}

利用此DMA2D传输函数,重新添加清屏函数的实现:

代码语言:javascript
复制
void lcd_clear(uint16_t color)
{
#if USE_DMA2D_EN
    dma2d_transfer_data_r2m((uint32_t *)LCD_FRAME_BUFFER, LCD_WIDTH, LCD_HEIGHT, 0, color);
#else
    uint16_t *ptr = (uint16_t*)LCD_FRAME_BUFFER;
    uint32_t i = 0;

    while (i++ < LCD_WIDTH*LCD_HEIGHT) {
        *(ptr+i) = color;
    }
#endif /* USE_DMA2D_EN */
}

上层测试代码不变,确保控制dma2d的宏使能:

代码语言:javascript
复制
#define USE_DMA2D_EN        1

编译、下载,在串口助手中查看清屏一次所需时间:

可以看到,刷屏一次只需31ms即可,并且在使用dma2d传输数据的情况下,数据传输时间和编译优化等级无关

三、LCD基本功能实现

LCD基本功能包括打点、读点、画线、绘图等函数。

1. 打点函数

打点函数的核心是计算当前用户给出的坐标位置在显存中的位置,两种实现如下:

代码语言:javascript
复制
void lcd_draw_point(uint16_t x, uint16_t y, uint16_t color)
{
    uint32_t pos;
    uint16_t *ptr;

    // check position.
    if (x > LCD_WIDTH || y > LCD_HEIGHT) {
        return;
    }

    // calculate the position offset in framebuffer.
    pos = x + y*LCD_WIDTH;
    ptr = (uint16_t*)LCD_FRAME_BUFFER;

    // modify the framebuffer.
#if USE_DMA2D_EN
    dma2d_transfer_data_r2m((uint32_t *)(ptr+pos), 1, 1, 0, color);
#else
    *(ptr+pos) = color;
#endif /* USE_DMA2D_EN */
}

2. 读点函数实现

读点函数实现的核心也是计算出用户给出的坐标位置在显存中的位置:

代码语言:javascript
复制
uint16_t lcd_read_point(uint16_t x, uint16_t y)
{
    uint32_t pos;
    uint16_t *ptr, data;

    // check position.
    if (x > LCD_WIDTH || y > LCD_HEIGHT) {
        return 0;
    }

    // calculate the position offset in framebuffer.
    pos = x + y*LCD_WIDTH;
    ptr = (uint16_t*)LCD_FRAME_BUFFER;

    // read the framebuffer.
    data = *(ptr+pos);

    return data;
}

3. 画线、画矩形、画圆

这三个功能都是基于打点函数,使用 Bresenham 算法,代码篇幅过多,如有兴趣可直接查看本篇源码。

4. 测试

在main函数中添加测试代码:

代码语言:javascript
复制
lcd_draw_line(0, 0, 1024, 600, GREEN);
lcd_draw_line(0, 300, 1024, 300, RED);
lcd_draw_line(512, 0, 512, 600, BLUE);
lcd_draw_line(1024, 0, 0, 600, YELLOW);

lcd_draw_rect(256, 150, 1024-256, 600-150, PINK);

编译,下载,结果如下图:

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-07-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Mculover666 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、使用CPU搬运数据到显存
    • 1. 编写lcd驱动头文件
      • 2. lcd驱动实现
        • 3. lcd单次清屏时间测试
        • 二、使用DMA2D加速显存数据搬运
          • 1. DMA2D
            • 2. 开启DMA2D
              • 3. 使用DMA2D实现lcd清屏函数
              • 三、LCD基本功能实现
                • 1. 打点函数
                  • 2. 读点函数实现
                    • 3. 画线、画矩形、画圆
                      • 4. 测试
                      领券
                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档