Skip to main content
Graduate II
February 19, 2025
Question

STM32H7S3L8 LL DMA USART TX transfer

  • February 19, 2025
  • 1 reply
  • 571 views

Hi,

I want to setup USART3 TX transfers using GPDMA, however, without success.

For such purpose I also followed this article: https://community.st.com/t5/stm32-mcus/how-to-configure-the-gpdma/ta-p/49412

STM32CubeMX setup is:

USART TX DMA Chan

simozz_0-1739974981911.png

The code for usart is the following:

/* USER CODE BEGIN Header */
/**
 ******************************************************************************
 * @file usart.h
 * @brief This file contains all the function prototypes for
 * the usart.c file
 ******************************************************************************
 * @attention
 *
 * Copyright (c) 2025 STMicroelectronics.
 * All rights reserved.
 *
 * This software is licensed under terms that can be found in the LICENSE file
 * in the root directory of this software component.
 * If no LICENSE file comes with this software, it is provided AS-IS.
 *
 ******************************************************************************
 */
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __USART_H__
#define __USART_H__

#ifdef __cplusplus
extern "C" {
#endif

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* USER CODE BEGIN Includes */
#include <string.h>
/* USER CODE END Includes */

/* USER CODE BEGIN Private defines */
#define DBG_USART
#define VCP_USART (USART_TypeDef *) USART3

#ifdef DBG_USART
 #ifndef VCP_USART
 #error "VCP_USART must be defined"
 #endif
#endif

#define USART_MAX_BUFF_SIZE 32

#define USART3_TX_BSY_B 0
#define USART3_RX_BSY_B 1

#define USART3_TX_BSY_M (1 << USART3_TX_BSY_B)
#define USART3_RX_BSY_M (1 << USART3_RX_BSY_B) 

/* USER CODE END Private defines */

void MX_USART3_UART_Init(void);

/* USER CODE BEGIN Prototypes */

extern uint8_t usartTxBuff[USART_MAX_BUFF_SIZE];
extern uint8_t usartRxBuff[USART_MAX_BUFF_SIZE];
extern uint8_t usart3StFlag;

void USART_Printf (char *p_str, uint32_t p_len);
void USART_DMA_Printf (char *p_str, uint32_t p_len);
void USART_Tx(USART_TypeDef *USARTx, uint32_t p_char);

/* USER CODE END Prototypes */

#ifdef __cplusplus
}
#endif

#endif /* __USART_H__ */

 

 

/* USER CODE BEGIN Header */
/**
 ******************************************************************************
 * @file usart.c
 * @brief This file provides code for the configuration
 * of the USART instances.
 ******************************************************************************
 * @attention
 *
 * Copyright (c) 2025 STMicroelectronics.
 * All rights reserved.
 *
 * This software is licensed under terms that can be found in the LICENSE file
 * in the root directory of this software component.
 * If no LICENSE file comes with this software, it is provided AS-IS.
 *
 ******************************************************************************
 */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "usart.h"

/* USER CODE BEGIN 0 */
uint8_t usartTxBuff[USART_MAX_BUFF_SIZE];
uint8_t usartRxBuff[USART_MAX_BUFF_SIZE];

uint8_t usart3StFlag;
/* USER CODE END 0 */

/* USART3 init function */

void MX_USART3_UART_Init(void)
{

 /* USER CODE BEGIN USART3_Init 0 */

 /* USER CODE END USART3_Init 0 */

 LL_USART_InitTypeDef USART_InitStruct = {0};

 LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
 LL_DMA_InitTypeDef DMA_InitStruct = {0};
 RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

 /** Initializes the peripherals clock
 */
 PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART234578;
 PeriphClkInit.Usart234578ClockSelection = RCC_USART234578CLKSOURCE_PCLK1;
 if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
 {
 Error_Handler();
 }

 /* Peripheral clock enable */
 LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_USART3);

 LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_GPIOD);
 /**USART3 GPIO Configuration
 PD8 ------> USART3_TX
 PD9 ------> USART3_RX
 */
 GPIO_InitStruct.Pin = LL_GPIO_PIN_8|LL_GPIO_PIN_9;
 GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
 GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
 GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
 GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
 GPIO_InitStruct.Alternate = LL_GPIO_AF_7;
 LL_GPIO_Init(GPIOD, &GPIO_InitStruct);

 /* USART3 DMA Init */

 /* GPDMA1_REQUEST_USART3_TX Init */
 DMA_InitStruct.SrcAddress = 0x00000000U;
 DMA_InitStruct.DestAddress = 0x00000000U;
 DMA_InitStruct.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
 DMA_InitStruct.BlkHWRequest = LL_DMA_HWREQUEST_SINGLEBURST;
 DMA_InitStruct.DataAlignment = LL_DMA_DATA_ALIGN_ZEROPADD;
 DMA_InitStruct.SrcBurstLength = 1;
 DMA_InitStruct.DestBurstLength = 1;
 DMA_InitStruct.SrcDataWidth = LL_DMA_SRC_DATAWIDTH_BYTE;
 DMA_InitStruct.DestDataWidth = LL_DMA_DEST_DATAWIDTH_BYTE;
 DMA_InitStruct.SrcIncMode = LL_DMA_SRC_FIXED;
 DMA_InitStruct.DestIncMode = LL_DMA_DEST_FIXED;
 DMA_InitStruct.Priority = LL_DMA_LOW_PRIORITY_LOW_WEIGHT;
 DMA_InitStruct.BlkDataLength = 0x00000000U;
 DMA_InitStruct.TriggerMode = LL_DMA_TRIGM_BLK_TRANSFER;
 DMA_InitStruct.TriggerPolarity = LL_DMA_TRIG_POLARITY_MASKED;
 DMA_InitStruct.TriggerSelection = 0x00000000U;
 DMA_InitStruct.Request = LL_GPDMA1_REQUEST_USART3_TX;
 DMA_InitStruct.TransferEventMode = LL_DMA_TCEM_BLK_TRANSFER;
 DMA_InitStruct.SrcAllocatedPort = LL_DMA_SRC_ALLOCATED_PORT0;
 DMA_InitStruct.DestAllocatedPort = LL_DMA_DEST_ALLOCATED_PORT0;
 DMA_InitStruct.LinkAllocatedPort = LL_DMA_LINK_ALLOCATED_PORT1;
 DMA_InitStruct.LinkStepMode = LL_DMA_LSM_FULL_EXECUTION;
 DMA_InitStruct.LinkedListBaseAddr = 0x00000000U;
 DMA_InitStruct.LinkedListAddrOffset = 0x00000000U;
 LL_DMA_Init(GPDMA1, LL_DMA_CHANNEL_2, &DMA_InitStruct);

 /* USER CODE BEGIN USART3_Init 1 */
 
 /* USER CODE END USART3_Init 1 */
 USART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1;
 USART_InitStruct.BaudRate = 115200;
 USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B;
 USART_InitStruct.StopBits = LL_USART_STOPBITS_1;
 USART_InitStruct.Parity = LL_USART_PARITY_NONE;
 USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX;
 USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
 USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16;
 LL_USART_Init(USART3, &USART_InitStruct);
 LL_USART_SetTXFIFOThreshold(USART3, LL_USART_FIFOTHRESHOLD_1_8);
 LL_USART_SetRXFIFOThreshold(USART3, LL_USART_FIFOTHRESHOLD_1_8);
 LL_USART_DisableFIFO(USART3);
 LL_USART_ConfigAsyncMode(USART3);
 LL_USART_Enable(USART3);
 /* USER CODE BEGIN USART3_Init 2 */
 // LL_USART_EnableIT_TC(USART3);
 // LL_USART_EnableIT_RXNE(USART3);
 
 usart3StFlag = 0;
 memset(usartTxBuff, 0, sizeof(usartTxBuff));
 memset(usartRxBuff, 0, sizeof(usartTxBuff));
 /* USER CODE END USART3_Init 2 */

}

/* USER CODE BEGIN 1 */
void USART_Printf (char *p_str, uint32_t p_len) {
 
 USART_TypeDef *usart_dbg = VCP_USART;
 LL_USART_EnableIT_TC(usart_dbg);
 for(uint32_t i = 0; i < p_len; i++) {
 USART_Tx(usart_dbg, p_str[i]);
 }

 USART_Tx(usart_dbg, '\r');
 USART_Tx(usart_dbg, '\n');
}

void USART_DMA_Printf (char *p_str, uint32_t p_len) {
 
 USART_TypeDef *usart_dbg = VCP_USART;
 uint32_t txLen = 0;
 
 if (p_len >= USART_MAX_BUFF_SIZE) {
 return;
 }

 txLen = p_len + 2;
 for (uint8_t i = 0; i < p_len; i++) {
 usartTxBuff[i] = p_str[i];
 }

 usartTxBuff[p_len] = '\r';
 usartTxBuff[p_len + 1] = '\n';
 
 LL_USART_ClearFlag_TC(usart_dbg);
 LL_USART_ClearFlag_TXFE(usart_dbg);
 LL_USART_ClearFlag_TCBGT(usart_dbg);
 
 LL_DMA_DisableChannel(GPDMA1, LL_DMA_CHANNEL_2);
 // 1. Write the USART_TDR register address in the DMA control register to configure it as
 // the destination of the transfer. The data is moved to this address from memory after
 // each TXE (or TXFNF if FIFO mode is enabled) event.
 LL_DMA_SetDestAddress(GPDMA1, LL_DMA_CHANNEL_2, LL_USART_DMA_GetRegAddr(usart_dbg, LL_USART_DMA_REG_DATA_TRANSMIT));
 // 2. Write the memory address in the DMA control register to configure it as the source of
 // the transfer. The data is loaded into the USART_TDR register from this memory area
 // after each TXE (or TXFNF if FIFO mode is enabled) event.
 LL_DMA_SetSrcAddress(GPDMA1, LL_DMA_CHANNEL_2, (uint32_t) &usartTxBuff[0]);
 // 3. Configure the total number of bytes to be transferred to the DMA control register.
 LL_DMA_SetSrcBurstLength(GPDMA1, LL_DMA_CHANNEL_2, txLen);
 LL_DMA_SetSrcDataWidth(GPDMA1, LL_DMA_CHANNEL_2, sizeof(uint8_t));
 LL_DMA_SetDestBurstLength(GPDMA1, LL_DMA_CHANNEL_2, txLen);
 LL_DMA_SetDestDataWidth(GPDMA1, LL_DMA_CHANNEL_2, sizeof(uint8_t));
 // 4. Configure the channel priority in the DMA register
 LL_DMA_SetChannelPriorityLevel(GPDMA1, LL_DMA_CHANNEL_2, LL_DMA_HIGH_PRIORITY);
 // 5. Configure DMA interrupt generation after half/ full transfer as required by the
 // application.
 LL_DMA_EnableIT_TC(GPDMA1, LL_DMA_CHANNEL_2);
 // 6. Clear the TC flag in the USART_ISR register by setting the TCCF bit in the
 // USART_ICR register.
 LL_USART_ClearFlag_TC(usart_dbg);
 // 7. Activate the channel in the DMA register
 LL_DMA_EnableChannel(GPDMA1, LL_DMA_CHANNEL_2);
 LL_USART_EnableDMAReq_TX(usart_dbg);

 // usart3StFlag |= USART3_TX_BSY_M;
}

void USART_Tx(USART_TypeDef *USARTx, uint32_t p_char) {
 LL_USART_TransmitData8(USARTx, p_char);
 while(!LL_USART_IsActiveFlag_TC(USARTx))
 ;
}
/* USER CODE END 1 */
/**
 * @brief This function handles GPDMA1 Channel 2 global interrupt.
 */
void GPDMA1_Channel2_IRQHandler(void)
{
 /* USER CODE BEGIN GPDMA1_Channel2_IRQn 0 */
 if (LL_DMA_IsActiveFlag_TO(GPDMA1, LL_DMA_CHANNEL_2)) {
 LL_DMA_ClearFlag_TO(GPDMA1, LL_DMA_CHANNEL_2);
 }

 if (LL_DMA_IsActiveFlag_SUSP(GPDMA1, LL_DMA_CHANNEL_2)) { 
 LL_DMA_ClearFlag_SUSP(GPDMA1, LL_DMA_CHANNEL_2);
 }
 
 if (LL_DMA_IsActiveFlag_USE(GPDMA1, LL_DMA_CHANNEL_2)) { 
 LL_DMA_ClearFlag_USE(GPDMA1, LL_DMA_CHANNEL_2);
 }
 
 if (LL_DMA_IsActiveFlag_ULE(GPDMA1, LL_DMA_CHANNEL_2)) { 
 LL_DMA_ClearFlag_ULE(GPDMA1, LL_DMA_CHANNEL_2);
 }
 
 if (LL_DMA_IsActiveFlag_DTE(GPDMA1, LL_DMA_CHANNEL_2)) {
 LL_DMA_ClearFlag_DTE(GPDMA1, LL_DMA_CHANNEL_2);
 }
 
 if (LL_DMA_IsActiveFlag_HT(GPDMA1, LL_DMA_CHANNEL_2)) {
 LL_DMA_ClearFlag_HT(GPDMA1, LL_DMA_CHANNEL_2);
 }
 
 if (LL_DMA_IsActiveFlag_TC(GPDMA1, LL_DMA_CHANNEL_2)) { 
 LL_DMA_ClearFlag_TC(GPDMA1, LL_DMA_CHANNEL_2);
 }
 
 if (LL_DMA_IsActiveFlag_IDLE(GPDMA1, LL_DMA_CHANNEL_2)) {
 // LL_DMA_ClearFlag_IDLE(GPDMA1, LL_DMA_CHANNEL_2);
 }
 
 if (LL_DMA_IsActiveFlag_MIS(GPDMA1, LL_DMA_CHANNEL_2)) { 
 // LL_DMA_ClearFlag_MIS(GPDMA1, LL_DMA_CHANNEL_2);
 }
 /* USER CODE END GPDMA1_Channel2_IRQn 0 */
 /* USER CODE BEGIN GPDMA1_Channel2_IRQn 1 */

 /* USER CODE END GPDMA1_Channel2_IRQn 1 */
}

 

If I use the USART transmission without DMA, it works fine (using USART_Printf function).

But using the DMA nothing is transmitted (using USART_DMA_Printf function), and neither IRQ is generated.

So what is wrong in this setup?

Thanks,

s.

    This topic has been closed for replies.

    1 reply

    Technical Moderator
    February 26, 2025

    Hi @simo zz ,

    Try to start with an example ready to use available examples in STM32CubeH7RS like STM32CubeH7RS/Projects/NUCLEO-H7S3L8/Examples/USART/USART_SlaveMode_DMA.

    You can even take the .ioc file and update it depending on your application case.

    -Amel