Montaż i uruchomienie - moduł komunikacyjny magistrali RS485
Zgodnie ze schematem ideowym została opracowana płytka PCB, którą pokazuje rysunek 8 (strona TOP) oraz rysunek 9 (strona BOTTOM).
Montaż raczej nie powinien sprawiać problemów. Pomimo że znakomita większość elementów jest przeznaczona do montażu powierzchniowego, jedynie sam mikrokontroler może sprawić trochę kłopotów. Odrobina staranności w czasie prac montażowych zawsze owocuje mniejszymi problemami w trakcie uruchamiania modułu. Przed pierwszym włączeniem do prądu należy usunąć zworkę ze złącza P501 (odłączyć układ od wyjścia stabilizatora napięcia).
Dopiero po skontrolowaniu wytwarzanego napięcia, które powinno wynosić +5V, można założyć zworkę i w ten sposób zasilić cały układ. W przypadku, gdy napięcie wyjściowe nie jest właściwe, należy sprawdzić przede wszystkim staranność lutowania.
Kolejnym krokiem przy uruchamianiu modułu jest konfiguracja mikrokontrolera (właściwe ustawienie FUSE). W przypadku mikrokontrolera ATMEGA 164 są to: CKDIV8 należy wyłączyć, wybrać wariant sygnału zegarowego (SUT_CKSEL) jako zewnętrzny rezonator kwarcowy z częstotliwością w przedziale 3...8 MHz.
Przydatnym jest zaznaczenie (postawienie ptaszka) w pozycji EESAVE (kasowanie pamięci FLASH niezbędne przy każdym zaprogramowaniu mikrokontrolera nie będzie jednocześnie kasować pamięci EEPROM, w której są przechowywane dane konfiguracyjne). Wykorzystując do tworzenia oprogramowania bezpłatny pakiet narzędziowy AVR STUDIO 4, można go zastosować do zmiany ustawień mikrokontrolera. Oczekiwany zestaw konfiguracji FUSE pokazuje rysunek 10.
Zbudowane urządzenie pokazuje fotografia tytułowa.
Oprogramowanie - moduł komunikacyjny magistrali RS485
Po zaprogramowaniu mikrokontroler przy pierwszym uruchomieniu zapisze on do swojej pamięci nieulotnej (EEPROM) własne nastawy domyślne (wkompilowane w program). Łącząc (interfejs RS232) moduł z odpowiednim programem konfiguracyjnym uruchomionym w komputerze PC, mamy możliwość zmiany parametrów zapisanych w pamięci nieulotnej i dopasowania go do realizacji ściśle określonej funkcji.
Struktura bloku konfiguracyjnego pokazana jest na listingu 1. Uwaga! Wszystkie wymienione w artykule listingi są dostępne w Elportalu.
Blok konfiguracyjny - zawarte informacje
- MyDeviceID – własny identyfikator sterownika jako ciąg składający się maksymalnie z 8 znaków, domyślnie jest PROC4,
- SuperiorDeviceID – identyfikator urządzenia przyłączonego po stronie interfejsu RS232 składający się maksymalnie z 8 znaków, domyślnie jest pusty (nie występuje),
- SubDeviceID [ ChannelsArraySize ] – zbiór identyfikatorów sterowników SLAVE przyłączonych po stronie interfejsu RS485 jako 16 elementów składających się maksymalnie z 8 znaków, domyślnie wszystkie są puste,
- Password – hasło wymagane przy aktywnej konfiguracji parametrów sterownika składające się maksymalnie z 6 znaków, domyślnie jest MAGIC.
Blok danych konfiguracyjnych jest opatrzony dodatkowo odpowiednią sygnaturą oraz zawiera 16-bitową sumę CRC. Pozwala to oprogramowaniu sterownika na rozpoznanie sytuacji, gdzie dane nie są jeszcze zainicjowane lub uległy jakiemukolwiek uszkodzeniu. Funkcja odczytu danych z pamięci EEPROM jest pokazana na listingu 2. Zadaniem użytej funkcji ReadEEBlock jest fizyczne wczytanie danych z pamięci nieulotnej.
W przypadku, gdy wczytany blok danych ma niewłaściwą sygnaturę (przykładowo, gdy został użyty fabrycznie nowy procesor), generowana jest domyślna postać danych i zapisana w pamięci EEPROM. Do tego celu użyta jest funkcja WriteEEBlock, której zadaniem jest fizyczny zapis danych. W dalszej kolejności weryfikowana jest suma kontrolna. W sytuacji niepoprawnej sumy, analogicznie jak wyżej, generowany jest zestaw parametrów domyślnych połączony z zapisem do pamięci nieulotnej.
Funkcjonalność programu jest zrealizowana w jego funkcji głównej main, listing 3. Zawiera ona skonfigurowanie wykorzystanych zespołów mikrokontrolera oraz zainicjowanie środowiska pracy (wywołanie funkcji HardwareInit oraz EnvirInit), zainicjowanie zmiennych występujących w programie (SoftwareInit) oraz opisane wyżej wczytanie pamięci konfiguracyjnej EEBlockOperation.
Uruchomiony TIMER0 jako układ generujący przerwania od upływu czasu w zmiennej Events umieszcza flagi określonych zdarzeń. Jedno z nich jest przewidziane do zmiany stanu lampki LED (takie elektroniczne „bicie serca”). Inne, oznaczające upływ określonego interwału czasu, związane jest z przekazaniem żetonu kolejnemu modułowi SLAVE oraz kolejne związane z wypróżnieniem ewentualnej kolejki komunikatów oczekujących na wysłanie poprzez interfejs RS485 (SendQueueElement).
W przypadku wymiany danych poprzez interfejs RS232 (w pełni dupleksowy) nie ma żadnych ograniczeń komunikacyjnych. Nadanie czegokolwiek jest realizowane bez jakiejkolwiek synchronizacji. W przypadku interfejsu RS485 pojawiające się komunikaty do wysłania są buforowane i fizycznie wysłane w odpowiednim momencie. Pokazuje to listing 4.
Funkcje istotne w transmisji (nadawaniu) poprzez interfejs RS485
- USART485ReceiveMode – funkcja przełączająca układ interfejsu RS485 do funkcji odbiornika,
- USART485TransmitMode – funkcja przełączająca układ interfejsu RS485 do funkcji nadajnika,
- USART485SendSerial – funkcja jedynie gromadząca dane w buforze cyklicznym związanym z kanałem szeregowym, nie występuje operacja fizycznego wysłania danych,
- USART1_TX_vect – funkcja obsługi przerwania generowanego nadajnikiem układu UART, istotnym elementem jest przełączenie interfejsu w sytuacji zakończenia wysyłania znaków (USART485ReceiveMode), od tej chwili wysyłanie danych jest odcięte,
- SendQueueElement – funkcja inicjująca wysyłanie danych, jej wywołanie jest synchronizowane w czasie i realizacja zaczyna się od przełączenia układu interfejsu RS485 do funkcji nadajnika, wysłania pierwszego zgromadzonego w buforze znaku, pozostałe znaki zostaną wysłane w wyniku naturalnych przerwań obsługiwanych przez funkcję USART1_TX_vect, która na zakończenie transmisji wszystkich danych przełączy układ interfejsu ponownie do funkcji odbiornika.
Odbieranie danych niczym nie różni się od typowych rozwiązań pełnodupleksowych, gdyż w fazie nadawania danych odpowiedni rezystor w torze odbiornika wytworzy stan pasywny (stan ciszy komunikacyjnej), żadne dane nie zostaną odebrane, nie wymaga to jakiejkolwiek synchronizacji czasowej.
Przetwarzanie odebranych danych jest zrealizowane w pętli głównej programu jako instrukcje wywołania funkcji SerialRS232Instance.SerialService oraz SerialRS485Instance.SerialService. Koncepcję pokazuje rysunek 11 (w rzeczywistości są to funkcje z parametrem, który jest wskaźnikiem do „własnej struktury” i rysunek nie ujmuje wszystkich szczegółów a jedynie pokazuje ideę algorytmu).
Z obsługą interfejsu RS232 skojarzona jest instancja SerialRS232Instance, która między innymi ma wskazanie (pole SerialService) na odpowiednią w danym kontekście procedurę przetwarzającą znaki odebrane z kanału szeregowego.
Początkowo wskaźnik jest zainicjowany, wskazując na funkcję SelectDestOperation. Każde jej wywołanie (listing 5) sprawdza, czy w danym kanale szeregowym są dane do odczytu. Jeżeli tak, to pobierany jest kolejny znak i ewentualnie zostaje zapisany do obszaru przewidzianego na identyfikator adresata. Zauważmy, że wywołanie funkcji SerialInstance->SerialDataPresent prowadzi do różnych miejsc odpowiednich dla danego kanału szeregowego (USART-232DataPresent lub USART485DataPresent, patrz: funkcja SoftwareInit).
Podobnie jest rozwiązane odczytanie znaku z bufora cyklicznego związanego z odpowiednim kanałem szeregowym (USART232Get-Serial lub USART485GetSerial, patrz: funkcja SoftwareInit). W sytuacji, gdy z kanału szeregowego zostanie odczytany znak separatora pół polecenia (znak „:”), to zostanie zmienione wskazanie na funkcję obsługi w wyniku podstawienia SerialInstance->SerialService=SelectSourceService.
Od tej chwili wywołanie w pętli głównej funkcji main będzie prowadzić do nowego miejsca. Podobnie jak poprzednio odebrane znaki będą gromadzone w innej zmiennej, aż do napotkania kolejnego separatora, w wyniku którego po wykonaniu podstawienia SerialInstance->SerialService=SelectCommand, analizator polecenia przełączy się na wyselekcjonowanie ze strumienia danych szeregowych kolejnej części polecenia.
W ten sposób następuje „dokręcenie się” po ewentualnym przejściu przez fazę wyselekcjonowania parametrów polecenia, do znaku kończącego polecenie (znaku CR o kodzie 0d hex). Wtedy następuje dekodowanie i realizacja odebranego polecenia (które też jest odmiennie wskazane, patrz: SoftwareInit). Warto zauważyć, że gromadzone znaki jako poszczególne elementy polecenia również znajdują się „we własnej” instancji.
Takie rozwiązanie pozwala na dużą uniwersalność: mamy jeden kod programu, który obsługuje dwa kanały szeregowe, a różniące obsługę funkcje są w postaci odpowiednich wskazań w obrębie instancji. Finalnie, ten „chwyt” realizuje dość złożoną funkcjonalność, związaną z wyselekcjonowaniem z polecenia poszczególnych jego części. W przypadku przetwarzania polecenia pochodzącego z kanału szeregowego z interfejsem RS485 działanie algorytmu jest identyczne, w końcu „idzie” po tym samym kodzie, używana jest jedynie inna zmienna, będącą instancją związaną z drugim kanałem szeregowym.
O ile dotychczasowa analiza jest identyczna, realizacja odebranego polecenia jest w każdym przypadku inna. Pokazuje to przykładowo funkcja SelectCommand, widoczna na listingu 6, która po napotkaniu znaku końca polecenia przechodzi do jego realizacji. Następuje to w wyniku wykonania instrukcji SerialInstance->ExecuteCommand(). W zależności od instancji wniesionej w parametrach funkcji SelectCommand, wymienione w poprzednim zdaniu wywołanie funkcji prowadzi do odmiennej części programu (patrz: SoftwareInit). W wariancie interfejsu RS232 jego realizację pokazuje listing 7.
W pierwszej kolejności jest sprawdzenie, czy adresatem polecenia jest sam procesor komunikacyjny (porównanie identyfikatora uzyskanego z polecenia z identyfikatorem zapamiętanym w bloku konfiguracyjnym jest realizowane przez funkcję MeDestination). W przypadku braku zgodności ze wzorcem następuje porównanie z ogólnym identyfikatorem (GENERAL) w wyniku wywołania funkcji GeneralDestination. Pozytywny wynik porównania w którymkolwiek wariancie oznacza, że polecenie jest kierowane do procesora komunikacyjnego.
W przeciwnym wypadku jest przekierowane do interfejsu RS485 (bez weryfikacji poprawności adresata, gdyż zostanie ono rozpoznane przez jakikolwiek moduł SLAVE lub nie znajdzie żadnego punktu docelowego). Samo przekierowanie sprowadza się do ponownego utworzenia z części składowych kompletnego polecenia. Stosowane funkcje do transmisji w rzeczywistości kolejkują dane do wysłania, a realne wysłanie nastąpi w wyniku wystąpienia określonego zdarzenia czasowego (wspomniana wcześniej funkcja SendQueueElement).
Używając pewnej analogii z sieci komputerowych, procesor komunikacyjny spełnia funkcję bramy domyślnej dla danych wysyłanych przez serwer www. Sam serwer nie jest zainteresowany topologią połączeń obsługiwanych przez procesor komunikacyjny. Podobnie polecenia odebrane z magistrali RS485 mogą być przekierowane do interfejsu RS232 i finalnie trafić do serwera www. W tym przypadku znów można posłużyć się analogią: cokolwiek przyjdzie z magistrali RS484, może zostać przekierowane do interfejsu RS232 (źródłem takich poleceń dla serwera jest procesor komunikacyjny).
Ekwiwalentną funkcję dla interfejsu RS485 pokazuje listing 8.
Tu również w pierwszej kolejności weryfikowany jest adresat polecenia. Jeżeli jest ono kierowane do serwera (ogólnie do urządzenia nadrzędnego), następuje przesłanie go do kanału związanego z interfejsem RS232 (jako złożenie polecenia z poszczególnych jego składowych, gdyż oryginalne polecenia nie jest przechowywane). Cała ta funkcja stanowi swoisty filtr danych kierowanych do urządzenia nadrzędnego (serwera www).
Rozpoznanie polecenia (wywołanie funkcji ExecuteAssociatedCommand, listing 9) opiera się na zbudowanej w pamięci FLASH tabeli wiążącej tekst polecenia z funkcją odpowiedniej obsługi. Tę ideę pokazuje rysunek 12, a samą funkcję – listing 9.
Wykaz dostępnych poleceń z ich krótkim opisem zamieszczony jest w obszernej tabeli 1 dostępnej w Elportalu. W pamięci FLASH utworzona jest tablica o liczbie elementów odpowiadającej liczbie rozpoznawanych poleceń, wiążąca w jednym elemencie wskazanie na funkcję obsługi (zawiera jej adres w pamięci) oraz wskazanie na wzorzec polecenia (również zawiera adres).
W pętli w funkcji (listing 9) następuje przepisanie jednego elementu tablicy (cała tablica jest umieszczona w pamięci FLASH, toteż jeden jej element musi zostać przepisany do pamięci RAM). Pojedynczy element tablicy o strukturze pokazanej na rysunku 12 z jednej strony wskazuje na wzorzec (postać tekstową), a z drugiej strony na funkcję obsługi. Porównując tekst polecenia wniesiony jako parametr do omawianej funkcji ze wzorcem zapisanym w pamięci FLASH, może nastąpić wywołanie funkcji skojarzonej z tekstem polecenia. Dodanie kolejnego polecenia wiąże się jedynie z rozbudową tablicy kojarzącej wzorzec polecenia oraz wskazanie na funkcję przewidzianą do obsługi.
Każde polecenie wymaga odpowiedniego kontekstu. Rozkazy ogólne (jak przykładowo HELLO) mogą być wykonane w każdej sytuacji, inne, konfigurujące (ogólnie wpływające na zawartość pamięci nieulotnej) wymagają kontekstu zalogowania. Zmiana kontekstu następuje po wykonaniu polecenia CONNECT, które wymaga podania hasła. Stan po poprawnym wykonaniu polecenia CONNECT w poniższej tabeli jest określany jako .
W wyniku realizacji polecenia często jest generowana odpowiedź, również w formacie polecenia. Takie rozwiązanie pozwala na konfigurację modułu bez konieczności jego demontażu. W przypadku modułów typu SLAVE przyłączonych poprzez procesor komunikacyjny, to co odbierze on z interfejsu RS232 będzie przekierowane na magistralę RS485 i odwrotnie, jeżeli dane są adresowane do urządzenia nadrzędnego, procesor komunikacyjny przekieruje do interfejsu RS232.
Jeżeli urządzeniem nadrzędnym jest serwer www, ogólnie urządzenie obsługujące protokół komunikacyjny TCP, nic nie stoi na przeszkodzie, by utworzyć gniazdo TCP o numerze 24 (usługa TELNET) i powstaje możliwość zdalnej konfiguracji całej gałęzi sterowników. Taka możliwość nie jest zrealizowana w oprogramowaniu, a wspominam o tym, by zachęcić Czytelników do własnych poszukiwań i eksperymentów. To nie jest nieosiągalne, odrobina wysiłku przyniesie sukces.
Większość „narzędzi” jest już gotowa (obsługa sieci pozwala na utworzenia gniazda o numerze 24, Windows oferuje program będący klientem do obsługi TELENT). Nic, tylko spróbować.
Na koniec należy skonfigurować zbudowany moduł. Ponieważ jest to procesor komunikacyjny, wymaga on „wbicia” identyfikatorów modułów SLAVE, które będą obsługiwane na magistrali RS485, identyfikatora przydzielonego dla serwera www, zmiany własnego identyfikatora, ale to już zrobimy w kolejnej części.