STM32 UART #2 || Use Interrupt & DMA to send Data

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
Hello and welcome to controllers tech. This is the second video in the STM32 UART series,  and today we will continue sending the data. We will use the interrupt, and the DMA to send  data to the serial console on the computer. I have already covered the basic configuration  in the previous video, so I will skip that part. In today’s video, I will only focus on  sending the data using interrupt and DMA. This is the project from the previous video, and  I will continue with it in today’s video also. Let’s open the cube MX configuration file. To demonstrate the advantage  of using interrupt and DMA,   we would need to run some other process also. So I am setting the pin PA5 as the output,  which is connected to the on board LED. We will monitor this LED as some  continuous process in the code. Go to the USART 2, NVIC tab,  and enable the global interrupt. I am not making any changes to  the parameters, as we will use   the same configuration as the previous video. Let’s also enable the DMA. We are sending the data, so select the USART 2 TX. There are different DMAs available in the MCU,  and DMA 1 stream 6 got selected for this purpose. The data direction is memory to peripheral as we  are sending the data from memory to the USART. The DMA can be used in the normal  mode, or the circular mode. I will demonstrate both, but let’s  leave it to the normal mode for now. The data width is set to byte as we can  transfer one byte at a time in the UART. After sending each data byte, the memory  address will be incremented automatically,   so leave this option checked. That is all the configuration we need,  click save to generate the project. Alright here you can see the  USART 2 DMA handler got defined. Now first we will see why we need to use  the interrupt or DMA in the first place. Let’s define an array of 10 kilobytes,  which we will end via the UART. Now after everything is initialized,  fill the array with some data. We will send the data in the while loop,   so modify the UART transkit function  to send the array we defined. The timeout is set to Haal max delay, this  means the function will never timeout. Basically we are allowing the UART to take  as much time as possible to send the data. Now toggle the LED, and give  a delay of 500 milliseconds. Basically we are sending the  data in the blocking mode,   so if the data transfer takes more time, the LED  will take more than 500 milliseconds to blink. Let’s build the code now. Alright there are no errors,  so let’s flash it to the board. Here you can see the LED is definitely  taking more than 500 milliseconds to blink. This is because the UART takes a lot of  time to transfer 10 kilobytes of the data. We can see the data on the console, and it  is arriving in groups every 500 milliseconds. The LED does not blink at the required rate. Sending a large amount of data in  the blocking mode is not a good idea,   as it delays the rest of the processes. This is because the CPU needs to  wait for the transfer to complete,   before it can execute the next statement. To overcome such an issue, we use the interrupt. Let’s define a variable to keep track  if the data has been transferred. In the while loop, we will only transmit  the data if this variable is set to 1. Here call the function HAL_UART_Transmit_IT. The parameters of the function are the UART  instance, the data buffer, and the size. After calling the function, set the variable to 0. In the interrupt mode, the data  is transferred in the background   while the CPU can process the rest of the tasks. When all the data has been transferred,   an interrupt will trigger, and the  transfer complete callback will be called. Inside the callback, we can  do the rest of the processing. Here we will simply set the variable to 1, so to  inform the CPU that the data has been transmitted. Let’s also define two counters,  which will keep track of how many   times the interrupt has been called,  and the while loop has run completely. Alright let’s build and debug the project. Let me add the counters to the live expression. Here you can see the interrupt counter  is half of that of the while loop. The while loop is running  independently irrespective   of if the data has been transferred or not. You can see the LED is now  blinking every 500 milliseconds. Here you can see the data received in the console,   and the reception is now continuous compared  to what we saw in the blocking mode. Basically it is taking around 1 second  to transfer 10 kilobytes of data,   and this is why the interrupt  counter is half of the while loop. Let me change the blink delay to 1  second, and debug the project again. Now you see both the counters are  incrementing at the same rate. The LED now blinks every 1 second. The data transfer is still continuous, and it  does not take 1 second delay into consideration. This is because the data is  transferred in the background,   and by the time the CPU waits for the HAAL  delay to finish, the data is transferred. Then the loop calls for the transfer again. So we were able to transfer  the data using interrupt,   and the CPU still managed the  LED blinking at the defined rate. We can always use interrupt to transfer the  data, but the transfer is still done by the CPU. So when transferring a large amount of  data, it puts too much load on the CPU. This is where the DMA comes in. DMA stands for Direct Memory Access  Controller, and it is used to provide   high-speed data transfers between peripherals  and memory, and between memory and memory. Data can be quickly moved by  the DMA without any CPU action,   This keeps CPU resources  free for other operations. We will use the function HAL_UART_Transmit_DMA,  to send the data via the DMA. The parameters are the same  as the interrupt function. Once the data has been transferred, the UART  transmit complete callback will be called,   and here we will set the variable to 1. We basically use the same setup  that we used for the interrupt. Let’s build and debug the project now. Here you can see the counters are incrementing  just as they were during the interrupt. The LED is blinking every 1 second,  so the process is running fine. We are receiving data continuously,  just as we were using the interrupt. I don’t have the means to show the CPU  load, but it is reduced while using the DMA. Now while configuring the DMA we came across the  DMA modes, the normal mode and the circular mode. Right now the DMA is configured in  the normal mode, and in this mode the   DMA does not reload the address  after the transfer is complete. Basically it transfers the data once, and after  the data has been transferred, the DMA stops. Let me show this by calling the transfer  function outside the while loop. Let’s build and debug the project. Here you can see the interrupt  counter only incremented once,   and the while loop continues to run. This is because the DMA transfers the data  once, so the interrupt is only triggered once. Here you can see the serial console received  10 kilobytes of data, and that’s it. Now let’s keep the same code,  go to the cube MX configuration,   and change the DMA mode to circular. Let’s build and debug the project again. You can see the interrupt counter is increasing  at a faster rate than the while loop. Basically the DMA is continuously sending the  same data, and the rate is faster than 1 second. This is why the counter is  increasing at a faster rate. In the circular mode, The source and the  destination addresses, and the number of   data to be transferred are automatically  reloaded after the transfer completes. So the DMA continues sending the same data to  the same address, and from the same address. It does that in the background,  and without affecting the CPU load. Now I will show how to use the circular mode  more effectively, and how to stop the DMA. Just like the transfer complete callback, we  also have a half transfer complete callback. This callback is triggered when the DMA  finishes transferring half the data. We will utilize this callback in our code. We are transferring 10 kilobytes of data,   so half transfer complete callback will be  called when DMA has transferred 5 kilobytes. Basically it will be called when  the first half has been transferred. Inside this callback we can load new  data to the first half of the buffer,   while the DMA is sending the second half. Similarly, when the second half is transferred,  the transfer complete callback will be called. The DMA is in the circular mode, so it  is transferring the first half again,   and during that time, we will load new  data to the second half of the buffer. Basically when the DMA transfers the second  half, we update the data in the first half,   and when it transfers the first half,  we update the data in the second half. This process will continue till  we issue a stop instruction. We can set some condition, and  once the condition is satisfied,   call the function UART_DMA_Stop to stop the DMA. In the main function, we still  need to send all the data at once,   so everything can start from there. Alright let’s build and debug the project. You can see the new data is  being received by the console. And the DMA stops in the end. The interrupt counter has been stopped  while the loop counter continues to count. The data was loaded as we programmed it to do. We can use the DMA in circular  mode to transfer large data,   whereas we can still have smaller buffers  and load the new data in the runtime itself. This is it for the video. I hope you understood the use of interrupt and  DMA while transferring the data via the UART. You should use the different  modes as per the data requirement. Use blocking mode for small  data, interrupt for more data,   and use DMA when you have a  lot of data to be transferred. I will continue with this series in the next  video, and we will see the receiving part. You can download the code from  the link in the description. Leave comments in case of any doubt. Keep watching, and have a nice day ahead.
Info
Channel: ControllersTech
Views: 3,516
Rating: undefined out of 5
Keywords: stm32, stm32f4, f103, discovery, nucleo, stm32f7, stm32G, cubeIDE, sensor, module, tutorial, example, can, i2c, spi, uart, learn, tutorials, usart, tx, rx, pin, parameter, configure, configuration, send, data, serial, transmit, receive, pc, computer, connection, device, console, print, 115200, 9600, baud, rate, word, length, number, string, cubeMX, oversampling, virtual, com, port, stm32l, interrupt, dma, complete, normal, circular, callback, half, DMA, IT, hal
Id: JaMwNT0m3Sw
Channel Id: undefined
Length: 12min 59sec (779 seconds)
Published: Sat Jan 06 2024
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.