Доброе время суток, продолжаем изучать микроконтроллеры ARM stm32f4, сегодня речь пойдет о часах реального времени RTC, а поставим мы себе задачу по времени переключать светодиод и сделаем себе будильник по которому нехотя будем вставать утром. И как всегда мы это будет делать на HAL-е, и тут нас ждет 2 бага со стороны хала. Начнем по порядку:
1. RTC имеет Alarm A и Alarm B (EXTI line 17)
2. RTC может проснуться по линии PA0(EXTI line 20)
3. RTC может сработать по метке (EXTI line 21)
Тактирование для RTC можно задать с помощью:
1. LSE – внешний часовой кварцевый генератор 32.738 kHz
2. LSI – внутренний генератор на 32 kHz
3. HSE – внешний кварцевой генератор для работы микроконтроллера, вход для RTC это частота HSE/Пред делитель.
И переходим непосредственно к самой программе.
stm32f4xx_hal_msp.c
void HAL_RTC_MspInit(RTC_HandleTypeDef* hrtc) { if(hrtc->Instance==RTC) { HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_0); HAL_NVIC_SetPriority(RTC_Alarm_IRQn, 0, 0); HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn); } } void HAL_RTC_MspDeInit(RTC_HandleTypeDef* hrtc) { if(hrtc->Instance==RTC) { HAL_NVIC_DisableIRQ(RTC_Alarm_IRQn); } }
main.c
RTC_HandleTypeDef hrtc; void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_RTC_Init(void); int main(void) { HAL_Init(); SystemClock_Config(); HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_0); HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0); MX_GPIO_Init(); MX_RTC_Init(); if(!(*(volatile uint32_t *) (BDCR_RTCEN_BB)))__HAL_RCC_RTC_ENABLE(); while (1) { } } void SystemClock_Config(void) { RCC_ClkInitTypeDef RCC_ClkInitStruct; RCC_PeriphCLKInitTypeDef PeriphClkInitStruct; RCC_OscInitTypeDef RCC_OscInitStruct; __PWR_CLK_ENABLE(); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI|RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.LSIState = RCC_LSI_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLM = 8; RCC_OscInitStruct.PLL.PLLN = 336; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; RCC_OscInitStruct.PLL.PLLQ = 7; HAL_RCC_OscConfig(&RCC_OscInitStruct); RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1 |RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5); PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC; PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSI; HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct); } void MX_RTC_Init(void) { RTC_TimeTypeDef sTime; RTC_DateTypeDef sDate; RTC_AlarmTypeDef sAlarm; hrtc.Instance = RTC; hrtc.Init.HourFormat = RTC_HOURFORMAT_24; hrtc.Init.AsynchPrediv = 127; hrtc.Init.SynchPrediv = 255; hrtc.Init.OutPut = RTC_OUTPUT_DISABLE; hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH; hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN; HAL_RTC_Init(&hrtc); sTime.Hours = 0; sTime.Minutes = 0; sTime.Seconds = 0; sTime.TimeFormat = RTC_HOURFORMAT12_AM; sTime.DayLightSaving = RTC_DAYLIGHTSAVING_SUB1H; sTime.StoreOperation = RTC_STOREOPERATION_RESET; HAL_RTC_SetTime(&hrtc, &sTime, FORMAT_BIN); sDate.WeekDay = RTC_WEEKDAY_MONDAY; sDate.Month = RTC_MONTH_JANUARY; sDate.Date = 1; sDate.Year = 0; HAL_RTC_SetDate(&hrtc, &sDate, FORMAT_BIN); sAlarm.AlarmTime.Hours = sTime.Hours; sAlarm.AlarmTime.Minutes = sTime.Minutes; sAlarm.AlarmTime.Seconds = sTime.Seconds + 5; sAlarm.AlarmTime.TimeFormat = RTC_HOURFORMAT12_AM; sAlarm.AlarmTime.DayLightSaving = RTC_DAYLIGHTSAVING_SUB1H; sAlarm.AlarmTime.StoreOperation = RTC_STOREOPERATION_RESET; sAlarm.AlarmMask = RTC_ALARMMASK_NONE; sAlarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_ALL; sAlarm.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_DATE; sAlarm.AlarmDateWeekDay = 1; sAlarm.Alarm = RTC_ALARM_A; HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, FORMAT_BIN); } void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; __GPIOC_CLK_ENABLE(); __GPIOH_CLK_ENABLE(); __GPIOA_CLK_ENABLE(); __GPIOG_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_0; GPIO_InitStruct.Mode = GPIO_MODE_EVT_RISING; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_13|GPIO_PIN_14; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_LOW; HAL_GPIO_Init(GPIOG, &GPIO_InitStruct); }
1-ый баг необходимо добавить после MX_RTC_Init(), следующую строку
if(!(*(volatile uint32_t *) (BDCR_RTCEN_BB)))__HAL_RCC_RTC_ENABLE();
2-ой баг в файле smt32f4xx_hal_rtc.c необходимо закомментировать данную строку
//sTime->SubSeconds = (uint32_t)(hrtc->Instance->SSR);
Будильник делается аналогично, для этого в функции MX_RTC-Init() ставим текущее время и выставляем время через которое будильник должен зазвенеть (например через 10 секунд).
main.c
void MX_RTC_Init(void) { RTC_TimeTypeDef sTime; RTC_DateTypeDef sDate; RTC_AlarmTypeDef sAlarm; hrtc.Instance = RTC; hrtc.Init.HourFormat = RTC_HOURFORMAT_24; hrtc.Init.AsynchPrediv = 127; hrtc.Init.SynchPrediv = 255; hrtc.Init.OutPut = RTC_OUTPUT_DISABLE; hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH; hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN; HAL_RTC_Init(&hrtc); sTime.Hours = 9; sTime.Minutes = 26; sTime.Seconds = 0; sTime.TimeFormat = RTC_HOURFORMAT12_AM; sTime.DayLightSaving = RTC_DAYLIGHTSAVING_SUB1H; sTime.StoreOperation = RTC_STOREOPERATION_RESET; HAL_RTC_SetTime(&hrtc, &sTime, FORMAT_BIN); sDate.WeekDay = RTC_WEEKDAY_MONDAY; sDate.Month = RTC_MONTH_JANUARY; sDate.Date = 1; sDate.Year = 0; HAL_RTC_SetDate(&hrtc, &sDate, FORMAT_BIN); sAlarm.AlarmTime.Hours = sTime.Hours; sAlarm.AlarmTime.Minutes = sTime.Minutes; sAlarm.AlarmTime.Seconds = sTime.Seconds + 10; sAlarm.AlarmTime.TimeFormat = RTC_HOURFORMAT12_AM; sAlarm.AlarmTime.DayLightSaving = RTC_DAYLIGHTSAVING_SUB1H; sAlarm.AlarmTime.StoreOperation = RTC_STOREOPERATION_RESET; sAlarm.AlarmMask = RTC_ALARMMASK_NONE; sAlarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_ALL; sAlarm.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_DATE; sAlarm.AlarmDateWeekDay = 1; sAlarm.Alarm = RTC_ALARM_A; HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, FORMAT_BIN); }
stm32f4xx_it.c
void RTC_Alarm_IRQHandler(void) { HAL_NVIC_ClearPendingIRQ(RTC_Alarm_IRQn); if(i%2){ HAL_GPIO_WritePin(GPIOG, GPIO_PIN_13, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOG, GPIO_PIN_14, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET); i++; } else{ HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOG, GPIO_PIN_14, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOG, GPIO_PIN_13, GPIO_PIN_SET); i++; } if(i >= 10) i = 0; HAL_RTC_GetTime(&hrtc, &sTime, FORMAT_BIN); sAlarm.AlarmTime.Hours = sTime.Hours; sAlarm.AlarmTime.Minutes = sTime.Minutes; sAlarm.AlarmTime.Seconds = (sTime.Seconds + 1 ) % 60; sAlarm.AlarmTime.TimeFormat = RTC_HOURFORMAT12_AM; sAlarm.AlarmTime.DayLightSaving = RTC_DAYLIGHTSAVING_SUB1H; sAlarm.AlarmTime.StoreOperation = RTC_STOREOPERATION_RESET; sAlarm.AlarmMask = RTC_ALARMMASK_DATEWEEKDAY|RTC_ALARMMASK_HOURS|RTC_ALARMMASK_MINUTES; sAlarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_ALL; sAlarm.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_DATE; sAlarm.AlarmDateWeekDay = 0x31; sAlarm.Alarm = RTC_ALARM_A; HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, FORMAT_BIN); HAL_RTC_AlarmIRQHandler(&hrtc); }
Так же добавляем в MX_GPIO_Init() следующие строки кода
__GPIOB_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_2; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_PULLDOWN; GPIO_InitStruct.Speed = GPIO_SPEED_LOW; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
В следующий раз поговорим про энергосберегающий режим, на сегодня все, всем пока, увидимся на просторах mcu.by.
8 thoughts on “Старт ARM. Часы реального времени RTC. HAL”
Здравствуйте! Недавно для себя начал изучать STM32F429I-DISCO и соответственно программирование , вот хочу подключить через HAL часы реального времени DS3231 (интерфейс I2C).
Подскажите как можно это все подключить.
Хочу сделать светильник с будильником!
Среда разработки EmBitz 0.42
Заранее благодарю за ответ!
Самый простой способ, взять исходники здесь https://github.com/rodan/ds3231, и исправить хардварную работу там с I2C, проверены способ. Получается достаточно красиво.
как проверить шли часы до нашей инициализации от батарейки или нет? чтобы не выставлять их при каждом включении прибора
Нужно запись в BKUP область любое число HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR0, < ваше значение>), и при старте прочитать HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR0), если значения совпали? значит часы продолжают отсчитывать время.
какие-то костыли. а нельзя проверить установку каких нибуть битов готовности например
К сожалению нету, только так.
…
/*##-2- Check if Data stored in BackUp register1: No Need to reconfigure RTC#*/
/* Read the Back Up Register 1 Data */
if (HAL_RTCEx_BKUPRead(&RtcHandle, RTC_BKP_DR1) != 0x32F2)
{
/* Configure RTC Calendar */
RTC_CalendarConfig();
}
else
{
/* Check if the Power On Reset flag is set */
if (__HAL_RCC_GET_FLAG(RCC_FLAG_PORRST) != RESET)
{
/* Turn on LED2: Power on reset occured */
BSP_LED_On(LED2);
}
/* Check if Pin Reset flag is set */
if (__HAL_RCC_GET_FLAG(RCC_FLAG_PINRST) != RESET)
{
/* Turn on LED4: External reset occured */
BSP_LED_On(LED4);
}
/* Clear source Reset Flag */
__HAL_RCC_CLEAR_RESET_FLAGS();
}
…
/*##-3- Writes a data in a RTC Backup data Register1 #######################*/
HAL_RTCEx_BKUPWrite(&RtcHandle, RTC_BKP_DR1, 0x32F2);
думаю лучше все таки проверить дату если год равен 0 то часы нужно выставлять)
Cогласен, так будет лучше.
Comments are closed.