LP-MSPM0C1104: DMA not updating with ADC12 sequence conversions after first trigger

Part Number: LP-MSPM0C1104
Other Parts Discussed in Thread: SYSCONFIG

Tool/software:

I am trying to read the results of the 12-bit ADC via DMA on an MSPM0 device.

The ADC is triggered by a Timer0 zero-event, which occurs every second (Timer Period = 1000 ms).
On each trigger, the ADC performs a non-repeated sequence-of-channels conversion across 3 ADC channels (configured MEM0, MEM1, MEM2).

Once the sequence completes, the ADC asserts the MEM2_RESULT_LOADED interrupt. This should then trigger the DMA, which is configured to transfer the results from the ADC MEMx registers into an array.

The problem:

  • The transfer works only the very first time the Timer hits zero.

  • Every subsequent DMA transfer always contains the same first set of values, even when the physical input signals change (e.g., channels tied to GND).

  • If I disable DMA and instead use the ADC interrupt handler to manually read the MEMx results, the values are updated correctly for every trigger.

What I’ve checked / observations:

  • ADC sequence itself is working and producing new results (verified through ISR).

  • The DMA channel is armed and interrupts fire as expected (DMA_DONE occurs).

  • However, the destination buffer is never updated with fresh conversions after the first trigger.

  • Buffer is a 32-bit array, DMA configured for half-word transfers (possible width mismatch?).

  • Source increment is currently enabled (might be wrong, since all results come from fixed ADC MEM registers).

  • DMA is initialized after the ADC is enabled — could ordering of ADC enable vs. DMA setup be relevant?

So it seems to be a configuration mismatch between ADC12 DMA triggering and the DMA channel setup (width, increment, init sequence) rather than an ADC problem.

Could you please confirm the expected configuration of DMA for multi-channel ADC sequences (e.g. srcIncrement, transfer width, init order)?

  • It really sounds like the DMA is not being (re-)enabled (DL_DMA_enableChannel()).

    Can you show the sequence you use to (re-)arm the conversion sequence? Maybe there's a clue there.

  • Excuse the late reply, i will present you my code. Interestingly the reading of the ADC values seems to function only when the DMA Address Mode is Single-Address to Single-Address. But that will be insufficient for my requirements, so I need to be able to use Block-Address-Transfers: 

    volatile bool gConversionDone = false;
    volatile uint32_t resultsADC[3] = {};

    int main(void)
    {
        SYSCFG_DL_init();

        NVIC_EnableIRQ(ADC12_0_INST_INT_IRQN);
        NVIC_EnableIRQ(UART_0_INST_INT_IRQN);

        DL_ADC12_enableConversions(ADC12_0_INST);

        DL_DMA_setSrcAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t) DL_ADC12_getMemResultAddress(ADC12_0_INST, DL_ADC12_MEM_IDX_0));
        DL_DMA_setDestAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t) (&resultsADC[0]));
        DL_DMA_setTransferSize(DMA, DMA_CH0_CHAN_ID, 3);
        DL_DMA_enableChannel(DMA, DMA_CH0_CHAN_ID);

        DL_TimerA_startCounter(TIMER_0_INST);

        while(1) {

            while (gConversionDone == false) {
                __WFE();
            }

            gConversionDone = false;

            DL_DMA_setTransferSize(DMA, DMA_CH0_CHAN_ID, 3);
            DL_DMA_enableChannel(DMA, DMA_CH0_CHAN_ID);

            DL_ADC12_enableConversions(ADC12_0_INST);

            uint32_t mv0 = (resultsADC[0] & 0x0FFF) * 3300 >> 12;
            sprintf(gStr0, "%u", mv0);

            uint32_t mv1 = (resultsADC[1] & 0x0FFF) * 3300 >> 12;
            sprintf(gStr1, "%u", mv1);

            uint32_t mv2 = (resultsADC[2] & 0x0FFF) * 1400 >> 12;
            sprintf(gStr2, "%u", mv2);  
           
        }
    }

    void ADC12_0_INST_IRQHandler() {
        switch (DL_ADC12_getPendingInterrupt(ADC12_0_INST)) {
            case DL_ADC12_IIDX_DMA_DONE:
                gConversionDone = true;
                break;
            case DL_ADC12_IIDX_MEM2_RESULT_LOADED:
                break;        
            default:
                break;
        }
    }

  • Hi Jo,
    Can you try resetting the source and destination addresses in the while loop? Maybe this could help if it's not updating the addresses correctly. Also, are you using a continuous timer and down count for Timer0?

    Best Regards,

    Diego Abad

  • What Diego said. After a non-repeated transfer, the source/destination addresses are left as they were; only after a repeated transfer are they restored to their original values.

  • I am not sure if I understand your suggestion correctly. Within the SysConfig I configured the DMA for Block to Block Transfer, Increment Mode, Half Word data length and Block Transfer Mode. So I should manually set the destination and source address for each transfer (using DL_DMA_SetSrcAddr and DL_DMA_SetDestAddr)? 

    I am using a periodic down counting timer for Timer0.

  • Yes, you should call DL_DMA_SetDestAddr (and maybe DL_DMA_SetSrcAddr) again before the DL_DMA_enableChannel call. Alternatively you could use a Block transfer with both source and destination non-incrementing.

    If you compare TRM (SLAU893C) Table 5-2 for DMATM=0 and =2, you'll notice that only DMATM=2 (Repeated) mentions "The DAMSA[sic], DMADA and DMASZ registers are reloaded to the original value when the DMASZ counted down to zero", so if the pointers are being incremented they're left that way at the end. As an experiment: try pausing your program in the debugger and check where DMADA points (that's how I noticed this artifact). 

    Unsolicited: Be careful about calling sprintf() in the C1104. My finding (here) was that sprintf("%d") costs >840 bytes of stack (80% of your SRAM). ltoa() might be a better choice. This isn't your problem now, but it will be eventually. 

**Attention** This is a public forum