Mikrokontrolery - Jak zacząć?

... czyli zbiór praktycznej wiedzy dot. mikrokontrolerów.
Pokazywanie postów oznaczonych etykietą tmf. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą tmf. Pokaż wszystkie posty

sobota, 9 kwietnia 2011

Compound literals, czyli jak wygodnie przekazywać parametry złożone

Autor: tmf
Redakcja: Dondu

Kurs języka C: Spis treści

Jakiś czas temu na Elektrodzie pojawiło się pytanie o to, w jaki sposób wygodnie przekazywać do funkcji złożony argument.

O co chodzi?
Spójrzmy na prototyp poniższej funkcji:
void foo(int *ptr);
Nasza funkcja foo przyjmuje jako argument wskaźnik na int. Skoro musimy jej przekazać wskaźnik, to znaczy, że jej wywołanie musi wyglądać tak jak poniżej:
int var=10;
foo(&var);
Wszystko pięknie, ale w powyższym trywialnym przykładzie, aby funkcji przekazać wartość 10, musimy utworzyć tymczasową zmienną var, nadać jej wartość, a następnie przekazać adres zmiennej var do funkcji foo. Z oczywistych powodów konstrukcje typu:
foo(&10);
lub też:
foo(10);
nie zadziałają wcale (błędy podczas kompilacji) lub nie zrobią tego, czego oczekujemy. Jak ten problem rozwiązać? Standard C99 wprowadza nam eleganckie rozwiązanie powyższego problemu pod postacią tzw. literałów złożonych (compound literals). Na czym one polegają? Ogólnie zamiast stworzyć tymczasowe zmienne poza wywołaniem funkcji, tak jak to pokazałem w powyższym przykładzie, możemy utworzyć tymczasowe „obiekty” w samym wywołaniu funkcji:
foo(&(int){10});

Jak widzimy zastosowanie takiego literału polega na podaniu w nawiasie okrągłym jego typu, a następnie w nawiasie klamrowym inicjalizatora typu – czyli po prostu wartości tworzonego obiektu.

Pamiętaj, że o ile zapis z wykorzystaniem literału złożonego jest krótszy i bardziej elegancki, to współczesne kompilatory są na tyle sprawne, że zarówno zapis z literałem złożonym, jak i zapis wykorzystujący zmienne tymczasowe powinien wygenerować identyczny kod asemblerowy. Stąd też stosowanie literałów złożonych w dużej mierze związane jest tylko z większą czytelnością zapisu kodu.

Obiekt będący literałem złożonym istnieje tylko w czasie wywołania funkcji foo – po jej zakończeniu jest automatycznie niszczony, nie możemy się już do niego odwoływać. Korzyści ze stosowania tego typu literałów jeszcze wyraźniej widać w przypadku struktur danych. Rzućmy okiem na kolejny przykład:

typedef union
{
   struct
   {
      uint8_t IB02      : 3;
      uint8_t AM        : 1;
      uint8_t ID        : 2;
      uint8_t TY        : 2;
      uint8_t DMode     : 1;
      uint8_t NoSync    : 1;
      uint8_t WMode     : 1;
      uint8_t DenMode   : 1;
      uint8_t IB12      : 1;
      uint8_t DFM       : 2;
      uint8_t VSMode    : 1;
   };
   uint16_t word;
} ssd2119_EntryMode_Reg;

Zdefiniowaliśmy sobie unię struktury anonimowej i typu uint16_t, który będzie reprezentował 16-bitową wartość jej pól. Dzięki temu mamy wygodny dostęp do poszczególnych pól (jak łatwo zauważyć pola te definiują bity sterujące jednego z rejestrów kontrolera SSD2119), a pole word będzie zawierać bieżącą reprezentację binarną całej struktury. Zadeklarujmy sobie teraz prototyp funkcji wykorzystującej powyższą unię:
void ssd2119_SendCmdWithData(uint8_t cmd, uint16_t data);
Jak widzimy, nasza unia przekazywana jest do funkcji jako argument data o typie uint16_t, a nie ssd2119_EntryMode_Reg. Dlaczego tak? Nasza funkcja ssd2119_SendCmdWithData wysyła do kontrolera LCD po prostu polecenie przekazywane jako argument cmd, a pole data zawiera tylko dane, które razem z poleceniem wysyłamy. Dane te są zależne od wybranego polecenia, w związku z tym ich typ może być inny niż ssd2119_EntryMode_Reg. Mamy już strukturę danych, a także funkcję wysyłającą polecenie do kontrolera, połączmy to teraz w całość. Klasycznie, polecenie zapisu do rejestru Entry Mode kontrolera wysłalibyśmy tworząc zmienną tymczasową:
ssd2119_EntryMode_Reg tmp={.DFM=0b11, .DenMode=1, .TY=0b01, .ID=0b11, .AM=0};
ssd2119_SendCmdWithData(0x11, tmp.word);
Ale możemy też to uczynić z wykorzystaniem literałów złożonych:
ssd2119_SendCmdWithData(0x11, (ssd2119_EntryMode_Reg){.DFM=0b11, .DenMode=1, .TY=0b01, .ID=0b11, .AM=0}.word);
W tym przypadku utworzony został obiekt tymczasowy o typie ssd2119_EntryMode_Reg, który inicjalizujemy analogicznie jak to robimy w przypadku inicjalizacji struktur/unii, a do funkcji, która oczekuje przecież typu uint16_t, przekazujemy wartość pola word reprezentującego naszą unię. Proste, prawda? W ten sposób uzyskujemy o wiele krótszy zapis i moim zdaniem bardziej przejrzysty.

Wykorzystanie takich literałów daje także inne korzyści, m.in. możliwość łatwej inicjalizacji typów złożonych, np. list – co wykorzystujemy np. podczas tworzenia menu i obiektów związanych z grafiką. Liczne przykłady wykorzystujące literały złożone wykorzystuję w swoich książkach „AVR. Praktyczne projekty” i „AVR. Układy peryferyjne”. Można się z nimi zapoznać pobierając darmowe przykłady (ze strony wydawnictwa Helion). Warto to zrobić, bo jak widać, literały złożone to prosty sposób na tworzenie przejrzystych programów. Chodzi tu nie tylko o uzyskaną zwartość kodu, ale także o niezaśmiecanie przestrzeni nazw zmiennymi tymczasowymi, co pozwala nam uniknąć wielu problemów.

Kurs języka C: Spis treści

Oceń artykuł.
Wasze opinie są dla nas ważne, gdyż pozwalają dopracować poszczególne artykuły.
Pozdrawiamy, Autorzy
Ten artykuł oceniam na:

środa, 30 marca 2011

ESP8266 WiFi: Bezpośrednia wymiana danych pomiędzy modułami


Autor: Tomasz Francuz
Redakcja: Dondu




Artykuł jest częścią cyklu: Moduły WiFi

W poprzednim odcinku udało nam się skonfigurować moduł ESP8266 do pracy w Internecie – podłączyliśmy się pod AP (Access Point), pobraliśmy adres, udało się (mam nadzieję) automatycznie dokonać update modułu.

Teraz czas na coś ciekawszego – wymianę danych pomiędzy modułami – w końcu m.in. w tym celu te moduły zakupiliśmy. Jednak zanim wymienimy dane musimy skonfigurować co najmniej dwa moduły WiFi pomiędzy którymi będziemy dane wymieniać.

Ze względu na wygodę oba moduły podłączymy do PC-ta, dzięki czemu łatwo będziemy mogli wysyłać polecenia i obserwować rezultaty. Czyli potrzebujemy dwa moduły z konwerterem RS232-USB.

A czy nie można prościej? Pamiętasz, jak pisałem, że wykorzystanie XMEGA jako konwertera USB-RS232 ma kilka zalet i lepiej w ten sposób podłączyć moduł do PC? Właśnie doszliśmy do jednej z zalet zastosowania XMEGA…

XMEGA jako wielokrotny konwerter USB-RS232

W drugiej części tego cyklu pokazując jak podłączyć ESP8266 do PC pokazałem prosty programik na XMEGA, który zamienia ją w konwerter USB-RS232. Ale XMEGA potrafi znacznie więcej. Jak być może wiesz, USB potrafi obsługiwać tzw. urządzenia złożone – co to takiego?

Normalnie jeśli podłączymy coś do USB to nam się pojawia nowe urządzenie w systemie. Lecz urządzenie USB może być widoczne jako kilka różnych typów urządzeń jednocześnie. W szczególności, kiedy podłączyliśmy XMEGA to pojawił się dodatkowy wirtualny port szeregowy. Ale możemy tak zaprogramować mikrokontroler (a raczej stworzyć taki deskryptor urządzenia USB), że ta sama XMEGA będzie emulowała kilka (1 do nawet 7) portów szeregowych. Ponieważ ten mikrokontroler dysponuje nawet 8 fizycznymi portami UART, możemy zmapować każdy wirtualny port szeregowy na fizyczny interfejs UART, do którego podłączymy moduł ESP8266!

Dzięki temu dwa moduły ESP8266 podłączymy do tej samej XMEGA. Będą one widoczne w systemie jako dwa oddzielne porty szeregowe, poprzez które będziemy się z modułami komunikować. Dzięki temu posiadając jeden komputer i XMEGA podłączymy sobie dwa moduły pomiędzy którymi będziemy wymieniać dane.

Nic nie stoi na przeszkodzie, aby moduły podłączyć do różnych komputerów PC – z tym, że nie jest to zbyt wygodne do testów i nic nam nie daje, poza zwiększeniem bałaganu na biurku.

Oczywiście jeśli nie używasz XMEGA to możesz podłączyć kolejne moduły przez USB przy pomocy innych sposobów, opisanych w części drugiej kursu.

Zacznijmy od części hardwarowej. Potrzebujemy tak jak poprzednio XMEGA podłączoną pod USB. Do USARTC0 tak jak do tej pory podłączymy moduł pierwszy (pin C2 – wyjście TxD modułu, pin C3 wejście RxD modułu), natomiast drugi moduł podłączymy pod USARTD0 (analogicznie pin D2 – wyjście TxD modułu, pin D3 wejście RxD modułu). Nic prostszego.

Pamiętaj, że każdy moduł WiFi może pobierać nawet 200 mA. Stąd też układ należy zasilić z zasilacza o odpowiedniej wydajności – można go także poprzez regulator LDO zasilić bezpośrednio z portu USB, co nam uprości układ.

Hardware mamy za sobą, czas na software. Tu nie będzie jakiś wielkich zmian – jedyne co musimy zrobić to nieznacznie zmodyfikować program pokazany w części drugiej niniejszego cyklu tak, aby XMEGA była widziana jako złożone urządzenie USB, emulujące dwa porty szeregowe.

W tym celu wystarczy w pliku conf_usb.h zmodyfikować linię:
# define  UDI_CDC_PORT_NB 1
 na:
# define  UDI_CDC_PORT_NB 2

Jak łatwo się domyśleć liczba 2 określa, że XMEGA będzie emulowała dwa porty szeregowe.

Jak pisałem XMEGA może emulować maksymalnie 7, co wynika z liczby obsługiwanych tzw. endpointów USB. Jednak nie przesadzaj z liczbą emulowanych portów – nasza implementacja pod każdy port rezerwuje sporo pamięci na bufor nadajnika i odbiornika. Jeśli uruchomimy ich zbyt wiele to tej pamięci nam zabraknie – nie jest to problem, musimy po prostu zmniejszyć wielkość używanych buforów. Ale jak to zrobić pozostawiam dociekliwym czytelnikom… Dość powiedzieć, że spokojnie możemy emulować dwa a nawet więcej wirtualnych portów szeregowych.

Druga zmiana, jaką musimy wprowadzić to modyfikacja identyfikatora PID urządzenia USB. W tym samym pliku dodajemy definicję:

#define  USB_DEVICE_PRODUCT_ID            USB_PID_ATMEL_ASF_TWO_CDC

Będziemy tu bazować na identyfikatorach PID przydzielonych poszczególnym urządzeniom przez firmę Atmel. Niezależnie, czy używamy urządzenia złożonego emulującego dwa, czy więcej porty szeregowe będziemy używać tego samego PID. Driver USB (a tak naprawdę tylko opis dostarczony przez Atmel, gdyż driver urządzeń CDC znajduje się w systemie operacyjnym), radzi sobie sam z taką sytuacją.

Drobna uwaga – jeśli zmienisz liczbę emulowanych portów szeregowych to być może Windows nie zaważy tej zmiany. Dlaczego? Ano twórcy systemu postanowili dodać mu trochę „inteligencji”, w efekcie Windows buforuje deskryptory urządzeń USB. Jeśli podłączymy urządzenie o tym samym VID/PID to Windows zamiast odczytać pełny deskryptor USB użyje deskryptora wcześniej zbuforowanego.

W efekcie, jeśli urządzenie zmieni liczbę emulowanych portów szeregowych to system tego nie zauważy. Rozwiązaniem jest zmiana PID lub odinstalowanie sterownika i ponowna jego instalacja. Jednak w naszym przypadku taki problem nie powinien wystąpić.

Teraz przejdźmy do pliku main.c. Tu musimy dodać po prostu obsługę kolejnego portu szeregowego i przekierowanie danych z USB na odpowiedni moduł. Zacznijmy od prostej tablicy, która mapuje wirtualne porty szeregowe na porty fizyczne w XMEGA:

USART_t *CDCtoUSART[UDI_CDC_PORT_NB]={&USARTC0, &USARTD0};    //Mapowanie kolejnych urządzeń USB CDC na porty USART

W efekcie wirtualny port szeregowy nr 0 będzie mapowany na USARTC0, a wirtualny port szeregowy nr 1 na USARTD0. Teraz czas na ich konfigurację. Tu widać piękno XMEGA w stosunku do starszych AVRów. Ponieważ wszystkie peryferia mają identyczne rejestry, wystarczy napisać jedną funkcję konfiguracyjną:

void usart_cfg(USART_t *usart)
{
 usart->CTRLB=USART_TXEN_bm | USART_RXEN_bm;   //Włącz nadajnik USART
 usart->CTRLC=USART_CHSIZE_8BIT_gc;            //Ramka 8 bitów, bez parzystości, 1 bit stopu
 usart->CTRLA=USART_RXCINTLVL_LO_gc;           //Włącz przerwanie odbiornika USART
}

Teraz po prostu wystarczy ją wywołać dla kolejnych interfejsów USART:

void usart_init()
{
 PORTC_OUTSET=PIN3_bm;
 PORTC_DIRSET=PIN3_bm;                          //Pin TxD musi być wyjściem
 PORTD_OUTSET=PIN3_bm;
 PORTD_DIRSET=PIN3_bm;                          //Pin TxD musi być wyjściem 
 
 usart_cfg(&USARTC0);
 usart_cfg(&USARTD0);
}

Dalej jest już z górki, tak jak poprzednio piszemy funkcje przekazujące odbierane z modułów znaki na odpowiednie wirtualne porty szeregowe:

ISR(USARTC0_RXC_vect)
{
 udi_cdc_multi_putc(0, USARTC0_DATA);
}

ISR(USARTD0_RXC_vect)
{
 udi_cdc_multi_putc(1, USARTD0_DATA);
}

i odwrotnie, kod przekazujący odebrane z PC dane na odpowiednie interfejsy USART:

 while (1)
 {
  if(udi_cdc_multi_is_rx_ready(0))
   USART_putchar(CDCtoUSART[0], udi_cdc_multi_getc(0));  //Przepisz to co otrzymałeś na USART sterujący modułem
  if(udi_cdc_multi_is_rx_ready(1))
   USART_putchar(CDCtoUSART[1], udi_cdc_multi_getc(1));  //Przepisz to co otrzymałeś na USART sterujący modułem
 }

Jak pewne zauważyłeś, zmieniły się nieco funkcje współpracy z USB – używamy udi_cdc_multi_putc zamiast udi_cdc_putc, udi_cdc_multi_is_rx_ready zamiast udi_cdc_is_rx_ready i udi_cdc_multi_getc zamiast udi_cdc_getc. Pomiędzy tymi funkcjami jest tylko jedna różnica - funkcje _multi_ przyjmują jako dodatkowy argument numer emulowanego wirtualnego portu szeregowego (w naszym przypadku będzie to 0 lub 1).

I to tyle – prawda, że proste? Pokazaną obsługę wielu wirtualnych portów szeregowych możemy wykorzystać szerzej, także w swoich aplikacjach – np. jeden port może służyć do normalnej wymiany danych, a drugi do przesyłania informacji o stanie aplikacji – co ułatwia jej debugowanie.


Do pobrania

Do pobrania pełny kod przykładu dla Atmel Studio: ESP-ESP-PC.ZIP (kopia)



Po zaprogramowaniu mikrokontrolera i jego podłączeniu do USB w menagerze urządzeń powinieneś zobaczyć dwa wirtualne porty szeregowe:


Menedżer urządzeń - widok na nowe porty szeregowe ESP8266
Menedżer urządzeń - widok na nowe porty szeregowe

U mnie akurat były to porty COM16 i COM15 (nazywają się one Communication Device Class ASF example2) W twoim przypadku zapewne będą to porty szeregowe o innym numerze. W razie czego numer portu możesz zmienić we właściwościach urządzenia – nam jednak do niczego taka zmiana nie jest potrzebna.

Co dalej?

Aby komunikować się z każdym modułem odpalimy sobie dwie instancje programu RealTerm (lub innego terminala, który używasz). Każdy terminal połączymy z jednym z wirtualnych portów szeregowych:


Terminale połączone z modułami ESP8266
Terminale połączone z modułami

Dzięki temu w każdym terminalu będą informacje powiązane z jednym z podłączonych do PC-ta modułów ESP8266.

Mamy już podłączone do PC-ta dwa moduły ESP8266 – przy czym nowy moduł jest nieskonfigurowany. Zanim więc przejdziemy dalej musimy go skonfigurować tak jak to robiliśmy w poprzedniej części, czyli:
  • przełączyć go w tryb klienta (stacji) WiFi,
  • podłączyć go pod AP (access point),
  • wykonać update firmware.
Jak to zrobić już wiesz, więc potraktuj konfigurację nowego modułu jako powtórkę i ćwiczenie.
Ponieważ będziemy w dalszej części nawiązywać najprostsze połączenie między dwoma modułami typu point-to-point, wygodniej będzie, jeśli modułom nadamy stałe adresy IP – nie będziemy korzystali z DHCP.

W tym celu pierwszemu modułowi nadamy adres 192.168.1.10, a drugiemu 192.168.1.11 (pamiętasz, że możemy to zrobić poleceniem AT+CIPSTA_DEF):

AT+CIPSTA_DEF="192.168.1.11","192.168.1.1","255.255.255.0"

Oczywiście adresy i maskę podsieci musisz dostosować do swojej konfiguracji sieci. Jeśli używasz innej konfiguracji sieci, to dostosuj do niej wszystkie adresy użyte w tym artykule.

Skoro mamy już skonfigurowane moduły, to przetestujmy, czy się wzajemnie „widzą”. W tym celu na jednym z modułów użyjemy bardzo pomocnego polecenia ping podając adres drugiego modułu:

AT+PING="192.168.1.10"

Jeśli wszystko jest ok, to dostaniemy odpowiedź:

+PING="192.168.1.10" +9
                                                                                
OK

Jeśli pomiędzy modułami nie ma łączności to uzyskamy:

+PING="192.168.1.10" +TIMEOUT
                                                                                
ERROR

Czym błąd może być spowodowany? Przede wszystkim:
  • brakiem podłączenia do AP,
  • złą konfiguracją sieci (błędny adres IP, błędna maska podsieci),
  • niewłaściwą konfiguracją modułu,
  • jeszcze jedną potencjalną przyczyną może być po prostu brak zasięgu – to łatwo sprawdzisz listując lokalne AP i sprawdzając parametr RSSI o którym pisałem w poprzedniej części. Niewykluczone też, że w przypadku braku zasięgu moduł po prostu nie zobaczy na liście naszego AP.
  • kiepskim zasilaniem modułu. Pamiętaj, że dwa moduły pobierają impulsowo ponad 400 mA, napięcie musi być dobrze filtrowane – wszelkie zakłócenia znacznie ograniczają zasięg modułu.

Połączenie można sprawdzić znanym nam już poleceniem AT+CIPSTATUS, oba moduły powinny zwrócić:

+CIPSTATUS STATUS:2
                                                                                
OK

Znaczenie statusów:

  • Status 2 oznacza, że moduł jest połączony z AP i ma prawidłowy adres IP.
  • Status 3 oznacza, że jest połączony z AP, ale nie ma adresu IP (albo go nie ustawiliśmy, albo nie działa serwer DHCP), 
  • Status 4 oznacza, że moduł nie jest połączony z AP. Oczywiście prawidłowe połączenie nie wyklucza błędnej konfiguracji sieci (adresu IP lub maski) – zawsze to dodatkowo musimy sprawdzić.


W końcu przesyłamy dane

Doszliśmy do momentu, w którym jesteśmy gotowi po raz pierwszy przesłać dane pomiędzy dwoma modułami. Aby zrozumieć nawiązywanie połączeń musimy dowiedzieć się czegoś więcej o komunikacji w sieci TCP/IP.

Urządzenia w sieci udostępniają tzw. usługi – taką usługa jest np. http, ftp, itd. Wiemy, że żeby połączyć się z jakąś stroną www musi ona być umieszczona na serwerze http. Nie inaczej jest w przypadku, gdy wymieniamy dane. Urządzenie (klient) wysyła je do innego urządzenia (serwer). Aby było to możliwe serwer musi nasłuchiwać połączeń ze strony klienta.

Musimy więc nasz moduł skonfigurować tak, aby nasłuchiwał połączeń. W sieci TCP/IP każde urządzenie ma do dyspozycji 65536 portów (numerowane 0-65535). Nasz serwer może nasłuchiwać na dowolnym z nich – no prawie dowolnym. Musimy pamiętać, że porty poniżej 1024 to porty specjalne, mające przypisane określone usługi, np. port 80 to http. Możemy je wykorzystać lecz musimy się liczyć z tym, że np. klient http (dowolna przeglądarka) będzie oczekiwać, że na tym porcie nasłuchuje serwer www. Ale to właściwie żaden problem.

Konfiguracja serwera

Zacznijmy od konfiguracji serwera – czyli modułu, który będzie oczekiwał na dane przesyłane z innych modułów – klientów. W tym celu musimy poznać parę nowych poleceń AT.

Aby było możliwe utworzenie serwera, moduł na którym chcemy to zrobić musi mieć możliwość przyjmowania wielu połączeń – w końcu do serwera może łączyć się wielu klientów, prawda? W tym celu wysyłamy polecenie:

AT+CIPMUX=1

na co moduł powinien odpowiedzieć:

+CIPMUX=1                                                                             
OK

Teraz czas na uruchomienie serwera, w naszym przypadku będzie nasłuchiwać na porcie 81:

AT+CIPSERVER=1,81

na co otrzymujemy:

IPSERVER=1,81                                                                                 
OK

Od tego momentu nasz serwer nasłuchuje na porcie 81. Łatwo możemy się o tym przekonać – w tym celu uruchamiamy przeglądarkę i w pasku adresu wpisujemy:

192.168.1.10:81

Powyższy adres to adres IP modułu, na którym uruchomiliśmy serwer, a 81 to port, na którym nasłuchuje. W efekcie w terminalu połączonym z serwerem zobaczymy informację o nawiązaniu połączenia:

0,CONNECT

i przesłane przez przeglądarkę dane:

+IPD,0,289:GET / HTTP/1.1
HOST: 192.168.1.10:81
USER-AGENT: MOZILLA/5.0 (WINDOWS NT 6.1; WOW64; RV:38.0) GECKO/20100101 FIREFOX/
38.0
ACCEPT: TEXT/HTML,APPLICATION/XHTML+XML,APPLICATION/XML;Q=0.9,*/*;Q=0.8
ACCEPT-LANGUAGE: EN-US,EN;Q=0.5
ACCEPT-ENCODING: GZIP, DEFLATE
CONNECTION: KEEP-ALIVE

W informacji o połączeniu widzimy, że połączenie zostało nawiązane (CONNECT), oraz otrzymujemy identyfikator połączenia – w tym przypadku 0. Identyfikator połączenia jest dla nas niezbędny – pamiętamy, że z serwerem na raz może łączyć się wiele klientów. Żeby wiedzieć które dane skąd pochodzą potrzebujemy identyfikator.

Kolejna część to dane przesłane przez przeglądarkę – są one poprzedzone nagłówkiem +IPD,0,289:

Co on oznacza?

  • +IPD - informuje nas, że mamy połączenie, w którym przesłano dane (IP data), 
  • 0 - identyfikator połączenia wynosi 0, 
  • 289 - to łączna liczba odebranych bajtów. Po dwukropku znajdują się dane wysłane przez przeglądarkę. Teraz, jeśli zamkniemy okno przeglądarki lub naciśniemy przycisk przerwania połączenia, moduł wyśle informację:


0,CLOSED

czyli że połączenie o identyfikatorze 0 zostało zamknięte (CLOSED).


Jeśli z modułem połączą się kolejni klienci to uzyskają oni kolejne identyfikatory, a więc 1, 2, 3, itd.

Pamiętaj jednak, że sam identyfikator nie identyfikuje jednoznacznie klienta – jeśli połączenie zostanie zamknięte, to kolejny klient dostanie ten sam identyfikator. Musimy więc śledzić stan połączeń, lub stosować inne metody, o których napiszę później.


Proste, prawda? A więc serwer mamy skonfigurowany, czas na klienta.


Konfiguracja klienta

Konfiguracja klienta jest równie prosta. Najpierw musimy nawiązać połączenie, my zrobimy transparentny most RS232-WiFi przy pomocy protokołu TCP (o tym nieco więcej będzie w przyszłości):

AT+CIPSTART="TCP","192.168.1.10",81

Podany adres IP to adres naszego serwera, który konfigurowaliśmy w poprzednim kroku, a 81 to oczywiście port, na którym nasłuchuje. W odpowiedzi uzyskujemy:

+CIPSTART="TCP","192.168.1.10",333                                            
CONNECT
                                                                       
OK

Od tej chwili mamy połączenie z serwerem, o czym nas serwer informuje wyświetlając w jego terminalu:

0,CONNECT

Teraz musimy nasze połączenie skonfigurować. Ponieważ robimy transparentny most, to wybieramy odpowiednią opcję:

AT+CIPMODE=1

co informuje, że przesyłamy dane tak jak lecą (pass through). Od tej chwili możemy przesyłać dane. W tym celu wydajemy jeszcze jedno polecenie:

AT+CIPSEND

Po jego wydaniu zobaczymy w terminalu znak zachęty ‘>’ – od tej chwili wszystko, co wpiszemy do terminala zostanie przesłane do serwera.

Pamiętaj, że wysyłając komendy AT automatycznie dołączaliśmy do nich znaki CR+LF. Przy przesyłaniu danych nie chcemy tego robić – odznacz więc odpowiednią opcję w terminalu.

Wpiszmy w terminalu klienta:

NA SERWERZE POJAWIA SIE WPISANY TU TEKST

W terminalu serwera powinniśmy zobaczyć:

+IPD,0,42:NA SERWERZE POJAWIA SIE WPISANY TU TEKST

czyli ponownie +IPD,0,42: informuje nas, że otrzymaliśmy informację z ID połączenia 0 o długości 42 bajtów i dalej znajdują się przesłane dane.

W tym trybie klient przesyła do serwera wszystko, co mu wyślemy przez interfejs UART. Doskonale się to nadaje do przesyłania danych. Stworzyliśmy więc most radiowy, po którym przesyłamy dane.

A jak wrócić do trybu poleceń AT? 

W tym celu musimy nadać +++ ale co ważne znaki te muszą być poprzedzone co najmniej jednosekundową przerwą w nadawaniu, po nich też przez sekundę nic nie możemy do modułu wysłać. Pamiętaj, aby po +++ nie wysyłać znaków CR+LF – jeśli je wyślemy to polecenie nie zostanie zinterpretowane jako koniec transmisji danych.

Po wysłaniu +++ na serwerze nic się nie pojawi – nie jest też kończone połączenie – jedyne co osiągnęliśmy to wyjście klienta z trybu przesyłania danych i przejście ponownie w tryb poleceń AT. Aby zakończyć połączenie musimy je jawnie zamknąć:

AT+CIPCLOSE

co spowoduje w terminalu klienta wyświetlenie:

+CIPCLOSE CLOSED
                                                                      
OK

A na terminalu serwera:

0,CLOSED

Proste, prawda? Oczywiście to nie koniec możliwości przesyłania danych, ale jak na pierwszy raz wystarczy. W kolejnym odcinku coś więcej o typach połączeń i pakietach.
Oceń artykuł.
Wasze opinie są dla nas ważne, gdyż pozwalają dopracować poszczególne artykuły.
Pozdrawiamy, Autorzy
Ten artykuł oceniam na:

wtorek, 29 marca 2011

F_CPU – gdzie definiować?


Autor: tmf
Redakcja: Dondu


Problem braku definicji zegara taktującego mikrokontroler występuje bardzo często wśród początkujących, niezależnie jaki kompilator wykorzystują. Zapominanie o tak istotnej sprawie powoduje nieprawidłowe działanie mikrokontrolera po zaprogramowaniu go skompilowanym kodem.

Temat ten jest rozszerzeniem tematu poruszonego tutaj: Błędy kompilacji programu

Na przykładzie kompilatora języka C (GNU Compiler Collection) pokażę, jakie problemy spotka nieuważny programista mikrokontrolerów.

Rzućmy okiem na prosty program:
#include <util/delay.h>

int main()
{
   _delay_us(100);   //opóźnienie 100 mikrosekund
}

Próba jego kompilacji spowoduje wygenerowanie następującego ostrzeżenia:

c:/winavr-20100110/lib/gcc/../../avr/include/util/delay.h:85:3: warning: #warning "F_CPU not defined for <util/delay.h>"


Część początkujących programistów pewnie zignoruje to ostrzeżenie, co szybko spowoduje lawinę pytań:

Dlaczego moje opóźnienia nie są takie jakie być powinny?
Z prostej przyczyny, braku definicji częstotliwości zegara taktującego Twój mikrokontroler, co w tej sytuacji jest automatycznie rozwiązane następująco:

Przy braku definicji F_CPU, dołączenie nagłówka delay.h oprócz wygenerowania ostrzeżenia skutkuje zdefiniowaniem F_CPU o wartości 1MHz.

Jest to spowodowane definucją F_CPU w pliku delay.h:
/* prevent compiler error by supplying a default */
#ifndef F_CPU
# warning "F_CPU not defined for <util/delay.h>"
# define F_CPU 1000000UL
#endif

która zabezpiecza kompilator przez przyjęcie wartości domyślnej równej 1MHz. Innymi słowy jeżeli przed linkowaniem pliku delay.h F_CPU nie jest zdefiniowane, zostanie ono ustawione na 1MHz.


Czytający tego bloga już wiedzą, żeby każdy warning traktować jak błąd – dobrze napisany program po kompilacji nie powinien generować żadnych ostrzeżeń i co zrozumiałe – błędów :-)

Problemem w powyższym kodzie jest brak definicji F_CPU – symbolu zawierającego częstotliwość taktowania procesora. Bez tej informacji nie da się wyliczyć opóźnień, dlatego kompilator GCC przyjmuje wartość domyślą równą 1MHz.

W wielu przykładowych programach dostępnych w Internecie można spotkać się z sytuacją w której F_CPU jest definiowane bezpośrednio w kodzie programu – jest to ryzykowne rozwiązanie – dlaczego? Pokażę to na przykładzie.

Oczywiście w tak banalnym kodzie jak powyższy dodanie definicji F_CPU przed włączeniem nagłówka delay.h jest w zupełności poprawne i wystarczające:

#define F_CPU 8000000UL  //definiujemy F_CPU na 8MHz
#include <util/delay.h>

int main()
{
   _delay_us(100);   //opóźnienie 100 mikrosekund
}


Stwórzmy więc mniej trywialny przykład, ale za to częściej występujący w życiu. Potrzebujemy dwa pliki źródłowe zawierające kod programu. Nasz pierwszy plik o nazwie test111.c zawiera:

#define F_CPU 8000000UL  //definiujemy F_CPU na 8MHz
#include <util/delay.h>

void blink();

int main()
{
   blink();
   _delay_us(100);   //opóźnienie 100 mikrosekund
}

Jak widać w funkcji main wywoływana jest funkcja blink(), której prototyp dla uproszczenia został zadeklarowany w tym samym pliku (pamiętaj, że to tylko dla uproszczenia – normalnie należałoby stworzyć plik nagłówkowy zawierający prototyp funkcji). Potrzebny jest także drugi plik źródłowy, zawierający definicję funkcji blink() – test112.c:

#include <avr/io.h>
#include <util/delay.h>

void blink()
{
   PORTA |= (1<<PA0);    //ustaw bit 0 portu A
   _delay_ms(100);       //opóźnienie 100 mikrosekund
   PORTA &= ~(1<<PA0);   //wyzeruj bit 0 portu A
}


Dla uproszczenia załóżmy, że oba pliki stanowią część projektu AVR Studio 4 – w ten sposób nie musimy się martwić o generowanie tych całych makefile i resztę. Po zapisaniu i kompilacji obu plików mamy upragnione:

Build succeeded with 0 Warnings...


Ale czy aby na pewno?
Zmieńmy cokolwiek w pliku test112.c (wystarczy dodać spację) i zapiszmy ponownie ten plik. Ponowna kompilacja i ... rozczarowanie:

Build succeeded with 1 Warnings...

Co do diaska jest grane? Rzut okiem na okno build i widzimy coś dziwnego:

c:/winavr-20100110/lib/gcc/../../avr/include/util/delay.h:85:3: warning: #warning "F_CPU not defined for "


Dziwna sprawa, w końcu w test111.c mamy F_CPU zdefiniowane, więc o co chodzi? Niby definicja F_CPU jest globalna, ale nie do końca, co jest związane ze sposobem kompilacji programu.

Każdy plik źródłowy kompilowany jest oddzielnie – stanowi odrębną jednostkę kompilacji. W sytuacji, w której zmieniamy zawartość tylko jednego pliku wchodzącego w skład złożonego projektu - tylko ten jeden plik jest rekompilowany, do linkowania całego programu zostaną wykorzystane pliki obiektowe otrzymane w wyniku wcześniejszych kompilacji (w naszym przykładzie plik test111.o).

Problem w tym, że F_CPU zostało zdefiniowane w pliku test111.c, ale nie w pliku test112.c – w efekcie jeśli najpierw nie jest kompilowany plik test111.c, to symbol F_CPU nie jest znany i pojawia się ostrzeżenie.


Czy warto się nim przejmować?
Zdecydowanie tak. W naszym przypadku powstanie aplikacja, która będzie miała chimeryczną definicję symbolu F_CPU. Funkcje opóźniające z pliku test111.c zostaną wyliczone dla wartości symbolu F_CPU wynoszącej 8MHz, podczas kompilacji test112.c symbol ten nie jest znany, więc zostanie przyjęta jego wartość domyślna wynosząca 1MHz – no i mamy cyrk.

Są też inne – mniej istotne powody, aby F_CPU nie definiować w kodzie programu.


Jak więc powyższy problem należy poprawnie rozwiązać? 
Ano należy przekazać definicję symbolu przy każdym wywołaniu gcc, stosując odpowiedni argument wywołania (opcja –D kompilatora):

-DF_CPU=8000000UL

Dzięki temu każdy plik źródłowy będzie skompilowany z właściwą wartością symbolu F_CPU. Opcję taką możemy umieścić w makefile (jeśli stosujemy własny), lub prościej, wybierając w AVR Studio 4 opcje Project/Configuration Options – w zakładce General wpisujemy w polu Frequency wybraną częstotliwość taktowania procesora:

Rys. 1 - AVR Studio 4

Możesz także definicję zegara zamieścić w Custom Options:

Rys. 2 - AVR Studio 4

Począwszy od AVR Studio 5 definiowanie F_CPU odbywa się podobnie jak na rys 2 w AVR Studio 4:

Rys. 3 - AVR Studio 5 i późniejsze Atmel Studio

Więcej informacji na temat Atmel Studio znajdziesz tutaj: Atmel Studio - zintegrowane IDE


Optymalizacja
Do prawidłowego działania funkcji _delay_ms() oraz _delay_us() musi być włączona optymalizacja!

W kodzie zostaw w celach archiwalnych
Z powodów archiwalnych warto jednak w kodzie zamieścić informację, dla jakiego zegara kod został przygotowany. Na ten temat znajdziesz informacje tutaj: Błędy kompilacji programu

Więcej o opóźnieniach znajdziesz w książce „Język C dla mikrokontrolerów AVR. Od podstaw do zaawansowanych aplikacji” – lub być może w przyszłości na tym blogu  :-)

Oceń artykuł.
Wasze opinie są dla nas ważne, gdyż pozwalają dopracować poszczególne artykuły.
Pozdrawiamy, Autorzy
Ten artykuł oceniam na:

poniedziałek, 28 marca 2011

Konkurs weekendowy - zadania Deucaliona i Tomasza Francuza


Autor: Dondu


Mały konkurs programistyczny, a właściwie dwa :-)


Generujemy dźwięki

... zadanie Deucaliona.

Jednym z efektownych zastosowań mikrokontrolerów jest odgrywanie za ich pomocą dźwięków i melodyjek. Spróbujmy więc zrobić mały konkurs na program, który realizuje to zadanie.

Co możemy wykorzystać? Prawie wszystko, czyli:
  • dźwięk możemy odtwarzać przy pomocy DAC, PWM, drabinki rezystorowej lub zewnętrznych układów,
  • do sterowania tym wszystkim możesz użyć dowolny mikrokontroler, a jeśli obejdziesz się bez mikrokontrolera to tym lepiej,
  • program powinien odgrywać wybrany przez autora dźwięk, muzykę, głos, cokolwiek.


Ale żeby nie było zbyt łatwo, oceniamy:
  • oryginalność konstrukcji,
  • powinieneś pokazać i krótko skomentować wykorzystane kluczowe funkcje do generowania dźwięku,
  • oczywiście im lepszej jakości dźwięk uzyskasz tym lepiej,
  • jeśli stosujesz jakieś nietypowe rozwiązania elektryczne dobrze by było, gdybyś je pokazał.


Inne uwagi:
  1. Do realizacji projektu możesz użyć gotowe minimoduły (to nie konkurs na lutowanie), możesz użyć płytki stykowej, możesz wszystko polutować pająkiem. Im to wszystko dziwniej będzie wyglądało tym lepiej.
  2. Pamiętaj, preferujemy własne (nawet jeśli nie doskonałe) rozwiązania. Projekty typu copy&paste z Internetu będą dyskwalifikowane, a znamy sporo sieciowych projektów :-)
  3. W celu końcowej oceny prześlij nam zdjęcia projektu oraz nagrany plik dźwiękowy odtworzony przy pomocy Twojego urządzenia. Ponieważ nagrywać może każdy PCet nie powinno być z tym problemu.
  4. Projekt musi być zrealizowany w języku C/C++. Nie dopuszczamy innych języków, typu Bascom, asembler itd.




Odświeżanie + synchronizacja + ramka = ...?

... zadanie Tomasza Francuza.

Drugi konkurs dla osób, które nie mają (albo mają) odpowiedni sprzęt.

Ponieważ nie każdy chce lutować (lub ma na to czas i odpowiedni sprzęt), proponuję drugi konkurs. Można go zrealizować w oparciu o sprzęt (dowolny minimoduł, procesor, pająk itd.), lub też bez sprzętu – w tym celu wystarczy symulator wybranego procesora, np. dostępny w pakiecie Atmel Studio.

Jeśli nie wiesz jak się posługiwać symulatorem, lub natrafiłeś na jakiś problem w czasie symulacji to daj nam znać – pomożemy. Ale tylko w kwestiach technicznych, nie pomagamy rozwiązać samego zadania. Rozwiązanie podamy po konkursie.

Co jest naszym celem w tej części?

Drugim efektownym zastosowaniem mikrokontrolera jest generowanie obrazu TV/VGA. Projektów tego typu można znaleźć sporo w Internecie, w tym konkursie dla uproszczenia zrealizujemy tylko część zadania – wygenerujemy właściwe impulsy synchronizacji dla monitora VGA.

Dlaczego dla VGA? Ano dlatego, że tak jest prościej – impulsy synchronizacji poziomej i pionowej są transmitowane osobnymi przewodami. Tak więc program powinien generować właściwe impulsy synchronizacji dla monitora VGA w trybie 640 na 400 pikseli zgodnie ze specyfikacją:

  • odświeżanie 70 Hz,
  • linia:
  • - czas trwania linii 31,777 µs,
  • - front porch – 0,635 µs,
  • - synchronizacja pozioma 3,8133 µs,
  • - czas trwania widocznej części linii 25,422 µs,
  • - back porch 1,906 µs
  • ramka:
  • - cała ramka 449 linii,
  • - front porch 12 linii (0,38133 ms),
  • - synchronizacja pionowa 2 linie (0,06355 ms),
  • - back porch 35 linii (1,11221 ms),
  • - część widoczna obrazu 400 linii (12,711 ms).

Czyli ma to wyglądać mniej więcej tak jak na rysunku poniżej:




My ograniczamy się wyłącznie do generowania impulsów synchronizacji (pozycje wytłuszczone), pamiętając, że muszą one wystąpić w odpowiednim momencie. Program można przetestować w symulatorze, można też wyjście mikrokontrolera podłączyć do monitora w celu sprawdzenia poprawności działania, np. tak:




Łączymy wyłącznie sygnały horizontal sync i vertical sync.

Uwaga! Większość monitorów wymaga, aby w czasie synchronizacji sygnały koloru (Red, Green i Blue) miały wartość 0V, stąd też jeśli chcesz sprawdzić swój program, możesz je na stałe podłączyć do masy układu.


Ocena nadesłanych rozwiązań

Jednak nie zniechęcaj się – miało być bez lutowania więc będzie. W tym konkursie chodzi o najlepszą koncepcję, a nie o uzyskanie konkretnych efektów.

Co będzie oceniane?:
  1. Oczywiście to, czy program ma szansę wygenerować poprawne impulsy synchronizacji.
  2. Pomysłowość.
  3. Maksymalne wykorzystanie zasobów sprzętowych mikrokontrolera.
  4. Prostota.
  5. Obciążenie CPU – im mniej czasu spędzi na generowaniu impulsów tym lepiej.

Pamiętaj, że podobnie jak monitory, my też dopuszczamy pewną tolerancję podanych wcześniej czasów synchronizacji – powiedzmy, że 5% jest dopuszczalne.

Co należy pokazać?
  • kod realizujący zadanie, abyśmy mogli go ocenić,
  • w przypadku mniej oczywistych i nietypowych rozwiązań schemat blokowy,
  • krótki opis jak to wszystko działa.

Jeśli uważasz, że nie dasz sobie z tym rady, lub nie znasz języka C, dopuszczamy rozwiązania typu sam opis i czytelny rysunek jak to ma działać. W takim przypadku abyśmy mogli ocenić koncepcję opis i rysunek muszą być bardziej szczegółowe, tak aby było wiadomo jak to ma działać.

Jeżeli realizujesz projekt, musi być zrealizowany w języku C/C++ (lub przedstawiona tylko koncepcja działania programu). Nie dopuszczamy innych języków, typu Bascom, asembler itd.


Oceń artykuł.
Wasze opinie są dla nas ważne, gdyż pozwalają dopracować poszczególne artykuły.
Pozdrawiamy, Autorzy
Ten artykuł oceniam na:

piątek, 25 marca 2011

Jak czytać noty katalogowe mikrokontrolerów AVR (cz. I)



Autor: tmf
Redakcja: Dondu


Artykuł składa się z dwóch części: Jak czytać noty katalogowe mikrokontrolerów AVR (cz. II)

Jednym z częstszych problemów z którymi spotykają się początkujący elektronicy jest interpretacja not katalogowych układów. W gąszczu różnych magicznych oznaczeń łatwo się pogubić, w efekcie szybko się zniechęcamy i porzucamy to niezwykle cenne źródło informacji.

W tym mini cyklu pokażę jak czytać noty mikrokontrolerów AVR, na przykładzie XMEGA. Dlaczego XMEGA? Bo w ich notach występuje wszystko co w notach innych AVR-ów, a w dodatku są to następcy popularnej rodziny ATMega.

Użytkownicy AVR-ów przywykli do tego, że wszelkie informacje na temat użytego procesora znajdują się w jego nocie katalogowej. W przypadku XMEGA jest nieco inaczej, informacje na temat rodziny (A, B, C, D, E) znajdują się w nocie ogólnej opisującej rodzinę, natomiast informacje szczegółowe, w tym charakterystyka elektryczna układu w nocie konkretnego procesora. Jednak w takiej nocie nie znajdziemy szczegółowego opisu np. peryferii – będzie tylko skrótowa informacja, że dany mikrokontroler określony układ peryferyjny posiada.

W efekcie w nocie „XMEGA A MANUAL” znajdziemy dokładny opis wszystkich peryferii rodziny XMEGA A, łącznie z opisem rejestrów i ich poszczególnych bitów. Ale w tej nocie na próżno szukać informacji np. o maksymalnym prądzie IO pinu, czy też układach peryferyjnych dostępnych np. w XMEGA128A1. Tego typu informacje znajdziemy w nocie szczegółowej ATXMEGA128A1.

Warto zauważyć, że procesory należące do tej samej rodziny, np. wspomnianej A1, różniące się wyłącznie wielkością dostępnej pamięci, mają wspólną notę katalogową. Stąd informacje na temat charakterystyki mikrokontrolerów XMEGA128A1, 64A1, 192A1, 256A1 i 384A1 znajdziemy w tej samej nocie.

Osoby korzystające z Atmel Studio mają nieco ułatwioną sytuację. Jeśli klikniemy na typ procesora na listwie narzędziowej to pojawi się okienko informacyjne:


Wybór noty katalogowej procesora w Atmel Studio.
Wybór noty katalogowej procesora w Atmel Studio

W okienku tym możemy kliknąć na nazwę procesora (np. ATXMEGA256A3BU) lub link Datasheets, co spowoduje przeniesienie nas na odpowiednią podstronę strony firmowej Atmela:


Na stronie Atmela można znaleźć różne dokumenty opisujące używany nikrokontroler.
Na stronie Atmela można znaleźć różne dokumenty
opisujące używany procesor

Na stronie tej mamy dostęp do wszystkich dokumentów powiązanych z wybranym mikrokontrolerem. Przede wszystkim na pierwszej pozycji widzimy dokument nazwany Atmel AVR XMEGA AU Manual – jest to dokument zawierający szczegółowy opis rodziny XMEGA AU. Zaraz pod nim widzimy dokument Atxmega256A3BU Complete – jest to dokument opisujący charakterystykę użytego mikrokontrolera.

Tego typu dokumenty nazywane są Complete – jeśli jest to finalna wersja dokumentu, lub Preliminary – jeśli dokument jest w fazie tworzenia. Dokumenty typu preliminary należy traktować jako wersje robocze. Poniżej tych dwóch najważniejszych dokumentów znajdziemy setki innych znajdujących się w sekcji Application Note – są to tzw. noty aplikacyjne. Dokumenty te są niezwykle ważne – zawierają one gotowe, rekomendowane przez firmę Atmel rozwiązania różnych problemów. Od bardzo ogólnych typu jak zasilić mikrokontroler, do bardzo szczegółowych, np. jak zrealizować regulator PID. Warto je przejrzeć, chociażby po tytułach. W przypadku not aplikacyjnych oprócz dokumentu pdf czasami mamy możliwość pobrania związanych z nim przykładowych kodów:


Do niektórych not można pobrać kody przykładów.
Do niektórych not można pobrać kody przykładów.

W takim przypadku oprócz symbolu dokumentu pdf obok znajdzie się mała ikonka symbolizująca folder.

Ok, widzimy więc, że dostęp do informacji jest niezwykle prosty. Przejdźmy więc do czytania not. Po otwarciu dokumentu pdf warto wyświetlić sobie indeks dokumentu, dzięki temu łatwo będziemy mogli poruszać się po jego poszczególnych sekcjach:

Każdy dokument posiada spis treści ułatwiający w nim nawigację.
Każdy dokument posiada spis treści ułatwiający
w nim nawigację.

Każdy dokument ma identyczny podział na sekcje, dzięki czemu niezależnie od użytego procesora, te same informacje zawsze znajdziemy w tych samych sekcjach. Zanim zaczniemy czytać informacje zawarte w nocie warto zwrócić uwagę na mały szczegół znajdujący się w prawym dolnym rogu pierwszej strony:


Struktura noty katalogowej.
Struktura noty katalogowej.

Jest to informacja (na pokazanym przykładzie 8077H-AVR-12/09) zawierająca numer noty, wersję i datę jej wydania. Czasami wydawane są aktualizacje not, w efekcie jeśli powołujemy się na notę katalogową to warto podać jej wersję i datę wydania. Numer noty ułatwia z kolei jej wyszukanie – zamiast podawać długi tytuł, możemy posłużyć się krótkim numerem, np. 8077.

Zobaczmy jak wyglądają najważniejsze sekcje noty.

Sekcja Features

Sekcja ta zawiera jednostronnicowe podsumowanie informacji o użytym mikrokontrolerze.


Sekcja Features.
Sekcja Features.


W tej części możemy szybko znaleźć bardzo ogólne informacje dotyczące mikrokontrolera – ilość dostępnej pamięci FLASH, SRAM i EEPROM, dostępnych układów peryferyjnych, typów obudów w jakich jest on dostępny, maksymalnej częstotliwości taktowania i napięć zasilających. Informacje te są bardzo ogólne, jeśli jednak interesuje nas np. ile USART-ów ma wybrany mikrokontroler, to w tej sekcji na to pytanie szybko znajdziemy odpowiedź.


Sekcja Pinout/Block Diagram

To kolejna krótka sekcja – zawiera ona rysunki obudów w jakich występuje wybrany mikrokontroler i sposób przyporządkowania podsystemów mikrokontrolera do wyprowadzeń obudowy.


Sekcja Pinout/Block Diagram.
Sekcja Pinout/Block Diagram.


Informacje zawarte w tej sekcji musimy zawsze bardzo dokładnie przeanalizować. Właściwie z konieczności dokładnej analizy tej sekcji zwalnia nas wyłącznie posiadanie sprawdzonej biblioteki, do której mamy pełne zaufanie, zawierającej definicję wybranego układu dla wykorzystywanego przez nas programu typu CAD.

Na przykład biblioteki AVR dla popularnego programu Eagle zawierają predefiniowane mikrokontrolery z przyporządkowanymi sygnałami do wyprowadzeń, w efekcie projektując schemat i PCB w Eagle sekcję tą możemy pominąć. To co będziemy widzieć na ekranie to nazwy symboliczne (np. PJ2), Eagle samo zadba o połączenie sygnału z odpowiednim pinem.

Pamiętaj, że jak do wszystkiego, także do gotowych bibliotek trzeba mieć ograniczone zaufanie. Nic nie szkodzi sprawdzić, czy sygnały zostały w bibliotece właściwie przyporządkowane do poszczególnych pinów.


Sekcje opisujące rdzeń i układy peryferyjne


Kolejne sekcje zawierają opisy poszczególnych podsystemów mikrokontrolera – jego rdzenia i układów peryferyjnych. W notach szczegółowych sekcje te zawierają jedynie bardzo skrótowe informacje, mieszczące się na 1-2 stronach. Sekcje te bardzo szczegółowo opisują dany układ peryferyjny w nocie dotyczącej wybranej rodziny mikrokontrolerów. W każdej nocie sekcje te są do siebie niezwykle podobne i na razie je pominiemy.


Sekcja Electrical Characteristics

Jest to najważniejsza sekcja noty szczegółowej procesora. Znajdziemy w niej wszystkie parametry elektryczne użytego mikorkontrolera. Właściwie nigdy nie powinniśmy rozpoczynać przygody z mikorkontrolerem nie przeglądając bardzo dokładnie tej sekcji jego noty.

Czasami wystarczy ograniczyć się do opisu elementów, które wykorzystujemy w danym projekcie, lecz zazwyczaj warto tę sekcję przestudiować dokładnie. Sekcja ta składa się z kilku elementów.

Absolute Maximum Ratings

Element Absolute Maximum Ratings, czyli maksymalne dopuszczalne warunki pracy układu to część, którą absolutnie i zawsze musimy przeczytać. Zawiera ona krytyczne informacje na temat procesora, dotyczące parametrów po przekroczeniu których możemy procesor nieodwracalnie uszkodzić.


Sekcja Electrical Characteristics.
Sekcja Electrical Characteristics.

Warto podkreślić, że parametry wymienione w tej sekcji to parametry maksymalne, po przekroczeniu, których procesor może ulec nieodwracalnemu uszkodzeniu.

Jeden z elektrodowiczów zadał pytanie:

Chumanista
Czy Atmega 128 przeżyje zasilanie 6,6V (4 paluszki)?


Po przeczytaniu sekcji Absolute Maximum Ratings odpowiedź jest znana i nie musimy się zdawać na opinie innych osób :-)

Nie znaczy to jednak, że procesor może pracować stale przy podanych parametrach. Podane parametry można osiągać krótkotrwale, jednak zalecane jest, aby w projektowanym układzie nigdy nie dochodziło do sytuacji, w której do tych parametrów się zbliżymy. Co one oznaczają? Przyglądnijmy im się po kolei.

Operating Temperature – jest to zakres temperatur w których może pracować mikrokontroler. Nie znaczy to jednak, że zawsze może on pracować w podanym zakresie. Szczególnie istotna jest tu maksymalna temperatura pracy. W pokazanym przykładzie wynosi ona 125 ºC – jest to maksymalna temperatura układu, a nie otoczenia. Maksymalna temperatura otoczenia będzie zależała od wielu czynników. Przede wszystkim od strat w mikrokontrolerze. Na skutek pobierania znacznego prądu z jego pinów IO struktura scalona może się nagrzewać, co ograniczy maksymalną temperaturę otoczenia.

Storage Temperature – jest to zakres temperatur, w którym mikrokontroler może być przechowywany. To znaczy, że w tym zakresie może on sobie leżeć niepodłączony na półce, lecz nie może on normalnie działać. Zazwyczaj zakres temperatur przechowywania jest szerszy niż zakres temperatur pracy.

Voltage on any Pin with respect to Ground – jest to zakres napięć jakie mogą panować na dowolnym pinie IO w stosunku do pinu masy (Ground). Napięcie to nie może być niższe niż -0,5V i wyższe niż Vcc+0,5V. Dlaczego? Ano dlatego, że każdy pin IO (z wyjątkiem pinu RESET w niektórych mikrokontrolerach) jest zabezpieczony diodami, które są integralną częścią struktury scalonej. Jeśli na przykład na pin doprowadzimy napięcie wyższe niż Vcc+0,5V to prąd zacznie płynąć od pinu do Vcc przez diodę ochronną. Po przekroczeniu jej maksymalnego prądu (który jest niewielki i nie przekracza 0,5 mA) może dojść do jej uszkodzenia. Owe 0,5 V (czasami 0,6 V) to nic innego jak napięcie przewodzenia takiej wbudowanej diody.

Maximum Operating Voltage – jest to maksymalne napięcie zasilania mikrokontrolera. Jednak nie znaczy to, że mikrokontroler takim napięciem może być zasilany. Po jego przekroczeniu zazwyczaj dochodzi do uszkodzenia układu, lecz nawet zbliżanie się do podanej wartości nie jest zalecane.

DC Current per IO Pin – jest to maksymalny prąd stały który można pobierać lub dostarczać do pinu IO. Dlaczego stały? Dlatego, że prąd w impulsie może być znacznie większy, generalnie im krótszy impuls tym większy prąd jaki możemy bezpiecznie pobierać/dostarczać do pinu IO. Oczywiście w rozsądnych granicach. A czym grozi przekroczenie dopuszczalnego prądu pinu? Konsekwencje wynikają z budowy struktury scalonej. Bufory wyjściowe mogą tworzyć pasożytnicze struktury tyrystorowe. Tyrystory mają to do siebie, że jak się włączą to wyłączyć je może wyłącznie zanik prądu. A co taki pasożytniczy tyrystor może włączyć? Między innymi duży impuls prądowy. W efekcie może zdarzyć się sytuacja, że na skutek dużego udaru prądowego dojdzie do wystąpienia tzw. zjawiska zatrzaśnięcia (latch up) – kiedy zaczynają przewodzi oba tranzystory bufora wyjściowego CPU, w efekcie dochodzi do dużych strat i uszkodzenia układu. Drugi powód jest związany również z budową buforów wyjściowych. Bufor taki tworzą dwa tranzystory MOSFET, a jak pamiętamy tranzystory takie cechują się pewną rezystancją źródło-dren. W efekcie na takim tranzystorze wydziela się moc równa P=I2R. Czyli mamy ciepło! :-)

DC Current Vcc and GND Pins – to kolejny niezwykle istotny parametr. Określa on maksymalny prąd pobierany przez cały układ scalony. Wydawałoby się, że jeśli użyty mikrokontroler ma np. 40 pinów IO, a dla każdego z nich maksymalny prąd jest równy 20 mA, to łącznie możemy pobierać 40*20 mA, czyli 800mA. Nic bardziej mylnego:

Musimy pamiętać, aby sumaryczny prąd wszystkich pinów nie przekroczył maksymalnego prądu dla pinów zasilania, w naszym przykładzie będzie to 200 mA.

W efekcie jeśli chcemy po równo obciążyć 40 pinów IO, to na każdy przypadać będzie prąd maksymalny równy „zaledwie” 5 mA! Tak naprawdę prąd ten należałoby pomniejszyć o prąd pobierany przez sam mikrokontroler. Dla niektórych układów istnieją dodatkowe ograniczenia – np. na maksymalny prąd pobierany z pinów danego portu IO.

DC characterisatics

Jest to ciekawa sekcja, w której zawarte są różne informacje na temat np. poboru prądu przez mikrokontroler dla różnych warunków jego pracy i przez różne podsystemy. Prąd pobierany przez mikrokontroler podawany jest dla określonej częstotliwości (jak wiemy im wyższa częstotliwość tym większy pobierany prąd) i przy określonych innych warunkach (tryb aktywny – Active, lub tryby o obniżonym poborze mocy, np. Idle).

Dodatkowo mamy wymienione typowe zapotrzebowanie na energię dla poszczególnych podsystemów mikrokontrolera (które możemy włączać i wyłączać). W efekcie całkowity pobór prądu równy jest sumie poboru prądu przez poszczególne podsystemy.


DC characterisatics.
DC characterisatics.

Tu musimy pamiętać, że w tych wyliczeniach nie jest uwzględniony prąd pobierany lub dostarczany do pinów IO mikrokontrolera. W efekcie jeśli do pinu podepniemy diodę LED, która bierze np. 5 mA, to oczywiście sumaryczny prąd o te 5 mA wzrośnie. Sekcja ta ma znaczenie dla osób, które projektują układy zasilane z baterii, przy normalnym zasilaniu zazwyczaj wartości w niej określone nie mają większego znaczenia – często prąd pobierany przez układy podłączone do mikrokontrolera jest większy niż prąd pobierany przez mikrokontroler.

W tej sekcji warto zwrócić uwagę na jeszcze jedną ważną cechę – sposób podawania parametrów. W notach większość parametrów podawana jest w trzech kolumnach zatytułowanych Min, Typ i Max. Kolumna Min określa minimalną wartość danego parametru. W kolumnie Typ mamy wartość typową, możliwą do uzyskania w większości przypadków, a w kolumnie Max mamy wartość maksymalną danego parametru.

W prezentowanej sekcji mamy podane parametry typowe, to znaczy, że niektóre mikrokontrolery będą charakteryzowały się lepszymi parametrami (niższym poborem energii), a może się zdarzyć, że pobór energii będzie większy (chociaż nie jest to typowe). O ile większy? Producent tego nie precyzuje, tego typu informacje można znaleźć w innych notach, w których dostępne są informacje na temat cech statystycznych danej serii – np. rozkładu prądów zasilających.


Sekcja Speed

Jest to sekcja krótka, ale zawsze warto na nią spojrzeć. W sekcji tej określona jest maksymalna częstotliwość taktowania dla określonego napięcia zasilania. Wartość ta pokazana jest tabelarycznie:

Częstotliwość taktowania - tabela.
Częstotliwość taktowania - tabela.

lub graficznie:

Częstotliwość taktowania - wykres.
Częstotliwość taktowania - wykres.

Jak interpretować ten wykres? Safe Operating Area to obszar w którym procesor może „bezpiecznie”, czyli pewnie działać. Jak widzimy obszar ten jest zaznaczony na szaro. Poza nim procesor może działać, ale nie jest to gwarantowane przez producenta i w budowanym układzie procesor nigdy nie powinien w tym obszarze się znaleźć. Widzimy, że maksymalna częstotliwość zależy od napięcia zasilającego. Dla napięć 1,6 do 1,8V XMEGA może być taktowana maksymalnie częstotliwością 12 MHz, w zakresie 1,8-2,7V częstotliwość ta rośnie proporcjonalnie do napięcia i osiąga wartość maksymalną 32 MHz, a powyżej 2,7V procesor może pracować z maksymalną częstotliwością równą 32 MHz.

Tu warto wspomnieć jeszcze o jednej rzeczy – tzw. overclockingu. Termin żywcem wzięty z PC, gdzie ludzie bawią się w tuningowanie komputera w celu uzyskania lepszych osiągów. W świecie mikrokontrolerów przetaktowywanie procesora jest pomysłem wyjątkowo kiepskim.

Dlaczego? Przede wszystkim dlatego, że mikrokontroler musi działać pewnie. O ile jak nam PC się zawiesi, to najwyżej stracimy stan gry, o tyle, zawieszenie się, czy nieprawidłowe działanie mikrokontrolera może mieć poważniejsze konsekwencje – procesory te sterują w końcu czasami krytycznymi procesami. Bardzo trudno jest sprawdzić, czy w wyniku przetaktowania procesor działa poprawnie, a szczególnie czy poprawnie działają jego poszczególne bloki funkcjonalne. Najbardziej narażone na negatywne efekty przetaktowania są pamięć FLASH i EEPROM, a po nich przetwornik ADC.

Warto zauważyć, że w naszym przypadku przetaktowaniem jest taktowanie procesora >32 MHz dla Vcc>2,7V, ale także >12 MHz dla zasilania w zakresie 1,6-1,8V.


Najbezpieczniej jest przyjąć, że jeśli przekraczamy maksymalne parametry określone w nocie, to wkraczany na teren nieznany, gdzie zdarzyć może się wszystko. Jeśli przekroczyłeś maksymalne parametry i w efekcie coś ci nie działa, to nie ma też sensu pytanie o przyczynę na forach.

To, że w identycznej aplikacji innej osobie wszystko będzie działać nic nie znaczy :-)

Tu wróćmy jeszcze na chwilę do naszych kolumn Min, Typ i Max. Jak widzimy producent dla poszczególnych napięć zasilających określił, że minimalna częstotliwość taktowania wynosi 0 MHz, a maksymalne jest zależna od napięcia. Nie określił wartości typowej.

Co to znaczy? To znaczy, że procesor może pracować w całym zakresie określonym przez wartość Min i Max. A co, gdyby wartość typowa była określona? To by znaczyło, że z jakiegoś powodu, najlepiej będzie jeśli użyjemy określonego jako typowe taktowania. Czasami tak się dzieje przy „gołych” pozbawionych kontrolera wyświetlaczach LCD. Dla określonej częstotliwości typowej np. trwałość LCD będzie największa, lub zakres jego pracy będzie najszerszy.


FLASH and EEPROM Memory Characteristics

To sekcja do której zagląda się rzadko – zazwyczaj zawarte w niej informacje są wspólne dla wszystkich procesorów danego producenta. W tabeli:


FLASH and EEPROM Memory Characteristics.
FLASH and EEPROM Memory Characteristics.

znajdziemy informacje na temat liczby cykli zapisu. Jak pamiętamy liczba zapisów do pamięci FLASH i EEPROM jest ograniczona. Write/Erase Cycles jest podane osobno dla pamięci Flash i EEPROM.

W naszym przykładzie liczba cykli zapisu wynosi dla Flash 10 tysięcy, a dla pamięci EEPROM 30-80 tysięcy cykli w zależności od temperatury. Jak widzimy podane wartości są wartościami minimalnymi, to znaczy, że procesor „wytrzyma” co najmniej podaną liczbę cykli, leczy typowo będzie to o wiele więcej.

Dla pamięci podane są też tzw. czasy retencji (Data retention) – czas przez jaki gwarantowane jest zachowanie stanu komórek, a wiec informacji, przez pamięć. Jak widzimy, czas ten jest podany dla różnych temperatur. Dla temperatury 25 ºC wynosi on dla obu pamięci 100 lat, ale dla 55 ºC już tylko 25 lat. A ile wyniósłby on dla 100 ºC? Tego producent nam wprost nie podaje, ale możemy ekstrapolować dane, które posiadamy. Jak widzimy dwukrotny wzrost temperatury spowodował 4-krotny spadek czasu retencji.

Wzrost temperatury z 55 do 100 ºC spowoduje więc kolejne mniej więcej 4-krotne skrócenie czasu retencji, a więc zostanie nam około 6 lat.

W tej samej sekcji mamy jeszcze jedną tabelę:

Czasy programowania EEPROM.
Czasy programowania EEPROM.

W tabeli tej mamy określone czasy programowania pamięci. Osobno czas kasowania całego układu (chip erase), czas kasowania strony (Page Erase), czas zapisu strony (Page Write), a także czas połączonego kasowania i zapisu (Page WriteAutomatic Page Erase and Write).

Do czego to nam potrzebne? Właściwie do niczego. Takim wyjątkiem jest sytuacja, w której projektujemy układ, który wymaga podtrzymania zasilania na czas zapisu danych do EEPROM. Aby wiedzieć ile potrzebujemy energii, musimy wiedzieć ile czasu zajmie zapis pamięci. Przykłady tego typu szczegółowo omawiam w książce "AVR. Praktyczne projekty".

To na razie tyle. W dalszych odcinkach (jeśli będzie takie zapotrzebowanie) omówię kolejne parametry zawarte w notach.


Część II-ga artykułu

Artykuł składa się z dwóch części: Jak czytać noty katalogowe mikrokontrolerów AVR (cz. II)


Oceń artykuł.
Wasze opinie są dla nas ważne, gdyż pozwalają dopracować poszczególne artykuły.
Pozdrawiamy, Autorzy
Ten artykuł oceniam na:

czwartek, 24 marca 2011

42-ie Spotkanie ŚRGM i PLSSUG Katowice


Autor: Dondu

28 mara 2013 Śląska Regionalna Grupa Microsoft'u oraz Polish SQL Server Group organizują spotkanie, na którym omawiane będą dwa tematy, w tym temat XMEGA prowadzony przez Tomasza Francuza (Tmf). Wstęp wolny, po zarejestrowaniu.





Poradnik wdrażania Private Cloud czyli klient vs. architekt
2013-03-28 18:10:00 - 2013-03-28 19:25:00, Kamil Skalski
Chmura prywatna to nie tylko Hyper-V i System Center! Praktyczne podejście do wdrożenia rozwiązań Cloud wymaga wykorzystania znacznie większego zbioru technologii, a w szczególności ich właściwej integracji, by odpowiedzieć na potrzeby klientów. Gdy architekt rozwiązania Private Cloud spotyka się ze swoim klientem rozpoczyna się proces układania kolejnych elementów wielkiej układanki informatycznej. Podczas sesji przedstawione zostaną najczęściej spotykane problemy klientów, ich potrzeby oraz przykładowe sposoby praktycznych rozwiązań.

Mikrokontrolery AVR XMEGA - specyfika programowania
2013-03-28 19:35:00 - 2013-03-28 20:50:00, Tomasz Francuz
Małe jest piękne. Pisząc różne aplikacje na "duże" komputery czesto zapominamy, że nasze otoczenie nie kształtują rzeczy duże, lecz małe. Sprawne funkcjonowanie naszego świata w co raz większym stopniu zależy od mikrokontrolerów i aplikacji na nich działających. Począwszy od klawiatury, a skończywszy na bioimplantach - wszystkim tym zawiadują niestrudzeni mali robotnicy - mikrokontrolery. Warto więc poznać je bliżej, a może nawet nauczyć się nimi posługiwać. Tym bardziej, że większość potrzebnych w tym celu narzędzi i wiedzy już masz. Może dzięki tej prezentacji odkryjesz w sobie programistę embedded, znajdziesz nową, ciekawą pracę? Postaram się pokazać główne różnice w programowaniu "dużych" komputerów i małych mikrokontrolerów na przykładzie nowej rodziny wydajnych i superenergooszczędnych mikrokontrolerów AVR XMEGA.

Termin: 28 marca 2013
Miejsce: Katowice, ul. 1-go Maja 133, budynek Kamsoft S.A., sala kinowa, poziom -1

Dodatkowe informacje:
- otwarty, bezpłatny parking na tyłach budynku.
- dojazd z centrum Katowic - tramwajami w kierunku Zawodzia.

Agenda:
17:30 - 18:00 - Rejestracja
18:00 - 18:10 - Rozpoczęcie spotkania, sprawy organizacyjne
18:10 - 19:25 - Poradnik wdrażania Private Cloud czyli klient vs. architekt (Kamil Skalski)
19:25 - 19:35 - przerwa
19:35 - 20:50 - Mikrokontrolery AVR XMEGA - specyfika programowania (Tomasz Francuz)
20:50 - 21:00 - Losowanie nagród, zakończenie spotkania.

Rejestracja
Prosimy o rejestrowanie się na spotkanie.

Udział w spotkaniu jest bezpłatny.

Oceń artykuł.
Wasze opinie są dla nas ważne, gdyż pozwalają dopracować poszczególne artykuły.
Pozdrawiamy, Autorzy
Ten artykuł oceniam na:

XMEGA: Emulacja portu szeregowego na XMEGA


Autor: tmf
Redakcja: Dondu

Artykuł jest częścią cyklu: Wstęp do XMEGA


Czy tego chcemy, czy nie port RS-232 powoli wymiera. Nowe laptopy go praktycznie nie posiadają, płyty główne normalnych PC-ów jakoś się jeszcze bronią. Co prawda RS-232 nie jest już zazwyczaj wyprowadzany na złącze z tyłu płyty, niemniej gdzieś tam, w czeluściach PCB możemy zazwyczaj ciągle znaleźć małe, 10-pinowe złącze, pod które można podłączyć śledzia z gniazdem RS-232.

Pytanie tylko po co?

RS-232 niewątpliwie jest prosty, ale z punktu widzenia elektronika ma dwie poważne wady:
  1. wymaga konwertera poziomów (tzw. transceivera, np. max232), jest to układ tani, ale zajmuje miejsce na PCB, wymaga zwykle 4 zewnętrznych kondensatorów, no i zawsze to jakiś element dodatkowy,
  2. złącze RS-232 nie ma wyprowadzonego zasilania. Co prawda parenaście mA można wyciągnąć z nieużywanych linii interfejsu, jednak nie jest to za wiele.

A jakie RS-232 ma zalety? Dosyć poważne:
  • prostota - nawet początkujący uruchomi transmisję w ciągu kilkunastu minut,
  • w USART wyposażony jest praktycznie każdy mikrokontroler, a nawet jeśli nie jest, to stosunkowo łatwo go emulować programowo.
  • możliwość komunikacji na duże odległości.

Wydaje się, że przedstawione zalety przeważają tym bardziej, że zanikający port RS-232 możemy zawsze protezować wykorzystując różnego typu przejściówki, z których najpopularniejsze oparte są na znanym układzie FT232 i różnych jego odmianach. Inna strategia polega na programowej emulacji USB bezpośrednio w procesorze dzięki bibliotece V-USB.

Emulacja programowa USB z pewnością jest tania i wymaga tylko kilku prostych elementów zewnętrznych, ale bardzo obciąża procesor i nie do końca jest zgodna ze standardem USB.

Z kolei wykorzystanie przejściówek opartych na FT232 kosztuje:
  • sam układ FT232 około 14-18zł (cena 2013r.), 
  • do tego zajmuje on trochę miejsca na PCB (co także kosztuje), 
  • no i nie każdy lubi lutować układy SMD. 

Przejściówki USB-RS232

Dla tych co nie lubią lub boją się montażu SMD różne firmy oferują zmontowane przejściówki w cenie 30-60zł. Taką przejściówkę wystarczy połączyć przy pomocy listy kołkowej z naszym układem i możemy się cieszyć możliwością komunikacji z PC przy pomocy wirtualnego portu szeregowego. Co ważne, od strony mikrokontrolera komunikacja ciągle przebiega z wykorzystaniem interfejsu USART.


Alternatywy
Wszystko pięknie, ale świat idzie do przodu i to co było na topie 2-3 lata temu obecnie niekoniecznie jest najlepszym rozwiązaniem. Na rynku mikrokontrolerów, w tym mikrokontrolerów AVR, pojawiły się układy dysponujące wbudowanym interfejsem USB (w wersji host, jak i device).

W rodzinie AVR sprzętowy interfejs USB-device (co wystarcza do implentacji dowolnego urządzenia USB podłączanego do PC) posiadają mikrokontrolery ATMega z literką U w nazwie.

Literka U musi być przy oznaczeniu mikrokontrolera, a nie po myślniku – wtedy znaczy coś innego.

Czyli np. ATMega32U2 posiada interfejs USB, z kolei ATMega32-AU, to zwykły mikrokontroler bez tego interfejsu.

Tak samo jest w rodzinie Xmega - ATXMEGA128A1U posiada sprzętowy interfejs USB, a ATXMEGA128A1-AU nie posiada.

Niemniej obecnie (2013r.) nie ma właściwie żadnego uzasadnienia dla używania rodziny ATMega, gdyż pod każdym względem (także cenowym) lepsze od nich są mikrokontrolery XMEGA.

Taki interfejs posiadają także pokazane wcześniej minimoduły z mikrokontrolerami XMEGA256A3-BU i XMEGA128A3U. Co więcej, posiadane moduły są wyposażone w gniazdo USB i wszystko co do komunikacji jest wymagane. Spróbujmy więc je wykorzystać.





Podłączenie - schemat

Jeśli ktoś nimi nie dysponuje i nie jest przekonany do bezpośredniego łączenia procesora z USB poniżej pokazuję schemat takiego połączenia. Jak widzimy jest on banalnie prosty – odpowiednie linie sygnałowe D+ i D- łączymy bezpośrednio z odpowiednimi sygnałami w gnieździe USB.



Naprawdę prościej już się nie da :-)

Jak widzimy piny mikrokontrolera PD7 i PD6 są połączone bezpośrednio z odpowiednimi stykami gniazda USB. Jest to możliwe, gdyż XMEGA posiadają wewnętrzne układy dopasowujące. Oczywiście zwolennicy dmuchania na zimne mogą umieścić dodatkowe rezystory szeregowe na tych liniach (typowo 22Ω).


Zasilanie

Warto zwrócić uwagę na jeszcze jedną rzecz – USB dostarcza napięcia zasilającego, które możemy wykorzystać do zasilania podłączonego układu (maksymalna wydajność przed enumeracją to 100mA, po enumeracji 500mA dla USB do 2.0 i 900mA dla USB 3.0 lub 1,5A bez jednoczesnej transmisji danych).

Jednak napięcie na tym interfejsie (około 5V) jest wyższe niż dopuszczalne dla XMEGA (maksymalnie 3,6V). Stąd też jeśli chcemy zasilać XMEGA z USB musimy dodać zewnętrzny regulator napięcia, typu low-drop (bardzo ważne, ze względu na niewielką różnicę napięć pomiędzy wejściem a wyjściem). Nie jest to żadnym problemem, gdyż zazwyczaj i tak jakiś regulator w układzie mamy.

Warto podkreślić, że posiadacze pokazanych wcześniej modułów, mają już taki regulator zamontowany. Odpowiednią zworką mogą wybierać zewnętrzne źródło napięcia lub zasilanie z USB.

Jak widzimy połączenia elektryczne są niezwykle proste, warto ewentualnie pomyśleć o dodatkowym filtrowaniu napięcia z USB (komputer jest potężnym źródłem zakłóceń), ale dla naszych rozważań nie jest to niezbędne.


Kwarc, czy generator wewnętrzny RC?

Uważni obserwatorzy naszego bloga i osoby znające się co nieco na AVR-ach z pewnością zaprotestują – jak to, a gdzie kwarc? Przecież na wewnętrznym generatorze RC, to w życiu nie zadziała!

No i są w błędzie. XMEGA posiadają naprawdę stabilny wewnętrzny generator RC, w efekcie nie tylko do realizacji transmisji RS-232, ale nawet do pracy z wykorzystaniem USB żadne zewnętrzne elementy nie są potrzebne! Tak więc przedstawiony schemat jest kompletnym, działającym układem interfejsu XMEGA-USB.





Program

Ok, mamy sprzęt, czas na program. W pokazanej implementacji wirtualnego portu szeregowego wykorzystane zostanie oprogramowanie dostarczone przez firmę Atmel.

Firma ta jest tak miła, że za darmo dostarcza nam gotowych bibliotek i przykładów. Taką gotową biblioteką zawierającą wszystko co potrzeba do realizacji RS-232 przez USB jest ASF (Atmel Software Framework).

Biblioteka trochę ciężka i na pierwszy rzut oka skomplikowana, dlatego osobiście preferuję „własne” rozwiązania. Ale nie ma co wyważać otwartych drzwi – zamiast narzekać wykorzystałem gotowy kod, tylko nieco go „odchudziłem” wyrzucając zbędne komponenty ASF i niepotrzebne definicje.

Lecz dobra wiadomość jest taka – dzięki pokazanemu przykładowi w ogóle nie musisz zagłębiać się w tajniki implementacji CDC (USB communications device class – klasy urządzeń komunikacyjnych, do których zalicza się m.in. urządzenie wirtualnego portu szeregowego). Miało być prosto – i będzie prosto!

Tak więc mamy nieco zmodyfikowaną bibliotekę firmy Atmel implementującą klasę CDC, dzięki czemu w ogóle nas nie interesuje jak to wszystko działa. Natomiast interesuje nas (a przynajmniej powinno) poznanie jak z tego korzystać.

Czas więc ściągnąć przykładowy projekt – jest on przygotowany dla Atmel Studio 6.1 i w tym programie możemy go bezpośrednio otworzyć. Ze wszystkich plików, które są zawarte w tym projekcie interesuje nas plik main.c, w którym znajduje się cała obsługa RS-232:

Przykład 1 (w kompilatorze)
int main(void)
{
  //Włączamy wszystkie poziomy przerwań (potrzebujemy tylko najniższy)
  PMIC.CTRL = PMIC_LOLVLEN_bm | PMIC_MEDLVLEN_bm |
              PMIC_HILVLEN_bm; 
  sei();

  sysclk_init();
  udc_start();

  while (1) {
    char ch;

    if (udi_cdc_is_rx_ready()) {
      ch = udi_cdc_getc();
      switch(ch) {
         case 'n'       :
           udi_cdc_write_buf("Pozdrowienia z XMEGA!\n\r", 23);
           break;
         default        :
           udi_cdc_write_buf("O co chodzi?\n\r", 14);
           break;
      }
    }
  }
}

Co nasz przykład robi?

Jak widzimy najpierw włączane są przerwania. Ponieważ kontroler przerwań w XMEGA obsługuje trzy poziomy przerwań, włączamy je wszystkie (chociaż nasz sterownik USB wykorzystuje tylko przerwania najniższego poziomu) i ustawiamy globalną flagę zezwolenia na przerwania instrukcją sei().

Następnie inicjalizowane są podsystemy mikrokontrolera – zegar oraz włączany jest sterownik USB.
Implementacja USB zaproponowana przez firmę Atmel jest o tyle fajna, że wykorzystuje ona przerwania i bufory, w efekcie cała obsługa USB jest zupełnie niezależna od kodu użytkownika.

Od tego momentu nasz mikrokontroler może wymieniać dane z PC udając port szeregowy.

W pętli głównej, sprawdzamy czy w buforze odbiornika znajduje się jakiś znak - funkcja udi_cdc_is_rx_ready(), jeśli tak to go odbieramy. Po sprawdzeniu co to za znak podejmujemy odpowiednią akcję – wysyłamy do komputera PC łańcuch znaków - funkcja udi_cdc_write_buf().

Funkcja ta przyjmuje jako argumenty adres bufora, w którym znajduje się wysyłany łańcuch oraz jego długość. Oczywiście mamy też do dyspozycji funkcję udi_cdc_putc(int value), która wysyła jeden znak (argument value).

I to wszystko, prawda, że proste?

Część osób uważnie czytających ten artykuł z pewnością zapyta – a gdzie inicjalizacja portu szeregowego? Przecież musimy ustawić odpowiednią szybkość transmisji, prawda?

Otóż nie. Jedną z przyjemnych cech emulacji portu szeregowego jest to, że szybkość transmisji nas zupełnie nie interesuje. Dlaczego? Ano dlatego, że jest to parametr istotny dla fizycznego interfejsu RS-232  dzięki niemu odbiornik wie jak szybko nadawane są dane przez nadajnik, co jest niezbędne, gdyż typowa transmisja RS-232 jest transmisją asynchroniczną (czyli nie przesyłamy żadnego zegara taktującego transmisję).

Tu dane wysyłane są przez interfejs USB, w efekcie sposób ich wysyłania i szybkość zupełnie nas nie interesują. Przez interfejs USB przechodzą one zawsze tak szybko jak tylko się da. To aplikacja może je wysyłać lub pobierać wolniej. W efekcie w terminalu możemy ustawić dowolną szybkość transmisji danych!

Ale zanim to zrobimy musimy wczytać nasz przykładowy program do XMEGA. Możemy to uczynić przy pomocy programatora, albo tak jak wcześniej pokazałem przy pomocy programu FLIP – właściciele prezentowanych wcześniej modułów dysponują XMEGA z preprogramowanym bootloaderem.

Po uruchomieniu programu, nasz ulubiony system powinien wykryć nowe urządzenie USB. Jeśli naszym ulubionym systemem jest MS Windows, to zapewne nie będzie on dysponował odpowiednimi sterownikami. Nie ma problemu – odpowiednie sterowniki znajdują się w katalogu RS232overUSB/RS232overUSB – plik atmel_devices_cdc.inf. Jeśli system nie znalazł właściwych sterowników, należy mu wskazać ten plik. Po poprawnym zainstalowaniu sterowników powinniśmy w menagerze urządzeń zobaczyć nowe urządzenie:


Jest nim EVK1XXX Virtual Com Port (COM7).

Dlaczego taka dziwaczna nazwa? Problem w tym, że urządzenia USB muszą posiadać swój unikalny identyfikator VID/PID. W naszym przykładzie pasożytujemy nieco na firmie Atmel, wykorzystując ich identyfikatory (pamiętaj jednak, że absolutnie nie wolno robić tego w urządzeniach komercyjnych!). 

Ponieważ użyliśmy VID/PID, który w pliku .inf ma przypisaną powyższą nazwę, stąd też nasz układ tak się przedstawia. Oczywiście można to zmienić, ale skorzystanie z pliku .inf firmy Atmel ma jeszcze jedną zaletę – jest on podpisany cyfrowo. Tak, to nie bajki, pliki tekstowe .inf też mają w Windows swoje podpisy cyfrowe.

O ile dla Windows XP i Windows 7 brak podpisu nie jest zasadniczo problemem, o tyle użytkownicy Windows 8 mieliby problem. Jeśli plik opisu sterownika (urządzenia klasy CDC nie wymagają instalacji żadnych sterowników) jest podpisany cyfrowo, to także użytkownicy Windows 8 mogą z naszego przykładu korzystać bez problemów.

Ok, na tym etapie zakładam, że twój system rozpoznał nowy układ, a sterowniki są poprawnie zainstalowane. Co dalej? Warto w menagerze urządzeń sprawdzić numer wirtualnego portu COM – na powyższym zrzucie był to COM7, ale w twoim przypadku może to być zupełnie inny port.

Nadeszła więc pora na test – odpalamy nasz ulubiony terminal. Ja używam RealTerm – nie jest to żaden cud, ale ma wszystko co jest potrzebne, no i jest za darmo (jeśli go nie masz, to go możesz pobrać stąd: http://realterm.sourceforge.net).

Uruchamiamy więc terminal, wybieramy port, który jest połączony z naszą XMEGA (możemy wybrać po numerze lub nazwie, np. \USBSER000 i zaczynamy zabawę.

A co nasz przykład robi? Po jego uruchomieniu oczekuje on na dane z komputera, po otrzymaniu litery n wysyła pozdrowienia, które powinniśmy zobaczyć na terminalu, po wysłaniu dowolnego innego znaku wysyła zapytanie „O co chodzi?” – poniższy rysunek.



I to wszystko, prawda, że proste?

Mam nadzieję, że przekonałem Was do używania USB zamiast archaicznych rozwiązań typu emulacja USB lub przejściówki USB - RS232. Jeśl będzie taka potrzeba to pokażemy więcej przykładów rozwiązań opartych na USB.


Program zajmuje 7,5kB - to dużo, czy mało?

Chwila, ale czemu ten prosty przykład zajmuje prawie 7,5kB? A dlaczego nie, pamiętaj, to XMEGA, mamy pamięci sporo! A pamięć leżąca odłogiem, to pamięć zmarnowana.

Nie przekonuje cię to? Mnie też nie  Prawda jest prozaiczna – nasz przykład, to przycięty fragment ASF – biblioteki fajnej, ale uniwersalnej. A jak wiemy za uniwersalność się płaci – najczęściej wzrostem objętości kodu i wolniejszym działaniem. Jeśli by ją dalej przyciąć i np. ustawiać zegar systemowy wpisując gotowe wartości, to spokojnie można zejść do 2-3 kB. Ciągle więcej niż w przypadku USART, ale za to o ile fajniej? :-)


Do pobrania

Pliki projektu Atmel Studio 6.1: RS232overUSB.zip

Plik IntelHex do bezpośredniego programowania dla XMEGA256A3BU (moduły Xplained XMEGA256A3BU lub moduł z modulowo.pl): RS232overUSB-XMEGA256A3BU.zip

Plik IntelHex do bezpśredniego programowania dla XMEGA128A3U (moduł z Leon Instruments): RS232overUSB-XMEGA128A3U.zip



Inne XMEGA

Jeśli posiadasz inny XMEGA niż użyty w projekcie XMEGA256A3BU, to po prostu zmień w projekcie typ procesora i przekompiluj go. Uzyskasz nowy plik hex, do zaprogramowania.



Kilka RS-232

Oczywiście możesz na raz podłączyć do PC więcej mikrokontrolerów XMEGA – w takiej sytuacji pojawią się kolejne porty szeregowe:



A czy jedna XMEGA może udawać kilka urządzeń USB? Jak sądzisz? :-)
Oczywiście, że tak, nawet nie jest to dużo trudniejsze, ale to już całkiem inna historia ...


Wstęp do XMEGA: Spis treści

Oceń artykuł.
Wasze opinie są dla nas ważne, gdyż pozwalają dopracować poszczególne artykuły.
Pozdrawiamy, Autorzy
Ten artykuł oceniam na:

Działy
Działy dodatkowe
Inne
O blogu




Dzisiaj
--> za darmo!!! <--
1. USBasp
2. microBOARD M8


Napisz artykuł
--> i wygraj nagrodę. <--


Co nowego na blogu?
Śledź naszego Facebook-a



Co nowego na blogu?
Śledź nas na Google+

/* 20140911 Wyłączona prawa kolumna */
  • 00

    dni

  • 00

    godzin

  • :
  • 00

    minut

  • :
  • 00

    sekund

Nie czekaj do ostatniego dnia!
Jakość opisu projektu także jest istotna (pkt 9.2 regulaminu).

Sponsorzy:

Zapamiętaj ten artykuł w moim prywatnym spisie treści.