Kurs STM32L4 – #16 – zdalne sterowanie IR, NEC (liczniki)

Kurs STM32L4 – #16 – zdalne sterowanie IR, NEC (liczniki)

Jeszcze długo w wielu zastosowaniach nic nie zastąpi pilota IR, który po prostu zawsze działa. Dlatego tym razem wykorzystamy niezastąpione liczniki, aby dekodować transmisję NEC.

Nie pójdziemy na łatwiznę, więc bibliotekę do tej komunikacji stworzymy samodzielnie (od zera).

Czego dowiesz się z tej części kursu STM32L4?

Podczas wykonywania ćwiczeń z tej części kursu dowiesz się, jak samodzielnie napisać niskopoziomową bibliotekę do obsługi popularnego pilota IR, który pracuje w standardzie NEC. Zaczniemy od analizy tego, jak wygląda komunikacja, przejdziemy przez proste testy, a skończymy na budowie własnej biblioteki, dzięki której za pomocą sprzętowych liczników będziemy rozpoznawać komendy nadawane przez pilota. Będzie to też dobra okazja do poznania kolejnych sztuczek z wykorzystaniem debuggera.

Zdalna komunikacja za pomocą IR

Jak działa pilot, wie chyba każdy, jednak nie zawsze zmieniając kanały w telewizji, zastanawiamy się nad szczegółami technicznymi takiego sterowania. Warto więc na początku uzmysłowić sobie, że nie każdy pilot będzie działał z każdym odbiornikiem. Wszystko zależy od częstotliwości sygnału oraz standardu wykorzystywanego przez dane urządzenie.

Różne częstotliwości, standardy komunikacji oraz adresy urządzeń to główne wyróżniki pilotów IR

Różne częstotliwości, standardy komunikacji oraz adresy urządzeń to główne wyróżniki pilotów IR

Standardów (jak to zwykle bywa) jest wiele. Do najpopularniejszych na pewno zaliczają się RC5 oraz NEC. Ten drugi jest szczególnie popularny z racji dużej dostępności małych pilotów – takich lub podobnych jak te, które znajdują się w zestawie do tego kursu. Dzięki temu można dobrać odpowiedniego pilota dla naszego urządzenia, bez konieczności budowania swojego.

Pilot IR

Punktem wyjściowym jest pilot. Warto więc od razu sprawdzić, czy działa poprawnie. Jak? Wystarczy włożyć do niego baterię (lub wyciągnąć plastikową osłonkę, która blokuje przepływ prądu), spojrzeć na diodę IR przez cyfrowy aparat fotograficzny (np. w telefonie) i sprawdzić, czy dioda błyska po naciśnięciu dowolnego przycisku. Zależnie od aktualnych warunków i ułożenia pilota powinniśmy zobaczyć, że dioda świeci na fioletowo – najlepiej test ten przeprowadzić w ciemności.

Niektóre aparaty mogą posiadać filtr, ale zawsze warto chociaż spróbować, bo taka sztuczka bywa bardzo przydatna podczas testowania pilotów podczerwieni. Jeśli świecenie diody nie będzie widoczne, można również wypróbować przedni aparat telefonu, bo producenci często w ramach oszczędności dodają filtr IR tylko do tylnego aparatu.

Działanie pilota IR widoczne na zdjęciu zrobionym w półmroku aparatem cyfrowym

Działanie pilota IR widoczne na zdjęciu zrobionym w półmroku aparatem cyfrowym

Protokół NEC

Nasz pilot przesyła dane w standardzie NEC, wypada zatem zacząć od omówienia używanego protokołu. Bardzo dobry opis znaleźć można na tej stronie. Dla formalności omówimy jednak to, co najważniejsze. Zacznijmy od tego, że informacja o wciśnięciu przycisku przesyłana jest raz, natomiast jeśli użytkownik przytrzymuje przycisk, to co 110 ms przesyłana jest krótka dodatkowa informacja.

Transmisja IR w standardzie NEC – komenda i sygnały o przytrzymaniu klawisza

Transmisja IR w standardzie NEC – komenda i sygnały o przytrzymaniu klawisza

To właśnie dlatego można zobaczyć miganie diody w aparacie cyfrowym – przesyłanie danych odbywa się o wiele za szybko, żebyśmy mogli zauważyć pojedyncze bity, ale sygnał o przytrzymaniu przycisku wysyłany co 110 ms jest już dla nas widoczny gołym okiem (tzn. przez aparat).

W celu dalszej analizy musimy „przybliżyć” wcześniejszy przebieg, aby skupić się na tym, jak dokładnie wygląda przesyłanie tzw. komendy.

Transmisja IR w standardzie NEC – komenda

Transmisja IR w standardzie NEC – komenda

Jak widać, na początku wysyłany jest blok trwający 9 ms, a następnie jest 4,5 ms przerwy. Później przesyłane są dane w postaci 8-bitowych bajtów – od najmniej do najbardziej znaczącego bitu.

Wciśnięcie przycisku powoduje przesłanie czterech bajtów. Są to kolejno: adres, zanegowany adres, komenda i zanegowana komenda. Nasz pilot zawsze jako adres przesyła zero, natomiast komendy to po prostu kody wciskanych klawiszy.

Z kolei wciśnięcie i przytrzymanie przycisku powoduje przesyłanie później co około 110 ms ramek o następującej postaci:

Transmisja IR w standardzie NEC – informacja o przytrzymaniu przycisku

Transmisja IR w standardzie NEC – informacja o przytrzymaniu przycisku

Na początku mamy blok trwający 9 ms, po którym następuje 2,25 ms przerwy oraz krótki (0,56 ms) sygnał. Informacja o kodzie przycisku nie jest przesyłana, więc odbiornik musi pamiętać, który przycisk został wciśnięty. Tak samo informacja o zwolnieniu przycisku nie jest przesyłana, po prostu brak danych oznacza jego zwolnienie.

Przesyłany sygnał jest modulowany, czyli np. na początku dioda nie świeci ciągle przez 9 ms, ale zamiast tego miga przez 9 ms z częstotliwością 38 kHz. Dzięki temu wpływ jasności otoczenia oraz zakłóceń na jakość komunikacji jest minimalny, a cały układ jest bardziej energooszczędny.

Dla lepszego zrozumienia tematu musimy zagłębić się w to jeszcze bardziej i zobaczyć, jak w standardzie NEC przesyłane są pojedyncze bity, bo „jedynka” wcale nie oznacza ciągłego świecenia diody, a „zero” nie jest reprezentowane przez brak świecenia.

Transmisja IR w standardzie NEC – kodowanie bitów

Transmisja IR w standardzie NEC – kodowanie bitów

Każdy bit rozpoczyna się trwającym 0,56 ms blokiem sygnału (modulowany stan wysoki), po którym następuje przerwa (brak sygnału) – to długość tej przerwy określa wartość konkretnego bitu. Jeśli przerwa trwa 0,56 ms, to przesyłany jest bit o wartości zero, z kolei przerwa trwająca 1,69 ms oznacza bit reprezentujący logiczną jedynkę.

Scalony odbiornik podczerwieni

Wiemy już, jak kodowane są dane wysyłane przez pilota, czas przystąpić do ich odbierania. Tutaj jest nam potrzebny scalony odbiornik podczerwieni – w zestawie do tego kursu znajduje się gotowy moduł. Producent deklaruje, że moduł może być zasilany napięciem od 2,7 V do 5,5 V i reaguje on na sygnał modulowany częstotliwością 38 kHz – czyli będzie to idealne rozwiązanie dla naszego projektu.

Przykładowy moduł ze scalonym odbiornikiem podczerwieni

Przykładowy moduł ze scalonym odbiornikiem podczerwieni

    Informacja o napięciu zasilania jest bardzo ważna – jak widzimy, możemy moduł zasilać z 3,3 V, więc tym razem unikniemy problemów z konwersjami napięć. Musimy jednak zagłębić się w temat bardziej, bo interesuje nas jeszcze specyfikacja samego odbiornika podczerwieni – w tym przypadku powinien to być układ VS1838B (lub jakiś jego zamiennik).

    W dokumentacji układu zobaczymy, że wymaga on kilku dodatkowych elementów, których nie ma na module. Jest duża szansa, że układ zadziała bez tych elementów (na 99%), ale jeśli nie, to przynajmniej będzie wiadomo, skąd biorą się problemy.

    Schemat podłączenia odbiornika VS1838B (fragment noty katalogowej)

    Schemat podłączenia odbiornika VS1838B (fragment noty katalogowej)

    Druga ważna sprawa do sprawdzenia w dokumentacji to opis sygnału na wyjściu odbiornika:

    Opis sygnałów na wyjściu odbiornika (fragment dokumentacji)

    Opis sygnałów na wyjściu odbiornika (fragment dokumentacji)

    Jak widzimy, odbiornik zapewnia dekodowanie sygnału, a na jego wyjściu pojawia się stan wysoki w przypadku braku transmisji danych, a niski – jeśli odbierane są dane z pilota. Jest to bardzo częsty sposób działania tego typu odbiorników, chociaż może się to wydawać nieintuicyjne.

    Wynika to z tego, że wyjście odbiornika jest typu otwarty-kolektor (bo jest tanie w realizacji i odporne na zwarcie z masą), więc stan wysoki na wyjściu odpowiada wyłączeniu tranzystora wyjściowego co redukuje pobór prądu, gdy układ nie odbiera żadnego sygnału.

    Schemat blokowy odbiornika VS1838B (fragment dokumentacji)

    Schemat blokowy odbiornika VS1838B (fragment dokumentacji)

    Warto też z ciekawości popatrzeć na schemat blokowy naszego odbiornika. Układ wygląda niepozornie i przypomina raptem obudowaną diodę, ale – jak widać – jest dosyć skomplikowany. Ważne jest też jego wyjście, czyli OUT – układ ma już rezystor podciągający, więc rezygnacja z rezystora 20 k na wyjściu nie powinna być problemem.

    Praktyczny test odbiornika VS1838B

    Część teoretyczna już właściwie za nami – możemy przystąpić do podłączania układu. W sieci znaleźć można przeróżne sposoby podłączenia tych modułów (z dodatkowymi elementami lub bez). Dodanie kondensatora 100 nF na pewno nie zaszkodzi i warto to zrobić.

    Schemat ideowy i montażowy odbiornika IR

    Schemat ideowy i montażowy odbiornika IR

    W trakcie pisania kursu nie zauważyliśmy konieczności podłączenia szeregowo rezystora – jeśli pojawią się problemy z działaniem układu, można podłączyć w to miejsce (zgodnie z dokumentacją odbiornika) rezystor 330 R lub dwa równolegle.

    Analiza odebranych danych

    Wiemy już, jak wygląda protokół NEC; możemy teraz zobaczyć, jak wyglądają dane wyjściowe naszego odbiornika. W tym celu przyda nam się analizator stanów logicznych lub oscyloskop. Osoby, które nie mają dostępu do takich urządzeń, zachęcamy do przeczytania tego fragmentu jako przykładu, jak można analizować działanie czujnika, do którego nie mamy odpowiedniej dokumentacji.

    Gdy włączymy zasilanie, możemy najpierw sprawdzić, jaki sygnał pojawia się na wyjściu, gdy nic nie jest przesyłane. W tym przypadku zgodnie z oczekiwaniem jest to stan wysoki, czyli napięcie 3,3 V.

    Sygnał na wyjściu odbiornika przy braku transmisji

    Sygnał na wyjściu odbiornika przy braku transmisji

    Znacznie ciekawiej robi się, gdy wciśniemy przycisk. Wtedy analizator wychwyci całkiem sporo:

    Sygnał na wyjściu odbiornika po wciśnięciu przycisku na pilocie

    Sygnał na wyjściu odbiornika po wciśnięciu przycisku na pilocie

    Na początku widać impuls trwający 9,143 ms, który odpowiada transmisji zmodulowanego sygnału. A zatem – jak widzimy – nasz odbiornik ma na wyjściu stan niski, gdy odbiera sygnał o częstotliwości 38 kHz, a stan wysoki, gdy takiego sygnału nie ma – podłączając się do wyjścia odbiornika, „nie widzimy” już tego, że sygnał wejściowy „miga” z częstotliwością 38 kHz (tym zajmuje się już odbiornik). Zobaczmy jeszcze nieco dokładniej, jak wygląda nasz sygnał. Po trwającym 9,143 ms stanie niskim mamy stan wysoki.

    Pomiar kolejnych wartości odebranych po naciśnięciu przycisku

    Pomiar kolejnych wartości odebranych po naciśnięciu przycisku

    Stan wysoki trwa 4,492 ms, w przybliżeniu jest to 4,5 ms, czyli tyle, ile powinna trwać przerwa po sygnale początkowym, a zatem wszystko się zgadza, bo tak jak już wspominaliśmy, wyjście odbiornika jest niejako zanegowane (odebranie 4,5 ms przerwy w sygnale jest sygnalizowane jako 4,5 ms stanu wysokiego). Idąc dalej, możemy przybliżyć widok, aby podejrzeć, jak wyglądają przesyłane dane.

    Zbliżenie na dane odebrane po naciśnięciu przycisku

    Zbliżenie na dane odebrane po naciśnięciu przycisku

    Jak widzimy, mamy tutaj stan niski, czyli transmisję zmodulowanego sygnału trwającą 623 µs. Jest to początek bitu, który powinien trwać około 560 µs – tolerancja jest ewidentnie niezbędna. Po stanie niskim widoczny jest stan wysoki, czyli przerwa w transmisji, której długość jest w przybliżeniu identyczna – to znaczy, że oglądamy przesyłanie bitu oznaczającego logiczne zero. Dla porównania od razu spoglądamy na transmisję bitu oznaczającego logiczną jedynkę:

    Sposób kodowania informacji na temat logicznej jedynki

    Sposób kodowania informacji na temat logicznej jedynki

    Podobnie jak poprzednio, najpierw widzimy stan niski o szerokości 622 µs, a po nim stan wysoki, który tym razem trwa 1631 µs (w widoku z analizatora widoczny jest okres sygnału, czyli suma obu stanów, która wynosi 2254 µs, więc wszystko jest tak, jak przewiduje standard). Na koniec możemy jeszcze zobaczyć, jak wygląda sygnał przytrzymania przycisku:

    Sposób kodowania informacji na temat przytrzymania przycisku

    Sposób kodowania informacji na temat przytrzymania przycisku

    Na początku widoczny jest stan niski przez 9 ms, a następnie wysoki przez 2,18 ms – to on właśnie przekazuje informację o przytrzymaniu przycisku. Sporo tych informacji, ale udało nam się podejrzeć, jak dokładnie działa kodowanie NEC. Wiemy, że pilot i odbiornik działają poprawnie. Taka analiza byłaby jeszcze ważniejsza, gdybyśmy dysponowali pilotem, który działałby w nieznanym standardzie.

    Dekodowanie NEC za pomocą STM32L4

    Wiemy już, jak wygląda komunikacja zgodnie ze standardem NEC. Wiemy także, jaki sygnał pojawia się na wyjściu naszego odbiornika. Czas przejść do najważniejszego, czyli do odbierania danych na STM32. Zaczynamy od projektu z STM32L476RG, który pracuje z częstotliwością 80 MHz. Uruchamiamy też debugger i USART2 w trybie asynchronicznym. Zaznaczamy również w opcjach projektu, że CubeMX ma wygenerować osobne pliki dla wszystkich modułów.

    Następnie dodajemy przekierowanie printf na UART – wystarczy dodanie pliku nagłówkowego:

    oraz kodu zbliżonego do poniższego:

    Jak widzieliśmy przed chwilą, informacje odbierane z pilota mają postać impulsów, których czas trwania chcemy zmierzyć. W części 8 kursu STM32L4 zobaczyliśmy, jak można wykorzystać liczniki do pomiaru czasu trwania impulsów – z tej samej opcji skorzystamy również tym razem.

    Przechodzimy do konfiguracji modułu TIM2 i wybieramy następujące opcje:

    • Slave Mode: Reset Mode
    • Trigger Source: TI1_ED
    • Clock Source: Internal Clock
    • Channel1: Input Capture direct mode
    • Następnie w parametrach:
      • Counter Settings > Prescaler: 79
      • Input Capture Channel 1 > Polarity Selection: Both Edges 

    Używamy tego samego trybu co w poprzednich częściach kursu. Ustawiamy dzielnik na 79, czyli nasz licznik będzie działał z częstotliwością 80 MHz / ( 79 + 1) = 1 MHz. Wybraliśmy również oba zbocza, bo chcemy mierzyć szerokość zarówno dodatnich, jak i ujemnych impulsów. Na koniec przechodzimy do zakładki NVIC Settings i włączamy przerwanie od licznika.

    Mierzyliśmy już wcześniej szerokość impulsów za pomocą STM32L4, więc wszystko powinno być jasne. Teraz tylko w ramach przypomnienia: aby uruchomić działanie modułu, korzystamy z dwóch funkcji:

    Po pomiarze zostanie zgłoszone przerwanie i wywołana funkcja HAL_TIM_IC_CaptureCallback, której pierwszą wersję kopiujemy wprost z części 8 kursu. Po zmianie używanego timera z htim3 na htim2 kod będzie wyglądał następująco:

    Zacznijmy od upewnienia się, że wszystko działa poprawnie, czyli kompilujemy program, uruchamiamy go pod kontrolę debuggera i wewnątrz funkcji HAL_TIM_IC_CaptureCallback wstawiamy breakpointa – konkretnie po tym, gdy odczytana wartość jest zapisywana do zmiennej captured_value. Program powinien działać do momentu, gdy naciśniemy przycisk na pilocie.

    Zatrzymanie programu po naciśnięciu przycisku na pilocie (wartość może być zupełnie inna)

    Zatrzymanie programu po naciśnięciu przycisku na pilocie (wartość może być zupełnie inna)

    Nasz program zgłasza przerwanie zarówno po wykryciu zbocza opadającego, jak i narastającego. A zatem jak tylko pojawił się sygnał na odbiorniku, otrzymaliśmy przerwanie. Niestety ten pierwszy pomiar to tak właściwie czas pomiędzy uruchomieniem układu a wciśnięciem przycisku, więc nie jest to wartość, którą chcemy mierzyć. Dane, które nas interesują, pojawią się podczas obsługi kolejnych przerwań, ale pierwsze zatrzymuje program, więc kolejnych już nie odbieramy.

    Nie pozostaje nam nic innego, jak tylko zmienić kod, tak aby najpierw zbierał do tablicy wyniki pewnej liczby pomiarów, a dopiero gdy będziemy mieli odpowiednio dużo danych, zatrzymamy program i wtedy zobaczymy, jak te dane wyglądają. Zmieńmy więc procedurę obsługi przerwania na następującą:

    Stała N_VAL określa, ile danych chcemy zbierać, zanim przejdziemy do debugowania kodu. Tablica o nazwie values służy nam do zbierania danych, a zmienna counter przechowuje informację o liczbie już zebranych danych. W procedurze obsługi przerwania sprawdzamy, czy wartość counter jest mniejsza niż N_VAL, a jeśli tak, to dopisujemy wynik, natomiast w przeciwnym przypadku wywołujemy ciekawe makro __BKPT z parametrem 0 (który jest ignorowany).

    Po uruchomieniu programu i naciśnięciu przycisku na pilocie powinniśmy zobaczyć efekt zbliżony do poniższego (oczywiście wartości mogą być inne).

    Wartości zmierzone przez licznik po wciśnięciu przycisku na pilocie

    Wartości zmierzone przez licznik po wciśnięciu przycisku na pilocie

    Jak widać, makro __BKPT spowodowało zatrzymanie programu, a tablica values przechowuje ostatnie N_VAL wyników pomiarów. Pierwszy wynik ma wartość spoza oczekiwanego zakresu, ale w kolejnych wartościach rozpoznamy czasy zgodne ze standardem NEC.

    Impuls o szerokości 9021 µs rozpoczyna odbieranie danych, po nim następuje przerwa o szerokości 4533 µs. Kolejne, krótkie impulsy (około 500–600 µs) to bity o wartości zero, w dalszej części powinny być widoczne na przemian impulsy krótkie i dłuższe (około 1650 µs), czyli logiczne jedynki.

    Dekodowanie impulsów

    Wiemy już, że nasze impulsy można podzielić na kilka kategorii:

    • impulsy krótkie, trwające około 560 µs – rozpoczynają bity, są też używane w kodowaniu 0,
    • impulsy długie, około 1650 µs – kodują bity 1,
    • impuls początkowy 9 ms,
    • następujący po nim impuls 4,5 ms,
    • przytrzymanie przycisku, trwające około 2,25 ms,
    • impulsy znacznie krótsze, od 560 µs, lub dłuższe, od 9 ms, można traktować jako błędne.

    Możemy więc zdefiniować typ wyliczeniowy, który będzie odpowiadał wszystkim rodzajom impulsów:

    Od razu tworzymy też funkcję, która na podstawie czasu trwania impulsu będzie określała jego typ. Przyjmujemy tu możliwie szeroką tolerancję, aby różnice w pomiarach nie powodowały błędów:

    Możemy w tej chwili wykorzystać nową funkcję podczas obsługi przerwania – zmieniamy typ danych, które trzymane są w tablicy, a podczas odbioru danych od razu wykorzystujemy funkcję calc_pulse. Teraz kod powinien wyglądać następująco:

    Po uruchomieniu i przetestowaniu tej wersji programu w debuggerze zobaczymy od razu tekstowe opisy odebranych sygnałów – dzięki tej sztuczce łatwiej będzie analizować zebrane dane.

    Podgląd odebranych danych w debuggerze

    Podgląd odebranych danych w debuggerze

    Teraz już widać, jak powinno przebiegać dekodowanie danych. Możemy jednak znacznie uprościć sobie zadanie – widać, że wartość PULSE_SHORT występuje zarówno w kodowaniu bitów o wartości 0, jak i tych, które reprezentują 1.

    Zgodnie ze standardem protokołu każdy bit ma na początku krótki impuls modulowanego sygnału. Odebranie zmodulowanego sygnału sprawia, że na wyjściu odbiornika pojawia się stan niski. Czas trwania stanu niskiego jest zawsze taki sam i moglibyśmy go pominąć, mierząc tylko czasy stanu wysokiego. Stan wysoki zaczyna się zboczem narastającym, a kończy opadającym, możemy więc zmienić konfigurację naszego timera i włączyć reakcję tylko na zbocze opadające.

    Zbliżenie na fragment transmisji przykładowych bitów

    Zbliżenie na fragment transmisji przykładowych bitów

    Wracamy do CubeMX, przechodzimy do modułu TIM2 i zmieniamy parametr Polarity Selection na Falling Edge. Zapisujemy projekt, generujemy nowy kod i zwiększamy liczbę próbek (N_VAL) na 64. Gdy teraz uruchomimy program, zobaczymy, że analiza danych będzie łatwiejsza. Na poniższym zrzucie zostały one dodatkowo ręcznie pokolorowane i połączone w konkretne grupy informacji.

    Analiza poszczególnych bitów transmisji

    Analiza poszczególnych bitów transmisji

    Wartości PULSE_ERROR pojawiają się między przesyłanymi danymi i nie musimy się nimi przejmować. Impuls startowy, trwający 9 ms, nie jest teraz widoczny, więc rozpoczęcie komunikacji widzimy jako impuls o szerokości 4,5 ms, czyli PULSE_4MS (zaznaczony na czerwono).

    Po nim przesyłany jest 8-bitowy adres urządzenia (zaznaczony na ciemnozielono). Impulsy krótkie oznaczają bity o wartości zero. Dalej przesyłany jest zanegowany adres (jasnozielony), dlatego kolejne 8 bitów to jedynki, które widzimy jako długie impulsy. Po adresie przesyłana jest 8-bitowa komenda (ciemnoniebieski). Podobnie jak w przypadku adresu, również komenda wysyłana jest dwa razy – druga kopia jest zanegowana (jasnoniebieski). Na koniec widoczne są też impulsy o szerokości 2 ms (fioletowe) – oznaczają one przytrzymanie poprzedniego klawisza.

    Uproszczone dekodowanie poleceń

    Możemy na razie pominąć rozpoznawanie tego, że użytkownik przytrzymał klawisz. Zaczynamy od podstawowej wersji programu, który będzie dekodował odebrane kody. Usuwamy kod testowy, czyli stałą N_VAL oraz zmienne values i counter. Następnie aktualizujemy kod obsługi przerwania:

    Podobnie jak poprzednio, wykorzystujemy funkcję calc_pulse do ustalenia typu impulsu, który został odebrany, ale teraz zamiast zapisywać wynik w tablicy values, wywołujemy funkcję process_pulse, której postać powinna być taka jak poniżej.

    Jak działa dekodowanie? Zmienna recevied_bits przechowuje liczbę odebranych bitów. Po odebraniu PULSE_SHORT przesuwamy zawartość zmiennej received_value o jeden w prawo. To sprawia, że na najwyższej pozycji pojawi się bit o wartości 0, co odpowiada dodaniu bitu zerowego do wyniku. Z kolei po odebraniu PULSE_LONG też przesuwamy received_value, ale dodatkowo ustawiamy najwyższy bit na 1 za pomocą alternatywy bitowej (takie działanie będzie dodawało bit o wartości 1).

    Doliczenie do 32 odebranych sygnałów będzie oznaczało odebranie całego kodu z pilota, który będzie dostępny w zmiennej received_value. Z kolei jeśli otrzymamy jakieś inne dane, to ustawiamy zmienną received_bits na 0, czyli resetujemy naszą procedurę odbiorczą.

    Przydatna będzie jeszcze funkcja odczytująca kod wciśniętego klawisza:

    Jeśli nie odebraliśmy jeszcze kompletu danych, to funkcja ir_read zwróci −1, co oznacza brak wciśnięcia klawisza. Jeśli odebraliśmy wszystkie dane, to funkcja zwróci kod przycisku, który jest dostępny jako bajt zaczynający od szesnastego bitu w zmiennej received_value. Na koniec licznik recevied_bits jest zerowany, dzięki czemu możliwe jest odebranie kodu kolejnego przycisku.

    Teraz możemy przejść do funkcji main i napisać prosty program testowy:

    Po uruchomieniu programu w terminalu powinny być wyświetlane kody klawiszy:

    Efekt działania uproszczonej wersji programu

    Efekt działania uproszczonej wersji programu

    Wykrywanie przytrzymania przycisków

    W wielu zastosowaniach zależy nam tylko na wykrywaniu wciśnięcia przycisku, ale jak już wiemy, nasz pilot przesyła też informację o przytrzymaniu danego klawisza. Warto więc dodać taką opcję do naszej funkcji. Wystarczy nieznacznie zmodyfikować funkcję process_pulse – musimy dodać tam obsługę sygnału PULSE_2MS. Dla większego porządku dodajemy od razu również obsługę PULSE_4MS.

    Odbiór sygnału PULSE_2MS jest dla nas jednoznaczny z informacją o przytrzymaniu klawisza – jednak musimy sami wiedzieć, jaki to był klawisz. Na szczęście, jeśli wcześniej odebraliśmy poprawne dane, to taka informacja jest zapisana w zmiennej received_value – wystarczy więc zmienić wartość zmiennej recevied_bits na 32, aby procedura ir_read zwróciła ponownie kod przycisku.

    Efekt działania programu, który rozpoznaje przytrzymanie klawisza

    Efekt działania programu, który rozpoznaje przytrzymanie klawisza

    Biblioteka do dekodowania NEC

    Cały kod działa już poprawnie, warto więc delikatnie go podrasować i zebrać w formie biblioteki, którą będzie można łatwo wykorzystać podczas tworzenia innych projektów. Zaczynamy od utworzenia pliku ir.h, którego zawartość powinna wyglądać następująco:

    Zostały tu dodane stałe opisujące kody przycisków dostępnych na pilocie. Dzięki temu będzie można łatwiej wykorzystać je w głównym programie. Trzeba jednak pamiętać, że różne wersje pilota mogą mieć dostępne różne klawisze, więc poszczególne kody mogą się czasami różnić.

    Wywołanie funkcji HAL_TIM_Base_Start oraz HAL_TIM_IC_Start_IT przeniesiemy do funkcji ir_init, tak aby w przyszłości nie musieć o nich pamiętać. Podobnie jak w poprzednich częściach kursu, zostawiamy procedurę obsługi przerwania, czyli HAL_TIM_IC_CaptureCallback, w pliku main.c, natomiast cały kod związany z faktyczną obsługą pilota umieścimy w funkcji ir_tim_interrupt.

    Ostatecznie plik ir.c może wyglądać następująco:

    Zmiany w programie głównym są minimalne i poza włączeniem nowego nagłówka ir.h ograniczają się do wywołania ir_init przed pętlą główną oraz zmiany funkcji obsługującej przerwania:

    Cały ten kod możemy teraz połączyć z informacjami, które zostały opisane w poprzedniej części kursu. Na koniec zbudujmy zatem prostą lampkę RGB, którą można sterować za pomocą pilota IR.

    Lampka RGB – WS2812B sterowane pilotem

    Zaczynamy od modyfikacji układu – podłączamy moduł diod WS2812B (dokładnie tak samo, jak zostało to opisane w poprzedniej części kursu). Trzeba tylko pamiętać, że konwerter i diody powinny być zasilane z 5 V. Odbiornik IR zasilamy standardowo z 3,3 V (bo może pracować z takim napięciem, a przy okazji obniżymy pobór mocy względem sytuacji, w której byłby on zasilany z 5V). W przypadku problemów z działaniem układu warto dodać tutaj jeszcze kondensatory elektrolityczne (np. 220 µF).

    Schemat ideowy i montażowy lampki RGB

    Schemat ideowy i montażowy lampki RGB

    Wracamy teraz do CubeMX i konfigurujemy timer TIM3, aby wykorzystać go do generowania sygnału dla diod RGB. Dla przypomnienia ustawiamy tu:

    • Clock Source: Internal Clock
    • Channel 1: PWM Generation CH1
    • Następnie w parametrach:
      • Counter Period: 99

      Dalej idziemy do zakładki DMA Settings i klikamy Add, wybieramy TIM3_CH1/TRIG i ustawienia:

      • Direction: Memory To Peripheral
      • Data Width: Peripheral - Half Word, Memory - Byte

      Po zapisaniu zmian i wygenerowaniu nowej wersji projektu przechodzimy do edycji kodu. Zaczynamy od dodania biblioteki obsługującej diody RGB (kopiujemy pliki utworzone podczas poprzedniej części kursu). Następnie w pliku main.c aktualizujemy listę plików nagłówkowych:

      A potem program testowy:

      Oczywiście potrzebna jest jeszcze funkcja obsługi przerwania (taka sama jak wcześniej – z wywołaniem funkcji ir_tim_interrupt. Jeśli wszystko przebiegło poprawnie, to naciśnięcie przycisków 1, 2 lub 3 powinno zmieniać kolor jednej diody na module. Z kolei przycisk ON/OFF będzie wyłączał diodę.

      Zadanie domowe

      1. Rozbuduj kod biblioteki IR, aby była sprawdzana poprawność odebranych danych (za pomocą weryfikacji tego, czy odebrano poprawne wartości opisujące zanegowany adres i komendę).
      2. Rozbuduj program lampki RGB, tak aby każde naciśnięcie klawisza 0 włączało dodatkową diodę na module (wszystkie włączone diody powinny świecić na ten sam kolor).
      3. Rozbuduj program lampki w taki sposób, aby za pomocą klawiszy 4 i 6 możliwe było zmniejszanie lub zwiększanie jasności aktualnie wybranego koloru (dla wszystkich włączonych diod).

      Podsumowanie – co warto zapamiętać?

      Oczywiście głównym zagadnieniem, które warto zapamiętać po lekturze tej części kursu, jest sposób na sprzętowe odbieranie i dekodowanie sygnału z pilotów IR. Najważniejszym celem tej części było jednak pokazanie tego, że liczniki sprzętowe to niezwykle uniwersalne mechanizmy, które mogą przydać się w wielu przeróżnych sytuacjach – wystarczy pomyśleć, jakim „koszmarem” byłoby dekodowanie sygnałów z pilota IR w sposób programowy (gdybyśmy dysponowali jedynie zwykłym cyfrowym wejściem).

      Czy wpis był pomocny? Oceń go:

      Średnia ocena 5 / 5. Głosów łącznie: 7

      Nikt jeszcze nie głosował, bądź pierwszy!

      Artykuł nie był pomocny? Jak możemy go poprawić? Wpisz swoje sugestie poniżej. Jeśli masz pytanie to zadaj je w komentarzu - ten formularz jest anonimowy, nie będziemy mogli Ci odpowiedzieć!

      W kolejnej części tego kursu zajmiemy się obsługą DS18B20, czyli popularnych czujników temperatury, które korzystają z protokołu 1-wire. Będzie to wymagało od nas stworzenia kolejnej biblioteki, a przy okazji sprawdzimy, jak w dość nietypowy sposób wykorzystać sprzętowy UART.

      Nawigacja kursu

      Główny autor kursu: Piotr Bugalski
      Współautor: Damian Szymański, ilustracje: Piotr Adamczyk
      Oficjalnym partnerem tego kursu jest firma STMicroelectronics
      Zakaz kopiowania treści kursów oraz grafik bez zgody FORBOT.pl

      IR, kursSTM32L4, nec, pilot, stm32l4, tsop

      Trwa ładowanie komentarzy...