Zurück zur Startseite

USB-Controller auf FPGA: Entwicklung für HID-Geräte am Beispiel einer Tastatur

Der Artikel widmet sich der Entwicklung eines USB 1.1-Controllers auf FPGA-Basis zur Verbindung von HID-Geräten wie einer Tastatur. Er behandelt modulare Architektur in Verilog, den Geräteinitialisierungsprozess, die Bedeutung der rechtzeitigen Übertragung von SOF-Paketen und die Analyse von HID-Deskriptoren.

Erstellen eines USB-Controllers auf FPGA: von der Theorie zu einer funktionierenden Tastatur
Advertisement 728x90

USB-1.1-Controller auf FPGA bauen: Praktischer Leitfaden zur HID-Tastatur-Integration

Die Entwicklung eines eigenen USB-Controllers auf einem FPGA erfordert ein solides Verständnis des Protokolls und der Hardware-Eigenarten. Dieser Leitfaden führt Schritt für Schritt durch die Umsetzung eines USB-1.1-Controllers, der eine Tastatur anschließt, und deckt zentrale Module, Geräteinitialisierung und HID-Deskriptor-Verarbeitung ab.

Hardware-Eigenschaften von USB 1.1

Im Gegensatz zu USB 2.0 verwendet USB 1.1 nur zwei digitale Leitungen (D+ und D-), die im Differenzmodus laufen und direkt an FPGA-Pins angeschlossen werden können. Es gibt zwei Geschwindigkeiten: Low Speed (1,5 Mbps) und Full Speed (12 Mbps). Das Gerät signalisiert seine Geschwindigkeit, indem es bei der Verbindung D- für Low Speed oder D+ für Full Speed hochzieht.

Wichtige Hardware-Aspekte:

Google AdInline article slot
  • Datenleitungen benötigen auf Host-Seite Pull-Down-Widerstände gegen Masse.
  • Schützen Sie FPGA-Eingänge vor 5-V-Spikes, z. B. mit 3,3-V-Zener-Dioden.
  • Für Tests eignen sich Boards wie die STM32 BluePill zur Emulation von HID-Geräten.

Architektur des USB-Controller-Moduls

Der Controller zerfällt in miteinander verknüpfte Module, die jeweils eine spezifische Aufgabe im USB-Protokollstapel übernehmen.

  • M_DATA — Kernmodul für Senden und Empfangen von Daten.
  • M_CRC16_USB — Berechnet CRC16-Prüfsummen für Datenpakete.
  • M_GET_PACKET — Behandelt Datenempfang: sendet IN-Token, empfängt Paket, prüft CRC und sendet ACK.
  • M_RECEIVE_MODULE — Datenempfangsmodul, einschließlich:

* M_GET_DATA — Liest Rohdaten aus dem Bus.

* M_MEMORY_BUF_CRC16 — Puffer, der CRC16 laufend berechnet.

Google AdInline article slot

* M_BUF_RETRANSLATOR — Leitet Daten aus Puffer nur nach erfolgreicher CRC-Prüfung weiter.

  • M_SEND_PACKET — Verwaltet Datensendung: OUT-Token, Datensendung und ACK-Wartezeit.
  • M_TRANSMIT_MODULE — Sendet Daten, Tokens, Start-of-Frame (SOF)-Pakete und Handshake-Pakete.
  • M_CRC_5 — Berechnet CRC5 für Tokens, SOF und Handshakes.
  • M_SEND_DATA — Sendet Datenpakete mit CRC16.
  • M_SEND_TOKEN — Sendet Tokens, SOF und Handshakes (alle mit CRC5).
  • M_SEND_END_OF_PACKET — Erzeugt End-of-Packet (EOP)-Signal.
  • M_DATA_TRANSFER — Physische Schicht für Bitübertragung auf dem Bus.
  • M_SOF_SENDER — Sendet periodisch SOF-Pakete für Takt-Synchronisation.
  • M_MAIN_AUTOMAT — Hauptkontrollautomat des Controllers.
  • M_USB_INIT — Behandelt USB-Geräteinitialisierung.
  • M_TRANSACTION — Verarbeitet Control-Transfers.
  • M_HID_ANALYZER — Parst HID-Deskriptoren zur Erkennung von Datenformaten.
  • MEMORY_BUF (4 Instanzen) — Puffer für HID-Analyse-Daten.

Geräteinitialisierungsprozess

Die Initialisierung beginnt mit der Geschwindigkeitserkennung aus den Zuständen von D+ und D-. Danach führt der Hauptkontrollautomat (M_MAIN_AUTOMAT) eine präzise Abfolge aus.

always @(posedge clk)
begin
    if (rst)
    begin
        State_main <= S_IDLE;
        SOF_En <= 1'b0;
        FullSpeedConnect <= 1'b0;
        // ... reset other signals
    end
    else
    begin
        case (State_main)
            S_IDLE:
                // Wait for connection and speed detection
                if (Dm & !Dp) // Low Speed
                begin
                    FullSpeedConnect <= 1'b0;
                    ResetTime <= 15_000; // 10 ms at 1.5 MHz
                    // ... set Low Speed timers
                    State_main <= S_POWER_RISE;
                end
                else if(Dp & !Dm) // Full Speed
                begin
                    FullSpeedConnect <= 1'b1;
                    ResetTime <= 120_000; // 10 ms at 12 MHz
                    // ... set Full Speed timers
                    State_main <= S_POWER_RISE;
                end
            S_POWER_RISE:
                // Wait 100 ms for power stabilization
                if (WaiteCount == PowerRiseTime || SKIP_POWER_RISE)
                begin
                    WaiteCount <= 0;
                    State_main <= S_USB_RESET;
                end
            S_USB_RESET:
                // Device reset (0 on D+ and D- for 10 ms)
                if (WaiteCount == ResetTime || SKIP_POWER_RISE)
                begin
                    WaiteCount <= 0;
                    State_main <= S_INIT_SOF_SENDER;
                end
            S_INIT_SOF_SENDER:
                // Start SOF packets right after reset
                if (Eof1) State_main <= S_WAITE_SOF;
                else SOF_En <= 1'b1;
            S_WAITE_SOF:
                if (!Eof1) State_main <= S_USB_RESET_RECOWERY;
            S_USB_RESET_RECOWERY:
                // Recovery after reset (10 ms)
                if (WaiteCount == ResetTime || SKIP_POWER_RISE)
                begin
                    WaiteCount <= 0;
                    State_main <= S_USB_INIT;
                end
            S_USB_INIT:
                // Run control transfer sequence
                if (InitComplite)
                begin
                    State_main <= S_REQUEST;
                end
                else if (InitFail)
                    State_main <= S_FAIL;
            // ... polling states (S_REQUEST, S_WAIT, etc.)
        endcase
    end
end

Wichtiger Tipp: Starten Sie das Senden von SOF (Start of Frame)-Paketen direkt nach der Reset-Phase (S_USB_RESET), nicht in der Recovery. SOF hält das Gerät mit dem Host synchron. Full Speed verwendet eine Framenummer mit CRC5; Low Speed sendet nur EOP (zwei Nullzyklen auf beiden Leitungen). Eine Verzögerung von SOF kann die Kompatibilität mit manchen Geräten stören.

Google AdInline article slot

HID-Deskriptor-Parsing und Gerätepolling

Nach Abschluss der Initialisierung – inklusive Adresszuweisung – wechselt der Controller in den Polling-Modus (S_REQUEST). Das M_HID_ANALYZER-Modul analysiert über Control-Transfers abgerufene Deskriptoren, um Datenpaketformate und -größen zu ermitteln. Für eine Tastatur extrahiert es Report-Anzahl, -Größe und IDs.

Typische HID-Polling-Schleife:

  • Senden eines IN-Tokens an Geräteadresse und Endpoint 1.
  • Empfangen des Datenpakets.
  • Prüfen von CRC16.
  • Senden von ACK bei Erfolg.
  • Extrahieren der Nutzlast (z. B. Tastencodes), Überspringen führender Header-Bytes.
  • Warten des Polling-Intervalls (z. B. 24 ms) vor nächstem Poll.

Bei NAK (Not Acknowledged) später erneut versuchen. Keine Antwort oder Fehler? Auf Wartezustand oder Re-Init umschalten.

Wichtige Erkenntnisse

  • SOF-Pakete pünktlich: Start-of-Frame direkt nach Reset senden – essenziell für zuverlässigen HID-Betrieb.
  • FPGA-Eingangsschutz: Datenleitungen vor 5 V mit geeigneter Schaltung schützen, da USB 3,3 V/5 V mischt.
  • Deskriptor-Parsing: HID funktioniert ohne Zerlegung der Deskriptoren zur Datenstruktur-Zuordnung nicht.
  • Fehlerbehandlung: NAKs, STALLs und Timeouts elegant managen für stabile Verbindungen.
  • Modulares Design: Aufteilung in Module erleichtert Debugging und Wiederverwendung in Projekten.

— Editorial Team

Advertisement 728x90

Weiterlesen