본문 바로가기
MCU/AVR

AVR128DA 시리즈 UART 드라이버 설계 및 구현

by linuxgo 2025. 9. 3.

개요

본 문서는 Microchip의 AVR128DA64/48/32/28 시리즈 마이크로컨트롤러에 내장된 UART(USART) 기능을 분석하고, 이를 활용할 수 있는 범용 드라이버를 설계 및 구현한 내용을 다룹니다. AVR128DA 시리즈는 최대 6개의 USART 모듈을 제공하며, 비동기/동기 통신, 다양한 보드레이트 설정, 패리티 및 스톱 비트 지원, 인터럽트 및 폴링 기반 송수신 기능을 포함합니다. 본 드라이버는 링버퍼를 기반으로 효율적인 데이터 송수신을 지원하며, printf를 통한 표준 출력 리디렉션, 가용 바이트 확인(uart_available), 링버퍼 초기화(uart_flush)와 같은 부가 기능을 제공합니다. 또한 AVR128DA64/48/32/28 전 제품군에 호환되도록 설계되어 Microchip Studio 및 AVR-GCC 환경에서 손쉽게 활용 가능합니다.

AVR128DA USART block diagram

사양

AVR128DA 시리즈의 UART(USART) 사양은 다음과 같습니다:

  • 모듈 수:
    • AVR128DA64: 최대 6개 (USART0~5).
    • AVR128DA48: 최대 5개 (USART0~4).
    • AVR128DA32: 최대 4개 (USART0~3).
    • AVR128DA28: 최대 3개 (USART0~2).
  • 핀 할당: PORTMUX로 TxD/RxD/XCK 핀 선택 가능 (예: USART0 기본: PA0(TxD), PA1(RxD)).
  • 전압 범위: 1.8V ~ 5.5V (VDD).
  • 기능:
    • 비동기/동기 모드.
    • 보드레이트: 300~2Mbps (24MHz CLK_PER 기준).
    • 프레임: 5~9비트 데이터, 1~2 스톱 비트, 패리티(없음/짝수/홀수).
    • 인터럽트: 송신 완료(TXCIF), 데이터 레지스터 비움(DREIF), 수신 완료(RXCIF), 스타트 프레임 감지(RXSIF).
    • 링버퍼: 송수신 데이터 관리 (64바이트, 폴링 및 인터럽트 지원).
    • printf: UART를 통한 표준 출력 지원.
    • 추가: 가용 바이트 확인, 링버퍼 비우기.
  • 클럭 의존성: Peripheral Clock (CLK_PER, 최대 24MHz).
  • 리셋 상태: USART 비활성화, 핀은 GPIO 입력 모드.
  • 전력 최적화: RXEN=0, TXEN=0으로 전력 절감.

레지스터 설정 상세

UART는 USARTn 레지스터를 통해 제어됩니다. 데이터시트의 Register Summary에 따라 주요 레지스터와 비트필드는 다음과 같습니다:

  • USARTn.RXDATAL (0x00):
    • 비트 [7:0]: DATA[7:0] (수신 데이터, 8비트).
    • 리셋: 0x00, 읽기 전용(R).
  • USARTn.RXDATAH (0x01):
    • 비트 [7]: RXCIF (수신 완료 플래그, 1 작성으로 클리어).
    • 비트 [6]: BUFOVF (버퍼 오버플로우 에러, 1 작성으로 클리어).
    • 비트 [5]: FERR (프레임 에러, 1 작성으로 클리어).
    • 비트 [4]: PERR (패리티 에러, 1 작성으로 클리어).
    • 비트 [0]: DATA[8] (9비트 모드의 상위 비트).
    • 리셋: 0x00, R/W.
  • USARTn.TXDATAL (0x02):
    • 비트 [7:0]: DATA[7:0] (송신 데이터, 8비트).
    • 리셋: 0x00, 쓰기 전용(W).
  • USARTn.TXDATAH (0x03):
    • 비트 [0]: DATA[8] (9비트 모드의 상위 비트).
    • 리셋: 0x00, W.
  • USARTn.STATUS (0x04):
    • 비트 [7]: RXCIF (수신 완료 플래그, 1 작성으로 클리어).
    • 비트 [6]: TXCIF (송신 완료 플래그, 1 작성으로 클리어).
    • 비트 [5]: DREIF (데이터 레지스터 비움 플래그).
    • 비트 [3]: RXSIF (스타트 프레임 감지 플래그, 1 작성으로 클리어).
    • 비트 [2]: ISFIF (인프레임 스타트 플래그).
    • 비트 [1]: BDF (브레이크 감지 플래그).
    • 비트 [0]: WFB (웨이트 포 브레이크 플래그).
    • 리셋: 0x20, R/W.
  • USARTn.CTRLA (0x05):
    • 비트 [7]: RXCIE (수신 완료 인터럽트 활성).
    • 비트 [6]: TXCIE (송신 완료 인터럽트 활성).
    • 비트 [5]: DREIE (데이터 레지스터 비움 인터럽트 활성).
    • 비트 [3]: RXSIE (스타트 프레임 감지 인터럽트 활성).
    • 비트 [1]: LBME (루프백 모드 활성).
    • 비트 [0]: ABEIE (자동 보드 에러 인터럽트 활성).
    • 비트 [0]: RS485 (RS485 모드: 0=비활성, 1=활성).
    • 리셋: 0x00, R/W.
  • USARTn.CTRLB (0x06):
    • 비트 [7]: MPCM (멀티프로세서 통신 모드 활성).
    • 비트 [6]: RXEN (수신기 활성).
    • 비트 [5]: TXEN (송신기 활성).
    • 비트 [3]: SFDEN (스타트 프레임 감지 활성).
    • 비트 [2]: ODME (오픈 드레인 모드 활성).
    • 비트 [1:0]: RXMODE[1:0] (0x0=정상, 0x1=CLK2x, 0x2=일반).
    • 리셋: 0x00, R/W.
  • USARTn.CTRLC (0x07):
    • 비동기 모드:
      • 비트 [7:6]: CMODE[1:0] (0x0=비동기, 0x1=동기).
      • 비트 [5:4]: PMODE[1:0] (패리티: 0x0=없음, 0x2=짝수, 0x3=홀수).
      • 비트 [3]: SBMODE (0: 1 스톱 비트, 1: 2 스톱 비트).
      • 비트 [2:0]: CHSIZE[2:0] (캐릭터 크기: 0x0=8비트 등).
    • 동기 모드:
      • 비트 [5]: UDORD (데이터 순서: 0=MSB 먼저, 1=LSB 먼저).
      • 비트 [4]: UCPHA (클럭 위상: 0=선행 에지 샘플링, 1=후행 에지 샘플링).
    • 리셋: 0x00, R/W.
  • USARTn.BAUD (0x08~0x09):
    • 비트 [15:0]: BAUD[15:0] (보드레이트 설정, 16비트).
    • 리셋: 0x0000, R/W.
  • USARTn.CTRLD (0x0A):
    • 비트 [7:6]: ABW[1:0] (자동 보드 윈도우 설정).
    • 리셋: 0x00, R/W.
  • USARTn.DBGCTRL (0x0B):
    • 비트 [0]: DBGRUN (디버그 모드에서 동작 유지).
    • 리셋: 0x00, R/W.
  • USARTn.EVCTRL (0x0C):
    • 비트 [0]: IREI (인프레임 이벤트 인터럽트 활성).
    • 리셋: 0x00, R/W.
  • USARTn.TXPLCTRL (0x0D):
    • 비트 [7:0]: TXPL[7:0] (송신 펄스 길이).
    • 리셋: 0x00, R/W.
  • USARTn.RXPLCTRL (0x0E):
    • 비트 [6:0]: RXPL[6:0] (수신 펄스 길이).
    • 리셋: 0x00, R/W.

UART 설정 절차

UART 설정은 다음 순서로 진행합니다:

  1. 클럭 설정: CLKCTRL로 CLK_PER 활성화 (예: 24MHz OSCHF).
  2. 포트 설정: PORTMUX로 TxD/RxD/XCK 핀 선택, GPIO 방향 설정 (TxD: 출력, RxD: 입력).
  3. 모드 설정: CTRLC로 비동기/동기, 패리티, 스톱 비트, 데이터 크기 설정.
  4. 보드레이트 설정: BAUD 레지스터로 보드레이트 계산 및 설정.
  5. 인터럽트 설정 (옵션): CTRLA로 RXCIE, TXCIE, DREIE 설정, STATUS 플래그 클리어.
  6. 송수신 활성화: CTRLB로 TXEN, RXEN 설정.
  7. 링버퍼 초기화: 송수신 링버퍼 초기화.
  8. printf 설정: uart_setup_stdioprintf 출력 리디렉션.
  9. 멀티플렉싱 확인: PORTMUX로 다른 주변 장치와 충돌 방지.
  10. 테스트: 송신/수신 데이터(폴링/인터럽트), 에러 상태(STATUS), 가용 바이트 확인, 링버퍼 비우기, printf 출력 확인.

PORTMUX를 통한 핀 테이블

PORTMUX 설정을 통해 TxD/RxD/XCK 핀을 변경할 수 있습니다. 아래는 데이터시트(Table 22-1)에 기반한 주요 USART 모듈의 핀 매핑 테이블입니다.

USART 모듈 PORTMUX 설정 TxD 핀 RxD 핀 XCK 핀 설명
USART0 DEFAULT (0x0) PA0 PA1 PA2 기본 핀 설정
USART0 ALT1 (0x1) PA4 PA5 PA6 대체 핀 1
USART1 DEFAULT (0x0) PC0 PC1 PC2 기본 핀 설정
USART1 ALT1 (0x1) PC4 PC5 PC6 대체 핀 1
USART2 DEFAULT (0x0) PF0 PF1 PF2 기본 핀 설정
USART2 ALT1 (0x1) PF4 PF5 PF6 대체 핀 1
USART3 DEFAULT (0x0) PB0 PB1 PB2 기본 핀 설정
USART3 ALT1 (0x1) PB4 PB5 PB6 대체 핀 1
USART4 DEFAULT (0x0) PE0 PE1 PE2 기본 핀 설정 (AVR128DA48 이상)
USART4 ALT1 (0x1) PE4 PE5 PE6 대체 핀 1
USART5 DEFAULT (0x0) PG0 PG1 PG2 기본 핀 설정 (AVR128DA64 전용)
USART5 ALT1 (0x1) PG4 PG5 PG6 대체 핀 1

설정 예시 (USART0을 ALT1으로 설정):

PORTMUX.USARTROUTEA |= (1 << 0); // USART0을 ALT1로 설정 (PA2:TxD, PA3:RxD)
PORTA.DIRSET = (1 << 2); // PA2(TxD) 출력
PORTA.DIRCLR = (1 << 3); // PA3(RxD) 입력
    

주의사항: PORTMUX 변경 시 SPI, TWI 등 다른 주변 장치와의 핀 충돌 확인 필수.

UART 설정 고려사항

  • 보드레이트 계산: BAUD = (64 * F_PER) / (S * BAUDRATE), S=16(정상), 8(CLK2x). 예: 24MHz에서 9600bps → BAUD=1600.
  • 링버퍼: 64바이트 순환 버퍼로 오버플로우 방지. 폴링(uart_transmit_buffered, uart_receive_buffered) 및 인터럽트(uart_transmit_it, uart_receive_it)에서 사용. uart_available로 가용 바이트 확인, uart_flush로 버퍼 비우기.
  • printf: putchar 재정의로 UART 출력. 폴링 기반으로 간단 구현.
  • 인터럽트: RXCIF, DREIF 플래그 클리어 필수. 다중 USART 지원 시 ISR_NAKED 또는 ISR_ALIAS 사용.
  • 포트 설정: PORTMUX로 대체 핀 선택 (위 테이블 참조).
  • 전력 최적화: 사용하지 않는 USART는 RXEN=0, TXEN=0.
  • 호환성: AVR128DA28은 USART0~2만 지원. <avr/io.h>로 모델별 정의 제공.
  • CCP 보호: CLKCTRL는 _PROTECTED_WRITE 사용.
  • 에러 처리: RXDATAH로 FERR, PERR, BUFOVF 확인 및 1 작성으로 클리어.

드라이버 구현 내용

드라이버는 AVR128DA64/48/32/28 호환으로 설계되었으며, UART 기능을 추상화합니다. 주요 구현은 다음과 같습니다:

  • 링버퍼: 64바이트 순환 버퍼로 송수신 데이터 관리. RingBuffer 구조체로 head/tail 관리. 폴링 및 인터럽트 방식 지원.
  • 폴링 기반 링버퍼: uart_transmit_buffereduart_receive_buffered로 다중 바이트 송수신.
  • 가용 바이트 확인: uart_available로 수신 링버퍼의 읽기 가능한 바이트 수 반환.
  • 링버퍼 비우기: uart_flush로 송수신 링버퍼 초기화.
  • printf 지원: uart_setup_stdioputchar 재정의, UART를 통한 표준 출력.
  • uart_init: USART 모듈 초기화 (포트, 보드레이트, 프레임 포맷, PORTMUX).
  • uart_transmit/receive: 단일 바이트 송수신 (폴링).
  • uart_transmit_it/receive_it: 인터럽트 기반 송수신 (링버퍼 사용).
  • uart_check_errors: 에러 상태 확인 (FERR, PERR, BUFOVF, 1 작성으로 클리어).
  • uart_enable/disable: 송수신 활성화/비활성화.
  • 클럭 설정: main.c에서 24MHz OSCHF, Auto-tune 활성화.
  • 다중 USART 지원: ISR_NAKED와 ISR_ALIAS로 USART0~5 인터럽트 처리.
  • 호환성: <avr/io.h>로 모델별 USART 정의 지원.

사용방법

UART 드라이버를 사용하는 방법은 다음과 같습니다:

  1. 헤더 파일 포함: 프로젝트에 uart_driver.h를 포함하고, <avr/io.h>, <stdio.h>, <util/delay.h>를 추가합니다.
  2. 클럭 설정: main.cclock_init_24mhz를 호출하여 24MHz OSCHF 클럭을 설정합니다.
  3. UART 인스턴스 생성: UART_Instance 구조체를 선언하고, uart_init으로 초기화합니다. 예:
    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);
                
  4. 송수신 활성화: uart_enable_txuart_enable_rx를 호출하여 송수신을 활성화합니다.
  5. printf 설정 (옵션): uart_setup_stdio로 UART를 통해 printf 출력을 설정합니다.
  6. 데이터 송수신:
    • 폴링 기반: uart_transmit_buffered로 데이터 송신, uart_receive_buffered로 데이터 수신.
    • 인터럽트 기반: uart_transmit_it_inituart_receive_it_init로 인터럽트 활성화 후, uart_transmit_ituart_receive_it로 송수신.
  7. 가용 바이트 확인: uart_available로 수신 링버퍼의 읽기 가능한 바이트 수를 확인합니다.
  8. 버퍼 비우기: uart_flush로 송수신 링버퍼를 초기화합니다.
  9. 에러 처리: uart_check_errors로 프레임 에러(FERR), 패리티 에러(PERR), 버퍼 오버플로우(BUFOVF)를 확인하고 클리어합니다.
  10. 다중 USART 사용: 각 USART(0~5)에 대해 별도의 UART_Instance를 생성하고 초기화합니다. 인터럽트 핸들러는 ISR_ALIASOF로 자동 처리됩니다.

코드 구현

  •  UART 드라이버 코드: uart_driver.h,uart_driver.c,main.c
  •   드라이버 코드를 이용한 예시코드:uart_echo_buffered_example.c, uart_monitor_example.c
/**
 * @file uart_driver.h
 * @brief AVR128DA64/48/32/28 UART 드라이버 헤더 파일
 * @details 모든 AVR128DA 시리즈(28/32/48/64) 호환. 비동기/동기 통신, 링버퍼(폴링/인터럽트),
 *          printf 리디렉션, 가용 바이트 확인, 버퍼 비우기, 에러 처리, 다중 USART 지원.
 *          각 USART 포트(USART0~USART5)에 대해 독립적인 표준 ISR 사용.
 * @author 작성자
 * @date 2025-09-13
 */
#ifndef UART_DRIVER_H
#define UART_DRIVER_H

#include <avr/io.h>     // AVR 레지스터 정의
#include <stdint.h>     // 표준 정수 타입
#include <stdio.h>      // printf 지원을 위한 표준 입출력

/**
 * @brief UART 보드레이트 정의
 * @details 일반적인 보드레이트 값(9600, 115200bps)을 상수로 정의
 */
#define UART_BAUD_9600  9600
#define UART_BAUD_115200 115200

/**
 * @brief UART 프레임 설정 정의
 * @details 데이터 비트, 스톱 비트, 패리티, 모드 설정을 위한 상수
 */
#define UART_DATA_5BIT  0x0 // 5비트 데이터
#define UART_DATA_6BIT  0x1 // 6비트 데이터
#define UART_DATA_7BIT  0x2 // 7비트 데이터
#define UART_DATA_8BIT  0x3 // 8비트 데이터
#define UART_DATA_9BIT  0x7 // 9비트 데이터
#define UART_STOP_1BIT  0   // 1 스톱 비트
#define UART_STOP_2BIT  1   // 2 스톱 비트
#define UART_PARITY_NONE 0x0 // 패리티 없음
#define UART_PARITY_EVEN 0x2 // 짝수 패리티
#define UART_PARITY_ODD  0x3 // 홀수 패리티
#define UART_MODE_ASYNC  0x0 // 비동기 모드
#define UART_MODE_SYNC   0x1 // 동기 모드

/**
 * @brief UART 에러 플래그 정의
 * @details 프레임 에러, 패리티 에러, 버퍼 오버플로우 플래그
 */
#define UART_ERROR_FERR   USART_FERR_bm   // 프레임 에러
#define UART_ERROR_PERR   USART_PERR_bm   // 패리티 에러
#define UART_ERROR_BUFOVF USART_BUFOVF_bm // 버퍼 오버플로우

/**
 * @brief UART 링버퍼 크기
 * @details 송수신 링버퍼 크기를 64바이트로 고정
 */
#define UART_BUFFER_SIZE 64

/**
 * @brief PORTMUX 설정 정의
 * @details 핀 매핑 선택 (기본, 대체 1, 대체 2)
 */
#define UART_PORTMUX_DEFAULT 0x0 // 기본 핀
#define UART_PORTMUX_ALT1    0x1 // 대체 핀 1
#define UART_PORTMUX_ALT2    0x2 // 대체 핀 2

/**
 * @brief 링버퍼 구조체
 * @details 송수신 데이터를 저장하는 순환 버퍼
 */
typedef struct {
    uint8_t buffer[UART_BUFFER_SIZE]; // 데이터 저장 배열
    volatile uint8_t head;           // 쓰기 인덱스
    volatile uint8_t tail;           // 읽기 인덱스
} RingBuffer;

/**
 * @brief UART 인스턴스 구조체
 * @details 각 USART 모듈의 상태와 설정 관리
 */
typedef struct {
    USART_t *usart;                  // USART 레지스터 포인터
    RingBuffer tx_buffer;            // 송신 링버퍼
    RingBuffer rx_buffer;            // 수신 링버퍼
    volatile uint8_t tx_busy;        // 송신 진행 상태 플래그
} UART_Instance;

/**
 * @brief UART 초기화
 * @param instance UART 인스턴스 포인터
 * @param usart USART 레지스터 포인터 (예: &USART0)
 * @param baud 보드레이트 (예: UART_BAUD_9600)
 * @param data_bits 데이터 비트 (예: UART_DATA_8BIT)
 * @param stop_bits 스톱 비트 (UART_STOP_1BIT, UART_STOP_2BIT)
 * @param parity 패리티 (UART_PARITY_NONE, UART_PARITY_EVEN, UART_PARITY_ODD)
 * @param mode 모드 (UART_MODE_ASYNC, UART_MODE_SYNC)
 * @param portmux PORTMUX 설정 (UART_PORTMUX_DEFAULT, UART_PORTMUX_ALT1, UART_PORTMUX_ALT2)
 * @details USART 모듈 초기화, 포트 설정, 링버퍼 초기화
 */
void uart_init(UART_Instance *instance, USART_t *usart, uint32_t baud, uint8_t data_bits, uint8_t stop_bits, uint8_t parity, uint8_t mode, uint8_t portmux);

/**
 * @brief UART 송신기 활성화
 * @param instance UART 인스턴스 포인터
 * @details CTRLB의 TXEN 비트를 설정하여 송신 활성화
 */
void uart_enable_tx(UART_Instance *instance);

/**
 * @brief UART 수신기 활성화
 * @param instance UART 인스턴스 포인터
 * @details CTRLB의 RXEN 비트를 설정하여 수신 활성화
 */
void uart_enable_rx(UART_Instance *instance);

/**
 * @brief UART 비활성화
 * @param instance UART 인스턴스 포인터
 * @details TXEN, RXEN 비트를 클리어하여 USART 비활성화
 */
void uart_disable(UART_Instance *instance);

/**
 * @brief 단일 바이트 송신 (폴링)
 * @param instance UART 인스턴스 포인터
 * @param data 송신 데이터
 * @details DREIF 플래그 확인 후 TXDATAL에 데이터 작성
 */
void uart_transmit(UART_Instance *instance, uint8_t data);

/**
 * @brief 단일 바이트 수신 (폴링)
 * @param instance UART 인스턴스 포인터
 * @return 수신 데이터
 * @details RXCIF 플래그 확인 후 RXDATAL 읽기
 */
uint8_t uart_receive(UART_Instance *instance);

/**
 * @brief 폴링 기반 링버퍼 송신
 * @param instance UART 인스턴스 포인터
 * @param data 송신 데이터
 * @param length 데이터 길이
 * @return 성공 여부 (0: 성공, 1: 버퍼 가득참)
 * @details 링버퍼에 데이터 저장 후 순차 송신
 */
uint8_t uart_transmit_buffered(UART_Instance *instance, uint8_t *data, uint8_t length);

/**
 * @brief 폴링 기반 링버퍼 수신
 * @param instance UART 인스턴스 포인터
 * @param data 수신 데이터 버퍼
 * @param length 읽을 데이터 길이
 * @return 수신된 바이트 수
 * @details 링버퍼에서 데이터 읽기
 */
uint8_t uart_receive_buffered(UART_Instance *instance, uint8_t *data, uint8_t length);

/**
 * @brief 인터럽트 기반 송신 초기화
 * @param instance UART 인스턴스 포인터
 * @details DREIE 비트를 설정하여 송신 인터럽트 활성화
 */
void uart_transmit_it_init(UART_Instance *instance);

/**
 * @brief 인터럽트 기반 수신 초기화
 * @param instance UART 인스턴스 포인터
 * @details RXCIE 비트를 설정하여 수신 인터럽트 활성화
 */
void uart_receive_it_init(UART_Instance *instance);

/**
 * @brief 인터럽트 기반 데이터 송신
 * @param instance UART 인스턴스 포인터
 * @param data 송신 데이터
 * @param length 데이터 길이
 * @return 성공 여부 (0: 성공, 1: 버퍼 가득참)
 * @details 링버퍼에 데이터 저장 후 인터럽트로 송신
 */
uint8_t uart_transmit_it(UART_Instance *instance, uint8_t *data, uint8_t length);

/**
 * @brief 인터럽트 기반 데이터 수신
 * @param instance UART 인스턴스 포인터
 * @param data 수신 데이터 버퍼
 * @param length 읽을 데이터 길이
 * @return 수신된 바이트 수
 * @details 링버퍼에서 데이터 읽기
 */
uint8_t uart_receive_it(UART_Instance *instance, uint8_t *data, uint8_t length);

/**
 * @brief UART 에러 상태 확인 및 클리어
 * @param instance UART 인스턴스 포인터
 * @return 에러 플래그 (UART_ERROR_FERR | UART_ERROR_PERR | UART_ERROR_BUFOVF)
 * @details RXDATAH의 에러 플래그 확인 및 클리어
 */
uint8_t uart_check_errors(UART_Instance *instance);

/**
 * @brief printf를 UART로 리디렉션
 * @param instance UART 인스턴스 포인터
 * @details putchar를 재정의하여 UART로 출력
 */
void uart_setup_stdio(UART_Instance *instance);
/**
 * @brief 수신 링버퍼의 가용 바이트 수 확인
 * @param instance UART 인스턴스 포인터
 * @return 읽기 가능한 바이트 수
 * @details 링버퍼의 head와 tail로 가용 바이트 계산
 */
uint8_t uart_available(UART_Instance *instance);

/**
 * @brief 송수신 링버퍼 비우기
 * @param instance UART 인스턴스 포인터
 * @details head, tail을 초기화하고 tx_busy 플래그 클리어
 */
void uart_flush(UART_Instance *instance);

#endif // UART_DRIVER_H
/**
 * @file uart_driver.c
 * @brief AVR128DA64/48/32/28 UART 드라이버 구현
 * @details USARTn 레지스터로 비동기/동기 통신 제어. 링버퍼(폴링/인터럽트),
 *          printf, 가용 바이트 확인, 버퍼 비우기, 에러 처리, 다중 USART 지원.
 *          - AVR128DA64: USART0~5 (PA0/PA1, PC0/PC1, PF0/PF1, PB0/PB1, PE0/PE1, PG0/PG1)
 *          - AVR128DA48: USART0~4 (PA0/PA1, PC0/PC1, PF0/PF1, PB0/PB1, PE0/PE1)
 *          - AVR128DA32/28: USART0~3 (PA0/PA1, PC0/PC1, PF0/PF1, PB0/PB1)
 * @author 작성자
 * @date 2025-09-13
 */
#include "uart_driver.h"
#include <avr/interrupt.h>

// UART 버퍼 크기 정의 (uart_driver.h에 정의되지 않은 경우)
#ifndef UART_BUFFER_SIZE
#define UART_BUFFER_SIZE 64
#endif

/**
 * @brief UART 인스턴스 배열
 * @details 최대 6개 USART 인스턴스 저장 (AVR128DA64 기준)
 */
static UART_Instance *uart_instances[6] = {NULL};

/**
 * @brief printf용 USART 포인터
 */
static USART_t *stdio_usart;

/**
 * @brief printf 리다이렉션용 putchar 함수
 */
static int uart_putchar(char c, FILE *stream) {
    if (!stdio_usart) return EOF;
    if (c == '\n') uart_putchar('\r', stream);
    while (!(stdio_usart->STATUS & USART_DREIF_bm));
    stdio_usart->TXDATAL = (uint8_t)c;
    return 0;
}

/**
 * @brief 전역 FILE 구조체 (stdout 리디렉션용)
 */
static FILE uart_stdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);

/**
 * @brief UART 보드레이트 계산
 * @param baud 보드레이트 (예: 9600)
 * @param clk2x CLK2x 모드 (0: 비활성, 1: 활성)
 * @return BAUD 레지스터 값
 */
static uint16_t calculate_baud(uint32_t baud, uint8_t clk2x) {
    uint8_t s = clk2x ? 8 : 16;
    return (uint16_t)((64.0 * F_CPU) / (s * baud));
}

/**
 * @brief UART 인스턴스 등록
 */
static void register_instance(UART_Instance *instance, USART_t *usart) {
    if (usart == &USART0) uart_instances[0] = instance;
    else if (usart == &USART1) uart_instances[1] = instance;
    else if (usart == &USART2) uart_instances[2] = instance;
    else if (usart == &USART3) uart_instances[3] = instance;
#if defined(__AVR_AVR128DA64__) || defined(__AVR_AVR128DA48__)
    else if (usart == &USART4) uart_instances[4] = instance;
#endif
#if defined(__AVR_AVR128DA64__)
    else if (usart == &USART5) uart_instances[5] = instance;
#endif
}

/**
 * @brief UART 초기화
 * @details PORTMUX로 핀 설정, 보드레이트 계산, 프레임 포맷 설정, 링버퍼 초기화
 */
void uart_init(UART_Instance *instance, USART_t *usart, uint32_t baud, uint8_t data_bits, uint8_t stop_bits, uint8_t parity, uint8_t mode, uint8_t portmux) {
    // USART 유효성 검증
#if !defined(__AVR_AVR128DA64__) && !defined(__AVR_AVR128DA48__)
    if (usart == &USART4) {
        #error "USART4 is only supported on AVR128DA64 and AVR128DA48"
    }
#endif
#if !defined(__AVR_AVR128DA64__)
    if (usart == &USART5) {
        #error "USART5 is only supported on AVR128DA64"
    }
#endif

    // PORTMUX 설정 및 GPIO 방향 설정 (데이터시트 DS40002183E 섹션 3.1 기반)
    if (usart == &USART0) {
        // USART0: 기본(PA0/PA1), ALT1(PA2/PA3), ALT2(PC0/PC1)
        // PA0(TxD: VQFN64:62, VQFN48:44, VQFN32:30, SPDIP28:22)
        PORTMUX.USARTROUTEA = (PORTMUX.USARTROUTEA & ~(0x3 << 0)) | (portmux << 0);
        if (portmux == UART_PORTMUX_DEFAULT) {
            PORTA.DIRSET = (1 << 0); // PA0(TxD) 출력
            PORTA.DIRCLR = (1 << 1); // PA1(RxD) 입력
            if (mode == UART_MODE_SYNC) PORTA.DIRSET = (1 << 2); // PA2(XCK) 출력
        } else if (portmux == UART_PORTMUX_ALT1) {
            PORTA.DIRSET = (1 << 2); // PA2(TxD) 출력
            PORTA.DIRCLR = (1 << 3); // PA3(RxD) 입력
            if (mode == UART_MODE_SYNC) PORTA.DIRSET = (1 << 4); // PA4(XCK) 출력
        } else if (portmux == UART_PORTMUX_ALT2) {
            PORTC.DIRSET = (1 << 0); // PC0(TxD) 출력
            PORTC.DIRCLR = (1 << 1); // PC1(RxD) 입력
            if (mode == UART_MODE_SYNC) PORTC.DIRSET = (1 << 2); // PC2(XCK) 출력
        }
    } else if (usart == &USART1) {
        // USART1: 기본(PC0/PC1), ALT1(PC2/PC3), ALT2(PA0/PA1)
        // PC0(TxD: VQFN64:16, VQFN48:10, VQFN32:6, SPDIP28:2)
        PORTMUX.USARTROUTEA = (PORTMUX.USARTROUTEA & ~(0x3 << 2)) | (portmux << 2);
        if (portmux == UART_PORTMUX_DEFAULT) {
            PORTC.DIRSET = (1 << 0); // PC0(TxD) 출력
            PORTC.DIRCLR = (1 << 1); // PC1(RxD) 입력
            if (mode == UART_MODE_SYNC) PORTC.DIRSET = (1 << 2); // PC2(XCK) 출력
        } else if (portmux == UART_PORTMUX_ALT1) {
            PORTC.DIRSET = (1 << 2); // PC2(TxD) 출력
            PORTC.DIRCLR = (1 << 3); // PC3(RxD) 입력
#if defined(__AVR_AVR128DA64__) || defined(__AVR_AVR128DA48__)
            if (mode == UART_MODE_SYNC) PORTC.DIRSET = (1 << 4); // PC4(XCK) 출력
#else
            if (mode == UART_MODE_SYNC) {
                #error "USART1 ALT1 XCK (PC4) not supported on AVR128DA32/28"
            }
#endif
        } else if (portmux == UART_PORTMUX_ALT2) {
            PORTA.DIRSET = (1 << 0); // PA0(TxD) 출력
            PORTA.DIRCLR = (1 << 1); // PA1(RxD) 입력
            if (mode == UART_MODE_SYNC) PORTA.DIRSET = (1 << 2); // PA2(XCK) 출력
        }
    } else if (usart == &USART2) {
        // USART2: 기본(PF0/PF1), ALT1(PF2/PF3)
        // PF0(TxD: VQFN64:44, VQFN48:34, VQFN32:20, SPDIP28:16)
        PORTMUX.USARTROUTEA = (PORTMUX.USARTROUTEA & ~(0x3 << 4)) | (portmux << 4);
        if (portmux == UART_PORTMUX_DEFAULT) {
            PORTF.DIRSET = (1 << 0); // PF0(TxD) 출력
            PORTF.DIRCLR = (1 << 1); // PF1(RxD) 입력
#if defined(__AVR_AVR128DA64__) || defined(__AVR_AVR128DA48__) || defined(__AVR_AVR128DA32__)
            if (mode == UART_MODE_SYNC) PORTF.DIRSET = (1 << 2); // PF2(XCK) 출력
#else
            if (mode == UART_MODE_SYNC) {
                #error "USART2 DEFAULT XCK (PF2) not supported on AVR128DA28"
            }
#endif
        } else if (portmux == UART_PORTMUX_ALT1) {
#if defined(__AVR_AVR128DA64__) || defined(__AVR_AVR128DA48__) || defined(__AVR_AVR128DA32__)
            PORTF.DIRSET = (1 << 2); // PF2(TxD) 출력
            PORTF.DIRCLR = (1 << 3); // PF3(RxD) 입력
            if (mode == UART_MODE_SYNC) PORTF.DIRSET = (1 << 4); // PF4(XCK) 출력
#else
            #error "USART2 ALT1 (PF2/PF3/PF4) not supported on AVR128DA28"
#endif
        }
    } else if (usart == &USART3) {
        // USART3: 기본(PB0/PB1), ALT1(PB2/PB3)
        // PB0(TxD: VQFN64:8, VQFN48:4)
#if defined(__AVR_AVR128DA64__) || defined(__AVR_AVR128DA48__)
        PORTMUX.USARTROUTEA = (PORTMUX.USARTROUTEA & ~(0x3 << 6)) | (portmux << 6);
        if (portmux == UART_PORTMUX_DEFAULT) {
            PORTB.DIRSET = (1 << 0); // PB0(TxD) 출력
            PORTB.DIRCLR = (1 << 1); // PB1(RxD) 입력
            if (mode == UART_MODE_SYNC) PORTB.DIRSET = (1 << 2); // PB2(XCK) 출력
        } else if (portmux == UART_PORTMUX_ALT1) {
            PORTB.DIRSET = (1 << 2); // PB2(TxD) 출력
            PORTB.DIRCLR = (1 << 3); // PB3(RxD) 입력
            if (mode == UART_MODE_SYNC) PORTB.DIRSET = (1 << 4); // PB4(XCK) 출력
        }
#else
        if (usart == &USART3) {
            #error "USART3 is only supported on AVR128DA64 and AVR128DA48"
        }
#endif
    }
#if defined(__AVR_AVR128DA64__) || defined(__AVR_AVR128DA48__)
    else if (usart == &USART4) {
        // USART4: 기본(PE0/PE1), ALT1(PE2/PE3), ALT2(PE4/PE5)
        // PE0(TxD: VQFN64:36, VQFN48:30)
        PORTMUX.USARTROUTEB = (PORTMUX.USARTROUTEB & ~(0x3 << 0)) | (portmux << 0);
        if (portmux == UART_PORTMUX_DEFAULT) {
            PORTE.DIRSET = (1 << 0); // PE0(TxD) 출력
            PORTE.DIRCLR = (1 << 1); // PE1(RxD) 입력
            if (mode == UART_MODE_SYNC) PORTE.DIRSET = (1 << 2); // PE2(XCK) 출력
        } else if (portmux == UART_PORTMUX_ALT1) {
            PORTE.DIRSET = (1 << 2); // PE2(TxD) 출력
            PORTE.DIRCLR = (1 << 3); // PE3(RxD) 입력
            if (mode == UART_MODE_SYNC) PORTE.DIRSET = (1 << 4); // PE4(XCK) 출력
        } else if (portmux == UART_PORTMUX_ALT2) {
            PORTE.DIRSET = (1 << 4); // PE4(TxD) 출력
            PORTE.DIRCLR = (1 << 5); // PE5(RxD) 입력
            if (mode == UART_MODE_SYNC) PORTE.DIRSET = (1 << 6); // PE6(XCK) 출력
        }
    }
#endif
#if defined(__AVR_AVR128DA64__)
    else if (usart == &USART5) {
        // USART5: 기본(PG0/PG1), ALT1(PG2/PG3)
        // PG0(TxD: VQFN64:52)
        PORTMUX.USARTROUTEB = (PORTMUX.USARTROUTEB & ~(0x3 << 2)) | (portmux << 2);
        if (portmux == UART_PORTMUX_DEFAULT) {
            PORTG.DIRSET = (1 << 0); // PG0(TxD) 출력
            PORTG.DIRCLR = (1 << 1); // PG1(RxD) 입력
            if (mode == UART_MODE_SYNC) PORTG.DIRSET = (1 << 2); // PG2(XCK) 출력
        } else if (portmux == UART_PORTMUX_ALT1) {
            PORTG.DIRSET = (1 << 2); // PG2(TxD) 출력
            PORTG.DIRCLR = (1 << 3); // PG3(RxD) 입력
            if (mode == UART_MODE_SYNC) PORTG.DIRSET = (1 << 4); // PG4(XCK) 출력
        }
    }
#endif

    // 보드레이트 설정
    uint8_t clk2x = 0;
    usart->BAUD = calculate_baud(baud, clk2x);

    // 프레임 포맷 설정
    if (mode == UART_MODE_ASYNC) {
        usart->CTRLC = (mode << 7) | (parity << 4) | (stop_bits << 3) | (data_bits << 0);
    } else if (mode == UART_MODE_SYNC) {
        uint8_t udord = 0; // MSB 먼저
        uint8_t ucpha = 0; // 선행 에지 샘플링
        usart->CTRLC = (mode << 7) | (udord << 5) | (ucpha << 4) | (data_bits << 0);
    }

    // 송수신 비활성화
    usart->CTRLB = 0x00;

    // 에러 플래그 초기화
    volatile uint8_t dummy = usart->RXDATAH;
    (void)dummy;

    // 링버퍼 및 인스턴스 초기화
    instance->tx_buffer.head = instance->tx_buffer.tail = 0;
    instance->rx_buffer.head = instance->rx_buffer.tail = 0;
    instance->tx_busy = 0;
    instance->usart = usart;
    stdio_usart = usart; // 주의: 다중 UART 사용 시 마지막 설정이 덮어씌워짐
    register_instance(instance, usart);
}

/**
 * @brief UART 송신기 활성화
 */
void uart_enable_tx(UART_Instance *instance) {
    instance->usart->CTRLB |= (1 << 5);
}

/**
 * @brief UART 수신기 활성화
 */
void uart_enable_rx(UART_Instance *instance) {
    instance->usart->CTRLB |= (1 << 6);
}

/**
 * @brief UART 비활성화
 */
void uart_disable(UART_Instance *instance) {
    instance->usart->CTRLB = 0x00;
}

/**
 * @brief 단일 바이트 송신 (폴링)
 */
void uart_transmit(UART_Instance *instance, uint8_t data) {
    while (!(instance->usart->STATUS & USART_DREIF_bm));
    instance->usart->TXDATAL = data;
}

/**
 * @brief 단일 바이트 수신 (폴링)
 */
uint8_t uart_receive(UART_Instance *instance) {
    while (!(instance->usart->STATUS & USART_RXCIF_bm));
    return instance->usart->RXDATAL;
}

/**
 * @brief 폴링 기반 링버퍼 송신
 */
uint8_t uart_transmit_buffered(UART_Instance *instance, uint8_t *data, uint8_t length) {
    uint8_t i;
    for (i = 0; i < length; i++) {
        uint8_t next_head = (instance->tx_buffer.head + 1) % UART_BUFFER_SIZE;
        if (next_head == instance->tx_buffer.tail) return 1;
        instance->tx_buffer.buffer[instance->tx_buffer.head] = data[i];
        instance->tx_buffer.head = next_head;
    }
    while (instance->tx_buffer.head != instance->tx_buffer.tail) {
        while (!(instance->usart->STATUS & USART_DREIF_bm));
        instance->usart->TXDATAL = instance->tx_buffer.buffer[instance->tx_buffer.tail];
        instance->tx_buffer.tail = (instance->tx_buffer.tail + 1) % UART_BUFFER_SIZE;
    }
    return 0;
}

/**
 * @brief 폴링 기반 링버퍼 수신
 */
uint8_t uart_receive_buffered(UART_Instance *instance, uint8_t *data, uint8_t length) {
    uint8_t count = 0;
    while (count < length && (instance->usart->STATUS & USART_RXCIF_bm)) {
        uint8_t next_head = (instance->rx_buffer.head + 1) % UART_BUFFER_SIZE;
        if (next_head != instance->rx_buffer.tail) {
            instance->rx_buffer.buffer[instance->rx_buffer.head] = instance->usart->RXDATAL;
            instance->rx_buffer.head = next_head;
        } else {
            (void)instance->usart->RXDATAL;
            break;
        }
    }
    count = 0;
    while (instance->rx_buffer.head != instance->rx_buffer.tail && count < length) {
        data[count++] = instance->rx_buffer.buffer[instance->rx_buffer.tail];
        instance->rx_buffer.tail = (instance->rx_buffer.tail + 1) % UART_BUFFER_SIZE;
    }
    return count;
}

/**
 * @brief 인터럽트 기반 송신 초기화
 */
void uart_transmit_it_init(UART_Instance *instance) {
    instance->usart->CTRLA |= (1 << 5);
}

/**
 * @brief 인터럽트 기반 수신 초기화
 */
void uart_receive_it_init(UART_Instance *instance) {
    instance->usart->CTRLA |= (1 << 7);
}

/**
 * @brief 인터럽트 기반 데이터 송신
 */
uint8_t uart_transmit_it(UART_Instance *instance, uint8_t *data, uint8_t length) {
    uint8_t i;
    for (i = 0; i < length; i++) {
        uint8_t next_head = (instance->tx_buffer.head + 1) % UART_BUFFER_SIZE;
        if (next_head == instance->tx_buffer.tail) return 1;
        instance->tx_buffer.buffer[instance->tx_buffer.head] = data[i];
        instance->tx_buffer.head = next_head;
    }
    if (!instance->tx_busy) {
        instance->tx_busy = 1;
        instance->usart->TXDATAL = instance->tx_buffer.buffer[instance->tx_buffer.tail];
        instance->tx_buffer.tail = (instance->tx_buffer.tail + 1) % UART_BUFFER_SIZE;
    }
    return 0;
}

/**
 * @brief 인터럽트 기반 데이터 수신
 */
uint8_t uart_receive_it(UART_Instance *instance, uint8_t *data, uint8_t length) {
    uint8_t count = 0;
    while (instance->rx_buffer.head != instance->rx_buffer.tail && count < length) {
        data[count++] = instance->rx_buffer.buffer[instance->rx_buffer.tail];
        instance->rx_buffer.tail = (instance->rx_buffer.tail + 1) % UART_BUFFER_SIZE;
    }
    return count;
}

/**
 * @brief UART 에러 상태 확인 및 클리어
 */
uint8_t uart_check_errors(UART_Instance *instance) {
    uint8_t errors = instance->usart->RXDATAH & (UART_ERROR_FERR | UART_ERROR_PERR | UART_ERROR_BUFOVF);
    if (errors) {
        volatile uint8_t dummy = instance->usart->RXDATAH;
        (void)dummy;
    }
    return errors;
}

/**
 * @brief printf를 UART로 리디렉션
 */
void uart_setup_stdio(UART_Instance *instance) {
    stdio_usart = instance->usart;
    stdout = &uart_stdout;
}

/**
 * @brief 수신 링버퍼의 가용 바이트 수 확인
 */
uint8_t uart_available(UART_Instance *instance) {
    return (UART_BUFFER_SIZE + instance->rx_buffer.head - instance->rx_buffer.tail) % UART_BUFFER_SIZE;
}

/**
 * @brief 송수신 링버퍼 비우기
 */
void uart_flush(UART_Instance *instance) {
    instance->tx_buffer.head = instance->tx_buffer.tail = 0;
    instance->rx_buffer.head = instance->rx_buffer.tail = 0;
    instance->tx_busy = 0;
    instance->usart->CTRLA &= ~(1 << 5);
}

/**
 * @brief 송신 데이터 레지스터 비움 인터럽트 핸들러
 */
static void uart_dre_handler(UART_Instance *instance) {
    if (instance->tx_buffer.head != instance->tx_buffer.tail) {
        instance->usart->TXDATAL = instance->tx_buffer.buffer[instance->tx_buffer.tail];
        instance->tx_buffer.tail = (instance->tx_buffer.tail + 1) % UART_BUFFER_SIZE;
    } else {
        instance->tx_busy = 0;
        instance->usart->CTRLA &= ~(1 << 5);
    }
}

/**
 * @brief 수신 완료 인터럽트 핸들러
 */
static void uart_rxc_handler(UART_Instance *instance) {
    uint8_t next_head = (instance->rx_buffer.head + 1) % UART_BUFFER_SIZE;
    if (next_head != instance->rx_buffer.tail) {
        instance->rx_buffer.buffer[instance->rx_buffer.head] = instance->usart->RXDATAL;
        instance->rx_buffer.head = next_head;
    } else {
        (void)instance->usart->RXDATAL;
    }
}

/**
 * @brief USART 인터럽트 핸들러 정의
 */
ISR(USART0_DRE_vect) {
    if (uart_instances[0]) uart_dre_handler(uart_instances[0]);
}

ISR(USART0_RXC_vect) {
    if (uart_instances[0]) uart_rxc_handler(uart_instances[0]);
}

ISR(USART1_DRE_vect) {
    if (uart_instances[1]) uart_dre_handler(uart_instances[1]);
}

ISR(USART1_RXC_vect) {
    if (uart_instances[1]) uart_rxc_handler(uart_instances[1]);
}

ISR(USART2_DRE_vect) {
    if (uart_instances[2]) uart_dre_handler(uart_instances[2]);
}

ISR(USART2_RXC_vect) {
    if (uart_instances[2]) uart_rxc_handler(uart_instances[2]);
}

#if defined(__AVR_AVR128DA64__) || defined(__AVR_AVR128DA48__)
ISR(USART3_DRE_vect) {
    if (uart_instances[3]) uart_dre_handler(uart_instances[3]);
}

ISR(USART3_RXC_vect) {
    if (uart_instances[3]) uart_rxc_handler(uart_instances[3]);
}

ISR(USART4_DRE_vect) {
    if (uart_instances[4]) uart_dre_handler(uart_instances[4]);
}

ISR(USART4_RXC_vect) {
    if (uart_instances[4]) uart_rxc_handler(uart_instances[4]);
}
#endif

#if defined(__AVR_AVR128DA64__)
ISR(USART5_DRE_vect) {
    if (uart_instances[5]) uart_dre_handler(uart_instances[5]);
}

ISR(USART5_RXC_vect) {
    if (uart_instances[5]) uart_rxc_handler(uart_instances[5]);
}
#endif
/**
 * @file main.c
 * @brief AVR128DA64/48/32/28 UART 드라이버 테스트 프로그램
 * @details 24MHz 클럭, USART0/1으로 printf, 폴링 기반 링버퍼, 
 *          가용 바이트 확인, 버퍼 비우기, 인터럽트 기반 테스트
 * @author 작성자
 * @date 2025-09-03
 */
#ifndef F_CPU
#define F_CPU 24000000UL // 시스템 클럭 주파수 24MHz 정의
#endif

#include <avr/io.h>      // AVR 레지스터 정의
#include <util/delay.h>  // 지연 함수
#include <stdio.h>       // printf 지원
#include <avr/interrupt.h> // 인터럽트 활성화
#include "uart_driver.h" // UART 드라이버 헤더

/**
 * @brief 시스템 클럭을 24MHz OSCHF로 설정하고 Auto-tune 활성화
 * @details 데이터시트(DS40002183A) 기반:
 *          - XOSCHFCTRLA: 32.768kHz 크리스털 활성 (ENABLE=1, SEL=0, RUNSTDBY=0).
 *          - OSCHCTRLA: FRQSEL=0x9(24MHz), AUTOTUNE=1.
 *          - MCLKCTRLB: 프리스케일러 비활성 (PEN=0).
 *          - MCLKCTRLA: OSCHF 선택 (CLKSEL=0x0).
 *          - _PROTECTED_WRITE로 CCP 보호.
 * @note TOSC1/TOSC2(PF6/PF7)에 32.768kHz 크리스털 연결 필수
 */
void clock_init_24mhz(void) {
    _PROTECTED_WRITE(CLKCTRL.XOSCHFCTRLA, CLKCTRL_ENABLE_bm | (0 << CLKCTRL_SEL_bp)); // 32.768kHz 크리스털 활성
    _PROTECTED_WRITE(CLKCTRL.OSCHCTRLA, (0x9 << CLKCTRL_FRQSEL_gp) | CLKCTRL_AUTOTUNE_bm); // 24MHz, Auto-tune
    _PROTECTED_WRITE(CLKCTRL.MCLKCTRLB, 0x00); // 프리스케일러 비활성
    _PROTECTED_WRITE(CLKCTRL.MCLKCTRLA, 0x00); // OSCHF 선택
    while ((CLKCTRL.MCLKSTATUS & CLKCTRL_OSCHF_bm) == 0); // 안정화 대기
}

/**
 * @brief 메인 함수
 * @details USART0/1으로 9600bps, 8-N-1 설정 후 폴링 및 인터럽트 기반 링버퍼 테스트
 */
int main(void) {
    clock_init_24mhz(); // 클럭 설정
    
    // 전역 인터럽트 활성화
    sei();

    // USART0 초기화: 9600bps, 8비트, 1 스톱 비트, 패리티 없음, 비동기, ALT1
    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_ALT1);
    uart_enable_tx(&usart0_instance); // 송신 활성화
    uart_enable_rx(&usart0_instance); // 수신 활성화
    uart_setup_stdio(&usart0_instance); // printf 설정

    // USART1 초기화: 인터럽트 기반, 9600bps, 8비트, 1 스톱 비트, 패리티 없음, 비동기, DEFAULT
    UART_Instance usart1_instance;
    uart_init(&usart1_instance, &USART1, UART_BAUD_9600, UART_DATA_8BIT, UART_STOP_1BIT, UART_PARITY_NONE, UART_MODE_ASYNC, UART_PORTMUX_DEFAULT);
    uart_enable_tx(&usart1_instance); // 송신 활성화
    uart_enable_rx(&usart1_instance); // 수신 활성화
    uart_transmit_it_init(&usart1_instance); // 송신 인터럽트 활성화
    uart_receive_it_init(&usart1_instance); // 수신 인터럽트 활성화

    // 테스트 메시지
    printf("UART Driver Test: USART0 (Polling) and USART1 (Interrupt)\r\n");
    _delay_ms(1000);

    // 폴링 기반 테스트 (USART0)
    uint8_t test_data[] = "Hello, AVR128DA!\r\n";
    if (uart_transmit_buffered(&usart0_instance, test_data, sizeof(test_data) - 1) == 0) {
        printf("USART0: Sent %d bytes successfully\r\n", sizeof(test_data) - 1);
    } else {
        printf("USART0: Transmit buffer full!\r\n");
    }
    _delay_ms(1000);

    // 인터럽트 기반 테스트 (USART1)
    if (uart_transmit_it(&usart1_instance, test_data, sizeof(test_data) - 1) == 0) {
        printf("USART1: Sent %d bytes via interrupt\r\n", sizeof(test_data) - 1);
    } else {
        printf("USART1: Transmit buffer full!\r\n");
    }
    _delay_ms(1000);

    // 수신 및 에러 테스트
    uint8_t rx_buffer[UART_BUFFER_SIZE];
    while (1) {
        // USART0: 폴링 기반 수신
        uint8_t rx_count = uart_receive_buffered(&usart0_instance, rx_buffer, sizeof(rx_buffer));
        if (rx_count > 0) {
            printf("USART0: Received %d bytes: ", rx_count);
            for (uint8_t i = 0; i < rx_count; i++) {
                printf("%c", rx_buffer[i]);
            }
            printf("\r\n");
        }

        // USART1: 인터럽트 기반 수신
        rx_count = uart_receive_it(&usart1_instance, rx_buffer, sizeof(rx_buffer));
        if (rx_count > 0) {
            printf("USART1: Received %d bytes: ", rx_count);
            for (uint8_t i = 0; i < rx_count; i++) {
                printf("%c", rx_buffer[i]);
            }
            printf("\r\n");
        }

        // 에러 확인
        uint8_t errors = uart_check_errors(&usart0_instance);
        if (errors) {
            printf("USART0 Errors: %s%s%s\r\n",
                   (errors & UART_ERROR_FERR) ? "Frame Error " : "",
                   (errors & UART_ERROR_PERR) ? "Parity Error " : "",
                   (errors & UART_ERROR_BUFOVF) ? "Buffer Overflow" : "");
        }

        errors = uart_check_errors(&usart1_instance);
        if (errors) {
            printf("USART1 Errors: %s%s%s\r\n",
                   (errors & UART_ERROR_FERR) ? "Frame Error " : "",
                   (errors & UART_ERROR_PERR) ? "Parity Error " : "",
                   (errors & UART_ERROR_BUFOVF) ? "Buffer Overflow" : "");
        }

        // 가용 바이트 확인
        printf("USART0 Available: %d bytes\r\n", uart_available(&usart0_instance));
        printf("USART1 Available: %d bytes\r\n", uart_available(&usart1_instance));

        // 주기적으로 버퍼 비우기 테스트
        if (uart_available(&usart0_instance) > UART_BUFFER_SIZE / 2) {
            printf("Flushing USART0 buffer\r\n");
            uart_flush(&usart0_instance);
        }
        if (uart_available(&usart1_instance) > UART_BUFFER_SIZE / 2) {
            printf("Flushing USART1 buffer\r\n");
            uart_flush(&usart1_instance);
        }

        _delay_ms(1000); // 1초 대기
    }

    return 0; // 프로그램 종료 (무한 루프이므로 도달하지 않음)
}
/**
 * @file uart_echo_buffered_example.c
 * @brief AVR128DA UART 드라이버 폴링 기반 에코 예제
 * @details USART0을 사용하여 수신 데이터를 링버퍼로 받아 동일 데이터를 송신 (에코).
 *          9600bps, 8-N-1, 비동기, DEFAULT 핀 설정 사용.
 * @author 작성자
 * @date 2025-09-03
 */
#ifndef F_CPU
#define F_CPU 24000000UL // 시스템 클럭 주파수 24MHz 정의
#endif

#include <avr/io.h>      // AVR 레지스터 정의
#include <util/delay.h>  // 지연 함수
#include <stdio.h>       // printf 지원
#include "uart_driver.h" // UART 드라이버 헤더

/**
 * @brief 시스템 클럭을 24MHz OSCHF로 설정하고 Auto-tune 활성화
 * @details 데이터시트(DS40002183A) 기반:
 *          - XOSC32KCTRLA: 32.768kHz 크리스털 활성 (ENABLE=1, RUNSTDBY=1).
 *          - OSCHFCTRLA: FRQSEL=24MHz, AUTOTUNE=1.
 *          - MCLKCTRLB: 프리스케일러 비활성 (PEN=0).
 *          - MCLKCTRLA: OSCHF 선택 (CLKSEL=0x0).
 *          - _PROTECTED_WRITE로 CCP 보호.
 * @note TOSC1/TOSC2(PF6/PF7)에 32.768kHz 크리스털 연결 필수
 */
void clock_init_24mhz(void) {
    // 32.768kHz 외부 크리스털 오실레이터(XOSC32K) 활성화
    _PROTECTED_WRITE(CLKCTRL.XOSC32KCTRLA, CLKCTRL_ENABLE_bm | CLKCTRL_RUNSTDBY_bm);
    
    // XOSC32K 안정화 대기 (타임아웃 약 1초)
    uint32_t timeout = 24000000UL;
    while (!(CLKCTRL.MCLKSTATUS & CLKCTRL_XOSC32KS_bm) && timeout--) {
        _delay_us(1);
    }
    if (timeout == 0) {
        // 타임아웃 시 디버깅용 무한 루프
        while (1);
    }
    
    // 내부 OSCHF를 24MHz로 설정, XOSC32K 오토튜닝 활성화
    _PROTECTED_WRITE(CLKCTRL.OSCHFCTRLA, CLKCTRL_FRQSEL_24M_gc | CLKCTRL_AUTOTUNE_bm);
    
    // 시스템 클럭 소스를 OSCHF로 설정
    _PROTECTED_WRITE(CLKCTRL.MCLKCTRLA, CLKCTRL_CLKSEL_OSCHF_gc);
    
    // 클럭 프리스케일러 비활성화
    _PROTECTED_WRITE(CLKCTRL.MCLKCTRLB, 0x00);
    
    // 시스템 클럭 안정화 대기 (타임아웃 약 1초)
    timeout = 24000000UL;
    while ((CLKCTRL.MCLKSTATUS & CLKCTRL_SOSC_bm) && timeout--) {
        _delay_us(1);
    }
    if (timeout == 0) {
        // 타임아웃 시 디버깅용 무한 루프
        while (1);
    }
}

/**
 * @brief 메인 함수
 * @details USART0을 초기화하고, 수신 데이터를 수신 링버퍼에서 읽어 송신 링버퍼로 에코.
 */
int main(void) {
    clock_init_24mhz(); // 클럭 설정

    // USART0 초기화: 9600bps, 8비트, 1 스톱 비트, 패리티 없음, 비동기, DEFAULT
    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); // printf 설정

    printf("UART Echo Test: Send data to USART0 to see it echoed back\r\n");
    _delay_ms(1000);

    uint8_t rx_buffer[UART_BUFFER_SIZE];
    while (1) {
        // 수신 데이터 확인
        uint8_t rx_count = uart_receive_buffered(&usart0_instance, rx_buffer, sizeof(rx_buffer));
        if (rx_count > 0) {
            // 수신 데이터를 송신 링버퍼로 에코
            if (uart_transmit_buffered(&usart0_instance, rx_buffer, rx_count) == 0) {
                printf("Echoed %d bytes\r\n", rx_count);
            } else {
                printf("Transmit buffer full!\r\n");
            }
        }

        // 에러 확인
        uint8_t errors = uart_check_errors(&usart0_instance);
        if (errors) {
            printf("Errors: %s%s%s\r\n",
                   (errors & UART_ERROR_FERR) ? "Frame Error " : "",
                   (errors & UART_ERROR_PERR) ? "Parity Error " : "",
                   (errors & UART_ERROR_BUFOVF) ? "Buffer Overflow" : "");
        }

        _delay_ms(100); // 100ms 대기
    }

    return 0; // 프로그램 종료 (무한 루프이므로 도달하지 않음)
}
/**
 * @file uart_monitor_example.c
 * @brief AVR128DA UART 드라이버 인터럽트 기반 시리얼 모니터 예제
 * @details USART1을 사용하여 인터럽트 기반으로 데이터를 수신하고, 
 *          수신된 데이터를 16진수로 출력하며, 에러 상태와 가용 바이트를 모니터링.
 * @author 작성자
 * @date 2025-09-03
 */
#ifndef F_CPU
#define F_CPU 24000000UL // 시스템 클럭 주파수 24MHz 정의
#endif

#include <avr/io.h>      // AVR 레지스터 정의
#include <util/delay.h>  // 지연 함수
#include <stdio.h>       // printf 지원
#include <avr/interrupt.h> // 인터럽트 활성화
#include "uart_driver.h" // UART 드라이버 헤더

/**
 * @brief 시스템 클럭을 24MHz OSCHF로 설정하고 Auto-tune 활성화
 * @details 데이터시트(DS40002183A) 기반:
 *          - XOSC32KCTRLA: 32.768kHz 크리스털 활성 (ENABLE=1, RUNSTDBY=1).
 *          - OSCHFCTRLA: FRQSEL=24MHz, AUTOTUNE=1.
 *          - MCLKCTRLB: 프리스케일러 비활성 (PEN=0).
 *          - MCLKCTRLA: OSCHF 선택 (CLKSEL=0x0).
 *          - _PROTECTED_WRITE로 CCP 보호.
 * @note TOSC1/TOSC2(PF6/PF7)에 32.768kHz 크리스털 연결 필수
 */
void clock_init_24mhz(void) {
    // 32.768kHz 외부 크리스털 오실레이터(XOSC32K) 활성화
    _PROTECTED_WRITE(CLKCTRL.XOSC32KCTRLA, CLKCTRL_ENABLE_bm | CLKCTRL_RUNSTDBY_bm);
    
    // XOSC32K 안정화 대기 (타임아웃 약 1초)
    uint32_t timeout = 24000000UL;
    while (!(CLKCTRL.MCLKSTATUS & CLKCTRL_XOSC32KS_bm) && timeout--) {
        _delay_us(1);
    }
    if (timeout == 0) {
        // 타임아웃 시 디버깅용 무한 루프
        while (1);
    }
    
    // 내부 OSCHF를 24MHz로 설정, XOSC32K 오토튜닝 활성화
    _PROTECTED_WRITE(CLKCTRL.OSCHFCTRLA, CLKCTRL_FRQSEL_24M_gc | CLKCTRL_AUTOTUNE_bm);
    
    // 시스템 클럭 소스를 OSCHF로 설정
    _PROTECTED_WRITE(CLKCTRL.MCLKCTRLA, CLKCTRL_CLKSEL_OSCHF_gc);
    
    // 클럭 프리스케일러 비활성화
    _PROTECTED_WRITE(CLKCTRL.MCLKCTRLB, 0x00);
    
    // 시스템 클럭 안정화 대기 (타임아웃 약 1초)
    timeout = 24000000UL;
    while ((CLKCTRL.MCLKSTATUS & CLKCTRL_SOSC_bm) && timeout--) {
        _delay_us(1);
    }
    if (timeout == 0) {
        // 타임아웃 시 디버깅용 무한 루프
        while (1);
    }
}

/**
 * @brief 메인 함수
 * @details USART1을 인터럽트 기반으로 초기화하고, 수신 데이터를 16진수로 출력.
 *          주기적으로 에러 상태와 가용 바이트를 모니터링.
 */
int main(void) {
    clock_init_24mhz(); // 클럭 설정

    // 전역 인터럽트 활성화
    sei();

    // USART0 초기화: printf용, 9600bps, 8비트, 1 스톱 비트, 패리티 없음, 비동기, DEFAULT
    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); // printf 설정

    // USART1 초기화: 인터럽트 기반, 9600bps, 8비트, 1 스톱 비트, 패리티 없음, 비동기, DEFAULT
    UART_Instance usart1_instance;
    uart_init(&usart1_instance, &USART1, UART_BAUD_9600, UART_DATA_8BIT, UART_STOP_1BIT, UART_PARITY_NONE, UART_MODE_ASYNC, UART_PORTMUX_DEFAULT);
    uart_enable_tx(&usart1_instance); // 송신 활성화
    uart_enable_rx(&usart1_instance); // 수신 활성화
    uart_transmit_it_init(&usart1_instance); // 송신 인터럽트 활성화
    uart_receive_it_init(&usart1_instance); // 수신 인터럽트 활성화

    printf("UART Serial Monitor Test: Send data to USART1 to see hex output\r\n");
    _delay_ms(1000);

    uint8_t rx_buffer[UART_BUFFER_SIZE];
    while (1) {
        // 인터럽트 기반 수신
        uint8_t rx_count = uart_receive_it(&usart1_instance, rx_buffer, sizeof(rx_buffer));
        if (rx_count > 0) {
            printf("USART1: Received %d bytes (Hex): ", rx_count);
            for (uint8_t i = 0; i < rx_count; i++) {
                printf("%02X ", rx_buffer[i]);
            }
            printf("\r\n");
        }

        // 에러 확인
        uint8_t errors = uart_check_errors(&usart1_instance);
        if (errors) {
            printf("USART1 Errors: %s%s%s\r\n",
                   (errors & UART_ERROR_FERR) ? "Frame Error " : "",
                   (errors & UART_ERROR_PERR) ? "Parity Error " : "",
                   (errors & UART_ERROR_BUFOVF) ? "Buffer Overflow" : "");
        }

        // 가용 바이트 확인
        uint8_t available = uart_available(&usart1_instance);
        if (available > 0) {
            printf("USART1 Available: %d bytes\r\n", available);
        }

        // 버퍼가 절반 이상 차면 비우기
        if (available > UART_BUFFER_SIZE / 2) {
            printf("Flushing USART1 buffer\r\n");
            uart_flush(&usart1_instance);
        }

        _delay_ms(500); // 500ms 대기
    }

    return 0; 
}

추가팁

  • 보드레이트 최적화: 높은 보드레이트(예: 115200bps)를 사용할 경우 CLK2x 모드를 활성화하여 BAUD 값을 낮추고, 클럭 에러를 최소화하세요.
  • 인터럽트 우선순위: 다중 USART 사용 시, 인터럽트 충돌을 방지하려면 CPUINT의 우선순위를 조정하거나, 중요 USART의 인터럽트를 먼저 처리하도록 설계하세요.
  • 전력 절감: 사용하지 않는 USART는 uart_disable로 비활성화하여 전력을 절감하세요.
  • 핀 충돌 방지: PORTMUX 설정 시 SPI, TWI 등 다른 주변 장치와의 핀 충돌을 확인하세요. 데이터시트의 Pinout 섹션을 참조하세요.
  • 링버퍼 크기 조정: UART_BUFFER_SIZE를 애플리케이션 요구사항에 맞게 조정하여 메모리 사용을 최적화하세요.
  • 디버깅: uart_check_errorsprintf를 활용해 에러 상태를 주기적으로 모니터링하세요.
  • 동기 모드: 동기 모드 사용 시 XCK 핀 설정과 클럭 위상/데이터 순서를 정확히 지정하세요.
  • 테스트 환경: Microchip Studio에서 시뮬레이터를 사용하거나, 실제 하드웨어에서 터미널 프로그램(예: PuTTY)을 통해 출력 확인하세요.

결론

본 UART 드라이버는 AVR128DA64/48/32/28 시리즈의 USART 기능을 완벽히 활용하도록 설계되었습니다. 링버퍼 기반의 효율적인 데이터 관리, 폴링 및 인터럽트 지원, printf 출력, 가용 바이트 확인, 버퍼 비우기, 에러 처리 등 다양한 기능을 제공하며, 모든 AVR128DA 모델에 호환됩니다. 다중 USART(USART0~5)를 지원하는 인터럽트 핸들러와 PORTMUX를 통한 유연한 핀 설정으로 다양한 애플리케이션에 적용 가능합니다. 제공된 예제 코드를 통해 쉽게 테스트할 수 있으며, 추가팁을 참고하여 성능을 최적화할 수 있습니다. 이 드라이버는 임베디드 시스템 개발에서 신뢰할 수 있는 UART 통신 솔루션을 제공합니다.