KursyPoradnikiInspirujące DIYForum

Kurs STM32 F4 – #8 – Zaawansowane funkcje liczników

Kurs STM32 F4 – #8 – Zaawansowane funkcje liczników

Większość urządzeń uzależnia swoją pracę od czasu. W celu skutecznej i optymalnej realizacji zadań wykorzystuje się timery (liczniki).

W tym artykule zostaną opisane zagadnienia takie jak generowanie PWM, dekodowanie sygnału kwadraturowego z enkoderów, a nawet analiza sygnału PWM, pochodzącego np. z aparatury RC.


Modulacja Szerokości Impulsu - PWM

Zapewne większość z Was wie czym jest PWM. Jeśli nie, to odsyłam do 6 części kursu elektroniki, w której zagadnienie to zostało szerzej opisane. W roli krótkiego przypomnienia posłużę się jedynie cytatem z Wikipedii:

PWM (ang. Pulse-Width Modulation) – metoda regulacji sygnału prądowego lub napięciowego, o stałej amplitudzie i częstotliwości, polegająca na zmianie wypełnienia sygnału, używana w zasilaczach impulsowych, wzmacniaczach impulsowych i układach sterujących pracą silników. Wikipedia, 14.06.2016

Ze względu na prostotę działania i bardzo łatwą realizację, zarówno sprzętową jak i programową, sygnał PWM używany jest w bardzo wielu zastosowaniach. Niektóre z nich to:

  • sterowanie prędkością silnika,
  • sterowanie jasnością diod świecących (LED),
  • przesyłanie informacji.

Gotowe zestawy do kursów Forbota

 Komplet elementów  Gwarancja pomocy  Wysyłka w 24h

Zestaw elementów do przeprowadzenia wszystkich ćwiczeń z kursu STM32 F4 można nabyć u naszego dystrybutora! Zestaw zawiera m.in. płytkę Discovery, wyświetlacz OLED, joystick oraz enkoder.

Zamów w Botland.com.pl »

Jak wygląda sygnał PWM?

W swojej najprostszej postaci, sygnał PWM jest sygnałem prostokątnym o zmiennej szerokości wypełnienia impulsu (duty). Zobrazujmy to na prostych przykładach. Sygnał PWM o wypełnieniu 50% to taki sygnał, gdzie w jednym okresie (T) stan wysoki trwa tyle samo co stan niski.

Przykład sygnału PWM.

Przykład sygnału PWM.

Ponieważ sygnał ten zazwyczaj zmienia się dosyć szybko (kilkadziesiąt - kilka tysięcy razy na sekundę), często traktuje się go jak sygnał o wartości napięcia proporcjonalnej do wypełnienia impulsu. Znaczy to tyle, że w teorii sygnał PWM o wypełnieniu równym 50% ma średnią wartość napięcia (Vavg) równą połowie wartości napięcia stanu wysokiego.

Inne przykłady sygnału PWM przedstawiono na rysunku poniżej:

Różne wypełnienie sygnału PWM.

Różne wypełnienie sygnału PWM.

Generowanie sygnału PWM w praktyce na STM32F4

Skoro wiemy już jak wygląda i jakimi parametrami cechuje się sygnał PWM, czas wykorzystać możliwości naszego mikrokontrolera i wygenerować taki sygnał w praktyce. Za realizację takich zadań jak generowanie sygnału PWM odpowiedzialne są timery. Skonfigurujemy teraz jeden z nich, tak aby móc regulować jasność świecenia diody LED na płytce Discovery.

Konfiguracja w Cube

Krok 1. Tworzymy nowy projekt i konfigurujemy główny zegar mikrokontrolera na maksymalną wartość (100MHz), tak jak robiliśmy to w jednym z poprzednich artykułów.

Krok 2. Klikamy w wyprowadzenie, na którym znajduje się czerwona dioda (PD14) i sprawdzamy, czy da się na tym pinie wyprowadzić kanał jednego z timerów.

KursF4_8_3

Konf. wyprowadzenia.

Okazuje się, że tak. Funkcja TIM4_CH3 oznacza kanał 3 timera 4. Po wybraniu odpowiedniej funkcji zauważamy, że konfigurowane wyprowadzenie zamiast na zielono, podświetliło się na żółto.

KursF4_8_4

Efekt wybrania opcji.

Jest to spowodowane tym, że kanał ten timera może pełnić wiele funkcji i użytkownik musi sprecyzować, o którą konkretnie mu chodzi.

Krok 3. Rozwijamy panel timera 4 z lewej strony i konfigurujemy kanał 3 tak, aby generował sygnał PWM. W tym miejscu musimy również sprecyzować, że timer będzie taktowany z wewnętrznej magistrali (Clock Source: Internal Clock).

KursF4_8_5

Odpowiednia konfiguracja licznika.

Krok 4. Przechodzimy do panelu konfiguracyjnego timera 4.

Konfiguracja 4 timera (licznika).

Konfiguracja 4 timera (licznika).

Pierwsze 4 parametry sekcji Counter Settings poznaliśmy już w artykule o zegarach, w którym konfigurowaliśmy prosty timer generujący przerwania cykliczne. Zarówno funkcja prescalera jak i rejestru CKD (Internal Clock Division) nie zmienia się w porównaniu do tamtego zastosowania.

Wypełnienie impulsu ustawiamy w rejestrze CCR. Rejestr ten może przyjąć minimalną wartość 0, a maksymalną ARR+1 (w rzeczywistości możemy tam wpisać dużo większe wartości, ale będzie to działać tak samo jak przy wpisaniu wartość ARR+1).

Przykład:

Wartość rejestru ARR wynosi 3. Oznacza to, że mamy dostępnych tylko 5 poziomów wypełnienia impulsu. Trzy poziomy pośrednie plus dwa dodatkowe - zerowy i maksymalny:

  • CCR = 0, duty = 0%
  • CCR = 1, duty = 25%
  • CCR = 2, duty = 50%
  • CCR = 3, duty = 75%
  • CCR = ARR+1= 4, duty = 100%

Spróbujmy zatem ustawić timer w taki sposób, aby generował sygnał PWM z częstotliwością 100Hz i z rozdzielczością 1%, czyli w zakresie 0-100. Przypominamy sobie wzór na częstotliwość generowania przerwania, który jest taki sam jak wzór na częstotliwość sygnału PWM (czyli na ilość okresów w jednej sekundzie):

FREQ = TIM_CLK/(ARR+1)(PSC+1)(CKD+1)

Znamy naszą częstotliwość (FREQ) oraz rozdzielczość (ARR+1), z jaką chcemy zadawać wypełnienie PWM. Zaglądając na początek artykułu o zegarach dowiemy się, że timer 4 znajduje się na magistrali APB1, a więc będzie taktowany częstotliwością 50MHz (TIM_CLK). Do ustalenia zostają nam więc tylko dwa rejestry (PSC i CKD).

Rozdzielczość: ARR+1 = 100, czyli: ARR = 99, co daje 99 poziomów pośrednich (1-99). Poza tym dostępne będą dla nas jeszcze poziom zerowy (0) i maksymalny (100).

Prescaler i CKD:

FREQ = TIM_CLK/(ARR+1)(PSC+1)(CKD+1)

100 = 50000000/(100)(PSC+1)(CKD+1)

(PSC+1)(CKD+1) = 5000

Przyjmujemy że CKD = 0 (No division):

PSC+1 = 5000

PSC = 4999

Tak wyliczonymi wartościami parametryzujemy nasz timer:

Parametry timera generującego sygnał PWM.

Parametry timera generującego sygnał PWM.

Drugą interesującą dla nas sekcją będzie ta dotycząca konkretnego kanału:

Parametry kanału timera generującego sygnał PWM.

Parametry kanału timera generującego sygnał PWM.

  • Mode - w trybie PWM Mode 1 pin wyjściowy timera utrzymywany jest w stanie wysokim, aż wartość licznika timera nie osiągnie wartości wpisanej do rejestru CCR, który przechowuje wpisaną przez nas wartość wypełnienia. Pin jest ustawiony w stan niski aż do momentu, gdy licznik się zresetuje (przekroczy wartość rejestru ARR+1). W tym trybie im większa wartość rejestru CCR, tym większe wypełnienie sygnału. Tryb PWM Mode 2 działa dokładnie na odwrót. Pin zaczyna ze stanu niskiego i dopiero przy zrównaniu się wartości licznika z wartością w rejestrze CCR, ustawiany jest w stan wysoki. W tym trybie zwiększanie wartości rejestru CCR zmniejsza wypełnienie sygnału. Zostawiamy domyślny PWM Mode 1.
  • Pulse - początkowa wartość wypełnienia wpisana do rejestru CCR. Zostawiamy domyślne 0.
  • Fast Mode - przydatny tylko w trybie timera One Pulse Mode, z którego nie będziemy tu korzystać. Zostawiamy domyślne Disable.
  • CH Polarity - zmiana polaryzacji pinu wyjściowego. Efekt działania jest dokładnie taki sam, jak zmiana z PWM Mode 1 na PWM Mode 2. Zostawiamy domyślne High.

Krok 5. Generujemy projekt pod nazwą 05_PWM.

Uruchomienie timera generującego sygnał PWM 

Krok 1. Jak zwykle, odszukujemy funkcji, która pozwoli nam uruchomić timer w odpowiednim trybie. Domyślamy się, że musimy zacząć szukać od HAL_TIM_PWM. Ponieważ na razie nie będziemy korzystać z przerwań ani z DMA, uruchamiamy timer:

Pierwszym parametrem jest oczywiście wskaźnik na strukturę konfiguracyjną, wskazującą który timer chcemy uruchomić. Drugi parametr precyzuje kanał, który ma zostać włączony.


Aby dowiedzieć się jakie mamy możliwości, wystarczy najechać kursorem myszki nad nazwę funkcji żeby wyświetlić krótką dokumentację.

KursF4_8_9

Podgląd skróconej dokumentacji funkcji.

Finalnie nasze wywołanie będzie miało następującą postać.

Krok 2. Następnie musimy wstawić żądaną wartość wypełnienia w odpowiednie miejsce. Takim miejscem jest wspomniany wcześniej TIM Capture/Compare register, czyli rejestr CCR. Rejestr ten jest składową głównej struktury timera, więc dostęp do niego będzie wyglądał następująco.

Krok 3. Aby zaobserwować zmiany w jasności świecenia diody, uzależnimy zmiany wypełnienia sygnału PWM od czasu.

Zadanie: Chcielibyśmy, aby jasność diody narastała przez 4 sekundy w zakresie 0 - ARR+1 (wartość maksymalna). Następnie jasność ma maleć przez kolejne 4 sekundy i tak w kółko.

Jedna z możliwości implementacji:
Wykorzystamy do tego domyślne przerwanie generowane przez SysTick, które wywoływane jest z częstotliwością 1KHz. Wiemy zatem, że w ciągu 4 sekund przerwanie wywoła się 4000 razy. Ponieważ w tym czasie musimy zinkrementować wartość wypełnienia 100 razy, oznacza to że inkrementacja powinna nastąpić co 4000/100 = 40 przerwań.

Kod realizujący to zadanie przedstawia poniższy listing.

Po wgraniu powyższego programu czerwona dioda na płytce Discovery powinna przez 4 sekundy świecić coraz mocniej, a następnie przez 4 sekundy coraz słabiej.

Efekt działania programu (powolne przygaszanie diody).

Efekt działania programu (powolne przygaszanie diody).

Generowanie sygnału PWM z wykorzystaniem DMA

Aby nie musieć ręcznie wpisywać wartości wypełnienia do rejestru, możemy wykorzystać DMA do przesyłania danych z naszej zmiennej. Nie jest to może jakoś bardzo przydatne, bo tak na prawdę możemy obliczać nowe wypełnienia bezpośrednio w rejestrze. Jest to jednak bardzo proste do zrobienia, więc pozwolę sobie na opisanie tego przykładu.

Konfiguracja Cube

W panelu konfiguracyjnym timera 4 dodajemy transmisję DMA do obsługi kanału 3. Domyślnie kierunek przesyłanych danych będzie ustawiony na Peripheral to Memory. My natomiast chcemy przesyłać dane z pamięci do timera, dlatego musimy zmienić ten parametr na Memory to Peripheral.

Mode zmieniamy na circular, ponieważ chcemy żeby transmisja danych odbywała się cały czas. Resztę opcji zostawiamy domyślnie.

Konfiguracja kanału DMA dla timera generującego sygnał PWM

Konfiguracja kanału DMA dla timera generującego sygnał PWM.

Uruchomienia timera generującego sygnał PWM z wykorzystaniem DMA

Po wygenerowaniu kodu musimy dokonać kilku drobnych modyfikacji. Zmienna przechowująca zadaną wartość wypełnienia musi zostać wyciągnięta na zewnątrz przerwania, ponieważ musimy mieć do niej dostęp z funkcji main.

Poza usunięciem zmiennej Duty, w obsłudze przerwania zmieni się tylko tyle, że teraz przy każdym jego wywołaniu nie będzięmy odwoływać się jawnie do rejestru CCR3 timera 4.

Ostatnią modyfikacją będzie uruchomienie timera w trybie wykorzystującym DMA:

Dochodzą tu 2 znane już z poprzednich odcinków parametry. Adres obszaru pamięci, z którego mają być pobierane dane oraz ich liczba danych do przesłania. Po wgraniu nowego programu na mikrokontroler wszystko powinno działać bez zmian.

Odczyt sygnału kwadraturowego z enkodera

Kolejnym bardzo często wykorzystywanym elementem w robotyce i nie tylko jest enkoder obrotowy. Jest to urządzenie generujące impulsy odpowiadające ruchowi obrotowemu. Enkodery stosuje się najczęściej, aby zmierzyć położenie kątowe wału silnika. Innym zastosowaniem może być np. pokrętło we wzmacniaczach audio.

Zasada działania enkodera

Standardowy enkoder obrotowy ma dwa wyjścia (kanały), na których generowany jest sygnał prostokątny. Sygnały są przesunięte względem siebie w fazie, dzięki czemu jest możliwe wykrycie kierunku obrotu. Przykład takiego sygnału przedstawiono na poniższym rysunku:

Przykład sygnału z enkodera obrotowego.

Przykład sygnału z enkodera obrotowego.

W pierwszej sytuacji sygnał na linii A wyprzedza w fazie sygnał na linii B, co jest tu interpretowane jako obrót zgodnie z ruchem wskazówek zegara (CW-Clockwise). W przeciwnej sytuacji, przedstawionej w niższej części rysunku, to sygnał B wyprzedza sygnał A, co oznacza obrót w drugą stronę (CCW-Counterclockwise).

Podstawowym parametrem każdego enkodera jest liczba impulsów na obrót. Jeżeli enkoder ma 10 impulsów na obrót, oznacza to, że jesteśmy w stanie zmierzyć położenie kątowe z dokładnością 360°/10 = 36° (więc rozdzielczość enkodera wynosi 36°)

Timer zlicza impulsy w rejestrze CNT i w zależności od kierunku obrotu, inkrementuje lub dekrementuje wartość tego rejestru. Spróbujmy zaimplementować taki odczyt w praktyce.

Konfiguracja Cube

Krok 1. Tworzymy nowy projekt w Cube.
Krok 2. Konfigurujemy timer 1 w trybie Encoder Mode.

Konfiguracja timera w trybie do dekodowania sygnałów z enkodera obrotowego

Konfiguracja timera w trybie do dekodowania sygnałów z enkodera obrotowego.

Zauważmy, że tryb Encoder Mode wykorzystuje dwa kanały timera. Ze względu na swoją specyfikę działania, blokuje również możliwość konfiguracji niektórych parametrów.

Krok 3. Przechodzimy do panelu konfiguracyjnego timera 1.

Panel konfiguracyjny timera w trybie Encoder Mode

Panel konfiguracyjny timera w trybie Encoder Mode.

W tym zastosowaniu timera nie generujemy żadnego sygnału, ani nie korzystamy w żaden sposób z taktowania podawanego na ten timer. Z tego powodu jedyny parametr jaki będzie nas interesował w sekcji Counter settings, to Counter Period (rejestr ARR).

Wpisana tam liczba będzie maksymalną wartością, którą może osiągnąć rejestr zliczający impulsy (CNT). Po przekroczeniu tej wartości rejestr zachowa się jak zwyczajna zmienna bez znaku, czyli zostanie zresetowany i zacznie liczyć od zera.

Wartość tę ustawiamy na 403. Dlaczego? Zaraz się  okaże.

  • Encoder Mode - określa na jakiej podstawie zliczane są impulsy w liczniku:
    • Encoder Mode TI1 - przy każdej zmianie poziomu sygnału na kanale drugim licznik jest inkrementowany lub dekrementowany w zależności od stanu na kanale pierwszym.
    • Encoder Mode TI1 - przy każdej zmianie poziomu sygnału na kanale pierwszym licznik jest inkrementowany lub dekrementowany, w zależności od stanu na kanale drugim.
    • Encoder Mode TI1 and TI2 - przy zmianie stanu dowolnego z kanałów następuje inkrementacja lub dekrementacja licznika. Ten tryb daje dwukrotnie więcej informacji niż poprzednie tryby. Ustawiamy ten tryb.
  • Polarity - definiuje na które zbocze będzie reagować timer. Pozostawiamy domyślnie Rising Edge.
  • IC Selection - Parametr który zupełnie nas nie interesuje, i którego nie możemy zmienić.
  • Prescaler Division Ration - Jak wyżej, ten parametr nie ma wpływu na działanie timera w trybie Encoder. Pozostawiamy domyślnie No division.
  • Input Filter - określa czas trwania próbkowania sygnału wejściowego podczas jego zmiany w cyklach zegara. Ustawiamy wartość maksymalną, a więc 15.

Na koniec konfiguracja naszego timera powinna wyglądać następująco.

Konfiguracja timera w trybie Encoder Mode.

Konfiguracja timera w trybie Encoder Mode.

Podłączenie enkodera do płytki Discovery

Enkoder znajdujący się na wyposażeniu zestawu posiada 5 wyprowadzeń:

Moduł enkodera z dedykowanym pokrętłem.

Moduł enkodera z dedykowanym pokrętłem.

SIA i SIB to kanały enkodera, które generują sygnał kwadraturowy. Należy je podłączyć pod wejścia naszego timera. SW, to wyprowadzenie przycisku znajdującego się w wale obrotowym enkodera. Ostatnie dwa wyprowadzenia to oczywiście zasilanie, które może być z zakresu 3-5.3V

Finalnie podłączenie będzie wyglądać następująco:

  • SIA - PE9
  • SIB - PE11
  • SW - nie podłączony
  • GND - GND
  • VCC - 3V
Podłączenie enkodera do zestawu Discovery.

Podłączenie enkodera do zestawu Discovery.

Uruchomienie timera dekodującego sygnał z enkoderów

Po wygenerowaniu kodu musimy uruchomić timer i odczytywać wartość licznika

Krok 1. Pierwszą rzeczą, której będziemy potrzebować jest zmienna do wpisywania aktualnego stanu licznika.

Zmienne są globalne, ponieważ będziemy je podglądać w STMStudio. Dlaczego są zadeklarowane jako volatile, okaże się już niedługo.

Krok 2. Następnie musimy uruchomić timer w trybie Encoder.

Krok 3. Ostatnią rzeczą, którą musimy zrobić jest przepisanie wartości licznika timera do zmiennej. Jak już wcześniej wspomniałem, timer zlicza impulsy w rejestrze CNT. Przepisanie będzie miało w takim razie następującą postać.

W tym właśnie miejscu pojawi się problem, jeżeli zmienna positions nie będzie zadeklarowana ze słowem kluczowym volatile.

Rozwiązania tej sytuacji są co najmniej dwa:

  1. Użyć słowa kluczowego volatile, które zapobiega wprowadzaniu optymalizacji.
  2. Wyłączyć optymalizację kodu.

Ze względu na drastyczność drugiego rozwiązania zalecam pozostanie przy pierwszym. Jeżeli ktoś jednak chciałby to przetestować, parametry optymalizacji można znaleźć w opcjach projektu, jak na poniższym obrazku.

Opcje optymalizacji kompilatora

Opcje optymalizacji kompilatora.

Aby zupełnie wyłączyć optymalizację, trzeba zmienić Optimization Level na None.

Krok 4. Kompilujemy projekt, wgrywamy go na płytkę, otwieramy STMStudio i importujemy zmienne pulse_count i positions do podglądu. Jeżeli nie pamiętasz jak się to robiło, warto wrócić do artykułu o STMStudio.

STMStudio z dodanymi zmiennymi pulse_count i positions

STMStudio z dodanymi zmiennymi pulse_count i positions

Po połączeniu się przez STMStudio możemy zacząć przeprowadzać testy. Zauważmy, że enkoder ma swoje "stabilne pozycje", które znajdują się średnio co 18° stopni (20 "pozycji" na obrót).

Dlaczego 4, a nie 1? Ponieważ podczas przejścia z jednej "stabilnej" pozycji do drugiej generowany jest pełen cykl sygnału kwadraturowego na kanałach SIA oraz SIB. W jednym takim cyklu występują cztery zbocza, a nasz timer skonfigurowany jest na reagowanie na każde z nich.

Cykl sygnału kwadraturowego z zaznaczonymi zboczami.

Cykl sygnału kwadraturowego z zaznaczonymi zboczami.

Można to sprawdzić przechodząc bardzo wolno z jednej stabilnej pozycji do drugiej, cały czas obserwując co się dzieje ze zmienną pulse_count w STMStudio.

Skąd natomiast wzięła się wartość 403 w rejestrze ARR? Jak już powiedzieliśmy, w naszym rozwiązaniu impulsy między pozycjami zliczane są co 4. W takim razie przekręcając enkoder o 100 pozycji (1 obrót), otrzymamy dokładnie 400 impulsów.

Chcielibyśmy, żeby sto pierwsze przekręcenie wyzerowało licznik. Aby to osiągnąć, należy ustawić wartość ARR właśnie na 403, ponieważ ostatni impuls nowej pozycji, a więc 404, przekroczy wartość ARR i  wyzeruje licznik. Zmienna positions zawiera, dzięki temu, liczbę stabilnych pozycji, o które przekręciliśmy enkoder.

Podgląd zmiennych w STMStudio.

Podgląd zmiennych w STMStudio.

Enkoder vs. potencjometr

Część z was pewnie zastanawia się: czy nie łatwiej użyć zamiast tego użyć potencjometru?

Ponieważ potencjometr działa na zupełnie innej zasadzie niż enkoder, występuje między nimi kilka fundamentalnych różnic, które decydują o ich przydatności w danym zastosowaniu.

Najważniejsze z nich to:

  • Standardowe potencjometry obracają się tylko w pewnym zakresie (np. 0-270°). Enkoder nie ma takiego ograniczenia.
  • Sygnał z potencjometru jest analogowy, a z enkodera cyfrowy. Sygnał analogowy może zostać łatwo zakłócony i obciążony błędem pomiarowym. 
  • Przy ponownym uruchomieniu systemu, potencjometr daje bezwzględną wartość pozycji. Enkoder natomiast nie posiada pamięci, więc wysyła informacje tylko o następującej rotacji, bez informacji o całkowitym "przekręceniu". 

Z drugiej strony, operując ramieniem robota bez jakichkolwiek innych czujników, bezpieczniejszym rozwiązaniem jest potencjometr. Używając tylko enkodera, przy ewentualnej utracie zasilania nie jesteśmy w stanie stwierdzić w jakiej pozycji znajduje się obecnie nasze ramie.

Odczytywanie sygnału PWM

Ostatnią przydatną funkcją timera, którą zajmiemy się w dzisiejszym artykule będzie odczytanie parametrów sygnału PWM. Znajduje to zastosowanie między innymi przy współpracy z modelarską aparaturą radiową, gdzie informacje przesyłane są właśnie za pomocą sygnału PWM.

W tym przykładzie posłużymy się całą zdobytą do tej pory w artykule wiedzą.

Krok 1. Tworzymy nowy projekt w Cube.
Krok 2. Konfigurujemy timer 4 aby generował sygnał PWM tak jak na początku tego artykułu.
Krok 3. Konfigurujemy timer 1 aby odczytywał sygnał kwadraturowy z enkodera.
Krok 4. Konfigurujemy timer 2 w trybie PWM Input na kanale 1.

Konfiguracja timera w trybie PWM Input.

Konfiguracja timera w trybie PWM Input.

Zauważmy, że w sekcji konfiguracji zajęte zostały dwa kanały, podczas gdy wyprowadzenie tylko jedno (PA0). Jest to spowodowane tym, że ten tryb korzysta z dwóch kanałów, ale są one wewnętrznie połączone, stąd wystarczy tylko jedno wyprowadzenie.

Krok 5. Konfigurujemy zegar główny na 100MHz.
Krok 6. Przechodzimy do panelu konfiguracyjnego timera 2.

Panel konfiguracyjny timera 2 skonfigurowanego w trybie PWM Input

Panel konfiguracyjny timera 2 skonfigurowanego w trybie PWM Input.

Musimy ustawić nasz zegar w taki sposób, aby jego okres był co najmniej takiej samej długości (najlepiej dłuższy) jak okres sygnału, który zamierzamy odczytywać. W naszym wypadku nie ma tego problemu, ponieważ znamy dokładnie częstotliwość odczytywanego sygnału PWM, która wynosi 100Hz.

Przyjmijmy w takim razie, że chcemy, aby okres naszego timera był dwukrotnie dłuższy, a więc żeby trwał 20 milisekund. Ponownie używając przytoczonego przed chwilą wzoru wiemy, że daje nam to częstotliwość 50Hz.

Rejestr ARR po raz kolejny oznacza rozdzielczość, z jaką będzie mierzony czas w odczytywanym sygnale. Przyjmijmy tę wartość na 1999 (rozdzielczość 2000). Dlaczego akurat tyle? Spodziewamy się, że okres sygnału PWM będzie zajmował dokładnie połowę czasu zliczania, dlatego na jego mierzenie pozostanie połowa ustawionej rozdzielczości. 1000 cykli (dokładność 0.1%) wydaje się wystarczające na mierzenie sygnału PWM, który po stronie nadawczej ustawiany jest z dokładnością do 1%.

Z takim zestawem danych możemy przystąpić do wyliczenia odpowiednich wartości. Przypomnijmy najważniejszy wzór występujący w timerach, a więc:

FREQ = TIM_CLK/(ARR+1)(PSC+1)(CKD+1)

Określiliśmy, że ARR = 1999, a FREQ = 50. Podstawiając wartości do wzoru otrzymujemy:

50 = 50000000/(1999+1)(PSC+1)(CKD+1)

(PSC+1)(CKD+1) = 500

Jak zwykle przyjmujemy, że CKD = 0.

PSC = 499

Konfigurujemy timer wyliczonymi wartościami.

Timer 2 skonfigurowany w trybie Input PWM na częstotliwość 50Hz.

Timer 2 skonfigurowany w trybie Input PWM na częstotliwość 50Hz.

Całą resztę parametrów pozostawiamy bez zmian.

Krok 7. Konfigurujemy timer generujący sygnał PWM tak jak w przykładzie na początku artykułu. Timer odczytujący sygnał z enkodera konfigurujemy tak samo jak w przykładzie z enkoderem. Generujemy projekt pod nazwą 05_PWM_IN.

Uruchomienie timera w trybie Input PWM

Naszym zadaniem będzie napisanie krótkiego programu, który będzie generował sygnał PWM o wypełnieniu ustawianym za pomocą enkodera kwadraturowego. Następnie za pomocą timera skonfigurowanego w trybie PWM Input trzeba będzie odczytać parametry sygnału PWM.

Krok 1. Będziemy potrzebować zmiennych do operowania na odczytanych danych.

Krok 2. Następnie musimy uruchomić wszystkie timery. W przypadku tego odpowiedzialnego za odczytywanie sygnału PWM, będziemy korzystać z funkcji trybu Input Capture.

Musimy uruchomić odczytywanie sygnału PWM na dwóch kanałach, ponieważ jeden z nich odpowiedzialny będzie za okres sygnału, a drugi za jego wypełnienie.

Krok 3. Po uruchomieniu timerów musimy odczytać wartości wejściowego sygnału PWM. Służy do tego funkcja z poniższego listingu.

Wiedząc, że odczyt kanału pierwszego da nam informację o długości trwania całego okresu, a kanału drugiego o długości trwania sygnału wysokiego, pętla nieskończona będzie wyglądać następująco.

Wadą tego rozwiązania jest to, że tryb Input PWM nie da nam informacji o wypełnieniu równym 0 lub 100%. Wynika to z tego, że timer reaguje na zmiany sygnału. Przy sygnale stałym, jakim jest PWM z wypełnieniem 0 lub 100, zmiany nie występują, więc timer ich nie "łapie" i nie wpisuje nic nowego do rejestru. Oczywiście da się to ominąć, ale wymaga to dodania kilku elementów do systemu.

Zapewne zastanawiacie się również dlaczego dodałem liczbę 1 do każego z odczytów. Można potraktować to jako zadanie domowe, żeby poczytać nieco o tym jak działają timery!

Podsumowanie

W tym odcinku nauczyliśmy się korzystać z bardziej zaawansowanych funkcjonalności timerów, które często przydają się w zastosowaniach robotycznych. Wiemy jak sterować jasnością diody, czy prędkością silnika za pomocą sygnału PWM. Umiemy już odczytać położenie kątowe wału silnika lub gałki enkodera obrotowego. Nauczyliśmy się także odczytywać parametry sygnału PWM, pochodzącego np. z modelarskiej aparatury radiowej.

W następnym odcinku poznamy interfejs I2C służący do komunikacji pomiędzy układami scalonymi. Przy okazji nauczymy się korzystać z umieszczonego na płytce akcelerometru.

Nawigacja kursu

To tyle na dziś Jeżeli macie jakieś pytania, sugestie, problemy - śmiało piszcie w komentarzach.

Autor kursu: Bartek (Popeye) Kurosz
Redakcja: Damian (Treker) Szymański

Załączniki

TIMER_ADVANCED (zip, 11 MB)

Projekty z artykułu.

enkodery, kursSTM32F4, licznik, PWM, stm32, timer

Trwa ładowanie komentarzy...