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.

TM4C123GH6PM: DMA Channel Array [1024] of 8BIT send to 32BIT CPU register

Part Number: TM4C123GH6PM

Hi,

it possible to setup a uDMA Channel for sending an Array [1024] of 8BIT to 32BIT CPU register?

  • Hello Kevin,

    There are more than one possible meaning to what you are asking here.

    Are you wanting to send 8 bit data into a 32 bit register where you fill the register with 4 transfer, or are you expecting that each time you send the data, the other 24 bits are padded with 0's such as sending 8 bits to UARTDR for the UART FIFO?

    Best Regards,

    Ralph Jacobi

  • Thank you very much for your answer.
    The uDMA channel is connected to a GPIO port.
    So the trigger is a GPIO port.
    The source of the data is a 1024 array of 32 BIT.
    The destination is a timer match register.
    Always one item per GPIO trigger should be sent from the array to the match register of the timer.
    In this configuration everything works.
    But I send only DECIMAL values smaller than 255.
    And I am running out of SRAM.
    And if I could make a 1024 array of 8BIT out of the 1024 array of 32 BIT. I can save a lot of memory.

    here my config:

    void init_uDMA()
    {
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
        while(!MAP_SysCtlPeripheralReady(SYSCTL_PERIPH_UDMA));
        
        // general uDMA setup
        MAP_uDMAEnable();
        MAP_uDMAControlBaseSet(uDMAControlTable);
        MAP_IntEnable(INT_UDMAERR);
        
        // led signal setup
        MAP_uDMAChannelAssign(UDMA_CH5_GPIOB);
        MAP_uDMAChannelAttributeDisable(UDMA_CH5_GPIOB, 
                                        UDMA_ATTR_ALTSELECT | 
                                        UDMA_ATTR_HIGH_PRIORITY | 
                                        UDMA_ATTR_REQMASK | 
                                        UDMA_ATTR_USEBURST);
        
        MAP_uDMAChannelControlSet(UDMA_CH5_GPIOB | UDMA_PRI_SELECT, 
                                  UDMA_SIZE_32 | UDMA_SRC_INC_32 | 
                                  UDMA_DST_INC_NONE | UDMA_ARB_1);
        
        MAP_uDMAChannelControlSet(UDMA_CH5_GPIOB | UDMA_ALT_SELECT, 
                                  UDMA_SIZE_32 | UDMA_SRC_INC_32 | 
                                  UDMA_DST_INC_NONE | UDMA_ARB_1);
    }

    void startStreamPri(uint16_t size)
    {
        MAP_uDMAChannelTransferSet(UDMA_CH5_GPIOB | UDMA_PRI_SELECT,
                                       UDMA_MODE_PINGPONG, &streamData_PRI,
                                       (void *)(TIMER1_BASE + TIMER_O_TAMATCHR), 
                                       size);
            
        DEBUG_PD6 = GPIO_PIN_6; // PRISTART
        MAP_uDMAChannelEnable(UDMA_CH5_GPIOB | UDMA_PRI_SELECT);
        streamStatus_PRI = ENABLED;
    }
    
    void startStreamAlt(uint16_t size)
    {
        MAP_uDMAChannelTransferSet(UDMA_CH5_GPIOB | UDMA_ALT_SELECT,
                                       UDMA_MODE_PINGPONG, &streamData_ALT,
                                       (void *)(TIMER1_BASE + TIMER_O_TAMATCHR), 
                                       size);
            
        DEBUG_PC4 = GPIO_PIN_4; // ALTSTART
        MAP_uDMAChannelEnable(UDMA_CH5_GPIOB | UDMA_ALT_SELECT);
        streamStatus_ALT = ENABLED;
    }

    static uint32_t streamData_PRI
    static uint32_t streamData_ALT

  • Hello Kevin,

    I see, this is an interesting use case and I haven't quite tried to do something like it before. I don't see any real issue on my end because you are updating a register value, so while the DMA says that the "source and destination data size must be the same for any given transfer", I think you can get away with it in this case.

    However, if not, my understanding here is that your goal is to just store everything as an 8-bit value:

    And if I could make a 1024 array of 8BIT out of the 1024 array of 32 BIT.

    So if you can't do an 8-bit transfer successfully, then perhaps you can find a means to type cast the data to transmit it? I haven't tried that before for a case like this, but that is the next solution which comes to mind.

    Best Regards,

    Ralph Jacobi

  • I have tried to type the data, but without success.
    Can you suggest me how to type the data?

  • Hello Kevin,

    Since its passed by reference it may not be possible, I couldn't find a simple method for doing so. Before I dig deeper on that, have you tried simply making an 8-bit array instead? If so did you get errors either from compiling or from the DMA?

    Best Regards,

    Ralph Jacobi

  • You mean a have to make a 8-bit array and with this config:

    MAP_uDMAChannelControlSet(UDMA_CH5_GPIOB | UDMA_PRI_SELECT, 
                                  UDMA_SIZE_32 | UDMA_SRC_INC_32 | 
                                  UDMA_DST_INC_NONE | UDMA_ARB_1);

  • Hello Kevin,

    No, you need to configure it to send 8-bit data size as well. So configure uDMA for 8-bit transfers and use an 8-bit array.

    Best Regards,

    Ralph Jacobi

  • I did this.

    It seems like that the values don't arrive the timer match register.

    I got a dma done interrupt and i can see the dma is working.

    EDIT:

    I watched the signal with a logic analyzer and i can see after the first transfer there is this in the match register:

    0b0000'0000'0000'0000'0110'0010'0110'0010

    But it have to be a DEZ 74.

    What is wrong?

    EDIT2:

    In the match register is:

    0b0000'0000'0000'0000'0000'0000'0110'0010

    After i start a udma channel then there is:

    0b0000'0000'0000'0000'0100'1010'0100'1010

    But it have to be:

    0b100'1010

    The DEZ number is 74.

    Why the number is in BIN doubble?

  • Hello Kevin,

    Can you share your current code for how you are configuring the Timer and the uDMA so I can both review it and run it on my board to further test?

    Best Regards,

    Ralph Jacobi

  • void init_SYS_CLOCK()
    {
        MAP_SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | 
                       SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
        
        MAP_SysCtlDelay(10);
    }
    

    void init_uDMA()
    {
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
        while(!MAP_SysCtlPeripheralReady(SYSCTL_PERIPH_UDMA));
        
        // general uDMA setup
        MAP_uDMAEnable();
        MAP_uDMAControlBaseSet(uDMAControlTable);
        MAP_IntEnable(INT_UDMAERR);
        
        MAP_uDMAChannelAssign(UDMA_CH5_GPIOB);
        MAP_uDMAChannelAttributeDisable(UDMA_CH5_GPIOB, 
                                        UDMA_ATTR_ALTSELECT | 
                                        UDMA_ATTR_HIGH_PRIORITY | 
                                        UDMA_ATTR_REQMASK | 
                                        UDMA_ATTR_USEBURST);
        
        MAP_uDMAChannelControlSet(UDMA_CH5_GPIOB | UDMA_PRI_SELECT, 
                                  UDMA_SIZE_8 | UDMA_SRC_INC_8 | 
                                  UDMA_DST_INC_NONE | UDMA_ARB_1);
        
        MAP_uDMAChannelControlSet(UDMA_CH5_GPIOB | UDMA_ALT_SELECT, 
                                  UDMA_SIZE_8 | UDMA_SRC_INC_8 | 
                                  UDMA_DST_INC_NONE | UDMA_ARB_1);
    }

    void init_TIMER()
    {
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER1);
        while(!MAP_SysCtlPeripheralReady(SYSCTL_PERIPH_TIMER1));
        
        MAP_GPIOPinConfigure(GPIO_PB4_T1CCP0);
        MAP_GPIOPinConfigure(GPIO_PB5_T1CCP1);
        
        MAP_GPIOPinTypeTimer(GPIO_PORTB_BASE, GPIO_PIN_4);
        MAP_GPIOPinTypeTimer(GPIO_PORTB_BASE, GPIO_PIN_5);
        
        MAP_TimerConfigure(TIMER1_BASE, TIMER_CFG_SPLIT_PAIR | 
                           TIMER_CFG_A_PWM | TIMER_CFG_B_PWM);
        
        MAP_TimerLoadSet(TIMER1_BASE, TIMER_BOTH, 99);
        
        MAP_TimerMatchSet(TIMER1_BASE, TIMER_A, 98);
        
        MAP_TimerMatchSet(TIMER1_BASE, TIMER_B, 23);
        
        MAP_TimerEnable(TIMER1_BASE, TIMER_BOTH);
    }

    void init_GPIO_B()
    {
        MAP_GPIODirModeSet(GPIO_PORTB_BASE, GPIO_PIN_0, 
                       GPIO_DIR_MODE_IN);
        
        MAP_GPIOPadConfigSet(GPIO_PORTB_BASE, GPIO_PIN_0, GPIO_STRENGTH_2MA, 
                         GPIO_PIN_TYPE_STD_WPU);
        
        MAP_GPIODMATriggerEnable(GPIO_PORTB_BASE, GPIO_PIN_0);
        
        MAP_GPIOIntTypeSet(GPIO_PORTB_BASE, GPIO_PIN_0, GPIO_FALLING_EDGE);
        MAP_GPIOIntEnable(GPIO_PORTB_BASE, GPIO_INT_DMA);
        
        // ------------------ //
        // Priority Levels
        // Priority 4 = 0xE0 
        // Priority 3 = 0x60
        // Priority 2 = 0x20
        // Priority 1 = 0x00
        // ------------------ //
        
        MAP_IntPrioritySet(INT_GPIOB, 0x20); // Priority 2
        MAP_IntEnable(INT_GPIOB);
    }

    void startStreamPri(uint16_t size)
    {
        MAP_uDMAChannelTransferSet(UDMA_CH5_GPIOB | UDMA_PRI_SELECT,
                                       UDMA_MODE_PINGPONG, &streamData_PRI,
                                       (void *)(TIMER1_BASE + TIMER_O_TAMATCHR), 
                                       size);
            
        DEBUG_PD6 = GPIO_PIN_6; // PRISTART
        MAP_uDMAChannelEnable(UDMA_CH5_GPIOB | UDMA_PRI_SELECT);
        streamStatus_PRI = ENABLED;
    }
    
    void startStreamAlt(uint16_t size)
    {
        MAP_uDMAChannelTransferSet(UDMA_CH5_GPIOB | UDMA_ALT_SELECT,
                                       UDMA_MODE_PINGPONG, &streamData_ALT,
                                       (void *)(TIMER1_BASE + TIMER_O_TAMATCHR), 
                                       size);
            
        DEBUG_PC4 = GPIO_PIN_4; // ALTSTART
        MAP_uDMAChannelEnable(UDMA_CH5_GPIOB | UDMA_ALT_SELECT);
        streamStatus_ALT = ENABLED;
    }

    static uint8_t streamData_PRI[1024];
    static uint8_t streamData_ALT[1024];

    PORTs HAVE TO BE BRIDGED:

    Need to be bridged: PB0 <---> PB5 with a cable

  • Hello Kevin,

    So I've replicated the issue with the 8-bit size array and I haven't found a workaround for that yet, but I did identify one compromise.

    If you use 16-bit array size and 16-bit transfers, I was seeing the correct data transfer to the Timer Match Register. That would cut your buffer size used up by 50% which should be notable.

    Right now I don't know if there is a way to get an 8-bit transfer working because I suspect the issue is the Timer Register is expecting 16-bits for the full size of the Match register and I don't believe there would be a way to get it to instead accept the upper 8 bits as '0' instead of repeating the data.

    Best Regards,

    Ralph Jacobi

  • Thank you very much!

    This will help me.

    I never test it with 16 bit ;-)

  • Hi Kevin,

    Glad that helps a lot. I talked with my colleague and we suspect the Timer is reading from the DMA until it gets 16 bits to fill the match register while the DMA just broadcasts the data until the Timer indicates it has gotten it successfully, and that is the reason that the Timer gets the repeat 8-bit data. We don't see a workaround to that issue, so the best solution will be the 16-bit value, so I'm glad that helps you.

    Best Regards,

    Ralph Jacobi