Forum: Mikrocontroller und Digitale Elektronik STM32 HAL_SPI_Transmit_IT blockiert


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Marc L. (dl5sez)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Ich frage mich ob es eine gute Idee war mit den HAL-Bibliotheken 
anzufangen ...

Mikrocontroller: STM32F205VCT6

Compiler: arm-none-eabi-gcc, gcc version 7.3.1 20180622 (release) 
[ARM/embedded-7-branch revision 261907] (15:7-2018-q2-4)

Die HAL-Bibliotheken wurden mittels STM32CubeMX, Version 4.27.0 
erstellt.

Aktivierte Peripherie:
SPI3 (Transmit Only Master), SPI3 global interrupt enabled
GPIOA.9 als output

Es soll (nicht blockierend) ein Byte gesendet werden:
1
uint8_t buffer[3] = { 1, 2, 3 };
2
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_9, GPIO_PIN_RESET);
3
HAL_SPI_Transmit_IT(&hspi3, buffer, 1);
4
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_9, GPIO_PIN_SET);

Weiterer Code wurde von mir zu dem von STM32CubeMX erzeugten Code nicht 
hinzugefügt.

Folgendes Problem:
Wird mittels HAL_SPI_Transmit_IT() nur ein Byte übergeben, so blockiert 
die Funktion bis das Byte gesendet wurde (siehe SCR203.PNG). Werden 
hingegen mehrere Bytes übergeben, so funktioniert die Funktion korrekt 
(siehe SCR204.PNG). Ich konnte aus dem HAL-Quellcode die Ursache dafür 
nicht finden.

Ein Bug? Oder eine fehlerhafte Konfiguration?

Gruß Marc

von Benjamin S. (recycler)


Bewertung
-3 lesenswert
nicht lesenswert
Er macht genau was er soll.
Du ziehst den CS auf low startest den Transfer und dann geht der CS auf 
high.
Was du aber willst ist folgendes:

Möglichkeit A: Aktives Warten - müsste mit folgendem gehen:
1
HAL_SPI_Transmit(&hspi3, buffer, 1);

Möglichkeit B: Du setzt den Befehl ab und nimmst deine Interruptfunktion 
"HAL_SPI_Transmit_IT" und im Interrupt Callback den CS wieder hoch. Da 
müsste ein Funktionsrumpf da sein.

Das bei einem Byte ein Blockierender Aufruf folgt kann sein, da müsste 
man aber genauer schauen. Da kann viel reinspielen.

Blockierender Aufruf würde ich mit der Funktion ohne IT machen.

von Marc L. (dl5sez)


Bewertung
0 lesenswert
nicht lesenswert
Benjamin S. schrieb:
> Er macht genau was er soll.

Nein, macht er nicht.
1
HAL_SPI_Transmit_IT(&hspi3, buffer, 1);
2
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_9, GPIO_PIN_SET);
Nachdem die beiden Befehlszeilen ausgeführt wurden, sollte das Signal 
GPIO_PIN_9 unmittelbar auf HIGH gehen, nicht erst nachdem der 
SPI-Transfer beendet ist. Sonst würde der Interrupt-Betrieb keinen Sinn 
machen. Er sollte nicht-blockierend sein.

> Du ziehst den CS auf low startest den Transfer und dann geht der CS auf
> high.

In der aktuellen Konfiguration gibt es kein CS. CS wird zum Senden nicht 
benötigt.

> Das bei einem Byte ein Blockierender Aufruf folgt kann sein, da müsste
> man aber genauer schauen. Da kann viel reinspielen.

Ja genau. Und genau deswegen schrieb ich diesen Post.

Gruß Marc

von Harry L. (mysth)


Bewertung
0 lesenswert
nicht lesenswert
Wenn du die *_IT-Funktionen nutzt, musst du auch den/die zugehörigen 
Callback(s) implementieren, sonst macht das alles wenig Sinn.
In deinem Fall ist das mindestens:
1
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi);

Besorg dir mal dieses User-Manual. Da wird auch HAL_SPI genau erklärt:

https://www.st.com/content/ccc/resource/technical/document/user_manual/56/32/53/cb/69/86/49/0e/DM00223149.pdf/files/DM00223149.pdf/jcr:content/translations/en.DM00223149.pdf

von Marc L. (dl5sez)


Bewertung
0 lesenswert
nicht lesenswert
Harry L. schrieb:
> Wenn du die *_IT-Funktionen nutzt, musst du auch den/die zugehörigen
> Callback(s) implementieren, sonst macht das alles wenig Sinn.
> In deinem Fall ist das mindestens:
>
1
> void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi);
2
>
>
> Besorg dir mal dieses User-Manual. Da wird auch HAL_SPI genau erklärt:
>
> 
https://www.st.com/content/ccc/resource/technical/document/user_manual/56/32/53/cb/69/86/49/0e/DM00223149.pdf/files/DM00223149.pdf/jcr:content/translations/en.DM00223149.pdf

Die Callback-Funktion ist für die Demonstration des Problems nicht 
notwendig, da sie erst NACH der Übertragung aufgerufen wird. Die 
Blockade besteht aber WÄHREND der Übertragung.

von Harry L. (mysth)


Bewertung
-3 lesenswert
nicht lesenswert
Marc L. schrieb:
> Harry L. schrieb:
>> Wenn du die *_IT-Funktionen nutzt, musst du auch den/die zugehörigen
>> Callback(s) implementieren, sonst macht das alles wenig Sinn.
>> In deinem Fall ist das mindestens:
>>
1
>> void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi);
2
>>
>>
>> Besorg dir mal dieses User-Manual. Da wird auch HAL_SPI genau erklärt:
>>
>>
> 
https://www.st.com/content/ccc/resource/technical/document/user_manual/56/32/53/cb/69/86/49/0e/DM00223149.pdf/files/DM00223149.pdf/jcr:content/translations/en.DM00223149.pdf
>
> Die Callback-Funktion ist für die Demonstration des Problems nicht
> notwendig, da sie erst NACH der Übertragung aufgerufen wird. Die
> Blockade besteht aber WÄHREND der Übertragung.

Ja, weil CS viel zu früh inaktiv wird.
Genau das muß im Callback passieren.
W.g.: ohne die Callbacks wird das nix mit den *_IT-Varianten.

von Marc L. (dl5sez)


Bewertung
0 lesenswert
nicht lesenswert
Harry L. schrieb:

> Ja, weil CS viel zu früh inaktiv wird.

Es gibt in meiner Konfiguration kein CS!

> W.g.: ohne die Callbacks wird das nix mit den *_IT-Varianten.

Ich geb's auf.

von Thomas E. (picalic)


Bewertung
0 lesenswert
nicht lesenswert
Meine Vermutung ist, daß es nur so aussieht, als ob es nicht 
funktioniert.
Wenn Du nur ein Byte sendest, wird ja sofort die DMA Transfer Complete 
ISR aufgerufen, d.h. bereits vor dem Befehl zum Setzen des Ports auf 
High, und möglicherweise wird dort blockierend auf den Abschluss der 
Übertragung des letzten Bytes gewartet. Normalerweise dürfte das 
notwendig sein, damit die (bei Dir nicht vorhandene) Callback-Funktion 
tatsächlich erst NACH dem Abschluss des kompletten Sendevorgangs 
aufgerufen wird und nicht schon, wenn das letzte Byte per DMA gerade 
erst aus dem Speicher gelesen wurde.

Ist aber bloß eine Theorie - von dem HAL-Zeugs halte ich mich lieber 
fern. Es reicht mir schon, die Bugs in meinem eigenen Code zu finden, da 
muss ich nicht auch noch den Code von ST debuggen... ;)

von Lutz (Gast)


Bewertung
-2 lesenswert
nicht lesenswert
Marc L. schrieb:
> Harry L. schrieb:
>
>> Ja, weil CS viel zu früh inaktiv wird.
>
> Es gibt in meiner Konfiguration kein CS!

Welche Funktion hat denn GPIO A9 in deinem Programm?

von Marc L. (dl5sez)


Bewertung
3 lesenswert
nicht lesenswert
Lutz schrieb:
> Welche Funktion hat denn GPIO A9 in deinem Programm?

GPIOA.9 dient dazu den Startzeitpunkt und den Endzeitpunkt der Funktion 
HAL_SPI_Transmit_IT() am Oszilloskop darzustellen.

von Doctor Snuggles (Gast)


Bewertung
-3 lesenswert
nicht lesenswert
Marc L. schrieb:
> Ich frage mich ob es eine gute Idee war mit den HAL-Bibliotheken
> anzufangen ...


Natürlich nicht. Wüsstest Du aber, wenn Du öfters hier im Forum mal 
mitgelesen hättest... Habe gerade mal in den Code geschaut. Der ist ja 
sowas von aufgeblasen. Dabei sind das nur ein paar Bits und ein paar 
Zeilen Code - selbst wenn man DMA nutzt - wenn man das in richtig macht.


Thomas E. schrieb:
> Meine Vermutung ist, daß es nur so aussieht, als ob es nicht
> funktioniert.
> Wenn Du nur ein Byte sendest, wird ja sofort die DMA Transfer Complete
> ISR aufgerufen, d.h. bereits vor dem Befehl zum Setzen des Ports auf
> High, und möglicherweise wird dort blockierend auf den Abschluss der
> Übertragung des letzten Bytes gewartet. Normalerweise dürfte das
> notwendig sein, damit die (bei Dir nicht vorhandene) Callback-Funktion
> tatsächlich erst NACH dem Abschluss des kompletten Sendevorgangs
> aufgerufen wird und nicht schon, wenn das letzte Byte per DMA gerade
> erst aus dem Speicher gelesen wurde.


Klar, dazu reicht ein Blick in dieses üble Framework:

Erst:
1
  /* Wait until TXE flag is set to send data */
2
  if(SPI_WaitOnFlagUntilTimeout(hspi, SPI_FLAG_TXE, RESET, SPI_TIMEOUT_VALUE) != HAL_OK)
3
  {
4
    hspi->ErrorCode |= HAL_SPI_ERROR_FLAG;
5
  }

Dann:
1
    if(SPI_WaitOnFlagUntilTimeout(hspi, SPI_FLAG_BSY, SET, SPI_TIMEOUT_VALUE) != HAL_OK)
2
    {
3
      hspi->ErrorCode |= HAL_SPI_ERROR_FLAG;
4
    }


Mein Vorschlag: Mach das selber. Ist wirklich trivial.

von Thomas E. (picalic)


Bewertung
0 lesenswert
nicht lesenswert
Marc L. schrieb:
> GPIOA.9 dient dazu den Startzeitpunkt und den Endzeitpunkt der Funktion
> HAL_SPI_Transmit_IT() am Oszilloskop darzustellen.

Du könntest den Pin nach Aufruf der Tx-Funktion in einer Endlosschleife 
togglen, dann könnte man sehen, ob da am Ende der Übertragung irgendwas 
blockiert und wie lange.

von Marc L. (dl5sez)


Angehängte Dateien:

Bewertung
1 lesenswert
nicht lesenswert
Thomas E. schrieb:
> Du könntest den Pin nach Aufruf der Tx-Funktion in einer Endlosschleife
> togglen, dann könnte man sehen, ob da am Ende der Übertragung irgendwas
> blockiert und wie lange.

Gute Idee!

Dann senden wir mal 3 Bytes:
1
HAL_SPI_Transmit_IT(&hspi3, buffer, 3);
2
while(true) { HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_9); }

Siehe Scope -> grauenhaft!

Doctor Snuggles schrieb:
> Mein Vorschlag: Mach das selber. Ist wirklich trivial.

Ich denke Du hast recht. Die Fehlersuche, Code-Analyse der 
HAL-Bibliotheken und daraus folgenden Workarounds haben mich schon 
soviel Zeit gekostet ...

: Bearbeitet durch User
von Marc L. (dl5sez)


Bewertung
1 lesenswert
nicht lesenswert
Inzwischen konnte ich die Ursache des Problems finden.

Das Problem ist, dass das Ende einer SPI-Übertragung nicht durch einen 
TX-Interrupt signalisiert wird. Die HAL-Bibliotheken behelfen sich damit 
über eine while-Schleife das Busy-Flag abzufragen. Dadurch entsteht die 
Blockade. Leider löst das Busy-Flag keinen Interrupt aus.

ST hat mir vorgeschlagen das Ende einer Übertragung über den 
RX-Interrupt signalisieren zu lassen. Das funktioniert sehr gut. 
Allerdings verwende ich inzwischen meinen eigenen Code.

Falls es jemanden interessiert, hier der Link zum 
Kommunikationsaustausch mit ST:
https://community.st.com/s/question/0D50X00009sW5LdSAK/halspitransmitit-blocks

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.