Starten Sie Rx USART + DMA in STM32L1 neu

Ich verwende einen STM32L1 auf einem Nucleo-L152RE-Board. Ich muss Geräte, die ich seriell steuere, mit ziemlich hohen Baudraten ausführen, also versuche ich, DMA auf dem USART zu aktivieren. Mit dem folgenden Code kann ich einen Rx-DMA starten, aber der zweite, den ich von der ISR aus starte, wird nie abgeschlossen:

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

Die erste DMA-Übertragung funktioniert gut, also lösche ich wohl nichts, bevor ich die zweite starte, aber ich kann nicht herausfinden, was es ist.

Abhängig von einigen äußeren Bedingungen startet der echte Code die DMA-Übertragung nicht immer von DMA1_Channel5_IRQHandler neu, sondern von woanders neu, deshalb kann ich den DMA nicht im Zirkularmodus verwenden.

Antworten (1)

Um eine weitere DMA-Transaktion zu starten, sollten Sie eine Transaktionslänge programmieren. Es kann nur programmiert werden, wenn ein DMA-Kanal deaktiviert ist. In Ihrem Fall könnte der Code also so aussehen:

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

Oder Sie können Ihre Funktion uart_receive_dma() verwenden, aber Sie sollten einen DMA-Kanal deaktivieren, bevor Sie ihn aufrufen.

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

Die zweite Variante macht dasselbe wie die erste, dauert aber viel länger.