Description

I have already written a tutorial about How to use UART in STM32 but it wasn’t a very good detailed tutorial. So today I am writing this one here. It will only cover the transmission part of UART, Reception of data will be covered in next one.

I will use all three methods to transmit serial data here i.e

  • using the poll —> HAL_UART_Transmit
  • using the interrupt —> HAL_UART_Transmit_IT
  • and using DMA —> HAL_UART_Transmit_DMA

using the POLL method

Starting with the simplest one i.e using the POLL method. The data is transmitted using blocking mode i.e the CPU will block every other operation until the data transfer is complete. This method is good to use if you are only using UART and nothing else, otherwise all other operations will be affected.

To transmit data using POLL method, simply use

....
uint8_t data[] = "HELLO WORLD \r\n";

HAL_UART_Transmit (&huart2, data, sizeof (data), 10);

toggle LED 

HAL_Delay (250);  // 250 ms delay

I am using toggle LED for you guys to better understand what happens when we try transmitting large data. This is best explained in the video, Please check it out. Also ’10’ is the timeout for the UART Tx here. At this point the data transmission is pretty quick and LED blinking rate is also constant.

Let’s try to transmit a large buffer now. I will keep the ‘timeout’ at ’10’ ms as it was before.

.....

uint8_t data[2000];

for (int i=0; i<2000; i++)
  {
    data[i] = 'x';
  }

toggle LED; 
HAL_Delay (250);
HAL_UART_Transmit (&huart2, data, sizeof (data), 10);

you will notice that LED blinking rate is constant but the complete data is not being transmitted.

This is because of the ‘timeout‘ we are using is 10 ms. Let’s increase the timeout now so that we can transfer the complete data.

.....

uint8_t data[2000];

for (int i=0; i<2000; i++)
  {
    data[i] = 'x';
  }

toggle LED; 
HAL_Delay (250);
HAL_UART_Transmit (&huart2, data, sizeof (data), 1000);  // timeout is 1sec

Now the complete data is being transmitted, but the LED blinking rate decreases. This is because the UART is transmitting  data in blocking mode and until the transfer completes, no other operation can take place. To overcome this problem we can use either INTERRUPT or DMA to transmit data.

USING the INTERRUPT

In interrupt mode, Transmission takes place in non-blocking mode or in the background. So the rest of the processes works as they should and when the data transmission is complete, a Tx Complete Callback is called where we can write instructions like “what to do after the transfer is complete?”.

You must watch the video if you want to see the working results. I can’t post them here. Check out the video at the end of the post.

..... 

uint8_t data[2000]; 

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
   HAL_UART_Transmit_IT(&huart2, data, sizeof (data));
}

main ()
{
for (int i=0; i<2000; i++) 
 { 
   data[i] = 'x'; 
 }

HAL_UART_Transmit_IT(&huart2, data, sizeof (data));

while (1)
{ 
  toggle LED;
  HAL_Delay (250);
}
}

In the above code, HAL_UART_TxCpltCallback will be called when the data transmission is complete and as you can see inside this function, I am again starting a new data transmission. This will result in continuous transmission of data and the rate of blinking will also remain constant as the data transfer takes place in non-blocking mode.

Let’s transmit data after some delay so that we can check whether the data transmitting is complete data…

NOW let’s say that we want the data to be transmitted after every 20 blinks of LED so that we can get enough time to analyze previously transmitted data before the new one comes.

..... 

uint8_t data[2000]; 
uint8_t count =0;

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
   // do nothing here
}

main ()
{
for (int i=0; i<2000; i++) 
 { 
   data[i] = 'x'; 
 }

while (1)
{ 
  toggle LED;
  HAL_Delay (250);
  
  count++;

  if ((count%20) == 0)
   {
     HAL_UART_Transmit_IT(&huart2, data, sizeof (data));
   }
}
}

Notice that I am calling HAL_UART_Transmit_IT inside the if loop, so that when 20 blinks are complete, than only the data gets transmitted. You will see that the complete data transmits after every 20 blinks.

USING the DMA

DMA also works somewhat same as interrupt, means that data transfer is in a non-blocking mode.

In DMA, when half the data gets transferred, a HALF TRANSFER COMPLETE INTERRUPT gets triggered and HAL_UART_TxHalfCpltCallback is called and when the data transfer completes, HAL_UART_TxCpltCallback is called.

Basically the idea behind this is when the second half of the data is being transmitted, we can write new data in the first half and that’s how DMA works as a circular buffer.

..... 

uint8_t data[2000]; 
uint8_t count =0;

void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart)
{
  for (int i=0; i<1000; i++)
    {
	data[i] = 'z';
    }
}



void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
    for (int i=0; i<1000; i++)
    {
	data[i] = 'y';
    }
}

main ()
{
for (int i=0; i<2000; i++) 
 { 
   data[i] = 'x'; 
 }

while (1)
{ 
  toggle LED;
  HAL_Delay (250);
  
  count++;

  if ((count%20) == 0)
   {
     HAL_UART_Transmit_DMA(&huart2, data, sizeof (data));
   }
}
}

As you can see above that when the control will enter the HAL_UART_TxHalfCpltCallback function the data in the first half of the buffer should change and this happens when the second half is being transmitted. You can see this in the images below

You can also use toggle to continuously monitor this change. Check that out in the video below.

3
Leave a Reply

avatar
3 Comment threads
0 Thread replies
0 Followers
 
Most reacted comment
Hottest comment thread
3 Comment authors
BeneditoWojciechAHAC Recent comment authors
  Subscribe  
newest oldest most voted
Notify of
AHAC
Guest
AHAC

many thanks for your help

Wojciech
Guest
Wojciech

Hi,

Thanks that’s a very nice description. Maybe you could show how to stop this transmission?

btw. looks like the loop in HAL_UART_TxCpltCallback with DMA should go from 1000 to 1999 (not 0 to 9999)

Benedito
Guest
Benedito

Hello! Thanks very much for your detailed explanation!
I’m trying to send the simbol ‘a’ to TX only for test as you did in the video, in the registers (in debug) I can see that RX receives it(I tied TX and RX), but in Hercules I don’t see anything. I open the COM port succesfully and then nothing appears. What could be the problem?

Menu