Getting started with STM32L053: UART Full duplex in DMA Mode with IDLE Line Detection

In the previous guide (here), we saw how to configure the UART in full duplex mode with DMA. However, the application need to know the number of transfer in advanced in order to receive data. Using IDLE line, the application doesn’t need to know number of character to be received in advanced.

In this guide, we shall cover the following:

  • Configure UART for IDLE interrupt.
  • Code.
  • Demo.

1. Configure UART for IDLE interrupt:

To add IDLE line interrupt as simple as enabling it in CR1 (Control Register 1) and enable USART2 interrupt in NVIC

	/*Enable IDLE Line Interrupt */
	USART2->CR1|=USART_CR1_IDLEIE;
	NVIC_EnableIRQ(USART2_IRQn);

For interrupt handler:

  • Stop the DMA (optional).
  • Check if the source is IDLE line.
  • Set the received to 1.
  • Clear the pending IDLE line interrupt flag

void USART2_IRQHandler()
	{
	if(USART2->ISR & USART_ISR_IDLE)
		{
			stop_dma();
			received=1;
			USART2->ICR|=USART_ICR_IDLECF;
		}

	}

In while loop:

Check if the received is set to 1 and print the received data using DMA also:

		if(received==1)
			{
				length=sprintf(data_tx,"Received data\" %s \"\r\n",data_rx);
				uart_transmit_dma(data_tx,length);
				while(inprogress==1);
				inprogress=0;
				received=0;
				flush_buffer();
				lunch_dma();
			}

2. Code:

Hence, the entire code is as following:

#include "stm32l0xx.h"
#include "stdio.h"
#include "delay.h"
#include "string.h"
#define SYS_FREQ		2097000
#define APB1_CLK		SYS_FREQ

#define UART_BAUDRATE	115200

volatile uint8_t received=0, inprogress=0,full=0;

#define buffer_size 1024
char data_tx[buffer_size];
char data_rx[buffer_size];
uint16_t length;




#define DMA_CSELR  (*(volatile unsigned int *)(0x400200a8))


void stop_dma();
void lunch_dma();
void flush_buffer();

static uint16_t compute_uart_bd(uint32_t PeriphClk, uint32_t BaudRate)
{
	return ((PeriphClk  +  (BaudRate/2U))/BaudRate);
}






void uart_transmit_dma(char *message, uint16_t len)
	{

		if(inprogress==0)
		{
			/*DMA1 Channel 7 is for USART_TX*/
			DMA1_Channel7->CCR &=~DMA_CCR_EN;

			while((DMA1_Channel7->CCR &DMA_CCR_EN));
			DMA1_Channel7->CCR|=DMA_CCR_MINC|DMA_CCR_DIR|DMA_CCR_TCIE;
			NVIC_EnableIRQ(DMA1_Channel4_5_6_7_IRQn);
			DMA1_Channel7->CPAR=(uint32_t)(&USART2->TDR);
			DMA_CSELR|=(0x04<<24);
			DMA1->IFCR|=DMA_IFCR_CTCIF7;
			DMA1_Channel7->CMAR=(uint32_t)message;
			DMA1_Channel7->CNDTR=(uint16_t)len;
			DMA1_Channel7->CCR|=DMA_CCR_EN;
			inprogress=1;
		}
		else return;
	}




int main(void)
{
	systick_init(2097000);
	/*Enable clock access to GPIOA*/
	RCC->IOPENR |= RCC_IOPENR_GPIOAEN;
	GPIOA->MODER|=GPIO_MODER_MODE2_1;
	GPIOA->MODER&=~GPIO_MODER_MODE2_0;

	GPIOA->MODER|=GPIO_MODER_MODE3_1;
	GPIOA->MODER&=~GPIO_MODER_MODE3_0;

	#define AF04 0x04
	GPIOA->AFR[0]|=(AF04<<8);
	GPIOA->AFR[0]|=(AF04<<12);


	RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
	USART2->BRR=compute_uart_bd(APB1_CLK,UART_BAUDRATE);
	/*Configure transfer direction*/
	USART2->CR1 = USART_CR1_TE|USART_CR1_RE;
	/*Enable IDLE Line Interrupt */
	USART2->CR1|=USART_CR1_IDLEIE;
	NVIC_EnableIRQ(USART2_IRQn);
	/*Enable DMA TX for UART*/
	USART2->CR3|=USART_CR3_DMAT|USART_CR3_DMAR;
	/*Enable Clock access to DMA1*/
	RCC->AHBENR|=RCC_AHBENR_DMA1EN;
	/*Launch DMA*/
	lunch_dma();
	/*Enable uart module*/
	USART2->CR1 |= USART_CR1_UE;


	length=sprintf(data_tx,"Hello from UART DMA\r\n");
	uart_transmit_dma(data_tx,length);
	while(inprogress==1);
	inprogress=0;


	while(1)
	{
		if(received==1)
			{
				length=sprintf(data_tx,"Received data\" %s \"\r\n",data_rx);
				uart_transmit_dma(data_tx,length);
				while(inprogress==1);
				inprogress=0;
				received=0;
				flush_buffer();
				lunch_dma();
			}
		if(full==1)
			{
				length=sprintf(data_tx,"full buffer data\" %s \"\r\n",data_rx);
				uart_transmit_dma(data_tx,length);
				while(inprogress==1);
				inprogress=0;
				full=0;
			}


	}

}





void stop_dma()
	{
		DMA1_Channel6->CCR&=~DMA_CCR_EN;
	}

void flush_buffer()
	{
	memset(data_rx,'\0',buffer_size);

	}

void lunch_dma()
	{
		/*Configure DMA1_Channel6 for USART2_RX*/
		DMA1_Channel6->CCR&=~DMA_CCR_EN;
		while((DMA1_Channel6->CCR &DMA_CCR_EN));
		DMA1_Channel6->CCR|=DMA_CCR_MINC|DMA_CCR_CIRC|DMA_CCR_TCIE;
		NVIC_EnableIRQ(DMA1_Channel4_5_6_7_IRQn);
		DMA1_Channel6->CPAR=(uint32_t)(&USART2->RDR);
		DMA_CSELR|=(0x04<<20);
		DMA1_Channel6->CMAR=(uint32_t)data_rx;
		DMA1_Channel6->CNDTR=buffer_size;
		DMA1_Channel6->CCR|=DMA_CCR_EN;
	}


void DMA1_Channel4_7_IRQHandler(void)
	{

		if(DMA1->ISR & DMA_ISR_TCIF7)
			{
				inprogress=0;
				DMA1->IFCR|=DMA_IFCR_CTCIF7;
			}
		if(DMA1->ISR & DMA_ISR_TCIF6)
			{
				full=1;
				DMA1->IFCR|=DMA_IFCR_CTCIF6;
			}

	}


void USART2_IRQHandler()
	{
	if(USART2->ISR & USART_ISR_IDLE)
		{
			stop_dma();
			received=1;
			USART2->ICR|=USART_ICR_IDLECF;
		}

	}

3. Demo:

Happy coding 🙂

Add Comment

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