Does anyone have a feel for how much drift to expect between the Pi's internal microsecond clock (SYST_CLO) and actual time?
i.e. in 1000 seconds how close to 1,000,000,000 ticks should I expect to see?
The issue had more to do with me trying to pulse a gpio once each (NTP) second boundary - and wondering why it happened every 1,000,025 Pi ticks.chrisryall wrote:if one wants a clock one should buy a clock; I think the issue here may be how to train the little Pi to update its clock regularly from a readily available Internet connection?
Maybe your time pulse is being started every 1,000,000 ticks (+/- tolerance) and 25 ticks to toggle GPIO and schedule again via instructions.joan wrote:The issue had more to do with me trying to pulse a gpio once each (NTP) second boundary - and wondering why it happened every 1,000,025 Pi ticks.
I'm trying to send a pulse on every real second boundary. It seems to work but every time I try to work out why I confuse myself. I did notice that each pulse was happening 25 microseconds later than I expected. But it turns out that the Pi I'm using runs at 1,000,025 ticks in an actual second so it seems to be correct. I'll sleep on it.techpaul wrote:Maybe your time pulse is being started every 1,000,000 ticks (+/- tolerance) and 25 ticks to toggle GPIO and schedule again via instructions.joan wrote:The issue had more to do with me trying to pulse a gpio once each (NTP) second boundary - and wondering why it happened every 1,000,025 Pi ticks.
Random thought if always asking for 1,000,000 and you get 1,000,025 what inaccuracy do you get when asking for 999,975 ticks?
Code: Select all
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
#include <pigpio.h>
#define GPIO 4
#define LEVEL 1
#define PULSE 200
#define VARY 200
static int gpio = GPIO;
static int plev = LEVEL;
static int plen = PULSE;
static int vary = VARY;
static unsigned long *varyA, *pulseA, *offA;
gpioPulse_t wave[3];
void fatal(char *fmt, ...)
{
char buf[256];
va_list ap;
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
fprintf(stderr, "%s\n", buf);
exit(EXIT_FAILURE);
}
void usage()
{
fprintf(stderr, "\n" \
"Usage: sudo ./pps [OPTION] ...\n"\
" -g 0-31 gpio (%d)\n"\
" -l 0,1 pulse level (%d)\n"\
" -p 1-5000 pulse micros (%d)\n"\
" -v 100-5000 vary micros (%d)\n"\
"EXAMPLE\n"\
"sudo ./square -g 23\n"\
" Generate pulse per second on gpio 23.\n"\
"\n", GPIO, LEVEL, PULSE, VARY);
}
static void initOpts(int argc, char *argv[])
{
int i, opt;
while ((opt = getopt(argc, argv, "g:l:p:v:")) != -1)
{
i = -1;
switch (opt)
{
case 'g':
i = atoi(optarg);
if ((i >= 0) && (i <= 31)) gpio = i;
else fatal("invalid -g option (%d)", i);
break;
case 'l':
i = atoi(optarg);
if ((i == 0) || (i == 1)) plev = i;
else fatal("invalid -l option (%d)", i);
break;
case 'p':
i = atoi(optarg);
if ((i > 0) && (i<=5000)) plen = i;
else fatal("invalid -p option (%d)", i);
break;
case 'v':
i = atoi(optarg);
if ((i > 0) && (i<=5000)) vary = i;
else fatal("invalid -v option (%d)", i);
break;
default: /* '?' */
usage();
exit(EXIT_FAILURE);
}
}
}
void callback(int gpio, int level, uint32_t tick)
{
int i, optional;
struct timespec tp;
uint32_t micro, t1, t2, diff, mt1;
uint32_t nextPulse, nextPulseTick, delay, fixed;
if (level == plev)
{
diff = 1000;
for (i=0; i<10; i++)
{
t1 = gpioTick();
clock_gettime(CLOCK_REALTIME, &tp);
t2 = gpioTick();
if ((t2-t1) < diff)
{
diff = t2-t1;
mt1 = t1;
micro = (tp.tv_nsec+500) / 1000;
}
}
if (diff < 6)
{
nextPulse = 1000000 - micro;
nextPulseTick = mt1 + (diff/2) + nextPulse;
delay = nextPulseTick - tick;
fixed = 1000000 - vary;
optional = delay - fixed;
if (optional < 0) optional += 1000000;
if (!optional) optional = 1;
*varyA = (optional * 4);
printf("diff=%d t=%u nt=%u micro=%d nextPulse=%d opt=%d\n",
diff, tick, nextPulseTick, micro, nextPulse, optional);
}
}
}
int main(int argc, char *argv[])
{
int off;
int i;
dmaCbs_t *p;
initOpts(argc, argv);
off = 1000000 - (plen+vary);
printf("gpio=%d, level=%d vary=%dus, off=%dus\n", gpio, plev, vary, off);
gpioCfgClock(1, 1, 1);
if (gpioInitialise()<0) return -1;
/* get pps callback */
gpioSetAlertFunc(gpio, callback);
varyA = &(waveCbVOadr(2)->length);
pulseA = &(waveCbVOadr(4)->length);
offA = &(waveCbVOadr(6)->length);
gpioSetMode(gpio, PI_OUTPUT);
if (plev) /* pulse is high */
{
wave[0].gpioOn = 0;
wave[0].gpioOff = (1<<gpio);
wave[0].usDelay = vary;
wave[1].gpioOn = (1<<gpio);
wave[1].gpioOff = 0;
wave[1].usDelay = plen;
wave[2].gpioOn = 0;
wave[2].gpioOff = (1<<gpio);
wave[2].usDelay = off;
}
else /* plen is low */
{
wave[0].gpioOn = (1<<gpio);
wave[0].gpioOff = 0;
wave[0].usDelay = vary;
wave[1].gpioOn = 0;
wave[1].gpioOff = (1<<gpio);
wave[1].usDelay = plen;
wave[2].gpioOn = (1<<gpio);
wave[2].gpioOff = 0;
wave[2].usDelay = off;
}
gpioWaveClear();
gpioWaveAddGeneric(3, wave);
gpioWaveTxStart(PI_WAVE_MODE_REPEAT);
printf("v=%d p=%d o=%d\n", *varyA, *pulseA, *offA);
while (1) sleep(1);
gpioTerminate();
}
Code: Select all
// link with gcc -lrt
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <sys/mman.h>
#include <sched.h>
int main()
{
struct sched_param param;
param.sched_priority = sched_get_priority_max(SCHED_FIFO);
if (sched_setscheduler(0, SCHED_FIFO, ¶m) < 0)
perror("warning: sched_setscheduler");
if (mlockall(MCL_CURRENT | MCL_FUTURE) < 0)
perror("warning: mlockall");
struct timespec t1, t2, r1, r2;
clock_gettime(CLOCK_MONOTONIC_RAW, &r1);
clock_gettime(CLOCK_REALTIME, &t1);
clock_gettime(CLOCK_MONOTONIC_RAW, &r1);
clock_gettime(CLOCK_REALTIME, &t1);
do {
clock_gettime(CLOCK_MONOTONIC_RAW, &r2);
clock_gettime(CLOCK_REALTIME, &t2);
} while (r2.tv_sec - r1.tv_sec < 1 || r2.tv_nsec < r1.tv_nsec);
long t = (t2.tv_sec-t1.tv_sec)*1000000000L + t2.tv_nsec-t1.tv_nsec;
long r = (r2.tv_sec-r1.tv_sec)*1000000000L + r2.tv_nsec-r1.tv_nsec;
printf("raw %ld.%09lds\n", r/1000000000, r%1000000000);
printf("ntp %ld.%09lds\n", t/1000000000, t%1000000000);
return 0;
}Code: Select all
raw 1.000001000s
ntp 0.999981266sCode: Select all
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/timex.h>
int main()
{
struct timex tx;
tx.modes = 0;
if (adjtimex(&tx) < 0) {
perror("adjtimex");
exit(1);
}
printf("current frequency: %ld\n", tx.freq);
return 0;
}Code: Select all
current frequency: -1235414
Thanks. I think I understand that now. I'll have a look at what adjtime says on my Pi to confirm the result.jojopi wrote:There are only two oscillators on board, and one of those is for the LAN. So I believe both the DMA clock and the kernel timer interrupt are ultimately derived from the same source and should be phase-locked.
The drift then is due to NTP correction. Rather than insert discontinuities for small adjustments, ntpd calls adjtimex(2) to tell the kernel to add slightly more or less than 1µs at each timer tick. You can see the difference by comparing elapsed times using clock_gettime() for both CLOCK_REALTIME and CLOCK_MONOTONIC_RAW:
...
(I am assuming you want to synchronise with the NTP clock. Otherwise just use CLOCK_MONOTONIC_RAW.)