Working with STM32F7 and UART part 4: Full Duplex using DMA

In the previous guide (here), we took a look how to configure the UART to transmit data to PC using DMA. In this guide, we shall configure the UART to receive characters using DMA.

This guide shall cover the following:

  • Configure UART for DMA RX
  • Configure DMA.
  • Support functions.
  • Code.
  • Results.

1. Configure UART for DMA RX:

This section is continuation from the previous guide

In order to configure the UART in RX mode with DMA, first we need to set PD9 to AF07 which is for USART3_RX pin as following:

GPIOD->MODER|=GPIO_MODER_MODER9_1;
#define AF07 0x07
GPIOD->AFR[1]|=(AF07<<4);

Now, we shall enable RX mode in CR1 of USART3:

USART3->CR1|=USART_CR1_RE; //enable RX

Then enable DMAR in CR3:

Finally enable UART module:

USART3->CR1|=USART_CR1_UE;

2. DMA Configuration:

First we need which Stream and Channel of DMA1 is responsible for USART3_RX. From the reference manual, we can find it is Stream1 Channel 4

Since the clock access is already enable, we shall first disable the stream and wait until the stream is disabled:

DMA1_Stream1->CR &=~(DMA_SxCR_EN);
	while((DMA1_Stream1->CR &(DMA_SxCR_EN)));

Then the configuration of the DMA as following:

  • Channel select is channel 4.
  • Memory increment mode.
  • Circular mode
  • Direction from peripheral to memory.
  • Transfer complete interrupt enabled.
#define ch4 0x04
	DMA1_Stream1->CR|=(ch4<<25)|DMA_SxCR_MINC|DMA_SxCR_TCIE|DMA_SxCR_CIRC;

Set the peripheral address to be USART3->RDR:

DMA1_Stream1->PAR=(uint32_t)&USART3->RDR;

Enable DMA1_Stream1 interrupt in the NVIC:

NVIC_EnableIRQ(DMA1_Stream1_IRQn);

For the DMA1_Channel1 interrupt handler:

  • Check if the source is from transfer complete
  • If it is from transfer complete, set rx_finshed to 1 and clear the pending interrupt.

void DMA1_Stream1_IRQHandler(void)
	{
		if(DMA1->LISR & DMA_LISR_TCIF1)
			{
				rx_finished=1;
				DMA1->LIFCR=DMA_LIFCR_CTCIF1;
			}

	}

3. Support function:

For launching the DMA, we shall use a function that takes two arguments:

  • Pointer to the buffer where the data shall be stored.
  • Length of the buffer.
void launch_rx_dma(uint8_t * buffer, uint16_t length)

Within this function, set the memory address to be the buffer:

DMA1_Stream1->M0AR= buffer;

Number of transfer to be the passed length:

DMA1_Stream1->NDTR=length;

Finally, launch the DMA:

DMA1_Stream1->CR|=DMA_SxCR_EN;

Within the header file, we shall use the following enumerated values:

typedef enum
	{
		buffer_full=1,
		buffer_empty=0
	}buffer_enums;

Also, check if the buffer is full:

uint8_t isBufferFull()
	{
		if(rx_finished==0)	{return buffer_empty;}
		else {rx_finished=0; return buffer_full;}
	}

4. Code:

Within main.c file:

#include "uart.h"
char uart_data_tx[40], uart_data_rx[5];

uint8_t length;

int main(void)
	{
		uart_tx_rx_dma_init();
		launch_rx_dma(&uart_data_rx, 5);
		while(1)
			{
				if(isBufferFull()){
				length=sprintf(uart_data_tx,"Received 5 characters are: %s\r\n",uart_data_rx);
				usart_dma_write(uart_data_tx,length);
				}
			}
	}

You may download the code from here:

5. Results:

Open you serial terminal and set the baud rate to 115200 and send any five characters:

Happy coding 🙂

Add Comment

Your email address will not be published. Required fields are marked *