diff options
-rw-r--r-- | docs/esp32/quickref.rst | 21 | ||||
-rw-r--r-- | docs/library/esp32.rst | 142 |
2 files changed, 163 insertions, 0 deletions
diff --git a/docs/esp32/quickref.rst b/docs/esp32/quickref.rst index 49f546a17..7127cc402 100644 --- a/docs/esp32/quickref.rst +++ b/docs/esp32/quickref.rst @@ -566,6 +566,27 @@ ESP32 S2: Provided to deinit the adc driver. +Pulse Counter (pin pulse/edge counting) +--------------------------------------- + +The ESP32 provides up to 8 pulse counter peripherals depending on the hardware, +with id 0..7. These can be configured to count rising and/or falling edges on +any input pin. + +Use the :ref:`esp32.PCNT <esp32.PCNT>` class:: + + from machine import Pin + from esp32 import PCNT + + counter = PCNT(0, pin=Pin(2), rising=PCNT.INCREMENT) # create counter + counter.start() # start counter + count = counter.value() # read count, -32768..32767 + counter.value(0) # reset counter + count = counter.value(0) # read and reset + +The PCNT hardware supports monitoring multiple pins in a single unit to +implement quadrature decoding or up/down signal counters. + Software SPI bus ---------------- diff --git a/docs/library/esp32.rst b/docs/library/esp32.rst index 4be6dc267..0cf0ebc62 100644 --- a/docs/library/esp32.rst +++ b/docs/library/esp32.rst @@ -195,6 +195,148 @@ Constants Used in `idf_heap_info`. + +.. _esp32.PCNT: + +PCNT +---- + +This class provides access to the ESP32 hardware support for pulse counting. +There are 8 pulse counter units, with id 0..7. + +See the :ref:`machine.Counter <machine.Counter>` and +:ref:`machine.Encoder <machine.Encoder>` classes for simpler and portable +abstractions of common pulse counting applications. These classes are +implemented as thin Python shims around :class:`PCNT`. + +.. class:: PCNT(id, *, ...) + + Returns the singleton PCNT instance for the given unit ``id``. + + Keyword arguments are passed to the ``init()`` method as described + below. + +.. method:: PCNT.init(*, ...) + + (Re-)initialise a pulse counter unit. Supported keyword arguments are: + + - ``channel``: see description below + - ``pin``: the input Pin to monitor for pulses + - ``rising``: an action to take on a rising edge - one of + ``PCNT.INCREMENT``, ``PCNT.DECREMENT`` or ``PCNT.IGNORE`` (the default) + - ``falling``: an action to take on a falling edge (takes the save values + as the ``rising`` argument). + - ``mode_pin``: ESP32 pulse counters support monitoring a second pin and + altering the behaviour of the counter based on its level - set this + keyword to any input Pin + - ``mode_low``: set to either ``PCNT.HOLD`` or ``PCNT.REVERSE`` to + either suspend counting or reverse the direction of the counter (i.e., + ``PCNT.INCREMENT`` behaves as ``PCNT.DECREMENT`` and vice versa) + when ``mode_pin`` is low + - ``mode_high``: as ``mode_low`` but for the behaviour when ``mode_pin`` + is high + - ``filter``: set to a value 1..1023, in ticks of the 80MHz clock, to + enable the pulse width filter + - ``min``: set to the minimum level of the counter value when + decrementing (-32768..-1) or 0 to disable + - ``max``: set to the maximum level of the counter value when + incrementing (1..32767) or 0 to disable + - ``threshold0``: sets the counter value for the + ``PCNT.IRQ_THRESHOLD0`` event (see ``irq`` method) + - ``threshold1``: sets the counter value for the + ``PCNT.IRQ_THRESHOLD1`` event (see ``irq`` method) + - ``value``: can be set to ``0`` to reset the counter value + + The hardware initialisation is done in stages and so some of the keyword + arguments can be used in groups or in isolation to partially reconfigure a + unit: + + - the ``pin`` keyword (optionally combined with ``mode_pin``) can be used + to change just the bound pin(s) + - ``rising``, ``falling``, ``mode_low`` and ``mode_high`` can be used + (singly or together) to change the counting logic - omitted keywords + use their default (``PCNT.IGNORE`` or ``PCNT.NORMAL``) + - ``filter`` can be used to change only the pulse width filter (with 0 + disabling it) + - each of ``min``, ``max``, ``threshold0`` and ``threshold1`` can + be used to change these limit/event values individually; however, + setting any will reset the counter to zero (i.e., they imply + ``value=0``) + + Each pulse counter unit supports two channels, 0 and 1, each able to + monitor different pins with different counting logic but updating the same + counter value. Use ``channel=1`` with the ``pin``, ``rising``, ``falling``, + ``mode_pin``, ``mode_low`` and ``mode_high`` keywords to configure the + second channel. + + The second channel can be used to configure 4X quadrature decoding with a + single counter unit:: + + pin_a = Pin(2, Pin.INPUT, pull=Pin.PULL_UP) + pin_b = Pin(3, Pin.INPUT, pull=Pin.PULL_UP) + rotary = PCNT(0, min=-32000, max=32000) + rotary.init(channel=0, pin=pin_a, falling=PCNT.INCREMENT, rising=PCNT.DECREMENT, mode_pin=pin_b, mode_low=PCNT.REVERSE) + rotary.init(channel=1, pin=pin_b, falling=PCNT.DECREMENT, rising=PCNT.INCREMENT, mode_pin=pin_a, mode_low=PCNT.REVERSE) + rotary.start() + +.. method:: PCNT.value([value]) + + Call this method with no arguments to return the current counter value. + + If the optional *value* argument is set to ``0`` then the counter is + reset (but the previous value is returned). Read and reset is not atomic and + so it is possible for a pulse to be missed. Any value other than ``0`` will + raise an error. + +.. method:: PCNT.irq(handler=None, trigger=PCNT.IRQ_ZERO) + + ESP32 pulse counters support interrupts on these counter events: + + - ``PCNT.IRQ_ZERO``: the counter has reset to zero + - ``PCNT.IRQ_MIN``: the counter has hit the ``min`` value + - ``PCNT.IRQ_MAX``: the counter has hit the ``max`` value + - ``PCNT.IRQ_THRESHOLD0``: the counter has hit the ``threshold0`` value + - ``PCNT.IRQ_THRESHOLD1``: the counter has hit the ``threshold1`` value + + ``trigger`` should be a bit-mask of the desired events OR'ed together. The + ``handler`` function should take a single argument which is the + :class:`PCNT` instance that raised the event. + + This method returns a callback object. The callback object can be used to + access the bit-mask of events that are outstanding on the PCNT unit.:: + + def pcnt_irq(pcnt): + flags = pcnt.irq().flags() + if flags & PCNT.IRQ_ZERO: + # reset + if flags & PCNT.IRQ_MAX: + # overflow... + ... etc + + pcnt.irq(handler=pcnt_irq, trigger=PCNT.IRQ_ZERO | PCNT.IRQ_MAX | ...) + + **Note:** Accessing ``irq.flags()`` will clear the flags, so only call it + once per invocation of the handler. + + The handler is called with the MicroPython scheduler and so will run at a + point after the interrupt. If another interrupt occurs before the handler + has been called then the events will be coalesced together into a single + call and the bit mask will indicate all events that have occurred. + + To avoid race conditions between a handler being called and retrieving the + current counter value, the ``value()`` method will force execution of any + pending events before returning the current counter value (and potentially + resetting the value). + + Only one handler can be in place per-unit. Set ``handler`` to ``None`` to + disable the event interrupt. + +.. Note:: + ESP32 pulse counters reset to *zero* when reaching the minimum or maximum + value. Thus the ``IRQ_ZERO`` event will also trigger when either of these + events occurs. + + .. _esp32.RMT: RMT |