Většina mikrokontrolérů má vestavěnou funkci sériového USB, kterou nelze změnit (např. ESP32, ESP8266, Arduino Nano atd.). Některé mikrokontroléry však umožňují přeprogramování funkcí USB (např. Raspberry Pico, ESP32-S2, seeeduino xiao atd.) a knihovna TinyUSB je pro to velmi vhodná.
Mikrokontrolér může fungovat buď jako USB hostitel, nebo jako zařízení. V tomto článku se zaměřím na funkcionalitu USB zařízení , protože právě zde tato knihovna vyniká.
Zařízení TinyUSB může fungovat jako
-
Zařízení CDC (např. pro sériovou komunikaci)
-
Zvukové zařízení (mikrofon, hudební přehrávač)
-
HID zařízení (např. myš nebo klávesnice)
-
MIDI zařízení (pro generování nebo přehrávání MIDI hudby)
-
Zařízení WebUSB
-
Síťové zařízení (poskytující funkce TCP/IP přes USB)
Ve složce s příklady najdete kód, který demonstruje, jak danou funkcionalitu používat.
Krátký úvod do USB
USB je docela skličující a nesmírně složité zařízení. Abyste pochopili, jak správně používat knihovnu, musíte pochopit koncept deskriptorů. BeyondLogic má vynikající úvod , který mohu doporučit. Stručně řečeno, obvykle máte:
-
Deskriptor jednoho zařízení
-
Obvykle jeden konfigurační deskriptor
-
Jeden nebo více deskriptorů rozhraní pro každou konfiguraci
-
Jeden nebo více deskriptorů koncových bodů pro každé rozhraní
Kromě těchto standardních deskriptorů může každé zařízení popsat své další deskriptory specifické pro dané zařízení. A konečně existuje možnost definovat řetězce a používat je s jejich indexovými pozicemi.
Anatomie příkladu TinyUSB
Příklady jsou obvykle strukturovány do 3 souborů:
-
tusb_config.h–- který obsahuje konfiguraci -
usb_descriptors.c-– soubor obsahuje deskriptory a související zpětná volání -
main.c– který obsahuje hlavní logiku a implementaci zpětných volání
Máte však poměrně velkou flexibilitu, jak toto strukturovat ve svých vlastních projektech:
-
Můžete použít C++, abyste měli implementační soubory
.cppmísto.c -
Obsah souborů
usb_descriptors.camain.cbyste mohli sloučit do jednoho implementačního souboru (např.myproject.c). -
Pokud se chcete vyhnout souboru
tusb_config.h, můžete použít definiciCFG_TUSB_CONFIG_FILE, která ukazuje na název vašeho vlastního hlavičkového souboru. Můžete to udělat např. ve vašem souboruCMakeList.txtpomocí
add_compile_options(-CFG_TUSB_CONFIG_FILE=myproject.h ...
Konfigurace TinyUSB — tusb_config.h
Příklady jsou poměrně složité, protože musí podporovat všechny typy prostředí. Můžeme se pokusit je zredukovat na naprosté minimum , což by mělo ve většině případů fungovat:
#pragma once
//--------------------------------------------------------------------
// DEVICE CONFIGURATION
//--------------------------------------------------------------------
//#define BOARD_DEVICE_RHPORT_NUM 0
#define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_HIGH_SPEED
#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED)
#define CFG_TUD_ENDPOINT0_SIZE 64
//------------- CLASS -------------//
#define CFG_TUD_CDC 0
#define CFG_TUD_MSC 0
#define CFG_TUD_HID 0
#define CFG_TUD_MIDI 1
#define CFG_TUD_VENDOR 0
// MIDI FIFO size of TX and RX
#define CFG_TUD_MIDI_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
#define CFG_TUD_MIDI_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
Definujeme maximální velikost koncového bodu a aktivujeme třídy, které chceme v našem projektu použít. Číslo udává počet rozhraní, která chceme definovat. Ve výše uvedeném příkladu definujeme, že použijeme dvě CDC a jedno MIDI zařízení.
Deskriptory — usb_descriptors.c
Deskriptory musí poskytnout implementaci následující 3 metody zpětného volání:
uint8_t const* tud_descriptor_device_cb(void)
uint8_t const* tud_descriptor_configuration_cb(uint8_t index)
uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid)
Objekt tud_descriptor_device_cb poskytuje profil zařízení a objekt tud_descriptor_configuration_cb poskytuje zřetězené profily konfigurace, rozhraní a koncového bodu. Objekt tud_descriptor_string_cb je zodpovědný za poskytování řetězců v kódování UTF8.
Veškerá složitost v souboru usb_descriptors.c souvisí s definicí deskriptorů!
Logika programu — main.c
Základní anatomie logiky TinyUSB je poměrně jednoduchá:
-
Desku je potřeba nastavit voláním funkce board_init().
-
Musíte nastavit funkci USB voláním tusb_init().
-
Aby MIDI engine fungoval, je potřeba volat tud_task() -– tak často, jak je to jen možné.
Zbývající části jsou specifické pro danou aplikaci: možná budete chtít provést nějaké skutečné generování MIDI nebo poslat nějaké zprávy přes CDC a blikat LED diodami.
int main(void)
{
board_init();
tusb_init();
while (1)
{
tud_task(); // tinyusb device task
midi_task();
led_blinking_task();
}
return 0;
}
Aby bylo možné reagovat na některé události, framework poskytuje následující zpětná volání:
void tud_mount_cb(void) // Invoked when device is mounted
void tud_umount_cb(void) // Invoked when device is unmounted
void tud_suspend_cb(bool remote_wakeup_en) // Invoked when usb bus is suspended
void tud_resume_cb(void) // Invoked when usb bus is resumed
V příkladech se tyto metody zpětného volání používají ke změně rychlosti blikání LED diody.