8000 ESP32 PCNT: Add Counter and Encoder. · IhorNehrutsa/micropython@87e4b65 · GitHub
[go: up one dir, main page]

Skip to content

Commit 87e4b65

Browse files
committed
ESP32 PCNT: Add Counter and Encoder.
Just rebase to Version 1.19.xx from micropython#6639
1 parent 2d406f2 commit 87e4b65

File tree

14 files changed

+1160
-1
lines changed

14 files changed

+1160
-1
lines changed

docs/esp32/general.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,12 @@ For your convenience, some of technical specifications are provided below:
5050
* SPI: 4 SPI interfaces (one used for FlashROM)
5151
* I2C: 2 I2C (bitbang implementation available on any pins)
5252
* I2S: 2
53+
* CAN bus: 1
5354
* ADC: 12-bit SAR ADC up to 18 channels
5455
* DAC: 2 8-bit DACs
56+
* PCNT: up to 8 channels
57+
* PWM: up to 16 channels
58+
* MCPWM: up to 2 channels
5559
* RMT: 8 channels allowing accurate pulse transmit/receive
5660
* Programming: using BootROM bootloader from UART - due to external FlashROM
5761
and always-available BootROM bootloader, the ESP32 is not brickable

docs/esp32/img/quad.png

88.3 KB
Loading

docs/esp32/pcnt.rst

Lines changed: 275 additions & 0 deletions
7802
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
PCNT - Counter and Encoder
2+
==========================
3+
4+
The Counter and Encoder use the ESP32 Pulse Counter (PCNT) hardware peripheral,
5+
see Espressif's `ESP-IDF Pulse Counter documentation.
6+
<https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/pcnt.html>`_
7+
8+
For the counter not to miss any pulses, the pulse duration should be longer than one ESP32 APB_CLK cycle (1 / 80 MHz = 12.5 ns).
9+
The pulses are sampled on the edges of the APB_CLK clock and may be missed if fall between the edges.
10+
With ideal input signal maximum frequency of measured pulses is APB_CLK / 2 = 80 MHz / 2 = 40 MHz.
11+
12+
The inputs have optional filters that can be used to discard unwanted glitches in the signal.
13+
The length of ignored pulses is provided in APB_CLK clock cycles.
14+
* Note: Filter value is a 10-bit value, so the maximum filter value should be limited to 1023.
15+
Maximum filtered glitches delay is 1023 * 12.5 ns = 12.7875 us.
16+
Big filter make cutbacks the input frequency: 1 / (12.7875 us * 2) = 39.1 kHz.
17+
* Note: Do not neglect circuitry methods to reduce noise (right powering and grounding, filtering, shielding,
18+
short conductors, twisted pair cable, differential signals, etc.).
19+
20+
There is only one interrupt for the peripheral, and that is managed inside the module.
21+
The user has no interrupt interface, and no interrupts are generated on each pulse.
22+
Interrupts arrive when the 16-bit hardware counter buffer overflows, so this module has a tiny interrupt footprint
23+
while providing support for up to 8 simultaneous counters (Encoder or Counter objects).
24+
25+
.. _esp32_machine.Counter:
26+
27+
Counter
28+
=======
29+
30+
The Pulse Counter service.
31+
32+
Constructor
33+
-----------
34+
35+
.. class:: Counter(id, src=None, \*, edge=Counter.RISING, direction=Counter.UP, filter_ns=0, scale=1)
36+
37+
The Counter starts to count immediately. Filtering is disabled.
38+
39+
- *id*. Values of *id* depend on a particular port and its hardware.
40+
Values 0, 1, etc. are commonly used to select hardware block #0, #1, etc.
41+
42+
- *src* is the pulse input :ref:`machine.Pin <machine.Pin>` to be monitored.
43+
*src* is required in the constructor.
44+
45+
- *direction* specifies the direction to count. Values for this include the constants
46+
47+
- Counter.UP - (default value) and
48+
- Counter.DOWN to control the direction by software or
49+
- :ref:`machine.Pin <machine.Pin>` object to control the direction externally. If ``Pin.value()``:
50+
F438 - 0 - count down,
51+
- 1 - count up.
52+
53+
- *edge* specifies which edges of the input signal will be counted by Counter:
54+
55+
- Counter.RISING : raise edges,
56+
- Counter.FALLING : fall edges,
57+
- Counter.RISING | Counter.FALLING : both edges.
58+
59+
- *filter_ns* specifies a ns-value for the minimal time a signal has to be stable
60+
at the input to be recognized. The largest value is 12787ns (1023 * 1000000000 / APB_CLK_FREQ).
61+
The default is 0 – no filter.
62+
63+
- *scale* sets the scale value. The default value is 1. You may treat the scale
64+
factor as **click per count**, **mm per count**, **inch per count** etc.
65+
66+
Methods
67+
-------
68+
69+
.. method:: Counter.init(*, src, ...)
70+
71+
Modify the settings of the Counter object. See the **Constructor** for details about the parameters.
72+
73+
.. method:: Counter.deinit()
74+
75+
Stops the Counter, disables interrupts and releases hardware resources used by the counter.
76+
A Soft Reset involve deinitializing all Encoder objects.
77+
78+
.. method:: Counter.filter([value])
79+
80+
Get, and optionally set, the filter value. 0 disable filtering.
81+
82+
.. method:: Counter.value([value])
83+
84+
Get, and optionally set, the counter *value* as a signed 64-bit integer.
85+
86+
.. method:: Counter.scaled([value])
87+
88+
Get, and optionally set, the current scaled value of the Counter as a float.
89+
90+
Pseudocode is::
91+
92+
def scaled(self, scaled=None):
93+
_scaled = self._value * self.scale
94+
if scaled is not None:
95+
self._value = round(scaled / self.scale)
96+
return _scaled
97+
98+
.. method:: Counter.irq(handler=None, trigger=Counter.IRQ_MATCH1 | Counter.IRQ_MATCH2 | Counter.IRQ_ZERO, value=0)
99+
100+
-*handler* specifies a function is called when the respective *trigger* event happens.
101+
The callback function *handler* receives a single argument, which is the Counter object.
102+
All events may share the same callback or have separate callbacks.
103+
The callback will be disabled, when called with handler=None. Counter.irq() disable all callbacks.
104+
The event which triggers the callback can be identified with the ``Counter.status()`` method.
105+
The Counter object which triggers the callback can be identified with the ``Counter.id()`` method.
106+
107+
-*trigger* events may be:
108+
109+
- Counter.IRQ_MATCH1 triggered when the counter matches the match1 value.
110+
- Counter.IRQ_MATCH2 triggered when the counter matches the match2 value.
111+
- Counter.IRQ_ZERO triggered when the counter matches the 0.
112+
113+
The default is - trigger=Counter.IRQ_MATCH1 | Counter.IRQ_MATCH2 | Counter.IRQ_ZERO.
114+
The events are triggered when the counter value and match value are identical, but
115+
callbacks have always a latency.
116+
117+
- *value* sets a counter match1/match2 value. When the counter matches these values,
118+
a callback function can be called. They are 0 by default.
119+
120+
Attention: ``Counter.irq()`` resets counter to 0.
121+
122+
.. method:: Counter.status()
123+
124+
Returns the event status flags of the recent handled Counter interrupt as a bitmap.
125+
126+
.. method:: Counter.id()
127+
128+
Returns id number.
129+
130+
.. method:: Counter.pause()
131+
132+
.. method:: Counter.resume()
133+
134+
Constants
135+
---------
136+
137+
.. data:: Counter.UP
138+
Counter.DOWN
139+
140+
Selects the counter direction.
141+
142+
.. data:: Counter.RISING
143+
Counter.FALLING
144+
145+
Selects the counted edges.
146+
147+
.. data:: Counter.IRQ_MATCH1
148+
Counter.IRQ_MATCH2
149+
Counter.IRQ_ZERO
150+
151+
Selects callback triggers.
152+
153+
::
154+
155+
from machine import Counter, Pin
156+
157+
try:
158+
def irq_handler(self):
159+
print('irq_handler()', self.id(), self.status(), self.value())
160+
161+
cnt = Counter(0, src=Pin(17, mode=Pin.IN), direction=Pin(16, mode=Pin.IN))
162+
163+
cnt.pause()
164+
flt = cnt.filter() # return current filter value.
165+
cnt.filter(10_000) # filter delay is 10ms
166+
c = cnt.value(0) # get current counter value, set the counter value to 0
167+
cnt.irq(irq_handler, Counter.IRQ_ZERO) # set irq handler
168+
cnt.resume()
169+
170+
_c = None
171+
while True:
172+
c = cnt.value() # get the counter value
173+
if _c != c:
174+
_c = c
175+
print('Counter =', c)
176+
finally:
177+
cnt.deinit() # free the input pins and counter.
178+
179+
180+
.. _esp32_machine.Encoder:
181+
182+
Encoder
183+
=======
184+
185+
This class provides a Quadrature Incremental Encoder service.
186+
See `Quadrature encoder outputs.
187+
<https://en.wikipedia.org/wiki/Incremental_encoder#Quadrature_outputs>`_
188+
189+
.. image:: img/quad.png
190+
:width: 397px
191+
192+
Constructor
193+
-----------
194+
195+
.. class:: Encoder(id, phase_a=None, phase_b=None, \*, x124=4, filter_ns=0, scale=1, match1=0, match2=0)
196+
197+
The Encoder starts to count immediately. Filtering is disabled.
198+
199+
- *id*. Values of *id* depend on a particular port and its hardware.
200+
Values 0, 1, etc. are commonly used to select hardware block #0, #1, etc.
201+
202+
- *phase_a*, *phase_b* are input pins :ref:`machine.Pin <machine.Pin>` for monitoring of quadrature encoder pulses.
203+
They are required in the constructor.
204+
205+
- *x124* is a hardware multiplier, possible values is 1, 2, 4. The default value is 4.
206+
More info in `Quadrature decoder state table <https://en.wikipedia.org/wiki/Incremental_encoder#Quadrature_decoder>`_.
207+
When more Encoder resolution is needed, it is possible for the encoder to count the leading
208+
and trailing edges of the quadrature encoder’s pulse train from one channel,
209+
which doubles (x2) the number of pulses. Counting both leading and trailing edges
210+
of both channels (A and B channels) of a quadrature encoder will quadruple (x4) the number of pulses:
211+
212+
- 1 - count the leading(or trailing) edges from one phase channel.
213+
- 2 - count the leading and trailing edges from one phase channel.
214+
- 4 - count both leading and trailing edges of both phase channels.
215+
216+
- *scale* sets the scale value. The default value is 1. You may treat the scale
217+
factor as **click per impulse**, **revolution per impulse**, **angle per impulse** etc.
218+
Hint: Set scale factor to 1/4 to balance the multiplier x124=4.
219+
220+
These keywords are the same as the Counter keywords, see above:
221+
- *filter_ns*
222+
- *match1*
223+
- *match2*
224+
225+
Methods
226+
-------
227+
228+
.. method:: Encoder.init(*, phase_a, ...)
229+
230+
Modify the settings of the Encoder object. See the **Constructor** for details about the parameters.
231+
232+
The Encoder has the same methods as the Counter and differs only
233+
in the constructor and internal hardware PCNT initialization.
234+
235+
Constants
236+
---------
237+
238+
.. data:: Encoder.IRQ_MATCH1
239+
Encoder.IRQ_MATCH2
240+
Encoder.IRQ_ZERO
241+
242+
Selects callback triggers.
243+
244+
::
245+
246+
from machine import Encoder, Pin
247+
248+
try:
249+
n = 0
250+
def irq_handler1(self):
251+
n -= 1
252+
print('irq_handler1()', self.id(), self.value(), n)
253+
254+
def irq_handler2(self):
255+
n += 1
256+
print('irq_handler2()', self.id(), self.value(), n)
257+
258+
enc = Encoder(0, phase_a=Pin(17, mode=Pin.IN), phase_b=Pin(16, mode=Pin.IN), match1=-1000, match2=1000)
259+
260+
enc.pause()
261+
flt = enc.filter() # return current filter value.
262+
enc.filter(10_000) # filter delay is 10ms
263+
c = enc.value(0) # get current encoder value, set the encoder value to 0
264+
enc.irq(irq_handler1, Encoder.IRQ_MATCH1) # set irq handler
265+
enc.irq(irq_handler2, Encoder.IRQ_MATCH2) # set irq handler
266+
enc.resume()
267+
268+
_c = None
269+
while True:
270+
c = enc.value() # get the encoder value
271+
if _c != c:
272+
_c = c
273+
print('Encoder =', c)
274+
finally:
275+
enc.deinit() # f 10000 ree the input pins and encoder.

docs/esp32/quickref.rst

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,45 @@ The RMT is ESP32-specific and allows generation of accurate digital pulses with
610610
# The channel resolution is 100ns (1/(source_freq/clock_div)).
611611
r.write_pulses((1, 20, 2, 40), 0) # Send 0 for 100ns, 1 for 2000ns, 0 for 200ns, 1 for 4000ns
612612

613+
Counter (Pulse/Edge Counter)
614+
----------------------------
615+
616+
The Counter counts the number of rising and/or falling edges on any input pin.
617+
It is a 64-bit signed hardware-based counter. Counter and Encoder share the same ESP32 PCNT hardware peripheral,
618+
the total summary available number of Counter and Encoder is up to 8.
619+
620+
See :ref:`machine.Counter <esp32_machine.Counter>` for details. Simplest usage is::
621+
622+
from machine import Pin, Counter
623+
624+
cnt = Counter(0, src=Pin(17, mode=Pin.IN), direction=Pin(16, mode=Pin.IN))
625+
_v = None
626+
while True:
627+
v = cnt.value() # get 64-bit signed value
628+
if _v != v:
629+
_v = v
630+
print('Counter value:', v)
631+
632+
Encoder (Quadrature Incremental Encoder)
633+
----------------------------------------
634+
635+
The Encoder counts the quadrature-encoded pulses on pair of input pins (two square wave signals A and B with
636+
~50% duty cycle and ~90-degree phase difference between them).
637+
It is a 64-bit signed hardware-based counter. Counter and Encoder share the same ESP32 PCNT hardware peripheral,
638+
the total summary available number of Counter and Encoder is up to 8.
639+
640+
See :ref:`machine.Encoder <esp32_machine.Encoder>` for details. Simplest usage is::
641+
642+
from machine import Pin, Encoder
643+
644+
enc = Encoder(0, phase_a=Pin(17, mode=Pin.IN), phase_b=Pin(16, mode=Pin.IN))
645+
_v = None
646+
while True:
647+
v = enc.value() # get 64-bit signed value
648+
if _v != v:
649+
_v = v
650+
print('Encoder value:', v)
651+
613652
OneWire driver
614653
--------------
615654

docs/esp32/tutorial/index.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ to `<https://www.python.org>`__.
1616

1717
.. toctree::
1818
:maxdepth: 1
19-
:numbered:
2019

2120
intro.rst
2221
pwm.rst

ports/esp32/boards/GENERIC_C3/mpconfigboard.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@
66
#define MICROPY_HW_ENABLE_SDCARD (0)
77
#define MICROPY_PY_MACHINE_DAC (0)
88
#define MICROPY_PY_MACHINE_I2S (0)
9+
#define MICROPY_PY_MACHINE_PCNT (0)

ports/esp32/boards/GENERIC_C3_USB/mpconfigboard.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@
66
#define MICROPY_HW_ENABLE_SDCARD (0)
77
#define MICROPY_PY_MACHINE_DAC (0)
88
#define MICROPY_PY_MACHINE_I2S (0)
9+
#define MICROPY_PY_MACHINE_PCNT (0)

0 commit comments

Comments
 (0)
0