Skocz do zawartości

STM32F103xx - synchronizacja timerów (TIM2 preskalerem TIM5)


Pomocna odpowiedź

Napisano

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.

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
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?

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

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...