I²C(Inter-Integrated Circuit)は、シリアルデータ線(SDA)とシリアルクロック線(SCL)の2本で構成される同期式シリアルバスです。標準モードでは最大100 kbps、高速モードでは400 kbpsまでの通信が可能です。SDAおよびSCLはオープンドレイン出力で、外部プルアップ抵抗が必要です。通信はマスタがスレーブアドレスを送信し、対応するスレーブがACKを返すことで確立されます。
基本的な通信シーケンス
I²Cのデータ転送は、スタートコンディション(START)、スレーブアドレス+R/Wビット、ACK/NACK、データバイト(複数可)、ストップコンディション(STOP)で構成されます。
遅延関数の定義
タイミング調整のため、マイクロ秒単位の遅延を挿入します:
#define I2C_DELAY() delay_us(10)
スタートおよびストップコンディション
START:SCLがHIGHの状態でSDAをHIGH→LOWに遷移。
STOP:SCLがHIGHの状態でSDAをLOW→HIGHに遷移。
void i2c_start(void) {
sda_set(1);
I2C_DELAY();
scl_set(1);
I2C_DELAY();
sda_set(0);
I2C_DELAY();
scl_set(0); // 次の操作に備えてSCLをLOWに保持
I2C_DELAY();
}
void i2c_stop(void) {
sda_set(0);
I2C_DELAY();
scl_set(1);
I2C_DELAY();
sda_set(1);
I2C_DELAY();
}
ACK/NACKの送信
マスタがACK(0)またはNACK(1)を送信する際、SCLがHIGHの間にSDAのレベルを固定します。
void i2c_send_ack(uint8_t nack) {
sda_set(nack); // 0=ACK, 1=NACK
I2C_DELAY();
scl_set(1);
I2C_DELAY();
scl_set(0);
I2C_DELAY();
}
ACKの受信
スレーブからのACKを確認するため、SDAを入力モードにしてSCL HIGH中に値を読み取ります。
uint8_t i2c_wait_ack(void) {
uint8_t ack;
sda_input(); // SDAを入力に設定
I2C_DELAY();
scl_set(1);
I2C_DELAY();
ack = sda_read(); // 0ならACK、1ならNACK
scl_set(0);
I2C_DELAY();
sda_output(); // 再び出力モードに戻す
return ack;
}
1バイトの送信
MSBから順に8ビットをクロック同期で送信します。
void i2c_write_byte(uint8_t byte) {
uint8_t i;
for (i = 0; i < 8; i++) {
sda_set((byte & (0x80U >> i)) ? 1 : 0);
I2C_DELAY();
scl_set(1);
I2C_DELAY();
scl_set(0);
I2C_DELAY();
}
}
1バイトの受信
SDAをHIGHにプルアップした状態で、SCLの立ち上がり時にビットをサンプリングします。
uint8_t i2c_read_byte(void) {
uint8_t i, data = 0;
sda_input(); // 受信用に設定
I2C_DELAY();
for (i = 0; i < 8; i++) {
scl_set(1);
I2C_DELAY();
if (sda_read()) {
data |= (0x80U >> i);
}
scl_set(0);
I2C_DELAY();
}
sda_output(); // 復帰
return data;
}