• Keine Ergebnisse gefunden

Einsatz eines USB Stack

N/A
N/A
Protected

Academic year: 2021

Aktie "Einsatz eines USB Stack"

Copied!
9
0
0

Wird geladen.... (Jetzt Volltext ansehen)

Volltext

(1)

Einsatz eines USB Stack

1 Aufgabe und Stellung des USB Stacks ... 1

2 Schnittstellen des Stacks ... 2

2.1 Konfiguration ... 2

2.2 Initialisierung ... 3

2.3 USB-Deskriptoren ... 5

2.4 Device-Callback ... 6

2.5 Start des Stack ... 7

2.5.1 Takterzeugung ... 7

2.5.2 Modulaktivierung ... 8

2.5.3 Speicherzugriff ... 8

2.5.4 ISR ... 8

2.5.5 Reihenfolge ... 9

2.6 Attach/Detach ... 9

1 Aufgabe und Stellung des USB Stacks

Wenn man in einem µC Daten über den USB austauschen möchte, dann kommt der sogenannte USB Stack zum Einsatz. Damit bezeichnet man die unteren Schichten eines hierarchischen Kommunikationsmodells wie beispielsweise des OSI-Modells (Stack: Stapel, hier Protokoll- stapel der unteren 3-4 Schichten). Ein solcher Stack kann zwar im Inneren recht komplex sein, aber die Verwendung gestaltet sich oft überraschend einfach, wenn man das Prinzip verstanden hat.

Zur Stellung des Stacks soll einmal die Datenübertragung über eine Hardware-UART und einmal über eine „Virtuelle UART“ (das entspricht bei USB einer CDC/ACM-Funktion) betrachtet werden.

Abbildung 1 zeigt die typische Bedienung einer UART über eine ISR. In Empfangsrichtung (RX) setzt das UART-Modul (die Hardware) zunächst die eintreffenden Bits zusammen, bis ein Datenwort fehlerfrei empfangen wurde. Dieses Datenwort wird in einem Peripherieregister (hier Receive) gespeichert und zudem wird ein Flag gesetzt.

Abbildung 1: Daten- und Kontrollfluss UART

Das Flag löst dann eine ISR aus, die der UART fest zugeordnet ist. Die ISR seht nach, welches

Ereignis eingetreten ist (es könnte ja auch ein Ereignis beim Senden gewesen sein) und

überträgt dann das Datenwort vom Peripherieregister in den Speicher (hier Data In), aus dem

ihn die Anwendung abholen kann.

(2)

Verwendet man anstelle der UART den USB, dann ist der Ablauf aus Sicht des Anwenderprogramms genau gleich (Abbildung 2). Auch hier kommen die Daten per Interrupt im Speicher Data In an.

Abbildung 2: Daten- und Kontrollfluss USB

Der Unterschied ist nur, dass jetzt zwischen der Hardware (USB Module) und der Anwendung noch der USB Stack als unsichtbarer Vermittler sitzt. Zunächst nimmt das USB-Modul die Daten beim Empfang entgegen und legt sie in einem Speicher ab, der einem Endpunkt (hier EP OUT) zugeordnet ist. Sobald ein Datenpaket fehlerfrei empfangen wurde, setzt die Hardware ein Flag. Dieses Flag löst eine ISR aus, die dem USB Modul zugeordnet ist. Die ISR ist aber jetzt Teil des USB Stacks. Der Stack ruft dann seinerseits eine Callback-Funktion auf, die vom Anwender zuvor beim Stack für Ereignisse an dem Endpunkten EP OUT und EP IN registriert wurde. Diese Callback-Funktion hat also genau die Aufgabe, die zuvor die ISR im Fall der UART hatte: Daten von den Endpunkten vom/zum Speicher der Anwendung zu kopieren.

Denkt man sich in Abbildung 2 die ISR im USB Stack weg, dann sehen sowohl Daten- wie auch Kontrollfluss genauso wie in Abbildung 1 aus.

2 Schnittstellen des Stacks 2.1 Konfiguration

Der Stack stellt Schnittstellen zur Hardware des µC sowie zur Anwendung bereit. Als Beispiel sollen in einer Anwendung über USB ein Massenspeicher (MSC), zwei serielle Schnittstellen (CDC/ACM) sowie einige Tasten (HID) angebunden werden.. Im µC steht dazu ein EHCI- kompatibles Modul (Device, High-Speed) als Hardware zur Verfügung. Der Stack muss also die passenden Schnittstellen in jeweils genügender Zahl enthalten (Abbildung 3).

Abbildung 3: Konfiguration des Stack

Dafür muss es jeweils die zugehörigen Programmteile im Stack geben. Damit diese

Programmteile mitkompiliert werden, muss dem Compiler mitgeteilt werden, welche Teile

benötigt werden. Dazu wird mit bedingter Kompilierung gearbeitet. Die für eine bestimmte

(3)

Anwendung gültigen Einstellungen sind dabei zentral in einer Datei (usb_device_config.h) zusammengefasst.

#define USB_DEVICE_CONFIG_EHCI 1

#define USB_DEVICE_CONFIG_HID 1

#define USB_DEVICE_CONFIG_CDC_ACM 2

#define USB_DEVICE_CONFIG_MSC 1

#define USB_DEVICE_CONFIG_ENDPOINTS (1+2+2+1+1)

Listing 1: Auszug aus der Konfigurationsdatei usb_device_config.h

Listing 1 zeigt einen Auszug aus dieser Datei für dieses Beispiel. Bei der Anzahl der Endpunkte wird bei diesem Stack das Maximum der IN- und OUT-Endpunkte genommen. Es gibt hier 6 IN-Endpunkte und 3 OUT-Endpunkte in allen verwendeten Klassen zusammen.. Dazu kommt noch der Endpunkt 0, der ja immer erforderlich ist. Die maximale Anzahl der benötigten Endpunkte ist also hier 7.

2.2 Initialisierung

Bevor der Stack seine Arbeit aufnehmen kann, muss er einmalig mit den benötigten Informationen versorgt werden. Das betrifft jedes der in Abbildung 3 gezeigten gelb unterlegten Teil. Der Anwender liefert die Informationen in einem vom Stack vorgegebenen Format und erhält dafür vom Stack für jede einzelne Funktion (z.B. die HID-Funktion) ein Handle (classhandle) zurück. Mit diesem Handle kann dann später die Anwendung gezielt eine Funktion auswählen. Zudem bekommt der Anwender ein Handle (devicehandle) zurück, das für alle späteren Ereignisse und Aktionen benutzt wird, die das gesamte Device betreffen.

Abbildung 4: Initialisierung des Stack

Es gibt nur eine einzige Initialisierungsfunktion die auch nur einmal aufgerufen wird.

Abbildung 4 zeigt die benötigten Informationen (blau unterlegt) und das Ergebnis (rot unterlegt). Für eine Funktion (F1) sind die Informationen auch im Detail dargestellt. Man sieht, dass hier zunächst die Klasse, die enthaltenen Interfaces, und die Eigenschaften der verwendeten Endpunkte aufgeführt sind. Exakt dieselbe Information muss später bei der Enumeration auch dem Host mitgeteilt werden. Hier wird sie aber nicht für die Enumeration benötigt, sondern für die technische Einstellung des Hardwaremoduls. Aus diesem Grund sind auch nicht alle Informationen, die der Host bekommt, hier enthalten. Beispielsweise fehlt das Abfrageintervall beim Typ Interrupt. Das ist nur für den Host wichtig, aber nicht für das Device.

Dafür wird hier pro USB-Funktion auch je eine Callback-Funktion (die dann in der Anwendung

zu finden ist) im Stack als Funktionszeiger registriert. Trifft dann später ein Ereignis an der

HID-Funktion auf (z.B. Empfang eines FEATURE-Report), dann ruft der Stack die Callback-

Funktion zu F1 auf.

(4)

Nach einer erfolgreichen Initialisierung hat die Init-Funktion alle classhandles und das devicehandle mit gültigen Werten beschrieben. Ab diesem Zeitpunkt kann die Anwendung mittels eines classhandles gezielt mit einer USB-Funktion im Stack Daten austauschen (gestrichelte Linien). Auch für das gesamte Device wird hier genau eine Callback-Funktion registriert.

Es bleibt der Anwendung überlassen, wie die Information zusammengetragen wird. Das kann zur Laufzeit geschehen (so machen es die Beispiele aus dem SDK), man kann das aber auch zur Compile Time machen. Vorgeschrieben ist nur das Format, in dem die Information dann dem Stack übergeben wird. Listing 2: Beispielcode für die Informationen zu Funktion 1Listing 2 zeigt eine statische Initialisierung einer passend definierten Struktur am Beispiel der Funktion 1. Dabei wird ausgiebig von Symboldefinitionen Gebrauch gemacht, da dieselbe Information noch einmal bei den USB Deskriptoren benötigt wird. Manche Symbole müssen vom Anwender definiert werden (z.B. Anzahl der Interfaces in der Funktion (USB_FUNC1_IF_CNT), viele aber beziehen sich auf USB Standards und sind schon vom Hersteller in Headern vorbereitet (HID_PROTOCOL_KEYBOARD).

// Anwendungsspezifische Definitionen für die Funktion 1

#define USB_FUNC1_IF_CNT 1

#define USB_FUNC1_IF 0

#define USB_FUNC1_EPIN 1

#define USB_FUNC1_EPIN_SIZE 5

// Vom Stack vorgegebener Aufbau der Informationen für eine Funktion

typedef struct

{

usb_device_class_struct_t classinformation;

usb_device_interface_list_t interfacelist;

usb_device_interfaces_struct_t interfaces[USB_FUNC1_IF_CNT];

usb_device_interface_struct_t interface[USB_FUNC1_IF_CNT];

usb_device_endpoint_struct_t endpoint_intin;

} USB_STACK_HID_CONFIG_T;

// Statisches Auffüllen der Struktur mit den tatsächlichen Informationen USB_STACK_HID_CONFIG_T usb_app_func1_config =

{

{ &usb_app_func1_config.interfacelist, kUSB_DeviceClassTypeHid, 1}, { USB_FUNC1_IF_CNT, usb_app_func1_config.interfaces},

{

{ HID_CLASS_GENERIC, HID_SUBCLASS_NONE, HID_PROTOCOL_KEYBOARD,

USB_FUNC1_IF, &usb_app_func1_config.interface[0], USB_FUNC1_IF_CNT}, },

{

{0, {1, &usb_app_func1_config.endpoint_intin}, NULL}, },

{ EP_INDIR(USB_FUNC1_EPIN) , USB_ENDPOINT_INTERRUPT, USB_FUNC1_EPIN_SIZE}

};

Listing 2: Beispielcode für die Informationen zu Funktion 1

Ist alles an Informationen vorbereitet, dann kann mit USB_DeviceClassInit() der Stack initiali- siert werden (Listing 3).

#define CONTROLLER_ID kUSB_ControllerEhci0

#define USB_APP_FUNCTIONS 4

usb_device_class_config_struct_t usb_function_information[USB_APP_FUNCTIONS];

usb_device_class_config_list_struct_t usb_config_list =

{

(5)

usb_function_information, USB_DeviceCallback, USB_APP_FUNCTIONS };

usb_device_handle devhandle;

USB_DeviceClassInit(CONTROLLER_ID, &usb_config_list, &devhandle));

Listing 3: Aufruf der Initialisierungsfunktion

Das Symbol CONTROLLER_ID ist dabei in der schon erwähnten Konfigurationsdatei usb_device_config.h enthalten. Das Symbol USB_APP_FUNCTIONS gibt die Anzahl der enthaltenen Funktionen an (im Beispiel 4). Der Parameter usb_config_list enthält in einem Feld die Informationen für diese vier Funktionen. In einem dieser Feldeinträge ist dann auch die in Listing 2 initialisierte Struktur usb_app_func1_config enthalten. Hier werden als Parameter Adressen übergeben, weil die Funktion die classhandles F1-F4 und das devicehandle (Abbildung 4) dort einträgt.

2.3 USB-Deskriptoren

Für eine erfolgreiche Enumeration muss der Stack dem Host die jeweils angeforderten De- skriptoren liefern können. Der Anwender muss also diese Deskriptoren nicht nur zusammen- stellen, sondern dem Stack auch mitteilen, wo sie jeweils zu finden sind. Das Format der Deskriptoren ist dabei von den jeweiligen USB-Standards (Core oder Class) festgelegt.

uint8_t usb_device_descriptor[] = {

USB_DEVICE_DESC_SIZE, USB_DEVICE_DESCRIPTOR_TYPE, WBVAL(USB_APP_USB_BCD_VERSION), 0xEF,

0x02, 0x01,

USB_MAX_PACKET0,

WBVAL(USB_APP_VENDORID), WBVAL(USB_APP_PRODUCTID),

WBVAL(USB_APP_DEVICE_BCD_VERSION), USB_APP_STR_MANUFACTURER,

USB_APP_STR_PRODUCT, USB_APP_STR_SERIALNUMBER, USB_APP_CONFIGURATION_COUNT };

typedef struct

{

uint8_t *buffer;

uint32_t length;

} usb_device_get_device_descriptor_struct_t;

extern usb_status_t USB_DeviceGetDeviceDescriptor

( usb_device_handle handle,

usb_device_get_device_descriptor_struct_t *deviceDescriptor );

usb_status_t USB_DeviceGetDeviceDescriptor ( usb_device_handle handle,

usb_device_get_device_descriptor_struct_t *deviceDescriptor) {

deviceDescriptor->buffer = usb_device_descriptor;

deviceDescriptor->length = USB_DESCRIPTOR_LENGTH_DEVICE;

return kStatus_USB_Success;

}

Abbildung 5: Deskriptor-Schnittstelle

(6)

Abbildung 5 zeigt links oben die beteiligten Teile. Blau unterlegt sind Daten, gelb Funktionen.

Der Stack muss während der Enumeration die Anfragen des Host beantworten. Dazu ruft er die Get/Set-Funktionen auf, die von der Anwendung bereitgestellt werden müssen. Die Funktions- deklarationen sind vom Stack vorgegeben, die Funktionsdefinition liegt dagegen in der Anwendung. Als Beispiel wird hier der Device Deskriptor benutzt. Zunächst hat der Anwender einen solchen Deskriptor nach dem Core-Standard zusammengestellt (rechts oben). Nahezu alle Einträge bestehen hier aus Symbolen, die natürlich auch definiert sein müssen. Der Vorteil bei der Verwendung von Symbolen ist, dass man sie bei Bedarf auch an anderer Stelle verwenden kann. Tatsächlich werden hier nahezu alle Symbole aus der Initialisierung wiederverwendet.

Um diesen Deskriptor zu bekommen, ruft der Stack dann zur Laufzeit die Funktion DeviceGetDeviceDescriptor() auf. Wie man sieht, bekommt die Funktion zwei Parameter mit:

Der Handle bestimmt, für welches Device der Deskriptor abgefragt wird. Im Beispiel gibt es nur ein Device, so dass dieser Parameter für alle Funktionen, die das Device betreffen, gleich sein wird. Er wird daher in dem Beispiel erst gar nicht abgefragt. Der zweite Parameter ist für den Rückgabewert bestimmt. Der Typ ist ebenfalls im Stack selbst definiert (siehe typedef).

Die Anwendung definiert nun die Funktion. Hier werden in die Struktur wie gefordert die Adresse des Deskriptors und seine Länge eingetragen. Dann meldet die Funktion die erfolgreiche Bearbeitung. Der Stack kann dann seinerseits dem Host den Device Descriptor zusenden.

Während der Enumeration wird der Host nur Fragen stellen (GET). Falls das Device aber HS- fähig ist, dann können sich die Deskriptoren für Highspeed leicht von denen für Fullspeed unterscheiden. Das betrifft die Größe der Endpunkte und die Berechnung der Abfragerate.

Damit je nach Anschluss an einen Host (FS oder HS) die richtigen Werte übergegeben werden, ruft der Stack vor der ersten Abfrage die Funktion USB_DeviceSetSpeed() auf. Damit wird der Anwendung die Anschlussgeschwindigkeit mitgeteilt. Die hier gewählte Methode ist, dann die betroffenen Einträge in den Deskriptoren anzupassen. Dafür müssen die Deskriptoren im RAM stehen. Man könnte auch zwei getrennte Sätze von Deskriptoren, einen für FS und einen für HS vorhalten. Dann könnten alle Deskriptoren im Flash stehen.

2.4 Device-Callback

Für jedes Device wird bei der Initialisierung genau eine Callback-Funktion registriert. Diese Funktion muss die Anwendung bereitstellen. Der Name ist beliebig, denn dem Stack wird ja nur ein Funktionszeiger übergeben. Im Beispiel ist das die Funktion USB_DeviceCallback (Listing 3, zweiter Parameter der Initialisierungsfunktion).

Wie Abbildung 6 zeigt, wird die diese Funktion bei Ereignissen aufgerufen, die vom USB- Stack dem gesamtem Device zugeordnet werden.

Abbildung 6: Aufruf der Device Callback-Funktion

(7)

Listing 4 zeigt die Funktionsdeklaration sowie einige ausgewählte Ereignisse. Der Stack liefert bei jedem Aufruf der Funktion das auslösende Ereignis als Parameter event

1

mit. Die Bedeutung des dritten Parameters hängt vom Ereignis ab.

usb_status_t USB_DeviceCallback(usb_device_handle handle, uint32_t event, void *param);

typedef enum _usb_device_event

{

kUSB_DeviceEventBusReset // USB bus reset signal detected kUSB_DeviceEventSuspend, // USB bus suspend signal detected

kUSB_DeviceEventDetach, // USB device is disconnected from a host.

kUSB_DeviceEventAttach, // USB device is connected to a host.

kUSB_DeviceEventSetConfiguration, // Set configuration.

….

Listing 4: Auszug aus usb_device.h

Man braucht nicht alle Ereignisse zu behandeln, aber einige sind für die Anwendung doch wichtig. Bei einem BusReset wird man die Einstellungen zu allen USB-Funktionen (im Bereich der Anwendung) zurücksetzen. Bei einem SetConfiguration kann man die Stromaufnahme bis zu dem Wert, den man in der zugehörigen Konfiguration selbst im USB Configuration Descriptor angegeben hat, erhöhen. Damit könnte der µC jetzt zum Beispiel mit höherem Takt laufen oder man interne bzw. externe Komponenten aktivieren.

Für manche Ereignisse (Beispiel Detach) kann bei der Konfiguration bestimmt werden, ob der Stack zur Laufzeit beim Ereigniseintritt die Callback-Funktion überhaupt aufruft. In der Voreinstellung würde das Abstecken des Kabels zum Host (Ereignis Detach) gar nicht zum Aufruf der Funktion führen.

2.5 Start des Stack

Nach der erfolgreichen Initialisierung ist der Stack soweit vorbereitet, dass er auf Ereignisse am USB reagieren. Der Stack hat dabei das in der Konfiguration gewählte USB Modul passend zur Anwendung eingestellt. Es aber auch erforderlich, dass die Hardware außerhalb des Stack soweit eingestellt ist, dass das USB Modul arbeitsfähig ist. Üblicherweise betrifft das die Erzeugung eines oder mehrerer Takte (Bustakt, Modultakt), die Modulaktivierung, die Zuweisung bestimmter Signale des Moduls an Pins des µC (z.B. das ID-Signal bei USB OTG) oder auch die Erlaubnis, vom Modul aus auf bestimmte Speicherbereiche zugreifen zu dürfen (DMA, Memory Protection Unit).

Die benötigten Einstellungen sind bei modernen µC nicht leicht zu finden. Daher empfiehlt es sich, eine funktionsfähige Initialisierung einfach zu übernehmen oder sich, sofern das angeboten wird, sich eine funktionsfähige Initialisierung von einem Herstellerwerkzeug erzeugen zu lassen.

2.5.1 Takterzeugung

Die benötigten Takte werden allgemein in einem dafür vorgesehenen Modul im µC über PLLs (Phase Locked Loop) erzeugt. Bei diesem µC kann das Werkzeug MCUXpresso Config Tool passenden Code automatisch erzeugen. Dieser Code wird dann von der Anwendung aufgerufen.

Alternativ sucht man sich aus einem Beispiel die dort verwendeten Funktionen heraus und verwendet sie in einer eigenen Initialisierung (Listing 5).

CLOCK_EnableUsbhs0PhyPllClock(kCLOCK_UsbPhySrcExt, BOARD_XTAL0_CLK_HZ);

Listing 5: Takterzeugung USB

1 Als Enumeration definiert, aber formal als uint32_t in der Funktionsdeklaration angegeben – siehe Listing.

(8)

Das Symbol BOARD_XTAL0_CLK_HZ definiert die Taktfrequenz des Quarzoszillators.

2.5.2 Modulaktivierung

Da das Config Tool (derzeit noch) nicht in der Lage ist, das Modul zu aktivieren, muss man hier in jedem Fall auf ein funktionierendes Beispiel zurückgreifen (Listing 6).

usb_phy_config_struct_t phyConfig = {

BOARD_USB_PHY_D_CAL, BOARD_USB_PHY_TXCAL45DP, BOARD_USB_PHY_TXCAL45DM, };

CLOCK_EnableUsbhs0Clock(kCLOCK_UsbSrcUnused, 0U);

USB_EhciPhyInit(CONTROLLER_ID, BOARD_XTAL0_CLK_HZ, &phyConfig);

Listing 6: Aktivierung der Module, USB HS und Sender/Empfänger (Phy)

Man sieht am Namen, dass die letzte Funktion Teil des USB Stack ist. Die hätte der Stack also prinzipiell auch selber bei der Initialisierung aufrufen können, wenn man die benötigten Parameter z.B. in der Konfigurationsdatei zur Verfügung stellt.

2.5.3 Speicherzugriff

USB Module bringen meist eine eigene DMA

2

-Einheit mit, die bei Bedarf auf den Speicher zugreift. Es kann sein, dass bestimmte Speicherbereiche in einem µC für diese DMA gar nicht erreichbar sind oder dass Zugriffsrechte vorhanden sein müssen. Hat ein µC eine sogenannte MPU (Memory Protection Unit), dann kann es sein, dass die DMA zwar auf einen Speicher- bereich zugreifen darf, auf einen anderen aber nicht. Liegen dann Informationen (z.B. die USB Deskriptoren), die später von der DMA gelesen werden, in einem geschützten Speicherbereich (z.B. Flash), dann werden entweder falsche Daten übertragen oder die Anwendung bricht mit einem Zugriffsfehler ab („Absturz“).

Auch hier orientiert sich man bei Bedarf an einem funktionsfähigen Beispiel (Listing 7).

// The system MPU must be disabled if the USB DMA shall access the flash memory SYSMPU_Enable(SYSMPU, 0);

// The USB DMA in the EHCI-Module is "master 5" for the flash controller.

// This master must have read access. See 32.5.1 in the Reference Manual.

FMC->PFAPR |= (0x01) << 2*5;

Listing 7: Beispiel für eine Zugriffsfreigabe

Glücklicherweise sind die Voreinstellungen nach einem Reset des µC in aller Regel so gewählt, dass die meisten Anwendungsfälle ohne Änderungen an der MPU funktionieren. Auch hier tritt das Problem nur auf, wenn Daten aus dem Flash per DMA übertragen werden sollen.

2.5.4 ISR

Dem USB Modul ist im µC mindestens eine ISR zugeordnet. Diese ISR muss vom Anwender bereitgestellt werden. Sofern das Ereignis nicht vom Anwender selbst behandelt wird (in der Praxis kommt das kaum vor), wird hier einfach die im USB Stack vorhandene ISR aufgerufen (Listing 8).

void USBHS_IRQHandler(void)

{

// Aufruf der ISR des USB Stacks

USB_DeviceEhciIsrFunction(deviceHandle);

2 Direct Memory Access

(9)

}

Listing 8: Typische ISR des USB Module

Da der USB Standard recht unkritische Zeitvorgaben macht, kann man für die ISR eine niedrige Priorität wählen.

2.5.5 Reihenfolge

Vor dem Aufruf der ersten Funktion im USB Stack sollte man die Takte erzeugen, das Modul aktivieren, Speicherzugriffe der DMA (soweit überhaupt erforderlich) zulassen und noch keine Interrupts aus dem USB Modul zulassen. Danach initialisiert man den Stack. Erst nach der erfolgreichen Initialisierung lässt man Interrupts aus dem Modul zu.

2.6 Attach/Detach

Sind alle äußeren und inneren Vorbereitung abgeschlossen, dann kann man dem Stack den Auftrag geben, sich am USB anzumelden (Listing 9).

USB_DeviceRun(deviceHandle);

Listing 9: Attach am USB

Der Stack beauftragt seinerseits das USB Modul mit einem Attach und kehrt sofort zum Aufrufer zurück. Das funktioniert auch ohne eine tatsächliche Verbindung zum Host. Erst wenn ein Host auf den Attach reagiert (Bus Reset) wird der Stack wieder aufgerufen (ISR) und daraufhin die Device Callback-Funktion der Anwendung. Man braucht also gar nicht zu warten, bis ein Kabel angesteckt wird.

Möchte man die Verbindung zum Host aktiv trennen, dann lässt man den Stack einen Detach

ausführen. Auch hier wird man vom Erfolg benachrichtigt und kann dann beispielsweise das

USB Modul und die zugehörigen Takte zur Energieersparnis abstellen.

Abbildung

Abbildung 2: Daten- und Kontrollfluss USB
Abbildung 4: Initialisierung des Stack
Abbildung 5 zeigt links oben die beteiligten Teile. Blau unterlegt sind Daten, gelb Funktionen

Referenzen

ÄHNLICHE DOKUMENTE

Doppelt verkettete Listen bestehen aus Listenzellen mit zwei Zeigern. • Ein Zeiger prev auf die

verschachtelten Prozeduren m¨ ussen viele Zugriffslinks verfolgt werden. Abhilfe: Displays

• Maintaining connectivity to legacy IPv4 devices/networks during the transition to IPv6-only deployments..

 Problem: im voraus nicht bekannt, wie groß das Array sein soll.  Also: zunächst ganz kleines Array erzeugen,

Idee und Anregung: Anton Weininger, Landshut 1 Worum geht es.. Diskretisierung des rgb-Farbwürfels 2 Der

Die Zusammensetzung zweier Schubspiege- lungen mit nicht parallelen Schubspiegelachsen ist also nach wie vor eine Drehung.. Der Drehwinkel ist nach wie vor das Doppelte des Winkels

Stacks intelligently show the most relevant items first or you can set the sort order to make sure the items you care about most always appear at the top of the stack.. To

Because Telcon configuration statements apply to a wide range of communications purposes and configurations, this section describes only how to configure the following