8000 esp32/modules/machine.py: Add Counter and Encoder shims. · fluispotter/micropython@0d2aa1c · GitHub
[go: up one dir, main page]

Skip to content

Commit 0d2aa1c

Browse files
jonathanhoggkapetan
authored andcommitted
esp32/modules/machine.py: Add Counter and Encoder shims.
Add thin Python shims around `esp32.PCNT` to implement the `machine.Counter` and `machine.Encoder` classes (as per micropython#8072 API).
1 parent 53c1269 commit 0d2aa1c

File tree

1 file changed

+98
-0
lines changed

1 file changed

+98
-0
lines changed

ports/esp32/modules/machine.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
from umachine import *
2+
3+
import esp32
4+
5+
if hasattr(esp32, "PCNT"):
6+
7+
class Counter:
8+
_INSTANCES = {}
9+
_PCNT = esp32.PCNT
10+
RISING = 1
11+
FALLING = 2
12+
UP = _PCNT.INCREMENT
13+
DOWN = _PCNT.DECREMENT
14+
15+
def __new__(cls, unit_id, *args, **kwargs):
16+
self = cls._INSTANCES.get(unit_id)
17+
if self is None:
18+
self = super(Counter, cls).__new__(cls)
19+
self._pcnt = self._PCNT(minimum=-32000, maximum=32000)
20+
self._offset = 0
21+
cls._INSTANCES[unit_id] = self
22+
if args or kwargs:
23+
self.init(*args, **kwargs)
24+
return self
25+
26+
def __init__(self, unit_id, *args, **kwargs):
27+
pass 10000
28+
29+
def init(self, *args, **kwargs):
30+
self._init(*args, **kwargs)
31+
self._pcnt.irq(self._wrap, self._PCNT.IRQ_MINIMUM | self._PCNT.IRQ_MAXIMUM)
32+
self._pcnt.start()
33+
34+
def deinit(self):
35+
self._pcnt.deinit()
36+
self._pcnt.init(minimum=-32000, maximum=32000)
37+
self._offset = 0
38+
39+
def value(self, value=None):
40+
# This loop deals with the possibility that a PCNT wrap occurs
41+
# between retrieving self._offset and self._pcnt.value()
42+
while True:
43+
offset = self._offset
44+
current = self._pcnt.value()
45+
if self._offset == offset:
46+
break
47+
current += offset
48+
if value is not None:
49+
# In-place addition for atomicity
50+
self._offset += value - current
51+
return current
52+
53+
def _wrap(self, mask):
54+
if mask & self._PCNT.IRQ_MINIMUM:
55+
self._offset -= 32000
56+
elif mask & self._PCNT.IRQ_MAXIMUM:
57+
self._offset += 32000
58+
59+
def _init(self, src, edge=RISING, direction=UP, filter_ns=0):
60+
self._pcnt.init(
61+
pin=src,
62+
rising=direction if edge & self.RISING else self._PCNT.IGNORE,
63+
falling=direction if edge & self.FALLING else self._PCNT.IGNORE,
64+
filter=min(max(0, filter_ns * 80 // 1000), 1023),
65+
)
66+
67+
class Encoder(Counter):
68+
_INSTANCES = {}
69+
70+
def _init(self, phase_a, phase_b, filter_ns=0, phases=4):
71+
if phases not in (1, 2, 4):
72+
raise ValueError("phases")
73+
self._pcnt.init(
74+
pin=phase_a,
75+
falling=self._PCNT.INCREMENT,
76+
rising=self._PCNT.DECREMENT,
77+
mode_pin=phase_b,
78+
mode_low=self._PCNT.HOLD if phases == 1 else self._PCNT.REVERSE,
79+
filter=min(max(0, filter_ns * 80 // 1000), 1023),
80+
)
81+
if phases == 4:
82+
self._pcnt.init(
83+
channel=1,
84+
pin=phase_b,
85+
falling=self._PCNT.DECREMENT,
86+
rising=self._PCNT.INCREMENT,
87+
mode_pin=phase_a,
88+
mode_low=self._PCNT.REVERSE,
89+
)
90+
else:
91+
self._pcnt.init(channel=1, pin=None, rising 5ACA =self._PCNT.IGNORE)
92+
self._phases = phases
93+
94+
def phases(self):
95+
return self._phases
96+
97+
98+
del esp32

0 commit comments

Comments
 (0)
0