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.

TI Connected Launchpad uDMA Auto-Initialization

Hi,

I am looking to find a way to make the TI Connected Launchpad uDMA engine auto-initialize to restart a transfer after the current transfer is completed.  This process seems to be detailed here (Section 2.8) for a different processor.  It looks as if there is an auto-reload bit that can be set in the DMACHmTCR2 register.  I am looking to achieve this same functionality on the Launchpad.  However, I have not been able to find a way to do this without using an interrupt to indicate that the transfer has been completed.  I then re-enable the DMA channel once the transfer is complete, since the DMA channel goes into Stop Mode after completing the transfer.  

What I am trying to accomplish with this functionality is the ability to transfer data from UART into memory directly without the need for an interrupt.

Thanks!

  • Hi Logan,

    You chosen a very bad comparison!!
    That MCU you referred is from a different family and core altogether!

    The DMA also is another one.
    You cant go into intel i7 processors documentation looking in how to use AMD MCUs specific hardware either
  • Hi Luis,

    I am well aware that what I have linked to is different from what I am using. Although I would not use AMD documentation for help with Intel processors, I would ask Intel if similar functionality existed, which is what I am asking.

    I am looking to implement what is shown in the documentation I linked to, but in the Launchpad family. Is there a register that performs roughly the same effects, or is there a way to auto reload the uDMA on my micro controller just as one is allowed on the other micro controller?

    Thanks.
  • Hi Logan,

    I apologize, I misread. I read that you saw the process to implement in another processor and you want to achieve the same on the Tiva - like you saw it exists and you wanted help implementing it, ignoring that it could not exist in the Tiva since it's based on another processor all together.

    The Tiva DMA unfortunately does not have a simple auto reload. Meaning that you are limited to a maximum of 1023 transfers before needing to re-setup the DMA and re-enable it.

    There are options like using ping-pong mode which allows to setup 2 transfers. Transfer #1 -> Transfer #1 done, automatically start Transfer #2 -> while Transfer #2 is occurring setup again #1 -> when #2 is done it goes automatically to #1 -> while #1 is occurring setup number #2 -> repeat
    Now this still needs a interrupt to when it's done unfortunately.

    There is the more complex mode Scatter-Gather that could possibly allow something similar but with that I can't help you much.
  • Hi,

    The scatter-gather mode indeed does do the job, I've implemented that before, both with UART and SPI. It's a little twisted, but once you get the hang of it it's actually quite simple (well, what isn't...). Before you go that route, please detail your idea a bit more - are you storing just the latest byte received, or do you have some kind of a buffer? In the latter case, how are you planning to ensure you don't over/underrun the buffer? For the former, I might just have the code for you...
  • @Veikko,

    Really like your desire to assist - and the (obvious) inside knowledge/expertise - poster's requirement demands.

    That said - does your method (avoid) interrupts? (poster's expressed goal - mais non?)

    I find it "unusual" that "interrupt is to be suppressed" - yet no "performance requirements" (at all) appear.   Would be of use/interest to learn why interrupt proves, "deal-breaker."

  • Yes, it does avoid interrupts - the scatter-gather sequence as it's last task loads the uDMA control table entry with the very same contents that the original uDMAChannelScatterGatherSet() did - from there on it's a merry-go-around.

    But indeed, would be nice to know why this sort of functionality is desired. For me, it was originally because I was stubborn and wanted to learn how to do it, and since then I've done it because I can. That said, every time I still start with "manually" moving bytes around to ensure that the "plumbing" works before I start fiddling with uDMA.
  • Hi Veikko,

    Thank you for your response.  It seems that you may have exactly what I need.  

    I am not concerned with overrunning the buffer, as the data will be uniform packets from a sensor.  The problem is, the sensor provides data at a constant fixed rate, which can either be enabled or disabled.  I want to buffer and store the last packet from the sensor, and I will use it only when needed.  The incoming packets will overwrite the existing data in the buffer.  When the data is needed, there is not enough time to wait for the sensor to send another packet and for this application, using the last read of the sensor that is located in the buffer is acceptable.  

    Would it be possible for you to provide a code example of how you achieved this with the scatter-gather mode? You mentioned that your code stores only the last byte, but we have a small buffer as the packet is 48 bytes.  

    Thanks! 

  • Logan,

    From the sound of things, I would not use looping uDMA for this. The reason is that the looping uDMA transfer has no way of telling whether the data stream starts at the correct location - ie, if for any reason just one byte is missed, the whole buffer is then shifted and the interpreted data is garbage. I had a similar situation with an absolute encoder that spat out position and status information over RS422.

    What I did was I opened the UART, "flushed" the buffers, then enabled RX uDMA and finally commanded the encoder to start auto-transmitting data. The DMA then happily kept transferring the data into my fixed-size buffer, over and over and over again. Sometimes it worked for a minute or so, sometimes it could fail within a couple of milliseconds (my code reading the buffer checked the constant headers and footers in the encoder message and flagged an error if they didn't match).

    The only reliable solution was to have the uDMA run only once, with the expected amount of characters, then check the received bytes in the uDMA interrupt handler. If the message was garbage, read the RX FIFO until you hit the footer character (or the FIFO becomes empty, in which case enable the regular UART RX interrupt and keep it on until the expected character is received). Then re-setup the uDMA (single task) and fire away. An error counter reveals that errors still happen, just this way they get fixed before the actual buffer used for feedback gets corrupted.

    I didn't stop there, however. I actually ended up giving up the constant transmission altogether - the sensor had a mode for transmitting just once, that is activated by sending a single command character. Then the only thing you needed to do in the uDMA interrupt was to flush the RX buffer to be sure there aren't any rogue characters left (if the data was garbage - skip flushing if valid), re-setup the uDMA and write that single-transmission command character to the TX FIFO. 

    -----

    In any case, 48 bytes sounds like an awful lot of data from a single sensor - is it in ASCII format perhaps? Do consider the CPU effort you need to convert that into an actually usable form - can you afford that at the supposedly time-critical moment of use?

    By the way, a trick for quickly re-configuring an uDMA transfer: copy the contents of ui32Control of the tDMAControlTable struct to the unused ui32Spare location prior to enabling the channel for the first time... Then, re-configuring is just a matter of ui32Control = ui32Spare; channelEnable();

    Trick two: use bit-banding for (re-)enabling the uDMA channel. Then things get really slick!

  • Veikko,

    Thank you for your detailed and thorough response. It is greatly appreciated.

    Our data packets are formatted in such a way that we can tell when the data is corrupt due to the presence of a checksum.

    The data is used very sporadically, but when used, it is in a time sensitive manner, it would be better for us to have the last packet in the buffer, which we can check for errors, and if needed, wait for a new one if the packet contains errors. Unfortunately, we are unable to disable constant transmission and poll the sensor for its data, as it only operates in a mode where data is constantly streamed.

    48 bytes is a fair amount of data, but we do have the time to process it. But, since the sensor outputs data at a moderately slow rate, we need to have the last packet ready to start processing.

    At this point, we would like to try the looping uDMA approach if possible. Would you be willing to post or provide us with a code sample that achieves this?

    Thanks again for all the help.
  • We did some behind-the-scenes troubleshooting and polishing with Logan, he got the scatter-gather loop working now. I will post here a stripped down summary of what it takes to make a single-shot scatter gather transfer into a self-restarting one. Especially Amit, please take a look and comment if you spot anything wrong. Of course, comments are most welcome from anyone.

    /**
     * Description of the scatter-gather uDMA loop method
     *
     * PREREQUISITE: You have already verified the desired uDMA functionality
     * with a "single-shot" scatter-gather operation.
     */
     
     // The uDMA channel to use (CH8 / UART0 RX as an example)
     #define UDMA_SGLOOP_CHANNEL UDMA_CH8_UART0RX
     
     // The uDMA control table, 1024-byte aligned. Note the data type
     // which differs from the one suggested by the driverlib programming
     // example. The resulting size is the same, however.
     #pragma DATA_ALIGN(uDMA_ControlTable, 1024)
     tDMAControlTable uDMA_ControlTable[64];
     
     // A copy of the "starting commands" of the scatter-gather operation
     tDMAControlTable uDMAsg_LoopTask;
     
     // The scatter-gather task list
     tDMAControlTable uDMAsg_TaskList[] =
    {
    	//
    	// Insert the "actual" tasks here, using uDMATaskStructEntry
    	//
    	uDMATaskStructEntry(
    		...
    	),
    
    	uDMATaskStructEntry(
    		...
    	),
    
    	...
    	
    	//
       	// Final task: reload the task list
       	//
       	// This task transfers the original tDMAControlTable entry,
       	// that was saved to uDMAsg_LoopTask, back to uDMA_ControlTable
       	// at the proper channel index. This will effectively restart
       	// the transfer automatically.
       	// tDMAControlTable has 4 uint32's, so the parameters are:
       	// -transfer 4 elements
       	// -element size 32 bits
       	// -increment both the source and destination buffers by 32 bits per element
       	// -arbitration size is 4 elements (we want to transfer every element in one go)
       	// -transfer mode should be the same as for the above tasks
        	uDMATaskStructEntry(
        		4,
        		UDMA_SIZE_32,
        		UDMA_SRC_INC_32,
        		&uDMAsg_LoopTask,
        		UDMA_DST_INC_32,
        		&(uDMA_ControlTable[UDMA_SGLOOP_CHANNEL & 0x1f]),
        		UDMA_ARB_4,
        		UDMA_MODE_PER_SCATTER_GATHER
        	)
    };
    
    // Inside your main (or wherever you set up the uDMA), do the following:
    void main(void) {
        // Peripheral setup etc
        ...
    	
        // Configure the scatter-gather transfer (replace ? with the amount of tasks in uDMAsg_TaskList)
        uDMAChannelScatterGatherSet(UDMA_SGLOOP_CHANNEL, ?, uDMAsg_TaskList, true);
    
        // Copy the primary control structure from the uDMA controltable
        uDMAsg_LoopTask.pvDstEndAddr = uDMA_ControlTable[UDMA_SGLOOP_CHANNEL & 0x1f].pvDstEndAddr;
        uDMAsg_LoopTask.pvSrcEndAddr = uDMA_ControlTable[UDMA_SGLOOP_CHANNEL & 0x1f].pvSrcEndAddr;
        uDMAsg_LoopTask.ui32Control = uDMA_ControlTable[UDMA_SGLOOP_CHANNEL & 0x1f].ui32Control;
        uDMAsg_LoopTask.ui32Spare = uDMA_ControlTable[UDMA_SGLOOP_CHANNEL & 0x1f].ui32Spare;
    
        // Finally enable uDMA (the above copying must be done before enabling!)
        uDMAChannelEnable(UDMA_SGLOOP_CHANNEL);
    
        // Rest of your code
        ...
    }

    I will also post some of the Q&A between us, it will hopefully prove useful to others as well. To follow soon...


  • Here's a selection of the Q&A we went through. Comments welcome, naturally. Logan, if I forgot to include something that you think would be valuable to others, feel free to add!

    What is the purpose of using UARTFIFOLevelSet when in the end all interrupts are disabled? Is this function call serving another purpose?

    This has to do with how the UART module signals the uDMA controller. The UART module has two flags, "I have (at least) one element ready for transfer" and "I have many elements ready for transfer" (also called 'single request' and 'burst request'). Now, whenever there is any data in the RX FIFO, the single request flag is active. When the amount of data in the RX FIFO exceeds the level set by UARTFIFOLevelSet, the burst request flag is active, too. Usually with UART things never get to the burst request (as the UART is comparatively slow compared to the uDMA), unless the uDMA module is configured not to listen to single requests (UDMA_ATTR_USEBURST). Using burst requests will allow you to get better performance out of the uDMA, but will also keep "fresh" data from appearing at the destination buffer until there is enough of it in the UART FIFO. There is also another uDMA parameter connected in this - that is the arbitration size. It determines how many transfers the uDMA module will (try) to complete in one go, before pausing and looking around whether there's a higher priority transfer waiting (and switching to doing that if there is).

    What is the purpose of copying the entries from uDMA_ControlTable into uDMAsg_LoopTask?

    The scatter-gather transfer utilizes both the primary and secondary "side" of the uDMA controller. The primary side has a task that transfers one tDMAControlTable at a time (4x 32 bits) from the tasklist to the secondary channel entry in the ControlTable. Then the secondary side activates, executing the "actual" transfer. Once the secondary side is done, it switches back to the primary side and transfers another tDMAControlTable to the secondary side. This goes on until the primary side task has finished, after which the last secondary side transfer finishes. Now, the trick is to have the last secondary side transfer "reboot" the whole operation. For this, we need the original instructions (created by the uDMAChannelScatterGatherSet call - see driverlib/udma.c for what happens there) for the primary side to be reloaded into the primary channel entry in the ControlTable. This is why the entries are copied from  uDMA_ControlTable to uDMAsg_LoopTask prior to enabling the transfer. Then, the final secondary side transfer copies them back.

    What is the purpose of the mask value (0x1f) in [UDMA_SGLOOP_CHANNEL & 0x1f]?

    The reason for the masking in the first place is that the definitions like UDMA_CH8_UART0RX have two pieces of information embedded in them: the uDMA channel number, and the channel mux ID (ie. which of the different available sources for each uDMA channel should be activated). The channel number is available in the low bits, and only values from 0 to 31 are valid - thus masking with 0x1f is sufficient and avoids any indexing errors with the ControlTable.

    Is there a method to determine where the scatter-gatherer is in the tasklist? Specifically which position is the destination buffer is going to be written to?

    Yes there is. Take a look at a TM4C12x datasheet, chapter "uDMA Channel Control Structure". The information you want is in Register 3, XFERSIZE - it is updated by the uDMA controller. That register is available in uDMA_ControlTable[UDMA_SGLOOP_CHANNEL & 0x1f].ui32Control. For the progress of each "sub-task", look at uDMA_ControlTable[(UDMA_SGLOOP_CHANNEL & 0x1f) | UDMA_ALT_SELECT].ui32Control.

  • After working with Veikko, I also wanted to provide my more complete working example with UART included to allow others to take what they may need from this example.  Veikko was fantastic in helping me resolve my problems and understands this microcontroller quite well and I really appreciate his help.  

    uint8_t data[600]
    
    // Temporary storage for the first uDMA primary task, for scatter-gather looping
    static tDMAControlTable carriage_uart_looptask;
    
    // The Scatter-Gather tasklist for the carriage UART
    static tDMAControlTable carriage_uart_tasklist[] =
    {
            // Task 1: Copy data from the UART buffer to assigned variable, 1024 times over
            uDMATaskStructEntry(
                    // Number of items to transfer
                    600,
                    // Number of bits in each element of data
                    UDMA_SIZE_8,
                    // Number of bits to incrament the source data by. This is none
                    // as no data is being transfered out.
                    UDMA_SRC_INC_NONE,
                    // Starting address of the source. This is 0 as no data
                    // is being transfered out.
                    0,
                    // Number of bits to incrament the destination data by. This is 8 as
                    // it mataches the 8 bits per location of the destination.
                    UDMA_DST_INC_8,
                    // Starting address of the destination data.
                    data,
                    // Ammount of data that is transfered before going to the bus and asking
                    // if it is our turn.
                    UDMA_ARB_1,
                    // Type of transfer
                    UDMA_MODE_PER_SCATTER_GATHER
            ),
    
            // Task 2: loop the task list
            uDMATaskStructEntry(
                    // Size of tasklist data
                    4,
                    // Each entry in the task list table is 32-bits long.
                    UDMA_SIZE_32,
                    // To move to the next element in the source data, go 32-bits.
                    UDMA_SRC_INC_32,
                    // Address of the table.
                    &carriage_uart_looptask,
                    // Source
                    UDMA_DST_INC_32,
                    // Location of desitnation data.
                    0,
                    // Transfer 4 things before requesting to bus for anohter transfer.
                    UDMA_ARB_4,
                    // Transfer in the scatter gather mode.
                    UDMA_MODE_PER_SCATTER_GATHER
            )
    };
    
    
    
    void SLIDE_CarriageUARTInit() {
        // Enable the uDMA subsystem
        uDMAEnable();
    
        // Enable the UART peripheral
        SysCtlPeripheralEnable(SYSCTL_PERIPH_UART1);
    
        // Configure the RX and TX pins
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
        GPIOPinConfigure(GPIO_PB0_U1RX);
        GPIOPinConfigure(GPIO_PB1_U1TX);
        GPIOPinTypeUART(GPIO_PORTB_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    
        // Set the UART FIFO levels
        UARTFIFOLevelSet(UART1_BASE, UART_FIFO_TX4_8, UART_FIFO_RX4_8);
    
        // No hardware flow control
        UARTFlowControlSet(UART1_BASE, UART_FLOWCONTROL_NONE);
    
        // Enable uDMA for the UART
        UARTDMAEnable(UART1_BASE, UART_DMA_RX);
    
        // Disable all interrupts
        UARTIntDisable(UART1_BASE, 0xffffffff);
    
        // Clear all interrupts
        UARTIntClear(UART1_BASE, 0xffffffff);
    
        // Set the UART clock source
        UARTClockSourceSet(UART1_BASE, UART_CLOCK_PIOSC);
    
        // Disable the uDMA channel to be used
        uDMAChannelDisable(UDMA_CHANNEL_UART1RX & 0xffff);
    
        // Assign the proper peripheral for the uDMA channel
        uDMAChannelAssign(UDMA_CHANNEL_UART1RX);
    
        // Reset the channel configuration
        uDMAChannelAttributeDisable(UDMA_CHANNEL_UART1RX|UDMA_PRI_SELECT, UDMA_ATTR_ALL);
        uDMAChannelAttributeDisable(UDMA_CHANNEL_UART1RX|UDMA_ALT_SELECT, UDMA_ATTR_ALL);
    
    
        // Install the dynamic pointer addresses to the tasklist
        // The ending address of the data transfer.
        carriage_uart_tasklist[0].pvSrcEndAddr = (void*)(UART1_BASE + UART_O_DR);
        carriage_uart_tasklist[1].pvDstEndAddr = (void*)((uint8_t*)pui8ControlTable + (((UDMA_CHANNEL_UART1RX & 0xffff) + 1) * sizeof(tDMAControlTable)) - 1);
    
        // Save the tasklist
        uDMAChannelScatterGatherSet((UDMA_CHANNEL_UART1RX & 0xffff), 2, carriage_uart_tasklist, true);
    
        // Copy the primary control structure from the uDMA controltable
        carriage_uart_looptask.pvDstEndAddr = pui8ControlTable[UDMA_CHANNEL_UART1RX & 0xffff].pvDstEndAddr;
        carriage_uart_looptask.pvSrcEndAddr = pui8ControlTable[UDMA_CHANNEL_UART1RX & 0xffff].pvSrcEndAddr;
        carriage_uart_looptask.ui32Control = pui8ControlTable[UDMA_CHANNEL_UART1RX & 0xffff].ui32Control;
        carriage_uart_looptask.ui32Spare = pui8ControlTable[UDMA_CHANNEL_UART1RX & 0xffff].ui32Spare;
    
    
        // Finally configure the UART speed and byte settings (this also enables the UART)
        UARTConfigSetExpClk(UART1_BASE, 16000000, 9600, (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE));
    
        // Finally enable uDMA for the SSI module and enable the uDMA channel itself
        uDMAChannelEnable(UDMA_CHANNEL_UART1RX & 0xffff);
    }

    Any comments or suggestions on the code are welcome!  

  • It might be easier for others to follow if you updated the code with the same variable names as the generic "guide" - the names in your code now don't really have anything to do with the example and might confuse people (they do make a lot of sense in the university project that example is a direct copy&paste from, though...)

    Also:

    Logan Adams said:
    // Starting address of the source. This is 0 as no data // is being transfered out. 0,

    This is misleading - it is set to zero because it will be filled later on in the initialization function (but for the purpose of this example, I'd suggest you set it directly in the tasklist - less chance for misunderstanding). The same for the final task's destination address.

  • Hi Veikko,

    Thank you for your response. Sorry it has taken me awhile to get back to this to update the thread. I have been extremely busy.

    Below is the code you suggested I change the comments and naming of variables on. As I want to ensure that the code I post is fully working, I have not changed the place where the entries in the loop task are initialized as you suggested since I cannot test it. 

     // The uDMA control table, 1024-byte aligned. Note the data type
     // which differs from the one suggested by the driverlib programming
     // example. The resulting size is the same, however.
     #pragma DATA_ALIGN(uDMA_ControlTable, 1024)
     tDMAControlTable uDMA_ControlTable[64];
     
      // The uDMA channel to use (CH8 / UART0 RX as an example)
     #define UDMA_SGLOOP_CHANNEL UDMA_CH8_UART0RX
    
    uint8_t data[600]
    
    // Temporary storage for the first uDMA primary task, for scatter-gather looping
    static tDMAControlTable uDMAsg_LoopTask;
    
    // The Scatter-Gather tasklist for the carriage UART
    static tDMAControlTable uDMAsg_TaskList[] =
    {
            // Task 1: Copy data from the UART buffer to assigned variable, 1024 times over
            uDMATaskStructEntry(
                    // Number of items to transfer
                    600,
                    // Number of bits in each element of data
                    UDMA_SIZE_8,
                    // Number of bits to increment the source data by. This is none
                    // as no data is being transferred out.
                    UDMA_SRC_INC_NONE,
                    // Set to zero as it will be initialized later
                    0,
                    // Number of bits to increment the destination data by. This is 8 as
                    // it matches the 8 bits per location of the destination.
                    UDMA_DST_INC_8,
                    // Starting address of the destination data.
                    data,
                    // Amount of data that is transferred before going to the bus and asking
                    // if it is our turn.
                    UDMA_ARB_1,
                    // Type of transfer
                    UDMA_MODE_PER_SCATTER_GATHER
            ),
    
            // Task 2: loop the task list
            uDMATaskStructEntry(
                    // Size of tasklist data
                    4,
                    // Each entry in the task list table is 32-bits long.
                    UDMA_SIZE_32,
                    // To move to the next element in the source data, go 32-bits.
                    UDMA_SRC_INC_32,
                    // Address of the table.
                    &uDMAsg_LoopTask,
                    // Source
                    UDMA_DST_INC_32,
                    // Set to 0 as it will be initialized later
                    0,
                    // Transfer 4 things before requesting to bus for another transfer.
                    UDMA_ARB_4,
                    // Transfer in the scatter gather mode.
                    UDMA_MODE_PER_SCATTER_GATHER
            )
    };
    
    
    
    void SLIDE_CarriageUARTInit() {
        // Enable the uDMA subsystem
        uDMAEnable();
    
        // Enable the UART peripheral
        SysCtlPeripheralEnable(SYSCTL_PERIPH_UART1);
    
        // Configure the RX and TX pins
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
        GPIOPinConfigure(GPIO_PB0_U1RX);
        GPIOPinConfigure(GPIO_PB1_U1TX);
        GPIOPinTypeUART(GPIO_PORTB_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    
        // Set the UART FIFO levels
        UARTFIFOLevelSet(UART1_BASE, UART_FIFO_TX4_8, UART_FIFO_RX4_8);
    
        // No hardware flow control
        UARTFlowControlSet(UART1_BASE, UART_FLOWCONTROL_NONE);
    
        // Enable uDMA for the UART
        UARTDMAEnable(UART1_BASE, UART_DMA_RX);
    
        // Disable all interrupts
        UARTIntDisable(UART1_BASE, 0xffffffff);
    
        // Clear all interrupts
        UARTIntClear(UART1_BASE, 0xffffffff);
    
        // Set the UART clock source
        UARTClockSourceSet(UART1_BASE, UART_CLOCK_PIOSC);
    
        // Disable the uDMA channel to be used
        uDMAChannelDisable(UDMA_SGLOOP_CHANNEL & 0xffff);
    
        // Assign the proper peripheral for the uDMA channel
        uDMAChannelAssign(UDMA_SGLOOP_CHANNEL);
    
        // Reset the channel configuration
        uDMAChannelAttributeDisable(UDMA_SGLOOP_CHANNEL|UDMA_PRI_SELECT, UDMA_ATTR_ALL);
        uDMAChannelAttributeDisable(UDMA_SGLOOP_CHANNEL|UDMA_ALT_SELECT, UDMA_ATTR_ALL);
    
    
        // Install the dynamic pointer addresses to the tasklist
        // The ending address of the data transfer.
        uDMAsg_TaskList[0].pvSrcEndAddr = (void*)(UART1_BASE + UART_O_DR);
        uDMAsg_TaskList[1].pvDstEndAddr = (void*)((uint8_t*)pui8ControlTable + (((UDMA_SGLOOP_CHANNEL & 0xffff) + 1) * sizeof(tDMAControlTable)) - 1);
    
        // Save the tasklist
        uDMAChannelScatterGatherSet((UDMA_SGLOOP_CHANNEL & 0xffff), 2, uDMAsg_TaskList, true);
    
        // Copy the primary control structure from the uDMA controltable
        uDMAsg_LoopTask.pvDstEndAddr = pui8ControlTable[UDMA_SGLOOP_CHANNEL & 0xffff].pvDstEndAddr;
        uDMAsg_LoopTask.pvSrcEndAddr = pui8ControlTable[UDMA_SGLOOP_CHANNEL & 0xffff].pvSrcEndAddr;
        uDMAsg_LoopTask.ui32Control = pui8ControlTable[UDMA_SGLOOP_CHANNEL & 0xffff].ui32Control;
        uDMAsg_LoopTask.ui32Spare = pui8ControlTable[UDMA_SGLOOP_CHANNEL & 0xffff].ui32Spare;
    
    
        // Finally configure the UART speed and byte settings (this also enables the UART)
        UARTConfigSetExpClk(UART1_BASE, 16000000, 9600, (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE));
    
        // Finally enable uDMA for the SSI module and enable the uDMA channel itself
        uDMAChannelEnable(UDMA_SGLOOP_CHANNEL & 0xffff);
    }

    Thanks again for all of your help!

    Logan

  • Hi Logan,

    No worries, we all have many things to do - this forum rarely is top priority.

    I see you exercise a healthy dose of precaution when it comes to modifying working code - better to be safe than sorry. For "outside" readers, I'll show here what the tasklist declaration would look like with the modifications (for brevity I omitted the in-line comments):

    static tDMAControlTable uDMAsg_TaskList[] =
    {
            uDMATaskStructEntry(
                    600,
                    UDMA_SIZE_8,
                    UDMA_SRC_INC_NONE,
                    (void*)(UART1_BASE + UART_O_DR),
                    UDMA_DST_INC_8,
                    data,
                    UDMA_ARB_1,
                    UDMA_MODE_PER_SCATTER_GATHER
            ),
    
            uDMATaskStructEntry(
                    4,
                    UDMA_SIZE_32,
                    UDMA_SRC_INC_32,
                    &uDMAsg_LoopTask,
                    UDMA_DST_INC_32,
                    &(uDMA_ControlTable[UDMA_SGLOOP_CHANNEL & 0x1f]),
                    UDMA_ARB_4,
                    UDMA_MODE_PER_SCATTER_GATHER
            )
    };

    (I did not test this either, so the usual disclaimers apply)

    Then, these lines become unnecessary:

        // Install the dynamic pointer addresses to the tasklist
        // The ending address of the data transfer.
        uDMAsg_TaskList[0].pvSrcEndAddr = (void*)(UART1_BASE + UART_O_DR);
        uDMAsg_TaskList[1].pvDstEndAddr = (void*)((uint8_t*)pui8ControlTable + (((UDMA_SGLOOP_CHANNEL & 0xffff) + 1) * sizeof(tDMAControlTable)) - 1);

    (There is btw still a naming error, the DMA control table is declared as uDMA_ControlTable but used here as pui8ControlTable)

    The pointer arithmetic on the last line of the above code block might confuse readers. To explain that, here's an addition to the earlier Q&A:


    What is the purpose behind why you set these two values: uDMAsg_TaskList[0].pvSrcEndAddr and uDMAsg_TaskList[1].pvDstEndAddr?

    Usually these values are set by the uDMATaskStructEntry macro, or the uDMAChannelTransferSet function. Internally, the uDMA controller works with end pointers whereas we humans understand starting pointers better - hence the macro and function take in the starting pointers and then do that same black magic that is done with the setting of uDMAsg_TaskList[1].pvDstEndAddr. (There is actually no black magic, it's just referring to the start of the next element after the final one, minus one byte.) That said, you could indeed set them directly when you declare the task list in the first place.

    Logan Adams said:
    Thanks again for all of your help!

    You're very much welcome.

    Veikko