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를 이용해 인터럽트 발생을 시각적으로 검증할 수 있습니다.