Help with DMA Controller becoming unresponsive
Hello,
I am having an issue with DMA1 getting "stuck" during operation. While DMA1 is stuck, DMA2 continues to run without any issues. I realize that this is likely a race somewhere, however I am hoping for any insight into how or why the DMA controller becomes unusable.
First some project details:
- Running on an stm32f746ig, with m7 r0p1 core
- We are using Cube HAL
- DMA1 Setup:
- Stream 4 is used in Direct Mode for SPI2 tx
- Stream 5 is used in Circular Mode for USART2 RX, flow control enabled, HalfRXCplt + RxCplt + IDLE detection, and using HAL_UART_DMAPause() when host buffers are full
- SPI2 is used for output to a display, we perform non-DMA transfers over this as well as larger DMA transfers
- USART2 is connected to another microcontroller, and we only use DMA for RX
When both SPI2 & USART2 are running at a high rate (lots of display updates over SPI2, lots of data in over USART2), eventually the DMA1 controller becomes unusable. The only way that this is detected is that I am expecting data over the USART2 Rx, and it never arrives.
The SPI2 DMA1s4 continues to run, is re-configurable, can be started / stopped and even the TxComplete callbacks are firing -- but no data is being sent out over SPI2. I can still successfully send data over SPI2, just not using the DMA controller.
The USART2 DMA1s5 is completely unusable, when I try to disable it by clearing `DMA_S5CR_EN`, it never clears and seemingly cannot be disabled. Any attempts to de-init/ re-init USART2 and its DMA stream get stuck trying to disable DMA1s5.
SPI2 Usage
The first line of the display is sent out by an RTOS task, using `SPI_Transmit_DMA()`. Subsequent lines are sent out in the `HAL_SPI_TxCpltCallback()` also using `SPI_Transmit_DMA()`.
SPI2 Initialization
SPI_HandleTypeDef lcd_spi;
lcd_spi.Instance = SPI2;
lcd_spi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
lcd_spi.Init.Direction = SPI_DIRECTION_2LINES;
lcd_spi.Init.CLKPhase = SPI_PHASE_1EDGE;
lcd_spi.Init.CLKPolarity = SPI_POLARITY_LOW;
lcd_spi.Init.DataSize = SPI_DATASIZE_8BIT;
lcd_spi.Init.FirstBit = SPI_FIRSTBIT_MSB;
lcd_spi.Init.TIMode = SPI_TIMODE_DISABLE;
lcd_spi.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
lcd_spi.Init.CRCPolynomial = 7;
lcd_spi.Init.NSS = SPI_NSS_SOFT;
lcd_spi.Init.Mode = SPI_MODE_MASTER;
HAL_SPI_Init(&lcd_spi);
DMA_HandleTypeDef spi_dma_tx;
spi_dma_tx.Instance = DMA1_Stream4;
spi_dma_tx.Init.Channel = DMA_CHANNEL_0;
spi_dma_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
spi_dma_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
spi_dma_tx.Init.MemBurst = DMA_MBURST_INC4;
spi_dma_tx.Init.PeriphBurst = DMA_PBURST_INC4;
spi_dma_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
spi_dma_tx.Init.PeriphInc = DMA_PINC_DISABLE;
spi_dma_tx.Init.MemInc = DMA_MINC_ENABLE;
spi_dma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
spi_dma_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
spi_dma_tx.Init.Mode = DMA_NORMAL;
spi_dma_tx.Init.Priority = DMA_PRIORITY_MEDIUM;
HAL_DMA_Init(&spi_dma_tx);
__HAL_LINKDMA(hspi, hdmatx, spi_dma_tx);
HAL_NVIC_SetPriority(DMA1_Stream4_IRQn, 5, 1);
HAL_NVIC_EnableIRQ(DMA1_Stream4_IRQn);USART2 Usage
DMA Rx is started in circular mode. It is being run with Flow Control enabled, and IDLE detection since we can receive any amount of data.
In the HAL_UART_RxCplt, HAL_UART_RxHalfCplt callbacks, and on IDLE interrupt, data in the dma buffer is transferred into a ring buffer that is shared with an RTOS thread. If that ring buffer is full, `HAL_UART_DMAPause()` is called until the RTOS thread drains the buffer, and then `HAL_UART_DMAResume()` is called.
USART2 Initialization
UART_HandleTypeDef huart2;
huart2.Instance = USART2;
huart2.Init.BaudRate = 2534400;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_RTS_CTS;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
HAL_UART_Init(&huart2);
HAL_NVIC_SetPriority(USART2_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(USART2_IRQn);
hdma_usart2_rx.Instance = DMA1_Stream5;
hdma_usart2_rx.Init.Channel = DMA_CHANNEL_4;
hdma_usart2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_usart2_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_usart2_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_usart2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_usart2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_usart2_rx.Init.Mode = DMA_CIRCULAR;
hdma_usart2_rx.Init.Priority = DMA_PRIORITY_MEDIUM;
hdma_usart2_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
hdma_usart2_rx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
hdma_usart2_rx.Init.MemBurst = DMA_MBURST_SINGLE;
hdma_usart2_rx.Init.PeriphBurst = DMA_PBURST_SINGLE;
HAL_DMA_Init(&hdma_usart2_rx);
__HAL_LINKDMA(&huart2, hdmarx, hdma_usart2_rx);
HAL_NVIC_SetPriority(DMA1_Stream5_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream5_IRQn);
/* Start receiving DMA */
SET_BIT(esp_uart.Instance->CR3, USART_CR3_EIE);
__HAL_UART_CLEAR_IDLEFLAG(&huart2);
SET_BIT(esp_uart.Instance->CR1, USART_CR1_PEIE | USART_CR1_IDLEIE);
HAL_UART_Receive_DMA(&huart2, dmabuf, sizeof(dmabuf));Thank you