Digital phase-locked loop in action

Digital phase-locked loop in action

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.

Principle

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.

PID

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:

Pole-zero biquad IIR filter

Pole-zero biquad IIR filter

You can calculate the multipliers for the pole-zero biquad filter by selecting a time constant for the filter:

Pole-zero IIR filter coefficients

Pole-zero IIR filter coefficients

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!


2 Responses so far.


  1. Angel G. says:

    Excellent project, thanks 🙂
    I’m trying to do something similar, probably using 2 timers:
    – first one – ext triggered, used to measure the time between the input pulses, second – used to recover/predict the input pulses based on measurements of the previous instants. I wonder whether I could do this without too complex filter.

    • Hello! The filter actually does not have to be that critical. It only has to work as a low-pass filter of some sort. The details affect the loop bandwidth and loop behaviour (locking time etc.) but if these are not critical then you can for example use a simple PI(D)-filter (you do not need derivative term at all). Even just calculating an average of the values will effectively behave as a low-pass filter and might work (I have not tried it).

Leave a Reply