노무현 대통령 배너


2008. 7. 9. 14:13

I2C 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 를 표시한다.

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

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_OUPUT()			{ ... 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
#define i2c_clock()			{ i2c_delay(); SCL_HIGH(); i2c_delay(); SCL_LOW(); }
#define i2c_start()			{ SDA_HIGH(); SCL_HIGH(); i2c_delay(); SDA_LOW(); i2c_delay(); SCL_LOW(); }
#define i2c_stop()			{ SCL_LOW(); SDA_LOW(); i2c_delay(); SCL_HIGH(); i2c_delay(); SDA_HIGH(); i2c_delay(); }


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

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

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

	i2c_delay();
	SCL_HIGH();							// clock back up
	i2c_delay();
	i = SDA_READ();						// get the ACK bit
	SCL_LOW();

	SDA_OUPUT();						// change direction back to output

	return i;
}


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++)
	{
		// MSB first
		data = data << 1;

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

	SDA_OUPUT();						// change direction back to output

	// send ACK
	SDA_LOW();
	i2c_clock();
	SDA_HIGH();							// leave with SDA HI

	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++)
	{
		// MSB first
		data = data << 1;

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

	SDA_OUPUT();						// change direction back to output

	// send NACK
	SDA_HIGH();
	i2c_clock();
	SDA_HIGH();							// leave with SDA HI

	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;
}