Skip to main content
R H
Associate
January 8, 2017
Question

UART Transmit with DMA Issues

  • January 8, 2017
  • 2 replies
  • 1778 views
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!

    This topic has been closed for replies.

    2 replies

    waclawek.jan
    Super User
    January 8, 2017
    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

    R H
    R HAuthor
    Associate
    January 8, 2017
    Posted on January 08, 2017 at 22:13

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

    waclawek.jan
    Super User
    January 10, 2017
    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

    R H
    R HAuthor
    Associate
    January 10, 2017
    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.