高通Camera HAL层Sensor驱动调试 - 第3节
3.1 I2C 与 SPI 通信协议对比
在 Sensor 驱动开发中,I2C 和 SPI 是最常用的两种板级通信总线。它们负责承载 Host(AP/ISP)与 Sensor 之间的寄存器读写、数据流控制以及固件下载。理解两者的电气特性、时序差异和适用场景,是正确配置 Sensor 驱动的基础。
| 对比维度 | I2C(Inter-Integrated Circuit) | SPI(Serial Peripheral Interface) |
|---|---|---|
| 信号线数量 | 2 线:SCL(时钟)、SDA(数据) | 4 线:SCLK(时钟)、MOSI、MISO、CS(片选) |
| 通信模式 | 半双工,主从架构 | 全双工,主从架构 |
| 最大速率 | 标准模式 100 kHz,快速模式 400 kHz,高速模式 3.4 MHz | 通常 10–50 MHz,部分可达 100 MHz |
| 寻址方式 | 7 位或 10 位设备地址 + 寄存器地址 | 通过硬件 CS 引脚片选,无设备地址 |
| 数据帧结构 | 起始条件 → 设备地址 + R/W → ACK → 数据字节 → ACK → 停止条件 | CS 拉低 → 发送命令/地址 → 数据交换 → CS 拉高 |
| 多设备支持 | 总线挂载多个设备,通过地址区分 | 每个设备独占 CS 引脚,或通过菊花链 |
| 典型应用场景 | 寄存器配置、参数读取、低功耗控制 | 高帧率图像数据流、固件批量下载 |
3.1.1 I2C 读写时序与驱动实现
I2C 的每次传输以 START 条件开始,以 STOP 条件结束。Sensor 寄存器写入通常采用“先写寄存器地址,再写数据”的组合格式。读取时则需要先发送寄存器地址,然后重新发送 START 条件(重复起始)并切换为读方向。
// I2C 写寄存器示例(伪代码)
int sensor_i2c_write(struct i2c_client *client, uint16_t reg, uint8_t val) {
uint8_t buf[3];
buf[0] = (reg >> 8) & 0xFF; // 寄存器高字节
buf[1] = reg & 0xFF; // 寄存器低字节
buf[2] = val; // 写入数据
return i2c_master_send(client, buf, 3);
}
// I2C 读寄存器示例(伪代码)
int sensor_i2c_read(struct i2c_client *client, uint16_t reg, uint8_t *val) {
uint8_t addr_buf[2];
addr_buf[0] = (reg >> 8) & 0xFF;
addr_buf[1] = reg & 0xFF;
i2c_master_send(client, addr_buf, 2); // 发送寄存器地址
return i2c_master_recv(client, val, 1); // 接收数据
}
i2c_transfer 或 i2c_smbus_read/write 系列函数操作。务必确认 Sensor 的寄存器地址宽度(8 位 / 16 位)以及是否支持自动地址递增。
3.1.2 SPI 读写时序与驱动实现
SPI 通信由主设备控制时钟并驱动 CS 片选。Sensor 作为从设备,在 CS 拉低期间接收命令并返回数据。SPI 模式(CPOL、CPHA)必须与 Sensor 数据手册严格匹配,否则通信将完全失败。
// SPI 写寄存器示例(伪代码)
int sensor_spi_write(struct spi_device *spi, uint16_t reg, uint8_t val) {
uint8_t tx_buf[3];
tx_buf[0] = 0x02; // 写命令(由 Sensor 定义)
tx_buf[1] = reg & 0xFF; // 寄存器地址
tx_buf[2] = val; // 数据
struct spi_transfer t = {
.tx_buf = tx_buf,
.len = 3,
};
return spi_sync_transfer(spi, &t, 1);
}
// SPI 读寄存器示例(伪代码)
int sensor_spi_read(struct spi_device *spi, uint16_t reg, uint8_t *val) {
uint8_t tx_buf[2] = {0x03, reg & 0xFF}; // 读命令 + 地址
uint8_t rx_buf[2] = {0};
struct spi_transfer t = {
.tx_buf = tx_buf,
.rx_buf = rx_buf,
.len = 2,
};
spi_sync_transfer(spi, &t, 1);
*val = rx_buf[1]; // 第二个字节为读取的数据
return 0;
}
cs_change 标志位控制片选行为,务必根据 Sensor 手册配置。
3.2 Sensor 寄存器读写原理
Sensor 内部通过寄存器映射表控制曝光、增益、帧率、输出格式、测试图案等所有行为。驱动层的核心任务就是将上层下发的参数(如曝光时间、模拟增益)转换为寄存器地址和数值,并通过 I2C/SPI 总线写入 Sensor。
3.2.1 寄存器地址空间与分组
不同 Sensor 的寄存器地址空间差异较大,常见的有 8 位地址(256 字节)和 16 位地址(64 KB)。高通 Camera HAL 层通过 sensor_lib_t 结构体中的 reg_addr_type 字段来区分。
| 地址宽度 | 地址范围 | 典型 Sensor 示例 | 驱动配置 |
|---|---|---|---|
| 8 位 | 0x00 – 0xFF | OV5640、GC2053 | reg_addr_type = 0 |
| 16 位 | 0x0000 – 0xFFFF | IMX298、IMX586 | reg_addr_type = 1 |
| 16 位 + 页选 | 多页,每页 256 字节 | AR1335、某些安森美 Sensor | 需额外写页选寄存器 |
3.2.2 寄存器读写流程
一次完整的 Sensor 寄存器写入流程包含以下步骤:
- 构造寄存器表: 将曝光、增益、帧长等参数转换为
{reg_addr, reg_value}键值对。 - 分组与排序: 按寄存器地址递增排序,合并连续地址的写入(批量写入优化)。
- 页选处理: 如果跨页,先写入页选寄存器切换页面。
- 总线写入: 调用 I2C/SPI 驱动接口发送数据。
- 校验(可选): 回读关键寄存器确认写入成功。
i2c_msgs 批量传输,将多个寄存器写入合并为一次 I2C 事务,可显著降低帧切换延迟。在 sensor_setting 中尽量使用 reg_setting 数组一次性下发。
3.3 I2C/SPI 通信架构图
下图展示了高通 Camera 系统中,HAL 层、Sensor 驱动库、I2C/SPI 控制器与 Sensor 硬件之间的分层通信架构。
3.4 寄存器读写实战要点
- 地址对齐: 16 位地址 Sensor 在 I2C 传输时,高字节在前(Big Endian),低字节在后。SPI 则通常先发命令字节,再发地址字节。
- 批量写入优化: 对于连续地址的寄存器(如增益、曝光的一组配置),使用 I2C 的“重复起始”或 SPI 的“连续传输模式”可减少总线开销。
- 延迟与等待: 某些寄存器写入后需要等待若干帧(Frame)或若干毫秒才能生效,驱动中应加入
msleep或帧同步等待。 - 调试手段: 使用高通
i2cget/i2cset工具或spidev_test在用户空间验证寄存器读写是否正确,再集成到驱动中。
3.5 本章小结
I2C 和 SPI 是 Sensor 驱动与硬件之间的通信桥梁。掌握两者的时序差异、驱动接口和寄存器读写原理,是后续进行 Sensor 上电序列、流控、曝光控制等调试工作的前提。在高通平台上,I2C 用于低速配置,SPI 用于高速批量传输,驱动开发者需要根据 Sensor 规格书正确选择总线并配置控制器参数。