Старт ARM. Часы реального времени RTC. HAL

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

  1. Здравствуйте! Недавно для себя начал изучать STM32F429I-DISCO и соответственно программирование , вот хочу подключить через HAL часы реального времени DS3231 (интерфейс I2C).
    Подскажите как можно это все подключить.
    Хочу сделать светильник с будильником!
    Среда разработки EmBitz 0.42
    Заранее благодарю за ответ!

  2. Самый простой способ, взять исходники здесь https://github.com/rodan/ds3231, и исправить хардварную работу там с I2C, проверены способ. Получается достаточно красиво.

  3. как проверить шли часы до нашей инициализации от батарейки или нет? чтобы не выставлять их при каждом включении прибора

  4. Нужно запись в BKUP область любое число HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR0, < ваше значение>), и при старте прочитать HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR0), если значения совпали? значит часы продолжают отсчитывать время.

    1. какие-то костыли. а нельзя проверить установку каких нибуть битов готовности например

      1. К сожалению нету, только так.

        /*##-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);

        1. думаю лучше все таки проверить дату если год равен 0 то часы нужно выставлять)

Comments are closed.