본문 바로가기
프로그래밍/Keil RTX

[C/Keil RTX] I2C 통신

by 채연2 2023. 1. 5.

 

 

 

 

임베디드 소프트웨어 개발을 하다보면 알기 싫지만 알아야 하는 많은 것들이 있다.

하기 싫지만 해야 하는 것이므로 오늘도 난 열심히 한다. 😂

 


I2C란?

I2C (Inter-Intergrated Circuit)2개의 선을 이용하는 직렬 통신 방식으로, 하나의 마스터와 여러 개의 슬레이브 장치가 통신하기 위한 프로토콜이다. 데이터 통신을 위한 선(SDA) 하나와 타이밍 동기화를 위한 선(SCL) 하나로 이루어져 있다. 이러한 특징 때문에 TWI (Two Wire Interface)라고도 불린다.

 

우선, I2C를 이해하기 위해서는 UART 통신과 SPI 통신을 알 필요가 있다.

※ UART 통신이란? (Universal Asyncronous Receiver/Transmitter)
비동기식 시리얼 통신으로 1:1 통신 방법이다. 클럭을 맞춰줘야 하고, 데이터 라인으로 들어오는 신호를 항상 주시해야 하므로 오버헤드가 있으며 하드웨어가 복잡해지는 단점이 있다.

※ SPI 통신이란? (Serial Peripheral Interface)
동기식 시리얼 통신으로 1:N 통신 방법이다. 클럭 라인을 이용해 데이터 라인을 동기화 하므로 하드웨어 구조도 간단하다.

 

I2C 통신은 SPI 통신의 단점들을 보완할 수 있는 동기식 시리얼 통신 방법이다. UART 통신처럼 단 두 라인만 사용하고 1008개의 슬레이브 장치를 지원한다. 또한 N:N 통신 지원이 가능하다. 하드웨어 요구사항은 SPI 보다 복잡하지만 UART 보다는 간단하고, 통식 속도면에서도 SPI와 UART 통신의 중간 쯤 된다.


I2C Interface Diagram

 

  • SDA (Serial DAta) : 데이터를 주고 받기 위한 선
  • SCL (Serial CLock) : 타이밍을 동기화 하기 위한 클럭 선

하나의 마스터와 하나 이상의 슬레이브로 이루어져 있으며, 이론적으로는 슬레이브는 최대 127개까지 연결 가능하다고 한다. SCL과 Data는 연결된 모든 Slave에게 전달이 된다. 하지만 해당 주소를 가진 디바이스만 응답하고 데이터를 주고 받는 형식으로 통신하게 된다.

 

Master에서 기준 클럭인 SCL을 생성하고, 이 클럭에 맞춰서 데이터를 SDA 라인을 통해 송신 및 수신을 하게 된다. 선이 하나이므로 송수신은 동시에 이뤄지지 않는다. 즉, 한 번에 송신, 수신 둘 중 하나만 할 수 있는 반이중 통식 방식이다. 데이터 송수신 여부는 마스터에서 주도하고, 데이터 송수신 시 반드시 슬레이브 주소를 마스터에 명시해 두어야 통신이 된다.

 

I2C 통신 속도는 100kHz, 400kHz, 3.4MHz를 주로 사용하며, 10kbps나 1Mbps를 사용하는 경우도 있다. 


I2C 통신 동작 방식

I2C 통신은 시작 신호데이터 신호, 정지 신호로 이루어진다. SDA와 SCL은 풀업 저항에 의해 기본적으로 HIGH 상태를 유지해줘야 한다. 그러다가 SDA 신호가 LOW로 떨어지게 되면 시작 신호(S)로 판단하게 된다.

 

한 클럭에 한 비트씩 데이터 신호를 만들고 모든 비트의 전송이 끝난 후 SCL 신호가 HIGH 신호가 되면 SDA 신호 역시 HIGH 신호로 만들어 정지 신호(P)를 만든다.

 

시작 신호 뒤에 나오는 첫 7비트는 슬레이브의 주소 값이어야 하며, 8번째 비트는 데이터를 읽어올지 또는 쓸지를 나타내는 신호 비트이다.


I2C 통신 Read / Write 동작

슬레이브의 주소 값과 읽기/쓰기 비트는 마스터에서 생성 가능하다.

 

Write의 경우 마스터에서 데이터를 생성해야 하고, Read의 경우 슬레이브에서 데이터를 생성 해야 한다. 8비트 데이터 전송 후 슬레이브에서 응답신호 (ACK)를 만들어 수신 확인을 해준다.

 

응답 신호는 기본적으로 LOW 여야 하며, 만일 슬레이브가 데이터를 전송하는 상태에서 모든 데이터 전송이 끝났을 경우 HIGH상태가 된다. 이를 NACK라고 하며, 이 외의 경우에 NACK 응답이 발생하는 경우는 통신 에러나 데이터 에러의 경우이다.

 

대부분의 칩에서 I2C 통신은 하드웨어 기능으로 구성되어 있어 I2C 관련 레지스터 비트를 설정하는 것만으로도 시작 신호, 데이터 신호, 클럭 신호, 정지 신호를 출력할 수 있다.


Keil에서의 I2C 통신 예제

 

I2C Init

#define I2C_CLK PC1
#define I2C_DAT PC0

CLK_EnableModuleClock(I2C0_MODULE);
SYS->GPC_MFPL = SYS_GPC_MFPL_PC1MFP_I2C0_SCL | SYS_GPC_MFPL_PC0MFP_I2C0_SDA

GPIO_SetMode(PC, BIT0, GPIO_MODE_OPEN_DRAIN);/* I2C_CLK */
GPIO_SetMode(PC, BIT1, GPIO_MODE_OPEN_DRAIN);/* I2C_DAT */
I2C_CLK = 1;
I2C_DAT = 1;

 

I2C Start

/* I2C start sequence is defined 
 * as a High to Low Transition on the data line 
 * as the CLK pin is high */
 
I2C_DAT = 1;
I2C_CLK = 1;
os_dly_wait(I2C_DELAY_TIME);

I2C_DAT = 0;
I2C_CLK = 0;
os_dly_wait(I2C_DELAY_TIME);

 

I2C Stop

/* I2C stop sequence is defined as
 * data pin is low, then CLK pin is high,
 * finally data pin is high. */
     
I2C_DAT = 0;
I2C_CLK = 1;
I2C_DAT = 1;

 

I2C Write

/* An I2C output byte is bits 7-0 (MSB to LSB).
 * Shift one bit at a time to the MDO output,
 * and then clock the data to the I2C Slave */
 
unsigned char i, temp;

/* Write to slave */
for (i = 0; i < 8; i++) {
    /* Send data bit */
    if ((data & 0x80) == 0x80) {
        I2C_DAT = 1;
    } else {
        I2C_DAT = 0;
    }

    data <<= 1;   /* Shift one bit */
    I2C_CLK = 1;  /* SCL: High */
    os_dly_wait(I2C_DELAY_TIME);

    I2C_CLK = 0; /* SCL: Low */
    os_dly_wait(I2C_DELAY_TIME);
}

I2C_DAT = 1;
I2C_CLK = 1; /* SCL: High */
os_dly_wait(I2C_DELAY_TIME);

temp = I2C_DAT; /* Read ACK bit from slave */
os_dly_wait(I2C_DELAY_TIME);

I2C_CLK = 0; /* SCL: Low */
os_dly_wait(I2C_DELAY_TIME);

 

I2C Read

unsigned char i, data;
data = 0x00;

/* Read from slave */
for (i = 0; i < 8; i++) {
    data <<= 1;			/* Shift one bit */
    data |= I2C_DAT;	/* Read data bit */
    I2C_CLK = 1;		/* SCL: High */
    os_dly_wait(I2C_DELAY_TIME);

    I2C_CLK = 0; 		/* SCL: Low */
    os_dly_wait(I2C_DELAY_TIME);
}

/* Send ACK bit to slave */
I2C_DAT = send_ack;
I2C_CLK = 1; 			/* SCL: High */
os_dly_wait(I2C_DELAY_TIME);

I2C_CLK = 0; 			/* SCL: Low */
os_dly_wait(I2C_DELAY_TIME);

 

 

 

 

320x100

댓글