Elektroda.pl
Elektroda.pl
X

Wyszukiwarki naszych partnerów

Wyszukaj w ofercie 200 tys. produktów TME
Europejski lider sprzedaży techniki i elektroniki.
Proszę, dodaj wyjątek elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

Realizacja komunikacji USB z procesorem AVR

Kubald 24 Mar 2013 18:35 17247 0
  • Realizacja komunikacji USB z procesorem AVR

    Od kiedy autor odkrył, że możliwe jest zbudowanie urządzenia wyposażonego w interfejs USB i opartego o procesor AVR, postanowił samemu takowe zbudować. Ponieważ opisy zastosowania bibliotek USBtiny i – bardziej wyczerpujący - V-USB były zbyt skomplikowane, autor postanowił stworzyć własny tutorial.

    Na początek nastąpi opis podstaw budowy urządzeń zasilanych z magistrali USB. Stanowi to wstęp do kolejnej części, gdzie zostanie przedstawiony przykład wykorzystania biblioteki V-USB, pozwalającej na obustronną komunikację z procesorem ATTiny2313.

    Do budowy najprostszego urządzenia potrzebny będzie: kabel USB, mała płytka stykowa, dioda LED i rezystor 330Ω oraz stabilizator LDO, np. typu LD1086V33 lub LE33CZ. Kabel może zostać przygotowany poprzez przecięcie dowolnego kabla komputerowego USB i dolutowanie przewodów do goldpinów. Kolory przewodów (czerwony, biały, zielony, czarny) odpowiadają kolejno VCC (+5V), D-, D+ oraz masie (0V). W razie potrzeby, więcej informacji nt. charakterystyk elektrycznych interfejsu USB można znaleźć w artykule na stronach Beyond Logic lub w specyfikacji standardu USB 2.0. Poprawność dostarczania zasilania można sprawdzić multimetrem lub podłączając przez rezystor diodę LED do tak przygotowanego kabla. Należy pamiętać jedynie, że wydajność prądowa portu USB jest niewielka (wynosi kilkaset miliamperów). W czasie montażu należy zwracać też uwagę na zwarcia, które mogą spowodować uszkodzenie komputera lub koncentratora USB.

    Ponieważ napięcie na liniach danych portu USB wynosi maksymalnie 3,3V, należy dokonać konwersji poziomów napięć, na przykład obniżając napięcie zasilania z 5V na 3,3V, zasilając układ zewnętrznym napięciem 3,3V bądź stosując rezystory albo diody Zenera do obniżania napięcia. Autor wybrał pierwszy z przedstawionych sposobów i wykorzystał stabilizator LD1086V33. Blok zasilania przedstawiono na poniższym schemacie:

    Realizacja komunikacji USB z procesorem AVR

    Celem sprawdzenia funkcjonowania całości należy zbudować prosty układ z diodą LED:

    Realizacja komunikacji USB z procesorem AVR





    Kiedy blok zasilania funkcjonuje prawidłowo, można przejść do przyłączania do portu USB procesora ATTiny 2313. W tym celu potrzebne będą, poza samym procesorem, dwa kondensatory 27 pF, rezonator kwarcowy 12 MHz, rezystory 68 Ω włączane na liniach danych, rezystory 1 MΩ i 1,5 kΩ do podciągania linii danych oraz rezystor 4,7 kΩ przyłączany do wejścia resetującego procesor.

    Autor zastrzega, że mimo zastosowania kwarcu 12MHz przy napięciu zasilania 3,3V, nigdy nie miał problemów ze stabilną pracą urządzeń, mimo że przekracza to specyfikację procesora (częstotliwości powyżej 10 MHz wymagają napięcia zasilania większego od 4,5V). Jeśli jednak występowałyby problemy, można spróbować zasilać procesor napięciem 5V, a linie danych D- i D+ podciągnąć za pomocą diod Zenera do zasilania, aby obniżyć napięcie tylko dla nich.

    Schemat obwodu do testów przedstawia się następująco:

    Realizacja komunikacji USB z procesorem AVR

    Kiedy wszystko jest gotowe, procesor można zaprogramować, aby sprawdzić, czy wszystko działa. Autor napisał prosty program testowy w C:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Realizacja komunikacji USB z procesorem AVR

    W procesorze należy zablokować wewnętrzny dzielnik zegara przez osiem, będzie on taktowany częstością ponad 8 MHz, z 14 cyklami zegarowymi i opóźnieniem uruchomienia wynoszącym 4,1 ms (CLKSEL = 1111, SUT = 10). Oznacza to, że niższy fuse bit powinien wynosić 0xEF. Zmieniając wartości niższego fuse bitu i zmiennej F_CPU w programie można mieć oczywiście wpływ na szybkość migania diody.

    Aby zaimplementować w urządzeniu komunikację za pomocą USB, potrzebna będzie biblioteka V-USB, którą można pobrać ze strony OBdev. Pobrane archiwum należy wypakować i przekopiować folder usbdrv do folderu z projektem. Następnie, należy skopiować plik usbconfig-prototype.h i zmienić jego nazwę na usbconfig.h. W pliku, w sekcji #define należy określić, do których pinów mikrokontrolera podłączone są linie USB oraz częstotliwość taktowania procesora:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Można także zapewnić, że urządzenie prześle do komputera informacje, że jest zasilane z portu USB i pobiera maksymalnie 50 mA prądu:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Wartości identyfikatora urządzenia (device ID) i dostawcy licencji (vendor ID) nie zostaną zmienione. Natomiast można zmodyfikować nazwę dostawcy i nazwę urządzenia. Znak „\” w poniższym przykładzie oznacza, że linia definicji nazwy dostawcy została podzielona na dwie.

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Plik usbconfig.h posiada bardzo obszerną dokumentację, więc można z powodzeniem dowiedzieć się, za co odpowiadają pozostałe opcje. Po skonfigurowaniu USB w omawianym pliku, zostajemy z „gołym” plikiem main.c:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Kod powinien być prosty do zrozumienia:
    - na początku zostaje dołączona bibioteka V-USB w postaci pliku usbdrv.h,
    - zaimplementowana jest instrukcja usbFunctionSetup() do obsługi portu USB,
    - w głównej części kodu timer watchdoga zostaje ustawiony na jedną sekundę, co spowoduje reset mikroprocesora, jeśli w czasie 1000 milisekund nie nastąpi wywołanie wdt_reset(),
    - polecenie usbInit() inicjuje bibliotekę V-USB,
    - ponowna enumeracja wywoływana jest instrukcją usbDeviceDisconnect() i następującą 500 ms po niej (przy resetowaniu watchdoga co 2 ms) instrukcją usbDeviceConnect(),
    - przerwania zostają uaktywnione,
    - realizowana jest nieskończona pętla, w której resetowany jest watchdog i inicjowana jest instrukcja usbPoll().

    Watchdog jest wykorzystywany ze względu na możliwość zawieszenia się programu, na przykład wskutek odczytu błędnych czy niekompletnych danych, co może spowodować brak odpowiedzi urządzenia USB. Dlatego w czasie nie dłuższym od jednej sekundy musi zostać wywołana instrukcja wdt_reset(). Jeśli to nie nastąpi, procesor zostanie zresetowany, a wykonywanie programu rozpocznie się od nowa. Aczkolwiek stosowanie watchdoga nie jest absolutnie konieczne, ale jest zalecane i powinno stanowić element „dobrej praktyki”, zapobiegający konieczności np. ręcznego resetowania urządzenia, kiedy to się zawiesi.

    Procedura ponownej enumeracji gwarantuje, że identyfikator urządzenia zostanie zapamiętany przez komputer i urządzenie w razie zresetowania ponownie zostanie poprawnie wykryte.

    Zanim zostanie pokazane, jak sterować urządzeniem ze strony komputera, należy zdefiniować jeszcze dwie podstawowe komendy przekazywane przez USB, a odpowiadające za włączanie i wyłączanie diody LED:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Dzięki temu automatycznie, po odebraniu komendy, będzie wywoływana metoda usbFunctionSetup(). Parametrem w tym przypadku jest bufor o rozmiarze 8 bajtów, zawierający strukturę nazywaną usbRequest_t, która zdefiniowana jest w pliku usbdrv.h. W przypadku tego przykładu interesująca jest jedynie właściwość bRequest, która przyjmuje wartości 0 (USB_LED_OFF) lub 1 (USB_LED_ON). Program sterujący diodą będzie wyglądał następująco:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Jeśli urządzenie ma być sterowane przez komputer pracujący pod kontrolą systemu Linux, sprawa jest łatwa – wystarczy pobrać bibliotekę LibUSB i skompilować klienta konsolowego do sterowania urządzeniem. W systemach Windows potrzebny będzie odpowiedni sterownik, a nader nieciekawy jest fakt, że przykłady tychże zamieszczone na stronie biblioteki V-USB nie działają pod kontrolą systemu Windows 7 w wydaniu 64-bitowym. Dlatego poniżej zostanie przedstawione, jak stworzyć taki sterownik samemu.

    Po podłączeniu urządzenia do portu USB od razu powinno zostać ono wykryte przez system jako nowe urządzenie. System będzie starał się wyszukać sterownik, co oczywiście nie powiedzie się. Wtedy w sukurs przyjdzie biblioteka libusb-win32. Zawarte w tej bibliotece narzędzie INF-wizard, generujące plik sterownika, wygląda następująco:

    Realizacja komunikacji USB z procesorem AVR

    Jeśli urządzenie nie zostanie wykryte, należy usunąć błędnie zainstalowane sterowniki lub nawet zresetować komputer albo wyczyścić pamięć cache USB. Jeśli wszystko się jednak powiedzie, program wygeneruje nam sterownik i zaproponuje jego instalację.

    Kolejnym krokiem jest napisanie klienta do komunikacji z urządzeniem. Autor w tym celu zaleca skorzystanie z oprogramowania kompilera GNU C oraz narzędzia MSYS, ale także Visual C lub inne narzędzia się sprawdzą. Pierwszą rzeczą po instalacji kompilera jest zmiana nazwy pliku lusb0_usb.h, pochodzącego z biblioteki libusb-win32, na usb.h.

    Do komunikacji z urządzeniem wykorzystana będzie funkcja usb_control_msg() zaimplementowana w bibliotece libusb-win32. Po wysłaniu instrukcji usb_control_msg() wykonywany jest poniższy kod, odpowiedzialny za sprawdzenie zwracanej wartości i jej długości oraz konwersję z formatu UTF16-LE do Latin1:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Poniższy kod odpowiada za wyszukanie urządzenia o odpowiednich identyfikatorach pośród wszystkich urządzeń podłączonych do magistrali USB:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Kiedy znana jest już wartość uchwytu (handle) urządzenia, reszta kodu jest bardzo prosta:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Należy zwrócić uwagę, że instrukcja usb_control_msg() operuje teraz parametrem USB_TYPE_VENDOR do wskazania, że przesyłana jest komenda sterująca. Po skompilowaniu całości otrzymamy program sterujący diodą LED.

    Jeśli uważnie przyjrzeć się kodowi klienta dla komputera, można zauważyć, że wysyłane do urządzenia wiadomości sterujące diodą mają typ USB_ENDPOINT_IN i że wykorzystywany jest też 256-bajtowy bufor, w którym umieszczone są dane wysłane przez urządzenie.

    Dane do komputera można wysłać następująco: najpierw zostanie zdefiniowana komenda USB_DATA_OUT i bufor, w którym znajdują się dane do wysłania:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Instrukcja usbFunctionSetup() musi zostać zmodyfikowana tak, aby zwracała wartość buforu:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Należy również dopisać kod realizujący odbiór danych po stronie komputera:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Wysyłanie danych z komputera do urządzenia może być zrealizowane za pomocą parametrów wValue i wIndex. Należy również zdefiniować komendę USB_DATA_WRITE:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Do programu dla procesora należy dopisać następujące instrukcje:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Po stronie programu komputerowego zostanie użyta kolejna funkcja, wysyłająca wiadomość „TEST”:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Po zrozumieniu działania funkcji usbFunctionWrite(), odbieranie danych za pomocą polecenia usbFunctionRead() nie powinno sprawiać większych problemów.

    Na stronie źródłowej (i kolejnych częściach tutorialu) znaleźć można bardziej szczegółowy opis działania poszczególnych przykładów kodów.


    Fajne!
TME logo Szukaj w ofercie
Zamknij 
Wyszukaj w ofercie 200 tys. produktów TME
TME Logo