В микроконтроллере STM32 есть встроенные часы реального времени которые могут работать от внутреннего низкоскоростного RC-резонатора (LSI), внешнего низкоскоростного кварцевого резонатора (LSE) или внешнего высокоскоростного резонатора с делителем (HSE/128). Внешний низкоскоростной кварцевый резонатор, с частотой 32768 Гц, использовать предпочтительнее, так как он обеспечивает большую точность хода часов и уже установлен на отладочной плате STM32.
Часы реального времени могут работать независимо от основного питания, для этого на вход vbat нужно подать питание напряжением 3 В (батарейка).
Ниже показан скетч позволяющий установить текущее время и вывести время в монитор порта. Работа часов реального времени управляется библиотекой RTClock, устанавливать библиотеку не нужно она входит в состав набора библиотек Arduino_STM32-master который устанавливаются при установке поддержки платы STM32F103C8T6 (как установить поддержку платы в Arduino IDE — http://rcl-radio.ru/?p=68376).
#include <RTClock.h> RTClock rtclock (RTCSEL_LSE, 32768); // внешний кварц 32768 Гц time_t tt, tt1; tm_t mtt; const char * delim = " :"; char s[128]; const char * weekdays[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; const char * months[] = {"Dummy", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; byte sec_time; void setup(){ Serial.begin(9600); ParseBuildTimestamp(mtt); // Unix время, отсчет от 00:00:00 1 января 1970 tt = rtclock.makeTime(mtt) + 25; // +25 сек ко времени, для коррекции времени загрузки скетча // rtclock.setTime(tt); tt1 = tt; } void loop(){ tt1 = tt; rtclock.breakTime(rtclock.now(), mtt); if(mtt.second!=sec_time){ sprintf(s, "RTC timestamp: %s %u %u, %s, %02u:%02u:%02u\n",months[mtt.month], mtt.day, mtt.year+1970, weekdays[mtt.weekday], mtt.hour, mtt.minute, mtt.second); Serial.println(s); sec_time = mtt.second; } } uint8_t str2month(const char * d){uint8_t i = 13;while ( (--i) && strcmp(months[i], d)!=0 );return i;} void ParseBuildTimestamp(tm_t & mt){ sprintf(s, "Timestamp: %s, %s\n", __DATE__, __TIME__);// Формат времени: "Dec 8 2017, 22:57:54" Serial.print(s); char * token = strtok(s, delim); // get first token // walk through tokens while( token != NULL ) { uint8_t m = str2month((const char*)token); if ( m>0 ) { mt.month = m; token = strtok(NULL, delim); // get next token mt.day = atoi(token); token = strtok(NULL, delim); // get next token mt.year = atoi(token) - 1970; token = strtok(NULL, delim); // get next token mt.hour = atoi(token); token = strtok(NULL, delim); // get next token mt.minute = atoi(token); token = strtok(NULL, delim); // get next token mt.second = atoi(token); } token = strtok(NULL, delim); } }
После прошивки микроконтроллера в мониторе порта Вы увидите примерно следующее:
Для установки текущего времени раскомментируйте строку:
rtclock.setTime(tt);
После заливки скетча, закомментируйте эту строчку и по новой залейте скетч:
// rtclock.setTime(tt);
Для примера можно собрать простые часы, дата и время будет выводится на LCD1602 с модулем I2C.
Подключение LCD1602 при использовании шины I2C к плате STM32 мало чем отличается от подключения к Arduino Nano, только вместо A4 A5 для Nano, в STM32 используются выводы B6 (SCL) B7 (SDA). При подключении платы STM32 к USB порту LCD1602 с I2C модулем питается от выхода 5V. При наличии внешнего источника питания 5 В, это напряжение необходимо подавать на VCC I2C модуля и на вход 5V платы STM32.
#include <RTClock.h> #include <Wire.h> #include <LiquidCrystal_I2C.h> // http://rcl-radio.ru/wp-content/uploads/2019/12/liquidcrystali2c-9.zip LiquidCrystal_I2C lcd(0x27,16,2); // Устанавливаем дисплей RTClock rtclock (RTCSEL_LSE, 32765); // внешний кварц 32768 Гц (коррекция погрешности кварца) time_t tt, tt1; tm_t mtt; const char * delim = " :"; char s[128]; const char * weekdays[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; const char * months[] = {"Dummy", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; byte sec_time; void setup(){ Serial.begin(9600); lcd.init(); lcd.backlight();// Включаем подсветку дисплея ParseBuildTimestamp(mtt); // Unix время, отсчет от 00:00:00 1 января 1970 tt = rtclock.makeTime(mtt) + 25; // +25 сек ко времени, для коррекции времени загрузки скетча // rtclock.setTime(tt); tt1 = tt; } void loop(){ tt1 = tt; rtclock.breakTime(rtclock.now(), mtt); if(mtt.second!=sec_time){ sprintf(s, "RTC timestamp: %s %u %u, %s, %02u:%02u:%02u\n",months[mtt.month], mtt.day, mtt.year+1970, weekdays[mtt.weekday], mtt.hour, mtt.minute, mtt.second); Serial.println(s); sec_time = mtt.second; lcd.setCursor(4, 0); lcd.print(mtt.hour/10);lcd.print(mtt.hour%10);lcd.print(":");lcd.print(mtt.minute/10);lcd.print(mtt.minute%10);lcd.print(":");lcd.print(mtt.second/10);lcd.print(mtt.second%10); lcd.setCursor(0, 1); if(mtt.day<10){lcd.print(" ");}lcd.print(mtt.day);lcd.print(" ");lcd.print(months[mtt.month]);lcd.print(" ");lcd.print(mtt.year+1970);lcd.print(" ");lcd.print(weekdays[mtt.weekday]); } } uint8_t str2month(const char * d){uint8_t i = 13;while ( (--i) && strcmp(months[i], d)!=0 );return i;} void ParseBuildTimestamp(tm_t & mt){ sprintf(s, "Timestamp: %s, %s\n", __DATE__, __TIME__);// Формат времени: "Dec 8 2017, 22:57:54" Serial.print(s); char * token = strtok(s, delim); // get first token // walk through tokens while( token != NULL ) { uint8_t m = str2month((const char*)token); if ( m>0 ) { mt.month = m; token = strtok(NULL, delim); // get next token mt.day = atoi(token); token = strtok(NULL, delim); // get next token mt.year = atoi(token) - 1970; token = strtok(NULL, delim); // get next token mt.hour = atoi(token); token = strtok(NULL, delim); // get next token mt.minute = atoi(token); token = strtok(NULL, delim); // get next token mt.second = atoi(token); } token = strtok(NULL, delim); } }
Форум — http://forum.rcl-radio.ru/viewtopic.php?pid=5416#p5416