Home  |   방명록 2021-10-17 (일) 
Untitled Document
  S e a r c h
M e n u
프로그램
개발자료
고전음악실
대청마루
집주인
비망록
갤러리
  B o a r d
게시판
Untitled Document
개발자료 Software / Hardware 개발 관련 정보

I2C : Inter IC Bus

I2C Bus

 

Inter-IC Bus (= TWI: Two-wire Serial Interface)

Philips에서 제안한 IC간 통신 방식으로 클럭(SCL)과 데이터(SDA)의 2라인을 사용하는 동기 양방향 2선식 Bus이다. 버스에 연결된 각 디바이스는 고유의 어드레스를 갖으며 필요에 따라 receiver와 transmitter로 동작한다. 전송속도는 standard mode에서 100kbps까지 이고 fast mode에서는 400kbps까지 가능하다.

transmitter: 버스에 데이타를 보내는 장치
receiver:     버스로부터 데이타를 받는 장치
master:       전송을 개시하는 장치로 클럭 펄스를 만들고, 전송을 종료하는 일을 한다.
slave:          master가 어드레싱한 장치

master가 start condition을 만들면 버스에 연결된 슬레이브 장치들이 이후의 데이터를 기다린다. master가 slave 어드레스를 보내면 각 칩은 자신의 고유 어드레스와 비교를 한다. 어드레스가 일치하는 칩은 이어지는 acknowledge 신호 구간에서 데이터를 low로 유지해 응답을 보낸다. 그러면 master는 데이터를 읽거나 쓰기를 할 수 있다. 모든 작업이 끝나면 master는 stop condition을 발생하고 버스를 release한다.

SDA H -\      /---\     /---\          /---\         /---\      /---
    L   \----/     \---/     \--------/     \-------/     \----/

SCL H ----\     /-\       /-\     /-\    /-\    /-\    /-\    /------
    L      \---/   \-----/   \---/   \--/   \--/   \--/   \--/


   | START   |   1    |    1    |   0  |  1   |  0   |   1   | STOP

* start condition
클럭과 데이터가 모두 high인 일반 상태에서 데이타 라인이 low로 떨어지는 상태.

SDA H ------\
    L        \-------
SCL H ---------\
    L           \----

* stop condition
클럭이 high인 동안에 데이타 라인이 low에서 high로 바뀌는 상태.

SDA H           /----
    L ---------/
SCL H        /-------
    L ------/

* 일반 데이터 전송에서는 클럭이 low일 때만 데이타의 상태를 바꿀 수 있다. 따라서 클럭이 high인 구간에만 유효한 데이타이다. master는 먼저 데이타 라인을 적당한 데이타로 바꾸고 클럭 라인을 일정시간 high로 하고 다음 SDA 라인의 상태를 바꾸기 전에 클럭을 low로 만든다. 데이타는 8bit이고 MSB가 먼저 전송된다. start 와 stop condition 사이에 전송되는 데이타의 수는 제한이 없다.

SDA H       /----------\
    L -----/\----------/\-------
SCL H          /----\
    L --------/      \----------

* acknowledge 조건
master든 slave든 transmitter는 8 bit 전송을 마치고 버스선을 high로 한다. 9번째 사이클 동안 receiver는 데이타 선을 low로 유지하여 acknowledge 를 표시한다.
핀를 open drain으로 하고 외부에 풀업 저항을 달기 때문에 기본 값은 high 상태이다. 인식 신호인 ACK 는 low 로 정해야 한다.
아무 변화가 없는 high 상태가 NACK 가 된다.
* slave 장치가 바쁜 경우는 ACK 로 응답하지 않을 수 있다. 그 때는 STOP condition을 주고 이후에 재시도 한다.

write시

한 byte write시

S

slave address

A

data write

A

P

여러 byte write시

S

slave address

A

data write

A

data write

A

...

data write

A

P

Note : S = start condition
          A = Wait for Acknowledge (master <-- slave)
          P = stop condition

read시

slave address를 보낸후 SDA를 놓아주면 slave가 8bit 데이터를 보내고 master는 ack신호를 slave에게 보내준다.

한 byte reading

S

slave address

WA

read byte

GNA

P


여러 byte reading

S

slave address

WA

read byte

GA

...

read byte

GNA

P

Note: WA = Wait for Acknowledge (master <-- slave)
          GA = Give Acknowledge (master --> slave)
GNA = Give Acknowledge with NACK (master --> slave) : end of reading

master 와 slave 간 handshake

slave 장치는 address 매치 및 데이터 바이트 수신시 ACK 신호를 보내준 이후에 내부 처리 시간이 필요한 경우는 clock 라인을 low 로 유지하여 master 장치가 다음 바이트를 송신하지 않고 대기하도록 할 수 있다. (Clock stretching)

I2C bus reset (I2C bus recovery)

I2C는 state machine으로 동작하는 구조라서 노이즈 등으로 master와 slave 의 state가 서로 어긋날 경우 더이상 동작하지 않게 된다. 보통 이런경우 slave가 SDA 라인을 계속해서 0으로 붙잡고 있게 된다. 이런 경우는 I2C버스를 리셋시켜 준다. 최대 9비트의 데이터를 만들어 주고 마지막에 STOP condition을 줘서 결국 slave의 state machine을 초기화 시켜주게 된다.
from Analog Device’s Application note AN-686 “Implementing an I2C Reset”
1) Master tries to assert a Logic 1 on the SDA line
2) Master still sees a Logic 0 and then generates a clock pulse on SCL (1-0-1 transition)
3) Master examines SDA. If SDA = 0, go to Step 2; if SDA = 1, go to Step 4
4) Generate a STOP condition

간단하게 GPIO 모드로 클럭을 11 만들어 주고 마지막에 STOP condition 을 보내서 i2c 버스를 초기화 한다.

I2C software driver pseudo code

#define SDA_HIGH()			{ ... your code here ... } // SDA pin high
#define SDA_LOW()			{ ... your code here ... } // SDA pin low
#define SCL_HIGH()			{ ... your code here ... } // SCL pin high
#define SCL_LOW()			{ ... your code here ... } // SCL pin low
#define SDA_OUTPUT()			{ ... your code here ... } // SDA pin output mode
#define SDA_INPUT()			{ ... your code here ... } // SDA pin input mode
#define SDA_READ()			{ ... your code here ... } // SDA pin read

#define i2c_delay()			delay_us(5)		// delay time for 100khz clock

void i2c_start(void)
{
	SDA_HIGH(); SCL_HIGH(); i2c_delay();
	SDA_LOW(); i2c_delay();
	SCL_LOW(); i2c_delay();
}

void i2c_stop(void)
{
	SDA_LOW();
	SCL_HIGH(); i2c_delay();
	SDA_HIGH(); i2c_delay();
}

unsigned char i2c_write(unsigned char data)
{
	unsigned char	i, ack;

	for(i=0; i<8; i++)
	{
		if(data & 0x80)	 SDA_HIGH();	// MSB first
		else		SDA_LOW();
		i2c_delay(); 

		SCL_HIGH(); i2c_delay();
		SCL_LOW(); i2c_delay();
		data = data << 1;
	}

	// read ACK
	SDA_HIGH();		// leave SDA HI
	SDA_INPUT();		// change direction to input on SDA line

	SCL_HIGH();		// clock back up
	i2c_delay();
	ack = SDA_READ();		// get the ACK bit
	SCL_LOW();
	SDA_OUTPUT();		// change direction back to output
	SCL_LOW();
	i2c_delay();

	return ack;
}

unsigned char i2c_read(void)
{
	unsigned char	i, data;

	SDA_HIGH();		// leave SDA HI
	SDA_INPUT();		// change direction to input on SDA line

	data = 0;
	for(i=0; i<8; i++)
	{
		data = data << 1;	// MSB first

		SCL_HIGH();	// clock HI
		i2c_delay();
		if(SDA_READ())	// get the Data bit
			data |= 1;
		SCL_LOW();	// clock LO
		i2c_delay();
	}

	SDA_OUTPUT();		// change direction back to output

	// send ACK
	SDA_LOW(); i2c_delay();

	SCL_HIGH(); i2c_delay();
	SCL_LOW(); i2c_delay();
	SDA_HIGH();

	return data;
}

unsigned char i2c_read_nack(void)
{
	unsigned char	i, data;

	SDA_HIGH();		// leave SDA HI
	SDA_INPUT();		// change direction to input on SDA line

	data = 0;
	for(i=0; i<8; i++)
	{
		data = data << 1;	// MSB first

		SCL_HIGH();	// clock HI
		i2c_delay();
		if(SDA_READ())	// get the Data bit
			data |= 1;
		SCL_LOW();	// clock LO
		i2c_delay();
	}

	SDA_OUTPUT();		// change direction back to output

	// send NACK
	SDA_HIGH(); i2c_delay();

	SCL_HIGH(); i2c_delay();
	SCL_LOW(); i2c_delay();
	SDA_HIGH();

	return data;
}

EEPROM read/write sample (AT24C32)

#define EEP_SLAVE_ADR			0xA0
#define TWI_WRITE				0x00
#define TWI_READ				0x01
#define TWI_ACK					0
#define TWI_NACK				1
#define TWI_ERROR				TWI_NACK


void eep_write(unsigned short addr, unsigned char data)
{
	i2c_start();
	i2c_write(EEP_SLAVE_ADR|TWI_WRITE);		// slave address
	i2c_write(addr>>8);					// word address high
	i2c_write(addr & 0xFF);				// word address low

	i2c_write(data);
	i2c_stop();

	delay_ms(6);						// 5ms EEPROM write cycle time
}


unsigned char eep_read(unsigned short addr)
{
	unsigned char	data;

	i2c_start();
	i2c_write(EEP_SLAVE_ADR|TWI_WRITE);		// slave address
	i2c_write(addr>>8);					// word address high
	i2c_write(addr & 0xFF);				// word address low

	i2c_start();
	i2c_write(EEP_SLAVE_ADR|TWI_READ);		// slave address with READ
	data = i2c_read_nack();
	i2c_stop();

	return data;
}

2004-06-14 [조회: 32767]

이전글: CRC : CRC-16, CRC-32에 대한 설명과 구현
다음글: SoftICE : SoftICE의 간단한 사용법

목록보기
조원
안녕하세요. 글 잘 봤습니다. 혹시 I2C bus reset (I2C bus recovery)과(와) 관련해서 참고하신 메뉴얼이나 문서를 저도 좀 참고할 수 있을까요? 2018-09-17 16:30
×
jsy
본문에도 있지만 구글에서 AN-686 로 검색하시면 pdf 문서가 나옵니다. 2018-09-17 16:45
×
조원
죄송합니다... 띄엄띄엄 밨군요...
죄송하지만, 추가적으로 궁금한게 있는데요...;; 혹시 i2c 커널 드라이버 코드에서 i2cdev_ioctl_rdrw라는 함수에 대해서 아시나요? 2018-09-17 17:45
×
 
이름 암호
(스팸 방지용)오늘의 날짜를 숫자만으로 입력하세요.(예: 12)

비밀번호
목록보기
 
Copyright ⓒ 2021 All Rights Reserved.