# DVP 模块设计文档 > 文档版本:1.0 · 日期:2026-03-15 · 状态:已发布 --- ## 1. 模块概述 DVP(Digital Video Port)模块负责将 Mini212G2 传感器输出的 8-bit CMOS 并行数据转换为内存中可访问的帧缓冲(FrameBuffer)。整个采集链路通过硬件 DMA ping-pong 和快速中断实现,CPU 占用极低。 源文件:`Debug/dvp.c` / `Debug/dvp.h` --- ## 2. DVP 硬件连接与引脚映射 所有 DVP 引脚配置为 **浮空输入(GPIO_Mode_IN_FLOATING)**。 | 引脚 | DVP 信号 | 说明 | |------|----------|------| | PA4 | D0 | 数据位 0 | | PA5 | D1 | 数据位 1 | | PA6 | D2 | 数据位 2 | | PA9 | D3 | 数据位 3 | | PA10 | D4 | 数据位 4 | | PC8 | D5 | 数据位 5 | | PC9 | D6 | 数据位 6 | | PC11 | D7 | 数据位 7 | | PB3 | VSYNC | 帧同步信号 | | PB8 | HSYNC | 行同步信号 | | PB9 | PIXCLK | 像素时钟 | --- ## 3. DVP 时序配置 在 `DVP_Init()` 中完成以下配置: ### 3.1 工作模式 | 配置项 | 值 | 说明 | |--------|-----|------| | 数据宽度 | `RB_DVP_D8_MOD` | 8-bit 并行数据 | | 采集模式 | `Video_Mode` | 连续帧采集(非快照模式) | | 捕获控制 | 持续捕获(CR1 CM=0) | 不限帧数 | ### 3.2 信号极性 | 信号 | 寄存器位 | 配置值 | 说明 | |------|----------|--------|------| | VSYNC | `RB_DVP_V_POLAR` | **1(高有效)** | DIGITAL_FIELD_VALID 高电平期间为有效帧行 | | HSYNC | `RB_DVP_H_POLAR` | 0(高有效)| 标准高有效行同步 | | PCLK | `RB_DVP_P_POLAR` | 0(上升沿采样)| 正常时钟极性 | > **与手册对应**:Mini212G2 FIELD_VALID 高电平为有效期,因此 V_POLAR 置 1(高有效),与硬件手册时序图一致。 ### 3.3 图像尺寸配置 | 寄存器 | 值 | 说明 | |--------|-----|------| | `DVP->COL_NUM` | 512(`BYTES_PER_LINE`) | 每行字节数 = 256 像素 × 2 字节 | | `DVP->ROW_NUM` | 1 | 每行触发一次 ROW_DONE 中断 | --- ## 4. DMA ping-pong 行缓冲机制 ### 4.1 缓冲区配置 ```c __attribute__((aligned(4))) uint8_t DMA_LineBuf0[512]; /* DMA_BUF0 */ __attribute__((aligned(4))) uint8_t DMA_LineBuf1[512]; /* DMA_BUF1 */ ``` 两个缓冲区分别挂载在 `DVP->DMA_BUF0` 和 `DVP->DMA_BUF1`,DVP 硬件在两者之间自动切换,无需 CPU 干预。 ### 4.2 缓冲切换逻辑 ROW_DONE 中断触发时,硬件已切换到 **下一个缓冲**开始写入。因此正确读取方法为: ```c /* BUF_TOG=1 表示 DMA 正在写 BUF0,此时应读 BUF1(刚写完的那个) */ uint8_t *src = (DVP->CR1 & RB_DVP_BUF_TOG) ? DMA_LineBuf0 : DMA_LineBuf1; ``` 即:`BUF_TOG` 状态位指示**当前正在写入的缓冲**,应读取**另一个**刚刚完成写入的缓冲。 ### 4.3 尺寸约束 修改传感器分辨率时,必须同时更新以下三个宏(`dvp.h`): ```c #define SENSOR_WIDTH 256 /* 像素列数 */ #define SENSOR_HEIGHT 192 /* 像素行数 */ #define BYTES_PER_LINE 512 /* SENSOR_WIDTH × 2 字节/像素 */ ``` --- ## 5. DVP IRQ 帧组装逻辑 中断处理函数声明: ```c void DVP_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast"))); ``` `WCH-Interrupt-fast` 属性使中断跳过通用寄存器入栈,显著降低中断延迟,确保在下一行像素时钟到来(Mini212G2 @ 25 Hz,每行约 42.67 µs)前完成 512 B 的 `memcpy`。 ### 5.1 帧同步(STR_FRM 中断) VSYNC 上升沿触发,是帧同步的**唯一锚点**: ```c if (DVP->IFR & RB_DVP_IF_STR_FRM) { DVP->IFR = RB_DVP_IF_STR_FRM; current_line_idx = 0; /* 重置行计数器 */ dvp_frame_count++; } ``` ### 5.2 行完成(ROW_DONE 中断) 每行像素传输完成后触发: ```c if (DVP->IFR & RB_DVP_IF_ROW_DONE) { DVP->IFR = RB_DVP_IF_ROW_DONE; uint8_t *src = (DVP->CR1 & RB_DVP_BUF_TOG) ? DMA_LineBuf0 : DMA_LineBuf1; uint32_t idx = current_line_idx; current_line_idx++; if (idx < SENSOR_HEIGHT) { memcpy(FrameBuffer[idx], src, BYTES_PER_LINE); if (idx == SENSOR_HEIGHT - 1) { /* 第 191 行完成 */ Frame_Ready_Flag = 1; /* 通知业务任务 */ Ready_Frame_Count = dvp_frame_count; /* 记录帧号 */ } } } ``` > **注意:先读 `idx` 再递增**,避免 off-by-one。STR_FRM 将 `idx` 置 0,首次 ROW_DONE 时 `idx=0`,将数据写入 `FrameBuffer[0]`,符合预期。 --- ## 6. FrameBuffer 数据格式 ```c __attribute__((aligned(4))) uint8_t FrameBuffer[SENSOR_HEIGHT][BYTES_PER_LINE]; /* = uint8_t[192][512] */ ``` ### 6.1 像素访问方式 FrameBuffer 声明为 `uint8_t` 数组,实际存储 `uint16_t` 小端序温度值: ```c /* 访问第 row 行、第 col 列的温度 */ uint16_t *pixels = (uint16_t *)FrameBuffer; uint16_t raw_val = pixels[row * SENSOR_WIDTH + col]; ``` ### 6.2 温度换算(TMP 模式) ``` 温度(°C)= raw_val × 0.1 ``` 例:`raw_val = 370` → `37.0°C` ### 6.3 字节序说明 CH32V307 为**小端序**(Little-Endian): | 偏移 | 内容 | |------|------| | `FrameBuffer[row][col*2 + 0]` | 温度低字节 | | `FrameBuffer[row][col*2 + 1]` | 温度高字节 | 传感器必须配置为 **CMOS8(LSB) 模式**(低字节先发),否则字节顺序反转,温度值错误。 ### 6.4 内存布局摘要 | 参数 | 值 | |------|-----| | 总大小 | 192 × 512 = 98,304 字节 ≈ 96 KB | | 每行 | 512 字节 = 256 像素 | | 像素格式 | uint16_t,小端,0.1°C/LSB,TMP 模式绝对温度 | | 对齐 | 4 字节对齐(`__attribute__((aligned(4)))`) | --- ## 7. 相关文档 | 文档 | 内容 | |------|------| | [系统总览.md](系统总览.md) | FreeRTOS 任务拓扑、Frame_Ready_Flag 同步机制 | | [Mini212G2预配置指南.md](../Mini212G2预配置指南.md) | CMOS8(LSB) + TMP 模式配置命令 |