Skip to content

Commit aa43ef2

Browse files
committed
Merge branch 'ESP32_PCNT_Encoder_Counter' into OWL_master_over_v1.26.1
2 parents ebb1e15 + 781f9a8 commit aa43ef2

File tree

14 files changed

+1300
-120
lines changed

14 files changed

+1300
-120
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: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
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, \*, direction=Counter.UP, _src=None, edge=Counter.RISING, filter_ns=0)
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+
- *_src* is the inverse pulse input :ref:`machine.Pin <machine.Pin>` to be monitored.
54+
If the *_src* keyword is present then the *direction* keyword does not matter.
55+
*src* and *_src* count in opposite directions, one in the UP direction
56+
and the other in the DOWN direction, i.e. as an incremental/decremental counter.
57+
58+
- *edge* specifies which edges of the input signal will be counted by Counter:
59+
60+
- Counter.RISING : raise edges,
61+
- Counter.FALLING : fall edges,
62+
- Counter.RISING | Counter.FALLING : both edges.
63+
64+
- *filter_ns* specifies a ns-value for the minimal time a signal has to be stable
65+
at the input to be recognized. The largest value is 12787ns (1023 * 1000000000 / APB_CLK_FREQ).
66+
The default is 0 – no filter.
67+
68+
Methods
69+
-------
70+
71+
.. method:: Counter.init(*, src, ...)
72+
73+
Modify the settings of the Counter object. See the **Constructor** for details about the parameters.
74+
75+
.. method:: Counter.deinit()
76+
77+
Stops the Counter, disables interrupts and releases hardware resources used by the counter.
78+
A Soft Reset involve deinitializing all Encoder objects.
79+
80+
.. method:: Counter.filter([value])
81+
82+
Get, and optionally set, the filter value. 0 disable filtering.
83+
84+
.. method:: Counter.value([value])
85+
86+
Get, and optionally set, the counter *value* as a signed 64-bit integer.
87+
Attention: Setting the counter brokes the IRQ_MATCH and IRQ_ZERO events.
88+
89+
.. method:: Counter.irq(handler=None, trigger=Counter.IRQ_MATCH | Counter.IRQ_ZERO, value=0)
90+
91+
-*handler* specifies a function is called when the respective *trigger* event happens.
92+
The callback function *handler* receives a single argument, which is the Counter object.
93+
All events may share the same callback or have separate callbacks.
94+
The callback will be disabled, when called with handler=None. Counter.irq() disable all callbacks.
95+
The event which triggers the callback can be identified with the ``Counter.status()`` method.
96+
The Counter object which triggers the callback can be identified with the ``Counter.id()`` method.
97+
98+
-*trigger* events may be:
99+
100+
- Counter.IRQ_MATCH triggered when the counter matches the match value.
101+
- Counter.IRQ_ZERO triggered when the counter matches the 0.
102+
- Counter.IRQ_ROLL_OVER triggered when the int16_t counter overloaded.
103+
- Counter.IRQ_ROLL_UNDER triggered when the int16_t counter underloaded.
104+
105+
The default is - trigger=Counter.IRQ_MATCH | Counter.IRQ_ZERO.
106+
The events are triggered when the counter value and match value are identical, but
107+
callbacks have always a latency.
108+
109+
- *value* sets a counter match value. When the counter matches these values,
110+
a callback function can be called. They are 0 by default.
111+
112+
Attention: ``Counter.irq()`` resets counter to 0.
113+
114+
.. method:: Counter.status()
115+
116+
Returns the event status flags of the recent handled Counter interrupt as a bitmap.
117+
118+
===== ==== ======================= =============================================================
119+
bit # mask trigger comment
120+
===== ==== ======================= =============================================================
121+
0 1 if zero event: 0 - when counting up, 1 - when counting down
122+
2 4 Counter.IRQ_MATCH match value event when counting up
123+
3 8 Counter.IRQ_MATCH match value event when counting down
124+
4 16 Counter.IRQ_ROLL_UNDER roll under event
125+
5 32 Counter.IRQ_ROLL_OVER roll over event
126+
6 64 Counter.IRQ_ZERO zero event
127+
===== ==== ======================= =============================================================
128+
129+
130+
.. method:: Counter.id()
131+
132+
Returns id number.
133+
134+
.. method:: Counter.pause()
135+
136+
.. method:: Counter.resume()
137+
138+
Constants
139+
---------
140+
141+
.. data:: Counter.UP
142+
Counter.DOWN
143+
144+
Selects the counter direction.
145+
146+
.. data:: Counter.RISING
147+
Counter.FALLING
148+
149+
Selects the counted edges.
150+
151+
.. data:: Counter.IRQ_MATCH
152+
Counter.IRQ_ZERO
153+
154+
Selects callback triggers.
155+
156+
::
157+
158+
from machine import Counter, Pin
159+
160+
try:
161+
def irq_handler(self):
162+
print('irq_handler()', self.id(), self.status(), self.value())
163+
164+
cnt = Counter(0, src=Pin(17, mode=Pin.IN), direction=Pin(16, mode=Pin.IN))
165+
166+
cnt.pause()
167+
flt = cnt.filter() # return current filter value.
168+
cnt.filter(10_000) # filter delay is 10ms
169+
c = cnt.value(0) # get current counter value, set the counter value to 0
170+
cnt.irq(irq_handler, Counter.IRQ_ZERO) # set irq handler
171+
cnt.resume()
172+
173+
_c = None
174+
while True:
175+
c = cnt.value() # get the counter value
176+
if _c != c:
177+
_c = c
178+
print('Counter =', c)
179+
finally:
180+
cnt.deinit() # free the input pins and counter.
181+
182+
183+
.. _esp32_machine.Encoder:
184+
185+
Encoder
186+
=======
187+
188+
This class provides a Quadrature Incremental Encoder service.
189+
See `Quadrature encoder outputs.
190+
<https://en.wikipedia.org/wiki/Incremental_encoder#Quadrature_outputs>`_
191+
192+
.. image:: img/quad.png
193+
:width: 397px
194+
195+
Constructor
196+
-----------
197+
198+
.. class:: Encoder(id, phase_a=None, phase_b=None, \*, phases=1, filter_ns=0, match=0)
199+
200+
The Encoder starts to count immediately. Filtering is disabled.
201+
202+
- *id*. Values of *id* depend on a particular port and its hardware.
203+
Values 0, 1, etc. are commonly used to select hardware block #0, #1, etc.
204+
205+
- *phase_a*, *phase_b* are input pins :ref:`machine.Pin <machine.Pin>` for monitoring of quadrature encoder pulses.
206+
They are required in the constructor.
207+
208+
- *phases* is a hardware multiplier, possible values is 1, 2, 4. The default value is 1.
209+
More info in `Quadrature decoder state table <https://en.wikipedia.org/wiki/Incremental_encoder#Quadrature_decoder>`_.
210+
When more Encoder resolution is needed, it is possible for the encoder to count the leading
211+
and trailing edges of the quadrature encoder’s pulse train from one channel,
212+
which doubles (x2) the number of pulses. Counting both leading and trailing edges
213+
of both channels (A and B channels) of a quadrature encoder will quadruple (x4) the number of pulses:
214+
215+
- 1 - count the leading(or trailing) edges from one phase channel.
216+
- 2 - count the leading and trailing edges from one phase channel.
217+
- 4 - count both leading and trailing edges of both phase channels.
218+
219+
These keywords are the same as the Counter keywords, see above:
220+
- *filter_ns*
221+
- *match*
222+
223+
Methods
224+
-------
225+
226+
.. method:: Encoder.init(*, phase_a, ...)
227+
228+
Modify the settings of the Encoder object. See the **Constructor** for details about the parameters.
229+
230+
The Encoder has the same methods as the Counter and differs only
231+
in the constructor and internal hardware PCNT initialization.
232+
233+
Constants
234+
---------
235+
236+
.. data:: Encoder.IRQ_MATCH
237+
Encoder.IRQ_ZERO
238+
239+
Selects callback triggers.
240+
241+
::
242+
243+
from machine import Encoder, Pin
244+
245+
try:
246+
n = 0
247+
def irq_handler1(self):
248+
n -= 1
249+
print('irq_handler1()', self.id(), self.value(), n)
250+
251+
def irq_handler2(self):
252+
n += 1
253+
print('irq_handler2()', self.id(), self.value(), n)
254+
255+
enc = Encoder(0, phase_a=Pin(17, mode=Pin.IN), phase_b=Pin(16, mode=Pin.IN), match=1000)
256+
257+
enc.pause()
258+
flt = enc.filter() # return current filter value.
259+
enc.filter(10_000) # filter delay is 10ms
260+
c = enc.value(0) # get current encoder value, set the encoder value to 0
261+
enc.irq(irq_handler1, Encoder.IRQ_MATCH) # set irq handler
262+
enc.resume()
263+
264+
_c = None
265+
while True:
266+
c = enc.value() # get the encoder value
267+
if _c != c:
268+
_c = c
269+
print('Encoder =', c)
270+
finally:
271+
enc.deinit() # free the input pins and encoder.

0 commit comments

Comments
 (0)