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.

Triggering ADC SOCA and SOCB from ePWM1 (up/down count mode)

Other Parts Discussed in Thread: CONTROLSUITE

Hi there,

I'm trying to use ePWM1 to trigger ADC sampling from SOCA and SOCB using up/dowm count mode on the PWM configs. The idea is to control a DC-DC converter with PWM at 20kHz and to acquire two samples, the first on SOCA at CTR=PRD and the second on SOCB at CTR=ZERO.

My board is C2000 LaunchPad XL F28027 Piccolo.

(i'm following the idea from TI shown in this page http://e2e.ti.com/blogs_/b/motordrivecontrol/archive/2012/04/04/so-which-pwm-technique-is-best-part-4.aspx)

The code I'm using for ADC and PWM is as follows:

/************************************************************************
***** ADC Configuration *****
************************************************************************/
void adc_init()
{
// Initialize the ADC
ADC_enableBandGap(myAdc);
ADC_enableRefBuffers(myAdc);
ADC_powerUp(myAdc);
ADC_enable(myAdc);
ADC_setVoltRefSrc(myAdc, ADC_VoltageRefSrc_Int);

// Clear ADCINT1 flag reinitialize for next SOC
ADC_clearIntFlag(myAdc, ADC_IntNumber_1);
// Enable ADCINT1 in PIE
PIE_enableAdcInt(myPie, ADC_IntNumber_1);
// Enable CPU Interrupt 1
CPU_enableInt(myCpu, CPU_IntNumber_10);
// Enable Global interrupt INTM
CPU_enableGlobalInts(myCpu);
// Enable Global realtime interrupt DBGM
CPU_enableDebugInt(myCpu);

//ADCINT1 trips after AdcResults latch
ADC_setIntPulseGenMode(myAdc, ADC_IntPulseGenMode_During);
//Enabled ADCINT1
ADC_enableInt(myAdc, ADC_IntNumber_1);
//Disable ADCINT1 Continuous mode
ADC_setIntMode(myAdc, ADC_IntNumber_1, ADC_IntMode_ClearFlag);
//setup EOC1 to trigger ADCINT1 to fire
ADC_setIntSrc(myAdc, ADC_IntNumber_1, ADC_IntSrc_EOC0);
// ADC_setIntSrc(myAdc, ADC_IntNumber_1, ADC_IntSrc_EOC1);
//set SOC0 channel select to ADCINA0

ADC_setSocChanNumber (myAdc, ADC_SocNumber_0, ADC_SocChanNumber_A0);
ADC_setSocChanNumber (myAdc, ADC_SocNumber_0, ADC_SocChanNumber_B0);

//set SOC0 start trigger on EPWM1A, due to round-robin SOC0 converts first then SOC1
ADC_setSocTrigSrc(myAdc, ADC_SocNumber_0, ADC_SocTrigSrc_EPWM1_ADCSOCA);
ADC_setSocTrigSrc(myAdc, ADC_SocNumber_0, ADC_SocTrigSrc_EPWM1_ADCSOCB);

//set SOC0 S/H Window to 7 ADC Clock Cycles, (6 ACQPS plus 1)
ADC_setSocSampleWindow(myAdc, ADC_SocNumber_0, ADC_SocSampleWindow_7_cycles);
}

/************************************************************************
***** PWM Configuration *****
************************************************************************/
void pwm_init(){
// Enable PWM clock
CLK_enablePwmClock(myClk, PWM_Number_1);

// Setup PWM
PWM_enableSocAPulse(myPwm); // Enable SOC on A group
PWM_enableSocBPulse(myPwm);
PWM_setSocAPulseSrc(myPwm, PWM_SocPulseSrc_CounterEqualPeriod); // Select SOC from from CPMA on upcount
PWM_setSocBPulseSrc(myPwm, PWM_SocPulseSrc_CounterEqualZero);
PWM_setSocAPeriod(myPwm, PWM_SocPeriod_FirstEvent); // Generate pulse on 1st event
PWM_setSocBPeriod(myPwm, PWM_SocPeriod_FirstEvent);

PWM_setPeriod(myPwm, 625); // Set period for ePWM1
PWM_setCounterMode(myPwm, PWM_CounterMode_UpDown); // count up and start
// Set Compare values
PWM_setCmpA(myPwm, 312); // Set compare A value

// Set actions
PWM_setActionQual_Zero_PwmA(myPwm, PWM_ActionQual_Set); // Set PWM1A on Zero
PWM_setActionQual_CntUp_CmpA_PwmA(myPwm, PWM_ActionQual_Clear); // Clear PWM1A on event A, up count
}

The ADC interrupt routine is as follows:

/*******************************************************

interrupt void adc_isr(void)
{
ADC_count++;

//discard ADCRESULT0 as part of the workaround to the 1st sample errata for rev0
A0_res1 = ADC_readResult(myAdc, ADC_ResultNumber_0);
A0_res2 = ADC_readResult(myAdc, ADC_ResultNumber_1);

// Clear ADCINT1 flag reinitialize for next SOC

ADC_clearIntFlag(myAdc, ADC_IntNumber_1);
// Acknowledge interrupt to PIE
PIE_clearInt(myPie, PIE_GroupNumber_10);

return;
}

/*******************************************************

My question are:

- EOC0 is enough for both SOCA and SOCB?

- Both SOCA and SOCB goes to SOC0 or one of them must go to SOC1?

- Do I need independent interrupt routine for SOCA and SOCB?

I have a real-time task showing the ADC_count variable each second, once that my PWM is set to 20kHz and I've set on trigger sample to CRT=PRD (SOCA) and another to CRT=0 (SOCB) I was expecting the value of the variable to be 40000 but the actual value is 20000. 

Am I thinking the wrong way? Am I doing something wrong?

Thanks in advance.

Mário

  • Hi Mario,

    I think the basic setup that you want would be:

    SOC0: Triggered from ePWM1 SOCA, sets ADCINT1 flag (conversion result will be in ADCRESULT0)

    SOC1: Triggered from ePWM1 SOCB, sets ADCINT2 flag (conversion result will be in ADCRESULT1)

    Depending on how often you need to do calculations with the ADC results, you could use both ADCINT1 and ADCINT2 to trigger separate ISRs.  Otherwise, it would be fine to read both conversion results in one ISR, since the conversion results get stored in separate registers.  

    Please also read the device errata, particularly the first sample erratum;  because of this, you either need to double sample for each of these ePWM conversions, or set the ADC in non-overlap and clock/2 mode (probably you will want the second).

    Another hint: if you look in ControlSUITE in the below directory, you will find the resources for all of our system kits (including various DC-DC kits).  If you grab some of the code from one of the DC-DC kits, it may be informative/a good starting point.  Note that if you can't find 28027 code (launchpad device), F2803x is almost identical from a software perspective.  

    C:\TI\controlSUITE\development_kits

  • Hi Devin,

    thank you for spending your time with my questions..

    Yes, the idea is to read both sample in one ISR. How can I redirect both interruptions to the same ISR function?

    I wont be able to look at that directory because I'm working under Debian Wheezy, is there any code available in the web?

    I'll take a look at device errata cause the things you told "..the ADC in non-overlap and clock/2 mode..." doesn't make the bell ring in my head.

    Regards,

    Mário

  • Mario,

    Yeah, you can get both interrupts to go to the same ISR.  For this:

    *enable ADCINT1 and ADCINT2 in ADC module

    ADC_enableInt(myAdc, ADC_IntNumber_1);

    ADC_enableInt(myAdc, ADC_IntNumber_2);

    *enable ADCINT1 and ADCINT2 interrupts in the PIE module

    PIE_enableAdcInt(myPie, ADC_IntNumber_1);

    PIE_enableAdcInt(myPie, ADC_IntNumber_2);

    *enable ADCINT1 and ADCINT2 interrupts to the CPU (these are both on CPU interrupt line 10, so only one enable needed - see table  3-12 from datasheet)

    CPU_enableInt(myCpu, CPU_IntNumber_10); 

    *Map both interrupts in the pie vector table to the same ISR (see again table 3-12).  Note that if you wanted different ISRs, you would have something like &adc_isr1 and &adc_isr2 functions instead of both to &adc_isr.

    PIE_registerPieIntHandler(myPie, PIE_GroupNumber_10, PIE_SubGroupNumber_1, (intVec_t)&adc_isr);

    PIE_registerPieIntHandler(myPie, PIE_GroupNumber_10, PIE_SubGroupNumber_2, (intVec_t)&adc_isr);

    It should be possible to get the ControlSUITE example code onto Linux.  Seethe below link.

    http://processors.wiki.ti.com/index.php/ControlSUITE_Linux_Installation

  • Devin,

    it seems that my ISR is now being called twice in each PWM period, just as I was expecting.

    But now the result value of the second sample is always zero. I'm trying to read the value from " ADC_readResult(myAdc, ADC_ResultNumber_1) ". Am I still missing something?

    I have associated the EOC1 to ADCINT2 as "ADC_setIntSrc(myAdc, ADC_IntNumber_2, ADC_IntSrc_EOC1);"

    Is it correct?

    Regards,

    Mário

  • Based on the code you posted before, maybe this is the problem:

    ADC_setSocChanNumber (myAdc, ADC_SocNumber_0, ADC_SocChanNumber_A0);
    ADC_setSocChanNumber (myAdc, ADC_SocNumber_0, ADC_SocChanNumber_B0);

    This configures SOC0 first for A0, then overwrites it to B0.  I think you want:

    ADC_setSocChanNumber (myAdc, ADC_SocNumber_0, ADC_SocChanNumber_A0);
    ADC_setSocChanNumber (myAdc, ADC_SocNumber_1, ADC_SocChanNumber_B0); 

    You may also want to double check that the trigger and acquisition window for both SOC0 and SOC1 are correctly configured.

  • Yes, I do want to check the trigger of each sample and the acquisition window in the next step.

    I do remember of changing the parameter that you've suggested, but the register on the debug mode ADCRESULT1 remain with the zero value.

    While we're exchanging messages I've managed to perform some modifications in the code, now I have two ISR's functions for the two sample acquisitions.

    The first one is doing good, I can check ADC_count value per second and the result value of the conversion.

    The second isn't doing that good, I'm able to check the ADC_count2 value per second but the result ADCRESULT1 isn't changing.

    The code of ADC initialization is as follows:

    PIE_registerPieIntHandler(myPie, PIE_GroupNumber_10, PIE_SubGroupNumber_2, (intVec_t)&adc_isr2);

    void adc_init()

    {
    // Initialize the ADC
    ADC_enableBandGap(myAdc);
    ADC_enableRefBuffers(myAdc);
    ADC_powerUp(myAdc);
    ADC_enable(myAdc);
    ADC_setVoltRefSrc(myAdc, ADC_VoltageRefSrc_Int);

    // Clear ADCINT1 flag reinitialize for next SOC
    ADC_clearIntFlag(myAdc, ADC_IntNumber_1);
    ADC_clearIntFlag(myAdc, ADC_IntNumber_2);
    // Enable ADCINT1 in PIE
    PIE_enableAdcInt(myPie, ADC_IntNumber_1);
    PIE_enableAdcInt(myPie, ADC_IntNumber_2);
    // Enable CPU Interrupt 1
    CPU_enableInt(myCpu, CPU_IntNumber_10);
    // Enable Global interrupt INTM
    CPU_enableGlobalInts(myCpu);
    // Enable Global real-time interrupt DBGM
    CPU_enableDebugInt(myCpu);


    //ADCINT1 trips after AdcResults latch
    ADC_setIntPulseGenMode(myAdc, ADC_IntPulseGenMode_During);

    //Enabled ADCINT1
    ADC_enableInt(myAdc, ADC_IntNumber_1);
    ADC_enableInt(myAdc, ADC_IntNumber_2);

    //Disable ADCINT1 Continuous mode
    ADC_setIntMode(myAdc, ADC_IntNumber_1, ADC_IntMode_ClearFlag);
    ADC_setIntMode(myAdc, ADC_IntNumber_2, ADC_IntMode_ClearFlag);

    //setup EOC1 to trigger ADCINT1 to fire
    ADC_setIntSrc(myAdc, ADC_IntNumber_1, ADC_IntSrc_EOC0);
    ADC_setIntSrc(myAdc, ADC_IntNumber_2, ADC_IntSrc_EOC1);

    //set SOC0 channel select to ADCINA0
    ADC_setSocChanNumber (myAdc, ADC_SocNumber_0, ADC_SocChanNumber_A0);
    ADC_setSocChanNumber (myAdc, ADC_SocNumber_1, ADC_SocChanNumber_B0);

    //set SOC0 start trigger on EPWM1A, due to round-robin SOC0 converts first then SOC1
    ADC_setSocTrigSrc(myAdc, ADC_SocNumber_0, ADC_SocTrigSrc_EPWM1_ADCSOCA);
    ADC_setSocTrigSrc(myAdc, ADC_SocNumber_1, ADC_SocTrigSrc_EPWM1_ADCSOCB);

    //set SOC0 S/H Window to 7 ADC Clock Cycles, (6 ACQPS plus 1)
    ADC_setSocSampleWindow(myAdc, ADC_SocNumber_0, ADC_SocSampleWindow_7_cycles);
    ADC_setSocSampleWindow(myAdc, ADC_SocNumber_1, ADC_SocSampleWindow_7_cycles);

    }

    interrupt void adc_isr2(void)
    {
    ADC_count2++;

    //discard ADCRESULT0 as part of the workaround to the 1st sample errata for rev0
    // A0_res1 = ADC_readResult(myAdc, ADC_ResultNumber_0);
    A0_res2 = ADC_readResult(myAdc, ADC_ResultNumber_1);

    // ADC0 = (A0_res1+A0_res2) >> 1;
    // ADC1 = (A1_res1+A1_res2) >> 1;

    // Clear ADCINT1 flag reinitialize for next SOC
    // ADC_clearIntFlag(myAdc, ADC_IntNumber_1);
    ADC_clearIntFlag(myAdc, ADC_IntNumber_2);


    // Acknowledge interrupt to PIE
    PIE_clearInt(myPie, PIE_GroupNumber_10);

    return;
    }

    Thanks a lot for your fast response.

    Regards,

    Mário

  • Devin, I just found the error of my code :)

    I just changed this line:

    ADC_setSocChanNumber (myAdc, ADC_SocNumber_1, ADC_SocChanNumber_B0);

    by this one:

    ADC_setSocChanNumber (myAdc, ADC_SocNumber_1, ADC_SocChanNumber_A1);

    and now its working.

    The result wasn't being stored where I was reading it.

    Thanks.

    Regards,

    Mário