第四章 接口协议基础(上):DVP并行接口

各位同学,今天我们来聊聊DVP并行接口。说实话,这个接口在现在的嵌入式圈子里,已经算是「老前辈」了。但你别小看它,很多工业级的热成像模组,至今还在用DVP。为什么?因为它简单、可靠、延迟低。

我记得2018年做一款安防热成像产品时,供应商推荐了MIPI接口的模组。结果调试了两个月,信号完整性总是出问题。后来换回DVP,一周就搞定了。嗯,有时候「老」不代表「过时」。

4.1 DVP接口长什么样?

DVP的全称是Digital Video Port,说白了就是一组并行数据线加上控制信号。我习惯把它想象成「一群人排队过马路」——数据线是行人,控制信号是红绿灯。

一个典型的DVP接口包含以下信号:

  • PCLK:像素时钟,相当于「节拍器」
  • HSYNC:行同步信号,告诉接收端「新的一行开始了」
  • VSYNC:场同步信号,告诉接收端「新的一帧开始了」
  • DATA[7:0] 或 DATA[9:0]:并行数据总线
  • PWDN:电源管理,可选
  • RESET:复位,可选

你想想看,比起MIPI那种高速串行接口,DVP的接线是不是直观多了?每个信号都有明确的物理意义。

4.2 像素时钟——DVP的「心跳」

像素时钟PCLK是DVP接口的核心。每个PCLK的上升沿(或下降沿),数据线上就会输出一个像素的数据。

这里有个公式,我建议你记下来:

PCLK频率 = 帧率 × 分辨率 × 每像素位数 / 数据位宽

举个例子:一个80×60像素的热成像模组,帧率30fps,每像素16位,数据位宽8位:

PCLK = 30 × 80 × 60 × 16 / 8 = 288,000 Hz ≈ 288 kHz

你看,才288kHz。比起动辄几十MHz的摄像头接口,DVP在低速场景下优势明显——走线随便拉,不用考虑阻抗匹配。

我的经验: 实际项目中,PCLK频率最好留20%的余量。我曾经在一个项目中,理论计算是500kHz,结果模组实际输出到了550kHz。幸好MCU的GPIO能扛住,不然又要改版。

4.3 行场同步信号——别搞反了极性

HSYNC和VSYNC这两个信号,新手最容易栽跟头。我刚开始做热成像时,就因为这个折腾了两天。

先说说它们的作用:

  • VSYNC:高电平(或低电平)表示一帧数据的开始。帧与帧之间会有消隐区。
  • HSYNC:高电平(或低电平)表示一行数据的开始。行与行之间也有消隐区。

关键来了——每个模组的极性可能不一样!有的模组VSYNC高有效,有的低有效。HSYNC同理。

避坑指南: 我曾经接手过一个项目,前任工程师把VSYNC的极性搞反了。结果图像上下颠倒,还以为是模组坏了。查了三天,最后发现是寄存器配置错了。所以,拿到新模组的第一件事——看数据手册的时序图!

典型的DVP时序图长这样:

VSYNC  ┌────────────────────────────────────┐
       │                                    │
       └────────────────────────────────────┘
       ← 帧消隐 →    ← 有效行 →    ← 帧消隐 →

HSYNC  ┌──┐    ┌──┐    ┌──┐
       │  │    │  │    │  │
       └──┘    └──┘    └──┘
       ← 行消隐 → ← 有效像素 → ← 行消隐 →

PCLK   ┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐
       ││││││││││││││││││
       └┘└┘└┘└┘└┘└┘└┘└┘

DATA   XXXX  ████████████  XXXX
      无效   有效像素数据   无效

注意看,在HSYNC有效期间,每个PCLK时钟对应一个像素数据。而在消隐区,数据线是无效的,可以忽略。

4.4 数据位宽配置——8位还是10位?

DVP的数据位宽通常有8位、10位、12位、16位几种。热成像模组最常见的是8位和16位。

为什么会有这个区别?说白了,就是「一次传多少数据」的问题。

位宽 每像素时钟数 适用场景 我的建议
8位 2个(16位像素) 低分辨率、低成本MCU 适合80×60以下分辨率
10位 需特殊处理 高精度测温 不常见,慎用
12位 需特殊处理 工业测温 需要DMA配合
16位 1个 高分辨率、高速 推荐,但占用引脚多

我个人习惯用8位模式。为什么?因为大多数MCU的GPIO口都是8位一组,正好对应一个端口。用16位模式的话,你得占用两组GPIO,还要考虑字节对齐的问题。

举个例子,一个16位的像素值0x3A5B,在8位模式下:

第一个PCLK:高字节 0x3A
第二个PCLK:低字节 0x5B

接收端需要自己拼起来。我一般这样写:

uint16_t pixel_value;
uint8_t high_byte, low_byte;

// 在PCLK中断中
high_byte = GPIO_ReadInputData(GPIOA);  // 第一个时钟
// 等待下一个PCLK
low_byte = GPIO_ReadInputData(GPIOA);   // 第二个时钟

pixel_value = (high_byte << 8) | low_byte;
重点: 8位模式下,两个PCLK才组成一个像素。这意味着你的中断处理频率要翻倍。如果帧率是30fps,分辨率80×60,那每秒要处理的中断次数是: 30 × 80 × 60 × 2 = 288,000次/秒 也就是每3.5微秒一次中断。普通MCU还能扛住,但如果你用8位单片机,就要小心了。

4.5 实际项目中的配置流程

好了,理论讲完了。咱们来点实战的。我一般按这个步骤配置DVP接口:

  1. 查数据手册:确认模组的VSYNC、HSYNC极性,以及PCLK是上升沿还是下降沿采样。
  2. 配置GPIO:把对应的引脚设为输入模式,注意上下拉。
  3. 配置外部中断:用PCLK的边沿触发中断,在中断里读取数据。
  4. 解析同步信号:在VSYNC中断里标记帧开始,在HSYNC中断里标记行开始。
  5. 缓存数据:用双缓冲机制,避免数据覆盖。

这里有个小技巧——我习惯用DMA来读取数据,而不是CPU轮询。DMA可以自动把GPIO的数据搬运到内存,CPU只需要处理帧完成中断就行。这样效率高得多。

我的配置模板: 对于STM32系列,我通常这样配: - 使用TIM作为PCLK的外部时钟 - 用DMA1的通道1来搬运数据 - 帧同步用EXTI中断 这样CPU占用率可以降到5%以下。

4.6 本章小结

DVP接口虽然「老」,但胜在简单可靠。你只要记住三点:

  • PCLK是核心,频率要算准
  • HSYNC/VSYNC极性别搞反
  • 数据位宽影响传输效率

下一章我们会讲DVP的进阶内容——如何用DMA提高效率,以及多模组同步的问题。嗯,到时候见。


公众号:蓝海资料掘金营,微信deep3321