在上一篇文章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外设由多个功能模块组成,各模块协同工作以完成复杂的图像处理任务。
- AS (Alpha Surface Graphics Buffers):
- 来源:来自图形处理器的图形数据或其它视频编码器/图像传感器处理单元的数据。
- 功能:提供带有Alpha通道的图像,用于后续的混合操作。
- PS (Processed Surface Video or Image Sensor Buffers):
- 来源:来自视频编解码器或图像传感器的处理数据。
- 功能:处理和存储需要进行缩放、颜色空间转换和旋转的图像数据。
- Alpha Surface Engine (AS Alpha Surface Engine):
- 功能:处理Alpha表面数据,将其与处理后的表面数据进行组合、Alpha混合和颜色键处理。
- Process Surface Scaling Engine (PS Process Surface Scaling Engine):
- 功能:对图像进行缩放操作,支持任意比例的放大或缩小。
- CSC1 (Color Space Converter 1):
- 功能:进行颜色空间转换,例如从RGB到YUV或从YUV到RGB。
- Rotation:
- 功能:对图像进行旋转操作,支持90度、180度、270度旋转以及镜像操作。
- Composite Alpha Blending Color Key:
- 功能:将处理后的图像与Alpha图像进行混合,支持Alpha混合和颜色键处理。
- 第二阶段的 Rotation:
- 功能:在将图像输出到显示控制器之前,对图像进行最后的旋转调整。
- IRAM Double Buffer:
- 功能:用于存储处理后的图像,提供双缓冲机制以提高图像处理效率。
- Display Buffer:
- 功能:最终输出的图像缓冲区,图像从这里传输到显示控制器进行显示。
- Display Controller:
- 功能:控制图像的最终显示,将处理后的图像数据发送到显示屏。
数据流总结
- 图像数据可以来自图形处理器或视频编码器/图像传感器。
- 数据进入PXP模块后,首先进行缩放、颜色空间转换和初步旋转处理。
- 经过初步处理的图像与Alpha图像在Alpha表面引擎中进行混合。
- 混合后的图像通过旋转模块进行进一步处理,调整到最终的显示角度。
- 最终处理后的图像存储在IRAM双缓冲区或直接进入显示缓冲区。
- 显示控制器将最终图像数据发送到显示屏进行显示。
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);
这些参数都要根据摄像头的手册进行设置,如果发现摄像头数据不对,特别检查一下这里的controlFlags
、resolution
、bytesPerPixel
和pixelFormat
。
CAMERA_RECEIVER_Init
函数就是设置这些参数了,将这些值设置到CSI的寄存器中,不展开分析了。
2.4.3 CSI设置
2.4.3.1 时钟
对于CSI来说,它有下面四个时钟:
Clock | Description |
---|---|
clk_ui | 用户接口时钟(User interface clock) clk_ui的频率必须确保从data_out输出接收到的数据等于或大于物理MIPI接口的总带宽。clk_ui除了带宽要求外,与clk无其他关系。 |
clk | RX控制器处理从D-PHY接收数据的输入时钟 该时钟必须等于或快于从Rx D-PHY接收的字节时钟RxByteClkHS_In0。 |
clk_esc | RX逃逸时钟(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
-
``csi2EscClockConfig
-
``csi2UiClockConfig
-
在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
,不交换字节顺序。 - bufferAddrU 和 bufferAddrV:分别为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:每行图像数据的字节数,根据缓冲区宽度和每像素的字节数计算。
- width 和 height:根据是否旋转帧,设置输出图像的宽度和高度。
代码步骤和解释
具体看注释:
// 将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);