본 문서는 Microchip AVR128DA64/48/32/28 시리즈에 내장된 아날로그 비교기(AC0, AC1, AC2) 모듈을 분석하고, 이를 활용한 범용 드라이버 설계 및 구현 방법을 설명합니다. 드라이버는 단일/윈도우 비교 모드, 히스테리시스, 전력 모드, 인터럽트 및 폴링 기반 처리, 이벤트 시스템(EVSYS) 통합, 슬립 모드 동작을 지원하며, 모든 AVR128DA 제품군(28/32/48/64핀)과 호환됩니다. AC0_AC(0x2E), AC1_AC(0x38), AC2_AC(0x50) 인터럽트를 활용해 효율적인 비교 처리를 제공하며, AVR-GCC 및 Microchip Studio 환경에서 동작합니다. 다양한 입력 선택과 전력 최적화를 지원합니다.
사양
AVR128DA 시리즈의 AC 사양은 다음과 같습니다:
- 모듈 수: AC0, AC1, AC2 (모든 모델 공통).
- 핀 할당:
- 양극 입력: AINP0~AINP3 (PD2, PE0, PE2, PD6 등, 모델별 상이).
- 음극 입력: AINN0~AINN2 (PD3, PD5, PD0, PD7 등).
- 출력: PA3 (AC0_OUT, AC1_OUT, AC2_OUT), PC3 (AC1_OUT), PD3 (AC2_OUT).
- 기능:
- 단일 모드: 양극/음극 입력 비교, 디지털 출력(CMP).
- 윈도우 모드: 두 비교기 쌍으로 전압 범위 비교 (Above, Inside, Below, Outside).
- 히스테리시스: None, Small(10mV), Medium(25mV), Large(50mV).
- 전력 모드: Low, Medium, High.
- 인터럽트: Rising, Falling, Both edges (AC0_AC: 0x2E, AC1_AC: 0x38, AC2_AC: 0x50).
- 이벤트 생성: ACn.OUT (비동기, 레벨 기반).
- 기준 전압: VREF (1.024V, 2.048V, 2.500V, 4.096V, VDD, VREFA), DACREF.
- 슬립 모드: Idle, Standby (RUNSTDBY=1), Power-Down.
- 클럭: CLK_PER (최대 24MHz), 비동기 이벤트 지원.
- 전력 최적화: ENABLE=0으로 전력 소모 없음.
- 리셋 상태: AC 비활성화, 출력 핀은 삼상태(tri-state).
인터럽트 벡터
모듈 | 벡터 번호 | 프로그램 주소 (워드) | 번호 소스 | 설명 |
AC0 | 23 | 0x2E | AC0_AC | 비교기 출력 토글 인터럽트 |
AC1 | 28 | 0x38 | AC1_AC | 비교기 출력 토글 인터럽트 |
AC2 | 40 | 0x50 | AC2_AC | 비교기 출력 토글 인터럽트 |
이벤트 시스템 (EVSYS) 통합
- 이벤트 생성기: ACn.OUT (비동기, 레벨 기반, AC0_OUT=0x20, AC1_OUT=0x21, AC2_OUT=0x22, n은 채널 번호 0~9).
- 채널: 최대 10개 (CHANNEL0~9), EVSYS.CHANNELn 레지스터로 설정.
- 사용자: ADC0_START (0x0C), TCAn.CNTA/CNTB, TCBn.CAPT, EVOUTx (PA2, PB2, PC2 등).
- 소프트웨어 이벤트: EVSYS.SWEVENTA (채널 0~7), EVSYS.SWEVENTA (채널 8~9) 로 펄스 이벤트 생성.
- 비동기 이벤트: ACn.OUT은 CLK_PER 없이 즉시 응답, Standby 모드에서 동작 (RUNSTDBY=1).
- 동기화: 비동기 이벤트가 동기 사용자(예: TCA)에 연결될 경우 2~3 CLK_PER 지연 발생.
VREF - Voltage Reference
- 기준 전압 소스: ADC0, DAC0, ACs가 각각 독립적인 VREF 선택 가능.
- 지원 전압: 1.024V, 2.048V, 2.500V, 4.096V, VDD, VREFA.
- ALWAYSON: 기준 전압 상시 활성화로 시작 시간 단축 (전력 소모 증가).
- VREF.ACREF 레지스터:
- 비트 7: ALWAYSON (0=자동, 1=항상 활성).
- 비트 2:0: REFSEL[2:0] (1.024V=0x0, 2.048V=0x1, 4.096V=0x2, 2.500V=0x3, VDD=0x5, VREFA=0x6).
레지스터 설정 상세
AC는 ACn 레지스터를 통해 제어됩니다. 데이터시트의 Register Summary에 따라 주요 레지스터와 비트필드는 다음과 같습니다.
- ACn.CTRLA (0x00):
- 비트 [7]: RUNSTDBY (Standby 모드에서 동작, 0=비활성, 1=활성).
- 비트 [4:3]: POWER[1:0] (전력 모드: 0x0=Low, 0x1=Medium, 0x2=High).
- 비트 [3]: EVOUT (이벤트 출력 활성화).
- 비트 [2:1]: HYSMODE[1:0] (히스테리시스: 0x0=None, 0x1=Small, 0x2=Medium, 0x3=Large).
- 비트 [1]: OUTEN (외부 핀 출력 활성화).
- 비트 [0]: ENABLE (AC 활성화).
- 리셋: 0x00, R/W.
- ACn.CTRLB (0x01):
- 비트 [1:0]: WINSEL[1:0] (윈도우 모드: 0x0=Below, 0x1=Above, 0x2=Inside, 0x3=Outside).
- 리셋: 0x00, R/W.
- ACn.MUXCTRL (0x02):
- 비트 [7]: INVERT (출력 반전).
- 비트 [6]: INITVAL (초기 출력값).
- 비트 [5:3]: MUXPOS[2:0] (양극 입력: AINP0~AINP3).
- 비트 [2:0]: MUXNEG[2:0] (음극 입력: AINN0~AINN2, DACREF, VREF).
- 리셋: 0x00, R/W.
- ACn.INTCTRL (0x04):
- 비트 [2:1]: INTMODE[1:0] (인터럽트 모드: 0x0=Both, 0x1=Rising, 0x2=Falling).
- 비트 [0]: CMP (인터럽트 활성화).
- 리셋: 0x00, R/W.
- ACn.STATUS (0x05):
- 비트 [3:2]: WINSTATE[1:0] (윈도우 상태: Above, Inside, Below, Outside).
- 비트 [1]: CMPSTATE (비교기 출력 상태).
- 비트 [0]: CMP (인터럽트 플래그, 1 작성으로 클리어).
- 리셋: 0x00, R.
- ACn.DACREF (0x06):
- 비트 [7:0]: DACREF[7:0] (DAC 기준 전압: VDACREF = DACREF/256 × VREF).
- 리셋: 0x00, R/W.
AC 설정 절차
- 클럭 설정: CLKCTRL로 CLK_PER을 24MHz OSCHF로 설정.
- VREF 설정: VREF.ACREF로 기준 전압 선택 (1.024V, 2.048V, 2.500V, 4.096V, VDD, VREFA).
- 입력 핀 설정: PORTx.DIRCLR로 AINPx, AINNx를 아날로그 입력으로 설정.
- 입력 선택: ACn.MUXCTRL의 MUXPOS, MUXNEG로 양극/음극 입력 선택 (DACREF는 음극 입력으로 사용 가능).
- 히스테리시스 설정: ACn.CTRLA의 HYSMODE로 노이즈 방지.
- 전력 모드 설정: ACn.CTRLA의 POWER로 응답 시간/전력 소모 조정.
- 윈도우 모드 설정 (옵션): ACn.CTRLB의 WINSEL로 비교 모드 설정, DACREF로 기준값 설정.
- 이벤트 설정 (옵션): EVSYS.CHANNELn에 ACn.OUT 설정 (n=0~9), 사용자(예: ADC0_START) 연결.
- 인터럽트 설정 (옵션): ACn.INTCTRL의 INTMODE로 Rising/Falling/Both 설정.
- AC 활성화: ACn.CTRLA의 ENABLE로 비교 시작.
- 결과 처리: ACn.STATUS의 CMPSTATE/WINSTATE 읽기, CMP 플래그 확인 및 클리어.
AC 동작
- 비교 시작: ENABLE=1로 비교 시작, 비동기 이벤트로 Standby 모드 동작 가능.
- 출력: CMPSTATE=1 (양극 > 음극), 0 (그 외). OUTEN=1로 외부 핀(PA3, PC3, PD3) 출력.
- 윈도우 비교: ACn과 ACm 쌍으로 전압 범위 비교, WINSTATE로 상태 확인.
- 이벤트: ACn.OUT을 EVSYS를 통해 ADC0_START, TCAn.CNTA 등으로 라우팅.
- 타이밍: 비동기 이벤트는 CLK_PER 없이 즉시 응답, 동기 사용자는 2~3 CLK_PER 지연.
- 결과 처리: CMPSTATE, WINSTATE 읽기, CMP 플래그로 인터럽트 발생.
드라이버 구현 내용
드라이버는 AVR128DA64/48/32/28 호환으로 설계되었으며, AC 기능을 추상화합니다. 주요 구현은 다음과 같습니다:
- 인터럽트 처리: AC0_AC(0x2E), AC1_AC(0x38), AC2_AC(0x50)로 비교 출력 토글 처리. 표준 ISR 사용.
- 비교 모드: 단일 모드, 윈도우 모드(Below, Above, Inside, Outside).
- 입력 선택: AINP0 ~AINP3 AINN0~ AINN2, DACREF, VREF.
- 히스테리시스: None, Small, Medium, Large.
- 이벤트 시스템: ACn.OUT을 EVSYS.CHANNELn에 연결, ADC0_START, EVOUTx(PA2 등) 지원.
- 소프트웨어 이벤트: EVSYS.SWEVENTA/B로 펄스 이벤트 생성.
- 슬립 모드: RUNSTDBY=1로 Standby 모드에서 비동기 이벤트 처리.
- 함수:
- ac_init: AC 초기화 (입력, 히스테리시스, 전력 모드, 윈도우 모드 등).
- ac_start: 비교 시작.
- ac_read: 폴링 기반 결과 읽기.
- ac_read_it: 인터럽트 기반 결과 읽기.
- ac_read_win_state: 윈도우 상태 읽기.
- ac_window_config: 윈도우 비교 설정.
- ac_enable_event: 이벤트 출력 및 EVSYS 채널 설정.
- ac_generate_swevent: 소프트웨어 이벤트 생성.
- ac_enable/disable: AC 활성화/비활성화.
- ac_set_invert: 출력 반전 설정.
- ac_set_initval: 초기 출력값 설정.
- ac_check_flags: CMP 플래그 확인 및 클리어.
사용방법
AC 드라이버를 사용하는 방법은 다음과 같습니다:
- 헤더 파일 포함: 프로젝트에 ac_driver.h를 포함하고, <avr/io.h>, <stdio.h>, <util/delay.h>를 추가합니다.
- 클럭 설정: main.c의 clock_init_24mhz를 호출하여 24MHz OSCHF 클럭을 설정합니다.
- AC 인스턴스 생성: AC_Instance 구조체를 선언하고, ac_init으로 초기화합니다. 예:
AC_Instance ac0_instance; ac_init(&ac0_instance, &AC0, AC_MUXPOS_AINP0, AC_MUXNEG_VREF, AC_REF_2V048, AC_HYSMODE_MEDIUM, AC_POWER_MEDIUM, AC_WINSEL_INSIDE, 1, 1);
- AC 활성화: ac_enable으로 AC를 활성화합니다.
- 비교 시작: ac_start로 비교 시작.
- 데이터 읽기:
- 폴링 기반: ac_read로 CMPSTATE 읽기, ac_read_win_state로 WINSTATE 읽기.
- 인터럽트 기반: ac_read_it_init으로 인터럽트 활성화 후, ac_read_it으로 결과 읽기.
- 이벤트 설정: ac_enable_event로 ACn.OUT을 EVSYS 채널(n=0~9)에 연결 (예: ADC0_START).
- 윈도우 비교 (옵션): ac_window_config로 DACREF 설정.
- 플래그 확인: ac_check_flags로 CMP 상태 확인 및 클리어.
코드 구현
AC 드라이버 코드는 다음과 같습니다:
ac_driver.h
/**
* @file ac_driver.h
* @brief AVR128DA64/48/32/28 아날로그 비교기(AC) 드라이버 헤더 파일
* @details AC0, AC1, AC2를 지원하며, 단일/윈도우 비교, 히스테리시스, 전력 모드, 인터럽트,
* 이벤트 시스템(EVSYS) 통합, 슬립 모드 동작을 제공. AVR-GCC 및 Microchip Studio 호환.
* 이벤트 시스템 상수는 <avr/io.h>에 정의된 값을 사용.
* @author linuxgo
* @date 2025-09-04
*/
#ifndef AC_DRIVER_H
#define AC_DRIVER_H
#include <avr/io.h>
#include <stdint.h>
#include <stdio.h>
/**
* @brief 모델별 핀 매핑
* @details AVR128DA 시리즈(28/32/48/64핀)에 따라 AC 입력 및 출력 핀 정의.
* 각 모델별로 사용 가능한 AINP, AINN, OUT, EVOUT 핀을 명시.
*/
#if defined(__AVR_AVR128DA28__) || defined(__AVR_AVR64DA28__) || defined(__AVR_AVR32DA28__)
#define AC0_AINP0_PIN PORTD, 2 // AC0 양극 입력 0 (PD2)
#define AC0_AINN0_PIN PORTD, 3 // AC0 음극 입력 0 (PD3)
#define AC0_OUT_PIN PORTA, 3 // AC0 출력 (PA3)
#define AC1_AINP0_PIN PORTD, 2 // AC1 양극 입력 0 (PD2)
#define AC1_AINN0_PIN PORTD, 5 // AC1 음극 입력 0 (PD5)
#define AC1_OUT_PIN PORTC, 3 // AC1 출력 (PC3)
#define AC2_AINP0_PIN PORTD, 2 // AC2 양극 입력 0 (PD2)
#define AC2_AINN0_PIN PORTD, 7 // AC2 음극 입력 0 (PD7)
#define AC2_OUT_PIN PORTD, 3 // AC2 출력 (PD3)
#define EVOUT_PIN PORTA, 2 // 이벤트 출력 A (PA2, EVOUTA)
#elif defined(__AVR_AVR128DA32__) || defined(__AVR_AVR64DA32__) || defined(__AVR_AVR32DA32__)
#define AC0_AINP0_PIN PORTD, 2
#define AC0_AINN0_PIN PORTD, 3
#define AC0_OUT_PIN PORTA, 3
#define AC1_AINP0_PIN PORTD, 2
#define AC1_AINN0_PIN PORTD, 5
#define AC1_OUT_PIN PORTC, 3
#define AC2_AINP0_PIN PORTD, 2
#define AC2_AINN0_PIN PORTD, 7
#define AC2_OUT_PIN PORTD, 3
#define EVOUT_PIN PORTA, 2
#elif defined(__AVR_AVR128DA48__) || defined(__AVR_AVR64DA48__) || defined(__AVR_AVR32DA48__)
#define AC0_AINP0_PIN PORTD, 2
#define AC0_AINN0_PIN PORTD, 3
#define AC0_OUT_PIN PORTA, 3
#define AC1_AINP0_PIN PORTD, 2
#define AC1_AINN0_PIN PORTD, 5
#define AC1_OUT_PIN PORTC, 3
#define AC2_AINP0_PIN PORTD, 2
#define AC2_AINN0_PIN PORTD, 7
#define AC2_OUT_PIN PORTD, 3
#define AC0_AINP1_PIN PORTE, 0 // AC0 양극 입력 1 (PE0)
#define AC0_AINP2_PIN PORTE, 2 // AC0 양극 입력 2 (PE2)
#define AC2_AINP2_PIN PORTE, 1 // AC2 양극 입력 2 (PE1)
#define EVOUT_PIN PORTA, 2
#elif defined(__AVR_AVR128DA64__) || defined(__AVR_AVR64DA64__)
#define AC0_AINP0_PIN PORTD, 2
#define AC0_AINN0_PIN PORTD, 3
#define AC0_OUT_PIN PORTA, 3
#define AC1_AINP0_PIN PORTD, 2
#define AC1_AINN0_PIN PORTD, 5
#define AC1_OUT_PIN PORTC, 3
#define AC2_AINP0_PIN PORTD, 2
#define AC2_AINN0_PIN PORTD, 7
#define AC2_OUT_PIN PORTD, 3
#define AC0_AINP1_PIN PORTE, 0
#define AC0_AINP2_PIN PORTE, 2
#define AC2_AINP2_PIN PORTE, 1
#define EVOUT_PIN PORTA, 2
#endif
/**
* @brief AC 설정 정의
* @details AC 모듈의 입력 선택, 기준 전압, 히스테리시스, 전력 모드, 윈도우 모드,
* 인터럽트 모드를 설정하기 위한 상수 정의.
*/
#define AC_MUXPOS_AINP0 0x0 // 양극 입력: AINP0
#define AC_MUXPOS_AINP1 0x1 // 양극 입력: AINP1
#define AC_MUXPOS_AINP2 0x2 // 양극 입력: AINP2
#define AC_MUXPOS_AINP3 0x3 // 양극 입력: AINP3
#define AC_MUXNEG_AINN0 0x0 // 음극 입력: AINN0
#define AC_MUXNEG_AINN1 0x1 // 음극 입력: AINN1
#define AC_MUXNEG_DACREF 0x2 // 음극 입력: DACREF
#define AC_MUXNEG_VREF 0x3 // 음극 입력: VREF
#define AC_REF_1V024 0x0 // 기준 전압: 1.024V
#define AC_REF_2V048 0x1 // 기준 전압: 2.048V
#define AC_REF_4V096 0x2 // 기준 전압: 4.096V
#define AC_REF_2V500 0x3 // 기준 전압: 2.500V
#define AC_REF_VDD 0x5 // 기준 전압: VDD
#define AC_REF_VREFA 0x6 // 기준 전압: VREFA
#define AC_HYSMODE_NONE 0x0 // 히스테리시스: 없음
#define AC_HYSMODE_SMALL 0x1 // 히스테리시스: Small (10mV)
#define AC_HYSMODE_MEDIUM 0x2 // 히스테리시스: Medium (25mV)
#define AC_HYSMODE_LARGE 0x3 // 히스테리시스: Large (50mV)
#define AC_POWER_LOW 0x0 // 전력 모드: Low
#define AC_POWER_MEDIUM 0x1 // 전력 모드: Medium
#define AC_POWER_HIGH 0x2 // 전력 모드: High
#define AC_WINSEL_BELOW 0x0 // 윈도우 모드: Below
#define AC_WINSEL_ABOVE 0x1 // 윈도우 모드: Above
#define AC_WINSEL_INSIDE 0x2 // 윈도우 모드: Inside
#define AC_WINSEL_OUTSIDE 0x3 // 윈도우 모드: Outside
#define AC_INTMODE_BOTH 0x0 // 인터럽트 모드: 양쪽 에지
#define AC_INTMODE_RISING 0x1 // 인터럽트 모드: 상승 에지
#define AC_INTMODE_FALLING 0x2 // 인터럽트 모드: 하강 에지
/**
* @brief AC 인스턴스 구조체
* @details AC 모듈의 상태와 설정을 관리하기 위한 구조체.
* ac: AC 레지스터 포인터, cmp_result: 비교 결과,
* win_state: 윈도우 모드 상태, flags: 인터럽트 플래그.
*/
typedef struct {
AC_t *ac; // AC 모듈 레지스터 포인터 (AC0, AC1, AC2)
volatile uint8_t cmp_result; // 비교 결과 (CMPSTATE: 0 또는 1)
volatile uint8_t win_state; // 윈도우 모드 상태 (Above, Inside, Below, Outside)
volatile uint8_t flags; // 인터럽트 플래그 (CMP)
} AC_Instance;
/**
* @brief AC 초기화
* @param instance AC 인스턴스 포인터
* @param ac AC 모듈 (AC0, AC1, AC2)
* @param muxpos 양극 입력 선택 (AC_MUXPOS_AINPx)
* @param muxneg 음극 입력 선택 (AC_MUXNEG_AINNx, DACREF, VREF)
* @param ref 기준 전압 선택 (AC_REF_x)
* @param hysmode 히스테리시스 모드 (AC_HYSMODE_x)
* @param power 전력 모드 (AC_POWER_x)
* @param winsel 윈도우 모드 선택 (AC_WINSEL_x)
* @param outen 출력 핀 활성화 (0=비활성, 1=활성)
* @param runstdby Standby 모드 동작 (0=비활성, 1=활성)
*/
void ac_init(AC_Instance *instance, AC_t *ac, uint8_t muxpos, uint8_t muxneg, uint8_t ref, uint8_t hysmode, uint8_t power, uint8_t winsel, uint8_t outen, uint8_t runstdby);
/**
* @brief AC 활성화
* @param instance AC 인스턴스 포인터
*/
void ac_enable(AC_Instance *instance);
/**
* @brief AC 비활성화
* @param instance AC 인스턴스 포인터
*/
void ac_disable(AC_Instance *instance);
/**
* @brief 비교 시작
* @param instance AC 인스턴스 포인터
*/
void ac_start(AC_Instance *instance);
/**
* @brief 폴링 기반 결과 읽기
* @param instance AC 인스턴스 포인터
* @return CMPSTATE (0: 양극 <= 음극, 1: 양극 > 음극)
*/
uint8_t ac_read(AC_Instance *instance);
/**
* @brief 인터럽트 기반 결과 읽기 초기화
* @param instance AC 인스턴스 포인터
* @param intmode 인터럽트 모드 (AC_INTMODE_x)
*/
void ac_read_it_init(AC_Instance *instance, uint8_t intmode);
/**
* @brief 인터럽트 기반 결과 읽기
* @param instance AC 인스턴스 포인터
* @return CMPSTATE (0: 양극 <= 음극, 1: 양극 > 음극)
*/
uint8_t ac_read_it(AC_Instance *instance);
/**
* @brief 윈도우 비교 상태 읽기
* @param instance AC 인스턴스 포인터
* @return WINSTATE (0: Below, 1: Above, 2: Inside, 3: Outside)
*/
uint8_t ac_read_win_state(AC_Instance *instance);
/**
* @brief 윈도우 비교 설정
* @param instance AC 인스턴스 포인터
* @param dacref DAC 기준 전압 값 (0~255)
*/
void ac_window_config(AC_Instance *instance, uint8_t dacref);
/**
* @brief 이벤트 출력 활성화 및 EVSYS 채널 설정
* @param instance AC 인스턴스 포인터
* @param channel EVSYS 채널 번호 (0~9)
*/
void ac_enable_event(AC_Instance *instance, uint8_t channel);
/**
* @brief 출력 반전 설정
* @param instance AC 인스턴스 포인터
* @param invert 출력 반전 (0=비반전, 1=반전)
*/
void ac_set_invert(AC_Instance *instance, uint8_t invert);
/**
* @brief 초기 출력값 설정
* @param instance AC 인스턴스 포인터
* @param initval 초기 출력값 (0=Low, 1=High)
*/
void ac_set_initval(AC_Instance *instance, uint8_t initval);
/**
* @brief 소프트웨어 이벤트 생성
* @param channel EVSYS 채널 번호 (0~9)
*/
void ac_generate_swevent(uint8_t channel);
/**
* @brief 플래그 확인 및 클리어
* @param instance AC 인스턴스 포인터
* @return CMP 인터럽트 플래그 상태
*/
uint8_t ac_check_flags(AC_Instance *instance);
#endif // AC_DRIVER_H
ac_driver.c
/**
* @file ac_driver.c
* @brief AVR128DA64/48/32/28 AC 드라이버 구현
* @details AC0, AC1, AC2를 지원하며, 인터럽트(0x2E, 0x38, 0x50), EVSYS 통합,
* 윈도우 모드, 슬립 모드 동작을 제공. 각 함수는 AC 모듈의 설정과
* 동작을 추상화하여 사용 편의성을 높임. 이벤트 시스템 상수는 <avr/io.h>에서 제공.
* @author linuxgo
* @date 2025-09-04
*/
#include "ac_driver.h"
#include <avr/interrupt.h>
/**
* @brief AC 인스턴스 배열
* @details AC0, AC1, AC2에 대한 인스턴스 포인터를 저장.
* 인터럽트 핸들러에서 해당 AC 모듈의 상태를 업데이트하기 위해 사용.
*/
static AC_Instance *ac_instances[3] = {NULL, NULL, NULL};
/**
* @brief AC 인스턴스 등록
* @details 주어진 AC 모듈(AC0, AC1, AC2)에 대해 인스턴스를 배열에 등록.
* @param instance AC 인스턴스 포인터
* @param ac AC 모듈 (AC0, AC1, AC2)
*/
static void register_instance(AC_Instance *instance, AC_t *ac) {
if (ac == &AC0) ac_instances[0] = instance;
else if (ac == &AC1) ac_instances[1] = instance;
else if (ac == &AC2) ac_instances[2] = instance;
}
/**
* @brief AC 초기화
* @details AC 모듈을 설정하고 인스턴스를 초기화.
* VREF, 입력 선택, 히스테리시스, 전력 모드, 윈도우 모드, 출력,
* Standby 모드 동작을 설정.
*/
void ac_init(AC_Instance *instance, AC_t *ac, uint8_t muxpos, uint8_t muxneg, uint8_t ref, uint8_t hysmode, uint8_t power, uint8_t winsel, uint8_t outen, uint8_t runstdby) {
// VREF 설정: 기준 전압 선택 (REFSEL[2:0])
VREF.ACREF = (VREF.ACREF & ~(0x7 << 0)) | (ref << 0);
// AC 설정: 히스테리시스, 전력 모드, 출력 활성화, Standby 모드 설정
ac->CTRLA = (hysmode << 2) | (power << 4) | (outen << 1) | (runstdby << 6);
ac->CTRLB = winsel; // 윈도우 모드 설정
ac->MUXCTRL = (muxpos << 3) | (muxneg << 0); // 양극/음극 입력 선택
ac->INTCTRL = 0x00; // 인터럽트 비활성화로 초기화
ac->STATUS = (1 << 0); // CMP 플래그 클리어
// 인스턴스 초기화: AC 포인터, 결과 및 상태 초기화
instance->ac = ac;
instance->cmp_result = 0;
instance->win_state = 0;
instance->flags = 0;
register_instance(instance, ac); // 인스턴스 등록
}
/**
* @brief AC 활성화
* @details AC 모듈의 ENABLE 비트를 설정하여 비교 동작 시작.
*/
void ac_enable(AC_Instance *instance) {
instance->ac->CTRLA |= (1 << 0); // ENABLE=1
}
/**
* @brief AC 비활성화
* @details AC 모듈의 ENABLE 비트를 클리어하여 비교 동작 중지.
*/
void ac_disable(AC_Instance *instance) {
instance->ac->CTRLA &= ~(1 << 0); // ENABLE=0
}
/**
* @brief 비교 시작
* @details AC 모듈을 활성화하여 비교 동작 시작. ac_enable과 동일.
*/
void ac_start(AC_Instance *instance) {
ac_enable(instance);
}
/**
* @brief 폴링 기반 결과 읽기
* @details AC의 CMPSTATE를 읽어 비교 결과를 반환.
* @return 0 (양극 <= 음극), 1 (양극 > 음극)
*/
uint8_t ac_read(AC_Instance *instance) {
return (instance->ac->STATUS & (1 << 1)) ? 1 : 0; // CMPSTATE 비트 확인
}
/**
* @brief 인터럽트 기반 결과 읽기 초기화
* @details 인터럽트 모드와 CMP 인터럽트를 활성화.
*/
void ac_read_it_init(AC_Instance *instance, uint8_t intmode) {
instance->ac->INTCTRL = (intmode << 1) | (1 << 0); // INTMODE와 CMP 설정
}
/**
* @brief 인터럽트 기반 결과 읽기
* @details 인터럽트 핸들러에서 저장된 비교 결과를 반환.
* @return CMPSTATE (0 또는 1)
*/
uint8_t ac_read_it(AC_Instance *instance) {
return instance->cmp_result;
}
/**
* @brief 윈도우 비교 상태 읽기
* @details 윈도우 모드의 상태(WINSTATE)를 반환.
* @return 0 (Below), 1 (Above), 2 (Inside), 3 (Outside)
*/
uint8_t ac_read_win_state(AC_Instance *instance) {
return (instance->ac->STATUS >> 2) & 0x03; // WINSTATE 비트 추출
}
/**
* @brief 윈도우 비교 설정
* @details DAC 기준 전압(DACREF)을 설정하여 윈도우 비교 기준값 지정.
* @param dacref DAC 기준 전압 값 (0~255, VDACREF = DACREF/256 × VREF)
*/
void ac_window_config(AC_Instance *instance, uint8_t dacref) {
instance->ac->DACREF = dacref; // DACREF 레지스터 설정
}
/**
* @brief 이벤트 출력 활성화 및 EVSYS 채널 설정
* @details ACn.OUT 이벤트 출력을 활성화하고, 지정된 EVSYS 채널에 연결.
* AC0 (0x20), AC1 (0x21), AC2 (0x22)에 따라 이벤트 생성기 상수 설정.
* 상수는 <avr/io.h>에 정의된 EVSYS_CHANNELn_ACm_OUT_gc 사용.
*/
void ac_enable_event(AC_Instance *instance, uint8_t channel) {
instance->ac->CTRLA |= (1 << 3); // EVOUT=1, 이벤트 출력 활성화
if (channel > 9) return; // 채널 번호 유효성 검사 (0~9)
// AC 모듈에 따라 적절한 이벤트 생성기 상수 선택
if (instance->ac == &AC0) {
switch (channel) {
case 0: EVSYS.CHANNEL0 = EVSYS_CHANNEL0_AC0_OUT_gc; break; // AC0_OUT (0x20)
case 1: EVSYS.CHANNEL1 = EVSYS_CHANNEL1_AC0_OUT_gc; break;
case 2: EVSYS.CHANNEL2 = EVSYS_CHANNEL2_AC0_OUT_gc; break;
case 3: EVSYS.CHANNEL3 = EVSYS_CHANNEL3_AC0_OUT_gc; break;
case 4: EVSYS.CHANNEL4 = EVSYS_CHANNEL4_AC0_OUT_gc; break;
case 5: EVSYS.CHANNEL5 = EVSYS_CHANNEL5_AC0_OUT_gc; break;
case 6: EVSYS.CHANNEL6 = EVSYS_CHANNEL6_AC0_OUT_gc; break;
case 7: EVSYS.CHANNEL7 = EVSYS_CHANNEL7_AC0_OUT_gc; break;
case 8: EVSYS.CHANNEL8 = EVSYS_CHANNEL8_AC0_OUT_gc; break;
case 9: EVSYS.CHANNEL9 = EVSYS_CHANNEL9_AC0_OUT_gc; break;
default: break;
}
} else if (instance->ac == &AC1) {
switch (channel) {
case 0: EVSYS.CHANNEL0 = EVSYS_CHANNEL0_AC1_OUT_gc; break; // AC1_OUT (0x21)
case 1: EVSYS.CHANNEL1 = EVSYS_CHANNEL1_AC1_OUT_gc; break;
case 2: EVSYS.CHANNEL2 = EVSYS_CHANNEL2_AC1_OUT_gc; break;
case 3: EVSYS.CHANNEL3 = EVSYS_CHANNEL3_AC1_OUT_gc; break;
case 4: EVSYS.CHANNEL4 = EVSYS_CHANNEL4_AC1_OUT_gc; break;
case 5: EVSYS.CHANNEL5 = EVSYS_CHANNEL5_AC1_OUT_gc; break;
case 6: EVSYS.CHANNEL6 = EVSYS_CHANNEL6_AC1_OUT_gc; break;
case 7: EVSYS.CHANNEL7 = EVSYS_CHANNEL7_AC1_OUT_gc; break;
case 8: EVSYS.CHANNEL8 = EVSYS_CHANNEL8_AC1_OUT_gc; break;
case 9: EVSYS.CHANNEL9 = EVSYS_CHANNEL9_AC1_OUT_gc; break;
default: break;
}
} else if (instance->ac == &AC2) {
switch (channel) {
case 0: EVSYS.CHANNEL0 = EVSYS_CHANNEL0_AC2_OUT_gc; break; // AC2_OUT (0x22)
case 1: EVSYS.CHANNEL1 = EVSYS_CHANNEL1_AC2_OUT_gc; break;
case 2: EVSYS.CHANNEL2 = EVSYS_CHANNEL2_AC2_OUT_gc; break;
case 3: EVSYS.CHANNEL3 = EVSYS_CHANNEL3_AC2_OUT_gc; break;
case 4: EVSYS.CHANNEL4 = EVSYS_CHANNEL4_AC2_OUT_gc; break;
case 5: EVSYS.CHANNEL5 = EVSYS_CHANNEL5_AC2_OUT_gc; break;
case 6: EVSYS.CHANNEL6 = EVSYS_CHANNEL6_AC2_OUT_gc; break;
case 7: EVSYS.CHANNEL7 = EVSYS_CHANNEL7_AC2_OUT_gc; break;
case 8: EVSYS.CHANNEL8 = EVSYS_CHANNEL8_AC2_OUT_gc; break;
case 9: EVSYS.CHANNEL9 = EVSYS_CHANNEL9_AC2_OUT_gc; break;
default: break;
}
}
}
/**
* @brief 출력 반전 설정
* @details 비교기 출력의 논리 반전 여부 설정.
*/
void ac_set_invert(AC_Instance *instance, uint8_t invert) {
if (invert) {
instance->ac->MUXCTRL |= (1 << 7); // INVERT=1, 출력 반전
} else {
instance->ac->MUXCTRL &= ~(1 << 7); // INVERT=0, 비반전
}
}
/**
* @brief 초기 출력값 설정
* @details 비교기 초기 출력값 설정.
*/
void ac_set_initval(AC_Instance *instance, uint8_t initval) {
if (initval) {
instance->ac->MUXCTRL |= (1 << 6); // INITVAL=1, High
} else {
instance->ac->MUXCTRL &= ~(1 << 6); // INITVAL=0, Low
}
}
/**
* @brief 소프트웨어 이벤트 생성
* @details 지정된 EVSYS 채널에 소프트웨어 이벤트 펄스 생성.
*/
void ac_generate_swevent(uint8_t channel) {
if (channel < 8) {
EVSYS.SWEVENTA = (1 << channel); // 채널 0~7: SWEVENTA 사용
} else if (channel < 10) {
EVSYS.SWEVENTB = (1 << (channel - 8)); // 채널 8~9: SWEVENTB 사용
}
}
/**
* @brief 플래그 확인 및 클리어
* @details CMP 인터럽트 플래그를 확인하고 클리어.
* @return CMP 플래그 상태 (0 또는 1)
*/
uint8_t ac_check_flags(AC_Instance *instance) {
uint8_t flags = instance->ac->STATUS & (1 << 0); // CMP 플래그 읽기
instance->ac->STATUS = flags; // 플래그 클리어
return flags;
}
/**
* @brief AC0 비교 완료 인터럽트 핸들러
* @details AC0의 비교 결과와 윈도우 상태를 업데이트하고 CMP 플래그 클리어.
*/
ISR(AC0_AC_vect) {
if (ac_instances[0]) {
ac_instances[0]->cmp_result = (AC0.STATUS & (1 << 1)) ? 1 : 0; // CMPSTATE 저장
ac_instances[0]->win_state = (AC0.STATUS >> 2) & 0x03; // WINSTATE 저장
ac_instances[0]->flags |= (1 << 0); // 플래그 설정
AC0.STATUS = (1 << 0); // CMP 플래그 클리어
}
}
/**
* @brief AC1 비교 완료 인터럽트 핸들러
* @details AC1의 비교 결과와 윈도우 상태를 업데이트하고 CMP 플래그 클리어.
*/
ISR(AC1_AC_vect) {
if (ac_instances[1]) {
ac_instances[1]->cmp_result = (AC1.STATUS & (1 << 1)) ? 1 : 0;
ac_instances[1]->win_state = (AC1.STATUS >> 2) & 0x03;
ac_instances[1]->flags |= (1 << 0);
AC1.STATUS = (1 << 0);
}
}
/**
* @brief AC2 비교 완료 인터럽트 핸들러
* @details AC2의 비교 결과와 윈도우 상태를 업데이트하고 CMP 플래그 클리어.
*/
ISR(AC2_AC_vect) {
if (ac_instances[2]) {
ac_instances[2]->cmp_result = (AC2.STATUS & (1 << 1)) ? 1 : 0;
ac_instances[2]->win_state = (AC2.STATUS >> 2) & 0x03;
ac_instances[2]->flags |= (1 << 0);
AC2.STATUS = (1 << 0);
}
}
ac_example.c
/**
* @file ac_example.c
* @brief AVR128DA64/48/32/28 AC 드라이버 테스트 프로그램
* @details AC0, AC1, AC2 및 윈도우 모드 테스트, EVSYS로 ADC0 시작 트리거 및 EVOUTA(PA2) 연결.
* 폴링 및 인터럽트 기반 동작을 테스트하며, UART로 결과 출력.
* @author linuxgo
* @date 2025-09-04
*/
#ifndef F_CPU
#define F_CPU 24000000UL // 시스템 클럭 24MHz
#endif
#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>
#include <avr/interrupt.h>
#include "ac_driver.h"
#include "adc_driver.h" // ADC 드라이버 (별도 구현 필요)
#include "uart_driver.h" // UART 드라이버 (별도 구현 필요)
/**
* @brief 시스템 클럭을 24MHz OSCHF로 설정
* @details CLK_PER을 24MHz로 설정하고, 자동 튜닝 활성화.
*/
void clock_init_24mhz(void) {
_PROTECTED_WRITE(CLKCTRL.XOSCHFCTRLA, CLKCTRL_ENABLE_bm | (0 << CLKCTRL_SEL_bp)); // 고속 발진기 활성화
_PROTECTED_WRITE(CLKCTRL.OSCHCTRLA, (0x9 << CLKCTRL_FRQSEL_gp) | CLKCTRL_AUTOTUNE_bm); // 24MHz, 자동 튜닝
_PROTECTED_WRITE(CLKCTRL.MCLKCTRLB, 0x00); // 프리스케일러 비활성화
_PROTECTED_WRITE(CLKCTRL.MCLKCTRLA, 0x00); // 기본 클럭 소스
while ((CLKCTRL.MCLKSTATUS & CLKCTRL_OSCHF_bm) == 0); // 발진기 안정화 대기
}
/**
* @brief 핀 설정 (모델별)
* @details AC 입력 및 출력 핀을 모델별로 설정. AINPx, AINNx는 입력, OUT 및 EVOUT은 출력.
*/
void configure_pins(void) {
#if defined(__AVR_AVR128DA28__) || defined(__AVR_AVR64DA28__) || defined(__AVR_AVR32DA28__)
PORTD.DIRCLR = PIN2_bm | PIN3_bm | PIN5_bm | PIN7_bm; // AC0_AINP0, AC0_AINN0, AC1_AINN0, AC2_AINN0
PORTA.DIRSET = PIN3_bm; // AC0_OUT
PORTC.DIRSET = PIN3_bm; // AC1_OUT
PORTD.DIRSET = PIN3_bm; // AC2_OUT
PORTA.DIRSET = PIN2_bm; // EVOUTA
#elif defined(__AVR_AVR128DA32__) || defined(__AVR_AVR64DA32__) || defined(__AVR_AVR32DA32__)
PORTD.DIRCLR = PIN2_bm | PIN3_bm | PIN5_bm | PIN7_bm;
PORTA.DIRSET = PIN3_bm;
PORTC.DIRSET = PIN3_bm;
PORTD.DIRSET = PIN3_bm;
PORTA.DIRSET = PIN2_bm;
#elif defined(__AVR_AVR128DA48__) || defined(__AVR_AVR64DA48__) || defined(__AVR_AVR32DA48__)
PORTD.DIRCLR = PIN2_bm | PIN3_bm | PIN5_bm | PIN7_bm;
PORTA.DIRSET = PIN3_bm;
PORTC.DIRSET = PIN3_bm;
PORTD.DIRSET = PIN3_bm;
PORTE.DIRCLR = PIN0_bm | PIN1_bm | PIN2_bm; // AC0_AINP1, AC2_AINP2, AC0_AINP2
PORTA.DIRSET = PIN2_bm;
#elif defined(__AVR_AVR128DA64__) || defined(__AVR_AVR64DA64__)
PORTD.DIRCLR = PIN2_bm | PIN3_bm | PIN5_bm | PIN7_bm;
PORTA.DIRSET = PIN3_bm;
PORTC.DIRSET = PIN3_bm;
PORTD.DIRSET = PIN3_bm;
PORTE.DIRCLR = PIN0_bm | PIN1_bm | PIN2_bm;
PORTA.DIRSET = PIN2_bm;
#endif
}
/**
* @brief 메인 함수
* @details AC0, AC1, AC2를 초기화하고, 폴링 및 인터럽트 기반 테스트 수행.
* AC0은 ADC0 시작 트리거, AC1/AC2는 EVOUTA(PA2)에 연결.
*/
int main(void) {
clock_init_24mhz(); // 24MHz 클럭 초기화
configure_pins(); // 핀 설정
sei(); // 전역 인터럽트 활성화
// USART0 초기화: 9600 baud, 8비트, 1 스톱 비트, 패리티 없음
UART_Instance usart0_instance;
uart_init(&usart0_instance, &USART0, UART_BAUD_9600, UART_DATA_8BIT, UART_STOP_1BIT, UART_PARITY_NONE, UART_MODE_ASYNC, UART_PORTMUX_DEFAULT);
uart_enable_tx(&usart0_instance);
uart_enable_rx(&usart0_instance);
uart_setup_stdio(&usart0_instance);
// ADC0 초기화: 단일 엔드, 12비트, 2.048V 기준, /4 프리스케일러, AIN0 입력
ADC_Instance adc0_instance;
adc_init(&adc0_instance, &ADC0, ADC_MODE_SINGLE, ADC_RES_12BIT, ADC_REF_2V048, ADC_PRESC_DIV4, ADC_INPUT_AIN0);
ADC0.CTRLA |= (1 << 3); // STARTEI=1, 이벤트 트리거 활성화
adc_enable(&adc0_instance);
// AC0 초기화: AINP0 vs VREF, 2.048V, Medium 히스테리시스, 출력 활성화, Standby 모드
AC_Instance ac0_instance;
ac_init(&ac0_instance, &AC0, AC_MUXPOS_AINP0, AC_MUXNEG_VREF, AC_REF_2V048, AC_HYSMODE_MEDIUM, AC_POWER_MEDIUM, AC_WINSEL_INSIDE, 1, 1);
ac_window_config(&ac0_instance, 128); // DACREF=128 (VREF의 50%)
ac_set_invert(&ac0_instance, 0); // 비반전
ac_set_initval(&ac0_instance, 0); // 초기 출력 Low
ac_enable_event(&ac0_instance, 0); // CHANNEL0에 AC0_OUT 연결
EVSYS.USERADC0START = 0; // ADC0_START를 CHANNEL0에 연결
ac_enable(&ac0_instance);
// AC1 초기화: AINP0 vs AINN0, 윈도우 모드 Below, Low 전력
AC_Instance ac1_instance;
ac_init(&ac1_instance, &AC1, AC_MUXPOS_AINP0, AC_MUXNEG_AINN0, AC_REF_2V048, AC_HYSMODE_SMALL, AC_POWER_LOW, AC_WINSEL_BELOW, 1, 1);
ac_window_config(&ac1_instance, 64); // DACREF=64
ac_set_invert(&ac1_instance, 0);
ac_set_initval(&ac1_instance, 0);
ac_enable_event(&ac1_instance, 1); // CHANNEL1에 AC1_OUT 연결
EVSYS.USEREVOUTA = 1; // EVOUTA(PA2)를 CHANNEL1에 연결
ac_enable(&ac1_instance);
// AC2 초기화: AINP0 vs AINN0, 윈도우 모드 Outside, Low 전력
AC_Instance ac2_instance;
ac_init(&ac2_instance, &AC2, AC_MUXPOS_AINP0, AC_MUXNEG_AINN0, AC_REF_2V048, AC_HYSMODE_SMALL, AC_POWER_LOW, AC_WINSEL_OUTSIDE, 1, 1);
ac_window_config(&ac2_instance, 192); // DACREF=192
ac_set_invert(&ac2_instance, 0);
ac_set_initval(&ac2_instance, 0);
ac_enable_event(&ac2_instance, 2); // CHANNEL2에 AC2_OUT 연결
EVSYS.USEREVOUTA = 2; // EVOUTA(PA2)를 CHANNEL2에 연결
ac_enable(&ac2_instance);
printf("AC Driver Test: AC0->ADC0, AC1, AC2, EVOUTA(PA2)\r\n");
_delay_ms(1000);
// 폴링 기반 테스트
ac_start(&ac0_instance);
ac_start(&ac1_instance);
ac_start(&ac2_instance);
uint8_t result0 = ac_read(&ac0_instance);
uint8_t win_state0 = ac_read_win_state(&ac0_instance);
uint8_t result1 = ac_read(&ac1_instance);
uint8_t win_state1 = ac_read_win_state(&ac1_instance);
uint8_t result2 = ac_read(&ac2_instance);
uint8_t win_state2 = ac_read_win_state(&ac2_instance);
printf("AC0 (Polling): CMPSTATE=%u, WINSTATE=%u\r\n", result0, win_state0);
printf("AC1 (Polling): CMPSTATE=%u, WINSTATE=%u\r\n", result1, win_state1);
printf("AC2 (Polling): CMPSTATE=%u, WINSTATE=%u\r\n", result2, win_state2);
_delay_ms(1000);
// 인터럽트 및 이벤트 기반 테스트
ac_read_it_init(&ac0_instance, AC_INTMODE_BOTH); // AC0: 양쪽 에지 인터럽트
ac_read_it_init(&ac1_instance, AC_INTMODE_RISING); // AC1: 상승 에지
ac_read_it_init(&ac2_instance, AC_INTMODE_FALLING); // AC2: 하강 에지
adc_read_it_init(&adc0_instance); // ADC0 인터럽트 초기화
ac_start(&ac0_instance);
ac_start(&ac1_instance);
ac_start(&ac2_instance);
while (1) {
if (ac_check_flags(&ac0_instance)) {
result0 = ac_read_it(&ac0_instance);
win_state0 = ac_read_win_state(&ac0_instance);
printf("AC0 (Interrupt): CMPSTATE=%u, WINSTATE=%u\r\n", result0, win_state0);
}
if (ac_check_flags(&ac1_instance)) {
result1 = ac_read_it(&ac1_instance);
win_state1 = ac_read_win_state(&ac1_instance);
printf("AC1 (Interrupt): CMPSTATE=%u, WINSTATE=%u\r\n", result1, win_state1);
}
if (ac_check_flags(&ac2_instance)) {
result2 = ac_read_it(&ac2_instance);
win_state2 = ac_read_win_state(&ac2_instance);
printf("AC2 (Interrupt): CMPSTATE=%u, WINSTATE=%u\r\n", result2, win_state2);
}
if (adc_check_flags(&adc0_instance)) {
uint16_t adc_result = adc_read_it(&adc0_instance);
printf("ADC0 (Event Triggered): %u (%.3fV)\r\n", adc_result, (float)adc_result * 2.048 / 4096.0);
}
_delay_ms(500);
}
return 0;
}
추가팁
- 이벤트 시스템 설정: ACn.OUT을 ADC0_START, TCAn.CNTA, 또는 EVOUTx(PA2 등)에 연결하여 CPU 개입 없이 주변 장치 제어 가능. <avr/io.h>에 정의된 상수(예: EVSYS_CHANNELn_ACm_OUT_gc)를 사용하세요.
- 윈도우 모드 설정: AC0와 AC1을 쌍으로 설정 시 동일한 MUXPOS (예: AINP0) 사용, WINSEL을 상호보완적으로 설정 (예: AC0=ABOVE, AC1=BELOW).
- VREF 주의사항: 기준 전압 변경 시 ADC0, DAC0 등 다른 장치의 노이즈를 방지하기 위해 관련 장치를 비활성화하세요.
- 전력 절감: 사용하지 않는 AC는 ac_disable로 비활성화하고, VREF.ACREF의 ALWAYSON=0으로 설정하여 전력을 절감하세요.
- 입력 보호: AINPx, AINNx 핀에 입력 전압이 VDD를 초과하지 않도록 보호 회로를 추가하세요.
- 디버깅: ac_check_flags와 printf를 활용해 CMPSTATE, WINSTATE, CMP 플래그를 모니터링하세요. Microchip Studio 시뮬레이터 또는 Curiosity Nano 보드에서 UART 출력(PuTTY 등)으로 확인 가능.
- 타이밍 고려: 비동기 이벤트는 CLK_PER 없이 즉시 응답, 동기 사용자는 2~3 CLK_PER 지연. Electrical Characteristics에서 시작 시간 확인.
결론
본 문서에서 구현한 AC 드라이버는 AVR128DA64/48/32/28 시리즈의 AC0, AC1, AC2 모듈을 효과적으로 활용할 수 있도록 설계되었습니다. 단일/윈도우 비교, 히스테리시스, 전력 모드, 이벤트 시스템 통합, 슬립 모드 지원 등 풍부한 기능을 제공하며, 모든 모델에서 동일하게 사용할 수 있습니다. 표준 ISR 기반 인터럽트 처리와 EVSYS를 통해 ADC, TCA 등과의 연동을 지원하여 Core Independent Peripheral(CIP) 구현이 가능합니다. 예제 코드를 통해 손쉽게 테스트와 확장이 가능하며, 이를 통해 임베디드 시스템 개발자는 신뢰성 높은 아날로그 비교 환경을 구현할 수 있습니다.
'MCU > AVR' 카테고리의 다른 글
AVR128DA64/48/32/28 클럭 드라이버 설계 및 구현 (0) | 2025.09.05 |
---|---|
AVR128DA64/48/32/28 RTC 드라이버 설계 및 구현 (0) | 2025.09.05 |
AVR128DA64/48/32/28 TCD 드라이버 설계 및 구현 (0) | 2025.09.05 |
AVR128DA64/48/32/28 TCB 드라이버 설계 및 구현 (0) | 2025.09.04 |
AVR128DA64/48/32/28 DAC 드라이버 설계 및 구현 (0) | 2025.09.04 |
AVR128DA64/48/32/28 ADC 드라이버 설계 및 구현 (0) | 2025.09.04 |
AVR128DA64/48/32/28 SPI 드라이버 설계 및 구현 (0) | 2025.09.03 |
AVR128DA64/48/32/28 I2C (TWI) 드라이버 설계 및 구현 (0) | 2025.09.03 |