3. Sensor驱动框架
好,我们直接进入正题。这一章要讲的是Sensor驱动的骨架——说白了,就是驱动怎么“挂”到系统里,怎么和硬件说话,以及内核怎么把你写的驱动叫起来干活。这三个东西搞清楚了,后面写代码就是填空。
3.1 V4L2子系统:Camera驱动的“居委会”
V4L2,全称Video for Linux 2,是Linux内核里专门管视频设备的子系统。你可以把它想象成一个居委会——所有Camera设备都得在它这儿登记,用户空间程序(比如gstreamer、Camera HAL)通过它来统一调度。
我个人习惯把V4L2的层次拆成三层:
- 底层:Sensor、ISP、MIPI控制器等硬件驱动,负责和寄存器打交道。
- 中间层:V4L2核心框架,提供统一的设备节点(/dev/videoX)、ioctl接口、buffer管理。
- 上层:应用层,通过open/ioctl/poll操作设备节点。
在瑞芯微RK平台上,Sensor驱动属于底层。它通过V4L2的subdev(子设备)接口注册到系统中。为什么用subdev?因为一个Camera模组不止Sensor,还有闪光灯、马达、eeprom,每个都是一个子设备。V4L2通过subdev把它们组织起来,统一管理。
核心概念:V4L2 subdev是Sensor驱动的“身份证”。没有它,内核不认识你,应用层也找不到你。
3.2 I2C/SPI通信:和Sensor“打电话”
Sensor和主控芯片之间怎么通信?绝大多数Sensor用I2C,少数高速或特殊Sensor用SPI。瑞芯微平台两种都支持,但I2C是主流。
I2C通信要点:
- Sensor作为从设备,主控通过I2C总线读写寄存器。
- 每个Sensor有一个7位或10位地址,驱动里通过i2c_client结构体来操作。
- 读写函数一般封装成sensor_read_reg/sensor_write_reg,底层调用i2c_transfer。
我在项目中遇到过一个问题:Sensor的I2C地址明明是0x30,但死活读不到数据。后来发现是数据手册上写的是8位地址,而内核用的是7位地址,需要右移一位。嗯,这种坑很常见,大家一定要仔细看手册。
SPI通信要点:
- SPI是全双工,速度比I2C快,适合传输大量数据。
- Sensor驱动里一般用spi_write_then_read或spi_sync_transfer。
- 注意片选信号和时钟极性的配置,错了就通信不上。
| 特性 | I2C | SPI |
|---|---|---|
| 总线占用 | 2线(SCL+SDA) | 4线(SCLK+MOSI+MISO+CS) |
| 速度 | 标准100k~400k,快的有1M | 几十M很常见 |
| 设备寻址 | 软件地址 | 硬件片选 |
| 典型场景 | 配置寄存器 | 高速数据流 |
我的建议:调试I2C/SPI通信时,先拿逻辑分析仪抓波形。别上来就怀疑代码,很多时候是硬件连接或时序问题。
3.3 Sensor驱动入口与probe流程
驱动入口,说白了就是内核怎么找到你的驱动。在Linux设备模型中,驱动通过platform_driver或i2c_driver结构体注册。Sensor驱动通常用i2c_driver,因为Sensor挂在I2C总线上。
一个典型的Sensor驱动入口长这样:
static const struct i2c_device_id sensor_id[] = {
{"rk_sensor", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, sensor_id);
static struct i2c_driver sensor_i2c_driver = {
.driver = {
.name = "rk_sensor",
.of_match_table = sensor_of_match,
},
.probe = sensor_probe,
.remove = sensor_remove,
.id_table = sensor_id,
};
module_i2c_driver(sensor_i2c_driver);
这里要注意:of_match_table是设备树匹配的关键。设备树里写什么compatible,驱动里就要对应什么。我曾经见过同事把设备树里的compatible写成了"rk,sensor",驱动里写的是"rockchip,sensor",结果驱动死活不probe。这种低级错误,排查起来很浪费时间。
probe流程是驱动初始化的核心。Sensor驱动的probe一般做这几件事:
- 获取设备树资源:读取reg、reset-gpios、power-gpios、clock等配置。
- 初始化I2C/SPI通信:验证通信是否正常,比如读chip ID。
- 分配并初始化V4L2 subdev:设置subdev的ops(操作函数集)。
- 注册subdev:调用v4l2_device_register_subdev。
- 初始化硬件:上电、复位、配置初始寄存器。
我画了一张流程图,把probe的完整路径展示出来:
你看,probe流程其实不复杂。但有一个关键点:chip ID验证。这是probe里最重要的一步。如果读不到正确的chip ID,说明I2C通信有问题,或者设备树配置错了。我建议在probe里加上详细的错误打印,方便调试。
注意:probe函数里不要做耗时操作,比如msleep(1000)。内核的probe是串行执行的,你卡住了,其他设备也得等着。我曾经见过一个驱动在probe里做了500ms的延时,结果整个系统的启动时间被拖慢了。把耗时的初始化放到单独的工作队列里去做。
3.4 设备树匹配:驱动和硬件的“红娘”
设备树(Device Tree)是Linux内核用来描述硬件配置的一种机制。在RK平台上,Sensor的设备树节点一般长这样:
&i2c4 {
status = "okay";
rk_sensor: camera-sensor@30 {
compatible = "rockchip,rk-sensor";
reg = <0x30>;
clocks = <&cru CLK_MIPI_CAMERA>;
clock-names = "xvclk";
power-gpios = <&gpio2 RK_PB6 GPIO_ACTIVE_HIGH>;
reset-gpios = <&gpio2 RK_PB7 GPIO_ACTIVE_LOW>;
pinctrl-names = "default";
pinctrl-0 = <&cif_clk>;
rockchip,camera-module-index = <0>;
rockchip,camera-module-facing = "back";
};
};
这里有几个关键字段:
- compatible:驱动和设备树匹配的钥匙,必须和驱动里的of_match_table一致。
- reg:I2C地址,注意是7位地址。
- clocks:Sensor的主时钟,一般是24MHz或27MHz。
- power-gpios/reset-gpios:电源和复位引脚,驱动里通过gpiod_get来获取。
设备树写错了,驱动根本进不了probe。我在项目中遇到过最离谱的一次,是设备树里reg写成了0x60,但Sensor实际地址是0x30。查了两天才发现,原来是数据手册里写的是8位地址0x60,设备树里应该用7位地址0x30。嗯,这种坑踩过一次就记住了。
3.5 总结一下
Sensor驱动框架的核心就三件事:
- V4L2子系统:提供注册和通信的框架,subdev是Sensor的“身份证”。
- I2C/SPI通信:和Sensor硬件对话的通道,读写寄存器全靠它。
- probe流程:驱动初始化的入口,从获取资源到注册subdev,一步都不能少。
这三件事搞明白了,Sensor驱动的基本骨架就有了。后面往里面填代码,就是水到渠成的事。