Ранее в посте Микроконтроллеры AVR: пример работы с часами реального времени DS1302 отмечалось, что DS1302 было бы довольно глупо использовать с микроконтроллерами STM32, так как у них есть встроенные часы реального времени. Давайте же попробуем разобраться, как происходит работа с этими встроенными RTC, и что они умеют.

Как обычно, для экспериментов я использовал плату Nucleo-F411RE , но описанные далее шаги можно повторить и для любой другой отладочной платы. Обратите внимание, что в мире STM32 встречаются микроконтроллеры с RTC, но без функции календаря . Их RTC умеет работать со временем, но не с датами. В качестве примера можно привести используемый в Blue Pill микроконтроллер STM32F103C8T6. Чтобы определить, есть ли в микроконтроллере хардварный календарь, внимательно читайте даташит.

Итак, для включения RTC следует открыть STM32CubeMX и произвести следующие изменения в проекте:

  • Во вкладке Pinout перейдите в Configuration → Peripherals → RCC. В выпадающем списке Low Speed Clock (LSE) выберите «Crystal/Ceramic Resonator». Этим мы говорим микроконтроллеру использовать внешний часовой кварц на 32768 Гц. Разумеется, предполагается, что он есть на вашей плате. Можно обойтись и внутренним генератором (LSI). Однако точность часов в этом случае будет оставлять желать лучшего.
  • В той же вкладке Pinout перейдите в Configuration → Peripherals → RTC, поставьте галочки «Activate Clock Source» и «Activate Calendar». Этим мы, собственно, включаем RTC.
  • Во вкладке Configuration появится кнопка RTC. Нажмите на нее и проверьте, что используется 24-х часовое время (Hour Format) и бинарный формат (Data Format).

Важно! При проектировании собственной платы нужно серьезно отнестись к выбору конденсаторов рядом с часовым кварцем. Если переборщить с емкостью этих конденсаторов, часы будут идти слишком медленно. На практике неплохо работают конденсаторы на 10 пФ.

Наконец, во вкладке Clock Configuration будет не лишним перепроверить, что LSE действительно используется:

Настройка LSE в STM32CubeMX

В Makefile понадобится добавить пару файлов к списку C_SOURCES:

$(FIRMWARE)/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rtc.c
$(FIRMWARE)/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rtc_ex.c

Теперь можно спокойно использовать RTC в коде. Например, получение даты и времени осуществляется так:

RTC_TimeTypeDef time ;
RTC_DateTypeDef date ;
HAL_StatusTypeDef res ;

res = HAL_RTC_GetTime ( & hrtc , & time , RTC_FORMAT_BIN ) ;
if ( res != HAL_OK ) {
UART_Printf ( «HAL_RTC_GetTime failed: %d r n » , res ) ;
return ;
}

res = HAL_RTC_GetDate ( & hrtc , & date , RTC_FORMAT_BIN ) ;
if ( res != HAL_OK ) {
UART_Printf ( «HAL_RTC_GetDate failed: %d r n » , res ) ;
return ;
}

А так происходит их изменение:

int RTC_Set (
uint8_t year , uint8_t month , uint8_t day ,
uint8_t hour , uint8_t min , uint8_t sec ,
uint8_t dow ) {
HAL_StatusTypeDef res ;
RTC_TimeTypeDef time ;
RTC_DateTypeDef date ;

memset ( & time , 0 , sizeof ( time ) ) ;
memset ( & date , 0 , sizeof ( date ) ) ;

date. WeekDay = dow ;
date. Year = year ;
date. Month = month ;
date. Date = day ;

res = HAL_RTC_SetDate ( & hrtc , & date , RTC_FORMAT_BIN ) ;
if ( res != HAL_OK ) {
UART_Printf ( «HAL_RTC_SetDate failed: %d r n » , res ) ;
return 1 ;
}

time . Hours = hour ;
time . Minutes = min ;
time . Seconds = sec ;

res = HAL_RTC_SetTime ( & hrtc , & time , RTC_FORMAT_BIN ) ;
if ( res != HAL_OK ) {
UART_Printf ( «HAL_RTC_SetTime failed: %d r n » , res ) ;
return 2 ;
}

return 0 ;
}

Заметьте, что структура RTC_TimeTypeDef помимо самого времени хранит ряд дополнительных полей. Они могут не очень аккуратно заполняться процедурой HAL_RTC_GetTime . Поэтому при изменении времени структуру лучше заполнить самостоятельно. Иначе можно увидеть, как часы показывают 27 часов, и налететь на прочие потрясающие баги.

Следует также иметь в виду, что в RTC не предусмотрено защиты от дурака, что позволяет выставить дату вроде 31 февраля. Если пользователь вашего устройства может изменять дату, вы должны предусмотреть в коде валидацию вводимых данных, знающую про високосные года и всякое такое. Эта тема уже поднималась в данном блоге, поэтому снова на ней я останавливаться не буду. Пример валидации вы найдете в полной версии исходников к данному посту (ищите в конце текста).

Для питания RTC нужно подать от 1.6 В до 3.6 В на пины микроконтроллера VBAT и GND. Я использовал батарейку CR2032:

Пример использования встроенных RTC микроконтроллеров STM32

Для вывода времени, даты, дня недели и выбранных цветов интерфейса я использовал TFT-экранчик на базе ST7735 . Для изменения даты, времени, и всего остального используются четыре кнопки. Действия кнопок сверху вниз: переход назад, перех вперед, уменьшить значение, увеличить значение.

Интересно, что при отсутствии основного питания батарейка питает не только часы, но и небольшое количество SRAM, так называемую backup memory (она же backup registers). Для доступа к ней при инициализации устройства нужно сказать:

void init ( ) {
/* … (пропущено) … */

HAL_PWR_EnableBkUpAccess ( ) ;
}

После чего можно осуществлять чтение и запись памяти. Я решил хранить в ней выбранный пользователем цвет интерфейса:

uint8_t chosen_color = ( uint8_t ) HAL_RTCEx_BKUPRead ( & hrtc , RTC_BKP_DR1 ) ;

// … (пропущено) …

if ( /* … */ ) { // color changed
HAL_RTCEx_BKUPWrite ( & hrtc , RTC_BKP_DR1 , ( uint32_t ) chosen_color ) ;
}

Как видите, backup memory поделена на 32-х битные регистры. Количество регистров зависит от микроконтроллера, их точное число можно узнать из даташита. Например, использованный мной STM32F411RE имеет 20 регистров. То есть, суммарно в backup memory он может хранить до 80 байт данных. Учитывая, что большинство микроконтроллеров STM32 не имеют встроенного EEPROM , наличие у них backup memory оказывается весьма кстати.

В целом, я не нашел серьезных дефектов во встроенных RTC. Время они показывают весьма точно, дату и день недели обновляют правильно, отключение основного питания переживают. RTC имеют и другие возможности, которые не были рассмотрены выше. Например, есть настройки для дополнительной калибровки часов. А еще RTC могут генерировать прерывания , выводящие микроконтроллер из энергосберегающего режима по расписанию. Однако эти моменты уже выходят за рамки данного поста. В качестве источника дополнительной информации можно порекомендовать книгу Mastering STM32 .

Полную версию исходников к этому посту вы найдете на GitHub .

EnglishRussianUkrainian