STM32 SPI Peripheriefehler?

Ich verwende STM32F051R8T6 auf STM32F0-Discovery-Board und M25P80-Flash-IC als Peripheriegerät. Ich habe das SPI in 3-Zeilen (+ 1 Software-Chip-Select-Zeile) im Vollduplex-Modus konfiguriert und verwende Polling zum Senden und Empfangen von Daten. Ich hatte viele Probleme damit, SPI richtig zum Laufen zu bringen, und alles lief darauf hinaus, dass diese Reihe von Tests nacheinander ausgeführt wurden, die einen Fehler aufdeckten, den ich nicht erklären kann.

Schritt 1: Fordern Sie eine Signatur vom Peripheriegerät an. Signatur ist 0x13, funktioniert 99% der Zeit.

Schritt 2: Aktivieren Sie das Schreiben an das Peripheriegerät, indem Sie ihm einen Befehl ausgeben.

Schritt 3: Lesen Sie den Inhalt des Statusregisters, um sicherzustellen, dass der vorherige Befehl durchgekommen ist.

Da das Peripheriegerät im Vollduplexmodus arbeitet, aber Daten niemals gleichzeitig in beide Richtungen gesendet werden, muss der Eingangspuffer die gleichen Abmessungen wie der Ausgangspuffer haben, und in ihn geschriebene Daten haben einen gewissen Versatz.

Problem entsteht im letzten Schritt. Wenn nur ein einzelnes Byte gelesen werden soll, registriert SPI keine Daten, die an es gesendet werden! Wenn mehr Bytes angefordert werden (durch Verwendung von 1 + X als viertem Funktionsparameter), sieht der Inhalt des Eingangspuffers wie folgt aus:

@ 1 + 3:
0x00 0x00 0x02 0x13

@ 1 + 4:
0x00 0x00 0x02 0x13 0x00

@ 1 + 5:
0x00 0x00 0x02 0x13 0x02 0x00

@ 1 + 6:
0x00 0x00 0x02 0x13 0x02 0x00 0x02

@ 1 + 7:
0x00 0x00 0x02 0x13 0x02 0x00 0x02 0x02

Und hier sind die Signale auf der MISO-Leitung (rot) und SCK (gelb) beim Lesen von insgesamt 1 + 3 Bytes. Das erste Paket ist der zweite Schritt. Vom Flash-IC wird erwartungsgemäß nichts übertragen. Zweites Paket ist Schritt Nr. 3. Peripherie ist während des ersten Bytes still und gibt dann 0x02 auf allen folgenden Bytes aus, aber nur das dritte Byte wird erfolgreich gelesen. Und dann liest es die Bytes 4 und 6 nicht (alle weiteren Bytes werden korrekt gelesen).

Geben Sie hier die Bildbeschreibung ein

Ich weiß also nicht, wie ich erklären soll, dass die gelesenen Daten nicht vollständig dem entsprechen, was ich auf dem Oszilloskop sehe. Und das Seltsamste ist - im dritten Schritt wird ein 0x13-Byte gelesen! Wenn der erste Schritt entfernt wird, wird dieses Byte als 0x00 gelesen. Scheint eine Speicherbeschädigung auf STM32-Seite zu sein, aber ich kann keinen Grund dafür finden, da Puffer mit memset() bereinigt werden und der interne Puffer von SPI mit HAL_SPI_FlushRxFifo(&hspi1) geleert wird.

Hier ist der Code:

uint8_t command;
uint8_t inBuffer[16];
uint8_t outBuffer[16];

/* Step 1: Check if IC is alive */
{
  memset(inBuffer, 0x00, 16);
  memset(outBuffer, 0x00, 16);
  command = OPCODE_RES;
  outBuffer[0] = command;

  spi_select(M25P80);
  HAL_SPI_TransmitReceive(&hspi1, outBuffer, inBuffer, (1 + 3) + 1, TIMEOUT);
  spi_deselect(M25P80);

  HAL_SPI_FlushRxFifo(&hspi1);
}

for (volatile uint32_t i = 0; i < 200; i++);

/* Step 2: Enable writing */
{
  memset(inBuffer, 0x00, 16);
  memset(outBuffer, 0x00, 16);
  command = OPCODE_WREN;
  outBuffer[0] = command;

  spi_select(M25P80);
  HAL_SPI_Transmit(&hspi1, outBuffer, 1, TIMEOUT);
  spi_deselect(M25P80);

  HAL_SPI_FlushRxFifo(&hspi1);
}

for (volatile uint32_t i = 0; i < 200; i++);

/* Step 3: Make sure writing is enabled */
{
  memset(inBuffer, 0x00, 16);
  memset(outBuffer, 0x00, 16);
  command = OPCODE_RDSR;
  outBuffer[0] = command;

  spi_select(M25P80);
  HAL_SPI_TransmitReceive(&hspi1, outBuffer, inBuffer, 1 + 1, TIMEOUT);
  spi_deselect(M25P80);

  HAL_SPI_FlushRxFifo(&hspi1);
}

Einige Details zur SPI-API von STM. In einem Anruf wie:

HAL_SPI_TransmitReceive(&hspi1, outBuffer, inBuffer, (1 + 3) + 1, TIMEOUT);

&hspi1 - peripheral handle  containing its settings and configurations
outBuffer - pointer to a memory location with data to be sent
inBuffer - pointer to a memory location to which received data is written
(1 + 3) + 1 - number of bytes to receive/transmit; 
              in this case 1 - command byte, 3 - dummy bytes, 1 - received signature byte
TIMEOUT - a value in the range of 10000 which indicates at which point transmission must be 
aborted if failed to finish

EDIT 1: SPI-Initialisierungscode, wie von STM32CubeMx generiert:

hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLED;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLED;
hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLED;
HAL_SPI_Init(&hspi1);

EDIT 2: Jetzt, wo ich die Geschwindigkeit auf 488 b / s gesenkt habe, funktioniert alles einwandfrei. Ich bleibe noch eine Weile bei diesem Hack.

Dein Bus sieht ziemlich laut aus. Zur Fehlerbehebung würde ich die Busgeschwindigkeit auf ein Kriechen verlangsamen und sehen, ob die Ergebnisse konsistenter werden. Außerdem funktioniert der Read-Enable-Befehl oft nur bei der allernächsten Anweisung. Ich bin mir bei dieser speziellen Erinnerung nicht sicher, aber Sie verpassen möglicherweise Ihre Gelegenheit, indem Sie das Statusregister lesen.
Was sind Ihre Taktpolaritäts- und Phaseneinstellungen? Können Sie uns Ihren SPI-Initialisierungscode zeigen?
@bitsmack Die Geschwindigkeit liegt bereits bei 250 kHz, was so niedrig ist, wie ich gehen kann, ohne die Uhren anderer Peripheriegeräte zu stören. Ich denke, ich könnte zu Testzwecken wirklich tiefer gehen. In Bezug auf das Verpassen einer Gelegenheit zum Lesen des Registers - dies hat absolut nichts damit zu tun, da ich die gesendeten Daten auf dem Oszilloskop sehen kann. Das Problem besteht darin, dass der STM32 sie aus irgendeinem Grund nicht richtig liest.
@BruceAbbott Ich habe das OP mit dem SPI-Initialisierungscode aktualisiert.
a) Sie zeigen nicht wirklich 0x13 in Ihrer Wellenform. Sie zeigen nur Befehl + drei Dummy-Bytes, Signatur fehlt; b) Bus ist HiZ während Leerbytes, Pull-up auf MISO aktivieren; c) Überprüfen Sie Ihr Pin-Setup und Multiplexing; d) M25P80 soll Daten bei fallender Flanke austakten, Ihre Wellenform zeigt Übergänge bei steigender Flanke, machen Sie daraus, was Sie wollen; e) Sie sollten 30 us nach RES warten, um in den Standby-Modus zu wechseln (vgl. Datenblatt), bevor Sie #CS freigeben. f) Führen Sie eine READ IDENTIFICATION durch, nicht WREN, und vergleichen Sie diese Daten mit den erwarteten Werten.

Antworten (3)

Meiner Meinung nach sollten Sie die Standard Peripheral Library für STM verwenden, da sie stabiler ist und Sie nicht die Low-Level-Sachen verbirgt, die Ihnen eine kontrollierbarere Umgebung bieten. Außerdem müssen Sie beim CS-Pin-Timing vorsichtig sein, viele Geräte müssen halten diesen Wert eine Weile, bis Sie die Übertragung starten.

Befehle, Adressen oder Eingangsdaten werden an der ansteigenden Flanke des Takteingangs zwischengespeichert, während Ausgangsdaten an der abfallenden Flanke des Takteingangs herausgeschoben werden.

Nicht mit SPI, nein. Siehe RM0091 als Referenz.
Entschuldigung, das M25P80-Datenblatt sagt das. Bedeutet das, dass Sie auf 1EDGE lesen und auf 2EDGE schreiben müssen?
Clock: Das C-Eingangssignal liefert das Timing der seriellen Schnittstelle. Befehle, Adressen oder Daten, die am seriellen Dateneingang (DQ0) vorhanden sind, werden an der steigenden Flanke des seriellen Takts (C) zwischengespeichert. Daten auf DQ1 ändern sich nach der fallenden Flanke von C.
Ah, Entschuldigung, ich habe Ihre ursprüngliche Nachricht falsch gelesen. Ja, M25P80 verschiebt Daten bei fallender Flanke. Was ist Ihr Punkt?
Wenn STM32 bei steigender Flanke liest und die Daten erst bei fallender Flanke beginnen, wie lesen Sie dann eine Adresse? 2 Bytes lesen und <4??
Nun wird bei einer fallenden Flanke etwas hinausgeschoben. Bei der nächsten steigenden Flanke wird abgetastet - genau in der Mitte. Das einzige, was in dieser Konfiguration unmöglich ist, ist, dass ein Slave Daten beim allerersten Bit der Nachricht verschiebt, aber das ist etwas, was ich nicht mache. Auch hier bin ich mir nicht sicher, ob ich genau verstehe, was Sie sagen.
Das macht uns zu zweit!!

Versuchen

hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;

Das Datenblatt für Ihr Gerät sagt "und Ausgangsdaten sind ab der fallenden Flanke von C verfügbar." Ich glaube, das ist, wo Sie das einstellen würden.