cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F722RE UART DMA RX not working properly

Grozea.Ion
Associate II

Hello all,

I have an STM32F722RETx that is receiving data on UART from an ESP8266. This data is variable in length and I use DMA + IDLE interrupt to know when to process it. When debugging the code I can only see the first byte in the buffer as no other bytes are received (the ESP is sending the correct data).

void USART3_IRQHandler(void)
{
  HAL_UART_IRQHandler(&huart3);
	if(__HAL_UART_GET_FLAG(&huart3, UART_FLAG_IDLE))	{
		__HAL_UART_CLEAR_IDLEFLAG(&huart3);
		HAL_UART_RxCpltCallback(&huart3);
	}
}

The UART is initialize using

void MX_USART3_UART_Init(void)
{
  huart3.Instance = USART3;
  huart3.Init.BaudRate = 115200;
  huart3.Init.WordLength = UART_WORDLENGTH_8B;
  huart3.Init.StopBits = UART_STOPBITS_1;
  huart3.Init.Parity = UART_PARITY_NONE;
  huart3.Init.Mode = UART_MODE_TX_RX;
  huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart3.Init.OverSampling = UART_OVERSAMPLING_16;
  huart3.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  huart3.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  if (HAL_UART_Init(&huart3) != HAL_OK)
  {
    Error_Handler();
  }
	__HAL_UART_CLEAR_IDLEFLAG(&huart3);
	if(HAL_UART_Receive_DMA(&huart3, (uint8_t *)ESP_RxBuff, ESP_RX_SIZE) != HAL_OK){
		Error_Handler();
	}
	__HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE);
}
void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{
 
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
  if(uartHandle->Instance==USART3)
  {
    PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_USART3;
    PeriphClkInitStruct.Usart3ClockSelection = RCC_USART3CLKSOURCE_PCLK1;
    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
    {
      Error_Handler();
    }
    __HAL_RCC_USART3_CLK_ENABLE();
    __HAL_RCC_GPIOC_CLK_ENABLE();
    GPIO_InitStruct.Pin = ESP_RX_Pin|ESP_TX_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF7_USART3;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
 
    hdma_usart3_rx.Instance = DMA1_Stream1;
    hdma_usart3_rx.Init.Channel = DMA_CHANNEL_4;
    hdma_usart3_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_usart3_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart3_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart3_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart3_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart3_rx.Init.Mode = DMA_NORMAL;
    hdma_usart3_rx.Init.Priority = DMA_PRIORITY_LOW;
    hdma_usart3_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_usart3_rx) != HAL_OK)
    {
      Error_Handler();
    }
    __HAL_LINKDMA(uartHandle,hdmarx,hdma_usart3_rx);
 
    hdma_usart3_tx.Instance = DMA1_Stream3;
    hdma_usart3_tx.Init.Channel = DMA_CHANNEL_4;
    hdma_usart3_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_usart3_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart3_tx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart3_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart3_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart3_tx.Init.Mode = DMA_NORMAL;
    hdma_usart3_tx.Init.Priority = DMA_PRIORITY_LOW;
    hdma_usart3_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_usart3_tx) != HAL_OK)
    {
      Error_Handler();
    }
 
    __HAL_LINKDMA(uartHandle,hdmatx,hdma_usart3_tx);
 
    HAL_NVIC_SetPriority(USART3_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(USART3_IRQn);
  }
}

The callback function is

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
{
  if(UartHandle->Instance == USART3)
  {
		while (huart3.gState == HAL_UART_STATE_BUSY_TX){ }	
		//Stop DMA	
		HAL_UART_DMAStop(&huart3);
		//Determine how many items of data have been received/
		uint8_t data_length = ESP_RX_SIZE - huart3.hdmarx->Instance->NDTR;
		//process the message
		if(data_length){
			processMsg(data_length);
		}
		memset(ESP_RxBuff, 0, data_length);
		//__HAL_UART_CLEAR_FLAG(&huart3, UART_CLEAR_IDLEF);
		HAL_UART_Receive_DMA(&huart3, (uint8_t *)ESP_RxBuff, ESP_RX_SIZE);
		__HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE);
	}
}

The issue is that the ESP_RxBuff is showing only one byte received and i do not understand why.

I have also tried to place the buffer in SRAM1 using the below scatter file and declaring the buffer

uint8_t ESP_RxBuff[ESP_RX_SIZE] __attribute__((section(".sram1")));

LR_IROM1 0x08000000 0x00080000  {    ; load region size_region
  ER_IROM1 0x08000000 0x00080000  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
   .ANY (+XO)
  }
;  RW_IRAM1 0x20000000 0x00040000  {  ; RW data
;   .ANY (+RW +ZI)
;  }
  RW_DTCM 0x20000000 0x00010000 {    ; 64 kB
    .ANY (+RW +ZI)                   ; RW data
    *(.dtcm)
  }
  RW_SRAM1 0x20010000 0x00002C000 {  ; 176 kB
    .ANY (+RW +ZI)                   ; RW data
	*(.sram1)
  }
}

In main.c the stating code is

/* Enable I-Cache---------------------------------------------------------*/
  SCB_EnableICache();
  /* Enable D-Cache---------------------------------------------------------*/
  SCB_EnableDCache();
  /* MCU Configuration--------------------------------------------------------*/
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
  /* USER CODE BEGIN Init */
  /* USER CODE END Init */
  /* Configure the system clock */
  SystemClock_Config();
  /* USER CODE BEGIN SysInit */
	//TIM4 - used to measure time between photogates events in Fast Sensor Mode
	//TIM7  - used to send battery values at regular intervals to android app
  /* USER CODE END SysInit */
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_SPI1_Init();
  MX_USART3_UART_Init();
  MX_ADC1_Init();
  //MX_TIM4_Init();
  MX_TIM7_Init();
  /* USER CODE BEGIN 2 */

By placing the buffer in SRAM1 there is no data coming into the buffer, only if the buffer is placed in DTCM I can see the first byte.

4 REPLIES 4
Karl Yamashita
Lead II

You wrote too much information on why it's not working. I don't even know where to start

If you find my answers useful, click the accept button so that way others can see the solution.
Grozea.Ion
Associate II

Hi,

I have added the init code for UART+DMA, the main function relevant content, the interrupt + callback.

Because on cortex M7 seems to be a general issue with DMA and memory coherency I have tried to move the buffer from DTCM where is stored by default in SRAM1 to see if i will solve the issue, and this did not worked.

I have removed the processMsg as it brings no relevant information to my question.

Grozea.Ion
Associate II

I have found the issue and solved the problem but I do not understand yet the root cause.

I had to disable the Overrun in the Advanced Features, I noticed that I was getting ORE bit set.

huart3.AdvancedInit.OverrunDisable = UART_ADVFEATURE_OVERRUN_DISABLE;

According to reference manual "An overrun error occurs when a character is received when RXNE has not been reset. Data can not be transferred from the shift register to the RDR register until the RXNE bit is cleared. The RXNE flag is set after every byte received. An overrun error occurs if RXNE flag is set when the next data is received or the previous DMA request has not been serviced"

Karl Yamashita
Lead II

If you want to use DMA with idle interrupt then you need to use HAL_UARTEx_ReceiveToIdle_DMA and HAL_UARTEx_RxEventCallback

Also, you should never process UART messages inside an interrupt as you're going to miss new messages during that time. Set a flag and process it in the main while loop.

Check this post I replied to which uses the above mentioned functions, saves the messages to a ring buffer and processing the messages outside of the interrupt.

https://community.st.com/s/question/0D53W00002CeLaOSAV/cannot-receive-full-string-on-uart-receive-st32h745-disco-board

If you find my answers useful, click the accept button so that way others can see the solution.