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.

TM4C129 vs TM4C123 DMA

Hello,

I am porting existing communication code from a TM4C123 to a TM4C129.

Using a blocking approach (no interrupts), UART DMA transfers work perfectly using the same code on both devices.  Using the "real" non-blocking approach (interrupting when a transfer is finished) that was carried over from the TM4C123 does not work on the TM4C129.  Data is still sent and received, but there are no interrupts to notify when the transfers are done.

The App Note that highlights the differences between these two series (http://www.ti.com/lit/an/spma065/spma065.pdf) made me expect that Tivaware already adjusted for the functional differences between the two devices, but that was obviously not the case.

I found some helpful information in other posts (https://e2e.ti.com/support/microcontrollers/tiva_arm/f/908/t/341482, https://e2e.ti.com/support/microcontrollers/tiva_arm/f/908/p/413752/1469291#1469291, https://e2e.ti.com/support/microcontrollers/tiva_arm/f/908/t/414362).  This information made it apparent that the App Note contains some misleading--if not completely inaccurate--information about the DMA (section 6) and UART (section 12) differences.

1.  Please confirm that the differences between the two devices--as far as the UART plus DMA--are not actually handled in Tivaware, and that I will have to handle things differently in my application for each device.

2.  I think I am close to getting the non-blocking approach working correctly for the TM4C129 series, but I am struggling with a problem.  I assume I'm running into a race condition, because the code below sometimes appears to work correctly for a while--until interrupts eventually stop occurring.  Other times, this same code will generate an interrupt before the first transfer is complete, and then never interrupt again, but it does keep running (no faults).  I am probably sequencing the Tivaware API calls incorrectly.  Please let me know if you see any potential problems:

static char uDMAControlTable[1024];

char buffer[] = {0,1,2,3,4,5,6,7,8,9};

void initUART0(void)
{
    // Enable GPIO port A
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);

    // Enable DMA
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
    MAP_IntEnable(INT_UDMAERR);
    MAP_uDMAEnable();
    MAP_uDMAControlBaseSet(uDMAControlTable);

    // Enable UART 0 peripheral
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);

    // Setup A0 and A1 as the RX and TX pins for UART 0
    MAP_GPIOPinConfigure(GPIO_PA0_U0RX);
    MAP_GPIOPinConfigure(GPIO_PA1_U0TX);
    MAP_GPIOPinTypeUART(GPIO_PORTA_BASE,GPIO_PIN_0);
    MAP_GPIOPinTypeUART(GPIO_PORTA_BASE,GPIO_PIN_1);

    // Use PIOSC as the UART clock source
    MAP_UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);

    // Configure UART (clock from PIOSC, 115.2k 8-N-1)
    MAP_UARTConfigSetExpClk(UART0_BASE, 16000000, 115200,
                (   UART_CONFIG_WLEN_8      |
                    UART_CONFIG_STOP_ONE    |
                    UART_CONFIG_PAR_NONE    ));

    // Trigger interrupt when FIFOs at half capacity
    MAP_UARTFIFOLevelSet(UART0_BASE,UART_FIFO_TX4_8,UART_FIFO_RX4_8);

    // Make sure interrupts are clear
    MAP_UARTIntClear(UART0_BASE,
                     (   UART_INT_RX | UART_INT_RT |
                         UART_INT_TX | UART_INT_OE |
                         UART_INT_BE | UART_INT_PE |
                         UART_INT_FE));

    // Enable UART 0
    MAP_UARTEnable(UART0_BASE);

    //
    // Put the attributes in a known state for the uDMA UART TX channel.  These
    // should already be disabled by default.
    //
    MAP_uDMAChannelAttributeDisable(UDMA_CHANNEL_UART0TX,
                                    UDMA_ATTR_ALTSELECT         |
                                    UDMA_ATTR_USEBURST          |
                                    UDMA_ATTR_HIGH_PRIORITY     |
                                    UDMA_ATTR_REQMASK           );

    //
    // Put the attributes in a known state for the uDMA UART RX channel.  These
    // should already be disabled by default.
    //
    MAP_uDMAChannelAttributeDisable(UDMA_CHANNEL_UART0RX,
                                    UDMA_ATTR_ALTSELECT         |
                                    UDMA_ATTR_USEBURST          |
                                    UDMA_ATTR_HIGH_PRIORITY     |
                                    UDMA_ATTR_REQMASK           );

    //
    // Set the USEBURST attribute for the uDMA UART TX channel.  This will
    // force the controller to always use a burst when transferring data from
    // the TX buffer to the UART.  This is somewhat more efficient bus usage
    // than the default which allows single or burst transfers.
    //
    MAP_uDMAChannelAttributeEnable( UDMA_CHANNEL_UART0TX,
                                    UDMA_ATTR_USEBURST          );

    //
    // Configure the control parameters for the UART TX.  The uDMA UART TX
    // channel is used to transfer a block of data from a buffer to the UART.
    // The data size is 8 bits.  The source address increment is 8-bit bytes
    // since the data is coming from a buffer.  The destination increment is
    // none since the data is to be written to the UART data register.  The
    // arbitration size is set to 4, which matches the UART TX FIFO trigger
    // threshold.
    //
    MAP_uDMAChannelControlSet(  UDMA_CHANNEL_UART0TX    |
                                UDMA_PRI_SELECT,
                                UDMA_SIZE_8             |
                                UDMA_SRC_INC_8          |
                                UDMA_DST_INC_NONE       |
                                UDMA_ARB_4              );

    //
    // Configure the control parameters for the primary control structure for
    // the UART RX channel.  The uDMA UART RX channel is used to transfer a
    // block of data from the UART to a buffer.  The transfer data size is
    // 8 bits, the source address does not increment since it will be reading
    // from a register.  The destination address increment is byte 8-bit bytes.
    // The arbitration size is set to 4 to match the RX FIFO trigger threshold.
    // The uDMA controller will use a 4 byte burst transfer if possible.  This
    // will be somewhat more efficient that single byte transfers.
    //
    MAP_uDMAChannelControlSet(  UDMA_CHANNEL_UART0RX    |
                                UDMA_PRI_SELECT,
                                UDMA_SIZE_8             |
                                UDMA_SRC_INC_NONE       |
                                UDMA_DST_INC_8          |
                                UDMA_ARB_4              );

    // Enable interrupt
    MAP_UARTIntEnable(UART0_BASE, UART_INT_DMARX | UART_INT_DMATX);
    MAP_IntEnable(INT_UART0);

    MAP_UARTDMAEnable(UART0_BASE, UART_DMA_RX);
}

void uartInterruptHandler(void)
{
    // If RX channel no longer enabled, receive finished
    if (!MAP_uDMAChannelIsEnabled(UDMA_CHANNEL_UART0RX))
    {

    }
    // Else if TX channel no longer enabled, transmit finished
    else if (!MAP_uDMAChannelIsEnabled(UDMA_CHANNEL_UART0TX))
    {
        MAP_UARTDMADisable(UART0_BASE, UART_DMA_TX);
    }

    uint32_t status = MAP_UARTIntStatus(UART0_BASE, true);
    MAP_UARTIntClear(UART0_BASE, status);
}

int main()
{
    // Setup clock
    MAP_SysCtlClockFreqSet( (SYSCTL_XTAL_16MHZ | SYSCTL_OSC_MAIN |
            SYSCTL_USE_PLL | SYSCTL_CFG_VCO_480), 120000000);

    // Initialize UART
    initUART0();

    for(;;)
    {
/*******************************************************************************
* Receive
*/
        // Setup to receive buffer
        MAP_uDMAChannelTransferSet( UDMA_CHANNEL_UART0RX | UDMA_PRI_SELECT,
                                    UDMA_MODE_BASIC,
                                    (void *)(UART0_BASE + UART_O_DR),
                                    &buffer,
                                    sizeof(buffer));

        // Enable DMA channel to begin receive
        MAP_uDMAChannelEnable(UDMA_CHANNEL_UART0RX);

//        MAP_UARTDMAEnable(UART0_BASE, UART_DMA_RX);

/*******************************************************************************
* Transmit
*/
        // Setup to send buffer
        MAP_uDMAChannelTransferSet( UDMA_CHANNEL_UART0TX | UDMA_PRI_SELECT,
                                    UDMA_MODE_BASIC,
                                    &buffer,
                                    (void *)(UART0_BASE + UART_O_DR),
                                    sizeof(buffer));

        // DMA channel must be enabled first, or an interrupt will occur immediately
        MAP_uDMAChannelEnable(UDMA_CHANNEL_UART0TX);
        MAP_UARTDMAEnable(UART0_BASE, UART_DMA_TX);

        while (MAP_uDMAChannelIsEnabled(UDMA_CHANNEL_UART0RX))
        {
            // Wait for the response before sending more
        }
    }
}

  • Hi Ben,

    May I ask to which vector is that interrupt mapped to?

    In the TM4C1294 you use associated peripheral vector interrupt on that channel - Ex: UART0 DMA TX done interrupt, the interrupt is generated on the UART0 vector. Kinda of like a "hijack".

    I made a code for the TM4C1294 UART+DMA and ported it over to TM4C123, I think the only difference was that. I think it was that and where the interrupt was cleared but that I will have to check it out.
  • Hi Luis,
    Thank you for the quick, helpful response.

    Luis Afonso said:
    Hi Ben,

    May I ask to which vector is that interrupt mapped to

    The interrupt was mapped to the UART0 interrupt.

    The problem was that I misunderstood how the interrupt statuses should be checked and cleared on the TM4C129 compared to the TM4C123.  Based on your feedback, I updated my example, which now works correctly.  Here it is for anyone else to reference:

    static char uDMAControlTable[1024];
    
    char buffer[] = {0,1,2,3,4,5,6,7,8,9};
    
    void initUART0(void)
    {
        // Enable GPIO port A
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    
        // Enable DMA
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
        MAP_IntEnable(INT_UDMAERR);
        MAP_uDMAEnable();
        MAP_uDMAControlBaseSet(uDMAControlTable);
    
        // Enable UART 0 peripheral
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
    
        // Setup A0 and A1 as the RX and TX pins for UART 0
        MAP_GPIOPinConfigure(GPIO_PA0_U0RX);
        MAP_GPIOPinConfigure(GPIO_PA1_U0TX);
        MAP_GPIOPinTypeUART(GPIO_PORTA_BASE,GPIO_PIN_0);
        MAP_GPIOPinTypeUART(GPIO_PORTA_BASE,GPIO_PIN_1);
    
        // Use PIOSC as the UART clock source
        MAP_UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);
    
        // Configure UART (clock from PIOSC, 115.2k 8-N-1)
        MAP_UARTConfigSetExpClk(UART0_BASE, 16000000, 115200,
                    (   UART_CONFIG_WLEN_8      |
                        UART_CONFIG_STOP_ONE    |
                        UART_CONFIG_PAR_NONE    ));
    
        // Trigger interrupt when FIFOs at half capacity
        MAP_UARTFIFOLevelSet(UART0_BASE,UART_FIFO_TX4_8,UART_FIFO_RX4_8);
    
        // Make sure interrupts are clear
        MAP_UARTIntClear(UART0_BASE,
                         (   UART_INT_RX | UART_INT_RT |
                             UART_INT_TX | UART_INT_OE |
                             UART_INT_BE | UART_INT_PE |
                             UART_INT_FE));
    
        // Enable UART 0
        MAP_UARTEnable(UART0_BASE);
    
        // Enable DMA on UART RX
        MAP_UARTDMAEnable(UART0_BASE, UART_DMA_RX);
    
        //
        // Put the attributes in a known state for the uDMA UART TX channel.  These
        // should already be disabled by default.
        //
        MAP_uDMAChannelAttributeDisable(UDMA_CHANNEL_UART0TX,
                                        UDMA_ATTR_ALTSELECT         |
                                        UDMA_ATTR_USEBURST          |
                                        UDMA_ATTR_HIGH_PRIORITY     |
                                        UDMA_ATTR_REQMASK           );
    
        //
        // Put the attributes in a known state for the uDMA UART RX channel.  These
        // should already be disabled by default.
        //
        MAP_uDMAChannelAttributeDisable(UDMA_CHANNEL_UART0RX,
                                        UDMA_ATTR_ALTSELECT         |
                                        UDMA_ATTR_USEBURST          |
                                        UDMA_ATTR_HIGH_PRIORITY     |
                                        UDMA_ATTR_REQMASK           );
    
        //
        // Set the USEBURST attribute for the uDMA UART TX channel.  This will
        // force the controller to always use a burst when transferring data from
        // the TX buffer to the UART.  This is somewhat more efficient bus usage
        // than the default which allows single or burst transfers.
        //
        MAP_uDMAChannelAttributeEnable( UDMA_CHANNEL_UART0TX,
                                        UDMA_ATTR_USEBURST          );
    
        //
        // Configure the control parameters for the UART TX.  The uDMA UART TX
        // channel is used to transfer a block of data from a buffer to the UART.
        // The data size is 8 bits.  The source address increment is 8-bit bytes
        // since the data is coming from a buffer.  The destination increment is
        // none since the data is to be written to the UART data register.  The
        // arbitration size is set to 4, which matches the UART TX FIFO trigger
        // threshold.
        //
        MAP_uDMAChannelControlSet(  UDMA_CHANNEL_UART0TX    |
                                    UDMA_PRI_SELECT,
                                    UDMA_SIZE_8             |
                                    UDMA_SRC_INC_8          |
                                    UDMA_DST_INC_NONE       |
                                    UDMA_ARB_4              );
    
        //
        // Configure the control parameters for the primary control structure for
        // the UART RX channel.  The uDMA UART RX channel is used to transfer a
        // block of data from the UART to a buffer.  The transfer data size is
        // 8 bits, the source address does not increment since it will be reading
        // from a register.  The destination address increment is byte 8-bit bytes.
        // The arbitration size is set to 4 to match the RX FIFO trigger threshold.
        // The uDMA controller will use a 4 byte burst transfer if possible.  This
        // will be somewhat more efficient that single byte transfers.
        //
        MAP_uDMAChannelControlSet(  UDMA_CHANNEL_UART0RX    |
                                    UDMA_PRI_SELECT,
                                    UDMA_SIZE_8             |
                                    UDMA_SRC_INC_NONE       |
                                    UDMA_DST_INC_8          |
                                    UDMA_ARB_4              );
    
        // Enable DMA interrupts on this UART
        MAP_UARTIntEnable(UART0_BASE, UART_INT_DMARX | UART_INT_DMATX);
    
        // Enable interrupts on this UART
        MAP_IntEnable(INT_UART0);
    }
    
    void uartInterruptHandler(void)
    {
        // If an RX interrupt
        if (MAP_UARTIntStatus(UART0_BASE, true) & UART_INT_DMARX)
        {
            // Disable DMA on UART RX, and clear the interrupt
            MAP_UARTDMADisable(UART0_BASE, UART_DMA_RX);
            MAP_UARTIntClear(UART0_BASE, UART_INT_DMARX );
        }
    
        // If an TX interrupt
        if (MAP_UARTIntStatus(UART0_BASE, true) & UART_INT_DMATX)
        {
            // Disable DMA on UART TX, and clear the interrupt
            MAP_UARTDMADisable(UART0_BASE, UART_DMA_TX);
            MAP_UARTIntClear(UART0_BASE, UART_INT_DMATX );
        }
    }
    
    int main()
    {
        // Setup clock
        MAP_SysCtlClockFreqSet( (SYSCTL_XTAL_16MHZ | SYSCTL_OSC_MAIN |
                SYSCTL_USE_PLL | SYSCTL_CFG_VCO_480), 120000000);
    
        // Initialize UART
        initUART0();
    
        for(;;)
        {
    /*******************************************************************************
    * Receive
    */
            // Setup to receive buffer
            MAP_uDMAChannelTransferSet( UDMA_CHANNEL_UART0RX | UDMA_PRI_SELECT,
                                        UDMA_MODE_BASIC,
                                        (void *)(UART0_BASE + UART_O_DR),
                                        &buffer,
                                        sizeof(buffer));
    
            // Enable DMA channel to begin receive
            MAP_uDMAChannelEnable(UDMA_CHANNEL_UART0RX);
    
            // Enable DMA on UART RX
            MAP_UARTDMAEnable(UART0_BASE, UART_DMA_RX);
    
    /*******************************************************************************
    * Transmit
    */
            // Setup to send buffer
            MAP_uDMAChannelTransferSet( UDMA_CHANNEL_UART0TX | UDMA_PRI_SELECT,
                                        UDMA_MODE_BASIC,
                                        &buffer,
                                        (void *)(UART0_BASE + UART_O_DR),
                                        sizeof(buffer));
    
            // DMA channel must be enabled first, or an interrupt will occur immediately
            MAP_uDMAChannelEnable(UDMA_CHANNEL_UART0TX);
    
            // Enable DMA on UART TX
            MAP_UARTDMAEnable(UART0_BASE, UART_DMA_TX);
    
            while (MAP_uDMAChannelIsEnabled(UDMA_CHANNEL_UART0RX))
            {
                // Wait for the response before sending more
            }
        }
    }



  • I'm glad that everything is working now :D