From 61bbd78eba8dea17d8bad52ea6f6ab5212c6463c Mon Sep 17 00:00:00 2001 From: robert-hh Date: Fri, 24 Sep 2021 21:47:31 +0200 Subject: [PATCH] mimxrt: Implement Quadrature Encoder and Counter classes. These classes are based on the Quadrature Encoder blocks of the i.MXRT MCUs. The i.MXRT 102x has two encoders, the other ones four. The i.MXRT 101x does not support this function. It is implemented as two classes, Encoder and Counter. The number of pins that can be uses as inputs is limited by the MCU architecture and the board schematics. The Encoder class supports: - Defining the module. - Defining the input pins. - Defining a pin for an index signal. - Defining a pin for a reset signal. - Defining an output pin showing the compare match signal. - Setting the number of cycles per revolution (min/max). - Setting the initial value for the position. - Setting the counting direction. - Setting a glitch filter. - Defining callbacks for getting to a specific position, overrun and underrun (starting the next revolution). These callbacks can be hard interrupts to ensure short latency. The encoder counts all phases of a cycle. The span for the position is 2**32, for the revolution is 2**16. The highest input frequency is CPU-Clock/24. Note that the "phases" argument is emulated at the API level (the hardware will always count all phases). The Counter mode counts single pulses on input A of the Encoder. The configuration supports: - Defining the module. - Defining the input pin. - Defining the counting direction, either fixed or controlled by the level of an input pin. - Defining a pin for an index signal. - Defining an output pin showing the compare match signal. - Setting the counter value. - Setting the glitch filter. - Defining a callback which is called at a certain value. - Settings for MIMXRT1015. The MIMXRT1015 MCU has only one encoder/counter unit. The counting range is 0 - 2**32-1 and a 16 bit overrun counter. The highest input frequency is CPU-Clock/12. The implementation of the `.irq()` method uses the common code from `shared/runtime/mpirq.c`, including the `irq().flags()` and `irq().trigger()` methods. Signed-off-by: robert-hh --- extmod/modmachine.h | 4 + ports/mimxrt/Makefile | 10 +- .../boards/MIMXRT1010_EVK/mpconfigboard.h | 2 + ports/mimxrt/boards/MIMXRT1011_af.csv | 4 +- .../boards/MIMXRT1015_EVK/mpconfigboard.h | 2 + ports/mimxrt/boards/MIMXRT1015_EVK/pins.csv | 2 + .../boards/MIMXRT1020_EVK/mpconfigboard.h | 2 + .../boards/OLIMEX_RT1010/mpconfigboard.h | 2 + ports/mimxrt/machine_encoder.c | 870 ++++++++++++++++++ ports/mimxrt/machine_pin.c | 22 +- ports/mimxrt/main.c | 4 + ports/mimxrt/modmachine.c | 9 + ports/mimxrt/mpconfigport.h | 2 + 13 files changed, 921 insertions(+), 14 deletions(-) create mode 100755 ports/mimxrt/machine_encoder.c diff --git a/extmod/modmachine.h b/extmod/modmachine.h index ef507aca74..53660a7b7a 100644 --- a/extmod/modmachine.h +++ b/extmod/modmachine.h @@ -217,6 +217,10 @@ extern const mp_obj_type_t machine_timer_type; extern const mp_obj_type_t machine_uart_type; extern const mp_obj_type_t machine_usbd_type; extern const mp_obj_type_t machine_wdt_type; +#if MICROPY_PY_MACHINE_QECNT +extern const mp_obj_type_t machine_encoder_type; +extern const mp_obj_type_t machine_counter_type; +#endif #if MICROPY_PY_MACHINE_SOFTI2C extern const mp_obj_type_t mp_machine_soft_i2c_type; diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index 9694464b5b..ba43e3c489 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -190,9 +190,16 @@ endif ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES), MIMXRT1015 MIMXRT1021 MIMXRT1052 MIMXRT1062 MIMXRT1064 MIMXRT1176)) SRC_HAL_IMX_C += \ $(MCUX_SDK_DIR)/drivers/qtmr_1/fsl_qtmr.c \ + $(MCUX_SDK_DIR)/drivers/enc/fsl_enc.c \ + $(MCUX_SDK_DIR)/drivers/xbara/fsl_xbara.c \ $(MCU_DIR)/drivers/fsl_romapi.c -INC_HAL_IMX += -I$(TOP)/$(MCUX_SDK_DIR)/drivers/qtmr_1 +INC_HAL_IMX += \ + -I$(TOP)/$(MCUX_SDK_DIR)/drivers/enc \ + -I$(TOP)/$(MCUX_SDK_DIR)/drivers/xbara \ + -I$(TOP)/$(MCUX_SDK_DIR)/drivers/qtmr_1 + +CFLAGS += -DMICROPY_PY_MACHINE_QECNT=1 endif # If not empty, then it is 10xx. @@ -256,6 +263,7 @@ SRC_C += \ machine_i2c.c \ machine_led.c \ machine_pin.c \ + machine_encoder.c \ machine_rtc.c \ machine_sdcard.c \ machine_spi.c \ diff --git a/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.h index a616fbec64..4950e70ef9 100644 --- a/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.h @@ -82,3 +82,5 @@ I2S_GPIO(1, WS, TX, GPIO_07, IOMUXC_GPIO_07_SAI1_TX_SYNC), \ I2S_GPIO(1, SD, TX, GPIO_04, IOMUXC_GPIO_04_SAI1_TX_DATA00), \ } + +#define XBARA1 XBARA diff --git a/ports/mimxrt/boards/MIMXRT1011_af.csv b/ports/mimxrt/boards/MIMXRT1011_af.csv index be8a9c634d..1899c4f7a3 100644 --- a/ports/mimxrt/boards/MIMXRT1011_af.csv +++ b/ports/mimxrt/boards/MIMXRT1011_af.csv @@ -20,14 +20,14 @@ GPIO_AD_03,LPSPI1_SDI,PIT_TRIGGER3,FLEXPWM1_PWM2_B,KPP_ROW2,GPT2_CLK,GPIO1_IO17, GPIO_AD_04,LPSPI1_SDO,PIT_TRIGGER2,FLEXPWM1_PWM2_A,KPP_COL2,GPT2_COMPARE1,GPIO1_IO18,SNVS_VIO_5_CTL,,,,ADC1_IN4,,ALT5 GPIO_AD_05,LPSPI1_PCS0,PIT_TRIGGER1,FLEXPWM1_PWM3_B,KPP_ROW1,GPT2_CAPTURE1,GPIO1_IO19,,,,,ADC1_IN5,,ALT5 GPIO_AD_06,LPSPI1_SCK,PIT_TRIGGER0,FLEXPWM1_PWM3_A,KPP_COL1,GPT2_COMPARE2,GPIO1_IO20,LPI2C1_HREQ,,,,ADC1_IN6,,ALT5 -GPIO_AD_07,LPI2C2_SDA,LPUART3_RXD,ARM_CM7_RXEV,LPUART2_RTS_B,GPT2_CAPTURE2,GPIO1_IO21,OCOTP_FUSE_LATCHED,XBAR1_INOUT03,,,ADC1_IN7,,ALT5 +GPIO_AD_07,LPI2C2_SDA,LPUART3_RXD,ARM_CM7_RXEV,LPUART2_RTS_B,GPT2_CAPTURE2,GPIO1_IO21,OCOTP_FUSE_LATCHED,XBAR_INOUT03,,,ADC1_IN7,,ALT5 GPIO_AD_08,LPI2C2_SCL,LPUART3_TXD,ARM_CM7_TXEV,LPUART2_CTS_B,GPT2_COMPARE3,GPIO1_IO22,EWM_OUT_B,JTAG_TRSTB,,,ADC1_IN8,,ALT7 GPIO_AD_09,LPSPI2_SDI,FLEXPWM1_PWM3_X,KPP_ROW2,ARM_TRACE_SWO,FLEXIO1_IO21,GPIO1_IO23,REF_CLK_32K,JTAG_TDO,,,ADC1_IN9,,ALT7 GPIO_AD_10,LPSPI2_SDO,FLEXPWM1_PWM2_X,KPP_COL2,PIT_TRIGGER3,FLEXIO1_IO22,GPIO1_IO24,USB_OTG1_ID,JTAG_TDI,,,ADC1_IN10,,ALT7 GPIO_AD_11,LPSPI2_PCS0,FLEXPWM1_PWM1_X,KPP_ROW1,PIT_TRIGGER2,FLEXIO1_IO23,GPIO1_IO25,WDOG1_B,JTAG_MOD,,,ADC1_IN11,,ALT7 GPIO_AD_12,LPSPI2_SCK,FLEXPWM1_PWM0_X,KPP_COL1,PIT_TRIGGER1,FLEXIO1_IO24,GPIO1_IO26,USB_OTG1_PWR,JTAG_TCK,,,ADC1_IN12,,ALT7 GPIO_AD_13,LPI2C1_SDA,LPUART3_RTS_B,KPP_ROW0,LPUART4_RTS_B,FLEXIO1_IO25,GPIO1_IO27,ARM_NMI,JTAG_TMS,,,ADC1_IN13,,ALT7 -GPIO_AD_14,LPI2C1_SCL,LPUART3_CTS_B,KPP_COL0,LPUART4_CTS_B,FLEXIO1_IO26,GPIO1_IO28,REF_CLK_24M,XBAR1_INOUT02,,,ADC1_IN14,,ALT5 +GPIO_AD_14,LPI2C1_SCL,LPUART3_CTS_B,KPP_COL0,LPUART4_CTS_B,FLEXIO1_IO26,GPIO1_IO28,REF_CLK_24M,XBAR_INOUT02,,,ADC1_IN14,,ALT5 GPIO_SD_00,FLEXSPI_B_SS0_B,SAI3_TX_SYNC,ARM_CM7_RXEV,CCM_STOP,FLEXIO1_IO06,GPIO2_IO00,SRC_BT_CFG2,,,,,,ALT5 GPIO_SD_01,FLEXSPI_B_DATA1,SAI3_TX_BCLK,FLEXPWM1_PWM0_B,CCM_CLKO2,FLEXIO1_IO07,GPIO2_IO01,SRC_BT_CFG1,,,,,,ALT5 GPIO_SD_02,FLEXSPI_B_DATA2,SAI3_TX_DATA,FLEXPWM1_PWM0_A,CCM_CLKO1,FLEXIO1_IO08,GPIO2_IO02,SRC_BT_CFG0,,,,,,ALT5 diff --git a/ports/mimxrt/boards/MIMXRT1015_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1015_EVK/mpconfigboard.h index f47bbf4fa1..1303aade72 100644 --- a/ports/mimxrt/boards/MIMXRT1015_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1015_EVK/mpconfigboard.h @@ -87,3 +87,5 @@ I2S_GPIO(1, WS, TX, GPIO_EMC_27, IOMUXC_GPIO_EMC_27_SAI1_TX_SYNC), \ I2S_GPIO(1, SD, TX, GPIO_EMC_25, IOMUXC_GPIO_EMC_25_SAI1_TX_DATA00), \ } + +#define XBARA1 XBARA diff --git a/ports/mimxrt/boards/MIMXRT1015_EVK/pins.csv b/ports/mimxrt/boards/MIMXRT1015_EVK/pins.csv index 2b50c7ca76..d41dc30aa0 100644 --- a/ports/mimxrt/boards/MIMXRT1015_EVK/pins.csv +++ b/ports/mimxrt/boards/MIMXRT1015_EVK/pins.csv @@ -22,6 +22,8 @@ A4,GPIO_AD_B1_15 A5,GPIO_AD_B1_14 RX,GPIO_EMC_33 TX,GPIO_EMC_32 +ENC1,GPIO_EMC_06 +ENC2,GPIO_EMC_07 SDA,GPIO_AD_B1_15 SCL,GPIO_AD_B1_14 SCK,GPIO_AD_B0_10 diff --git a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h index b4f777cf93..5f41dfdfd9 100644 --- a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h @@ -181,3 +181,5 @@ { IOMUXC_GPIO_AD_B0_15_ENET_TDATA01, 0, 0xB0E9u }, \ { IOMUXC_GPIO_EMC_40_ENET_MDIO, 0, 0xB0E9u }, \ { IOMUXC_GPIO_EMC_41_ENET_MDC, 0, 0xB0E9u }, + +#define XBARA1 XBARA diff --git a/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.h b/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.h index 1ad2e9df5e..4d48864719 100644 --- a/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.h +++ b/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.h @@ -88,3 +88,5 @@ I2S_GPIO(3, WS, TX, GPIO_SD_00, IOMUXC_GPIO_SD_00_SAI3_TX_SYNC), /* pin D9 */ \ I2S_GPIO(3, SD, TX, GPIO_SD_02, IOMUXC_GPIO_SD_02_SAI3_TX_DATA) /* pin D11 */ \ } + +#define XBARA1 XBARA diff --git a/ports/mimxrt/machine_encoder.c b/ports/mimxrt/machine_encoder.c new file mode 100755 index 0000000000..b3744ffabd --- /dev/null +++ b/ports/mimxrt/machine_encoder.c @@ -0,0 +1,870 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * Copyright (c) 2026 Robert Hammelrath + * + * 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. + */ + +#if MICROPY_PY_MACHINE_QECNT + +#include "py/runtime.h" +#include "py/mphal.h" +#include "py/objint.h" +#include "shared/runtime/mpirq.h" +#include "extmod/modmachine.h" +#include "modmachine.h" +#include "fsl_clock.h" +#include "fsl_enc.h" +#include "fsl_xbara.h" +#include "fsl_iomuxc.h" +#include "fsl_gpio.h" + +typedef struct _machine_encoder_obj_t { + mp_obj_base_t base; + ENC_Type *instance; + int8_t id; + bool active; + uint8_t input_a; + uint8_t input_b; + uint8_t mode; + uint8_t match_pin; + uint8_t phases_inv; + uint32_t max_count; + uint32_t min_count; + uint32_t filter; + mp_obj_t reset_pin; + uint16_t mp_irq_flags; + uint16_t mp_irq_trigger; + mp_irq_obj_t *mp_irq_obj; + enc_config_t enc_config; +} machine_encoder_obj_t; + +typedef struct _encoder_xbar_signal_t { + xbar_output_signal_t enc_input_a; + xbar_output_signal_t enc_input_b; + xbar_output_signal_t enc_index; + xbar_output_signal_t enc_home; + xbar_output_signal_t enc_trigger; + xbar_input_signal_t enc_match; +} encoder_xbar_signal_t; + +#define ENCODER_ALL_INTERRUPTS (0x7f) + +#define XBAR_IN (1) +#define XBAR_OUT (0) + +#define COUNTER_UP (-2) +#define COUNTER_DOWN (-3) +#define MODE_ENCODER (0) +#define MODE_COUNTER (1) +#define MP_ENCODER_ALLOWED_FLAGS (kENC_HOMETransitionFlag | kENC_INDEXPulseFlag | kENC_PositionCompareFlag | kENC_PositionRollUnderFlag | kENC_PositionRollOverFlag) +static void encoder_deinit_single(machine_encoder_obj_t *self); + +#if defined MIMXRT117x_SERIES + +#define XBAR_ENC_DIR_OFFSET_1 (4) +#define XBAR_ENC_DIR_REGISTER_1 GPR20 +#define XBAR_ENC_DIR_OFFSET_2 (32) +#define XBAR_ENC_DIR_REGISTER_2 GPR21 +#define XBAR_OUT_MIN (4) +#define XBAR_OUT_MAX (42) +#define XBAR_STRING "XBAR1_INOUT" +#define XBAR_STRING_LEN strlen(XBAR_STRING) + +static encoder_xbar_signal_t xbar_signal_table[FSL_FEATURE_SOC_ENC_COUNT] = { + { kXBARA1_OutputDec1Phasea, + kXBARA1_OutputDec1Phaseb, + kXBARA1_OutputDec1Index, + kXBARA1_OutputDec1Home, + kXBARA1_OutputDec1Trigger, + kXBARA1_InputDec1PosMatch }, + + { kXBARA1_OutputDec2Phasea, + kXBARA1_OutputDec2Phaseb, + kXBARA1_OutputDec2Index, + kXBARA1_OutputDec2Home, + kXBARA1_OutputDec2Trigger, + kXBARA1_InputDec2PosMatch }, + + { kXBARA1_OutputDec3Phasea, + kXBARA1_OutputDec3Phaseb, + kXBARA1_OutputDec3Index, + kXBARA1_OutputDec3Home, + kXBARA1_OutputDec3Trigger, + kXBARA1_InputDec3PosMatch }, + + { kXBARA1_OutputDec4Phasea, + kXBARA1_OutputDec4Phaseb, + kXBARA1_OutputDec4Index, + kXBARA1_OutputDec4Home, + kXBARA1_OutputDec4Trigger, + kXBARA1_InputDec4PosMatch }, +}; + +#else // defined MIMXRT117x_SERIES + +#if !defined(XBAR_ENC_DIR_OFFSET) +#define XBAR_ENC_DIR_OFFSET (12) +#define XBAR_ENC_DIR_REGISTER GPR6 +#define XBAR_OUT_MIN (4) +#define XBAR_OUT_MAX (19) +#endif +#define XBAR_STRING "XBAR_INOUT" +#define XBAR_STRING_LEN strlen(XBAR_STRING) + +static encoder_xbar_signal_t xbar_signal_table[FSL_FEATURE_SOC_ENC_COUNT] = { + { kXBARA1_OutputEnc1PhaseAInput, + kXBARA1_OutputEnc1PhaseBInput, + kXBARA1_OutputEnc1Index, + kXBARA1_OutputEnc1Home, + kXBARA1_OutputEnc1Trigger, + kXBARA1_InputEnc1PosMatch }, + + #if FSL_FEATURE_SOC_ENC_COUNT > 1 + + { kXBARA1_OutputEnc2PhaseAInput, + kXBARA1_OutputEnc2PhaseBInput, + kXBARA1_OutputEnc2Index, + kXBARA1_OutputEnc2Home, + kXBARA1_OutputEnc2Trigger, + kXBARA1_InputEnc2PosMatch }, + + #if FSL_FEATURE_SOC_ENC_COUNT > 2 + + { kXBARA1_OutputEnc3PhaseAInput, + kXBARA1_OutputEnc3PhaseBInput, + kXBARA1_OutputEnc3Index, + kXBARA1_OutputEnc3Home, + kXBARA1_OutputEnc3Trigger, + kXBARA1_InputEnc3PosMatch }, + + { kXBARA1_OutputEnc4PhaseAInput, + kXBARA1_OutputEnc4PhaseBInput, + kXBARA1_OutputEnc4Index, + kXBARA1_OutputEnc4Home, + kXBARA1_OutputEnc4Trigger, + kXBARA1_InputEnc4PosMatch }, + + #endif + #endif +}; +#endif // defined MIMXRT117x_SERIES + +static machine_encoder_obj_t *encoder_table[FSL_FEATURE_SOC_ENC_COUNT]; +static ENC_Type *enc_instances[] = ENC_BASE_PTRS; +static IRQn_Type enc_irqn[] = ENC_COMPARE_IRQS; + +__attribute__((section(".ram_functions"))) void irq_callback(int irq_num) { + machine_encoder_obj_t *self = encoder_table[irq_num]; + if (self != NULL) { + self->mp_irq_flags = ENC_GetStatusFlags(self->instance); + // In case of a position match event, disable that interrupt such that is is only handled + // once until enabled again. This is needed since otherwise the match interrupt will + // be triggered again as long as the match condition is true. + if (self->mp_irq_flags & kENC_PositionCompareFlag) { + ENC_DisableInterrupts(self->instance, kENC_PositionCompareInerruptEnable); + } + ENC_ClearStatusFlags(self->instance, self->mp_irq_flags); + __DSB(); + mp_irq_handler(self->mp_irq_obj); + } +} + +__attribute__((section(".ram_functions"))) void ENC1_IRQHandler(void) { + irq_callback(0); +} + +#if FSL_FEATURE_SOC_ENC_COUNT > 1 + +__attribute__((section(".ram_functions"))) void ENC2_IRQHandler(void) { + irq_callback(1); +} + +#if FSL_FEATURE_SOC_ENC_COUNT > 2 +__attribute__((section(".ram_functions"))) void ENC3_IRQHandler(void) { + irq_callback(2); +} + +__attribute__((section(".ram_functions"))) void ENC4_IRQHandler(void) { + irq_callback(3); +} +#endif +#endif + +static void mp_machine_encoder_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_encoder_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (self->mode == MODE_ENCODER) { + int32_t min = (int32_t)self->min_count; + if (min > 0) { + min += (self->phases_inv - 1); + } + int32_t max = (int32_t)self->max_count; + if (max > 0) { + max += (self->phases_inv - 1); + } + int32_t match = (int32_t)self->enc_config.positionCompareValue; + if (match > 0) { + match += (self->phases_inv - 1); + } + mp_printf(print, "Encoder(%d, phases=%d, max=%ld, min=%ld, match=%ld, filter_ns=%lu)", + self->id, 4 / self->phases_inv, + max / self->phases_inv, min / self->phases_inv, + match / self->phases_inv, self->filter); + } else { + mp_printf(print, "Counter(%d, max=%ld, min=%ld, match=%ld, filter_ns=%lu)", + self->id, self->max_count, self->min_count, + self->enc_config.positionCompareValue, self->filter); + } +} + +// Utility functions +// + +static void encoder_set_iomux(const machine_pin_obj_t *pin, const machine_pin_af_obj_t *af) { + IOMUXC_SetPinMux(pin->muxRegister, af->af_mode, af->input_register, af->input_daisy, pin->configRegister, 0U); + IOMUXC_SetPinConfig(pin->muxRegister, af->af_mode, af->input_register, af->input_daisy, pin->configRegister, 0x10B0U); +} + +// decode the AF objects module and Port numer. Returns NULL if it is not a XBAR object +static const machine_pin_af_obj_t *af_name_decode_xbar(const machine_pin_af_obj_t *af_obj, + xbar_input_signal_t *io_number) { + const char *str; + size_t len; + size_t xlen = XBAR_STRING_LEN; + str = (char *)qstr_data(af_obj->name, &len); + // test for the name starting with XBAR + if (len < (xlen + 2) || strncmp(str, XBAR_STRING, xlen) != 0) { + return NULL; + } + // Get I/O number, e.g. XBAR_INOUT03 + *io_number = (str[xlen] - '0') * 10 + (str[xlen + 1] - '0'); + return af_obj; +} + +static const machine_pin_af_obj_t *find_xbar_af(const machine_pin_obj_t *pin, xbar_input_signal_t *io_number) { + const machine_pin_af_obj_t *af = NULL; + for (int i = 0; i < pin->af_list_len; ++i) { + af = af_name_decode_xbar(&(pin->af_list[i]), io_number); + if (af != NULL) { + return af; + } + } + mp_raise_ValueError(MP_ERROR_TEXT("invalid input Pin")); +} + +static uint8_t connect_pin_to_encoder(mp_obj_t desc, xbar_output_signal_t encoder_signal, uint8_t direction) { + xbar_input_signal_t xbar_pin; + const machine_pin_obj_t *pin = pin_find(desc); + const machine_pin_af_obj_t *af = find_xbar_af(pin, &xbar_pin); + encoder_set_iomux(pin, af); + if (direction == XBAR_IN) { + XBARA_SetSignalsConnection(XBARA1, xbar_pin, encoder_signal); + } else { + // No API here, so do basic Register access. + #if defined MIMXRT117x_SERIES + if (xbar_pin >= XBAR_OUT_MIN && xbar_pin <= XBAR_OUT_MAX) { + if (xbar_pin < XBAR_ENC_DIR_OFFSET_2) { + IOMUXC_GPR->XBAR_ENC_DIR_REGISTER_1 |= 1 << (xbar_pin - XBAR_ENC_DIR_OFFSET_1); + XBARA_SetSignalsConnection(XBARA1, encoder_signal, xbar_pin); + } else { + IOMUXC_GPR->XBAR_ENC_DIR_REGISTER_2 |= 1 << (xbar_pin - XBAR_ENC_DIR_OFFSET_2); + XBARA_SetSignalsConnection(XBARA1, encoder_signal, xbar_pin); + } + } else { + mp_raise_ValueError(MP_ERROR_TEXT("invalid match Pin")); + } + #else + if (xbar_pin >= XBAR_OUT_MIN && xbar_pin <= XBAR_OUT_MAX) { + IOMUXC_GPR->XBAR_ENC_DIR_REGISTER |= 1 << (xbar_pin + XBAR_ENC_DIR_OFFSET); // Compare the offset 12 with other MCU + XBARA_SetSignalsConnection(XBARA1, encoder_signal, xbar_pin); + } else { + mp_raise_ValueError(MP_ERROR_TEXT("invalid match Pin")); + } + #endif // defined MIMXRT117x_SERIES + } + return xbar_pin; +} + +static void clear_encoder_registers(machine_encoder_obj_t *self) { + // Create a High pulse on the Trigger input, clearing Position, Revolution and Hold registers. + XBARA_SetSignalsConnection(XBARA1, kXBARA1_InputLogicHigh, xbar_signal_table[self->id].enc_home); + XBARA_SetSignalsConnection(XBARA1, kXBARA1_InputLogicLow, xbar_signal_table[self->id].enc_home); + if (self->reset_pin) { + connect_pin_to_encoder(self->reset_pin, xbar_signal_table[self->id].enc_home, XBAR_IN); + } +} + +// +// Functions for configuring the ENC Device +// +// Calculate the filter parameters based on a filter_ns value, telling the shortest +// pulse that will be detected. +// +static uint32_t calc_filter(uint32_t filter_ns, uint16_t *count, uint16_t *period) { + + #if defined MIMXRT117x_SERIES + uint32_t freq_khz = CLOCK_GetRootClockFreq(kCLOCK_Root_Bus) / 1000; + #else + uint32_t freq_khz = CLOCK_GetIpgFreq() / 1000; + #endif + + uint32_t cycles = (filter_ns * (freq_khz / 1000)) / 1000; + if (cycles == 0) { + // Set filter off + *count = 0; + *period = 0; + } else { + uint16_t pmax = cycles / 10; + if (pmax > 255) { + pmax = 255; + } + if (pmax == 0) { + pmax = 1; + } + uint16_t cnt; + cnt = cycles / pmax; + if (cnt > 10) { + cnt = 10; + } + *count = cnt >= 3 ? cnt - 3 : 0; + *period = pmax; + } + return ((1000000000 / freq_khz) + 1) * (*count + 3) * *period / 1000; +} + +// MicroPython API functions +// +static void mp_machine_encoder_init_helper_common(machine_encoder_obj_t *self, + mp_arg_val_t args[], enc_config_t *enc_config) { + + enum { ARG_reset, ARG_match, ARG_match_pin, ARG_filter_ns, ARG_max, ARG_min, ARG_index }; + + // Check for a Home pin, resetting the counters + if (args[ARG_reset].u_obj != MP_ROM_INT(-1)) { + if (args[ARG_reset].u_obj != mp_const_none) { + self->reset_pin = args[ARG_reset].u_obj; + connect_pin_to_encoder(self->reset_pin, xbar_signal_table[self->id].enc_home, XBAR_IN); + self->enc_config.HOMETriggerMode = kENC_HOMETriggerOnRisingEdge; + } else { + XBARA_SetSignalsConnection(XBARA1, kXBARA1_InputLogicLow, xbar_signal_table[self->id].enc_home); + self->reset_pin = NULL; + } + } + + // Check for a Match pin for the compare match signal + if (args[ARG_match_pin].u_obj != MP_ROM_INT(-1)) { + if (args[ARG_match_pin].u_obj != mp_const_none) { + self->match_pin = connect_pin_to_encoder(args[ARG_match_pin].u_obj, xbar_signal_table[self->id].enc_match, XBAR_OUT); + } else { + // Disconnect the XBAR from the output by switching it to an input. + #if defined MIMXRT117x_SERIES + if (self->match_pin < XBAR_ENC_DIR_OFFSET_2) { + IOMUXC_GPR->XBAR_ENC_DIR_REGISTER_1 &= ~(1 << (self->match_pin - XBAR_ENC_DIR_OFFSET_1)); + } else { + IOMUXC_GPR->XBAR_ENC_DIR_REGISTER_2 &= ~(1 << (self->match_pin - XBAR_ENC_DIR_OFFSET_2)); + } + #else + IOMUXC_GPR->XBAR_ENC_DIR_REGISTER &= ~(1 << (self->match_pin + XBAR_ENC_DIR_OFFSET)); + #endif + } + } + + if (args[ARG_match].u_obj != mp_const_none) { + uint32_t compare = mp_obj_int_get_truncated(args[ARG_match].u_obj) * self->phases_inv; + self->enc_config.positionCompareValue = compare; + self->instance->LCOMP = (uint16_t)(compare) & 0xffff; // Lower 16 pos bits. + self->instance->UCOMP = (uint16_t)(compare >> 16U) & 0xffff; // Upper 16 pos bits. + } else { + // Set to the reset value + self->enc_config.positionCompareValue = 0xFFFFFFFFU; + self->instance->LCOMP = 0xffff; + self->instance->UCOMP = 0xffff; + // disable the interrupt to avoid false trigger + ENC_DisableInterrupts(self->instance, kENC_PositionCompareInerruptEnable); + } + + if (args[ARG_filter_ns].u_int >= 0) { + self->filter = calc_filter(args[ARG_filter_ns].u_int, + &(enc_config->filterCount), &(enc_config->filterSamplePeriod)); + } + + if (args[ARG_max].u_obj != mp_const_none) { + self->max_count = mp_obj_int_get_truncated(args[ARG_max].u_obj) * self->phases_inv; + } + + if (args[ARG_min].u_obj != mp_const_none) { + self->min_count = mp_obj_int_get_truncated(args[ARG_min].u_obj) * self->phases_inv; + } + + enc_config->positionModulusValue = + self->max_count == 0 ? 0xffffffff : self->max_count + self->phases_inv - 1; + enc_config->positionInitialValue = self->min_count; + enc_config->enableModuloCountMode = true; + + // Count cycles on RollOverModulus or index pulse + if (args[ARG_index].u_obj != MP_ROM_INT(-1)) { + if (args[ARG_index].u_obj != mp_const_none) { + connect_pin_to_encoder(args[ARG_index].u_obj, xbar_signal_table[self->id].enc_index, XBAR_IN); + enc_config->revolutionCountCondition = kENC_RevolutionCountOnINDEXPulse; + enc_config->INDEXTriggerMode = kENC_INDEXTriggerOnRisingEdge; + } else { + enc_config->revolutionCountCondition = kENC_RevolutionCountOnRollOverModulus; + XBARA_SetSignalsConnection(XBARA1, kXBARA1_InputLogicLow, xbar_signal_table[self->id].enc_index); + } + } + + // Initialize the ENC module and start + ENC_Init(self->instance, enc_config); + clear_encoder_registers(self); + // Set the position and clear the rev register + ENC_SetInitialPositionValue(self->instance, self->min_count); + ENC_DoSoftwareLoadInitialPositionValue(self->instance); + self->instance->REV = 0; + ENC_ClearStatusFlags(self->instance, 0xff); // Clear all status flags + self->active = true; +} + +static void mp_machine_encoder_init_helper(machine_encoder_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_phases, ARG_reset, + ARG_match, ARG_match_pin, ARG_filter_ns, ARG_max, ARG_min, ARG_index}; + + static const mp_arg_t allowed_args[] = { + { MP_QSTR_phase_a, MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, + { MP_QSTR_phase_b, MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, + { MP_QSTR_phases, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_reset, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_INT(-1)} }, + { MP_QSTR_match, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, + { MP_QSTR_match_pin, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_INT(-1)} }, + { MP_QSTR_filter_ns, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_max, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, + { MP_QSTR_min, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, + { MP_QSTR_index, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_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); + + // Process the Encoder specific keyword arguments + // Get referred Pin object(s) and connect them to the encoder + if (args[ARG_phase_a].u_obj != mp_const_none) { + self->input_a = connect_pin_to_encoder(args[ARG_phase_a].u_obj, xbar_signal_table[self->id].enc_input_a, XBAR_IN); + } + if (args[ARG_phase_b].u_obj != mp_const_none) { + self->input_b = connect_pin_to_encoder(args[ARG_phase_b].u_obj, xbar_signal_table[self->id].enc_input_b, XBAR_IN); + } + // Check for valid input pins + if (self->input_a == 0 || self->input_b == 0 || self->input_a == self->input_b) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid or missing input pins")); + } + + // Get the Phases argument + if (args[ARG_phases].u_int != -1) { + if ((args[ARG_phases].u_int != 1) && (args[ARG_phases].u_int != 2) && (args[ARG_phases].u_int != 4)) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid value for phases")); + } + self->phases_inv = 4 / args[ARG_phases].u_int; + } + + // Set the common options + mp_machine_encoder_init_helper_common(self, args + ARG_reset, &self->enc_config); + + ENC_DoSoftwareLoadInitialPositionValue(self->instance); /* Update the position counter with initial value. */ +} + +// Qencoder(id, input_a, input_b, [args]) +static mp_obj_t mp_machine_encoder_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + // Check number of arguments + mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); + + CLOCK_EnableClock(kCLOCK_Iomuxc); // just in case it was not set yet + XBARA_Init(XBARA1); + + uint8_t id = mp_obj_get_int(args[0]); + if (id < 0 || id >= FSL_FEATURE_SOC_ENC_COUNT) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid encoder/counter id")); + } + // check, if the encoder is already in use, and if yes, deinit it + if (encoder_table[id] != NULL) { + encoder_deinit_single(encoder_table[id]); + } + + // Connect the trigger input to low level + XBARA_SetSignalsConnection(XBARA1, kXBARA1_InputLogicLow, xbar_signal_table[id].enc_trigger); + + // Create and populate the Encoder object. + machine_encoder_obj_t *self = encoder_table[id]; + if (self == NULL) { + self = mp_obj_malloc(machine_encoder_obj_t, &machine_encoder_type); + encoder_table[id] = self; + } + // Set defaults for ENC Config. + self->id = id; + self->input_a = 0; + self->input_b = 0; + self->instance = enc_instances[id + 1]; + self->max_count = 0; + self->min_count = 0; + self->mp_irq_flags = 0; + self->mp_irq_trigger = 0; + self->mp_irq_obj = NULL; + self->match_pin = 0; + self->phases_inv = 4; // default: phases = 1 + self->reset_pin = NULL; + self->mode = MODE_ENCODER; + + // Set defaults for ENC Config + ENC_GetDefaultConfig(&self->enc_config); + self->enc_config.revolutionCountCondition = kENC_RevolutionCountOnRollOverModulus; + + // 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 void encoder_deinit_single(machine_encoder_obj_t *self) { + if (self->active) { + if (self->mp_irq_obj && self->mp_irq_obj->handler) { + DisableIRQ(enc_irqn[self->id + 1]); + ENC_DisableInterrupts(self->instance, ENCODER_ALL_INTERRUPTS); + } + if (self->match_pin != 0) { + // Disconnect the XBAR from the output by switching it to an input. + #if defined MIMXRT117x_SERIES + if (self->match_pin < XBAR_ENC_DIR_OFFSET_2) { + IOMUXC_GPR->XBAR_ENC_DIR_REGISTER_1 &= ~(1 << (self->match_pin - XBAR_ENC_DIR_OFFSET_1)); + } else { + IOMUXC_GPR->XBAR_ENC_DIR_REGISTER_2 &= ~(1 << (self->match_pin - XBAR_ENC_DIR_OFFSET_2)); + } + #else + IOMUXC_GPR->XBAR_ENC_DIR_REGISTER &= ~(1 << (self->match_pin + XBAR_ENC_DIR_OFFSET)); + #endif + } + ENC_Deinit(self->instance); + } + self->active = false; +} + +// encoder_deinit_all() +void machine_encoder_deinit_all(void) { + for (int i = 0; i < ARRAY_SIZE(encoder_table); i++) { + if (encoder_table[i] != NULL) { + encoder_deinit_single(encoder_table[i]); + encoder_table[i] = NULL; + } + } +} + +// encoder.deinit() +static mp_obj_t machine_encoder_deinit(mp_obj_t self_in) { + encoder_deinit_single(MP_OBJ_TO_PTR(self_in)); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(machine_encoder_deinit_obj, machine_encoder_deinit); + +// encoder.value([value]) +static mp_obj_t machine_encoder_value(size_t n_args, const mp_obj_t *args) { + machine_encoder_obj_t *self = MP_OBJ_TO_PTR(args[0]); + if (!self->active) { + mp_raise_ValueError(MP_ERROR_TEXT("device stopped")); + } + uint32_t actual_value = ENC_GetPositionValue(self->instance); + if (n_args > 1) { + uint32_t value = mp_obj_int_get_truncated(args[1]) * self->phases_inv; + // Set the position register + ENC_SetInitialPositionValue(self->instance, value); + ENC_DoSoftwareLoadInitialPositionValue(self->instance); + // Reset the INIT Value, unlocking the counter. + ENC_SetInitialPositionValue(self->instance, 0); + } + // Get the position as signed 32 bit value. + int32_t value = (int32_t)actual_value; + if (value > 0) { + value += (self->phases_inv - 1); + } + return mp_obj_new_int(value / self->phases_inv); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_encoder_value_obj, 1, 2, machine_encoder_value); + +// encoder.cycles([value]) +static mp_obj_t machine_encoder_cycles(size_t n_args, const mp_obj_t *args) { + machine_encoder_obj_t *self = MP_OBJ_TO_PTR(args[0]); + if (!self->active) { + mp_raise_ValueError(MP_ERROR_TEXT("device stopped")); + } + + int16_t cycles = (int16_t)ENC_GetRevolutionValue(self->instance); + if (n_args > 1) { + // Set the revolution value + self->instance->REV = mp_obj_get_int(args[1]); + } + return MP_OBJ_NEW_SMALL_INT(cycles); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_encoder_cycles_obj, 1, 2, machine_encoder_cycles); + +// -------------------------------------- IRQ set-up -------------------------- + +static mp_uint_t encoder_irq_trigger(mp_obj_t self_in, mp_uint_t new_trigger) { + machine_encoder_obj_t *self = MP_OBJ_TO_PTR(self_in); + self->mp_irq_trigger = new_trigger; + // Clear previous interrupts and enable the requested interrupts. + ENC_ClearStatusFlags(self->instance, ENCODER_ALL_INTERRUPTS); + ENC_DisableInterrupts(self->instance, ENCODER_ALL_INTERRUPTS); + ENC_EnableInterrupts(self->instance, new_trigger); + return 0; +} + +static mp_uint_t encoder_irq_info(mp_obj_t self_in, mp_uint_t info_type) { + machine_encoder_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (info_type == MP_IRQ_INFO_FLAGS) { + return self->mp_irq_flags; + } else if (info_type == MP_IRQ_INFO_TRIGGERS) { + return self->mp_irq_trigger; + } + return 0; +} + +static const mp_irq_methods_t encoder_irq_methods = { + .trigger = encoder_irq_trigger, + .info = encoder_irq_info, +}; + +// encoder.irq(trigger=ENCODER.IRQ_MATCH, handler=None, hard=False) +static mp_obj_t machine_encoder_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + mp_arg_val_t args[MP_IRQ_ARG_INIT_NUM_ARGS]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_IRQ_ARG_INIT_NUM_ARGS, mp_irq_init_args, args); + machine_encoder_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + bool any_args = n_args > 1 || kw_args->used != 0; + + if (!self->active) { + mp_raise_ValueError(MP_ERROR_TEXT("device stopped")); + } + + if (self->mp_irq_obj == NULL) { + self->mp_irq_obj = mp_irq_new(&encoder_irq_methods, MP_OBJ_FROM_PTR(self)); + self->mp_irq_obj->ishard = false; + } + + if (any_args) { + // Check the handler + mp_obj_t handler = args[MP_IRQ_ARG_INIT_handler].u_obj; + if (handler != mp_const_none && !mp_obj_is_callable(handler)) { + mp_raise_ValueError(MP_ERROR_TEXT("handler must be None or callable")); + } + + // Check the trigger + mp_uint_t trigger = args[MP_IRQ_ARG_INIT_trigger].u_int; + mp_uint_t not_supported = trigger & ~MP_ENCODER_ALLOWED_FLAGS; + if (trigger != 0 && not_supported) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("trigger 0x%04x unsupported"), not_supported); + } + + self->mp_irq_obj->handler = handler; + self->mp_irq_obj->ishard = args[MP_IRQ_ARG_INIT_hard].u_bool; + self->mp_irq_trigger = trigger; + + // Clear pending interrupt flags + ENC_ClearStatusFlags(self->instance, ENCODER_ALL_INTERRUPTS); + ENC_DisableInterrupts(self->instance, ENCODER_ALL_INTERRUPTS); + if (self->mp_irq_obj->handler != mp_const_none) { + ENC_EnableInterrupts(self->instance, trigger); + EnableIRQ(enc_irqn[self->id + 1]); + } else { + ENC_DisableInterrupts(self->instance, trigger); + DisableIRQ(enc_irqn[self->id + 1]); + } + } + return MP_OBJ_FROM_PTR(self->mp_irq_obj); +} +MP_DEFINE_CONST_FUN_OBJ_KW(machine_encoder_irq_obj, 1, machine_encoder_irq); + +// encoder.init([kwargs]) +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 const mp_rom_map_elem_t machine_encoder_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_encoder_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&machine_encoder_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_irq), MP_ROM_PTR(&machine_encoder_irq_obj) }, + { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&machine_encoder_value_obj) }, + { MP_ROM_QSTR(MP_QSTR_cycles), MP_ROM_PTR(&machine_encoder_cycles_obj) }, + + { MP_ROM_QSTR(MP_QSTR_IRQ_RESET), MP_ROM_INT(kENC_HOMETransitionFlag) }, + { MP_ROM_QSTR(MP_QSTR_IRQ_INDEX), MP_ROM_INT(kENC_INDEXPulseFlag) }, + { MP_ROM_QSTR(MP_QSTR_IRQ_MATCH), MP_ROM_INT(kENC_PositionCompareFlag) }, + { MP_ROM_QSTR(MP_QSTR_IRQ_ROLL_UNDER), MP_ROM_INT(kENC_PositionRollUnderFlag) }, + { MP_ROM_QSTR(MP_QSTR_IRQ_ROLL_OVER), MP_ROM_INT(kENC_PositionRollOverFlag) }, +}; +static MP_DEFINE_CONST_DICT(machine_encoder_locals_dict, machine_encoder_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + machine_encoder_type, + MP_QSTR_Encoder, + MP_TYPE_FLAG_NONE, + make_new, mp_machine_encoder_make_new, + print, mp_machine_encoder_print, + locals_dict, &machine_encoder_locals_dict + ); + + +// --- Counter class code ---------- + +static void mp_machine_counter_init_helper(machine_encoder_obj_t *self, + size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_src, ARG_direction, ARG_reset, ARG_match, ARG_match_pin, ARG_filter_ns, ARG_max, ARG_min, ARG_index }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_src, MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, + { MP_QSTR_direction, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_INT(-1)} }, + { MP_QSTR_reset, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_INT(-1)} }, + { MP_QSTR_match, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, + { MP_QSTR_match_pin, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_INT(-1)} }, + { MP_QSTR_filter_ns, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_max, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, + { MP_QSTR_min, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, + { MP_QSTR_index, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_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); + + if (args[ARG_src].u_obj != mp_const_none) { + self->input_a = connect_pin_to_encoder(args[ARG_src].u_obj, xbar_signal_table[self->id].enc_input_a, XBAR_IN); + } + if (self->input_a == 0) { + mp_raise_ValueError(MP_ERROR_TEXT("missing input pin")); + } + + mp_obj_t direction = args[ARG_direction].u_obj; + if (direction != MP_ROM_INT(-1)) { + if (direction == MP_ROM_INT(COUNTER_UP)) { + XBARA_SetSignalsConnection(XBARA1, kXBARA1_InputLogicLow, xbar_signal_table[self->id].enc_input_b); + } else if (direction == MP_ROM_INT(COUNTER_DOWN)) { + XBARA_SetSignalsConnection(XBARA1, kXBARA1_InputLogicHigh, xbar_signal_table[self->id].enc_input_b); + } else { + connect_pin_to_encoder(direction, xbar_signal_table[self->id].enc_input_b, XBAR_IN); + } + } + + // Set the common options and start + mp_machine_encoder_init_helper_common(self, args + ARG_reset, &self->enc_config); +} + +// Counter(id, input, [args]) +static mp_obj_t mp_machine_counter_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + // Check number of arguments + mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); + + CLOCK_EnableClock(kCLOCK_Iomuxc); // just in case it was not set yet + XBARA_Init(XBARA1); + + uint8_t id = mp_obj_get_int(args[0]); + if (id < 0 || id >= FSL_FEATURE_SOC_ENC_COUNT) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid encoder/counter id")); + } + // check, if the encoder is already in use, and if yes, deinit it + if (encoder_table[id] != NULL) { + encoder_deinit_single(encoder_table[id]); + } + + // Connect input_b and the trigger input to a fixed level. + XBARA_SetSignalsConnection(XBARA1, kXBARA1_InputLogicLow, xbar_signal_table[id].enc_input_b); + XBARA_SetSignalsConnection(XBARA1, kXBARA1_InputLogicLow, xbar_signal_table[id].enc_trigger); + + // Create and populate the Qencoder object. + machine_encoder_obj_t *self = encoder_table[id]; + if (self == NULL) { + self = mp_obj_malloc(machine_encoder_obj_t, &machine_counter_type); + encoder_table[id] = self; + } + self->id = id; + self->input_a = 0; + self->input_b = 0; + self->instance = enc_instances[id + 1]; + self->max_count = 0; + self->min_count = 0; + self->mp_irq_flags = 0; + self->mp_irq_trigger = 0; + self->mp_irq_obj = NULL; + self->match_pin = 0; + self->phases_inv = 1; + self->reset_pin = NULL; + self->mode = MODE_COUNTER; + + // Set defaults for ENC Config + ENC_GetDefaultConfig(&self->enc_config); + + // Set the mode to a 32 bit counter + self->enc_config.decoderWorkMode = kENC_DecoderWorkAsSignalPhaseCountMode; + self->enc_config.revolutionCountCondition = kENC_RevolutionCountOnRollOverModulus; + + // 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); +} + +// counter.init([kwargs]) +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 const mp_rom_map_elem_t machine_counter_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_counter_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&machine_encoder_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_irq), MP_ROM_PTR(&machine_encoder_irq_obj) }, + { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&machine_encoder_value_obj) }, + { MP_ROM_QSTR(MP_QSTR_cycles), MP_ROM_PTR(&machine_encoder_cycles_obj) }, + + { MP_ROM_QSTR(MP_QSTR_IRQ_RESET), MP_ROM_INT(kENC_HOMETransitionFlag) }, + { MP_ROM_QSTR(MP_QSTR_IRQ_INDEX), MP_ROM_INT(kENC_INDEXPulseFlag) }, + { MP_ROM_QSTR(MP_QSTR_IRQ_MATCH), MP_ROM_INT(kENC_PositionCompareFlag) }, + { MP_ROM_QSTR(MP_QSTR_IRQ_ROLL_UNDER), MP_ROM_INT(kENC_PositionRollUnderFlag) }, + { MP_ROM_QSTR(MP_QSTR_IRQ_ROLL_OVER), MP_ROM_INT(kENC_PositionRollOverFlag) }, + { 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); + +MP_DEFINE_CONST_OBJ_TYPE( + machine_counter_type, + MP_QSTR_Counter, + MP_TYPE_FLAG_NONE, + make_new, mp_machine_counter_make_new, + print, mp_machine_encoder_print, + locals_dict, &machine_counter_locals_dict + ); + +MP_REGISTER_ROOT_POINTER(struct _machine_encoder_obj_t *encoder_table[FSL_FEATURE_SOC_ENC_COUNT]); + +#endif // MICROPY_PY_MACHINE_QECNT diff --git a/ports/mimxrt/machine_pin.c b/ports/mimxrt/machine_pin.c index 4f623214bc..61b540076c 100644 --- a/ports/mimxrt/machine_pin.c +++ b/ports/mimxrt/machine_pin.c @@ -93,7 +93,7 @@ int GPIO_get_instance(GPIO_Type *gpio) { return 0; } -void call_handler(GPIO_Type *gpio, int gpio_nr, int pin) { +__attribute__((section(".ram_functions"))) void call_handler(GPIO_Type *gpio, int gpio_nr, int pin) { uint32_t mask = 1 << pin; uint32_t isr = gpio->ISR & gpio->IMR; for (int i = 0; i < 16; i++, pin++, mask <<= 1) { @@ -122,43 +122,43 @@ void call_handler(GPIO_Type *gpio, int gpio_nr, int pin) { // 10 GPIO IRQ handlers, each covering 16 bits. -void GPIO1_Combined_0_15_IRQHandler(void) { +__attribute__((section(".ram_functions"))) void GPIO1_Combined_0_15_IRQHandler(void) { call_handler(gpiobases[1], 1, 0); } -void GPIO1_Combined_16_31_IRQHandler(void) { +__attribute__((section(".ram_functions"))) void GPIO1_Combined_16_31_IRQHandler(void) { call_handler(gpiobases[1], 1, 16); } -void GPIO2_Combined_0_15_IRQHandler(void) { +__attribute__((section(".ram_functions"))) void GPIO2_Combined_0_15_IRQHandler(void) { call_handler(gpiobases[2], 2, 0); } -void GPIO2_Combined_16_31_IRQHandler(void) { +__attribute__((section(".ram_functions"))) void GPIO2_Combined_16_31_IRQHandler(void) { call_handler(gpiobases[2], 2, 16); } -void GPIO3_Combined_0_15_IRQHandler(void) { +__attribute__((section(".ram_functions"))) void GPIO3_Combined_0_15_IRQHandler(void) { call_handler(gpiobases[3], 3, 0); } -void GPIO3_Combined_16_31_IRQHandler(void) { +__attribute__((section(".ram_functions"))) void GPIO3_Combined_16_31_IRQHandler(void) { call_handler(gpiobases[3], 3, 16); } -void GPIO4_Combined_0_15_IRQHandler(void) { +__attribute__((section(".ram_functions"))) void GPIO4_Combined_0_15_IRQHandler(void) { call_handler(gpiobases[4], 4, 0); } -void GPIO4_Combined_16_31_IRQHandler(void) { +__attribute__((section(".ram_functions"))) void GPIO4_Combined_16_31_IRQHandler(void) { call_handler(gpiobases[4], 4, 16); } -void GPIO5_Combined_0_15_IRQHandler(void) { +__attribute__((section(".ram_functions"))) void GPIO5_Combined_0_15_IRQHandler(void) { call_handler(gpiobases[5], 5, 0); } -void GPIO5_Combined_16_31_IRQHandler(void) { +__attribute__((section(".ram_functions"))) void GPIO5_Combined_16_31_IRQHandler(void) { call_handler(gpiobases[5], 5, 16); } diff --git a/ports/mimxrt/main.c b/ports/mimxrt/main.c index 9f3d47f8cc..664d1c4c16 100644 --- a/ports/mimxrt/main.c +++ b/ports/mimxrt/main.c @@ -60,6 +60,7 @@ #include "extmod/vfs.h" extern uint8_t _sstack, _estack, _gc_heap_start, _gc_heap_end; +extern void machine_encoder_deinit_all(void); void board_init(void); @@ -195,6 +196,9 @@ int main(void) { machine_pwm_deinit_all(); #endif soft_timer_deinit(); + #if MICROPY_PY_MACHINE_QECNT + machine_encoder_deinit_all(); + #endif gc_sweep_all(); mp_deinit(); } diff --git a/ports/mimxrt/modmachine.c b/ports/mimxrt/modmachine.c index ad078ff3ac..491e21ee70 100644 --- a/ports/mimxrt/modmachine.c +++ b/ports/mimxrt/modmachine.c @@ -55,6 +55,13 @@ #else #define MICROPY_PY_MACHINE_SDCARD_ENTRY #endif +#if MICROPY_PY_MACHINE_QECNT +#define MICROPY_PY_MACHINE_ENCODER_ENTRY { MP_ROM_QSTR(MP_QSTR_Encoder), MP_ROM_PTR(&machine_encoder_type) }, +#define MICROPY_PY_MACHINE_COUNTER_ENTRY { MP_ROM_QSTR(MP_QSTR_Counter), MP_ROM_PTR(&machine_counter_type) }, +#else +#define MICROPY_PY_MACHINE_ENCODER_ENTRY +#define MICROPY_PY_MACHINE_COUNTER_ENTRY +#endif #define MICROPY_PY_MACHINE_EXTRA_GLOBALS \ MICROPY_PY_MACHINE_LED_ENTRY \ @@ -62,6 +69,8 @@ { MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&machine_timer_type) }, \ { MP_ROM_QSTR(MP_QSTR_RTC), MP_ROM_PTR(&machine_rtc_type) }, \ MICROPY_PY_MACHINE_SDCARD_ENTRY \ + MICROPY_PY_MACHINE_ENCODER_ENTRY \ + MICROPY_PY_MACHINE_COUNTER_ENTRY \ \ /* Reset reasons */ \ { MP_ROM_QSTR(MP_QSTR_PWRON_RESET), MP_ROM_INT(MP_PWRON_RESET) }, \ diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index 5c8b4930ce..b76f459e2d 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -57,6 +57,8 @@ uint32_t trng_random_u32(void); // Optimisations +// Compiler configuration + // Python internal features #define MICROPY_TRACKED_ALLOC (MICROPY_SSL_MBEDTLS) #define MICROPY_READER_VFS (1)