-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Description
Description
While debugging LoRaWAN I discovered there is a systematic error while using timer_set. When setting a timer to T units of time, it actually gets triggered in T + X/100*T (with X a board specific drift).
This issue is at least present on samr21-xpro (0.48% drift) and b-l072z-lrwan1 (1% drift).
This is causing some problems in LoRaWAN. For the Join Request procedure, the first reception window should be opened 5 seconds right after there's a TX done event. In b-l072z-lrwan1 the RX window is opened 47 ms later than expected. This explains why this compensation works ok on this board but produces problems in others (@danpetry I know you were having issues with this).
Steps to reproduce the issue
I ran the following test (samr21-xpro, 32bit timer) which produces timer cycles (with timeout increasing linearly, 200ms step) and measured the LED pin with a logic analyzer:
#include <stdio.h>
#include "xtimer.h"
#include "timex.h"
#include "periph/timer.h"
#include "random.h"
#include "msg.h"
#include "thread.h"
#define T 200000
#define TIMER TIMER_DEV(1)
#define TIMER_MAX 0xffffffff
#define TIMER_OFFSET 0xffff
#define LED_OFF LED0_OFF
#define LED_TOGGLE LED0_TOGGLE
unsigned int i=T;
static void cb(void *arg, int chan)
{
(void) arg;
(void) chan;
LED_TOGGLE;
if (i+T < TIMER_MAX)
timer_set(TIMER, 0, i);
i += T;
}
int main(void)
{
// Init timer with 1 MHz
timer_init(TIMER, 1000000ul, cb, NULL);
LED_OFF;
timer_set(TIMER, 0, TIMER_OFFSET);
return 0;
}
Expected results
The actual timeout timer for the n cycle (e.g how long does the nth cycle take) in milliseconds would be:
t'[n] = TIMER_OVERHEAD + n*200
Here TIMER_OVERHEAD is the intrinsic delay of timer_set (ISR + callback served). Since TIMER_OVERHEAD should be constant:
t''[n] = diff(t'[n]) = 200
So for a real measurement, I would expect diff(t[n]) to be centered in 200 and have some values around.
Actual results
I got the result of the logic analyzer here. It contains the timestamp when there was a flank. Then, I ran the following using python's pandas to extract the min, avg and max value of the sample's diff(t[n]):
import pandas as pd
data = pd.read_csv("results/untitled.csv")
data = data["Time[s]"][3:]
p_data = data.diff().diff()*1000 #Get derivative of cycles timeout timer in ms
p_data.min(), p_data.mean(), p_data.max()
Got (200.57575000000583, 200.9604469696972, 201.59066666666803)
So, diff(t[n]) is slightly to the right. There's a deviation of ((200.9604469696972 - 200)/200 * 100) = 0.48%, as mentioned before.
TL;DR
This code toggles the LED every 10 seconds:
#include <stdio.h>
#include "timex.h"
#include "periph/timer.h"
#define T 10000000
#define TIMER TIMER_DEV(1)
#define TIMER_MAX 0xffffffff
#define TIMER_OFFSET 0xffff
#define LED_OFF LED0_OFF
#define LED_TOGGLE LED0_TOGGLE
unsigned int i=T;
static void cb(void *arg, int chan)
{
(void) arg;
(void) chan;
LED_TOGGLE;
if (i+T < TIMER_MAX)
timer_set(TIMER, 0, T);
//i += T;
}
int main(void)
{
//int interval = 0;
timer_init(TIMER, 1000000ul, cb, NULL);
LED_OFF;
timer_set(TIMER, 0, T);
return 0;
}
Here is the output of the logic analyzer when I ran this snippet on samr21-xpro. Then, I calculated the length between LED toggles:
Time[s] Channel 0
2 10.051050 1.0
3 10.050970 -1.0
4 10.050887 1.0
5 10.050525 -1.0
6 10.050817 1.0
Greater than 10 (avg: 10.05085). Drift here is:
((10.05085-10)/10) * 100= 0.51 %
Sort of consistent with the value reported before for samr21-xpro