Skip to main content
vd_it
Associate II
June 14, 2022
Solved

Issues getting DMA's to work reliably with USART1 and LPUART

  • June 14, 2022
  • 2 replies
  • 3097 views

Brief: I am using cubemx to configure and generate code.

MCU: stm32wb55rgvx

firmware package name and version: FW_WB V1.13.3

Details:

I am working on a project where the stm must act as a bridge between the PC and the device under test (DUT). We use the newtmgr tool on the PC to transfer/upload the firmware on the DUT via the stm at baudrate of 1MHz. For this, we are using two uarts on the stm i.e. USART1 connected to the PC and LPUART1 connected to the DUT. All packets received from the PC must be relayed to the DUT and all packets/responses received from the DUT must be forwarded to the PC.

I have attached the .ioc file that shows how I have configured the two uarts to be used with DMA's. I'd like the DMA's to work in the following cyclic order since it's a request-response mechanism.

  • Receive packet from the PC over USART1-RX
  • Relay this packet to the DUT over LPUART1-TX
  • Receive a response from the DUT on LPUART-RX
  • Relay this packet to the PC over USART1-TX

With the above configuration, the transfer starts to work but stops after about 20%, as shown below image. You can also see on channel-2 i.e. (PC->STM), there's a massive delay of 3s introduced which could be breaking the upload process.

0693W00000NrnUcQAJ.pngBelow are code snippets implementing the bridge.

/**
 * @brief Initialises the UART ports to receive data, called once in the main.
 */
void test_process_init() {
	if (HAL_OK != HAL_UARTEx_ReceiveToIdle_DMA(pc.uart,
				(uint8_t *)pc.buffer, sizeof(pc.buffer))){
		 Error_Handler();
	}
 
	if (HAL_OK != HAL_UARTEx_ReceiveToIdle_DMA(dut.uart,
				(uint8_t *)dut.buffer, sizeof(dut.buffer))){
		 Error_Handler();
	}
	enter_boot_mode(); // puts dut in serial recovery mode
}

The above is called once in the main on boot to initiate dma reception. The below code snippet shows how I managing/bridging the transfers.

UART_HandleTypeDef huart1;
UART_HandleTypeDef hlpuart1;
 
struct uart {
	UART_HandleTypeDef *uart;
	char buffer[1024];
	volatile bool buffer_ready;
	size_t length;
};
 
static struct uart pc= { .uart = &huart1, };
static struct uart dut= { .uart = &hlpuart1, };
 
/**
 * @brief Special service to capture the following events (bridge the transfers)
 * - RX line IDLE for 1 frame time
 */
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size){
	struct uart *rec_uart = &dut;
	struct uart *tx_uart = &pc;
	if (huart == pc.uart) {
		rec_uart = &pc;
		tx_uart = &dut;
	}
 
	HAL_StatusTypeDef status = HAL_UART_Transmit_DMA(tx_uart->uart, (uint8_t*)rec_uart->buffer, Size);
	if(status != HAL_OK){
		if(status == HAL_BUSY){
			// do nothing
		}else{
			Error_Handler();
		}
	}
 
	status = HAL_UARTEx_ReceiveToIdle_DMA(rec_uart->uart, (uint8_t *)rec_uart->buffer, sizeof(rec_uart->buffer));
	if(status != HAL_OK){
		if(status == HAL_BUSY){
			// do nothing
		}else{
			Error_Handler();
		}
	}
}

Any support in getting this to work reliably would be helpful. Cheers.

This topic has been closed for replies.
Best answer by gbm

Abandon HAL, use interrupts and two big circular buffers - it's much easier to do it that way than to use DMA for your task.

2 replies

gbm
gbmBest answer
Lead III
June 14, 2022

Abandon HAL, use interrupts and two big circular buffers - it's much easier to do it that way than to use DMA for your task.

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice
vd_it
vd_itAuthor
Associate II
June 22, 2022

Did this and worked like magic. I also realized the interrupt handlers generated by cubemx are massive. Cheers.

vd_it
vd_itAuthor
Associate II
June 14, 2022

I thought for baud rates of 1 MHz and above, the recommended way is to use DMA's. Could you please shed some light on this: The HAL uses interrupts for RX and TX too, why do you suggest abandoning it?

I'd also like some feedback on what I have done above and why it isn't working.

Tesla DeLorean
Guru
June 14, 2022

Yeah, I'd probably lean to DMA too. It helped on an STM32L072 based design.

There I used DMA to create a circular ring buffer for the receive, it reduced loading, and increased robustness.

Didn't use interrupts, or HAL, ST's solution is just too cluttered and cumbersome.

Sorry, I not going to unravel broken code.

Tips, Buy me a coffee, or three.. PayPal VenmoUp vote any posts that you find helpful, it shows what's working..
vd_it
vd_itAuthor
Associate II
June 14, 2022

I'd like the idea of using dma's without interrupts too. Would you mind sharing such an example or snippet if you have one? - Also, the cubemx tends to generate code with interrupts enabled for the dma's, and it's greyed out causing unable to disable it.

Do you disable the interrupts in the main and then go about implementing your own code/driver to do the above without interrupts?