Микроконтроллеры STM32: использование встроенных RTC

11 июля 2018

Ранее в посте Микроконтроллеры 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. Любые вопросы и дополнения, как обычно, всячески приветствуются!

Метки: , .

Понравился пост? Узнайте, как можно поддержать развитие этого блога.

Также подпишитесь на RSS, Facebook, ВКонтакте, Twitter или Telegram.