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!
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!
Das Set-Macro ist nicht selbstgestrickt, sondern ist Bestandteil der CMSIS!
Hallo, was passiert denn, wenn Du mal SPI1->CR1 |= (1 << SPI_CR1_SPE_Pos); oder SPI1->CR1 |= (SPI_CR1_SPE); versuchst? Jogibär
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.
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
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.
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
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 ;-)
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
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.
Und hier ist der diassemblierte Code meiner SPI-Initialisierungsfunktion.
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!
Warum nicht mal die Beispiele angucken? https://github.com/STMicroelectronics/STM32CubeG4/tree/master/Projects/NUCLEO-G474RE/Examples_LL/SPI
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. |
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!?
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
Danke für den Hinweis - werde das so schnell wie möglich ausprobieren (momentan muss ich wieder mal was anderes machen).
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)!
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.
Hast recht - wenn ich vor Verlassen der Funktion das SR-Register mehrmal lese, geht die FIFO full Meldung recht schnell weg!
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.