Reducing load on CPU by using DMA | STM32F4 | DMA | CMSIS

Direct Memory Access

What is Direct Memory Access? In embedded systems like the STM32F401 microcontroller, Direct Memory Access (DMA) is a feature that enhances the efficiency of data transfers between peripherals and memory without involving the CPU. DMA controllers are hardware blocks designed to offload data transfer tasks from the CPU, thereby reducing its workload and improving overall system performance.

DMA operates by allowing peripherals such as ADCs, DACs, UARTs, and memory controllers to directly access the system's memory, either for reading data from memory or writing data to memory. This direct access eliminates the need for the CPU to intervene in each data transfer, making it particularly beneficial for high-speed data movement operations.

If you want to read more about DMA, checkout this blog on embedded.com on Direct Memory Access.

DMA Registers

The DMA peripheral uses multiple registers for Interrupts, Configuration, and other peripheral-specific functionalities. The ones used in this blog are:

  1. DMA High Interrupt Flag Clear Register (DMA_HIFCR)
  2. DMA Stream X Configuration Register (DMA_SxCR)
  3. DMA Stream X Number of Data Register (DMA_SxNDTR)
  4. DMA Stream X Peripheral Address Register (DMA_SxPAR)
  5. DMA Stream X Memory 0 Address Register (DMA_SxM0AR)
  6. USART Control Register 3 (USART_CR3)
Additionally, we will use the USART Control Register 3 to set the DMA mode for Transmission. 

Configuration

What we are trying to accomplish here is, to transfer a huge chunk of data from Memory to a Peripheral (USART2) using DMA, while the CPU is engaged in Turning ON and OFF the USER LED on the Nucleo Development Board at an interval of 500 milliseconds. We will be configuring the USART2 at a baud rate of 9600. Since we will be using the USART2 Tx as the Peripheral Data Register, we have to choose DMA1, since it is connected to the AHB/APB2 Bridge and the USART2 is on the APB2 Bus. USART2 Tx is on the Stream 6 Channel 4 on the DMA1 peripheral.

Walkthrough

Function Headers


We have declared function headers for Initializing the DMA peripheral, Configuring the DMA Stream and Channel, a function to start the DMA Transfer, and an Interrupt Handler to service when the Transfer is Complete. We have also declared function headers for Initializing, and Turning ON & OFF the USER LED.

Dummy Data



Declare a character array and fill it with dummy data. I took a paragraph from the Wikipedia page on Direct Memory Access for this. Then, store the length of the character array on a 16-bit integer. Note that the DMA_SxNDTR register takes values from 0 to 65535, that is, the DMA peripheral can transfer that number of data items, be it 8/16/32 bits. 

Initializing the DMA Peripheral


First and foremost, enable the clock to DMA1, by setting the RCC_AHB1ENR_DMA1EN bit in the RCC_AHB1ENR register. Then, enable the DMA mode for Transmission by setting the USART_CR3_ DMAT bit in the USART_CR3 register. Since no physical interface is required, we do not have to configure any GPIO pins.

Configuring the DMA Stream and Channel


In the DMA_SxCR Register, select the Channel from the table given in the Reference Manual (RM0368). Since we are taking USART2 Tx, it is on Stream 6 Channel 4, that is, CHSEL[2:0] is set to 0b100. Set the Memory and Peripheral Data size to 8-bits (default). Enable the Memory Increment Mode to increment the memory address from which the transfer occurs to advance to the next byte of data. Enable the Circular Mode by setting Bit 8 to handle circular buffers and continuous data flow. Then, set the data transfer direction, from Memory to Peripheral ( DIR[1:0] = 0x01 ). Furthermore, enable the Transfer Complete Interrupt to fire an interrupt once the transmission is complete.

Starting the DMA Transfer


Setting the Enable bit in the DMA_SxCR register will immediately start the DMA transfer. So, we will call this function at the right moment.

Interrupt Handler for DMA Completion


Once the data transfer is complete, that is when the NDTR counter reaches zero, the TCIF6 bit in the DMA_HISR register is set. Even though we have enabled the Circular mode, we will now clear the CTCIF6 bit in the DMA_HIFCR register to clear the interrupt and then disable the DMA Stream 6. The function name is already defined in the startup file, startup_stm32f401xe.s

LED functions for Demonstration


Here, we configure the GPIO Port A Pin 5 (PA5) into Output Mode, and the LED_On() and LED_Off() functions set and reset the PA5 pin using the BSRR register respectively.

Main function


At first, call the Initialization functions for UART, LED and DMA. Then call the DMA_ConfigureTransfer() function. From the stm32f401xe.h file, we can get the IRQ number for DMA Stream 6. Now, call the NVIC_EnableIRQ() function with the IRQ number we obtained before. Then, start the DMA transfer. In the while() loop, continuously turn ON and turn OFF the USER LED to show that the CPU is engaged full time while the DMA transfer occurs.

Popular posts from this blog

SignGlove: Bridging the Communication Gap for Paralyzed Patients

Cifradopro: A baremetal Hardware Security Module using the STM32L4S5 Cortex-M4 MCU

AESecureIO: Embedded Data Encryption and Decryption System