I.MX RT1170之MIPI CSI摄像头初始化和显示流程详解

11 篇文章 5 订阅

在上一篇文章I.MX RT1170之MIPI DSI初始化和显示流程详解中,我们介绍了RT1170单片机中MIPI DSI显示屏初始化和显示的详细步骤,那这一节就来介绍MIPI的另一个接口应用:摄像头CSI的初始化和配置流程。

对于摄像头来说,一般我们还要将摄像头的内容显示到LCD上,所以本篇的例程也涉及MIPI DSI的初始化,就不详细介绍了,具体参考上一篇文章。

1 基础

1.1 引脚定义

与MIPI DSI一样,我们可以选择不同厂家的不同摄像头,所以就有不同的配置。另外,不同摄像头的MIPI引脚定义也不太一样。常见的引脚如下:

1、CLKP 和 CLKN

  • 时钟信号:这两个引脚组成时钟差分对,用于同步数据传输。
  • 作用:提供同步信号,使得主处理器和摄像头模块能够以一致的速率传输和接收数据。

2、DP0 和 DN0

  • 数据通道 0:这两个引脚组成数据差分对。
  • 作用:传输视频数据和其他相关信息。数据通道 0 是必需的,能够在高速(HS)和低功耗(LP)模式下工作。

3、DP1 和 DN1

  • 数据通道 1:这两个引脚组成另一个数据差分对,通常用于提高数据传输带宽。
  • 作用:与数据通道 0 一起工作,增加数据传输速率。多个数据通道可以并行工作,进一步提高传输效率。

4、I2C 接口:用于摄像头模块的配置和控制。

  • 作用:通过I2C接口,主处理器可以发送命令和配置参数到摄像头模块,例如分辨率设置、曝光控制、白平衡等。
  • 特点:I2C是一种双向、串行通信协议,具有时钟线(SCL)和数据线(SDA)两条信号线,通常用于低速外围设备的通信。

实际上就比DSI多了一个I2C接口,这样可以配置摄像头的一些参数。当然还有复位、电源引脚等,这个根据不同的厂家有不同的定义。

1.2 PXP

RT1170中包括一个名为PXP(Pixel Pipeline)的图像处理单元,专门用于加速图像处理操作,减轻主处理器的负担,提高系统性能。

PXP外设是一个强大的图像处理引擎,主要用于处理图像数据,支持各种图像操作,包括图像缩放、颜色空间转换、图像混合和旋转等。

1. 图像缩放

PXP可以对图像进行缩放操作,支持放大和缩小。

  • 双线性插值:提供高质量的图像缩放效果。
  • 任意比例缩放:支持非整数的缩放比例,灵活适应不同的应用需求。

2. 颜色空间转换

PXP支持多种颜色空间之间的转换,包括:

  • RGB到YUV转换:用于视频处理应用中。
  • YUV到RGB转换:用于显示设备中。

3. 图像混合

PXP支持将多个图像层混合在一起,常用于实现透明效果和图像叠加。

  • Alpha混合:支持全局和每像素的Alpha混合。
  • 色键混合:支持基于颜色键的图像混合。

4. 图像旋转

PXP可以对图像进行90度、180度和270度的旋转操作,以及镜像操作。

  • 旋转:适用于改变图像显示方向的应用。
  • 镜像:支持水平和垂直镜像。

5. 图像格式转换

PXP支持多种图像格式的转换,例如:

  • RGB565:16位色彩格式。
  • RGB888:24位色彩格式。
  • ARGB8888:带Alpha通道的32位色彩格式。

PXP 的结构与工作原理

PXP外设由多个功能模块组成,各模块协同工作以完成复杂的图像处理任务。

在这里插入图片描述

  1. AS (Alpha Surface Graphics Buffers):
    • 来源:来自图形处理器的图形数据或其它视频编码器/图像传感器处理单元的数据。
    • 功能:提供带有Alpha通道的图像,用于后续的混合操作。
  2. PS (Processed Surface Video or Image Sensor Buffers):
    • 来源:来自视频编解码器或图像传感器的处理数据。
    • 功能:处理和存储需要进行缩放、颜色空间转换和旋转的图像数据。
  3. Alpha Surface Engine (AS Alpha Surface Engine):
    • 功能:处理Alpha表面数据,将其与处理后的表面数据进行组合、Alpha混合和颜色键处理。
  4. Process Surface Scaling Engine (PS Process Surface Scaling Engine):
    • 功能:对图像进行缩放操作,支持任意比例的放大或缩小。
  5. CSC1 (Color Space Converter 1):
    • 功能:进行颜色空间转换,例如从RGB到YUV或从YUV到RGB。
  6. Rotation:
    • 功能:对图像进行旋转操作,支持90度、180度、270度旋转以及镜像操作。
  7. Composite Alpha Blending Color Key:
    • 功能:将处理后的图像与Alpha图像进行混合,支持Alpha混合和颜色键处理。
  8. 第二阶段的 Rotation:
    • 功能:在将图像输出到显示控制器之前,对图像进行最后的旋转调整。
  9. IRAM Double Buffer:
    • 功能:用于存储处理后的图像,提供双缓冲机制以提高图像处理效率。
  10. Display Buffer:
  • 功能:最终输出的图像缓冲区,图像从这里传输到显示控制器进行显示。
  1. Display Controller:
  • 功能:控制图像的最终显示,将处理后的图像数据发送到显示屏。

数据流总结

  1. 图像数据可以来自图形处理器或视频编码器/图像传感器。
  2. 数据进入PXP模块后,首先进行缩放、颜色空间转换和初步旋转处理。
  3. 经过初步处理的图像与Alpha图像在Alpha表面引擎中进行混合。
  4. 混合后的图像通过旋转模块进行进一步处理,调整到最终的显示角度。
  5. 最终处理后的图像存储在IRAM双缓冲区或直接进入显示缓冲区。
  6. 显示控制器将最终图像数据发送到显示屏进行显示。

2 代码执行步骤

2.1 硬件初始化

1、复位显示混合模块

和DSI一样,复位显示混合模块,否则未断电开启调试时,显示可能不正常,这个与RT1170有关。

static void BOARD_ResetDisplayMix(void)
{
    /*
     * Reset the displaymix, otherwise during debugging, the
     * debugger may not reset the display, then the behavior
     * is not right.
     */
    SRC_AssertSliceSoftwareReset(SRC, kSRC_DisplaySlice);
    while (kSRC_SliceResetInProcess == SRC_GetSliceResetState(SRC, kSRC_DisplaySlice))
    {
    }
}

2、初始化引脚

前面说了,我们要将摄像头的数据显示到MIPI DSI接口的LCD上,所以上一节相关的引脚我们都要初始化,包括:背光、复位等引脚。

对于MIPI CSI来说,同样地,MIPI相关引脚没有任何复用功能,默认已经是初始化好的了。所以我们还要初始化的引脚如下:

  • 摄像头复位引脚
  • 摄像头电源引脚(可省略,即一直供电)
  • I2C引脚:控制摄像头的参数

注意在RT1170中,I2C的SCL和SDA都要打开software input on选项,它用于通过软件控制引脚的输入信号。这项设置允许软件读取引脚的状态,即使引脚配置为其他功能(如输出),这样才满足I2C协议。

2.2 摄像头和LCD缓冲区

这里来看一下声明的两个缓冲区,都是放在non-cacheable区域:

#define FRAME_BUFFER_ALIGN 32
AT_NONCACHEABLE_SECTION_ALIGN(
    static uint8_t s_lcdBuffer[2][1280][720 * 2], FRAME_BUFFER_ALIGN);

#define DEMO_CAMERA_BUFFER_ALIGN  64
AT_NONCACHEABLE_SECTION_ALIGN(static uint8_t s_cameraBuffer[3][720][1280 * 4],
                              DEMO_CAMERA_BUFFER_ALIGN);

1、LCD

  • FRAME_BUFFER_ALIGN对齐:这是因为考虑了64位AXI数据总线的宽度和32字节缓存行的大小,32字节对齐可以确保数据传输和内存访问更加高效
  • 假设屏幕为1280*720,数据格式用RGB565(2字节)
  • 设置两个这样的缓冲区:因为摄像头图像实时在更新,显示当前画面的同时要用另一个缓冲区接受下一帧图像

2、Camera

  • DEMO_CAMERA_BUFFER_ALIGN对齐:除了LCD相同的原因外,高分辨率摄像头(1280x720)的图像数据量非常大。64字节对齐可以提高数据吞吐量,也利于DMA传输,确保图像数据以最快的速度传输到处理单元或存储器。
  • 摄像头输出的图像为720*1280的,格式为4个字节,这些参数看摄像头手册可以查到,摄像头输出的是XYUV8888格式的图像
  • 设置三个缓冲区:提高图像捕获和处理的效率,避免丢帧,提供流畅的图像流。

可以看到这里摄像头图像的格式和LCD的格式不匹配,像素也不匹配(需要旋转90°),在单片机中做这种图像的转换非常占CPU运算时间,所以我们就可以用刚刚提到的PXP来完成这些操作。

为什么都设置为non-cacheable?

对于摄像头来说很好理解,摄像头数据通过DMA从MIPI DSI传到buffer缓冲区中,CPU并不知道DMA的这些操作,所以肯定要设置为non-cacheable。

那对于LCD来说,也是使用PXP这个外设进行转换的,和DMA一样,CPU不知道buffer发生了改变,如果使用了Cache就会有数据一致性的问题,所以都要放在non-cacheable区域。

2.3 初始化PXP

初始化PXP模块

  • PXP_Init(DEMO_PXP);
    • 作用:初始化PXP模块,将其配置为默认状态,为后续的图像处理做好准备。

设置处理表面的背景颜色

  • PXP_SetProcessSurfaceBackGroundColor(DEMO_PXP, 0U);
    • 作用:设置处理表面的背景颜色为黑色(0U)。这确保在处理区域内没有图像数据的地方显示为黑色。

设置处理表面的位置

  • PXP_SetProcessSurfacePosition(DEMO_PXP, 0U, 0U, DEMO_BUFFER_HEIGHT - 1U, DEMO_BUFFER_WIDTH - 1U);
    • 作用:定义处理表面的区域,起始位置为(0,0),终止位置为(DEMO_BUFFER_HEIGHT - 1, DEMO_BUFFER_WIDTH - 1)。这一步明确了PXP处理图像的范围。

禁用Alpha表面

  • PXP_SetAlphaSurfacePosition(DEMO_PXP, 0xFFFFU, 0xFFFFU, 0U, 0U);
    • 作用:禁用Alpha表面,将其位置设置为无效值(0xFFFFU)。在这个配置中,不使用Alpha混合功能。

设置颜色空间转换模式

  • PXP_SetCsc1Mode(DEMO_PXP, kPXP_Csc1YCbCr2RGB);
    • 作用:将颜色空间转换模式设置为从YCbCr到RGB。这是为了在图像处理过程中进行颜色空间的转换,使得输出图像符合显示设备的颜色格式。

启用颜色空间转换

  • PXP_EnableCsc1(DEMO_PXP, true);
    • 作用:启用颜色空间转换功能,使PXP在处理图像时执行YCbCr到RGB的转换。

2.4 初始化摄像头

2.4.1 初始化I2C

前面有提到,摄像头的对比度、亮度等参数都是通过I2C更改的,所以我们要初始化I2C:

const clock_root_config_t lpi2cClockConfig = {
        .clockOff = false,
        .mux      = BOARD_CAMERA_I2C_CLOCK_SOURCE,
        .div      = BOARD_CAMERA_I2C_CLOCK_DIVIDER,
    };

CLOCK_SetRootClock(BOARD_CAMERA_I2C_CLOCK_ROOT, &lpi2cClockConfig);
BOARD_LPI2C_Init(BOARD_CAMERA_I2C_BASEADDR,CLOCK_GetRootClockFreq(BOARD_CAMERA_I2C_CLOCK_ROOT));

打开时钟,初始化外设即可。

2.4.2 摄像头接收参数初始化

camera_config_t cameraConfig;

memset(&cameraConfig, 0, sizeof(cameraConfig));
/* CSI input data bus is 24-bit, and save as XYUV8888.. */
cameraConfig.pixelFormat                = kVIDEO_PixelFormatXYUV;
cameraConfig.bytesPerPixel              = DEMO_CAMERA_BUFFER_BPP;
cameraConfig.resolution                 = FSL_VIDEO_RESOLUTION(DEMO_CAMERA_WIDTH, DEMO_CAMERA_HEIGHT);
cameraConfig.frameBufferLinePitch_Bytes = DEMO_CAMERA_WIDTH * DEMO_CAMERA_BUFFER_BPP;
cameraConfig.interface                  = kCAMERA_InterfaceGatedClock;
cameraConfig.controlFlags               = DEMO_CAMERA_CONTROL_FLAGS;
cameraConfig.framePerSec                = DEMO_CAMERA_FRAME_RATE;

CAMERA_RECEIVER_Init(&cameraReceiver, &cameraConfig, NULL, NULL);

这些参数都要根据摄像头的手册进行设置,如果发现摄像头数据不对,特别检查一下这里的controlFlagsresolutionbytesPerPixelpixelFormat

CAMERA_RECEIVER_Init函数就是设置这些参数了,将这些值设置到CSI的寄存器中,不展开分析了。

2.4.3 CSI设置

2.4.3.1 时钟

对于CSI来说,它有下面四个时钟:

ClockDescription
clk_ui用户接口时钟(User interface clock)
clk_ui的频率必须确保从data_out输出接收到的数据等于或大于物理MIPI接口的总带宽。clk_ui除了带宽要求外,与clk无其他关系。
clkRX控制器处理从D-PHY接收数据的输入时钟
该时钟必须等于或快于从Rx D-PHY接收的字节时钟RxByteClkHS_In0。
clk_escRX逃逸时钟(RX Escape Clock)
这必须与Rx D-PHY接收的逃逸时钟相同。
pclk像素时钟(Pixel Clock)

下面来看一下代码中的设置:

const clock_root_config_t csi2ClockConfig = {
    .clockOff = false,
    .mux      = 5,
    .div      = 8,
};

const clock_root_config_t csi2EscClockConfig = {
    .clockOff = false,
    .mux      = 5,
    .div      = 8,
};

const clock_root_config_t csi2UiClockConfig = {
    .clockOff = false,
    .mux      = 5,
    .div      = 8,
};


// MIPI CSI2 连接到 CSI
CLOCK_EnableClock(kCLOCK_Video_Mux);
VIDEO_MUX->VID_MUX_CTRL.SET = (VIDEO_MUX_VID_MUX_CTRL_CSI_SEL_MASK);

CLOCK_SetRootClock(kCLOCK_Root_Csi2, &csi2ClockConfig);
CLOCK_SetRootClock(kCLOCK_Root_Csi2_Esc, &csi2EscClockConfig);
CLOCK_SetRootClock(kCLOCK_Root_Csi2_Ui, &csi2UiClockConfig);
  • csi2ClockConfig:这个时钟应等于或快于从接收DPHY接收的字节时钟(D0_HS_BYTE_CLKD)。官方开发板有两个数据通道,MIPI CSI像素格式是每像素16位,支持的最大分辨率是720*1280@30Hz,所以MIPI CSI2时钟应快于720*1280*30 = 27.6MHz,这里选择60MHz。

  • csi2EscClockConfig :ESC时钟应在60~80 MHz范围内。

  • csi2UiClockConfig:UI时钟应等于或快于输入像素时钟。摄像头支持的最大分辨率是720*1280@30Hz,所以这个时钟应快于720*1280*30 = 27.6MHz,这里选择60MHz。

  • 在RT中有MIPI CSI-2和CSI接口(具体查看手册),将MIPI CSI-2接口接收到的图像数据流路由到CSI(Camera Sensor Interface)模块,从而使CSI模块能够处理和管理这些数据

2.4.3.2 启用DHPU电源

启用MIPI DPHY电源并关闭电源隔离,以准备接收MIPI信号:

PGMC_BPC4->BPC_POWER_CTRL |= (PGMC_BPC_BPC_POWER_CTRL_PSW_ON_SOFT_MASK | PGMC_BPC_BPC_POWER_CTRL_ISO_OFF_SOFT_MASK);
2.4.3.3 CSI2接收配置
csi2rx_config_t csi2rxConfig = {0};
csi2rxConfig.laneNum          = DEMO_CAMERA_MIPI_CSI_LANE;
csi2rxConfig.tHsSettle_EscClk = ....;
CSI2RX_Init(MIPI_CSI2RX, &csi2rxConfig);

tHsSettle_EscClk 是 MIPI CSI-2 接口中的一个重要参数,用于定义数据通道进入高速传输状态时的“HS Settle Time”。这是在高速模式和低功耗模式之间切换时的时间间隔,确保数据通道稳定且不产生误码。这里根据不同的摄像头分辨率、帧率等参数都有不同的参数配置,摄像头厂商手册一般有提供,如果没提供的话,可以自行根据效果测试。

2.4.4 摄像头初始化:OV5640

前面摄像头接收参数为摄像头输出的数据格式,这里我们设置一下摄像头本身的参数,这里使用的摄像头是OV5640。

cameraConfig.pixelFormat   = kVIDEO_PixelFormatYUYV;
cameraConfig.bytesPerPixel = 2;
cameraConfig.resolution    = FSL_VIDEO_RESOLUTION(DEMO_CAMERA_WIDTH, DEMO_CAMERA_HEIGHT);
cameraConfig.interface     = kCAMERA_InterfaceMIPI;
cameraConfig.controlFlags  = DEMO_CAMERA_CONTROL_FLAGS;
cameraConfig.framePerSec   = DEMO_CAMERA_FRAME_RATE;
cameraConfig.csiLanes      = DEMO_CAMERA_MIPI_CSI_LANE;
CAMERA_DEVICE_Init(&cameraDevice, &cameraConfig);

对于这里的pixelFormat,可以通过I2C设置OV5640的分辨率。对于其它的一些参数,如分辨率resolution,在CAMERA_DEVICE_Init函数中就是检查一下是否和OV5640的匹配,这里就不详细展开CAMERA_DEVICE_Init(OV5640_Init)初始化函数了,因为这是特定于OV5640的初始化(主要是通过I2C初始化摄像头)。

2.4.5 开启传输和设置buffer

CAMERA_DEVICE_Start(&cameraDevice);

/* Submit the empty frame buffers to buffer queue. */
for (uint32_t i = 0; i < DEMO_CAMERA_BUFFER_COUNT; i++)
{
	CAMERA_RECEIVER_SubmitEmptyBuffer(&cameraReceiver, (uint32_t)(s_cameraBuffer[i]));
}

这里开启传输CAMERA_DEVICE_Start也是通过I2C控制摄像头开始通过传感器获得摄像头数据。CAMERA_RECEIVER_SubmitEmptyBuffer则是这里实现了一个循环buffer缓冲区,将我们前面定义的三个摄像头接收buffer提交过去,等摄像头数据来了,就可以循环保存到这些buffer中了。

2.5 摄像头数据->LCD显示流程

初始化LCD的流程和上一节基本一致,这里就不过多介绍了。比上一节多的是,我们还需要将摄像头和LCD联系起来。

2.5.1 LCD回调函数

对于LCDIFV2来说,我们可以设置一个回调函数:

g_dc.ops->setCallback(&g_dc, 0, DEMO_BufferSwitchOffCallback, NULL);

当一帧图像数据传输完成时,LCDIFV2会产生中断,并调用预设的回调函数。这种情况下,回调函数可以用于处理后续的图像数据传输或进行屏幕刷新操作。来看一下这个函数:

static volatile bool s_newFrameShown = false;
static volatile uint8_t s_lcdActiveFbIdx;

static void DEMO_BufferSwitchOffCallback(void *param, void *switchOffBuffer)
{
    s_newFrameShown = true;
    s_lcdActiveFbIdx ^= 1;
}

由于这是在中断中执行的,所以用到的两个变量都要声明为volatile,防止打开编译器优化的时候,编译器把变量优化掉了。

  • s_lcdActiveFbIdx:前面我们LCD buffer定义了两个,这个变量就用来标识显示buffer的索引
  • s_newFrameShown:这个在每次LCDIFV2显示完图像后设置为false,然后等下次摄像头数据转换完成后在此中断中设置为true

2.5.2 主任务分析

现在来分析一下我们的任务中是如何将摄像头数据转化给LCD显示的。

2.5.2.1 PXP配置

前面我们说了,摄像头输出的数据和我们LCD显示的格式有一定的出入,所以我们需要用PXP进行转换:

1、设置处理表面的背景颜色为黑色。

PXP_SetProcessSurfaceBackGroundColor(DEMO_PXP, 0);

2、配置PXP旋转输出缓冲区,旋转90度,不进行翻转

PXP_SetRotateConfig(DEMO_PXP, kPXP_RotateOutputBuffer, kPXP_Rotate90, kPXP_FlipDisable);

3、设置缩放,输入尺寸为摄像头分辨率,输出尺寸为显示缓冲区尺寸

PXP_SetProcessSurfaceScaler(DEMO_PXP, DEMO_CAMERA_WIDTH, DEMO_CAMERA_HEIGHT, DEMO_BUFFER_HEIGHT, DEMO_BUFFER_WIDTH);
2.5.2.2 主程序分析

接下来我们就使能摄像头的数据传输:

CAMERA_RECEIVER_Start(&cameraReceiver);

摄像头的CMOS传感器获取到数据后将其通过MIPI协议传给我们的MIPI-CSI外设,然后再给我们的摄像头显示。下面看一下while循环中完成了什么事:

while (1)
{
    /* Wait to get the full frame buffer to show. */
    while (kStatus_Success != CAMERA_RECEIVER_GetFullBuffer(&cameraReceiver, &cameraReceivedFrameAddr))
    {
    }

    /* Wait for the previous frame buffer is shown, and there is available
    inactive buffer to fill. */
    while (s_newFrameShown == false)
    {
    }

    /* Convert the camera input picture to RGB format. */
    psBufferConfig.bufferAddr = cameraReceivedFrameAddr;
    PXP_SetProcessSurfaceBufferConfig(DEMO_PXP, &psBufferConfig);

    lcdFrameAddr                   = s_lcdBuffer[s_lcdActiveFbIdx ^ 1];
    outputBufferConfig.buffer0Addr = (uint32_t)lcdFrameAddr;
    PXP_SetOutputBufferConfig(DEMO_PXP, &outputBufferConfig);

    PXP_Start(DEMO_PXP);

    /* Wait for PXP process complete. */
    while (!(kPXP_CompleteFlag & PXP_GetStatusFlags(DEMO_PXP)))
    {
    }
    PXP_ClearStatusFlags(DEMO_PXP, kPXP_CompleteFlag);

    /* Return the camera buffer to camera receiver handle. */
    CAMERA_RECEIVER_SubmitEmptyBuffer(&cameraReceiver, (uint32_t)cameraReceivedFrameAddr);

    /* Show the new frame. */
    s_newFrameShown = false;
    g_dc.ops->setFrameBuffer(&g_dc, 0, lcdFrameAddr);
}

现在来一步步分析一下:

1、等待从摄像头接收完整的帧缓冲区

在摄像头传输完图像数据后会将数据填入我们设置的buffer中,此时CAMERA_RECEIVER_GetFullBuffer会返回成功,表示有新的图像帧可以显示。

uint32_t cameraReceivedFrameAddr;

while (kStatus_Success != CAMERA_RECEIVER_GetFullBuffer(&cameraReceiver, &cameraReceivedFrameAddr))
{
}
  • 摄像头中断后处理数据的过程在CSI_TransferHandleIRQ中,这里不详细分析了

2、等待前一帧显示完成

/* Wait for the previous frame buffer is shown, and there is available
inactive buffer to fill. */
while (s_newFrameShown == false)
{
}

3、将摄像头输入图像转换为RGB格式

配置处理表面缓冲区(psBufferConfig)

pxp_ps_buffer_config_t psBufferConfig = {
    .pixelFormat = kPXP_PsPixelFormatYUV1P444, /* Note: This is 32-bit per pixel */
    .swapByte    = false,
    .bufferAddrU = 0U,
    .bufferAddrV = 0U,
    .pitchBytes  = DEMO_CAMERA_WIDTH * DEMO_CAMERA_BUFFER_BPP,
};
  • pixelFormat:设置处理表面的像素格式为 kPXP_PsPixelFormatYUV1P444,即每像素32位的YUV格式。
  • swapByte:设置为 false,不交换字节顺序。
  • bufferAddrUbufferAddrV:分别为U和V分量的缓冲区地址,设为0表示未使用。
  • pitchBytes:每行图像数据的字节数,根据摄像头的宽度和每像素的字节数计算。

配置输出缓冲区(outputBufferConfig)

/* Output config. */
pxp_output_buffer_config_t outputBufferConfig = {
    .pixelFormat    = kPXP_OutputPixelFormatRGB565,
    .interlacedMode = kPXP_OutputProgressive,
    .buffer1Addr    = 0U,
    .pitchBytes     = DEMO_BUFFER_WIDTH * DEMO_LCD_BUFFER_BPP,
    .width  = DEMO_BUFFER_HEIGHT,
    .height = DEMO_BUFFER_WIDTH,
};
  • pixelFormat:设置输出缓冲区的像素格式为 kPXP_OutputPixelFormatRGB565,即每像素16位的RGB格式。
  • interlacedMode:设置为 kPXP_OutputProgressive,即逐行扫描模式。
  • buffer1Addr:输出缓冲区地址,设为0表示未初始化。
  • pitchBytes:每行图像数据的字节数,根据缓冲区宽度和每像素的字节数计算。
  • widthheight:根据是否旋转帧,设置输出图像的宽度和高度。

代码步骤和解释

具体看注释:

// 将bufferAddr设置为摄像头接收到的帧缓冲区地址
psBufferConfig.bufferAddr = cameraReceivedFrameAddr;

// 根据psBufferConfig中的设置,初始化PXP处理表面,包括设置像素格式和数据地址
PXP_SetProcessSurfaceBufferConfig(DEMO_PXP, &psBufferConfig);

// 前面解释过了,s_lcdActiveFbIdx^1表示刚刚转换完的LCD数据的索引
lcdFrameAddr                   = s_lcdBuffer[s_lcdActiveFbIdx ^ 1];

// 设置新的LCD帧缓冲区地址
outputBufferConfig.buffer0Addr = (uint32_t)lcdFrameAddr;

// 根据outputBufferConfig中的设置,初始化PXP输出缓冲区,包括设置像素格式、扫描模式和数据地址、旋转等
PXP_SetOutputBufferConfig(DEMO_PXP, &outputBufferConfig);

// 启动PXP处理
PXP_Start(DEMO_PXP);

// 等待PXP处理完毕
while (!(kPXP_CompleteFlag & PXP_GetStatusFlags(DEMO_PXP)))
{
}
PXP_ClearStatusFlags(DEMO_PXP, kPXP_CompleteFlag);

PXP完成图像变换之后,我们就可以显示图像了:

// 首先返回camera的buffer,因为我们已经将数据通过PXP转化到LCD buffer了
CAMERA_RECEIVER_SubmitEmptyBuffer(&cameraReceiver, (uint32_t)cameraReceivedFrameAddr);
// 显示到LCD
s_newFrameShown = false;  //成功显示后在中断中设置为true
g_dc.ops->setFrameBuffer(&g_dc, 0, lcdFrameAddr);
  • 25
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tilblackout

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值