Skip to content

Commit 87e4b65

Browse files
committed
ESP32 PCNT: Add Counter and Encoder.
Just rebase to Version 1.19.xx from #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
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+
- 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() # free 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)
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