My application is essentially PWM control for synchronous buck converter, wherein my PWM frequency is 100 kHz, and I want to sample inductor current and output voltage as multiple samples per switching period.
My implementation is as follows:
- ePWM 4A and 4B for the low and high side FETs PWM
- ePWM 3A as ADC SOC trigger
- Using ADC A2 as interrupt
I'm having trouble verifying 1MHz ADC sampling. Just for analysing and verifying ADC interrupt timing, I've used a counter to increment during each interrupt routine (keeping track of how much microseconds has elapsed), and when it reaches a value equal to 1 second, I increment a flag to denote that 1 second of interrupt routine has been serviced. With a stopwatch, I compare the 'flag' updating and the real time to ensure both are in the same speed.
This works accurately for ePWM3 frequencies upto 200 kHz, but beyond that the 'flag' update gets slower than 1 second. I'm not sure if my method of evaluating ADC interrupt timing is reliable/accurate or if there's something that actually affects ADC interrupt servicing beyond a certain rate. But I can see on an external oscilloscope that the ePWM3 pulses' frequency matches whatever the TBPRD and the prescalers refer to (in this case, 1 MHz). I even tried disabling the ePWM4 pulses just to rule out any prioritization issues, but still doesn't work. Please find attached the code.
// ePWM 4A: 100 kHz, 27% duty, up-down counting // ePWM 4B: 100 kHz, 27% duty, up-down counting, deadband + complementary #include "F28x_Project.h" #include "math.h" void Gpio_select(); void Setup_ePWM4(); void Setup_ePWM3(); void ConfigureADC(void); void SetupADCSoftware(void); extern interrupt void adca2_isr(void); float duty=0.27; //open-loop duty ratio of control FET float timerprd; //EPWM3 period in micro seconds for ADC SOC trigger long int counter=0; //counter to check CPU Timer0 timing int flag=0; //checking 1 sec counter mark float AdcaResult0; float ADC_voltage=0.0; long int adc_f=1000e3; // ADC sampling rate int adc_x=1, adc_y=1; // ADC ePWM PRD prescalers Uint16 PWM3_PRD; //TBPRD for ePWM3 void main(void) { InitSysCtrl(); DINT; InitPieCtrl(); IER = 0x0000; IFR = 0x0000; InitPieVectTable(); PWM3_PRD = (100e6/adc_f/adc_x/adc_y)-1; timerprd = 1e6/adc_f; EALLOW; PieVectTable.ADCA2_INT = &adca2_isr; EDIS; IER |= M_INT10; PieCtrlRegs.PIEIER10.bit.INTx2 = 1; Gpio_select(); //Setup_ePWM4(); Setup_ePWM3(); ConfigureADC(); SetupADCSoftware(); EINT; ERTM; while(1); } void Gpio_select() { EALLOW; GpioCtrlRegs.GPAMUX1.bit.GPIO4=1; GpioCtrlRegs.GPAMUX1.bit.GPIO5=1; GpioCtrlRegs.GPAMUX1.bit.GPIO6=1; GpioCtrlRegs.GPAMUX1.bit.GPIO7=1; EDIS; } void Setup_ePWM4() { EPwm4Regs.TBCTL.bit.CLKDIV=0; EPwm4Regs.TBCTL.bit.HSPCLKDIV=0; EPwm4Regs.TBCTL.bit.CTRMODE=2; EPwm4Regs.TBPRD=500; EPwm4Regs.AQCTLA.all=0x0060; //EPwm3Regs.AQCTLB.all=0x0090; EPwm4Regs.CMPA.bit.CMPA=500*(1-duty); EPwm4Regs.DBCTL.bit.OUT_MODE=3; EPwm4Regs.DBCTL.bit.POLSEL=2; EPwm4Regs.DBRED.all=50; EPwm4Regs.DBFED.all=50; } void Setup_ePWM3() { EPwm3Regs.TBCTL.bit.CLKDIV=0; EPwm3Regs.TBCTL.bit.HSPCLKDIV=0; EPwm3Regs.TBCTL.bit.CTRMODE=0; EPwm3Regs.TBPRD=PWM3_PRD; EPwm3Regs.AQCTLA.all=0x0012; EPwm3Regs.CMPA.bit.CMPA=PWM3_PRD/2; EPwm3Regs.ETSEL.bit.SOCAEN=1; EPwm3Regs.ETSEL.bit.SOCASEL=1; EPwm3Regs.ETPS.bit.SOCAPRD=1; EPwm3Regs.ETCLR.bit.SOCA=1; EPwm3Regs.ETSEL.bit.INTEN=1; EPwm3Regs.ETSEL.bit.INTSEL=1; EPwm3Regs.ETPS.bit.INTPRD=1; EPwm3Regs.ETCLR.bit.INT=1; } void ConfigureADC(void) { EALLOW; AdcaRegs.ADCCTL2.bit.PRESCALE = 6; // Set ADCCLK divider to SYSCLK/4 AdcSetMode(ADC_ADCA, ADC_RESOLUTION_12BIT, ADC_SIGNALMODE_SINGLE); AdcaRegs.ADCCTL1.bit.INTPULSEPOS = 1; AdcaRegs.ADCCTL1.bit.ADCPWDNZ = 1; // Set value to power up the ADCs EDIS; DELAY_US(1000); // Set value for delay of 1 ms to allow ADC time to power up } void SetupADCSoftware(void) { Uint16 acqps; if(ADC_RESOLUTION_12BIT == AdcaRegs.ADCCTL2.bit.RESOLUTION) { acqps = 14; // Determine minimum acquisition window (in SYSCLKS) } else { acqps = 63; // Determine minimum acquisition window (in SYSCLKS) } EALLOW; AdcaRegs.ADCSOC0CTL.bit.CHSEL = 4; // Set value for channel selection AdcaRegs.ADCSOC0CTL.bit.ACQPS = acqps; AdcaRegs.ADCSOC0CTL.bit.TRIGSEL = 9; // Sets ePWM3 as SOC0 trigger source AdcaRegs.ADCINTSEL1N2.bit.INT2SEL = 0; // Event for which INT2 flag is set AdcaRegs.ADCINTSEL1N2.bit.INT2E = 1; // Enable INT2 flag AdcaRegs.ADCINTFLGCLR.bit.ADCINT2 = 1; // Make sure INT2 flag is cleared EDIS; } extern interrupt void adca2_isr(void) { AdcaRegs.ADCINTFLGCLR.bit.ADCINT2 = 1; if(1 == AdcaRegs.ADCINTOVF.bit.ADCINT2) { AdcaRegs.ADCINTFLGCLR.bit.ADCINT2 = 1; AdcaRegs.ADCINTOVFCLR.bit.ADCINT2 = 1; } AdcaResult0 = AdcaResultRegs.ADCRESULT0; // Write code to store results to this variable ADC_voltage = AdcaResult0/4095*3; // Convert ADC result to voltage counter++; if(counter>=1000000/timerprd) { flag++; counter=0; } PieCtrlRegs.PIEACK.all = PIEACK_GROUP10; }
I'm relatively new to DSP programming, so please bear with my limited knowledge if I'm missing to add some additional information or making a simple mistake over here. Kindly requesting support with this matter. Let me know if I can provide any other supporting information to better assist my query.
Thanks in advance,
SV