Skip to content

Addition of a machine.Counter class (ESP32 only initially) #5496

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 150 additions & 0 deletions docs/library/machine.Counter.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
.. currentmodule:: machine
.. _machine.Counter:

class Counter -- hardware counter
=================================

A hardware counter counts the transitions or pulses on an input pin and accumulates the count into a
hardware register. This register can be read, reset, and typically thresholds can be set to trigger
interrupts when specific values are reached.

Counters are related to timers and in some
microprocessors use the same hardware. From a usage perspective, counters count
edges (or pulses) that happen at arbitrary points in time and the resulting register
values over time are expressed simply as counts whereas timers tend to count
regular clock pulses and the resulting register values are expressed as units of time.

Most hardware counter units can count up or down, can count on positive or negative
incoming edges and can reset at/to some max value. On many microprocessors the input
can be fed through a prescaler which divides the incoming pulses by a power of two.
It is also common to find a filter which can cause short pulses due to glitches to be
ignored.

Example usage::

from machine import Counter, Pin
ctr = Counter(0, pin=4)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pin=Pin(4)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes & no... I'm copying the conversation from the slack thread https://micropython.slack.com/archives/C48TVHRQC/p1578281256187400?thread_ts=1578281225.187300&cid=C48TVHRQC:

jimmo 1 day ago
the general idea is that anything that takes a pin should be able to accept either a machine.Pin instance or a port-specific value

jimmo 1 day ago
so on STM32, the port-specific value is a string, which is either a "CPU" pin name (e.g. PA1) or a "BOARD" pin name (e.g. "X1" on a pyboard, or "A0", "D12", etc on an arduino-layout-compatible board)
each board definition defines a pins.csv which maps the board pins to CPU pins, and the CPU pins are generated automatically based on which stm32 part it is (edited)

jimmo 1 day ago
on ESP32, the port-specific value is the pin number, and the pins.csv board mapping etc isn't implemented (i recently added basic support for board definitions, and hopefulyl someone who is more involved with ESP32 can finish it off and add pins.csv)

[...]

TvE 1 day ago
Is it OK to use pin numbers (ints) on the esp32 now and then later the code can test the type of the pin parameter and if it's a string look up the pin by name and if it's a Pin use that?

jimmo 1 day ago
yes, that will at least be compatible with the rest of ESP32

jimmo 1 day ago
i hope that in the future that just a few extra calls to the esp32 version of pin_find will be all that's required

jimmo 1 day ago
then ESP32 will support pin number, pin name, and pin obj in all cases

sleep(10)
print ctr.value()

Some use-cases for using a Counter are: counting motor shaft revolutions and deriving
rotational velocity, measuring wind
speed using an anemometer that produces N pulses per revolution, measuring rain using a
tipping bucket.

Availability of this class: esp32.

Constructors
------------

.. class:: Counter(id, ...)

Construct a Counter object for a specific hardware counter unit identified
by ``id``. Values 0, 1, etc. are commonly used.

With no additional parameters, the Counter object is created but the hardware is not
initialised (it has the settings from the last initialisation, if any).
If extra arguments are given, the hardware is initialised.
See ``init`` for parameters of initialisation.

Methods
-------

.. method:: Counter.init(pin, direction=Counter.UP, edge=Counter.RISING, limit=None, reset=True)

Initialise a counter by connecting it to an input pin.
The input pin can be specified as a pin number (int), a pin name (str), or a Pin object
(however, some ports may limit the choice, the ESP32 in particular).
Optionally specify the
counting direction and whether the rising or falling edge should be counted.
Additional keyword parameters can customize the counter:
- ``direction``: Counter.UP, Counter,DOWN
- ``edge``: Counter.RISING, Counter.FALLING, Counter.BOTH
- ``limit``: when counting up the register is reset to zero at the next edge after
it reaches the limit. This means that a limit of ``N`` produces the N+1 values 0..N.
When counting down the register is set to the limit value at the next edge after it
reaches zero.
- ``reset``: if True init resets the counter, else it leaves the current register value
unchanged which can be useful to preserve counts when an application restarts.

ESP32 notes:
- Only supports pin numbers for now.
- Supports the following parameters: direction, edge, limit, and reset (**verify**).
- The ESP32's counter units support two channels, which really means two pin pairs, so one pin
could cause an increment and the other a decrement. Only one channel is currently suported.
- The ESP32's counter units support additional features (e.g. a control
pin and intermediate trigger values) but these are currently not supported.
- When counting down the ESP32's hardware starts at zero, counts to a negative limit, then
resets back to zero. This class shifts the values such that the counter counts down
to zero from the limit, which is the more typical implementation on most microprocessors.

.. method:: Counter.deinit()

Dissociates the counter unit from any Pin and disables any interrupt.

.. method:: Counter.pause()

Disables the counter unit without changing its configuration or losing its value.
If the unit is already paused this is a no-op.

.. method:: Counter.resume()

Resumes the counter unit after a pause.
If the unit is already running this is a no-op.

.. method:: Counter.irq(trigger=Counter.ZERO, priority=1, handler=None, wake=machine.IDLE)

Configure an interrupt handler to be called when the trigger condition is met.
The interrupt handler is called from a hardware interrupt and may not allocate
memory; see :ref:`isr_rules`.

The arguments are:

- ``trigger``: the condition that triggers an interrupt.
``Counter.ZERO``: when the counter reaches or is reset to zero.
``Counter.LIMIT``: when the counter reaches or is reset to the limit value.
These values can be OR’ed together to trigger on multiple events.
- ``priority``: the priority level of the interrupt, the values are port-specific,
but higher values always represent higher priorities.
- ``handler``: the function to be called when the interrupt triggers. The handler
must take exactly one argument which is the Counter instance.
- ``wake``: selects the power mode in which this interrupt can wake up the system.
It can be machine.IDLE, machine.SLEEP or machine.DEEPSLEEP.
These values can also be OR’ed together to make a pin generate interrupts in more
than one power mode.

ESP32 notes:
- The ESP32's 8 units with two channels each are exposed as 16 units such that ids
0 and 1 correspond to unit 0, ids 2 and 3 to unit 1, etc.
- Supported triggers are ZERO and LIMIT.
- The priority is ignored (**verify**).
- For wake only machine.IDLE is supported.
- The counter unit samples the input at the APB clock frequency, which is 80Mhz or 12.5ns,
this places a lower bound on the width of pulses that can reliably be detected.
(If variable frequency is enabled for low power operation using machine.freq then
the APB clock may be as low as set with that method's min_freq parameter.)

This method returns a callback object.

.. method:: Counter.value([x])

Reads or writes the current value of the counter.

Constants
---------

.. data:: Counter.UP
Counter.DOWN

Selects the counting direction.

.. data:: Counter.RISING
Counter.FALLING
Counter.BOTH

Selects the input pin transition/edge that is counted.

.. data:: Counter.ZERO
Counter.LIMIT

Selects the state that causes an interrupt, see the description of the Counter.irq method.
1 change: 1 addition & 0 deletions ports/esp32/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,7 @@ SRC_C = \
machine_dac.c \
machine_i2c.c \
machine_pwm.c \
machine_counter.c \
machine_uart.c \
modmachine.c \
modnetwork.c \
Expand Down
Loading