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 .cpp místo .c

  • Obsah souborů usb_descriptors.c a main.c byste mohli sloučit do jednoho implementačního souboru (např. myproject.c).

  • Pokud se chcete vyhnout souboru tusb_config.h, můžete použít definici CFG_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 souboru CMakeList.txt pomocí

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.

Zdroje a odkazy