Перезапустите Rx USART + DMA в STM32L1.

Я использую STM32L1 на плате Nucleo-L152RE. У меня есть устройства, которыми я управляю через последовательный порт, работающие на довольно высоких скоростях, поэтому я пытаюсь включить DMA на USART. С помощью приведенного ниже кода я могу запустить один Rx DMA, но второй, который я запускаю из ISR, никогда не завершается:

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);
}

Первая передача по DMA работает хорошо, поэтому я предполагаю, что я что-то не очищаю перед запуском второй, но я не могу понять, что это такое.

В зависимости от каких-то внешних условий реальный код не всегда перезапускает передачу DMA из DMA1_Channel5_IRQHandler, а перезапускает ее откуда-то еще, поэтому я не могу использовать DMA в циклическом режиме.

Ответы (1)

Чтобы начать другую транзакцию DMA, вы должны запрограммировать длину транзакции. Его можно запрограммировать только при отключенном канале DMA. Итак, в вашем случае код может выглядеть так:

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);
}

Или вы можете использовать свою функцию uart_receive_dma(), но вы должны отключить канал DMA перед ее вызовом.

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

Второй вариант будет делать то же самое, что и первый, но займет гораздо больше времени.