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.

TMS320F28335: Getting random bad data from A/D converter through DMA

Part Number: TMS320F28335
Other Parts Discussed in Thread: SYSBIOS, C2000WARE

I am using the 28035 DMA subsystem to buffer data from the A/D converter. The A/D converter is triggered by an ePWM module. The ePWM is configured as up/down with a SOC generated at timer 0 and timer period match. The DMA1 is configured to transfer the A/D results to a buffer, 16 words/burst and 2 bursts per transfer triggered by ADC sequence 1 complete. DMA complete triggers an ISR that processes the data and resets the DMA. This system has been in place for several months and seemed to be working well.

I was debugging a strange 'glitch' that would occur in the output current of an LLC converter. Infrequently there would be a disturbance of the output current for no apparent reason. It seemed to happen randomly. I've traced this back to an occasional 0 appearing in the DMA buffer. In the operating mode at the time, the A2D count should have been ~3200, so i would get a long stream of values around 3200, the a single 0, then back to 3200 again. The DMA buffer is 32 words long and contains 2 samples of each A/D channel. The LLC current in my case is in buffer indices 9 and 25 (current is the 10th A/D channel in the sequence). The 0 is always at index 9 and never at index 25. There's no code that clears the DMA buffer, it's declared const in the C code. The project is entirely written in C and C++ with no assembly language.

I suspected some kind of memory corruption so I moved to the DMA buffer to an unused section of SARAM. It didn't change anything. I've also tried changing the sequence order. That just proved it's happening on other channels as well. I've also verified that the signal going into the A/D converter is correct.

I'm out of ideas; I can't find anything in the datasheets that says I'm doing something wrong, and can't find anything in the errata about the A/D converter and the DMA.

Has anyone else had issues using the DMA with the A/D converter?

  • Kevin,

    Would you be able to share your ADC and DMA initialization code snippets?

    -Tommy
  • ADC initialization:
    int16_t init_adc( void )
    {
    InitAdc();

    // ADCTRL1 setup
    // AdcRegs.ADCTRL1.bit.SUSMOD = 01; // stop after current sequence
    // AdcRegs.ADCTRL1.bit.ACQ_PS = 0010; // acquisition size = 3
    // AdcRegs.ADCTRL1.bit.SEQ_CASC = 1; // cascaded sequencer mode
    // Everything else 0
    AdcRegs.ADCTRL1.all = 0x1210;

    // ADCTRL2 setup
    // AdcRegs.ADCTRL2.bit.EPWM_SOCB_SEQ = 1; // SOCB
    // AdcRegs.ADCTRL2.bit.RST_SEQ1 = 1; // reset to CONV0
    // AdcRegs.ADCTRL2.bit.INT_ENA_SEQ1 = 1; // enable SEQ1 interrupt
    // Everything else 0
    AdcRegs.ADCTRL2.all = 0xC800;

    // Need a 25Mhz clock for ADC. ADC Clock is HSPCLK/2*ADCCLKPS
    AdcRegs.ADCTRL3.bit.ADCCLKPS = ADC_MODCLK;

    //
    // * set sequence channels
    // * They need to be in the same order as the RawAdc struct!!
    //
    AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 8; // input current phA - B0
    AdcRegs.ADCCHSELSEQ1.bit.CONV01 = 9; // input current phB - B1
    AdcRegs.ADCCHSELSEQ1.bit.CONV02 = 10; // input current phC - B2
    AdcRegs.ADCCHSELSEQ1.bit.CONV03 = 0; // input voltage phA - A0
    AdcRegs.ADCCHSELSEQ2.bit.CONV04 = 1; // input voltage phB - A1
    AdcRegs.ADCCHSELSEQ2.bit.CONV05 = 2; // input voltage phC - A2
    AdcRegs.ADCCHSELSEQ2.bit.CONV06 = 3; // dc link pos - A3
    AdcRegs.ADCCHSELSEQ2.bit.CONV07 = 11; // dc link neg - B3

    AdcRegs.ADCCHSELSEQ3.bit.CONV08 = 4; // output voltage - A4
    AdcRegs.ADCCHSELSEQ3.bit.CONV09 = 12; // output current - B4
    AdcRegs.ADCCHSELSEQ3.bit.CONV10 = 7; // output bus A - A7
    AdcRegs.ADCCHSELSEQ3.bit.CONV11 = 14; // output bus B - B6

    AdcRegs.ADCCHSELSEQ4.bit.CONV12 = 13; // internal temp
    AdcRegs.ADCCHSELSEQ4.bit.CONV13 = 15; // unused
    AdcRegs.ADCCHSELSEQ4.bit.CONV14 = 5; // IGBT temp
    AdcRegs.ADCCHSELSEQ4.bit.CONV15 = 6; // low V sense
    AdcRegs.ADCMAXCONV.all = 15;

    // copy default scale factors, will be overwritten by eeprom data after initialization
    for ( uint16_t idx = 0; idx < RAW_ADC_DATA_SIZE; idx++ )
    {
    FastAdcCal.i[idx] = defaultCalibrationScaleFactors[idx];
    }

    AdcDMAConfigure();

    dmaDownSampleCount = 0;

    MuxAdcState = MuxStateRead0;
    MuxDelayCount = MuxDelay;
    SetAdcMux0();

    // this is no longer a sysbios HWI, but a plain old isr.
    EALLOW;
    PieVectTable.DINTCH1 = &DMAComplete_isr;
    EDIS;

    PieCtrlRegs.PIEIER7.bit.INTx1 = 1;
    IER |= M_INT7;

    return 1;

    }

    DMA initialization:
    void AdcDMAConfigure( void )
    {
    volatile int16_t* dmaDst = &DMABuffer[0];
    volatile Uint16* dmaSrc = &AdcMirror.ADCRESULT0;

    // initialize DMA hardware
    DMAInitialize();

    // configure dma source and destination addresses
    DMACH1AddrConfig( (Uint16*)dmaDst, dmaSrc );

    // 16 words/burst(X-1), increment source 1 word, increment dest 1 word
    DMACH1BurstConfig( 15, 1, 1 );

    // 2 bursts/transfer(X-1), src transfer step, dest transfer step
    DMACH1TransferConfig( 1, 1, 1 );

    // wrap source after 1 burst, no offset, wrap dest after 2 bursts, no offset
    DMACH1WrapConfig( 0, 0, 1, 0 );

    // trigger source = ADC sequence1 interrupt, enable interrupt(from adc), disable one-shot and continuous,
    // no sync, 16-bit, interupt enabled(to cpu) at end of sequence
    DMACH1ModeConfig( DMA_SEQ1INT, PERINT_ENABLE, ONESHOT_DISABLE, CONT_DISABLE, SYNC_DISABLE, SYNC_SRC,
    OVRFLOW_DISABLE, SIXTEEN_BIT, CHINT_END, CHINT_ENABLE );

    StartDMACH1();
    }
  • Kevin,

    Can you confirm that you are using F28035?  This code looks like it is more likely from F28335.

    -Tommy

  • Tommy,

    I apologise. Yes, i am using the 28335.

    Kevin
  • Kevin,

    This does seem weird. Is it easy to produce the 0-value conversion such that you can try some experiments? Is this behavior evident on multiple boards/devices?

    -Tommy
  • Tommy,

    I have tried this on 2 different boards with the same result. I have devised a test case that I've used to capture this, so I can recreate it.

    Kevin
  • Kevin,

    A quick clarification for my sake.  How are you generating SOCB on both TBCTR=0 and TBCTR=PRD?  Are you updating the SOCBSEL value using an intermediate ISR?

    It would be good if we can try to isolate the root cause of this behavior.

    An easy start would be to set a Hardware Watchpoint at &DMABuffer[9] to look for spurious CPU writes.

    The next experiment would be to see if either (AdcMirror.ADCRESULT9 == 0) or (AdcRegs.ADCRESULT9 == 0) when (DMABuffer[9] == 0). This check would likely need to take place in an intermediate ISR that is triggered by either SEQ1INT or PWM at an equivalent time.  The goal here is to see if the zero value is coming from the ADC core (ADCRESULT9) or if it's the result of some faulty bus operation.  You can either check ADCRESULT9 for a bad value using software or you can set a breakpoint for when (DMABuffer[9] == 0) and manually inspect the register through emulation.

    -Tommy

  • Tommy,

    I am using ePWM1, 2 and 3 for a three phase PFC rectifier. All are set to 30kHz up/down and synchronized. ePWM1 is set to trigger the ADC on timer 0 and ePWM2 is set to trigger at period match.

    Setting a watchpoint if easy enough as long as the power converters are not running. My test up to now have all be done while the converters are on.

    I've been unable to check the ADC result registers because the bad data has always been in the first DMA buffer, and the results registers would have been overwritten by the second set of samples.

    Kevin
  • Kevin,

    You might be able to check on the ADC result registers using an ePWM ISR. For example, if CONV0-CONV7 is triggered by ePWM1=0, you can generate an ePWM interrupt using a CMPx count-up match at 70 ADCCLK cycles later, which should be approximately when all 16 conversions are ready.

    -Tommy
  • Tommy,

    Okay, thanks. I will give that a try. I may not have a result until Monday. I will let you know.

    Kevin
  • Tommy,

    I've been able to verify that the data from the A/D converter is correct, but the data from the DMA is not. I created a ePWM ISR to check for 0 data in the results registers and never saw any, while I was still getting 0 data in the DMA results buffer. I checked both timer 0 and timer period match to check both sample intervals.

    I've also verified that it is not CPU data corruption by

    1) Setting a hardware watchpoint when data in the DMA buffer changed and

    2) Moving the DMA buffer to from L4SARAM to L7SARAM.

    I ran the controller all weekend with the watchpoint enabled and the program never halted.

    Kevin

  • Kevin,

    Thanks for the update. I will try to reproduce your DMA corruption.

    To make sure that I have the same setup, what is your SYSCLK frequency and clock source (INTOSC, XTAL, or digital clock)? What is the phase difference between EPWM1 and EPWM2? How often does the zero happen? I assume that it's always occurring on the EPWM1 trigger?

    -Tommy
  • Tommy,

    SYSCLK = 150MHz. We are using a 30MHz crystal oscillator connected to XCLKIN. The phase difference is 0 between the ePWMs. I can send you the epwm initialisation code if you want it.
    It can take several minutes for the 0 data to occur. In the last test, I ran for ~30 minutes and counted 33 instances of 0 data in the buffer at this particular location. I'm sampling at 60kHz, so that works out to about 1 in 3.27M samples.

    Kevin
  • Kevin,

    Yes, your ePWM initialization code will be helpful.

    Thanks,
    Tommy
  • Tommy,

    There doesn't seem to be a easier way to upload source code...

    PWM initialization:

    void init_pwm( void )
    {
        EALLOW;
        SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 0;      // Stop all the TB clocks
        EDIS;

        EPwm1Regs.TBCTR = 0;
        EPwm2Regs.TBCTR = 0;
        EPwm3Regs.TBCTR = 0;
        EPwm4Regs.TBCTR = 0;
        EPwm5Regs.TBCTR = 0;
        EPwm6Regs.TBCTR = 0;

        EPwm1Regs.TBCTL.all = 0;
        EPwm2Regs.TBCTL.all = 0;
        EPwm3Regs.TBCTL.all = 0;
        EPwm4Regs.TBCTL.all = 0;
        EPwm5Regs.TBCTL.all = 0;
        EPwm6Regs.TBCTL.all = 0;

        // Setup Sync
        EPwm1Regs.TBCTL.bit.SYNCOSEL = TB_CTR_ZERO;  // Sync source for rectifier
        EPwm2Regs.TBCTL.bit.SYNCOSEL = TB_SYNC_IN;   // Pass through
        EPwm3Regs.TBCTL.bit.SYNCOSEL = TB_SYNC_IN;   // Pass through
        EPwm4Regs.TBCTL.bit.SYNCOSEL = TB_CTR_ZERO;  // Sync source for dc/dc
        EPwm5Regs.TBCTL.bit.SYNCOSEL = TB_SYNC_IN;   // Pass through
        EPwm6Regs.TBCTL.bit.SYNCOSEL = TB_SYNC_IN;   // Pass through

        // Rectifier sync, dc/dc no sync
        EPwm1Regs.TBCTL.bit.PHSEN = TB_DISABLE;         // EPwm1 is sync source for rectifier
        EPwm2Regs.TBCTL.bit.PHSEN = TB_ENABLE;         // Sync EPwm2 to EPwm1
        EPwm3Regs.TBCTL.bit.PHSEN = TB_ENABLE;         // Sync EPwm3 to EPwm2
        EPwm4Regs.TBCTL.bit.PHSEN = TB_DISABLE;         //
        EPwm5Regs.TBCTL.bit.PHSEN = TB_DISABLE;         // Start out disabled, sync is only for burst mode
        EPwm6Regs.TBCTL.bit.PHSEN = TB_DISABLE;      // EPwm6 not currently used.

        EPwm1Regs.TBPHS.half.TBPHS = 0;                 // no phase shift between pwms
        EPwm2Regs.TBPHS.half.TBPHS = 0;
        EPwm3Regs.TBPHS.half.TBPHS = 0;
        EPwm4Regs.TBPHS.half.TBPHS = 0;
        EPwm5Regs.TBPHS.half.TBPHS = 0;
        EPwm6Regs.TBPHS.half.TBPHS = 0;

        // let timers run on emulator suspend
        EPwm1Regs.TBCTL.bit.FREE_SOFT = 3;
        EPwm2Regs.TBCTL.bit.FREE_SOFT = 3;
        EPwm3Regs.TBCTL.bit.FREE_SOFT = 3;
        EPwm4Regs.TBCTL.bit.FREE_SOFT = 3;
        EPwm5Regs.TBCTL.bit.FREE_SOFT = 3;
        EPwm6Regs.TBCTL.bit.FREE_SOFT = 3;

           // setup a/d trigger from epwm1 and epwm2. Use zero on epwm1 and prd on epwm2
        // as SOC triggers to get a 50kHz sampling rate

        // 15 socBen                = 1     ePWM triggers SOCB
        // 14:12 socBsel            = 001   on timer 0
        // 11 socAen                = 0     no SOCA
        // 10:8 socAsel             = 000   na
        // 7:4 resvd                = 0000
        // 3 inten                  = 0     no ISR
        // 2:0 insel                = 000   NA
        EPwm1Regs.ETSEL.all = 0x9000;

        // ePwmRegs.ETPS setup
        // 15:14 SOCBCNT            = 00
        // 13:12 SOCBPRD            = 01    generate on 1st event
        // 11:10 SOCACNT            = 00
        // 9:8 SOCAPRD              = 00
        // 7:4 reserved             = 0000
        // 3:2 INTCNT               = 00
        // 1:0 INTPRD               = 00
        EPwm1Regs.ETPS.all = 0x1000;

        // 15 socBen                = 1     ePWM triggers SOCB
        // 14:12 socBsel            = 010   on timer prd
        // 11 socAen                = 0     no SOCA
        // 10:8 socAsel             = 000   na
        // 7:4 resvd                = 0000
        // 3 inten                  = 0     no ISR
        // 2:0 insel                = 000   NA
        EPwm2Regs.ETSEL.all = 0xa000;

        // ePwmRegs.ETPS setup
        // 15:14 SOCBCNT            = 00
        // 13:12 SOCBPRD            = 01    generate on 1st event
        // 11:10 SOCACNT            = 00
        // 9:8 SOCAPRD              = 00
        // 7:4 reserved             = 0000
        // 3:2 INTCNT               = 00
        // 1:0 INTPRD               = 00
        EPwm2Regs.ETPS.all = 0x1000;


        // EPwm1: Rectifier PHA
        EPwm1Regs.TBPRD = RECTIFIER_PWM_PERIOD;
        EPwm1Regs.TBCTL.bit.CTRMODE = TB_COUNT_UPDOWN;       // Count up/down
        EPwm1Regs.ETSEL.bit.INTEN = 0;                      // no interrupt
        EPwm1Regs.AQCTLA.bit.CAU = AQ_SET;
        EPwm1Regs.AQCTLA.bit.CAD = AQ_CLEAR;

        // EPwm2: Rectifier PHB
        EPwm2Regs.TBPRD = RECTIFIER_PWM_PERIOD;
        EPwm2Regs.TBCTL.bit.CTRMODE = TB_COUNT_UPDOWN;       // Count up/down
        EPwm2Regs.ETSEL.bit.INTEN = 0;                       // no interrupt
        EPwm2Regs.AQCTLA.bit.CAU = AQ_SET;
        EPwm2Regs.AQCTLA.bit.CAD = AQ_CLEAR;

        // EPwm3: Rectifier PHC
        EPwm3Regs.TBPRD = RECTIFIER_PWM_PERIOD;
        EPwm3Regs.TBCTL.bit.CTRMODE = TB_COUNT_UPDOWN;       // Count up/down
        EPwm3Regs.ETSEL.bit.INTEN = 0;                       // no interrupt
        EPwm3Regs.AQCTLA.bit.CAU = AQ_SET;
        EPwm3Regs.AQCTLA.bit.CAD = AQ_CLEAR;

        // EPwm4, used for burst control
        // EPwm4 must be used so it can set the synchronisation point for EPwm5 not the other way around
        // (Burst control sets the syncronisation for general LLC operation, not the other way around)
        EPwm4Regs.TBCTL.bit.CTRMODE = TB_COUNT_UP;          // Count up/down
        EPwm4Regs.ETSEL.bit.INTEN = 0;                      // no interrupt

        EPwm4Regs.TBPRD = BURST_PERIOD;

        // Assume no burst control at startup for the moment
        // Both held high to allow all pulses through
        EPwm4Regs.AQCTLA.bit.ZRO = AQ_SET;
        EPwm4Regs.AQCTLB.bit.ZRO = AQ_SET;

        // 50% Duty
        EPwm4Regs.CMPA.half.CMPA = EPwm4Regs.TBPRD >> 1;
        EPwm4Regs.CMPB = EPwm4Regs.TBPRD >> 1;

        // Apply clock divider to allow for lower frequency range
        EPwm4Regs.TBCTL.bit.CLKDIV = EPWM4DIVBIN;

        // Allow software force of A and B outputs
        // Software force will set both outputs low
        EPwm4Regs.AQSFRC.bit.ACTSFA = 1;
        EPwm4Regs.AQSFRC.bit.ACTSFB = 1;

        // EPwm5: LLC primary gates
        EPwm5Regs.TBCTL.bit.CTRMODE = TB_COUNT_UPDOWN;       // Count up/down
        EPwm5Regs.ETSEL.bit.INTEN = 0;                       // no interrupt
        // Set actions, single pwm output using the deadband module to create a complementary pair
        EPwm5Regs.TBPRD = LLC_INIT_PWM_PERIOD;
        EPwm5Regs.AQCTLA.bit.CAU = AQ_SET;
        EPwm5Regs.AQCTLA.bit.CAD = AQ_CLEAR;

        EPwm5Regs.DBCTL.bit.POLSEL = 2;                        // complementary active low
        EPwm5Regs.DBCTL.bit.OUT_MODE = 3;
        // 6.67ns per tick, 500ns desired deadtime.
        // DB reg = 500/6.67 ~= 33
        EPwm5Regs.DBFED = 33;
        EPwm5Regs.DBRED = 33;
        EPwm5Regs.CMPA.half.CMPA = EPwm4Regs.TBPRD >> 1;

        // EPwm6: routed to CPLD for debug purposes, otherwise not used.
        EPwm6Regs.TBPRD = DEBUG_PWM_PERIOD;
         EPwm6Regs.TBCTL.bit.CTRMODE= TB_COUNT_UP;              // Count up
         EPwm6Regs.ETSEL.bit.INTEN = 0;                       // no interrupt
         EPwm6Regs.AQCTLA.bit.ZRO = AQ_SET;
         EPwm6Regs.AQCTLA.bit.CAU = AQ_CLEAR;
         EPwm6Regs.AQCTLB.bit.ZRO = AQ_SET;
         EPwm6Regs.AQCTLB.bit.CBU = AQ_CLEAR;

         // stop ECAP timers
         ECap1Regs.ECCTL2.bit.TSCTRSTOP = 1;
         ECap1Regs.ECCTL2.bit.TSCTRSTOP = 1;
         ECap1Regs.ECCTL2.bit.TSCTRSTOP = 1;
         ECap1Regs.ECCTL2.bit.TSCTRSTOP = 1;

         // initialize APWMs
         ECap1Regs.ECCTL1.all = EC_FREEZE;
         ECap2Regs.ECCTL1.all = EC_FREEZE;
         ECap3Regs.ECCTL1.all = EC_FREEZE;
         ECap4Regs.ECCTL1.all = EC_FREEZE;

         // set to pwm mode
         ECap1Regs.ECCTL2.bit.CAP_APWM = EC_APWM_MODE;
         ECap2Regs.ECCTL2.bit.CAP_APWM = EC_APWM_MODE;
         ECap3Regs.ECCTL2.bit.CAP_APWM = EC_APWM_MODE;
         ECap4Regs.ECCTL2.bit.CAP_APWM = EC_APWM_MODE;

         ECap1Regs.ECCTL2.bit.APWMPOL = EC_ACTV_HI;
         ECap2Regs.ECCTL2.bit.APWMPOL = EC_ACTV_HI;
         ECap3Regs.ECCTL2.bit.APWMPOL = EC_ACTV_HI;
         ECap4Regs.ECCTL2.bit.APWMPOL = EC_ACTV_HI;

         // reset the timers
         ECap1Regs.TSCTR = 0UL;
         ECap2Regs.TSCTR = 0UL;
         ECap3Regs.TSCTR = 0UL;
         ECap4Regs.TSCTR = 0UL;

         // load the period
         ECap1Regs.CAP1 = APWM_PERIOD;
         ECap2Regs.CAP1 = APWM_PERIOD;
         ECap3Regs.CAP1 = APWM_PERIOD;
         ECap4Regs.CAP1 = APWM_PERIOD;

         // load 0 duty cycle
         ECap1Regs.CAP2 = 0;
         ECap2Regs.CAP2 = 0;
         ECap3Regs.CAP2 = 0;
         ECap4Regs.CAP2 = 0;

         // configure pins as ECAPs
         EALLOW;
         GpioCtrlRegs.GPAMUX2.bit.GPIO24 = 1;            // configure as ECAP1
         GpioCtrlRegs.GPAMUX2.bit.GPIO25 = 1;            // configure as ECAP2
         GpioCtrlRegs.GPAMUX2.bit.GPIO26 = 1;            // configure as ECAP3
    // TODO: After dropping support for the -01 board, this should be changed to APWM.
         GpioCtrlRegs.GPAMUX2.bit.GPIO27 = 0;            // not using this one yet, need to reserve as long as we need to support
                                                         // PLD version 1.00
         EDIS;

         // start APWM timers
         ECap1Regs.ECCTL2.bit.TSCTRSTOP = EC_RUN;
         ECap2Regs.ECCTL2.bit.TSCTRSTOP = EC_RUN;
         ECap3Regs.ECCTL2.bit.TSCTRSTOP = EC_RUN;
         ECap4Regs.ECCTL2.bit.TSCTRSTOP = EC_RUN;

         EALLOW;
         SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 1;         // Start all the timers synced
        EDIS;
    }

    Some relevant constants:

    #define PERIPHERAL_CLOCK        150000000
    #define LLC_INITIAL_FREQ        250000
    #define LLC_INIT_PWM_PERIOD        PERIPHERAL_CLOCK / LLC_INITIAL_FREQ / 2

    #define APWM_FREQ               20000
    #define APWM_PERIOD             PERIPHERAL_CLOCK / APWM_FREQ

    #define DEBUG_PWM_FREQ          200000
    #define DEBUG_PWM_PERIOD        PERIPHERAL_CLOCK / DEBUG_PWM_FREQ

    extern uint16_t ApwmCmprOutput;
    extern uint16_t ApwmCmprInput;

    #define APWM_CMPR_OUTPUT        2750
    #define APWM_CMPR_INPUT         2000

    #define BURST_PERIOD            PERIPHERAL_CLOCK / 10000                            // Start with 10kHz

    // rectifier constants
    #define RECTIFIER_PWM_FREQ        30000                                                // pwm frequency in hertz
    #define RECTIFIER_PWM_PERIOD    PERIPHERAL_CLOCK / RECTIFIER_PWM_FREQ / 2            // timer period value (up/down counter)

    Kevin

  • Tommy,

    Just some additional information: I modified the source code to ignore the 1st 16 items in the DMA buffer and only use the second 16. I ran the system overnight with no bad samples. This seems to confirm that the bad samples occur only on the first DMA pass and never on the second.

    Kevin
  • Kevin,

    Thanks for the information. I'll let you know what I find.

    -Tommy
  • Kevin,

    Quick update. I made a small program using your configurations to check for zeros when transferring ADCRESULTS to RAM with DMA.

    I did not detect any zeros on my setup overnight, but I'm still making improvements on the code. I'll need to switch tasks for today and continue tomorrow. If I am unable to reproduce the zeros tomorrow I may have you run my example on your setup to see if we correlate.

    -Tommy
  • Tommy,

    Thanks for the input. I've made some modifications to bypass the DMA and use the ADC results registers directly. The system seems much better behaved, although I did capture a few samples that shouldn't have been possible. I have a couple of unused A/D channels so I added oversampling of this particular channel to the sequence. In the data that I captured, one channel read 79 counts, while the other 2 read 2320 and 2319.

    I am starting to suspect either the A/D converter setup, or some kind of power supply decoupling or layout issue. I would like to either try the small project that you have been testing on our setup, or run our code on a TI eval board.

    Kevin

  • Kevin,

    I'm attaching a modified version of the f2833x adc_dma example from C2000Ware.  I have this running on an F2833x controlCARD to look for data corruption.

    I had a lot of difficulty profiling the EPWM -> ADC triggering behavior until I realized that your ADC initialization code was setting ADCCLKPS = ADC_MODCLK, which by itself is fine, but the original C2000Ware example also sets HISPCP=ADC_MODCLK so I had the ADC running at SYSCLK/36 instead of SYSCLK/6.  It would be a good idea to double-check your HISPCP setting in case it is still at the default SYSCLK/2.

    The attached example is simplified to trigger the ADC on EPWM1=0 and EPWM2=PRD.  Each sequence will sample all 16 channels and trigger a DMA transfer.  The LED heartbeat will blink continuously with code execution.

    The SEQ1INT CPU interrupt is enabled so that the AdcMirror values can be compared against the values in RAM after each ADC sequence.  Code execution will halt if there is a mismatch.

    The DMA buffer in RAM will also be checked for zero values in the DINTCH1 ISR.  Code execution will halt if a zero is found.

    //###########################################################################
    //
    // FILE:   Example_2833xAdcToDMA.c
    //
    // TITLE:  ADC to DMA Example
    //
    //! \addtogroup f2833x_example_list
    //! <h1> ADC to DMA (adc_dma)</h1>
    //!
    //! This ADC example uses ADC to convert 4 channels for each SOC received,
    //! with total of 10 SOCs.
    //! Each SOC initiates 4 conversions.
    //! DMA is used to capture the data on each SEQ1_INT. DMA will re-sort
    //! the data by channel sequentially, i.e. all channel0 data will be
    //! together and all channel1 data will be together.
    //!
    //! \b Watch \b Variables \n
    //! - DMABuf1	- DMA Buffer
    //
    //###########################################################################
    // $TI Release: F2833x Support Library v2.00.00.00 $
    // $Release Date: Sun Mar 25 13:25:02 CDT 2018 $
    // $Copyright:
    // Copyright (C) 2009-2018 Texas Instruments Incorporated - http://www.ti.com/
    //
    // Redistribution and use in source and binary forms, with or without 
    // modification, are permitted provided that the following conditions 
    // are met:
    // 
    //   Redistributions of source code must retain the above copyright 
    //   notice, this list of conditions and the following disclaimer.
    // 
    //   Redistributions in binary form must reproduce the above copyright
    //   notice, this list of conditions and the following disclaimer in the 
    //   documentation and/or other materials provided with the   
    //   distribution.
    // 
    //   Neither the name of Texas Instruments Incorporated nor the names of
    //   its contributors may be used to endorse or promote products derived
    //   from this software without specific prior written permission.
    // 
    // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
    // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
    // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
    // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
    // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
    // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
    // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
    // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    // $
    //###########################################################################
    
    //
    // Included Files
    //
    #include "DSP28x_Project.h"     // Device Headerfile and Examples Include File
    
    //
    // Defines for ADC start parameters
    //
    #if (CPU_FRQ_150MHZ)     // Default - 150 MHz SYSCLKOUT
        //
        // HSPCLK = SYSCLKOUT/2*ADC_MODCLK2 = 150/(2*3)   = 25.0 MHz
        //
        #define ADC_MODCLK 0x3
    #endif
    #if (CPU_FRQ_100MHZ)
        //
        // HSPCLK = SYSCLKOUT/2*ADC_MODCLK2 = 100/(2*2)   = 25.0 MHz
        //
        #define ADC_MODCLK 0x2
    #endif
    
    //
    // ADC module clock = HSPCLK/2*ADC_CKPS   = 25.0MHz/(1*2) = 12.5MHz
    //
    #define BURST_SIZE 16
    
    //
    // Globals
    //
    Uint16 j=0;
    
    #pragma DATA_SECTION(DMABuf1,"DMARAML4");
    volatile Uint16 DMABuf1[32];
    
    __interrupt void local_DINTCH1_ISR(void);
    __interrupt void local_ADC_ISR(void);
    
    /*
    __interrupt void local_EPWM1_ISR(void);
    __interrupt void local_EPWM2_ISR(void);
    */
    
    void init_adc( void );
    void init_dma( void );
    void init_pwm( void );
    
    Uint16 junk;
    
    volatile Uint16 *pSrc;
    volatile Uint16 *pDst;
    
    #define ADCINTPWMSIZE 10
    
    Uint32 AdcIntCnt = 0;
    Uint16 AdcIntPWM[ADCINTPWMSIZE][3];
    Uint32 DmaIntCnt = 0;
    Uint16 DmaIntPWM[ADCINTPWMSIZE/2];
    
    //
    // Main
    //
    void main(void)
    {
        //
        // Step 1. Initialize System Control:
        // PLL, WatchDog, enable Peripheral Clocks
        // This example function is found in the DSP2833x_SysCtrl.c file.
        //
        InitSysCtrl();
    
        //
        // Specific clock setting for this example
        //
        EALLOW;
        SysCtrlRegs.HISPCP.all = ADC_MODCLK;	// HSPCLK = SYSCLKOUT/ADC_MODCLK
        EDIS;
    
        //
        // Step 2. Initialize GPIO:
        // This example function is found in the DSP2833x_Gpio.c file and
        // illustrates how to set the GPIO to it's default state.
        //
        // InitGpio();  // Skipped for this example
    
        //
        // Step 3. Clear all interrupts and initialize PIE vector table:
        // Disable CPU interrupts
        //
        DINT;
    
        //
        // Initialize the PIE control registers to their default state.
        // The default state is all PIE interrupts disabled and flags
        // are cleared.
        // This function is found in the DSP2833x_PieCtrl.c file.
        //
        InitPieCtrl();
    
        //
        // Disable CPU interrupts and clear all CPU interrupt flags:
        //
        IER = 0x0000;
        IFR = 0x0000;
    
        //
        // Initialize the PIE vector table with pointers to the shell Interrupt
        // Service Routines (ISR).
        // This will populate the entire table, even if the interrupt
        // is not used in this example.  This is useful for debug purposes.
        // The shell ISR routines are found in DSP2833x_DefaultIsr.c.
        // This function is found in DSP2833x_PieVect.c.
        //
        InitPieVectTable();
    
        //
        // Interrupts that are used in this example are re-mapped to
        // ISR functions found within this file.
        //
        EALLOW;	// Allow access to EALLOW protected registers
        PieVectTable.DINTCH1   = &local_DINTCH1_ISR;
        PieVectTable.SEQ1INT   = &local_ADC_ISR;
    /*
        PieVectTable.EPWM1_INT = &local_EPWM1_ISR;
        PieVectTable.EPWM2_INT = &local_EPWM2_ISR;
    */
        EDIS;   // Disable access to EALLOW protected registers
    
        IER = M_INT7 | M_INT1; // M_INT3 |
        EnableInterrupts();
    
        //
        // Step 4. Initialize all the Device Peripherals:
        // This function is found in DSP2833x_InitPeripherals.c
        //
        // InitPeripherals(); // Not required for this example
        EALLOW;
        GpioCtrlRegs.GPAMUX2.bit.GPIO31 = 0;
        GpioCtrlRegs.GPADIR.bit.GPIO31  = 1;
        EDIS;
    
        for(j=0;j<ADCINTPWMSIZE;j++) {
            AdcIntPWM[j][0] = 0;
            AdcIntPWM[j][1] = 0;
            AdcIntPWM[j][2] = 0;
        }
    
        for(j=0;j<(ADCINTPWMSIZE/2);j++) {
            DmaIntPWM[j] = 0;
        }
    
        init_adc();
        init_dma();
        init_pwm();
    
        //
        // For this example will re-start manually
        //
        while(1);
    }
    
    //
    // local_DINTCH1_ISR - INT7.1(DMA Channel 1)
    //
    __interrupt void 
    local_DINTCH1_ISR(void)
    {
        Uint16 j;
        //
        // To receive more interrupts from this PIE group, acknowledge this 
        // interrupt
        //
        StartDMACH1();
        PieCtrlRegs.PIEACK.all = PIEACK_GROUP7;
        EALLOW;
        DmaRegs.CH1.CONTROL.bit.PERINTCLR = 1;
        EDIS;
    
        DmaIntPWM[DmaIntCnt%(ADCINTPWMSIZE/2)] = EPwm4Regs.TBCTR;
    
    /*
        if((DmaIntCnt%(ADCINTPWMSIZE/2)) == ((ADCINTPWMSIZE/2)-1)) {
            asm(" ESTOP0");
        }
    */
    
        DmaIntCnt++;
    
        if( (DmaIntCnt % 0x2000) == 0 ) {
            GpioDataRegs.GPATOGGLE.bit.GPIO31 = 1; // Toggle LED
        }
    
        //
        // Next two lines for debug only to halt the processor here
        // Remove after inserting ISR Code
        //
        for(j=0;j<sizeof(DMABuf1);j++) {
            if( DMABuf1[j] == 0 ) {
                asm(" ESTOP0");
            }
        }
    }
    
    
    __interrupt void
    local_ADC_ISR(void)
    {
        //
        // To receive more interrupts from this PIE group, acknowledge this
        // interrupt
        //
        AdcIntPWM[AdcIntCnt%ADCINTPWMSIZE][0] = EPwm4Regs.TBCTR;
        AdcIntPWM[AdcIntCnt%ADCINTPWMSIZE][1] = EPwm2Regs.TBCTR;
        AdcIntPWM[AdcIntCnt%ADCINTPWMSIZE][2] = EPwm2Regs.TBSTS.bit.CTRDIR;
    
        if( (EPwm2Regs.TBCTR > (AdcIntPWM[AdcIntCnt%ADCINTPWMSIZE][1] - 650)) && (EPwm2Regs.TBSTS.bit.CTRDIR == 0) ) {
            pSrc = (volatile Uint16*)&AdcMirror.ADCRESULT0;
            pDst = (volatile Uint16*)&DMABuf1[16];
        } else {
            pSrc = (volatile Uint16*)&AdcMirror.ADCRESULT0;
            pDst = (volatile Uint16*)&DMABuf1[0];
        }
    
        AdcIntCnt++;
    
        for(j=0;j<16;j++) {
            if( *pSrc != *pDst ) {
                asm(" ESTOP0");
            }
            pSrc++;
            pDst++;
        }
    
        AdcRegs.ADCTRL2.bit.RST_SEQ1 = 1;
        AdcRegs.ADCST.bit.INT_SEQ1_CLR = 1;
        PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;
    }
    
    
    void init_adc( void )
    {
        InitAdc();  // For this example, init the ADC
    
        // ADCTRL1 setup
        // AdcRegs.ADCTRL1.bit.SUSMOD = 01; // stop after current sequence
        // AdcRegs.ADCTRL1.bit.ACQ_PS = 0010; // acquisition size = 3
        // AdcRegs.ADCTRL1.bit.SEQ_CASC = 1; // cascaded sequencer mode
        // Everything else 0
    //    AdcRegs.ADCTRL1.all = 0x10210;
        AdcRegs.ADCTRL1.bit.SUSMOD = 0;
        AdcRegs.ADCTRL1.bit.ACQ_PS = 2;
        AdcRegs.ADCTRL1.bit.SEQ_CASC = 1;
    
        // ADCTRL2 setup
        // AdcRegs.ADCTRL2.bit.EPWM_SOCB_SEQ = 1; // SOCB
        // AdcRegs.ADCTRL2.bit.RST_SEQ1 = 1; // reset to CONV0
        // AdcRegs.ADCTRL2.bit.INT_ENA_SEQ1 = 1; // enable SEQ1 interrupt
        // Everything else 0
    //    AdcRegs.ADCTRL2.all = 0xC800;
        AdcRegs.ADCTRL2.bit.EPWM_SOCB_SEQ = 1;
    //    AdcRegs.ADCTRL2.bit.EPWM_SOCA_SEQ1 = 1;
        AdcRegs.ADCTRL2.bit.RST_SEQ1 = 1;
        AdcRegs.ADCTRL2.bit.INT_ENA_SEQ1 = 1;
        AdcRegs.ADCTRL2.bit.INT_MOD_SEQ1 = 0;
        PieCtrlRegs.PIEIER1.bit.INTx1 = 1;
        //PieCtrlRegs.PIEIER1.bit.INTx6 = 1;
    
        // Need a 25Mhz clock for ADC. ADC Clock is HSPCLK/2*ADCCLKPS
    //    AdcRegs.ADCTRL3.bit.ADCCLKPS = ADC_MODCLK;
    
        //
        // * set sequence channels
        // * They need to be in the same order as the RawAdc struct!!
        //
        AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 8; // input current phA - B0
        AdcRegs.ADCCHSELSEQ1.bit.CONV01 = 9; // input current phB - B1
        AdcRegs.ADCCHSELSEQ1.bit.CONV02 = 10; // input current phC - B2
        AdcRegs.ADCCHSELSEQ1.bit.CONV03 = 0; // input voltage phA - A0
        AdcRegs.ADCCHSELSEQ2.bit.CONV04 = 1; // input voltage phB - A1
        AdcRegs.ADCCHSELSEQ2.bit.CONV05 = 2; // input voltage phC - A2
        AdcRegs.ADCCHSELSEQ2.bit.CONV06 = 3; // dc link pos - A3
        AdcRegs.ADCCHSELSEQ2.bit.CONV07 = 11; // dc link neg - B3
    
        AdcRegs.ADCCHSELSEQ3.bit.CONV08 = 4; // output voltage - A4
        AdcRegs.ADCCHSELSEQ3.bit.CONV09 = 12; // output current - B4
        AdcRegs.ADCCHSELSEQ3.bit.CONV10 = 7; // output bus A - A7
        AdcRegs.ADCCHSELSEQ3.bit.CONV11 = 14; // output bus B - B6
    
        AdcRegs.ADCCHSELSEQ4.bit.CONV12 = 13; // internal temp
        AdcRegs.ADCCHSELSEQ4.bit.CONV13 = 15; // unused
        AdcRegs.ADCCHSELSEQ4.bit.CONV14 = 5; // IGBT temp
        AdcRegs.ADCCHSELSEQ4.bit.CONV15 = 6; // low V sense
        AdcRegs.ADCMAXCONV.all = 15;
    }
    
    
    void init_dma( void )
    {
        volatile Uint16* dmaDst = &DMABuf1[0];
        volatile Uint16* dmaSrc = &AdcMirror.ADCRESULT0;
        Uint16 j;
    
        for(j=0;j<sizeof(DMABuf1);j++) {
            DMABuf1[j] = 0xFFFF;
        }
    
        // initialize DMA hardware
        DMAInitialize();
    
        // configure dma source and destination addresses
        DMACH1AddrConfig( (Uint16*)dmaDst, dmaSrc );
    
        // 16 words/burst(X-1), increment source 1 word, increment dest 1 word
        DMACH1BurstConfig( (BURST_SIZE-1), 1, 1 );
    
        // 2 bursts/transfer(X-1), src transfer step, dest transfer step
        DMACH1TransferConfig( 1, 1, 1 );
    
        // wrap source after 1 burst, no offset, wrap dest after 2 bursts, no offset
        DMACH1WrapConfig( 0, 0, 1, 0 );
    
        // trigger source = ADC sequence1 interrupt, enable interrupt(from adc), disable one-shot and continuous,
        // no sync, 16-bit, interupt enabled(to cpu) at end of sequence
        DMACH1ModeConfig( DMA_SEQ1INT, PERINT_ENABLE, ONESHOT_DISABLE, CONT_DISABLE, SYNC_DISABLE, SYNC_SRC,
        OVRFLOW_DISABLE, SIXTEEN_BIT, CHINT_END, CHINT_ENABLE );
    
        StartDMACH1();
    }
    
    
    void init_pwm( void )
    {
        #define PERIPHERAL_CLOCK        150000000
        #define LLC_INITIAL_FREQ        250000
        #define LLC_INIT_PWM_PERIOD        PERIPHERAL_CLOCK / LLC_INITIAL_FREQ / 2
    
        #define APWM_FREQ               20000
        #define APWM_PERIOD             PERIPHERAL_CLOCK / APWM_FREQ
    
        #define DEBUG_PWM_FREQ          200000
        #define DEBUG_PWM_PERIOD        PERIPHERAL_CLOCK / DEBUG_PWM_FREQ
    
        #define APWM_CMPR_OUTPUT        2750
        #define APWM_CMPR_INPUT         2000
    
        #define BURST_PERIOD            PERIPHERAL_CLOCK / 10000                            // Start with 10kHz
    
        // rectifier constants
        #define RECTIFIER_PWM_FREQ        30000                                                // pwm frequency in hertz
        #define RECTIFIER_PWM_PERIOD    PERIPHERAL_CLOCK / RECTIFIER_PWM_FREQ / 2            // timer period value (up/down counter)
    
        EALLOW;
        SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 0;      // Stop all the TB clocks
        EDIS;
    
        EPwm1Regs.TBCTR = 1;
        EPwm2Regs.TBCTR = 1;
        EPwm3Regs.TBCTR = 1;
        EPwm4Regs.TBCTR = 1;
        EPwm5Regs.TBCTR = 1;
        EPwm6Regs.TBCTR = 1;
    
        EPwm1Regs.TBCTL.all = 0;
        EPwm2Regs.TBCTL.all = 0;
        EPwm3Regs.TBCTL.all = 0;
        EPwm4Regs.TBCTL.all = 0;
        EPwm5Regs.TBCTL.all = 0;
        EPwm6Regs.TBCTL.all = 0;
    /*
        // Setup Sync
        EPwm1Regs.TBCTL.bit.SYNCOSEL = TB_CTR_ZERO;  // Sync source for rectifier
        EPwm2Regs.TBCTL.bit.SYNCOSEL = TB_SYNC_IN;   // Pass through
        EPwm3Regs.TBCTL.bit.SYNCOSEL = TB_SYNC_IN;   // Pass through
        EPwm4Regs.TBCTL.bit.SYNCOSEL = TB_CTR_ZERO;  // Sync source for dc/dc
        EPwm5Regs.TBCTL.bit.SYNCOSEL = TB_SYNC_IN;   // Pass through
        EPwm6Regs.TBCTL.bit.SYNCOSEL = TB_SYNC_IN;   // Pass through
    
        // Rectifier sync, dc/dc no sync
        EPwm1Regs.TBCTL.bit.PHSEN = TB_DISABLE;         // EPwm1 is sync source for rectifier
        EPwm2Regs.TBCTL.bit.PHSEN = TB_ENABLE;         // Sync EPwm2 to EPwm1
        EPwm3Regs.TBCTL.bit.PHSEN = TB_ENABLE;         // Sync EPwm3 to EPwm2
        EPwm4Regs.TBCTL.bit.PHSEN = TB_DISABLE;         //
        EPwm5Regs.TBCTL.bit.PHSEN = TB_DISABLE;         // Start out disabled, sync is only for burst mode
        EPwm6Regs.TBCTL.bit.PHSEN = TB_DISABLE;      // EPwm6 not currently used.
    
        EPwm1Regs.TBPHS.half.TBPHS = 0;                 // no phase shift between pwms
        EPwm2Regs.TBPHS.half.TBPHS = 0;
        EPwm3Regs.TBPHS.half.TBPHS = 0;
        EPwm4Regs.TBPHS.half.TBPHS = 0;
        EPwm5Regs.TBPHS.half.TBPHS = 0;
        EPwm6Regs.TBPHS.half.TBPHS = 0;
    */
        // STOP timers on emulator suspend
        EPwm1Regs.TBCTL.bit.FREE_SOFT = 0;
        EPwm2Regs.TBCTL.bit.FREE_SOFT = 0;
        EPwm3Regs.TBCTL.bit.FREE_SOFT = 0;
        EPwm4Regs.TBCTL.bit.FREE_SOFT = 0;
        EPwm5Regs.TBCTL.bit.FREE_SOFT = 0;
        EPwm6Regs.TBCTL.bit.FREE_SOFT = 0;
    
        // ePwmRegs.ETPS setup
        // 15:14 SOCBCNT            = 00
        // 13:12 SOCBPRD            = 01    generate on 1st event
        // 11:10 SOCACNT            = 00
        // 9:8 SOCAPRD              = 00
        // 7:4 reserved             = 0000
        // 3:2 INTCNT               = 00
        // 1:0 INTPRD               = 00
    //    EPwm1Regs.ETPS.all = 0x1000;
        EPwm1Regs.ETPS.bit.SOCBPRD = ET_1ST;
    
        // setup a/d trigger from epwm1 and epwm2. Use zero on epwm1 and prd on epwm2
        // as SOC triggers to get a 50kHz sampling rate
    
        // 15 socBen                = 1     ePWM triggers SOCB
        // 14:12 socBsel            = 001   on timer 0
        // 11 socAen                = 0     no SOCA
        // 10:8 socAsel             = 000   na
        // 7:4 resvd                = 0000
        // 3 inten                  = 0     no ISR
        // 2:0 insel                = 000   NA
    //    EPwm1Regs.ETSEL.all = 0x9000;
        EPwm1Regs.ETSEL.bit.SOCBEN = 1;
        EPwm1Regs.ETSEL.bit.SOCBSEL = ET_CTR_ZERO;
        EPwm1Regs.ETSEL.bit.INTSEL = ET_CTRU_CMPB;
    /*
        EPwm1Regs.ETCLR.bit.INT   = 1;
        EPwm1Regs.ETSEL.bit.INTEN = 0;
        EPwm1Regs.ETPS.bit.INTPRD = 0;
        PieCtrlRegs.PIEIER3.bit.INTx1 = 1;
    */
    
        // ePwmRegs.ETPS setup
        // 15:14 SOCBCNT            = 00
        // 13:12 SOCBPRD            = 01    generate on 1st event
        // 11:10 SOCACNT            = 00
        // 9:8 SOCAPRD              = 00
        // 7:4 reserved             = 0000
        // 3:2 INTCNT               = 00
        // 1:0 INTPRD               = 00
    //    EPwm2Regs.ETPS.all = 0x1000;
        EPwm2Regs.ETPS.bit.SOCBPRD = ET_1ST;
    
        // 15 socBen                = 1     ePWM triggers SOCB
        // 14:12 socBsel            = 010   on timer prd
        // 11 socAen                = 0     no SOCA
        // 10:8 socAsel             = 000   na
        // 7:4 resvd                = 0000
        // 3 inten                  = 0     no ISR
        // 2:0 insel                = 000   NA
    //    EPwm2Regs.ETSEL.all = 0xa000;
        EPwm2Regs.ETSEL.bit.SOCBEN = 1;
        EPwm2Regs.ETSEL.bit.SOCBSEL = ET_CTR_PRD;
        EPwm2Regs.ETSEL.bit.INTSEL = ET_CTRD_CMPB;
    /*
        EPwm2Regs.ETCLR.bit.INT   = 1;
        EPwm2Regs.ETSEL.bit.INTEN = 0;
        EPwm2Regs.ETPS.bit.INTPRD = 0;
        PieCtrlRegs.PIEIER3.bit.INTx2 = 1;
    */
    
        // EPwm1: Rectifier PHA
        EPwm1Regs.TBPRD = RECTIFIER_PWM_PERIOD;
        EPwm1Regs.TBCTL.bit.CTRMODE = TB_COUNT_UPDOWN;       // Count up/down
    //    EPwm1Regs.ETSEL.bit.INTEN = 0;                      // no interrupt
        EPwm1Regs.AQCTLA.bit.CAU = AQ_SET;
        EPwm1Regs.AQCTLA.bit.CAD = AQ_CLEAR;
    //    EPwm1Regs.CMPB           = PWM_INT_OFFSET;
    
        // EPwm2: Rectifier PHB
        EPwm2Regs.TBPRD = RECTIFIER_PWM_PERIOD;
        EPwm2Regs.TBCTL.bit.CTRMODE = TB_COUNT_UPDOWN;       // Count up/down
    //    EPwm2Regs.ETSEL.bit.INTEN = 0;                       // no interrupt
        EPwm2Regs.AQCTLA.bit.CAU = AQ_SET;
        EPwm2Regs.AQCTLA.bit.CAD = AQ_CLEAR;
    //    EPwm2Regs.CMPB           = RECTIFIER_PWM_PERIOD - PWM_INT_OFFSET;
    
        // EPwm3: Rectifier PHC
        EPwm3Regs.TBPRD = RECTIFIER_PWM_PERIOD;
        EPwm3Regs.TBCTL.bit.CTRMODE = TB_COUNT_UPDOWN;       // Count up/down
        EPwm3Regs.ETSEL.bit.INTEN = 0;                       // no interrupt
        EPwm3Regs.AQCTLA.bit.CAU = AQ_SET;
        EPwm3Regs.AQCTLA.bit.CAD = AQ_CLEAR;
    
        // EPwm4, used for burst control
        // EPwm4 must be used so it can set the synchronisation point for EPwm5 not the other way around
        // (Burst control sets the syncronisation for general LLC operation, not the other way around)
        EPwm4Regs.TBCTL.bit.CTRMODE = TB_COUNT_UP;          // Count up/down
        EPwm4Regs.ETSEL.bit.INTEN = 0;                      // no interrupt
    
        //EPwm4Regs.TBPRD = BURST_PERIOD;
        EPwm4Regs.TBPRD = 0xFFFF;
    
        // Assume no burst control at startup for the moment
        // Both held high to allow all pulses through
        EPwm4Regs.AQCTLA.bit.ZRO = AQ_SET;
        EPwm4Regs.AQCTLB.bit.ZRO = AQ_SET;
    
        // 50% Duty
        EPwm4Regs.CMPA.half.CMPA = EPwm4Regs.TBPRD >> 1;
        EPwm4Regs.CMPB = EPwm4Regs.TBPRD >> 1;
    
        // Apply clock divider to allow for lower frequency range
    //    EPwm4Regs.TBCTL.bit.CLKDIV = TB_DIV2;
    
        // Allow software force of A and B outputs
        // Software force will set both outputs low
        EPwm4Regs.AQSFRC.bit.ACTSFA = 1;
        EPwm4Regs.AQSFRC.bit.ACTSFB = 1;
    
        // EPwm5: LLC primary gates
        EPwm5Regs.TBCTL.bit.CTRMODE = TB_COUNT_UPDOWN;       // Count up/down
        EPwm5Regs.ETSEL.bit.INTEN = 0;                       // no interrupt
        // Set actions, single pwm output using the deadband module to create a complementary pair
        EPwm5Regs.TBPRD = LLC_INIT_PWM_PERIOD;
        EPwm5Regs.AQCTLA.bit.CAU = AQ_SET;
        EPwm5Regs.AQCTLA.bit.CAD = AQ_CLEAR;
    
        EPwm5Regs.DBCTL.bit.POLSEL = 2;                        // complementary active low
        EPwm5Regs.DBCTL.bit.OUT_MODE = 3;
        // 6.67ns per tick, 500ns desired deadtime.
        // DB reg = 500/6.67 ~= 33
        EPwm5Regs.DBFED = 33;
        EPwm5Regs.DBRED = 33;
        EPwm5Regs.CMPA.half.CMPA = EPwm4Regs.TBPRD >> 1;
    
        // EPwm6: routed to CPLD for debug purposes, otherwise not used.
        EPwm6Regs.TBPRD = DEBUG_PWM_PERIOD;
        EPwm6Regs.TBCTL.bit.CTRMODE= TB_COUNT_UP;              // Count up
        EPwm6Regs.ETSEL.bit.INTEN = 0;                       // no interrupt
        EPwm6Regs.AQCTLA.bit.ZRO = AQ_SET;
        EPwm6Regs.AQCTLA.bit.CAU = AQ_CLEAR;
        EPwm6Regs.AQCTLB.bit.ZRO = AQ_SET;
        EPwm6Regs.AQCTLB.bit.CBU = AQ_CLEAR;
    
        EALLOW;
        SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 1;         // Start all the timers synced
        EDIS;
    }
    
    
    /*
    __interrupt void
    local_EPWM1_ISR(void)
    {
        //
        // To receive more interrupts from this PIE group, acknowledge this
        // interrupt
        //
        PieCtrlRegs.PIEACK.all = PIEACK_GROUP3;
        EPwm1Regs.ETCLR.bit.INT = 1;
    
        //
        // Next two lines for debug only to halt the processor here
        // Remove after inserting ISR Code
        //
    
        pSrc = (volatile Uint16*)&AdcMirror.ADCRESULT0;
        pDst = (volatile Uint16*)&DMABuf1[16];
    
        while(*pSrc != *pDst);
        asm(" ESTOP0");
    
        if(0 && (DmaIntCnt < 1) ) {
            // Skip first round before ADC has results
        } else {
            for(j=0;j<16;j++) {
                if( *pSrc != *pDst ) {
                    asm(" ESTOP0");
                }
                pSrc++;
                pDst++;
            }
        }
    }
    
    __interrupt void
    local_EPWM2_ISR(void)
    {
        //
        // To receive more interrupts from this PIE group, acknowledge this
        // interrupt
        //
        PieCtrlRegs.PIEACK.all = PIEACK_GROUP3;
        EPwm2Regs.ETCLR.bit.INT = 1;
    
        //
        // Next two lines for debug only to halt the processor here
        // Remove after inserting ISR Code
        //
        pSrc = (volatile Uint16*)&AdcMirror.ADCRESULT0;
        pDst = (volatile Uint16*)&DMABuf1[0];
    
        while(*pSrc != *pDst);
        asm(" ESTOP0");
    
        if(0 && (DmaIntCnt < 1) ) {
            // Skip first round before ADC has results
        } else {
            for(j=0;j<16;j++) {
                if( *pSrc != *pDst ) {
                    asm(" ESTOP0");
                }
                pSrc++;
                pDst++;
            }
        }
    }
    */
    
    //
    // End of File
    //
    
    

    -Tommy

  • Hi Tommy,

    Thanks for the input. The code that you provided is similar to what I had done previously. (HISPCP setting in correctly set). I've since found that when our controller is powered by itself, or in the system with the power converters off, there are no errors. The errors only occur when the power converters are operational, and I've also now seen bad data in the ADC results registers directly, so I no longer believe that the DMA is not functioning correctly. I now believe that the noise injected into the A/D converter is somehow causing bad values to occasionally appear. I've modified the code to sample the same pin multiple times in a sequence, and what I've found on occasion, one of the ADCRESULT registers would be wildly different than the others, and the result is always very low. For example, ADCRESULT9, 10, 11 and 12 were all setup to sample the same pin sequentially.

    ADCRESULT9 = 74

    ADCRESULT10 = 3037

    ADCRESULT11 = 3039

    ADCRESULT12 = 3035

    In any case, I no longer believe this is a processor or software issue but a system issue. Your assistance has been useful and greatly appreciated, thank you.

    Kevin

  • Kevin,

    I'm glad to hear that you are making progress in isolating the problem.

    I suppose that the next step would be to see if the bad conversions originate from noise on a single ADC channel or if it is universal noise on the PCB or within the ADC core itself.  Here are some initial thoughts.

    Are you able to reproduce bad conversions using your oversampling method on multiple ADC channels?  For example, what if you assign a different channel to ADCRESULT9-12?

    Likewise, I recall that you have only seen bad conversions on ADCRESULT9.  Would the bad conversion shift to another ADCRESULTn position if you reorder the channel conversion sequence?  If it tracks with the channel assignment, then we can focus on the specific channel.

    If the bad conversions stick to ADCRESULT9 even after reordering the channel sequence, then you can try to vary the ACQ_PS setting to see if the bad conversion shifts to another ADCRESULTn position.  If it shifts ADCRESULTn positions, then there is probably something affecting the ADC core at a specific point in time.

    -Tommy

  • Kevin,

    It has been almost two weeks since your last update. I assume that you were able to resolve your issue. If this isn’t the case, please reject this resolution and reply to this thread. If this thread is locked, please make a new thread describing the current status of your issue.

    -Tommy
  • Hi Tommy,

    I was able to resolve the issue by changing the grounding on the controller. Thanks for the help.

    Kevin

  • Kevin,

    Thanks for following up with your resolution. It will be useful for anyone else who reads this thread.

    -Tommy