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