cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H7 Huge program with UART Tx using DMA

CLeo.1
Senior II

Hey guys!

This probably came up 5000 times, however I did try to look for a solution before I ask this one. Couldn't find one.

I am having issues transmitting data via uart with DMA.

Problem:

The problems I am having is that the RxBuffer is not receiving anything when trying to transmit data.

What I have tried:

  • initialize the DMA before the peripheral (Nothing).
  • Clearing TC & making sure TXE = 1 before any sort of data
  • Read that if you're getting a FIFO error and its direct mode just ignore, however Rx is not being received what so ever

Suspicious things:

I know its a DMA issue as if I write to the peri directly, for example UART4->TDR = 0x05, the Uart Rx receives it no problem.

another weird thing that has happened is during debugging. When I step into the function that deals with transmission, if I step over each block of code it works, however if I reset the debugging process and instead of stepping through the function and just run it all one shot, it doesnt work.

The logic in code:

  1. Init UART (CLOCK, GPIO, DMA, UART, INTERRUPT)
  2. Send data into a function where it will then transfer into the buffer associated with the DMA, it makes sure TXE = 1 then enables the DMA
  3. Waits for the TC interrupt to trigger and clears it
  4. Done

Main:

#include <stdint.h>
 
#include "EXIT_FACTORY.h"
#include "CLOCK_FACTORY.h"
#include "I2S_FACTORY.h"
#include "UART_FACTORY.h"
#include "stm32h753xx.h"
#include "OPCODE.h"
 
static int I2S1_TxBUFF[4096];
static int I2S1_RxBUFF[4096];
 
static uint8_t UART4_RxBUFF[1];
static uint8_t UART4_TxBUFF[1];
 
int main (void) {
 
	INIT_CLOCK();
	INIT_I2S(I2S1_RxBUFF, I2S1_TxBUFF);
	INIT_UART(UART4_RxBUFF, UART4_TxBUFF);
	INIT_EXIT();
 
	SEND_UART_ERROR(0x05, UART4_TxBUFF);
 
	//TIMER
	while(1) {
 
	}
 
 
}
 
 
void UART4_IRQHandler() {
 
	if (((UART4->ISR) & (USART_ISR_TC)) != 0) {
		 UART4->ICR |= USART_ICR_TCCF;
	}
 
}
 
void DMA1_Stream2_IRQHandler() {
 
	if (((DMA1->LISR) & (DMA_LISR_TCIF2)) != 0) {
		DMA1->LIFCR |= DMA_LIFCR_CTCIF2;
	}
 
}

UART_FACTORY.c (Includes all the functions)

/*
 * UART_FACTORY.c
 *
 *  Created on: Apr. 28, 2021
 *      Author: Christopher
 */
#include <stdint.h>
#include "stm32h753xx.h"
 
 
void SET_UART_CLOCK() {
 
	RCC->APB1LENR |= RCC_APB1LENR_UART4EN;
	RCC->D2CCIP2R &= ~(RCC_D2CCIP2R_USART28SEL);
	RCC->D2CCIP2R |= RCC_D2CCIP2R_USART234578SEL_PLLCLK;
 
}
 
 
void SET_UART_GPIO() {
 
	RCC->AHB4ENR &= ~(RCC_AHB4ENR_GPIODEN);
	RCC->AHB4ENR |= RCC_AHB4ENR_GPIODEN;
 
	GPIOD->MODER &= ~((GPIO_MODER_MODE0) | (GPIO_MODER_MODE1));
	GPIOD->MODER |= (GPIO_MODER_MODE0_AF) | (GPIO_MODER_MODE1_AF);
 
	GPIOD->AFR[0] &= ~((GPIO_AFRL_AFSEL0) | (GPIO_AFRL_AFSEL1));
	GPIOD->AFR[0] |= (GPIO_AFRL_AFSEL0_UART4_RX) | (GPIO_AFRL_AFSEL1_UART4_TX);
 
}
 
void SET_UART_INTERRUPT() {
 
	NVIC_EnableIRQ(DMA1_Stream2_IRQn);
	NVIC_SetPriority(DMA1_Stream2_IRQn,1);
 
	NVIC_EnableIRQ(UART4_IRQn);
	NVIC_SetPriority(UART4_IRQn,1);
 
}
 
void SET_UART_DMA(uint8_t * UART4_RxBUFF, uint8_t * UART4_TxBUFF) {
 
	// ENABLE DMA
	RCC->AHB1ENR &= ~(RCC_AHB1ENR_DMA1EN);
	RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN;
 
	//Setup DMA
	DMAMUX1_Channel2->CCR &= ~(DMAMUX_CxCR_DMAREQ_ID);
	DMAMUX1_Channel2->CCR |=  (DMAMUX_CxCR_DMAREQ_ID_UART4_Rx);
 
	DMAMUX1_Channel3->CCR &= ~(DMAMUX_CxCR_DMAREQ_ID);
	DMAMUX1_Channel3->CCR |=  (DMAMUX_CxCR_DMAREQ_ID_UART4_Tx);
 
	DMA1_Stream2->CR &= ~((DMA_SxCR_CT)    |
			              (DMA_SxCR_PL)    |
						  (DMA_SxCR_MSIZE) |
						  (DMA_SxCR_PSIZE) |
						  (DMA_SxCR_MINC)  |
						  (DMA_SxCR_CIRC)  |
						  (DMA_SxCR_DIR)   |
						  (DMA_SxCR_PFCTRL)|
						  (DMA_SxCR_TCIE)  |
						  (DMA_SxCR_HTIE));
 
	DMA1_Stream2->CR |=   (DMA_SxCR_CT_MEM0)           |
			              (DMA_SxCR_PL_HIGH)           |
						  (DMA_SxCR_MSIZE_8BIT)        |
						  (DMA_SxCR_PSIZE_8BIT)        |
						  (DMA_SxCR_CIRC)              |
						  (DMA_SxCR_DIR_PERI_TO_MEM)   |
						  (DMA_SxCR_PFCTRL_DMA_FLOW)   |
						  (DMA_SxCR_TCIE);
 
 
	DMA1_Stream3->CR &= ~((DMA_SxCR_CT)    |
			              (DMA_SxCR_PL)    |
						  (DMA_SxCR_MSIZE) |
						  (DMA_SxCR_PSIZE) |
						  (DMA_SxCR_MINC)  |
						  (DMA_SxCR_CIRC)  |
						  (DMA_SxCR_DIR)   |
						  (DMA_SxCR_PFCTRL)|
						  (DMA_SxCR_TCIE)  |
						  (DMA_SxCR_HTIE));
 
	DMA1_Stream3->CR |=   (DMA_SxCR_CT_MEM0)            |
			              (DMA_SxCR_PL_HIGH)            |
						  (DMA_SxCR_MSIZE_8BIT)         |
						  (DMA_SxCR_PSIZE_8BIT)         |
						  (DMA_SxCR_DIR_MEM_TO_PERI)    |
						  (DMA_SxCR_PFCTRL_DMA_FLOW);
 
	DMA1_Stream2->NDTR = 0x01;
	DMA1_Stream3->NDTR = 0x01;
 
	DMA1_Stream2->PAR  = (uint32_t)&UART4->RDR;
	DMA1_Stream2->M0AR = (uint32_t)UART4_RxBUFF;
 
	DMA1_Stream3->PAR  = (uint32_t)&UART4->TDR;
	DMA1_Stream3->M0AR = (uint32_t)UART4_TxBUFF;
 
	DMA1_Stream2->CR |= DMA_SxCR_EN;
 
}
 
void SET_UART() {
 
	UART4->BRR = 0x1047;
	UART4->CR3 |= (USART_CR3_DMAT) | (USART_CR3_DMAR);
	UART4->CR1 |= (USART_CR1_M1_8BITS) | (USART_CR1_M0_8BITS) | (USART_CR1_RE) | (USART_CR1_TE) | (USART_CR1_TCIE);
	UART4->CR2 |= (USART_CR2_STOP_1BIT);
	UART4->CR1 |= USART_CR1_UE;
	UART4->ICR |= USART_ICR_TCCF;
 
}
 
void SEND_UART_ERROR(uint8_t ERROR_CODE, uint8_t * UART4_TxBUFF) {
 
	if (((UART4->ISR) & (USART_ISR_TXE_TXFNF)) != 0) {
		UART4_TxBUFF[0] = ERROR_CODE;
		DMA1_Stream3->CR |= DMA_SxCR_EN;
	}
}
 
void INIT_UART(uint8_t * UART4_RxBUFF, uint8_t * UART4_TxBUFF) {
 
	SET_UART_CLOCK();
	SET_UART_GPIO();
	SET_UART_DMA(UART4_RxBUFF, UART4_TxBUFF);
    SET_UART();
	SET_UART_INTERRUPT();
 
}

 What I feel like is happening:

A FIFOE (FIFO ERROR) is presented in the DMA Status flag, saying there's some type of underrun, or overrunn error.

When this error triggers it disables the DMA completely before it can send out the value from the buffer into the Peripheral.

The reason why when I step into the function and do it step by step I think is the DMA is maybe finally ready, or the data is there when is see it. What i am trying to say I think something might be still trying to start up while I am trying to shove data in it.

the real question is I guess is why the DMA is fully ready when in debug mode oppose to just free running

UPDATE 1:

Placed a breakpoint in the Rx Uart handler. Pressing the play button while in debug mode brings me right into the handler which means that Rx is receiving something, however it does not get populated, so am I transferring the wrong info?

UPDATE 2:

a band-aid solution was not to use DMA with the Tx, but just manually checking for TXE and TC and just hard sending values into the UART->TDR register, been working flawlessly. Is there just a problem with DMA Tx?

1 REPLY 1
Imen.D
ST Employee

Hello @CLeo.1​ ,

Thanks for your post and for sharing your issue in detail.

For DMA usage with STM32H7 devices in case DMA is not working, or the transmitted/received data are corrupted, please consider this FAQ article: DMA is not working on STM32H7 devices

Imen

When your question is answered, please close this topic by clicking "Accept as Solution".
Thanks
Imen