Naguissa / uTimerLib

Arduino tiny and cross-device compatible timer library
https://www.foroelectro.net/electronica-digital-microcontroladores-f8/utimerlib-libreria-arduino-para-eventos-temporizad-t191.html
GNU Lesser General Public License v3.0
20 stars 9 forks source link

imprecisse timing on SAMD21 (Arduino Zero) #20

Closed daniel-mohr closed 9 months ago

daniel-mohr commented 9 months ago

cf. #18

@Naguissa Sorry, to open again this problem.

But it is not solved and I can provide you a solution.

I had a deeper look in your code and find the reason an a solution.

My example in #18 was not correct and has a few typos.

Simple and dirty testing script would be:

#include "uTimerLib.h"

#define TRIGGERUS 1000000
#define CALCIT 256

#define SERIAL_BAUD_RATE 115200 // baud = bits per second
#define SERIAL_TIMEOUT 1000 // timeout in 1e-6 seconds

unsigned long start_microstime = 0;
unsigned long stop_microstime = 0;
volatile uint8_t it = 0;
volatile unsigned long duration = 0;

void handler (void) {
  if (it++ == 0) {
    stop_microstime = micros();
    duration = stop_microstime - start_microstime;
    start_microstime = stop_microstime;
  }
}

void setup() {
  Serial.begin(SERIAL_BAUD_RATE);
  Serial.setTimeout(SERIAL_TIMEOUT);
  while (!Serial);
  Serial.println("######");
  //TimerLib.setInterval_us(handler, TRIGGERUS);
  TimerLib.setInterval_s(handler, 1);
  delay(3*TRIGGERUS/1000);
}

void loop() {
    delay(max(3, CALCIT*TRIGGERUS/1000));
    static unsigned long mtime;
    mtime = millis();
    Serial.print("mtime: ");
    Serial.print(mtime);
    Serial.print(" microstime_delta: ");
    Serial.print(((double) duration) / CALCIT);
    Serial.println(" us");
}

As a reference we can do the same with another library (my one):

#include <fast_samd21_tc3.h>

#define TRIGGERUS 1000000
#define CALCIT 256

#define SERIAL_BAUD_RATE 115200 // baud = bits per second
#define SERIAL_TIMEOUT 1000 // timeout in 1e-6 seconds

unsigned long start_microstime = 0;
unsigned long stop_microstime = 0;
volatile uint8_t it = 0;
volatile unsigned long duration = 0;

void TC3_Handler(void) {
  if (it++ == 0) {
    stop_microstime = micros();
    duration = stop_microstime - start_microstime;
    start_microstime = stop_microstime;
  }
  TC3->COUNT16.INTFLAG.bit.MC0 = 1; // clears the interrupt
}

void setup() {
  Serial.begin(SERIAL_BAUD_RATE);
  Serial.setTimeout(SERIAL_TIMEOUT);
  while (!Serial);
  Serial.println("######");
  fast_samd21_tc3_configure(TRIGGERUS);
  delay(3*TRIGGERUS/1000);
}

void loop() {
    delay(max(3, CALCIT*TRIGGERUS/1000));
    static unsigned long mtime;
    mtime = millis();
    Serial.print("mtime: ");
    Serial.print(mtime);
    Serial.print(" microstime_delta: ");
    Serial.print(((double) duration) / CALCIT);
    Serial.println(" us");
}

These small scripts allow to get a approximated measurement:

set value uTimerLib v1.7.1 fast_samd21_tc v0.2.3
1000000 us / 1 s 1000021.33 us 1000000.00 us
100000 us 100011.22 us 100000.00 us
10000 us 10002.66 us 10000.00 us
1000 us 1002.69 us 1000.00 us
10 us 12.66 us 10.00 us

The reason for the additional time in uTimerLib is _TC->COUNT.reg = 0;. This is at the moment set at a more or less random time. It depends on all the if clauses. And during running these if clauses some time is lost. Using TC_CTRLA_WAVEGEN_MFRQ instead of TC_CTRLA_WAVEGEN_NFRQ makes setting _TC->COUNT.reg = 0; unnecessary.

During reading your code I also find a few points which I tried to enhance.