※ 주의사항 ※
본 블로그는 수업 내용을 바탕으로 제가 이해한 부분을 정리한 블로그입니다.
본 내용을 참고로만 보시고, 틀린 부분이 있다면 지적 부탁드립니다!
감사합니다😁
안녕하세요!!
오늘은 아래와 같은 내용을 확인해보겠습니다.
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)
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
- Master의 SCL 주파수를 구할때 쓰는 BitRate를 저장하는 레지스터
- Master의 SCL 주파수를 산출하기 위해서는 아래와 같은 공식을 사용한다.
- TWBR이 72인 경우 SCL은 100,000hz(100Khz)에 해당된다. (분주율은 1이라 가정)
2-2. TWSR 레지스터 : TWi Status Register
- TWPS 비트는 SCL 주파수를 산출하는데 필요한 분주율을 결정하는 비트
2-3. TWCR 레지스터 : TWi Control Register ✔중요
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
- 송수신되는 데이터를 저장하는 레지스터
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를 사용할때 사용하겠습니다.
댓글