- 在 openspec/changes/archive/ 下新增 2026-03-13-check-dvp-dma-ir 归档目录
- 新增设计文档 (design.md),包含背景、目标、决策和风险评估
- 新增提案文档 (proposal.md),说明变更原因、内容和影响
- 新增功能规范 (spec.md),定义 DVP 配置验证、DMA 内存传输正确性和诊断输出要求
- 新增任务清单 (tasks.md),列出代码审查、中断处理和诊断验证的具体步骤
- 更新 openspec/config.yaml,添加上下文注释
- 在 openspec/specs/ 下新增 dvp-dma-ir-capture 功能规范
🐛 fix(dvp): 修复 DVP 任务中的竞态条件并增强调试输出
- 在 DVP_Task() 中访问 Line_Ready_Flag 和 Ready_Line_Ptr 前禁用 DVP 中断,操作后重新启用,防止数据竞争
- 增强第0行的调试打印,每秒计算并输出帧率 (fps)
- 新增打印第0行前8个字节的原始数据,用于验证数据有效性
- 使用外部定义的 sys_tick_ms 进行时间计算,确保时间基准一致
115 lines
4.0 KiB
C
115 lines
4.0 KiB
C
#include "dvp.h"
|
||
#include "ch32v30x_dvp.h"
|
||
#include "eth_driver.h"
|
||
#include "string.h"
|
||
|
||
__attribute__((aligned(4))) uint8_t DMA_LineBuf0[BYTES_PER_LINE];
|
||
__attribute__((aligned(4))) uint8_t DMA_LineBuf1[BYTES_PER_LINE];
|
||
|
||
volatile uint8_t Line_Ready_Flag = 0;
|
||
volatile uint8_t *Ready_Line_Ptr = NULL;
|
||
volatile uint32_t current_line_idx = 0;
|
||
static uint32_t frame_count = 0;
|
||
|
||
extern u8 socket[];
|
||
extern volatile uint32_t sys_tick_ms;
|
||
|
||
void DVP_Init(void)
|
||
{
|
||
GPIO_InitTypeDef GPIO_InitStructure = {0};
|
||
NVIC_InitTypeDef NVIC_InitStructure = {0};
|
||
|
||
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC, ENABLE);
|
||
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DVP, ENABLE);
|
||
|
||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
|
||
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_9 | GPIO_Pin_10;
|
||
GPIO_Init(GPIOA, &GPIO_InitStructure);
|
||
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_11;
|
||
GPIO_Init(GPIOC, &GPIO_InitStructure);
|
||
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_8 | GPIO_Pin_9;
|
||
GPIO_Init(GPIOB, &GPIO_InitStructure);
|
||
|
||
NVIC_InitStructure.NVIC_IRQChannel = DVP_IRQn;
|
||
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
|
||
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
|
||
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
|
||
NVIC_Init(&NVIC_InitStructure);
|
||
|
||
DVP_Cfg(DVP_DMA_Disable, DVP_FLAG_FIFO_RESET_Enable, DVP_RX_RESET_Enable);
|
||
DVP_Mode(RB_DVP_D8_MOD, Video_Mode);
|
||
DVP->CR0 &= ~(RB_DVP_P_POLAR | RB_DVP_V_POLAR | RB_DVP_H_POLAR);
|
||
DVP->DMA_BUF0 = (uint32_t)DMA_LineBuf0;
|
||
DVP->DMA_BUF1 = (uint32_t)DMA_LineBuf1;
|
||
DVP->ROW_NUM = 1;
|
||
DVP->COL_NUM = BYTES_PER_LINE;
|
||
DVP_INTCfg(ENABLE, RB_DVP_IE_STR_FRM | RB_DVP_IE_ROW_DONE);
|
||
DVP_Cfg(DVP_DMA_Enable, DVP_FLAG_FIFO_RESET_Disable, DVP_RX_RESET_Disable);
|
||
DVP->CR0 |= RB_DVP_ENABLE;
|
||
}
|
||
|
||
void DVP_Task(void)
|
||
{
|
||
if (!Line_Ready_Flag) return;
|
||
|
||
NVIC_DisableIRQ(DVP_IRQn);
|
||
uint8_t *line = (uint8_t *)Ready_Line_Ptr;
|
||
uint32_t idx = current_line_idx;
|
||
Line_Ready_Flag = 0;
|
||
NVIC_EnableIRQ(DVP_IRQn);
|
||
|
||
/* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͼ<EFBFBD><CDBC><EFBFBD>У<EFBFBD><D0A3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>У<EFBFBD><D0A3><EFBFBD> socket <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
||
if (idx < SENSOR_HEIGHT && socket[0] != 0xFF)
|
||
{
|
||
/* 4<>ֽ<EFBFBD><D6BD><EFBFBD>ͷ<EFBFBD><CDB7>֡<EFBFBD><D6A1>(2B) + <20>к<EFBFBD>(2B) */
|
||
uint8_t hdr[4] = {
|
||
(frame_count >> 8) & 0xFF, frame_count & 0xFF,
|
||
(idx >> 8) & 0xFF, idx & 0xFF
|
||
};
|
||
u32 len = 4;
|
||
WCHNET_SocketSend(socket[0], hdr, &len);
|
||
len = BYTES_PER_LINE;
|
||
WCHNET_SocketSend(socket[0], line, &len);
|
||
|
||
/* ÿ֡<C3BF><D6A1>0<EFBFBD>д<EFBFBD>ӡһ<D3A1>Σ<EFBFBD>ȷ<EFBFBD><C8B7>֡<EFBFBD>ʡ<EFBFBD>֡<EFBFBD>ź<EFBFBD>socket״̬<D7B4><CCAC><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
||
if (idx == 0) {
|
||
static uint32_t last_fps_tick = 0;
|
||
static uint32_t last_frame_count = 0;
|
||
uint32_t now = sys_tick_ms;
|
||
|
||
if (now - last_fps_tick >= 1000) {
|
||
uint32_t fps = frame_count - last_frame_count;
|
||
printf("[DVP] frame=%lu fps=%lu socket=%d tick=%lu\r\n", frame_count, fps, socket[0], now);
|
||
printf(" Row0 Data: %02X %02X %02X %02X %02X %02X %02X %02X\r\n",
|
||
line[0], line[1], line[2], line[3], line[4], line[5], line[6], line[7]);
|
||
last_fps_tick = now;
|
||
last_frame_count = frame_count;
|
||
}
|
||
}
|
||
}
|
||
else if (idx == 0 && socket[0] == 0xFF)
|
||
{
|
||
/* socket δ<><CEB4><EFBFBD><EFBFBD>ʱ<EFBFBD><CAB1>ʾ */
|
||
printf("[DVP] frame=%lu waiting for TCP...\r\n", frame_count);
|
||
}
|
||
memset(line, 0, BYTES_PER_LINE);
|
||
}
|
||
|
||
void DVP_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
|
||
void DVP_IRQHandler(void)
|
||
{
|
||
if (DVP->IFR & RB_DVP_IF_STR_FRM)
|
||
{
|
||
DVP->IFR = RB_DVP_IF_STR_FRM;
|
||
current_line_idx = 0;
|
||
frame_count++;
|
||
}
|
||
if (DVP->IFR & RB_DVP_IF_ROW_DONE)
|
||
{
|
||
DVP->IFR = RB_DVP_IF_ROW_DONE;
|
||
Ready_Line_Ptr = (DVP->CR1 & RB_DVP_BUF_TOG) ? DMA_LineBuf0 : DMA_LineBuf1;
|
||
current_line_idx++;
|
||
Line_Ready_Flag = 1;
|
||
}
|
||
}
|