AnsweredAssumed Answered

UART Transmit with DMA Issues

Question asked by Fan.Tool on Jan 8, 2017
Latest reply on Jan 10, 2017 by Fan.Tool

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
    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


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


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!