Forum: Mikrocontroller und Digitale Elektronik STM32F103 SPI Slave + Master


von Anton Weiss (Gast)


Angehängte Dateien:

Lesenswert?

Hallo!

Ich versuche mich an einer SPI Kommunkation zwischen 2 STM32F103 Boards. 
Dabei kann ich mit dem Master senden, und dem Slave empfangen. Weiter 
als das komme ich jedoch nicht. Ich habe versucht mich in die Register 
einzulesen und es scheint mir richtig zu sein. Jedoch kann ich mit dem 
Slave nichts senden, MISO bleibt einfach HIGH.
Dabei habe ich die Grundstruktur mit CubeMX erzeugt.

Die Slave while Schleife sieht folgendermaßen aus:
1
while (1)
2
  {
3
    if(GPIOB->IDR & (1 << 10))
4
    {
5
      SPI2->CR1 |= 1UL << 9;
6
7
      while(!(SPI2->SR & (1<<1)));
8
      SPI2->CR1 &= ~(1UL << 8);
9
      SPI2->DR = output;
10
11
      while(!(SPI2->SR & (1<<0)));
12
      input = SPI2->DR;
13
      SPI2->CR1 |= 1UL << 8;
14
15
      output = input;
16
      GPIOB->BSRR = 1<<8;
17
    }
18
  }

von Anton Weiss (Gast)


Angehängte Dateien:

Lesenswert?

Anbei noch ein Beispiel aus dem LogicAnalyzer.

von Bastian W. (jackfrost)


Lesenswert?

Hi,

Was ist das für ein SPI slave ?
CS ist normal low active.

Gruß JackFrost

von Korinthen Sucher (Gast)


Lesenswert?

Bastian W. schrieb:
> Was ist das für ein SPI slave ?

Man!!

Anton Weiss schrieb:
> Ich versuche mich an einer SPI Kommunkation zwischen 2 STM32F103 Boards.

von pegel (Gast)


Lesenswert?

Warum benutzt du die direkten Register Zugriffe?
HAL hat für alles so schöne Funktionen.
Wenn du wissen willst was mit den Registern passiert,
lass dir diese Funktionen einfach anzeigen.

Ein Beispiel für alle Arten der SPI Übertragung findet sich unter:

STM32Cube_FW_F1_V1.6.1/Projects/STM32F103RB-Nucleo/Examples/SPI/

von Anton Weiss (Gast)


Lesenswert?

Alleine HAL_SPI_TransmitReceive(..) hat knapp 200 Zeilen, prinzipiell 
geht SPI auch kürzer, vor allem wenn ich eigentlich nur 1 byte abfragen 
möchte.

Unabhängig davon, habe ich bereits die HAL Funktionen probiert, und 
komme exakt genauso weit.

von pegel (Gast)


Lesenswert?

Willst du nur 1 Byte vom Slave holen?
Möglicherweise musst du dazu ein Dummy Byte vom Master senden.

Das mit den CS High, ist tatsächlich eigenartig.

von Anton Weiss (Gast)


Lesenswert?

Naja so wie ich es im Datenblatt verstehe, steuere ich bei NSS Software 
das Chipselect über ein SSI Bit, was ich widerum low setze am Anfang und 
wieder high am Ende.
CS high dient nur um in der while Schleife den Slave Transceive Block 
auszulösen.

[c]
SPI2->CR1 &= ~(1UL << 8);
[\c]

von Chris D. (myfairtux) (Moderator) Benutzerseite


Lesenswert?

Bit 8SSI: Internal slave select
This bit has an effect only when the SSM bit is set. The value of this 
bit is forced onto the NSS pin and the IO value of the NSS pin is 
ignored.

Und zu Bit 9:
"When the SSM bit is set, the NSS pin input is replaced with the value 
from the SSI bit."

D.h. Dein Slave-SPI ist freigegeben, wenn Du nichts sendest (Low) und 
wenn Du sendest, deaktivierst Du sie sauber (HIGH) ;-)

Die Polarität lässt sich mWn auch nicht ändern. Das ließ mich neulich 
etwas grübeln, weil ich die SPI über invertierende H11L1-Optokoppler 
führen wollte, aber glücklicherweise konnte mein Master (Raspberry) das 
CS-Signal invertiert ausgeben :-)

P.S.: Ich mag den ganzen HAL-Kram auch nicht mehr - die gesamte 
Schnittstelle kann man mit zwei Zeilen viel übersichtlicher 
konfigurieren.

: Bearbeitet durch Moderator
von Anton Weiss (Gast)


Lesenswert?

Ja das mit CS war auch so gemeint. Ich versteh das schon so das wenn SSM 
gesetzt ist, ich quasi ein externes NSS Signal durch SSI ersetze...

Das SSM und SSI setze ich ja vor der while Schleife auf 1, und erst nach 
der Abfrage ob mein Transmit leer ist, setze ich es auf 0 und beschreibe 
das DataRegister. Hier würde ich ja nun eine Regung auf MISO 
erwarten....

Das CS High kommt nur von einem Pin mit dem ich das Ganze überhaupt 
starte. Da ich ja durch SOFTNSS nun keinen Start mehr mit polling 
bekomme.

Oder ist das Verständnis hier nicht ganz richtig?

von Chris D. (myfairtux) (Moderator) Benutzerseite


Lesenswert?

Anton Weiss schrieb:
> Ja das mit CS war auch so gemeint. Ich versteh das schon so das wenn SSM
> gesetzt ist, ich quasi ein externes NSS Signal durch SSI ersetze...
>
> Das SSM und SSI setze ich ja vor der while Schleife auf 1, und erst nach
> der Abfrage ob mein Transmit leer ist, setze ich es auf 0 und beschreibe
> das DataRegister. Hier würde ich ja nun eine Regung auf MISO
> erwarten....

Ah ok.

Aber wenn Du NSS anfangs vor der While-Schleife auf 1 setzt und damit 
die Schnittstelle abkoppelst/deaktivierst -  warum sollte die 
SPI-Schnittstelle dann danach irgendetwas im Statusregister ändern?

Solange NSS auf 1 ist, macht die SPI nix - dann wird weder geschoben 
noch irgendein Statusbit gesetzt. So habe ich das jedenfalls verstanden.

NSS musst Du während der gesamten Zeit aktiv (Low) halten, also noch 
bevor irgendetwas vom Master kommt.

Wieso überlässt Du das eigentlich nicht dem Master? Der weiss, was gut 
für seinen Sklaven ist ;-)

: Bearbeitet durch Moderator
von Anton Weiss (Gast)


Lesenswert?

Weil ich bei HardwareNSS keine Regung gesehen hab. Und der Softwareteil 
eigentlich genauso gehen sollte.

Bei den Projektbeispielen hab ich dann stattdessen das Problem das 
HAL_SPI_TransmitReceive erstmal in den HAL_Delay geht und nie wieder 
kommt.

von Chris D. (myfairtux) (Moderator) Benutzerseite


Lesenswert?

Anton Weiss schrieb:
> Weil ich bei HardwareNSS keine Regung gesehen hab. Und der Softwareteil
> eigentlich genauso gehen sollte.

Ja, aber wohl nur, wenn man NSS dann auch auf 0 zieht.

Und vermutlich muss dies auch rechtzeitig geschehen, da der SPI-Takt ja 
deutlich unter dem Prozessortakt liegt.

> Bei den Projektbeispielen hab ich dann stattdessen das Problem das
> HAL_SPI_TransmitReceive erstmal in den HAL_Delay geht und nie wieder
> kommt.

Ich mache das lieber immer direkt nach der Registerbeschreibung. Für die 
STMF0 gibt es auch so schöne Beispiele ganz ohne StdLib und HAL im 
Reference Manual. Leider gibt es die ab F1 aufwärts nicht (nennt sich 
"STM32SnippetsF0") :-/

Hier ist der Code meines Slaves (BluePill, also auch STM32F103, Master 
ist ein Raspberry über galvanische Trennung. Die Ein-/Ausgänge müssen 
natürlich vorher gesetzt werden):
1
  // SPI als Slave initialisieren:
2
  SPI->CR2 = 0;
3
  // Hardware-NSS aktivieren, unidirektional, Slave, 8 Bit, SPI aktivieren, CPOL und CPHA auf Null (steigende erste Flanke)
4
  SPI->CR1 = SPI_CR1_SPE;
5
6
  // Hauptschleife
7
  while (1)
8
  {
9
    // Warte auf gefüllten SPI-Empfangspuffer
10
    while ((SPI->SR & SPI_SR_RXNE) == 0)
11
    {
12
      // Währenddessen werte A/D-Eingänge aus:
13
      // Wenn die Wandlung beendet ist, speichere diese ab
14
      if (ADC1->SR & ADC_SR_EOC)
15
      {
16
        // Speichere Wert in Zwischenspeicher
17
        adc [nr] = ADC1->DR;
18
        // Starte neue Wandlung 
19
        ADC1->CR2 |= ADC_CR2_ADON;
20
        // Wähle nächsten Zwischenspeicher
21
        if (nr < 5) nr++; else nr = 0;
22
      }
23
    }
24
    
25
    // Lese Byte aus
26
    cmd = SPI->DR;
27
28
    // Wenn wir ein Dummybyte empfangen, mache nichts (= nur Ausgabe eines Datenbytes)
29
    if (cmd == 0xFF)
30
    {
31
      // Wenn wir einen ADC-Wert auslesen, schiebe noch das High-Byte nach
32
      if (mode)
33
      {
34
        SPI->DR = tmp;
35
        mode = 0;
36
      }
37
      continue;
38
    }
39
    ...
40
  }

Da ist NSS aber eben hardwarebasierend.

Ich warte also jeweils auf einen gefüllten Empfangspuffer, lese und 
werte diesen "Befehl" dann aus und packe die Antwort des Slaves dann in 
SPI->DR (falls es eine Antwort gibt). Der Master schiebt dann noch ein 
Dummy-Byte nach und holt sich so die Antwort. Bei 12-Bit-A/D-Werten 
benötigt man natürlich zwei Bytes (wie oben im Code angedeutet ist).

BTW: Du solltest beim Setzen von Registerbits immer auf die Definitionen 
in der stm32f3xx.h zurückgreifen anstatt irgendwelche Zahlen zu 
schieben, sonst wird das Ganze sehr unübersichtlich (und fehleranfällig)

: Bearbeitet durch Moderator
von Anton W. (Firma: TU Darmstadt) (ravnicas)


Lesenswert?

Sieht gut aus, es funktioniert nun.

Ich habe MOSI auf Input und MISO auf PushPull gesetzt.. Beides beim 
Slave logischerweise. Ist mir bei den CubeMX Examples nicht aufgefallen 
das es dort passiert ist. Aus dem Datenblatt ist mir das nicht 
ersichtlich geworden, da scheinbar PushPull auch empfangen kann, aber 
Input nicht senden.

Hardware NSS funktioniert nun auch beim Slave. Master läuft noch mit 
HAL.

Vielen dank an der Stelle schonmal.

: Bearbeitet durch User
von Chris D. (myfairtux) (Moderator) Benutzerseite


Lesenswert?

Anton W. schrieb:
> Sieht gut aus, es funktioniert nun.
>
> Ich habe MOSI auf Input und MISO auf PushPull gesetzt.. Beides beim
> Slave logischerweise.
> Ist mir bei den CubeMX Examples nicht aufgefallen
> das es dort passiert ist. Aus dem Datenblatt ist mir das nicht
> ersichtlich geworden, da scheinbar PushPull auch empfangen kann, aber
> Input nicht senden.

Jepp, so habe ich die Pins meines Slaves auch konfiguriert: PP und AF 
bei SCK und MISO, "floating in" bei MOSI.

> Hardware NSS funktioniert nun auch beim Slave. Master läuft noch mit
> HAL.
>
> Vielen dank an der Stelle schonmal.

Gerne. Passte halt gerade wirklich gut, weil ich das letzte Woche alles 
selbst "durchgemacht" habe ;-)

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.