cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F072CBU6 DMA UART Transfer Issues

Churby
Visitor

I am using the DMA to transfer data from UART 1 (Channel 2) and UART 2 (Channel 4). Every once and awhile the data seen on UART 2 which has a lower DMA priority will be "corrupted" by the UART 1 data. I know this because when I disable UART 1 routines, UART 2 receives perfectly everytime. This is never an issue with transmission on UART 2 even when UART 1 is enabled.

This is on the STM32F072CBU6 MCU. Both DMA protocols are initialized identically for UART 1 and UART 2.

I will post most of the code related to these functions.

Any ideas would be greatly appreciated!

 

UART initializations:

uint32_t Tmp;

huart1.Instance = USART1;

STM32_GPIO_Init(RS485_TXD_GPIO_Port, RS485_TXD_Pin, GPIO_MODE_AF_PP, GPIO_PULLUP, GPIO_SPEED_FREQ_HIGH, GPIO_AF1_USART1, 0);
STM32_GPIO_Init(RS485_RXD_GPIO_Port, RS485_RXD_Pin, GPIO_MODE_AF_PP, GPIO_PULLUP, GPIO_SPEED_FREQ_HIGH, GPIO_AF1_USART1, 0);

hdma_usart1_tx.Instance = DMA1_Channel2;
hdma_usart1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_usart1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_usart1_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_usart1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_usart1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_usart1_tx.Init.Mode = DMA_NORMAL;
hdma_usart1_tx.Init.Priority = DMA_PRIORITY_LOW;

Tmp = hdma_usart1_tx.Instance->CCR;
Tmp &= ((uint32_t)~(DMA_CCR_PL | DMA_CCR_MSIZE | DMA_CCR_PSIZE | DMA_CCR_MINC | DMA_CCR_PINC | DMA_CCR_CIRC | DMA_CCR_DIR));
Tmp |= DMA_MEMORY_TO_PERIPH | DMA_PINC_DISABLE | DMA_MINC_ENABLE |
DMA_PDATAALIGN_BYTE | DMA_MDATAALIGN_BYTE | DMA_NORMAL | DMA_PRIORITY_LOW;
hdma_usart1_tx.Instance->CCR = Tmp;
hdma_usart1_tx.ChannelIndex = (((uint32_t)hdma_usart1_tx.Instance - (uint32_t)DMA1_Channel1) / ((uint32_t)DMA1_Channel2 - (uint32_t)DMA1_Channel1)) << 2u;
hdma_usart1_tx.DmaBaseAddress = DMA1;
hdma_usart1_tx.XferCpltCallback = NULL;

huart1.hdmatx = &hdma_usart1_tx;
hdma_usart1_tx.Parent = &huart1;

STM32_NVIC_SetPriority(USART1_IRQn, 0, 0);
STM32_NVIC_EnableIRQ(USART1_IRQn);

USART1->CR1 &= ~USART_CR1_UE;
USART1->CR1 = 0x0C;
USART1->CR2 = 0x00;
USART1->CR3 = 0x3000;
USART1->BRR = 417;
USART1->CR1 = 0x2D;

 

UART IRQ Handler:

    uint32_t ISRFlags;
    uint32_t ControlReg1its;

    ISRFlags = huart->Instance->ISR;
    ControlReg1its = huart->Instance->CR1;

    //
    // Clear Errors - even if there aren't any
    //
    huart->Instance->ICR = UART_CLEAR_PEF | UART_CLEAR_FEF | UART_CLEAR_NEF | UART_CLEAR_OREF;

    /* UART in mode Receiver */
    if (huart->Instance == USART1) {
        if (((ISRFlags & USART_ISR_RXNE) != 0) && ((ControlReg1its & USART_CR1_RXNEIE) != 0)) {
            SerialUsart1.DmaRingBuffer[SerialUsart1.RingBufferIn++] = (uint8_t)huart->Instance->RDR;
            return;
        }
    }
    else if (huart->Instance == USART2) {
        if (((ISRFlags & USART_ISR_RXNE) != 0) && ((ControlReg1its & USART_CR1_RXNEIE) != 0)) {
            SerialUsart2.DmaRingBuffer[SerialUsart2.RingBufferIn++] = (uint8_t)huart->Instance->RDR;
            return;
        }
    }

    /* UART in mode Transmitter (transmission end) -----------------------------*/
    if (((ISRFlags & USART_ISR_TC) != 0) && ((ControlReg1its & USART_CR1_TCIE) != 0)) {
        UART_EndTransmit_IT(huart);
    }

 

DMA Initialization:

    uint32_t Tmp;

    Tmp = hdma->Instance->CCR;
    Tmp &= ((uint32_t)~(DMA_CCR_PL | DMA_CCR_MSIZE | DMA_CCR_PSIZE | DMA_CCR_MINC | DMA_CCR_PINC | DMA_CCR_CIRC | DMA_CCR_DIR));
    Tmp |= hdma->Init.Direction |
        hdma->Init.PeriphInc | hdma->Init.MemInc |
        hdma->Init.PeriphDataAlignment | hdma->Init.MemDataAlignment |
        hdma->Init.Mode | hdma->Init.Priority;
    hdma->Instance->CCR = Tmp;
    hdma->ChannelIndex = (((uint32_t)hdma->Instance - (uint32_t)DMA1_Channel1) / ((uint32_t)DMA1_Channel2 - (uint32_t)DMA1_Channel1)) << 2u;
    hdma->DmaBaseAddress = DMA1;
    hdma->XferCpltCallback = NULL;

 

DMA Interrupt Handler:

    uint32_t FlagIt = hdma->DmaBaseAddress->ISR;
    uint32_t SourceIt = hdma->Instance->CCR;

    /* Transfer Complete Interrupt management ***********************************/

    if ((RESET != (FlagIt & (DMA_FLAG_TC1 << hdma->ChannelIndex))) && (RESET != (SourceIt & DMA_IT_TC))) {
        if ((hdma->Instance->CCR & DMA_CCR_CIRC) == 0u) {
            /* Disable the transfer complete  & transfer error interrupts */
            /* if the DMA mode is not CIRCULAR */
            hdma->Instance->CCR &= ~(DMA_IT_TC | DMA_IT_TE);
        }

        /* Clear the transfer complete flag */
        hdma->DmaBaseAddress->IFCR = DMA_FLAG_TC1 << hdma->ChannelIndex;

        if (hdma->XferCpltCallback != NULL) {
            /* Transfer complete callback */
            hdma->XferCpltCallback(hdma);
        }
        /* Transfer Error Interrupt management ***************************************/
    } else if ((RESET != (FlagIt & (DMA_FLAG_TE1 << hdma->ChannelIndex))) && (RESET != (SourceIt & DMA_IT_TE))) {
        /* When a DMA transfer error occurs */
        /* reset the processor */
        Error_Handler(FILE_DMA, __LINE__);
    } else {
    }
6 REPLIES 6
Karl Yamashita
Lead II

Use the </> to post your code so it's formatted.

If you find my answers useful, click the accept button so that way others can see the solution.
BarryWhit
Senior III

You say you use DMA to transfer data "from" the UARTs, but your DMA is tied to tx handle and uses DMA_MEMORY_TO_PERIPH. Which is it?

 

I normally use Cube generated code but, doesn't the initializing of values under hdma_usart1_tx.Init in preperation for calling some kind of HAL init function? without one, where are these values actually used?

 

How is DmaRingBuffer actually used as a ring buffer? is SerialUsartX.RingBufferIn a uint8_t?

 

When you say the data "seen" on uart2 is corrupted, where are you looking? are you talking about the data received by the other end?

 

 

- If someone's post helped resolve your issue, thank them by clicking "Accept as Solution".
- Once you've solved your issue, please post an update with any further details.

Hi Barry,

 

I accidentally marked your reply as an accepted solution...

You say you use DMA to transfer data "from" the UARTs, but your DMA is tied to tx handle and uses DMA_MEMORY_TO_PERIPH. Which is it? It is confusing that it is tied to the tx handle, however, the interrupt routine for the DMA will receive data if the RXNE flag is set and will also send data until the transmit complete flag is set. Potentially, DMA_MEMORY_TO_PERIPH is what is causing this issue and I will play around with this.

 

I normally use Cube generated code but, doesn't the initializing of values under hdma_usart1_tx.Init in preperation for calling some kind of HAL init function? without one, where are these values actually used? I inherited this code and am starting to release that likely the instance of dma_usart1_tx should be replaced with hdma and called with both tx and rx... something else I will work with.

 

How is DmaRingBuffer actually used as a ring buffer? is SerialUsartX.RingBufferIn a uint8_t? As data is stored in the ring buffer, it is constantly checked for synchronization bytes along with a data length, once it finds the sync, it starts storing data. Yes, RingBufferIn is a uint8_t.

 

When you say the data "seen" on uart2 is corrupted, where are you looking? are you talking about the data received by the other end? On my o-scope all the data sent from an external source is correct, however, the data red by my MCU will have most bytes correct with a few wrong. Enough bytes where it synchronizes.

Karl Yamashita
Lead II

Your code

SerialUsart1.DmaRingBuffer[SerialUsart1.RingBufferIn++] = (uint8_t)huart->Instance->RDR;

is copying 1 byte at a time from the uart data register. If you're using the DMA, you should be pointing the DMA to an array to hold multiple bytes. And in the interrupt you would copy those multiple bytes to your ring buffer.

Since you are only interrupting on 1 bytes at a time, the DMA isn't needed. 

 

If you're receiving data in packets with some idle time in between packets, you can use DMA with idle. See this https://github.com/karlyamashita/Nucleo-G431RB_Three_UART/wiki

 

If you find my answers useful, click the accept button so that way others can see the solution.

@Churby wrote:

I accidentally marked your reply as an accepted solution...


You can un-mark it:

https://community.st.com/t5/stm32-mcus-embedded-software/sd-card-no-file-system-error/m-p/641422/highlight/true#M45514

 

Hi Karl,

 

Thank you for this - I will switch it over to have the DMA hold multiple bytes.