Windows XP Windows 7 Windows 2003 Windows Vista Windows 綜合教學 Linux 系統教學
Windows 10 Windows 8 Windows 2008 Windows NT Windows Server 電腦軟體教學
您现在的位置: Windows教程網 >> Windows教程 >  >> Linux 系統教學 >> Linux 教學

Linux I2C 子系統驅動開發深度解析:總線、演算法與晶片驅動

編輯:Linux 教學

I2C 協議與 Linux 子系統架構

I2C (Inter-Integrated Circuit) 為 Philips 開發的雙線串行總線,廣泛用於嵌入式系統傳感器、EEPROM、電源管理晶片等。Linux 2.6+ 將 I2C 核心納入主線,架構分為四層:

  1. I2C Core:核心框架,管理適配器、驅動註冊、設備探測、sysfs 介面
  2. I2C Bus Driver (適配器驅動):控制器硬體驅動 (如 CPU 內建 I2C、GPIO 模擬、PCI/USB 轉接器)
  3. I2C Algorithm Driver:傳輸協議實現 (Bit-banging、硬體 FIFO、SMBus 協議)
  4. I2C Chip Driver (客戶端驅動):具體晶片驅動 (如 LM75、AT24Cxx、MPU6050、燈驅動 IC)

總線驅動開發:struct i2c_adapter

定義於 include/linux/i2c.h,核心欄位:


struct i2c_adapter {
    struct module *owner;           // THIS_MODULE
    unsigned int class;             // 支援的設備類別 (I2C_CLASS_HWMON, I2C_CLASS_SPD...)
    const struct i2c_algorithm *algo; // 演算法操作集
    char name[I2C_NAME_SIZE];       // 顯示名稱 (出現在 /sys/bus/i2c/devices/i2c-X/name)
    struct device dev;              // 設備模型整合
    int nr;                         // 適配器編號 (動態分配)
    ...
};

最小實現範例


static struct i2c_algorithm tiny_algo = {
    .master_xfer   = tiny_master_xfer,  // 標準 I2C 傳輸
    .smbus_xfer    = tiny_smbus_xfer,   // SMBus 協議 (可選)
    .functionality = tiny_func,         // 支援功能位圖
    .name          = "tiny-algo",
};

static struct i2c_adapter tiny_adapter = {
    .owner = THIS_MODULE,
    .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
    .algo  = &tiny_algo,
    .name  = "tiny-i2c-adapter",
};

static int __init tiny_i2c_init(void)
{
    // 硬體初始化: GPIO 方向、時鐘、中斷...
    return i2c_add_adapter(&tiny_adapter);
}

static void __exit tiny_i2c_exit(void)
{
    i2c_del_adapter(&tiny_adapter);
}

演算法驅動:struct i2c_algorithm

定義傳輸原語,核心回調:


struct i2c_algorithm {
    int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
    int (*smbus_xfer)(struct i2c_adapter *adap, u16 addr, unsigned short flags,
                      char read_write, u8 command, int size, union i2c_smbus_data *data);
    u32 (*functionality)(struct i2c_adapter *adap);
    ...
};

功能位圖 (functionality)


static u32 tiny_func(struct i2c_adapter *adap)
{
    return I2C_FUNC_I2C |                    // 標準 I2C 讀寫
           I2C_FUNC_SMBUS_READ_BYTE |        // SMBus 單位元組讀
           I2C_FUNC_SMBUS_WRITE_BYTE |       // SMBus 單位元組寫
           I2C_FUNC_SMBUS_READ_BYTE_DATA |   // SMBus 讀位元組資料
           I2C_FUNC_SMBUS_WRITE_BYTE_DATA |  // SMBus 寫位元組資料
           I2C_FUNC_SMBUS_READ_WORD_DATA |   // SMBus 讀字資料
           I2C_FUNC_SMBUS_WRITE_WORD_DATA |  // SMBus 寫字資料
           I2C_FUNC_SMBUS_BLOCK_DATA;        // SMBus 區塊資料
}

master_xfer 實現要點


static int tiny_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
    struct i2c_msg *msg;
    int i, ret;

    for (i = 0, msg = msgs; i < num; i++, msg++) {
        // 1. 發送 START 條件
        // 2. 發送位址 (msg->addr << 1 | (msg->flags & I2C_M_RD))
        // 3. 根據 msg->flags 處理讀/寫
        //    - 寫: 發送 msg->buf[0..msg->len-1]
        //    - 讀: 接收 msg->len 位元組至 msg->buf
        // 4. 發送 STOP 條件 (最後一個訊息或不支援重複 START)
        // 5. 處理 ACK/NACK、仲裁失敗、超時
    }
    return num; // 返回成功傳輸的訊息數
}

內建演算法驅動 (可直接複用)

驅動文件適用場景註冊函數
i2c-algo-bit.cGPIO 模擬 (Bit-banging)、簡單控制器i2c_bit_add_bus()
i2c-algo-pcf.cPCF8584 風格控制器pcf_add_bus()
i2c-algo-ite.cITE IT87xx 系列ite_add_bus()
i2c-algo-ibm_ocp.cIBM PPC 405 OCPibm_ocp_add_bus()

晶片驅動開發:struct i2c_driver


static int mychip_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    // 1. 驗證晶片 ID (讀取 WHO_AM_I 等)
    // 2. 分配私有數據結構
    // 3. 初始化硬體 (配置寄存器)
    // 4. 註冊子系統 (hwmon, input, regulator, v4l2...)
    // 5. 建立 sysfs 屬性
    return 0;
}

static int mychip_remove(struct i2c_client *client)
{
    // 反向清理
    return 0;
}

static const struct i2c_device_id mychip_id[] = {
    { "mychip", 0 },
    { }
};
MODULE_DEVICE_TABLE(i2c, mychip_id);

static struct i2c_driver mychip_driver = {
    .driver = {
        .name   = "mychip",
        .of_match_table = mychip_of_match, // Device Tree 支援
    },
    .probe    = mychip_probe,
    .remove   = mychip_remove,
    .id_table = mychip_id,
    .detect   = mychip_detect, // 可選:總線掃描自動探測
};

module_i2c_driver(mychip_driver);

Device Tree 綁定


/* dts 片段 */
&i2c1 {
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_i2c1>;

    mychip@48 {
        compatible = "vendor,mychip";
        reg = <0x48>;           // 7-bit 位址
        vcc-supply = <&vdd_3v3>; // 電源管理 (可選)
        interrupt-parent = <&gpio2>;
        interrupts = <15 IRQ_TYPE_LEVEL_LOW>; // 中斷腳位 (可選)
    };
};

用戶空間介面與調試


# 列出所有適配器
ls /sys/bus/i2c/devices/i2c-*

# 掃描總線設備 (需 root)
i2cdetect -l
i2cdetect -y 1     # 掃描 i2c-1 總線

# 直接讀寫 (調試用)
i2cget -y 1 0x48 0x00  # 讀取位址 0x48, 寄存器 0x00
i2cset -y 1 0x48 0x01 0x55  # 寫入

# 內核日誌
dmesg -T | grep -i i2c

常見問題與最佳實踐

問題原因解決
探測失敗 (-ENXIO)位址錯誤、晶片未上電、總線被佔用檢查原理圖、電源序列、i2cdetect 掃描
傳輸超時 (-ETIMEDOUT)SCL 被拉低 (晶片時鐘延展)、GPIO 方向錯示波器抓波形、啟用 I2C 核心調試
NACK 錯誤設備不存在、位址衝突、寄存器不可讀寫確認 7-bit 位址、晶片資料手冊
多主機仲裁失敗同時有其他主機發起傳輸軟體重試、硬體設計避免多主機

效能優化建議

  • 使用 I2C_FUNC_SMBUS_BLOCK_DATA 批量讀寫 (最多 32 位元組)
  • 啟用 DMA 傳輸 (控制器支援時)
  • 合併連續寄存器讀寫為單次 Block 事務
  • 適當提高總線頻率 (Standard 100kHz → Fast 400kHz → Fast+ 1MHz → High-speed 3.4MHz)
  • 使用 i2c_transfer_buffer_flags() 避免內核緩衝區分配
Copyright © Windows教程網 All Rights Reserved