cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H743 USART DMA

Manu Abraham
Senior

Hi,

Can someone please help ?

Has someone got USART Peripheral with DMA transfers working on the STM32H743ZI2 ?

I have been trying to get DMA transfers to work on the H743. The DMA stalls after the very first DMA transfer and stays there itself.

Searching through the Erratum, found that USART peripheral DMA issue. It suggests to set bit 20 of DMA_SxCR register as a fix.

Did try the same, but had no success. Is there anything else that needs to be done to fix the situation ?

Attached screenshot of the UART3 (ST Link VCP) output.

0693W000001sAK3QAM.png

Inlined minimalist code, which I have been trying.

Any thoughts ?

Thanks,

Manu

/**
 * UART3 DMA Tx/Rx test application over ST Link Virtual COM Port
 * LED1 turns ON after sending string to VCP
 */
 
#include "stm32h7xx_ll_bus.h"
#include "stm32h7xx_ll_cortex.h"
#include "stm32h7xx_ll_dma.h"
#include "stm32h7xx_ll_gpio.h"
#include "stm32h7xx_ll_pwr.h"
#include "stm32h7xx_ll_rcc.h"
#include "stm32h7xx_ll_system.h"
#include "stm32h7xx_ll_usart.h"
#include "stm32h7xx_ll_utils.h"
 
#define APB_Div			4
 
__IO uint8_t tx_done = 0;
 
 
const uint8_t txstr[] = "STM32H743 USART DMA Test: \r\n";
uint8_t txlen = sizeof (txstr);
 
/**
 * System Clock Configuration
 * The system Clock is configured as follow :
 * 	System Clock source	= PLL1 (HSE BYPASS)
 *	SYSCLK(Hz)		= 400000000 (CPU Clock)
 *	HCLK(Hz)		= 200000000 (AXI and AHBs Clock)
 *	AHB Prescaler		= 2
 *	D1 APB3 Prescaler	= 2 (APB3 Clock  100MHz)
 *	D2 APB1 Prescaler	= 2 (APB1 Clock  100MHz)
 *	D2 APB2 Prescaler	= 2 (APB2 Clock  100MHz)
 *	D3 APB4 Prescaler	= 2 (APB4 Clock  100MHz)
 *	HSE Frequency(Hz)	= 8000000
 *	PLL_M			= 4
 *	PLL_N			= 400
 *	PLL_P			= 2
 *	PLL_Q			= 4
 *	PLL_R			= 2
 *	VDD(V)			= 3.3
 *	Flash Latency(WS)	= 4
 */
static void SystemClock_Config(void)
{
	/* Power Configuration */
	LL_PWR_ConfigSupply(LL_PWR_LDO_SUPPLY);
	LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1);
	while (LL_PWR_IsActiveFlag_VOS() == 0) { }
 
	LL_RCC_HSE_EnableBypass();
	LL_RCC_HSE_Enable();
	while (LL_RCC_HSE_IsReady() != 1) { }
 
	LL_FLASH_SetLatency(LL_FLASH_LATENCY_4);
 
	/* Main PLL configuration and activation */
	LL_RCC_PLL_SetSource(LL_RCC_PLLSOURCE_HSE);
	LL_RCC_PLL1P_Enable();
	LL_RCC_PLL1Q_Enable();
	LL_RCC_PLL1R_Enable();
	LL_RCC_PLL1FRACN_Disable();
	LL_RCC_PLL1_SetVCOInputRange(LL_RCC_PLLINPUTRANGE_2_4);
	LL_RCC_PLL1_SetVCOOutputRange(LL_RCC_PLLVCORANGE_WIDE);
	LL_RCC_PLL1_SetM(4);
	LL_RCC_PLL1_SetN(400);
	LL_RCC_PLL1_SetP(2);
	LL_RCC_PLL1_SetQ(4);
	LL_RCC_PLL1_SetR(2);
	LL_RCC_PLL1_Enable();
	while (LL_RCC_PLL1_IsReady() != 1) { }
 
	LL_RCC_SetSysPrescaler(LL_RCC_SYSCLK_DIV_1);
	LL_RCC_SetAHBPrescaler(LL_RCC_AHB_DIV_2);
	LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_2);
	LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_2);
	LL_RCC_SetAPB4Prescaler(LL_RCC_APB4_DIV_2);
	LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL1);	/* PLL1 as System Clock Source */
	while (LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL1) { }
 
	SysTick_Config(400000000 / 1000);			/* 1ms Systick */
	SystemCoreClock = 400000000;
}
 
void led_init(void)
{
	LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_GPIOB);	/* GPIO Clk */
	LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_GPIOE);
 
	LL_GPIO_SetPinMode(GPIOB,
			   LL_GPIO_PIN_0,
			   LL_GPIO_MODE_OUTPUT);		/* PB.0 -> LED1 */
 
	LL_GPIO_SetPinMode(GPIOE,
			   LL_GPIO_PIN_1,
			   LL_GPIO_MODE_OUTPUT);		/* PE.1 -> LED2 */
 
	LL_GPIO_SetPinMode(GPIOB,
			   LL_GPIO_PIN_14,
			   LL_GPIO_MODE_OUTPUT);		/* PB.14 -> LED3 */
}
 
void config_usart(void)
{
	LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_GPIOD);	/* GPIO Clk */
 
	LL_GPIO_SetPinMode(GPIOD, LL_GPIO_PIN_8, LL_GPIO_MODE_ALTERNATE);
	LL_GPIO_SetAFPin_8_15(GPIOD, LL_GPIO_PIN_8, LL_GPIO_AF_7);
	LL_GPIO_SetPinSpeed(GPIOD, LL_GPIO_PIN_8, LL_GPIO_SPEED_FREQ_HIGH);
	LL_GPIO_SetPinOutputType(GPIOD, LL_GPIO_PIN_8, LL_GPIO_OUTPUT_PUSHPULL);
	LL_GPIO_SetPinPull(GPIOD, LL_GPIO_PIN_8, LL_GPIO_PULL_UP);
 
	LL_GPIO_SetPinMode(GPIOD, LL_GPIO_PIN_9, LL_GPIO_MODE_ALTERNATE);
	LL_GPIO_SetAFPin_8_15(GPIOD, LL_GPIO_PIN_9, LL_GPIO_AF_7);
	LL_GPIO_SetPinSpeed(GPIOD, LL_GPIO_PIN_9, LL_GPIO_SPEED_FREQ_HIGH);
	LL_GPIO_SetPinOutputType(GPIOD, LL_GPIO_PIN_9, LL_GPIO_OUTPUT_PUSHPULL);
	LL_GPIO_SetPinPull(GPIOD, LL_GPIO_PIN_9, LL_GPIO_PULL_UP);
 
	LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_USART3);			/* USART3 Clk */
	LL_RCC_SetUSARTClockSource(LL_RCC_USART234578_CLKSOURCE_PCLK1);		/* Clk source */
 
	LL_USART_SetTransferDirection(USART3, LL_USART_DIRECTION_TX_RX);
	LL_USART_ConfigCharacter(USART3,
				 LL_USART_DATAWIDTH_8B,
				 LL_USART_PARITY_NONE,
				 LL_USART_STOPBITS_1);
 
	LL_USART_SetBaudRate(USART3,
			     (SystemCoreClock / APB_Div),
			     LL_USART_PRESCALER_DIV1,
			     LL_USART_OVERSAMPLING_16,
			     115200);
 
	LL_USART_Enable(USART3);
 
	while ((!(LL_USART_IsActiveFlag_TEACK(USART3))) ||
	       (!(LL_USART_IsActiveFlag_REACK(USART3)))) { }
}
/**
 * Bugfix!
 * 2.3.1 DMA stream locked when transferring UART data
 *
 * Description:
 * When a USART/UART is issuing a DMA request to transfer data,
 * if a concurrent transfer occurs, the requested transfer
 * may not be served and the DMA stream may stay locked.
 *
 * Workaround:
 * Use the alternative peripheral DMA channel protocol
 * by setting bit 20 of the DMA_SxCR register.
 */
__STATIC_INLINE void LL_DMA_EnablePeriphDMA(DMA_TypeDef *DMAx, uint32_t Stream)
{
	register uint32_t dma_base_addr = (uint32_t)DMAx;
 
	SET_BIT(((DMA_Stream_TypeDef *)(dma_base_addr + LL_DMA_STR_OFFSET_TAB[Stream]))->CR, DMA_SxCR_TRBUFF);
}
 
//__STATIC_INLINE void LL_DMA_DisablePeriphDMA(DMA_TypeDef *DMAx, uint32_t Stream)
//{
//	register uint32_t dma_base_addr = (uint32_t)DMAx;
 
//	CLEAR_BIT(((DMA_Stream_TypeDef *)(dma_base_addr + LL_DMA_STR_OFFSET_TAB[Stream]))->CR, DMA_SxCR_TRBUFF);
//}
 
void config_dma(void)
{
	LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);
 
	NVIC_SetPriority(DMA1_Stream3_IRQn, 0);
	NVIC_EnableIRQ(DMA1_Stream3_IRQn);
 
	/* Tx DMA */
	LL_DMA_SetPeriphRequest(DMA1, LL_DMA_STREAM_3, LL_DMAMUX1_REQ_USART3_TX);
	LL_DMA_EnablePeriphDMA(DMA1, LL_DMA_STREAM_3);
 
	LL_DMA_SetDataTransferDirection(DMA1,
					LL_DMA_STREAM_3,
					LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
 
	LL_DMA_SetStreamPriorityLevel(DMA1, LL_DMA_STREAM_3, LL_DMA_PRIORITY_HIGH);
	LL_DMA_SetMode(DMA1, LL_DMA_STREAM_3, LL_DMA_MODE_NORMAL);
 
	LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_STREAM_3, LL_DMA_PERIPH_NOINCREMENT);
	LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_STREAM_3, LL_DMA_MEMORY_INCREMENT);
	LL_DMA_SetPeriphSize(DMA1, LL_DMA_STREAM_3, LL_DMA_PDATAALIGN_BYTE);
	LL_DMA_SetMemorySize(DMA1, LL_DMA_STREAM_3, LL_DMA_PDATAALIGN_BYTE);
 
	/* DMA IRQ */
	LL_DMA_EnableIT_TC(DMA1, LL_DMA_STREAM_3);
	LL_DMA_EnableIT_TE(DMA1, LL_DMA_STREAM_3);
}
 
void start_dma(void)
{
	LL_GPIO_ResetOutputPin(GPIOB, LL_GPIO_PIN_0);		/* Done: LED OFF */
	LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_3, txlen);
	LL_DMA_ConfigAddresses(DMA1,
			       LL_DMA_STREAM_3,
			       (uint32_t)txstr,
			       LL_USART_DMA_GetRegAddr(USART3, LL_USART_DMA_REG_DATA_TRANSMIT),
			       LL_DMA_GetDataTransferDirection(DMA1, LL_DMA_STREAM_3));
 
	LL_USART_EnableDMAReq_TX(USART3);			/* Enable DMA TX Interrupt */
	LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_3);		/* Enable DMA Channel Tx */
}
 
void stop_dma(void)
{
	while (tx_done != 1) { }
	LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_3);		/* Disable DMA1 Tx Channel */
	LL_USART_DisableDMAReq_TX(USART3);			/* Disable DMARQ */
 
	LL_GPIO_SetOutputPin(GPIOB, LL_GPIO_PIN_0);		/* Done: LED ON */
}
 
void led_blink(uint8_t led, uint32_t Period)
{
	while (1) {
		switch (led) {
		case 1:
			LL_GPIO_TogglePin(GPIOB, LL_GPIO_PIN_0);
			break;
		case 2:
			LL_GPIO_TogglePin(GPIOE, LL_GPIO_PIN_1);
			break;
		case 3:
			LL_GPIO_TogglePin(GPIOB, LL_GPIO_PIN_14);
			break;
		}
		LL_mDelay(Period);
	}
}
 
void DMA1_Stream3_IRQHandler(void)
{
	if (LL_DMA_IsActiveFlag_TC3(DMA1)) {
		LL_DMA_ClearFlag_TC3(DMA1);
		tx_done = 1;
	} else if (LL_DMA_IsActiveFlag_TE3(DMA1)) {
		LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_3);		/* Disable DMA1 Tx Channel */
		led_blink(3, 1000);					/* Set LED3 to indicate error */
	}
}
 
int main(void)
{
	SystemClock_Config();
	led_init();
	config_usart();
	config_dma();
 
	start_dma();
	stop_dma();
 
//	LL_mDelay(2000);
 
	start_dma();
	stop_dma();
 
	while (1) { }
}

4 REPLIES 4
TDK
Guru

I don't see you configure the DMAMUX anywhere. Should be a call to LL_DMAMUX_SetRequestID to connect the dma stream to the peripheral.

Nevermind, looks like LL_DMA_SetPeriphRequest does the same thing (so why are there different function that do the same thing?)

The more I look at LL code, the less I like it even compared to HAL. I like the idea in theory but in practice just means you need to learn LL language as well as register language.

In any case, I do have this working on the H7 but can't spot what is different with our codes.

If you feel a post has answered your question, please click "Accept as Solution".

The document at least for the DMAMUX section gives me a headache. The way the language is used, makes understanding harder. HAL causes a nightmare. LL does not make things any better.

For the G474, DMAMUX required a Clock Enable. That's gone with the H7 ?

I tried setting the DMAMUX, as inlined, but does not seem to make any difference. Any thoughts ?

void config_dma(void)
{
	LL_DMAMUX_SetRequestID(DMAMUX1, LL_DMAMUX_CHANNEL_0, LL_DMAMUX1_REQ_USART3_TX);
	LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);
 
	NVIC_SetPriority(DMA1_Stream3_IRQn, 0);
	NVIC_EnableIRQ(DMA1_Stream3_IRQn);
 
	/* Tx DMA */
	LL_DMA_SetPeriphRequest(DMA1, LL_DMA_STREAM_3, LL_DMAMUX1_REQ_USART3_TX);
	LL_DMA_EnablePeriphDMA(DMA1, LL_DMA_STREAM_3);
 
	LL_DMA_SetDataTransferDirection(DMA1,
					LL_DMA_STREAM_3,
					LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
 
	LL_DMA_SetStreamPriorityLevel(DMA1, LL_DMA_STREAM_3, LL_DMA_PRIORITY_HIGH);
	LL_DMA_SetMode(DMA1, LL_DMA_STREAM_3, LL_DMA_MODE_NORMAL);
 
	LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_STREAM_3, LL_DMA_PERIPH_NOINCREMENT);
	LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_STREAM_3, LL_DMA_MEMORY_INCREMENT);
	LL_DMA_SetPeriphSize(DMA1, LL_DMA_STREAM_3, LL_DMA_PDATAALIGN_BYTE);
	LL_DMA_SetMemorySize(DMA1, LL_DMA_STREAM_3, LL_DMA_PDATAALIGN_BYTE);
 
	/* DMA IRQ */
	LL_DMA_EnableIT_TC(DMA1, LL_DMA_STREAM_3);
	LL_DMA_EnableIT_TE(DMA1, LL_DMA_STREAM_3);
}

I understand now, why it did not work.

EnablePeriphDMA() was set too early, probably the rest of the DMA was set incorrectly.

moving it down, fixed the issue.

Maybe the 2 functions can be included in a future CubeH7 package, so that these 2 LL functions need not be carried around ?

The Stream DMA enable for the USART peripheral does not seem to be a bug at all. The way it is described makes it sound a bit hopeless, but is quite harmless.

@ST, It would be nice, if someone adds these 2 functions to a future CubeH7 package ..

/**
 * Bugfix!
 * 2.3.1 DMA stream locked when transferring UART data
 *
 * Description:
 * When a USART/UART is issuing a DMA request to transfer data,
 * if a concurrent transfer occurs, the requested transfer
 * may not be served and the DMA stream may stay locked.
 *
 * Workaround:
 * Use the alternative peripheral DMA channel protocol
 * by setting bit 20 of the DMA_SxCR register.
 */
__STATIC_INLINE void LL_DMA_EnablePeriphDMA(DMA_TypeDef *DMAx, uint32_t Stream)
{
	register uint32_t dma_base_addr = (uint32_t)DMAx;
 
	SET_BIT(((DMA_Stream_TypeDef *)(dma_base_addr + LL_DMA_STR_OFFSET_TAB[Stream]))->CR, DMA_SxCR_TRBUFF);
}
 
__STATIC_INLINE void LL_DMA_DisablePeriphDMA(DMA_TypeDef *DMAx, uint32_t Stream)
{
	register uint32_t dma_base_addr = (uint32_t)DMAx;
 
	CLEAR_BIT(((DMA_Stream_TypeDef *)(dma_base_addr + LL_DMA_STR_OFFSET_TAB[Stream]))->CR, DMA_SxCR_TRBUFF);
}

> The more I look at LL code, the less I like it even compared to HAL. I like the idea in theory but in practice just means you need to learn LL language as well as register language.

This! Though I don't think "renaming the registers" and making another set of names with the same level of functionality is a good idea even in theory...