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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Adobe XD最新版号查询,如何使用?

Adobe XD是Adobe家推出的基于矢量的原型设计合作工具&#xff0c;被业界视为应对Sketch的“对抗”产品。Adobe XD不同于Sketch的系统限制&#xff0c;灵活性比较高&#xff0c;Windows和Mac都可以使用。自2017年推出以来&#xff0c;Adobe XD版经历了多次更新&#xff0c;这篇文…

Android RelativeLayout Rtl布局下的bug:paddingStart会同时作用于左右内边距

问题现象 如上图&#xff0c;只是设置了paddingStart&#xff0c;在RTL布局下&#xff0c;左右都产生了10dp的间距。其他布局如LinearLayout&#xff0c;FrameLayout则没有这个问题。 private void positionAtEdge(View child, LayoutParams params, int myWidth) {if (isLayou…

tensorrt-llm与vllm的量化性能比较

准备部署lora微调好的语言大模型&#xff0c;有tensorrt-llm和vllm两种加速策略可选&#xff0c;而量化策略也有llm.int8&#xff0c;gptq&#xff0c;awq可用&#xff0c; 怎样的组合才能获得最佳精度与速度呢&#xff0c;这是个值得探讨的问题&#xff0c;本文以llama-factor…

代理记账公司的五大问题及其解决方案

代理记账公司是现代企业管理中不可或缺的一部分&#xff0c;它为企业的日常运营提供了专业、高效的服务&#xff0c;随着行业的发展和竞争的加剧&#xff0c;代理记账公司的面临的问题也日益突出&#xff0c;这些问题主要表现在以下几个方面&#xff1a; 业务流程不规范 许多代…

【前端】display:none和visibility:hidden两者的区别

&#x1f60e; 作者介绍&#xff1a;我是程序员洲洲&#xff0c;一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专家博主。公粽号&#xff1a;洲与AI。 &#x1f913; 欢迎大家关注我的专栏&#xff0c;我将分享Web前后端开发、…

C语言 | Leetcode C语言题解之第132题分割回文串II

题目&#xff1a; 题解&#xff1a; int minCut(char* s) {int n strlen(s);bool g[n][n];memset(g, 1, sizeof(g));for (int i n - 1; i > 0; --i) {for (int j i 1; j < n; j) {g[i][j] (s[i] s[j]) && g[i 1][j - 1];}}int f[n];for (int i 0; i <…

YOLOv8改进 | Conv篇 | 利用YOLOv10提出的C2fUIB魔改YOLOv8(附代码 + 完整修改教程)

一、本文介绍 本文给大家带来的改进机制是利用YOLOv10提出的C2fUIB模块助力YOLOv8进行有效涨点&#xff0c;其中C2fUIB模块所用到的CIB模块是一种紧凑的倒置块结构&#xff0c;它采用廉价的深度卷积进行空间混合&#xff0c;并采用成本效益高的点卷积进行通道混合。本文针对该…

AI大数据统计《庆余年2》中的小人物有哪些?

《庆余年2》除了主角表演经常&#xff0c;每个配角小人物也很出彩。那到底有哪些小人物呢&#xff1f; 在deepseek中输入提示词&#xff1a; 你是一个Python编程专家&#xff0c;要写一个Python脚本&#xff0c;具体步骤如下&#xff1a; 读取文档&#xff1a;"D:\qyn\…

数据结构与算法之Floyd弗洛伊德算法求最短路径

目录 前言 Floyd弗洛伊德算法 定义 步骤 一、初始化 二、添加中间点 三、迭代 四、得出结果 时间复杂度 代码实现 结束语 前言 今天是坚持写博客的第18天&#xff0c;希望可以继续坚持在写博客的路上走下去。我们今天来看看数据结构与算法当中的弗洛伊德算法。 Flo…

Leetcode3164. 优质数对的总数 II

Every day a Leetcode 题目来源&#xff1a;3164. 优质数对的总数 II 解法1&#xff1a;统计因子 遍历 nums1&#xff0c;统计所有元素的因子个数&#xff0c;记录到哈希表 cnt 中。 遍历 nums2&#xff0c;那么有 cnt[nums2[i]*k] 个数可以被 nums2[i]*k 整除&#xff0c;…

利用conda进行R的安装

1.miniconda3的安装 官网&#xff1a;Miniconda — Conda documentation 找到对应系统、Python版本下载 wget https://mirrors.ustc.edu.cn/anaconda/miniconda/Miniconda3-latest-Linux-x86_64.sh #wget -c https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x…

信息系统项目管理师0141:产品范围和项目范围(9项目范围管理—9.1管理基础—9.1.1产品范围和项目范围)

点击查看专栏目录 文章目录 第9章 项目范围管理9.1 管理基础9.1.1 产品范围和项目范围 第9章 项目范围管理 项目范围管理包括确保项目做且只做所需的全部工作&#xff0c;以成功完成项目。项目范围管理主要在于定义和控制哪些工作应该包括在项目内&#xff0c;哪些不应该包含在…

Spring运维之boot项目开发关键之日志操作以及用文件记录日志

日志基础 日志 在企业级开发中还是比较重要的 我们来写一个日志 RestController RequestMapping("/books") public class Controller {//创建记录日志的对象private static final Logger log LoggerFactory.getLogger(Controller.class);GetMappingpublic String …

java自学阶段二:JavaWeb开发60(mybatis学习)

目录&#xff1a; 学习目标mybatis的基础用法&#xff08;新增、删除、修改、查询&#xff09; 一&#xff1a;学习目标&#xff1a; 1&#xff09;了解mybatis的基础概念&#xff1b; 2&#xff09;学会mybatis的基础用法&#xff1b; 二、mybatis的基础概念&#xff1a; M…

在Cisco Packet Tracer上配置NAT

目录 前言一、搭建网络拓扑1.1 配置PC机1.2 配置客户路由器1.3 配置ISP路由器 二、配置NAT2.1 在客户路由器中配置NAT2.2 测试是否配置成功 总结 前言 本篇文章是在了解NAT的原理基础上&#xff0c;通过使用Cisco Packet Tracer 网络模拟器实现模拟对NAT的配置&#xff0c;以加…

C++ | Leetcode C++题解之第132题分割回文串II

题目&#xff1a; 题解&#xff1a; class Solution { public:int minCut(string s) {int n s.size();vector<vector<int>> g(n, vector<int>(n, true));for (int i n - 1; i > 0; --i) {for (int j i 1; j < n; j) {g[i][j] (s[i] s[j]) &…

Jenkins+Rancher2.7部署构建

在Jenkins中使用rancher插件时需要去查找工作负载地址 在Rancher2.7没有查看Api按钮了需要自己去查找 1.进入https://192.168.x.xx:6443/v3/projects/ 2.输入在rancher中要查找的的项目名称并点击deployment连接进入下一个页面 3.找到自己的deployment随便点一个进去 4.浏览…

Nginx设置缓存后,访问网页404 问题原因及解决方案(随手记)

目录 问题描述Nginx文件 解决方案查看error_log日志问题原因修改文件并测试Nginx文件测试 总结 问题描述 在Nginx中设置缓存expires后&#xff0c;结果重启nginx&#xff0c;网站访问404了。 Nginx文件 server {listen 80;server_name bird.test.com;location / {root /app/…

Echarts 在指定部分做文字标记

文章目录 需求分析1. demo12. demo22. demo3 定位解决需求 实现在Echarts的折线图中,相同Y值的两点之间显示’abc’ 分析 1. demo1 使用 ECharts 的 markLine 功能来在相邻两个点之间添加标记。其中,我们通过设置标记的 yAxis 和 label 来控制标记的位置和显示内容。最后…

VL53L4CX TOF开发(2)----修改测距范围及测量频率

VL53L4CX TOF开发.2--修改测距范围及测量频率 概述视频教学样品申请完整代码下载测距范围测量频率硬件准备技术规格系统框图应用示意图生成STM32CUBEMX选择MCU串口配置IIC配置 XSHUTGPIO1X-CUBE-TOF1app_tof.c详细解释测量频率修改修改测距范围 概述 最近在弄ST和瑞萨RA的课程…