This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TM4C129ENCPDT: Cancel uDMA/UART transfer in progress / recover from errors?

Part Number: TM4C129ENCPDT

When using TivaWare, how can a DMA transfer between UART and memory, which has already been started, be cancelled without putting the system into an inconsistent state?

Building upon that, if a UART error or some other hardware/software error occurs during the transfer, how can the UART and DMA be "cleaned up" to a working state again?

  • Hello twelve12pm,

    For the first part of your questions, I believe this post answered by Bob should provide you the needed info: https://e2e.ti.com/support/microcontrollers/tiva_arm/f/908/t/602918

    As for the second part of your questions, I am not sure if that is a stack specific or application specific question.

    • If you are wanting to clean the UART/uDMA channels, then APIs such as uDMAErrorStatusClear and uDMAChannelDisable would work for the DMA side, and for the UART side you'd want to use APIs such as UARTRxErrorClear, UARTDMADisable and/or UARTDisable. UARTDisable will flush the TX FIFO while UARTDMADisable doesn't flush FIFO's but just disables the UART/uDMA link.
    • If you are concerned about being able to track when an error occurs and resend a packet, that would be more application specific in terms of how you handle the report back of a transfer error. You'd need a retry mechanism and also be able to track which packet failed, and then you'd be able to re-transmit it.

  • I'm not so concerned about higher level protocols yet.

    For now, we are trying to get the low level aspect of the UART and DMA to work robustly.

    Our communication is a simple back-and-forth: We transmit a block of data. Then we wait to receive the reply. Then we transmit the next block, and so on. This works without DMA but is slow and high overhead. We implemented DMA to offload the burden of shuttling bytes between memory and the UART FIFOs, counting how many bytes are left, etc.

    We do most processing in main context but detect completion in the UART interrupt.

    It works, but only under perfect conditions.

    If some error occurs, our code has been getting stuck and does not make a proper recovery.

    When we detected an error (such as timeout waiting for the complete reply to arrive) we performed:

    // Disable DMA on DMA side
    
    MAP_uDMAChannelDisable(u->RxDmaCh);
    
    // Disable DMA on UART side
    
    MAP_UARTDMADisable(u->Base, UART_DMA_RX);

    This stops DMA but was not a complete recovery and our code was getting stuck. We traced the cause of being "stuck" to a call to MAP_uDMAChannelModeGet() to test if the channel mode is UDMA_MODE_STOP before initiating a new transfer. The code is not actually "stuck" but our state machine does not proceed to the next state until the mode changes to the expected value of UDMA_MODE_STOP. The two calls above were leaving the control structure in an inconsistent state: mode remains UDMA_MODE_BASIC.

    I could not find a TivaWare function to reset that, so I call MAP_uDMAChannelTransferSet() passing UDMA_MODE_STOP for the mode.

    This appeared to work, but intermittently failed in the following spectacular manner: once in a while (due to some race condition?) software would totally lock up (get stuck for real). I traced that to the UART ISR being triggered constantly. Even though we called MAP_UARTIntClear(), something kept asserting the interrupt and we would exit the ISR handler only to immediately reenter it. Main context appeared to be somewhere around the MAP_uDMAChannelDisable() call.

    To mitigate that, the recovery looks like this now:

    // Disable DMA interrupt in UART
    
    MAP_UARTIntDisable(u->Base, UART_INT_DMARX);
    
    // Disable DMA on DMA side
    
    MAP_uDMAChannelDisable(u->RxDmaCh);
    
    // Disable DMA on UART side
    
    MAP_UARTDMADisable(u->Base, UART_DMA_RX);
    
    // Clean up the DMA control structure
    
    MAP_uDMAChannelTransferSet(
    
    u->RxDmaCh | UDMA_PRI_SELECT,
    
    UDMA_MODE_STOP,
    
    RxBuf,
    
    (void *)(u->Base + UART_O_DR),
    
    SizeOfRxBuf
    
    );
    
    // Clear DMA interrupt flag in case already asserted
    
    MAP_UARTIntClear(u->Base, UART_INT_DMARX);

    We are still running into trouble and are tracing the causes.

    I will say, it seems we need an awful lot of code to manage the UART and DMA.