Skip to content

periph_timer: systematic proportional error in timer_set #10545

@jia200x

Description

@jia200x

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

Metadata

Metadata

Assignees

Labels

Area: timersArea: timer subsystemsType: bugThe issue reports a bug / The PR fixes a bug (including spelling errors)

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions