Using counters to mirror 1PPS
pgbpsu
Posts: 460
I'd like to use a counter to produce a "1PPS" in a set-it-and-forget-it mode and I'm having trouble.
My first attempt was to use a counter in duty mode so that my fake PPS signal would match (in width) the real one I have (100uS). I wasn't able to get that working. I couldn't tell if that was because I needed to do additional monitoring of the signal in Duty mode or simply because I got it wrong. But I couldn't even tell which of those scenarios I was in (probably the latter).
So I moved to an NCO/PWM mode which would have .5 second width which I'm not crazy about but I can live with. I have 2 questions
1) is Duty mode with a 100uS high pulse really out if I can't dedicate an entire cog to it?
2) how can I sync my fake PPS (code below) to the real PPS?
I've looked over the App note and that's what got me thinking that Duty needs some babysitting. The code from Kye's routines does produce a 1hz square wave, but I'm at a loss when it comes to syncing it to the actual PPS.
Thanks,
Peter
My first attempt was to use a counter in duty mode so that my fake PPS signal would match (in width) the real one I have (100uS). I wasn't able to get that working. I couldn't tell if that was because I needed to do additional monitoring of the signal in Duty mode or simply because I got it wrong. But I couldn't even tell which of those scenarios I was in (probably the latter).
So I moved to an NCO/PWM mode which would have .5 second width which I'm not crazy about but I can live with. I have 2 questions
1) is Duty mode with a 100uS high pulse really out if I can't dedicate an entire cog to it?
2) how can I sync my fake PPS (code below) to the real PPS?
PRI FAKE_PPS(frequency) | buffer, counter ' Configure the status LED. ' taken from Kye's SD card routines. ' Frequency must be between 0 and (clkfreq / 2). Otherwise output is always 1. buffer := ((0 < frequency) and (frequency =< (clkfreq >> 1))) outa[OUTPUT_PIN] := 0 ctra := (constant((%00100 << 26) + OUTPUT_PIN)) dira[OUTPUT_PIN] := true counter := 1 repeat 32 ' Preform (((frequency << 32) / clkfreq) + 1) frequency <<= 1 counter <-= 1 if(frequency => clkfreq) frequency -= clkfreq counter += 1 frqa := (counter) ' Output is always 0 if frequency is 0. ' next 3 lines were added to try to sync to PPS but clearly that doesn't work waitpeq(0, constant(|<PPS_pin), 0) ' wait for pin to go low waitpne(0, constant(|<PPS_pin), 0) ' wait for pin to go high phsa := 0
I've looked over the App note and that's what got me thinking that Duty needs some babysitting. The code from Kye's routines does produce a 1hz square wave, but I'm at a loss when it comes to syncing it to the actual PPS.
Thanks,
Peter
Comments
Even so, the output will not be exactly one Hz. The dominant period with be 0.9942 second (1.005828 Hz) when the frqa=frqb=54. To achieve nominally 1Hz you would need a 4.131584 or 8.263168 MHz crystal.
I don't think you can sync or phase lock a prop oscillator tightly to the external pps source without using a cog. However, you might be able to sync long term in spin, depending on the goal, because it would take about 170 seconds for the 0.9942 Hz free running counters to drift by 1 Hz in relation to the pps reference (gps?).
Thanks for responding and for clarifying the need for maintenance when running in Duty mode. Before being able to get back to the internet I found, in my pdf of the Education Kit labs, that set-it-and-forget-it does not apply to this mode. Running my prop at 10Khz isn't really an option, but that would do what I want.
You figured out what I mean by doesn't work. My waitxxx commands do respond to the PPS which I have. But setting phsa:=0 on the rising edge of the of the real 1PPS does not align my fake pps with the real one. That's what I meant by doesn't work. Sorry for not being clearer. But your suggestions address that issue. I have a scope and can experiment until the two line up as closely as possible.
Terry-
The solution you presented is genius! I'll give it a try as soon as I get back to the hardware setup which has a PPS. My external oscillator is actually 5.12Mhz so my system clock is 81.92Mhz. I'll rework the math in your example and see where that gets me. However your explanation about the time required to drift off by one second leaves me wondering if I'm trying to solve my real problem the wrong way.
I have a datalogger which has GPS (and 1PPS). The value of the system counter is captured every 1PPS and recorded so data can be time stamped. Problem is the GPS is a power hog so I want to sleep it to save power. My plan is to have the GPS on at boot up, use the 1PPS from the GPS to stamp data for while, then put it to sleep, turn it on later and record it again. The data would have to be adjusted for drift later in processing, but we'd just draw a straight line between the times when the 1PPS was present.
From a coding standpoint, I thought all my time stamping could be just the same regardless of which PPS my code was watching for. I would set a flag in the data to let the post-processing software know if the time stamp is from a real 1PPS or my fake PPS. However your description of drift makes me think I'd be better of with waitcnt based timer.
Do I understand the drift inherent in my fake 1PPS correctly? Sleep times for the GPS will exceed 170 seconds (I'd like to sleep it for 10 minutes at a time).
Thanks,
Peter
Graham
That would certainly work, but we're trying to make something out of hardware we already have. I'm afraid mods are out at this point. It needs to be a software solution. There's no way to avoid correcting the timing of samples that are acquired when the PPS is not present. And I doubt that an RTC would be accurate enough to prevent this. Over 20 minutes I expect our 5.12Mhz oscillators to be decent (at least as good as an external RTC). There are more than a couple ways to solve this in software. I was hoping to use the counters since they could be setup and left alone without costing me any cogs or any (significant) code space. Maybe I was being too optimistic.
p
INT(2^32 / 52) * 12.207031 nanoseconds = 1.00824614258 second.
That is a frequency of 0.9918213 Hz
The drift comes from the 0.008246 second per second remainder, and that amounts to 1/0.008246 = 121 seconds for it to be off by 1 second. Pretty dismal for your intents.
If you want that 1Hz precisely, you'll need a binary power crystal, or go to plan B.
Thanks again for your input. It seems like this is a case of "no free lunch". It looks like my best bet is to use waitcnt rather than a cog. I don't have the specs for our oscillator on hand, but it has to give a time base better than 1 in 120. The reason I was avoiding this is because my time keeping code will look one way when I have a PPS and another way when I don't. Oh well. The other reason was I didn't want to tie up a cog in a waitcnt. Although after thinking about it I realize I can probably do other quick non-time critical stuff as long as I know it will take less than a second. I can return from that in time to finish waiting for 1 second to have expired via waitcnt. So at the cost of a bit of code, I think the waitcnt operator is where my solution lies.
Thanks again for all the input and the clever use of counters to get a 100uS pulse without resorting to DUTY mode.
Regards,
Peter
(Oops.. Just realized I exactly agree with what Tracy Allen said a couple posts above)