cancel
Showing results for 
Search instead for 
Did you mean: 

UART Transmit with DMA Issues

R H
Associate II
Posted on January 08, 2017 at 20:53

Edit: compilable example code posted below in comments. The problem now with this code is that too many messages are being sent in addition to the wrong data being sent.

I am using VisualGDB and a STM32F303RET6.

I am having trouble trying to get the UART transmitter to work with the DMA controller. The UART transmitter works fine when I do a single transmission by manually updating the TDR. But, when I use the DMA controller, the data that is transmitted is not what is expected.

It appears to me that the data being sent is coming from a memory address other than the address that I have told the DMA controller to point to in CMAR. But, when looking at the address of the data and the address in the CMAR, it matches. So, I am not sure what the problem is but I do wonder if the problem is something I am not understanding with C and C++. Since VisualGDB operates in C++ files, I write C++ wrappers for the C functions that do the heavy lifting with modifying the registers. When, I tried using a data pointer for the memory address to read from that was compiled under the C compiler, I could not get the DMA controller to operate correctly. When I pass the DMA controller a memory address from a pointer that was compiled under the C++ compiler, it works but sends the wrong data.

Hopefully, someone can help me.

Here is my code to setup the DMA controller for UART1.

void c_uart_setup_dma(uint8_t port, uint8_t * data)

{

uint16_t reg;

uint32_t * tmp;

volatile uint32_t src_address;

volatile uint32_t dest_address;

DMA_TypeDef * dma;

DMA_Channel_TypeDef * dma_chan;

USART_TypeDef * uart;

// Assign the DMA channel and DMA controller based on the UART port

c_uart_setdma(port, dma, dma_chan);

// Assign the pointer for the UART port based on which one we want to modify

c_uart_setport(port, uart);

// set the source and destination address locations

tmp = (uint32_t*)&data;

src_address = *(uint32_t*)tmp;

dest_address = (uint32_t)&uart->TDR;

// test to see if this changes the data at the correct location

*data = 0;

*(data + 1) = 0;

*(data + 2) = 0;

// configure the DMA channel

// set the RCC clock for the DMA controller

RCC->AHBENR |= 0x01; // enable DMA1

RCC->AHBENR |= 0x02; // enable DMA2

// set the peripheral register address data will be read from/written to

dma_chan->CPAR = dest_address;

// set the memory address data will be read from/written to

dma_chan->CMAR = src_address;

// configure the total number of data items to be transferred

dma_chan->CNDTR = (uint16_t)0x03;

// configure the channel priority, data transfer direction, mode, data size, and interrupt

reg = DMA_CCR_PL | DMA_CCR_DIR | DMA_CCR_TEIE | DMA_CCR_TCIE | DMA_CCR_MINC;

dma_chan->CCR |= reg;

// activate the DMA channel

dma_chan->CCR |= DMA_CCR_EN;

// clear the TC flag

uart->ICR |= USART_ICR_TCCF;

// enable the DMA transmitter in the UART

//uart->CR3 |= USART_CR3_DMAT;

// enable the DMA IRQ

c_uart_set_dma_irq(port);

}

And here is what the debug shows when stopped at the end of this setup function:

0690X0000060MopQAE.jpg

Here is the code for my transmit function:

void c_uart_transmit_dma(uint8_t port)

{

uint32_t * source;

uint32_t address;

USART_TypeDef * uart;

DMA_TypeDef * dma;

DMA_Channel_TypeDef * dma_chan;

// set the UART port

c_uart_setport(port, uart);

// set the DMA channel

c_uart_setdma(port, dma, dma_chan);

// disable the DMA channel

dma_chan->CCR &= ~DMA_CCR_EN;

// set the number of bytes to transfer

dma_chan->CNDTR |= 0x03;

// activate the DMA channel

dma_chan->CCR |= DMA_CCR_EN;

// clear the TC flag by writing the TCCF bit in ICR

uart->ICR |= USART_ICR_TCCF;

// enable the DMA transmitter in the UART

uart->CR3 |= USART_CR3_DMAT;

}

Any help would be greatly appreciated!

5 REPLIES 5
Posted on January 08, 2017 at 21:58

This is a mess. How is c_uart_setup_dma() called and what is c_uart_transmit_dma() supposed to do?

Better than explaining the above, construct and post a minimal but complete compilable code exhibiting the problem.

JW

Posted on January 08, 2017 at 22:13

Allright. Let me see if I can put something together.

Posted on January 08, 2017 at 23:51

 ,

 ,

Ok this should work.

extern 'C'

 ,

{

 ,

♯ include <,stm32f3xx_hal.h>,

 ,

}

 ,

 ,

uint8_t * out_data,

 ,

 ,

extern 'C' void DMA_transmit(),

 ,

extern 'C' void DMA_setup(uint8_t * data),

 ,

void initialize_data(),

 ,

 ,

extern 'C'

 ,

void set_systemclock()

 ,

{

 ,

 , ,  ,uint32_t freq = 0,

 ,

 , ,  ,uint32_t mask,

 ,

 , ,  ,

 ,

 , ,  ,freq = HAL_RCC_GetSysClockFreq(), , ,  ,// Check the system clock frequency

 ,

 , ,  ,

 ,

 , ,  ,// configure and set the PLL

 ,

 , ,  ,RCC->,CFGR |= (2 <,<, 0), , ,  , , ,  ,// SWS: PLL used as system clock

 ,

 , ,  ,RCC->,CFGR |= (0x7 <,<, 18), , ,  ,// PLLMUL: input x 9

 ,

 , ,  ,RCC->,CR |= (1 <,<, 24), , ,  , , ,  ,// PLLON: Enable the PLL

 ,

 , ,  ,

 ,

 , ,  ,// wait for the PLL to be locked and ready

 ,

 , ,  ,mask = (1 <,<, 25),

 ,

 , ,  ,mask &,= RCC->,CR,

 ,

 , ,  ,while (mask == 0)

 ,

 , ,  ,{

 ,

 , ,  , , ,  ,mask = (1 <,<, 25),

 ,

 , ,  , , ,  ,mask &,= RCC->,CR,

 ,

 , ,  ,}

 ,

 , ,  ,

 ,

 , ,  ,freq = HAL_RCC_GetSysClockFreq(), , ,  ,// Check the system clock frequency

 ,

}

 ,

 ,

extern 'C'

 ,

void SysTick_Handler(void)

 ,

{

 ,

 , ,  ,HAL_IncTick(),

 ,

 , ,  ,HAL_SYSTICK_IRQHandler(),

 ,

}

 ,

 ,

 ,

extern 'C'

 ,

int main(void)

 ,

{

 ,

 , ,  ,HAL_Init(),

 ,

 ,

 , ,  ,

 ,

 , ,  ,uint32_t system_freq,

 ,

 , ,  ,set_systemclock(), , ,  , , ,  , , ,  , , ,  , , ,  , , ,  , , ,  ,// set the system clock: HSI ->, PLL = 72 MHz

 ,

 , ,  ,system_freq = HAL_RCC_GetSysClockFreq(), , ,  ,// Check the system clock frequency

 ,

 , ,  ,

 ,

 , ,  ,initialize_data(),

 ,

 , ,  ,DMA_setup(out_data),

 ,

 , ,  ,

 ,

 , ,  ,for (,,)

 ,

 , ,  ,{

 ,

 , ,  , , ,  ,

 ,

 , ,  , , ,  ,HAL_Delay(200),

 ,

 , ,  , , ,  ,DMA_transmit(),

 ,

 , ,  , , ,  ,

 ,

 , ,  ,}

 ,

}

 ,

 ,

 ,

void initialize_data()

 ,

{

 ,

 , ,  ,out_data = new uint8_t[3],

 ,

 , ,  ,

 ,

 , ,  ,out_data[0] = 2,

 ,

 , ,  ,out_data[1] = 4,

 ,

 , ,  ,out_data[2] = 8,

 ,

}

 ,

 ,

void DMA_setup(uint8_t * data)

 ,

{

 ,

 , ,  ,uint16_t reg,

 ,

 , ,  ,uint32_t * tmp,

 ,

 , ,  ,volatile uint32_t src_address,

 ,

 , ,  ,volatile uint32_t dest_address,

 ,

 ,

 , ,  ,

 ,

 , ,  ,// turn on the RCC clock for the GPIO port

 ,

 , ,  ,RCC->,AHBENR |= (1 <,<, 17),

 ,

 , ,  ,

 ,

 , ,  ,// initialize pin

 ,

 , ,  ,GPIOA->,OSPEEDR |= (3 <,<, (9 * 2)), , ,  ,// high speed

 ,

 , ,  ,GPIOA->,OTYPER |= (0 <,<, 9), , ,  , , ,  , , ,  ,// output state push pull

 ,

 , ,  ,GPIOA->,PUPDR |= (2 <,<, (9 * 2)), , ,  , , ,  ,// set output with pull down

 ,

 , ,  ,GPIOA->,MODER |= (2 <,<, (9 * 2)), , ,  , , ,  ,// configure port mode

 ,

 , ,  ,GPIOA->,AFR[1] |= (7 <,<, (1 * 4)), , ,  , , ,  ,// connect the I/O to the desired alternate function

 ,

 , ,  ,

 ,

 , ,  ,// turn on the RCC clock for the UART

 ,

 , ,  ,RCC->,APB2ENR |= (1 <,<, 14),

 ,

 , ,  ,

 ,

 , ,  ,// set the buadrate

 ,

 , ,  ,uint32_t sysclock = HAL_RCC_GetSysClockFreq(),

 ,

 , ,  ,uint16_t baudrate = (sysclock / 2) / baudrate, // divide by 2 because AHB bus is half?? , ,  ,

 ,

 , ,  ,USART1->,BRR = baudrate,

 ,

 , ,  ,

 ,

 , ,  ,// configure and initialize the USART

 ,

 , ,  ,USART1->,CR1 |= (USART_CR1_UE | USART_CR1_TE | USART_CR1_TCIE),

 ,

 , ,  ,

 ,

 , ,  ,// clear the TC flag

 ,

 , ,  ,USART1->,ICR |= USART_ICR_TCCF,

 ,

 , ,  ,

 ,

 , ,  ,// set the source and destination address locations

 ,

 , ,  ,tmp = (uint32_t*)&,data,

 ,

 , ,  ,src_address = *(uint32_t*)tmp,

 ,

 , ,  ,dest_address = (uint32_t)&,USART1->,TDR,

 ,

 , ,  ,

 ,

 , ,  ,// configure the DMA channel

 ,

 , ,  ,// set the RCC clock for the DMA controller

 ,

 , ,  ,RCC->,AHBENR |= 0x01, // enable DMA1

 ,

 , ,  ,RCC->,AHBENR |= 0x02, // enable DMA2

 ,

 , ,  ,

 ,

 , ,  ,// set the peripheral register address data will be read from/written to

 ,

 , ,  ,DMA1_Channel4->,CPAR = dest_address,

 ,

 ,

 , ,  ,// set the memory address data will be read from/written to

 ,

 , ,  ,DMA1_Channel4->,CMAR = src_address,

 ,

 , ,  ,

 ,

 , ,  ,// configure the total number of data items to be transferred

 ,

 , ,  ,DMA1_Channel4->,CNDTR |= (uint16_t)0x03,

 ,

 , ,  ,

 ,

 , ,  ,// configure the channel priority, data transfer direction, mode, data size, and interrupt

 ,

 , ,  ,reg = DMA_CCR_PL | DMA_CCR_DIR | DMA_CCR_TEIE | DMA_CCR_TCIE | DMA_CCR_MINC,

 ,

 , ,  ,DMA1_Channel4->,CCR |= reg,

 ,

 , ,  ,

 ,

 , ,  ,// activate the DMA channel

 ,

 , ,  ,DMA1_Channel4->,CCR |= DMA_CCR_EN,

 ,

 , ,  ,

 ,

 , ,  ,// clear the TC flag

 ,

 , ,  ,USART1->,ICR |= USART_ICR_TCCF,

 ,

 , ,  ,

 ,

 , ,  ,// enable the DMA IRQ

 ,

 , ,  ,NVIC_ClearPendingIRQ(DMA1_Channel4_IRQn),

 ,

 , ,  ,NVIC_SetPriority(DMA1_Channel4_IRQn, 13),

 ,

 , ,  ,NVIC_EnableIRQ(DMA1_Channel4_IRQn),

 ,

}

 ,

 ,

 ,

void DMA_transmit()

 ,

{

 ,

 , ,  ,// disable the DMA channel

 ,

 , ,  ,DMA1_Channel4->,CCR &,= ~DMA_CCR_EN,

 ,

 , ,  ,

 ,

 , ,  ,// set the number of bytes to transfer

 ,

 , ,  ,DMA1_Channel4->,CNDTR |= (uint16_t)0x03,

 ,

 , ,  ,

 ,

 , ,  ,// activate the DMA channel

 ,

 , ,  ,DMA1_Channel4->,CCR |= DMA_CCR_EN,

 ,

 , ,  ,

 ,

 , ,  ,// clear the TC flag by writing the TCCF bit in ICR

 ,

 , ,  ,USART1->,ICR |= USART_ICR_TCCF,

 ,

 , ,  ,

 ,

 , ,  ,// enable the DMA transmit request in UART

 ,

 , ,  ,USART1->,CR3 |= USART_CR3_DMAT,

 ,

}

 ,

 ,

 ,

extern 'C' void DMA1_Channel4_IRQHandler()

 ,

{

 ,

 , ,  ,

 ,

 , ,  ,// disable the DMA transmit request in UART

 ,

 , ,  ,USART1->,CR3 &,= ~USART_CR3_DMAT,

 ,

 , ,  ,

 ,

 , ,  ,// clear the interrupt flag that was set by hardware

 ,

 , ,  ,DMA1->,IFCR |= DMA_IFCR_CGIF4 | DMA_IFCR_CTEIF4,

 ,

}
Posted on January 10, 2017 at 06:05

Edit: compilable example code posted below in comments. The problem now with this code is that too many messages are being sent in addition to the wrong data being sent.

I don't see anything wrong in the code.

I wouldn't enable DMA if DMA_setup() since you don't intend to transmit at that point.

And I definitively wouldn't OR in     

DMA1_Channel4->CNDTR |= (uint16_t)0x03;

in DMA_transmit(),

But IMO neither of these grant described symptoms.

Can't this be UART-related issue anyway, such as baudrate mismatch problem, or problematic transmission circuitry (ground fault for example)? You could try to transmit a similar (but not the same to be distinguishable) message 'manually' by polling, in the same program, before starting DMA.

Also, I am no C++ expert. From C standpoint the play with tmp and src_address in DMA_setup() is unnecessary,

src_address = (uint32_t)data;

should work the same; again, this should not be source of described problems.

Also, I'd try with a static/global data buffer, rather than put it on heap (can't heap collide with stack?)

JW

Posted on January 10, 2017 at 22:10

Hey Jan,

Big thanks for looking into this for me. I was able to get everything working. It turns out that the correct data was being sent, I was just not interpreting it correctly in the logic analyzer. One of the problems, as you guessed, was a misconfiguration in the UART. I did not enable 'send MSB first' which is what the MIDI protocol on the logic analyzer was expecting. I retried my code but used 1, 2, 4 on the data to be sent and noticed the correct high-low on the data stream but saw that it was 'backwards'.

Between that and moving the code for declaring the pointer to the data stream in the C++ portion of the code, everything is working now.