RTV forum PL | NewsGroups PL

Biblioteka standardowa time.h i mikrokontrolery

NOWY TEMAT

elektroda NewsGroups Forum Index - Elektronika Polska - Biblioteka standardowa time.h i mikrokontrolery

Goto page 1, 2  Next

Atlantis
Guest

Wed Sep 12, 2018 9:05 am   



Może ktoś z was będzie miał jakiś pomysł, bo od paru dni nie mogę dojść
do tego, co robię nie tak. Sytuacja wygląda następująco.

Środowisko: Płytka NUCLEO-L031K6, programowana za pomocą IDE
AtollicSTUDIO. Projekt generowany narzędziem STM32CubeMX.

Software: Biblioteka do obsługi DCF77, przeportowana z Arduino. Do tego
biblioteka sterująca wyświetlaczem od Nokii, przeportowana z AVR.
Obydwie zdają się działać prawidłowo.

DCF odczytuje w przerwaniu impulsy z modułu i sprawdza poprawność ramek.
Kontrolowane jest m.in. to, czy odebrany czas nie różni się za bardzo od
systemowego zegara. Jeśli mamy do czynienia z taką sytuacją, konieczne
jest odebranie dwóch prawidłowych ramek, jedna po drugiej. Oryginał
korzystał w tym celu z jakiejś arduinowej biblioteki, ja przerobiłem to
na standardowe time.h, podkładając wszędzie gdzie trzeba wywołania
time(NULL).

Oczywiście zdefiniowałem w kodzie swoją własną funkcję time(), która
czyta systemowy RTC, wypełnia pobranymi wartościami strukturę struct tm,
a następnie przy pomocy mktime() generuje timestamt time_t.

Sam moduł RTC jest obsługiwany za pomocą bibliotek HAL. Po odebraniu
nowej ramki zegar jest ustawiany wartościami uzyskanymi za pośrednictwem
gmtime().

I teraz przechodząc do sedna sprawy: na początku wszystko zdaje się
działać prawidłowo. Po starcie układu zegar zaczyna odliczać w górę, a
jego aktualną wartość wyświetla się na LCD. Jeśli pojawią się
odpowiednie warunki propagacyjne, DCF zaczyna dobierać ramki i
przestawia zegar. Dodatkowo na LCD wyrzucam też info o czasie ostatniej
poprawnej synchronizacji. Potrafi to działać prawidłowo przez kilka
godzin, aż w końcu coś się wysypuje. Na przykład wczoraj około 20.00 UTC
zegar stwierdził, że jest 4.00 UTC następnego dnia. Żeby było ciekawiej,
kolejne ramki DCF były nadal odbierane, a na ekranie pojawiała się
informacja o udanych synchronizacjach, oznaczonych prawidłowym czasem (!).

Kilka rzeczy nie daje mi spokoju:
- Dokładność tej anomalii - obserwowałem ją kilka razy i OIDP zawsze
było to osiem godzin do przodu. Nie chodzi więc o odebranie jakimś cudem
błędnej ramki.
- Przygotowana przeze mnie funkcje time() najwyraźniej zwraca cały czas
prawidłowego timestampa, bo inaczej kolejne synchronizacje nie
dochodziłyby do skutku. Program stwierdziłby rozjechanie się RTC z
odbieranym czasem, czekając na dwie poprawne ramki. Wtedy ustawiłby
zegar i wszystko wróciłoby do normy. Tak się jednak nie dzieje. Po
pojawieniu się anomalia pozostaje na stałe.

W chwili obecnej do pobierania czasu z RTC używam kombinacji time() i
gmtime(), a uzyskane wartości ze struktury wyrzucam na ekran. Po udanej
synchronizacji odebrany czas z DCF jest zapisywany do zmiennej i również
trafia na ekran za pośrednictwem gmtime().

Ktoś ma jakiś pomysł, co mogę robić nie tak? Może time.h w przypadku
mikrokontrolerów wymaga jakiegoś przygotowania (poza podstawieniem
własnej funkcji time())? W jaki sposób chociażby definiuje się w niej
strefę czasową. Pod Linuksem ustawiało się zmienna środowiskową. A na
małym mikrokontrolerze?

Grzegorz Niemirowski
Guest

Wed Sep 12, 2018 9:49 am   



Atlantis <marekw1986NOSPAM_at_wp.pl> napisał(a):
Quote:
- Przygotowana przeze mnie funkcje time() najwyraźniej zwraca cały czas
prawidłowego timestampa, bo inaczej kolejne synchronizacje nie
dochodziłyby do skutku. Program stwierdziłby rozjechanie się RTC z
odbieranym czasem, czekając na dwie poprawne ramki. Wtedy ustawiłby
zegar i wszystko wróciłoby do normy. Tak się jednak nie dzieje. Po
pojawieniu się anomalia pozostaje na stałe.
W chwili obecnej do pobierania czasu z RTC używam kombinacji time() i
gmtime(), a uzyskane wartości ze struktury wyrzucam na ekran. Po udanej
synchronizacji odebrany czas z DCF jest zapisywany do zmiennej i również
trafia na ekran za pośrednictwem gmtime().
Ktoś ma jakiś pomysł, co mogę robić nie tak? Może time.h w przypadku
mikrokontrolerów wymaga jakiegoś przygotowania (poza podstawieniem
własnej funkcji time())? W jaki sposób chociażby definiuje się w niej
strefę czasową. Pod Linuksem ustawiało się zmienna środowiskową. A na
małym mikrokontrolerze?

Dlaczego pisałeś własną funkcję time()? Ta funkcja już jest gotowa, a Twoim
zadaniem jest napisanie funkcji _gettimeofday(). time() z biblioteki newlib
woła _gettimeofday_r() a ta z kolei _gettimeofday(), którą trzeba
dostarczyć.
Czy monitorowałeś zawartość RTC, np. wypisując zawartość na porcie
szeregowym albo przez semihosting?
Strefę czasową ustawia się tak samo:
setenv("TZ","CET-1CEST,M3.5.0/2,M10.5.0/3",1);

--
Grzegorz Niemirowski
https://www.grzegorz.net/

Atlantis
Guest

Wed Sep 12, 2018 12:54 pm   



On 12.09.2018 11:49, Grzegorz Niemirowski wrote:

Quote:
Dlaczego pisałeś własną funkcję time()?

Hmm... Tak zawsze robiłem na PIC32. Tam próba odwołania się do time()
bez zdefiniowania własnej wersji tej funkcji powodowała wyświetlenie
warninga:

"Linking code with default time() stub. Hint: Write an app-specific
implementation."


Quote:
Ta funkcja już jest gotowa, a Twoim zadaniem jest napisanie funkcji
_gettimeofday(). time() z biblioteki newlib woła _gettimeofday_r() a
ta z kolei _gettimeofday(), którą trzeba dostarczyć. Czy

To rozwiązanie charakterystyczne dla STM32, czy stanowi ogólnie przyjęty
standard i mogę się spodziewać, że tak samo będzie się to robiło także w
przypadku innych rodzin MCU, a PIC32 jest tutaj jakimś wyjątkiem?

W ogóle istnieje gdzieś jakiś zasób, który tłumaczyłby w jaki sposób
spiąć niskopoziomowo standardową bibliotekę C z własnym sprzętem?

Grzegorz Niemirowski
Guest

Wed Sep 12, 2018 2:47 pm   



Atlantis <marekw1986NOSPAM_at_wp.pl> napisał(a):
Quote:
To rozwiązanie charakterystyczne dla STM32, czy stanowi ogólnie przyjęty
standard i mogę się spodziewać, że tak samo będzie się to robiło także w
przypadku innych rodzin MCU, a PIC32 jest tutaj jakimś wyjątkiem?

ARM, nie STM32, dla ścisłości. Kompilator (gcc-arm-none-eabi) nawet nie wie,
że jest takie coś jak STM32.
Nie wiem jak jest na innych architekturach, z 32-bitowych używam tylko
STM32. Ale w każdym razie z kompilatorem (jako programem) dostarczana jest
też biblioteka języka C. Popularną biblioteką C dla mikrokontrolerów ARM
jest newlib. I ona wymaga własnie _gettimeofday(), inaczej będzie błąd
linkowania. Widocznie w bibliotece rozprowadzanej z kompilatorem dla PIC32
to jest uproszczone, nie ma dodatkowych warstw w postaci _gettimeofday_r() i
_gettimeofday() i trzeba od razu napisać time().

Quote:
W ogóle istnieje gdzieś jakiś zasób, który tłumaczyłby w jaki sposób
spiąć niskopoziomowo standardową bibliotekę C z własnym sprzętem?

Ogólnie jest tak, że w przypadku standardowych funkcji C komunikujących się
z tym, co jest na zewnątrz aplikacji (czas, pliki, standardowe we/wy,
dynamiczna alokacja pamięci), funkcje te wywołują funkcje systemu
operacyjnego (system calls - syscalls). Ponieważ na mikrokontrolerze nie ma
systemu operacyjnego, trzeba właśnie te funkcje systemowe napisać samemu.
Dlatego jeśli chodzi o ARM GCC, możesz napotkać na posty ludzi, którym
linker wywala brak funkcji _write(), _read(), czy _sbrk(). Więc odpowiadając
na Twoje pytanie, to nie znam takiego jednego zasobu, ale googlałbym po
słowach kluczowych newlib syscalls arm. Można znaleźć np.
https://balau82.wordpress.com/2010/12/16/using-newlib-in-arm-bare-metal-programs/
Przykładowy plik syscalli z minimalnymi funkcjami:
https://github.com/ROBOTIS-GIT/OpenCR/blob/master/arduino/opencr_develop/opencr_bootloader/common/bsp/opencr/cfg/syscalls.c
Dzięki temu, że piszesz własną implementację syscalli możesz np.
przekierować printf() (bo on woła pod spodem _write()) na port szeregowy
albo konsolę semihostingu:
int _write(int file, char *ptr, int len)
{
if (CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk) {
for (int DataIdx = 0; DataIdx < len; DataIdx++)
{
SH_SendChar(*ptr++);
}
}
return len;
}

--
Grzegorz Niemirowski
https://www.grzegorz.net/

Atlantis
Guest

Wed Sep 12, 2018 6:58 pm   



On 12.09.2018 16:47, Grzegorz Niemirowski wrote:

Quote:
ARM, nie STM32, dla ścisłości. Kompilator (gcc-arm-none-eabi) nawet nie
wie, że jest takie coś jak STM32.

Czyli rozumiem, że inne ARM-y programuje się w podobny sposób,
korzystając z tego samego kompilatora? Na przykład takie AT91SAM7* będą
miały to rozwiązane w ten sam sposób, a różnice będą wynikały głównie z
nieco innego zestawu peryferiów (np. brak SysTick) oraz innych bibliotek
do ich obsługi?

Tak z ciekawości. Jak to wyglądało w przypadku AVR-ów? W czasach, gdy
uczyłem się tych mikrokontrolerów nigdy nie musiałem podpinać
niskopoziomowych funkcji do obsługi biblioteki standardowej. Zamiast
printf używałem zestawu snprintf + uart_puts, a operacje związane z
czasem załatwiałem za pomocą zestawu własnych funkcji. Jestem jednak
ciekaw na ile dałoby się to zrobić w teorii. Zwłaszcza, że przecież
niektóre z wyższych modeli Atmegi i AtXmegi miały już dość sporo pamięci...

Marek
Guest

Wed Sep 12, 2018 7:53 pm   



On Wed, 12 Sep 2018 14:54:19 +0200, Atlantis <marekw1986NOSPAM_at_wp.pl>
wrote:
Quote:
przypadku innych rodzin MCU, a PIC32 jest tutaj jakimś wyjątkiem?

Nie wiem czy wyjątkiem, libc w pic32 jest niekompletne i zawiera
błędy (przynajmniej w tym co było dostępne przed Harmony). Nie da
się zaplementowac poprawnie stdio mimo, że jest przygotowane
nagłówkowo by to zaimplementować przez własne prymitywy _fopen()
_write(), _read() (mimo wlasnej implementacji powyższych fopen() nie
działalo prawidłowo, nie pamiętam w czym był problem). Nie zawsze
działa prawidłowo sscanf(). To tak w skrócie.

--
Marek

Grzegorz Niemirowski
Guest

Wed Sep 12, 2018 10:07 pm   



Atlantis <marekw1986NOSPAM_at_wp.pl> napisał(a):
Quote:
Czyli rozumiem, że inne ARM-y programuje się w podobny sposób,
korzystając z tego samego kompilatora? Na przykład takie AT91SAM7* będą
miały to rozwiązane w ten sam sposób, a różnice będą wynikały głównie z
nieco innego zestawu peryferiów (np. brak SysTick) oraz innych bibliotek
do ich obsługi?

Tak, zgadza się. W obrębie danego kompilatora wszystko będzie odbywało się
bardzo podobnie. Standardowe biblioteki C nie zajmują się peryferiami, nie
są aż tak niskopoziomowe i daje to pewną przenośność. Z resztą na
Windowsie/Linuksie Twój program też nie zajmuje się obsługą karty graficznej
czy pakietami TCP/IP. Od tego jest system operacyjny i sterowniki.
Kompilator interesuje przede wszystkim rdzeń mikrokontrolera, lista rozkazów
(np. Thumb2) czy dostępność FPU. Do tego opcje linkera, jak adresacja
RAM/Flash oraz dołączanie bibliotek. Nie ma znaczenia SysTick czy RTC.
Możesz zerknąć na pliki Makefile dla różnych ARMowych projektów i sprawdzić
jakie parametry są przekazywane do kompilatora.

Quote:
Tak z ciekawości. Jak to wyglądało w przypadku AVR-ów? W czasach, gdy
uczyłem się tych mikrokontrolerów nigdy nie musiałem podpinać
niskopoziomowych funkcji do obsługi biblioteki standardowej. Zamiast
printf używałem zestawu snprintf + uart_puts, a operacje związane z
czasem załatwiałem za pomocą zestawu własnych funkcji. Jestem jednak
ciekaw na ile dałoby się to zrobić w teorii. Zwłaszcza, że przecież
niektóre z wyższych modeli Atmegi i AtXmegi miały już dość sporo
pamięci...

W AVR GCC jest taka funkcja:
FILE* fdevopen(int(*)(char, FILE *)put, int(*)(FILE *)get);
Służy ona do definiowania strumienia we/wy. Podajesz jej jako parametry
wskaźniki na funkcje do wysyłania i odbierania znaku. Parametry mogą być
NULami. I jest taki myk, że pierwsze wywołanie fdevopen(), które ma parametr
put niebędący NULem, powoduje zdefiniowanie strumienia stdout (standardowego
wyjścia). Jeśli więc masz funkcję uart_putc(), czyli dla pojedynczego znaku,
to możesz napisać:
int put(char c, FILE * file) {
uart_putc(c);
return 0;
}
I wywołać fdevopen():
fdevopen(&put, NULL);
Od tego momentu możesz używać printf() a wyniki jego działania będą szły na
port szeregowy. Albo np. na LCD jeśli zamiast uart_putc() użyjesz funkcji do
wyświetlania literki na wyświetlaczu. Możesz też zdefiniować drugi strumień
i odwoływać się do niego za pomocą fprintf() jeśli chcesz wysyłać teksty
metodą printfową zarówno na LCD jak i UART.
To wszystko jest opisane w dokumentacji AVR GCC, a dokładniej AVR libc
https://www.nongnu.org/avr-libc/user-manual/group__avr__stdio.html

Co do czasu, to AVR libc ma wewnętrzny licznik, który trzeba inkrementować
wywołując co sekundę funkcję system_tick(). A najpierw oczywiście
zainicjalizować wołając set_system_time(). Wtedy funkcja time() będzie mogła
zwrócić właściwy czas. Opis jest na stronie
https://www.nongnu.org/avr-libc/user-manual/group__avr__time.html Jak widać
obsługa czasu jest okrojona. Nie można np. ustawić strefy czasowej oraz
czasu letniego za pomocą zmiennej środowiskowej. Zamiast tego ustawia się
strefę funkcją set_zone() a czas letni jest załatwiany poprzez wskazanie
funkcją set_dst() funkcji, która obliczy przesunięcie czasu letniego na
podstawie podanego timestampa (przykładowo dzisiaj zwróci 3600 a w grudniu
0). Czyli jeśli wywołamy funkcję localtime(), to zostanie wzięty czas z
funkcji time(), dodane przesunięcie wynikające ze strefy czasowej (podane
jako parametr set_zone()) oraz dodane przesunięcie z funkcji wskazanej przez
set_dst(). A skąd wziąć funkcję do czasu letniego? W bibliotece
util/eu_dst.h jest taka funkcja dla Unii Europejskiej. Ale nie działa
poprawnie Smile Bug+fix: https://savannah.nongnu.org/bugs/?44327

Czyli podsumowując, w AVR GCC zamiast syscalli wymyślili takie obejścia.

--
Grzegorz Niemirowski
https://www.grzegorz.net/

Atlantis
Guest

Thu Sep 13, 2018 5:46 am   



Hmm... Wygląda na to, że problem leży głębiej i dotyczy raczej RTC
(ewentualnie funkcji bibliotecznych odpowiedzialnych za odczytywanie
czasy), niż biblioteki standardowej time.h.

Zgodnie z sugestiami, które tu padły, zastąpiłem usunąłem swoją własną
wersje funkcji time() i napisałem własną wersję _gettimeofday.

int _gettimeofday (struct timeval* tp, struct timezone* tzp) {
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;

if (tp) {
tp->tv_sec = mktime(&dsttime);
tp->tv_usec = 0;
}

if (tzp) {
tzp->tz_minuteswest = 0;
tzp->tz_dsttime = 0;
}

return 0;
}

Następnie w pętli głównej usunąłem gmtime(), zamiast tego wyświetlając
na LCD aktualna wartość zwracaną przez time(). Efekt był dość...
Dziwny... Mianowicie liczba złożona z dwóch ostatnich cyfr faktycznie
zwiększała swoją wartość o jeden co sekundę. Natomiast trzecia, czwarta
i piata cyfra od końca co chwilę zmieniała swoją wartść "tam i z
powrotem" - raz było 500 z czymś, potem ponad 600, potem znów 500 z
czymś i tak dalej...

Postanowiłem więc zrobić eksperyment i stworzyłem zmienną uint32_t _rtc,
która była zwiększana o 1 w przerwaniu alarmu RTC. Podpiąłem ją do
funkcji _gettimeofday i problem zniknął.

Ktoś wie gdzie może leżeć przyczyna takiego zachowania? Co robię nie tak
czytając RTC? Przykład u góry.

Jacek Radzikowski
Guest

Thu Sep 13, 2018 6:37 am   



On 09/13/18 01:46, Atlantis wrote:
[...]
Quote:
Następnie w pętli głównej usunąłem gmtime(), zamiast tego wyświetlając
na LCD aktualna wartość zwracaną przez time(). Efekt był dość...
Dziwny... Mianowicie liczba złożona z dwóch ostatnich cyfr faktycznie
zwiększała swoją wartość o jeden co sekundę. Natomiast trzecia, czwarta
i piata cyfra od końca co chwilę zmieniała swoją wartść "tam i z
powrotem" - raz było 500 z czymś, potem ponad 600, potem znów 500 z
czymś i tak dalej...

Postanowiłem więc zrobić eksperyment i stworzyłem zmienną uint32_t _rtc,
która była zwiększana o 1 w przerwaniu alarmu RTC. Podpiąłem ją do
funkcji _gettimeofday i problem zniknął.

Ktoś wie gdzie może leżeć przyczyna takiego zachowania? Co robię nie tak
czytając RTC? Przykład u góry.

To mocno śmierdzi pisaniem po stosie, i problem wcale nie musi być w
twoim kodzie. Uprość maksymalnie program testowy, nie używaj LCD ani
innych wodotrysków, tylko pisz na konsolę szeregową. Jeśli w dalszym
ciągu będą problemy, to błąd najprawdopodobniej siedzi gdzieś w obsłudze
RTC. Jeśli nie, to dodawaj po kolejne elementy i patrz kiedy zacznie
wariować. Wtedy możesz zacząć szukać w którym komponencie jest problem.

Nie bez znaczenia jest też wersja kompilatora. Dwa razy się mocno
przejechałem na arm-gcc: Po raz pierwszy, kiedy próbowałem skompilować
bibliotekę do obsługi launchpadowego LCD od TI. Darmowy kompilator
wszystko pięknie kompilował, przykłady się linkowały i uruchamiały, nie
działał tylko ekran dotykowy. Udało mi się zawęzić region gdzie powstaje
błąd do kilku linijek kodu, i tam utknąłem. Nie było tam żadnych
sztuczek zależnych od wersji kompilatora, nic do czego można by się
przyczepić. Po skompilowaniu gcc od TI wszystko zadziałało poprawnie.
Drugi raz wyłożyłem się na klasach z metodami wirtualnymi. Wystarczyło
dodać słówko "virtual" do deklaracji metody i program szedł w maliny.
Tym razem śledziłem wykonanie instrukcja po instrukcji, i znalazłem że
momencie powrotu z metody wskaźnik stosu wskazywał w jakieś losowe
miejsce w pamięci. I znów, z gcc od TI i ich własnym kompilatorem
wszystko działało poprawnie. Ale wystarczyła sama zmiana wywoływanego
kompilatora w projekcie wygenerowanym przez CCS żeby problem powrócił. W
tym momencie się poddałem, a projekt czeka aż będę miał czas i wenę żeby
znów się nim zająć.

Jacek.

Atlantis
Guest

Thu Sep 13, 2018 7:03 am   



On 13.09.2018 08:37, Jacek Radzikowski wrote:

Quote:
To mocno śmierdzi pisaniem po stosie, i problem wcale nie musi być w
twoim kodzie. Uprość maksymalnie program testowy, nie używaj LCD ani
innych wodotrysków, tylko pisz na konsolę szeregową. Jeśli w dalszym
ciągu będą problemy, to błąd najprawdopodobniej siedzi gdzieś w obsłudze
RTC. Jeśli nie, to dodawaj po kolejne elementy i patrz kiedy zacznie
wariować. Wtedy możesz zacząć szukać w którym komponencie jest problem.

Płytka prototypowa na której działa ten przykład jest dość skromna - to
fakt. Flash jest w chwili obecnej prawie całkowicie zapchany, jednak
pamięci RAM pozostało jeszcze całkiem sporo. Wątpię, żeby mogło dojść do
napisania stosu. Biblioteka LCD działała prawidłowo na AVR, a po
przeportowaniu na STM32 program z nią również działa poprawnie, pod
warunkiem zastąpienia wbudowanego RTC osobną zmienną przechowującą
timestampa.

Jedyne co mi jeszcze przychodzi do głowy, to próba odczytywania RTC z
parametrem RTC_FORMAT_BCD, a następnie konwertowania do postaci binarnej
za pomocą zestawu własnych funkcji.

W każdym razie sposób w jaki pobieram dane z RTC i wypełniam nimi
strukturę struct tm wygląda w porzadku?

Atlantis
Guest

Thu Sep 13, 2018 7:05 am   



Z ciekawości zobaczę też chyba jak ten projekt zachowa się po
przeniesieniu na jakąś "większą" płytkę Nucleo albo Discovery. Tylko
muszę znaleźć na to chwilę czasu. Wink

Jacek Radzikowski
Guest

Thu Sep 13, 2018 7:14 am   



On 09/13/18 03:03, Atlantis wrote:
Quote:
On 13.09.2018 08:37, Jacek Radzikowski wrote:

To mocno śmierdzi pisaniem po stosie, i problem wcale nie musi być w
twoim kodzie. Uprość maksymalnie program testowy, nie używaj LCD ani
innych wodotrysków, tylko pisz na konsolę szeregową. Jeśli w dalszym
ciągu będą problemy, to błąd najprawdopodobniej siedzi gdzieś w obsłudze
RTC. Jeśli nie, to dodawaj po kolejne elementy i patrz kiedy zacznie
wariować. Wtedy możesz zacząć szukać w którym komponencie jest problem.

Płytka prototypowa na której działa ten przykład jest dość skromna - to
fakt. Flash jest w chwili obecnej prawie całkowicie zapchany, jednak
pamięci RAM pozostało jeszcze całkiem sporo. Wątpię, żeby mogło dojść do
napisania stosu. Biblioteka LCD działała prawidłowo na AVR, a po
przeportowaniu na STM32 program z nią również działa poprawnie, pod
warunkiem zastąpienia wbudowanego RTC osobną zmienną przechowującą
timestampa.

Jedyne co mi jeszcze przychodzi do głowy, to próba odczytywania RTC z
parametrem RTC_FORMAT_BCD, a następnie konwertowania do postaci binarnej
za pomocą zestawu własnych funkcji.

W każdym razie sposób w jaki pobieram dane z RTC i wypełniam nimi
strukturę struct tm wygląda w porzadku?

Ilość dostępnej pamięci nie ma żadnego znaczenia. Wystarczy zaalokować o
1 bajt za mało na dane i struktury w pamięci zaczynają na siebie
nachodzić. Nawet jeśli ponad nimi będzie jeszcze kilka MB nieużywanej
pamięci.
To że biblioteka działa na jednej platformie że oznacza że będzie
działać na innej. Dlatego do debugowania zawsze powinno się używać jak
najprostszego kodu testowego, z minimalną ilością zależności od
dodatkowych komponentów.

Poza tym że nie pochwaliłeś się jak inicjalizujesz hrtc, to na pierwszy
rzut oka nie ma do czego się przyczepić.

Jacek.

Grzegorz Niemirowski
Guest

Thu Sep 13, 2018 9:18 am   



Atlantis <marekw1986NOSPAM_at_wp.pl> napisał(a):
Quote:
struct tm dstTime;

Nie inicjalizujesz wszystkich pól tej struktury, np. tm_isdst. Wyzeruj ją:
przy deklaracji
struct tm dstTime = {0};

Quote:
Postanowiłem więc zrobić eksperyment i stworzyłem zmienną uint32_t _rtc,
która była zwiększana o 1 w przerwaniu alarmu RTC. Podpiąłem ją do
funkcji _gettimeofday i problem zniknął.

Przy okazji: zawsze używaj time_t bo nie masz gwarancji, że timestamp będzie
32-bitowy. To się może zmieniać w zależności od wersji kompilatora.

Quote:
Ktoś wie gdzie może leżeć przyczyna takiego zachowania? Co robię nie tak
czytając RTC? Przykład u góry.

Moim zdaniem czytasz dobrze. Czemu nie wyświetlisz sobie poszczególnych pól
na LCD albo serialu? Im więcej danych diagnostycznych tym lepiej :)

--
Grzegorz Niemirowski
https://www.grzegorz.net/

Atlantis
Guest

Fri Sep 14, 2018 7:33 am   



On 13.09.2018 11:18, Grzegorz Niemirowski wrote:

Quote:
Nie inicjalizujesz wszystkich pól tej struktury, np. tm_isdst. Wyzeruj
ją: przy deklaracji
struct tm dstTime = {0};

Spróbowałem nawet zerowania struktury za pomocą funkcji memset, ale to
chyba nie to.
Mam jeszcze jedną hipotezę - zauważyłem, że podczas ustawiania zegara na
początku pracy programu (kod wygenerowany przez STM32CubeMX) podawane są
również dodatkowe opcje (np. coś związanego ze zmianą czasu) a także
dzień tygodnia. W swojej funkcji synchronizującej czas pominąłem te
linijki. Po powrocie do domu zobaczę, jak będzie się zachowywał
uzupełniony kod.

Tak BTW przyszedł mi do głowy jeszcze jeden pomysł - z tego co pamiętam
w niektórych modelach PIC32 przed zmianą ustawień zegara konieczne było
odblokowanie tej możliwości poprze wpisanie odpowiedniej wartości do
jednego z rejestrów. Może coś takiego ma też miejsce przynajmniej w
niektórych STM32? W takiej sytuacji oczekiwałbym jednak, że autorzy HAL
wzięli to pod uwagę. Może jednak trzeba to zrobić osobno?


Quote:
Przy okazji: zawsze używaj time_t bo nie masz gwarancji, że timestamp
będzie 32-bitowy. To się może zmieniać w zależności od wersji kompilatora.

Hmm... Przecież chyba właśnie na tym polega sens stosowania typów
zmiennych w formacie *int*_t? Rozumiem, gdybym użył typu unsigned long,
jednak uint32_t 32-bitową zmienną bez znaku? Czyżbym nie miał racji?

Grzegorz Niemirowski
Guest

Fri Sep 14, 2018 9:00 am   



Atlantis <marekw1986NOSPAM_at_wp.pl> napisał(a):
Quote:
Spróbowałem nawet zerowania struktury za pomocą funkcji memset, ale to
chyba nie to.

W takim razie nie wiem. Niemniej ciągle się kłania monitorowanie wartości
RTC. To też jest odpowiedź na poniższe dwa Twoje akapity. Bez sprawdzenia
poprawności działania RTC nie ma
co się w ogóle zajmować time.h.

Quote:
Mam jeszcze jedną hipotezę - zauważyłem, że podczas ustawiania zegara na
początku pracy programu (kod wygenerowany przez STM32CubeMX) podawane są
również dodatkowe opcje (np. coś związanego ze zmianą czasu) a także
dzień tygodnia. W swojej funkcji synchronizującej czas pominąłem te
linijki. Po powrocie do domu zobaczę, jak będzie się zachowywał
uzupełniony kod.

Zainicjuj zgodnie z samplami.
sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
sTime.StoreOperation = RTC_STOREOPERATION_RESET;
Nie zostawiaj niezainicjowanych pól w strukturach.

Quote:
Tak BTW przyszedł mi do głowy jeszcze jeden pomysł - z tego co pamiętam
w niektórych modelach PIC32 przed zmianą ustawień zegara konieczne było
odblokowanie tej możliwości poprze wpisanie odpowiedniej wartości do
jednego z rejestrów. Może coś takiego ma też miejsce przynajmniej w
niektórych STM32? W takiej sytuacji oczekiwałbym jednak, że autorzy HAL
wzięli to pod uwagę. Może jednak trzeba to zrobić osobno?

W STM32 też tak jest i to chyba we wszystkich. Autorzy HAL jak najbardziej o
to zadbali. Obejrzyj sobie kod funkcji ustawiających datę i czas. Jest tam
wykonywane odblokowywanie rejestrów.

Quote:
Przy okazji: zawsze używaj time_t bo nie masz gwarancji, że timestamp
będzie 32-bitowy. To się może zmieniać w zależności od wersji
kompilatora.
Hmm... Przecież chyba właśnie na tym polega sens stosowania typów
zmiennych w formacie *int*_t? Rozumiem, gdybym użył typu unsigned long,
jednak uint32_t 32-bitową zmienną bez znaku? Czyżbym nie miał racji?

Oczywiście jak najbardziej masz rację, że uint32_t to typ 32-bitowy bez
znaku i masz gwarancję, że zawsze tak będzie. Natomiast mnie chodziło o typ
time_t. Napisałem kiedyś takie coś:
struct tm * t = localtime((time_t *)&seconds);
a seconds było zadeklarowane jako uint32_t
I to działało poprawnie w GCC 5.4. Natomiast w GCC 7.2 przestało,
localtime() zaczęło zwracać bzdury. Dlaczego? Bo time_t zmieniono na
64-bitowy i localtime() pobierało za pomocą wskaźnika nie tylko zmienną
seconds ale także 4 bajty leżące obok w pamięci. Gdybym od razu zadeklarował
seconds jako time_t to nie byłoby problemu przy zmianie wersji GCC. uint32_t
to był nadal uint32_t, ale time_t zmieniono z uint32_t na uint64_t. Pewnie w
Twoim kodzie nie ma takiego problemu, ale pomyślałem, że warto wspomnieć.

--
Grzegorz Niemirowski
https://www.grzegorz.net/

Goto page 1, 2  Next

elektroda NewsGroups Forum Index - Elektronika Polska - Biblioteka standardowa time.h i mikrokontrolery

NOWY TEMAT

Regulamin - Zasady uzytkowania Polityka prywatnosci Kontakt RTV map News map