Skip to main content
Explorer
August 12, 2024
Question

Transmit UART with DMA in IDLE interrupt of Receive DMA

  • August 12, 2024
  • 7 replies
  • 3545 views

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. 

    This topic has been closed for replies.

    7 replies

    Graduate II
    August 12, 2024

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

    DBrau.2Author
    Explorer
    August 12, 2024

    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. 

    Super User
    August 13, 2024

    Show relevant portions of code.

    JW

    DBrau.2Author
    Explorer
    August 13, 2024

     

    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);
    
    }
    Super User
    August 13, 2024

    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

     

    DBrau.2Author
    Explorer
    August 13, 2024

    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

    Super User
    August 13, 2024

    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

    DBrau.2Author
    Explorer
    August 13, 2024

    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.

    DBrau.2Author
    Explorer
    August 13, 2024

    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?

    Super User
    August 13, 2024

    At this point, you are dealing with details of your own system design, and I don't think I can help with that.

    JW

     

    DBrau.2Author
    Explorer
    August 13, 2024

    I can add that I have replaced DMA channel with TX of another UART and not it seems to work correctly. RX and TX on one UART use not only different DMA channels but different DMA.

    Graduate II
    August 13, 2024

    Don't write your code in HAL_UART_IRQHandler and leave it as is. I've seen other programmers do this and their code don't work correctly, which i confirmed. 

    void UART5_IRQHandler(void)
    {
     HAL_UART_IRQHandler(&huart5);
    }

    Instead do it the HAL_UARTEx_RxEventCallback callback. After some testing, i found that writing code in the callback instead, fixed the other programmers issue. See if this works for you?

    You might want to pass Size to your EngineAT_execute to help you copy the correct amount of bytes to your buffer?

    void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
    {
    	if(huart == &huart5)
    	{
    		EngineAT_execute();
    	}
    }