Kampis Elektroecke

Betriebsmodus: USB-Device


Im letzten Teil bin ich etwas detailierter auf die Grundlagen des USB eingegangen, um ein erstes Verständnis über den Ablauf der USB-Kommunikation zu schaffen. Dieses Wissen soll jetzt genutzt werden um den AT90USB1287 als USB-Device zu betreiben.

Für den Anfang soll der Mikrocontroller als USB-Device agieren. Dazu muss das USB-Modul als USB-Device initialisiert und konfiguriert werden. Die Initialisierung erfolgt in mehreren Schritten. Den Anfang mache ich mit diesem Teil des Tutorials, wo ich auf die Initialisierung des Mikrocontrollers als USB-Device eingehe. Der nächste Teil wird sich mit der Konfiguration der USB-Endpunkte beschäftigen und anschließend werden die Deskriptoren erstellt und das Gerät beim Host angemeldet und eine Kommunikation etabliert.

Die komplette Konfiguration des USB-Moduls unterteilt sich in die folgenden Schritte:

  • Konfiguration des USB-Controllers (Teil dieses Abschnittes)
  • Konfiguration des USB Device-Controllers (wird in den nächsten Abschnitten erklärt)
  • Konfiguration des USB Host-Controller (Teil eines anderen Kapitels)

Allgemeine Konfigurationen werden im USB-Controller-Modul durchgeführt und für die jeweilige Betriebsart wird der Host-, bzw. Device-Controller genutzt. Da der Mikrocontroller als USB-Device arbeiten soll, werden sowohl der USB-Controller, als auch der USB-Device-Controller benötigt.

Bevor der USB-Controller genutzt werden kann, muss er zurückgesetzt werden, damit sich dieser in einem definierten Zustand befindet. Dazu wird das USBE-Bit im USBCON-Register des USB-Controllers gelöscht und direkt wieder gesetzt:

USBCON &= ~(0x01 << USBE);
USBCON |= (0x01 << USBE);

Im weiteren Verlauf dieses Programms sollen bestimmte Aktionen nur durchgeführt werden, wenn sich das USB-Modul in einem bestimmten Zustand befindet (z. B. soll die Anmeldung beim Host nicht durchgeführt werden, wenn der USB-Controller nicht initialisiert worden ist). Daher wird der Zustand zusätzlich noch in einer Variable gespeichert.

extern volatile USB_State_t _USBDeviceState;

_USBDeviceState = USB_STATE_UNATTACHED;

Das weitere Vorgehen kann dem Datenblatt entnommen werden.

Nach einem Reset ist automatisch der USB-Device-Modus aktiviert, da der externe UID-Pin des Mikrocontrollers deaktiviert ist (UIDE = 0) und das UIMOD-Bit gesetzt ist. Weiterhin wird bei einem Reset die PLL für das USB-Modul deaktiviert, indem das FRZCLK-Bit im USBCON-Register gesetzt wird. Auch dieses Bit muss nach einem Reset gelöscht werden.

USBCON &= ~(0x01 << FRZCLK);

Es wird empfohlen den internen Spannungsregler zu verwenden um die Versorgungsspannung der Datenleitungen zu gewährleisten. Dieser Spannungsregler ist nach einem Reset automatisch inaktiv und muss daher aktiviert werden, indem das UVREGE-Bit im UHWCON-Register gesetzt wird.

UHWCON |= (0x01 << UVREGE);

Damit sich der Mikrocontroller am USB anhängen und der Host das neue Gerät erkennen kann, muss zudem das VBUS-Pad über das OTGPADE-Bit im USBCON-Register aktiviert werden.

USBCON |= (0x01 << OTGPADE);

Die Konfigurationen am USB-Controller sind damit abgeschlossen. Das USB-Modul arbeitet nun im Device-Modus und der USB-Controller ist einsatzbereit. Jetzt muss noch der Device-Modus konfiguriert werden.

Die USB-Geschwindigkeit wird über das LSM-Bit im UDCON-Register gesteuert. Je nachdem ob das Bit gesetzt oder gelöscht wird, wird der Pull-Up Widerstand für die Datenleitung beim Verbinden entweder an D+ oder D- gelegt.

In diesem Beispiel soll sich der Mikrocontroller als Low-Speed Device anmelden. Dafür muss das LSM-Bit im UDCON-Register gesetzt werden.

UDCON |= (0x01 << LSM);

Sowohl der USB-Controller, als auch der USB-Device-Controller verfügen über eine ganze Reihe von Interrupts um auf externe Signale zu reagieren.

Aktuell wird lediglich der VBUS-Interrupt benötigt, da dieser für die Plug-In Erkennung genutzt wird. Dieser Interrupt wird über das VBUSTE-Bit im USBCON-Register aktiviert.

USBCON |= (0x01 << VBUSTE);

Damit der Mikrocontroller mit dem Bus verbunden werden kann, muss noch das DETACH-Bit im UDCON-Register gelöscht werden. Dadurch wird der ausgewählte Pull-Up Widerstand mit der entsprechenden Datenleitung verbunden und das Gerät wird vom Host erkannt.

UDCON &= ~(0x01 << DETACH);

Für eine ordnungsgemäße Funktion des USB-Controllers wird ein 48 MHz Takt benötigt, aus dem der 12 MHz Takt für den Full-Speed, bzw. der 1,5 MHz Takt für den Low-Speed Modus generiert wird. Dieser Takt wird mit Hilfe einer internen PLL und einem externen 8 MHz Quarz erzeugt.

Es ist durchaus sinnvoll, dass die PLL erst aktiviert wird, wenn eine Verbindung mit einem Host erkannt wird um die Stromaufnahme des Gerätes bei Nichtbenutzung zu reduzieren. Die PLL wird über das PLLCSR-Register gesteuert und konfiguriert. Bei einem AT90USB1287 muss ein externes 8 MHz Quarz verwendet werden um den USB-Takt zu erzeugen. Weiterhin muss sowohl das PLLP1-, als auch das PLLP0-Bit im PLLCSR-Register gesetzt werden.

Die Konfiguration der PLL erfolgt in der ISR des USB-Controllers, sobald ein VBUS-Interrupt ausgelöst worden ist. 

ISR(USB_GEN_vect)
{
	if(USBINT & (0x01 << VBUSTI))
	{
		USBINT &= ~(0x01 << VBUSTI);

		if(USBSTA & (0x01 << VBUS))
		{
			PLLCSR = (((0x01 << PLLP1) | (0x01 << PLLP0)) | (0x01 << PLLE));
	 		while(!(PLLCSR & (0x01 << PLOCK)));
			_USBDeviceState = USB_STATE_POWERED;
		}
		else
		{
			PLLCSR = 0x00;
			_USBDeviceState = USB_STATE_UNATTACHED;
		}
	}
}

Anschließend wird das Interruptflag gelöscht und geprüft ob ein Gerät angeschlossen ist, indem das VBUS-Bit im USBSTA-Register abgefragt wird.

Erst wenn alle Bedingungen zutreffen wird die PLL konfiguriert. Dazu wird der Vorteiler über das PLLP1– und das PLLP0-Bit eingestellt und die PLL über das PLLE-Bit aktiviert. Durch die Abfrage des PLOCK-Bits wird gewartet bis die PLL eingeschwungen ist.

PLLCSR = (((0x01 << PLLP1) | (0x01 << PLLP0)) | (0x01 << PLLE));
while(!(PLLCSR & (0x01 << PLOCK)));

Als letztes wird der interne Zustand des USB-Treibers aktualisiert. 

_USBDeviceState = USB_STATE_POWERED;

Das Programm kann jetzt gestartet und der Mikrocontroller mit einem Host verbunden werden. Beim Einstecken wird allerdings noch nicht so viel passieren, da der Host bisher lediglich erkennt, dass ein Gerät da ist, aber nicht um was für einen Gerätetyp es sich handelt. Daher meldet der Host einen Fehler beim Anfordern einer Gerätebeschreibung.

 

Dieses Problem soll dann in den nächsten Teilen des Tutorials gelöst werden. Doch bevor dieses Thema angegangen werden kann, muss der Kontrollendpunkt des USB-Gerätes initialisiert werden. Dies soll Thema des nächsten Teils des USB-Tutorials sein.

Zurück

5 Kommentare

  1. Hallo,
    herzlichen Dank für die diesen tollen Artikel. Ich habe schon seit ewigen Zeiten das
    USB_key-Board bei mir rumliegen und will es jetzt für mein Orgel-Projekt benutzen:
    Ich möchte eine Tastatur scannen und dann die Midi-Noten über USB an den PC
    senden. Ich erhoffe mir eine deutlich geringere Verzögerung beim Spielen.
    Zur Zeit sendet die Tastatur Mididaten an ein Midi-USB-Gerät.
    Eine kleine Frage hätte ich noch: Warum muß in der ISR(USB_GEN_vect) VBUSTE abgefragt
    werden? Es wurde ja im Setup bereits gesetzt.
    Nochmal vielen Dank und herzliche Grüße aus Clausthal

    Hans-Georg Braun

    1. Hallo Hans-Georg,

      das VBUSTE-Bit dient zur Aktivierung des Interrupts für die Plug-In Erkennung. Im Interrupt checke ich lediglich ob dieser Interrupt auch aktiviert worden ist, da der Vektor USB_GEN ein gemeinsamer Vektor für viele Interruptquellen ist und es soll ja nicht der Code für den VBUS-Interrupt ausgeführt werden, wenn dieser nicht aktiviert ist. Daher wird der Interrupt erst mit

      USBCON |= (0x01 << VBUSTE);

      aktiviert und über

      (USBCON & (0x01 << VBUSTE));

      wird dann gecheckt, ob der Interrupt auch aktiviert ist.

      Gruß
      Daniel

      1. Hallo Daniel,
        vielen Dank für Deine Antwort.
        Worauf ich hinaus will ist folgendes: das VBUS_Transmission_Interrupt_Flag
        kann ja nur gesetzt werden wenn VBUSTE gesetzt ist (Fig. 22-11 im DB). Sonst wird dieser Interrupt blockiert.
        Wenn ich also VBUSTI abfrage und es gesetzt ist, muss VBUSTE gesetzt
        worden sein. Oder habe ich da was falsch verstanden?
        Gruß
        Hans-Georg

        1. Hallo Hans-Georg,

          ich glaube du hast recht. Anscheinend habe ich das Bild falsch verstanden :)
          Danke für den Hinweis. Ich ändere das nachher mal und update das Tutorial.

          Gruß
          Daniel

  2. Hallo Daniel,
    vielen Dank für die hilfreichen Erklärungen zu USB.
    Ich habe ein USBKEY2- Board und verzweifle gerade an Power Down und Idle- Modes. Ich habe mich über die mitgelieferte Firmware und die dortige usb_config.h bis zu Suspend und Wakeup- Aktionen im ISR USB_GEN_vect vorgekämpft, aber jetzt in ich mit meinem (sehr begrenzten) Latein am Ende. Nicht einmal wenn ich in dieser ISR nur Idle- Mode einstelle, schließt die Enumerierung nach dem Programmier- Reset ab.
    Meine Frage ist im Endeffekt: Kann man im Device- Mode ohne die USB- Verbindung zum Host zu trennen (Detach/Attach), das Device in den Idle oder besser noch in den Power down- Modus versetzen? Wenn ja, wie geht das?
    Beste Grüße,
    Hubert

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert