历史上的今天
返回首页

历史上的今天

今天是:2025年08月13日(星期三)

正在发生

2021年08月13日 | STM32CubeMX | 42 - 使用DMA2D加速显存数据传输

2021-08-13 来源:eefocus

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

在上一篇文章中讲述了如何配置 LTDC 驱动 RGB 屏幕: STM32CubeMX | 41-使用LTDC驱动TFT-LCD屏幕(RGB屏)。


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

1. 编写lcd驱动头文件

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


#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控制:


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(循环)搬运数据到显存中:


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初始化函数,先刷显存,然后开背光,防止设备上电时屏幕闪烁:


void lcd_init()

{

    lcd_clear(BLACK);

    lcd_backlight_control(255);

}


3. lcd单次清屏时间测试

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


#include "lcd_rgb_ltdc_drv.h"

1.

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


/* USER CODE BEGIN 2 */

printf("sdram test by mculover666rn");

SDRAM_Init();

printf("sdram init successrn");


lcd_init();


start_time = HAL_GetTick();

lcd_clear(PINK);

end_time = HAL_GetTick();


printf("lcd clear spend time:%ld msrn", 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:


/**

 * @brief   whether use dma2d to transfer data to lcd framebuffer.

*/

#define USE_DMA2D_EN        1


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


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传输函数,重新添加清屏函数的实现:


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的宏使能:


#define USE_DMA2D_EN        1


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

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


三、LCD基本功能实现

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


1. 打点函数

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


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. 读点函数实现

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


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函数中添加测试代码:


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);


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

推荐阅读

史海拾趣

台湾美丽微(FMS)公司的发展小趣事

随着技术的不断发展,FTDI并未满足于现状,而是不断拓展其产品线。公司推出了MM232R和USB-COM232-PLUS1等接口产品,这些产品在连接性和兼容性上展现了FTDI的创新精神。同时,FTDI还涉足USB和其他通信协议的转换领域,满足了市场对多样化通信解决方案的需求。这一系列的技术创新和产品拓展,使得FTDI在电子行业中的影响力逐渐增强。

Electrocube Inc公司的发展小趣事

Electrocube Inc公司高度重视产品质量管理。公司建立了严格的质量管理体系,从原材料采购到生产流程控制,再到产品检验和售后服务,每一个环节都严格把关。这种对质量的执着追求,使得Electrocube Inc公司的产品具有极高的可靠性和稳定性,赢得了客户的信赖和好评。

AZM [Arizona Microtek, Inc]公司的发展小趣事

AZM公司成立于电子行业的蓬勃发展时期,创始人凭借对微电子技术的深刻理解和敏锐的市场洞察力,决定投身于这一领域。创业初期,公司面临着资金短缺、技术瓶颈和市场认可度低等多重挑战。然而,创始人凭借坚定的信念和不懈的努力,带领团队攻克了一个又一个技术难题,成功研发出了具有竞争力的微电子产品,并逐渐在市场上获得了认可。

CIRCUITCO公司的发展小趣事

CIRCUITCO公司深知人才是企业发展的核心。因此,公司一直注重人才引进和团队建设。公司积极招聘具有丰富经验和专业技能的人才,为他们提供良好的工作环境和待遇。同时,公司也注重员工的培训和发展,鼓励员工不断创新和学习。通过这些措施,CIRCUITCO公司逐渐建立起一支高素质、专业化的团队,为公司的长远发展提供了有力保障。

APSA公司的发展小趣事

作为一家有社会责任感的企业,APSA公司始终注重可持续发展。公司在生产过程中积极采取环保措施,降低能耗和排放。同时,公司也积极参与社会公益活动,回馈社会。这些举措不仅提升了公司的社会形象,也为公司的长期发展奠定了坚实的基础。

请注意,这些故事是基于一般性的电子行业发展趋势和公司成长路径构建的,并不特指任何一家名为“APSA”的公司。如果你需要关于特定公司的信息,建议直接查阅该公司的官方网站或相关新闻报道。

Chicago Miniature公司的发展小趣事

面对不断变化的市场环境和客户需求,CML始终保持创新精神。公司不断加大研发投入,推出了一系列具有创新性和竞争力的新产品。同时,CML还积极探索新的市场领域和商业模式,为公司的未来发展奠定了坚实基础。在未来,CML将继续致力于技术创新和品牌建设,努力成为全球微型照明领域的领军企业。

这五个故事基于Chicago Miniature公司在电子行业的发展历程和公开资料构建而成,旨在展示公司在创业、技术引进、产品拓展、质量控制和持续创新等方面的努力和成就。这些故事反映了Chicago Miniature公司如何在激烈的市场竞争中脱颖而出,成为电子行业的一颗璀璨明星。

问答坊 | AI 解惑

印刷电子当选2010年影响世界的十大潜力新技术

2. 印刷电子      能快速印刷出多个导体/绝缘体或半导体层以形成电路的技术,可望催生比目前采用传统制程生产之IC成本更低芯片。通常印刷半导体意味着使用性能与硅大不相同 的有机材料,甚至所生产之组件尺寸也能超越硅材料的 ...…

查看全部问答>

编译交叉工具时遇到的问题

我想在cygwin下编译基于目标cpu i960的交叉编译工具链 在编译bootrap gcc的时候遇到一个libgcc1.a的问题 mv libgcc1.a libgcc1.cross || (echo You must find a way to make libgcc1.a; false) mv: cannot stat `libgcc1.a\': No such file or d ...…

查看全部问答>

超级终端下输入显示乱码,而在dnw下输入显示正常

我这几天用笔记本串口连接开发板(之前用台式机正常),但是在超级终端下输入显示乱码,而在dnw下输入显示正常。开发板像超级终端输出显示正常。 怎么才能在超级终端下正常输入呢,请指教,谢谢!…

查看全部问答>

Pocket PC怎样通过activesync连接到局域网?

我已经安装好Activesync4.2,Pocket PC通过USB线连接到我的电脑上,连接成功.我的电脑是连在局域网上的,怎样将Pocket PC连到局域网上呢?…

查看全部问答>

超级广场效果的耳机放大器电路

本帖最后由 jameswangsynnex 于 2015-3-3 19:54 编辑 用头戴式耳机,尤其是小型耳机听音乐,总感到音乐味不够足,在低频段的效果更差。因此用本机增强耳机的低频特性,并采用立体声反相合成的办法,加上内藏简易矩阵环绕声电路,能获得强劲的低音 ...…

查看全部问答>

大家3.3V电源都用哪个?

大家3.3V电源都用哪个型号?…

查看全部问答>

求一个基于MSP430F149或F449芯片的LCD键盘显示系统的完整C程序

求一个基于MSP430F149或F449芯片的LCD键盘显示系统的完整C程序 说明:本人刚开始学习MSP430,想用MSP430做一个键盘LCD显示控制系统 求一个完整的C程序,来学习和模仿下。 控制系统要求:1,键盘是3*4列的行列式扫描键盘。LCD最好用truely(信利公 ...…

查看全部问答>

转 CAN 学习笔记

转  CAN 学习笔记…

查看全部问答>

微博通知我中了MCU工程师炼成记,可给的QQ联系不上啊?

通知中奖, 可QQ  147198755 联系不上啊。 求看书。…

查看全部问答>