Skocz do zawartości
atlantis86

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

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

Udostępnij ten post


Link to post
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.


×
×
  • Utwórz nowe...