본문 바로가기
MCU/AVR

AVR128DA64/48/32/28 Sleep Controller 드라이버 설계 및 구현

by linuxgo 2025. 9. 5.
반응형

개요

본 문서는 Microchip의 AVR128DA64/48/32/28 시리즈 마이크로컨트롤러의 **Sleep Controller (SLPCTRL)**를 분석하고, 이를 활용한 범용 슬립 컨트롤러 드라이버를 설계 및 구현한 내용을 다룹니다. AVR128DA 시리즈는 세 가지 슬립 모드(Idle, Standby, Power-Down)를 제공하여 저전력 애플리케이션 요구사항을 충족하며, 전력 소모와 성능을 최적화합니다. 본 드라이버는 SLPCTRL의 슬립 모드 설정, 전압 레귤레이터 제어, 인터럽트 기반 웨이크업, Configuration Change Protection(CCP) 준수, 전력 최적화, 그리고 슬립 모드에서 정상 모드(Active 모드)로의 복귀를 지원합니다. AVR128DA 전 제품군(64/48/32/28)에 호환되도록 설계되었으며, Microchip Studio 및 AVR-GCC 환경에서 쉽게 통합 가능합니다.

SLPCTRL 블록 다이어그램

AVR128DA Sleep Controller block diagram

드라이버는 다음과 같은 기능을 제공합니다:

  • 슬립 모드 지원: Idle, Standby, Power-Down 모드 설정.
  • 전압 레귤레이터 제어: Normal(AUTO) 및 Performance(FULL) 모드, 고온 저누설(HTLLEN) 설정.
  • 인터럽트 웨이크업: PORT, BOD, RTC 등 웨이크업 소스 설정 및 처리.
  • 정상 모드 복귀: 인터럽트 서비스 루틴(ISR) 실행 후 Active 모드로 복귀.
  • CCP 보호: _PROTECTED_WRITE로 안전한 레지스터 수정.
  • 전력 최적화: 불필요한 주변 장치 및 클럭 비활성화, Standby 모드에서 RUNSTDBY 제어.
  • 상태 확인: 현재 슬립 모드 및 전압 레귤레이터 상태 조회.

사양

AVR128DA 시리즈의 SLPCTRL 사양은 다음과 같습니다:

  • 슬립 모드:
    • Idle: CPU 정지, 모든 주변 장치 동작, 모든 인터럽트로 웨이크업.
    • Standby: 고주파 클럭 정지, RUNSTDBY로 주변 장치 선택적 동작, 제한된 인터럽트 웨이크업.
    • Power-Down: 고주파 클럭 정지, 제한된 주변 장치(RTC, WDT, BOD 등) 동작, 고온 저누설(HTLLEN) 옵션.
  • 전압 레귤레이터 모드:
    • Normal (AUTO): Active/Idle에서 최대 성능, 32.768 kHz 오실레이터 사용 시 저전력.
    • Performance (FULL): 모든 모드에서 최대 성능, 빠른 웨이크업.
    • HTLLEN: 70°C 이상에서 누설 전류 감소 (Power-Down 전용, TWI/CCL 비활성화 필요).
  • 웨이크업 소스:
    • Idle: 모든 인터럽트 (PORT, BOD, RTC, TWI, CCL, USART, TCAn, TCBn, ACn, ADCn, PTC, ZCD).
    • Standby: PORT, BOD, RTC, TWI, CCL, USART Start-Of-Frame, TCAn.
    • Power-Down (HTLLEN=0): PORT, BOD, RTC, TWI, CCL.
    • Power-Down (HTLLEN=1): PORT, BOD, RTC.
  • 웨이크업 시간:
    • Idle: 6 클럭 사이클.
    • Standby/Power-Down: 6 클럭 사이클 + 오실레이터/레귤레이터 시작 시간.
    • BOD 대기: FUSE.BODCFG의 ACTIVE 비트로 BOD 준비까지 대기 가능.
  • CCP 보호: SLPCTRL.VREGCTRL 레지스터는 _PROTECTED_WRITE로 수정.
  • 전압 범위: 1.8V ~ 5.5V (VDD).
  • 리셋 상태:
    • SLPCTRL.CTRLA: 0x00 (슬립 모드 비활성, SEN=0).
    • SLPCTRL.VREGCTRL: 0x00 (HTLLEN=0, PMODE=AUTO).

Power-Down 모드의 중요성

Power-Down 모드는 가장 낮은 전력 소모를 제공하며, IoT 디바이스, 배터리 구동 애플리케이션에 적합합니다. 주요 특징은 다음과 같습니다:

  • 초저전력: 고주파 클럭 정지, ~1 µA 소모 (VDD=5V, OSC32K 사용).
  • 고온 최적화: HTLLEN=1로 70°C 이상에서 누설 전류 감소.
  • 제한된 웨이크업: PORT, BOD, RTC 인터럽트로 깨어남.
  • 활용 사례:
    • 스마트 워치: RTC로 주기적 웨이크업.
    • 센서 노드: 배터리 수명 연장.
    • 원격 모니터링: 초저전력 대기.

슬립 모드 진입 및 웨이크업 메커니즘

  • 슬립 모드 진입:
    • SLPCTRL.CTRLA의 **SMODE[2:0]**로 모드 설정 (Idle, Standby, Power-Down).
    • SEN 비트를 1로 설정하여 슬립 활성화.
    • SLEEP 명령(asm("SLEEP")) 실행으로 CPU가 슬립 상태로 전환.
  • 웨이크업 메커니즘:
    • 인터럽트: 설정된 웨이크업 소스(PORT, RTC, BOD 등)에 의해 인터럽트 발생 시 장치가 깨어남.
    • 리셋: 외부 리셋, Brown-out 리셋 등으로 깨어남.
    • 웨이크업 시, 인터럽트 서비스 루틴(ISR)이 실행되고, ISR 종료 후 SLEEP 명령 직후의 명령부터 정상 실행 재개.
  • 정상 모드(Active 모드) 복귀:
    • 슬립 모드에서 깨어난 후, CPU는 자동으로 Active 모드로 전환.
    • SLPCTRL.CTRLA의 SEN 비트는 자동으로 0으로 리셋되지 않으므로, 필요 시 애플리케이션에서 SEN=0으로 설정하여 슬립 모드 비활성화.
    • 클럭 소스와 전압 레귤레이터는 슬립 모드 설정에 따라 복원 (예: Standby/Power-Down에서는 오실레이터 시작 시간 필요).
    • 애플리케이션은 웨이크업 후 상태를 확인하고 필요한 초기화를 수행 (예: UART 재설정, LED 상태 업데이트).

레지스터 설정 상세

SLPCTRL은 두 개의 주요 레지스터로 제어됩니다. 데이터시트(DS40002183E)에 따라 레지스터와 비트필드는 다음과 같습니다:

  • SLPCTRL.CTRLA (0x00):
    • 비트 [3:1]: SMODE[2:0] (슬립 모드: 0x0=IDLE, 0x1=STANDBY, 0x2=PDOWN).
    • 비트 [0]: SEN (슬립 활성화: 0=비활성, 1=활성).
    • 리셋: 0x00, 읽기/쓰기(R/W).
  • SLPCTRL.VREGCTRL (0x01):
    • 비트 [4]: HTLLEN (고온 저누설: 0=비활성, 1=활성).
    • 비트 [2:0]: PMODE[2:0] (전원 모드: 0x0=AUTO, 0x1=FULL).
    • 리셋: 0x00, R/W, CCP 보호.

슬립 모드 설정 및 웨이크업 절차

슬립 모드 진입, 웨이크업, 그리고 정상 모드로의 복귀는 다음 순서로 진행합니다:

  1. 인터럽트 설정:
    • 웨이크업 소스(예: PORT, RTC)와 전역 인터럽트 활성화.
    • 슬립 모드별 사용 가능한 인터럽트 확인 (예: Power-Down에서 TWI/CCL 제한).
  2. 슬립 모드 선택:
    • SLPCTRL.CTRLA의 SMODE[2:0]로 IDLE/STANDBY/PDOWN 설정.
  3. 슬립 활성화:
    • SLPCTRL.CTRLA의 SEN=1로 슬립 모드 활성화.
  4. 전압 레귤레이터 설정:
    • SLPCTRL.VREGCTRL로 PMODE 및 HTLLEN 설정 (CCP 준수).
  5. SLEEP 명령:
    • asm("SLEEP")로 슬립 모드 진입.
  6. 웨이크업 처리:
    • 인터럽트 발생 시 ISR 실행.
    • ISR에서 웨이크업 플래그 처리 및 상태 업데이트.
  7. 정상 모드 복귀:
    • ISR 종료 후 SLEEP 명령 다음 명령부터 실행.
    • 필요 시 SEN=0으로 슬립 비활성화 또는 재설정.
  8. 테스트:
    • UART 출력 또는 LED로 슬립/웨이크업/정상 모드 동작 확인.

슬립 모드 설정 고려사항

  • CCP 보호: SLPCTRL.VREGCTRL은 _PROTECTED_WRITE로 수정. 미사용 시 쓰기 무시.
  • 웨이크업 소스:
    • Standby/Power-Down에서는 제한된 인터럽트 소스만 사용 가능.
    • HTLLEN=1일 때 TWI/CCL 비활성화 필수.
  • 전력 최적화:
    • 불필요한 주변 장치 비활성화 (예: RUNSTDBY=0).
    • Power-Down에서 OSC32K/XOSC32K로 ~1 µA 소모.
  • 웨이크업 시간:
    • Idle: ~6 클럭 사이클 (빠름).
    • Standby/Power-Down: 오실레이터 및 레귤레이터 시작 시간 추가.
  • 애플리케이션별 최적화:
    • 고성능: Idle 모드로 빠른 웨이크업 (예: 실시간 제어).
    • 저전력: Power-Down 모드, RTC 웨이크업 (예: IoT 디바이스).
    • 고온 환경: HTLLEN=1로 누설 전류 감소.
  • 인터럽트 관리:
    • 슬립 진입 전 인터럽트 활성화 필수 (미활성화 시 리셋으로만 복구).
    • PORT 핀은 완전 비동기 인터럽트 지원.
  • 정상 모드 복귀:
    • 웨이크업 후 클럭 소스 및 레지스터 상태 확인.
    • 애플리케이션에서 필요한 초기화 수행 (예: UART, 타이머).
  • 전력 소모 분석:
    • Idle: ~2 mA (VDD=5V, 8 MHz).
    • Standby: ~10 µA (RUNSTDBY=0).
    • Power-Down: ~1 µA (HTLLEN=1, OSC32K).

드라이버 구현 내용

슬립 컨트롤러 드라이버는 AVR128DA64/48/32/28 호환으로 설계되었으며, 슬립 모드 설정과 웨이크업 처리를 추상화합니다. 주요 구현은 다음과 같습니다:

  • 슬립 모드 설정: Idle, Standby, Power-Down 모드 초기화 함수.
  • 전압 레귤레이터 제어: AUTO/FULL 모드, HTLLEN 설정.
  • 인터럽트 웨이크업: PORT, RTC 기반 웨이크업 설정 및 ISR 처리.
  • 정상 모드 복귀: 웨이크업 후 Active 모드 전환 및 상태 복원.
  • CCP 보호: _PROTECTED_WRITE로 VREGCTRL 안전 수정.
  • 전력 최적화: 불필요한 주변 장치 및 클럭 비활성화.
  • 상태 조회: 현재 슬립 모드 및 전압 레귤레이터 상태 반환.
  • 호환성: <avr/io.h>로 모델별 정의 지원.

사용방법

슬립 컨트롤러 드라이버를 효과적으로 사용하기 위한 단계별 가이드를 제공합니다.

1. 프로젝트 설정

  • 필수 헤더 파일 포함:
    • sleep_driver.h, <avr/io.h>, <avr/cpufunc.h> 포함.
    • <avr/io.h>: 레지스터 정의 제공.
    • <avr/cpufunc.h>: _PROTECTED_WRITE 매크로 제공.
    #include <avr/io.h>
    #include <avr/cpufunc.h>
    #include "sleep_driver.h"
    
  • Microchip Studio 설정:
    • 새 AVR 프로젝트 생성 (GCC C Executable Project).
    • 대상 디바이스: AVR128DA64/48/32/28.
    • Toolchain: AVR-GCC, 디버거: SimAVR 또는 AVR-ISP mkII.
    • sleep_driver.h, sleep_driver.c 추가.
  • F_CPU 정의:
    • 클럭 주파수에 맞게 정의 (예: #define F_CPU 8000000UL).

2. 슬립 모드 선택

애플리케이션 요구사항에 따라 적합한 슬립 모드 함수 선택:

  • Idle 모드:
    • 함수: sleep_init_idle()
    • 사용 사례: 빠른 웨이크업, 실시간 제어.
    • 전력 소모: ~2 mA (8 MHz).
    sleep_init_idle();
    sleep_enter();
    
  • Standby 모드:
    • 함수: sleep_init_standby()
    • 사용 사례: 주기적 작업, 센서 모니터링.
    • 전력 소모: ~10 µA (RUNSTDBY=0).
    sleep_init_standby();
    sleep_enter();
    
  • Power-Down 모드:
    • 함수: sleep_init_powerdown()
    • 사용 사례: 배터리 구동 IoT 디바이스.
    • 전력 소모: ~1 µA (HTLLEN=1).
    sleep_init_powerdown(true);
    sleep_enter();
    
  • 커스텀 설정:
    • 함수: sleep_init_custom()
    • 사용 사례: 특정 전압 레귤레이터 모드(AUTO/FULL) 설정.
    sleep_init_custom(SLEEP_MODE_STANDBY, SLEEP_PMODE_FULL);
    sleep_enter();
    

3. 하드웨어 준비

  • PORT 인터럽트:
    • 웨이크업용 핀 (예: PC0) 설정, 완전 비동기 지원 확인.
    PORTC.PIN0CTRL = PORT_ISC_BOTHEDGES_gc;
    PORTC.INTFLAGS = PORT_INT0_bm;
    
  • RTC 설정:
    • XOSC32K(TOSC1/TOSC2, PF6/PF7)에 32.768 kHz 크리스털 연결.
    • RTC 인터럽트로 주기적 웨이크업.
    RTC.CLKSEL = RTC_CLKSEL_XOSC32K_gc;
    RTC.PER = 32767;
    RTC.INTCTRL = RTC_OVF_bm;
    RTC.CTRLA = RTC_PRESCALER_DIV1_gc | RTC_RTCEN_bm;
    

4. 웨이크업 및 정상 모드 복귀

  • 웨이크업 처리:
    • 인터럽트 발생 시 ISR에서 플래그 처리 및 상태 업데이트.
    • 예: RTC 인터럽트로 웨이크업 후 카운터 증가.
    ISR(RTC_CNT_vect) {
        RTC.INTFLAGS = RTC_OVF_bm;
        rtc_ticks++;
    }
    
  • 정상 모드 복귀:
    • ISR 종료 후 SLEEP 명령 다음 명령부터 실행.
    • 필요 시 SEN=0으로 슬립 비활성화.
    SLPCTRL.CTRLA &= ~SLPCTRL_SEN_bm; // 슬립 비활성화
    
  • 상태 조회:
    • sleep_get_status()로 현재 상태 확인.
    SleepStatus status;
    sleep_get_status(&status);
    printf("Mode=%d, PMODE=%d, HTLLEN=%d\r\n", status.mode, status.pmode, status.htllen);
    

5. 테스트 및 디버깅

  • UART 출력:
    • USART0(PA0: TXD, PA1: RXD)로 상태 출력, 9600bps.
    uart_init(&usart0_instance, &USART0, UART_BAUD_9600, UART_DATA_8BIT, UART_STOP_1BIT, UART_PARITY_NONE, UART_MODE_ASYNC, UART_PORTMUX_DEFAULT);
    printf("Sleep Test: Power-Down Mode\r\n");
    
  • RTC 웨이크업:
    • 1초 주기 RTC 인터럽트로 슬립/웨이크업 테스트.
  • LED 토글:
    • 웨이크업 시 LED(예: PA7) 토글로 정상 모드 확인.
    PORTA.DIRSET = PIN7_bm;
    PORTA.OUTTGL = PIN7_bm;
    
  • Microchip Studio 디버거:
    • I/O View로 SLPCTRL.CTRLA, VREGCTRL 모니터링.
    • SimAVR 또는 Atmel-ICE 사용.

6. 프로젝트 통합

  • 드라이버 파일 추가:
    • sleep_driver.h, sleep_driver.c를 프로젝트에 추가.
    • main.c에서 초기화 및 웨이크업 처리:
    int main(void) {
        sleep_init_powerdown(true);
        sei();
        while (1) {
            sleep_enter();
            // 웨이크업 후 처리
            PORTA.OUTTGL = PIN7_bm; // LED 토글
        }
        return 0;
    }
    
  • 다른 모듈 통합:
    • RTC, PORT 인터럽트와 함께 사용.
    • CLKCTRL 설정(F_CPU)에 맞게 동기화.

7. 문제 해결 팁

  • 슬립 모드 진입 실패:
    • SEN 비트 확인: SLPCTRL.CTRLA의 SEN=1 설정.
    • SLEEP 명령 확인: asm("SLEEP") 사용.
  • 웨이크업 실패:
    • 인터럽트 활성화 확인: 전역 인터럽트(sei()) 및 소스별 인터럽트.
    • MCLKSTATUS로 클럭 안정화 확인.
  • 정상 모드 복귀 문제:
    • ISR에서 플래그 클리어 확인.
    • SEN 비트 비활성화 여부 점검.
  • 전력 소모 과다:
    • RUNSTDBY 비활성화: RTC, WDT 외 주변 장치 끄기.
    • HTLLEN=1로 Power-Down에서 누설 전류 감소.
  • TWI/CCL 문제:
    • HTLLEN=1 시 TWI/CCL 비활성화 확인.
  • 디버깅 로그:
    • sleep_get_status()로 상태 출력.
    • Microchip Studio로 SLPCTRL 레지스터 확인.

8. 예제 프로젝트 워크플로우

  • 저전력 IoT 디바이스:
    1. sleep_init_powerdown(true) 호출.
    2. RTC로 1초 주기 웨이크업 설정.
    3. 웨이크업 후 LED 토글 및 UART 출력.
    4. 예시: sleep_low_power_example.c.
  • 실시간 제어:
    1. sleep_init_idle() 호출.
    2. PORT 인터럽트로 즉시 웨이크업.
    3. 정상 모드에서 상태 처리.
    4. 예시: sleep_idle_example.c.
  • 고온 애플리케이션:
    1. sleep_init_powerdown(true) 호출, HTLLEN 활성화.
    2. RTC 웨이크업 및 정상 모드 복귀 테스트.
    3. 예시: sleep_high_temp_example.c.

9. 추가 리소스

  • 데이터시트: AVR128DA64/48/32/28 데이터시트(DS40002183E).
  • Microchip Studio: AVR-GCC 및 SimAVR 가이드.
  • 하드웨어 툴: AVR-ISP mkII, Atmel-ICE, Curiosity Nano AVR128DA48.
  • 커뮤니티: AVR Freaks, Microchip 기술 지원 포털.

코드 구현

sleep_driver.h

/**
 * @file sleep_driver.h
 * @brief AVR128DA64/48/32/28 슬립 컨트롤러 드라이버 헤더 파일
 * @details 모든 AVR128DA 시리즈 호환. Idle, Standby, Power-Down 설정,
 *          전압 레귤레이터 제어, 인터럽트 웨이크업, 정상 모드 복귀, CCP 보호, 상태 조회.
 * @author linuxgo
 * @date 2025-09-05
 */
#ifndef SLEEP_DRIVER_H
#define SLEEP_DRIVER_H

#include <avr/io.h>
#include <avr/cpufunc.h>

/**
 * @brief 슬립 모드 정의
 * @details SLPCTRL.CTRLA의 SMODE 값
 */
#define SLEEP_MODE_IDLE    0x0  // Idle 모드
#define SLEEP_MODE_STANDBY 0x1  // Standby 모드
#define SLEEP_MODE_PDOWN   0x2  // Power-Down 모드

/**
 * @brief 전원 모드 정의
 * @details SLPCTRL.VREGCTRL의 PMODE 값
 */
#define SLEEP_PMODE_AUTO 0x0  // Normal (AUTO) 모드
#define SLEEP_PMODE_FULL 0x1  // Performance (FULL) 모드

/**
 * @brief 슬립 상태 구조체
 * @details 현재 슬립 모드, 전원 모드, HTLLEN 상태 저장
 */
typedef struct {
    uint8_t mode;     // 슬립 모드 (SLEEP_MODE_*)
    uint8_t pmode;    // 전원 모드 (SLEEP_PMODE_*)
    uint8_t htllen;   // 고온 저누설 (0=비활성, 1=활성)
    uint8_t active;   // Active 모드 여부 (0=슬립, 1=Active)
} SleepStatus;

/**
 * @brief 슬립 초기화: Idle 모드
 * @details 빠른 웨이크업, 모든 주변 장치 동작
 */
void sleep_init_idle(void);

/**
 * @brief 슬립 초기화: Standby 모드
 * @details 고주파 클럭 정지, RUNSTDBY로 선택적 동작
 */
void sleep_init_standby(void);

/**
 * @brief 슬립 초기화: Power-Down 모드
 * @param htllen 고온 저누설 활성화 여부
 * @details 초저전력, 제한된 웨이크업 소스
 */
void sleep_init_powerdown(bool htllen);

/**
 * @brief 슬립 초기화: 커스텀 설정
 * @param mode 슬립 모드 (SLEEP_MODE_*)
 * @param pmode 전원 모드 (SLEEP_PMODE_*)
 * @details 사용자 정의 슬립 및 전원 모드 설정
 */
void sleep_init_custom(uint8_t mode, uint8_t pmode);

/**
 * @brief 슬립 모드 진입
 * @details SLEEP 명령 실행
 */
void sleep_enter(void);

/**
 * @brief 정상 모드로 복귀
 * @details SEN 비트 비활성화 및 상태 복원
 */
void sleep_resume_active(void);

/**
 * @brief 슬립 상태 조회
 * @param status 슬립 상태 구조체 포인터
 * @details 현재 슬립 모드, 전원 모드, HTLLEN, Active 상태 반환
 */
void sleep_get_status(SleepStatus *status);

#endif // SLEEP_DRIVER_H

sleep_driver.c

/**
 * @file sleep_driver.c
 * @brief AVR128DA64/48/32/28 슬립 컨트롤러 드라이버 구현
 * @details SLPCTRL 레지스터로 슬립 모드, 전압 레귤레이터 설정.
 *          인터럽트 웨이크업, 정상 모드 복귀, CCP 보호, 상태 조회.
 * @author linuxgo
 * @date 2025-09-05
 */
#include "sleep_driver.h"

/**
 * @brief 슬립 초기화: Idle 모드
 * @details 빠른 웨이크업, 모든 주변 장치 동작
 */
void sleep_init_idle(void) {
    // 전압 레귤레이터: AUTO 모드
    _PROTECTED_WRITE(SLPCTRL.VREGCTRL, SLEEP_PMODE_AUTO);
    // 슬립 모드: IDLE, SEN=1
    SLPCTRL.CTRLA = (SLEEP_MODE_IDLE << SLPCTRL_SMODE_gp) | SLPCTRL_SEN_bm;
}

/**
 * @brief 슬립 초기화: Standby 모드
 * @details 고주파 클럭 정지, RUNSTDBY로 선택적 동작
 */
void sleep_init_standby(void) {
    // 전압 레귤레이터: AUTO 모드, HTLLEN=0 (Standby에서는 사용 불가)
    _PROTECTED_WRITE(SLPCTRL.VREGCTRL, SLEEP_PMODE_AUTO);
    // 슬립 모드: STANDBY, SEN=1
    SLPCTRL.CTRLA = (SLEEP_MODE_STANDBY << SLPCTRL_SMODE_gp) | SLPCTRL_SEN_bm;
}

/**
 * @brief 슬립 초기화: Power-Down 모드
 * @param htllen 고온 저누설 활성화 여부
 * @details 초저전력, 제한된 웨이크업 소스
 */
void sleep_init_powerdown(bool htllen) {
    // TWI, CCL 비활성화 (HTLLEN=1 요구사항)
    if (htllen) {
        TWI0.CTRLA = 0;
        CCL.CTRLA = 0;
    }
    // 전압 레귤레이터: AUTO 모드, HTLLEN 설정
    _PROTECTED_WRITE(SLPCTRL.VREGCTRL, (htllen ? SLPCTRL_HTLLEN_bm : 0) | SLEEP_PMODE_AUTO);
    // 슬립 모드: PDOWN, SEN=1
    SLPCTRL.CTRLA = (SLEEP_MODE_PDOWN << SLPCTRL_SMODE_gp) | SLPCTRL_SEN_bm;
}

/**
 * @brief 슬립 초기화: 커스텀 설정
 * @param mode 슬립 모드 (SLEEP_MODE_*)
 * @param pmode 전원 모드 (SLEEP_PMODE_*)
 * @details 사용자 정의 슬립 및 전원 모드 설정
 */
void sleep_init_custom(uint8_t mode, uint8_t pmode) {
    // HTLLEN은 Power-Down에서만 사용 가능
    uint8_t htllen = (mode == SLEEP_MODE_PDOWN && pmode == SLEEP_PMODE_AUTO) ? SLPCTRL_HTLLEN_bm : 0;
    if (htllen) {
        TWI0.CTRLA = 0;
        CCL.CTRLA = 0;
    }
    // 전압 레귤레이터 설정
    _PROTECTED_WRITE(SLPCTRL.VREGCTRL, htllen | pmode);
    // 슬립 모드 설정
    SLPCTRL.CTRLA = (mode << SLPCTRL_SMODE_gp) | SLPCTRL_SEN_bm;
}

/**
 * @brief 슬립 모드 진입
 * @details SLEEP 명령 실행
 */
void sleep_enter(void) {
    asm("SLEEP");
}

/**
 * @brief 정상 모드로 복귀
 * @details SEN 비트 비활성화 및 상태 복원
 */
void sleep_resume_active(void) {
    // 슬립 비활성화
    SLPCTRL.CTRLA &= ~SLPCTRL_SEN_bm;
}

/**
 * @brief 슬립 상태 조회
 * @param status 슬립 상태 구조체 포인터
 * @details 현재 슬립 모드, 전원 모드, HTLLEN, Active 상태 반환
 */
void sleep_get_status(SleepStatus *status) {
    status->mode = (SLPCTRL.CTRLA & SLPCTRL_SMODE_gm) >> SLPCTRL_SMODE_gp;
    status->pmode = SLPCTRL.VREGCTRL & SLPCTRL_PMODE_gm;
    status->htllen = (SLPCTRL.VREGCTRL & SLPCTRL_HTLLEN_bm) ? 1 : 0;
    status->active = (SLPCTRL.CTRLA & SLPCTRL_SEN_bm) ? 0 : 1; // SEN=0이면 Active
}

main.c

/**
 * @file main.c
 * @brief AVR128DA64/48/32/28 슬립 컨트롤러 드라이버 테스트 프로그램
 * @details Idle, Standby, Power-Down 모드 테스트, 웨이크업 및 정상 모드 복귀, UART로 상태 출력
 * @author linuxgo
 * @date 2025-09-05
 */
#ifndef F_CPU
#define F_CPU 8000000UL // 기본 클럭 주파수 8 MHz
#endif

#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>
#include <string.h>
#include <avr/interrupt.h>
#include "sleep_driver.h"
#include "uart_driver.h"

volatile uint8_t port_triggered = 0;
volatile uint32_t rtc_ticks = 0;

/**
 * @brief PORTC 인터럽트 핸들러
 */
ISR(PORTC_PORT_vect) {
    PORTC.INTFLAGS = PORT_INT0_bm;
    port_triggered = 1;
}

/**
 * @brief RTC 오버플로우 인터럽트 핸들러
 */
ISR(RTC_CNT_vect) {
    RTC.INTFLAGS = RTC_OVF_bm;
    rtc_ticks++;
}

int main(void) {
    // UART 초기화: printf용, USART0, 9600bps, 8-N-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);

    // PORT 인터럽트 설정: PC0, 양방향 에지
    PORTC.PIN0CTRL = PORT_ISC_BOTHEDGES_gc;
    PORTC.INTFLAGS = PORT_INT0_bm;

    // RTC 설정: XOSC32K, 1초 주기 오버플로우
    _PROTECTED_WRITE(CLKCTRL.XOSC32KCTRLA, CLKCTRL_CSUT_16K_gc | CLKCTRL_ENABLE_bm);
    while (!(CLKCTRL.MCLKSTATUS & CLKCTRL_XOSC32KS_bm));
    RTC.CLKSEL = RTC_CLKSEL_XOSC32K_gc;
    RTC.PER = 32767;
    RTC.INTCTRL = RTC_OVF_bm;
    RTC.CTRLA = RTC_PRESCALER_DIV1_gc | RTC_RTCEN_bm;

    // LED 출력 설정: PA7
    PORTA.DIRSET = PIN7_bm;

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

    // 테스트 시작 메시지
    printf("Sleep Driver Test Start\r\n");
    _delay_ms(1000);

    // 슬립 상태 구조체
    SleepStatus status;

    // 테스트 1: Idle 모드
    sleep_init_idle();
    sleep_get_status(&status);
    printf("Test 1: Idle Mode, Mode=%d, PMODE=%d, HTLLEN=%d, Active=%d\r\n",
           status.mode, status.pmode, status.htllen, status.active);
    sleep_enter();
    if (port_triggered) {
        printf("Woken up by PORT interrupt\r\n");
        port_triggered = 0;
        sleep_resume_active(); // 정상 모드 복귀
        PORTA.OUTTGL = PIN7_bm; // LED 토글
    }
    sleep_get_status(&status);
    printf("After Wake-up: Active=%d\r\n", status.active);
    _delay_ms(1000);

    // 테스트 2: Standby 모드
    sleep_init_standby();
    sleep_get_status(&status);
    printf("Test 2: Standby Mode, Mode=%d, PMODE=%d, HTLLEN=%d, Active=%d\r\n",
           status.mode, status.pmode, status.htllen, status.active);
    sleep_enter();
    if (port_triggered) {
        printf("Woken up by PORT interrupt\r\n");
        port_triggered = 0;
        sleep_resume_active();
        PORTA.OUTTGL = PIN7_bm;
    }
    sleep_get_status(&status);
    printf("After Wake-up: Active=%d\r\n", status.active);
    _delay_ms(1000);

    // 테스트 3: Power-Down 모드, HTLLEN 활성화
    sleep_init_powerdown(true);
    sleep_get_status(&status);
    printf("Test 3: Power-Down Mode, Mode=%d, PMODE=%d, HTLLEN=%d, Active=%d\r\n",
           status.mode, status.pmode, status.htllen, status.active);
    sleep_enter();
    if (rtc_ticks > 0) {
        printf("Woken up by RTC interrupt, Ticks=%ld\r\n", rtc_ticks);
        sleep_resume_active();
        PORTA.OUTTGL = PIN7_bm;
    }
    sleep_get_status(&status);
    printf("After Wake-up: Active=%d\r\n", status.active);
    _delay_ms(1000);

    // 테스트 4: 커스텀 설정 (Standby, FULL)
    sleep_init_custom(SLEEP_MODE_STANDBY, SLEEP_PMODE_FULL);
    sleep_get_status(&status);
    printf("Test 4: Custom Standby, FULL Mode, Mode=%d, PMODE=%d, HTLLEN=%d, Active=%d\r\n",
           status.mode, status.pmode, status.htllen, status.active);
    sleep_enter();
    if (port_triggered) {
        printf("Woken up by PORT interrupt\r\n");
        port_triggered = 0;
        sleep_resume_active();
        PORTA.OUTTGL = PIN7_bm;
    }
    sleep_get_status(&status);
    printf("After Wake-up: Active=%d\r\n", status.active);
    _delay_ms(1000);

    // Power-Down으로 복원
    sleep_init_powerdown(true);
    printf("Restored to Power-Down Mode\r\n");

    while (1) {
        sleep_enter();
        if (rtc_ticks > 0) {
            printf("Woken up by RTC, Ticks=%ld\r\n", rtc_ticks);
            sleep_resume_active();
            PORTA.OUTTGL = PIN7_bm;
            sleep_init_powerdown(true); // 다음 슬립 준비
        }
    }
    return 0;
}

sleep_low_power_example.c

/**
 * @file sleep_low_power_example.c
 * @brief AVR128DA 슬립 컨트롤러 저전력 예제
 * @details Power-Down 모드, RTC 웨이크업, 정상 모드 복귀, UART 출력
 * @author linuxgo
 * @date 2025-09-05
 */
#ifndef F_CPU
#define F_CPU 32768UL
#endif

#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>
#include <avr/interrupt.h>
#include "sleep_driver.h"
#include "uart_driver.h"

volatile uint32_t rtc_ticks = 0;

/**
 * @brief RTC 오버플로우 인터럽트 핸들러
 */
ISR(RTC_CNT_vect) {
    RTC.INTFLAGS = RTC_OVF_bm;
    rtc_ticks++;
}

int main(void) {
    // 32.768 kHz XOSC32K 설정
    _PROTECTED_WRITE(CLKCTRL.XOSC32KCTRLA, CLKCTRL_CSUT_16K_gc | CLKCTRL_ENABLE_bm);
    while (!(CLKCTRL.MCLKSTATUS & CLKCTRL_XOSC32KS_bm));

    // RTC 설정: 1초 주기 오버플로우
    RTC.CLKSEL = RTC_CLKSEL_XOSC32K_gc;
    RTC.PER = 32767;
    RTC.INTCTRL = RTC_OVF_bm;
    RTC.CTRLA = RTC_PRESCALER_DIV1_gc | RTC_RTCEN_bm;

    // UART 초기화
    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);

    // LED 출력 설정: PA7
    PORTA.DIRSET = PIN7_bm;

    // Power-Down 모드 설정: HTLLEN 활성화
    sleep_init_powerdown(true);

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

    // 슬립 상태 확인
    SleepStatus status;
    sleep_get_status(&status);
    printf("Low Power Test: Mode=%d, PMODE=%d, HTLLEN=%d, Active=%d\r\n",
           status.mode, status.pmode, status.htllen, status.active);

    while (1) {
        sleep_enter();
        if (rtc_ticks > 0) {
            printf("Woken up by RTC, Ticks=%ld\r\n", rtc_ticks);
            sleep_resume_active();
            PORTA.OUTTGL = PIN7_bm; // 정상 모드 복귀 시 LED 토글
            sleep_init_powerdown(true); // 다음 슬립 준비
            sleep_get_status(&status);
            printf("After Wake-up: Active=%d\r\n", status.active);
        }
        _delay_ms(1000);
    }
    return 0;
}

sleep_idle_example.c

/**
 * @file sleep_idle_example.c
 * @brief AVR128DA 슬립 컨트롤러 Idle 모드 예제
 * @details Idle 모드, PORT 인터럽트 웨이크업, 정상 모드 복귀, UART 출력
 * @author linuxgo
 * @date 2025-09-05
 */
#ifndef F_CPU
#define F_CPU 8000000UL
#endif

#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>
#include <avr/interrupt.h>
#include "sleep_driver.h"
#include "uart_driver.h"

volatile uint8_t port_triggered = 0;

/**
 * @brief PORTC 인터럽트 핸들러
 */
ISR(PORTC_PORT_vect) {
    PORTC.INTFLAGS = PORT_INT0_bm;
    port_triggered = 1;
}

int main(void) {
    // PORT 인터럽트 설정: PC0, 양방향 에지
    PORTC.PIN0CTRL = PORT_ISC_BOTHEDGES_gc;
    PORTC.INTFLAGS = PORT_INT0_bm;

    // UART 초기화
    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);

    // LED 출력 설정: PA7
    PORTA.DIRSET = PIN7_bm;

    // Idle 모드 설정
    sleep_init_idle();

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

    // 슬립 상태 확인
    SleepStatus status;
    sleep_get_status(&status);
    printf("Idle Mode Test: Mode=%d, PMODE=%d, HTLLEN=%d, Active=%d\r\n",
           status.mode, status.pmode, status.htllen, status.active);

    while (1) {
        sleep_enter();
        if (port_triggered) {
            printf("Woken up by PORT interrupt\r\n");
            port_triggered = 0;
            sleep_resume_active();
            PORTA.OUTTGL = PIN7_bm; // 정상 모드 복귀 시 LED 토글
            sleep_get_status(&status);
            printf("After Wake-up: Active=%d\r\n", status.active);
            sleep_init_idle(); // 다음 슬립 준비
        }
        _delay_ms(1000);
    }
    return 0;
}

sleep_high_temp_example.c

/**
 * @file sleep_high_temp_example.c
 * @brief AVR128DA 슬립 컨트롤러 고온 예제
 * @details Power-Down 모드, HTLLEN 활성화, RTC 웨이크업, 정상 모드 복귀
 * @author linuxgo
 * @date 2025-09-05
 */
#ifndef F_CPU
#define F_CPU 32768UL
#endif

#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>
#include <avr/interrupt.h>
#include "sleep_driver.h"
#include "uart_driver.h"

volatile uint32_t rtc_ticks = 0;

/**
 * @brief RTC 오버플로우 인터럽트 핸들러
 */
ISR(RTC_CNT_vect) {
    RTC.INTFLAGS = RTC_OVF_bm;
    rtc_ticks++;
}

int main(void) {
    // 32.768 kHz XOSC32K 설정
    _PROTECTED_WRITE(CLKCTRL.XOSC32KCTRLA, CLKCTRL_CSUT_16K_gc | CLKCTRL_ENABLE_bm);
    while (!(CLKCTRL.MCLKSTATUS & CLKCTRL_XOSC32KS_bm));

    // RTC 설정: 1초 주기 오버플로우
    RTC.CLKSEL = RTC_CLKSEL_XOSC32K_gc;
    RTC.PER = 32767;
    RTC.INTCTRL = RTC_OVF_bm;
    RTC.CTRLA = RTC_PRESCALER_DIV1_gc | RTC_RTCEN_bm;

    // UART 초기화
    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);

    // LED 출력 설정: PA7
    PORTA.DIRSET = PIN7_bm;

    // Power-Down 모드 설정: HTLLEN 활성화
    sleep_init_powerdown(true);

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

    // 슬립 상태 확인
    SleepStatus status;
    sleep_get_status(&status);
    printf("High Temp Test: Mode=%d, PMODE=%d, HTLLEN=%d, Active=%d\r\n",
           status.mode, status.pmode, status.htllen, status.active);

    while (1) {
        sleep_enter();
        if (rtc_ticks > 0) {
            printf("Woken up by RTC, Ticks=%ld\r\n", rtc_ticks);
            sleep_resume_active();
            PORTA.OUTTGL = PIN7_bm; // 정상 모드 복귀 시 LED 토글
            sleep_get_status(&status);
            printf("After Wake-up: Active=%d\r\n", status.active);
            sleep_init_powerdown(true); // 다음 슬립 준비
        }
        _delay_ms(1000);
    }
    return 0;
}

추가 팁

  • Power-Down 최적화:
    • HTLLEN=1로 70°C 이상에서 누설 전류 감소.
    • TWI/CCL 비활성화 필수.
  • 웨이크업 시간:
    • Idle: 6 클럭 사이클로 즉시 응답.
    • Standby/Power-Down: CLKCTRL 설정에 따라 추가 지연.
  • 인터럽트 관리:
    • 슬립 진입 전 전역 인터럽트(sei()) 활성화.
    • PORT 핀은 비동기 인터럽트 지원.
  • 정상 모드 복귀:
    • sleep_resume_active()로 SEN 비트 비활성화.
    • 웨이크업 후 주변 장치 재초기화 가능 (예: UART).
  • CCP 준수:
    • _PROTECTED_WRITE로 VREGCTRL 수정, 4 CPU 사이클 내 완료.
  • 전력 절감:
    • Power-Down에서 OSC32K/XOSC32K로 ~1 µA 소모.
    • RUNSTDBY=0으로 불필요한 주변 장치 비활성화.
  • 테스트 환경:
    • UART 출력(PuTTY, 9600bps)으로 상태 확인.
    • LED 토글 또는 오실로스코프로 웨이크업 및 정상 모드 확인.
  • 애플리케이션 시나리오:
    • 고성능: Idle 모드로 빠른 응답(예: 제어 시스템).
    • 저전력: Power-Down 모드, RTC 웨이크업(예: IoT 센서).
    • 고온: HTLLEN=1로 누설 전류 감소(예: 산업 장비).

결론

본 슬립 컨트롤러 드라이버는 AVR128DA64/48/32/28 시리즈의 SLPCTRL 기능을 완벽히 활용하도록 설계되었습니다. Idle, Standby, Power-Down 모드를 지원하며, 전압 레귤레이터 제어, 인터럽트 웨이크업, 정상 모드 복귀, CCP 보호, 상태 조회 기능을 제공합니다. _PROTECTED_WRITE로 안정성을 보장하며, 저전력 IoT, 실시간 제어, 고온 애플리케이션 등 다양한 시나리오에 유연하게 적용 가능합니다. 제공된 예제 코드는 Idle, Power-Down, 고온 테스트를 포함하며, 웨이크업 및 정상 모드 복귀를 명확히 구현하여 Microchip Studio에서 검증 가능합니다.

반응형