From 3b44fa533bbf58477775de1c81442dd6c617dde3 Mon Sep 17 00:00:00 2001 From: Ihor Nehrutsa Date: Fri, 25 Aug 2023 07:57:17 +0300 Subject: [PATCH] esp32/encoder: Add Encoder and Counter classes. Signed-off-by: IhorNehrutsa --- docs/esp32/general.rst | 4 + docs/esp32/img/quad.png | Bin 0 -> 90384 bytes docs/esp32/pcnt.rst | 256 ++++++ docs/esp32/quickref.rst | 39 + docs/esp32/tutorial/index.rst | 1 - .../boards/ESP32_GENERIC_C3/mpconfigboard.h | 1 + ports/esp32/esp32_common.cmake | 1 + ports/esp32/machine_encoder.c | 783 ++++++++++++++++++ ports/esp32/machine_encoder.h | 42 + ports/esp32/main.c | 3 + ports/esp32/modmachine.c | 4 + ports/esp32/modmachine.h | 3 + ports/esp32/mpconfigport.h | 3 + 13 files changed, 1139 insertions(+), 1 deletion(-) create mode 100644 docs/esp32/img/quad.png create mode 100644 docs/esp32/pcnt.rst create mode 100644 ports/esp32/machine_encoder.c create mode 100644 ports/esp32/machine_encoder.h diff --git a/docs/esp32/general.rst b/docs/esp32/general.rst index 8137c042d921..40e97f1e8baf 100644 --- a/docs/esp32/general.rst +++ b/docs/esp32/general.rst @@ -50,8 +50,12 @@ For your convenience, some of technical specifications are provided below: * SPI: 4 SPI interfaces (one used for FlashROM) * I2C: 2 I2C (bitbang implementation available on any pins) * I2S: 2 +* CAN bus: 1 * ADC: 12-bit SAR ADC up to 18 channels * DAC: 2 8-bit DACs +* PCNT: up to 8 channels +* PWM: up to 16 channels +* MCPWM: up to 2 channels * RMT: 8 channels allowing accurate pulse transmit/receive * Programming: using BootROM bootloader from UART - due to external FlashROM and always-available BootROM bootloader, the ESP32 is not brickable diff --git a/docs/esp32/img/quad.png b/docs/esp32/img/quad.png new file mode 100644 index 0000000000000000000000000000000000000000..a47a38df8d3dba12bf9b92db0b3bcb12b496cf07 GIT binary patch literal 90384 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 literal 0 HcmV?d00001 diff --git a/docs/esp32/pcnt.rst b/docs/esp32/pcnt.rst new file mode 100644 index 000000000000..d36cec2786e4 --- /dev/null +++ b/docs/esp32/pcnt.rst @@ -0,0 +1,256 @@ +PCNT - Counter and Encoder +========================== + +The Counter and Encoder use the ESP32 Pulse Counter (PCNT) hardware peripheral, +see Espressif's `ESP-IDF Pulse Counter documentation. +`_ + +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). +The pulses are sampled on the edges of the APB_CLK clock and may be missed if fall between the edges. +With ideal input signal maximum frequency of measured pulses is APB_CLK / 2 = 80 MHz / 2 = 40 MHz. + +The inputs have optional filters that can be used to discard unwanted glitches in the signal. +The length of ignored pulses is provided in APB_CLK clock cycles. +* Note: Filter value is a 10-bit value, so the maximum filter value should be limited to 1023. +Maximum filtered glitches delay is 1023 * 12.5 ns = 12.7875 us. +Big filter make cutbacks the input frequency: 1 / (12.7875 us * 2) = 39.1 kHz. +* Note: Do not neglect circuitry methods to reduce noise (right powering and grounding, filtering, shielding, +short conductors, twisted pair cable, differential signals, etc.). + +There is only one interrupt for the peripheral, and that is managed inside the module. +The user has no interrupt interface, and no interrupts are generated on each pulse. +Interrupts arrive when the 16-bit hardware counter buffer overflows, so this module has a tiny interrupt footprint +while providing support for up to 8 simultaneous counters (Encoder or Counter objects). + +.. _esp32_machine.Counter: + +Counter +======= + +The Pulse Counter service. + +Constructor +----------- + +.. class:: Counter(id, src=None, \*, direction=Counter.UP, _src=None, edge=Counter.RISING, filter_ns=0) + + The Counter starts to count immediately. Filtering is disabled. + + - *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. + + - *src* is the pulse input :ref:`machine.Pin ` to be monitored. + *src* is required in the constructor. + + - *direction* specifies the direction to count. Values for this include the constants + + - Counter.UP - (default value) and + - Counter.DOWN to control the direction by software or + - :ref:`machine.Pin ` object to control the direction externally. If ``Pin.value()``: + - 0 - count down, + - 1 - count up. + + - *_src* is the inverse pulse input :ref:`machine.Pin ` to be monitored. + If the *_src* keyword is present then the *direction* keyword does not matter. + *src* and *_src* count in opposite directions, one in the UP direction + and the other in the DOWN direction, i.e. as an incremental/decremental counter. + + - *edge* specifies which edges of the input signal will be counted by Counter: + + - Counter.RISING : raise edges, + - Counter.FALLING : fall edges, + - Counter.RISING | Counter.FALLING : both edges. + + - *filter_ns* specifies a ns-value for the minimal time a signal has to be stable + at the input to be recognized. The largest value is 12787ns (1023 * 1000000000 / APB_CLK_FREQ). + The default is 0 – no filter. + +Methods +------- + +.. method:: Counter.init(*, src, ...) + + Modify the settings of the Counter object. See the **Constructor** for details about the parameters. + +.. method:: Counter.deinit() + + Stops the Counter, disables interrupts and releases hardware resources used by the counter. + A Soft Reset involve deinitializing all Encoder objects. + +.. method:: Counter.filter([value]) + + Get, and optionally set, the filter value. 0 disable filtering. + +.. method:: Counter.value([value]) + + Get, and optionally set, the counter *value* as a signed 64-bit integer. + +.. method:: Counter.irq(handler=None, trigger=Counter.IRQ_MATCH1 | Counter.IRQ_ZERO, value=0) + + -*handler* specifies a function is called when the respective *trigger* event happens. + The callback function *handler* receives a single argument, which is the Counter object. + All events may share the same callback or have separate callbacks. + The callback will be disabled, when called with handler=None. Counter.irq() disable all callbacks. + The event which triggers the callback can be identified with the ``Counter.status()`` method. + The Counter object which triggers the callback can be identified with the ``Counter.id()`` method. + + -*trigger* events may be: + + - Counter.IRQ_MATCH1 triggered when the counter matches the match1 value. + - Counter.IRQ_ZERO triggered when the counter matches the 0. + + The default is - trigger=Counter.IRQ_MATCH1 | Counter.IRQ_ZERO. + The events are triggered when the counter value and match value are identical, but + callbacks have always a latency. + + - *value* sets a counter match1 value. When the counter matches these values, + a callback function can be called. They are 0 by default. + +Attention: ``Counter.irq()`` resets counter to 0. + +.. method:: Counter.status() + + Returns the event status flags of the recent handled Counter interrupt as a bitmap. + +.. method:: Counter.id() + + Returns id number. + +.. method:: Counter.pause() + +.. method:: Counter.resume() + +Constants +--------- + +.. data:: Counter.UP + Counter.DOWN + + Selects the counter direction. + +.. data:: Counter.RISING + Counter.FALLING + + Selects the counted edges. + +.. data:: Counter.IRQ_MATCH1 + Counter.IRQ_ZERO + + Selects callback triggers. + +:: + + from machine import Counter, Pin + + try: + def irq_handler(self): + print('irq_handler()', self.id(), self.status(), self.value()) + + cnt = Counter(0, src=Pin(17, mode=Pin.IN), direction=Pin(16, mode=Pin.IN)) + + cnt.pause() + flt = cnt.filter() # return current filter value. + cnt.filter(10_000) # filter delay is 10ms + c = cnt.value(0) # get current counter value, set the counter value to 0 + cnt.irq(irq_handler, Counter.IRQ_ZERO) # set irq handler + cnt.resume() + + _c = None + while True: + c = cnt.value() # get the counter value + if _c != c: + _c = c + print('Counter =', c) + finally: + cnt.deinit() # free the input pins and counter. + + +.. _esp32_machine.Encoder: + +Encoder +======= + +This class provides a Quadrature Incremental Encoder service. +See `Quadrature encoder outputs. +`_ + +.. image:: img/quad.png + :width: 397px + +Constructor +----------- + +.. class:: Encoder(id, phase_a=None, phase_b=None, \*, x124=4, filter_ns=0, match1=0) + + The Encoder starts to count immediately. Filtering is disabled. + + - *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. + + - *phase_a*, *phase_b* are input pins :ref:`machine.Pin ` for monitoring of quadrature encoder pulses. + They are required in the constructor. + + - *x124* is a hardware multiplier, possible values is 1, 2, 4. The default value is 4. + More info in `Quadrature decoder state table `_. + When more Encoder resolution is needed, it is possible for the encoder to count the leading + and trailing edges of the quadrature encoder’s pulse train from one channel, + which doubles (x2) the number of pulses. Counting both leading and trailing edges + of both channels (A and B channels) of a quadrature encoder will quadruple (x4) the number of pulses: + + - 1 - count the leading(or trailing) edges from one phase channel. + - 2 - count the leading and trailing edges from one phase channel. + - 4 - count both leading and trailing edges of both phase channels. + + These keywords are the same as the Counter keywords, see above: + - *filter_ns* + - *match1* + +Methods +------- + +.. method:: Encoder.init(*, phase_a, ...) + + Modify the settings of the Encoder object. See the **Constructor** for details about the parameters. + +The Encoder has the same methods as the Counter and differs only +in the constructor and internal hardware PCNT initialization. + +Constants +--------- + +.. data:: Encoder.IRQ_MATCH1 + Encoder.IRQ_ZERO + + Selects callback triggers. + +:: + + from machine import Encoder, Pin + + try: + n = 0 + def irq_handler1(self): + n -= 1 + print('irq_handler1()', self.id(), self.value(), n) + + def irq_handler2(self): + n += 1 + print('irq_handler2()', self.id(), self.value(), n) + + enc = Encoder(0, phase_a=Pin(17, mode=Pin.IN), phase_b=Pin(16, mode=Pin.IN), match1=1000) + + enc.pause() + flt = enc.filter() # return current filter value. + enc.filter(10_000) # filter delay is 10ms + c = enc.value(0) # get current encoder value, set the encoder value to 0 + enc.irq(irq_handler1, Encoder.IRQ_MATCH1) # set irq handler + enc.resume() + + _c = None + while True: + c = enc.value() # get the encoder value + if _c != c: + _c = c + print('Encoder =', c) + finally: + enc.deinit() # free the input pins and encoder. diff --git a/docs/esp32/quickref.rst b/docs/esp32/quickref.rst index e03a5ffbc9d2..0ed2c2a917ea 100644 --- a/docs/esp32/quickref.rst +++ b/docs/esp32/quickref.rst @@ -661,6 +661,45 @@ The RMT is ESP32-specific and allows generation of accurate digital pulses with # The channel resolution is 100ns (1/(source_freq/clock_div)). r.write_pulses((1, 20, 2, 40), 0) # Send 0 for 100ns, 1 for 2000ns, 0 for 200ns, 1 for 4000ns +Counter (Pulse/Edge Counter) +---------------------------- + +The Counter counts the number of rising and/or falling edges on any input pin. +It is a 64-bit signed hardware-based counter. Counter and Encoder share the same ESP32 PCNT hardware peripheral, +the total summary available number of Counter and Encoder is up to 8. + +See :ref:`machine.Counter ` for details. Simplest usage is:: + + from machine import Pin, Counter + + cnt = Counter(0, src=Pin(17, mode=Pin.IN), direction=Pin(16, mode=Pin.IN)) + _v = None + while True: + v = cnt.value() # get 64-bit signed value + if _v != v: + _v = v + print('Counter value:', v) + +Encoder (Quadrature Incremental Encoder) +---------------------------------------- + +The Encoder counts the quadrature-encoded pulses on pair of input pins (two square wave signals A and B with +~50% duty cycle and ~90-degree phase difference between them). +It is a 64-bit signed hardware-based counter. Counter and Encoder share the same ESP32 PCNT hardware peripheral, +the total summary available number of Counter and Encoder is up to 8. + +See :ref:`machine.Encoder ` for details. Simplest usage is:: + + from machine import Pin, Encoder + + enc = Encoder(0, phase_a=Pin(17, mode=Pin.IN), phase_b=Pin(16, mode=Pin.IN)) + _v = None + while True: + v = enc.value() # get 64-bit signed value + if _v != v: + _v = v + print('Encoder value:', v) + OneWire driver -------------- diff --git a/docs/esp32/tutorial/index.rst b/docs/esp32/tutorial/index.rst index c6242d731f18..b3ad11b4ad45 100644 --- a/docs/esp32/tutorial/index.rst +++ b/docs/esp32/tutorial/index.rst @@ -16,7 +16,6 @@ to ``__. .. toctree:: :maxdepth: 1 - :numbered: intro.rst pwm.rst diff --git a/ports/esp32/boards/ESP32_GENERIC_C3/mpconfigboard.h b/ports/esp32/boards/ESP32_GENERIC_C3/mpconfigboard.h index 42e77ecb1a6d..2d415242f7a0 100644 --- a/ports/esp32/boards/ESP32_GENERIC_C3/mpconfigboard.h +++ b/ports/esp32/boards/ESP32_GENERIC_C3/mpconfigboard.h @@ -6,6 +6,7 @@ #define MICROPY_HW_ENABLE_SDCARD (0) #define MICROPY_PY_MACHINE_DAC (0) #define MICROPY_PY_MACHINE_I2S (0) +#define MICROPY_PY_MACHINE_PCNT (0) // Enable UART REPL for modules that have an external USB-UART and don't use native USB. #define MICROPY_HW_ENABLE_UART_REPL (1) diff --git a/ports/esp32/esp32_common.cmake b/ports/esp32/esp32_common.cmake index 40ae98822d43..4712aa561881 100644 --- a/ports/esp32/esp32_common.cmake +++ b/ports/esp32/esp32_common.cmake @@ -69,6 +69,7 @@ list(APPEND MICROPY_SOURCE_PORT machine_touchpad.c machine_dac.c machine_i2c.c + machine_encoder.c modmachine.c network_common.c network_lan.c diff --git a/ports/esp32/machine_encoder.c b/ports/esp32/machine_encoder.c new file mode 100644 index 000000000000..08096ff1fbf4 --- /dev/null +++ b/ports/esp32/machine_encoder.c @@ -0,0 +1,783 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * This file was generated by micropython-extmod-generator https://github.com/prusnak/micropython-extmod-generator + * from Python stab file pcnt.py + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Ihor Nehrutsa + * Copyright (c) 2021 Jonathan Hogg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* +ESP32 Pulse Counter +https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/pcnt.html +Wrapped around +https://github.com/espressif/esp-idf/blob/master/components/driver/include/driver/pcnt.h +https://github.com/espressif/esp-idf/blob/master/components/hal/include/hal/pcnt_types.h +https://github.com/espressif/esp-idf/blob/master/components/driver/pcnt.c +See also +https://github.com/espressif/esp-idf/tree/master/examples/peripherals/pcnt/pulse_count_event +*/ + +/* +ESP32 Quadrature Counter based on Pulse Counter(PCNT) +Based on +https://github.com/madhephaestus/ESP32Encoder +https://github.com/bboser/MicroPython_ESP32_psRAM_LoBo/blob/quad_decoder/MicroPython_BUILD/components/micropython/esp32/machine_dec.c +See also +https://github.com/espressif/esp-idf/tree/master/examples/peripherals/pcnt/rotary_encoder +*/ + +#include "py/mpprint.h" +#include "py/runtime.h" +#include "mphalport.h" +#include "modmachine.h" + +#if MICROPY_PY_MACHINE_PCNT + +#include "rom/gpio.h" +#include "driver/pcnt.h" +#include "driver/pulse_cnt.h" +#include "soc/pcnt_struct.h" +#include "esp_err.h" + +#include "machine_encoder.h" + +extern const mp_obj_type_t machine_pin_type; + +#define GET_INT mp_obj_get_int_truncated +// #define GET_INT mp_obj_get_ll_int // need PR: py\obj.c: Get 64-bit integer arg. #80896 + +STATIC pcnt_isr_handle_t pcnt_isr_handle = NULL; +STATIC mp_pcnt_obj_t *pcnts[PCNT_UNIT_MAX] = {}; + +/* Decode what PCNT's unit originated an interrupt + * and pass this information together with the event type + * the main program using a queue. + */ +STATIC void IRAM_ATTR pcnt_intr_handler(void *arg) { + uint32_t intr_status = PCNT.int_st.val; + uint32_t status_unit; + for (int id = 0; id < PCNT_UNIT_MAX; ++id) { + if (intr_status & BIT(id)) { + PCNT.int_clr.val = BIT(id); // clear the interrupt + status_unit = PCNT.status_unit[id].val; + mp_pcnt_obj_t *self = pcnts[id]; + if (self != NULL) { + if (status_unit & PCNT_EVT_H_LIM) { + self->counter += INT16_ROLL; + } else if (status_unit & PCNT_EVT_L_LIM) { + self->counter -= INT16_ROLL; + } + + self->status = status_unit; + if (status_unit & PCNT_EVT_THRES_1) { + int16_t count; + pcnt_get_counter_value(self->unit, &count); + if ((self->counter + count) == self->match1) { + mp_sched_schedule(self->handler_match1, MP_OBJ_FROM_PTR(self)); + mp_hal_wake_main_task_from_isr(); + } + } + if (status_unit & PCNT_EVT_THRES_0) { + int16_t count; + pcnt_get_counter_value(self->unit, &count); + if ((self->counter + count) == self->match1) { + mp_sched_schedule(self->handler_match1, MP_OBJ_FROM_PTR(self)); + mp_hal_wake_main_task_from_isr(); + } + } + if (status_unit & PCNT_EVT_ZERO) { + if (self->counter == 0) { + //self->status |= PCNT_EVT_ZERO; + mp_sched_schedule(self->handler_zero, MP_OBJ_FROM_PTR(self)); + mp_hal_wake_main_task_from_isr(); + } + } + } + //PCNT.int_clr.val = BIT(id); // clear the interrupt + } + } +} + +STATIC void register_isr_handler(void) { + if (pcnt_isr_handle == NULL) { + check_esp_err(pcnt_isr_register(pcnt_intr_handler, (void *)0, (int)0, (pcnt_isr_handle_t *)&pcnt_isr_handle)); + if (pcnt_isr_handle == NULL) { + mp_raise_msg(&mp_type_Exception, MP_ERROR_TEXT("wrap interrupt failed")); + } + PCNT.int_clr.val = 0xff; // clear the interrupts + } +} + +// TODO: Remove after: esp32/machine_pin.c: Allow small int argument in machine_pin_get_id(). #8113 +// from ports/esp32/machine_sdcadr.c +STATIC gpio_num_t pin_or_int(const mp_obj_t arg) { + if (mp_obj_is_small_int(arg)) { + return MP_OBJ_SMALL_INT_VALUE(arg); + } else { + // This raises a value error if the argument is not a Pin. + return machine_pin_get_id(arg); + } +} + +/* Calculate the filter parameters based on an ns value + 1 / 80MHz = 12.5ns - min filter period + 12.5ns * FILTER_MAX = 12.5ns * 1023 = 12787.5ns - max filter period */ +#define ns_to_filter(ns) ((ns * (APB_CLK_FREQ / 1000000) + 500) / 1000) +#define filter_to_ns(filter) (filter * 1000 / (APB_CLK_FREQ / 1000000)) + +STATIC uint16_t get_filter_value_ns(pcnt_unit_t unit) { + uint16_t value; + check_esp_err(pcnt_get_filter_value(unit, &value)); + return filter_to_ns(value); +} + +STATIC void set_filter_value(pcnt_unit_t unit, int16_t value) { + /* + if ((value < 0) || (value > FILTER_MAX)) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("correct filter value is 0..%d ns"), filter_to_ns(FILTER_MAX)); + } + */ + if (value < 0) { + value = 0; + } else if (value > FILTER_MAX) { + value = FILTER_MAX; + } + + check_esp_err(pcnt_set_filter_value(unit, value)); + if (value) { + check_esp_err(pcnt_filter_enable(unit)); + } else { + check_esp_err(pcnt_filter_disable(unit)); + } +} + +STATIC void pcnt_disable_events(mp_pcnt_obj_t *self) { + if (self->handler_match1 != MP_OBJ_NULL) { + check_esp_err(pcnt_event_disable(self->unit, PCNT_EVT_THRES_1)); + check_esp_err(pcnt_event_disable(self->unit, PCNT_EVT_THRES_0)); + self->handler_match1 = MP_OBJ_NULL; + } + if (self->handler_zero != MP_OBJ_NULL) { + check_esp_err(pcnt_event_disable(self->unit, PCNT_EVT_ZERO)); + self->handler_zero = MP_OBJ_NULL; + } +} + +STATIC void pcnt_deinit(mp_pcnt_obj_t *self) { + if (self != NULL) { + check_esp_err(pcnt_counter_pause(self->unit)); + + pcnt_intr_disable(self->unit); + + check_esp_err(pcnt_event_disable(self->unit, PCNT_EVT_L_LIM)); + check_esp_err(pcnt_event_disable(self->unit, PCNT_EVT_H_LIM)); + pcnt_disable_events(self); + + check_esp_err(pcnt_set_pin(self->unit, PCNT_CHANNEL_0, PCNT_PIN_NOT_USED, PCNT_PIN_NOT_USED)); + check_esp_err(pcnt_set_pin(self->unit, PCNT_CHANNEL_1, PCNT_PIN_NOT_USED, PCNT_PIN_NOT_USED)); + + pcnts[self->unit] = NULL; + + // m_del_obj(mp_pcnt_obj_t, self); // Is it need?? + } +} + +// This called from Ctrl-D soft reboot +void machine_encoder_deinit_all(void) { + for (int id = 0; id < PCNT_UNIT_MAX; ++id) { + pcnt_deinit(pcnts[id]); + } + if (pcnt_isr_handle != NULL) { + check_esp_err(pcnt_isr_unregister(pcnt_isr_handle)); + pcnt_isr_handle = NULL; + } +} + +// ================================================================= +// Common classes methods +STATIC mp_obj_t machine_PCNT_deinit(mp_obj_t self_obj) { + pcnt_deinit(MP_OBJ_TO_PTR(self_obj)); + return MP_ROM_NONE; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_PCNT_deinit_obj, machine_PCNT_deinit); + +// ----------------------------------------------------------------- +STATIC mp_obj_t machine_PCNT_filter(size_t n_args, const mp_obj_t *args) { + mp_pcnt_obj_t *self = MP_OBJ_TO_PTR(args[0]); + mp_int_t value = get_filter_value_ns(self->unit); + if (n_args > 1) { + set_filter_value(self->unit, ns_to_filter(mp_obj_get_int(args[1]))); + } + return MP_OBJ_NEW_SMALL_INT(value); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_PCNT_filter_obj, 1, 2, machine_PCNT_filter); + +// ----------------------------------------------------------------- +STATIC mp_obj_t machine_PCNT_count(size_t n_args, const mp_obj_t *args) { + mp_pcnt_obj_t *self = MP_OBJ_TO_PTR(args[0]); + + int16_t count; + check_esp_err(pcnt_get_counter_value(self->unit, &count)); + int64_t counter = self->counter; + + if (n_args > 1) { + uint64_t new_counter = GET_INT(args[1]); + if (new_counter) { + self->counter = new_counter - count; + } else { + check_esp_err(pcnt_counter_clear(self->unit)); + self->counter = 0; + } + } + return mp_obj_new_int_from_ll(counter + count); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_PCNT_count_obj, 1, 2, machine_PCNT_count); + +// ----------------------------------------------------------------- +STATIC mp_obj_t machine_PCNT_get_count(mp_obj_t self_obj) { + mp_pcnt_obj_t *self = MP_OBJ_TO_PTR(self_obj); + + int16_t count; + pcnt_get_counter_value(self->unit, &count); // no error checking to speed up + return mp_obj_new_int_from_ll(self->counter + count); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_PCNT_get_count_obj, machine_PCNT_get_count); + +// ----------------------------------------------------------------- +STATIC mp_obj_t machine_PCNT_pause(mp_obj_t self_obj) { + mp_pcnt_obj_t *self = MP_OBJ_TO_PTR(self_obj); + + check_esp_err(pcnt_counter_pause(self->unit)); + return MP_ROM_NONE; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_PCNT_pause_obj, machine_PCNT_pause); + +// ----------------------------------------------------------------- +STATIC mp_obj_t machine_PCNT_resume(mp_obj_t self_obj) { + mp_pcnt_obj_t *self = MP_OBJ_TO_PTR(self_obj); + + check_esp_err(pcnt_counter_resume(self->unit)); + return MP_ROM_NONE; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_PCNT_resume_obj, machine_PCNT_resume); + +// ----------------------------------------------------------------- +STATIC mp_obj_t machine_PCNT_id(mp_obj_t self_obj) { + mp_pcnt_obj_t *self = MP_OBJ_TO_PTR(self_obj); + return MP_OBJ_NEW_SMALL_INT(self->unit); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_PCNT_id_obj, machine_PCNT_id); + +// ----------------------------------------------------------------- +mp_obj_t machine_PCNT_status(mp_obj_t self_in) { + mp_pcnt_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_NEW_SMALL_INT(self->status); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_PCNT_status_obj, machine_PCNT_status); + +static inline counter_t remainder_of_division(counter_t divident, counter_t divider) { + return divident - divident / divider * divider; +} + +// ----------------------------------------------------------------- +STATIC mp_obj_t machine_PCNT_irq(size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_handler, ARG_trigger, ARG_value }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_handler, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_trigger, MP_ARG_INT, {.u_int = PCNT_EVT_THRES_1 | PCNT_EVT_ZERO} }, + { MP_QSTR_value, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + }; + + mp_pcnt_obj_t *self = pos_args[0]; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_pos_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t handler = args[ARG_handler].u_obj; + mp_uint_t trigger = args[ARG_trigger].u_int; + + if (trigger & ~(PCNT_EVT_THRES_1 | PCNT_EVT_ZERO)) { + mp_raise_ValueError(MP_ERROR_TEXT("trigger")); + } + + if (handler == mp_const_none) { + if (trigger & PCNT_EVT_THRES_1) { + pcnt_event_disable(self->unit, PCNT_EVT_THRES_1); + pcnt_event_disable(self->unit, PCNT_EVT_THRES_0); + } + if (trigger & PCNT_EVT_THRES_0) { + pcnt_event_disable(self->unit, PCNT_EVT_THRES_0); + } + if (trigger & PCNT_EVT_ZERO) { + pcnt_event_disable(self->unit, PCNT_EVT_ZERO); + } + } else { + if (trigger & PCNT_EVT_THRES_1) { + if (args[ARG_value].u_obj != MP_OBJ_NULL) { + counter_t match1 = GET_INT(args[ARG_value].u_obj); + if (self->match1 != match1) { + self->match1 = match1; + pcnt_event_disable(self->unit, PCNT_EVT_THRES_1); + pcnt_event_disable(self->unit, PCNT_EVT_THRES_0); + + int16_t count; + pcnt_get_counter_value(self->unit, &count); + self->counter += count; + + #ifdef USE_INT64 + counter_t counter_match = remainder_of_division(self->counter + self->match1, INT16_ROLL); + #else + counter_t counter_match = (self->match1 - self->counter % INT16_ROLL) % INT16_ROLL; + #endif + check_esp_err(pcnt_set_event_value(self->unit, PCNT_EVT_THRES_1, (int16_t)counter_match)); + check_esp_err(pcnt_set_event_value(self->unit, PCNT_EVT_THRES_0, (int16_t)(-INT16_ROLL + counter_match))); + + pcnt_counter_clear(self->unit); + } + } + self->handler_match1 = handler; + pcnt_event_enable(self->unit, PCNT_EVT_THRES_1); + pcnt_event_enable(self->unit, PCNT_EVT_THRES_0); + } + if (trigger & PCNT_EVT_ZERO) { + self->handler_zero = handler; + pcnt_event_enable(self->unit, PCNT_EVT_ZERO); + } + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_PCNT_irq_obj, 1, machine_PCNT_irq); + +// ================================================================= +// class Counter(object): +STATIC void mp_machine_Counter_init_helper(mp_pcnt_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_src, ARG_direction, ARG__src, ARG_edge, ARG_filter_ns }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_src, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_direction, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR__src, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_edge, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_filter_ns, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + self->bPinNumber = COUNTER_UP; + self->edge = RISING; + + if (args[ARG_src].u_obj != MP_OBJ_NULL) { + self->aPinNumber = pin_or_int(args[ARG_src].u_obj); + } + if (self->aPinNumber == PCNT_PIN_NOT_USED) { + mp_raise_ValueError(MP_ERROR_TEXT(MP_ERROR_TEXT("src"))); + } + + mp_obj_t direction = args[ARG_direction].u_obj; + if (args[ARG__src].u_obj != MP_OBJ_NULL) { + self->bPinNumber = pin_or_int(args[ARG__src].u_obj); + self->x124 = -1; + } else { + self->x124 = 0; + if (direction != MP_OBJ_NULL) { + if (mp_obj_is_type(direction, &machine_pin_type) || mp_obj_is_small_int(direction)) { + self->bPinNumber = pin_or_int(direction); + } else { + self->bPinNumber = mp_obj_get_int(direction); + if (!((self->bPinNumber == COUNTER_UP) || (self->bPinNumber == COUNTER_DOWN))) { + mp_raise_ValueError(MP_ERROR_TEXT(MP_ERROR_TEXT("direction"))); + } + } + } + } + + if (args[ARG_edge].u_int != -1) { + self->edge = args[ARG_edge].u_int; + } + + // Prepare configuration for the PCNT unit + pcnt_config_t r_enc_config; + r_enc_config.unit = self->unit; + r_enc_config.channel = PCNT_CHANNEL_0; + + r_enc_config.pulse_gpio_num = self->aPinNumber; // Pulses // Pulse input GPIO number, a negative value will be ignored + r_enc_config.ctrl_gpio_num = self->bPinNumber; // Direction // Control signal input GPIO number, a negative value will be ignored + + // What to do on the positive / negative edge of pulse input? + if (self->edge & RISING) { + r_enc_config.pos_mode = PCNT_COUNT_INC; // Count up on the positive edge + } else { + r_enc_config.pos_mode = PCNT_COUNT_DIS; // Keep the counter value on the positive edge + } + if (self->edge & FALLING) { + r_enc_config.neg_mode = PCNT_COUNT_INC; // Count up on the negative edge + } else { + r_enc_config.neg_mode = PCNT_COUNT_DIS; // Keep the counter value on the negative edge + } + + // What to do when control input is low or high? + if (self->bPinNumber == COUNTER_UP) { + r_enc_config.lctrl_mode = PCNT_MODE_KEEP; // Keep the primary counter mode if low + r_enc_config.hctrl_mode = PCNT_MODE_REVERSE; // Reverse counting direction if high + } else { + r_enc_config.lctrl_mode = PCNT_MODE_REVERSE; // Reverse counting direction if low + r_enc_config.hctrl_mode = PCNT_MODE_KEEP; // Keep the primary counter mode if high + } + + // Set the maximum and minimum limit values to watch + r_enc_config.counter_h_lim = INT16_ROLL; + r_enc_config.counter_l_lim = -INT16_ROLL; + + check_esp_err(pcnt_unit_config(&r_enc_config)); + + // make sure channel 1 is not set + r_enc_config.unit = self->unit; + r_enc_config.channel = PCNT_CHANNEL_1; // channel 1 + + if (args[ARG__src].u_obj != MP_OBJ_NULL) { + r_enc_config.pulse_gpio_num = self->bPinNumber; // Pulse input GPIO number, a negative value will be ignored + r_enc_config.ctrl_gpio_num = PCNT_PIN_NOT_USED; // Control signal input GPIO number, a negative value will be ignored + + // What to do when control input is low or high? + r_enc_config.lctrl_mode = PCNT_MODE_REVERSE; // Reverse counting direction if low + r_enc_config.hctrl_mode = PCNT_MODE_KEEP; // Keep the primary counter mode if high + } else { + r_enc_config.pos_mode = PCNT_COUNT_DIS; // disabling channel 1 + r_enc_config.neg_mode = PCNT_COUNT_DIS; // disabling channel 1 + + r_enc_config.lctrl_mode = PCNT_MODE_DISABLE; // disabling channel 1 + r_enc_config.hctrl_mode = PCNT_MODE_DISABLE; // disabling channel 1 + } + + check_esp_err(pcnt_unit_config(&r_enc_config)); + + if (direction != MP_OBJ_NULL) { + if (GPIO_ID_IS_PIN_REGISTER(r_enc_config.ctrl_gpio_num)) { + gpio_set_direction(r_enc_config.ctrl_gpio_num, GPIO_MODE_INPUT_OUTPUT); + } + } + + if (args[ARG_filter_ns].u_int != -1) { + self->filter = ns_to_filter(args[ARG_filter_ns].u_int); + } + // Filter out bounces and noise + set_filter_value(self->unit, self->filter); + pcnts[self->unit] = self; + + // Enable interrupts for PCNT unit + check_esp_err(pcnt_counter_pause(self->unit)); + register_isr_handler(); + check_esp_err(pcnt_intr_enable(self->unit)); + // Enable events on maximum and minimum limit values + check_esp_err(pcnt_event_enable(self->unit, PCNT_EVT_H_LIM)); + check_esp_err(pcnt_event_enable(self->unit, PCNT_EVT_L_LIM)); + check_esp_err(pcnt_counter_clear(self->unit)); + self->counter = 0; + check_esp_err(pcnt_counter_resume(self->unit)); +} + +STATIC int find_free_unit() { + for (int id = 0; id < PCNT_UNIT_MAX; ++id) { + if (!pcnts[id]) { + return id; + } + } + return -1; +} + +STATIC void pcnt_init_new(mp_pcnt_obj_t *self, size_t n_args, const mp_obj_t *args) { + self->aPinNumber = PCNT_PIN_NOT_USED; + self->bPinNumber = PCNT_PIN_NOT_USED; + + self->filter = 0; + self->counter = 0; + + self->status = 0; + self->match1 = 0; + self->handler_match1 = MP_OBJ_NULL; + self->handler_zero = MP_OBJ_NULL; + + self->unit = mp_obj_get_int(args[0]); + if (self->unit == -1) { + self->unit = find_free_unit(); + if (self->unit < 0) { + mp_raise_msg_varg(&mp_type_RuntimeError, MP_ERROR_TEXT("out of PCNT units:%d"), PCNT_UNIT_MAX - 1); + } + } + if ((self->unit < 0) || (self->unit >= PCNT_UNIT_MAX)) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("id must be from 0 to %d"), PCNT_UNIT_MAX - 1); + } + if (pcnts[self->unit] != MP_OBJ_NULL) { + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("already used")); + } + + if (n_args >= 2) { + self->aPinNumber = pin_or_int(args[1]); + } +} + +STATIC mp_obj_t machine_Counter_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 1, 4, true); + + // create Counter object for the given unit + mp_pcnt_obj_t *self = m_new_obj(mp_pcnt_obj_t); + self->base.type = &machine_Counter_type; + + pcnt_init_new(self, n_args, args); + + // Process the remaining parameters + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + mp_machine_Counter_init_helper(self, n_args - 1, args + 1, &kw_args); + return MP_OBJ_FROM_PTR(self); +} + +STATIC mp_obj_t machine_Counter_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + mp_machine_Counter_init_helper(args[0], n_args - 1, args + 1, kw_args); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(machine_Counter_init_obj, 1, machine_Counter_init); + +STATIC void common_print_kw(const mp_print_t *print, mp_pcnt_obj_t *self) { + if (self->handler_match1 != MP_OBJ_NULL) { + mp_printf(print, ", match1=%ld", self->match1); + } + if (self->handler_zero != MP_OBJ_NULL) { + mp_printf(print, ", match=0"); + } + mp_printf(print, ", filter_ns=%d)", filter_to_ns(self->filter)); +} + +STATIC void machine_Counter_print(const mp_print_t *print, mp_obj_t self_obj, mp_print_kind_t kind) { + mp_pcnt_obj_t *self = MP_OBJ_TO_PTR(self_obj); + + mp_printf(print, "Counter(%u, src=Pin(%u)", self->unit, self->aPinNumber); + if (self->x124 < 0) { + mp_printf(print, ", _src=Pin(%u)", self->bPinNumber); + } else { + if (self->bPinNumber >= 0) { + mp_printf(print, ", direction=Pin(%u)", self->bPinNumber); + } else { + mp_printf(print, ", direction=Counter.%s", self->bPinNumber == COUNTER_UP ? "UP" : "DOWN"); + } + } + mp_printf(print, ", edge=Counter.%s", self->edge == RISING ? "RISING" : self->edge == FALLING ? "FALLING" : "RISING | Counter.FALLING"); + common_print_kw(print, self); +} + +// Register class methods +#define COMMON_METHODS \ + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&machine_PCNT_deinit_obj) }, \ + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&machine_PCNT_deinit_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&machine_PCNT_count_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_get_value), MP_ROM_PTR(&machine_PCNT_get_count_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_filter_ns), MP_ROM_PTR(&machine_PCNT_filter_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_pause), MP_ROM_PTR(&machine_PCNT_pause_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_resume), MP_ROM_PTR(&machine_PCNT_resume_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_id), MP_ROM_PTR(&machine_PCNT_id_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&machine_PCNT_status_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_irq), MP_ROM_PTR(&machine_PCNT_irq_obj) } + +#define COMMON_CONSTANTS \ + { MP_ROM_QSTR(MP_QSTR_IRQ_ZERO), MP_ROM_INT(PCNT_EVT_ZERO) }, \ + { MP_ROM_QSTR(MP_QSTR_IRQ_MATCH1), MP_ROM_INT(PCNT_EVT_THRES_1) } + +STATIC const mp_rom_map_elem_t machine_Counter_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_Counter_init_obj) }, + COMMON_METHODS, + COMMON_CONSTANTS, + { MP_ROM_QSTR(MP_QSTR_RISING), MP_ROM_INT(RISING) }, + { MP_ROM_QSTR(MP_QSTR_FALLING), MP_ROM_INT(FALLING) }, + { MP_ROM_QSTR(MP_QSTR_UP), MP_ROM_INT(COUNTER_UP) }, + { MP_ROM_QSTR(MP_QSTR_DOWN), MP_ROM_INT(COUNTER_DOWN) }, +}; +STATIC MP_DEFINE_CONST_DICT(machine_Counter_locals_dict, machine_Counter_locals_dict_table); + +// Create the class-object itself +MP_DEFINE_CONST_OBJ_TYPE( + machine_Counter_type, + MP_QSTR_Counter, + MP_TYPE_FLAG_NONE, + make_new, machine_Counter_make_new, + print, machine_Counter_print, + locals_dict, &machine_Counter_locals_dict + ); + +// ================================================================= +// class Encoder(object): +STATIC void mp_machine_Encoder_init_helper(mp_pcnt_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_phase_a, ARG_phase_b, ARG_x124, ARG_filter_ns }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_phase_a, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_phase_b, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_x124, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_filter_ns, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t src = args[ARG_phase_a].u_obj; + if (src != MP_OBJ_NULL) { + self->aPinNumber = pin_or_int(src); + } + if (self->aPinNumber == PCNT_PIN_NOT_USED) { + mp_raise_ValueError(MP_ERROR_TEXT(MP_ERROR_TEXT("phase_a"))); + } + src = args[ARG_phase_b].u_obj; + if (src != MP_OBJ_NULL) { + self->bPinNumber = pin_or_int(src); + } + if (self->bPinNumber == PCNT_PIN_NOT_USED) { + mp_raise_ValueError(MP_ERROR_TEXT(MP_ERROR_TEXT("phase_b"))); + } + + if (args[ARG_x124].u_int != -1) { + if ((args[ARG_x124].u_int == 1) || (args[ARG_x124].u_int == 2) || (args[ARG_x124].u_int == 4)) { + self->x124 = args[ARG_x124].u_int; + } else { + mp_raise_ValueError(MP_ERROR_TEXT(MP_ERROR_TEXT("x124 must be 1, 2, 4"))); + } + } + + // Set up Encoder PCNT configuration + pcnt_config_t r_enc_config; + r_enc_config.unit = self->unit; + r_enc_config.channel = PCNT_CHANNEL_0; // channel 0 + + r_enc_config.pulse_gpio_num = self->aPinNumber; // Rotary Encoder Chan A + r_enc_config.ctrl_gpio_num = self->bPinNumber; // Rotary Encoder Chan B + + r_enc_config.pos_mode = PCNT_COUNT_INC; // X1 X2 X4 Count on Rising-Edges + r_enc_config.neg_mode = (self->x124 != 1) ? PCNT_COUNT_DEC : PCNT_COUNT_DIS; // X2 X4 Count on Falling-Edges + + r_enc_config.lctrl_mode = PCNT_MODE_KEEP; // Rising A on HIGH B + r_enc_config.hctrl_mode = PCNT_MODE_REVERSE; // Rising A on LOW B + + // Set the maximum and minimum limit values to watch + r_enc_config.counter_h_lim = INT16_ROLL; + r_enc_config.counter_l_lim = -INT16_ROLL; + + check_esp_err(pcnt_unit_config(&r_enc_config)); + + if (self->x124 == 4) { // X4 + // set up second channel for full quad + r_enc_config.unit = self->unit; + r_enc_config.channel = PCNT_CHANNEL_1; // channel 1 + + r_enc_config.pulse_gpio_num = self->bPinNumber; // make prior control into signal + r_enc_config.ctrl_gpio_num = self->aPinNumber; // and prior signal into control + + r_enc_config.pos_mode = PCNT_COUNT_INC; // Count on Rising-Edges + r_enc_config.neg_mode = PCNT_COUNT_DEC; // Count on Falling-Edge + + r_enc_config.lctrl_mode = PCNT_MODE_REVERSE; // prior high mode is now low + r_enc_config.hctrl_mode = PCNT_MODE_KEEP; // prior low mode is now high + + check_esp_err(pcnt_unit_config(&r_enc_config)); + } else { + // make sure channel 1 is not set when not full quad + r_enc_config.unit = self->unit; + r_enc_config.channel = PCNT_CHANNEL_1; // channel 1 + + r_enc_config.pos_mode = PCNT_COUNT_DIS; // disabling channel 1 + r_enc_config.neg_mode = PCNT_COUNT_DIS; // disabling channel 1 + + r_enc_config.lctrl_mode = PCNT_MODE_DISABLE; // disabling channel 1 + r_enc_config.hctrl_mode = PCNT_MODE_DISABLE; // disabling channel 1 + + check_esp_err(pcnt_unit_config(&r_enc_config)); + } + + if (args[ARG_filter_ns].u_int != -1) { + self->filter = ns_to_filter(args[ARG_filter_ns].u_int); + } + // Filter out bounces and noise + set_filter_value(self->unit, self->filter); + pcnts[self->unit] = self; + + // Enable interrupts for PCNT unit + check_esp_err(pcnt_counter_pause(self->unit)); + register_isr_handler(); + check_esp_err(pcnt_intr_enable(self->unit)); + // Enable events on maximum and minimum limit values + check_esp_err(pcnt_event_enable(self->unit, PCNT_EVT_H_LIM)); + check_esp_err(pcnt_event_enable(self->unit, PCNT_EVT_L_LIM)); + check_esp_err(pcnt_counter_clear(self->unit)); + self->counter = 0; + check_esp_err(pcnt_counter_resume(self->unit)); +} + +STATIC mp_obj_t machine_Encoder_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 1, 5, true); + + // create Encoder object for the given unit + mp_pcnt_obj_t *self = m_new_obj(mp_pcnt_obj_t); + self->base.type = &machine_Encoder_type; + + pcnt_init_new(self, n_args, args); + + if (n_args >= 3) { + self->bPinNumber = pin_or_int(args[2]); + } + self->x124 = 4; + + // Process the remaining parameters + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + mp_machine_Encoder_init_helper(self, n_args - 1, args + 1, &kw_args); + return MP_OBJ_FROM_PTR(self); +} + +STATIC mp_obj_t machine_Encoder_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + mp_machine_Encoder_init_helper(args[0], n_args - 1, args + 1, kw_args); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(machine_Encoder_init_obj, 1, machine_Encoder_init); + +STATIC void machine_Encoder_print(const mp_print_t *print, mp_obj_t self_obj, mp_print_kind_t kind) { + mp_pcnt_obj_t *self = MP_OBJ_TO_PTR(self_obj); + + mp_printf(print, "Encoder(%u, phase_a=Pin(%u), phase_b=Pin(%u), x124=%d", self->unit, self->aPinNumber, self->bPinNumber, self->x124); + common_print_kw(print, self); +} + +// Register class methods +STATIC const mp_rom_map_elem_t machine_Encoder_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_Encoder_init_obj) }, + COMMON_METHODS, + COMMON_CONSTANTS +}; +STATIC MP_DEFINE_CONST_DICT(machine_Encoder_locals_dict, machine_Encoder_locals_dict_table); + +// Create the class-object itself +MP_DEFINE_CONST_OBJ_TYPE( + machine_Encoder_type, + MP_QSTR_Encoder, + MP_TYPE_FLAG_NONE, + make_new, machine_Encoder_make_new, + print, machine_Encoder_print, + locals_dict, &machine_Encoder_locals_dict + ); + +#endif // MICROPY_PY_MACHINE_PCNT diff --git a/ports/esp32/machine_encoder.h b/ports/esp32/machine_encoder.h new file mode 100644 index 000000000000..bf229afaf69c --- /dev/null +++ b/ports/esp32/machine_encoder.h @@ -0,0 +1,42 @@ +#ifndef MICROPY_INCLUDED_MACHINE_ENCODER_H +#define MICROPY_INCLUDED_MACHINE_ENCODER_H + +// #define USE_INT64 +#ifdef USE_INT64 +typedef int64_t counter_t; +#else +typedef int32_t counter_t; +#endif + +#define INT16_ROLL ((counter_t)32767) + +#define FILTER_MAX 1023 + +enum edgeKind { + RISING = 0x1, + FALLING = 0x2 +}; + +#define COUNTER_UP (-2) +#define COUNTER_DOWN (-4) + +typedef struct _mp_pcnt_obj_t { + mp_obj_base_t base; + int unit; + + int aPinNumber; + int bPinNumber; + + volatile counter_t counter; + + counter_t match1; + mp_obj_t handler_match1; + mp_obj_t handler_zero; + int status; + + int filter; + enum edgeKind edge; // Counter only + int8_t x124; // Encoder: multiplier 124 // Counter: 0 is 'direction=' keyword used, -1 is '_src=' keyword used +} mp_pcnt_obj_t; + +#endif // MICROPY_INCLUDED_MACHINE_ENCODER_H diff --git a/ports/esp32/main.c b/ports/esp32/main.c index 044b43655c66..44774d145581 100644 --- a/ports/esp32/main.c +++ b/ports/esp32/main.c @@ -183,6 +183,9 @@ void mp_task(void *pvParameter) { // deinitialise peripherals machine_pwm_deinit_all(); // TODO: machine_rmt_deinit_all(); + #if MICROPY_PY_MACHINE_PCNT + machine_encoder_deinit_all(); + #endif machine_pins_deinit(); machine_deinit(); #if MICROPY_PY_SOCKET_EVENTS diff --git a/ports/esp32/modmachine.c b/ports/esp32/modmachine.c index 461ddacdad1d..61cda6b631e2 100644 --- a/ports/esp32/modmachine.c +++ b/ports/esp32/modmachine.c @@ -330,6 +330,10 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { #if MICROPY_PY_MACHINE_UART { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&machine_uart_type) }, #endif + #if MICROPY_PY_MACHINE_PCNT + { MP_ROM_QSTR(MP_QSTR_Counter), MP_ROM_PTR(&machine_Counter_type) }, + { MP_ROM_QSTR(MP_QSTR_Encoder), MP_ROM_PTR(&machine_Encoder_type) }, + #endif // Reset reasons { MP_ROM_QSTR(MP_QSTR_reset_cause), MP_ROM_PTR(&machine_reset_cause_obj) }, diff --git a/ports/esp32/modmachine.h b/ports/esp32/modmachine.h index 06a1d7b0e2b6..2d2bc3bb77bf 100644 --- a/ports/esp32/modmachine.h +++ b/ports/esp32/modmachine.h @@ -12,6 +12,8 @@ typedef enum { extern const mp_obj_type_t machine_touchpad_type; extern const mp_obj_type_t machine_dac_type; extern const mp_obj_type_t machine_sdcard_type; +extern const mp_obj_type_t machine_Counter_type; +extern const mp_obj_type_t machine_Encoder_type; void machine_init(void); void machine_deinit(void); @@ -19,6 +21,7 @@ void machine_pins_init(void); void machine_pins_deinit(void); void machine_pwm_deinit_all(void); // TODO: void machine_rmt_deinit_all(void); +void machine_encoder_deinit_all(void); void machine_timer_deinit_all(void); void machine_i2s_init0(); NORETURN mp_obj_t machine_bootloader(size_t n_args, const mp_obj_t *args); diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index 706baae5b29a..c46e4721268a 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -116,6 +116,9 @@ #define MICROPY_PY_MACHINE_SPI (1) #define MICROPY_PY_MACHINE_SPI_MSB (0) #define MICROPY_PY_MACHINE_SPI_LSB (1) +#ifndef MICROPY_PY_MACHINE_PCNT +#define MICROPY_PY_MACHINE_PCNT (1) +#endif #define MICROPY_PY_MACHINE_SOFTSPI (1) #ifndef MICROPY_PY_MACHINE_DAC #define MICROPY_PY_MACHINE_DAC (1)