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.

EK-TM4C123GXL: I need a example code for udma

Part Number: EK-TM4C123GXL

Hello,

I setup GPIO PB2 as PWM Signal generated from Timer3a.

The Signal runs.

But i need a basic setup to setup the Timer3a to trigger a dma request.

The Timer3a have to trigger dma when Timer3a has a timeout.

I have a logicanalyzier to debug the signal.

My actual Timer Setup:

void init_TIMER_FOR_UDMA()
{
    //
    // Use GPIO PB2 as PWM Signal generated from Timer T3CCP0 Timer3a
    //
    
    //
    // The Timer1 peripheral must be enabled for use.
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER3);
    while(!SysCtlPeripheralReady(SYSCTL_PERIPH_TIMER3));
    
    //
    // Enable interrupts from the TIMER 3A software channel.
    //
    //IntEnable(INT_TIMER3A);
    
    //
    // Configure the GPIO pin muxing for the Timer/CCP function.
    // This is only necessary if your part supports GPIO pin function muxing.
    // Study the data sheet to see which functions are allocated per pin.
    // TODO: change this to select the port/pin you are using
    //
    GPIOPinConfigure(GPIO_PB2_T3CCP0);
    
    //
    // Configure the ccp settings for CCP pin.  This function also gives
    // control of these pins to the SSI hardware.  Consult the data sheet to
    // see which functions are allocated per pin.
    // TODO: change this to select the port/pin you are using.
    //
    GPIOPinTypeTimer(GPIO_PORTB_BASE, GPIO_PIN_2);
    
    //
    // Configure Timer3A as a 16-bit periodic timer.
    //
    TimerConfigure(TIMER3_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_PWM);
    
    //
    // Set the Timer3A load value to 50000.  For this example a 66% duty cycle
    // PWM signal will be generated.  From the load value (i.e. 50000) down to
    // match value (set below) the signal will be high.  From the match value
    // to 0 the timer will be low.
    //
    //TimerLoadSet(TIMER3_BASE, TIMER_A, (SysCtlClockGet() / 1));
    
    
    TimerLoadSet(TIMER3_BASE, TIMER_A, 65535);
    
    
    //
    // Set the Timer3A match value to load value / 3.
    //
    //TimerMatchSet(TIMER3_BASE, TIMER_A, TimerLoadGet(TIMER3_BASE, TIMER_A) / 4);
    //TimerMatchSet(TIMER3_BASE, TIMER_A, ((TimerLoadGet(TIMER3_BASE, TIMER_A) / 4)) * 3);
    
    
    TimerMatchSet(TIMER3_BASE, TIMER_A, 65535 / 4);
    
    
    //
    // Enables individual timer interrupt sources.
    //
    //TimerIntEnable(TIMER3_BASE, TIMER_TIMA_TIMEOUT);
    
    //
    // Enables the events that can trigger a uDMA request.
    //
    TimerDMAEventSet(TIMER3_BASE, TIMER_DMA_TIMEOUT_A);
    
    //
    // Gets the enabled attributes of a uDMA channel.
    //
    //static uint32_t ret_can_tri_req = 0;
    //ret_can_tri_req = TimerDMAEventGet(TIMER3_BASE);
    
    // Breakpoint
    //for(int i = 0; i < 3; i++);
    
    //
    // Enable Timer3A.
    //
    TimerEnable(TIMER3_BASE, TIMER_A);
    
    //
    // Wait for the clock configuration to set.
    //
    SysCtlDelay(10);
}

My actual DMA Setup:

void init_uDMA_TIMER_REQUEST()
{
    //
    // Use GPIO PF1 to show the Datatransfer via uDMA.
    // Trigger is GPIO PB2 as PWM Signal
    // generated from Timer T3CCP0 Timer3a.
    
    // REGISTER     0x40025000
    // OFFSET       0x00000008
    // PF1 REGISTER 0x40025008
    //

    //
    // PF1 Data Register.
    //
    #define PF1_REG ( uint32_t *)0x40025008

    //
    // Set PF1 as Digital Output.
    //
    GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_1);    

    //
    // Prepare source buffer with 2 increment 0x0, 0x2.
    //
    for (int i = 0; i < (MEM_BUFFER_SIZE - 1); i = i +2)
    {
        pui8SourceBuffer[i] = 0x2;
    }

    //
    // Enable the UDMA peripheral
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
    while(!SysCtlPeripheralReady(SYSCTL_PERIPH_UDMA));

    //
    // Enable the uDMA controller error interrupt.  This interrupt will occur
    // if there is a bus error during a transfer.
    //
    IntRegister(INT_UDMAERR, uDMAErrorHandler);
    IntEnable(INT_UDMAERR);
    
    //
    // Enable interrupts from the uDMA software channel.
    //
    IntRegister(INT_UDMA, uDMAIntHandler);
    IntEnable(INT_UDMA);
    
    //
    // Enable interrupts
    //
    IntMasterEnable();
    
    //
    // Enable the uDMA controller.
    //
    uDMAEnable();

    //
    // Set the base for the channel control table.
    //
    uDMAControlBaseSet(&pui8DMAControlTable[0]);

    // NOT DONE !!!!!!!!
    //
    // Assigns a peripheral mapping for a uDMA channel.
    //
    //uDMAChannelAssign(UDMA_CH2_TIMER3A);
    
    // NOT DONE !!!!!!!!
    //
    // Put the attributes in a known state for the uDMA ADC0 channel.  These
    // should already be disabled by default.
    //
    uDMAChannelAttributeDisable(UDMA_SEC_CHANNEL_TMR3A, UDMA_ATTR_ALTSELECT | 
                                UDMA_ATTR_HIGH_PRIORITY | UDMA_ATTR_REQMASK);
    
    // NOT DONE !!!!!!!!
    //
    // Enables attributes of a uDMA channel.
    //
    //uDMAChannelAttributeEnable(UDMA_CH2_TIMER3A, UDMA_ATTR_USEBURST | 
    //                           UDMA_ATTR_HIGH_PRIORITY);

    //
    // Now set up the characteristics of the transfer for 8-bit data size, with
    // source and destination increments in bytes, and a byte-wise buffer copy.
    // A bus arbitration size of 8 is used.
    //
    uDMAChannelControlSet(UDMA_SEC_CHANNEL_TMR3A | UDMA_PRI_SELECT, 
                          UDMA_SIZE_8 | UDMA_SRC_INC_8 | 
                          UDMA_DST_INC_32 | UDMA_ARB_64);

    //
    // The transfer buffers and transfer size are now configured. The transfer
    // uses AUTO mode, which means that the transfer automatically runs to
    // completion after the first request.
    //
    uDMAChannelTransferSet(UDMA_SEC_CHANNEL_TMR3A | UDMA_PRI_SELECT, 
                           UDMA_MODE_AUTO, pui8SourceBuffer, PF1_REG, MEM_BUFFER_SIZE);

    //
    // Set the USEBURST attribute for the uDMA ADC0 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.
    //
    uDMAChannelAttributeEnable(UDMA_SEC_CHANNEL_TMR3A, UDMA_ATTR_USEBURST);
    
    // NOT DONE !!!!!!!!
    //
    // Finally, the channel must be enabled. Because this is a software-
    // initiated transfer, a request must also be made. The request starts the
    // transfer.
    //
    uDMAChannelEnable(UDMA_SEC_CHANNEL_TMR3A);
    
    //
    // Wait for the clock configuration to set.
    //
    SysCtlDelay(10);
}

  • Hi,

      TivaWare has example for uDMA that you can find in C:\ti\TivaWare_C_Series-2.2.0.295\examples\boards\ek-tm4c123gxl\udma_demo. The example use uDMA to transfer data between memories and also between memory and UART. 

      I found this below snippet of code for transfer to timer module. This is for TM4C129. You can take a take and adapt to your application. 

    
    void
    Timer2AIntHandler(void)
    {
    
        uint32_t ui32Status;
        uint32_t ui32Mode;
    
    
        ui32Status = ROM_UARTIntStatus(TIMER2_BASE, 1);
    
        //
        // Clear any pending status, even though there should be none since no UART
        // interrupts were enabled.  If UART error interrupts were enabled, then
        // those interrupts could occur here and should be handled.  Since uDMA is
        // used for both the RX and TX, then neither of those interrupts should be
        // enabled.
        //
        ROM_UARTIntClear(TIMER2_BASE, ui32Status);
    
        //
        // Check the DMA control table to see if the ping-pong "A" transfer is
        // complete.  The "A" transfer uses receive buffer "A", and the primary
        // control structure.
        //
        //
        ui32Mode = ROM_uDMAChannelModeGet(UDMA_CH4_TIMER2A | UDMA_PRI_SELECT);
    
        //
        // If the UART4 DMA TX channel is disabled, that means the TX DMA transfer
        // is done.
        //
    
        if(!ROM_uDMAChannelIsEnabled(UDMA_CH4_TIMER2A))
        {
    
            //
            // Start another DMA transfer to UART4 TX.
            //
            ROM_uDMAChannelTransferSet(UDMA_CH4_TIMER2A | UDMA_PRI_SELECT,
                                       UDMA_MODE_BASIC, g_ui8TxBuf,
                                       (void *)(TIMER2_BASE + TIMER_O_TAMATCHR),
                                       sizeof(g_ui8TxBuf));
    
            //
            // The uDMA TX channel must be re-enabled.
            //
            ROM_uDMAChannelEnable(UDMA_CH4_TIMER2A);
        }
    }
    
    //*****************************************************************************
    //
    // Initializes the UART4 peripheral and sets up the TX and RX uDMA channels.
    // The UART is configured for loopback mode so that any data sent on TX will be
    // received on RX.  The uDMA channels are configured so that the TX channel
    // will copy data from a buffer to the UART TX output.  And the uDMA RX channel
    // will receive any incoming data into a pair of buffers in ping-pong mode.
    //
    //*****************************************************************************
    void
    InitTimer2ATransfer(void)
    {
        uint_fast16_t ui16Idx;
    
        //
        // Enable the peripherals used by this example.
        //
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER2);
    
        //
        // For this example T2CCP0 is used with port A pin 4.
        // The actual port and pins used may be different on your part, consult
        // the data sheet for more information.
        // GPIO port B needs to be enabled so these pins can be used.
        // TODO: change this to whichever GPIO port you are using.
        //
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    
        //
        // Configure the GPIO pin muxing for the Timer/CCP function.
        // This is only necessary if your part supports GPIO pin function muxing.
        // Study the data sheet to see which functions are allocated per pin.
        // TODO: change this to select the port/pin you are using
        //
        GPIOPinConfigure(GPIO_PA4_T2CCP0);
    
        //
        // Configure the ccp settings for CCP pin.  This function also gives
        // control of these pins to the Timer hardware.  Consult the data sheet to
        // see which functions are allocated per pin.
        // TODO: change this to select the port/pin you are using.
        //
        GPIOPinTypeTimer(GPIO_PORTA_BASE, GPIO_PIN_4);
    
        //
        // Enable DMA done interrupt
        //
        TimerIntEnable(TIMER2_BASE, TIMER_TIMA_DMA | TIMER_TIMA_TIMEOUT);
    
        //
        TimerDMAEventSet(TIMER2_BASE,TIMER_DMA_TIMEOUT_A);
    
    
        TimerControlLevel(TIMER2_BASE, TIMER_A, true);
    
        //
        TimerConfigure(TIMER2_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_PWM);
    
        // Set the Timer2A preload to /10
        TimerPrescaleSet(TIMER2_BASE, TIMER_A, 0x49);
        //
        // Set the Timer2A load value to 0x3E00.
        //
        TimerLoadSet(TIMER2_BASE, TIMER_A, 0x3E00);
    
        //
        // Set the Timer2A match value to load value / 2.
    
        TimerPrescaleMatchSet(TIMER2_BASE, TIMER_A,
                      TimerPrescaleGet(TIMER2_BASE, TIMER_A) / 2);
        //
        TimerMatchSet(TIMER2_BASE, TIMER_A,
                      TimerLoadGet(TIMER2_BASE, TIMER_A) / 2);
    
    
        //
        // Enable Timer1B.
        //
        TimerEnable(TIMER2_BASE, TIMER_A);
    
        //
        // Fill the TX buffer with a simple data pattern.
        //
        for(ui16Idx = 0; ui16Idx < UART_TXBUF_SIZE; ui16Idx++)
        {
            g_ui8TxBuf[ui16Idx] = ui16Idx;
        }
    
        //
        // Put the attributes in a known state for the uDMA UART4TX channel.  These
        // should already be disabled by default.
        //
    
        uDMAChannelAssign(UDMA_CH4_TIMER2A);
        ROM_uDMAChannelAttributeDisable(UDMA_CH4_TIMER2A,
                                        UDMA_ATTR_ALTSELECT |
                                        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.
        //
        ROM_uDMAChannelAttributeEnable(UDMA_CH4_TIMER2A, 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.
    
        ROM_uDMAChannelControlSet(UDMA_CH4_TIMER2A | UDMA_PRI_SELECT,
                                  UDMA_SIZE_8 | UDMA_SRC_INC_8 |
                                  UDMA_DST_INC_NONE |
                                  UDMA_ARB_1);
    
        //
        // Set up the transfer parameters for the uDMA UART TX channel.  This will
        // configure the transfer source and destination and the transfer size.
        // Basic mode is used because the peripheral is making the uDMA transfer
        // request.  The source is the TX buffer and the destination is the UART
        // data register.
        //
        //
        ROM_uDMAChannelTransferSet(UDMA_CH4_TIMER2A | UDMA_PRI_SELECT,
                                   UDMA_MODE_BASIC, g_ui8TxBuf,
                                   (void *)(TIMER2_BASE + TIMER_O_TAMATCHR),
                                   sizeof(g_ui8TxBuf));
    
        //
        // Now both the uDMA UART TX and RX channels are primed to start a
        // transfer.  As soon as the channels are enabled, the peripheral will
        // issue a transfer request and the data transfers will begin.
        //
        ROM_uDMAChannelEnable(UDMA_CH4_TIMER2A);
    }
    
    
    
    //*****************************************************************************
    //
    // Configure the UART and its pins.  This must be called before UARTprintf().
    //
    //*****************************************************************************
    void
    ConfigureUART(void)
    {
        //
        // Enable the GPIO Peripheral used by the UART.
        //
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    
        //
        // Enable UART0
        //
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
        ROM_SysCtlPeripheralSleepEnable(SYSCTL_PERIPH_UART0);
    
        //
        // Configure GPIO Pins for UART mode.
        //
        ROM_GPIOPinConfigure(GPIO_PA0_U0RX);
        ROM_GPIOPinConfigure(GPIO_PA1_U0TX);
        ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    
        //
        // Initialize the UART for console I/O.
        //
        UARTStdioConfig(0, 115200, g_ui32SysClock);
    }
    
    //*****************************************************************************
    //
    // This example demonstrates how to use the uDMA controller to transfer data
    // between memory buffers and to and from a peripheral, in this case a UART.
    // The uDMA controller is configured to repeatedly transfer a block of data
    // from one memory buffer to another.  It is also set up to repeatedly copy a
    // block of data from a buffer to the UART output.  The UART data is looped
    // back so the same data is received, and the uDMA controlled is configured to
    // continuously receive the UART data using ping-pong buffers.
    //
    // The processor is put to sleep when it is not doing anything, and this allows
    // collection of CPU usage data to see how much CPU is being used while the
    // data transfers are ongoing.
    //
    //*****************************************************************************
    int
    main(void)
    {
    
        //
        // Set the clocking to run directly from the crystal at 120MHz.
        //
        g_ui32SysClock = MAP_SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ |
                                                 SYSCTL_OSC_MAIN |
                                                 SYSCTL_USE_PLL |
                                                 SYSCTL_CFG_VCO_480), 120000000);
    
        //
        // Enable the GPIO port that is used for the on-board LED.
        //
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPION);
    
        //
        // Enable the GPIO pins for the LED (PN0).
        //
        ROM_GPIOPinTypeGPIOOutput(GPIO_PORTN_BASE, GPIO_PIN_0);
    
        //
        // Initialize the UART.
        //
        ConfigureUART();
    	UARTprintf("\033[2J\033[H");
        UARTprintf("uDMA for UART4TX Example \n");
    
        //
        // Enable the uDMA controller at the system level.
        //
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
    
        //
        // Enable the uDMA controller error interrupt.  This interrupt will occur
        // if there is a bus error during a transfer.
        //
        ROM_IntEnable(INT_UDMAERR);
    
        // Enable the processor to respond to interrupts.
        IntMasterEnable();
    
        //
        // Enable the uDMA controller.
        //
        ROM_uDMAEnable();
    
        //
        // Point at the control table to use for channel control structures.
        //
        ROM_uDMAControlBaseSet(pui8ControlTable);
    
    
        //
        // Initialize the uDMA Timer2A transfers.
        //
        InitTimer2ATransfer();
    
        //
        // Loop forever with the CPU not sleeping, so the debugger can connect.
        //
        while(1)
        {
    			  //
            // Turn on the LED.
            //
            GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_0, GPIO_PIN_0);
    
            //
            // Delay for a bit.
            //
            SysCtlDelay(g_ui32SysClock / 20 / 3);
    
            //
            // Turn off the LED.
            //
            GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_0, 0);
    
            //
            // Delay for a bit.
            //
            SysCtlDelay(g_ui32SysClock / 20 / 3);
        }
    }

  • Thanks for the reply. I studied the datasheet, the examples project, the example peripherals, the driverlib and the docu from the sdk lib.

    I cannot find my problem.

    When i do a uDMAChannelRequest(UDMA_SEC_CHANNELTMR3A);

    The DMA start to transfer.

    The problem is between timer request and udma.

    it is possible with a timer with is setup as pwm to make a request to the dma ?

  • Hi, 

      Let me try to understand what you are trying to do. Looking at your code you seem to setup the uDMA to transfer from memory to PF1 register. I suppose you want to use timer module to trigger the uDMA to start the transfer, right? If this is the case, why are you calling uDMAChannelRequest(UDMA_SEC_CHANNELTMR3A)? uDMAChannelRequest is meant to generate a software request. You should let the timer generate request to the uDMA to start the transfer. 

  • Hi,

    I am very lost.

    What i try:

    1. Setup a Timer to pwm

    2. when the timer is timeout (pos edge) then i need a dma request

    3. the dma data source contains 8bit array with 1024 items (the maximum)

    4. for every request the dma transfer 1 item from the array  from source to the destination data is also a array (at the moment for testing)

    void init_TIMER_FOR_UDMA()
    {
        SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER3);
        while(!SysCtlPeripheralReady(SYSCTL_PERIPH_TIMER3));
        
        GPIOPinTypeTimer(GPIO_PORTB_BASE, GPIO_PIN_2);
        GPIOPinConfigure(GPIO_PB2_T3CCP0);
        
        TimerConfigure(TIMER3_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_PWM);
        TimerLoadSet(TIMER3_BASE, TIMER_A, (SysCtlClockGet() / 800000));
        TimerMatchSet(TIMER3_BASE, TIMER_A, TimerLoadGet(TIMER3_BASE, TIMER_A) / 4);
        //TimerMatchSet(TIMER3_BASE, TIMER_A, ((TimerLoadGet(TIMER3_BASE, TIMER_A) / 4)) * 3);
        
        TimerControlEvent(TIMER3_BASE,TIMER_A,TIMER_EVENT_POS_EDGE);
        
        TimerIntEnable(TIMER3_BASE, TIMER_TIMA_TIMEOUT);
        TimerDMAEventSet(TIMER3_BASE, TIMER_DMA_TIMEOUT_A);
        TimerEnable(TIMER3_BASE, TIMER_A);
        SysCtlDelay(10);
    }

    //*****************************************************************************
    //
    // The size of the memory transfer source and destination buffers (in words).
    //
    //*****************************************************************************
    #define MEM_BUFFER_SIZE         1024
    
    //*****************************************************************************
    //
    // The source and destination buffers used for memory transfers.
    //
    //*****************************************************************************
    static uint8_t pui8SourceBuffer[MEM_BUFFER_SIZE];
    static uint8_t pui8DestBuffer[MEM_BUFFER_SIZE];
    
    //*****************************************************************************
    //
    // The control table used by the uDMA controller.  This table must be aligned
    // to a 1024 byte boundary.
    //
    //*****************************************************************************
    
    #pragma data_alignment=1024
    uint8_t pui8DMAControlTable[1024];
    
    
    
    void init_uDMA_TIMER_REQUEST()
    {
        #define PF1_REG ( uint32_t *)0x40025008
      
        //test data
        for (int i = 0; i < (MEM_BUFFER_SIZE - 1); i = i +1)
        {
            pui8SourceBuffer[i] = i;
        }
        SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
        while(!SysCtlPeripheralReady(SYSCTL_PERIPH_UDMA));
    
        uDMAEnable();
        uDMAControlBaseSet(pui8DMAControlTable);
        uDMAChannelAssign(UDMA_CH2_TIMER3A);
        
        uDMAChannelAttributeDisable(UDMA_SEC_CHANNEL_TMR3A, UDMA_ATTR_ALTSELECT | 
                                    UDMA_ATTR_HIGH_PRIORITY | UDMA_ATTR_REQMASK);
        
        uDMAChannelAttributeEnable(UDMA_SEC_CHANNEL_TMR3A, UDMA_ATTR_USEBURST | UDMA_ATTR_REQMASK);
    
        uDMAChannelControlSet(UDMA_SEC_CHANNEL_TMR3A | UDMA_PRI_SELECT, 
                              UDMA_SIZE_8 | UDMA_SRC_INC_8 | 
                              UDMA_DST_INC_8 | UDMA_ARB_1);
    
        
        uDMAChannelTransferSet(UDMA_SEC_CHANNEL_TMR3A | UDMA_PRI_SELECT, 
                               UDMA_MODE_BASIC, pui8SourceBuffer, pui8DestBuffer, 4);
    
        uDMAChannelEnable(UDMA_SEC_CHANNEL_TMR3A);
        SysCtlDelay(10);
    }

    I need a pwm signal. with setup the duty cycle every period himself.

    every new pos edge from the pwm the dma get a request from the timer

    to send data to the MATCHSET Register from the timer.

    the data is an 8bit array with 1024 items

    in every item are the data for the MATCHSET Register.

    the importing thing is:

    a request from timer to the dma transfer 1 item from array from source to destination

    1. request from timer -> pui8SourceBuffer[0] to destination

    2. request from timer -> pui8SourceBuffer[1] to destination

    3. request from timer -> pui8SourceBuffer[2] to destination

    4. request from timer -> pui8SourceBuffer[3] to destination

    .....

    when all items 1024 transferd the dma channel will be close by himself.

    and wait for another start like

    - uDMAChannelTransferSet(........);

    - uDMAChannelEnable(.....);

     

    Very thanks for your help.

  • Hi,

      I think I know what you are trying to do. One thing I'm pretty sure about is that you cannot configure the timer in PWM mode and at the same time generate DMA request. I have tested it myself on a TM4C129 device. What seems to be partially working for me on a TM4C129 device is that you need to use two timers. You can use timerA to generate DMA request and timerB to generate PWM. Both timerA and timerB can be configured to have the same preload value. The preload value for timerA would have been the timeout value and the same preload value would have been the PWM period. When timerA times out, it generates a request to the DMA module. The DMA module will then transfer from your buffer array to the match register for timerB. With the that, I can't get the same thing to work on TM4C123 yet. 

  • Hi,

      Here is one working example that uses Timer2A to generate DMA request repeatedly upon timeout. The PWM duty cycle of Timer2B is changed each time when Timer2A timeout. 

     

    #include <stdint.h>
    #include <stdbool.h>
    #include "inc/hw_ints.h"
    #include "inc/hw_memmap.h"
    #include "inc/hw_types.h"
    #include "inc/hw_uart.h"
    #include "inc/hw_timer.h"
    #include "driverlib/fpu.h"
    #include "driverlib/gpio.h"
    #include "driverlib/interrupt.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/rom.h"
    #include "driverlib/rom_map.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/systick.h"
    #include "driverlib/uart.h"
    #include "driverlib/udma.h"
    #include "driverlib/timer.h"
    #include "utils/cpu_usage.h"
    #include "utils/uartstdio.h"
    #include "utils/ustdlib.h"
    
    //*****************************************************************************
    //
    //! \addtogroup example_list
    //! <h1>uDMA (udma_demo)</h1>
    //!
    //! This example application demonstrates the use of the uDMA controller to
    //! transfer data between memory buffers, and to transfer data to and from a
    //! UART.  The test runs for 10 seconds before exiting.
    //!
    //! UART0, connected to the ICDI virtual COM port and running at 115,200,
    //! 8-N-1, is used to display messages from this application.
    //
    //*****************************************************************************
    
    //****************************************************************************
    //
    // System clock rate in Hz.
    //
    //****************************************************************************
    uint32_t g_ui32SysClock;
    
    //*****************************************************************************
    //
    // The number of SysTick ticks per second used for the SysTick interrupt.
    //
    //*****************************************************************************
    #define SYSTICKS_PER_SECOND     100
    
    //*****************************************************************************
    //
    // The transmit  buffers used for the UART transfers.
    //
    //*****************************************************************************
    static uint16_t g_ui16TxBuf;
    
    //*****************************************************************************
    //
    // The count of uDMA errors.  This value is incremented by the uDMA error
    // handler.
    //
    //*****************************************************************************
    static uint32_t g_ui32uDMAErrCount = 0;
    
    //*****************************************************************************
    //
    // The count of times the uDMA interrupt occurred but the uDMA transfer was not
    // complete.  This should remain 0.
    //
    //*****************************************************************************
    static uint32_t g_ui32BadISR = 0;
    
    
    //*****************************************************************************
    //
    // The number of seconds elapsed since the start of the program.  This value is
    // maintained by the SysTick interrupt handler.
    //
    //*****************************************************************************
    static uint32_t g_ui32Seconds = 0;
    
    //*****************************************************************************
    //
    // The control table used by the uDMA controller.  This table must be aligned
    // to a 1024 byte boundary.
    //
    //*****************************************************************************
    #if defined(ewarm)
    #pragma data_alignment=1024
    uint8_t pui8ControlTable[1024];
    #elif defined(ccs)
    #pragma DATA_ALIGN(pui8ControlTable, 1024)
    uint8_t pui8ControlTable[1024];
    #else
    uint8_t pui8ControlTable[1024] __attribute__ ((aligned(1024)));
    #endif
    
    //*****************************************************************************
    //
    // The error routine that is called if the driver library encounters an error.
    //
    //*****************************************************************************
    #ifdef DEBUG
    void
    __error__(char *pcFilename, uint32_t ui32Line)
    {
    }
    #endif
    
    //*****************************************************************************
    //
    // The interrupt handler for the SysTick timer.  This handler will increment a
    // seconds counter whenever the appropriate number of ticks has occurred.  It
    // will also call the CPU usage tick function to find the CPU usage percent.
    //
    //*****************************************************************************
    void
    SysTickHandler(void)
    {
        static uint32_t ui32TickCount = 0;
    
        //
        // Increment the tick counter.
        //
        ui32TickCount++;
    
        //
        // If the number of ticks per second has occurred, then increment the
        // seconds counter.
        //
        if(!(ui32TickCount % SYSTICKS_PER_SECOND))
        {
            g_ui32Seconds++;
        }
    }
    
    //*****************************************************************************
    //
    // The interrupt handler for uDMA errors.  This interrupt will occur if the
    // uDMA encounters a bus error while trying to perform a transfer.  This
    // handler just increments a counter if an error occurs.
    //
    //*****************************************************************************
    void
    uDMAErrorHandler(void)
    {
        uint32_t ui32Status;
    
        //
        // Check for uDMA error bit
        //
        ui32Status = ROM_uDMAErrorStatusGet();
    
        //
        // If there is a uDMA error, then clear the error and increment
        // the error counter.
        //
        if(ui32Status)
        {
            ROM_uDMAErrorStatusClear();
            g_ui32uDMAErrCount++;
        }
    }
    
    //*****************************************************************************
    //
    // The interrupt handler for uDMA interrupts from the memory channel.  This
    // interrupt will increment a counter, and then restart another memory
    // transfer.
    //
    //*****************************************************************************
    void
    uDMAIntHandler(void)
    {
        g_ui32BadISR++;
    }
    
    //*****************************************************************************
    //
    // The interrupt handler for Timer2A
    //
    //*****************************************************************************
    
    void
    Timer2AIntHandler(void)
    {
    
        uint32_t ui32Status;
    
        //
        // Read the interrupt status of the TMR2A.
        //
    
        ui32Status = MAP_UARTIntStatus(TIMER2_BASE, 1);
    
        //
        // Clear any pending status
        //
        MAP_UARTIntClear(TIMER2_BASE, ui32Status);
    
        if (g_ui16TxBuf == 0xFE) {
            g_ui16TxBuf = 0x1;
        } else {
            g_ui16TxBuf++;
        }
    
        // When DMA is done with the channel, the channel is also disabled
        // in the UDMA_ENASET register along with the mode changed to UDMA_MODE_STOP.
    
        if(!MAP_uDMAChannelIsEnabled(UDMA_CH4_TIMER2A))
        {
            //
            // Start another DMA transfer again.
            //
            MAP_uDMAChannelTransferSet(UDMA_CH4_TIMER2A | UDMA_PRI_SELECT,
                                       UDMA_MODE_BASIC, &g_ui16TxBuf,
                                       (void *)(TIMER2_BASE + TIMER_O_TBMATCHR),
                                       sizeof(g_ui16TxBuf));
    
            //
            // The uDMA TX channel must be re-enabled.
            //
            MAP_uDMAChannelEnable(UDMA_CH4_TIMER2A);
        }
    }
    
    //*****************************************************************************
    //
    // Initializes Timer2A to generate periodic DMA request to uDMA. Upon receiving
    // the request from Timer2A, it will transfer duty cycle value to Timer2B which
    // is setup for PWM.
    //*****************************************************************************
    void
    InitTimer2ATransfer(void)
    {
        //
        // Enable the peripherals used by this example.
        //
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER2);
    
        //
        // For this example T2CCP1 is used with port B pin 1.
        // The actual port and pins used may be different on your part, consult
        // the data sheet for more information.
        // GPIO port B needs to be enabled so these pins can be used.
        // TODO: change this to whichever GPIO port you are using.
        //
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
    
        //
        // Configure the GPIO pin muxing for the Timer/CCP function.
        // This is only necessary if your part supports GPIO pin function muxing.
        // Study the data sheet to see which functions are allocated per pin.
        // TODO: change this to select the port/pin you are using
        //
        GPIOPinConfigure(GPIO_PB1_T2CCP1);
    
        //
        // Configure the ccp settings for CCP pin.  This function also gives
        // control of these pins to the Timer hardware.  Consult the data sheet to
        // see which functions are allocated per pin.
        // TODO: change this to select the port/pin you are using.
        //
        GPIOPinTypeTimer(GPIO_PORTB_BASE, GPIO_PIN_1);
    
        //
        // Configure Timer2A for periodic DMA request and Timer2B for PWM
        TimerConfigure(TIMER2_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_PERIODIC | TIMER_CFG_B_PWM);
    
        //
        // Timer2A will generate DMA request every 1ms
        //
        TimerLoadSet(TIMER2_BASE, TIMER_A, 0xFF);
    
        //
        // Timer2B will generate PWM with 1ms PWM period
        //
        TimerLoadSet(TIMER2_BASE, TIMER_B, 0xFF);
    
        //
        // Enable DMA done interrupt
        //
        TimerIntEnable(TIMER2_BASE, TIMER_TIMA_DMA);
    
        //
        // Enable Timer2A for interrupt
        //
        IntEnable(INT_TIMER2A);
    
        //
        // Enable Timer2A timeout event for DMA
        //
        TimerDMAEventSet(TIMER2_BASE,TIMER_DMA_TIMEOUT_A);
    
        //
        // Only reload the match value after counter reaches zero.
        //
        TimerUpdateMode(TIMER2_BASE, TIMER_B, TIMER_UP_MATCH_TIMEOUT);
    
        //
        // Enable Timer2.
        //
        TimerEnable(TIMER2_BASE, TIMER_BOTH);
    
        //
        // Fill the TX buffer with a simple data pattern.
        //
    
        g_ui16TxBuf = 0x0;
    
        //
        // Assign Timer2A to channel 4
        //
        uDMAChannelAssign(UDMA_CH4_TIMER2A);
    
        //
        // Put the attributes in a known state for the uDMA Timer2A channel.  These
        // should already be disabled by default.
        //
    
        MAP_uDMAChannelAttributeDisable(UDMA_CH4_TIMER2A,
                                        UDMA_ATTR_ALTSELECT |
                                        UDMA_ATTR_HIGH_PRIORITY |
                                        UDMA_ATTR_REQMASK);
    
        //
        // Set the USEBURST attribute for the uDMA Timer2A TX channel.
        //
        MAP_uDMAChannelAttributeEnable(UDMA_CH4_TIMER2A, UDMA_ATTR_USEBURST);
    
        //
        // Configure the control parameters for the Timer2A. Both source and
        // destination do not increment address. Make arbitration size equal
        // to 1 since we only transfer 1 16-bit half-word of data.
        //
    
        MAP_uDMAChannelControlSet(UDMA_CH4_TIMER2A | UDMA_PRI_SELECT,
                                  UDMA_SIZE_16 | UDMA_SRC_INC_NONE |
                                  UDMA_DST_INC_NONE |
                                  UDMA_ARB_1);
    
        //
        // Set up the transfer parameters for the uDMA UART TX channel. The
        // source is the buffer while the destination is the Timer2B match
        // register.
        //
        MAP_uDMAChannelTransferSet(UDMA_CH4_TIMER2A | UDMA_PRI_SELECT,
                                   UDMA_MODE_BASIC, &g_ui16TxBuf,
                                   (void *)(TIMER2_BASE + TIMER_O_TBMATCHR),
                                   sizeof(g_ui16TxBuf));
    
        //
        // Start DMA operation
        //
        MAP_uDMAChannelEnable(UDMA_CH4_TIMER2A);
    
    }
    
    //*****************************************************************************
    //
    // Configure the UART and its pins.  This must be called before UARTprintf().
    //
    //*****************************************************************************
    void
    ConfigureUART(void)
    {
        //
        // Enable the GPIO Peripheral used by the UART.
        //
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    
        //
        // Enable UART0
        //
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
    
        //
        // Configure GPIO Pins for UART mode.
        //
        MAP_GPIOPinConfigure(GPIO_PA0_U0RX);
        MAP_GPIOPinConfigure(GPIO_PA1_U0TX);
        MAP_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    
        //
        // Use the internal 16MHz oscillator as the UART clock source.
        //
        UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);
    
        //
        // Initialize the UART for console I/O.
        //
        UARTStdioConfig(0, 115200, 16000000);
    }
    
    //*****************************************************************************
    //
    // This example demonstrates how to use the uDMA controller to transfer data
    // between memory buffers and to and from a peripheral, in this case a UART.
    // The uDMA controller is configured to repeatedly transfer a block of data
    // from one memory buffer to another.  It is also set up to repeatedly copy a
    // block of data from a buffer to the UART output.  The UART data is looped
    // back so the same data is received, and the uDMA controlled is configured to
    // continuously receive the UART data using ping-pong buffers.
    //
    // The processor is put to sleep when it is not doing anything, and this allows
    // collection of CPU usage data to see how much CPU is being used while the
    // data transfers are ongoing.
    //
    //*****************************************************************************
    int
    main(void)
    {
        //
        // Set the clocking to run directly from the crystal at 80MHz.
        //
        SysCtlClockSet(SYSCTL_SYSDIV_2_5|SYSCTL_USE_PLL|SYSCTL_OSC_MAIN|SYSCTL_XTAL_16MHZ);
    
        //
        // Initialize the UART.
        //
        ConfigureUART();
        UARTprintf("\033[2J\033[H");
        UARTprintf("uDMA for Timer Example \n");
    
        //
        // Enable the uDMA controller at the system level.
        //
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
    
        //
        // Enable the uDMA controller error interrupt.  This interrupt will occur
        // if there is a bus error during a transfer.
        //
        MAP_IntEnable(INT_UDMAERR);
    
        // Enable the processor to respond to interrupts.
        IntMasterEnable();
    
        //
        // Enable the uDMA controller.
        //
        MAP_uDMAEnable();
    
        //
        // Point at the control table to use for channel control structures.
        //
        MAP_uDMAControlBaseSet(pui8ControlTable);
    
    
        //
        // Initialize the uDMA Timer2A transfers.
        //
        InitTimer2ATransfer();
    
        //
        // Loop forever with the CPU not sleeping, so the debugger can connect.
        //
        while(1)
        {
    
        }
    }

  • Thank you very much for your answer.
    I immediately went back to my project.
    With a frequency of 800kHz for one period, the PWM signal is not stable.
    I still don't really understand how a DMA channel works. I have read the datasheet several times.
    If I set a DMA channel like in the code.
    I expect the following sequence:
    The data source is an array with 16 bit each.
    The array has 120 elements as an example.
    1. if i turn on the DMA channel then.
    2. the DMA channel waits for a request.
    3. the counter produces an interrupt every 800kHz and makes a request to the channel from the dma.
    4. exactly one element is transferred from the data source.
    5. as long as not all array elements are requested, the DMA channel is not closed.
    6. the CPU has now time for other things.
    7. at some point the last element of the array is transferred.
    8. then the DMA channel is closed.
    9. i get an interrupt that the channel is finished.
    10. and have to restart the channel with a command.

    Translated with www.DeepL.com/Translator (free version)

    Code doesnt work.

  • Your problem is the below line where you specify the transfer size equal to the array size which 260. You are asking the uDMA to transfer 260 duty cycles in every PWM period. You only want to change the duty cycle once per PWM period, not 260 times. With what you do, the PWM most likely takes the last element value. Think about it if you are doing using CPU interrupt. Before a PWM period is complete, will you want to update the match register 260 times?

    MAP_uDMAChannelTransferSet(UDMA_CH4_TIMER2A | UDMA_PRI_SELECT,
    UDMA_MODE_BASIC, &g_ui16TxBuf_ARRAY,
    (void *)(TIMER2_BASE + TIMER_O_TBMATCHR),
    sizeof(g_ui16TxBuf_ARRAY));

  • I don't understand.
    In the function uDMAChannelTransferSet()
    I can set how many items should be transferred per transfer.

    WIth this command : UDMA_ARB_1

    MAP_uDMAChannelControlSet(UDMA_CH4_TIMER2A | UDMA_PRI_SELECT,
    UDMA_SIZE_16 | UDMA_SRC_INC_16 |
    UDMA_DST_INC_NONE |
    UDMA_ARB_1);

    >Before a PWM period is complete, will you want to update the match register 260 times?<

    I always will transfer one item per period to the match register.

    On the next request, the next array element is to be transferred.

    How i can setup the timer request to a single request?

    I need to transfer 1024 items, 1 items at a time every 1250 ns, configure a timer to generate a periodic timeout at 1250 ms. Configure the μDMA transfer for a total of 1024 items, with a burst size of 1 items. Each time the timer times out, the μDMA controller transfers 1 items, until all 1024 items have been transferred.

    Configure the μDMA transfer for a total of 1024 items:

    void uDMAChannelTransferSet(uint32_t ui32ChannelStructIndex, uint32_t ui32Mode, void *pvSrcAddr, void *pvDstAddr, uint32_t ui32TransferSize)

    ui32TransferSize is the number of data items to transfer. Have to be 1024

    Right?

    with a burst size of 1 items:

    void uDMAChannelControlSet(uint32_t ui32ChannelStructIndex, uint32_t ui32Control)

    The arbitration size determines how many items are transferred before the uDMA controller re- arbitrates for the bus. Choose the arbitration size from one of UDMA_ARB_1, UDMA_ARB_2, UDMA_ARB_4, UDMA_ARB_8, through UDMA_ARB_1024 to select the arbitration size from 1 to 1024 items, in powers of .

    Have to be 1

    right?

    Or do I understand the parameters wrong and have to swap them ?

  • WIth this command : UDMA_ARB_1

    MAP_uDMAChannelControlSet(UDMA_CH4_TIMER2A | UDMA_PRI_SELECT,
    UDMA_SIZE_16 | UDMA_SRC_INC_16 |
    UDMA_DST_INC_NONE |
    UDMA_ARB_1);

    This merely sets up the arbitration size. Setting UDMA_ARB_1 means if there are multiple pending DMA channels, it will do one transfer and then switch out to other channels. Since you only have one channel, it will not switch out. It will continue to transfer the entire 260 elements per request. That is why in my example I only set up the transfer count to 1. When each DMA transfer is done, it signals to the timer module to generate an interrupt. In the ISR, you will re-enable the transfer again. I understand the desire to transfer 260 elements with each corresponding to one PWM period and has as little as possible of CPU intervention but that is not how it works as explained. 

  • I found this in the Datasheet p. 720.

    For example, to transfer 256 items, 8 items at a time every 10 ms, configure a timer to generate a periodic timeout at 10 ms. Configure the μDMA transfer for a total of 256 items, with a burst size of 8 items. Each time the timer times out, the μDMA controller transfers 8 items, until all 256 items have been transferred.

    i need this it is importend for my project.

  • For example, to transfer 256 items, 8 items at a time every 10 ms, configure a timer to generate a periodic timeout at 10 ms. Configure the μDMA transfer for a total of 256 items, with a burst size of 8 items. Each time the timer times out, the μDMA controller transfers 8 items, until all 256 items have been transferred.

    What you describe here will not work for you. It is still having 256 items. Bursting is only meant to improve the transfer efficiency. If you burst more than one item (e.g. 8 items as in the description from the datasheet example), it will transfer 8 duty cycles for each DMA request. If there are no other pending channels to serve, uDMA will burst 8 items for a total of (256 / 8 = 32) times to complete the 256 items. Transferring 256 items will easily finish before 10ms. If 10ms is your PWM period then within one PWM period you have updated the match register 256 times. I have explained a few times already. I don't know what else to say. I already said you need to configure transfer size to 1. Agree that configuring the transfer size to 1 perhaps is not any better than using CPU directly to move data in the interrupt.