STM32F4 실시간 시계(RTC) 구현 및 활용

1. 하드웨어 구성

이 실험에서 사용하는 주요 하드웨어 자원은 다음과 같습니다:

  • LED(DS0)
  • UART(시리얼 통신)
  • TFT-LCD 디스플레이
  • 내장 RTC(Real-Time Clock)

RTC는 STM32F4 칩 내부에 통합된 모듈로, 소프트웨어를 통해 설정할 수 있습니다. 단, RTC는 백업 전원이 필요하며, 개발보드(ALIENTEK Explorer STM32F4)에 장착된 보조 배터리가 있어야 전원 차단 후에도 시간 정보를 유지할 수 있습니다.

2. 소프트웨어 구현

2.1 시간 설정 함수

// RTC 시간 설정 (24시간 기준)
HAL_StatusTypeDef configure_rtc_time(uint8_t hour, uint8_t minute, uint8_t second)
{
    RTC_TimeTypeDef time_config = {0};
    time_config.Hours = hour;
    time_config.Minutes = minute;
    time_config.Seconds = second;
    time_config.TimeFormat = RTC_HOURFORMAT_24;
    time_config.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
    time_config.StoreOperation = RTC_STOREOPERATION_RESET;
    
    return HAL_RTC_SetTime(&rtc_handle, &time_config, RTC_FORMAT_BIN);
}

2.2 날짜 설정 함수

// RTC 날짜 설정
HAL_StatusTypeDef configure_rtc_date(uint8_t year, uint8_t month, uint8_t day, uint8_t weekday)
{
    RTC_DateTypeDef date_config = {0};
    date_config.Year = year;
    date_config.Month = month;
    date_config.Date = day;
    date_config.WeekDay = weekday;
    
    return HAL_RTC_SetDate(&rtc_handle, &date_config, RTC_FORMAT_BIN);
}

2.3 RTC 초기화

// RTC 초기화 및 최초 설정 여부 확인
uint8_t init_rtc(void)
{
    rtc_handle.Instance = RTC;
    rtc_handle.Init.HourFormat = RTC_HOURFORMAT_24;
    rtc_handle.Init.AsynchPrediv = 0x7F;   // 비동기 분주비
    rtc_handle.Init.SynchPrediv = 0xFF;    // 동기 분주비
    rtc_handle.Init.OutPut = RTC_OUTPUT_DISABLE;
    rtc_handle.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
    rtc_handle.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;

    if (HAL_RTC_Init(&rtc_handle) != HAL_OK)
        return 2;

    // 백업 레지스터를 통해 최초 설정 여부 확인
    if (HAL_RTCEx_BKUPRead(&rtc_handle, RTC_BKP_DR0) != 0x5050)
    {
        configure_rtc_time(17, 41, 0);
        configure_rtc_date(24, 10, 11, RTC_WEEKDAY_FRIDAY);
        HAL_RTCEx_BKUPWrite(&rtc_handle, RTC_BKP_DR0, 0x5050);
    }
    return 0;
}

2.4 RTC 클록 소스 설정

// RTC 클록 소스(LSE) 설정
void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc)
{
    RCC_OscInitTypeDef osc_init = {0};
    RCC_PeriphCLKInitTypeDef clk_init = {0};

    __HAL_RCC_PWR_CLK_ENABLE();
    HAL_PWR_EnableBkUpAccess();

    osc_init.OscillatorType = RCC_OSCILLATORTYPE_LSE;
    osc_init.LSEState = RCC_LSE_ON;
    HAL_RCC_OscConfig(&osc_init);

    clk_init.PeriphClockSelection = RCC_PERIPHCLK_RTC;
    clk_init.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
    HAL_RCCEx_PeriphCLKConfig(&clk_init);

    __HAL_RCC_RTC_ENABLE();
}

2.5 알람 설정

// 요일 기반 알람 설정 (알람 A)
void set_weekly_alarm(uint8_t weekday, uint8_t hour, uint8_t min, uint8_t sec)
{
    RTC_AlarmTypeDef alarm_cfg = {0};
    alarm_cfg.AlarmTime.Hours = hour;
    alarm_cfg.AlarmTime.Minutes = min;
    alarm_cfg.AlarmTime.Seconds = sec;
    alarm_cfg.AlarmTime.TimeFormat = RTC_HOURFORMAT_24;
    alarm_cfg.AlarmMask = RTC_ALARMMASK_NONE;
    alarm_cfg.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_WEEKDAY;
    alarm_cfg.AlarmDateWeekDay = weekday;
    alarm_cfg.Alarm = RTC_ALARM_A;

    HAL_RTC_SetAlarm_IT(&rtc_handle, &alarm_cfg, RTC_FORMAT_BIN);

    HAL_NVIC_SetPriority(RTC_Alarm_IRQn, 1, 2);
    HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn);
}

2.6 주기적 웨이크업 타이머 설정

// 주기적 웨이크업 인터럽트 설정 (1초 간격)
void setup_wakeup_timer(uint32_t clock_source, uint16_t reload_val)
{
    __HAL_RTC_WAKEUPTIMER_CLEAR_FLAG(&rtc_handle, RTC_FLAG_WUTF);
    HAL_RTCEx_SetWakeUpTimer_IT(&rtc_handle, reload_val, clock_source);

    HAL_NVIC_SetPriority(RTC_WKUP_IRQn, 2, 2);
    HAL_NVIC_EnableIRQ(RTC_WKUP_IRQn);
}

2.7 인터럽트 처리 루틴

// 알람 인터럽트 핸들러
void RTC_Alarm_IRQHandler(void)
{
    HAL_RTC_AlarmIRQHandler(&rtc_handle);
}

// 알람 A 콜백 함수
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
    printf("ALARM A TRIGGERED!\r\n");
}

// 웨이크업 인터럽트 핸들러
void RTC_WKUP_IRQHandler(void)
{
    HAL_RTCEx_WakeUpTimerIRQHandler(&rtc_handle);
}

// 웨이크업 콜백 함수
void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc)
{
    LED1_TOGGLE(); // LED1 상태 토글
}

2.8 메인 루프

int main(void)
{
    RTC_TimeTypeDef current_time;
    RTC_DateTypeDef current_date;
    uint8_t display_buf[40];
    uint8_t tick = 0;

    HAL_Init();
    SystemClock_Config(); // 168MHz 설정
    delay_init(168);
    uart_init(115200);
    LED_Init();
    LCD_Init();
    usmart_dev.init(84);
    
    init_rtc();
    setup_wakeup_timer(RTC_WAKEUPCLOCK_CK_SPRE_16BITS, 0); // 1Hz 웨이크업

    LCD_SetTextColor(RED);
    LCD_ShowString(30, 50, "Explorer STM32F4");
    LCD_ShowString(30, 70, "RTC DEMO");
    LCD_ShowString(30, 90, "by hazy1k");
    LCD_ShowString(30, 110, "2024/10/11");

    while (1)
    {
        tick++;
        if ((tick % 10) == 0) // 100ms마다 업데이트
        {
            HAL_RTC_GetTime(&rtc_handle, ¤t_time, RTC_FORMAT_BIN);
            sprintf((char*)display_buf, "Time:%02d:%02d:%02d", 
                    current_time.Hours, current_time.Minutes, current_time.Seconds);
            LCD_ShowString(30, 140, display_buf);

            HAL_RTC_GetDate(&rtc_handle, ¤t_date, RTC_FORMAT_BIN);
            sprintf((char*)display_buf, "Date:20%02d-%02d-%02d", 
                    current_date.Year, current_date.Month, current_date.Date);
            LCD_ShowString(30, 160, display_buf);

            sprintf((char*)display_buf, "Week:%d", current_date.WeekDay);
            LCD_ShowString(30, 180, display_buf);
        }

        if ((tick % 20) == 0)
            LED0_TOGGLE();

        delay_ms(10);
    }
}

3. 핵심 요약

RTC 모듈은 LSE(Low-Speed External) 크리스탈을 클록 소스로 사용하며, 백업 도메인에서 동작합니다. 초기 설정 후 백업 레지스터(RTC_BKP_DR0)를 통해 재부팅 시 중복 설정을 방지할 수 있습니다. 알람과 웨이크업 타이머는 인터럽트 기반으로 동작하며, 저전력 모드에서도 시간 기반 이벤트를 트리거할 수 있습니다. 디스플레이와 UART를 통해 실시간 시간 정보를 확인할 수 있으며, LED를 이용해 인터럽트 발생을 시각적으로 검증할 수 있습니다.

태그: STM32F4 RTC HAL LowPower LSE

9월 28일 04:48에 게시됨