2. Sensor驱动基础:I2C通信协议、MIPI CSI-2接口、Sensor寄存器读写方法
在RK平台Camera驱动开发中,Sensor驱动是连接硬件与上层ISP的桥梁。本章聚焦Sensor驱动的三大基础:I2C控制通道、MIPI CSI-2数据通道以及寄存器读写方法。理解这些基础是编写稳定、高效Sensor驱动的先决条件。
2.1 I2C通信协议与Sensor控制
I2C(Inter-Integrated Circuit)是Sensor驱动中最常用的控制总线,用于读写Sensor内部的寄存器,完成初始化、曝光控制、增益调整、帧率设置等操作。
2.1.1 I2C总线物理层与协议层
- 物理层:两条线——SCL(时钟线)和SDA(数据线),上拉电阻接VDD。Sensor作为从设备,拥有7位或10位从机地址。
- 协议层:起始条件(SDA在SCL高电平时下降)、停止条件(SDA在SCL高电平时上升)、数据字节(MSB先行,每字节后跟ACK/NACK)。
- 速率:标准模式100kHz,快速模式400kHz,高速模式3.4MHz。Sensor驱动中常用100kHz或400kHz。
clock-frequency属性指定速率。
2.1.2 Sensor I2C寄存器读写时序
Sensor寄存器读写通常采用复合格式:先发送设备地址(写),再发送寄存器地址(16位或8位),最后发送数据。读操作需先写寄存器地址,然后重新发送设备地址(读)并读取数据。
| 操作 | 步骤1 | 步骤2 | 步骤3 | 步骤4 |
|---|---|---|---|---|
| 写寄存器 | Start + DevAddr(W) | RegAddr (16bit) | Data (8bit) | Stop |
| 读寄存器 | Start + DevAddr(W) | RegAddr (16bit) | ReStart + DevAddr(R) | Read Data + Stop |
// Linux内核I2C传输示例:写Sensor寄存器
static int sensor_write_reg(struct i2c_client *client, u16 reg, u8 val)
{
struct i2c_msg msg;
u8 buf[3];
int ret;
buf[0] = (reg >> 8) & 0xFF; // 寄存器高8位
buf[1] = reg & 0xFF; // 寄存器低8位
buf[2] = val; // 写入值
msg.addr = client->addr;
msg.flags = 0; // 写操作
msg.len = 3;
msg.buf = buf;
ret = i2c_transfer(client->adapter, &msg, 1);
if (ret < 0)
dev_err(&client->dev, "I2C write reg 0x%04x failed: %d\n", reg, ret);
return ret;
}
2.1.3 I2C通信故障排查清单
- 地址错误:Sensor从机地址是否左移1位?Linux内核使用7位地址,但i2c_client->addr需与硬件匹配。
- 时序不匹配:I2C时钟频率过高或SCL/SDA上拉电阻不足,导致波形畸变。
- ACK丢失:Sensor未响应,检查供电、复位、电源时序。
- 寄存器地址宽度:部分Sensor使用8位寄存器地址,部分使用16位,需确认datasheet。
2.2 MIPI CSI-2接口与数据通道
MIPI CSI-2(Camera Serial Interface 2)是移动和嵌入式领域最主流的摄像头图像数据传输接口。RK平台通过MIPI D-PHY接收Sensor输出的RAW数据或YUV数据。
2.2.1 CSI-2分层架构
| 层级 | 功能 | 协议单元 |
|---|---|---|
| 应用层 | 图像数据处理(像素格式、帧结构) | 像素值、帧起始/结束码 |
| 协议层 | 数据包封装、虚拟通道、数据类型标识 | 长包/短包、ECC、CRC |
| 物理层 | 差分信号传输、时钟与数据恢复 | D-PHY Lane、HS/LP模式 |
2.2.2 D-PHY Lane配置与带宽计算
Sensor通过1/2/4条数据Lane(通道)传输数据,每条Lane的速率可达1.5Gbps(D-PHY v1.2)或更高。带宽计算公式:
总带宽 = Lane数 × 每条Lane速率 × 编码效率(8b/10b编码为80%)
所需带宽 = 分辨率宽 × 分辨率高 × 帧率 × 每像素比特数
所需带宽 = 3840 × 2160 × 30 × 10 = 2.488 Gbps
每条Lane速率 = 2.488 / 4 / 0.8 = 777.5 Mbps → 实际配置800Mbps/Lane
2.2.3 CSI-2数据包格式
CSI-2传输的基本单元是数据包,分为长包(Long Packet)和短包(Short Packet)。长包传输像素数据,短包传输帧同步、行同步等控制信息。
// 长包结构
// | 数据标识符(DI) | 字计数(WC) | ECC | 数据载荷 | 填充字节 | CRC |
// | 1字节 | 2字节 | 1字节 | 可变长度 | 可选 | 2字节 |
// 短包结构
// | 数据标识符(DI) | 数据字段 | ECC |
// | 1字节 | 2字节 | 1字节 |
2.3 Sensor寄存器读写方法
在RK平台Sensor驱动中,寄存器读写是核心操作。除了直接使用I2C传输函数,Linux内核提供了丰富的辅助API和抽象层。
2.3.1 内核I2C读写API
| API函数 | 说明 | 适用场景 |
|---|---|---|
i2c_smbus_read_byte_data() |
读8位寄存器(8位地址) | 简单Sensor,寄存器地址8位 |
i2c_smbus_write_byte_data() |
写8位寄存器(8位地址) | 简单Sensor,寄存器地址8位 |
i2c_master_send() / i2c_master_recv() |
原始I2C消息发送/接收 | 自定义协议、16位寄存器地址 |
i2c_transfer() |
传输一组I2C消息(支持复合操作) | 批量读写、读前写地址 |
regmap API |
寄存器映射缓存框架 | 复杂Sensor,需要缓存和批量操作 |
2.3.2 使用regmap管理Sensor寄存器
regmap是Linux内核提供的通用寄存器映射框架,可以简化I2C/SPI设备的寄存器读写,并提供缓存、批量操作、调试接口等能力。
// 使用regmap实现Sensor寄存器读写
static const struct regmap_config sensor_regmap_config = {
.reg_bits = 16, // 寄存器地址16位
.val_bits = 8, // 寄存器值8位
.max_register = 0xFFFF, // 最大寄存器地址
.cache_type = REGCACHE_RBTREE, // 使用红黑树缓存
};
static int sensor_probe(struct i2c_client *client)
{
struct regmap *regmap;
regmap = devm_regmap_init_i2c(client, &sensor_regmap_config);
if (IS_ERR(regmap)) {
dev_err(&client->dev, "regmap init failed\n");
return PTR_ERR(regmap);
}
// 通过regmap读写寄存器
regmap_write(regmap, 0x3000, 0x10); // 写寄存器0x3000 = 0x10
regmap_read(regmap, 0x3000, &val); // 读寄存器0x3000
return 0;
}
2.3.3 寄存器初始化序列管理
Sensor初始化通常需要写入大量寄存器配置,驱动中应使用结构化的初始化序列表,而不是散落在代码各处的单次写操作。
// 寄存器初始化序列结构体
struct sensor_regval {
u16 reg;
u8 val;
};
// 初始化序列表(示例)
static const struct sensor_regval sensor_init_regs[] = {
{0x0103, 0x01}, // 软件复位
{0x0100, 0x00}, // 进入待机模式
{0x3030, 0x10}, // PLL配置
{0x3032, 0x40}, // 时钟分频
{0x3010, 0x00}, // 模拟电路配置
{0x3012, 0x08}, // ADC配置
{0x3020, 0x30}, // 输出格式设置
{0x3022, 0x01}, // 数据Lane使能
{0x0100, 0x01}, // 进入流模式
};
// 批量写入函数
static int sensor_write_regs(struct regmap *regmap,
const struct sensor_regval *regs, int count)
{
int i, ret;
for (i = 0; i < count; i++) {
ret = regmap_write(regmap, regs[i].reg, regs[i].val);
if (ret < 0) {
pr_err("write reg 0x%04x failed: %d\n", regs[i].reg, ret);
return ret;
}
// 部分Sensor需要寄存器间延迟
if (regs[i].reg == 0x0103) // 复位后需等待
usleep_range(10000, 12000);
}
return 0;
}
2.3.4 寄存器读写调试技巧
- I2C总线抓取:使用
i2c-tools中的i2cdump、i2cget、i2cset验证硬件通信是否正常。 - 动态调试:在驱动中增加
dev_dbg()打印每次寄存器读写,通过dynamic_debug控制开关。 - regmap debugfs:挂载debugfs后查看
/sys/kernel/debug/regmap/下对应设备的寄存器缓存和实际值。 - 回读验证:写寄存器后立即回读,确认写入值与预期一致,排查I2C通信问题。
2.4 本章小结
Sensor驱动的基础在于对I2C控制通道和MIPI CSI-2数据通道的深入理解。I2C负责配置Sensor内部寄存器,MIPI CSI-2负责将图像数据传输到SoC。掌握寄存器读写方法,特别是使用regmap框架和结构化初始化序列,能够显著提升驱动的可维护性和稳定性。在下一章中,我们将基于这些基础,构建完整的Sensor驱动框架。