Forum: Mikrocontroller und Digitale Elektronik STM32G474, SPI2 läßt sich nicht einschalten


von Uli N. (uln)


Angehängte Dateien:

Lesenswert?

Hat von Euch jemand eine Idee, weshalb ich das SPI2 des STM32G474 nicht 
einschalten kann. Bevor ich Zeile 63 ausführe, steht im CR1-Register 
0x2C,
nach Ausführung der Zeile 63 nicht wie erwartet 0x6C, sondern 0x28 - 
anstatt dass das SPE-Bit gesetzt wird, wird das MSTR-Bit gelöscht!

Danke für alle Hinweise!

von STM Apprentice (Gast)


Lesenswert?

Uli N. schrieb:
> wird das MSTR-Bit gelöscht!

Bevor du mit einzelnen Bitmanipulationen arbeitest benutze
doch die vielen LL-Macros und -Funktionen aus der STM Lib
um deine Hardware zu setzen. Bei deinen selbstgestrickten
Setzfunktionen kannst du nicht sicher sein ob du alle
Registerzugriffe immer richtig machst. Ich tu mir diese
Bit-Fummelei und deren Überprüfen jedenfalls nicht an.

CubeMx bietet auch (auch für LL) eine excellente generierte
Initialisierung an bei der dann nichts schiefgeht ....

Kann auch vorkommen dass eine bestimmte von dir gewünschte
Konfiguration nicht funktioniert weil die Mehrfachbelegung
der I/O Resourcen das gerade nicht zulässt. CubeMx würde
dir so einen Konflikt mitteilen. HAL muss ich nicht haben
aber CubeMx macht einem das Leben leichter!

von Uli N. (uln)


Lesenswert?

Das Set-Macro ist nicht selbstgestrickt, sondern ist Bestandteil der 
CMSIS!

von Michael J. (jogibaer)


Lesenswert?

Hallo,

was passiert denn, wenn Du mal

SPI1->CR1 |= (1 << SPI_CR1_SPE_Pos);

oder

SPI1->CR1 |= (SPI_CR1_SPE);

versuchst?

Jogibär

von Uli N. (uln)


Lesenswert?

Gute Idee - werd' ich am Montag gleich mal testen!
Vermute aber, dass das Ergebnis genauso sein wird, da ich mir auch schon 
den Assemler-Code angeschaut habe - den behersche ich zwar nicht, aber 
zumindest war schon mal soviel zu sehen dass zu einem Register 0x40 
"ge-or-t" und nicht
etwa 0x04 "ge-and-et" wird.

von Michael J. (jogibaer)


Lesenswert?

Hallo,

pSPI->CR1 = .. ist Absicht, oder?

Nicht das Du eigentlich pSPI->CR1 |= .. meinst.

Ansonsten doch einfach STM32CubeIDE im Debug Modus.
Da kannst Du per F5 die Befehle nacheinander durch takten.

Jogibär

PS: Bin auch kein Fan von HAL

Ist ein ODER nicht richtig?
Du willst doch zusätzlich ein Bit setzen?

0x40 ist schon Bit Nr. 6

: Bearbeitet durch User
von Uli N. (uln)


Lesenswert?

Ja in Zeile 45 ist pSPI->CR1 = .. Absicht - möchte alle Bits des 
Registers entsprechend den davor stehenden Kommentaren setzen.

Und genau das mach' ich doch - in der STM32CubeIDE im Debug Modus mit F5 
Zeile 63 ausführen - und die Bilder zeigen die SPI2-Register vor und 
nach diesem Schritt.

Ja, ein OR sollte richtig sein.

Will den SPI ohne DMA und ohne Interrupts betreiben - die Konfiguration 
der IO-Pins kann ich am Montag noch nachreichen, denke aber, das selbst 
eine noch so blöde Konfiguration selbiger keinen Einfluss auf das Setzem 
des SPI2-Enable-Bits haben sollte, eher schon eine "verquerte" 
Konfiguration des SPI-Devices selbst - und die sollte aus den paar 
Initialisierungsbefehlen ersichtlich sein.

Habe das Programm zunächst parallel in einer CubeMX- und einer 
bare-metal Version entwickelt - letzter Stand war da 28k zu 4k Code-Size 
(bisher hauptsächlich Initialisierungscode) - mit CubeMX wird letztlich 
kaum ein Bootloader und zweimal die Applikations in die 128kB Variante 
passen, deshalb jetzt die Konsentration auf die bare-metal-Version.

von Michael J. (jogibaer)


Lesenswert?

Hallo,

bei mir funktioniert es.
1
#ifdef STM32G071K
2
// Takt einschalten
3
RCC->APBENR2   |= (RCC_APBENR2_SPI1EN);
4
SPI1->CR1  |= (1 << SPI_CR1_SPE_Pos);

Das Bit SPE wird gesetzt (allerdings bei SPI1).
Eventuell liegt es dem verwendeten Prozessor.
Ich würde mir mal das Datenblatt genauer ansehen.

Mit welchen Parametern rufst Du eigentlich die spiConfig auf?
Dein SPI_TypeDef ist ja aus der .h Datei der CPU, oder?
1
typedef struct
2
{
3
  __IO uint32_t CR1;      /*!< SPI Control register 1 (not used in I2S mode),       Address offset: 0x00 */
4
  __IO uint32_t CR2;      /*!< SPI Control register 2,                              Address offset: 0x04 */
5
  __IO uint32_t SR;       /*!< SPI Status register,                                 Address offset: 0x08 */
6
  __IO uint32_t DR;       /*!< SPI data register,                                   Address offset: 0x0C */
7
  __IO uint32_t CRCPR;    /*!< SPI CRC polynomial register (not used in I2S mode),  Address offset: 0x10 */
8
  __IO uint32_t RXCRCR;   /*!< SPI Rx CRC register (not used in I2S mode),          Address offset: 0x14 */
9
  __IO uint32_t TXCRCR;   /*!< SPI Tx CRC register (not used in I2S mode),          Address offset: 0x18 */
10
  __IO uint32_t I2SCFGR;  /*!< SPI_I2S configuration register,                      Address offset: 0x1C */
11
  __IO uint32_t I2SPR;    /*!< SPI_I2S prescaler register,                          Address offset: 0x20 */
12
} SPI_TypeDef;

Ich bin hier etwas überfragt, da ich ehrlich zugeben muß,
daß ich so was bei mir bisher noch nicht genutzt habe.
Ich bin kein Profi.
Was hat es für einen Vorteil, über einen Zeiger auf SPI_TypeDef zu 
gehen?
Ich spreche die Register direkt an.

Außerdem mußt Du ja die SPI2 ansprechen (SPI2_CR1).
Wie hast Du das gelöst?

Jogibär

von Uli N. (uln)


Lesenswert?

Aufruf;
spiConfig(SPI2, 5);

Vorteil ist, dass ich den gleichen Code bei jeder SPI-Einheit verwenden 
kann, wenn ich die SPI-Einheiten in gleicher Weise nutze (hier z.B. ohne 
DMA und ohne Interrupt).

(weiter oben hätte es natürlich ~04UL und Konzentration stehen sollen 
;-)

von Michael J. (jogibaer)


Lesenswert?

Hallo,

OK, da steht SPI2.

Das kommt wohl dann alles aus der .h Datei:
1
#define PERIPH_BASE        (0x40000000UL)  /*!< Peripheral base address */
2
#define APBPERIPH_BASE     (PERIPH_BASE)
3
#define SPI2_BASE          (APBPERIPH_BASE + 0x00003800UL)
4
#define SPI2               ((SPI_TypeDef *) SPI2_BASE)

Ach so wird das zusammengeklaubt und benutzt.
Danke für die Erklärung.
Ist ein schöner Ansatz und sehr effizient.

Jogibär

: Bearbeitet durch User
von Uli N. (uln)


Angehängte Dateien:

Lesenswert?

Vor allem ist es flexibler - so hab' ich letzthin einfach den Timer 
ausgetauscht, der mein ADC-Engine antreibt und eine Timer verwendet, 
dessen Output auf einen Pin gelegt werden kann, um so Messungen mit dem 
Oszi machen zu können.

Das dritte Bild zeigt dann gleich auch, wie ich die SPI-IOs konfiguriert 
habe.

von Uli N. (uln)


Angehängte Dateien:

Lesenswert?

Und hier ist der diassemblierte Code meiner 
SPI-Initialisierungsfunktion.

von Uli N. (uln)


Angehängte Dateien:

Lesenswert?

Statt

    SET_BIT(pSpi->CR1, (1 << SPI_CR1_SPE_Pos));

einfach

    pSpi->CR1 |= SPI_CR1_SPE;

zu verwenden, ändert am Verhalten nichts.

Ich kann aber unmittelbar nach dem Einschalten der SPI-Clock das SPE-Bit 
mit

    SET_BIT(pSpi->CR1, (1 << SPI_CR1_SPE_Pos));

tatsächlich setzen!!!

Wenn ich dagegen erst das CR2-Register beschreibe und dann die 
Konfiguration von CR1 und das Einschalten des SPI-Devices mittels einer 
einzigen Instruktion durchführe,

    pSpi->CR1 =  SPI_CR1_SPE | (val << SPI_CR1_BR_Pos) | SPI_CR1_MSTR;

landet im CR1-Register letztlich wieder der Wert 0x28!

von Jan (Gast)


Lesenswert?


von Bauform B. (bauformb)


Lesenswert?

Vielleicht floatet der NSS-Pin oder ist Low; aus dem RM0440:
1
39.5.11 SPI error flags
2
Mode fault (MODF) Mode fault occurs when the master device has its
3
internal NSS signal (NSS pin in NSS hardware mode, or SSI bit in
4
NSS software mode) pulled low. This automatically sets the MODF bit.
5
Master mode fault affects the SPI interface in the following ways:
6
 • The MODF bit is set and an SPI interrupt is generated if the
7
   ERRIE bit is set.
8
 • The SPE bit is cleared. This blocks all output from the device
9
   and disables the SPI interface.
10
 • The MSTR bit is cleared, thus forcing the device into slave mode.
11
(...)
12
As a security, hardware does not allow the SPE and MSTR bits to be
13
set while the MODF bit is set.

von Uli N. (uln)


Lesenswert?

Danke für den Link - den kenne ich noch nicht!

von Uli N. (uln)


Angehängte Dateien:

Lesenswert?

Das mit dem MODF-Bit ist mal interessant!!!

Zum einen hab' ich gleich mal entdeckt, dass ich

  GPIOB->ODR     = 0x18000000UL;

statt

  GPIOB->ODR     = 0x00001800UL;

geschrieben habe und der Pin deshalb tatsächlich auf "0" gezogen wurde.

Aber auch nach Korrektur ändert sich nichts.

Vor Ausführung von Zeile 63 steht im CR1 Register 0x2C und das MODF-Bit 
ist nicht gesetzt, nach Ausführung von Zeile 63 habe ich wieder 0c28 im 
CR1-register stehen, aber es ist auch tatsächlich das MODF-Bit gesetzt.

Ich verstehe wohl irgendwas bezüglich der Bits SSM, SSI, NSSP und SSOE 
noch nicht?

Da ich Master sein möchte, habe ich SSM und SSI auf "0" konfiguriert, da 
ich meine Chip-Selects "zu Fuss" mit GPIOs mache, habe ich NSSP und SSOE 
auf "0" konfiguriert!?

von Bauform B. (bauformb)


Lesenswert?

Uli N. schrieb:
> Ich verstehe wohl irgendwas bezüglich der Bits SSM, SSI, NSSP und SSOE
> noch nicht?
>
> Da ich Master sein möchte, habe ich SSM und SSI auf "0" konfiguriert, da
> ich meine Chip-Selects "zu Fuss" mit GPIOs mache, habe ich NSSP und SSOE
> auf "0" konfiguriert!?

Den Abschnitt "39.5.5 Slave select (NSS) pin management" verstehe ich 
so, dass für deine Anwendung 2 Varianten funktionieren sollten:
entweder
a) SSM = 0, SSI = 0, SSOE = 1;
oder
b) SSM = 1, SSI = 1, SSOE = 0;
NSSP sollte in beiden Fällen egal sein (egal mit 51% für NSSP = 1)

a) wegen
1
Bit 2 SSOE: SS output enable
2
  0: SS output is disabled in master mode and the SPI interface
3
     can work in multimaster configuration
4
  1: SS output is enabled in master mode and when the SPI interface
5
     is enabled. The SPI interface cannot work in a multimaster
6
     environment.
b) wegen
1
Bit 8 SSI: Internal slave select
2
  This bit has an effect only when the SSM bit is set. The value of
3
  this bit is forced onto the NSS pin and the I/O value of the NSS
4
  pin is ignored.
STM scheint den Begriff "Pin" nicht so genau zu nehmen, ich finde b) 
ergibt mehr Sinn, wenn hier mit Pin der interne Eingang des SPI-Moduls 
gemeint ist. Verwirrung kommt auch daher, dass in einem 
Multimastersystem ein Master jederzeit Slave werden kann.

: Bearbeitet durch User
von Uli N. (uln)


Lesenswert?

Danke für den Hinweis - werde das so schnell wie möglich ausprobieren 
(momentan muss ich wieder mal was anderes machen).

von Uli N. (uln)


Angehängte Dateien:

Lesenswert?

Hab' nun Variante b implementiert, weil auch ich finde, dass b) mehr 
Sinn ergibt - und es funktioniert damit.

Allerdings kann ich nicht, wie vermutet, auf einen Schlag 48 Bits 
versenden, bei 32 Bits ist Schluss - hatte eigentlich damit gerechnet, 
dass ich, wenn ich in CR2 FRXTH=0 und DS=15 wähle, auf SPI2->DR direkt 
hintereinander drei 16bit-Worte schreiben kann, weil das erste gleich 
ins Schieberegister wandert - dem ist aber nicht so (vermutlich ist das 
Schieberegister nur 8bit breit)!

von Bauform B. (bauformb)


Lesenswert?

Uli N. schrieb:
> weil das erste gleich ins Schieberegister wandert

Vielleicht bist du auch nur zu schnell, wenn nämlich der Takt für das 
"gleich wandern" der SPI-Takt ist und nicht der APB-Takt. Je nachdem, 
wie das Verhältnis der Taktfrequenzen ist, sind das nur ganz wenige 
Zyklen, aber solange muss man irgendwie warten.

von Uli N. (uln)


Angehängte Dateien:

Lesenswert?

Hast recht - wenn ich vor Verlassen der Funktion das SR-Register mehrmal 
lese, geht die FIFO full Meldung recht schnell weg!

von Uli N. (uln)


Angehängte Dateien:

Lesenswert?

Hab' es hingekommen - mit der Sequenz
1
    pSpi->CR2 = (0 << SPI_CR2_FRXTH_Pos) | (15 << SPI_CR2_DS_Pos);
2
    for (cnt = 0; (cnt < 4); ) {                      //{ send 2 half words
3
      uint8_t temp = pDat->d08[cnt++];                
4
      pDat->d08[0] = pDat->d08[cnt++];
5
      pDat->d08[1] = temp;
6
      pSpi->DR = pDat->d16[0];
7
    }                                                 //} send 2 half words
8
    uint8_t temp = pDat->d08[4];                      //{ send 3rd half word
9
    pDat->d08[0] = pDat->d08[5];
10
    pDat->d08[1] = temp;
11
    cnt = 0;
12
    while ((pSpi->SR & SPI_SR_FTLVL_Msk) == SPI_SR_FTLVL_Msk) cnt++;
13
    pSpi->DR = pDat->d16[0];                          //} send 3rd half word
lassen sich 6 Bytes raushauen, ohne lange warten zu müssen - cnt wird, 
soweit zu sehen, maximal bis auf 3 inkrementiert. Etwas schade ist, dass 
schon unmittelbar nach dem Eintreffen des 48 Bits das OVR Bit gesetzt 
wird und nicht erst beim Eintreffen des 49 Bits, so dass die beiden 
letzten Bytes in diesem Fall leider nicht gelesen werden können.

: Bearbeitet durch User
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.