Forum: Mikrocontroller und Digitale Elektronik STM32 als SPI Slave: CS hardware- oder softwaregesteuert?


von Johannes B. (groovycat)


Lesenswert?

Hallo,

wenn man einen STM32F4 über die cubeMX HAL als SPI-Master konfiguriert, 
kann man bei der Konfiguration wählen, ob der CS- bzw. NSS-Pin per 
Hardware oder per Software gesteuert werden soll.


Bei spi.Init.NSS = SPI_NSS_HARD übernimmt der SPI-Master die Steuerung 
der CS-Leitung. Das heißt, wenn Daten über MOSI gesendet werden, zieht 
der SPI-Master die CS-Leitung automatisch auf Low. Bei spi.Init.NSS = 
SPI_NSS_SOFT muss man sich selbst um den CS-Pin kümmern. Das heißt man 
definiert einen beliebigen Pin als ganz normalen Output ohne alternative 
Funktion und zieht diesen vor dem Senden dann manuell auf Low.

Nun frage ich mich aber, wie das bei einer Slave-Konfiguration 
funktioniert. Ein SPI-Slave soll ja nur aktiv werden, wenn seine 
CS-Leitung vom Master auf Low gezogen wird. Nun könnte man ja beim Slave 
auf die Idee kommen, spi.Init.NSS = SPI_NSS_SOFT zu konfigurieren und 
dann den CS-Pin als Input konfigurieren? In diesem Fall wüsste der Slave 
doch aber gar nicht, dass der CS-Pin als Input existiert? Woraus ich 
schlussfolgern würde, dass bei der Slave-Konfiguration nur spi.Init.NSS 
= SPI_NSS_HARD zulässig ist (der entsprechende CS-Pin bekommt dann als 
alternative Funktion den SPI zugewiesen)?

Viele Grüße

von Alex D. (daum)


Lesenswert?

Der SPI hat zusätzlich noch das SSI (Slave Select Internal) bit, wenn 
SPI auf Software SS konfiguriert ist, wird der Zustand dieses bit 
verwendet, also auch bei Slave. Dazu müsste also ein externer Interrupt 
auf der SS Leitung verwendet werden und in dem Interrupt wird das SSI 
bit gesetzt.
Wozu das gut sein soll, kann ich mir zwar nicht vorstellen, aber 
funktionieren würde es.

von Johannes B. (groovycat)


Lesenswert?

Alex D. schrieb:
> Der SPI hat zusätzlich noch das SSI (Slave Select Internal) bit, wenn
> SPI auf Software SS konfiguriert ist, wird der Zustand dieses bit
> verwendet, also auch bei Slave. Dazu müsste also ein externer Interrupt
> auf der SS Leitung verwendet werden und in dem Interrupt wird das SSI
> bit gesetzt.

Vielen Dank!

Alex D. schrieb:
> Wozu das gut sein soll, kann ich mir zwar nicht vorstellen, aber
> funktionieren würde es.

Man könnte erst den DMA starten oder mit Pollen beginnen, nachdem CS 
wirklich auf Low gezogen wurde. So könnte sich der Slave über CS mit dem 
Master synchronisieren. Bei STM32 ist es aber auch möglich an einem 
hardwaregesteuerten CS ein Interrupt zu konfigurieren (Pins, die eine 
Alternate function wie z.B. SPI erhalten, können zusätzlich auch auf 
Interrupts konfiguriert werden). So gesehen, wäre es eigentlich völlig 
egal, für welche Variante man sich entscheidet.

von mr. mo (Gast)


Lesenswert?

Johannes B. schrieb:
> Man könnte erst den DMA starten oder mit Pollen beginnen, nachdem CS
> wirklich auf Low gezogen wurde. So könnte sich der Slave über CS mit dem
> Master synchronisieren. Bei STM32 ist es aber auch möglich an einem
> hardwaregesteuerten CS ein Interrupt zu konfigurieren (Pins, die eine
> Alternate function wie z.B. SPI erhalten, können zusätzlich auch auf
> Interrupts konfiguriert werden). So gesehen, wäre es eigentlich völlig
> egal, für welche Variante man sich entscheidet.

Wieso nimmst du den CS nicht als SPI_NSS_HARD und lässt den DMA 
arbeiten? Das ist schneller und kostet keine Rechenleistung. Kann dir 
nicht sagen wie man das in Cube konfiguriert, aber mit den LL-Drivern 
geht das problemlos.

Wenn du den CS via externen Interrupt nutzt, kann es bei schneller 
Datenübertragungen zu langsam sein den DMA von Hand anzuwerfen. 
(Erfahrung musste ich bereits machen :))

von Peter D. (peda)


Lesenswert?

Viele Programmierer denken, /CS ist überflüssig. Und solange beide MCs 
gleichzeitig bestromt werden, geht das auch ne ganze Weile gut.
Später merkt man aber, daß es unzuverlässig ist. Dann wird mit dem 
Timerinterrupt ein SW-Sync als Krücke dazu gebastelt.

von Johannes B. (groovycat)


Lesenswert?

mr. mo schrieb:
> Wieso nimmst du den CS nicht als SPI_NSS_HARD und lässt den DMA
> arbeiten? Das ist schneller und kostet keine Rechenleistung. Kann dir
> nicht sagen wie man das in Cube konfiguriert, aber mit den LL-Drivern
> geht das problemlos.

Für meinen Anwendungsfall tauschen sich Master und Slave Nachrichten 
fester Länge aus. Der DMA soll im Slave, nachdem die feste Länge über 
SPI empfangen wurde, ein Interrupt triggern. Das Problem ist folgendes: 
Mikrocontroller A startet und beginnt als SPI-Master etwas zu versenden. 
Während Mikrocontroller A noch am Senden ist, startet Mikrocontroller B 
und beginnt sofort als SPI-Slave etwas zu empfangen. Mikrocontroller B 
empfängt dann nur den letzten Teil der Nachricht. Der Interrupt des DMAs 
wird dadurch nicht ausgelöst, weil noch nicht die komplette Länge 
empfangen wurde. Beim nächsten Schleifendurchlauf sendet Mikrocontroller 
A dann die nächste Nachricht fester Länge. In Mikrocontroller B löst der 
DMA dann ein Interrupt aus, weil der DMA-Puffer nun die Länge der 
Nachricht erreicht hat. Fälschlicherweise glaubt dann Mikrocontroller B 
er hat nun eine zusammengehörige Nachricht empfangen, aber eigentlich 
besteht diese Nachricht aus zwei unterschiedlichen Nachrichten vom 
Master.

Deshalb habe ich mir Gedanken gemacht, wie man das synchronisieren 
könnte. Dabei kamen mir zwei Ideen. A) Den DMA erst starten, wenn CS vom 
Master auf Low gezogen wurde (daher meine Frage hier im Forum). B) Der 
Master sendet mehrmals eine bestimmte Reihenfolge von Startbytes. Der 
Slave schreibt alles, was er empfängt, in einen größeren Puffer, 
durchsucht diesen Puffer nach den Startbytes und kann sich so mit dem 
Master synchronisieren.

mr. mo schrieb:
> Wenn du den CS via externen Interrupt nutzt, kann es bei schneller
> Datenübertragungen zu langsam sein den DMA von Hand anzuwerfen.
> (Erfahrung musste ich bereits machen :))

Dann fällt die Lösung A) wohl flach. Aber der Slave könnte stattdessen 
Pollen, eine Antwort an den Master senden, wenn er synchron ist und 
danach erst den DMA anwerfen.

von Alex D. (daum)


Lesenswert?

Johannes B. schrieb:
> Deshalb habe ich mir Gedanken gemacht, wie man das synchronisieren
> könnte. Dabei kamen mir zwei Ideen. A) Den DMA erst starten, wenn CS vom
> Master auf Low gezogen wurde (daher meine Frage hier im Forum). B) Der
> Master sendet mehrmals eine bestimmte Reihenfolge von Startbytes. Der
> Slave schreibt alles, was er empfängt, in einen größeren Puffer,
> durchsucht diesen Puffer nach den Startbytes und kann sich so mit dem
> Master synchronisieren.

Wird bei deiner Übertragung CS während der ganzen Übertragung LOW 
gehalten? Dann kannst du einfach bei steigender Flanke von CS am Slave 
den Buffer verwerfen, falls dieser nicht voll ist.

Wenn CS immer nur für 1 Byte LOW geht, dann is es wohl am besten ein 
Protokoll darüber zu bauen, bei dem der Start und das Ende eindeutig 
erkannt werden können.

von Johannes B. (groovycat)


Lesenswert?

Alex D. schrieb:
> Wird bei deiner Übertragung CS während der ganzen Übertragung LOW
> gehalten? Dann kannst du einfach bei steigender Flanke von CS am Slave
> den Buffer verwerfen, falls dieser nicht voll ist.
>
> Wenn CS immer nur für 1 Byte LOW geht, dann is es wohl am besten ein
> Protokoll darüber zu bauen, bei dem der Start und das Ende eindeutig
> erkannt werden können.

CS wird während der ganzen Übertragung LOW gehalten. Ich finde, den 
Puffer bei steigender Flanke zu verwerfen, ist eine sehr gute Idee. Wenn 
nach der steigenden Flanke an CS der DMA noch nicht fertig ist, weiß 
man, dass die Nachricht nicht vollständig war. Dann setzt man die 
Adresse des DMA manuell wieder an den Anfang und ist somit bei der 
nächsten Nachricht synchron.

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.