1. AVR128DB48 I2C 모듈 개요
Microchip의 AVR128DB48 마이크로컨트롤러는 두 개의 I2C(TWI: Two-Wire Interface) 모듈(TWI0, TWI1)을 제공하여 센서, 디스플레이, 메모리 장치 등과의 통신에 적합합니다. I2C 모듈은 마스터 및 슬레이브 모드를 지원하며, 저전력 애플리케이션과 높은 호환성을 제공합니다. 이 문서에서는 AVR128DB48의 TWI0 및 TWI1 설정 방법, Bitfield 구조를 활용한 레지스터 설정, 그리고 실용적인 예제 코드를 제공하여 초보자와 숙련된 개발자 모두 쉽게 활용할 수 있도록 돕습니다.
주요 사양
- I2C 채널:
- TWI0: SDA (PC2), SCL (PC3)
- TWI1: SDA (PF2), SCL (PF3)
- 지원 모드: 마스터, 슬레이브
- 클럭 주파수: 최대 400kHz (Fast-mode), 100kHz (Standard-mode)
- 주요 레지스터 (TWI0 및 TWI1 공통):
- TWIx.MCTRLA: 마스터 제어 레지스터
- TWIx.MBAUD: 마스터 보율 설정
- TWIx.MSTATUS: 마스터 상태
- TWIx.MDATA: 마스터 데이터
- TWIx.SCTRLA: 슬레이브 제어 레지스터
- TWIx.SADDR: 슬레이브 주소
- 인터럽트: 데이터 전송/수신, 에러, 충돌 감지
- 전력 관리: 저전력 모드에서 동작 지원
- 전압 레벨: 1.8V~5.5V 지원
- 기타: 외부 풀업 저항 (4.7kΩ 권장) 필수
2. I2C Bitfield 설정 상세
AVR128DB48의 I2C 모듈은 <avr/io.h> 헤더 파일을 통해 Bitfield 구조로 접근합니다. TWI0와 TWI1은 동일한 레지스터 구조를 사용하며, TWI0.* 또는 TWI1.*로 구분됩니다. 주요 레지스터는 다음과 같습니다:
2.1 TWIx.MCTRLA (마스터 제어 레지스터)
- bit.ENABLE: TWI 모듈 활성화 (1: 활성화)
- bit.SMEN: 스마트 모드 활성화
- bit.TIMEOUT: 타임아웃 설정
2.2 TWIx.MBAUD (마스터 보율 레지스터)
- 보율 설정: BAUD = ((F_CPU / (2 * F_SCL)) - 5)
- 예: 24MHz에서 100kHz 설정 시, BAUD = ((24000000 / (2 * 100000)) - 5) = 115
2.3 TWIx.MSTATUS (마스터 상태 레지스터)
- bit.BUSSTATE: 버스 상태 (Idle, Owner, Busy)
- bit.RIF: 읽기 인터럽트 플래그
- bit.WIF: 쓰기 인터럽트 플래그
- bit.CLKHOLD: 클럭 홀드 상태
2.4 TWIx.MDATA (마스터 데이터 레지스터)
- 데이터 송수신용 레지스터
- 쓰기: 전송할 데이터
- 읽기: 수신된 데이터
3. I2C 설정 절차
- 시스템 초기화:
- 시스템 클럭 설정 (24MHz, set_system_clock())
- 인터럽트 비활성화 (cli())
- 포트 설정:
- TWI0: PC2(SDA), PC3(SCL)에 외부 풀업 저항 연결
- TWI1: PF2(SDA), PF3(SCL)에 외부 풀업 저항 연결
- PORTx.DIRSET으로 출력 설정
- I2C 모듈 활성화:
- TWIx.MCTRLA로 마스터 모드 활성화
- TWIx.MBAUD로 보율 설정
- 마스터 동작:
- TWIx.MADDR로 슬레이브 주소 전송
- TWIx.MDATA로 데이터 송수신
- 인터럽트 설정 (선택):
- TWIx.MCTRLB로 인터럽트 조건 설정
- 상태 확인:
- TWIx.MSTATUS로 버스 상태 및 에러 확인
4. I2C 설정 고려사항
- 클럭 설정: 정확한 보율 계산 및 클럭 안정화 확인
- 풀업 저항: SDA, SCL 라인에 4.7kΩ 저항 필수
- 버스 충돌: 다중 마스터 환경에서 TWIx.MSTATUS.BUSERR 확인
- 저전력: 스마트 모드(SMEN)로 전력 소모 감소
- 타임아웃: 긴 대기 시간 방지를 위해 타임아웃 설정 권장
- 채널 선택: TWI0은 기본 채널, TWI1은 추가 장치 연결이나 핀 충돌 회피 시 사용
5. 실용적인 I2C 예제 코드 (Bitfield 구조)
아래는 AVR128DB48의 TWI0 및 TWI1을 활용한 예제 코드입니다. Atmel Studio 또는 MPLAB X IDE에서 AVR-GCC로 실행 가능합니다. 예제는 24LC256 EEPROM을 대상으로 작성되었습니다.
5.1 예제 1: TWI0 마스터로 EEPROM에 데이터 쓰기 및 읽기
// File: i2c_twi0_eeprom.c
// Description: AVR128DB48 TWI0 마스터로 24LC256 EEPROM에 데이터 쓰기/읽기
// Compiler: AVR-GCC
// Target: AVR128DB48
#include <avr/io.h> // AVR 입출력 관련 헤더 파일
#include <util/delay.h> // 지연 함수 관련 헤더 파일
#define F_CPU 24000000UL // 시스템 클록 주파수를 24MHz로 정의 (지연 함수 정확도)
#define I2C_BAUD(F_SCL) (((F_CPU / (2 * F_SCL)) - 5)) // I2C 보율 계산 (100kHz)
#define EEPROM_ADDR 0x50 // 24LC256 EEPROM의 7비트 I2C 주소
void set_system_clock(void) {
// 외부 32.768kHz 크리스털 오실레이터(XOSC32K) 활성화
_PROTECTED_WRITE(CLKCTRL.XOSC32KCTRLA, CLKCTRL_ENABLE_bm | CLKCTRL_RUNSTDBY_bm);
// CLKCTRL_ENABLE_bm: XOSC32K 활성화
// CLKCTRL_RUNSTDBY_bm: 스탠바이 모드에서 XOSC32K 유지
while (!(CLKCTRL.MCLKSTATUS & CLKCTRL_XOSC32KS_bm)); // XOSC32K 안정화 대기
// 내부 OSCHF를 24MHz로 설정하고 XOSC32K 참조 오토튜닝 활성화
_PROTECTED_WRITE(CLKCTRL.OSCHFCTRLA, CLKCTRL_FRQSEL_24M_gc | CLKCTRL_AUTOTUNE_bm);
// CLKCTRL_FRQSEL_24M_gc: 24MHz 클럭 선택
// CLKCTRL_AUTOTUNE_bm: 오토튜닝 활성화
_PROTECTED_WRITE(CLKCTRL.MCLKCTRLA, CLKCTRL_CLKSEL_OSCHF_gc); // OSCHF를 시스템 클럭 소스로 설정
_PROTECTED_WRITE(CLKCTRL.MCLKCTRLB, 0x00); // 클럭 프리스케일러 비활성화 (24MHz 그대로 사용)
}
void i2c_twi0_init(void) {
// PC2(SDA), PC3(SCL)을 출력으로 설정
PORTC.DIRSET = PIN2_bm | PIN3_bm; // PC2, PC3 핀을 출력으로 설정
PORTC.PIN2CTRL |= PORT_PULLUPEN_bm; // PC2(SDA)에 내부 풀업 저항 활성화
PORTC.PIN3CTRL |= PORT_PULLUPEN_bm; // PC3(SCL)에 내부 풀업 저항 활성화
// TWI0 마스터 모드 활성화
TWI0.MCTRLA = TWI_ENABLE_bm; // TWI0 모듈 활성화
TWI0.MBAUD = I2C_BAUD(100000); // 100kHz 보율 설정
TWI0.MSTATUS = TWI_BUSSTATE_IDLE_gc; // I2C 버스 상태를 Idle로 초기화
}
void i2c_twi0_start(uint8_t addr) {
// I2C 통신 시작: 슬레이브 주소 전송
TWI0.MADDR = (addr << 1); // 7비트 주소를 왼쪽으로 시프트하고 쓰기 비트(0) 추가
while (!(TWI0.MSTATUS & (TWI_WIF_bm | TWI_RIF_bm))); // 쓰기 또는 읽기 플래그 대기
}
void i2c_twi0_write(uint8_t data) {
// 데이터 전송
TWI0.MDATA = data; // 데이터 레지스터에 데이터 쓰기
while (!(TWI0.MSTATUS & TWI_WIF_bm)); // 쓰기 완료 대기
}
uint8_t i2c_twi0_read_ack(void) {
// 데이터 읽기 (ACK 전송)
TWI0.MCTRLB &= ~TWI_ACKACT_bm; // ACK 전송 설정
while (!(TWI0.MSTATUS & TWI_RIF_bm)); // 읽기 완료 대기
return TWI0.MDATA; // 읽은 데이터 반환
}
void i2c_twi0_stop(void) {
// I2C 통신 종료: Stop 조건 전송
TWI0.MCTRLB = TWI_MCMD_STOP_gc; // Stop 명령 설정
}
void eeprom_write_byte(uint16_t mem_addr, uint8_t data) {
// EEPROM에 1바이트 쓰기
i2c_twi0_start(EEPROM_ADDR); // EEPROM 주소로 통신 시작
i2c_twi0_write(mem_addr >> 8); // 메모리 주소 상위 바이트 전송
i2c_twi0_write(mem_addr & 0xFF); // 메모리 주소 하위 바이트 전송
i2c_twi0_write(data); // 데이터 전송
i2c_twi0_stop(); // 통신 종료
_delay_ms(5); // EEPROM 쓰기 시간 대기 (5ms)
}
uint8_t eeprom_read_byte(uint16_t mem_addr) {
// EEPROM에서 1바이트 읽기
i2c_twi0_start(EEPROM_ADDR); // EEPROM 주소로 쓰기 모드 시작
i2c_twi0_write(mem_addr >> 8); // 메모리 주소 상위 바이트 전송
i2c_twi0_write(mem_addr & 0xFF); // 메모리 주소 하위 바이트 전송
i2c_twi0_start(EEPROM_ADDR | 1); // 읽기 모드로 재시작 (주소 + 읽기 비트)
uint8_t data = i2c_twi0_read_ack(); // 데이터 읽기 (ACK 전송)
i2c_twi0_stop(); // 통신 종료
return data; // 읽은 데이터 반환
}
void gpio_init(void) {
// PB3(LED0)를 출력으로 설정
PORTB.DIRSET = PIN3_bm; // PB3 핀을 출력으로 설정
PORTB.OUTSET = PIN3_bm; // PB3 초기값 High (LED 켬)
}
int main(void) {
set_system_clock(); // 시스템 클럭 설정
i2c_twi0_init(); // TWI0 초기화
gpio_init(); // GPIO 초기화
while (1) {
// EEPROM 0x00 주소에 0x55 쓰기
eeprom_write_byte(0x00, 0x55); // 0x00 주소에 데이터 0x55 쓰기
_delay_ms(100); // 100ms 대기
// EEPROM 0x00 주소에서 데이터 읽기
uint8_t data = eeprom_read_byte(0x00); // 0x00 주소에서 데이터 읽기
// 읽은 데이터가 0x55면 LED 토글
if (data == 0x55) {
PORTB.OUTTGL = PIN3_bm; // PB3 출력 토글 (LED 깜빡임)
}
_delay_ms(500); // 500ms 대기 (LED 점멸 주기)
}
return 0; // 프로그램 종료 (도달하지 않음)
}
설명:
- 기능: TWI0을 통해 24LC256 EEPROM에 데이터를 쓰고 읽어 PB3 LED를 토글
- 설정: TWI0 마스터 모드, 100kHz, PC2(SDA)/PC3(SCL) 풀업 저항
- 출력: EEPROM 데이터가 0x55일 때 LED 점멸
5.2 예제 2: TWI1 마스터로 EEPROM에 데이터 쓰기
// File: i2c_twi1_eeprom.c
// Description: AVR128DB48 TWI1 마스터로 24LC256 EEPROM에 데이터 쓰기
// Compiler: AVR-GCC
// Target: AVR128DB48
#include <avr/io.h> // AVR 입출력 관련 헤더 파일
#include <util/delay.h> // 지연 함수 관련 헤더 파일
#define F_CPU 24000000UL // 시스템 클록 주파수를 24MHz로 정의
#define I2C_BAUD(F_SCL) (((F_CPU / (2 * F_SCL)) - 5)) // I2C 보율 계산 (100kHz)
#define EEPROM_ADDR 0x50 // 24LC256 EEPROM의 7비트 I2C 주소
void set_system_clock(void) {
// 외부 32.768kHz 크리스털 오실레이터(XOSC32K) 활성화
_PROTECTED_WRITE(CLKCTRL.XOSC32KCTRLA, CLKCTRL_ENABLE_bm | CLKCTRL_RUNSTDBY_bm);
// CLKCTRL_ENABLE_bm: XOSC32K 활성화
// CLKCTRL_RUNSTDBY_bm: 스탠바이 모드에서 XOSC32K 유지
while (!(CLKCTRL.MCLKSTATUS & CLKCTRL_XOSC32KS_bm)); // XOSC32K 안정화 대기
// 내부 OSCHF를 24MHz로 설정하고 XOSC32K 참조 오토튜닝 활성화
_PROTECTED_WRITE(CLKCTRL.OSCHFCTRLA, CLKCTRL_FRQSEL_24M_gc | CLKCTRL_AUTOTUNE_bm);
// CLKCTRL_FRQSEL_24M_gc: 24MHz 클럭 선택
// CLKCTRL_AUTOTUNE_bm: 오토튜닝 활성화
_PROTECTED_WRITE(CLKCTRL.MCLKCTRLA, CLKCTRL_CLKSEL_OSCHF_gc); // OSCHF를 시스템 클럭 소스로 설정
_PROTECTED_WRITE(CLKCTRL.MCLKCTRLB, 0x00); // 클럭 프리스케일러 비활성화
}
void i2c_twi1_init(void) {
// PF2(SDA), PF3(SCL)을 출력으로 설정
PORTF.DIRSET = PIN2_bm | PIN3_bm; // PF2, PF3 핀을 출력으로 설정
PORTF.PIN2CTRL |= PORT_PULLUPEN_bm; // PF2(SDA)에 내부 풀업 저항 활성화
PORTF.PIN3CTRL |= PORT_PULLUPEN_bm; // PF3(SCL)에 내부 풀업 저항 활성화
// TWI1 마스터 모드 활성화
TWI1.MCTRLA = TWI_ENABLE_bm; // TWI1 모듈 활성화
TWI1.MBAUD = I2C_BAUD(100000); // 100kHz 보율 설정
TWI1.MSTATUS = TWI_BUSSTATE_IDLE_gc; // I2C 버스 상태를 Idle로 초기화
}
void i2c_twi1_start(uint8_t addr) {
// I2C 통신 시작: 슬레이브 주소 전송
TWI1.MADDR = (addr << 1); // 7비트 주소를 왼쪽으로 시프트하고 쓰기 비트(0) 추가
while (!(TWI1.MSTATUS & (TWI_WIF_bm | TWI_RIF_bm))); // 쓰기 또는 읽기 플래그 대기
}
void i2c_twi1_write(uint8_t data) {
// 데이터 전송
TWI1.MDATA = data; // 데이터 레지스터에 데이터 쓰기
while (!(TWI1.MSTATUS & TWI_WIF_bm)); // 쓰기 완료 대기
}
void i2c_twi1_stop(void) {
// I2C 통신 종료: Stop 조건 전송
TWI1.MCTRLB = TWI_MCMD_STOP_gc; // Stop 명령 설정
}
void eeprom_write_byte(uint16_t mem_addr, uint8_t data) {
// EEPROM에 1바이트 쓰기
i2c_twi1_start(EEPROM_ADDR); // EEPROM 주소로 통신 시작
i2c_twi1_write(mem_addr >> 8); // 메모리 주소 상위 바이트 전송
i2c_twi1_write(mem_addr & 0xFF); // 메모리 주소 하위 바이트 전송
i2c_twi1_write(data); // 데이터 전송
i2c_twi1_stop(); // 통신 종료
_delay_ms(5); // EEPROM 쓰기 시간 대기 (5ms)
}
void gpio_init(void) {
// PB3(LED0)를 출력으로 설정
PORTB.DIRSET = PIN3_bm; // PB3 핀을 출력으로 설정
PORTB.OUTSET = PIN3_bm; // PB3 초기값 High (LED 켬)
}
int main(void) {
set_system_clock(); // 시스템 클럭 설정
i2c_twi1_init(); // TWI1 초기화
gpio_init(); // GPIO 초기화
while (1) {
// EEPROM 0x00 주소에 0xAA 쓰기
eeprom_write_byte(0x00, 0xAA); // 0x00 주소에 데이터 0xAA 쓰기
PORTB.OUTTGL = PIN3_bm; // PB3 출력 토글 (LED 깜빡임)
_delay_ms(500); // 500ms 대기 (LED 점멸 주기)
}
return 0; // 프로그램 종료 (도달하지 않음)
}
설명:
- 기능: TWI1을 통해 24LC256 EEPROM에 데이터를 쓰고 PB3 LED 점멸
- 설정: TWI1 마스터 모드, 100kHz, PF2(SDA)/PF3(SCL) 풀업 저항
- 출력: 데이터 쓰기 성공 시 LED 점멸
5.3 예제 3: TWI0 인터럽트 기반 EEPROM 데이터 읽기
// File: i2c_twi0_interrupt.c
// Description: AVR128DB48 TWI0 인터럽트로 24LC256 EEPROM 데이터 읽기
// Compiler: AVR-GCC
// Target: AVR128DB48
#include <avr/io.h> // AVR 입출력 관련 헤더 파일
#include <avr/interrupt.h> // 인터럽트 처리 관련 헤더 파일
#define F_CPU 24000000UL // 시스템 클록 주파수를 24MHz로 정의
#define I2C_BAUD(F_SCL) (((F_CPU / (2 * F_SCL)) - 5)) // I2C 보율 계산 (100kHz)
#define EEPROM_ADDR 0x50 // 24LC256 EEPROM의 7비트 I2C 주소
volatile uint8_t i2c_data = 0; // 읽은 데이터를 저장하는 전역 변수
volatile uint8_t i2c_state = 0; // I2C 상태를 추적하는 전역 변수 (인터럽트 상태 관리)
void set_system_clock(void) {
// 외부 32.768kHz 크리스털 오실레이터(XOSC32K) 활성화
_PROTECTED_WRITE(CLKCTRL.XOSC32KCTRLA, CLKCTRL_ENABLE_bm | CLKCTRL_RUNSTDBY_bm);
// CLKCTRL_ENABLE_bm: XOSC32K 활성화
// CLKCTRL_RUNSTDBY_bm: 스탠바이 모드에서 XOSC32K 유지
while (!(CLKCTRL.MCLKSTATUS & CLKCTRL_XOSC32KS_bm)); // XOSC32K 안정화 대기
// 내부 OSCHF를 24MHz로 설정하고 XOSC32K 참조 오토튜닝 활성화
_PROTECTED_WRITE(CLKCTRL.OSCHFCTRLA, CLKCTRL_FRQSEL_24M_gc | CLKCTRL_AUTOTUNE_bm);
// CLKCTRL_FRQSEL_24M_gc: 24MHz 클럭 선택
// CLKCTRL_AUTOTUNE_bm: 오토튜닝 활성화
_PROTECTED_WRITE(CLKCTRL.MCLKCTRLA, CLKCTRL_CLKSEL_OSCHF_gc); // OSCHF를 시스템 클럭 소스로 설정
_PROTECTED_WRITE(CLKCTRL.MCLKCTRLB, 0x00); // 클럭 프리스케일러 비활성화
}
void i2c_twi0_init(void) {
// PC2(SDA), PC3(SCL)을 출력으로 설정
PORTC.DIRSET = PIN2_bm | PIN3_bm; // PC2, PC3 핀을 출력으로 설정
PORTC.PIN2CTRL |= PORT_PULLUPEN_bm; // PC2(SDA)에 내부 풀업 저항 활성화
PORTC.PIN3CTRL |= PORT_PULLUPEN_bm; // PC3(SCL)에 내부 풀업 저항 활성화
// TWI0 마스터 모드 및 인터럽트 활성화
TWI0.MCTRLA = TWI_ENABLE_bm | TWI_RIEN_bm | TWI_WIEN_bm; // TWI0 활성화, 읽기/쓰기 인터럽트 활성화
TWI0.MBAUD = I2C_BAUD(100000); // 100kHz 보율 설정
TWI0.MSTATUS = TWI_BUSSTATE_IDLE_gc; // I2C 버스 상태를 Idle로 초기화
}
ISR(TWI0_TWIM_vect) {
// TWI0 마스터 인터럽트 서비스 루틴
if (TWI0.MSTATUS & TWI_WIF_bm) { // 쓰기 인터럽트 플래그 확인
if (i2c_state == 1) { // 상태 1: 슬레이브 주소 전송 후
TWI0.MDATA = 0x00; // 메모리 주소 상위 바이트(0x00) 전송
i2c_state = 2; // 다음 상태로 전환
} else if (i2c_state == 2) { // 상태 2: 상위 주소 전송 후
TWI0.MDATA = 0x00; // 메모리 주소 하위 바이트(0x00) 전송
i2c_state = 3; // 다음 상태로 전환
} else if (i2c_state == 3) { // 상태 3: 하위 주소 전송 후
TWI0.MADDR = (EEPROM_ADDR << 1) | 1; // 읽기 모드로 주소 재전송 (주소 + 읽기 비트)
i2c_state = 4; // 다음 상태로 전환
}
} else if (TWI0.MSTATUS & TWI_RIF_bm) { // 읽기 인터럽트 플래그 확인
if (i2c_state == 4) { // 상태 4: 데이터 읽기
i2c_data = TWI0.MDATA; // 읽은 데이터를 전역 변수에 저장
TWI0.MCTRLB = TWI_MCMD_STOP_gc; // Stop 조건 전송
i2c_state = 0; // 초기 상태로 리셋
}
}
}
void eeprom_read_byte_async(uint16_t mem_addr) {
// 비동기 EEPROM 데이터 읽기 시작
i2c_state = 1; // 상태를 1로 설정 (주소 전송 시작)
TWI0.MADDR = (EEPROM_ADDR << 1); // 쓰기 모드로 슬레이브 주소 전송
}
void gpio_init(void) {
// PB3(LED0)를 출력으로 설정
PORTB.DIRSET = PIN3_bm; // PB3 핀을 출력으로 설정
PORTB.OUTSET = PIN3_bm; // PB3 초기값 High (LED 켬)
}
int main(void) {
set_system_clock(); // 시스템 클럭 설정
i2c_twi0_init(); // TWI0 초기화
gpio_init(); // GPIO 초기화
sei(); // 글로벌 인터럽트 활성화
while (1) {
// EEPROM 0x00 주소에서 데이터 비동기 읽기
eeprom_read_byte_async(0x00); // 비동기 읽기 시작
while (i2c_state != 0); // 읽기 완료 대기 (i2c_state가 0이 될 때까지)
if (i2c_data == 0x55) { // 읽은 데이터가 0x55인지 확인
PORTB.OUTTGL = PIN3_bm; // PB3 출력 토글 (LED 깜빡임)
}
_delay_ms(500); // 500ms 대기 (LED 점멸 주기)
}
return 0; // 프로그램 종료 (도달하지 않음)
}
설명:
- 기능: TWI0 인터럽트를 사용하여 24LC256 EEPROM에서 데이터를 비동기적으로 읽고 PB3 LED를 토글
- 설정: TWI0 마스터 모드, 100kHz, PC2(SDA)/PC3(SCL) 풀업 저항, 읽기/쓰기 인터럽트 활성화
- 출력: EEPROM 데이터가 0x55일 때 LED 점멸
5.4 예제 4: TWI1 다중 바이트 EEPROM 쓰기/읽기
// File: i2c_twi1_multi_byte.c
// Description: AVR128DB48 TWI1으로 24LC256 EEPROM에 다중 바이트 쓰기/읽기
// Compiler: AVR-GCC
// Target: AVR128DB48
#include <avr/io.h> // AVR 입출력 관련 헤더 파일
#include <util/delay.h> // 지연 함수 관련 헤더 파일
#define F_CPU 24000000UL // 시스템 클록 주파수를 24MHz로 정의
#define I2C_BAUD(F_SCL) (((F_CPU / (2 * F_SCL)) - 5)) // I2C 보율 계산 (100kHz)
#define EEPROM_ADDR 0x50 // 24LC256 EEPROM의 7비트 I2C 주소
void set_system_clock(void) {
// 외부 32.768kHz 크리스털 오실레이터(XOSC32K) 활성화
_PROTECTED_WRITE(CLKCTRL.XOSC32KCTRLA, CLKCTRL_ENABLE_bm | CLKCTRL_RUNSTDBY_bm);
// CLKCTRL_ENABLE_bm: XOSC32K 활성화
// CLKCTRL_RUNSTDBY_bm: 스탠바이 모드에서 XOSC32K 유지
while (!(CLKCTRL.MCLKSTATUS & CLKCTRL_XOSC32KS_bm)); // XOSC32K 안정화 대기
// 내부 OSCHF를 24MHz로 설정하고 XOSC32K 참조 오토튜닝 활성화
_PROTECTED_WRITE(CLKCTRL.OSCHFCTRLA, CLKCTRL_FRQSEL_24M_gc | CLKCTRL_AUTOTUNE_bm);
// CLKCTRL_FRQSEL_24M_gc: 24MHz 클럭 선택
// CLKCTRL_AUTOTUNE_bm: 오토튜닝 활성화
_PROTECTED_WRITE(CLKCTRL.MCLKCTRLA, CLKCTRL_CLKSEL_OSCHF_gc); // OSCHF를 시스템 클럭 소스로 설정
_PROTECTED_WRITE(CLKCTRL.MCLKCTRLB, 0x00); // 클럭 프리스케일러 비활성화
}
void i2c_twi1_init(void) {
// PF2(SDA), PF3(SCL)을 출력으로 설정
PORTF.DIRSET = PIN2_bm | PIN3_bm; // PF2, PF3 핀을 출력으로 설정
PORTF.PIN2CTRL |= PORT_PULLUPEN_bm; // PF2(SDA)에 내부 풀업 저항 활성화
PORTF.PIN3CTRL |= PORT_PULLUPEN_bm; // PF3(SCL)에 내부 풀업 저항 활성화
// TWI1 마스터 모드 활성화
TWI1.MCTRLA = TWI_ENABLE_bm; // TWI1 모듈 활성화
TWI1.MBAUD = I2C_BAUD(100000); // 100kHz 보율 설정
TWI1.MSTATUS = TWI_BUSSTATE_IDLE_gc; // I2C 버스 상태를 Idle로 초기화
}
void i2c_twi1_start(uint8_t addr) {
// I2C 통신 시작: 슬레이브 주소 전송
TWI1.MADDR = (addr << 1); // 7비트 주소를 왼쪽으로 시프트하고 쓰기 비트(0) 추가
while (!(TWI1.MSTATUS & (TWI_WIF_bm | TWI_RIF_bm))); // 쓰기 또는 읽기 플래그 대기
}
void i2c_twi1_write(uint8_t data) {
// 데이터 전송
TWI1.MDATA = data; // 데이터 레지스터에 데이터 쓰기
while (!(TWI1.MSTATUS & TWI_WIF_bm)); // 쓰기 완료 대기
}
uint8_t i2c_twi1_read_ack(void) {
// 데이터 읽기 (ACK 전송)
TWI1.MCTRLB &= ~TWI_ACKACT_bm; // ACK 전송 설정
while (!(TWI1.MSTATUS & TWI_RIF_bm)); // 읽기 완료 대기
return TWI1.MDATA; // 읽은 데이터 반환
}
uint8_t i2c_twi1_read_nack(void) {
// 데이터 읽기 (NACK 전송, 마지막 바이트용)
TWI1.MCTRLB |= TWI_ACKACT_bm; // NACK 전송 설정
while (!(TWI1.MSTATUS & TWI_RIF_bm)); // 읽기 완료 대기
return TWI1.MDATA; // 읽은 데이터 반환
}
void i2c_twi1_stop(void) {
// I2C 통신 종료: Stop 조건 전송
TWI1.MCTRLB = TWI_MCMD_STOP_gc; // Stop 명령 설정
}
void eeprom_write_bytes(uint16_t mem_addr, uint8_t *data, uint8_t len) {
// EEPROM에 다중 바이트 쓰기
i2c_twi1_start(EEPROM_ADDR); // EEPROM 주소로 통신 시작
i2c_twi1_write(mem_addr >> 8); // 메모리 주소 상위 바이트 전송
i2c_twi1_write(mem_addr & 0xFF); // 메모리 주소 하위 바이트 전송
for (uint8_t i = 0; i < len; i++) { // 지정된 길이만큼 데이터 반복 전송
i2c_twi1_write(data[i]); // 각 바이트 전송
}
i2c_twi1_stop(); // 통신 종료
_delay_ms(5); // EEPROM 쓰기 시간 대기 (5ms)
}
void eeprom_read_bytes(uint16_t mem_addr, uint8_t *data, uint8_t len) {
// EEPROM에서 다중 바이트 읽기
i2c_twi1_start(EEPROM_ADDR); // EEPROM 주소로 쓰기 모드 시작
i2c_twi1_write(mem_addr >> 8); // 메모리 주소 상위 바이트 전송
i2c_twi1_write(mem_addr & 0xFF); // 메모리 주소 하위 바이트 전송
i2c_twi1_start(EEPROM_ADDR | 1); // 읽기 모드로 재시작 (주소 + 읽기 비트)
for (uint8_t i = 0; i < len - 1; i++) { // 마지막 바이트 전까지 ACK로 읽기
data[i] = i2c_twi1_read_ack(); // 데이터 읽기 (ACK 전송)
}
data[len - 1] = i2c_twi1_read_nack(); // 마지막 바이트 읽기 (NACK 전송)
i2c_twi1_stop(); // 통신 종료
}
void gpio_init(void) {
// PB3(LED0)를 출력으로 설정
PORTB.DIRSET = PIN3_bm; // PB3 핀을 출력으로 설정
PORTB.OUTSET = PIN3_bm; // PB3 초기값 High (LED 켬)
}
int main(void) {
set_system_clock(); // 시스템 클럭 설정
i2c_twi1_init(); // TWI1 초기화
gpio_init(); // GPIO 초기화
uint8_t write_data[3] = {0xAA, 0xBB, 0xCC}; // 쓰기용 데이터 배열
uint8_t read_data[3] = {0}; // 읽기용 데이터 배열
while (1) {
// EEPROM 0x00 주소에 3바이트 쓰기
eeprom_write_bytes(0x00, write_data, 3); // [0xAA, 0xBB, 0xCC] 쓰기
_delay_ms(100); // 100ms 대기
// EEPROM 0x00 주소에서 3바이트 읽기
eeprom_read_bytes(0x00, read_data, 3); // 3바이트 읽기
// 읽은 데이터가 [0xAA, 0xBB, 0xCC]인지 확인
if (read_data[0] == 0xAA && read_data[1] == 0xBB && read_data[2] == 0xCC) {
PORTB.OUTTGL = PIN3_bm; // PB3 출력 토글 (LED 깜빡임)
}
_delay_ms(500); // 500ms 대기 (LED 점멸 주기)
}
return 0; // 프로그램 종료 (도달하지 않음)
}
설명:
- 기능: TWI1을 통해 24LC256 EEPROM에 다중 바이트(3바이트) 쓰기 및 읽기 후 PB3 LED 토글
- 설정: TWI1 마스터 모드, 100kHz, PF2(SDA)/PF3(SCL) 풀업 저항
- 출력: 읽은 데이터가 [0xAA, 0xBB, 0xCC]일 때 LED 점멸
6. TWI0와 TWI1 선택 기준
- TWI0: 기본 I2C 채널로, Curiosity Nano와 같은 평가 보드에서 표준 핀(PC2, PC3)으로 사용. 단일 I2C 통신에 적합.
- TWI1: 추가 I2C 장치 연결이 필요하거나, TWI0 핀이 다른 기능(UART, SPI 등)으로 사용 중일 때 활용. PF2, PF3 핀 사용.
- 멀티플렉싱 주의: 핀 충돌 방지를 위해 데이터시트의 핀 기능 확인.
7. 사용 방법
7.1 환경 설정
- AVR-GCC 설치: Atmel Studio 또는 MPLAB X IDE에 AVR-GCC 툴체인 설치
- 헤더 파일: <avr/io.h>, <util/delay.h>, <avr/interrupt.h> 포함
- 프로젝트 설정: AVR128DB48 타겟으로 프로젝트 생성
- 클럭 정의: #define F_CPU 24000000UL
7.2 코드 실행
- 각 예제를 별도의 .c 파일로 저장하거나, main.c에 복사
- Atmel Studio/MPLAB X IDE에서 빌드 및 플래싱
7.3 하드웨어 준비
- TWI0 연결: PC2(SDA), PC3(SCL)에 4.7kΩ 풀업 저항 및 24LC256 EEPROM 연결
- TWI1 연결: PF2(SDA), PF3(SCL)에 4.7kΩ 풀업 저항 및 24LC256 EEPROM 연결
- LED 출력: PB3에 LED 및 330Ω 저항 연결
- 전원: 1.8V~5.5V 전원 공급
7.4 디버깅
- Atmel Studio/MPLAB X IDE 디버거 사용
- TWIx.MSTATUS, TWIx.MDATA 레지스터 확인
- 오실로스코프로 SDA/SCL 신호 점검
8. 추가 팁
- 클럭 설정: set_system_clock()로 XOSC32K 안정화 확인
- 노이즈 감소: 외부 풀업 저항 및 PCB 배선 최적화
- 인터럽트 사용: 예제 3처럼 인터럽트를 활용하여 CPU 부하 감소
- 다중 바이트: 예제 4처럼 다중 바이트 전송으로 효율성 향상
- Microchip 리소스: AVR128DB48 데이터시트, I2C Application Notes
- 문제 해결:
- 통신 실패: 풀업 저항 및 TWIx.MSTATUS.BUSERR 확인
- 데이터 오류: TWIx.MSTATUS.RIF/WIF 플래그 점검
- 느린 응답: 보율 설정 및 타임아웃 조정
- 커뮤니티: Microchip Community, AVR Freaks 포럼 참고
9. 결론
이 문서는 AVR128DB48의 TWI0 및 TWI1 I2C 모듈 설정 방법과 Bitfield 구조를 활용한 예제 코드를 제공하여 I2C 기반 애플리케이션 구현을 지원합니다. TWI0는 기본 채널로 단일 장치 통신에 적합하며, TWI1은 추가 장치 연결이나 핀 충돌 회피 시 유용합니다. 인터럽트 및 다중 바이트 전송 예제를 통해 효율적인 I2C 통신을 구현할 수 있습니다.
키워드: AVR128DB48, I2C, TWI0, TWI1, 마이크로컨트롤러, Atmel Studio, MPLAB X IDE, EEPROM, 풀업 저항, 마스터 모드, 인터럽트, 다중 바이트, 24LC256
'MCU > AVR' 카테고리의 다른 글
[AVR128DB48] 이벤트 시스템 사용 방법 및 예제 코드 (0) | 2025.08.20 |
---|---|
[AVR128DB48] ADC 및 DAC 사용 방법 및 예제 코드 (0) | 2025.08.20 |
[AVR128DB48] Watchdog 사용 방법 및 예제 코드 (0) | 2025.08.19 |
[AVR128DB48] SPI 사용 방법 및 예제 코드 (0) | 2025.08.19 |
[AVR128DB48] UART 사용 방법 및 예제 코드 (1) | 2025.08.19 |
[AVR128DB48] GPIO 사용 방법 및 예제 코드 (0) | 2025.08.18 |
[AVR128DB48] 프로젝트 설정 절차 및 기본 프로그램 작성 (0) | 2025.08.18 |
[AVR128DA48] I2C Bit-bang을 STM32 HAL 스타일로 코드 구현 (1) | 2025.08.05 |