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
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=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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy