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.

TMS320F28379D: DMA does not automatically clear the ADC interrupt flag

Part Number: TMS320F28379D
Other Parts Discussed in Thread: C2000WARE

Tool/software:

I want to collect a voltage signal and transfer it to a specified array with DMA. After following the routine modification, I found that the program could not trigger the end of transfer interruption for DMA. I tried to clear the ADC's interrupt flag after each ADC transition, and the DMA was able to transfer the data normally and enter the end interrupt. However, I saw in the user manual that DMA will actively signal an interrupt purge to the interrupt source.

I'm wondering why my program didn't clear the ADC's interrupt flag. There is no code to clear ADC interrupts or even enable the corresponding interrupt function in the DMA transfer ADC result routine, but the program works normally. Here's my code, begging for help.

#include "driverlib.h"
#include "device.h"
#include "F28x_Project.h"

#define RESULTS_BUFFER_SIZE     256

void initADC(void);
void initEPWM(void);
void initADCSOC(void);
void initializeDMA(void);
void configureDMAChannels(void);

__interrupt void adcA1ISR(void);
__interrupt void dmach1ISR(void);

#pragma DATA_SECTION(adcAResults, "ramgs0");
uint16_t adcAResults[RESULTS_BUFFER_SIZE];
volatile uint16_t bufferFull;

void main(void)
{

    // Initialize device clock and peripherals
    Device_init();

    // Disable pin locks and enable internal pullups.
    Device_initGPIO();

    /*
    #ifdef _STANDALONE
    #ifdef _FLASH
        // Send boot command to allow the CPU2 application to begin execution
        Device_bootCPU2(C1C2_BROM_BOOTMODE_BOOT_FROM_FLASH);
    #else
        // Send boot command to allow the CPU2 application to begin execution
        Device_bootCPU2(C1C2_BROM_BOOTMODE_BOOT_FROM_RAM);
    #endif // _FLASH
    #endif // _STANDALONE
    */

    // Initialize PIE and clear PIE registers. Disables CPU interrupts.
    Interrupt_initModule();

    // Initialize the PIE vector table with pointers to the shell Interrupt
    // Service Routines (ISR).
    Interrupt_initVectorTable();

    // Interrupts that are used in this example are re-mapped to ISR functions
    // found within this file.
    Interrupt_register(INT_ADCA1, &adcA1ISR);

    // ISR for DMA ch1 - occurs when DMA transfer is complete
    Interrupt_register(INT_DMA_CH1, &dmach1ISR);

    // Enable specific PIE & CPU interrupts:
    // DMA interrupt - Group 7, interrupt 1
    Interrupt_enable(INT_DMA_CH1);

    // Set up the ADC and the ePWM and initialize the SOC
    initADC();
    initEPWM();
    initADCSOC();

    // Initialize the DMA & configure DMA channels 1
    initializeDMA();
    configureDMAChannels();

    bufferFull = 0;

    // Enable ADC interrupt
    Interrupt_enable(INT_ADCA1);

    // Clearing all pending interrupt flags
    DMA_clearTriggerFlag(DMA_CH1_BASE);   // DMA channel 1
    HWREGH(ADCA_BASE + ADC_O_INTFLGCLR) = 0x1U; // ADCA

    // Enable Global Interrupt (INTM) and realtime interrupt (DBGM)
    EINT;
    ERTM;

    for(;;)
    {
        // Start DMA
        DMA_startChannel(DMA_CH1_BASE);

        // Start ePWM1, enabling SOCA and putting the counter in up-count mode
        EPWM_enableADCTrigger(EPWM1_BASE, EPWM_SOC_A);
        EPWM_setTimeBaseCounterMode(EPWM1_BASE, EPWM_COUNTER_MODE_UP);

        // Wait while ePWM1 causes ADC conversions which then cause interrupts.
        // When the results buffer is filled, the bufferFull flag will be set.
        while(bufferFull == 0)
        {

        }
        bufferFull = 0;     // Clear the buffer full flag

        // Stop ePWM1, disabling SOCA and freezing the counter
        EPWM_disableADCTrigger(EPWM1_BASE, EPWM_SOC_A);
        EPWM_setTimeBaseCounterMode(EPWM1_BASE, EPWM_COUNTER_MODE_STOP_FREEZE);

        // Software breakpoint. At this point, conversion results are stored in
        // adcAResults.
        // Hit run again to get updated conversions.
        ESTOP0;
    }
}


// Function to configure and power up ADCA.
void initADC(void)
{
    // Set ADCDLK divider to /4
    ADC_setPrescaler(ADCA_BASE, ADC_CLK_DIV_4_0);

    // Set resolution and signal mode (see #defines above) and load
    // corresponding trims.
    ADC_setMode(ADCA_BASE, ADC_RESOLUTION_12BIT, ADC_MODE_SINGLE_ENDED);

    // Set pulse positions to late
    ADC_setInterruptPulseMode(ADCA_BASE, ADC_PULSE_END_OF_CONV);

    // Power up the ADC and then delay for 1 ms
    ADC_enableConverter(ADCA_BASE);
    DEVICE_DELAY_US(1000);
}

// Function to configure ePWM1 to generate the SOC.
void initEPWM(void)
{
    // Disable SOCA
    EPWM_disableADCTrigger(EPWM1_BASE, EPWM_SOC_A);

    // Configure the SOC to occur on the first up-count event
    EPWM_setADCTriggerSource(EPWM1_BASE, EPWM_SOC_A, EPWM_SOC_TBCTR_U_CMPA);
    EPWM_setADCTriggerEventPrescale(EPWM1_BASE, EPWM_SOC_A, 1);

    // Set the compare A value to 1000 and the period to 1999
    // Assuming ePWM clock is 100MHz, this would give 50kHz sampling
    // 50MHz ePWM clock would give 25kHz sampling, etc.
    // The sample rate can also be modulated by changing the ePWM period
    // directly (ensure that the compare A value is less than the period).
    EPWM_setCounterCompareValue(EPWM1_BASE, EPWM_COUNTER_COMPARE_A, 49);
    EPWM_setTimeBasePeriod(EPWM1_BASE, 99);

    // Set the local ePWM module clock divider to /1
    EPWM_setClockPrescaler(EPWM1_BASE,
                           EPWM_CLOCK_DIVIDER_1,
                           EPWM_HSCLOCK_DIVIDER_1);

    // Freeze the counter
    EPWM_setTimeBaseCounterMode(EPWM1_BASE, EPWM_COUNTER_MODE_STOP_FREEZE);
}

// Function to configure ADCA's SOC0 to be triggered by ePWM1.
void initADCSOC(void)
{
    // Configure SOC0 of ADCA to convert pin A1. The EPWM1SOCA signal will be
    // the trigger.
    // - For 12-bit resolution, a sampling window of 15 (75 ns at a 200MHz
    //   SYSCLK rate) will be used.
    // - NOTE: A longer sampling window will be required if the ADC driving
    //   source is less than ideal (an ideal source would be a high bandwidth
    //   op-amp with a small series resistance). See TI application report
    //   SPRACT6 for guidance on ADC driver design.
    ADC_setupSOC(ADCA_BASE, ADC_SOC_NUMBER0, ADC_TRIGGER_EPWM1_SOCA,
                 ADC_CH_ADCIN0, 15);

    // Set SOC0 to set the interrupt 1 flag. Enable the interrupt and make
    // sure its flag is cleared.
    ADC_setInterruptSource(ADCA_BASE, ADC_INT_NUMBER1, ADC_SOC_NUMBER0);
    ADC_enableInterrupt(ADCA_BASE, ADC_INT_NUMBER1);
    ADC_clearInterruptStatus(ADCA_BASE, ADC_INT_NUMBER1);
}

void initializeDMA(void)
{
    // Perform a hard reset on DMA
    DMA_initController();

    // Allow DMA to run free on emulation suspend
    DMA_setEmulationMode(DMA_EMULATION_FREE_RUN);
}

void configureDMAChannels(void)
{
    // DMA channel 1 set up for ADCA
    DMA_configAddresses(DMA_CH1_BASE, (uint16_t *)&adcAResults,
                        (uint16_t *)(ADCARESULT_BASE+ADC_SOC_NUMBER0));

    DMA_configBurst(DMA_CH1_BASE, 1, 0, 0);
    DMA_configTransfer(DMA_CH1_BASE, RESULTS_BUFFER_SIZE, 0, 1);
    DMA_configMode(DMA_CH1_BASE, DMA_TRIGGER_ADCA1,
                  (DMA_CFG_ONESHOT_DISABLE | DMA_CFG_CONTINUOUS_DISABLE |
                   DMA_CFG_SIZE_16BIT));

    DMA_enableTrigger(DMA_CH1_BASE);
    DMA_disableOverrunInterrupt(DMA_CH1_BASE);
    DMA_setInterruptMode(DMA_CH1_BASE, DMA_INT_AT_END);
    DMA_enableInterrupt(DMA_CH1_BASE);
}

// ADC A Interrupt 1 ISR
__interrupt void adcA1ISR(void)
{
    // Add the latest result to the buffer
    //adcAResults[index++] = ADC_readResult(ADCARESULT_BASE, ADC_SOC_NUMBER0);

    // Clear the interrupt flag
    ADC_clearInterruptStatus(ADCA_BASE, ADC_INT_NUMBER1);

    // Check if overflow has occurred
    /*
    if(true == ADC_getInterruptOverflowStatus(ADCA_BASE, ADC_INT_NUMBER1))
    {
        ADC_clearInterruptOverflowStatus(ADCA_BASE, ADC_INT_NUMBER1);
        ADC_clearInterruptStatus(ADCA_BASE, ADC_INT_NUMBER1);
    }
    */

    // Acknowledge the interrupt
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP1);

}

#pragma CODE_SECTION(dmach1ISR, ".TI.ramfunc");
__interrupt void dmach1ISR(void)
{
    bufferFull = 1;

    // Acknowledge interrupt
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP7);
}

  • Hello,

    Note that with ADC/DMA operation the timings are a little complicated - see the below Errata:

    Can you compare your code configurations against the C2000ware example in path: [C2000ware install]\driverlib\f2837xd\examples\cpu1\adc\adc_ex6_soc_continous_dma?

    Best Regards,

    Delaney