5 years, 5 months ago.

CPU freezes on second attempt to use the DMA to write to UART.

I have been trying exhaustively to program my STM32F7xx microcontroller to use DMA to transmit to UART. Three things are going on and I cannot explain or understand why this is happening, and hope somebody can help me out with this issue.

  1. In the main while loop, I am printing three interrupt status flags. These flags are set if the corresponding ISR has been called. I added this to check if the ISR was called without adding blocking statements in the ISRs. None of the interrupts, however, are called.
  2. The DMA only transmits 1 sequence of 513 bytes. When I modify the while loop in my main to only contain HAL_UART_Transmit_DMA(&handleUart4, dmxBuffer, 513);, nothing changes, the function is only called/executed once.
  3. In the while loop, I print the status of the ISR flags. After printing, the CPU stops/locks/shutdown/exits the while loop. At first, I thought I was congesting the AHB by using the UART to my terminal and the UART for the DMA controller. I disabled my terminal, and used LEDs, this didn't change anything.

Currently, I have three running hypothesis and am not sure how I can prove/disprove either one:

  • Interrupts are disabled for my microcontroller
  • The IVR table contains a different function pointer for the DMA1 Stream4 global interrupt than the function I have defined.
  • The CPU is reading/writing to SRAM while the DMA is reading from the same addresses before forwarding it to the UART. This can cause the AHB/APB1 bus to congest and cause the CPU to lockup.

What is going on here?

#include "stm32f7xx.h"
#include "mbed.h"
 
uint8_t dmxBuffer[513];
volatile bool irqA = false;
volatile bool irqB = false;
volatile bool irqC = false;
 
Serial pc(USBTX, USBRX, 115200);
 
UART_HandleTypeDef handleUart4;
DMA_HandleTypeDef handleDma;
 
void initialiseGPIO()
{
  GPIO_InitTypeDef GPIO_InitStruct;
 
  __GPIOA_CLK_ENABLE();
 
  /**UART4 GPIO Configuration
  PA0     ------> USART4_TX
  */
  GPIO_InitStruct.Pin = GPIO_PIN_0;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
  GPIO_InitStruct.Alternate = GPIO_AF8_UART4;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
 
void initialiseDMAController()
{
  /* DMA controller clock enable */
  __DMA1_CLK_ENABLE();
 
  /* Peripheral DMA init*/
  handleDma.Instance = DMA1_Stream4;
  handleDma.Init.Channel = DMA_CHANNEL_4;
  handleDma.Init.Direction = DMA_MEMORY_TO_PERIPH;
  handleDma.Init.PeriphInc = DMA_PINC_DISABLE;
  handleDma.Init.MemInc = DMA_MINC_ENABLE;
  handleDma.Init.PeriphDataAlignment = DMA_MDATAALIGN_BYTE;
  handleDma.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
  handleDma.Init.Mode = DMA_NORMAL;
  handleDma.Init.Priority = DMA_PRIORITY_MEDIUM;
  handleDma.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
  HAL_DMA_Init(&handleDma);
 
  //Define 
  __HAL_LINKDMA(&handleUart4,hdmatx,handleDma);
 
  /* DMA interrupt init */
  HAL_NVIC_SetPriority(DMA1_Stream4_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Stream4_IRQn);
}
 
void initialiseUart()
{
  __UART4_CLK_ENABLE();
 
  handleUart4.Instance = UART4;
  handleUart4.Init.BaudRate = 250000;
  handleUart4.Init.WordLength = UART_WORDLENGTH_8B;
  handleUart4.Init.StopBits = UART_STOPBITS_2;
  handleUart4.Init.Parity = UART_PARITY_NONE;
  handleUart4.Init.Mode = UART_MODE_TX;
  handleUart4.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  handleUart4.Init.OverSampling = UART_OVERSAMPLING_16;
  HAL_UART_Init(&handleUart4);
 
  /* Peripheral interrupt init*/
  HAL_NVIC_SetPriority(UART4_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(UART4_IRQn);
}
 
/* This function handles DMA1 stream4 global interrupt. */
void DMA1_Stream4_IRQHandler(void)
{
  irqA = true;
  HAL_DMA_IRQHandler(&handleDma);
}
 
/* This function handles the UART4 interups */
void UART4_IRQHandler(void)
{
  irqB = true;
  HAL_UART_IRQHandler(&handleUart4);
}
 
//HAL_UART_TxCpltCallback
/* This callback function is called when the DMA successfully transmits all scheduled bytes. */
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
  irqC = true;
}
 
 
int main(void)
{
  /* Reset of all peripherals */
  HAL_Init();
 
  //Initialise peripherals
  initialiseGPIO();
  initialiseDMAController();
  initialiseUart();
 
  //Fill buffer with test data
  for (int x = 0; x < 100; x++)
  {
      dmxBuffer[x] = x;
  }
 
  //Now instruct the UART peripheral to transmit 513 bytes using the DMA controller.
  HAL_UART_Transmit_DMA(&handleUart4, dmxBuffer, 513);
 
  while(1)
  {
        pc.printf("irqA: %d - irqB: %d - irqC: %d\r\n", irqA, irqB, irqC);
        wait_ms(100); //Wait to see if any of the interupt handlers / callback functions are called
 
        //Check if all bytes are sent, if so, retransmit
        if (irqC)
        {
            irqC = false;
            HAL_UART_Transmit_DMA(&handleUart4, dmxBuffer, 513);
        }
 
  }
}

Hi Alex - please post your code if you still need assistance.

posted by Ralph Fulchiero 29 Nov 2018

Thanks Ralph! I totally forgot to post the code.

posted by Alex van Rijs 29 Nov 2018

1 Answer

5 years, 4 months ago.

Hi Alex,

After a quick review of your code we see two possible explanations:

1) The call to HAL_UART_TxCpltCallback() only occurs if the DMA is in circular mode (and you are in normal mode). Please see:

2) We don't think you need to enable UART interrupts as the DMA is feeding the UART. This might be interfering in some way.

Have you confirmed that you are receiving data from the first DMA transfer?

Regards,

Ralph, Team Mbed

Hi Alex,

Sorry - but I jumped the gun on my response! After closer inspection, in normal mode the DMA transmit complete function is manually setting the UART transmit complete interrupt - so you should be seeing that fire (and have it enabled):

posted by Ralph Fulchiero 29 Nov 2018

Hi Ralph,

Thanks for your answer. I only just found out that you tried to answer the question. Thanks a lot!

As you suggested, I tried to disable the UART interrupt. This did not change anything. According to my debugger, the stm is stuck in an infinite loop. If I pause my IDE, the program counter points to WWDG_IRQHandler(). I do not expect that it has anything to do with the window watchdog as its the first label in the infinite loop assembly section.

I read on the internet that it could be a hard fault. As handlers are defined as weak, I tried overwriting the hard fault handler. Unfortunately, the breakpoint I set in this handler is never called as well. How can I confirm if, and if so, which hard fault is active?

posted by Alex van Rijs 11 Dec 2018