Forum: Mikrocontroller und Digitale Elektronik STM32 - SPI-Clock zu lang


von Terence S. (takeshi)


Angehängte Dateien:

Lesenswert?

Microcontroller: STM32G474VET6

Hallo zusammen,

ich versuche mich gerade darin ein SPI-Modul nicht mit HAL, sondern auf 
Registerebene (und ohne DMA) anzusprechen. Der Controller soll Daten mit 
einer Länge von 8 Bit senden. Die Konfiguration ist dem angehängten Bild 
zu entnhemen. Habe mir die Beschreibung aller Register angesehen und bin 
der Meinung, das sollte so passen, aber da liege ich wohl falsch. Oder 
ich sende einfach falsch.

Testweise möchte ich den Wert 0xAA (also 1010 1010) senden. Dazu 
schreibe ich einmalig direkt in das Data-Register:
1
uint_16_t spitx = 0xAA;
2
uint_16_t spisr = 0;
3
4
// [...]
5
6
SPI1->DR = spitx;       // Daten senden
7
spisr = SPI1->SR;       // Status-Register auslesen

Wie auf dem Oszillogramm zu erkennen ist, wird der Wert auch übertragen, 
aber der Clock ist doppelt so lang, nämlich 16 Bit. Nun gut, dachte ich, 
liegt vielleicht an der 16-Bit-Variable. Stelle ich aber die Datenlänge 
auf 4 Bit, dann wird nur ein 0xA übertragen und der Clock zählt 8 Takte.
Die Datenlänge stimmt also immer mit dem überein, was ich einstelle, der 
Clock ist aber genau doppelt so lang, als würde ich direkt danach noch 
einmal eine 0 ins Datenregister schreiben, was aber nicht der Fall ist.

Vor dem Senden hat das Status-Register den Wert 0x0002 (0000 0000 0000 
0010), direkt nach dem Senden 0x1082 (0001 0000 1000 0010).
Der TX-Puffer ist also weiterhin leer (TXE), weil das SPI-Modul die 
Daten bereits sendet, der Puffer dafür schon wieder leer geräumt wurde. 
BSY ist gesetzt, logisch. FTLVL hat den Wert 2 (10), was zu dem Problem 
passt, es sind angeblich 16 Bit drin, statt der 8 Bit, die es sein 
sollten.

Was mache ich da falsch, kann mir da jemand weiterhelfen?

von fern schätzer (Gast)


Lesenswert?

Terence S. schrieb:
> Was mache ich da falsch, kann mir da jemand weiterhelfen?

Versuche mal das Datenregister mit einer uint8-Variable zu
beschreiben. Der STM könnte sich bemüssigt fühlen bei 16-Bit
Zugriff auch gleich zwei Bytes zu übernehmen und zu senden.

Wenn das so ist könntest du ja deine Variable

uint_16_t spitx = 0xAA;

mal versuchshalber mit 0x55AA zu belegen um zu sehen ob das
auf der SPI-Leitung herauskommt.

von AVerr (Gast)


Lesenswert?

Wie fern schätzer schon richtig erkannt hat hängt die Zahl der Bytes die 
geschrieben werden vom Zugriff auf DR ab: Wenn du darauf 16bit schreibst 
schreibt er beide Bytes in den FIFO.
Aus dem G4 HAL:
1
*((__IO uint8_t *)&hspi->Instance->DR) = (*hspi->pTxBuffPtr);
So wird nur 1 byte reingeschrieben.

von AVerr (Gast)


Lesenswert?

Das ganze wird übrigens im Reference Manual unter dem Punkt "Data 
packing" genauer erklärt :)

von Werner R. (wt_r)


Lesenswert?

Hallo,
 anstatt

> SPI1->DR = spitx;       // Daten senden

*(volatile uint8_t*) &SPI1->DR = spitx;

Gruss Werner

von Terence S. (takeshi)


Lesenswert?

Danke für die zahlreichen Antworten! Zunächst das Wichtigste:

Werner R. schrieb:
> *(volatile uint8_t*) &SPI1->DR = spitx;

Damit funktioniert es, darauf wäre ich echt nie selbst gekommen! Die 
Datenlänge stimmt nun immer mit der eingestellten Größe überein, wie es 
soll. Damit wäre ich jetzt eigentlich fertig, allerdings würde ich das 
gern auch verstehen. Da geraten meine C-Kenntnisse ein wenig an ihre 
Grenzen, ich versuche es erst einmal selbst zu verstehen.
1
&SPI1->DR
 liefert mir die Adresse des Registers.
1
volatile uint8_t*
 kenne ich nur von einer Deklaration bzw. Definition einer 
Zeiger-Variagble, die auf eine uint8_t-Variable zeigt, die sich wiederum 
unabhängig vom Programmablauf ändern kann (Register). Ich ändere also 
das Datenformat der Register-Adresse auf uint8_t, da das Register 
eigentlich 16 Bit hat. Und mit dem Asterisk am Anfang schreibe ich dann 
wiederum in den Speicher an der angegebenen Adresse. Letztlich sorge ich 
dafür, dass die Variable, in die ich schreibe, als uint8_t-Variable 
behandelt wird statt als uint16_t. Ohne den ganzen Zauber "optimiert" 
der Compiler das so, dass er meine Variable, die ich in das Register 
schreibe, auf uint6_t ändert, bevor er sie schreibt. Verstehe ich das 
richtig?

Eine uint_8_t-Variable hatte ich schon getestet, das brachte nämlich 
keine Änderung, was mir nun auch logisch erscheint.

So, wie ich das bisher verstehe, auch mit dem Reference Manual, bestimmt 
die Datenlänge zunächst, ob das FIFO in 4x8 oder 2x16 Bit organisiert 
wird. Bei einer Länge bis einschließlich 8 Bit ist es 4x8 Bit und 
darüber 2x16 Bit. Unabhängig davon ist aber das Datenregister 16 Bit 
groß. Schreibe ich 16 Bit rein, werden diese 16 Bit in das FIFO 
geschrieben. Das SPI-Modul findet dann die 16 Bit im FIFO wieder. Ist 
die Datenlänge auf 16 Bit eingestellt, werden diese 16 Bit gesendet, bei 
8 Bit dann 2x 8 Bit. Und bei einer anderen Länge werden die 
überschüssigen Bits einfach verworfen.
Hab testweise einmal die Länge auf 12 Bit gestellt und wieder ganz 
normal eine uint16_t-Variable verwendet, dann klappt es wie erwartet, es 
werden 12 Bit gesendet.
Sollte ich einen groben Fehler drin haben, bitte korrigieren, 
andernfalls hilft die Erläuterung vielleicht noch jemand anderem.

: Bearbeitet durch User
von Werner R. (wt_r)


Angehängte Dateien:

Lesenswert?

Terence S. schrieb:
>
> Sollte ich einen groben Fehler drin haben, bitte korrigieren,
> andernfalls hilft die Erläuterung vielleicht noch jemand anderem.

Hallo Terence,
was Du ausführlich und im wesentlichen richtig beschrieben hast,
ist im Anhang kompakt zusammen gefasst.

Grüsse Werner

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.