본문 바로가기
대한상공회의소 스마트팩토리 교육/IoT 디바이스 개발

[IoT 디바이스 개발] AVR(ATmega128A)«수업-19» : I2C(Inter-Integrated Circuit) 통신

by 나는영하 2022. 2. 25.

※ 주의사항 

본 블로그는 수업 내용을 바탕으로 제가 이해한 부분을 정리한 블로그입니다.
본 내용을 참고로만 보시고, 틀린 부분이 있다면 지적 부탁드립니다!

감사합니다😁

 

안녕하세요!!

오늘은 아래와 같은 내용을 확인해보겠습니다.

 

I2C 통신

SDA & SCL

I2C 통신 오픈 라이브러리


1. I2C 통신 개요

1-1. I2C 통신 개요

1) 시리얼 통신 방식 비교

  UART SPI I2C
동기 / 비동기 비동기 동기 동기
전이중 / 반이중 전이중 전이중 반이중
연결 방식 1:1 1:n 1:n
연결선
개수
1개
슬레이브
연결
데이터 2 2 1(반이중)
클록 0 1 1
제어 0 1 0
합계 2 4 2
N개 슬레이브 연결 2n 3+n 2
슬레이브 선택 - 하드웨어
(SS라인)
소프트웨어
(주소 지정)

 

2) I2C 통신 특징

- 반이중 방식 : 송신과 수신을 위한 공통의 1개 데이터 선 사용(SDA : Serial Data)

- 1:n 통신 : Master - Slaver 구조

- 동기 통신 : 데이터 동기화를 위한 별도의 클록선 사용 (SCL : Serial Clock)

- 슬레이브 선택 : 소프트웨어 방식으로 슬레이브를 선택하기 위한 주소 사용 (컴퓨터의 IP 개념과 비슷)

- ATmega128A에서는 SDA를 위해 PD1번핀, SCL을 위해 PD0번핀을 사용하도록 지정

- SDA와 SCL에 각각 풀업저항을 연결하여 사용

 

1-2. I2C 주소 지정

- 1:n 통신에서 특정 슬레이브 선택을 위해 소프트웨어 방식의 주소 사용

- 주소는 7비트로 구성

 → '0000 000'은 마스터가 모든 슬레이브에게 동시에 메세지를 보내는 용도

- 나머지 1비트는 읽기/쓰기를 선택하기 위해 사용

 → 읽기(HIGH) : 슬레이브에서 전송되는 데이터를 마스터에서 읽어드림

 → 쓰기(LOW) : 마스터에서 슬레이브로 데이터를 전송

 

1-3. 데이터 송수신 방법 (SDA & SCL)

데이터 '01001110' 전송

1) 데이터 전송

- 데이터(SDA)는 클록(SCL)이 HIGH인 구간에서만 샘플링(데이터 전송) 가능

- 데이터(SDA)는 클록(SCL)이 LOW인 구간에서만 데이터 변경 가능(1 ↔ 0)

 

2) 데이터 시작 및 끝

- 데이터 시작 : SCL이 HIGH인 구간에서 SDA가 HIGH → LOW로 바뀜

- 데이터 끝 : SCL이 HIGH인 구간에서 SDA가 LOW → HIGH로 바뀜

 

3) I2C 데이터 전송 구조

START 7비트 주소 R/W ACK
NACK
1바이트 데이터 ACK
NACK
1바이트 데이터 ACK
NACK
STOP

- 쓰기와 읽기에 따라 ACK 신호나 1바이트 데이터 신호의 전송 주체가 바뀐다.

- START 신호, 7비트 주소, R/W신호, STOP 신호는 마스터 측에서 전송한다.

- 1바이트 데이터 송신 후 수신측에서 ACK신호 또는 NACK 신호를 전송하는데 ACK 신호는 정상적으로 데이터를 수신했음을 알려주는 신호이다. 송신측에서 데이터를 다 보냈으면 마지막에 STOP 즉 송신 종료 신호를 보내준다.

 


 

2. I2C 통신 관련 레지스터

2-1. TWBR 레지스터 : TWi Bit rate Register

TWBR 레지스터 구조

- Master의 SCL 주파수를 구할때 쓰는 BitRate를 저장하는 레지스터

- Master의 SCL 주파수를 산출하기 위해서는 아래와 같은 공식을 사용한다.

- TWBR이 72인 경우 SCL은 100,000hz(100Khz)에 해당된다. (분주율은 1이라 가정)

 

2-2. TWSR 레지스터 : TWi Status Register 

TWSR 레지스터 구조

- TWPS 비트는 SCL 주파수를 산출하는데 필요한 분주율을 결정하는 비트

TWPS0, TWPS1 비트 값에 따른 분주율

 

2-3. TWCR 레지스터 : TWi Control Register ✔중요

TWCR 레지스터 구조

1) TWINT(7번 비트) : TWI Interrupt Flag

- TWI의 현재 작업이 완료되고 응용 소프트웨어의 응답을 기다릴때 해당 비트가 세트된다

- SREG 레지스터의 I비트(전역 인터럽트)가 세트되고 TWCR 레지스터의 TWIE가 세트되어 있을때 현재작업이 완료되고 TWINT 비트가 세트되면 ISR을 실행한다.

 

2) TWEA(6번 비트) : TWI Enable Acknowledge Bit

- ACK 펄스의 생성을 제어한다. 본 비트가 세트되어 있을때 아래와 같은 상황에서 ACK 펄스 생성

→ 슬레이브 모드에서 자신의 주소를 수신하고 나서

→ 슬레이브 모드에서 TWAR 레지스터의 TWGCE 비트가 세트되어 있고 슬레이브로 전달되는 일반 호출을 수신한 경우

→ 마스터나 슬레이브 모드에서 한바이트 데이터를 수신한 경우★

 

3)TWSTA(5번 비트) : TWI Start Condition Bit

- 마스터 모드에서 전송을 시작하기 위해 해당 비트 세트

 

4) TWSTO(4번 비트) : TWI Stop Condition Bit

- 마스터 모드에서 전송을 종료하기 위해 해당 비트 세트

 

5) TWWC(3번 비트) : TWI Write Collision Flag

- TWINT 비트가 0인경우 즉 데이터 전송 작업이 완료되지 않은 경우 TWDR 레지스터에 데이터를 기록하려고 하면 세트된다.

-TWINT 비트가 1인 경우 TWDR 레지스터에 데이터를 기록하면 클리어 된다

 

6) TWIE(0번 비트) : TWI Interrupt Enable

- TWI 데이터 전송이 완료된 경우 인터럽트 발생을 허용한다

(SREG레지스터의 I비트가 세트되고, TWIE 비트가 세트된 경우 전송이 완료되면 인터럽트 발생)

 

2-4. TWDR 레지스터 : TWi Data Register

TWDR 레지스터 구조

- 송수신되는 데이터를 저장하는 레지스터

 


 

3. I2C 통신으로 LCD Display 출력 실습 - ①

- ATmega128A SCL/SDA 핀 → I2C 8574(PCF 8574T 칩)  → LCD Display(HD44780 칩) 경로를 통해 ATmega128내의 I2C 통신으로 LCD Display에 문자열을 출력하는 실습을 2개의 글로 나누어서 실습해보겠습니다. 오늘은 ATmega128A에서 SCL/SDA핀에서 PCF 8574T 칩으로 데이터를 보내는 과정이 담긴 오픈 라이브러리를 해석하고 정리해보겠습니다. 😁

 

3-1. I2C 헤더 파일

#ifndef I2C_H_
#define I2C_H_

#define I2C_READ 0X01
#define i2C_WRITE 0X00
typedef unsigned char uint8_t;
typedef unsigned int uint16_t;
void i2c_init(void);
void i2c_start(uint8_t adress);
void i2c_transmit(uint8_t data);
uint8_t i2c_receive_ack(void);
uint8_t i2c_receive_nack(void);
void i2c_transmit_nbytes(uint8_t adress, uint8_t* data, uint16_t length);
void i2c_receive_nbytes(uint8_t adress, uint8_t* data, uint16_t length);
void i2c_stop(void);

#endif

- 7비트 주소 뒤에 읽기/쓰기를 선택하기 위해 I2C_READ와 I2C_WRITE에 해당 비트 지정

- I2C 초기화, I2C 시작, I2C 1바이트 데이터 전송, I2C ACK 및 NACK 신호 전송, I2C n바이트 데이터 전송, I2C 수신, I2C 전송 종료 등 사용자 정의 함수 선언

 

3-2. I2C 소스 파일

※ 자주 사용되고 필요한 함수부분만 발췌해서 간단한 설명을 하겠습니다.

1) I2C 초기화 함수

void i2c_init(void)
{
	PORTD |= (1 << 0 )| (1 << 1);
	TWBR = (uint8_t)TWBR_val;
}

- ATmega128A의 SCL핀(PD0)과 SDA핀(PD1)을 출력으로 설정

- TWBR레지스터의 값을 지정해주면서 I2C 통신에서 사용될 SCL의 클록 설정 

 

2) I2C 시작 함수

void i2c_start(uint8_t adress)
{
	TWCR = (1 << TWINT) | (1<<TWSTA) | (1<<TWEN);
	while( !(TWCR & (1<<TWINT)));
	
	TWDR = adress;
	TWCR = (1<<TWINT) | (1<<TWEN);
	
	while (! (TWCR&(1<<TWINT)));
}

- I2C 통신을 시작하는데 필요한 TWCR 레지스터의 3개 비트 TWINT, TWSTA, TWEN을 세트한다.

- TWI의 작업이 완료된경우(즉, 대기상태인경우)에 I2C 통신을 시작한다(TWINT 비트 세트)

- 함수 호출하는 곳에서 인자를 전달받아 7비트의 슬레이브 주소와 읽기/쓰기 비트를 전달하는 과정

 

3) I2C 1바이트 전송 함수

void i2c_transmit(uint8_t data)
{
	TWDR = data;
	TWCR = (1<<TWINT) | (1<<TWEN);
	
	while(!(TWCR & (1<< TWINT)));
}

- TWDR 레지스터에 데이터를 저장하고 TWINT 비트가 세트되면 슬레이브에 송신한다.

 

4) I2C n바이트 전송 함수

void i2c_transmit_nbytes(uint8_t adress, uint8_t* data, uint16_t length)
{
	i2c_start(adress | i2C_WRITE);
	for(uint16_t i =0; i<length; i++)
	{
		i2c_transmit(data[i]);
		_delay_ms(1);
	}
	i2c_stop();
}

- 슬레이브 7비트 주소에 0x00을 더할 값으로 I2C 시작함수를 호출한다

- 데이터의 바이터 크기에 따라 for문을 수행하는데 I2C_transmit 함수를 통해 1바이트씩 전송한다. 

 

※ 추후 I2C.h와 I2C.c파일을 라이브러리로 저장해서 I2C 통신이나 LCD Display를 사용할때 사용하겠습니다.

댓글