硬汉嵌入式论坛

 找回密码
 立即注册
查看: 317|回复: 4
收起左侧

[DMA2D] DMA2D转码YUV为RGB

[复制链接]

1

主题

3

回帖

6

积分

新手上路

积分
6
发表于 2025-8-18 22:59:53 | 显示全部楼层 |阅读模式
本帖最后由 T0n0T 于 2025-8-18 23:03 编辑

stm32h7尝试DMA2D将 YUV422(YUYV) 转码为 RGB565({r[4:0], g[5:3]}, {g[2:0], b[4:0]}) 时, 尝试软件转码时可以在 st7789 驱动的屏幕上正常显示, 但尝试使用 DMA2D 硬件转码时, 得不到预期图像, 呈现出分割画面, 花屏;
1. 软件转码效果如下图片所示,并贴上代码
[C] 纯文本查看 复制代码
void soft_y422_to_rgb565(uint32_t* Src, uint32_t* Dst, uint16_t xsize, uint16_t ysize)
{
    uint8_t*  pSrc = (uint8_t*)Src;
    uint16_t* pDst = (uint16_t*)Dst;

    // 确保宽度是偶数,因为YUYV422格式每两个像素共享一个U和V
    uint16_t width = (xsize >> 1) << 1;

    for (uint16_t y = 0; y < ysize; y++) {
        for (uint16_t x = 0; x < width; x += 2) {
            // YUYV422格式:Y0 U Y1 V (4字节表示2个像素)
            uint8_t Y0 = *pSrc++;
            uint8_t U  = *pSrc++;
            uint8_t Y1 = *pSrc++;
            uint8_t V  = *pSrc++;

            // 将U,V从[0,255]范围转换到[-128,127]
            int16_t U_bias = U - 128;
            int16_t V_bias = V - 128;

            // 转换第一个像素 (Y0UV)
            // 使用整数运算提高效率,乘法系数已经左移10位
            int16_t R0 = Y0 + ((1436 * V_bias) >> 10);
            int16_t G0 = Y0 - ((352 * U_bias + 731 * V_bias) >> 10);
            int16_t B0 = Y0 + ((1814 * U_bias) >> 10);

            // 限制到有效范围[0,255]
            R0 = (R0 < 0) ? 0 : ((R0 > 255) ? 255 : R0);
            G0 = (G0 < 0) ? 0 : ((G0 > 255) ? 255 : G0);
            B0 = (B0 < 0) ? 0 : ((B0 > 255) ? 255 : B0);

            // 打包为RGB565格式:{r[4:0],g[5:0],b[4:0]}
            uint16_t rgb565_0 = ((R0 & 0xF8) << 8) | ((G0 & 0xFC) << 3) | (B0 >> 3);
            // st7789 需要字节交换
            *pDst++           = (rgb565_0 << 8) | (rgb565_0 >> 8);

            // 转换第二个像素 (Y1UV)
            int16_t R1 = Y1 + ((1436 * V_bias) >> 10);
            int16_t G1 = Y1 - ((352 * U_bias + 731 * V_bias) >> 10);
            int16_t B1 = Y1 + ((1814 * U_bias) >> 10);

            // 限制到有效范围[0,255]
            R1 = (R1 < 0) ? 0 : ((R1 > 255) ? 255 : R1);
            G1 = (G1 < 0) ? 0 : ((G1 > 255) ? 255 : G1);
            B1 = (B1 < 0) ? 0 : ((B1 > 255) ? 255 : B1);

            // 打包为RGB565格式
            uint16_t rgb565_1 = ((R1 & 0xF8) << 8) | ((G1 & 0xFC) << 3) | (B1 >> 3);
            // st7789 需要字节交换
            *pDst++           = (rgb565_1 << 8) | (rgb565_1 >> 8);
        }

        // 如果宽度是奇数,处理最后一个像素
        if (xsize & 1) {
            // 读取最后的YUYV数据,但只处理一个像素
            uint8_t Y0 = *pSrc++;
            uint8_t U  = *pSrc++;
            pSrc += 2; // 跳过Y1和V,因为只需要一个像素

            int16_t U_bias = U - 128;
            // 对于奇数像素,假设V值与U相同或使用上一行的V值
            int16_t V_bias = U_bias;

            int16_t R0 = Y0 + ((1436 * V_bias) >> 10);
            int16_t G0 = Y0 - ((352 * U_bias + 731 * V_bias) >> 10);
            int16_t B0 = Y0 + ((1814 * U_bias) >> 10);

            R0 = (R0 < 0) ? 0 : ((R0 > 255) ? 255 : R0);
            G0 = (G0 < 0) ? 0 : ((G0 > 255) ? 255 : G0);
            B0 = (B0 < 0) ? 0 : ((B0 > 255) ? 255 : B0);

            uint16_t rgb565_0 = ((R0 & 0xF8) << 8) | ((G0 & 0xFC) << 3) | (B0 >> 3);
            // st7789 需要字节交换
            *pDst++           = (rgb565_0 << 8) | (rgb565_0 >> 8);
        }
    }
}



2. 硬件转码效果如下图片所示,并贴上代码(参考了论坛帖子), 这里不包含时钟初始化

[C] 纯文本查看 复制代码
void dma2d_y422_to_rgb565(uint32_t* Src, uint32_t* Dst, uint16_t xsize, uint16_t ysize)
{
    uint32_t inputLineOffset = 0;
    uint32_t regValue, regMask;

    inputLineOffset = xsize % 16;
    if (inputLineOffset != 0) {
        inputLineOffset = 16 - inputLineOffset;
    }

    regValue = DMA2D_INPUT_YCBCR | (DMA2D_REPLACE_ALPHA << 16) |
               (DMA2D_REGULAR_ALPHA << 20) |
               (DMA2D_RB_REGULAR << 21) |
               (0xFFU << 24) |
               (DMA2D_CSS_422 << 18);

    regMask = DMA2D_BGPFCCR_CM | DMA2D_BGPFCCR_AM | DMA2D_BGPFCCR_ALPHA | DMA2D_BGPFCCR_AI | DMA2D_BGPFCCR_RBS | DMA2D_FGPFCCR_CSS;

    /* Setup DMA2D Configuration */
    DMA2D->CR     = 0x00010000UL | (1 << 9);
    DMA2D->OPFCCR = DMA2D_OUTPUT_RGB565;
    DMA2D->OOR    = 240 - xsize;
    DMA2D->OPFCCR |= (DMA2D_REGULAR_ALPHA << 20);
    DMA2D->OPFCCR |= (DMA2D_RB_REGULAR << 21);
    DMA2D->FGPFCCR |= (regMask & regValue);
    DMA2D->FGOR  = inputLineOffset;
    DMA2D->NLR   = (uint32_t)(xsize << 16) | (uint32_t)ysize;
    DMA2D->OMAR  = (uint32_t)Dst;
    DMA2D->FGMAR = (uint32_t)Src;

    /* Enable the transfer complete, transfer error and configuration error interrupts */
    // DMA2D->CR |= DMA2D_IT_TC | DMA2D_IT_TE | DMA2D_IT_CE;

    /* Enable the Peripheral */
    DMA2D->CR |= DMA2D_CR_START;
    while (DMA2D->CR & DMA2D_CR_START) {
        __NOP();
    }
}


调用的代码如下
[C] 纯文本查看 复制代码
SRAM_SET_RAM_D1 uint32_t disp[240 * 320 / 2];
SRAM_SET_RAM_D1 uint32_t camera_buffer[240 * 320 / 2]; // 240x320 RGB565
//... 中间省略
soft_y422_to_rgb565(camera_buffer, disp, 240, 320);
            // dma2d_y422_to_rgb565(camera_buffer, disp, 240, 320);
            st7789_draw_image(0, 0, 240, 320, (uint16_t*)disp);

希望得到指点!
IMG_20250818_224832.jpg
IMG_20250818_224913.jpg
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
118335
QQ
发表于 2025-8-19 08:57:37 | 显示全部楼层
这个是确定可用的

[C] 纯文本查看 复制代码
/*
*********************************************************************************************************
*	函 数 名: DMA2D_Copy_YCbCr_To_RGB
*	功能说明: YCbCr转RGB输出
*	形    参: pSrc:    数据源地址
*	          pDst:    数据目的地址
*	          x:       X轴首地址
*	          y:       Y轴首地址 
*	          xsize:   目的区域的X轴大小,即每行像素数
*	          ysize:   目的区域的Y轴大小,即行数
*	          PixelFormat:   目标区颜色格式
*	          ChromaSampling : YCbCr Chroma sampling : 4:2:0, 4:2:2 or 4:4:4  
*	返 回 值: 无
*********************************************************************************************************
*/
static void DMA2D_Copy_YCbCr_To_RGB(uint32_t *pSrc, 
	                                uint32_t *pDst, 
                                    uint16_t x, 
                                    uint16_t y, 
                                    uint16_t xsize, 
                                    uint16_t ysize, 
                                    uint32_t PixelFormat,
                                    uint32_t ChromaSampling)
{   
	uint32_t cssMode = DMA2D_CSS_420;
	uint32_t inputLineOffset = 0;  

	/* 处理输出行偏移 */
	if(ChromaSampling == JPEG_420_SUBSAMPLING)
	{
		cssMode = DMA2D_CSS_420;

		inputLineOffset = xsize % 16;
		if(inputLineOffset != 0)
		{
			inputLineOffset = 16 - inputLineOffset;
		}    
	}
	else if(ChromaSampling == JPEG_444_SUBSAMPLING)
	{
		cssMode = DMA2D_NO_CSS;

		inputLineOffset = xsize % 8;
		if(inputLineOffset != 0)
		{
			inputLineOffset = 8 - inputLineOffset;
		}    
	}
	else if(ChromaSampling == JPEG_422_SUBSAMPLING)
	{
		cssMode = DMA2D_CSS_422;

		inputLineOffset = xsize % 16;
		if(inputLineOffset != 0)
		{
			inputLineOffset = 16 - inputLineOffset;
		}      
	}  

	/* DMA2D采用存储器到存储器模式,并且执行FPC颜色格式转换, 这种模式是前景层作为DMA2D输入 */  
	DMA2D->CR      = 0x00010000UL | (1 << 9);
	DMA2D->OOR     = 0;

	/* 输出格式 */
	DMA2D->OPFCCR  = PixelFormat 
					 | (DMA2D_REGULAR_ALPHA << 20) 
					 | (DMA2D_RB_REGULAR << 21);  

	/* 前景层输入格式 */	
	DMA2D->FGPFCCR = DMA2D_INPUT_YCBCR 
					 | (DMA2D_REPLACE_ALPHA << 16) 
					 | (DMA2D_REGULAR_ALPHA << 20)
					 | (DMA2D_RB_REGULAR << 21)   
					 | (0xFFU << 24)              
					 | (cssMode << 18);		

	DMA2D->FGOR    = inputLineOffset;
	DMA2D->NLR     = (uint32_t)(xsize << 16) | (uint16_t)ysize;      
	DMA2D->OMAR    = (uint32_t)pDst;
	DMA2D->FGMAR   = (uint32_t)pSrc;  

	/* 启动传输 */
	DMA2D->CR   |= DMA2D_CR_START;   

	/* 等待DMA2D传输完成 */
	while (DMA2D->CR & DMA2D_CR_START) {} 
}
回复

使用道具 举报

1

主题

3

回帖

6

积分

新手上路

积分
6
 楼主| 发表于 2025-8-19 09:34:36 | 显示全部楼层
eric2013 发表于 2025-8-19 08:57
这个是确定可用的

[mw_shl_code=c,true]/*

谢谢回复, 但是你的代码试了一下, 得到的效果没区别;
我目前的 YUV422 是来源于摄像头的 DCMI 裁剪后的输出, 考虑 DMA2D 的 YCrBr 处理基于 8x8 的像素块, 是否和 JPEG 解码出来的数据输入到 DMA2D 不一样? 还没仔细研究过 JPEG 编码, 若有不对请硬汉哥指教
回复

使用道具 举报

3

主题

40

回帖

49

积分

初级会员

积分
49
发表于 2025-8-19 12:59:47 来自手机 | 显示全部楼层
来源图片高度和宽度是不是16的倍数?
回复

使用道具 举报

1

主题

3

回帖

6

积分

新手上路

积分
6
 楼主| 发表于 2025-8-19 14:52:49 | 显示全部楼层
spi-sd 发表于 2025-8-19 12:59
来源图片高度和宽度是不是16的倍数?

dcmi 数据 240*320 个yuv422像素, st7789 240*320 个 RGB565 像素, 都是uint16_t 为单位, 也都是16的倍数, 应该不至于吧
回复

使用道具 举报

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

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2025-9-26 19:42 , Processed in 0.045046 second(s), 28 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

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