[特殊字符] 驱动开发硬核特训 · Day 4

发布于:2025-04-07 ⋅ 阅读:(18) ⋅ 点赞:(0)

主题:从硬件总线到驱动控制 —— I2C 协议与传感器驱动开发全解析

I2C(Inter-Integrated Circuit)总线是一种广泛用于嵌入式设备的串行通信协议,因其低成本、简单布线和多从设备支持,成为连接各种传感器(温湿度、加速度计、压力等)、EEPROM、IO 扩展器的首选方案。

本期内容围绕 I2C 协议原理、SoC 控制器设计、Linux I2C 驱动框架、自定义传感器驱动开发及调试技巧展开。重点聚焦 I2C 控制流程、寄存器操作、中断与轮询模式、设备树配置、驱动 API 与适配实例,帮助你从硬件协议出发全面掌握 I2C 驱动能力。


在这里插入图片描述

🎯 本期特训结构

模块 内容概览
I2C 总线原理 硬件结构、通信协议、信号时序、主从机制
控制器架构 SoC 中 I2C 控制器模块的工作流程与寄存器解释
Linux 驱动框架 I2C adapter 与 client 机制、核心 API 解析
设备树配置 reg、compatible、clock、scl-gpios、interrupts 等
驱动编写流程 编写 platform 驱动适配某一具体 I2C 传感器(如 BH1750、MPU6050)
实战平台案例 基于 i.MX6ULL + BH1750 光照传感器完整驱动开发
常见问题与调试 设备不响应、时序错误、ACK 失败、挂载失败

📘 Part 1:I2C 协议与硬件原理

✅ 1. I2C 总线基础结构
  • 两条信号线:SCL(时钟)与 SDA(数据)
  • 多主多从机制:唯一地址分配,支持多个从设备
  • 开漏结构:需外接上拉电阻
  • 传输单位:以字节(8bit)为单位,附带 1bit ACK
✅ 2. I2C 通信流程
  1. 主机发送 START 信号(SDA 下降沿,SCL 高)
  2. 发送 7 位从设备地址 + 1 位 R/W 位
  3. 从机返回 ACK(拉低 SDA)
  4. 主机读写数据,期间每字节都需从机 ACK
  5. 主机发送 STOP 信号(SDA 上升沿,SCL 高)
✅ 3. 波形时序图解
START   Address   ACK   Data   ACK   ...   STOP
 |         |       |     |     |           |
↓SDA     7+1bit  ← 1bit  8bit  ← 1bit    ↑SDA

📘 Part 2:I2C 控制器硬件结构分析(以 i.MX6ULL 为例)

✅ 1. 控制器模块说明

i.MX6ULL SoC 内部包含多个 I2C 控制器(I2C1 ~ I2C4),每个控制器控制一条独立 I2C 总线。

  • 基地址示例:
    • I2C1:0x021A0000
    • I2C2:0x021A4000

主要寄存器:

寄存器名 功能
I2C_I2CR 控制寄存器(使能、主从、发起 START)
I2C_I2SR 状态寄存器(是否忙、ACK 等)
I2C_IADR 从设备地址寄存器(用于 slave 模式)
I2C_I2DR 数据寄存器(收发数据)
I2C_IFDR 时钟分频器设置
✅ 2. 控制流程关键点
  • 设置 I2C_I2CR 的 MST 和 TX 位,发起 START
  • 写入地址字节,等待中断或轮询 ACK
  • 写数据或读数据,操作 I2C_I2DR
  • 结束后清除中断,发 STOP 信号

📘 Part 3:Linux I2C 驱动框架

✅ 1. I2C adapter 与 client
  • struct i2c_adapter:表示一个 I2C 控制器
  • struct i2c_client:表示连接在 adapter 上的某个 I2C 外设

I2C 驱动注册流程:

  1. 控制器驱动注册 i2c_adapter(如 i2c-imx)
  2. 设备树声明某个 i2c 从设备节点(如 bh1750@23)
  3. 匹配 compatible,调用 i2c_driver.probe()
  4. 在 probe 中获取 i2c_client,使用 i2c_smbus_*()i2c_transfer() 进行通信
✅ 2. 核心通信 API
int i2c_smbus_read_byte_data(struct i2c_client *client, u8 reg);
int i2c_smbus_write_byte_data(struct i2c_client *client, u8 reg, u8 val);
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);

📘 Part 4:设备树中的 I2C 配置

✅ 1. 控制器节点
&i2c1 {
    clock-frequency = <100000>;
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_i2c1>;
    status = "okay";
};
✅ 2. 从设备节点
bh1750@23 {
    compatible = "rohm,bh1750";
    reg = <0x23>;
};

属性说明:

属性 说明
reg I2C 地址(7bit)
compatible 与驱动绑定的标识
clock-frequency I2C 速率(100k/400k)

📘 Part 5:BH1750 光照传感器驱动开发

✅ 1. 芯片说明
  • I2C 地址:0x23 或 0x5C
  • 单指令启动模式,输出 16bit 光照值(Lux)
  • 通信流程:写启动命令 → 读 2 字节数据
✅ 2. 驱动核心结构
static int bh1750_probe(struct i2c_client *client, const struct i2c_device_id *id) {
    int ret;
    u8 cmd = 0x10; // Continuously H-Resolution Mode
    ret = i2c_smbus_write_byte(client, cmd);
    if (ret < 0) return ret;
    msleep(180); // 等待数据稳定
    ret = i2c_smbus_read_word_data(client, 0x00);
    printk("Light = %d lux\n", be16_to_cpu(ret));
    return 0;
}

📘 Part 6:平台实战案例 —— i.MX6ULL + BH1750

✅ 硬件连接说明
  • SCL → I2C1_SCL(对应 GPIO1_IO16)
  • SDA → I2C1_SDA(对应 GPIO1_IO17)
  • 外接上拉电阻 4.7kΩ 至 3.3V
✅ 步骤概览
  1. 修改设备树,添加 bh1750@23 节点
  2. 编译设备树、内核并部署至开发板
  3. 编写驱动模块,注册 i2c_driver
  4. 加载驱动后通过 dmesg 打印光照值

🧪 常见问题与调试技巧

问题 可能原因
I2C 无法通信 未上拉、设备地址错误、频率不兼容
probe 不触发 compatible 不匹配、reg 地址错误
读取数据不正常 没有正确发送启动命令或读取顺序错误
总线异常挂死 多主冲突或时序误配置

调试建议:

  • 使用示波器观察 SDA/SCL 波形
  • 使用 i2cdetect -y 1 查看设备是否存在
  • 加 log 追踪通信过程

🧠 本日知识点总结

分类 核心内容
I2C 协议原理 START/STOP、地址位、ACK 时序
控制器结构 I2C_I2CR、I2DR、IFDR 配置与流程
驱动框架 i2c_adapter、i2c_client、i2c_driver 机制
设备树配置 clock-frequency、reg、compatible、pinctrl
驱动开发 读写流程、smbus API 使用、i2c_transfer 应用
调试技巧 i2cdetect 工具使用、log 跟踪、SDA/SCL 波形分析

✅ 总结

I2C 总线看似简单,实则涵盖了从硬件时序控制、控制器寄存器配置、协议状态转换,到 Linux 驱动注册、通信 API 使用、设备树适配的完整驱动开发链路。掌握 I2C 驱动不仅有助于理解嵌入式外设通信机制,也为后续复杂协议(如 SPI、MIPI、USB)打下坚实基础。

📘 Day 5 预告:输入子系统详解 + gpio-keys 与键盘矩阵驱动开发实战

敬请持续关注,每日精讲一类驱动,从硬件到驱动,让理解变成能力。