-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathadafruit_ticks.py
141 lines (110 loc) · 5.75 KB
/
adafruit_ticks.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
# SPDX-FileCopyrightText: Copyright (c) 2021 Jeff Epler for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
`adafruit_ticks`
================================================================================
Work with intervals and deadlines in milliseconds
* Author(s): Jeff Epler
Implementation Notes
--------------------
**Software and Dependencies:**
* Adafruit CircuitPython firmware for the supported boards:
https://github.com/adafruit/circuitpython/releases
"""
# imports
from micropython import const
__version__ = "0.0.0+auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ticks.git"
_TICKS_PERIOD = const(1 << 29)
_TICKS_MAX = const(_TICKS_PERIOD - 1)
_TICKS_HALFPERIOD = const(_TICKS_PERIOD // 2)
# Get the correct implementation of ticks_ms. There are three possibilities:
#
# - supervisor.ticks_ms is present. This will be the case starting in CP7.0
#
# - time.ticks_ms is present. This is the case for MicroPython & for the "unix
# port" of CircuitPython, used for some automated testing.
#
# - time.monotonic_ns is present, and works. This is the case on most
# Express boards in CP6.x, and most host computer versions of Python.
#
# - Otherwise, time.monotonic is assumed to be present. This is the case
# on most non-express boards in CP6.x, and some old host computer versions
# of Python.
#
# Note that on microcontrollers, this time source becomes increasingly
# inaccurate when the board has not been reset in a long time, losing the
# ability to measure 1ms intervals after about 1 hour, and losing the
# ability to meausre 128ms intervals after 6 days. The only solution is to
# either upgrade to a version with supervisor.ticks_ms, or to switch to a
# board with time.monotonic_ns.
try:
from supervisor import ticks_ms # pylint: disable=unused-import
except (ImportError, NameError):
import time
if _ticks_ms := getattr(time, "ticks_ms", None):
def ticks_ms() -> int:
"""Return the time in milliseconds since an unspecified moment,
wrapping after 2**29ms.
The wrap value was chosen so that it is always possible to add or
subtract two `ticks_ms` values without overflow on a board without
long ints (or without allocating any long integer objects, on
boards with long ints).
This ticks value comes from a low-accuracy clock internal to the
microcontroller, just like `time.monotonic`. Due to its low
accuracy and the fact that it "wraps around" every few days, it is
intended for working with short term events like advancing an LED
animation, not for long term events like counting down the time
until a holiday."""
return _ticks_ms() & _TICKS_MAX # pylint: disable=not-callable
else:
try:
from time import monotonic_ns as _monotonic_ns
_monotonic_ns() # Check that monotonic_ns is usable
def ticks_ms() -> int:
"""Return the time in milliseconds since an unspecified moment,
wrapping after 2**29ms.
The wrap value was chosen so that it is always possible to add or
subtract two `ticks_ms` values without overflow on a board without
long ints (or without allocating any long integer objects, on
boards with long ints).
This ticks value comes from a low-accuracy clock internal to the
microcontroller, just like `time.monotonic`. Due to its low
accuracy and the fact that it "wraps around" every few days, it is
intended for working with short term events like advancing an LED
animation, not for long term events like counting down the time
until a holiday."""
return (_monotonic_ns() // 1_000_000) & _TICKS_MAX
except (ImportError, NameError, NotImplementedError):
from time import monotonic as _monotonic
def ticks_ms() -> int:
"""Return the time in milliseconds since an unspecified moment,
wrapping after 2**29ms.
The wrap value was chosen so that it is always possible to add or
subtract two `ticks_ms` values without overflow on a board without
long ints (or without allocating any long integer objects, on
boards with long ints).
This ticks value comes from a low-accuracy clock internal to the
microcontroller, just like `time.monotonic`. Due to its low
accuracy and the fact that it "wraps around" every few days, it is
intended for working with short term events like advancing an LED
animation, not for long term events like counting down the time
until a holiday."""
return int(_monotonic() * 1000) & _TICKS_MAX
def ticks_add(ticks: int, delta: int) -> int:
"Add a delta to a base number of ticks, performing wraparound at 2**29ms."
if -_TICKS_HALFPERIOD < delta < _TICKS_HALFPERIOD:
return (ticks + delta) % _TICKS_PERIOD
raise OverflowError("ticks interval overflow")
def ticks_diff(ticks1: int, ticks2: int) -> int:
"""Compute the signed difference between two ticks values,
assuming that they are within 2**28 ticks"""
diff = (ticks1 - ticks2) & _TICKS_MAX
diff = ((diff + _TICKS_HALFPERIOD) & _TICKS_MAX) - _TICKS_HALFPERIOD
return diff
def ticks_less(ticks1: int, ticks2: int) -> bool:
"""Return true if ticks1 is before ticks2 and false otherwise,
assuming that they are within 2**28 ticks"""
return ticks_diff(ticks1, ticks2) < 0