4. Sensor驱动开发实战:OV5640驱动移植、I2C读写时序、MIPI CSI接口配置
好,咱们进入第四讲。这一章,我打算手把手带你走一遍Sensor驱动的开发流程。选用的Sensor是OV5640,这颗芯片在市场上非常常见,500万像素,性价比高,资料也多,非常适合用来做教学案例。
说白了,驱动开发的核心就三件事:让主控能跟Sensor说话(I2C)、让数据能传过来(MIPI)、让Sensor乖乖工作(初始化配置)。咱们一个一个来拆解。
4.1 OV5640驱动移植:从零开始
移植驱动,不是让你从头写一个。MTK平台已经提供了Sensor驱动的框架,我们只需要把OV5640的配置填进去就行。我个人习惯,先把官方提供的驱动模板复制一份,改个名字,然后开始填充。
驱动文件通常放在 kernel-4.19/drivers/misc/mediatek/imgsensor/src/common/v1/ 这个路径下。你找到 ov5640_mipi_raw.c 这个文件,它就是我们的主战场。
嗯,这里要注意,文件名里的 mipi_raw 表示数据接口是MIPI,输出格式是RAW。如果你的Sensor是YUV输出,那文件名就是 ov5640_mipi_yuv.c。别搞混了。
4.2 I2C读写时序:跟Sensor对话
I2C是Sensor的控制通道。主控通过I2C总线,向Sensor的寄存器写入配置值,或者读取状态信息。OV5640的I2C地址是0x3C(写)和0x3D(读),这个地址在数据手册里能找到。
我在项目中遇到过,有些开发板上的I2C上拉电阻没焊,导致通信失败。你想想看,I2C总线是开漏输出,没有上拉电阻,信号根本拉不上去。所以,第一步先检查硬件。
MTK平台封装好了I2C读写函数,我们直接调用就行。核心函数就两个:
// 写Sensor寄存器
int iReadRegI2C(u8 *a_pSendData, u16 a_sizeSendData,
u8 *a_pRecvData, u16 a_sizeRecvData,
u16 i2cId);
// 读Sensor寄存器
int iWriteRegI2C(u8 *a_pSendData, u16 a_sizeSendData,
u16 i2cId);
别看参数多,其实很简单。写操作就是:发送设备地址 + 寄存器地址 + 数据值。读操作就是:先发送设备地址 + 寄存器地址,再重新发送设备地址(读方向),然后接收数据。
我曾经踩过一个坑:OV5640的寄存器地址是16位的,但有些Sensor是8位的。如果你用8位地址去访问16位地址的Sensor,读回来的数据全是错的。所以,一定要确认好地址宽度。
4.3 MIPI CSI接口配置:数据通道
MIPI CSI是图像数据的传输通道。OV5640支持1-lane、2-lane和4-lane模式。MT8678的MIPI CSI接口支持4-lane,所以我们用4-lane模式,带宽最充裕。
配置MIPI接口,主要是在驱动里设置以下几个参数:
| 参数 | 说明 | OV5640典型值 |
|---|---|---|
| lane数 | 数据通道数量 | 4 |
| MIPI时钟频率 | 每lane的数据传输速率 | 336 Mbps(1080p@30fps) |
| 数据类型 | RAW10、RAW8、YUV等 | RAW10 |
| VC(Virtual Channel) | 虚拟通道号 | 0 |
这些参数在驱动初始化函数里配置。MTK的驱动框架里,有一个 struct SENSOR_MIPI_INFO_STRUCT 结构体,专门用来存放MIPI配置信息。我们直接赋值就行。
static struct SENSOR_MIPI_INFO_STRUCT ov5640_mipi_info = {
.mipi_lane_num = 4, // 4-lane
.mipi_data_rate = 336, // 336 Mbps per lane
.mipi_data_type = MIPI_RAW10, // RAW10格式
.mipi_vc = 0, // 虚拟通道0
};
这里有个细节:MIPI时钟频率不是随便填的。它跟分辨率、帧率、像素位深有关。计算公式是:
MIPI时钟频率 = (水平像素 × 垂直像素 × 帧率 × 位深) / (lane数 × 2)
举个例子,1080p(1920×1080)@30fps,RAW10(10bit),4-lane:
(1920 × 1080 × 30 × 10) / (4 × 2) = 77.76 Mbps
但实际中,还要考虑消隐区(blanking)的开销,所以通常要留20%~30%的余量。336 Mbps这个值,是OV5640数据手册里推荐的,我们直接拿来用就行。
4.4 驱动初始化流程:让Sensor动起来
驱动初始化,说白了就是给Sensor写一堆寄存器配置。OV5640的初始化序列很长,有几百个寄存器要配置。这些配置值,Sensor厂商会提供,我们直接复制到驱动里就行。
初始化函数通常叫 ov5640_init(),里面是一个大数组,每个元素包含寄存器地址和值:
static struct OV5640_REG ov5640_init_regs[] = {
{0x3103, 0x11},
{0x3008, 0x82},
{0x3008, 0x42},
// ... 几百行配置
{0x3008, 0x02},
};
写配置的时候,有个顺序问题。有些寄存器必须在其他寄存器之前写,否则Sensor会工作异常。我建议,严格按照厂商提供的顺序来,不要自作聪明去调整。
我曾经试过,为了优化启动速度,把一些不重要的配置删掉了。结果Sensor输出图像全是花屏。嗯,从那以后我再也不敢乱删配置了。
4.5 避坑指南:我踩过的那些坑
做Sensor驱动开发,坑不少。我总结几个常见的,你遇到了可以少走弯路。
- I2C通信失败:先检查硬件连接,上拉电阻、电源、地线。再用示波器抓I2C波形,看地址对不对,ACK信号有没有。
- MIPI信号不稳定:检查MIPI时钟频率是否匹配,差分线阻抗是否100欧姆,走线长度是否等长。
- 图像花屏:大概率是初始化配置不对,或者MIPI lane数配置错了。重新核对数据手册。
- Sensor不输出图像:检查PWDN(掉电)和RESET(复位)引脚的电平。OV5640的PWDN引脚,低电平是正常工作,高电平是掉电。
嗯,这一章的内容就这些。说白了,Sensor驱动开发就是跟I2C和MIPI打交道。把这两个接口调通了,剩下的就是填配置表。下一章,咱们聊聊图像数据的接收和处理,那才是真正有意思的部分。