Доброе время суток, продолжаем изучать микроконтроллеры 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.