Hi,
I am using a Teensy 4.1 for a project that requires a PI loop which decides the duty cycle of one PWM output. The PI loop has to be run at 25kHz while the frequency of the PWM output it controls will be at 10kHz. There will be another PWM output which is at a constant duty cycle at 250Hz
I am using three instances of IntervalTimers, one at 25kHz for the PI, one at 10kHz for the first PWM and one at 250Hz for the second PWM.
The issue I am facing is that even after reducing the Analog Read times by using the ADC library (even with Conversion and Sampling time set to FAST_SPEED) the PI interrupt takes around 8 microseconds to execute (around 4 microseconds for one Analog Input, using two at the moment but this may increase to 3 or 4 later in the same interrupt). If at the time the PWM output state has to be switched and the PI loop is executed, the PWM output state gets switched anywhere from 0 to 8 (sometimes 16) microseconds later than required and thus does not create a stable PWM waveform.
If there is a better way of writing this or optimizing/using other libraries or control methods or just a better approach to this I would be very happy for your help. If there is a better way of reading the analog inputs that may contribute as well. Code for both IntervalTimer Interrupts is mentioned below.
Below is the code for PWM generation of 10kHz. The dcTmrATime and dcTmrBTime values are the On and Off values off the PWM cycle (100 microseconds for 10kHz cycle)
Below is the code for the PID calculations
A0 has a potentiometer connected for manipulating the Setpoint
A1 has a Current Transformer which is the Feedback of the PI loop
I am using a Teensy 4.1 for a project that requires a PI loop which decides the duty cycle of one PWM output. The PI loop has to be run at 25kHz while the frequency of the PWM output it controls will be at 10kHz. There will be another PWM output which is at a constant duty cycle at 250Hz
I am using three instances of IntervalTimers, one at 25kHz for the PI, one at 10kHz for the first PWM and one at 250Hz for the second PWM.
The issue I am facing is that even after reducing the Analog Read times by using the ADC library (even with Conversion and Sampling time set to FAST_SPEED) the PI interrupt takes around 8 microseconds to execute (around 4 microseconds for one Analog Input, using two at the moment but this may increase to 3 or 4 later in the same interrupt). If at the time the PWM output state has to be switched and the PI loop is executed, the PWM output state gets switched anywhere from 0 to 8 (sometimes 16) microseconds later than required and thus does not create a stable PWM waveform.
If there is a better way of writing this or optimizing/using other libraries or control methods or just a better approach to this I would be very happy for your help. If there is a better way of reading the analog inputs that may contribute as well. Code for both IntervalTimer Interrupts is mentioned below.
Below is the code for PWM generation of 10kHz. The dcTmrATime and dcTmrBTime values are the On and Off values off the PWM cycle (100 microseconds for 10kHz cycle)
Code:
// DC Bus PWM Interrupt Routine
void dcInt() {
if (dcTmrA == HIGH){
dcTmrA = LOW;
dcOutState = HIGH;
dcTmr.update(dcTmrBTime);
}
else {
dcTmrA = HIGH;
dcOutState = LOW;
dcTmr.update(dcTmrATime);
}
digitalWriteFast(dcBusPin,dcOutState);
}
Below is the code for the PID calculations
A0 has a potentiometer connected for manipulating the Setpoint
A1 has a Current Transformer which is the Feedback of the PI loop
Code:
// PID 1 Interrupt Routine
void pidInt(){
//Get Analog Data and Scale
anaVal = (double)map(analogRead(A0),0.0,1023.0,0.0,2000.0); //0 to 2000 mA - Setpoint
ctV = (double)map(analogRead(A1),0.0,1023.0,0.0,3300.0); //0 to 3300 mV - Feedback (2500mV is 0 mA)
dcCur = (double)map(ctV,2500.0, 3300.0, 0,8192.0); //2500 to 3300 mV is 0 to 8192 mA - Convert CT Voltage to Current
//PI Calculations
//Error
dcCurError = dcCurSP - dcCur;
//Proportional
dcCurProp = dcCurError * dcCurKp;
//Integrator
dcCurIntegrator = dcCurIntegrator + 0.5 * dcCurKi * dcCurSampleTime/1000000 * (dcCurError + dcCurPrevError); //Sample Time Variable in microseconds
//Anti-wind-up via integrator clamping
if(dcCurIntegrator >= dcCurLimitIntMax) dcCurIntegrator = dcCurLimitIntMax;
if(dcCurIntegrator <= dcCurLimitIntMin) dcCurIntegrator = dcCurLimitIntMin;
//Summation of P and I
dcCurOutput = dcCurProp + dcCurIntegrator;
//Output Limits
if(dcCurOutput >= dcCurLimitMax) dcCurOutput = dcCurLimitMax;
if(dcCurOutput <= dcCurLimitMin) dcCurOutput = dcCurLimitMin;
//values to carry forward
dcCurPrevError = dcCurError;
//Output to PWM time
dcTmrATime = (int)dcCurOutput; //0 to 100% output of PI control is same as 0 to 100 microseconds of PWM for 10kHz
dcTmrBTime = 100 - dcTmrATime;
}
Last edited: