STM32 EmbeddedC Baremetal Programming

Working with USART and DMA in STM32 (ARM Cortex M3)

A step-by-step guide on how to use DMA to transmit and receive data to and from USART

Rohit Nimkar
4 min readDec 23, 2022

I/O is one of our embedded application's slowest and most time-consuming parts. The application runs the best when the CPU is given the responsibility it is meant for i.e. processing. If we can delegate the task of reading/writing from memory to reading/writing the peripherals to someone else, then we can achieve this. This is when DMA comes to our help.

DMA usart flow
DMA for USART1 Transmit mode

DMA (Direct Memory Acess)

Direct memory access (DMA) is used in order to provide high-speed data transfer between peripherals and memory as well as memory to memory. Data can be quickly moved by DMA without any CPU actions. This keeps CPU resources free for other operations.

STM32F1 has a single DMA controller with 7 channels. It also has an arbiter for handling the priority between the DMA requests.

Features

  • 7 channels with individual software triggers
  • 4 software-programmable priority levels
  • Independent source and destination transfer sizes (byte, half-word, and word)
  • Support for circular buffer management
  • 3 event flags for transfer-complete, half-transfer-complete and transfer-error
  • Programmable number of transfers: up to 65535
  • Access to Flash, SRAM, APB1, APB2 and AHB peripherals as source and destination

Transfer direction

Data can be transferred in any one of the following directions

  1. Memory to Memory
  2. Memory to Peripheral
  3. Peripheral to Memory
  4. Peripheral to Peripheral

DMA Transactions

After an event, the peripheral sends a request signal to the DMA Controller. The DMA controller serves the request depending on the channel priorities. As soon as the DMA Controller accesses the peripheral, an Acknowledge is sent to the peripheral by the DMA Controller.
The peripheral releases its request as soon as it gets the Acknowledge from the DMA Controller. Once the request is de-asserted by the peripheral, the DMA Controller releases the Acknowledge. If there are more requests, the peripheral can initiate the next transaction.
This complete flow is termed as 1 DMA Transaction. It consists of the following 3 operations:

  1. Data is loaded from the memory pointed by CPAR/CMAR register into an internal holding register
  2. Data is then transferred from the holding register to the address pointed by the memory pointed by CMAR/CPAR register according to the direction of the transaction
  3. Decrement the pending transactions counter

DMA Channels

Each channel can handle DMA transfer between a peripheral register located at a fixed address and a memory address. The amount of data to be transferred (up to 65535) is programmable. The register which contains the number of data items to be transferred is decremented after each transaction.

Programmable data sizes

Transfer data sizes of the peripheral and memory are fully programmable through the PSIZE and MSIZE bits in the DMA_CCRx register.

Pointer incrementation

The memory and peripheral counters can optionally be incremented after each transaction. This feature can be enabled/disabled using the PINC and MINC bits in the DMA_CCRx register.

Circular mode

After the counter (number of data items to be transferred) reaches 0, it is reloaded with the initial value and the process repeats itself.

Channel configuration procedure

The following sequence should be followed to configure the DMA channel. In this example, we have configured channel 4 of DMA1 to transmit data on USART1 in circular mode.

Set peripheral address

Set the peripheral register address in the DMA_CPARx register. The data will be moved to this address from the memory after the peripheral event.

Set Channel Peripheral Address register

Set memory address

Set the memory address in the DMA_CMARx register. The data will be written to or read from this memory after the peripheral event.

Set Channel Memory Address Register

Set the total no. of transactions

Configure the total number of data to be transferred in the DMA_CNDTRx register. After each peripheral event, this value will be decremented.

Set Channel Counter register

Configure channel priority

Configure the channel priority using the PL[1:0] bits in the DMA_CCRx register.

Set priority as low

Configure transaction properties

Configure data transfer direction, peripheral & memory incremented mode, and peripheral & memory data size in the DMA_CCRx register.

Direction, Size and Increment configuration

Enable circular mode and interrupts

Enable circular mode to repeat transactions continuously and enable interrupt after half and/or full transfer.

Enable Circular mode and Transfer complete Interrupt

Activate channel

Activate the channel by setting the ENABLE bit in the DMA_CCRx register. As soon as the channel is enabled, it can serve any DMA request from the peripheral connected to the channel.

Activate the channel and listen for a request

Once half of the bytes are transferred, the half-transfer flag (HTIF) is set and an interrupt is generated if the Half-Transfer Interrupt Enable bit (HTIE) is set. At the end of the transfer, the Transfer Complete Flag (TCIF) is set and an interrupt is generated if the Transfer Complete Interrupt Enable bit (TCIE) is set.

USART

The USART1 Transmitter is mapped to Channel 4 of DMA1. You can configure the USART1 peripheral by following the directions given in the following article.

Enable DMA Request for USART Transmitter

USART transmitter can be configured to send requests to DMA when the data register is empty. This can be achieved by setting the DMAT bit in the USARTx_CR3 register.

main() function

This function sets up the clock and initializes the DMA and USART1 peripherals.

as circular mode is enabled, we need not do anything after this. The DMA controller will retransmit the string continuously.

Getting the source code

The complete source code for this demo is available in the following GitHub repository. Please follow the instructions listed in the README file for building and flashing the binary on the target micro-controller.

--

--

Rohit Nimkar

Know a little about coding but aim to self employ myself from it.