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)