Forum: Mikrocontroller und Digitale Elektronik STM32 HAL CDC-USB-Treiber "Flow Control"


von Kai K. (l1800turbo)


Lesenswert?

Hallo,

ich stehe beim HAL-Treiber von ST zum STM32F407 gerade ein bisschen auf 
dem Schlauch..

Ich möchte den Controller als Client über USB Daten empfangen lassen, in 
diesem Fall eine Firmware, die der Controller dann über SPI auf ein 
DAB-IC programmiert.
Grundsätzlich funktioniert das schon, jedoch möchte ich sicherstellen, 
dass kein Buffer überläuft und nur neue USB-Daten nachkommen, wenn auch 
Platz dafür ist.

In unzähligen Tutorials wurde immer auf die Funktion "CDC_Receive_FS" 
verwiesen, die so lange NAK Pakete an den Host sendet, bis man sie 
verlässt.
Ich hab das mal ganz aggressiv mit einer While-Schleife an einem Taster 
versucht:
1
static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
2
{
3
  /* USER CODE BEGIN 6 */
4
5
  // Blocken, wenn Knopf gedrückt ist -> USB-NAK-Pakete senden
6
  while(HAL_GPIO_ReadPin(B1_GPIO_Port, B1_Pin) == 1);
7
8
  USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
9
  USBD_CDC_ReceivePacket(&hUsbDeviceFS);
10
  return (USBD_OK);
11
  /* USER CODE END 6 */
12
}

Der Host wartet dann geduldig, wie gewünscht.

Das Problem ist jetzt aber, dass die CDC_Receive_FS ja eine 
Callback-Funktion aus einem Interrupt ist, sprich, alles andere im 
Controller geht auch nicht weiter. Er könnte jetzt also auch nicht die 
Zeit nutzen, um den Buffer zu verarbeiten.

Müsste man nicht irgendwo vorher schon prüfen, ob der Buffer weniger als 
ein maximales USB-Paket rein bekommt und dann einen Schalter umlegen, 
der immer mit NAK antworten lässt, bis wieder Platz ist?
Oder ist der Ansatz Blödsinn und ich setze falsch an?

Vielen Dank für Ideen/Einwände!

von Harry L. (mysth)


Lesenswert?

Mir scheint, du versuchst ein Problem zu lösen, das so nicht existiert.

Beim RX wirst du kaum einen Überlauf des Buffer provozieren können, so 
lange der Rest deines Programms Ok ist.

Die serielle Übertragung ist in jedem Fall deutlich langsamer als USB 
und du hast genügend Zeit deine Daten abzuholen, wenn du bei der 
Software nicht völlig geschlampt hast.

Die Interupts sind auch kein Problem, da die USB-Interrupts mit sehr 
hoher Priorität laufen und i.d.R. jeden anderen Interrupt unterbrechen 
können.

Beim TX kannst du den Anschluss zwar flutem, aber du erkennst am 
Rückgabe-Wert der Transmit-Funktion, ob ausreichend Platz im Puffer war.

: Bearbeitet durch User
von Programmierer (Gast)


Lesenswert?

Kai K. schrieb:
> USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
>
>   USBD_CDC_ReceivePacket(&hUsbDeviceFS);

Ruf das halt nicht in dieser Funktion auf, sondern später, z.B. in einem 
externen Interrupt wenn der Pin auf 0 gegangen ist.

Kai K. schrieb:
> Müsste man nicht irgendwo vorher schon prüfen, ob der Buffer weniger als
> ein maximales USB-Paket rein bekommt und dann einen Schalter umlegen,
> der immer mit NAK antworten lässt, bis wieder Platz ist?

Der Controller sendet immer NAK-Pakete, bis du eben den Empfang 
aktivierst (in der HAL über ReceivePacket). Mach das halt einfach 
später, wenn du bereit bist, das Paket zu empfangen, und nicht im 
Empfangs-Callback.

von W.S. (Gast)


Lesenswert?

Harry L. schrieb:
> Mir scheint, du versuchst ein Problem zu lösen, das so nicht existiert.

Mir scheint, du referierst gerade über ein Thema, von dem du zu wenig 
verstehst.

Kai K. schrieb:
> Müsste man nicht irgendwo vorher schon prüfen, ob der Buffer weniger als
> ein maximales USB-Paket rein bekommt und dann einen Schalter umlegen,
> der immer mit NAK antworten lässt, bis wieder Platz ist?
> Oder ist der Ansatz Blödsinn und ich setze falsch an?

Nö, nö, deine Gedanken gehen schon in die richtige Richtung. Im Prinzip 
kann man das so lösen, daß man den USB-Puffer erst dann als leer 
(entleert bzw. abgearbeitet) kennzeichnet, wenn man  die Daten auch 
wirklich daraus entnommen hat.

Allerdings tut sich dabei je nach verbautem USB-Core ein kleines Problem 
auf: Wenn der Core nur dann ein Interrupt-Flag setzt, wenn der Puffer 
gefüllt wurde und nun bereit steht, dann kommen keine weiteren 
Interrupts mehr, solange man den Puffer nicht als entleert kennzeichnet. 
Wenn nun aber die ganze USB-Bedienung in einer ISR erfolgt, dann muß man 
sich von woanders drum kümmern, daß man das als entleert setzen 
irgendwann erledigt, sonst stockt ab da der Datenverkehr zum Bulk-Out 
Endpunkt. Dito (bloß umgekehrt) die ganze Puffer-Geschichte beim 
Bulk-In.

Bei Cores, die das Ein- bzw. Ausschalten des NAK_BI bzw. NAK_BO 
ermöglichen, ist das alles kein Problem, aber gerade bei dem Zeugs, was 
(soweit ich mich damit befaßt hab) bei ST verbaut wird, sieht das ein 
bissel kröpeliger aus.

W.S.

von Programmierer (Gast)


Lesenswert?

W.S. schrieb:
> aber gerade bei dem Zeugs, was
> (soweit ich mich damit befaßt hab) bei ST verbaut wird, sieht das ein
> bissel kröpeliger aus.

Das haben wir doch schon durch diskutiert, dass das nur bei deiner 
skurrilen Programmierweise zum Problem wird, und du den NAKI-Interrupt 
völlig zweckentfremdest. Bei sinnvoller Ablaufprogrammierung sieht das 
bei allen USB-Cores gleich aus.

von Programmierer (Gast)


Lesenswert?

... man kann das hier z.B. wunderbar im EXTI-Interrupt machen. Da 
brauchts keinen NAKI-Interrupt für.

von Harry L. (mysth)


Lesenswert?

W.S. schrieb:
> Mir scheint, du referierst gerade über ein Thema, von dem du zu wenig
> verstehst.

Nö, du hast null Ahnung davon, wie die HAL funktioniert.

von Jim M. (turboj)


Lesenswert?

Die USB Hardware im STM32 sollte ein enable Bit für die jeweiligend EP 
haben.

Bei "Puffer voll" schaltet der Interrupt Handler dieses Bit dann ab, 
d.h. der EP erzeugt keine Interrupts mehr wenn Daten ankommen. Die USB 
Hardware sendet dann NAK automagisch.

Auf der Seite wo der Puffer leer gemacht wird muss dann regelmäßig 
dieses EP Enable Bit wieder angeschaltet werden.

HAL kann das AFAIK nicht von sich aus, aber ich habe mich jetzt auch 
nicht durch die Doku gequält.

Vorsicht: Je nachdem wie HAL den USB Interrrupt programmiert hat, könnte 
man im Interrupt Handler trotz deaktiviertem Bit landen, wenn andere EP 
Interrupts getriggert wurden.

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.