Nie takie USB straszne jak je malują

Wszystko co dotyczy płytek z rodziny Discovery firmy STM
ODPOWIEDZ
elvis
Użytkownik
Posty: 57
Rejestracja: 30 lis 2018, 17:50

Nie takie USB straszne jak je malują

Post autor: elvis » 03 lip 2019, 16:29

O interfejsie USB chyba każdy słyszał, co więcej każdy chyba wie że jest trudny, skomplikowany i straszny. Spróbuję w tym i kilku kolejnych wpisach nieco powalczyć z tą opinią - albo chociaż wyjaśnić samemu sobie jak ten magiczny interfejs działa.
Od razu wyjaśnienie: nie jestem ekspertem od USB, znam go tylko pobieżnie. To nie jest wykład, ale raczej próba zrozumienia jak to cudo działa. Jeśli coś napiszę źle, nie do końca prawdziwie itd. to z góry przepraszam. Mam jednak nadzieję że uda mi się chociaż trochę odczarować czarną magię, która USB otacza.

USB jest z nami już dość długo, powstało więc całkiem sporo jego wersji. Ja skupiam się na USB 2.0, co więcej na komunikacji Full-Speed, czyli 12 Mb/s. Nie staram się również walczyć z wersjami dla USB-Host, czy USB-OTG. Interesuje prosta implementacja urządzenia (device) na mikrokontrolerze STM32.

Na początek wybór modelu mikrokontrolera i płytki prototypowej. Chciałbym zacząć od czegoś prostego, więc unikam STM32F4 z interfejsem OTG. Zostaje F0 lub F1 (oba mają taki sam interfejs sprzętowy). Wybrałem STM32L053 oraz płytkę STM32L053-Discovery (https://www.st.com/en/evaluation-tools/ ... overy.html), bo ma wyprowadzone dwa gniazda USB - jedno dla programatora ST/Link, a drugie idealne do zabawy.

Obrazek

Na początek uruchamiam niezbyt lubiany przeze mnie CubeMX i generuję gotowy projekt z obsługą USB-HID. HID (Human Interface Device - https://en.wikipedia.org/wiki/Human_interface_device) to wszelkiej maści myszki, klawiatury czy joysticki, które możemy podłączyć do komputera (hosta).
Po utworzeniu projektu można skompilować kod i uruchomić. Biblioteka dostarczana przez ST działa i na początek warto zrozumieć co i jak ona robi.
Niestety początkowy kod nie robi nic - muszę więc dodać coś co ożywi program. W początkowej części programu dodaję:

Kod: Zaznacz cały

struct mouse_report {
	uint8_t buttons;
	int8_t xrel;
	int8_t yrel;
	int8_t wrel;
};

extern USBD_HandleTypeDef hUsbDeviceFS;
Natomiast w pętli głównej prosty kod testowy:

Kod: Zaznacz cały

  while (1)
  {
	  struct mouse_report rpt = {0};
	  rpt.xrel = 10;
	  USBD_HID_SendReport(&hUsbDeviceFS, (uint8_t*)&rpt, sizeof(rpt));

	  HAL_Delay(500);
  }
Program nie jest zbyt rozbudowany. Co pół sekundy wysyła raport, na który składa się aktualny stan przycisków myszy oraz względne przesunięcie. Po uruchomieniu kursor powoli przesuwa się w prawo - powoli, bo inaczej ciężko byłoby cokolwiek zrobić...

Jak to działa?

Na tym etapie właściwie można byłoby zakończyć pracę. Mam w końcu program, który działa - zajmuje co prawda aż 17 KB a raptem niemrawo przesuwa kursor, ale skoro planowałem się czegoś o USB dowiedzieć czas podłączyć analizator i "podpatrzeć" jak ta komunikacja po USB faktycznie wygląda. A w pierwszej chwili wygląda to nieco groźnie:

Obrazek

Czas więc przeanalizować krok po kroku co też się wydarzyło.
Na początek pojawia się reset interfejsu. Normalnie linie D+ i D- mają przeciwne napięcia, host może jednak oba połączyć z masą co oznacza żądanie resetu interfejsu. Widzimy takie działanie na początku:

Obrazek

Trochę wybiegając w przyszłość - po stronie STM32 sprzęt wykrywa reset i generuje przerwanie. Nie jest to reset mikroprocesora, jedynie informacja żeby ustawić domyślne parametry komunikacji.
Po resecie host sprawdza, czy urządzenie obsługuje tryb High-Speed, czyli prędkość 480 Mb/s. Ponieważ STM32L053 tego nie obsługuje, a na początek Full-Speed mi wystarcza, nie trzeba z tym nic robić. Cały test odbywa się w pełni sprzętowo, brak reakcji urządzenia oznacza przejście w tryb Full-Speed.
W kolejnym kroku host wysyła zapytanie o typ podłączonego urządzenia:

Obrazek

GET_DEVICE_DESCRIPTOR pobiera deskryptor, czyli po prostu tablicę bajtów, która przechowuje informację o typie i możliwościach tego co właśnie zostało do USB podpięte. Okazuje się że USB używa mnóstwa deskryptorów, później do tego jeszcze wrócę. Na szczęście większość deskryptorów to po prostu stałe zaszyte w programie - możemy więc raz je wpisać i zapomnieć ;-)

Następny komunikat to ustawienie adresu, czyli SET_ADDRESS

Obrazek

Nowe urządzenie podłączone do hosta komunikuje się najpierw za pomocą adresu 0. Jednak czasem wygodniej byłoby mieć więcej urządzeń podłączonych do USB, host przypisuje więc urządzeniom adresy z zakresu 1-127. W tym przykładzie urządzenie otrzymało adres 27 i w kolejnych etapach komunikacji będzie go używało. Znowu - w STM32L053 to wszystko jest realizowane sprzętowo. Po resecie wystarczy do rejestru DADDR spisać zero, a po otrzymaniu SET_ADDRESS nową wartość.

Host potrzebuje więcej informacji o podłączonym urządzeniu, więc wysyła kolejne zapytania:

Obrazek

W międzyczasie pojawiła się ramka SOF, czyli synchronizacja. Jest wysyłana co 1ms nie musimy na nią zwracać uwagi.
Host pobrał jeszcze raz deskryptor urządzenia (pewnie windows ma sklerozę więc lubi wiele razy to samo pobierać), zapytania o napisy (String Descriptor) pozwalają np. ustalić czytelną dla użytkownika nazwę urządzenia, czy też numer seryjny. Najważniejsze jest na końcu - deskryptor konfiguracji. Host nie wiedział jak duży jest ten deskryptor, więc najpierw zapytał o 9 bajtów. W nich zawarta jest faktyczna długość deskryptora, za drugim razem wysłał to samo zapytanie ale już o pełne 34 bajty.

Przy okazji małe wyjaśnienie - kolumny Dev i Ep. Pierwsza to adres urządzenia, który ma teraz wartość 27. Natomiast Ep to numer endpoint-a, czyli bufora. Wszystkie ustawienia są zawsze wykonywane za pomocą endpoint-a zero, do tego też wrócę.

W tym momencie host wie już wszystko co potrzebował i wysyła żądanie SET_CONFIGURATION.

Obrazek

Niektóre urządzenia USB mają więcej niż jedną konfigurację. Przykładowo mogą wymagać 100mA do podstawowego działania, a dopiero jak host zmieni konfigurację na wymagającą 500mA oferować dodatkowe funkcje. W prostym przykładzie mamy tylko jedną konfigurację, więc jej wybór nie jest wielkim zaskoczeniem.

Do tego momentu wszystkie polecenia były niezależne od typu urządzenia, tak samo działałyby dla CDC (konwerter USB-UART), czy innych urządzeń. Kolejne komunikaty są już związane z wybranym typem urządzenia, czyli HID.

Obrazek

Pojawia się tutaj jeszcze jeden deskryptor. GET_REPORT_DESCRIPTOR pobiera opis używanych raportów. To właśnie tutaj zdefiniowany jest "typ" naszego urządzenia - domyślnie jest to mysza. Zwracając inny deskryptor moglibyśmy emulować klawiaturę, joystick itd.

W tym momencie konfiguracja została zakończona. Teraz widzimy już tylko jak działa nasza nowa myszka:

Obrazek

Komunikaty INPUT_REPORT są wysyłane przez wywołanie funkcji USBD_HID_SendReport w programie głównym. Ich parametry też podaje nasz program i jak widzimy są zgodne z tym co nasz program robi.

Tak wygląda komunikacja przez USB widziana z perspektywy interfejsu. W kolejnych wpisach postaram się nieco wyjaśnić nowe pojęcia - endpointy, adresy oraz użyte deskryptory.

Awatar użytkownika
l3n1n
Moderator
Posty: 322
Rejestracja: 28 paź 2017, 8:46
Lokalizacja: 3M

Re: Nie takie USB straszne jak je malują

Post autor: l3n1n » 03 lip 2019, 17:06

Imponujący kawał bardzo dobrej roboty. Dzięki

Awatar użytkownika
SunRiver
Administrator
Posty: 772
Rejestracja: 08 paź 2017, 11:27
Lokalizacja: Opole
Kontakt:

Re: Nie takie USB straszne jak je malują

Post autor: SunRiver » 03 lip 2019, 18:12

nio i super :P
Cieszy mnie że zaczyna przybywać fajnych treści na forumie :)
.... z każdym bitem serca ....
💫SunDUINO
💦GitHUB
💦Google Drive
💦Sotton

Awatar użytkownika
xbary
Użytkownik
Posty: 100
Rejestracja: 08 paź 2017, 19:59

Re: Nie takie USB straszne jak je malują

Post autor: xbary » 06 lip 2019, 12:59

A ten Debuger USB skąd wziąć?


Z tej perspektywy USB jest o wiele bardziej zjadliwym tematem :)

elvis
Użytkownik
Posty: 57
Rejestracja: 30 lis 2018, 17:50

Re: Nie takie USB straszne jak je malują

Post autor: elvis » 06 lip 2019, 13:06

Najwygodniej jest używać analizatora sprzętowego, który można np. pożyczyć w pracy ;-) Niestety są dość drogie - ja używam Beagle: https://www.totalphase.com/products/beagle-usb480/
Są też programowe analizatory usb np. https://freeusbanalyzer.com/

Dodano po 3 minutach 20 sekundach:
Do protokołu full-speed można chyba też użyć zwykłego analizatora logicznego np. Salea lub dobrego klona. Nie testowałem tego, ale oprogramowanie salea ma dekodowanie USB, więc powinno działać.

Dodano po 1 minucie 5 sekundach:
https://support.saleae.com/protocol-ana ... -protocols

ODPOWIEDZ