Digital phase-locked loop with ARM - Page 1
Forum section: electronics / general

author: Dago
author_rank: Frequent Contributor
timestamp: July 23, 2014, 06:27:25 am
content: I'm implementing a digital phase-locked loop with an STM32F303CCT6. I basically need to lock in to a square wave coming in (but I also need to have the possibility to detune the frequency or do sanity checks on it) and output a 50% duty cycle PWM-signal out with the same frequency and phase. The frequency is in the range of maybe 40-60kHz.Currently I have a setup where I measure the time between the rising edge of the reference signal (with a comparator triggering a timer) and the rising edge of the PWM-signal (so kinda like a XOR phase detector). This time difference is used as an input to a PID-controller (no integral parameter since it's a self-integrating process) which controls the period register on the advanced-control timer I'm using.My current setup almost works, it locks to the frequency alright but the phase is all over the place. I played around a lot with the PD-parameters yesterday and I couldn't make it any better. It basically seems to lock to the frequency with pretty much any parameters but otherwise I'm having a hard time saying anything concrete about the effect of the parameters (just a subjective guess that "this MIGHT have less jittering and phase running around").I have a feeling the I might need to be able to somehow "synchronize" the PWM-timers phase to the reference signal but I'm not sure how. Only thing I can think of is just resetting the whole counter and starting again when the comparator gets an edge from the reference signal but this doesn't seem right (I would get distorted cycles with non 50% duty cycle etc. and maybe even some additional glitches).Any ideas on how to achieve this? Is there something fundamentally wrong with my "algorithm"? Is there a better way to quantify the performance (looking at the square waves with my DS1074Z is almost useless because the phase is jittering so much that the other square wave is just a blur)?Btw. I ran in to this article:http://edn.com/design/test-and-measurement/4323787/Two-gates-and-a-microprocessor-form-digital-PLLwhich is pretty much what I want to achieve but I would like to avoid extra hardware (if possible). Also I cannot do that clock gating trick with this ARM if I want to use the high-frequency doubled PLL clock for the timer (for better frequency resolution). I'm also not totally sure what is the point of the clock gating trick?

author: AndyC_772
author_rank: Super Contributor
timestamp: July 23, 2014, 06:54:04 am
content: I've done this sort of thing in FPGAs before, and I completely agree it can be difficult to get it stable.Try dividing down the reference and PWM clocks before comparing their phase. If you're updating the period register based on a measurement made over just one cycle, you may simply have too much high frequency gain.The key to getting it more stable is to introduce some low pass filtering and damping. Try dividing down your clocks to, say, 100 Hz or so, and put a limit on the amount by which the PWM period may change each cycle.This should allow you to achieve a response which is more stable, but it'll probably oscillate either side of the correct frequency because there's no damping. A small integral term added to the PWM period will help damp this out.

author: Dago
author_rank: Frequent Contributor
timestamp: July 23, 2014, 06:13:30 pm
content: Quote from: AndyC_772 on July 23, 2014, 06:54:04 amHey, thanks for the good idea! I tried it but it didn't make much difference though. I think I found my problem... I think the resolution of the timer is just not good enough. I tried this by trying to adjust the frequency by hand and get the frequency good enough so that the two signals would stay in phase or just slowly drift and I couldn't do it. At exactly one period register value the frequency is close enough that I can see that the other signal is a square wave (but still moving quite fast). A single step higher or lower and it just gets much worse.I think I need to use a DDS to get good enough frequency resolution... I'm not too keen on this idea because then I also somehow need to generate dead-time and so forth. But AD9837 (http://www.analog.com/static/imported-files/data_sheets/AD9837.PDF) seems to have a square wave out so it might be pretty nice for my application.

author: mrflibble
author_rank: Super Contributor
timestamp: July 23, 2014, 06:47:15 pm
content: I've done something similar, and FWIW:On an upcount you changer the CC register by +1 and the reload register by +2for the next cycle only. For the cycle after that you reload it with the nominal value again. And for a downcount I think you can guess it.With the +1/+2 like that you fulfill your 50% duty cycle requirement. And for rest ... it's just a single pwm period a little shorter/longer so as to align the phase.But as you already figured ... if the frequency resolution is too low you may have to take additional steps as well. For example by adding a divider first. IMO divide by two is almost never a bad idea. If only because that way you don't have to worry about duty cycles so much...Incidentally what are your timing requirements? Because stm32 is quite reasonable in the timing department, but it's notgreat. Definitely not anywhere near what you can do with fpga.

author: AndyC_772
author_rank: Super Contributor
timestamp: July 23, 2014, 06:52:59 pm
content: OK, sounds like you need to modulate the PWM period from one cycle to the next, in order to give yourself the effect of fractional bits in the timer period. IIRC the timers have a shadow period register which you can write at any time during the cycle and which automatically updates at the end of the cycle. Update this in an ISR when the timer updates.So, for example, if you want the effect of a timer period which is 10.25 clocks, you can write an interrupt driven routine to change the period on a cycle-by-cycle basis to be 10, 11, 10, 10. It shouldn't be too difficult to come up with a dithering algorithm based on a running accumulator and error calculation.

author: mrflibble
author_rank: Super Contributor
timestamp: July 24, 2014, 12:17:21 am
content: Well, not entirely. IMO the +1 / -1 count method I described is perfectly acceptable during the phase locking part. But if you need a large amount of dither to reach the required frequency resolution that's not really the way to go. Unless your application doesn't care too much about a large amount of jitter, but does care about long term frequency accuracy.If you want to use dithering to reach frequency accuracy, then you can precalc a large number of cycles, and set up a dma circular buffer. How large depends a bit on your division ratio used for your phase detector...And re-reading the OP:QuoteThat particular problem could very well be due to the non-50% duty cycle you mentioned. If you divide your signal by two and trigger on that, things might suddenly look a lot more stable... Not sure about your case, but that is something that sprang to mind while reading that.

author: Dago
author_rank: Frequent Contributor
timestamp: July 24, 2014, 06:26:13 am
content: Thanks for the ideas guys!This project is an induction heater controller so it is controlling a H-bridge driving an LCLR tank circuit at resonance based on feedback from a current transformer. So jitter and frequency stability are not tremendously important... I guess jitter can cause the bridge to not soft-switch which is not wanted but depends on the amount of jitter in relation to the actual current... I'm not sure about how steep the resonance peak is and what kind of frequency accuracy I need to hit it. Need to do a simulation about that. Or wind my work coil and measure.Dithering sounds like a bit of a hack... I might look in to it though because it's pretty simple to do. Personally I'm starting to lean towards the DDS+some kind of deadtime generator (CPLD maybe) solution.

author: nctnico
author_rank: Super Contributor
timestamp: July 24, 2014, 07:34:30 am
content: Another way around is using a VCO to drive the CPU clock. But the amount of frequency adjustment per time the controller can tolerate may depend on the PLL in the controller.Dithering isn't bad. It just adds some phase noise but that will be straightened out by the resonance circuit because it acts like a filter.

author: mrflibble
author_rank: Super Contributor
timestamp: July 24, 2014, 03:22:03 pm
content: Quote from: Dago on July 24, 2014, 06:26:13 amFor that I would expect the resolution on stm32f3 to be enough. That 60 kHz is 1200 counts @ 72 MHz. And as nctnico already pointed out, dithering in this case probably isn't a problem. Maybe it costs you a little in terms of efficiency, but probably not that much. Not sure.And at least with the PWM you already have your deadtime all sorted. Right? Or doesn't stm32f3 have useable deadtime on PWM? I know the F4 has it, but I forgot if the F3 has the same deadtime capabilities.Edit:just checked it. Looks like you're all set for deadtime.STM32F302xx STM32F303xx- One 16-bit timer with 2 IC/OCs, 1 OCN/PWM, deadtime generation and emergency stop- Two 16-bit timers with IC/OC/OCN/PWM, deadtime generation and emergency stop

author: free_electron
author_rank: Super Contributor
timestamp: July 24, 2014, 03:32:49 pm
content: This is a classic real-time problem.Jitter comes from the control loop. Your code does not always take the exact amount of instructions to execute. One branch somewhere and depending if you went left or right will alter the point in time where you update the pwm register.This can be solved by setting a flag in the pll code that an update is required. A hardware timer fires an interrupt. the isr (i terrupt service routine) then checks the flag and if set writes the register.Now the time from interrupt to writing is a constant no matter what. Code is now time deterministic updates happen on queue from a hardware timer and the ha dling code has a constant execution time.If you can use the same master clock for both the hardware timer and the pwm generator then it will lock perfectly every time.

author: dannyf
author_rank: Super Contributor
timestamp: July 24, 2014, 03:50:14 pm
content: Depending on how the pwm is implemented, you could be reseting its phase each time you adjust the output. Use center aligned pwm instead.

author: mrflibble
author_rank: Super Contributor
timestamp: July 24, 2014, 04:06:59 pm
content: Based on Dago's other projects I was more or less assuming that he's already doing the updates in an ISR. Because not doing that would make things ... interesting.And a weeee bit unstable.

author: f5r5e5d
author_rank: Frequent Contributor
timestamp: July 24, 2014, 07:34:56 pm
content: if you are heating something with your resonant circuit in an induction heater then Q can't be infiniteeven unloaded the power resonator is unlikely to have Q of many 100s

author: Dago
author_rank: Frequent Contributor
timestamp: July 24, 2014, 08:04:58 pm
content: Quote from: mrflibble on July 24, 2014, 03:22:03 pmQuote from: f5r5e5d on July 24, 2014, 07:34:56 pmYes the resolution will be plenty enough to hit the resonance but it looks like it is not good enough (at least without dithering) to get the phase properly locked for zero-current swithing which is my goal. the DPLL seems to lock to the frequency very well with pretty much any PID-parameters. You can feed 2x PLL clock to the timer which in my case results to 138 MHz clock for the timer. This results to around 15 Hz frequency resolution at around 30 kHz where I was doing some testing.I'm using center-aligned PWM and pre-load so changing the period should not change the phase.I had a friend suggest to try to use a biquad filter as the loop filter and calculate the poles so that there is basically just one parameter to play around with.I'm also toying with the idea of making an FPGA based PWM-controller based on the "phase accumulator" idea from DDS chips (just omit the sin part) which should result in very good frequency resolution. 32bit accumulator with 200 MHz clock would result in ~0.05 Hz resolution. Plus I could offload loads of other stuff to the FPGA as well...

author: nctnico
author_rank: Super Contributor
timestamp: July 24, 2014, 09:05:48 pm
content: Quote from: free_electron on July 24, 2014, 03:32:49 pmRunning the entire DPLL code in the interrupt is much easier. For timing constrained things I usually update the registers first and then have the software calculate the next value.

author: Dago
author_rank: Frequent Contributor
timestamp: July 25, 2014, 10:03:23 pm
content: http://youtu.be/NLZCMP4IBv8Here is the best lock I have managed to achieve so far. Almost good but still glitching a bit. There seems to be a fairly constant jitter of 4µs. I'm not sure if that's due to the frequency resolution or something else. Nothing should cause such a long delay in my code.

author: mikerj
author_rank: Super Contributor
timestamp: July 26, 2014, 12:12:59 pm
content: Quote from: Dago on July 25, 2014, 10:03:23 pmAssuming you are running at 30KHz, 4us change in period is a frequency change of approximately 4KHz. As you already confirmed that the hardware is capable of a 15Hz step then it suggests your code is the problem, possibly a loss of precision or scaling problem if you are using integer maths.Have you tried running under a debugger and looking at the PWM register values being output by your control loop? Posting the code for your PID control loop would probably help as well.

author: Dago
author_rank: Frequent Contributor
timestamp: August 07, 2014, 08:06:36 pm
content: Slight development, I changed the interrupt priorities (so that they are not equal, not sure how they stack then) and enabled optimizations for the compiler (forgot that, they were disabled for debugging) and played around with the PID-parameters more and got the locking kinda better:This was achieved with the proportional term being 0 and the integral term being 0. Just a derivative term seems to work the best. But it still wanders around and sometimes jumps to 90 degree phase shift and sometimes to 180 degrees. Also I'm dividing the frequencies by ten in my code (doesn't seem to make a huge difference, works without the division as well).Looks like the proportional term is useless because it straight up amplifies the jitter/noise. The problem with a derivative term only is that now it does not really lock to the frequency too well unless it is close. It just matches the phase.

author: Dago
author_rank: Frequent Contributor
timestamp: August 08, 2014, 01:03:40 pm
content: Success!I threw out the PID-controller and replaced it with an IIR filter (stripped down biquad filter) and this seems to do the trick:There are still some problems to solve (currently it is locking to bit-under 180 degrees phase shift, most likely something simple) and then I need to figure out some kind of sanity checks to limit the frequency range (sometimes it jumps to the min or max frequency and stays there for some reason) and play around with the time constants/multipliers for the filter. The video is with the first values I tried. Also currently the input frequency is not divided down, that is also something to try.Thanks for the idea and help with the loop filter to jahonen!

author: Dago
author_rank: Frequent Contributor
timestamp: August 20, 2014, 06:37:53 am
content: I wrote a blog post about this DPLL project:http://www.dgkelectronics.com/implementing-digital-phase-locked-loop/Currently I've been making a VHDL version. I have it working with the XOR-type phase-frequency detector (using the ieee proposed fixed-point library):http://www.dgkelectronics.com/storage/electronics/induction_heater/digi/filter5.pngThe problem with that version that counts the time between the XOR edges is that there is a phase-difference that is proportional to the frequency. To get rid of that I would need to divide with the reference signal period length and obviously I would like to avoid division on an FPGA.I converted it to use a (4046) type2 phase detector which is this funny state machine which either outputs plus, minus or zero. Plus means the filter gets maximum value as the input and minus means it gets minimum value as input and zero means the filter output is not changed. In my VHDL this is represented by two std_logic signals, the other one means if the state is + or - and the other one means if the filter is updated or not (so it is 0 when state is zero, the other signal is then "don't care").The problem with this however is that the phase detector is running at the "main" clock frequency of 200 MHz and the filter is updated at 4096th of that (~50kHz). The filter then just basically reads the whatever state from the phase detector when it is updated and this does not seem to work (because the PFD output changes state at a much higher frequency). I think I basically would need to somehow filter and downsample the PFD output but I have no idea how to do that on a binary signal. The other complication is how should the "zero" state be handled?Another problem with this type2 PFD is that there doesn't seem to be an easy way to cause deliberate phase-difference (with the XOR you could just add or subtract from the measured phase-difference value and voilá).Any ideas?

author: danfo098
author_rank: Contributor
timestamp: September 02, 2014, 02:24:18 pm
content: Why not run the phase detector and the filter at the same rate? I think a good compromise is 100 MHz for both of them. Then lower the values of the coefficients in the filter to compensate for the increased bandwidth.To get an arbitrary phase shift in the output add a second phase accumulator (DDS) and also drive it with the output from the filter but add a phase offset value to it.

author: Coyote
author_rank: Contributor
timestamp: March 30, 2021, 09:34:24 am
content: @Dago I was searching of your ADPLL project and I can't find it. I am almost 100% sure, that I have seen it some year ago on GitHub, and your project in VHDL HardHeat is vanished as well.

author: mrflibble
author_rank: Super Contributor
timestamp: May 21, 2021, 12:11:59 pm
content: Quote from: Coyote on March 30, 2021, 09:34:24 amForkity-fork:https://github.com/wuyou33/HardHeathttps://github.com/MarkoBursic/HardHeatRelated:https://github.com/Hyvok/ARM-induction-heater-controller