Syncing NCO output to trigger
pgbpsu
Posts: 460
What's the best way (or any better way for that matter) to sync a 1.024Mhz counter generated square-wave output to an input signal?
I'm using Chip's counter code to setup a 1.024Mhz square wave output on a given pin. I want that waveform to start when a trigger signal arrives on another pin. I'm currently setting up the counter then waiting for the trigger then setting the 1.024Mhz pin as an output. My trigger is actually a 1 pulse-per-second from a GPS. I don't really care which PPS acts as the trigger, but I want it to trigger ON the PPS so I
1. startup with 5Mhz oscillator with PLL set to 16 (80Mhz system clock)
2. measure the number of system clocks in each of 10 seconds
3. average that to get the "actual" number of CNTS in one second
4. set the system clock to this value value
5. calculate CTRB and FRQB for my 1.024Mhz output signal and set them
6. wait for a PPS and grab system CNTS
7. predict when the next PPS will arrive, subtract from that the amount of time it takes to make pin an output.
8. wait for that system CNT to arrive
9. make pin output
This works okay. My output waveform is within a few hundred nanoseconds of my PPS although the first pulse is often a runt pulse- not a full pulse. I'm sure this has to do with the phase relationship between my 1.024Mhz output and when I set the pin to an output. So how can I do this better?
And secondly, I want to produce this same 1.024Mhz waveform on another board and have it look as close as possible (frequency and phase) to the first one for a few seconds. I believe checking the system clock against the a 1PPS should give me the actual system clock allowing me to get my 1.024Mhz frequency accurate. And syncing the output to the PPS I should be able to phase lock signal on different boards, at least for a few seconds. I understand there are temperature issues but for proof of concept I'm doing this on a workbench where there are few air currents and a stable temperature (less than 1 degree C) over seconds to minutes.
Thanks for reading and for any input.
Relevant code below.
I'm using Chip's counter code to setup a 1.024Mhz square wave output on a given pin. I want that waveform to start when a trigger signal arrives on another pin. I'm currently setting up the counter then waiting for the trigger then setting the 1.024Mhz pin as an output. My trigger is actually a 1 pulse-per-second from a GPS. I don't really care which PPS acts as the trigger, but I want it to trigger ON the PPS so I
1. startup with 5Mhz oscillator with PLL set to 16 (80Mhz system clock)
2. measure the number of system clocks in each of 10 seconds
3. average that to get the "actual" number of CNTS in one second
4. set the system clock to this value value
5. calculate CTRB and FRQB for my 1.024Mhz output signal and set them
6. wait for a PPS and grab system CNTS
7. predict when the next PPS will arrive, subtract from that the amount of time it takes to make pin an output.
8. wait for that system CNT to arrive
9. make pin output
This works okay. My output waveform is within a few hundred nanoseconds of my PPS although the first pulse is often a runt pulse- not a full pulse. I'm sure this has to do with the phase relationship between my 1.024Mhz output and when I set the pin to an output. So how can I do this better?
And secondly, I want to produce this same 1.024Mhz waveform on another board and have it look as close as possible (frequency and phase) to the first one for a few seconds. I believe checking the system clock against the a 1PPS should give me the actual system clock allowing me to get my 1.024Mhz frequency accurate. And syncing the output to the PPS I should be able to phase lock signal on different boards, at least for a few seconds. I understand there are temperature issues but for proof of concept I'm doing this on a workbench where there are few air currents and a stable temperature (less than 1 degree C) over seconds to minutes.
Thanks for reading and for any input.
Relevant code below.
PUB MAIN '' Setup PPS as input and measure system counts per second. dira[PPS_PIN] := 0 ' set to input repeat idx from 0 to ARRAY_SIZE - 1 waitpeq(0, constant(|<PPS_PIN), 0) ' wait for pin to go low waitpne(0, constant(|<PPS_PIN), 0) ' wait for pin to go high cntsPerSecond[idx] := cnt '' calculate mean value mean := 0 repeat idx from 1 to ARRAY_SIZE - 1 mean += (cntsPerSecond[idx]-cntsPerSecond[idx-1]) mean := mean/(idx-1) '' make mean value new system clock value clkset($6F,mean) ' with to pll16x PAUSE_MS(1000) ' wait for it to take effect '' Synthesize frequencies on pin 0 and pin 1 SynthFreq(MCLK_PIN, 1_024_000) 'determine ctr and frq for pin1 CTRB := ctr 'set CTRB FRQB := frq 'set FRQB waitpeq(0, constant(|<PPS_PIN), 0) ' wait for pin to go low waitpne(0, constant(|<PPS_PIN), 0) ' wait for pin to go high startTime := cnt + mean - 820 ' remove CNTS required to make pin an output waitcnt(startTime) ' wait just less than one second DIRA[MCLK_PIN]~~ 'make pin output repeat ' stay alive so counters don't quit waitcnt(0)
Comments
EG:
This takes 12 clocks.
Thanks for responding. I'm currently looking at moving this into a PASM cog to do the output generation. I think starting up the signal, then turning the pin on (as Chip has done as well) is giving me the runt pulse to start with. I'm not sure how big a deal this is, but I think I may be better able to sync this output signal each second to keep it in phase with the 1PPS if I move away from the counter and into a PASM cog doing waitcnt with an occasional waitpeq.
-Phil
If the 1pps is there on all boards this should not have "at least for a few seconds" issues ?
Remember the 1.024MHz is also going to have its own jitter, which hopefully you are Ok with ?
If you start at 80MHz, that divides by 78.125, & you will be 12.5ns quantized in phase & frequency,
My maths gives
Ta=78/80e6;Tb=79/80e6;A=7;B=1;1/((A*Ta+B*Tb)/(A+B))
ans = 1024000
so 7 cycles of Fa and one cycle of Fb is what your pin will actually deliver.
Only over multiples of 8 cycles, will this average to exactly 1.024MHz
Fa=80e6/78 Fa = 1025641.02564Hz
Fb=80e6/79 Fb = 1012658.22784Hz
Thanks for commenting on the right way to do this. Re-reading the Counter App note, it clearly states that the value of FRQx is added to the value of PHSx every clock cycle. Simple enough. However, when does this summing begin? Always, or only once I load CTRB with the NCO/pin info?
Since I want bit 31 of the sum of PHSB and FRQB to be 1 right after I set the CTRB register, I tried presetting PHSB to
What I think should happen is on the first clock AFTER setting the CTRB, FRQB (which I set to: $0D1B_76C6) is added to PHSB (set to $72E4_893A) which results in $8000_0000. Bit 31 is 1 so APIN should be 1. Believe it or not, that not doing what I'd hoped. The rising edge of my 1.024Mhz output is delayed from the PPS (which I expected). The delay however varies more than I would like. Capturing a handful of images with the scope I get delays between my 1PPS and the start of the 1.024Mhz of 5-7uS. This seems a bit large to be hub window access. Secondly, my first pulse isn't as narrow as it should be. Based on the way the NCO mode works this must be caused by my choice of PHSB primer.
The question now becomes how to preload the PHSB register with the correct value.
-Peter
Thanks for your post. You are correct about the width of individual pulses. I believe 7 short pulses and one long pulse will be acceptable, but to make things simpler for testing, I've shifted my output frequency down to 1.000Mhz. However, because the crystal isn't really 5Mhz (PLLed up to 80Mhz) my output waveforms still aren't perfect, but they are slightly improved.
-Peter
BTW, you will get a symmetrical, jitterless waveform if you output at 1.25 MHz (80 / 64).
-Phil
With this sort of problem I often also output a port-pin pulse, so you know what the Pin-Sw Sync delays are, and can confirm how much of the the delay is counter-side and how much is capture-side related.
This method has a faster response time and the waveform is more stable in the first few pulses.
I've attached 2 plots showing the waveforms. In both figures the time base is 2.0uS/div and the vertical scale is 2.0V/div. The first figure (cntr.bmp) shows the 1PPS arriving (in yellow) about 5.2uS before the beginning of my counter-generated output (blue). The lag here varies a bit (I've seen as high as 7.5uS) and the first few cycles of this are not right. The second plot (asm.bmp) show the same story only using my PASM code to generate the square-wave output. You can see the lag from the PPS is much improved (stable at 50ns which is expected) and the waveform looks correct right from the get go.
Although I'd like to do this with the counters because it would save me a cog, I'm satisfied (for now) that I can get this output wave synced to the PPS, but only at the start. The third figure 2asm.bmp shows the same code running on two independent boards. The only thing they share is a PPS pulse. The (external) trigger point (right in the center) is the PPS. Both boards then start putting out the PASM generated square wave. They start out in phase. The portion of the waveform from the left edge of the screen unto the trigger point is actually the end of the waveform from the previous 1PPS.
There are two problems here. The first is that the waveform doesn't run all the way to the end of the second. The more critical problem for me is the two waves are out of phase. This can be seen by the mismatch between the two waveforms. But in reality it's worse than that. My PASM code has a loop structure that decrements each time through the loop. It is supposed to generate 2E6 toggles for each second. That doesn't work correctly. I have to add additional iterations to the loop (manually basically) to cycles all the way to the end of the second. Furthermore, I have a different number of cycles for the 2 different boards. In fact one board has 10 more cycles per second. So my error (both phase and frequency) is worse than what the above plot shows.
Getting those first cycles synced has been solved. However keeping them synced through the whole second has not. I thought I could do this by measuring the performance of the crystal in use (using subsequent PPSs). Either this can't work because of error in the crystals or I'm just doing it wrong.
I'm not convinced it can't be done. But I am convinced I'm not doing well enough.