LP-MSPM0C1104: trigger an interrupt after data transfer from DMA

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

Dear Experts, I am fighting with a problem to indicate "work is done":

I sample ADC on LP-MSPM0C1104 for 128 results, transfer them into the variable for FFT[] and can see values coming from ADC in memory location for the array. Now I want to call the ISR to set a variable dma_done = true; I am searching the way how to program "DL_DMA_INTERRUPT_CHAN_DONE". All work done so far is done by SYSCONFIG.

But I cannot see how to configure such an event in the DMA-block.

Your example in https://dev.ti.com/tirex/explore/node?isTheia=false&node=A__AI.BkXZOjs1Mumz8FFaLTg__MSPM0-SDK__a3PaaoK__LATEST

checks in ISR for ADC if work is done:

switch (DL_ADC12_getPendingInterrupt(ADC12_0_INST)) {
        case DL_ADC12_IIDX_DMA_DONE:
            gCheckADC = true;
            break;
        default:
            break;

I wonder, why you don't choose the way via ISR for DMA, means howto fire the ISR?

void DMA_IRQHandler(void) {
   /*
   if (DMA->CH0CTL & DMA_CHCTL_TCIF) {
        DMA->CH0CTL &= ~DMA_CHCTL_TCIF; // Flag löschen
     */
        dma_done = true;
}
 
The /**/ are inserted,, because there are no such declaration found. But a Breakpoint on line with dma_done should indicate the call and entrance into the ISR.
 
Any guidelines?
 
BR Bernd
 
  • Hi Bernd,
    Let me give this issue a look, and I'll get back to you by Monday next week.

    Best Regards,

    Diego Abad

  • Dear Diego, that's ok to me. Meanwhile I am digging in slau893c. Enjoy weekend and BR Bernd

  • Hi Bernd,
    It seems the example is taking advantage of the Direct peripheral to DMA trigger from ADC. I think you can use also the interrupts from a general channel DMA like in the dma_table_transfer examples.

    Best Regards,

    Diego Abad

  • Dear Diego,

    Many thanks for the hint to the other example(s) Every day I am learning a little bit more. What I have figured out and finally it works, DMA is directly configurable through the ADC block in SYSCONFIG. What I have done this morning, in different order as shown, removed the DMA ISR from my code. I have introduced two memory cells for debugging "dbg_ms_counter1" and "dbg_ms_counter2", where I store the counter value of my millisecond timer/counter.

    I have rearranged my intro codelines and added DL_ADC12_enableInterrupt(ADC12_0_INST, DL_ADC12_INTERRUPT_DMA_DONE);

    In slau893c I have seen in figure above 11.2 ADC Opreation, DMADONE is shown. To my understanding, DMADONE comes from ADC ISR and not through DMA ISR call.
    Here is my ADC ISR: 
    void ADC12_0_INST_IRQHandler(void)
    {
        int iixx = DL_ADC12_getPendingInterrupt(ADC12_0_INST);
        dbgCtr++;
        dbg_ms_counter2 = ms_counter;
        if (dbgCtr == 128 ) {
            dbgCtr = 0;
        }
        switch (iixx) {
            case DL_ADC12_IIDX_DMA_DONE:
                gCheckADC = true;
                break;
            case DL_ADC12_IIDX_UNDERFLOW:
                gADCError = true;
                break;
            default:
                break;
        }
    }
     
    At the moment and for purpose of debugging and learning, my program hangs in a loop and triggers the ADC->FIFO->DMA->...DONE one time.
    When I stop the debugging process, i see all 128 data from ADC in the right place in memory and can read dbgCtr is "1". The difference between millisecond counters is 64 and if I change the number from 128 to 4, the time difference is 4.
    This means: ADC ISR is called only one time. That's what I want.
    For all other readers of this request/discussion, feel free to read my code snip from the beginning of program and the config block from SYSCONFIG:
       
    int main(void)
    {
        SYSCFG_DL_init();

        NVIC_ClearPendingIRQ(UART_0_INST_INT_IRQN);
        NVIC_EnableIRQ(UART_0_INST_INT_IRQN);
        NVIC_EnableIRQ(TIMER_0_INST_INT_IRQN);

        /* Calling WFI after calling DL_SYSCTL_enableSleepOnExit will result in
         * only ISR code to be executed. This is done to showcase the device's
         * low power consumption when sleeping.
         */
        // DL_SYSCTL_enableSleepOnExit();

        for( uint8_t i = 0; i < NFFT; i++) {
            xFFT[i].real = 0; xFFT[i].imag = i;
        }
    ...
        /* Configure DMA source, destination and size */
        DL_DMA_setSrcAddr(DMA, DMA_CH0_CHAN_ID,
            (uint32_t) DL_ADC12_getFIFOAddress(ADC12_0_INST));

        DL_DMA_setDestAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t) &xFFT[0]);
        DL_DMA_setTransferSize(DMA, DMA_CH0_CHAN_ID, NFFT);

        DL_DMA_enableChannel(DMA, DMA_CH0_CHAN_ID);
        DL_ADC12_enableDMA(ADC12_0_INST);
       
        DL_ADC12_enableInterrupt(ADC12_0_INST, DL_ADC12_INTERRUPT_DMA_DONE);
        NVIC_EnableIRQ(ADC0_INT_IRQn);

        gCheckADC = false;
        dbg_ms_counter1 = ms_counter;
        DL_ADC12_startConversion(ADC12_0_INST);

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

        while (1) {
    ...
    These lines are part of file generated by SYSCONFIG:
    /* ADC12_0 Initialization */
    static const DL_ADC12_ClockConfig gADC12_0ClockConfig = {
        .clockSel       = DL_ADC12_CLOCK_ULPCLK,
        .divideRatio    = DL_ADC12_CLOCK_DIVIDE_4,
        .freqRange      = DL_ADC12_CLOCK_FREQ_RANGE_20_TO_24,
    };
    SYSCONFIG_WEAK void SYSCFG_DL_ADC12_0_init(void)
    {
        DL_ADC12_setClockConfig(ADC12_0_INST, (DL_ADC12_ClockConfig *) &gADC12_0ClockConfig);
        DL_ADC12_initSingleSample(ADC12_0_INST,
            DL_ADC12_REPEAT_MODE_ENABLED, DL_ADC12_SAMPLING_SOURCE_AUTO, DL_ADC12_TRIG_SRC_SOFTWARE,
            DL_ADC12_SAMP_CONV_RES_10_BIT, DL_ADC12_SAMP_CONV_DATA_FORMAT_UNSIGNED);
        DL_ADC12_configConversionMem(ADC12_0_INST, ADC12_0_ADCMEM_0,
            DL_ADC12_INPUT_CHAN_5, DL_ADC12_REFERENCE_VOLTAGE_VDDA, DL_ADC12_SAMPLE_TIMER_SOURCE_SCOMP0, DL_ADC12_AVERAGING_MODE_DISABLED,
            DL_ADC12_BURN_OUT_SOURCE_DISABLED, DL_ADC12_TRIGGER_MODE_AUTO_NEXT, DL_ADC12_WINDOWS_COMP_MODE_DISABLED);
        DL_ADC12_enableFIFO(ADC12_0_INST);
        DL_ADC12_setSampleTime0(ADC12_0_INST,750);
        DL_ADC12_enableDMA(ADC12_0_INST);
        DL_ADC12_setDMASamplesCnt(ADC12_0_INST,1);
        DL_ADC12_enableDMATrigger(ADC12_0_INST,(DL_ADC12_DMA_MEM0_RESULT_LOADED));
        /* Enable ADC12 interrupt */
        DL_ADC12_clearInterruptStatus(ADC12_0_INST,(DL_ADC12_INTERRUPT_DMA_DONE));
        DL_ADC12_enableInterrupt(ADC12_0_INST,(DL_ADC12_INTERRUPT_DMA_DONE));
        DL_ADC12_enableConversions(ADC12_0_INST);
    }

    static const DL_DMA_Config gDMA_CH0Config = {
        .transferMode   = DL_DMA_SINGLE_TRANSFER_MODE,
        .extendedMode   = DL_DMA_NORMAL_MODE,
        .destIncrement  = DL_DMA_ADDR_STRIDE_2,
        .srcIncrement   = DL_DMA_ADDR_UNCHANGED,
        .destWidth      = DL_DMA_WIDTH_HALF_WORD,
        .srcWidth       = DL_DMA_WIDTH_HALF_WORD,
        .trigger        = ADC12_0_INST_DMA_TRIGGER,
        .triggerType    = DL_DMA_TRIGGER_TYPE_EXTERNAL,
    };

    SYSCONFIG_WEAK void SYSCFG_DL_DMA_CH0_init(void)
    {
        DL_DMA_initChannel(DMA, DMA_CH0_CHAN_ID , (DL_DMA_Config *) &gDMA_CH0Config);
    }
    SYSCONFIG_WEAK void SYSCFG_DL_DMA_init(void){
        SYSCFG_DL_DMA_CH0_init();
    }
    To all others, enjoy reading..
    To Diego, TNX, this is resolved... BR Bernd