I2C (Inter-Integrated Circuit) 為 Philips 開發的雙線串行總線,廣泛用於嵌入式系統傳感器、EEPROM、電源管理晶片等。Linux 2.6+ 將 I2C 核心納入主線,架構分為四層:
定義於 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 {
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);
...
};
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 區塊資料
}
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.c | GPIO 模擬 (Bit-banging)、簡單控制器 | i2c_bit_add_bus() |
i2c-algo-pcf.c | PCF8584 風格控制器 | pcf_add_bus() |
i2c-algo-ite.c | ITE IT87xx 系列 | ite_add_bus() |
i2c-algo-ibm_ocp.c | IBM PPC 405 OCP | ibm_ocp_add_bus() |
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);
/* 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 位元組)i2c_transfer_buffer_flags() 避免內核緩衝區分配Linux「Too many open files」錯誤徹底解決指南:臨時與永久修復方案
Linux C/C++ 連接 MySQL 編譯鏈接完全指南:mysql_config 最佳實踐與常見錯誤排查
解決 mkimage command not found:U-Boot 編譯 mkimage 工具與安裝指南
Linux 文件鎖:建議性鎖 vs 強制性鎖 核心差異與啟用指南
Linux 誕生史:從 Minix 到 0.01 版本核心的醞釀與發布
Linux chmod 755 與 777 權限含義與差異詳解
Linux 下產生 crypt 加密密碼的六種方法:mkpasswd、htpasswd、openssl、Perl、Python、MySQL