Vollduplex-SPI-Master mit DMA - STM32F105

Ich versuche, einen SPI-Treiber für einen STM32F105 mit DMA-Funktionalität zu schreiben. Ich verwende die Standard Peripheral Library von ST. Ich benutze keine Interrupts. Ich kann mit dem Zielgerät sprechen, wenn ich Standard-SPI-Routinen verwende , aber ich habe den DMA nicht herausgefunden ... Das Gerät ist der Flash-Speicher-IC Spansion S25FL164K .

Testweise versuche ich die Gerätekennungen auszulesen. Ich sollte ein einzelnes Byte senden und dann drei Bytes als Antwort erhalten. Da der STM32 der SPI-Master ist, muss er Taktimpulse im Wert von drei Bytes bereitstellen, um die Antwort zu erhalten. Im Allgemeinen muss das Master-Gerät drei Dummy-Bytes senden, um diese Uhr zu erstellen. Mit DMA sagen Sie ihm, wie viele Bytes er empfangen soll, und er soll die Wellenformen für Sie erstellen.

Mit meinem Code wird das erste Byte übertragen, aber es werden keine Taktimpulse für die zurückgegebenen Werte erzeugt. Dies impliziert, dass die DMA- und SPI-Peripherieuhren richtig konfiguriert sind. Ich habe online gesucht und kann (überraschenderweise) keine Beispiele, Tutorials oder App-Notizen finden, die sich mit der Sende-dann-Empfangs-Sequenzierung befassen.

Ich gehe davon aus, dass ich einfach die Reihenfolge falsch verstehe.

Für TX kann ich:

  • Aktivieren Sie das DMA-Gerät
  • Deaktivieren Sie das DMA-Gerät.
  • Aktivieren Sie das SPI-Gerät, um Signale an den DMA zu senden,
  • Deaktivieren Sie diese Signalisierung
  • DMA-Flags löschen und
  • SPI-Flags löschen.

Ich kann dasselbe separat für RX tun.

Ich habe verschiedene Permutationen zum Aktivieren / Deaktivieren dieser verschiedenen Funktionen ausprobiert. Anstatt zu versuchen, sie alle hier aufzulisten, hoffe ich, dass mir jemand die richtige Methode erklärt :) Jede Hilfe wird geschätzt; Ich habe meinen Kopf gegen diesen geschlagen.

Hier ist meine DMA-Konfiguration, gefolgt von einer der fehlgeschlagenen Iterationen der SPI-Lesefunktion:

void ConfigureDMA(void)
{
    DMA_InitTypeDef     DMA_InitStructure;

    // Enable DMA1 Peripheral Clock (SPI_DECAWAVE and SPI_BUS)
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

    // Configure SPI_BUS RX Channel
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; // From SPI to memory
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI2->DR;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryBaseAddr = 0; // To be set later
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_BufferSize = 1; // To be set later
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel4, &DMA_InitStructure);

    // Configure SPI_BUS TX Channel
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; // From memory to SPI
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI2->DR
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryBaseAddr = 0; // To be set later
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_BufferSize = 1; // To be set later
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel5, &DMA_InitStructure);

} // end ConfigureDMA()

Beim Funktionsaufruf sind CommandBuffer und DataBuffer Zeiger auf Arrays. Diese Funktion bleibt beim Warten im Abschnitt "Warten, bis die Daten empfangen werden" hängen, aber selbst wenn ich diese Überprüfung weglasse, werden die Taktimpulse immer noch nie erstellt.

void spiFlashRead(uint8_t CommandLength, const uint8_t *CommandBuffer,
        uint16_t DataLength, uint8_t *DataBuffer)
{
    // Prepare the DMA
    DMA1_Channel5->CNDTR = CommandLength;
    DMA1_Channel5->CMAR = (uint32_t)CommandBuffer;
    DMA1_Channel4->CNDTR = DataLength;
    DMA1_Channel4->CMAR = (uint32_t)DataBuffer;

    // Enable the DMAs - They will await signals from the SPI hardware
    DMA_Cmd(DMA1_Channel5, ENABLE); // TX
    DMA_Cmd(DMA1_Channel4, ENABLE); // RX

    // Activate the Flash CS
    GPIO_ResetBits(SPI_MEM_CS_GPIO, SPI_MEM_CS);

    // Enable the SPI communication to the TX DMA, which will send the command
    SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, ENABLE);

    // Wait until the command is sent to the DR
    while (!DMA_GetFlagStatus(DMA1_FLAG_TC5));

    // Wait until the transmission is completed
    while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);
    while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_BSY) == RESET);

    // Disable the TX DMA and clear DMA flags
    SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, DISABLE);
    DMA_Cmd(DMA1_Channel5, DISABLE);
    DMA_ClearFlag(DMA1_FLAG_GL4 | DMA1_FLAG_HT4 | DMA1_FLAG_TC4 | DMA1_FLAG_GL5 | DMA1_FLAG_HT5 | DMA1_FLAG_TC5);
    //NOTE: I checked the SPI OVR flag here, and it wasn't set...

    // Enable SPI communication to the RX DMA, which should receive the data
    SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx, ENABLE);

    // Wait until the data is received
    while (!DMA_GetFlagStatus(DMA1_FLAG_TC4));

    // Disable the DMAs
    DMA_Cmd(DMA1_Channel4, DISABLE); // RX
    DMA_Cmd(DMA1_Channel5, DISABLE); // TX

    // Release the Flash CS
    GPIO_SetBits(SPI_MEM_CS_GPIO, SPI_MEM_CS);

} // end spiFlashRead()

Antworten (2)

SPI erstellt die Wellenformen nicht für Sie. Es ist Vollduplex und die Master-Seite empfängt nur gleichzeitig wie sie sendet.

Der DMA fordert den Empfänger nicht auf, Daten zu erhalten, es ist umgekehrt – wenn der Empfänger zufällig etwas empfangen hat, fordert er den DMA auf, es zu übertragen.

Sie müssen wahrscheinlich sowohl Sende- als auch Empfangsarrays auf eine Länge von 4 einstellen und das erste empfangene ignorieren.

Ich habe es einmal zum Laufen gebracht, fragen Sie gerne nach mehr.

Ah ich sehe. Ich frage mich, woher meine falsche Annahme kam ... Ich werde es versuchen und sehen, was passiert :) Danke!
Danke Venny, ich konnte es jetzt zum Laufen bringen. Du bist ein Lebensretter!

Ich hatte das gleiche Problem, als ich versuchte, die SPI-Vollduplex-Übertragung mit dem DMA in beide Richtungen auf dem STM32F103 (Maple Mini) durchzuführen.

Der Empfangspuffer enthielt am Anfang ein unerwartetes Byte. Ich musste es ignorieren und die Puffergröße um ein Byte erhöhen, damit der Rx-DMA-Kanal alle empfangenen Bytes erhält. Ich habe es jedoch geschafft, die Lösung zu finden.

Im RM0008-Referenzhandbuch fand ich den Abschnitt 25.3.9 SPI-Kommunikation mit DMA (direkte Speicheradressierung) und sagte:

Beim Empfang wird jedes Mal, wenn RXNE auf 1 gesetzt wird, eine DMA-Anforderung ausgegeben. Der DMA liest dann das SPI_DR-Register (dies löscht das RXNE-Flag).

Also habe ich versucht, das RXNE-Flag auf dem SPI zu löschen, bevor die DMA-Übertragung gestartet wurde:

Empfangspuffer nicht leer (RXNE)

Wenn es gesetzt ist, zeigt dieses Flag an, dass es gültige empfangene Daten im Rx-Puffer gibt. Es wird gelöscht, wenn SPI_DR gelesen wird.

Glücklicherweise hat dies geholfen und ich konnte saubere Daten ohne Müll erhalten.