Skocz do zawartości

STM32F103xx - synchronizacja timerów (TIM2 preskalerem TIM5)


pion3k

Pomocna odpowiedź

Witam,

co jakiś czas mam problem z brakiem inkrementacji rejestru CNT wolniejszego timera, inkrementowanego przez przepełnienie timera szybszego.

Konfiguracja timerów odbywa się w sposób standardowy, tzn. pomijając ustawienia jednostek Capture, ustawiam też kaskadowe połączenie timerów zgodnie z dokumentacją. TIM5 ma być inkrementowany poprzez przepełnienie licznika TIM2. W tym celu:

1) [TIM2] Ustawiam bity 6:4 MMS = 010 (przepełnienie TIM2 powoduje załączenie TRGO)

2) [TIM5] Wybieram wejściowy trigger służący do synchronizacji licznika (ITR0, TS=000 rejestru TIM5_SMCR)

3) [TIM5] Ustawiam zbocze narastające jako taktowanie wolniejszego licznika (TRGI, SMS=111 rejestru TIM5_SMCR)

4) Odpalam liczniki TIM2, TIM5

Dodam, że rejestry TIM2->CNT i TIM5->CNT były wyzerowane.

Generalnie program działa dobrze, przepełnienie TIM2 powoduje inkrementację TIM5 ale... nie zawsze!

W programie wygląda to tak:

// funkcja main(), pętla while(1)
// część programu, wywołanie funkcji

static volatile unsigned short tim5_value, tim2_value;
static volatile unsigned long Timer25a, Timer25b;

tim5_value = TIM5->CNT;
tim2_value = TIM2->CNT;
Timer25a = tim2_value + (tim5_value << 16);  //zapisz do zmiennej Timer25a wartość dwóch rejestrów

// tutaj występuje kilka instrukcji nie mających związku z timerami

tim5_value = TIM5->CNT;  //powtarzamy odczytanie rejestrów (różnica Timer25a-Timer25b da nam czas)
tim2_value = TIM2->CNT;
Timer25b = tim2_value + (tim5_value << 16);  //zapisz do zmiennej Timer25b wartość dwóch rejestrów

Jak wspomniałem, program działa bez zarzutów, jednak co jakiś czas przepełnienie TIM2 nie powoduje inkrementacji TIM5. Zdiagnozowałem to przy debugowaniu, i tak:

1) Na początku zmienna Timer23a ma wartość np. 0x2171 C519

2) A za chwilę zmienna Timer23b ma wartość np. 0x2171 0013

Mimo, że nastąpiło przepełnienie licznika szybszego (o czym świadczą 4 najmłodsze bajty) to licznik TIM5 (cztery najstarsze bajty) się nie inkrementował. Dodam, że rejestry ARR wynoszą odpowiednio:

TIM2->ARR=0xFFFF;

TIM5->ARR=0x2AEA;

Z góry dziękuję za pomoc.

Link do komentarza
Share on other sites

Moim zdaniem to nie jest problem timerów tylko sposobu odczytu. Popatrz, może zajść taka sytuacja:

1. Niech w chwili zero timery mają wartości jak z Twojego przykładu: 0x2171, 0xC519

2. Odczytujesz starszy timer i masz 0x2171

3. Przychodzi przerwanie (których w systemie na pewno jest mnóstwo), w czasie obsługi którego młodszy timer przepełnia się i prawidłowo inkrementuje starszy.

4. Odczytujesz młodszy timer i widzisz 0x0013

5. Wyciągasz błędny wniosek, że timery w procesorze popsuły się 🙁

Dostępy do rejestrów/zmiennych, które zmieniają się niezależnie od biegu programu powinny być atomowe. Dotyczy to wszystkiego co jest większe niż pojedyncza (z definicji atomowa) operacja procesora. Każde dwa dostępy do pamięci/rejestrów mogą być rozdzielone dowolnie długą przerwą związaną z przełączaniem kontekstu programu.

Podejrzewam, że ujęcie tych dwóch następujących po sobie odczytów w sekcję krytyczną (nawias z instrukcji wyłączenia i włączenia systemu przerwań) znacznie zmniejszy prawdopodobieństwo błędnego odczytu, ale.. nie załatwi sprawy. Nawet gdyby nie było działających przerwań, zawsze może zajść sytuacja gdy timery zinkrementują się pomiędzy odczytami. Wtedy - stosując swoją metodę - także zobaczysz błąd. Spróbuj wykombinować jak uchronić się przed ogłupieniem programu. Pierwsze co przychodzi mi do głowy to sekwencja trzech odczytów: starszy, młodszy, starszy. Gdy oba odczyty starszego są identyczne, jest OK. Gdy różne, zaszła inkrementacja i ponowny odczyt młodszego powinien załatwić sprawę. Oczywiście wszystko w sekcji krytycznej.

  • Pomogłeś! 1
Link do komentarza
Share on other sites

Moim zdaniem to nie jest problem timerów tylko sposobu odczytu. Popatrz, może zajść taka sytuacja:

1. Niech w chwili zero timery mają wartości jak z Twojego przykładu: 0x2171, 0xC519

2. Odczytujesz starszy timer i masz 0x2171

3. Przychodzi przerwanie (których w systemie na pewno jest mnóstwo), w czasie obsługi którego młodszy timer przepełnia się i prawidłowo inkrementuje starszy.

4. Odczytujesz młodszy timer i widzisz 0x0013

5. Wyciągasz błędny wniosek, że timery w procesorze popsuły się 🙁

Dostępy do rejestrów/zmiennych, które zmieniają się niezależnie od biegu programu powinny być atomowe. Dotyczy to wszystkiego co jest większe niż pojedyncza (z definicji atomowa) operacja procesora. Każde dwa dostępy do pamięci/rejestrów mogą być rozdzielone dowolnie długą przerwą związaną z przełączaniem kontekstu programu.

Podejrzewam, że ujęcie tych dwóch następujących po sobie odczytów w sekcję krytyczną (nawias z instrukcji wyłączenia i włączenia systemu przerwań) znacznie zmniejszy prawdopodobieństwo błędnego odczytu, ale.. nie załatwi sprawy. Nawet gdyby nie było działających przerwań, zawsze może zajść sytuacja gdy timery zinkrementują się pomiędzy odczytami. Wtedy - stosując swoją metodę - także zobaczysz błąd. Spróbuj wykombinować jak uchronić się przed ogłupieniem programu. Pierwsze co przychodzi mi do głowy to sekwencja trzech odczytów: starszy, młodszy, starszy. Gdy oba odczyty starszego są identyczne, jest OK. Gdy różne, zaszła inkrementacja i ponowny odczyt młodszego powinien załatwić sprawę. Oczywiście wszystko w sekcji krytycznej.

Wygląda na to, że masz rację! Użyłem wstawek asemblerowych do chwilowego zablokowania wszystkich przerwań i jak na tę chwilę działa 🙂 Wielkie dzięki!

Mam jeszcze jedno pytanie: czy istnieje możliwość (na czas odczytu) zablokowania wartości rejestru CNT timera szybszego, następnie odczytanie CNT timera wolniejszego (który się nie przekręci bo przecież zablokowałem przed chwilą szybszy timer) a następnie wznowienie jego działania?

Link do komentarza
Share on other sites

Skoro możesz timer wystartować to możesz go i zatrzymać - to jasne, ale to trochę przeczy idei autonomicznych bloków liczących czas/zdarzenia. Im częściej będziesz w ten sposób czytał timer tym wolniej będzie on liczył 🙁 Radzę jednak przemyśleć sprawę wielokrotnych odczytów, bo chwilowe wyłączenie przerwań tylko zmniejsza prawdopodobieństwo zajścia zdarzenia ale nie redukuje go do zera. W końcu na to trafisz a jeśli program będzie bezmyślnie takim wynikom wierzył, może(sz) się przejechać.

Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

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!

Gość
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...

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.