2. Sensor硬件接口与电气特性
做Sensor驱动,说白了就是跟硬件接口打交道。你写的代码最终要控制物理层的信号,电源没给对,时序没卡准,Sensor压根不工作。我刚开始调Sensor时,就吃过上电时序的亏,板子一上电,Sensor纹丝不动,查了三天才发现是DVDD比AVDD早来了2ms。嗯,这种坑踩过一次就记住了。
2.1 MIPI CSI-2物理层:数据是怎么飞起来的
CSI-2是Camera和SoC之间的图像传输通道。物理层目前主流是D-PHY,C-PHY也在高端平台慢慢铺开。说白了,D-PHY是差分信号,C-PHY是三相信号,两者不兼容,选型时得看SoC支持哪种。
核心要点:CSI-2物理层只管把像素数据从Sensor搬到AP,不负责控制Sensor寄存器。控制走I2C/SPI,数据走MIPI,两条线各干各的。
2.1.1 D-PHY:差分对,简单可靠
D-PHY用一对差分线(Dp/Dn)传数据,另一对传时钟。我习惯把时钟对叫CLK,数据对叫DATA,一个CLK Lane可以配1~4个DATA Lane。分辨率越高,需要的Lane越多。
- 速率范围:80 Mbps ~ 2.5 Gbps per Lane
- 信号电平:差分摆幅200 mV ~ 1.2 V,共模电压200 mV左右
- 工作模式:HS(高速)传数据,LP(低功耗)传控制
- 状态机:LP-11 → LP-01 → LP-00 进入HS,HS结束后回到LP-11
D-PHY的HS模式是电流驱动,LP模式是电压驱动。我在调试时遇到过一个问题:LP模式下Sensor能正常识别,但一切到HS就丢包。后来发现是PCB上CLK和DATA的等长没做好,差了300mil,导致采样窗口偏移。嗯,Layout的坑,驱动工程师也得懂一点。
2.1.2 C-PHY:三相线,带宽更高
C-PHY不用差分对,改用三根线(A/B/C),每根线有三种电平状态。三根线组合起来,一个Symbol能传2.28 bit数据。你想想看,同样的线数,C-PHY的带宽比D-PHY高不少。
| 参数 | D-PHY | C-PHY |
|---|---|---|
| 物理线数 | 2 per Lane(Dp/Dn) | 3 per Trio(A/B/C) |
| 每线速率 | 2.5 Gbps max | 2.5 Gsym/s |
| 等效带宽 | 2.5 Gbps per Lane | ~5.7 Gbps per Trio |
| 信号类型 | 差分 | 三相电平 |
| 兼容性 | 广泛 | 高端SoC |
C-PHY的优点是带宽高、EMI低,但协议复杂,调试工具也少。我个人建议,除非SoC强制要求C-PHY,否则先用D-PHY,成熟稳定,示波器一看就知道信号对不对。
调试小技巧:用示波器抓D-PHY的HS信号时,把探头设为差分模式,触发条件设成LP-11→LP-00的跳变。这样能准确抓到HS的起始位置,方便看眼图。
2.2 I2C/SPI控制接口:怎么跟Sensor说话
Sensor的寄存器读写,99%用I2C,少数用SPI。I2C只需要两根线(SCL/SDA),SPI需要四根(SCLK/MOSI/MISO/CS)。我做过一个项目,Sensor同时支持I2C和SPI,通过一个引脚电平选择。嗯,这种设计挺常见,选型时注意看Datasheet。
2.2.1 I2C:两根线,慢但够用
I2C的标准速率是100 kHz,快速模式400 kHz,高速模式3.4 MHz。Sensor寄存器读写量不大,400 kHz完全够用。我习惯把I2C速率设在400 kHz,再高容易受PCB走线影响。
// I2C写Sensor寄存器示例
int sensor_write_reg(uint8_t slave_addr, uint16_t reg_addr, uint8_t data) {
uint8_t buf[3];
buf[0] = (reg_addr >> 8) & 0xFF; // 高字节地址
buf[1] = reg_addr & 0xFF; // 低字节地址
buf[2] = data; // 写入数据
return i2c_master_write(slave_addr, buf, 3);
}
这里有个坑:有些Sensor的寄存器地址是8位,有些是16位。我遇到过Sensor手册写的是16位地址,但实际只认低8位,高8位忽略。调试时先读一下Device ID,确认地址格式对不对。
2.2.2 SPI:四根线,快且灵活
SPI适合需要批量读写Sensor的场景,比如OTA升级固件。SPI速率可以跑到几十MHz,但Sensor内部逻辑不一定跟得上。我建议先按10 MHz起步,稳定后再往上提。
// SPI读Sensor寄存器示例
int sensor_spi_read(uint8_t reg_addr, uint8_t *data) {
uint8_t tx_buf[2];
tx_buf[0] = 0x80 | reg_addr; // 读命令 + 地址
tx_buf[1] = 0x00; // dummy byte
spi_transfer(tx_buf, data, 2);
return 0;
}
注意:SPI的极性(CPOL)和相位(CPHA)必须和Sensor匹配。我见过有人把CPOL设反了,读回来的数据全是0xFF。查了半天,最后发现是模式0和模式3的区别。
2.3 电源管理:AVDD/DVDD/IOVDD
Sensor一般需要三路电源:AVDD(模拟)、DVDD(数字核心)、IOVDD(IO接口)。这三路电压值因Sensor型号而异,但有个规律:AVDD最高(2.8V常见),DVDD最低(1.2V或1.8V),IOVDD和SoC的IO电压对齐(1.8V或2.8V)。
| 电源轨 | 典型电压 | 电流需求 | 纹波要求 |
|---|---|---|---|
| AVDD | 2.8V | 50~200 mA | < 10 mVpp |
| DVDD | 1.2V / 1.8V | 30~100 mA | < 20 mVpp |
| IOVDD | 1.8V / 2.8V | 10~50 mA | < 30 mVpp |
AVDD的纹波要求最严,因为模拟电路对电源噪声敏感。我遇到过Sensor输出图像有横纹,用示波器一看,AVDD上有200 mV的开关噪声。后来在电源输出端加了个10 μF + 0.1 μF的去耦电容,纹波降到5 mV,图像立马干净了。
个人习惯:Layout时AVDD走线单独铺铜,远离DVDD和IOVDD。电源入口放一个磁珠,隔离数字噪声。Sensor的每个电源引脚旁边都放一个0.1 μF电容,位置越近越好。
2.4 上电时序:顺序错了,Sensor不干活
上电时序是驱动调试的第一步,也是最容易出问题的地方。Sensor Datasheet里会给出一个时序图,标明AVDD、DVDD、IOVDD、MCLK、RST、I2C的先后顺序。我见过最严格的时序要求是:AVDD先上,DVDD后上,间隔至少1 ms。
// 典型上电时序代码(伪代码)
void sensor_power_on(void) {
// 1. 先上AVDD
regulator_enable(REG_AVDD, 2800000); // 2.8V
delay_ms(2); // 等待稳定
// 2. 再上DVDD
regulator_enable(REG_DVDD, 1200000); // 1.2V
delay_ms(1);
// 3. 最后上IOVDD
regulator_enable(REG_IOVDD, 1800000); // 1.8V
delay_ms(1);
// 4. 提供MCLK
clk_enable(MCLK, 24000000); // 24 MHz
delay_ms(1);
// 5. 释放RST
gpio_set_level(RST_PIN, 1);
delay_ms(10); // 等待Sensor初始化
// 6. 开始I2C通信
i2c_init();
}
我曾经遇到一个Sensor,上电时序要求DVDD不能比AVDD早超过5 ms。但板子上的电源芯片启动时间不一致,DVDD总是比AVDD早10 ms。结果Sensor上电后I2C地址对不上,读Device ID全是0xFF。后来在DVDD路径上加了个RC延时,把时序调对了。
避坑指南:上电时序不要只看Datasheet的典型值,要看最小值和最大值。比如AVDD到DVDD的延迟,典型值是1 ms,但最小值可能是0 ms,最大值可能是5 ms。按典型值设计,万一电源芯片有偏差,Sensor就可能不工作。
2.5 知识结构总览
下面这张图把本章的核心知识点串起来了。从物理层到控制接口,再到电源和时序,每一步都是Sensor正常工作的前提。
这张图把本章的知识点分成了四个块:物理层、控制接口、电源、时序。每个块下面又有子节点,方便你快速定位问题。调试时遇到Sensor不工作,先按这个结构排查:电源给了吗?时序对吗?I2C能通吗?MIPI信号正常吗?一步步来,问题总能找到。