Mikrokontrolery AVR. Język C - podstawy programowania: serwer HTTP

Mikrokontrolery AVR. Język C - podstawy programowania: serwer HTTP
Pobierz PDF Download icon
Z końcem 2010 roku na polskim rynku pokazała się wydana przez firmę Atnel książka autorstwa Mirosława Kardasia pt: \"Mikrokontrolery AVR - Język C - Podstawy Programowania\". Postanowiliśmy przybliżyć czytelnikom tę interesującą pozycję poprzez opublikowanie jej fragmentu poświęconego tworzeniu serwera http na mikrokontrolerach AVR. Przykłady prezentowane w książce mogą być z łatwością wykonane z użyciem zestawów deweloperskich firmy Atnel.
?)); plen=fill_tcp_data_p(buf,plen,PSTR(?Witaj !\n?)); plen=fill_tcp_data_p(buf,plen,PSTR(?twój serwer www działa znakomicie\n\n?)); plen=fill_tcp_data_p(buf,plen,PSTR(??)); plen=fill_tcp_data_p(buf,plen,PSTR(?
www.atnel.pl?)); plen=fill_tcp_data_p(buf,plen,PSTR(?\n?)); return(plen); } // główna funkcja programu int main(void){ uint16_t dat_p; // CLKPR=(1<200 OK?)); goto SENDTCP; } // just one web page in the ?root directory? of the web server if (strncmp(?/ ?,(char *)&(buf[dat_p+4]),2)==0){ dat_p=print_webpage(buf); goto SENDTCP; }else{ dat_p=fill_tcp_data_p(buf,0,PSTR(?HTTP/1.0 401 Unauthorized\r\nContent-Type: text/html\r\n\r\ n

401 Unauthorized

?)); goto SENDTCP; } SENDTCP: www_server_reply(buf,dat_p); // wysłanie strony http // tcp koniec obsługi portu 80 } return (0); } Na listingu  3. Przedstawiono cały kod programu serwera HTTP uruchomiony na mikrokontrolerze ATmega32. Na początku typowe jest dołączanie plików nagłówko- wych zarówno tych systemowych, jak i po- trzebnych bibliotek. Warto zauważyć, że oznaczyłem jako komentarz linię dołączającą plik timeout.h, który wcześniej usunęliśmy. MAC, IP i port Następnie mamy do czynienia z bardzo istotną rzeczą, jaką jest przydzielenie odpo- wiedniego adresu MAC dla naszego urzą- dzenia, oraz adresu IP. Trzeba pamiętać, że oba muszą być unikalne, tzn. że nie mogą przybierać takiej samej wartości jak w  in- nym urządzeniu pracującym w twojej sieci lokalnej. Musisz, zatem sprawdzić w syste- mie, (np. Windows), jaki adres IP ma twój komputer. Można tego dokonać za pomocą polecenia ?ipconfig /all? wydanego z  linii poleceń w  konsoli typu DOS. Zakładam oczywiście, że czytelnik ma dostęp do lokal- nej sieci LAN, w której pracuje jakiś router, a ten wyznacza dostępną klasę adresów IP 85ELEKTRONIKA PRAKTYCZNA 3/2011 Mikrokontrolery AVR Co sugeruje, iż można bez cienia wątpli- wości wprowadzić własny adres np.: static uint8_t mymac[6] = {?A?,?L?,?A?,0x10,0x00,0x24}; Niestety w  ten sposób przypadkowo utworzony adres MAC może powodować przedziwne zachowania się przykładowych programów w  sieci lokalnej lub rozległej. Przedziwne z powodu braku znajomości pra- widłowych sposobów tworzenia MAC adre- sów. Często właśnie przypadkowa zmiana MAC adresu w przykładowych programach z  tej strony powoduje, że nie działają one zgodnie z oczekiwaniem i przez to wielu lu- dzi się zniechęca do dalszych prób ze stosem TCP. Początkujący nie zwraca uwagi na to, że dokonał zmiany adresu MAC, tylko na to, że kod jest identyczny jak w przykładach, a po- mimo to informacje raz się pojawiają, a in- nym czasem nie docierają do albo z kompu- tera. Zasada tworzenia adresów MAC została zilustrowana na rysunku 2. Za niepowodzenia początkujących w ustalaniu adresu MAC odpowiadają dwa pierwsze najmłodsze bity z 1. oktetu. Być może autorzy tuxgraphics.org wybrali trzy pierwsze litery świadomie, szczególnie pierwszą literę ?T?. Kod litery T wynosi 0x54. (binarnie: 0b0101 0100), zatem dwa najmłodsze bity 1. oktetu ustawione są na 0 i taki adres MAC praktycznie w każdych Dokładnie tak samo wprowadzamy 6 cyfr, które są bezpośrednio kodami ASCII podanych w  apostrofach liter. Uwagę może wzbudzić pierwszy bajt o wartości 0. Niestety autorzy z tuxgraphics.org popełnili błąd tłu- macząc w kodzie sposób, jak można uzyskać własny adres MAC. W ich przypadku jest to: static uint8_t mymac[6] = {?T?,?U?,?X?,0x10,0x00,0x24}; W  rzeczywistości trzy pierwsze litery TUX zostały zaprezentowane w postaci szes- nastkowej w ich przykładach: static uint8_t mymac[6] = {0x54,0x55,0x58 ,0x10,0x00,0x24}; Jednocześnie tłumaczą, jak uzyskać wła- sny MAC adres, cytuję: ?// how did I get the mac addr? Translate the first 3 numbers into ascii is: TUX? w sieci. Np. jeśli komputer będzie miał ad- res 192.168.0.100, niemal na pewno można urządzeniu przypisać adres IP z  zakresu od: 192.168.0.1 do 192.168.0.254 pomijając adres IP, który jest przypisany komputero- wi PC oraz adres tzw. bramy domyślnej. Adres tej bramy także zostanie wyświetlo- ny w konsoli, przy czym zwykle w tej kla- sie domyślnie wynosi on: 192.168.0.1 ale w  teorii może być on różny, w  zależności od routera. Warto sprawdzić, w jakiej części klasy router przydziela adresy automatycz- nie, a którą jej część pozostawia dla adresów przydzielanych statycznie (przez użytkow- nika). Tu wszystko zależy od konfiguracji routera. Na komputerze autora router za po- mocą DHCP przydziela automatycznie adre- sy w zakresie 192.168.0.2 do 192.168.0.99, dlatego wybrałem dowolny adres z zakresu: 192.168.0.100 do 192.168.0.254, konkret- nie: 192.168.0.110. Nieco inaczej i prościej jest z  dobraniem własnego adresu MAC dla naszego urządzenia. W zasadzie można wpisać 6 dowolnych przypadkowych liczb do tablicy mymac[6]. Można także zasto- sować nieco bardziej przyjazny zapis, np. w takiej postaci: // definicja własnego MAC adresu urządzenia static uint8_t mymac[6] = {0,?M?,?I?,?R?,?E?,?K?}; Rys. 2. Zasada tworzenia adresów MAC. R E K L A M A 86 ELEKTRONIKA PRAKTYCZNA 3/2011 NOTATNIK KONSTRUKTORA warunkach nie będzie powodował proble- mów z przykładami prezentowanymi przez autorów. Nie będę dalej szczegółowo opisy- wał, co oznacza ?unicast?, ?multicast? czy ?locally administrated?, ponieważ temat nieco odbiega od naszego głównego wąt- ku. Podpowiadam tylko, że warto, aby do takich podstawowych prób, testów obydwa bity miały wartość równą 0. Uniemożliwia to korzystanie ze wszystkich liter, gdyż w części z nich, ich kody ASCII będą miały ustawione obydwa te bity lub któryś z nich na wartość 1. Dlatego podany wyżej przy- kład z trzema pierwszymi literami ALA nie będzie działał w każdym przypadku zgod- nie z  oczekiwaniami. Reasumując, z  po- wodów opisanych wyżej, najbezpieczniej będzie, jeśli zawsze pierwszy oktet MAC adresu będzie miał wartość 0, wtedy unik- niemy przykrych niespodzianek i długiego poszukiwania błędów we własnym progra- mie. Przy prawidłowo dobranych numerach MAC adresu wystąpi raczej bardzo nikłe prawdopodobieństwo, że jakieś urządzenie w sieci LAN będzie miało już wybrany przez nas adres. W  dalszej kolejności ustawiamy port, na którym będzie nasłuchiwał nasz ser- wer WWW. Typowo odbywa się to na porcie 80. Dzięki temu, we własnej przeglądarce internetowej wystarczy wpisać w  miejsce nazwy strony adres IP (w moim przypadku 192.168.0.110) oraz nacisnąć klawisz EN- TER. Jeśli jednak zmienimy port np. na war- tość 8090, to wywołanie w przeglądarce bę- dzie już wymagało wyspecyfikowania tegoż portu: 192.168.0.110:8090. Definicja bufora Kolejna kwestia dotyczy definicji bufora do obsługi komunikacji TCP. Obsługuje on zarówno dane przychodzące, jak i  wycho- dzące. Jego wielkość zwiększyłem do 850 bajtów. Można ją jeszcze zwiększać, ale trze- ba pamiętać o  obserwacji zajętości pamięci RAM po kompilacji. Nie może zostać jej zbyt mało, gdyż dojdzie do problemów z działa- niem stosu. W dalszej kolejności mamy de- finicję dwóch funkcji narzędziowych. Pierw- sza z nich służy tylko do przygotowania od- powiedzi z serwera w formacie HTTP o tym, że zapytanie do serwera było poprawne, druga natomiast, print_webpage(), odpowie- dzialna jest bezpośrednio za wygląd strony WWW, jaką podaje nasz serwer. Funkcja ta napełnia tylko bufor ramki TCP, natomiast samo wysyłanie odbywa się za pomocą in- nych funkcji w  dalszej części programu. Na jej wyjściu otrzymujemy długość strony w bajtach. Główna część programu Następnie rozpoczyna się główna funk- cja programu, main(). Na początku dekla- rowane są potrzebne zmienne, oraz do- konywana inicjalizacja sprzętowej części karty sieciowej, ustawianie adresów MAC oraz IP itd. Później rozpoczyna się pętla nieskończona, w  której wciąż na początku dokonywany jest odczyt danych z  układu ENC28J60, jeśli takie w ogóle pojawiły się na jego wejściu. Posługujemy się tutaj zmienną dat_p. Jej wartość po odczycie odpowiada ilości odczytanych bajtów. Jeśli jest równa 0 to oznacza, że nie było żadnego żądania i pętla jest kontynuowana bez wykonywania pozostałych poleceń, które są umieszczone w  dalszej części programu. Trzeba pamię- tać o tej konstrukcji, bo jeśli np. umieścimy jakiś własny dodatkowy kod także poniżej sprawdzania ilości odczytanych bajtów, to nie zostanie on nigdy wykonany, dopóki nie nadejdzie żądanie do serwera. Aby wybrnąć z tej sytuacji, np. gdy chcemy umieścić dla celów testowych naszą wyżej omawianą funkcję SuperDebounce(), która w zależno- ści od stanu klawisza zapalałaby bądź gasi- ła diodę LED, trzeba umieścić ją w tej pętli albo przed odczytem czy sprawdzaniem zmiennej dat_p, albo nieco inaczej napisać kod całej pętli biorąc pod uwagę to, że ko- lejne polecenia powinny być wykonywane tylko i wyłącznie, gdy mamy jasną sytuację, że nadeszły jakieś dane. Dalsza część programu W  dalszej części programu, jeśli na- dejdzie już jakieś zapytanie z zewnątrz do naszej karty sieciowej, to po odczytaniu w buforze zostanie umieszczona ramka TCP wraz z danymi, które nas interesują. Dlatego w pierwszym warunku obsługujemy troszkę po macoszemu zapytania typu GET, nato- miast w  kolejnym sprawdzamy w  uprosz- czony sposób, czy nastąpiło odwołanie do domyślnej strony w głównym folderze ?/ ?. Występująca tutaj spacja oznacza, że pod- czas wywołania naszego adresu w  prze- glądarce nie określono żadnej konkretnej nazwy pliku do odczytu. W  przeciwnym wypadku moglibyśmy na tym właśnie po- ziomie rozpoznawać dostępne dla zapytań pliki, np.: if (strncmp(?/index.html?,(char *)&(buf[dat_p+4]), 11)==0) To spowodowałoby, że nasz serwer wyświetli stronę WWW tylko dla zapytań wpisanych w  przeglądarce w  ten sposób: ?192.168.0.110/index.html?. Można przy- gotować kilka różnych stron HTML w  pa- mięci FLASH mikrokontrolera albo też na karcie pamięci SD i wysyłać je w zależno- ści od nadchodzących żądań z  zewnątrz. Ten prosty przykład nie pokazuje wpraw- dzie wprost możliwości sterowania mikro- kontrolerem poprzez wyświetlanie strony www, ale istnieje wiele innych przykładów na stronie tuxgraphics.org, dzięki którym można bez problemu sterować zdalnie do- wolnymi procesami. Niestety jest jednak pewien problem, którego nie można pominąć. Otóż biblioteki stosu tcp, o  których wspomniałem wyżej, są wciąż rozwijane, ale także dosyć mocno modyfikowane, co powoduje, że praktycz- nie w  każdym pobranym przykładowym projekcie z  tuxgraphics.org spotkamy się z mniej lub więcej zmodyfikowanymi funk- cjami w  plikach ?ip_arp_udp_tcp.*?. Są one czasem wprost dostosowane do kon- kretnego projektu, przez co niestety nie można powiedzieć, że to biblioteki w pełni uniwersalne. Nie wchodząc zatem nieco głębiej w  tajniki działania stosu TCP czy- telnik może mieć problemy, aby płynnie modyfikować opisane tam projekty, jeśli chodzi o  wprowadzanie większych zmian w  ich funkcjonowaniu. Proponuję jednak przećwiczyć i  przetestować chociaż kilka projektów dostosowując je tak, jak pokaza- łem w  tym rozdziale, do własnego mikro- kontrolera. Łatwo wtedy dostrzec pewne powtarzające się zależności, z  których ja- sno wyłoni się sposób podstawowej obsługi stosu TCP. W  dużym skrócie, jego obsługa sprowadza się tak, jak w tym przykładowym omawianym tu programie prostego serwera HTTP, do ciągłego sprawdzania, czy układ enc28j60 otrzymał jakieś dane. W  dalszej kolejności zwykle sprawdzamy, czy zapy- tanie, które dotarło, nie jest zapytaniem czysto informacyjnym związanym ustala- niem adresów, obsługi ARP, czy też obsługi zewnętrznych poleceń typu ping. W innych projektach znaleźć można sprawdzanie, czy ramka, którą odczytał układ ENC28J60, jest przeznaczona właśnie dla nas, i dopie- ro po tym następują procedury parsowania (sprawdzania) danych przesłanych w ram- ce, które mogą być przydatne do sterowania procesami. Obsługa stosu już w kodzie pro- gramu, mając do dyspozycji chociaż podsta- wowe funkcje, nie jest wcale taka trudna. Dodam że chociaż na schematach przykła- dowych urządzeń z tuxgraphics.org prawie wszędzie podłączony jest sygnał INT z kar- ty sieciowej do wejścia INTx mikrokontro- lera, to jednak w  kodzie jest ono nie uży- wane. A szkoda. Można sobie wprowadzić taką obsługę, wystarczy zainicjalizować to przerwanie, w  naszym zestawie będzie to wejście INT2 i napisać najprostszą obsługę przerwania w  postaci ustawiania zwykłej flagi, która będzie następnie sprawdzana i zerowana w pętli głównej. Oznaczać ona będzie zdarzenie odbioru danych z  karty sieciowej. Można dzięki temu nieco upro- ścić oprogramowanie w  pętli głównej pro- gramu, zamiast wciąż badać, czy bufor jest pusty. Wprawdzie w  tak prostym przykła- dzie nie robi to wielkiej różnicy, jednak daje pewne możliwości dla przyszłej wygodniej- szej rozbudowy całego programu. Mirosław Kardaś Atnel
Artykuł ukazał się w
Marzec 2011
DO POBRANIA
Pobierz PDF Download icon
Elektronika Praktyczna Plus lipiec - grudzień 2012

Elektronika Praktyczna Plus

Monograficzne wydania specjalne

Elektronik czerwiec 2020

Elektronik

Magazyn elektroniki profesjonalnej

Raspberry Pi 2015

Raspberry Pi

Wykorzystaj wszystkie możliwości wyjątkowego minikomputera

Świat Radio lipiec 2020

Świat Radio

Magazyn użytkowników eteru

APA - Automatyka Podzespoły Aplikacje czerwiec 2020

APA - Automatyka Podzespoły Aplikacje

Technika i rynek systemów automatyki

Elektronika Praktyczna czerwiec 2020

Elektronika Praktyczna

Międzynarodowy magazyn elektroników konstruktorów

Praktyczny Kurs Elektroniki 2018

Praktyczny Kurs Elektroniki

24 pasjonujące projekty elektroniczne

Elektronika dla Wszystkich czerwiec 2020

Elektronika dla Wszystkich

Interesująca elektronika dla pasjonatów