cancel
Showing results for 
Search instead for 
Did you mean: 

STM32L476RG UART TX proccess via DMA without UART interrupt

volkan
Associate II

Hello Friends,

I have a problem about Uart send via DMA. I had created a configuration via cube. the configuration has Uart2 Rx on DMA1 channel6 and Uart2 Tx on DMA1 channel7. Both of DMA channels are in normal mode. Uart2 interrupts are closed. My MCU platform is Nucleo-L476RG. I use hercules for uart tx rx tests. My aim is to send and receive messages with DMA without Uart Rx and Tx interrputs. I have to use HAL library but I don't like it, I'm used to using CMSIS library :(

I can receive messages many times from terminal without uart receive interrupt, but I can send message only once. When I try to send another message to terminal, there is no happen. I checked it in debug mode, I saw that HAL_UART_STATE_READY was busy in stm32l4xx_hal_uart.c file. I would like to point that message that was sent only once after reset can sent completely. HAL_UART_TxCpltCallback doesn't run when DMA send proccess is complete. But DMA1_Channel7_IRQHandler run two times (transfer half and full complete).

My second question is to receive message with DMA but I don't want to get interrupt half complete transfer. I want to get full trasfer interrupt only. I found HTIE flag in CCR register in datasheet and close it after HAL_UART_Receive_DMA function. Is it correct approach for this?

You can see my part of code in below.

/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
 
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <string.h>
#include <stdio.h>
 
/* USER CODE END Includes */
 
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
 
/* USER CODE END PTD */
 
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define Uart_Print(__message)   HAL_UART_Transmit(&huart2,(uint8_t *)__message,strlen(__message),10)
/* USER CODE END PD */
 
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
 
/* USER CODE END PM */
 
/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart2;
DMA_HandleTypeDef hdma_usart2_rx;
DMA_HandleTypeDef hdma_usart2_tx;
 
/* USER CODE BEGIN PV */
uint8_t UART2_rxBuffer[12] = {0};
uint8_t UART2_txBuffer[] = {"volkan benzer\n\0"};
/* USER CODE END PV */
 
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_USART2_UART_Init(void);
/* USER CODE BEGIN PFP */
 
/* USER CODE END PFP */
 
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
 
/* USER CODE END 0 */
 
/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
	uint32_t waitTimer = 0;
  /* USER CODE END 1 */
 
  /* 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 */
 
  /* USER CODE END SysInit */
 
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */
	
	
 
	HAL_UART_Receive_DMA (&huart2, UART2_rxBuffer, 12);
	
	hdma_usart2_rx.Instance->CCR &= 0xFFFFFFFB;						//close half complete interrupt
  /* USER CODE END 2 */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    if((HAL_GPIO_ReadPin(B1_GPIO_Port, B1_Pin) == 0) && (waitTimer == 0))
		{
			HAL_UART_Transmit_DMA (&huart2, UART2_txBuffer, strlen((char *)UART2_txBuffer));			
			waitTimer = HAL_GetTick();		
		}
		else
		{
			if(waitTimer != 0) 
			{
				if(HAL_GetTick() > (waitTimer + 100))
					waitTimer = 0;
			}
		}
		/* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}
 
/* USER CODE BEGIN 4 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	HAL_UART_Receive_DMA(&huart2, UART2_rxBuffer, 12);
	Uart_Print("Rx CallBack\n\0");
}
 
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
	HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
}
/* USER CODE END 4 */

void DMA1_Channel6_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Channel6_IRQn 0 */
 
  /* USER CODE END DMA1_Channel6_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_usart2_rx);
 
  /* USER CODE BEGIN DMA1_Channel6_IRQn 1 */
	hdma_usart2_rx.Instance->CCR &= 0xFFFFFFFB;
	Uart_Print("Rx Kesme geldi\n\0");
  /* USER CODE END DMA1_Channel6_IRQn 1 */
}
 
/**
  * @brief This function handles DMA1 channel7 global interrupt.
  */
void DMA1_Channel7_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Channel7_IRQn 0 */
  /* USER CODE END DMA1_Channel7_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_usart2_tx);	
  /* USER CODE BEGIN DMA1_Channel7_IRQn 1 */
	//HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
  /* USER CODE END DMA1_Channel7_IRQn 1 */
}

11 REPLIES 11
TDK
Guru

There are many examples of how to use UART with DMA in the CubeMX repository. Here is one:

https://github.com/STMicroelectronics/STM32CubeL4/blob/5e1553e07706491bd11f4edd304e093b6e4b83a4/Projects/NUCLEO-L452RE/Examples/UART/UART_TwoBoards_ComDMA/Src/main.c

If you feel a post has answered your question, please click "Accept as Solution".
tsuna
Associate II

Before the next send

HAL_UART_DMAStop (&huart2);

HAL_UART_Abort (&huart2);

Must be executed. This will change as follows.

  huart-> gState = HAL_UART_STATE_READY;

  huart-> RxState = HAL_UART_STATE_READY;

  huart-> ReceptionType = HAL_UART_RECEPTION_STANDARD;

To know the end of DMA transmission USART_ISR_TXE is good to monitor.

T J
Lead

Tx only ?

This is non blocking code

this may be too much for you...

too complicated for normal folk.

but this will run at full tilt, without any code added to the HAL RxTx interrupts

use Tx DMA as Normal Buffer

and Rx DMA as Circular Buffer

#define URxSize 0x0100  // 256 bytes
#define UTxSize 0x2000 //   8k      // can be lowered if you use USB only, Uart serial buffers cant cope with data overrun.
 
 
void initSerialStreams()    //initialiseStreams
{
    
    for (int i = 0; i < Streams; i++)
        U[i].uart = 0;         // clear all stream devices
 
    U[0].uart = 1;            //USB port;
    U[1].uart = &huart1;
//    U[2].uart = &huart2;
//    U[3].uart = &huart5;
 
// Zero DataBuffers on startup  
// not really needed but good to check when installing this code.
// can use pattern fill like 0xA5
 
        int bufLength = (Streams * (URxSize + UTxSize)) >> 2;         //4 bytes to a DWORD         
        uint32_t * ptr = URxBuf;         // buffer start
        while(!(--bufLength < 0)) 
            *ptr++ = 0;        // or 0xA5
        
    
    for (int i = 0; i < Streams; i++)
    {
        U[i].RxBufSize = URxSize;
        U[i].TxBufSize = UTxSize; 
        U[i].TxBuf = (char*) UTxBuf + i*UTxSize;
        U[i].RxBuf = (char*) URxBuf + i*URxSize;     
        
        U[i].TxPtrIN = 0; 
        U[i].TxPtrOUT = 0;
        U[i].RxPtrIN = 0; 
        U[i].RxPtrOUT = 0; 
        U[i].TxAlmostFull = false;
        U[i].TxBufHasData  = false;
        U[i].checkReInitialisation = false;
        if (U[i].uart > 20)    // not including the USB ports
            initUartRxDMA(i);
        
    }
}

this code was too long :(

T J
Lead
// *********** Configure Stream Buffers ************** //
typedef struct
{
    int TxPtrIN, TxPtrOUT, RxPtrIN, RxPtrOUT;
    int TxAlmostFull, TxBufHasData;
    int TxBufSize, RxBufSize;
    char *TxBuf, *RxBuf;  
    UART_HandleTypeDef *uart;    
    uint16_t hasReceivedByte;
}SPorts;
 
SPorts U[Streams];
 
// poll this in the while (1) loop 
void CheckTxBuffers(void)    // Tx buffers are linear, not circular
{
    
    for (int i = 0; i < Streams; i++)  // port number
        {
            SPorts *thisUart = &U[i];
            if (thisUart->TxBufHasData)
            {
                    if (thisUart->uart > 20) // not USB
                    {
                        char uartState = HAL_UART_GetState(thisUart->uart);
                        if ((uartState == HAL_UART_STATE_READY) || (uartState == HAL_UART_STATE_BUSY_RX))
                        {
                            thisUart->TxBufHasData = false;                       	// sending now
           
                           //current cache buffer Length
            
                            int serialBufferLength = thisUart->TxPtrIN - thisUart->TxPtrOUT;   
                            //SCB_CleanDCache_Region(PtrOut, serialBufferLength);
                            char * PtrOut =     thisUart->TxBuf + thisUart->TxPtrOUT;
                                               
            
                            if (HAL_UART_Transmit_DMA(thisUart->uart, PtrOut, serialBufferLength) == HAL_OK)
                            {
                                thisUart->TxPtrOUT = thisUart->TxPtrIN;
                                thisUart->TxBuf[thisUart->TxPtrIN] = 0;                       // null
                            }
                            else
                            {
                                //_Error_Handler(__FILE__, __LINE__); /* Transfer error in transmission process */
                            }
 
                else
                {
                    // send to USB port
 
                    int serialBufferLength = thisUart->TxPtrIN - thisUart->TxPtrOUT;   
                    char * PtrOut =     thisUart->TxBuf + thisUart->TxPtrOUT;
                    //SCB_CleanDCache_Region(PtrOut, serialBufferLength);
                                
                                   
#ifdef installUSB
                    reportUSBTxisBusy = true;
                    char CDCBusy = true;
                    uint8_t FSCDCState = checkFSCDCComplete();
                    if (!FSCDCState)    // not busy
                        {
                            uint16_t currentBufferLength =  serialBufferLength;
                            const int maxPacketSize = 256;
                            if (currentBufferLength)
                            {           
                                
                                if(currentBufferLength < 8192)   //CDC_DATA_FS_MAX_PACKET_SIZE is 64 ?)
                                {          
                                    if (currentBufferLength < maxPacketSize) {
                                        CDCBusy = CDC_Transmit_FS(thisUart->TxBuf + thisUart->TxPtrOUT, currentBufferLength);
                                        if (!CDCBusy)  //success
                                        {                                        
                                            thisUart->TxPtrOUT += currentBufferLength;
                                            thisUart->TxBufHasData = false;
                                            
                                            if (thisUart->TxPtrOUT == thisUart->TxPtrIN) 
                                            {
                                                thisUart->TxPtrOUT = 0;
                                                thisUart->TxPtrIN = 0;                                
                                            }
                                        }
                                    }
                                    else 
                                    {
                                        CDCBusy = CDC_Transmit_FS(thisUart->TxBuf + thisUart->TxPtrOUT, maxPacketSize);
                                        if (!CDCBusy)
                                        {
                                            thisUart->TxPtrOUT += maxPacketSize;
                                            if (thisUart->TxPtrOUT == thisUart->TxPtrIN) {
                                                thisUart->TxBufHasData = false;      
                                                
                                                thisUart->TxPtrOUT = 0;
                                                thisUart->TxPtrIN = 0;                                
                                            }
 
                                        }
                                    }                       
                                }
                                else
                                {
                                    int length =  sprintf(thisUart->TxBuf, "\ndumped oversize frame\e[K\n");
                                    CDCBusy = CDC_Transmit_FS(thisUart->TxBuf, length); 
                                    if (!CDCBusy)
                                    {
                                        thisUart->TxPtrOUT = thisUart->TxPtrIN;
                                        thisUart->TxBufHasData = false;                       	// sending now
                                                    
                                    }
                                }
                            }                            
                        }   
                    
#endif
 
                        }
               }
      }
}
 
 
// add to print buffer;
 
int putsPort(int port, char * ptr2)     // port 1 is uart1
{
    const char*ptr =  ptr2;
    const int DefaultPort = 0;        // USB port    
    int length = 0;
 
    {
        if (port >= Streams)
            port = DefaultPort;
    
        // check String has data
        // does the first element have a char ?
        if(ptr[length]) {
            checkTxDMA(port);         // check this port's queue length and shift the queue around if needed
             
    
          SPorts *uart =   &U[port];        
          uint32_t PtrIN =  uart->TxPtrIN;    
          uint32_t ascii = 0;// =  ptr[length];
        
          do {
              // add this text
      // transfer to DMA buffer to null terminator 0x00
 // PTRin should not get to the end of the buffer
        
            ascii = ptr[length++];
              uart->TxBuf[PtrIN++] = ascii;
            
              if (PtrIN >= UTxSize)
                  PtrIN	= 0;              	
          } while (ascii) ;
        
            uart->TxBuf[PtrIN] = 0;
       
            uart->TxBufHasData = true;   
            uart->TxPtrIN = PtrIN;
          
        }
    }
    return length;
}
 
void checkTxDMA(int port) // check DMA queue length
{
    SPorts *thisUart = &U[port];
    
    thisUart->TxAlmostFull = false;    
    
    if(thisUart->TxPtrIN >= ((UTxSize * 3) / 4))		// we are about to run out of room
       thisUart->TxAlmostFull = true;
 
    if (thisUart->TxAlmostFull) {
            
        int currentBufferLength, newBufferLength;
        currentBufferLength = thisUart->TxPtrIN - thisUart->TxPtrOUT;
            
        newBufferLength = 0;
            
        if (currentBufferLength) {
            //CleanDCache_UartTxBuf(port);
            
            if (currentBufferLength < UTxSize - 2) {
                while (thisUart->TxPtrIN > thisUart->TxPtrOUT)
                    thisUart->TxBuf[newBufferLength++] = thisUart->TxBuf[thisUart->TxPtrOUT++];
 
                    
                thisUart->TxPtrOUT = 0;
                thisUart->TxPtrIN = newBufferLength;
 
            }
            else {
                // this is a fatal error
                // reset and clean up
               thisUart->TxPtrOUT = 0;
               thisUart->TxPtrIN = 0;  
 
                char errorMessage[96];
                int msg_length = 0;
               // sprintf(errorMessage, "currentBufferLength %d is too high, some of the string has been dumped  port %d,  length %d\n", currentBufferLength, port, currentBufferLength);
              //  printTW(console, errorMessage); // seperate print error message redirected to the console
 
                while (errorMessage[msg_length] > 0) {
 
                    // transfer to DMA buffer to null terminator 0x00
                    thisUart->TxBuf[thisUart->TxPtrIN++] = errorMessage[msg_length++];    
                    if(thisUart->TxPtrIN >=  UTxSize) thisUart->TxPtrIN = 0;            		
                       //SCB_CleanDCache_Region(thisUart->TxBuf, UTxSize);     
                }
            }
        }
        else {
            thisUart->TxPtrOUT = 0;
            thisUart->TxPtrIN = 0;
        }
        thisUart->TxAlmostFull = false;       
    }
}
 

T J
Lead

and this bit:

void initUartRxDMA(int port) 
{        
    UART_HandleTypeDef *huart = U[port].uart;   
    {
        int length = 0;
        if (HAL_UART_Receive_DMA(U[port].uart, U[port].RxBuf, U[port].RxBufSize) != HAL_OK) 
        {
            length += sprintf(string + length, "init Uart %d Rx DMA Buffer Failed\n", port + 1);
            HAL_StatusTypeDef reInitDMABuffers = HAL_UART_DMAStop(U[port].uart);
            HAL_Delay(5);
            if (HAL_UART_Receive_DMA(U[port].uart, U[port].RxBuf, U[port].RxBufSize) != HAL_OK)
                length += sprintf(string + length, "init Uart %d Rx DMA Buffer Failed Twice\n", port + 1);
            else
                length += sprintf(string + length, "init Uart %d Rx DMA Buffer OK!\n", port + 1);
        }
        else
            length += sprintf(string + length, "init Uart %d Rx DMA Buffer OK!\n", port + 1);
    }    
    
    HAL_Delay(10);
    //printTW(console, string); 
}

Guenael Cadier
ST Employee

Dear @volkan​ 

In order to be able to complete the DMA transfer, from UART point of view, UART IRQ should be enabled, and IRQ Handler in your it.c file should be calling HAL_UART_IRQHandler().

Here is what happens :

  • DMA Transfer complete interrupt correspond to last byte of data to be loaded in UART TX data register. But this byte is not yet sent.
  • Then UART Transmit Complete interrupt is enabled.
  • on UART TC interrupt, DMA transfer is considered as completed. Tx complete callback will be called.

In code example mentionned by @TDK​ above, you will observe :

  • UART IT enabling in hal_msp.c file (done on HAL_UART_Init() call)
  /* NVIC for USART, to catch the TX complete */
  HAL_NVIC_SetPriority(USARTx_IRQn, 0, 1);
  HAL_NVIC_EnableIRQ(USARTx_IRQn);
  • UART Interrupt processing in it.c file
void USARTx_IRQHandler(void)
{
  HAL_UART_IRQHandler(&UartHandle);
}

Hope this helps.

volkan
Associate II

Dear @Guenael Cadier​ 

Thanks for your reply. I will test it asap.

Dear Tsuna,

Thank you for your reply. I will test it asap.

volkan
Associate II

Dear Tj,

Thank you for your reply. I will test it asap.