文章目录前言一、I2C 物理结构以及基本原理1.1硬件连接结构1.2主从关系二、Linux系统下的 I2C 节点结构2.1Linux I2C 子系统三大部分三、具体实验代码内容3.1 修改设备树3.2 写寄存器头文件3.3写 AP3216C I2C 驱动把 I2C 设备包装成 Linux 可访问设备3.4 写用户态测试 APP用 open/read 验证驱动是否工作四、MIPI摄像头 V4L2相关设备搭建总结前言本章内容了解I2C控制器驱动并在理解适配器驱动下自己写挂在 I2C 总线下面的设备驱动一、I2C 物理结构以及基本原理1.1硬件连接结构共有两条线SCL-串行时钟线SDA-串行数据线支持一个主机挂多个从机每个从机通过不同的 I2C 地址区分。1.2主从关系主机一般负责数据发送包括从机地址读写数据开始结束信号等从机只根据主机命令接收或发送数据。二、Linux系统下的 I2C 节点结构第一层SoC 的 I2C 控制器第二层挂在 I2C 控制器下面的 I2C 从设备2.1Linux I2C 子系统三大部分大致可以分成三部分I2C core负责统一管理 I2C 总线、设备、驱动注册和匹配I2C adapter表示 SoC 里的一个 I2C 控制器比如 i2c5I2C client driver表示挂在 I2C 总线下面的某个从设备比如 AP3216C表示这个从设备对应的软件驱动比如 ap3216c_driverrk3x_i2c_probe的作用申请 rk3x_i2c 私有结构体解析设备树里的 I2C 时序和频率初始化 i2c_adapter设置 i2c_algorithm映射 I2C 控制器寄存器获取中断号并注册中断获取并准备时钟调用 i2c_add_adapter() 注册 adapter具体流程再platform驱动有讲后续讲解三、具体实验代码内容可以分为五个部分3.1 修改设备树3.2 写 AP3216C 寄存器头文件3.3 写 AP3216C I2C 驱动3.4 写用户态测试 APP3.5 编译、加载、运行测试3.1 修改设备树修改设备树增加节点以及节点内的compatible和从机地址等其他硬件资源I2C的reg是从机地址但是其他的不一定spi片选号 chip select3.2 写寄存器头文件AP3216C 是一个真实存在的 I2C 外设芯片。它有自己的数据手册里面规定了 I2C 地址、寄存器地址、寄存器含义、数据格式和初始化时序。驱动开发的工作就是把这些手册内容翻译成内核代码1. 设备树描述这个芯片挂在哪条 I2C 总线上2. 驱动代码根据 compatible 匹配这个芯片3. 驱动通过 I2C 读写 AP3216C 的寄存器4. 驱动把这个芯片注册成 Linux 能管理的设备5. 如果需要用户态访问再创建 /dev/ap3216c 这种设备节点头文件中需要写数据寄存器的地址同时设置子设备的工作模式需要把芯片手册里的“寄存器表/命令表”翻译成驱动代码能直接使用的宏。PS:0x0F 对应什么功能不是我们自己定的而是芯片厂商在硬件内部已经设计好的。数据手册只是把这个固定规则告诉我们。3.3写 AP3216C I2C 驱动把 I2C 设备包装成 Linux 可访问设备第一件事注册 i2c_driver让驱动能和设备树里的 i2c_client 匹配。第二件事注册字符设备让用户态能通过 /dev/ap3216c 访问传感器。AP3216C 实验其实有两条线。第一条线设备发现和驱动匹配线-i2c_client、i2c_driver、bus、probe绑定client和字符设备节点实现用户和内核间的数据交换这里具体是驱动拿到client就可以通过驱动内部读写函数函数访问硬件了第二条线用户态访问线-alloc_chrdev_region()、cdev_init()、cdev_add()等创建用户态节点只有这样才可以实现用户与内核态的数据交换在 device 和 driver 匹配成功、probe 被调用之后驱动会进一步把这个client绑定某设备字符设备、subv4l2设备到某个 Linux 子系统中。如果注册到字符设备框架就形成 /dev/xxx如果注册到 V4L2/media 框架就形成 v4l2_subdev/media entity根据设备所在总线类型选择对应 driver 结构体根据设备通信方式封装对应读写函数根据用户态访问方式注册对应 Linux 子系统接口。3.4 写用户态测试 APP用 open/read 验证驱动是否工作四、MIPI摄像头 V4L2相关设备搭建具体流程如下这里是从我记得笔记拉下来的csdn的引用不太好看需要的同学可以直接复制下来让AI给你详细讲清楚设备树i2c2 {ov1385536 {compatible “ovti,ov13855”;reg 0x36;reset-gpios …;clocks …;port {endpoint {remote-endpoint csi_in; };};↓内核解析设备树创建 i2c_client/sys/bus/i2c/devices/2-0036↓加载 ov13855 驱动i2c_add_driver(ov13855_i2c_driver)↓I2C bus 匹配compatible 匹配成功↓调用 ov13855_probe(client)↓probe 中申请 ov13855 私有结构体保存 client获取 clk/gpio/regulatorpower onI2C 读 chip ID初始化 v4l2_subdev初始化 media pad初始化 controls注册 async subdev↓media frameworksensor subdev 等待和 CSI/ISP 绑定形成 media graph↓用户态open(“/dev/videoX”)VIDIOC_S_FMTVIDIOC_REQBUFSVIDIOC_STREAMON↓capture 驱动启动 pipeline调用 sensor s_stream(1)↓sensor 驱动I2C 写寄存器表I2C 写 stream on↓sensor 开始通过 MIPI CSI-2 输出图像↓CSI / ISP / video capture 接收图像↓用户态 DQBUF 获取图像帧总结下面以正点原子教程中的I2C字符实验内容来总结V4L2结构可以看上面的流程基本原理是一样的只不过根据注册的不同设备字符设备、V4L2等可能具体驱动和字符设备注册流程不同AP3216C 的寄存器地址由芯片数据手册规定驱动文件通过引用 ap3216creg.h把这些寄存器地址宏用于 I2C 读写函数中。设备树负责描述 AP3216C 挂在哪条 I2C 总线上、I2C 地址是多少以及可能需要的 GPIO、clock、regulator 等资源。内核解析设备树后创建 i2c_clienti2c_client 表示 AP3216C 在 I2C 总线模型中的设备对象里面保存 I2C 地址、所属 adapter 以及通用 device 信息。ap3216c_driver 注册到 I2C 子系统后与 i2c_client 匹配成功并进入 probe。probe 中申请驱动私有结构体 ap3216c_dev把 i2c_client 保存进去同时注册字符设备 cdev创建 /dev/ap3216c。这样用户态通过 /dev/ap3216c 调用 open/read 时会进入 file_operations 对应函数而这些函数内部再通过 ap3216c_read_regs/ap3216c_write_regs 和 i2c_transfer 访问 AP3216C 硬件寄存器。注意client中储存了I2C 地址所属 I2C adapter 表示这个设备挂在哪个 I2C 控制器下面3.compatible “ovti,ov13855” 参与和 i2c_driver 的 of_match_table 匹配通用 struct device这个 struct device 里关联着原始设备树节点后续其他内容需要驱动来读取并储存本质上因为client是适配很多的他们可能有不同的子设备都叫做client都写进去太臃肿了