STM32N6的开发日记(5):数字摄像头接口像素流水线DCMIPP让MCU拥有高性能摄像头资源

博主主要在WX写作,C站消息不能及时看见,如有需要联系请关注:《实在太懒于是不想取名》获取联系方式。

STM32N6作为意法半导体推出的首款集成自研神经处理单元的STM32产品以"MCU+NPU"的异构架构重新定义了边缘AI的算力边界,是意法半导体的MCU最前沿技术栈,不过由于其高难度技术应用以及需要的极其深厚的STM32使用经验以及神经网络基础概念,因此上手难度非常的高。

自从STM32N6发布以来,博主有幸获得一块STM32N6570-DK开发板,闲暇之余陆陆续续折腾如何开发。因此将会陆陆续续发表一些使用STM32N6的使用笔记,以供将来的使用者参考。

回顾学习历程,踩了很多很多的坑,在后续使用STM32N6的文章中也会向大家陆续介绍这些点。

上一期我们介绍了如何在STM32N6中使用LTDC显示一张图片,本期我们介绍STM32N6的摄像头DCMIPP外设如何使用,实现摄像头图片采集并显示到我们的LCD显示屏中。

DCMIPP外设

DCMI全名Digital camera interface数字摄像头外设是 STM32 高性能系列专为对接数字图像传感器设计的硬件接口。它支持并行数据传输模式,可直接连接 OV7670、OV5640 等常见 CMOS 摄像头模组。它能通过硬件同步信号(行同步、场同步、像素时钟)采集摄像头输出的原始图像数据,支持 8/10/12 位不同位宽的像素格式。它可配置 DMA 直接传输模式,将采集到的图像数据自动写入指定内存区域,无需 CPU 持续干预,减轻处理器负担。它具备硬件裁剪、窗口设置功能,可只采集指定区域的图像,降低数据传输量。

在STM32N6系列中有更为高级的DCMIPP全称是Digital camera interface pixel pipeline即数字摄像头接口像素流水线,它是高分辨率摄像头子系统的像素处理链路核心,可从并行接口或 CSI 接口获取像素数据,完成抽取、裁剪、缩小、色彩转换、伽马校正、自动曝光等硬件处理后直接将数据写入内存。

传统 DCMI(如 STM32F4/F7/H7 系列的标配版本)原生不支持 CSI 接口,仅提供并行摄像头接口(最高 8/10/12 位并行数据总线),若需对接 CSI 接口的摄像头,必须额外外接 CSI 转并行的桥接芯片才能实现兼容。DCMIPP不仅可以直连CSI相机,更是通过全硬件链路完成采集 - 处理 - 存储,大幅降低 CPU 负载和系统功耗。

PIPE流水线

STM32N6的DCMIPP有着三条流水线:PIPE0/1/2。

Pipe0 是纯直通无加工的基础流水线,仅将摄像头输入的数据原样写入内存,不做像素提取、格式重构等复杂处理,仅对 CSI 接口输入的 10/12/14 位原始拜尔数据做解包适配,整理为 16 位格式存储,降低后续软件处理难度。它仅支持基础的 2D 图像裁剪与简单抽取操作,不做色彩转换、缩放、去马赛克等图像处理,是最轻量化、延迟最低的采集通道,适合只需要原始数据、由软件自行处理的场景。

Pipe1 是DCMIPP的核心主流水线,承担视频编码、软件分析、神经网络输入等主要视觉任务,支持图像缩小、色彩空间转换、输出多平面像素格式。它可通过内置硬件完成伽马校正、自动曝光、黑电平校准,直接适配无内置ISP的普通图像传感器,还能硬件实现拜尔数据去马赛克,将原始Raw Bayer转为标准RGB 格式。Pipe1拥有完整的硬件图像处理能力,与Pipe2 共享全部高级图像算法,是处理复杂图像、实现高清视觉功能的核心通道。

Pipe2是专用的辅助显示流水线,面向屏幕实时显示等附属需求设计,支持图像缩小与多种像素格式输出,但格式仅限单平面类型,专门针对LTDC显示输出做了优化。它不承担复杂的算法分析与编码任务,专注于稳定、高效输出可直接给显示屏使用的图像数据,配合LTDC外设可实现摄像头画面实时预览,与主流水线Pipe1同时工作,互不干扰,实现一路处理分析、一路同步显示的双路并行工作模式。

STM32N6570-DK中的摄像头

STM32N6570-DK上的摄像头是一颗CSI摄像头,型号IMX335是索尼推出的高性能 CMOS 数字图像传感器,I2C控制接口,CSI数据总线。

CubeMX设置

在上一期LTDC的基础上,我们修改LTDC的参数,将其填充满整个屏幕:

将层大小修改为800*480,可以填充满整个屏幕,像素方式选择RGB888

DCMIPP配置

使能CSI模式,接着配置DCMIPP,DCMIPP格式选择RGB888和LTDC显示方式一致。

DCMIPP配置如上图所示。

其他配置

首先是要配置I2C1用于和IMX335通讯并且修改DCMIPP的时钟到300MHZ。

RIF管理中,授予DCMIPP和CSI的权限。

PC8作为复位引脚,PD2是使能引脚。

将IC18的时钟改为20MHZ,这个时钟将用于CSI时钟使用。

可能是由于CubeMX的BUG,CSI的时钟需要自己配置。

首先是IMX335的驱动部分,这部分太长了,我就直接贴代码吧,这个代码是官方给的历程修改过来的:

复制代码
#ifndef IMX335_H
#define IMX335_H
#include"main.h"
#define IMX335_OK                0
#define IMX335_ERROR            -1
#define IMX335_REG_MODE_SELECT  0x3000
#define IMX335_MODE_STREAMING   0x00
#define IMX335_MODE_STANDBY     0x01
#define IMX335_REG_HOLD         0x3001
#define IMX335_REG_VMAX         0x3030
#define IMX335_REG_SHUTTER      0x3058
#define IMX335_REG_GAIN         0x30E8
#define IMX335_REG_TPG          0x329E
#define IMX335_REG_ID           0x3912
#define IMX335_CHIP_ID          0x00
#define IMX335_REG_HREVERSE     0x304E
#define IMX335_REG_VREVERSE     0x304F
#define AREA3_ST_ADR_1_LSB      0x3074
#define AREA3_ST_ADR_1_MSB      0x3075
#define IMX335_SHUTTER_MIN      9
#define IMX335_EXPOSURE_DEFAULT 23814
#define IMX335_GAIN_MIN         (0 * 1000)
#define IMX335_GAIN_MAX         (72 * 1000)
#define IMX335_GAIN_UNIT_MDB    300
#define IMX335_EXPOSURE_MIN     0
#define IMX335_EXPOSURE_MAX     33266
#define IMX335_WIDTH            2592
#define IMX335_HEIGHT           1944
#define IMX335_INCK_6MHZ        0
#define IMX335_INCK_18MHZ       1
#define IMX335_INCK_24MHZ       2
#define IMX335_INCK_27MHZ       3
#define IMX335_INCK_74MHZ       4
#define IMX335_MIRROR_FLIP_NONE 0x00
#define IMX335_FLIP             0x01
#define IMX335_MIRROR           0x02
#define IMX335_MIRROR_FLIP      0x03
/* Sensor Info Structure (Simplified) */
typedefstruct {
    char name[32];
    uint8_t bayer_pattern;
    uint8_t color_depth;
    uint32_t width;
    uint32_t height;
    uint32_t gain_min;
    uint32_t gain_max;
    uint32_t exposure_min;
    uint32_t exposure_max;
} IMX335_SensorInfo_t;

/* Exported Functions */
int32_tIMX335_Init(I2C_HandleTypeDef *hi2c, uint16_t DevAddr);
int32_tIMX335_ReadID(I2C_HandleTypeDef *hi2c, uint16_t DevAddr, uint32_t *Id);
int32_tIMX335_SetGain(I2C_HandleTypeDef *hi2c, uint16_t DevAddr, int32_t gain);
int32_tIMX335_SetExposure(I2C_HandleTypeDef *hi2c, uint16_t DevAddr, int32_t exposure);
int32_tIMX335_SetFrequency(I2C_HandleTypeDef *hi2c, uint16_t DevAddr, int32_t frequency);
int32_tIMX335_SetFramerate(I2C_HandleTypeDef *hi2c, uint16_t DevAddr, int32_t framerate);
int32_tIMX335_MirrorFlipConfig(I2C_HandleTypeDef *hi2c, uint16_t DevAddr, uint32_t Config);
int32_tIMX335_GetSensorInfo(IMX335_SensorInfo_t *Info);
int32_tIMX335_SetTestPattern(I2C_HandleTypeDef *hi2c, uint16_t DevAddr, int32_t mode);
#endif/* IMX335_H */

IMX335.c

cpp 复制代码
#include"imx335.h"
#include<string.h>
structregval {
    uint16_t addr;
    uint8_t val;
};
staticconststructregvalres_2592_1944_regs[] = {
    {0x3000, 0x01}, {0x3002, 0x00}, {0x300C, 0x3B}, {0x300D, 0x2A},
    {0x3018, 0x04}, {0x302C, 0x3C}, {0x302E, 0x20}, {0x3056, 0x98},
    {0x3074, 0xC8}, {0x3076, 0x30}, {0x304C, 0x00}, {0x314C, 0xC6},
    {0x315A, 0x02}, {0x3168, 0xA0}, {0x316A, 0x7E}, {0x31A1, 0x00},
    {0x3288, 0x21}, {0x328A, 0x02}, {0x3414, 0x05}, {0x3416, 0x18},
    {0x3648, 0x01}, {0x364A, 0x04}, {0x364C, 0x04}, {0x3678, 0x01},
    {0x367C, 0x31}, {0x367E, 0x31}, {0x3706, 0x10}, {0x3708, 0x03},
    {0x3714, 0x02}, {0x3715, 0x02}, {0x3716, 0x01}, {0x3717, 0x03},
    {0x371C, 0x3D}, {0x371D, 0x3F}, {0x372C, 0x00}, {0x372D, 0x00},
    {0x372E, 0x46}, {0x372F, 0x00}, {0x3730, 0x89}, {0x3731, 0x00},
    {0x3732, 0x08}, {0x3733, 0x01}, {0x3734, 0xFE}, {0x3735, 0x05},
    {0x3740, 0x02}, {0x375D, 0x00}, {0x375E, 0x00}, {0x375F, 0x11},
    {0x3760, 0x01}, {0x3768, 0x1B}, {0x3769, 0x1B}, {0x376A, 0x1B},
    {0x376B, 0x1B}, {0x376C, 0x1A}, {0x376D, 0x17}, {0x376E, 0x0F},
    {0x3776, 0x00}, {0x3777, 0x00}, {0x3778, 0x46}, {0x3779, 0x00},
    {0x377A, 0x89}, {0x377B, 0x00}, {0x377C, 0x08}, {0x377D, 0x01},
    {0x377E, 0x23}, {0x377F, 0x02}, {0x3780, 0xD9}, {0x3781, 0x03},
    {0x3782, 0xF5}, {0x3783, 0x06}, {0x3784, 0xA5}, {0x3788, 0x0F},
    {0x378A, 0xD9}, {0x378B, 0x03}, {0x378C, 0xEB}, {0x378D, 0x05},
    {0x378E, 0x87}, {0x378F, 0x06}, {0x3790, 0xF5}, {0x3792, 0x43},
    {0x3794, 0x7A}, {0x3796, 0xA1}, {0x37B0, 0x36}, {0x3A00, 0x01}
};
/* Mode Registers */
staticconststructregvalmode_2l_10b_regs[] = {
    {0x3050, 0x00}, {0x319D, 0x00}, {0x341C, 0xFF}, {0x341D, 0x01},
    {0x3A01, 0x01}
};
/* Frequency Registers */
staticconststructregvalinck_74Mhz_regs[] = {
    {0x300C, 0xB6}, {0x300D, 0x7F}, {0x314C, 0x80}, {0x314D, 0x00},
    {0x315A, 0x03}, {0x3168, 0x68}, {0x316A, 0x7F}
};
staticconststructregvalinck_27Mhz_regs[] = {
    {0x300C, 0x42}, {0x300D, 0x2E}, {0x314C, 0xB0}, {0x314D, 0x00},
    {0x315A, 0x02}, {0x3168, 0x8F}, {0x316A, 0x7E}
};
staticconststructregvalinck_24Mhz_regs[] = {
    {0x300C, 0x3B}, {0x300D, 0x2A}, {0x314C, 0xC6}, {0x314D, 0x00},
    {0x315A, 0x02}, {0x3168, 0xA0}, {0x316A, 0x7E}
};
staticconststructregvalinck_18Mhz_regs[] = {
    {0x300C, 0x2D}, {0x300D, 0x1F}, {0x314C, 0x84}, {0x314D, 0x00},
    {0x315A, 0x01}, {0x3168, 0x6B}, {0x316A, 0x7D}
};
staticconststructregvalinck_6Mhz_regs[] = {
    {0x300C, 0x0F}, {0x300D, 0x0B}, {0x314C, 0xC6}, {0x314D, 0x00},
    {0x315A, 0x00}, {0x3168, 0xA0}, {0x316A, 0x7C}
};
/* Framerate Registers */
staticconststructregvalframerate_10fps_regs[] = { {0x3030, 0xC0}, {0x3031, 0x34} };
staticconststructregvalframerate_15fps_regs[] = { {0x3030, 0x2A}, {0x3031, 0x23} };
staticconststructregvalframerate_20fps_regs[] = { {0x3030, 0x60}, {0x3031, 0x1A} };
staticconststructregvalframerate_25fps_regs[] = { {0x3030, 0x1A}, {0x3031, 0x15} };
staticconststructregvalframerate_30fps_regs[] = { {0x3030, 0x94}, {0x3031, 0x11} };
/* Mirror/Flip Registers */
staticconststructregvalmirrorflip_mode_regs[][10] = {
    { {AREA3_ST_ADR_1_LSB, 0xC8}, {AREA3_ST_ADR_1_MSB, 0x00}, {IMX335_REG_HREVERSE, 0x00}, {IMX335_REG_VREVERSE, 0x00},
      {0x3081, 0x02}, {0x3083, 0x02}, {0x30B6, 0x00}, {0x30B7, 0x00}, {0x3116, 0x08}, {0x3117, 0x00} },
    { {AREA3_ST_ADR_1_LSB, 0xF8}, {AREA3_ST_ADR_1_MSB, 0x0F}, {IMX335_REG_HREVERSE, 0x00}, {IMX335_REG_VREVERSE, 0x01},
      {0x3081, 0xFE}, {0x3083, 0xFE}, {0x30B6, 0xFA}, {0x30B7, 0x01}, {0x3116, 0x02}, {0x3117, 0x00} },
    { {AREA3_ST_ADR_1_LSB, 0xC8}, {AREA3_ST_ADR_1_MSB, 0x00}, {IMX335_REG_HREVERSE, 0x01}, {IMX335_REG_VREVERSE, 0x00},
      {0x3081, 0x02}, {0x3083, 0x02}, {0x30B6, 0x00}, {0x30B7, 0x00}, {0x3116, 0x08}, {0x3117, 0x00} },
    { {AREA3_ST_ADR_1_LSB, 0xF8}, {AREA3_ST_ADR_1_MSB, 0x0F}, {IMX335_REG_HREVERSE, 0x01}, {IMX335_REG_VREVERSE, 0x01},
      {0x3081, 0xFE}, {0x3083, 0xFE}, {0x30B6, 0xFA}, {0x30B7, 0x01}, {0x3116, 0x02}, {0x3117, 0x00} }
};
/* Test Pattern Registers */
staticconststructregvaltest_pattern_enable_regs[] = {
    {0x3148, 0x10}, {0x3280, 0x00}, {0x329C, 0x01}, {0x32A0, 0x11},
    {0x3302, 0x00}, {0x3303, 0x00}, {0x336C, 0x00}
};

staticconststructregvaltest_pattern_disable_regs[] = {
    {0x3148, 0x00}, {0x3280, 0x01}, {0x329C, 0x00}, {0x32A0, 0x10},
    {0x3302, 0x32}, {0x3303, 0x00}, {0x336C, 0x01}
};
#define IMX335_1H_PERIOD_USEC (1000000.0F / 4500 / 30)
/* Private Functions */
staticint32_timx335_write_reg(I2C_HandleTypeDef *hi2c, uint16_t DevAddr, uint16_t reg, uint8_t *data, uint16_t len){
    return HAL_I2C_Mem_Write(hi2c, DevAddr, reg, I2C_MEMADD_SIZE_16BIT, data, len, HAL_MAX_DELAY);
}
staticint32_timx335_read_reg(I2C_HandleTypeDef *hi2c, uint16_t DevAddr, uint16_t reg, uint8_t *data, uint16_t len){
    return HAL_I2C_Mem_Read(hi2c, DevAddr, reg, I2C_MEMADD_SIZE_16BIT, data, len, HAL_MAX_DELAY);
}
staticint32_timx335_write_table(I2C_HandleTypeDef *hi2c, uint16_t DevAddr, const struct regval *regs, uint32_t size){
    for (uint32_t i = 0; i < size; i++) {
        if (imx335_write_reg(hi2c, DevAddr, regs[i].addr, (uint8_t *)&regs[i].val, 1) != HAL_OK) {
            return IMX335_ERROR;
        }
    }
    return IMX335_OK;
}
/* Exported Functions */
int32_tIMX335_Init(I2C_HandleTypeDef *hi2c, uint16_t DevAddr){
    uint8_t standby = IMX335_MODE_STANDBY;
    if (imx335_write_reg(hi2c, DevAddr, IMX335_REG_MODE_SELECT, &standby, 1) != HAL_OK) return IMX335_ERROR;

    if (imx335_write_table(hi2c, DevAddr, res_2592_1944_regs, sizeof(res_2592_1944_regs) / sizeof(struct regval)) != IMX335_OK) return IMX335_ERROR;
    if (imx335_write_table(hi2c, DevAddr, mode_2l_10b_regs, sizeof(mode_2l_10b_regs) / sizeof(struct regval)) != IMX335_OK) return IMX335_ERROR;
    if (imx335_write_table(hi2c, DevAddr, inck_24Mhz_regs, sizeof(inck_24Mhz_regs) / sizeof(struct regval)) != IMX335_OK) return IMX335_ERROR;  // Default 24MHz

    uint8_t streaming = IMX335_MODE_STREAMING;
    if (imx335_write_reg(hi2c, DevAddr, IMX335_REG_MODE_SELECT, &streaming, 1) != HAL_OK) return IMX335_ERROR;

    HAL_Delay(10);  // Simple delay
    return IMX335_OK;
}
int32_tIMX335_ReadID(I2C_HandleTypeDef *hi2c, uint16_t DevAddr, uint32_t *Id){
    uint8_t tmp;
    if (imx335_read_reg(hi2c, DevAddr, IMX335_REG_ID, &tmp, 1) != HAL_OK) return IMX335_ERROR;
    *Id = tmp;
    return IMX335_OK;
}

int32_tIMX335_SetGain(I2C_HandleTypeDef *hi2c, uint16_t DevAddr, int32_t gain){
    if (gain < IMX335_GAIN_MIN || gain > IMX335_GAIN_MAX) return IMX335_ERROR;
    gain /= IMX335_GAIN_UNIT_MDB;
    uint8_t hold = 1;
    if (imx335_write_reg(hi2c, DevAddr, IMX335_REG_HOLD, &hold, 1) != HAL_OK) return IMX335_ERROR;
    if (imx335_write_reg(hi2c, DevAddr, IMX335_REG_GAIN, (uint8_t *)&gain, 2) != HAL_OK) return IMX335_ERROR;
    hold = 0;
    if (imx335_write_reg(hi2c, DevAddr, IMX335_REG_HOLD, &hold, 1) != HAL_OK) return IMX335_ERROR;
    return IMX335_OK;
}
int32_tIMX335_SetExposure(I2C_HandleTypeDef *hi2c, uint16_t DevAddr, int32_t exposure){
    uint32_t vmax;
    if (imx335_read_reg(hi2c, DevAddr, IMX335_REG_VMAX, (uint8_t *)&vmax, 4) != HAL_OK) return IMX335_ERROR;
    uint32_t shutter = vmax - (exposure / (uint32_t)IMX335_1H_PERIOD_USEC);
    if (shutter < IMX335_SHUTTER_MIN) return IMX335_ERROR;
    uint8_t hold = 1;
    if (imx335_write_reg(hi2c, DevAddr, IMX335_REG_HOLD, &hold, 1) != HAL_OK) return IMX335_ERROR;
    if (imx335_write_reg(hi2c, DevAddr, IMX335_REG_SHUTTER, (uint8_t *)&shutter, 3) != HAL_OK) return IMX335_ERROR;
    hold = 0;
    if (imx335_write_reg(hi2c, DevAddr, IMX335_REG_HOLD, &hold, 1) != HAL_OK) return IMX335_ERROR;
    return IMX335_OK;
}
int32_tIMX335_SetFrequency(I2C_HandleTypeDef *hi2c, uint16_t DevAddr, int32_t frequency){
    conststructregval *regs;
    uint32_t size;
    switch (frequency) {
        case IMX335_INCK_74MHZ: regs = inck_74Mhz_regs; size = sizeof(inck_74Mhz_regs) / sizeof(struct regval); break;
        case IMX335_INCK_27MHZ: regs = inck_27Mhz_regs; size = sizeof(inck_27Mhz_regs) / sizeof(struct regval); break;
        case IMX335_INCK_24MHZ: regs = inck_24Mhz_regs; size = sizeof(inck_24Mhz_regs) / sizeof(struct regval); break;
        case IMX335_INCK_18MHZ: regs = inck_18Mhz_regs; size = sizeof(inck_18Mhz_regs) / sizeof(struct regval); break;
        default: regs = inck_6Mhz_regs; size = sizeof(inck_6Mhz_regs) / sizeof(struct regval); break;
    }
    return imx335_write_table(hi2c, DevAddr, regs, size);
}
int32_tIMX335_SetFramerate(I2C_HandleTypeDef *hi2c, uint16_t DevAddr, int32_t framerate){
    conststructregval *regs;
    uint32_t size = 2;  // All framerate tables have 2 entries
    switch (framerate) {
        case10: regs = framerate_10fps_regs; break;
        case15: regs = framerate_15fps_regs; break;
        case20: regs = framerate_20fps_regs; break;
        case25: regs = framerate_25fps_regs; break;
        default: regs = framerate_30fps_regs; break;  // 30fps
    }
    return imx335_write_table(hi2c, DevAddr, regs, size);
}
int32_tIMX335_MirrorFlipConfig(I2C_HandleTypeDef *hi2c, uint16_t DevAddr, uint32_t Config){
    uint32_t idx = (Config > 3) ? 0 : Config;
    return imx335_write_table(hi2c, DevAddr, mirrorflip_mode_regs[idx], 10);
}
int32_tIMX335_GetSensorInfo(IMX335_SensorInfo_t *Info){
    if (!Info) return IMX335_ERROR;
    strcpy(Info->name, "IMX335");
    Info->bayer_pattern = 0;  // RGGB
    Info->color_depth = 10;
    Info->width = IMX335_WIDTH;
    Info->height = IMX335_HEIGHT;
    Info->gain_min = IMX335_GAIN_MIN;
    Info->gain_max = IMX335_GAIN_MAX;
    Info->exposure_min = IMX335_EXPOSURE_MIN;
    Info->exposure_max = IMX335_EXPOSURE_MAX;
    return IMX335_OK;
}
int32_tIMX335_SetTestPattern(I2C_HandleTypeDef *hi2c, uint16_t DevAddr, int32_t mode){
    if (mode >= 0) {
        uint8_t val = (uint8_t)mode;
        if (imx335_write_reg(hi2c, DevAddr, IMX335_REG_TPG, &val, 1) != HAL_OK) return IMX335_ERROR;
        return imx335_write_table(hi2c, DevAddr, test_pattern_enable_regs, sizeof(test_pattern_enable_regs) / sizeof(struct regval));
    } else {
        return imx335_write_table(hi2c, DevAddr, test_pattern_disable_regs, sizeof(test_pattern_disable_regs) / sizeof(struct regval));
    }
}

main.c

cpp 复制代码
 uint16_t IMX335_Addr = 0x34;
  int32_t ret;
  ret = IMX335_Init(&hi2c1, IMX335_Addr);
  uint32_t chip_id;
  ret = IMX335_ReadID(&hi2c1, IMX335_Addr, &chip_id);
  // 设置增益(20dB)
  IMX335_SetGain(&hi2c1, IMX335_Addr, 20000); 
  // 设置曝光时间
  IMX335_SetExposure(&hi2c1, IMX335_Addr, 10000);   // 单位 us
    // 设置帧率(30fps)
  IMX335_SetFramerate(&hi2c1, IMX335_Addr, 30);

先调用IMX335初始化摄像头。

DCMIPP文件修改

cpp 复制代码
  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_8, GPIO_PIN_SET);
  HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
  HAL_Delay(3);

在DCMIPP.c中,我们先拉高摄像头的复位和使能引脚,让摄像头工作。

cpp 复制代码
DCMIPP_DownsizeTypeDef DownsizeConf = {0};
DownsizeConf.HRatio      = 26542;
DownsizeConf.VRatio      = 33178;
DownsizeConf.HSize       = 800;
DownsizeConf.VSize       = 480;
DownsizeConf.HDivFactor  = 316;
DownsizeConf.VDivFactor  = 253;

if(HAL_DCMIPP_PIPE_SetDownsizeConfig(&hdcmipp, DCMIPP_PIPE1, &DownsizeConf) != HAL_OK)
{
  Error_Handler();
}
if(HAL_DCMIPP_PIPE_EnableDownsize(&hdcmipp, DCMIPP_PIPE1) != HAL_OK)
{
  Error_Handler();
}

接着我们配置DCMIPP流水线PIPE1的缩放比例,这个缩放比例可以在芯片参考手册中找到计算方式:

由于摄像头的图像输入是2592*1944,我们需要将其缩放到800*480,因此我们要对流水线PIPE1进行缩放系数设置。

HRATIO和VRATIO分别对应水平和垂直缩放系数,8192对应1倍,65535对应缩小7.999倍,因此我们可以先计算2592到800对应的倍数是3.24倍

我们将这个系数和8192相乘再取整约为26542。

用这个方法我们同样计算VRATIO:33178

同样下面的缩放因子,根据手册计算可以得到316和252.

完整的dcmipp初始化函数如下:

cpp 复制代码
voidMX_DCMIPP_Init(void)
{

/* USER CODE BEGIN DCMIPP_Init 0 */
  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_8, GPIO_PIN_SET);
  HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
  HAL_Delay(3);
/* USER CODE END DCMIPP_Init 0 */
  DCMIPP_CSI_PIPE_ConfTypeDef pCSI_PipeConfig = {0};
  DCMIPP_CSI_ConfTypeDef pCSI_Config = {0};
  DCMIPP_PipeConfTypeDef pPipeConfig = {0};
/* USER CODE BEGIN DCMIPP_Init 1 */
/* USER CODE END DCMIPP_Init 1 */
  hdcmipp.Instance = DCMIPP;
if (HAL_DCMIPP_Init(&hdcmipp) != HAL_OK)
  {
    Error_Handler();
  }
/** Pipe 1 Config
  */
  pCSI_PipeConfig.DataTypeMode = DCMIPP_DTMODE_DTIDA;
  pCSI_PipeConfig.DataTypeIDA = DCMIPP_DT_RAW10;
  pCSI_PipeConfig.DataTypeIDB = DCMIPP_DT_RAW10;
if (HAL_DCMIPP_CSI_PIPE_SetConfig(&hdcmipp, DCMIPP_PIPE1, &pCSI_PipeConfig) != HAL_OK)
  {
    Error_Handler();
  }
  pCSI_Config.PHYBitrate = DCMIPP_CSI_PHY_BT_1600;
  pCSI_Config.DataLaneMapping = DCMIPP_CSI_PHYSICAL_DATA_LANES;
  pCSI_Config.NumberOfLanes = DCMIPP_CSI_TWO_DATA_LANES;
  HAL_DCMIPP_CSI_SetConfig(&hdcmipp, &pCSI_Config);
  pPipeConfig.FrameRate = DCMIPP_FRAME_RATE_ALL;
  pPipeConfig.PixelPipePitch = 2400;
  pPipeConfig.PixelPackerFormat = DCMIPP_PIXEL_PACKER_FORMAT_RGB888_YUV444_1;
if (HAL_DCMIPP_PIPE_SetConfig(&hdcmipp, DCMIPP_PIPE1, &pPipeConfig) != HAL_OK)
  {
    Error_Handler();
  }
if (HAL_DCMIPP_CSI_SetVCConfig(&hdcmipp, 0U, DCMIPP_CSI_DT_BPP10) != HAL_OK)
  {
    Error_Handler();
  }
/* USER CODE BEGIN DCMIPP_Init 2 */
  DCMIPP_DownsizeTypeDef DownsizeConf = {0};
DownsizeConf.HRatio      = 25656;
DownsizeConf.VRatio      = 33161;
DownsizeConf.HSize       = 800;
DownsizeConf.VSize       = 480;
DownsizeConf.HDivFactor  = 316;
DownsizeConf.VDivFactor  = 253;

if(HAL_DCMIPP_PIPE_SetDownsizeConfig(&hdcmipp, DCMIPP_PIPE1, &DownsizeConf) != HAL_OK)
{
  Error_Handler();
}
if(HAL_DCMIPP_PIPE_EnableDownsize(&hdcmipp, DCMIPP_PIPE1) != HAL_OK)
{
  Error_Handler();
}
/* USER CODE BEGIN DCMIPP_Init 2 */
/* USER CODE END DCMIPP_Init 2 */

接着在main.c中将dcmipp的传输目的地设置到LTDC的显示缓存区,设置连续拍照模式,我们就可以得到如下的效果:

由于CSI相机还需要进一步调整参数如白平衡、曝光时间、增益等专业参数,下一期介绍STM32N6的ISP库实现色彩添加:

相关推荐
天涯铭2 小时前
深入浅出:单片机I/O模式与上拉电阻
单片机·上拉电阻·gpio输出
iCxhust2 小时前
8088汇编测试程序 (MASM/TASM) — 显示 “HELLO 8088!“ + “LCD1602 OK“
汇编·单片机·嵌入式硬件·51单片机·微机原理
Bryce_Zhou3 小时前
STM32U5A9J-DK 介绍
stm32·单片机·嵌入式硬件
Bryce_Zhou3 小时前
stm32cubex创建freertos
单片机
快乐的划水a3 小时前
单片机仿Linux驱动开发(三)
linux·驱动开发·单片机
lzx186488437023 小时前
锂电池11V升23V 1.2A恒流升压DC-DC转换芯片_AH1102
嵌入式硬件·集成测试·硬件工程·ic
踏着七彩祥云的小丑3 小时前
嵌入式——认识电子元器件——轻触按键开关系列
单片机·嵌入式硬件
Bryce_Zhou4 小时前
stm32U5A9配置USART+DMA
stm32·单片机·嵌入式硬件
踏着七彩祥云的小丑4 小时前
嵌入式——认识电子元器件——三极管系列
单片机·嵌入式硬件