1
\$\begingroup\$

I am using an STM32L1 on a Nucleo-L152RE board. I have to devices I control through serial running at rather high baudrates, so I am trying to enable DMA on the USART. With the code below I can launch one Rx DMA, but the second one, which I start from the ISR never completes:

void uart_receive_dma() {

    DMA_InitTypeDef  DMA_InitStructure;

    DMA_DeInit(DMA1_Channel5);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &USART1->DR;
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) buffer;  
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize = UART_PACKET_SIZE;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
    DMA_Init(uart_rx_dma_channel[handler->uart_index], &DMA_InitStructure);

    /* RX */
    DMA_ITConfig(DMA1_Channel5, DMA_IT_TC, ENABLE);
    DMA_Cmd(DMA1_Channel5, ENABLE);
    USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
}

void DMA1_Channel5_IRQHandler(void){ 

    DMA_ClearITPendingBit(DMA1_IT_TC5);
    uart_receive_dma();

}

int main(void) {

    RCC_Configuration();
    GPIO_Configuration();
    NVIC_Configuration();
    USART_Configuration();
    /* First Rx, works and MA1_Channel5_IRQHandler gets called
    uart_receive_dma();
    while(1);
}

The first DMA transfer works well, so I guess I am not clearing something before starting the second one, but I can't figure out what it is.

Depending on some external conditions, the real code doesn't always restart the DMA transfer from DMA1_Channel5_IRQHandler but restarts it from somewhere else, that's why I can't use the DMA in circular mode.

\$\endgroup\$

1 Answer 1

5
\$\begingroup\$

To start another DMA transaction you should program a transaction length. It can be programmed only when a DMA channel disabled. So in your case code might looks like this:

void DMA1_Channel5_IRQHandler(void) { 
    DMA_ClearITPendingBit(DMA1_IT_TC5);
    DMA_Cmd(DMA1_Channel5, DISABLE);
    DMA1_Channel5->CNDTR = UART_PACKET_SIZE; // <--- transaction length
    DMA_Cmd(DMA1_Channel5, ENABLE);
}

Or you can use your function uart_receive_dma(), but you should disable a DMA channel prior to calling it.

void DMA1_Channel5_IRQHandler(void) { 
    DMA_ClearITPendingBit(DMA1_IT_TC5);
    DMA_Cmd(DMA1_Channel5, DISABLE);
    uart_receive_dma();
}

Second variant will do same thing as first, but will take much more time.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.