Forum: Mikrocontroller und Digitale Elektronik Atmel SAM - USB iso Endpoint Funktionsweise


von Markus G. (usound)


Lesenswert?

Hallo zusammen,

ich befasse mich gerade näher mit der USB-Implementierung auf einem 
Atmel SAM D21 uC. Die Lib aus dem ASF funktioniert problemlos. Mir fehlt 
es nur etwas am Verständnis der Funktionsweise. Konkret geht es um einen 
isochronen Transfer.
Im Beispiel wird ein simpler Loopback in Verbindung mit einer 
Testsoftware auf dem PC implementiert. Hier mal ein Code-Ausschnitt der 
hauptsächlich wichtigen Funktionen:
1
bool udi_vendor_iso_out_run(uint8_t * buf, iram_size_t buf_size,
2
    udd_callback_trans_t callback)
3
{
4
  return udd_ep_run(UDI_VENDOR_EP_ISO_OUT,
5
      false,
6
      buf,
7
      buf_size,
8
      callback);
9
}
10
11
void main_vendor_iso_out_received(udd_ep_status_t status,
12
    iram_size_t nb_transfered, udd_ep_id_t ep)
13
{
14
  uint8_t *buf_ptr;
15
  UNUSED(ep);
16
17
  if (UDD_EP_TRANSFER_OK != status) {
18
    return; // Transfer aborted, then stop loopback
19
  }
20
21
  if (nb_transfered) {
22
    ui_loop_back_state(true);
23
    // Send on IN endpoint the data received on endpoint OUT
24
    buf_ptr = &main_buf_loopback[ main_buf_iso_sel
25
        *(sizeof(main_buf_loopback)/2) ];
26
    udi_vendor_iso_in_run(
27
        buf_ptr,
28
        nb_transfered,
29
        main_vendor_iso_in_received);
30
  }
31
32
  // Switch of buffer
33
  main_buf_iso_sel = main_buf_iso_sel? 0:1;
34
35
  buf_ptr = &main_buf_loopback[ main_buf_iso_sel
36
      *(sizeof(main_buf_loopback)/2) ];
37
38
  // Send on IN endpoint the data received on endpoint OUT
39
  udi_vendor_iso_out_run(
40
      buf_ptr,
41
      udd_is_high_speed()?
42
      UDI_VENDOR_EPS_SIZE_ISO_FS,
43
      main_vendor_iso_out_received);
44
}

Es wird also zunächst die Funktion aufgerufen, um den Endpoint zu 
bedienen (Daten empfangen)
--> "udd_ep_run".
Wenn die Daten empfangen wurden, wird eine Callback-Funktion aufgerufen
--> "main_vendor_iso_out_received"
In der Callback-Funktion wird wieder die ursprüngliche Funktion 
udd_ep_run aufgerufen.
Da das nun ja beliebig lange so weiterlaufen würde und sich im Grunde 
unendlich rekursiv selbst aufruft, müsste doch irgendwann der Stack 
überlaufen, wenn nicht irgendwann wieder zurückgesprungen wird?

Ich habe diverse Tests durchgeführt. Das scheint so aber zu laufen.

Rein von der Logik hatte ich eigentlich folgenden Ansatz:
- nach erfolgreicher Beendigung der USB-Enumeration bekomme ich bei 
jedem erkannten Start-of-Frame (SOF) einen Interrupt
- theoretisch müsste ich nun einfach hergehen und in jedem SOF-Interrupt 
1x udd_ep_run aufrufen, um die Daten im zugehörigen Frame zu 
transferieren.

Somit wäre die Funktion sauber terminiert bzw es wird wieder 
zurückgesprungen.
Ich habe nun diverse Tests gemacht. Es scheint, dass dabei Daten 
verloren gehen.
In der Programmierung rund um die USB-Schnittstelle bin ich noch recht 
neu, deshalb die Frage, ob mir da jemand etwas auf die Sprünge helfen 
kann.

Beste Grüße,
Markus

: Bearbeitet durch User
von Jim M. (turboj)


Lesenswert?

Markus G. schrieb:
> Da das nun ja beliebig lange so weiterlaufen würde und sich im Grunde
> unendlich rekursiv selbst aufruft, müsste doch irgendwann der Stack
> überlaufen, wenn nicht irgendwann wieder zurückgesprungen wird?

Es wird zurück gesprungen - der callback wird nämlich erst vom USB 
Interrupt Handler aufgerufen.

Lass Dir mal im Callback den Stack Trace komplett bis zum Usrsprung 
anzeigen - das sollte im USB Interrupt landen.

von Markus G. (usound)


Lesenswert?

Jim M. schrieb:
> Es wird zurück gesprungen - der callback wird nämlich erst vom USB
> Interrupt Handler aufgerufen.
>
> Lass Dir mal im Callback den Stack Trace komplett bis zum Usrsprung
> anzeigen - das sollte im USB Interrupt landen.

Danke für den Hinweis!
Habe den Call-Stack geprüft (auf die Idee bin ich warum auch immer noch 
nicht selbst gekommen) und das von dir geschilderte Verhalten 
entsprechend verifizieren können.
Der Callback wird vom USB Interrupt Handler aufgerufen.

Das heißt, im Grunde funktioniert das Ganze so:
udd_ep_run wird aufgerufen, das sagt dem USB-Modul eigentlich nur, dass 
zum nächst möglichen Zeitpunkt Daten empfangen werden sollen und die 
zugehörigen Register werden entsprechend gesetzt. Der Callback wird dann 
praktisch "hinterlegt". Die Funktion udd_ep_run wird dann beendet. Ist 
nun die Datenübertragung abgeschlossen, ruft der USB-Handler den 
Callback auf. Dann wird die nächste Übertragung vorbereitet.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.