zhoujie 82d672a2d3 📝 docs(openspec): 新增 DVP-DMA-IR 检查的文档和规范
- 在 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 进行时间计算,确保时间基准一致
2026-03-13 23:25:48 +08:00

115 lines
4.0 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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;
}
}