I’m currently building an induction heater. I’ve already built one controller based on an analog PLL so this time I decided to go all digital and use an ARM Cortex-M4 microcontroller (STM32F303). For this I needed to implement a digital phase-locked loop running on the MCU.
An ADPLL is basically an analog phase-locked loop but implemented in software. You take the phase difference between a reference signal (the one you are trying to lock to) and your output signal with a phase-frequency detector and feed that difference to a filter that calculates a value that you then use to adjust your oscillator and try to match the frequency and phase of the reference signal. In an ADPLL even the oscillator is digital. In my version I’m using the STM32 integrated PWM module.
For the phase-frequency detector I’m simply using a comparator that starts a timer when it receives a rising edge. That timer is stopped when the microcontrollers pulse-width modulation module (=the oscillator) outputs a rising edge. If the timer is not running then it can also be started by the rising edge from the PWM module and stopped by the comparator.
That time difference is then fed to a filter which outputs a value that is used to adjust the PWM period (=frequency). For the filter my first idea was to use a P(I)D-controller which basically acts as a pole-zero filter with just the proportional and the derivative terms.
I had quite a lot of trouble getting the ADPLL to lock robustly with the PID. With some combination of parameters it seemed to lock well but the phase was jittering a lot. Without the proportional term and just the derivative term the ADPLL locked very poorly but if it managed to lock the lock was fairly good. Not good enough though. It looked like the proportional term was needed for good locking behavior but it let all the noise straight through and caused jittering. I also added a simple first-order low-pass filter on the input and it seemed to make it slightly better.
IIR loop filter
A friend suggested that I should try implementing a biquad filter (a type of infinite impulse response filter). The version with one pole and one zero is basically a digital implementation of a regular loop filter with two resistors and a capacitor, just transformed to the discrete digital domain:
You can calculate the multipliers for the pole-zero biquad filter by selecting a time constant for the filter:
tp is the main time constant and tz is usually tp divided by 3 to 4 (rule of thumb from Art of Electronics). T is time between samples (so approximately 1/f_out). I selected the loop filter time constant tp to be 1ms with the Stetson-Harrison method so tz is 0.25ms. After calculating the required parameters with the loop filter time constant deduced with the Stetson-Harrison method I implemented the filter. After fixing a few bugs with it it started to lock and the locking seemed to be very robust. Now I just need to implement some sanity checking and phase/frequency offsetting and I’m good to go.
I will upload the code to GitHub once I’ve cleaned it up a bit. There is at least one known bug that there is a small frequency dependant phase error because I’m measuring the time between the edges and not the actual phase difference (time between edges divided by length of the reference signal period).
Edit: Code has been uploaded to GitHub: Hyvok/ARM-induction-heater-controller
Thanks to jahonen for the idea and help for the IIR filter!