docs/library/machine: Add docs for Counter and Encoder.

Add documentation for `machine.Counter` and `machine.Encoder` as currently
implemented by the esp32 port, but intended to be implemented by other
ports.

Originally authored by: Ihor Nehrutsa <Ihor.Nehrutsa@gmail.com> and
Jonathan Hogg <me@jonathanhogg.com>.

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
This commit is contained in:
Jonathan Hogg 2022-09-12 18:37:22 +01:00 committed by Damien George
parent 327655905e
commit 641ca2eb06
6 changed files with 192 additions and 0 deletions

View file

@ -587,6 +587,21 @@ Use the :ref:`esp32.PCNT <esp32.PCNT>` class::
The PCNT hardware supports monitoring multiple pins in a single unit to
implement quadrature decoding or up/down signal counters.
See the :ref:`machine.Counter <machine.Counter>` and
:ref:`machine.Encoder <machine.Encoder>` classes for simpler abstractions of
common pulse counting applications::
from machine import Pin, Counter
counter = Counter(0, Pin(2)) # create a counter as above and start it
count = counter.value() # read the count as an arbitrary precision signed integer
encoder = Encoder(0, Pin(12), Pin(14)) # create an encoder and begin counting
count = encoder.value() # read the count as an arbitrary precision signed integer
Note that the id passed to these ``Counter()`` and ``Encoder()`` objects must be
a PCNT id.
Software SPI bus
----------------

View file

@ -336,6 +336,9 @@ implemented as thin Python shims around :class:`PCNT`.
value. Thus the ``IRQ_ZERO`` event will also trigger when either of these
events occurs.
See the :ref:`machine.Counter <machine.Counter>` and
:ref:`machine.Encoder <machine.Encoder>` classes for simpler abstractions of
common pulse counting applications.
.. _esp32.RMT:

View file

@ -0,0 +1,93 @@
.. currentmodule:: machine
.. _machine.Counter:
class Counter -- pulse counter
==============================
Counter implements pulse counting by monitoring an input signal and counting
rising or falling edges.
Minimal example usage::
from machine import Pin, Counter
counter = Counter(0, Pin(0, Pin.IN)) # create Counter for pin 0 and begin counting
value = counter.value() # retrieve current pulse count
Availability: **ESP32**
Constructors
------------
.. class:: Counter(id, ...)
Returns the singleton Counter object for the the given *id*. Values of *id*
depend on a particular port and its hardware. Values 0, 1, etc. are commonly
used to select hardware block #0, #1, etc.
Additional arguments are passed to the :meth:`init` method described below,
and will cause the Counter instance to be re-initialised and reset.
On ESP32, the *id* corresponds to a :ref:`PCNT unit <esp32.PCNT>`.
Methods
-------
.. method:: Counter.init(src, *, ...)
Initialise and reset the Counter with the given parameters:
- *src* specifies the input pin as a :ref:`machine.Pin <machine.Pin>` object.
May be omitted on ports that have a predefined pin for a given hardware
block.
Additional keyword-only parameters that may be supported by a port are:
- *edge* specifies the edge to count. Either ``Counter.RISING`` (the default)
or ``Counter.FALLING``. *(Supported on ESP32)*
- *direction* specifies the direction to count. Either ``Counter.UP`` (the
default) or ``Counter.DOWN``. *(Supported on ESP32)*
- *filter_ns* specifies a minimum period of time in nanoseconds that the
source signal needs to be stable for a pulse to be counted. Implementations
should use the longest filter supported by the hardware that is less than
or equal to this value. The default is 0 (no filter). *(Supported on ESP32)*
.. method:: Counter.deinit()
Stops the Counter, disabling any interrupts and releasing hardware resources.
A Soft Reset should deinitialize all Counter objects.
.. method:: Counter.value([value])
Get, and optionally set, the counter value as a signed integer.
Implementations must aim to do the get and set atomically (i.e. without
leading to skipped counts).
This counter value could exceed the range of a :term:`small integer`, which
means that calling :meth:`Counter.value` could cause a heap allocation, but
implementations should aim to ensure that internal state only uses small
integers and therefore will not allocate until the user calls
:meth:`Counter.value`.
For example, on ESP32, the internal state counts overflows of the hardware
counter (every 32000 counts), which means that it will not exceed the small
integer range until ``2**30 * 32000`` counts (slightly over 1 year at 1MHz).
In general, it is recommended that you should use ``Counter.value(0)`` to reset
the counter (i.e. to measure the counts since the last call), and this will
avoid this problem.
Constants
---------
.. data:: Counter.RISING
Counter.FALLING
Select the pulse edge.
.. data:: Counter.UP
Counter.DOWN
Select the counting direction.

View file

@ -0,0 +1,72 @@
.. currentmodule:: machine
.. _machine.Encoder:
class Encoder -- quadrature decoding
====================================
Encoder implements decoding of quadrature signals as commonly output from
rotary encoders, by counting either up or down depending on the order of two
input pulses.
Minimal example usage::
from machine import Pin, Encoder
counter = Counter(0, Pin(0, Pin.IN), Pin(1, Pin.IN)) # create Encoder for pins 0, 1 and begin counting
value = counter.value() # retrieve current count
Availability: **ESP32**
Constructors
------------
.. class:: Encoder(id, ...)
Returns the singleton Encoder object for the the given *id*. Values of *id*
depend on a particular port and its hardware. Values 0, 1, etc. are commonly
used to select hardware block #0, #1, etc.
Additional arguments are passed to the :meth:`init` method described below,
and will cause the Encoder instance to be re-initialised and reset.
On ESP32, the *id* corresponds to a :ref:`PCNT unit <esp32.PCNT>`.
Methods
-------
.. method:: Encoder.init(phase_a, phase_b, *, ...)
Initialise and reset the Encoder with the given parameters:
- *phase_a* specifies the first input pin as a
:ref:`machine.Pin <machine.Pin>` object.
- *phase_b* specifies the second input pin as a
:ref:`machine.Pin <machine.Pin>` object.
These pins may be omitted on ports that have predefined pins for a given
hardware block.
Additional keyword-only parameters that may be supported by a port are:
- *filter_ns* specifies a minimum period of time in nanoseconds that the
source signal needs to be stable for a pulse to be counted. Implementations
should use the longest filter supported by the hardware that is less than
or equal to this value. The default is 0 (no filter). *(Supported on ESP32)*
- *phases* specifies the number of signal edges to count and thus the
granularity of the decoding. e.g. 4 phases corresponds to "4x quadrature
decoding", and will result in four counts per pulse. Ports may support
either 1, 2, or 4 phases and the default is 1 phase. *(Supported on ESP32)*
.. method:: Encoder.deinit()
Stops the Encoder, disabling any interrupts and releasing hardware resources.
A Soft Reset should deinitialize all Encoder objects.
.. method:: Encoder.value([value])
Get, and optionally set, the encoder value as a signed integer.
Implementations should aim to do the get and set atomically.
See :meth:`machine.Counter.value` for details about overflow of this value.

View file

@ -268,6 +268,8 @@ Classes
machine.I2S.rst
machine.RTC.rst
machine.Timer.rst
machine.Counter.rst
machine.Encoder.rst
machine.WDT.rst
machine.SD.rst
machine.SDCard.rst

View file

@ -188,6 +188,13 @@ Glossary
Most MicroPython boards make a REPL available over a UART, and this is
typically accessible on a host PC via USB.
small integer
MicroPython optimises the internal representation of integers such that
"small" values do not take up space on the heap, and calculations with
them do not require heap allocation. On most 32-bit ports, this
corresponds to values in the interval ``-2**30 <= x < 2**30``, but this
should be considered an implementation detail and not relied upon.
stream
Also known as a "file-like object". A Python object which provides
sequential read-write access to the underlying data. A stream object