Skocz do zawartości

NUCLEO-L031K6, HAL, RTC, time.h i dziwne przesunięcie czasu


atlantis86

Pomocna odpowiedź

Od niedawna eksperymentuję z układami STM32 przy wykorzystaniu bibliotek HAL, narzędzia STM32CubeMX oraz środowiska AtollicSTUDIO. Nie znaczy to jednak, że nie mam doświadczenia z mikrokontrolerami - od kilku lat hobbystycznie programuję AVR-y i PIC32, zdarzało mi się także zajmować m.in. starszymi układami z rodziny 8051. Przejście na rodzinę STM32 wydawało mi się początkowo łatwe i bezbolesne, jednak teraz natknąłem się na dziwnego i mocno specyficznego buga, którego przyczyny nie jestem w stanie namierzyć.

Przechodząc do meritum:

Przeportowałem arduinową bibliotekę do obsługi modułów DCF77. Przepisałem ją w C, usunąłem nawiązania do bibliotek Arduino, zastępując je funkcjami standardowej biblioteki C, usunąłem też kilka błędów. Wygląda na to, że biblioteka działa - to znaczy przy dobrych warunkach propagacyjnych (szczególnie wieczorem) odbiera wiarygodnie wyglądające timestampy.

Ponieważ bity parzystości używane przez DCF77 nie są dostatecznym zabezpieczeniem, biblioteka używa kilka sposobów sprawdzania poprawności odebranego czasu. Między innymi porównuje go z czasem systemowym. W oryginale była do tego używana jakaś biblioteka Arduino, ja użyłem time.h. Wiązało się to z koniecznością zdefiniowania własnej wersji funkcji time(), zwracającej czas systemowy z RTC w formie timestampa:

time_t time (time_t * timer) {
	RTC_TimeTypeDef timeStruct;
	RTC_DateTypeDef dateStruct;
	struct tm dstTime;
	HAL_RTC_GetTime(&hrtc, &timeStruct, RTC_FORMAT_BIN);
	HAL_RTC_GetDate(&hrtc, &dateStruct, RTC_FORMAT_BIN);
	dstTime.tm_hour = timeStruct.Hours;
	dstTime.tm_min = timeStruct.Minutes;
	dstTime.tm_sec = timeStruct.Seconds;
	dstTime.tm_year = dateStruct.Year + 100;
	dstTime.tm_mon = dateStruct.Month - 1;
	dstTime.tm_mday = dateStruct.Date;
	return mktime(&dstTime);
}

W pętli głównej programu znajduje się następujący fragment, odpowiedzialny z synchronizację czasu i jego wyświetlanie na LCD:

if ( (tmptime = DCF77_getUTCTime()) ) {
  printf("Time updated: %lu\r\n", tmptime);
  setRealTimeClock(tmptime);
  lastupdate = tmptime;
}

if (rtcTickFlag) {
  tmptime = time(NULL);
  tmupd = gmtime((const time_t*)&tmptime);
  snprintf(buffer, sizeof(buffer)-1, "%02d:%02d:%02d", tmupd->tm_hour, tmupd->tm_min, tmupd->tm_sec);
  PCD_GotoXYFont(1,1);
  PCD_Str(FONT_1X, (uint8_t*)buffer);
  snprintf(buffer, sizeof(buffer)-1, "%02d-%02d-%02d", tmupd->tm_mday, (tmupd->tm_mon)+1, (tmupd->tm_year)-100);
  PCD_GotoXYFont(1,2);
  PCD_Str(FONT_1X, (uint8_t*)buffer);
  if (lastupdate != 0) {
    tmupd = gmtime((const time_t*)&lastupdate);
    PCD_GotoXYFont(1,4);
    PCD_Str(FONT_1X, (uint8_t*)"Updated:");
    snprintf(buffer, sizeof(buffer)-1, "%02d:%02d:%02d", tmupd->tm_hour, tmupd->tm_min, tmupd->tm_sec);
    PCD_GotoXYFont(1,5);
    PCD_Str(FONT_1X, (uint8_t*)buffer);
    snprintf(buffer, sizeof(buffer)-1, "%02d-%02d-%02d", tmupd->tm_mday, (tmupd->tm_mon)+1, (tmupd->tm_year)-100);
    PCD_GotoXYFont(1,6);
    PCD_Str(FONT_1X, (uint8_t*)buffer);
  }
  PCD_Upd();
  rtcTickFlag = 0;
}

Funkcja setRealTimeClock() wygląda następująco:

void setRealTimeClock (time_t tm) {
	RTC_TimeTypeDef timeStruct;
	RTC_DateTypeDef dateStruct;
	struct tm *dstTime;

	dstTime = gmtime(&tm);
	timeStruct.Hours = dstTime->tm_hour;
	timeStruct.Minutes = dstTime->tm_min;
	timeStruct.Seconds = dstTime->tm_sec;
	dateStruct.Year = (dstTime->tm_year)-100;
	dateStruct.Month = (dstTime->tm_mon)+1;
	dateStruct.Date = dstTime->tm_mday;
	HAL_RTC_SetTime(&hrtc, &timeStruct, RTC_FORMAT_BIN);
	HAL_RTC_SetDate(&hrtc, &dateStruct, RTC_FORMAT_BIN);
}

Na początku wszystko zdaje się działać prawidłowo. Układ startuje, RTC odlicza czas. Gdy pojawiają się odpowiednie warunki, moduł DCF77 zaczyna odbierać ramki i ustawia zegar. Na wyświetlaczu pojawia się właściwy czas UTC, jak również informacja o momencie ostatniej synchronizacji. Przez jakiś czas wszystko gra. Jednak w pewnym momencie dzieje się coś dziwnego. Aktualnie wyświetlany czas rozjeżdża się o jakieś osiem godzin do przodu. Na przykład wczoraj o 20.00 UTC pokazywał godziną 4.00 UTC dnia następnego. Żeby było jeszcze dziwniej informacja o ostatnio odebranej ramce wciąż pokazuje właściwy czas.

Nie wiem na którym etapie pojawia się przekłamanie. Do konwersji czasu na formę tekstową używam przecież tych samych funkcji, Gdyby problem związany był z RTC albo moja funkcją time(), to przecież kolejne synchronizacje nie byłyby możliwe - biblioteka DCF77 wychwytywałaby sporą różnicę pomiędzy czasem systemowym i odebranym. W takim wypadku po odebraniu dwóch kolejnych prawidłowych ramek uznałaby, że czas systemowy jets zły i tak czy inaczej ustawiła RTC. Tymczasem na wyświetlaczu pojawiają się informacje o kolejnej udanej synchronizacji, a aktualny czas i tak jest wyświetlany błędnie...

Ktoś ma pojęcie o co może chodzić?

W załączniku przesyłam kompletny projekt, w razie gdyby przytoczone powyżej fragmenty kodu źródłowego okazały się niewystarczające.

DCF77_LCD.zip

Link do komentarza
Share on other sites

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.