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

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

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

文章目录

  • 1 基础
    • 1.1 引脚定义
    • 1.2 PXP
  • 2 代码执行步骤
    • 2.1 硬件初始化
    • 2.2 摄像头和LCD缓冲区
    • 2.3 初始化PXP
    • 2.4 初始化摄像头
      • 2.4.1 初始化I2C
      • 2.4.2 摄像头接收参数初始化
      • 2.4.3 CSI设置
        • 2.4.3.1 时钟
        • 2.4.3.2 启用DHPU电源
        • 2.4.3.3 CSI2接收配置
      • 2.4.4 摄像头初始化:OV5640
      • 2.4.5 开启传输和设置buffer
    • 2.5 摄像头数据->LCD显示流程
      • 2.5.1 LCD回调函数
      • 2.5.2 主任务分析
        • 2.5.2.1 PXP配置
        • 2.5.2.2 主程序分析

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。

  • 不懂的可以参考我的MPU文章:L1 Cache之I-Cache和D-cache详解

那对于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);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/679862.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

软件产品必须要进行鉴定测试吗?测试流程和作用简析

软件产品是现代社会中不可或缺的一部分&#xff0c;它们在商业、娱乐、科技等领域的应用广泛且深入。然而&#xff0c;我们是否关注过这些软件产品的鉴定测试呢?鉴定测试是什么?它的测试流程有哪些?又有什么作用呢?在本文中&#xff0c;我们将为您全面解析这些问题。 鉴定…

大数据学习问题记录

问题记录 node1突然无法连接finalshell node1突然无法连接finalshell 今天我打开虚拟机和finalshell的时候&#xff0c;发现我的node1连接不上finalshell,但是node2、node3依旧可以链接&#xff0c;我在网上找了很多方法&#xff0c;但是是关于全部虚拟机连接不上finalshell&a…

USB HOST DWC3 初始化

https://www.cnblogs.com/newjiang/p/15675746.html 如果dr_mode为device&#xff0c;则初始化gadget。 如果dr_mode为host&#xff0c;需要初始化xHCI驱动。在dwc3_host_init函数的最后调用platform_device_add(xhci)添加platform device&#xff08;xhci-hcd&#xff09;&a…

从当当网批量获取图书信息

爬取当当网图书数据并保存到本地&#xff0c;使用request、lxml的etree模块、pandas保存数据为excel到本地。 爬取网页的url为&#xff1a; http://search.dangdang.com/?key{}&actinput&page_index{} 其中key为搜索关键字&#xff0c;page_index为页码。 爬取的数据…

(九)Spring教程——ApplicationContext中Bean的生命周期

1.前言 ApplicationContext中Bean的生命周期和BeanFactory中的生命周期类似&#xff0c;不同的是&#xff0c;如果Bean实现了org.springframework.context.ApplicationContextAware接口&#xff0c;则会增加一个调用该接口方法setApplicationContext()的步骤。 此外&#xff0c…

[数据集][图像分类]蘑菇分类数据集3122张215类别

数据集类型&#xff1a;图像分类用&#xff0c;不可用于目标检测无标注文件 数据集格式&#xff1a;仅仅包含jpg图片&#xff0c;每个类别文件夹下面存放着对应图片 图片数量(jpg文件个数)&#xff1a;3122 分类类别数&#xff1a;215 类别名称:[“almond_mushroom”,“amanita…

C# 中文字符串转GBK字节的示例

一、编写思路 在 C# 中&#xff0c;将中文字符串转换为 GBK 编码的字节数组需要使用 Encoding 类。然而&#xff0c;Encoding 类虽然默认并不直接支持 GBK 编码&#xff0c;但是可以通过以下方式来实现这一转换&#xff1a; 1.使用系统已安装的编码提供者&#xff08;如果系统…

【Spring Cloud Alibaba】开源组件Sentinel

目录 什么是Sentinel发展历史与Hystrix的异同 Sentinel可以做什么&#xff1f;Sentinel的功能Sentinel的开源生态Sentinel的用户安装Sentinel控制台预备环境准备Sentinel 分为两个部分:下载地址 项目集成Sentinel创建项目修改依赖信息添加启动注解添加配置信息在控制器类中新增…

Java 新手入门:基础知识点一览

Java 新手入门&#xff1a;基础知识点一览 想要踏入 Java 的编程世界&#xff1f;别担心&#xff0c;这篇文章将用简单易懂的表格形式&#xff0c;带你快速了解 Java 的基础知识点。 一、Java 是什么&#xff1f; 概念解释Java一种面向对象的编程语言&#xff0c;拥有跨平台、…

最新PHP众筹网站源码 支持报名众筹+商品众筹+公益众筹等多种众筹模式 含完整代码包和部署教程

在当今互联网飞速发展的时代&#xff0c;众筹模式逐渐成为了创新项目、商品销售和公益活动融资的重要渠道。分享一款最新版的PHP众筹网站源码&#xff0c;支持报名众筹、商品众筹和公益众筹等多种众筹模式。该源码包含了完整的代码包和详细的部署教程&#xff0c;让新手也可以轻…

机器学习:算法到底学到了什么?

梯度下降算法 初始化参数 θ 0 \theta^0 θ0可以训练 MAML Maml和pre-train的区别是Maml用到了标注数据 把不同任务的数据放一起进行训练一个模型&#xff0c;当作Maml的baseline 领域迁移迁移学习 Maml好的原因 优化器也可以被学习 网络架构搜索 数据增强 样本权重分配 除…

创建自己的sdk

要把造轮子当作一种享受 右键class出来这个界面,在里面勾选静态库(.lib) 代码如下,我们要将其封装为一个sdk供别人安装使用 cpp文件如下 头文件如下,namespace可以写在头文件里面声明 生成 将那两个文件移到那个文件夹中 我们拿到生成的这两个文件了 截下来演示怎么导入到另…

docker-compose部署 kafka 3.7 启用账号密码认证

文章目录 1. 部署1.1 创建工作目录1.2 yml文件1&#xff09;文件内容2&#xff09;说明&#xff1a; 1.3 启动 2. 测试2.1 kafkamap搭建&#xff08;测试工具&#xff09;2.2 权限测试 1. 部署 1.1 创建工作目录 创建kafka目录&#xff0c;进入该目录 1.2 yml文件 1&#x…

Docker中搭建likeadmin

一、使用Docker中的docker-compose搭建likeadmin 1.去网址&#xff1a;https://gitee.com/likeadmin/likeadmin_php中下载likeadmin 注册一个giee账号后 点那个克隆下载 按照序号在终端复制粘贴进去。 接着&#xff0c;输入ls 可以发现有一个这个&#xff1a; 里面有一个like…

Cell|3D病理弱监督模型在前列腺癌患者治疗中的应用|顶刊精析·24-06-04

小罗碎碎念 这篇文章我之前分析过一遍&#xff0c;本期在原有基础上进行修改。 在正式阅读之前&#xff0c;小罗友情提醒大家重点关注一下几个方向&#xff1a; 从2D组织切片计算的TLS面积已被验证为多种肿瘤类型的预后和免疫治疗响应的生物标志物&#xff0c;然而在小样本活检…

ToxVidLLM:一个用于检测有害视频的多模态多任务框架

在一个社交媒体平台赋予用户成为内容创作者力量的时代&#xff0c;数字领域见证了前所未有的信息传播激增&#xff0c;到2023年&#xff0c;近82%的互联网流量是视频内容。因此&#xff0c;像抖音和YouTub这样的平台已经成为主要的信息来源。一个显著的统计数据凸显了这些平台的…

38【Aseprite 作图】包子——拆解

1 包子轮廓 2 画包子中间的褶皱&#xff0c;褶皱颜色更深一点&#xff0c;不要直接斜着&#xff0c;而是要连着

3. redis常见部署架构

redis常见部署架构 一、redis常见部署架构1、常见部署架构2、多实例部署2.1 规划安装目录、配置文件2.2.2 编辑实例配置文件2.2.3 启动实例2.2.4 测试数据读写 3、redis主从复制3.1 规划3.2 从服务器配置3.3 验证主从状态3.4 主从角色切换 4、分片集群4.1 原理4.2 分片集群的部…

基于单片机的船舱温度临界报警系统

摘 要 : 针对传统的船舱温度临界报警系统&#xff0c;由于温度监控不到位导致报警不及时的问题&#xff0c;提出一个基于单片机的船舱温度临界报警系统设计。该设计将单片机作为核心控制硬件&#xff0c;控制系统整体电路。同时设计数据采集模块&#xff0c;利用温度测量仪测试…

【vue3|第6期】如何正确地更新和替换响应式对象reactive

日期&#xff1a;2024年6月5日 作者&#xff1a;Commas 签名&#xff1a;(ง •_•)ง 积跬步以致千里,积小流以成江海…… 注释&#xff1a;如果您觉得有所帮助&#xff0c;帮忙点个赞&#xff0c;也可以关注我&#xff0c;我们一起成长&#xff1b;如果有不对的地方&#xff…