Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixed imprecisse timing on SAMD21 (Arduino Zero) #21

Merged
merged 9 commits into from
Sep 29, 2023
11 changes: 11 additions & 0 deletions .arduino-ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
compile:
platforms:
- uno
- due
- zero
- leonardo
- m4
- esp32
#- esp8266
- mega2560
- nano_every
16 changes: 16 additions & 0 deletions .github/workflows/arduino-lint.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
name: arduino-lint

on: [push, pull_request]

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# https://github.com/actions/checkout
- uses: arduino/arduino-lint-action@v1
# https://github.com/arduino/arduino-lint-action
with:
library-manager: update
compliance: strict
13 changes: 13 additions & 0 deletions .github/workflows/arduino_ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
name: arduino_ci

on: [push, pull_request]

jobs:
arduino_ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# https://github.com/actions/checkout
- uses: Arduino-CI/action@stable-1.x
# https://github.com/marketplace/actions/arduino_ci
1 change: 1 addition & 0 deletions contributors.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
Naguissa - https://github.com/Naguissa
svlPanelMaker - https://github.com/svlPanelMaker - SAMD21 timing issue fix
Raf (Raffaele Rialdi) - https://github.com/raffaeler - ESP32 hardware timer (better precission than ticker library)
Daniel Mohr - https://github.com/daniel-mohr - SAMD21 timing issue fix
2 changes: 1 addition & 1 deletion library.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name=uTimerLib
version=1.7.1
version=1.7.2
author=Naguissa <naguissa@foroelectro.net>
maintainer=Naguissa <naguissa@foroelectro.net>
sentence=Tiny and cross-device compatible timer library
Expand Down
72 changes: 40 additions & 32 deletions src/hardware/uTimerLib.SAMD21.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,26 +52,41 @@

Prescaler:
Prescalers: GCLK_TC, GCLK_TC/2, GCLK_TC/4, GCLK_TC/8, GCLK_TC/16, GCLK_TC/64, GCLK_TC/256, GCLK_TC/1024
Base frequency: 84MHz
Base frequency: 48MHz

We will use TCC2, as there're some models with only 3 timers (regular models have 5 TCs)

REMEMBER! 16 bit counter!!!


Name Prescaler Freq Base Delay Overflow delay
GCLK_TC 1 48MHz 0,020833333us 1365,333333333us; 1,365333333333ms
GCLK_TC/2 2 24MHz 0,041666667us 2730,666666667us; 2,730666666667ms
GCLK_TC/4 4 12MHz 0,083333333us 5461,333333333us; 5,461333333333ms
GCLK_TC/8 8 6MHz 0,166666667us 10922,666666667us; 10,922666666667ms
GCLK_TC/16 16 3MHz 0,333333333us 21845,333333333us; 21,845333333333ms
GCLK_TC/64 64 750KHz 1,333333333us 87381,333311488us; 87,381333311488ms
GCLK_TC/256 256 187,5KHz 5,333333333us 349525,333311488us; 349,525333311488ms
GCLK_TC/1024 1024 46.875Hz 21,333333333us 1398101,333333333us; 1398,101333333333ms; 1,398101333333333s
GCLK_TC 1 48MHz ~0,020833333us 1365,3125us; 1,3653125ms
GCLK_TC/2 2 24MHz ~0,041666667us 2730,625us; 2,730625ms
GCLK_TC/4 4 12MHz ~0,083333333us 5461,25us; 5,46125ms
GCLK_TC/8 8 6MHz ~0,166666667us 10922,5us; 10,9225ms
GCLK_TC/16 16 3MHz ~0,333333333us 21845us; 21,845ms
GCLK_TC/64 64 750KHz ~1,333333333us 87380us; 87,380ms
GCLK_TC/256 256 187,5KHz ~5,333333333us 349520us; 349,52ms
GCLK_TC/1024 1024 46.875kHz ~21,333333333us 1398080us; 1398,08ms; 1,39808s

In general:
freq = 48 MHz / prescaler
base_delay = 1 / freq
overflow_delay = UINT16_MAX * base_delay

Will be using:
GCLK_TC/16 for us
GCLK_TC/1024 for s

GCLK_TC/16:
freq = 48 MHz / prescaler = 48 MHz / 16 = 3 MHz
base_delay = 1 / freq = 1 / 3e6 s = 1/3 us ~= 0.333333333 us
overflow_delay = UINT16_MAX * base_delay = 65535 / 3 us = 21845 us

GCLK_TC/1024:
freq = 48 MHz / prescaler = 48 MHz / 1024 = 46.875 kHz = 46875 Hz
base_delay = 1 / freq = 1 / 46875 s = ~= 21.333333333us
overflow_delay = UINT16_MAX * base_delay = 65535 / 46875 s = 1.39808 s
*/

// Enable clock for TC
Expand All @@ -82,23 +97,23 @@
_TC->CTRLA.reg &= ~TC_CTRLA_ENABLE;
while (_TC->STATUS.bit.SYNCBUSY == 1); // sync

// Set Timer counter Mode to 16 bits + Set TC as normal Normal Frq + Prescaler: GCLK_TC/16
_TC->CTRLA.reg |= (TC_CTRLA_MODE_COUNT16 + TC_CTRLA_WAVEGEN_NFRQ + TC_CTRLA_PRESCALER_DIV16);
// Set Timer counter Mode to 16 bits + Set TC as normal Match Frq + Prescaler: GCLK_TC/16
_TC->CTRLA.reg |= (TC_CTRLA_MODE_COUNT16 + TC_CTRLA_WAVEGEN_MFRQ + TC_CTRLA_PRESCALER_DIV16);
while (_TC->STATUS.bit.SYNCBUSY == 1); // sync

if (us > 21845) {
__overflows = _overflows = us / 21845.333333333;
__remaining = _remaining = ((us - (21845.333333333 * _overflows)) / 0.333333333) + 0.5; // +0.5 is same as round
__overflows = _overflows = us / 21845.0;
__remaining = _remaining = (us - (21845 * _overflows)) * 3 - 1;
} else {
__overflows = _overflows = 0;
__remaining = _remaining = (us / 0.333333333) + 0.5; // +0.5 is same as round
__remaining = _remaining = us * 3 - 1;
}

if (__overflows == 0) {
_loadRemaining();
_remaining = 0;
} else {
_TC->CC[0].reg = 65535;
_TC->CC[0].reg = UINT16_MAX;
_TC->INTENSET.reg = 0; // disable all interrupts
_TC->INTENSET.bit.OVF = 1; // enable overfollow
// Skip: while (_TC->STATUS.bit.SYNCBUSY == 1); // sync
Expand Down Expand Up @@ -126,7 +141,7 @@
}

/*
GCLK_TC/1024 1024 46.875Hz 21,333333333us 1398101,333333333us; 1398,101333333333ms; 1,398101333333333s
GCLK_TC/1024 1024 46.875kHz ~21,333333333us 1398080us; 1398,08ms; 1,39808s
*/

// Enable clock for TC
Expand All @@ -137,23 +152,23 @@
_TC->CTRLA.reg &= ~TC_CTRLA_ENABLE;
while (_TC->STATUS.bit.SYNCBUSY == 1); // sync

// Set Timer counter Mode to 16 bits + Set TC as normal Normal Frq + Prescaler: GCLK_TC/1024
_TC->CTRLA.reg |= (TC_CTRLA_MODE_COUNT16 + TC_CTRLA_WAVEGEN_NFRQ + TC_CTRLA_PRESCALER_DIV1024);
// Set Timer counter Mode to 16 bits + Set TC as normal Match Frq + Prescaler: GCLK_TC/1024
_TC->CTRLA.reg |= (TC_CTRLA_MODE_COUNT16 + TC_CTRLA_WAVEGEN_MFRQ + TC_CTRLA_PRESCALER_DIV1024);
while (_TC->STATUS.bit.SYNCBUSY == 1); // sync

if (s > 1) {
__overflows = _overflows = s / 1.398101333333333;
__remaining = _remaining = ((s - (1.398101333333333 * _overflows)) / 0.000021333333333) + 0.5; // +0.5 is same as round
__overflows = _overflows = s / 1.39808;
__remaining = _remaining = ((s * 100000) % 139808) * 480 / 1024 - 1; // for integer s this is always an integer
} else {
__overflows = _overflows = 0;
__remaining = _remaining = (s / 0.000021333333333) + 0.5; // +0.5 is same as round
__remaining = _remaining = s * 46875 - 1;
}

if (__overflows == 0) {
_loadRemaining();
_remaining = 0;
} else {
_TC->CC[0].reg = 65535;
_TC->CC[0].reg = UINT16_MAX;
_TC->INTENSET.reg = 0; // disable all interrupts
_TC->INTENSET.bit.OVF = 1; // enable overfollow
// Skip: while (_TC->STATUS.bit.SYNCBUSY == 1); // sync
Expand All @@ -174,7 +189,6 @@
* Note: This is device-dependant
*/
void uTimerLib::_loadRemaining() {
_TC->COUNT.reg = 0; // Reset to 0
_TC->CC[0].reg = _remaining;
_TC->INTENSET.reg = 0; // disable all interrupts
_TC->INTENSET.bit.MC0 = 1; // enable compare match to CC0
Expand Down Expand Up @@ -217,28 +231,22 @@
if (_type == UTIMERLIB_TYPE_TIMEOUT) {
clearTimer();
} else if (_type == UTIMERLIB_TYPE_INTERVAL) {
if (__overflows == 0) {
_remaining = __remaining;
_loadRemaining();
_remaining = 0;
} else {
if (__overflows != 0) {
_overflows = __overflows;
_remaining = __remaining;

_TC->COUNT.reg = 0; // Reset to 0
_TC->INTENSET.reg = 0; // disable all interrupts
_TC->INTENSET.bit.OVF = 0; // enable overfollow
_TC->INTENSET.bit.MC0 = 1; // disable compare match to CC0
_TC->CC[0].reg = 65535;
_TC->CC[0].reg = UINT16_MAX;
}
}
_cb();
} else if (_overflows > 0) { // Reload for SAMD21
_TC->COUNT.reg = 0; // Reset to 0
_TC->INTENSET.reg = 0; // disable all interrupts
_TC->INTENSET.bit.OVF = 0; // enable overfollow
_TC->INTENSET.bit.MC0 = 1; // disable compare match to CC0
_TC->CC[0].reg = 65535;
_TC->CC[0].reg = UINT16_MAX;
}
}

Expand Down