본문 바로가기
MCU/AVR

AVR128DB48 Event System 사용 방법 및 예제 코드(수정)

by linuxgo 2025. 8. 20.

1. AVR128DB48 Event System 모듈 개요

Microchip의 AVR128DB48 마이크로컨트롤러는 Event System (EVSYS) 모듈을 통해 주변 장치 간 직접적인 신호 전달을 지원합니다. 이는 CPU 개입 없이 저지연, 저전력 통신을 가능하게 하며, 타이머, ADC, 비교기 등 다양한 애플리케이션에 적합합니다. 이 문서에서는 EVSYS 설정 방법, 레지스터 구조, 그리고 실용적인 예제 코드를 제공하여 초보자와 숙련된 개발자 모두 쉽게 활용할 수 있도록 돕습니다.

AVR128DB Event system

주요 사양

  • EVSYS 채널: 최대 10개 (CHANNEL0 ~ CHANNEL9)
  • 지원 기능:
    • 동기/비동기 이벤트 처리
    • 슬립 모드 지원 (SleepWalking)
    • 소프트웨어 이벤트 트리거
    • QDEC(Quadrature Decoder) 지원
  • 주요 레지스터:
    • SWEVENTA/B: 소프트웨어 이벤트 트리거
    • CHANNELn: 이벤트 생성자 선택 (n = 0~9)
    • USERm: 이벤트 사용자 채널 연결 (m = CCLLUT0A, ADCn START 등)
  • 이벤트 유형: 펄스(Pulse) 또는 레벨(Level)
  • 전력 관리: 저전력 모드 지원
  • 전압 레벨: 1.8V~5.5V 동작 지원

2. Event System Bitfield 설정

AVR128DB48의 EVSYS 레지스터는 Bitfield 구조로 정의되며, <avr/io.h> 헤더 파일을 통해 접근합니다. 주요 레지스터는 다음과 같습니다:

2.1 SWEVENTA (소프트웨어 이벤트 A)

  • 비트 [7:0] SWEVENTA[7:0]: 채널 0~7에 소프트웨어 이벤트 발생 (동기 채널용)

2.2 SWEVENTB (소프트웨어 이벤트 B)

  • 비트 [7:0] SWEVENTB[7:0]: 채널 8~9에 해당 (비트 0~1 사용, 나머지 reserved)

2.3 CHANNELn (채널 설정, n = 0~9)

  • 비트 [7:0] CHANNELn[7:0]: 이벤트 생성자 ID 선택 (0x00 = OFF, 예: RTC OVF, TCAn OVF 등)

2.4 USERm (사용자 설정, m = USERCCLLUT0A 등)

  • 비트 [7:0] USERm[7:0]: [5:0] = 채널 번호 (0~9), 상위 비트 reserved

3. Event System 설정 절차

  1. 시스템 초기화:
    •   set_system_clock()으로 시스템 클럭 설정 (예: 24MHz)
    •   인터럽트 비활성화 (cli())
  2. 포트 설정:
    •   이벤트 출력 핀 설정 (필요 시 EVSYS EVOUTx 사용)
  3. 채널 설정:
    •   CHANNELn 레지스터로 생성자 선택
  4. 사용자 연결:
    •    USERm 레지스터로 채널 연결
  5. 이벤트 활성화:
    •   주변 장치 설정에서 이벤트 입력 활성화 (예: TCA.EVCTRL)
  6. 실행:
    •   설정 후 즉시 동작

4. Event System 설정 고려사항

  • 클럭 설정: 시스템 클럭 안정화 확인
  • 동기/비동기: 비동기 이벤트는 슬립 모드에서 유용
  • 채널 충돌: 동일 채널에 여러 사용자 주의
  • 저전력: 불필요한 채널 비활성화
  • 핀 멀티플렉싱: EVSYS EVOUTx와 다른 기능 충돌 확인

5. Event System 예제 코드 

아래는 AVR128DB48 EVSYS를 Bitfield 구조로 설정한 예제 코드입니다. Atmel Studio 또는 MPLAB X IDE와 AVR-GCC 환경에서 실행 가능합니다.

5.1 예제 1: GPIO 핀 이벤트 생성자 (PA0 버튼으로 CCL 및 TCB 트리거)

// File: evsys_gpio_generator.c
// Description: AVR128DB48 이벤트 시스템 예제 (PA0 핀 이벤트로 CCL 및 TCB 트리거)
// Compiler: AVR-GCC
// Target: AVR128DB48
#define F_CPU 24000000UL  // 시스템 클록 주파수를 24 MHz로 정의 (지연 함수 정확도를 위해 필요)
#include <avr/io.h>        // AVR 입출력 관련 헤더 파일
#include <avr/interrupt.h> // 인터럽트 처리 관련 헤더 파일
#include <util/delay.h>    // 지연 함수 관련 헤더 파일


volatile uint16_t timer_count = 0; // TCB0 카운트 값을 저장하는 전역 변수 (인터럽트에서 사용)

void set_system_clock(void) {
	// 32.768 kHz 외부 크리스털 오실레이터(XOSC32K) 활성화
	_PROTECTED_WRITE(CLKCTRL.XOSC32KCTRLA, CLKCTRL_ENABLE_bm | CLKCTRL_RUNSTDBY_bm);
	// CLKCTRL.XOSC32KCTRLA 레지스터에 쓰기 보호 해제 후 설정
	// CLKCTRL_ENABLE_bm: XOSC32K 활성화
	// CLKCTRL_RUNSTDBY_bm: 스탠바이 모드에서도 XOSC32K 동작 유지

	// XOSC32K가 안정될 때까지 대기
	while (!(CLKCTRL.MCLKSTATUS & CLKCTRL_XOSC32KS_bm));
	// CLKCTRL.MCLKSTATUS의 XOSC32KS_bm 비트가 1이 될 때까지 대기 (안정화 확인)

	// 내부 OSCHF를 24 MHz로 설정하고 XOSC32K를 참조하여 오토튜닝 활성화
	_PROTECTED_WRITE(CLKCTRL.OSCHFCTRLA, CLKCTRL_FRQSEL_24M_gc | CLKCTRL_AUTOTUNE_bm);
	// CLKCTRL.OSCHFCTRLA 레지스터에 쓰기 보호 해제 후 설정
	// CLKCTRL_FRQSEL_24M_gc: OSCHF를 24 MHz로 설정
	// CLKCTRL_AUTOTUNE_bm: XOSC32K를 참조한 오토튜닝 활성화

	// OSCHF를 시스템 클록 소스로 설정
	_PROTECTED_WRITE(CLKCTRL.MCLKCTRLA, CLKCTRL_CLKSEL_OSCHF_gc);
	// CLKCTRL.MCLKCTRLA 레지스터에 쓰기 보호 해제 후 설정
	// CLKCTRL_CLKSEL_OSCHF_gc: 시스템 클록 소스를 OSCHF로 선택

	// 클록 프리스케일러 비활성화 (24 MHz 그대로 사용)
	_PROTECTED_WRITE(CLKCTRL.MCLKCTRLB, 0x00);
	// CLKCTRL.MCLKCTRLB 레지스터를 0으로 설정하여 프리스케일러 비활성화
}

void gpio_init(void) {
	// PA0를 입력 핀으로 설정 (버튼 연결)
	PORTA.DIRCLR = PIN0_bm; // PA0 핀의 방향을 입력으로 설정
	PORTA.PIN0CTRL |= PORT_PULLUPEN_bm; // PA0에 풀업 저항 활성화 (버튼 입력 안정화)

	// PB3를 출력 핀으로 설정 (LED 연결)
	PORTB.DIRSET = PIN3_bm; // PB3 핀의 방향을 출력으로 설정 (Curiosity Nano의 LED0)
}

void ccl_init(void) {
	// CCL(구성 가능 논리) 모듈 설정: PA0 이벤트 입력을 PB3 출력으로 연결
	CCL.LUT0CTRLA = CCL_ENABLE_bm; // LUT0(논리 유닛 0) 활성화
	CCL.LUT0CTRLB = CCL_INSEL0_EVENTA_gc; // LUT0의 입력으로 이벤트 채널 0 선택
	CCL.LUT0CTRLC = CCL_OUTEN_bm; // LUT0의 출력 활성화 (PB3에 연결)
	CCL.TRUTH0 = 0x01; // 진리표 설정: 입력 그대로 출력 (PA0 상태를 PB3에 반영)
	CCL.CTRLA = CCL_ENABLE_bm; // CCL 모듈 전체 활성화
}

void timer_init(void) {
	// TCB0 설정: 이벤트 입력으로 카운트 증가
	TCB0.CTRLB = TCB_CNTMODE_INT_gc; // TCB0를 이벤트 입력 카운트 모드로 설정
	TCB0.CTRLA = TCB_CLKSEL_DIV1_gc | TCB_ENABLE_bm; // 24MHz 클럭 사용, 타이머 활성화
	// TCB_CLKSEL_CLKDIV1_gc: 클럭 분주 없이 24MHz 사용
	// TCB_ENABLE_bm: TCB0 타이머 활성화
}

void evsys_init(void) {
	// 이벤트 채널 0: PA0 핀 이벤트 연결
	EVSYS.CHANNEL0 = EVSYS_CHANNEL0_PORTA_PIN0_gc; // PA0 핀을 이벤트 생성자로 선택
	// 비동기 모드로 설정 (기본값, SYNC 비트 설정 안 함)

	// 이벤트 사용자: CCL 및 TCB0에 채널 0 연결
	EVSYS.USERCCLLUT0A= EVSYS_USER_CHANNEL0_gc; // CCL에 이벤트 채널 0 연결
	EVSYS.USERTCB0CAPT = EVSYS_USER_CHANNEL0_gc; // TCB0에 이벤트 채널 0 연결
}

ISR(TCB0_INT_vect) {
	// TCB0 인터럽트 서비스 루틴: PA0 이벤트로 카운트 증가 시 호출
	timer_count = TCB0.CNT; // TCB0의 현재 카운트 값을 읽어 저장
	TCB0.INTFLAGS = TCB_CAPT_bm; // TCB0 캡처 인터럽트 플래그 클리어
}

int main(void) {
	// 시스템 클록 설정 호출
	set_system_clock(); // 24MHz 시스템 클럭과 XOSC32K 설정

	// GPIO 초기화 호출
	gpio_init(); // PA0(입력), PB3(출력) 설정

	// CCL 초기화 호출
	ccl_init(); // CCL을 통해 PA0 이벤트를 PB3 출력으로 라우팅

	// 타이머 초기화 호출
	timer_init(); // TCB0 설정 (이벤트 카운트 모드)

	// 이벤트 시스템 초기화 호출
	evsys_init(); // PA0 이벤트를 CCL 및 TCB0로 연결

	// 글로벌 인터럽트 활성화
	sei(); // TCB0 인터럽트 서비스 루틴 실행 허용

	while (1) {
		// 무한 루프: 이벤트 시스템이 PA0 버튼 이벤트를 CCL(즉시 LED 제어) 및 TCB0(카운트)로 라우팅
		_delay_ms(10); // 버튼 디바운싱을 위한 10ms 대기
	}

	return 0; // 프로그램 종료 (실제로는 도달하지 않음)
}

설명:

  • 이벤트 생성자: PA0 핀 (버튼 입력)
  • 이벤트 사용자: CCL (PB3 LED 제어), TCB0 (카운트 증가)
  • 기능: PA0 버튼 입력이 이벤트 채널 0을 통해 CCL로 전달되어 PB3 LED를 즉시 제어하고, 동시에 TCB0의 카운트를 증가시켜 인터럽트에서 카운트 값을 확인
  • 설정: PA0 입력(풀업), PB3 출력, 이벤트 채널 0, CCL 및 TCB0 사용자 설정
  • 출력: 버튼 누름/놓음에 따라 PB3 LED ON/OFF, TCB0 카운트 증가

5.2 예제 2: 타이머 이벤트 생성자 (TCA0 오버플로우로 ADC 및 TCB 트리거)

// File: evsys_timer_generator.c
// Description: AVR128DB48 이벤트 시스템 예제 (TCA0 오버플로우로 ADC 및 TCB 트리거)
// Compiler: AVR-GCC
// Target: AVR128DB48
#define F_CPU 24000000UL  // 시스템 클록 주파수를 24 MHz로 정의 (지연 함수 정확도를 위해 필요)

#include <avr/io.h>        // AVR 입출력 관련 헤더 파일
#include <avr/interrupt.h> // 인터럽트 처리 관련 헤더 파일
#include <util/delay.h>    // 지연 함수 관련 헤더 파일


volatile uint16_t timer_count = 0; // TCB0 카운트 값을 저장하는 전역 변수
volatile uint16_t adc_value = 0;   // ADC 값을 저장하는 전역 변수

void set_system_clock(void) {
	// 32.768 kHz 외부 크리스털 오실레이터(XOSC32K) 활성화
	_PROTECTED_WRITE(CLKCTRL.XOSC32KCTRLA, CLKCTRL_ENABLE_bm | CLKCTRL_RUNSTDBY_bm);
	// CLKCTRL.XOSC32KCTRLA 레지스터에 쓰기 보호 해제 후 설정
	// CLKCTRL_ENABLE_bm: XOSC32K 활성화
	// CLKCTRL_RUNSTDBY_bm: 스탠바이 모드에서도 XOSC32K 동작 유지

	// XOSC32K가 안정될 때까지 대기
	while (!(CLKCTRL.MCLKSTATUS & CLKCTRL_XOSC32KS_bm));
	// CLKCTRL.MCLKSTATUS의 XOSC32KS_bm 비트가 1이 될 때까지 대기 (안정화 확인)

	// 내부 OSCHF를 24 MHz로 설정하고 XOSC32K를 참조하여 오토튜닝 활성화
	_PROTECTED_WRITE(CLKCTRL.OSCHFCTRLA, CLKCTRL_FRQSEL_24M_gc | CLKCTRL_AUTOTUNE_bm);
	// CLKCTRL.OSCHFCTRLA 레지스터에 쓰기 보호 해제 후 설정
	// CLKCTRL_FRQSEL_24M_gc: OSCHF를 24 MHz로 설정
	// CLKCTRL_AUTOTUNE_bm: XOSC32K를 참조한 오토튜닝 활성화

	// OSCHF를 시스템 클록 소스로 설정
	_PROTECTED_WRITE(CLKCTRL.MCLKCTRLA, CLKCTRL_CLKSEL_OSCHF_gc);
	// CLKCTRL.MCLKCTRLA 레지스터에 쓰기 보호 해제 후 설정
	// CLKCTRL_CLKSEL_OSCHF_gc: 시스템 클록 소스를 OSCHF로 선택

	// 클록 프리스케일러 비활성화 (24 MHz 그대로 사용)
	_PROTECTED_WRITE(CLKCTRL.MCLKCTRLB, 0x00);
	// CLKCTRL.MCLKCTRLB 레지스터를 0으로 설정하여 프리스케일러 비활성화
}

void timer_init(void) {
	// TCA0 타이머 설정: 약 1ms 주기로 오버 이벤트 생성
	TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV256_gc | TCA_SINGLE_ENABLE_bm;
	// TCA0 클럭을 24MHz/256 = 93.75kHz로 설정하고 타이머 활성화
	// TCA_SINGLE_CLKSEL_DIV256_gc: 클럭 분주율 256
	// TCA_SINGLE_ENABLE_bm: 타이머 활성화

	TCA0.SINGLE.PER = 93; // 주기 설정: 93.75kHz / 93 = 약 1ms 오버플로우
}

void tcb_init(void) {
	// TCB0 설정: 이벤트 입력으로 카운트 증가
	TCB0.CTRLB = TCB_CNTMODE_INT_gc; // TCB0를 이벤트 입력 카운트 모드로 설정
	TCB0.CTRLA = TCB_CLKSEL_DIV1_gc | TCB_ENABLE_bm; // 24MHz 클럭 사용, 타이머 활성화
	// TCB_CLKSEL_CLKDIV1_gc: 클럭 분주 없이 24MHz 사용
	// TCB_ENABLE_bm: TCB0 타이머 활성화
}

void adc_init(void) {
	// ADC0 설정: PA0에서 아날로그 입력 읽기
	VREF.ADC0REF = VREF_REFSEL_2V500_gc; // ADC 레퍼런스 전압을 2.5V로 설정
	PORTA.DIRCLR = PIN0_bm;              // PA0 핀을 입력으로 설정 (아날로그 입력)
	ADC0.MUXPOS = ADC_MUXPOS_AIN0_gc;    // PA0를 ADC 입력 채널로 선택
	ADC0.CTRLA = ADC_ENABLE_bm;          // ADC 활성화, 기본 12비트 해상도
}

void evsys_init(void) {
	// 이벤트 채널 0: TCA0 오버플로우 이벤트 연결
	EVSYS.CHANNEL0 = EVSYS_CHANNEL0_TCA0_OVF_LUNF_gc; // TCA0 오버플로우를 이벤트 생성자로 선택
	// 비동기 모드로 설정 (기본값, SYNC 비트 설정 안 함)

	// 이벤트 사용자: ADC0 및 TCB0에 채널 0 연결
	EVSYS.USERADC0START = EVSYS_USER_CHANNEL0_gc; // ADC0에 이벤트 채널 0 연결 (변환 트리거)
	EVSYS.USERTCB0CAPT= EVSYS_USER_CHANNEL0_gc; // TCB0에 이벤트 채널 0 연결 (카운트 트리거)
}

ISR(TCB0_INT_vect) {
	// TCB0 인터럽트 서비스 루틴: TCA0 오버플로우 이벤트로 카운트 증가 시 호출
	timer_count = TCB0.CNT; // TCB0의 현재 카운트 값을 읽어 저장
	TCB0.INTFLAGS = TCB_CAPT_bm; // TCB0 캡처 인터럽트 플래그 클리어
}

ISR(ADC0_RESRDY_vect) {
	// ADC0 인터럽트 서비스 루틴: TCA0 오버플로우로 트리거된 ADC 변환 완료 시 호출
	adc_value = ADC0.RES; // 12비트 ADC 변환 결과 읽기 (0~4095)
	ADC0.INTFLAGS = ADC_RESRDY_bm; // ADC 결과 준비 인터럽트 플래그 클리어
}

int main(void) {
	// 시스템 클록 설정 호출
	set_system_clock(); // 24MHz 시스템 클럭과 XOSC32K 설정

	// 타이머 초기화 호출
	timer_init(); // TCA0 설정 (1ms 주기 오버플로우)

	// TCB0 초기화 호출
	tcb_init(); // TCB0 설정 (이벤트 카운트 모드)

	// ADC 초기화 호출
	adc_init(); // PA0 입력 및 ADC0 설정

	// 이벤트 시스템 초기화 호출
	evsys_init(); // TCA0 오버플로우를 ADC0 및 TCB0로 연결

	// 글로벌 인터럽트 활성화
	sei(); // ADC0 및 TCB0 인터럽트 서비스 루틴 실행 허용

	while (1) {
		// 무한 루프: TCA0 오버플로우 이벤트가 ADC0 변환과 TCB0 카운트를 주기적으로 트리거
		// adc_value 및 timer_count를 UART 또는 디버거로 출력 가능
		_delay_ms(100); // 결과 출력 주기 조정을 위한 대기
	}

	return 0; // 프로그램 종료 (실제로는 도달하지 않음)
}

설명:

  • 이벤트 생성자: TCA0 오버플로우 (1ms 주기)
  • 이벤트 사용자: ADC0 (PA0에서 변환 트리거), TCB0 (카운트 증가)
  • 기능: TCA0 오버플로우 이벤트가 이벤트 채널 0을 통해 ADC0 변환을 시작하고 TCB0 카운트를 증가시킴
  • 설정: TCA0(1ms 주기), PA0(ADC 입력), TCB0 이벤트 카운트, 이벤트 채널 0
  • 출력: ADC 값(12비트, 0~4095) 및 TCB0 카운트 증가 (1ms마다)

5.3 예제 3: ADC 이벤트 생성자 (ADC0 결과 준비로 TCB )

// File: evsys_adc_generator.c
// Description: AVR128DB48 이벤트 시스템 예제 (ADC0 결과 준비로 TCB 및 DAC 트리거)
// Compiler: AVR-GCC
// Target: AVR128DB48
#define F_CPU 24000000UL  // 시스템 클록 주파수를 24 MHz로 정의 (지연 함수 정확도를 위해 필요)

#include <avr/io.h>        // AVR 입출력 관련 헤더 파일
#include <avr/interrupt.h> // 인터럽트 처리 관련 헤더 파일
#include <util/delay.h>    // 지연 함수 관련 헤더 파일


volatile uint16_t timer_count = 0; // TCB0 카운트 값을 저장하는 전역 변수
volatile uint16_t adc_value = 0;   // ADC 값을 저장하는 전역 변수

void set_system_clock(void) {
	// 32.768 kHz 외부 크리스털 오실레이터(XOSC32K) 활성화
	_PROTECTED_WRITE(CLKCTRL.XOSC32KCTRLA, CLKCTRL_ENABLE_bm | CLKCTRL_RUNSTDBY_bm);
	// CLKCTRL.XOSC32KCTRLA 레지스터에 쓰기 보호 해제 후 설정
	// CLKCTRL_ENABLE_bm: XOSC32K 활성화
	// CLKCTRL_RUNSTDBY_bm: 스탠바이 모드에서도 XOSC32K 동작 유지

	// XOSC32K가 안정될 때까지 대기
	while (!(CLKCTRL.MCLKSTATUS & CLKCTRL_XOSC32KS_bm));
	// CLKCTRL.MCLKSTATUS의 XOSC32KS_bm 비트가 1이 될 때까지 대기 (안정화 확인)

	// 내부 OSCHF를 24 MHz로 설정하고 XOSC32K를 참조하여 오토튜닝 활성화
	_PROTECTED_WRITE(CLKCTRL.OSCHFCTRLA, CLKCTRL_FRQSEL_24M_gc | CLKCTRL_AUTOTUNE_bm);
	// CLKCTRL.OSCHFCTRLA 레지스터에 쓰기 보호 해제 후 설정
	// CLKCTRL_FRQSEL_24M_gc: OSCHF를 24 MHz로 설정
	// CLKCTRL_AUTOTUNE_bm: XOSC32K를 참조한 오토튜닝 활성화

	// OSCHF를 시스템 클록 소스로 설정
	_PROTECTED_WRITE(CLKCTRL.MCLKCTRLA, CLKCTRL_CLKSEL_OSCHF_gc);
	// CLKCTRL.MCLKCTRLA 레지스터에 쓰기 보호 해제 후 설정
	// CLKCTRL_CLKSEL_OSCHF_gc: 시스템 클록 소스를 OSCHF로 선택

	// 클록 프리스케일러 비활성화 (24 MHz 그대로 사용)
	_PROTECTED_WRITE(CLKCTRL.MCLKCTRLB, 0x00);
	// CLKCTRL.MCLKCTRLB 레지스터를 0으로 설정하여 프리스케일러 비활성화
}

void adc_init(void) {
	// ADC0 설정: PA0에서 아날로그 입력 읽기
	VREF.ADC0REF = VREF_REFSEL_2V500_gc; // ADC 레퍼런스 전압을 2.5V로 설정
	PORTA.DIRCLR = PIN0_bm;              // PA0 핀을 입력으로 설정 (아날로그 입력)
	ADC0.MUXPOS = ADC_MUXPOS_AIN0_gc;    // PA0를 ADC 입력 채널로 선택
	ADC0.CTRLA = ADC_ENABLE_bm | ADC_FREERUN_bm; // ADC 활성화, 프리 러닝 모드
	// ADC_FREERUN_bm: 연속 변환 모드 활성화
	ADC0.COMMAND = ADC_STCONV_bm;        // ADC 변환 시작
}

void tcb_init(void) {
	// TCB0 설정: 이벤트 입력으로 카운트 증가
	TCB0.CTRLB = TCB_CNTMODE_INT_gc; // TCB0를 이벤트 입력 카운트 모드로 설정
	TCB0.CTRLA = TCB_CLKSEL_DIV1_gc | TCB_ENABLE_bm; // 24MHz 클럭 사용, 타이머 활성화
	// TCB_CLKSEL_CLKDIV1_gc: 클럭 분주 없이 24MHz 사용
	// TCB_ENABLE_bm: TCB0 타이머 활성화
}

void dac_init(void) {
	// DAC0 설정: PA6에서 아날로그 출력
	VREF.DAC0REF |= VREF_REFSEL_2V500_gc; // DAC 레퍼런스 전압을 2.5V로 설정
	DAC0.CTRLA = DAC_ENABLE_bm | DAC_OUTEN_bm; // DAC 활성화, PA6 출력 활성화
}

void evsys_init(void) {
	// 이벤트 채널 0: ADC0 결과 준비 이벤트 연결
	EVSYS.CHANNEL0 = EVSYS_CHANNEL0_ADC0_RESRDY_gc; // ADC0 결과 준비를 이벤트 생성자로 선택
	// 비동기 모드로 설정 (기본값, SYNC 비트 설정 안 함)

	// 이벤트 사용자: TCB0  연결
	EVSYS.USERTCB0COUNT = EVSYS_USER_CHANNEL0_gc; // TCB0에 이벤트 채널 0 연결 (카운트 트리거)
}

ISR(TCB0_INT_vect) {
	// TCB0 인터럽트 서비스 루틴: ADC0 결과 준비 이벤트로 카운트 증가 시 호출
	timer_count = TCB0.CNT; // TCB0의 현재 카운트 값을 읽어 저장
	TCB0.INTFLAGS = TCB_CAPT_bm; // TCB0 캡처 인터럽트 플래그 클리어
}

ISR(ADC0_RESRDY_vect) {
	// ADC0 인터럽트 서비스 루틴: ADC 변환 완료 시 호출
	adc_value = ADC0.RES; // 12비트 ADC 변환 결과 읽기 (0~4095)
	DAC0.DATA = (adc_value >> 2) & 0x03FF; // 12비트 ADC 값을 10비트 DAC 값으로 변환하여 출력
	ADC0.INTFLAGS = ADC_RESRDY_bm; // ADC 결과 준비 인터럽트 플래그 클리어
}

int main(void) {
	// 시스템 클록 설정 호출
	set_system_clock(); // 24MHz 시스템 클럭과 XOSC32K 설정

	// ADC 초기화 호출
	adc_init(); // PA0 입력 및 ADC0 설정 (프리 러닝 모드)

	// TCB0 초기화 호출
	tcb_init(); // TCB0 설정 (이벤트 카운트 모드)

	// DAC 초기화 호출
	dac_init(); // DAC0 설정 (PA6 출력)

	// 이벤트 시스템 초기화 호출
	evsys_init(); // ADC0 결과 준비 이벤트를 TCB0 및 DAC0로 연결

	// 글로벌 인터럽트 활성화
	sei(); // ADC0 및 TCB0 인터럽트 서비스 루틴 실행 허용

	while (1) {
		// 무한 루프: ADC0 결과 준비 이벤트가 TCB0 카운트와 DAC0 출력을 트리거
		// adc_value 및 timer_count를 UART 또는 디버거로 출력 가능
		_delay_ms(100); // 결과 출력 주기 조정을 위한 대기
	}

	return 0; // 프로그램 종료 (실제로는 도달하지 않음)
}

설명:

  • 이벤트 생성자: ADC0 결과 준비 (프리 러닝 모드)
  • 이벤트 사용자: TCB0 (카운트 증가), DAC0 (PA6 출력 업데이트)
  • 기능: ADC0의 결과 준비 이벤트가 이벤트 채널 0을 통해 TCB0 카운트를 증가시키고 DAC0에 ADC 값을 출력
  • 설정: PA0(ADC 입력), PA6(DAC 출력), TCB0 이벤트 카운트, 이벤트 채널 0
  • 출력: ADC 값(12비트)을 PA6에서 아날로그 출력, TCB0 카운트 증가

6. 사용 방법

6.1 환경 설정

  • AVR-GCC 설치: Atmel Studio 또는 MPLAB X IDE에 AVR-GCC 툴체인 설치
  • 헤더 파일: <avr/io.h>, <util/delay.h> 포함
  • 프로젝트 설정: AVR128DB48 타겟으로 프로젝트 생성
  • 클럭 정의: #define F_CPU 24000000UL로 설정

6.2 코드 실행

  • 각 예제를 .c 파일로 저장하거나 main.c에 복사
  • Atmel Studio/MPLAB X IDE에서 빌드 및 플래싱

6.3 하드웨어 준비

  • 이벤트 출력: EVSYS EVOUTx로 핀 확인 (필요 시)
  • 전원: 1.8V~5.5V 공급
  • 디버깅 도구: 오실로스코프로 이벤트 신호 점검

6.4 디버깅

  • Atmel Studio 디버거 사용
  • EVSYS.CHANNELn, USERm 레지스터 확인
  • 이벤트 발생 타이밍 점검

7. 추가 팁

  • 생성자 선택: 애플리케이션에 적합한 생성자 확인 (예: RTC OVF, TCAn CMP)
  • 채널 관리: 중복 사용 피하기
  • Microchip 리소스: AVR128DB48 데이터시트, Application Notes 참조
  • 문제 해결:
    • 이벤트 없음: CHANNELn, USERm 설정 확인
    • 지연: 동기/비동기 설정 점검
  • 커뮤니티: Microchip Community, AVR Freaks 참고

8. 결론

이 문서는 AVR128DB48 EVSYS 모듈의 설정 방법과 Bitfield 구조를 활용한 예제 코드를 제공하여 저전력, 저지연 통신 애플리케이션에 적용 가능하도록 구성했습니다. 초보자, 숙련자는 소프트웨어 트리거 예제를 확장하여 복잡한 시스템을 구현할 수 있습니다.

키워드: AVR128DB48, EVSYS, Event System, 마이크로컨트롤러, Atmel Studio, MPLAB X IDE, 저전력 통신, 인터럽트