I2C 프로토콜 사양 문서
THE I2C-BUS SPECIFICATION VERSION 2.1 JANUARY 2000: https://www.csd.uoc.gr/~hy428/reading/i2c_spec.pdf I2C(Inter-IC)는 종종 IIC 또는 TWI(Two-Wire Interface)로도 불리며, 일부 표준에서는 SMBus와 유사한 하위 프로토콜로 분류된다.
전기적 기초
이더넷은 단순히 데이터선(SDA)과 클럭선(SCL) 두 개로 구성되며, 각각에 상향 저항(보상 저항)을 연결해야 한다. 이는 오픈 드레인(Open Drain) 회로를 사용하기 때문이다.
오픈 드레인의 작용:
- 출력/입력 모드에서 저전압 또는 고저항 상태(개방 상태)를 생성 가능
- 고전압은 외부 상향 저항을 통해 발생하며, 저전압 상태에서는 드라이버가 직접 낮은 전압을 강제
- 결과적으로 동일한 선 상에서 다수의 장치가 공유 가능하고, 반복적인 제어 신호를 처리할 수 있음
표준 전원 수준은 1.8V, 3.3V, 5V이며, 상향 저항 값은 3.3KΩ ~ 10KΩ 범위로 선택되며, 전송 속도와 부하 용량에 따라 조정됨. 다른 전압 수준 간 호환성 설계도 표준에 포함되어 있다.
연결 방식
모든 슬레이브 장치의 SDA 핀은 마스터 쪽으로 병렬 연결되고, 모든 슬레이브의 SCL 핀 역시 마스터의 클럭 신호에 연결된다.
시퀀스 특징
- 대기 상태: SCL과 SDA 모두 고전압
- 전송 중: 클럭 펄스가 고전압일 때 데이터 라인이 유효
- 시작 신호: SDA 하강 에지
- 종료 신호: SDA 상승 에지
공동 충돌 처리:
- 한쪽이 저전압을 출력하면 전체 선이 낮아짐
- 동일한 클럭 신호를 공유하므로 모든 장치는 동기화됨
- 데이터 불일치 감지 시 통신 중단 가능
FPGA 기반 구현 시, 표준의 Table 5 및 Fig. 31의 타이밍 요구사항을 반드시 준수해야 함.
주요 전송 모드
전송 속도: 0100kHz (표준), 400kHz3.4MHz (고속)
다양한 속도 모드의 장치가 혼합된 시스템에서도 동작 가능하며, 속도는 자동으로 협상됨.
고속 모드 활성화 시, 관리 코드(관리 바이트)를 사용하여 인식 가능.
주소 형식은 7비트 또는 10비트로 구분되며, 대부분의 경우 7비트 사용.
7비트 주소 후에는 읽기/쓰기 비트가 추가됨.
일반 속도에서의 데이터 전송 (7비트 주소 모드)
데이터 흐름은 다음과 같은 구조로 표현 가능:
장치 주소 등록 주소 데이터
- 장치 주소: 7비트 + 읽기/쓰기 비트 (총 8비트)
- 등록 주소: 8비트 단위로 지정 (예: 16비트 주소 사용 시 두 바이트 전송)
- 실제 데이터: 8비트 단위로 전송 가능
- 각 바이트마다 슬레이브로부터 응답(ACK/NACK) 필요
이 프로토콜은 근거리, 저속, 간단한 메시지 교환에 적합하며, 주로 등록 레지스터의 읽기/쓰기에 사용된다.
예: 16비트 등록 주소, 32비트 데이터 쓰기
DEV_ADDR REG_ADDR0 REG_ADDR1 REG_DATA0 REG_DATA1 REG_DATA2 REG_DATA3
역할: 직관적인 데이터 구조와 컴퓨터 아키텍처의 레지스터 모델이 일치하여, 8/16/32/64비트 정수형 데이터와 잘 맞음.
디버깅 도구
리눅스 환경에서는 i2c-tools 패키지를 활용함.
-
i2cdetect: 총선에 연결된 장치의 주소 스캔 예:i2cdetect -y -r 0→ I2C 버스 0에서 읽기 모드로 스캔 (비표준 장치는 응답 없음)-r대신-q사용 시 빠른 쓰기 모드 스캔 가능 -
i2cdump: 특정 장치의 전체 레지스터 내용 읽기
팁: i2cdetect로 장치를 찾지 못했을 경우, 반복적으로 i2cdump를 실행해 각 주소에 응답이 있는지 확인 가능.
응답이 있는 주소는 해당 장치가 존재함.
i2cdetect가 응답하지 않는 문제 해결 시, 소스 코드 참조 가능:
https://github.com/mozilla-b2g/i2c-tools/blob/master/tools/i2cdetect.c#L50
→ 슬레이브가 SMBUG_READ 명령을 지원해야 함.
사례 참고: https://bbs.aw-ol.com/topic/2304/analysis-linux-i2c-tools-pitfalls
변종 프로토콜
I2C 기반으로 다양한 확장 프로토콜 존재:
- SMBus (System Management Bus)
- MDIO (Management Data Input/Output)
- I3C (Improved Inter-Integrated Circuit)
- MIPI I3C 등
모두 1개의 클럭선과 1개의 데이터선을 사용하며, 오픈 드레인 구조로 양방향 통신 가능. 병렬 연결이 가능하고, 하드웨어 호환성 유지.
리눅스 커널 I2C & SMBus 서브시스템
공식 문서
https://www.kernel.org/doc/html/v5.14/driver-api/i2c.html 경로: » The Linux driver implementer’s API guide » I2C and SMBus Subsystem SMBus는 I2C의 엄격한 하위 집합이며, 대부분의 경우 호환됨.
SoC 내장 I2C 컨트롤러용 커널 API
// 기본 전송 함수
int i2c_master_send(const struct i2c_client *client, const char *buf, int count);
int i2c_master_recv(const struct i2c_client *client, char *buf, int count);
// DMA 안정성 제공 버전
int i2c_master_send_dmasafe(const struct i2c_client *client, const char *buf, int count);
int i2c_master_recv_dmasafe(const struct i2c_client *client, char *buf, int count);
주요 구조체 정의
// 장치 식별 정보
struct i2c_device_identity {
u16 manufacturer_id;
u16 part_id;
u8 die_revision;
};
// 보드 정보 설정
struct i2c_board_info {
char type[I2C_NAME_SIZE];
unsigned short flags;
unsigned short addr;
const char *dev_name;
void *platform_data;
struct device_node *of_node;
struct fwnode_handle *fwnode;
const struct software_node *swnode;
const struct resource *resources;
unsigned int num_resources;
int irq;
};
// 시퀀스 시간 조건
struct i2c_timings {
u32 bus_freq_hz;
u32 scl_rise_ns;
u32 scl_fall_ns;
u32 scl_int_delay_ns;
u32 sda_fall_ns;
u32 sda_hold_ns;
u32 digital_filter_width_ns;
u32 analog_filter_cutoff_freq_hz;
};
총선 제어 관련 함수
void i2c_lock_bus(struct i2c_adapter *adapter, unsigned int flags);
int i2c_trylock_bus(struct i2c_adapter *adapter, unsigned int flags);
void i2c_unlock_bus(struct i2c_adapter *adapter, unsigned int flags);
전원 제어
void i2c_mark_adapter_suspended(struct i2c_adapter *adap);
void i2c_mark_adapter_resumed(struct i2c_adapter *adap);
특성 검사
bool i2c_check_quirks(struct i2c_adapter *adap, u64 quirks);
초기화 및 제거
int i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len);
struct i2c_client * i2c_verify_client(struct device *dev);
struct i2c_client * i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *info);
void i2c_unregister_device(struct i2c_client *client);
// 가상 장치 생성
struct i2c_client * i2c_new_dummy_device(struct i2c_adapter *adapter, u16 address);
struct i2c_client * devm_i2c_new_dummy_device(struct device *dev, struct i2c_adapter *adapter, u16 address);
// 보조 장치 생성
struct i2c_client * i2c_new_ancillary_device(struct i2c_client *client, const char *name, u16 default_addr);
struct i2c_adapter * i2c_verify_adapter(struct device *dev);
int i2c_handle_smbus_host_notify(struct i2c_adapter *adap, unsigned short addr);
int i2c_add_adapter(struct i2c_adapter *adapter);
void i2c_del_adapter(struct i2c_adapter *adap);
int i2c_add_numbered_adapter(struct i2c_adapter *adap);
int devm_i2c_add_adapter(struct device *dev, struct i2c_adapter *adapter);
void i2c_parse_fw_timings(struct device *dev, struct i2c_timings *t, bool use_defaults);
void i2c_del_driver(struct i2c_driver *driver);
슬레이브 장치 읽기/쓰기
int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
int i2c_transfer_buffer_flags(const struct i2c_client *client, char *buf, int count, u16 flags);
int i2c_get_device_id(const struct i2c_client *client, struct i2c_device_identity *id);
u8 *i2c_get_dma_safe_msg_buf(struct i2c_msg *msg, unsigned int threshold);
void i2c_put_dma_safe_msg_buf(u8 *buf, struct i2c_msg *msg, bool xferred);
u8 i2c_smbus_pec(u8 crc, u8 *p, size_t count);
s32 i2c_smbus_read_byte(const struct i2c_client *client);
s32 i2c_smbus_write_byte(const struct i2c_client *client, u8 value);
s32 i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command);
s32 i2c_smbus_write_byte_data(const struct i2c_client *client, u8 command, u8 value);
s32 i2c_smbus_read_word_data(const struct i2c_client *client, u8 command);
s32 i2c_smbus_write_word_data(const struct i2c_client *client, u8 command, u16 value);
s32 i2c_smbus_read_block_data(const struct i2c_client *client, u8 command, u8 *values);
s32 i2c_smbus_write_block_data(const struct i2c_client *client, u8 command, u8 length, const u8 *values);
s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, char read_write, u8 command, int protocol, union i2c_smbus_data *data);
s32 i2c_smbus_read_i2c_block_data_or_emulated(const struct i2c_client *client, u8 command, u8 length, u8 *values);
struct i2c_client * i2c_new_smbus_alert_device(struct i2c_adapter *adapter, struct i2c_smbus_alert_setup *setup);
리눅스 커널의 I2C/SMBus 아키텍처
슬레이브 장치 드라이버 작성
-
장치 생성 방법 (선택 가능):
-
장치 트리(Device Tree)
-
ACPI (X86 플랫폼용)
-
코드 내 생성:
i2c_new_client_device()호출 -
프로브 함수 기반:
probe()함수에서 동적 생성 (예:lm90_detect) -
사용자 공간:
/sys/bus/i2c/devices/i2c-3/new_device에 이름/주소 입력
I2C Core 계층
어댑터 (Controller Driver)
장치 드라이버 작성자는 보통 원급 제조사가 제공하는 것이며, 커널 내부에서 관리됨.
struct i2c_adapter {
struct module *owner;
unsigned int class;
const struct i2c_algorithm *algo;
void *algo_data;
const struct i2c_lock_operations *lock_ops;
struct rt_mutex bus_lock;
struct rt_mutex mux_lock;
int timeout;
int retries;
struct device dev;
unsigned long locked_flags;
int nr;
char name[48];
struct completion dev_released;
struct mutex userspace_clients_lock;
struct list_head userspace_clients;
struct i2c_bus_recovery_info *bus_recovery_info;
const struct i2c_adapter_quirks *quirks;
struct irq_domain *host_notify_domain;
struct regulator *bus_regulator;
struct dentry *debugfs;
};
알고리즘 인터페이스
struct i2c_algorithm {
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
int (*master_xfer_atomic)(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);
int (*smbus_xfer_atomic)(struct i2c_adapter *adap, u12 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data);
u32 (*functionality)(struct i2c_adapter *adap);
#if IS_ENABLED(CONFIG_I2C_SLAVE)
int (*reg_slave)(struct i2c_client *client);
int (*unreg_slave)(struct i2c_client *client);
#endif
};
장치 정보
struct i2c_client {
unsigned short flags;
unsigned short addr;
char name[I2C_NAME_SIZE];
struct i2c_adapter *adapter;
struct device dev;
int init_irq;
int irq;
struct list_head detected;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
i2c_slave_cb_t slave_cb;
#endif
void *devres_group_id;
};
장치 드라이버 인터페이스
struct i2c_driver {
unsigned int class;
int (*probe)(struct i2c_client *client, const struct i2c_device_id *id);
int (*remove)(struct i2c_client *client);
int (*probe_new)(struct i2c_client *client);
void (*shutdown)(struct i2c_client *client);
void (*alert)(struct i2c_client *client, enum i2c_alert_protocol protocol, unsigned int data);
int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
struct device_driver driver;
const struct i2c_device_id *id_table;
int (*detect)(struct i2c_client *client, struct i2c_board_info *info);
const unsigned short *address_list;
struct list_head clients;
};
드라이버 경로 구조
drivers/i2c/디렉터리 아래:i2c-core-base.c,i2c-core-of.c,i2c-core-acpi.c,i2c-core-slave.c,i2c-core-smbus.ci2c-dev.c: SoC I2C 컨트롤러의 문자 장치 드라이버i2c-mux.c: 다중화 I2C 채널 지원 드라이버i2c-smbus.c: SMBus 기능 구현i2c-stub.c: I2C 시뮬레이터i2c-slave-*: I2C 슬레이브 모드 시뮬레이션algos/: 오래된 컨트롤러 알고리즘 (PCF8584, PCA9564 등)busses/: 제조사별 I2C 컨트롤러 드라이버 (예:i2c-bcm2835.c,i2c-xiic.c)muxes/: 외부 다중화 IC 드라이버 (PCA9544 등)
요약
i2c-core는 컨트롤러 모델을 제공하여 하드웨어 독립성 확보- 제조사 드라이버(
drivers/i2c/busses)는 컨트롤러 IP에 맞춰 등록되는 파라미터만 설정 - 외부 장치 드라이버는
writing-clients.html가이드에 따라 작성 - 확장용 다중화 장치는
drivers/i2c/muxes사용 - SMBus는 별도의 API 계층 제공
예제
- SoC 컨트롤러 드라이버: AMD Zynq UltraScale TRM,
drivers/i2c/busses/i2c-xiic.c - 외부 장치 드라이버: TI DAC7571,
drivers/iio/dac/ti-dac5571.c