USB w urządzeniach elektronicznych

| Technika

Port USB jest obecnie nieodłącznym wyposażeniem współczesnych komputerów, a liczba dołączanych do niego urządzeń takich jak myszy, klawiatury, drukarki, aparaty, modemy GSM, pamięci masowe, programatory, debuggery, ładowarki, cały czas rośnie. Czasy, kiedy poszczególne urządzenia miały własne porty, np. LPT dla drukarek i skanerów, minęły bezpowrotnie. Jest to znaczący postęp i ułatwienie dla użytkowników sprzętu, ale nie dla producentów, gdyż oznacza to rezygnację z dotychczas stosowanych łatwych w implementacji portów komunikacyjnych takich jak RS 232. Współcześnie istnieje kilka możliwości zrealizowania transmisji przez port USB, takich jak moduły sprzętowe wbudowane w mikrokontrolery, układy konwertujące standard USB na inny, dedykowane przejściówki. Każde z tych rozwiązań ma wady i zalety warte omówienia.

USB w urządzeniach elektronicznych
Protokół komunikacyjny

Komunikacja w standardzie USB polega na przesyłaniu pakietów, które dzielą się na:

  • tokeny - określają one charakter przesyłanych informacji (kontrolny bądź dane), wyznaczają początek ramki i zarządzają magistralą,
  • pakiet danych - informacje zapisywane do urządzenia bądź z niego odczytywane,
  • pakiety potwierdzenia - zawiera informację o odebraniu danych bądź instrukcji sterujących oraz zdarzeniach występujących podczas transmisji: potwierdzenie pozytywne i negatywne, wewnętrzne błędy.

Przesyłane i odbierane dane są umieszczane w tzw. punktach końcowych (endpoints), które w rzeczywistości są buforami w formie kolejki FIFO zlokalizowanymi w pamięci. Dane są w nich zapisywane lub odczytywane w sposób sekwencyjny. Ze względu na przyjęty sposób adresowania, urządzenie może mieć maksymalnie 32 punkty końcowe (16 wejściowych i 16 wyjściowych).

Najważniejsze parametry to: rozmiar oraz wykorzystywany tryb transmisji (patrz ramka). Specyfikacja USB nakazuje, aby każde urządzenie obsługiwało Endpoint0 spełniający funkcje kontrolne. Jest on jako jedyny dwukierunkowy, pozostałe punkty końcowe mają charakter wyjściowy lub wejściowy, gdzie wyjściowy (Out) oznacza przesyłanie danych z hosta do urządzenia.

Należy wspomnieć, że punkty końcowe mają tylko urządzenia, host-komputer jest ich pozbawiony. Ze standardem USB nierozerwalnie związane jest pojęcie deskryptora. Są to tablice o ściśle określonej strukturze przechowujące pełną informację o urządzeniu. Zawierają one wpisy, takie jak: numery VID oraz PID, klasę urządzenia, wersję USB obsługiwaną przez urządzenie, maksymalny rozmiar pakietu czy nazwę produktu.

Korzystając z bibliotek dostarczonych przez producentów mikrokontrolerów, warto zmienić przynajmniej trzy parametry: identyfikatory VID/PID, a także nazwę urządzenia, która będzie widoczna w systemie. Przykładowe deskryptory pochodzące z biblioteki Microchipa pokazano na listingu 1. Deskryptory tworzą hierarchiczną strukturę o organizacji pokazanej na rysunku 2.

Deskryptor urządzenia służy przede wszystkim do identyfikacji sprzętu i zawiera informacje niezbędne do zainstalowania poprawnego sterownika. Pozwala również określić liczbę dostępnych konfiguracji urządzenia, gdyż niekiedy może występować więcej niż jedna, np. praca normalna, praca w stanie obniżonego poboru mocy. W takiej sytuacji zdefiniowana jest większa liczba deskryptorów konfiguracji.

Listing 1. Deskryptory USB pochodzące z biblioteki Microchipa

/* Device Descriptor */
ROM USB _ DEVICE _ DESCRIPTOR device _ dsc=
{
0x12, // Size of this descriptor in bytes
USB _ DESCRIPTOR _ DEVICE, // DEVICE descriptor type
0x0200, // USB Spec Release Number in BCD format
0x00, // Class Code
0x00, // Subclass code
0x00, // Protocol code
USB _ EP0 _ BUFF _ SIZE, // Max packet size for EP0, see
usb _ confi g.h
0x04D8, // Vendor ID
0x003F, // Product ID: Mouse in a circle fw demo
0x0002, // Device release number in BCD format
0x01, // Manufacturer string index
0x02, // Product string index
0x00, // Device serial number string index
0x01 // Number of possible confi gurations
};
/* Confi guration 1 Descriptor */
ROM BYTE confi gDescriptor1[]={
/* Confi guration Descriptor */
0x09,//sizeof(USB _ CFG _ DSC), // Size of this descriptor in bytes
USB _ DESCRIPTOR _ CONFIGURATION, // CONFIGURATION descriptor
type
0x29,0x00, // Total length of data for this cfg
1, // Number of interfaces in this cfg
1, // Index value of this confi guration
0, // Confi guration string index
_ DEFAULT | _ SELF, // Attributes, see usb _ device.h
50, // Max power consumption (2X mA)
/* Interface Descriptor */
0x09,//sizeof(USB _ INTF _ DSC), // Size of this descriptor in bytes
USB _ DESCRIPTOR _ INTERFACE, // INTERFACE descriptor type
0, // Interface Number
0, // Alternate Setting Number
2, // Number of endpoints in this intf
HID _ INTF, // Class code
0, // Subclass code
0, // Protocol code
0, // Interface string index
/* HID Class-Specifi c Descriptor */
0x09,//sizeof(USB _ HID _ DSC)+3, // Size of this descriptor in bytes
DSC _ HID, // HID descriptor type
0x11,0x01, // HID Spec Release Number in BCD format (1.11)
0x00, // Country Code (0x00 for Not supported)
HID _ NUM _ OF _ DSC, // Number of class descriptors, see usbcfg.h
DSC _ RPT, // Report descriptor type
HID _ RPT01 _ SIZE,0x00,//sizeof(hid _ rpt01), // Size of the report
descriptor
/* Endpoint Descriptor */
0x07,/*sizeof(USB _ EP _ DSC)*/
USB _ DESCRIPTOR _ ENDPOINT, //Endpoint Descriptor
HID _ EP | _ EP _ IN, //EndpointAddress
_ INTERRUPT, //Attributes
0x40,0x00, //size
0x01, //Interval
/* Endpoint Descriptor */
0x07,/*sizeof(USB _ EP _ DSC)*/
USB _ DESCRIPTOR _ ENDPOINT, //Endpoint Descriptor
HID _ EP | _ EP _ OUT, //EndpointAddress
_ INTERRUPT, //Attributes
0x40,0x00, //size
0x01 //Interval
};
//Language code string descriptor
ROM struct{BYTE bLength;BYTE bDscType;WORD string[1];}sd000={
sizeof(sd000),USB _ DESCRIPTOR _ STRING,{0x0409
}};
//Manufacturer string descriptor
ROM struct{BYTE bLength;BYTE bDscType;WORD string[25];}sd001={
sizeof(sd001),USB _ DESCRIPTOR _ STRING,
{‘M’,’i’,’c’,’r’,’o’,’c’,’h’,’i’,’p’,’ ‘,
‘T’,’e’,’c’,’h’,’n’,’o’,’l’,’o’,’g’,’y’,’ ‘,’I’,’n’,’c’,’.’
}};
//Product string descriptor
ROM struct{BYTE bLength;BYTE bDscType;WORD string[22];}sd002={
sizeof(sd002),USB_DESCRIPTOR_STRING,
{‘S’,’i’,’m’,’p’,’l’,’e’,’ ‘,’H’,’I’,’D’,’ ‘,
‘D’,’e’,’v’,’i’,’c’,’e’,’ ‘,’D’,’e’,’m’,’o’
}};

Każda z takich konfiguracji może oferować kilka interfejsów do jednoczesnej realizacji różnorodnych zadań, przykładem jest napęd DVD umożliwiający zapisywanie danych, odtwarzanie muzyki oraz wideo. Deskryptor punktu końcowego opisuje element będący źródłem lub odbiornikiem informacji w urządzeniu.

Po podłączeniu urządzenia peryferyjnego konieczne jest jego rozpoznanie, sprawdzenie poprawności komunikacji, konfiguracja, przydzielenie adresu oraz instalacja sterowników. Proces ten nosi nazwę enumeracji. Dołączony sprzęt zostanie zaakceptowany tylko wtedy, gdy host będzie w stanie spełnić stawiane wymagania: przydział żądanego pasma czy zapewnienie wydajność prądowej na poziomie 500mA.

W przeciwnym przypadku komunikacja nie będzie możliwa mimo fizycznego połączenia. Enumeracja rozpoczyna się od wymuszenia na liniach danych stanu niskiego przez minimum 10ms, co jest informacją dla urządzenia, żeby przełączyło się w stan domyślny. Obejmuje on pracę z adresem 0 i otwarcie zerowego punktu końcowego (endpoint0), co pozwala ustanowić kanał komunikacyjny i skonfigurować urządzenie.

Komunikacja wówczas odbywa się za pomocą pakietów zawierających 8 bajtów, aby obsłużyć nawet najprostsze urządzenia. Po wprowadzeniu układu w stan domyślny host ma możliwość komunikowania się z nim i pobrania deskryptorów. Pozwoli to poznać konfigurację urządzenia: stosowany rozmiar pakietów danych, liczbę punktów kontrolnych i odczytać pozostałe deskryptory, a urządzenie otrzymuje nowy, unikalny adres wykorzystywany do późniejszej komunikacji.

W przeciwieństwie do starszych interfejsów komunikacyjnych, takich jak RS232, standard USB definiuje mechanizmy odpowiedzialne za wykrywanie błędów transmisji. Podstawowym działaniem w takiej sytuacji jest nieodsyłanie potwierdzenia. Jego brak wymusi retransmisję uszkodzonego pakietu. Warto zauważyć, że nie odnosi się to do trybu izochronicznego, gdyż nie gwarantuje on, że dostarczone pakiety pozbawione są przekłamań.

Wykrycie nieprawidłowości jest możliwe dzięki dołączaniu do pakietów sumy kontrolnej CRC i ograniczeniu czasu oczekiwania na odesłanie potwierdzenia. Po przekroczeniu dopuszczalnego czasu, odpowiadającego czasowi trwania od 16 do 18 bitów, pakiet zostanie uznany za stracony. Utracie synchronizacji, zwłaszcza w sytuacji przesyłania dużych bloków danych, zapobiega mechanizm przełączania pakietów.

Polega on na naprzemiennym przesyłaniu pakietów danych Data 0 oraz Data 1 różniących się identyfikatorem PID. Nadajnik oraz odbiornik przechowują bit (toggle bit) informujący o tym, który z pakietów powinien zostać nadany. Stan bitu ulega zmianie na przeciwny po poprawnym odbiorze. Oznacza to, że nadajnik wysyła pakiet Data 0, a odbiornik sprawdza, czy bit jest wyzerowany.

Po stwierdzeniu, że tak jest, uznaje się, że dostarczono poprawny pakiet danych - nadajnik i odbiornik zmieniają stan bitu na przeciwny. W kolejnym kroku wysyłany zostanie pakiet Data 1. Po jego odebraniu odbiornik sprawdzi, czy bit jest ustawiony, jeżeli tak będzie, bit jest ponownie negowany i wysyłany jest pakiet Data 0.

Procedura ta powtarza się cyklicznie. Nietrudno zauważyć, że utrata jednego pakietu sprawi, że zapamiętany bit nie będzie zgodny z zawartością pola PID w przesłanym pakiecie. Po stwierdzeniu takiego stanu rzeczy rozpocznie się przywracanie synchronizmu.

Zobacz również