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:
- 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.
* 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.
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
Noch keine Kommentare.