niedziela, 24 listopada 2019

PIC32MM i moduł Ethernetowy Wiznet W5500 - serwer UDP, zajęcia praktyczne cz.1

Moduł W5500 sprawił mi dużo satysfakcji podczas jego poznawania. Zagadnienia z jakimi przyszło mi się zmierzyć po drodze były mi obce, dlatego podwójna satysfakcja, że udało się zgłębić "tajemną" sieciową wiedzę. Dziękuję ci zatem module W5500 za poszerzenie moich horyzontów wiedzy :).Przetarłem szlak ,zatem zapraszam do wspaniałej przygody z modułem Ethernetowym W5500 firmy Wiznet. W artykule pokażę jak prosto ogarnąć moduł W5500 za pomocą PIC32MM . W zajęciach praktycznych nawiążemy komunikację za pomocą protokołu  UDP pomiędzy naszym modułem W5500 a lokalnym PC . Po drodze skorzystamy z narzędzi sieciowych takich jak WIRESHARK aby podsłuchać sieć. Ponadto pokażę w jak prosty sposób możemy wysyłać pakiety UDP z poziomu systemu LINUX. Artykuł będzie wstępem przed kontynuacją tematu , którego zwieńczeniem będzie pełna realizacja komunikacji z wybraną Chmurą. Mam nadzieję , że mi się uda to zrealizować. Taki wytyczam sobie cel mojego spotkania z modułem W5500 . Howgh.

 Moja baza sprzętowa :

- płytka firmy Microchip PIC32MM USB Curiosity Development Board z PIC32MM0256GPM064 na pokładzie.






- moduł ETH Wiz click serbskiej firmy Mikroelektronika, moduł jest w standardzie gniazda obsługiwanego m.in przez Microchipa. Dlatego jest wygodny do współpracy z płytkami tej firmy.




 - konwerter UART -> USB na popularnym chipsecie firmy Silicon Labs CP2102.


- Router LTE firmy Huawei model B525 abonament u operatora telefonii komórkowej. W tym artykule skupiamy się na sieci lokalnej, nie wychodzimy na zewnątrz tej sieci , więc w sumie nie musimy być nawet do internetu podłączeni. Ważne aby Router miał port LAN RJ45 a to obecnie standard.


- kabel Ethernet RJ45. Długość jaka tam komu pasuje.

- przynajmniej jeden PC z systemem LINUX podłączony do Routera np po WiFi.

- analizator stanów logicznych , przydatny do upewnienia się, że komunikacja między modułem W5500 a MCU na poziomie SPI jest poprawna.


Moja baza programistyczna :

- MPLABX-IDE ver 5.30 + kompilator XC32 ver 2.30 + plugins MCC

Moduł W5500 wpinamy do gniazda mikroBUS literka B na płytce Curiosity.

Zabawę zaczynamy od utworzenia nowego projektu w MPLABX-IDE dla PIC32MM0256GPM064. U mnie projekt nazywa się pic32mm_Ethernet :

Po utworzeniu projektu , tworzymy w głównym drzewie katalogów, katalog ioLibrary. W katalogu tym odtworzymy strukturę oficjalnej biblioteki dla W5500. A skąd taką bibliotekę można pobrać ? a stąd : ioLibrary Driver 
Ponieważ po pobraniu oficjalnej biblioteki nie będzie ona nam działać ,dopóki jej nie skonfigurujemy (standardowo jest skonfigurowana dla W5100), dlatego zalecam pliki biblioteczne pobrać z utworzonego przeze mnie projektu , który będzie dostępny w linkach na dole artykułu . Jeśli ktoś chciałby sam bibliotekę przysposobić to trzeba zajrzeć do dokumentacji z tego linku : ioLibrary Driver - dokumentacja i dotrzeć do opisu Todo List .Plik z dokumentacją biblioteki jest w dziwnym formacie .chm. Na jednym kompie otwiera się na innym nie chce się otwierać.



W programach jakie znajdziemy na GitHub innych użytkowników, rzadko zdarza się aby ktoś odtwarzał strukturę oryginalnej biblioteki dla W5500, z reguły ludzie bezczeszczą oryginał i dołączają pliki bez składu i ładu do jednego wspólnego wora.

Poniżej na obrazku pokazuję strukturę katalogów i plików jakie ja sobie dołączyłem do projektu z oficjalnej biblioteki ioLibrary dla modułów Wiznet. Wzorując się oczywiście na oryginalnej strukturze biblioteki. Najpierw musimy utworzyć katalogi a potem dołączać do nich pliki.


Na potrzeby tego artykułu w zasadzie potrzebujemy tylko zawartości katalogu Ethernet ,ale ja już na wyrost sobie dołączyłem zawartość katalogu Internet a w nim DHCP, DNS i MQTT. W programie na potrzeby bieżącego artykułu nie będziemy korzystać z plików z katalogu Internet. Pliki biblioteczne  jak pisałem wcześniej zalecam dołączyć z mojego programu na GitHub (będzie dostępny w linkach pod artykułem).

No dobrze zakładam, że mamy już odtworzoną w naszym projekcie strukturę biblioteki ioLibrary ze stosownymi plikami. Teraz zajmiemy się konfiguracją MCU na potrzeby współpracy z modułem W5500. Poniżej docelowa pinologia jaką zastosujemy w projekcie :

/*----------------------------------------------------------------------------*/
/* Wiznet W5500 Ethernet Module connect  */
/*----------------------------------------------------------------------------*/
// ** Sample application for PIC32MM **
//   
//    W5500      --->    MCU PIC32MM
//     RST                      -->  RC14
//     MOSI                    -->  RA7 (MISO)
//     MISO                    -->  RA8 (MOSI)
//     SCK                      -->  RA10
//     CS                        -->  RC11
//     INT                      -->  RD2  (not used))
/*----------------------------------------------------------------------------*/
/* UART for Printf() function                           */
/*----------------------------------------------------------------------------*/
//    Required connections for UART:
//   
//    CP210x Module     ---> MCU PIC32MM
//    TX                                   --> RA6 (not used))
//    RX                                   --> RC12 (TX)
//**************************************************

Tutaj jedna uwaga, w module markowym Mikroelektroniki W5500 MOSI jest połączone z MISO w MCU a W5500 MISO z MOSI w MCU. W przypadku chińskich modułów mamy chińską logikę i tam W5500 MOSI jest połączone z MOSI w MCU a W5500 MISO z MISO w MCU. Oczywiście chodzi o oznaczenia na module na których można się oszukać w przypadku chińczyka.

Odpalamy wtyczkę MCC w MPLABX-IDE. Na kolejnych obrazkach pokazuje moje ustawienia na kolejnych zakładkach (uważnie klonować moje ustawienia !):

SYSTEM MODULE  :


SPI3 (to musimy dodać najpierw z zakładki Device Resources) :



Tu ważna uwaga odnośnie SPI, moduł W5500 łyka tylko tryb 0 i 3 SPI.


UART1 (to musimy dodać najpierw z zakładki Device Resources) :


Po co nam UART w projekcie ? PIC-e mają tę wspaniałą właściwość, że możemy w prosty sposób uzyskać funkcjonalność dla funkcji jezyka C - Printf(). Daje nam to ogromne możliwości w zakresie łatwej implementacji wyrzucania strumienia tekstu / danych po UART. Z tą funkcjonalnością uruchomienie modułu W5500 było czystą przyjemnością. Szybkie wyrzucanie informacji na ekran ze stanu zawartości rejestrów W5500, przychodzących komunikatów do modułu za pomocą jednej funkcji Printf() - to po prostu bajka.

Pin Module :


Pinów RA6 (U1RX) i RD2 (INT_W5500) fizycznie nie używam ale w projekcie są zaimplementowane pod przyszłe potrzeby. Piny Output :  RC11, RC14 i Input : RD2 musimy sami zapiąć kłódeczkami. Pozostałe zostaną ustawione sprzętowo .

To już wszytko co trzeba zrobić w MCC , kończymy tu działanie "klikając" w zakładkę Generate w okienku Project Resources i wychodzimy z MCC jeden raz "klikając" w ikonkę MCC. Przełączamy się w MPLABX-IDE na widok Projects.

Najprościej cały mój projekt sklonować z GitHuba i otworzyć go w MPLABX-IDE.

Cała funkcjonalność programu jest zawarta w pliku  main.c . Omówię tutaj jego istotne elementy. Najpierw trzeba wspomnieć o tym, że aby biblioteka ioLibrary chciała w ogóle gadać z nami, musimy przekazać jej callbacki dla zapisu i odczytu bajtu po SPI oraz dla ustawiania stanów na pinie CS .

Definicje funkcji callback pokazuje obrazek poniżej , jak widzimy są to bardzo proste funkcje :


Na obrazku poniżej widać w jaki sposób rejestrujemy powyższe funkcje w bibliotece ioLibrary czyli krótko mówiąc przekazujemy prostym mechanizmem callbackowym funkcje do biblioteki a co z tym zrobi biblioteka to już nas w sumie nie interesuje.


No dobrze biblioteka ioLibrary widzi nasze funkcje możemy zacząć rozmawiać z biblioteką .

Ponieważ inicjalizacja modułu W5500 wiąże się m.in z ustawieniem parametrów sieciowych dlatego lecę po kolei co istotne w tym zakresie w pliku main.c :
Najpierw ustawienia sieciowe dotyczące numerów gniazd i portów na których będzie realizowane połączenie z modułem W5500 :


W programie mam zaimplementowany mini-serwer UDP i TCP ale my zajmujemy się w tym artykule tylko UDP.
Moduł W5500 ma dostępnych 8 gniazd dla potrzeb ruchu sieciowego. A co to jest to gniazdo ? wyobraźmy sobie, że jest to wtyczka w najwyższej warstwie modelu TCP/IP w warstwie aplikacji do której możemy się podłączyć wtyczką czyli drugim interfejsem sieciowym . Mając 8 gniazd możemy symultanicznie otworzyć 8 różnych połączeń. Pomiędzy warstwą aplikacji a warstwą transportową TCP/IP gniazdko łączymy tunelem zwanym portem :) Aby nawiązać połączenie z innego PC (a w nim interfejsu sieciowego) z naszym modułem W5500 , musimy w parametrach inicjujących połączenie z PC podać adres IP naszego modułu i numer portu. Numeru gniazdka nie podajemy bo to już jest właściwość "intymna" naszego modułu o której tylko on wie :).

W programie powołana jest struktura pnetinfo o typie wiz_NetInfo , tutaj po odczytaniu z rejestrów modułu ,będą dostępne ustawione w module parametry sieciowe. Można sobie je w prosty sposób wyświetlić za pomocą funkcji Printf(), jest to wszystko w pliku main.c.

Teraz najważniejsze z punktu widzenia ustawień sieciowych naszego modułu czyli adresy IP , maski etc. wszystko to musimy wpisać w strukturę jak poniżej :


Najpierw "wymyślamy" sobie adres MAC naszego modułu ja posłużyłem się generatorem MAC znalezionym w sieci. Tu z tym nie powinno być problemu. Kolejnym parametrem do ustawienia jest adres IP naszego modułu. Aby nasz adres był "dobry" musimy uwzględnić dwie rzeczy. Po pierwsze musimy znać tzw adres bramy. Jest to adres po którym logujemy się do Routera. U mnie jest na naklejce pod spodem Routera. Mój adres bramy wygląda tak 192.168.8.1 dodatkowo z naklejki odczytałem maskę podsieci 255.255.255.0. Teraz musimy wykontycypować adres naszego modułu mając na uwadze , że teoretycznie może on być z zakresu 192.168.8.2-255. Nie możemy jednak tak sobie luźno do tematu podejść, musimy znać zakres adresów DHCP jakimi posługuje się nasz Router i wybrać adres spoza tej puli. DHCP to mechanizm przydzielania automatycznie adresów sieciowych urządzeniom , które się podłączają do naszego Routera. Nasz moduł W5500 ma możliwość skorzystania z mechanizmu DHCP ale ja tym jeszcze się nie zajmowałem. Moduł W5500 łączy się na stałym adresie IP , który musimy podać . DHCP w moim Routerze miało górny zakres adresów .200. Ja sobie wybrałem cyferkę poza zakresem - 220 . Adres IP jaki zostanie wpisany do mojego modułu W5500 wygląda tak 192.168.8.220.

Kolejna daną jaką musimy podać do struktury gWIZNETINFO jest adres bramy a to już wiemy 192.168.8.1. Adresu DNS nie znamy i nie będzie używany w naszym programie-przykładzie . Dałem tutaj adres bramy ale równie dobrze można podać same zera. Konfiguracja sieciowa za nami , teraz trzeba zapisać strukturę z danymi sieciowymi do modułu W5500 ale do tego dojdziemy. Teraz istotne elementy funkcji inicjalizującej W5500_Init ():

Definicja buforów TX i RX , bufory wykorzystują gniazda , mamy ich 8 szt w naszym module zatem każdemu przydzielamy w ten sposób 2kB w odbiorze i nadawaniu. Wyczerpujemy tym samym pulę dostępnej na ten cel pamięci wewnętrznej modułu 32kB. Robimy to jak poniżej :



Rejestracja callbacków (już wspominałem wcześniej) i buforów dla gniazd :



A poniżej fragment funkcji W5500_Init () w którym sprawdzamy tzw. PHY Link ,w uproszczeniu czy  medium transmisyjne czyli kabel Ethernetowy łączy nasz moduł W5500 z Routerem. Jeśli brak fizycznego połączenia to tutaj program stanie, wyświetlony zostanie po UART za pomocą funkcji Printf() odpowiedni komunikat a program będzie czekał na wetknięcie kabelka po obu stronach :). Odpytywanie czy PHY Link jest, następuje co 0.5 s.


Ostatnim elementem funkcji W5500_Init () jest wywołanie funkcji network_init(). Funkcja network_init() robi dwie istotne rzeczy , wpisuje konfigurację sieciową do modułu W5500 i po tym pobiera z rejestrów modułu zapisane dane. W ten sposób upewniamy się czy konfiguracja sieciowa została poprawnie zapisana w rejestrach modułu. Obraz funkcji poniżej :



Prawidłowy obrazek jaki powinniśmy zobaczyć po uruchomieniu programu i podłączeniu naszego modułu do Routera wygląda jak poniżej (do wyrzutu danych po UART za pomocą funkcji Printf() wykorzystuję program Putty



Ustawienia dla programu Putty poniżej :





Opiszę teraz pokrótce funkcję UDP_Serwer() , która realizuje nam na poziomie aplikacji serwer UDP. Funkcja ta jest wywoływana cyklicznie w pętli głównej programu. Co robi fizycznie funkcja ? Jeśli gniazdo nr 1 nie jest otwarte to je otwiera na ustawionym porcie 10001 ,po czym następuje etap nasłuchiwania sieci. Jeśli coś przyjdzie na nasz adres IP i nasz port UDP 10001 czyli do naszego gniazda , wiadomość zostanie pobrane do roboczego bufora gDATABUF , zawartość tego bufora zostanie zwrotnie odesłana klientowi i wysłana również do UART tym razem nie funkcją Printf() ale funkcją typowo UART-ową, UART1_WriteBuffer(). Czyli bawimy się w tzw echo. To co przyszło do naszego mini-serwera" UDP zostanie odesłane klientowi. Funkcja serwera UDP działa rewelacyjnie. Jak głupi osioł wysyłałem sobie pakiety do modułu i patrzyłem z otwartą buzią na odebrane z "prędkością światła" ramki :) Magia normalnie :).

Ciało funkcji mini-serwera UDP prezentuję poniżej na dwóch kolejnych obrazkach :



Wspomnę jak testowałem komunikację SPI z modułem za pomocą analizatora stanów logicznych. Ponieważ mój analizator nie jest szybki musiałem zwolnić zegar SPI z 12 MHz na 2 MHz, zrobimy to m.in w MCC w zakładce dla SPI.
Zakomentowałem część sprawdzającą PHY Link


a w pętli głównej użyłem  funkcji bibliotecznej getSHAR(); odczytującej zawartość MAC adresu z modułu.


Program testowy co 100 ms odczytuje zawartość rejestru modułu W5500 w którym jest przechowywany wcześniej wpisany adres MAC. Jeśli wszystko jest w porządku na analizatorze zobaczymy obrazek jak poniżej :


Na pierwszy wysłanych do modułu trzech bajtach musimy zobaczyć sekwencję odpowiedzi 1,2,3 ,po tej sekwencji moduł wyśle  nam na kolejnych 6 bajtach adres MAC. Adres musi się zgadzać z tym co wcześniej zostało zapisane do modułu w procesie inicjalizacji. Zwróćmy uwagę też na to co na CS zostanie ustawione ,bo podczas całej 9 bajtowej sekwencji musi na CS panować stan niski. Możliwe błędy w przypadku nie uzyskania powyższej struktury transmisji to zły tryb pracy SPI, błędne połączenia . Z tego co zerknąłem na forum Wizneta, właśnie najczęściej ludzie legną na SPI.

Przechodzimy do najfajnieszej części czyli sprawdzenie czy moduł W5500 działa w sieci. Tutaj przyda się komputer PC najlepiej z systemem Linux podłączony do Routera np po WiFi. Zakładam, że moduł W5500 jest wpięty do Routera za pomocą kabla RJ45 i program uruchomiony. Uruchamiamy konsolę Linuxową w PC i wpisujemy polecenie ping 192.168.8.220 (adres IP naszego modułu.). Jeśli nasz moduł jest w sieci i wszystko jest oki ,zobaczymy komunikat jak poniżej :


Aby przerwać pingowanie wciskamy klawisze CTRL+C.
Teraz możemy przejść do wysłania komunikatu UDP . Robię to najprościej jak tylko się da i w konsoli Linuxa wpisuję polecenie :

echo "Hello W5500" > /dev/udp/192.168.8.220/1001

PC z Linuxem musi być skomunikowany z Routerem np po WiFi. Po wciśnięciu enter zobaczymy taki efekt po stronie modułu W5500 :



Lewe czarne okienko to konsola Linuxa a prawe to okienko Putty i komunikaty z naszego MCU wysyłane po UART m.in funkcją Printf(). W konsoli Linuxowej wysłaliśmy wiadomość UDP "Hello W5500", nasz moduł W5500 odebrał to , a MCU pobrał dane z modułu i wyświetlił je na terminalu Putty. Wyślijmy drugi komunikat "Poland is a beautiful country" , efekt wysłania i odebrania poniżej :


Po stronie PC z Linuxem nie widzimy jednak komunikatów zwrotnych z naszego modułu, aby przekonać się , że one są na 100 % odpalimy sobie program Wireshark. Program Wireshark odpalamy z uprawnieniami admina : sudo wireshark bo inaczej nie wykryjemy interfejsów sieciowych. Po wysłaniu komunikatu UDP do naszego modułu widzimy sytuację jak poniżej :


Pirwsza ramka wyszła z adresu mojego PC z Linuxem czyli 192.168.8.104 do modułu W5500 na adres 192.168.8.220. Druga ramka to odpowiedź naszego modułu w postaci echa czyli dane poszły z adresu  192.168.8.220 do 192.168.8.104. Wszystko gra i buczy nic mi nie sprawiło takiej radości jak uruchomienie modułu W5500 bo wiązało się to m.in ze zgłębieniem tajemnej wiedzy sieciowej :) . Stałem się bardziej świadomym użytkownikiem sieci :).

Zabawę z wysyłaniem komunikatów UDP możemy z powodzeniem zrealizować za pomocą telefonu komórkowego podłączonego po WiFi z naszym Routerem. Jest dużo aplikacji do tego przeznaczonych.
Bazując na moim prostym programie możemy już z marszu zrobić w bardzo prosty sposób sterowanie telefonem komórkowym urządzeniami domowymi. A to dopiero przedsmak ogromnych możliwości jakie drzemią w duecie  W5500 + MCU. Moje aspiracje sięgają dwustronnej komunikacji z chmurą na zmiennym adresie IP :)

W linkach poniżej znajduje się projekt , który wgrałem na GitHub. Sciągamy go do MPLABX-IDE za pomocą Team --> Remote --> Clone lub w konsoli Linuxa :

git clone https://github.com/PICmajster/PIC32MM_W5500.git

Moduł i program działają doskonale. Miłej zabawy :)


Pozdrawiam
picmajster.blog@gmail.com


Linki :

1 komentarz: