본문 바로가기
MCU/AVR

[AVR128DB48] 이벤트 시스템 사용 방법 및 예제 코드

by linuxgo 2025. 8. 20.
반응형

1. AVR128DB48 이벤트 시스템 모듈 개요

Microchip의 AVR128DB48은 AVR DB 시리즈의 8비트 마이크로컨트롤러로, 이벤트 시스템(Event System)을 통해 주변 장치 간의 효율적인 비동기 통신을 지원합니다. 이벤트 시스템은 CPU 개입 없이 하드웨어 이벤트(예: GPIO 핀 입력, 타이머 오버플로우, ADC 완료)를 다른 주변 장치로 전달하여 저지연, 저전력 애플리케이션(예: 센서 트리거, PWM 제어, 인터럽트 처리)에 적합합니다. 이 문서는 이벤트 생성자(GPIO, 타이머, ADC)를 기준으로 AVR128DB48의 이벤트 시스템 설정 방법, Bitfield 구조를 활용한 레지스터 설정, 그리고 실용적인 예제 코드를 제공하여 초보자와 숙련된 개발자 모두 쉽게 활용할 수 있도록 돕습니다.

이벤트 시스템 주요 사양

  • 채널: 6개의 이벤트 채널 (EVSYS.CHANNEL0 ~ CHANNEL5)
  • 이벤트 생성자: GPIO 핀, 타이머(TCA/TCB), ADC, CCL, USART 등
  • 이벤트 사용자: 타이머, ADC, DAC, CCL, TWI 등
  • 주요 기능:
    •   비동기 이벤트 라우팅
    •   동기/비동기 채널 선택
    •   이벤트 스트로브 및 엣지 감지
  • 주요 레지스터:
    •   EVSYS.CHANNELn: 이벤트 생성자 선택 (n=0~5)
    •   EVSYS.USERm: 이벤트 사용자 설정 (m=0~15)
    •   EVSYS.STROBE: 이벤트 스트로브 트리거
  • 클럭 소스: 시스템 클럭 (최대 24MHz, OSCHF 내부 오실레이터)
  • 전력 관리: 저전력 모드에서 이벤트 시스템 동작 지원
  • 전압 레벨: 1.8V~5.5V 동작 지원

2. 이벤트 시스템 Bitfield 설정 상세

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

2.1 EVSYS.CHANNELn (이벤트 채널 설정, n=0~5)

  •   GEN: 이벤트 생성자 선택
    •   예: EVSYS_GEN_PORTA_PIN0_gc (PA0 핀 이벤트)
    •   예: EVSYS_GEN_TCA0_OVF_LUNF_gc (TCA0 오버플로우 이벤트)
    •   예: EVSYS_GEN_ADC0_RESRDY_gc (ADC0 결과 준비 이벤트)
  •   SYNC: 동기/비동기 모드 선택 (1: 동기, 0: 비동기)

2.2 EVSYS.USERm (이벤트 사용자 설정, m=0~15)

  •   CHANNEL: 이벤트 채널 선택
    •   예: EVSYS_CHANNEL_CHANNEL0_gc (채널 0 선택)
  •   사용자 예:
    •   EVSYS_USER_TCB0_CAPT (TCB0 캡처 이벤트)
    •   EVSYS_USER_ADC0_START (ADC0 변환 시작)
    •   EVSYS_USER_CCL (CCL 입력)

2.3 EVSYS.STROBE

  •   CHnSTR: 특정 채널에 이벤트 스트로브 트리거 (n=0~5)
    •   예: EVSYS_STROBE_CH0STR_bm (채널 0 스트로브)

3. 이벤트 시스템 설정 절차

  1. 시스템 초기화:
    •   set_system_clock()로 시스템 클럭 설정 (24MHz, XOSC32K 오토튜닝)
    •   인터럽트 비활성화 (cli())
  2. 이벤트 생성자 설정:
    •   EVSYS.CHANNELn에 이벤트 소스 선택 (예: GPIO, 타이머, ADC)
  3. 이벤트 사용자 설정:
    •   EVSYS.USERm에 이벤트 채널 연결 (예: ADC, 타이머, CCL)
  4. 이벤트 채널 활성화:
    •   별도의 활성화 불필요 (설정 후 즉시 동작)
  5. 이벤트 트리거:
    •   이벤트 소스 발생 시 자동 라우팅 또는 EVSYS.STROBE로 수동 트리거
  6. 결과 확인:
    •   연결된 주변 장치의 동작 확인 (예: ADC 결과, 타이머 카운트, CCL 출력)

4. 이벤트 시스템 설정 고려사항

  • 클럭 안정성: 시스템 클럭과 XOSC32K 안정화 확인
  • 이벤트 충돌: 동일 채널에 다중 생성자/사용자 연결 주의
  • 비동기 모드: 고속 이벤트 처리 시 비동기 모드 권장
  • 저전력: 이벤트 시스템은 CPU 개입 최소화로 전력 소모 감소
  • 멀티플렉싱: GPIO 핀이 이벤트 소스로 사용될 경우 다른 기능(UART, SPI 등) 충돌 주의
  • 이벤트 타이밍: 생성자와 사용자의 클럭 동기화 주의 (동기 모드 사용 시)

5. 실용적인 이벤트 시스템 예제 코드 (생성자 기준)

아래는 AVR128DB48 이벤트 시스템을 Bitfield 구조로 설정한 3개의 예제 코드로, 각 예제는 특정 이벤트 생성자(GPIO 핀, 타이머, ADC)를 기준으로 구성되었습니다. 모든 코드에는 상세한 주석이 포함되어 있으며, 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

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

#define F_CPU 24000000UL  // 시스템 클록 주파수를 24 MHz로 정의 (지연 함수 정확도를 위해 필요)

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_CLKDIV1_gc | TCB_ENABLE_bm; // 24MHz 클럭 사용, 타이머 활성화
    // TCB_CLKSEL_CLKDIV1_gc: 클럭 분주 없이 24MHz 사용
    // TCB_ENABLE_bm: TCB0 타이머 활성화
}

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

    // 이벤트 사용자: CCL 및 TCB0에 채널 0 연결
    EVSYS.USERCCL = EVSYS_CHANNEL_CHANNEL0_gc; // CCL에 이벤트 채널 0 연결
    EVSYS.USERTCB0 = EVSYS_CHANNEL_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

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

#define F_CPU 24000000UL  // 시스템 클록 주파수를 24 MHz로 정의 (지연 함수 정확도를 위해 필요)

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 주기로 오버 Seungcheol 이벤트 생성
    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_CLKDIV1_gc | TCB_ENABLE_bm; // 24MHz 클럭 사용, 타이머 활성화
    // TCB_CLKSEL_CLKDIV1_gc: 클럭 분주 없이 24MHz 사용
    // TCB_ENABLE_bm: TCB0 타이머 활성화
}

void adc_init(void) {
    // ADC0 설정: PA0에서 아날로그 입력 읽기
    VREF.CTRLA = VREF_ADC0REFSEL_2V5_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_GEN_TCA0_OVF_LUNF_gc; // TCA0 오버플로우를 이벤트 생성자로 선택
    // 비동기 모드로 설정 (기본값, SYNC 비트 설정 안 함)

    // 이벤트 사용자: ADC0 및 TCB0에 채널 0 연결
    EVSYS.USERADC0 = EVSYS_CHANNEL_CHANNEL0_gc; // ADC0에 이벤트 채널 0 연결 (변환 트리거)
    EVSYS.USERTCB0 = EVSYS_CHANNEL_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 및 DAC 트리거)

// File: evsys_adc_generator.c
// Description: AVR128DB48 이벤트 시스템 예제 (ADC0 결과 준비로 TCB 및 DAC 트리거)
// Compiler: AVR-GCC
// Target: AVR128DB48

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

#define F_CPU 24000000UL  // 시스템 클록 주파수를 24 MHz로 정의 (지연 함수 정확도를 위해 필요)

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.CTRLA = VREF_ADC0REFSEL_2V5_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_CLKDIV1_gc | TCB_ENABLE_bm; // 24MHz 클럭 사용, 타이머 활성화
    // TCB_CLKSEL_CLKDIV1_gc: 클럭 분주 없이 24MHz 사용
    // TCB_ENABLE_bm: TCB0 타이머 활성화
}

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

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

    // 이벤트 사용자: TCB0 및 DAC에 채널 0 연결
    EVSYS.USERTCB0 = EVSYS_CHANNEL_CHANNEL0_gc; // TCB0에 이벤트 채널 0 연결 (카운트 트리거)
    EVSYS.USERDAC0 = EVSYS_CHANNEL_CHANNEL0_gc; // DAC0에 이벤트 채널 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>, <avr/interrupt.h>, <util/delay.h> 포함
  • 프로젝트 설정: AVR128DB48 타겟으로 프로젝트 생성
  • 클럭 정의: #define F_CPU 24000000UL로 시스템 클럭 설정

6.2 코드 실행

  • 각 예제를 별도의 .c 파일로 저장하거나, main.c에 복사
  • 다른 예제 코드 주석 처리
  • Atmel Studio/MPLAB X IDE에서 빌드 및 플래싱

6.3 하드웨어 준비

  • GPIO 입력: PA0에 버튼(예제 1) 또는 아날로그 신호(예: 가변 저항, 예제 2, 3) 연결
  • LED 출력: PB3에 LED 및 전류 제한 저항(예: 330Ω) 연결 (예제 1)
  • DAC 출력: PA6에 멀티미터 또는 오실로스코프로 출력 확인 (예제 3)
  • 전원: 1.8V~5.5V 전원 공급
  • 디바운싱: 버튼 입력 시 10ms 이상 대기 또는 하드웨어 디바운싱 회로 추가

6.4 디버깅

  • Atmel Studio/MPLAB X IDE의 디버거 사용
  • EVSYS.CHANNELn, EVSYS.USERm, 관련 주변 장치 레지스터(ADC0.RES, TCB0.CNT, DAC0.DATA) 확인
  • 오실로스코프 또는 로직 분석기로 이벤트 신호 및 출력 점검

7. 추가 팁

  • 클럭 설정: set_system_clock()로 XOSC32K 안정화 및 24MHz 설정 확인
  • 노이즈 감소: 입력 핀에 풀업 저항 및 외부 디바운싱 회로 추가, 아날로그 입력에 커패시터(0.1µF) 사용
  • Microchip 리소스: AVR128DB48 데이터시트, Application Notes, AVR-GCC 예제
  • 문제 해결:
    •   이벤트 미작동: EVSYS.CHANNELn 및 EVSYS.USERm 설정, 생성자/사용자 활성화 확인
    •   출력 오류: ADC, TCB, DAC, CCL 레지스터 및 핀 설정 확인
    •   버튼 입력 불안정: 디바운싱 처리(소프트웨어/하드웨어) 추가
  • 커뮤니티: Microchip Community, AVR Freaks 포럼 참고

8. 결론

이 문서는 AVR128DB48의 이벤트 시스템 모듈을 이벤트 생성자(GPIO, 타이머, ADC)를 기준으로 설정 방법과 Bitfield 구조를 활용한 예제 코드를 제공하여 CPU 개입 없이 주변 장치 간 통신을 구현할 수 있도록 구성했습니다. 상세한 주석이 포함된 코드는 초보자가 각 생성자의 동작을 쉽게 이해하도록 돕고, 숙련자는 다중 사용자 연결 및 복잡한 이벤트 라우팅을 활용하여 효율적인 시스템을 구현할 수 있도록 지원합니다.

키워드: AVR128DB48, 이벤트 시스템, AVR, 마이크로컨트롤러, Atmel Studio, MPLAB X IDE, 이벤트 생성자, GPIO 이벤트, 타이머 오버플로우, ADC 결과 준비, CCL, TCB, DAC

반응형