In previous USART example, both transmit and receive will requie significant MCU invovlment in moving data in and out the buffer. This is (receiving example) what we usually perform in old days.
But now with a DMA(Direct Memory Access), a lot of processing can be offload to hardware instead. Using DMA, automated the byte transfer between USART buffer to RAM.
This is what we will be doing in this tutorial, we will be sending data out(TX) using DMA.
The sample code will read character being received and retranmist received characters back to sender using DMA. The receiving section remain as previous example, using interrupt and MCU to read the bytes from USART receiving buffer and place into dedicated variable.
Please take note this example only use DMA for TX(transmit), and not RX(receive).
Source Code Explaination
The sample code can get obtain from
here.
Code Initialisation
During code initialisation, below peripheral has to be initialised.
- GPIOA - Enable USART Tx/Rx pin and use as USART function
- USART - Enable USART block and related USART settings (e.g. baud rate, stop bits)
- DMA - Enable DMA function in USART transmission
- At the end of initilisation code , a message " Hello World" will be transmit out.
One changes on USART SFR is to enable a DMA transmit mode as shown in below:
USART1->CR3 |= USART_CR3_DMAT;
Some of the key settings in DMA includes:
- Source address: when the data come from
- Destination address: when the data should transfer to
- Transfer bytes: number of data to transfer
- Data size: Data size for each transfer : 8/16/32bits
- Transfer type: Peripheral to memory or vice versa, memory to memory
- Interrupt type: Half transfer or complete transfer
Main Loop
In the main loop, MCU will continue check if there is any data being received. If data has been recevied, then it will read the received data and perform data transmission using DMA.
The configuration of DMA block is perform in USART1_Tx().
To ensure next DMA sending is only start when last sending is complete, a variable TXBuffers.Complete is using. Before start of transmission, this flag will be turn to 0. Upon complete of data sending, an DMA interrupt will be generated, and a call back function will be called to turn this flag into 1. Before data transmission, this flag will be check and system will enter a while loop if this flag has value 0 (meaning previous transmission is still in progress).
**This may cause a problem if previous transmission has large number of data to be send while system waited and eventually timeout and discard current data. To solve this, change in such a way reading on received data only happen when last DMA send is complete.
Lastly, I found a similar tutorial on
here. This tutorial is more detail with oscilloscope measurement to prove the advantage of using DMA.