piątek, 4 listopada 2022

STM32G0 + UART + moduł radiowy LORA firmy CDEBYTE

Sezon rowerowy dobiega końca czas zatem zmienić dyscyplinę sportu na bardziej stacjonarną. W lukach startowych mam przygotowane dwa zestawy modułów LORA E220-400T22D firmy CDEBYTE sterowane po UART. Moim zadaniem będzie uruchomienie transmisji UART w STM32G0 i rozmowa z modułem radiowym oraz komunikacja pomiędzy dwoma modułami radiowymi . Dla urozmaicenia program testowy napiszę w STM32CUBE IDE , oczywiście bez użycia HAL-a. Pokażę krok po kroku jak  będę dochodził do wyznaczonego celu .

Moja baza sprzętowa i programowa :

2 x Płytka developerska z STM32G071, mojej konstrukcji rzecz jasna :

2 x Moduły radiowe LORA E220-400T22D firmy CDEBYTE obsadzone w moją płytkę-konwerter ze złączem w standardzie Mikroelektroniki MikroBUS.

 

Programator/debugger J-Link EDU Mini - Przerwanie łańcuchów dostaw odbiło się na dostępności tego fajnego programatora, szkoda. Można oczywiście użyć STlinka. STM32CUBE IDE ogranicza nas w zasadzie do dwóch rodzin programatorów STLink i J-LINK.

STM32CUBE IDE  - do pisania i debugowania kodu.

Pierwszą czynnością jaką wykonam to utworzę szablon pustego projektu w  STM32CUBE IDE i usunę z niego HAL-a. Struktura projektu po usunięciu HAL-a i ścieżek do HAL , wygląda u mnie jak poniżej. Sama czynność usunięcia HAL-a z projektu jest operacją relatywnie prostą. 


Dodaję do projektu pliki konfiguracyjne sprzętu w których tworzę funkcję sprzętowej konfiguracji dla mojego MCU.


Piny jakich będę używał w MCU do UART to 

PB6 - TX  (UART1)

PA10 - RX. (UART1)

Zacznijmy zatem od konfiguracji zegarów. Na schemacie zegarów MCU patrzymy do jakiej szyny zegarowej podłączony jest UART1, widzimy , że do szyny APB :



Szukamy zatem w module RCC stosownego rejestru w którym włączymy zegar dla UART1:


Zapisy włączające zegary dla portów A,B,C i UART1 jak poniżej :


Teraz przechodzę do konfiguracji pinów , najpierw pomocnicze definicje w pliku pin_config.h dla LED1, LED2 znajdujących się na mojej płytce MikroBUS z modułem LORA , LED znajdującej się na płytce z MCU oraz piny funkcjonalne modułu LORA :  M0 , M1 , AUX



Konfiguruję piny w pliku pin_config.c . W tym  PB6 , PA10 dla UART1 i piny AUX, M0, M1 dla modułu LORA. Ważne aby piny M0 i M1 ustawić jak najszybciej w stan wysoki czym wymusimy wejście w tryb MODE3 (uśpienie/programowanie) modułu LORA.



Konfiguruję moduł UART1 w pliku uart_config.c, najprościej jak tylko się da bez wnikania w szczegóły i dodatkowe "wodotryski" modułu włączam FIFO i na razie działam bez przerwań ale  przerwania od UART1 w module NVIC włączam bo będę  korzystał z nich w późniejszym czasie:




Jeszcze wspomnę na wszelki wypadek, jeśli tworzymy jakiś nowy katalog z plikami .h to musimy go dodać do ścieżki we właściwościach projektu :


W pliku uart.c tworzę dwie podstawowe funkcje do wysyłania po UART , bajtu , stringa oraz jedną wyspecjalizowaną funkcję do wysyłania całych struktur z danymi. Ta funkcja jest szczególnie ciekawa bo umożliwia wysłanie po UART struktury dowolnego typu jako jednego ciągu danych.



W pliku main.c tworzę prosty kod do przetestowania działania UART1 . Działanie programu sprowadza się do wysłania co ok 0,1 s stringu "Test" po UART1. Dioda LED sygnalizuje nam czy się nie wieszamy na UART1 no i czy SysTick działa :).



Podłączam analizator stanów logicznych do pinu PB6 (UART TX) i upewniam się , że moduł UART1 wysyła stringa . Efekt jak poniżej :




Wszystko działa zgodnie z oczekiwaniem czyli mam pewność , że UART1   został poprawnie zainicjalizowany i działa. . Czas zająć się modułem LORA. Dokumentacja modułu jest prosta  i bardzo krótka znajdziemy ją na stronie producenta : E220-400T22D , w moim odczuciu producent nie przywiązywał do niej zbyt dużej uwagi , znalazłem tam przynajmniej dwa błędy , brak rozwinięcia podstawowych skrótów. Brakuje niektórych ważnych informacji np. przykładu jak zrealizować Broadcasting dla modułów skonfigurowanych na różnych kanałach.
 
Moduł jest "bezobsługowy", ma kilka podstawowych ustawień i na tym koniec. Nie ustawiamy parametrów toru radiowego z wyjątkiem mocy nadawanej i numeru kanału. Znajdziemy tutaj takie funkcjonalności jak WOR ( Work On Radio) czyli tor radiowy jest załączany cyklicznie na zdefiniowane okresy czasu po których się automatycznie wyłącza  , LBT (Listen Befor Talk) - monitoring toru radiowego pod kątem poziomu szumów przed wysłaniem danych, szyfrowanie transmisji, transmisja transparentna , automatyczny watchdog. W jednym pakiecie możemy przesłać maksymalnie 200 bajtów danych. Maksymalna moc nadawania deklarowana przez producenta to 22dBm , minimalna moc to 10 dBm

Na początek sprawdzę czy moduł odpowiada na przesyłane po UART dane konfiguracyjne w trybie Mode 3, tryb ten jest trybem uśpienia modułu i jednocześnie trybem w którym możemy zmieniać konfigurację modułu. Do zmiany trybów pracy służą piny M0 i M1, pin AUX sygnalizuje nam czy moduł jest czymś zajęty czy nie. Stan niski na AUX informuje nas, że moduł jest zajęty. Zmiana trybów pracy nie jest wtedy możliwa. Jeśli AUX ma stan wysoki , możemy wtedy zmieniać tryb pracy modułu, ale dopiero po pewnej zwłoce od pojawienia się stanu wysokiego na AUX. Producent zaleca aby przyjąć metodologię polegająca na wykryciu stanu wysokiego na AUX i po zwłoce 2 ms od tego wykrycia zmieniać tryb pracy. Dodatkową zwłokę 1 ms potrzebujemy na rozpoczęcie pracy w nowym trybie.

Do trybu Mode 3 wchodzimy po ustawieniu M0 i M1 w stan wysoki.

W pliku lora.h tworzę definicje i deklaracje, które będą pomocne przy rozmowie z modułem. W szczególności dokonuję opisu rejestrów modułu za pomocą struktur i unii tak aby zrobić wygodny dostęp do całych rejestrów jak i poszczególnych pól bitowych. Warto sobie dokładnie prześledzić jak to zrobiłem bo ma to zastosowanie do każdego urządzenia z rejestrami jakie spotkamy na swojej drodze. Kiedyś poczyniłem artykuł  jak opisywać rejestry na przykładzie modułu radiowego SI4463 , jest to bardzo przydatna w praktyce wiedza i zachęcam do stosowania jej .



Efektem wyjściowym z pliku lora.h jest deklaracja struktury configurationLoraRegister w której znajdą się docelowe ustawienia wszystkich rejestrów modułu LORA.

W pliku lora.c definiuję strukturę configurationLoraRegister i wypełniam jej wszystkie pola czyli ustawiam wszystkie rejestry w module. Znajdziemy tutaj też funkcję do zmiany ustawiania trybu pracy oraz funkcję inicjalizującą moduł. 

W ramach konfiguracji ustawię m.in moc nadawania na 10 dB oraz  adres na wartość FF FF co oznacza , że moduł nie będzie filtrował odbieranych danych i odbierze wszystko co leci na kanale 0 (taki numer kanału jest domyślnie ustawiony).




Po krótce przyjrzyjmy się funkcji inicjalizującej, która odpowiada za przesłanie ustawień do rejestrów modułu LORA.  W tym celu najpierw musimy ustawić moduł w tryb MODE3 (M0 = 1 i M1 = 1) , w którym możemy "rozmawiać" z rejestrami modułu. Ja to już zrobiłem w pliku konfiguracyjnym pin_config.c ustawiając tam piny M0 = 1 i M1 = 1, więc mamy tutaj w sumie niepotrzebne powtórzenie operacji ,ale tak łatwiej na to spojrzeć w kodzie testowym i ważne jest aby te piny jak najszybciej ustawić po uruchomieniu modułu. Potem wykonujemy sekwencję zapisującą do rejestrów czyli przesyłamy bajt 0xC0 (Write Register), adres początku w naszym przypadku 0x00 co zbiega się z adresem rejestru REG_ADDRESS_ADDH, kolejny wysyłany bajt to ilość przesyłanych bajtów. Ponieważ zapisuję dane do wszystkich rejestrów , więc będzie to 8 bajtów. Na koniec przesyłam jednym ciągiem strukturę configurationLoraRegister . Moduł LORA w odpowiedzi na przesłane dane zwróci nam kopię przesłanych danych z pierwszym bajtem ustawionym na 0xC1 (Read Register)

Sprawdzę zatem czy zapis do rejestrów się powiedzie. W tym celu w pliku main.c modyfikuję program testowy. Wywołuję funkcję inicjalizującą moduł LoraModuleInit() w której przesyłam strukturę z danymi do rejestrów modułu LORA oraz odpalam co 200 ms diodę LED . Tutaj ważna uwaga, przed pierwszym wysłaniem danych do rejestrów, przy uruchomieniu, potrzebna jest zwłoka ok 40 ms liczona od ustawienia M0 = 1 i M1 = 1. Bez tej zwłoki nie otrzymamy odpowiedzi zwrotnej z modułu na wysłane dane do rejestrów. 



Uruchamiam program i podpinam analizator stanów logicznych pod piny RX , TX , AUX , M0 , M1  i patrzę co się dzieje.


Wysłana ramka z danymi do modułu LORA :


Odpowiedź modułu LORA na wysłane do niego dane :



Przy okazji widzimy jak się AUX zachowuje po wysłaniu do modułu danych. Można przyjąć , że narastające zbocze na AUX kończy zajętość modułu w zakresie przetwarzania danych w rejestrach. Na końcu odebranych danych widzimy nadmiarowy bajt 0xFF . Nie ma on żadnego znaczenia.

Na tę chwilę wiem, że moduł LORA przyjmuje dane konfiguracyjne i potwierdza ich przyjęcie, zwrotną informacją. Czyli mam jakiś krok milowy zaliczony. Na razie jest łatwo i przyjemnie zobaczymy co dalej. 

Warto tutaj wspomnieć, że producent modułu, firma CDEBYTE specjalizuje się w modułach radiowych i ma dosyć spore portfolio w tym zakresie. Moduły , które przykuły moją uwagę to jednostki DTU dalekiego zasięgu nawet do łączności na dystansie i 40 km. Ciekawe jak dla mnie urządzenie o oznaczeniu E90-DTU(433L30E) lub E90-DTU(433SL30E) , gdzie na odległości ok 10 km możemy zrealizować łączność radiową  z węzłami ETHERNET. Prawdopodobnie zajmę się tym w niedalekiej przyszłości.

Przechodzimy teraz do ciekawszych zagadnień czyli przesyłanie danych drogą radiową . W tym celu wyciągam z szafy drugą płytkę STM32G0 z modułem LORA. Jeden moduł będzie nadawał cyklicznie co 200 ms string "messageToSendLora" , drugi moduł podepnę pod analizator stanów i będę podglądał co przychodzi na RX tego modułu. Jeśli dane zostaną poprawnie przesłane drogą radiową to zobaczę na RX mojego stringa. Na tym etapie nie piszę jakiegoś poważnego softu do odbioru i parsowania danych bo to nastąpi po przemyśleniu co będę przesyłał i w jakiej formie na potrzeby docelowego urządzenia . Moduły radiowe mam zamiar użyć do moich zegarków LED , aby drogą radiową synchronizować czas. Może jeszcze po drodze jakaś komunikacja z chmurą etc. Chodzi mi jeszcze po głowie pomysł jakiegoś komunikatora przenośnego, wysyłanie krótkich wiadomości tekstowych coś ala SMS na komórkach.

Najpierw piszę prosty kod testowy dla modułu , który będzie nadawał stringa a wygląda on tak :


Moduł nadający stringa skonfigurowałem w trybie Fixed co oznacza , że po stronie odbiorczej zostanie ucięty nagłówek z adresem i numerem kanału i otrzymamy samą informację czyli naszego stringa. Drugi możliwy do wybrania tryb - Transparentny, spowoduje odebranie nagłówka po stronie odbiorczej. Format przesyłanej wiadomości to dwa pierwsze bajty określają  adres odbiorcy a trzeci bajt określa kanał na którym będzie przesyłana wiadomość. Kolejne bajty to dane , które chcemy wysłać. Sam proces wysyłania jest automatyczny nic nie musimy zatwierdzać . 

Przed wysyłaniem czy odbieraniem wiadomości moduł może być w trybie uśpienia w którym pobiera w/g deklaracji producenta ok 5 uA prądu. I jest to tryb MODE 3. W trybie tym nie odbierzemy wiadomości ani nie wyślemy a szkoda. Aby odebrać wiadomość moduł musimy wybudzić z letargu np. ustawiając w nim stan MODE 0.

Kod dla drugiego modułu, który będzie odbierał wiadomość wygląda jak poniżej .



Włączam oba moduły nadający wiadomość i odbierający ją. Do modułu odbierającego dane podłączam analizator stanów logicznych i obserwuję pin RX. Moduły ustawiłem na adres 0xFFFF i kanał 0. Ustawienie na tym adresie powoduje, że moduł nie filtruje adresów i odbiera wszystko co leci na jego kanale czyli w naszym przypadku na kanale nr. 0.

Obraz wiadomości którą cyklicznie przesyłam co 200 ms :


Po stronie modułu który odbiera wiadomość na pinie RX widzimy :


Czyli komunikacja działa bez zająknięcia. Wszystko co zostało nadane jest odbierane.

Na tę chwilę mogę na pewno stwierdzić, że podstawową zaletą modułu E220-400T22D jest prostota jego obsługi, która sprowadza się praktycznie do operowania komunikacją UART. Brakuje tutaj takich opcji jak obsługa CRC czy zaawansowanego szyfrowania np. AES. Nie mniej w prostych i typowych dla LORA zastosowaniach, moduł powinien się sprawdzić.

Pokazałem jak możliwie w najprostszy sposób można sobie radzić przy uruchamianiu modułów radiowych E220-400T22D firmy CDEBYTE. Skoro jesteśmy jednak przy UART to nie wypada pominąć milczeniem tematu bufora kołowego. Zaimplementujmy sobie w programie testowym obsługę takiego bufora . Bufor kołowy wykorzystam w odbiorze czyli w module , który odbiera wiadomość . Będę chciał wykryć konkretną wiadomość w ciągu przesyłanych danych i zareagować na jej wykrycie np. zapaleniem diody LED.

Zapis odbieranych danych będzie dokonywał się do bufora kołowego w przerwaniu odbiorczym UART RX. Musimy zatem te przerwanie włączyć. W NVIC przerwanie już włączyłem wcześniej. Teraz włączam przerwanie RX w module sprzętowym UART1 a wygląda to jak poniżej :


Jak działa bufor kołowy możemy sobie doczytać w necie. Bardzo dobrze wytłumaczone to zagadnienie jest w książkach Mirosława Kardasia w Bluebook i Greenbook. Nie wyobrażam sobie zresztą aby nie mieć tych pozycji w swojej biblioteczce jeśli naszą pasją są mikrokontrolery.

W tym artykule skupiam się na implementacji bufora kołowego na potrzeby mojego programu testowego. Ogólna idea jest taka aby kolejne odebrane bajty z UART, pakować do bufora o skończonej wielkości . Przy czym przyjmuje się aby wielkość takiego bufora była minimum 3 x większa od najdłuższej wysyłanej wiadomości. Każda wiadomość będzie kończyć się znakiem Enter kod ASCII CR - 13, język C - '\r'. Jeśli do naszego bufora odbiorczego nadejdzie kompletna wiadomość a poznamy to po wystąpieniu znaku Enter. Przepiszemy tę kompletną wiadomość do bufora pomocniczego i będziemy mogli rozpocząć obróbkę tej wiadomości np. jej "parsowanie" .

Tworzę plik ringbuffer.h w którym deklaruję wszystkie niezbędne elementy do stworzenia obsługi kołowego bufora odbiorczego.


W pliku ringbuffer.c definiuję wszystkie niezbędne elementy do stworzenia obsługi kołowego bufora odbiorczego. Tworzę funkcję do pobrania jednego znaku z bufora kołowego oraz całego stringa zakończonego znakiem Enter. Funkcja uartRxStringEvent() stworzona została na potrzeby mojego programu testowego . W funkcji tej pobieramy jedną kompletną wiadomość z bufora kołowego do bufora pomocniczego i porównujemy ją ze stringiem , który wysyłamy po stronie modułu nadającego. Funkcja ta spełnia rolę prostego "eventa" bez użycia "callbacków" i będzie wywoływana w pętli głównej programu


Dane odebrane z UART1 pakowane są znak po znaku do bufora kołowego, czynność tą fizycznie realizujemy w funkcji obsługującej przerwania od UART1.



Zmodyfikujemy nasz program testowy. Po stronie modułu odbierającego wygląda on jak poniżej :


Część zakomentowana znajdować się będzie w płytce z modułem nadającym. Zwracam tutaj uwagę na znak Enter \r na końcu stringa wysyłanego ,bo bez tego nie zidentyfikujemy po stronie odbiorczej tego stringa z użyciem mechanizmu bufora kołowego.

Efektem działania programu po stronie modułu odbiorczego jest zapalenie się diody LED na płytce, po wykryciu w odbieranych danych stringa messageToSendLora . I tak działa nam pięknie mechanizm bufora kołowego , który jest nieodzownym elementem zabaw z UART. Warto też mieć na uwadze, że UART jest stosunkowo wolnym medium komunikacyjnym dlatego jego obsługę należy powiązać z DMA, szczególnie wtedy kiedy przesyłamy dłuższe stringi. STM32G071 ma 7 kanałów DMA. Docelowo do współpracy z modułem radiowym LORA wykorzystam obsługę bufora kołowego z DMA.
 
W buforze kołowym nie uwzględniłem bufora nadawczego a jest to element , który również należy wziąć pod uwagę aby mieć kompletny mechanizm obsługujący odbiór i nadawanie. Mając ogarnięty bufor kołowy możemy pokusić się np. o projekty w których użyjemy zdalnej  modyfikacji parametrów dowolnego urządzenia za pomocą np. telefonu komórkowego . W urządzeniu wystarczy tylko podpiąć moduł bluetoooth.

Moduł radiowy mam ogarnięty czas zatem do wykorzystania go w moich zegarkach. Ale najpierw muszę zbudować bazę , która będzie mi "rozsiewała" czas "atomowy" do moich zegarków RTOS-owych. Bazę taką zbuduję w oparciu o moduł Ethernetowy W5500 i moduł radiowy LORA E220-400T22D. Bazę połączę kablem RJ45 z routerem WI-Fi. Czas będę pozyskiwał z jakiegoś ogólno dostępnego serwera czasu i za pomocą modułu radiowego rozsyłał po okolicy w promieniu 2 km :). Już się nie mogę doczekać efektów. Mega fajnie jest skonstruować i ożywić jakieś urządzenie własnymi rękoma i poczuć się jak Tata Pinokia.


Pozdrawiam

PICmajster
picmajster.blog@gmail.com









1 komentarz:

  1. Dostawy AVRdb w microchip gdzieś na luty i grudzień przyszłego roku :) te stm-y są dostępne i tanie ponieważ to Włoska firma ? i klepią je w Europie ?

    OdpowiedzUsuń