I2C가 뭔지 헤메다가 몇자 적는다.

용어적 설명부터

I²C  는 필립스에서 개발한 직렬 컴퓨터 버스이며 마더보드, 임베디드 시스템, 휴대전화등에 저속의 주변 기기를 연결하기 위해 사용된다. I²C 라는 이름은 $2의 약자이며 아이-스퀘어-씨 라고 발음한다.

I²C 는 풀업 저항이 연결된 직렬 데이터(SDA)와 직렬 클럭(SCL)이라는 두 개의 양 방향 오픈 컬렉터 라인을 사용한다. 최대 전압은 +5 V 이며, 일반적으로 +3.3V 시스템이 사용되지만 다른 전압도 가능하다.

I²C 레퍼런스 디자인은 7 비트의 주소 공간을 가지며, 이 중 16개는 예약되어 있으므로, 동일한 버스에 최대 112개의 노드를 연결할 수 있다. 가장 일반적으로 사용되는 I²C 버스의 모드는 표준 모드인 100 kbit/s와, 저속 모드인 10 kbit/s 가 사용된다. 최신 리비전의 I²C 는 보다 빠르게 동작하며, 패스트(fast) 모드인 400 kbit/s와 고속(high-speed) 모드인 3.4 Mbit/s를 지원한다. 최대 1008 노드까지 연결 가능한 10 비트 주소 지정 등의 확장된 기능들을 지원한다.

란다.. 휴!!~~ 처음엔 이 용어 부터도 너무 어려웠다.
우선 용어부터 풀어보자면 필립스에서 개발한 직렬 통신 프로토콜이다?? 뭐 그다지 풀어야 할 내용은 없는데 필립스 에서 개발했다는것만 기억할련다 ^^ 또한 I2C (아이 투 씨), 아이-스퀘어-씨 라고 발음 한다고 한다. 또한 위에서 언급되었듯이 직렬 데이터(SDA)와 직렬 클럭(SCL)이라는 라인을 이용해 통신을 하게 된다. 전압으로 최대 5V 를 이용될수 있으며 3.3V이 보통 사용되지만 다른 전압도 가능하다고 되어 있다. 뭐 SDA 와 SCL 이라는 두 라인으로 I2C 통신을 한다는 말이다.

"SDA라 읽컫는 라인과 SCL 이라 일컫는 라인을 통해 통신을 한다."

또한 I2C는 마스터 모드와 슬레이브 모드 두개가 존재하며 마스터가 슬레이브를 호출하는 형태의 통신이다.

각 슬레이브는 각각 주소(7비트)로 구분되어 지며 112개의 슬레이브가 존재할수 있다.














 I2C 데이터 전송

I2C 데이터 전송 방법은

  • 데이터 전송 시작 : START
  • 데이터 전송지 주소 : Address
  • 데이터 속성 (읽기/쓰기) : R/W
  • 데이터 : Data
  • 데이터 수신 여부 : Ack
  • 데이터 전송 종료 : STOP


즉 데이터 전송시작(START) -> 주소(Address) -> 읽기 또는 쓰기(R/W) ->상대편 수신여부->데이터 -> 상대편 수신여부(Ack) -> 종료 (STOP ) 형태로 데이터를 보내주면 되는 것이다.






 시작 : 1비트
 주소 : 7비트
 읽기 또는 쓰기 : 1비트
 데이터 : 8비트
 수신 여부 : 1비트
 종료 : 1비트

뭐 이런 형태라고 책에는 나와 있는데 처음에는 많이 헤멨다. 저게 무슨 말인지 ㅡㅡ;;;
START와 STOP 은 어떻게 구분하지 라는 의문도 들었다.

뭐 찾아보니 HIGH, LOW 변화로 START 와 STOP 을 구분하게 된다는 설명이다.
START는 SCL 선이 HIGH를 유지하는 상태에서 SDA선이 HIGH에서 LOW으로 변화되는 시점을 시작 이라는 시점으로 판단한다는 거다.
STOP은 SCL선이 HIGH일때, SDA선이 LOW에서 HIGH로 올라가는 시점을 중지 라는 시점으로 판단한다.
그럼 데이터는 ??? 데이터는 SCL이 HIGH에서 LOW로 떨어지는 순간 SDA의 상태(HIGH인지, LOW인지)를 판단하여 1, 0 으로 판단한다고 한다. 모르겠다 ㅡㅡ;;; 그림을 보자.

















자, 그림으로 보면 SCL이 HIGH인 순간 SDA의 상태를 보고 1, 0을 판단하고 있다.
비트전환 이라고 해서 SDA의 값의 전환을 주기 위한 시간도 있다. ㅋㅋㅋ 뭐 대충 알았다. 아니 그냥 알아 들었다고 하고 그냥 어떻게 코드로 표현하는지 보자.

#define SDA_IN          PINC_Bit4
#define SDA_OUT         PORTC_Bit4
#define SDA             PORTC_Bit4
#define SCL             PORTC_Bit5

/*------------------------------------------------------------------------------------------------*/
/*      I2CStart                                                                  */
/*              _____         ________                                                              */
/*      SDA:       |_______|                                                                      */
/*                  ___     ___                                                                     */
/*      SCL:  ___|   |___|   |______                                                              */
/*------------------------------------------------------------------------------------------------*/
void I2CStart(void)
{
    DDRC = 0xFC; // SDA OUTPUT MODE
    SDA_OUT=1;
    USEC_Delay(1);
    SCL=0; USEC_Delay(2);
    SCL=1; USEC_Delay(1);
    SDA_OUT=0; USEC_Delay(1);
    SCL=0; USEC_Delay(2);
    SCL=1; USEC_Delay(1);
    SDA_OUT=1; USEC_Delay(1);
    SCL=0; USEC_Delay(2);
}

위 코드는 I2C 통신을 하기 위해서 START를 보내는 부분이다. START의 판단 조건은 SCL이 HIGH인 상태에서 SDA가 HIGH에서 LOW로 내려가는 시점이라고 했다.
SDA_OUT=1 // SDA가 HIGH 유지
Delay(1)
SCL=0 ; Delay(2); // SCL이 LOW
SCL=1; Delay(1); // SCL을 HIGH로 변환

SDA_OUT=0; Delay(1); // SDA를 HIGH에서 LOW로 변환
SCL=0; Delay(2); // SCL을 LOW로
SCL=1; Delay(1); // SCL을 HIGH로 변경
SDA_OUT=1; Delay(1) // SDA를 HIGH
SCL=0;Delay(2); // SCL을 LOW로..

BYTE I2CWrite_Data(BYTE val)
{
    BYTE i = 0;
    BYTE error = 0;
    DDRC = 0xFC;
    USEC_Delay(1);
    for(i=0x80; i>0; i/=2)
    {
        if (i & val) SDA_OUT = 1;      
        else SDA_OUT = 0;
        USEC_Delay(1);
        SCL = 1; USEC_Delay(1);
        SCL = 0; USEC_Delay(1);
    }
    SDA_OUT = 0; USEC_Delay(1);
    SCL = 1;
    error = SDA_IN; USEC_Delay(1);
    SCL = 0; USEC_Delay(1);
    SDA_OUT = 1; USEC_Delay(1);
    return error;
}
BYTE I2CRead_Data(BYTE ACKn)
{
    BYTE i,val=0;
    DDRC = 0xEC;
    for (i=0x80;i>0;i/=2)
    {
        SCL=1;
        if (SDA_IN) val=(val | i); USEC_Delay(1);
        SCL=0; USEC_Delay(1);
    }
    DDRC = 0xFC;
    SDA_OUT=!ACKn;
    SCL=1; USEC_Delay(1);
    SCL=0; USEC_Delay(1);
    SDA_OUT=1;
    return val;
}
void I2CStart(void)
{
    DDRC = 0xFC; // SDA OUTPUT MODE
    SDA_OUT=1;
    USEC_Delay(1);
    SCL=0; USEC_Delay(2);
    SCL=1; USEC_Delay(1);
    SDA_OUT=0; USEC_Delay(1);
    SCL=0; USEC_Delay(2);
    SCL=1; USEC_Delay(1);
    SDA_OUT=1; USEC_Delay(1);
    SCL=0; USEC_Delay(2);
}
void I2CStop(void)
{
    DDRC = 0xFC; // SDA OUTPUT MODE
    SDA_OUT=0;
    USEC_Delay(1);
    SCL=0; USEC_Delay(2);
    SCL=1; USEC_Delay(1);
    SDA_OUT=1; USEC_Delay(1);
    SCL=0; USEC_Delay(2);
}
void main()
{
 Data = 0x40;
 I2CStart();      // START
 I2CWrite_Data( 0x07 ) // Address (3)
 I2CRead_Data(1)    // ACK
 I2CWrite_Data(Data); // Data
 I2CRead_Data(1);   // ACK
 I2CStop();      // STOP
 
}


위 코드는 슬레이브(0x03, 000 0011) 에게 쓰기 모드로 데이터 0x40을 전송하는 코드이다.

부랴부랴 이해했던 내용을 작성하려다 보니 내용상 허술하기도 하고 부실하기도 하다.

'프로그래밍 > 마이컴' 카테고리의 다른 글

ATMEGA32L External Interrupt  (0) 2013.08.14

+ Recent posts