cancel
Showing results for 
Search instead for 
Did you mean: 

Transmit UART with DMA in IDLE interrupt of Receive DMA

DBrau.2
Associate III

Hello.

Could anyone explain me why when I try to transmit data by UART using DMA in IDLE interrupt  function from receive DMA on the same UART working in circular mode, once in several attempts my reception lost some data? TX and RX use different channels of DMA.

When I change Trasmit_DMA to casual Transmit in work even though the interrupt handling takes more time. 

12 REPLIES 12
######
Senior

Are you using the same buffer location for transmit and receive? Or are you somehow copying the data to another buffer before transmitting?

No, transmission use another buffer thar reception. I can add that receive buffer is a circular buffer where data is received by DMA in circular mode and parsed in IDLE interrupt to ensure that received a full message. 

It's AT command communication where I look for expected response or incoming command, parse that, move the tail and send the next command from another buffer. 

Show relevant portions of code.

JW

DBrau.2
Associate III

 

void UART5_IRQHandler(void)
{
  HAL_UART_IRQHandler(&huart5);
  USER_UART_IRQHandler(&huart5);
}
void USER_UART_IRQHandler(UART_HandleTypeDef *huart)
{
	if(RESET != __HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE))   //Judging whether it is idle interruption
	{
		__HAL_UART_CLEAR_IDLEFLAG(huart);                     //Clear idle interrupt sign (otherwise it will continue to enter interrupt)
		HAL_UART_RxIdleCallback(huart);                          //Call interrupt handler
	}
}
void HAL_UART_RxIdleCallback(UART_HandleTypeDef *huart)
{
	if(huart == &huart5)
	{
		EngineAT_execute();
	}
}
void EngineAT_execute()
{
    EngineAT_Response_fifo_update();   //copy data from circular dma receive buffer to another buffer where sync data will be parsed
    EVENT_CMDs(this, fifoBytesFilled(this->p_circle_response_async));   //parse async data from circular dma buffer
    this->nextStep(this)? EngineAT_success() : EngineAT_fail();   //parse sync data, check that and potentially transmit data by dma on uart5
    fifoResetBytes(this->p_circle_response_async, fifoBytesFilled(this->p_circle_response_async));   //move the tail in circular dma receive buffer
}
// Example of function called as nextStep
static bool st2_parse_at_start(struct EAT_Proc* p_eatProc)
{
	if(wait_response(p_eatProc, (uint8_t*)OK, cleanRxbuff_Yes_Allways, 0U, 900))
		return false;
	p_eatProc->send("AT+IPR=115200\r\n");
	p_eatProc->setNextStep(st2_1_parse_at_start, NOW_OUT); //1s
	return true;
}
// Function used to transmit data
static void EngineAT_SendSimcomNBytes(size_t size, char *data)
{
    if(size > this->dma_tx_buff->size)
    {
    	DebugPrintSimcom("TOO SMALL TX BUFFER, need %d bytes\r\n", size);
    	size = this->dma_tx_buff->size;
    }
	memcpy(this->dma_tx_buff->data, data, size);
	HAL_UART_Transmit_DMA(&huart5, this->dma_tx_buff->data, size);
}
/* UART5 init function */
void MX_UART5_Init(void)
{

  huart5.Instance = UART5;
  huart5.Init.BaudRate = 115200;
  huart5.Init.WordLength = UART_WORDLENGTH_8B;
  huart5.Init.StopBits = UART_STOPBITS_1;
  huart5.Init.Parity = UART_PARITY_NONE;
  huart5.Init.Mode = UART_MODE_TX_RX;
  huart5.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart5.Init.OverSampling = UART_OVERSAMPLING_16;
  huart5.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  huart5.Init.ClockPrescaler = UART_PRESCALER_DIV1;
  huart5.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_RXOVERRUNDISABLE_INIT|UART_ADVFEATURE_DMADISABLEONERROR_INIT;
  huart5.AdvancedInit.OverrunDisable = UART_ADVFEATURE_OVERRUN_DISABLE;
  huart5.AdvancedInit.DMADisableonRxError = UART_ADVFEATURE_DMA_DISABLEONRXERROR;
  if (HAL_UART_Init(&huart5) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_SetTxFifoThreshold(&huart5, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_SetRxFifoThreshold(&huart5, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_DisableFifoMode(&huart5) != HAL_OK)
  {
    Error_Handler();
  }

}

void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{
  if(uartHandle->Instance==UART5)
  {
  /* USER CODE BEGIN UART5_MspInit 0 */

  /* USER CODE END UART5_MspInit 0 */
    /* UART5 clock enable */
    __HAL_RCC_UART5_CLK_ENABLE();

    __HAL_RCC_GPIOC_CLK_ENABLE();
    __HAL_RCC_GPIOD_CLK_ENABLE();
    /**UART5 GPIO Configuration
    PC12     ------> UART5_TX
    PD2     ------> UART5_RX
    */
    GPIO_InitStruct.Pin = GPIO_PIN_12;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF5_UART5;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_2;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF5_UART5;
    HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

    /* UART5 DMA Init */
    /* UART5_RX Init */
    hdma_uart5_rx.Instance = DMA2_Channel1;
    hdma_uart5_rx.Init.Request = DMA_REQUEST_UART5_RX;
    hdma_uart5_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_uart5_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_uart5_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_uart5_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_uart5_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_uart5_rx.Init.Mode = DMA_CIRCULAR;
    hdma_uart5_rx.Init.Priority = DMA_PRIORITY_HIGH;
    if (HAL_DMA_Init(&hdma_uart5_rx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(uartHandle,hdmarx,hdma_uart5_rx);

    /* UART5_TX Init */
    hdma_uart5_tx.Instance = DMA2_Channel2;
    hdma_uart5_tx.Init.Request = DMA_REQUEST_UART5_TX;
    hdma_uart5_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_uart5_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_uart5_tx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_uart5_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_uart5_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_uart5_tx.Init.Mode = DMA_NORMAL;
    hdma_uart5_tx.Init.Priority = DMA_PRIORITY_LOW;
    if (HAL_DMA_Init(&hdma_uart5_tx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(uartHandle,hdmatx,hdma_uart5_tx);

    /* UART5 interrupt Init */
    HAL_NVIC_SetPriority(UART5_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(UART5_IRQn);
  /* USER CODE BEGIN UART5_MspInit 1 */
    __HAL_UART_ENABLE_IT(&huart5, UART_IT_IDLE);

  /* USER CODE END UART5_MspInit 1 */
  }
}

 

void MX_DMA_Init(void)
{

  /* DMA controller clock enable */
  __HAL_RCC_DMAMUX1_CLK_ENABLE();
  __HAL_RCC_DMA1_CLK_ENABLE();
  __HAL_RCC_DMA2_CLK_ENABLE();

  /* DMA interrupt init */
  /* DMA1_Channel1_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 1, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
  /* DMA1_Channel2_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel2_IRQn, 1, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel2_IRQn);
  /* DMA1_Channel3_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel3_IRQn, 1, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel3_IRQn);
  /* DMA1_Channel4_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel4_IRQn, 1, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);
  /* DMA1_Channel5_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel5_IRQn, 1, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);
  /* DMA1_Channel6_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel6_IRQn, 1, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn);
  /* DMA1_Channel7_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel7_IRQn, 1, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel7_IRQn);
  /* DMA2_Channel1_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Channel1_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA2_Channel1_IRQn);
  /* DMA2_Channel2_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Channel2_IRQn, 1, 0);
  HAL_NVIC_EnableIRQ(DMA2_Channel2_IRQn);
  /* DMA2_Channel3_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Channel3_IRQn, 2, 0);
  HAL_NVIC_EnableIRQ(DMA2_Channel3_IRQn);
  /* DMA2_Channel4_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Channel4_IRQn, 2, 0);
  HAL_NVIC_EnableIRQ(DMA2_Channel4_IRQn);

}

There's lot of code executed in the UART interrupt, that's never a good aproach, although in itself may not be cause of this particular problem.

But I should've asked as the first thing, which STM32?

And what exactly are you experiencing?

> my reception lost some data

So it's not the Tx DMA which is problematic?

What are exactly the symptoms, how do you detect them?

JW

 

Yeah, I know that it's quite a lot of operations in interrupt handler, but it works if I transmit without DMA when it takes more time.

I should get response from my modem after every command I send. For example I send command to set baudrate AT+IPR=115000 and get OK, wen I send SMS by AT+CMGS="AAAA" and I get +CMGS: 4 and OK.

So once in a while when I send for example CMGS I don't receive response. I'm sure it is send by simcom because before using IDLE interrupt I hadn't had that problem.  I've checked the receive DMA buffer and the message is really not there. 

And it works when I turn off DMA on transmission.

The CPU I use is STM32G474VE

That you (and by "you" I mean the program in 'G4) don't receive "OK" from modem may mean also that the modem hasn't received your query (i.e. you did not transmit it or did not transmit it completely). You should be logging/sniffing both lines using e.g. a UART/USB converter and a terminal program in PC (or if you want to get fancy, using a LA or oscilloscope capable of decoding UART) to see what really happens. I suspect, the latter (i.e. you don't transmit properly).

Is the cascade in the Rx interrupt the *only* place from where transmission/Tx DMA is initiated?

JW

DBrau2_0-1723542263132.png

Thanks, it may be true and I don't know why I didn't think of that. Here is the example of error. In fact now I see that if that message had been sent the modem would not have been blocked because it's all data it need to response. Below there is CME ERROR from modem which tell that message was correctly handled. 

Unfortunately, I don't currently have a soldering iron on hand to connect directly to the line.

Data are transmitted also in main loop if there is defined delay in sending command but the code is written to send simultaneously only one message, wait for response and then send next. Also every RX and TX message is forwarded to debug uart which print screen is visible above.

Could the cause be that RX dma has higher priority than TX and as visible on print screen in time of sending data in SMS there is another incoming message +IPCLOSE which  break transmission?