cancel
Showing results for 
Search instead for 
Did you mean: 

UART DMA Rx/Tx

DJ1
Associate III

I am working on STM32F401RE, transmitting data via UART using DMA 1(channel 6) , also i want to receive data using DMA 1 (channel 5). I want to use the DMA in single byte transfers in direct mode. Can you help me how to do so correctly because i tried following single byte transfers from the reference manual but it didn't work. I am getting output for the same in Circular mode. Can any share proper procedure for the same.

11 REPLIES 11

I don't see point in transferring 1 byte using DMA.

JW

Danish1
Lead II

"it didn't work" doesn't give us much of a clue as to why or how things weren't as you expected.

Help us to help you. Show us what you did (i.e. source code), what you expected to happen, what actually happened.

How strongly do you know the hardware is set up as needed? Does the UART behave when you're doing direct and/or interrupt-driven transfers?

The Reference Manual is the definitive source for everything we need to know about an stm32. But it can be difficult to understand what's written there. So it's useful sometimes to look at the STM32Cube sample code as well.

I'm not sure what you mean by "direct mode" and "single byte transfers". The overheads of DMA mean it's only worth doing when you have multiple bytes to send and/or receive.

There's a complication for UART RX with DMA. If you don't know how many characters that will arrive, the DMA won't give a "transfer complete" interrupt when a message is complete.

DJ1
Associate III
void dma_init()
{
	// Disable the direct mode
	//DMA1->DMA_S6FCR |= (1 << 2);
	// FIFO threshold setting
	DMA1->DMA_S6FCR |= (3 << 0);
	// Set channel 4 for DMA stream6 for UART_Tx
	DMA1->DMA_S6CR |= (4 << 25);
	// MBURST selection for INCR4 beats
	//DMA1->DMA_S6CR |= (1 << 23);
	// PBURST selection for single transfer of byte beats
	DMA1->DMA_S6CR &= ~(3 << 21);
	// MSIZE 1 byte
	DMA1->DMA_S6CR &= ~(3 << 13);
	// PSIZE 1 byte
	DMA1->DMA_S6CR &= ~(3 << 12);
	// MINC EN, PINC at 0
	DMA1->DMA_S6CR |= (1 << 10);
	// Circular mode enable
	//DMA1->DMA_S6CR |= (1 << 8);
	// DIR 1, Memory to Peripheral
	DMA1->DMA_S6CR |= (1 << 6);
	// Flow Controller here is DMA
	DMA1->DMA_S6CR &= ~(1 << 5);
	// No of Data Register: store number of bytes
	DMA1->DMA_S6NDTR = 8;
}
 
void dma_call()
{
	// Peripheral Address
	DMA1->DMA_S6PAR = (uint32_t) &(USART_2.pUSARTx->USART_DR);
	// Memory address
	DMA1->DMA_S6M0AR = (uint32_t) tx;
	// Enable DMA bit
	DMA1->DMA_S6CR |= (1 << 0);
 
}
 
 
 
int main()
{
	GPIOA_PCLK_EN;
 
	// timer init
	tim_init();
 
	// GPIO AF
	GPIO_USART.pGPIOx = GPIOA;
	GPIO_USART.GPIO_PinConfig.GPIO_PinMode = GPIO_MODE_AF;
	GPIO_USART.GPIO_PinConfig.GPIO_PinAltFunMode = 7;
	GPIO_USART.GPIO_PinConfig.GPIO_PinOPType = GPIO_OUT_PUSHPULL;
	GPIO_USART.GPIO_PinConfig.GPIO_PinOutSpeed = GPIO_OUT_SPEED_HIGH;
	GPIO_USART.GPIO_PinConfig.GPIO_PinPuPdControl = GPIO_OUT_PU;
 
	// Here it is very important to call the GPIO_init func for each and every pin initialization
	// bcoz or else pinmode selection does not happen properly if dealing with multiple pin init.
	// Rx Pin
	GPIO_USART.GPIO_PinConfig.GPIO_PinNumber = PIN_NO_3;
	GPIO_Init(&GPIO_USART);
	// Tx Pin
	GPIO_USART.GPIO_PinConfig.GPIO_PinNumber = PIN_NO_2;
	GPIO_Init(&GPIO_USART);
 
 
 
	// RCC enable
	USART2_CLK_EN;
 
	// USART 2 peripheral init
	USART_2.pUSARTx = USART2;
	USART_2.USART_Config.Baud_Rate = USART_BAUD_9600;
	USART_2.USART_Config.NoOfStopBits = USART_STOPB_1_;
	USART_2.USART_Config.WordLength = USART_WORDLEN_8;
	USART_2.USART_Config.USART_Mode = USART_TxRx;
	USART_2.USART_Config.ParityControl = USART_Parity_Disable;
	USART_2.USART_Config.HWFlowControl = USART_DI_RTS_CTS;
	// USART Init
	USART_Init(&USART_2);
 
	//UART DMA transmitter enable
	USART_2.pUSARTx->USART_CR3 |= (1 << 7);
 
	// Enable UART RxNE Interrupt
	// USART_2.pUSARTx->USART_CR1 |= ((1 << 5));
	// USART Peripheral Enable
	USART_EnDi(USART_2.pUSARTx, 1);
 
	// DMA CLK EN
	DMA1_CLK_EN;
	dma_init();
 
	// dma function call
	dma_call();
 
 
 
	while(1)
	{
	}
}

DJ1
Associate III

NOTE: In the above code all the header files as well as controller specific header file has also been developed by us, so you might find the names to be little different.

If you go through the reference manual for the same MCU board you will find there is "Direct Mode" and "FIFO Mode", in case of FIFO mode the data gets transmitted based on the FIFO threshold level. In case of Direct mode the byte gets transferred without FIFO. In case of "Circular DMA mode" the code is working fine as it is continuously transmitting data, but i want the DMA transfer to happen only once. Meaning that entire buffer of data should be transmitted and then the DMA should halt.

This is Direct Mode:

"When the DMA is configured in direct mode (FIFO disabled), to transfer data in memory-to-

peripheral mode, the DMA preloads one data from the memory to the internal FIFO to

ensure an immediate data transfer as soon as a DMA request is triggered by a peripheral."

Things i tried:

  1. I tried enabling the DMA interrupts like Half transfer and Transfer Complete but it is not even generating an interrupt though my data is getting received as well as transmitted in case of "Circular Mode."
  2. I tried disabling the "Direct mode" by setting the DMDIS bit in DMA FIFO control register. Again with FIFO mode only, circular buffer is working but if i want data transfers to happen only once that is not happening.

Now, as you said there is no point in using DMA for single byte. Got it. But what if i dont want the transmission to take place continuously. It should transmit the data buffer of lets say 10 bytes, all at once and then stop. Again when my data gets updated due to some external dependencies then iw ill transmit that updated buffer again. This is how I want to control DMA for my application. Only when required.

At last simply consider i have a buffer of 10 bytes which gets updated after a certain interval. I will call my "dma_call" function once i get all my data inside buffer updated. Then only the DMA transfer should take place. How can i achieve that. Please do share some insights.

Thankyou for replying!

DJ1
Associate III

You may find additional header files and code files inside the zip.

Piranha
Chief II

Use the CMSIS defined peripheral, register and bit field definitions, not reinvent those.

Polled UART Rx works?

Read out and check/post content of UART, DMA and relevant GPIOn registers.

JW

DJ1
Associate III

True. But it was our need to work in a manner without any dependencies and involving only the peripherals we are using for our application, just because of that we developed the files with our own conventions.

What is your say, on how to resolve the requirement.

"At last simply consider i have a buffer of 10 bytes which gets updated after a certain interval. I will call my "dma_call" function once i get all my data inside buffer updated. Then only the DMA transfer should take place. How can i achieve that. Please do share some insights."

In the New Folder zip file, i have attached all the registers and GPIO's being used and the related files. If you can just help me with a basic process on how to send a specific length of data using UART Tx DMA that also only when needed. It would be of great help!