Forum: Mikrocontroller und Digitale Elektronik STM32H743ZI - SPI DMA HAL Interrupt Probleme


von Dabidududuu (Gast)


Lesenswert?

Ich hab leider erneut Probleme bei der Peripherie-Konfiguration des 
STM32H7. Diesmal ist ist es SPI DMA welches nicht funktioniert.
Ich versuche gesteuert von einem Falling-Edge Interrupt eines GPIOs 
einen SPI DMA Transmit/Receive auszulösen.

Dazu meine GPIO-Konfiguration:
1
  __HAL_RCC_GPIOD_CLK_ENABLE();
2
3
  GPIO_InitTypeDef drdy_typedef;
4
  drdy_typedef.Pin = GPIO_PIN_15;
5
  drdy_typedef.Mode = GPIO_MODE_IT_FALLING;
6
  drdy_typedef.Pull = GPIO_NOPULL;
7
  HAL_GPIO_Init(GPIOD, &drdy_typedef);
8
9
  /* EXTI interrupt init*/
10
  HAL_NVIC_SetPriority(EXTI15_10_IRQn, 0, 0);
11
  HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);

mit Interrupt:
1
  void EXTI15_10_IRQHandler(void) {
2
    // trigger
3
4
    HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);
5
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_15);
6
  }

Meine SPI-Konfiguration:
1
  spi_handle.Instance = SPI1;
2
  spi_handle.Init.Mode = SPI_MODE_MASTER;
3
  spi_handle.Init.Direction = SPI_DIRECTION_2LINES;
4
  spi_handle.Init.DataSize = SPI_DATASIZE_8BIT;
5
  spi_handle.Init.CLKPolarity = SPI_POLARITY_LOW;
6
  spi_handle.Init.CLKPhase = SPI_PHASE_2EDGE;
7
  spi_handle.Init.NSS = SPI_NSS_SOFT;
8
  spi_handle.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;
9
  spi_handle.Init.FirstBit = SPI_FIRSTBIT_MSB;
10
  spi_handle.Init.TIMode = SPI_TIMODE_DISABLE;
11
  spi_handle.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
12
  spi_handle.Init.CRCPolynomial = 0x00;
13
  spi_handle.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
14
  spi_handle.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;
15
  spi_handle.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;
16
  spi_handle.Init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
17
  spi_handle.Init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
18
  spi_handle.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE;
19
  spi_handle.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;
20
  spi_handle.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;
21
  spi_handle.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_ENABLE;
22
  spi_handle.Init.IOSwap = SPI_IO_SWAP_DISABLE;
23
24
  const HAL_StatusTypeDef ret = HAL_SPI_Init(&spi_handle);
25
  if(ret != HAL_OK) {
26
    std::cout << "[Error] HAL_SPI_Init failed!\r\n";
27
    exit(-1);
28
  }
29
30
31
  HAL_NVIC_SetPriority(SPI1_IRQn, 0, 0);
32
  HAL_NVIC_EnableIRQ(SPI1_IRQn);

und SPI DMA:
1
    HAL_DMA_MuxSyncConfigTypeDef pSyncConfig;
2
3
    __HAL_RCC_DMA1_CLK_ENABLE();
4
5
    hdma_spi1_rx.Instance = DMA1_Stream0;
6
    hdma_spi1_rx.Init.Request = DMA_REQUEST_SPI1_RX;
7
    hdma_spi1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
8
    hdma_spi1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
9
    hdma_spi1_rx.Init.MemInc = DMA_MINC_ENABLE;
10
    hdma_spi1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
11
    hdma_spi1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
12
    hdma_spi1_rx.Init.Mode = DMA_NORMAL;
13
    hdma_spi1_rx.Init.Priority = DMA_PRIORITY_LOW;
14
    hdma_spi1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
15
    if (HAL_DMA_Init(&hdma_spi1_rx) != HAL_OK)
16
    {
17
      Error_Handler();
18
    }
19
20
    pSyncConfig.SyncSignalID = HAL_DMAMUX1_SYNC_EXTI0;
21
    pSyncConfig.SyncPolarity = HAL_DMAMUX_SYNC_NO_EVENT;
22
    pSyncConfig.SyncEnable = DISABLE;
23
    pSyncConfig.EventEnable = ENABLE;
24
    pSyncConfig.RequestNumber = 1;
25
    if (HAL_DMAEx_ConfigMuxSync(&hdma_spi1_rx, &pSyncConfig) != HAL_OK)
26
    {
27
      Error_Handler();
28
    }
29
30
    __HAL_LINKDMA(&spi_handle,hdmarx,hdma_spi1_rx);
31
32
    /* SPI1_TX Init */
33
    hdma_spi1_tx.Instance = DMA1_Stream1;
34
    hdma_spi1_tx.Init.Request = DMA_REQUEST_SPI1_TX;
35
    hdma_spi1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
36
    hdma_spi1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
37
    hdma_spi1_tx.Init.MemInc = DMA_MINC_ENABLE;
38
    hdma_spi1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
39
    hdma_spi1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
40
    hdma_spi1_tx.Init.Mode = DMA_NORMAL;
41
    hdma_spi1_tx.Init.Priority = DMA_PRIORITY_LOW;
42
    hdma_spi1_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
43
    if (HAL_DMA_Init(&hdma_spi1_tx) != HAL_OK)
44
    {
45
      Error_Handler();
46
    }
47
48
    pSyncConfig.SyncSignalID = HAL_DMAMUX1_SYNC_EXTI0;
49
    pSyncConfig.SyncPolarity = HAL_DMAMUX_SYNC_NO_EVENT;
50
    pSyncConfig.SyncEnable = DISABLE;
51
    pSyncConfig.EventEnable = ENABLE;
52
    pSyncConfig.RequestNumber = 1;
53
    if (HAL_DMAEx_ConfigMuxSync(&hdma_spi1_tx, &pSyncConfig) != HAL_OK)
54
    {
55
      Error_Handler();
56
    }
57
58
    __HAL_LINKDMA(&spi_handle,hdmatx,hdma_spi1_tx);
59
60
    /* DMA interrupt init */
61
    /* DMA1_Stream0_IRQn interrupt configuration */
62
    HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 0, 0);
63
    HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn);
64
    /* DMA1_Stream1_IRQn interrupt configuration */
65
    HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 0, 0);
66
    HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);
67
    /* DMAMUX1_OVR_IRQn interrupt configuration */
68
    HAL_NVIC_SetPriority(DMAMUX1_OVR_IRQn, 0, 0);
69
    HAL_NVIC_EnableIRQ(DMAMUX1_OVR_IRQn);

Was passiert:
- EXTI15_10_IRQHandler (GPIO-Interrupt) feuert, ich starte die 
Übertragung mit:
1
    SCB_CleanDCache_by_Addr(reinterpret_cast<uint32_t*>(((uint32_t)spiTxBuffer) & ~uint32_t(0x1F)), sizeof(spiTxBuffer) + 32);
2
    SCB_InvalidateDCache_by_Addr((uint32_t*)(((uint32_t)spiRxBuffer) & ~(uint32_t)0x1F), sizeof(spiRxBuffer) + 32);
3
    const auto ret = HAL_SPI_TransmitReceive_DMA(this->spi_handle, spiTxBuffer, spiRxBuffer, BytesPerTimerInterval);
4
5
    if(ret != HAL_OK) {
6
      std::cout << "HAL_SPI_TransmitReceive failed: " << (int)ret << "\r\n";
7
    }

(Übertragung liefert "HAL_OK")

- DMA1_Stream0_IRQHandler und DMA1_Stream1_IRQHandler feuern:
1
  void DMAMUX1_OVR_IRQHandler(void) {
2
    HAL_DMAEx_MUX_IRQHandler(&hdma_spi1_rx);
3
    HAL_DMAEx_MUX_IRQHandler(&hdma_spi1_tx);
4
  }
5
  void DMA1_Stream0_IRQHandler(void) {
6
    std::cout << "DMA1 Stream 0 (RX)\r\n";
7
    HAL_DMA_IRQHandler(&hdma_spi1_rx);
8
  }
9
  void DMA1_Stream1_IRQHandler(void) {
10
    std::cout << "DMA1 Stream 0 (TX)\r\n";
11
    // Handle Interrupt
12
    HAL_DMA_IRQHandler(&hdma_spi1_tx);
13
  }

- ich überprüfe das Ergebnis:
1
  std::cout << "FIRST 4 STATUS BITS: [";
2
  std::cout << (int)( spiRxBuffer[0] >> 4 ) << "]\r\n";

Ergebnis ist 0 (sollte es eigentlich nicht sein) und hab hier wird nie 
wieder ein EXTI15_10_IRQHandler (GPIO) gefeuert, sobald ich das 
DMA_Transmit weglasse feuert er wieder ständig (mit Oszi gemessen liegt 
auch ein entsprechendes Signal an).

Warum ich das mit dem Cache gemacht hab:
https://community.st.com/s/article/FAQ-DMA-is-not-working-on-STM32H7-devices

Konkret hab ich die Buffer so global definiert:
1
__attribute__((section(".dma_buffer"))) __attribute__ ((aligned (32))) uint8_t spiTxBuffer[BytesPerInterval];
2
__attribute__((section(".dma_buffer"))) __attribute__ ((aligned (32))) uint8_t spiRxBuffer[BytesPerInterval];

Weiterhin hab ich mit dem Oszi festgestellt, dass kein SPI-Transmit 
stattfindet (also SCLK immer auf low Pegel). Bei diesem Transmit sollen 
eigentlich keine Daten übertragen sondern nur empfangen werden (deswegen 
ist der spiTxBuffer-Inhalt auch auf 0 gesetzt), allerdings sehe ich auch 
keine Daten am MISO-Pin.

Hat jemand eine Idee? Vor allem das mit dem GPIO-Interrupt verwirrt 
mich.

P.S: Alle Interrupts sind diesmal in "extern "C"" definiert, daran 
sollte es also nicht liegen (und sie feuern ja auch)

von Simi (Gast)


Lesenswert?

Hallo

Hatte letzthin auch Probleme mit dem DMA und ADC. Grund war ein HAL 
Fehler. Die Initialisierung war in der falschen Reihenfolge 
(MX_DMA_Init() und MX_XXX_Init()). Hab es gerade nicht zur Hand, kann 
nicht genau sagen wo es war...

Gruss

Simi

von Johannes S. (Gast)


Lesenswert?

ich würde sagen der DMA IRQ wird nicht korrekt benutzt (mit HAL): da 
soll man nicht die ISR selber definieren, sondern einen Callback 
registrieren, z.B.
1
  HAL_DMA_RegisterCallback(&hdma_memtomem_dma2_stream0,
2
      HAL_DMA_XFER_CPLT_CB_ID, &dmaXFerComplete);
für einen transfer complete (half complete gäbe es auch noch).
Die ISR muss da ein bisschen mehr erledigen, z.B. andere IRQ Quellen 
quittieren.

Das Cache putzen ist nicht nötig wenn man den Buffer in das CCMRAM legt, 
der wird nicht gepuffert. Das putzen ist eine aufwändige Aktion die 
viele Zyklen kostet.

Und das cout scheint doch dein Freund zu sein, warum nicht ein good old 
printf? Die iostreams sind auf dem µC nicht optimiert und kosten doch 
locker +200 kB Code. Wobei auch ein printf nicht immer in einer ISR 
erlaubt ist, bei Verwendung eines RTOS bekommst du da Faults weil auch 
printf Speicherallokationen enthält.

edit:
bzw für SPI mit DMA gibt es sowas:
1
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef * hspi)
2
{
3
    if (hspi == mySPI) {
4
       // do something
5
    }
6
}

von Dabidududuu (Gast)


Lesenswert?

Johannes S. schrieb:
> Die ISR muss da ein bisschen mehr erledigen, z.B. andere IRQ Quellen
> quittieren.

Okay, ich probier es mal so. Kann gut sein, dass dann irgendwas mit dem 
GPIO Interrupt gemacht wird. Danke!

Johannes S. schrieb:
> Und das cout scheint doch dein Freund zu sein, warum nicht ein good old
> printf?

Bin halt in C++ unterwegs und hab da die __io_putchar Funktion 
umgeschrieben damit std::cout auf den UART printet.

Johannes S. schrieb:
> Die iostreams sind auf dem µC nicht optimiert und kosten doch
> locker +200 kB Code.

Am Ende kommt das cout / printf sowieso aus den Interrupts raus, das ist 
nur um auf der Konsole zu sehen ob es läuft. +/-200kByte Code sind bei 
dem H7 egal.

von Johannes S. (Gast)


Lesenswert?

in den HAL .c Quellen ist am Anfang immer eine kurze Beschreibung welche 
Schritte z.B. für SPI mit DMA oder Interrupts nötig ist.
Einfacher ist mittlerweile den Code von CubeMX generieren zu lassen, da 
sind viele Feinheiten drin die in den kurzen Kommentaren des HAL 
Quellcodes nicht unbedingt drin stehen.
Und die iostreams, naja, das ist schon viel Holz und das Exception 
Handling zieht man sich da afaik auch mit rein. Für printf muss auch nur 
eine Funktion definiert werden, auch das erledigt CubeMX.
C++ nutze ich auch, und gleich ein OS mit C++ API: Mbed-OS. Das SPI 
läuft da zwar nicht mit DMA, aber für STM32 wird unter der Haube auch 
HAL verwendet und man es einfach in eigenen Klassen verwenden. Oder 
vorhandene um solche Features per Vererbung erweitern. Für die 
Interrupts kann man dann so nette Sachen wie CThunk verwenden um im 
Interrupt Methoden einer Instanz aufzurufen, damit kann man solche 
Sachen auch zur Laufzeit starten.
Und in den Interrupts kann ich dann eine Trace Funktion verwenden, das 
ist auch schneller als die Interruptbehandlung durch eine lahme 
Schnittstelle auszubremsen.

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.