From 1ab21f31a334e60438fb163980d0685f4406dba2 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Fri, 24 Sep 2021 21:47:31 +0200 Subject: [PATCH 1/9] mimxrt: Implement a Quadrature Encoder and Counter class. These classes are base 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 Home signal. - Defining an output pin showing the compare match signal. - Setting the number of cycles per revolution. - Setting the initial value for the position. - Setting the counting direction. - Setting a glitch filter. - Setting the value counter as signed or unsigned integers. - 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. The Counter mode counts single pulses on input A of the Encoder. The configuration support: - 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 ouput pin showing th compare match signal. - Setting the counter value. - Setting the glitch filter. - Returing the value counter as signed or unsigned integer. - Defining a callback which is called at a certain value. The counting range is 0 - 2**32-1 and a 16 bit overrun counter. The highest input frequency is CPU-Clock/12. 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/MIMXRT1020_EVK/mpconfigboard.h | 2 + ports/mimxrt/machine_encoder.c | 698 ++++++++++++++++++ ports/mimxrt/machine_pin.c | 22 +- ports/mimxrt/main.c | 4 + ports/mimxrt/modmachine.c | 9 + ports/mimxrt/mpconfigport.h | 2 + 10 files changed, 743 insertions(+), 14 deletions(-) create mode 100644 ports/mimxrt/machine_encoder.c diff --git a/extmod/modmachine.h b/extmod/modmachine.h index ef507aca7408c..53660a7b7ab64 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 fea7e56da22f4..e6e7a1fa8e504 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -184,9 +184,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. @@ -251,6 +258,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 a616fbec64a59..4950e70ef9dd3 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 be8a9c634dd55..1899c4f7a3867 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/MIMXRT1020_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h index b4f777cf93b00..5f41dfdfd9adf 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/machine_encoder.c b/ports/mimxrt/machine_encoder.c new file mode 100644 index 0000000000000..411a6b04f8c7c --- /dev/null +++ b/ports/mimxrt/machine_encoder.c @@ -0,0 +1,698 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * Copyright (c) 2021 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 "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; + uint8_t input_a; + uint8_t input_b; + uint8_t mode; + bool is_signed; + uint8_t match_pin; + uint32_t cpc; + uint32_t filter; + uint16_t status; + uint16_t requested_irq; + mp_irq_obj_t *irq; + 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_TRIGGER_MATCH (kENC_PositionCompareFlag) +#define ENCODER_TRIGGER_ROLL_OVER (kENC_PositionRollOverFlag) +#define ENCODER_TRIGGER_ROLL_UNDER (kENC_PositionRollUnderFlag) +#define ENCODER_ALL_INTERRUPTS (0x7f) + +#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_IN (1) +#define XBAR_OUT (0) + +#define COUNTER_UP (-2) +#define COUNTER_DOWN (-3) +#define MODE_ENCODER (0) +#define MODE_COUNTER (1) + +STATIC void encoder_deinit_single(machine_encoder_obj_t *self); + +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 }, + + { 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 +}; + +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->status = 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->status & kENC_PositionCompareFlag) { + ENC_DisableInterrupts(self->instance, kENC_PositionCompareInerruptEnable); + } + ENC_ClearStatusFlags(self->instance, self->status); + __DSB(); + mp_irq_handler(self->irq); + } +} + +__attribute__((section(".ram_functions"))) void ENC1_IRQHandler(void) { + irq_callback(0); +} + +__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 + +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); + mp_printf(print, + self->is_signed ? "%s %d cpc=%lu match=%ld filter=%luns>\n" : "%s %d cpc=%lu match=%lu filter=%luns>\n", + self->mode == MODE_ENCODER ? "id, self->cpc, self->enc_config.positionCompareValue, self->filter); +} + +// Utililty 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; + str = (char *)qstr_data(af_obj->name, &len); + // test for the name starting with XBAR_INOUT + if (len < 12 || strncmp(str, "XBAR_INOUT", 10) != 0) { + return NULL; + } + // Get I/O number, e.g. XBAR_INOUT03 + *io_number = (str[10] - '0') * 10 + (str[11] - '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 (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")); + } + } + 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_trigger); + XBARA_SetSignalsConnection(XBARA1, kXBARA1_InputLogicLow, xbar_signal_table[self->id].enc_trigger); +} + +// +// 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) { + + uint32_t freq_khz = CLOCK_GetIpgFreq() / 1000; + 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_match_pin, ARG_filter_ns, ARG_cpc, ARG_signed, ARG_index }; + + // 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. + IOMUXC_GPR->XBAR_ENC_DIR_REGISTER &= ~(1 << (self->match_pin + XBAR_ENC_DIR_OFFSET)); + } + } + + 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_cpc].u_obj != mp_const_none) { + uint32_t cpc = mp_obj_int_get_truncated(args[ARG_cpc].u_obj); + self->cpc = cpc; + if (cpc == 0) { + enc_config->enableModuloCountMode = false; + enc_config->positionModulusValue = 0; + enc_config->positionInitialValue = 0; + self->is_signed = false; + } else { + enc_config->enableModuloCountMode = true; + enc_config->positionModulusValue = cpc - 1; + self->is_signed = true; + } + } + + if (args[ARG_signed].u_int >= 0) { + self->is_signed = !!args[ARG_signed].u_int; + } + + // 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); + ENC_ClearStatusFlags(self->instance, 0xff); // Clear all status flags +} + +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_home, + ARG_match_pin, ARG_filter_ns, ARG_cpc, ARG_signed, 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_home, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_INT(-1)} }, + { 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_cpc, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, + { MP_QSTR_signed, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { 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")); + } + + // Check for a Home pin, resetting the counters + if (args[ARG_home].u_obj != MP_ROM_INT(-1)) { + if (args[ARG_home].u_obj != mp_const_none) { + connect_pin_to_encoder(args[ARG_home].u_obj, 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); + } + } + + // Set the common options + mp_machine_encoder_init_helper_common(self, args + ARG_match_pin, &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, dinit 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 Qencoder object. + machine_encoder_obj_t *self = m_new_obj(machine_encoder_obj_t); + encoder_table[id] = self; + self->id = id; + self->input_a = 0; + self->input_b = 0; + self->base.type = &machine_encoder_type; + self->instance = enc_instances[id + 1]; + self->cpc = 0; + self->status = 0; + self->irq = NULL; + self->match_pin = 0; + self->is_signed = true; + self->mode = MODE_ENCODER; + + // Set defaults for ENC Config + ENC_GetDefaultConfig(&self->enc_config); + self->enc_config.enableTRIGGERClearPositionCounter = true; + 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->irq->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. + IOMUXC_GPR->XBAR_ENC_DIR_REGISTER &= ~(1 << (self->match_pin + XBAR_ENC_DIR_OFFSET)); + } + ENC_Deinit(self->instance); +} + +// 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.status() +mp_obj_t machine_encoder_status(mp_obj_t self_in) { + machine_encoder_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_NEW_SMALL_INT(self->status & (self->requested_irq | kENC_LastCountDirectionFlag)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_encoder_status_obj, machine_encoder_status); + +// 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]); + uint32_t actual_value = ENC_GetPositionValue(self->instance); + if (n_args > 1) { + // Set the encoder position value and clear the rev counter. + uint32_t value = mp_obj_int_get_truncated(args[1]); + clear_encoder_registers(self); + // Set the position and rev register + ENC_SetInitialPositionValue(self->instance, value); + ENC_DoSoftwareLoadInitialPositionValue(self->instance); + // Reset the INIT Value + ENC_SetInitialPositionValue(self->instance, 0); + } + // Get the position as signed or unsigned 32 bit value. + if (self->is_signed) { + return mp_obj_new_int((int32_t)actual_value); + } else { + return mp_obj_new_int_from_uint(actual_value); + } +} +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]); + 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); + +// encoder.irq(trigger=ENCODER.IRQ_MATCH, value=nnn, 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) { + enum { ARG_trigger, ARG_value, ARG_handler, ARG_hard }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_trigger, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_value, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, + { MP_QSTR_handler, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_hard, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + machine_encoder_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + if (self->irq == NULL) { + self->irq = m_new_obj(mp_irq_obj_t); + self->irq->base.type = &mp_irq_type; + self->irq->parent = MP_OBJ_FROM_PTR(self); + self->irq->methods = NULL; + self->irq->ishard = false; + } + + uint16_t trigger = args[ARG_trigger].u_int & + (ENCODER_TRIGGER_MATCH | ENCODER_TRIGGER_ROLL_UNDER | ENCODER_TRIGGER_ROLL_OVER); + + if (args[ARG_value].u_obj != mp_const_none) { + uint32_t value = mp_obj_int_get_truncated(args[ARG_value].u_obj); + self->enc_config.positionCompareValue = value; + self->instance->LCOMP = (uint16_t)(value) & 0xffff; /* Lower 16 pos bits. */ + self->instance->UCOMP = (uint16_t)(value >> 16U) & 0xffff; /* Upper 16 pos bits. */ + trigger |= ENCODER_TRIGGER_MATCH; + } + + self->irq->handler = args[ARG_handler].u_obj; + self->irq->ishard = args[ARG_hard].u_bool; + self->requested_irq = trigger; + // Clear pending interrupt flags + ENC_ClearStatusFlags(self->instance, ENCODER_ALL_INTERRUPTS); + if (self->irq->handler != mp_const_none) { + ENC_DisableInterrupts(self->instance, ENCODER_ALL_INTERRUPTS); + 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_const_none; +} +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); + +// encoder.id() +STATIC mp_obj_t machine_encoder_id(mp_obj_t self_in) { + machine_encoder_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_NEW_SMALL_INT(self->id); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_encoder_id_obj, machine_encoder_id); + +STATIC const mp_rom_map_elem_t machine_encoder_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&machine_encoder_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_encoder_init_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_status), MP_ROM_PTR(&machine_encoder_status_obj) }, + { MP_ROM_QSTR(MP_QSTR_id), MP_ROM_PTR(&machine_encoder_id_obj) }, + + { MP_ROM_QSTR(MP_QSTR_IRQ_MATCH), MP_ROM_INT(ENCODER_TRIGGER_MATCH) }, + { MP_ROM_QSTR(MP_QSTR_IRQ_ROLL_UNDER), MP_ROM_INT(ENCODER_TRIGGER_ROLL_UNDER) }, + { MP_ROM_QSTR(MP_QSTR_IRQ_ROLL_OVER), MP_ROM_INT(ENCODER_TRIGGER_ROLL_OVER) }, +}; +STATIC MP_DEFINE_CONST_DICT(machine_encoder_locals_dict, machine_encoder_locals_dict_table); + +const mp_obj_type_t machine_encoder_type = { + { &mp_type_type }, + .name = MP_QSTR_Encoder, + .print = mp_machine_encoder_print, + .make_new = mp_machine_encoder_make_new, + .locals_dict = (mp_obj_dict_t *)&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_match_pin, ARG_filter_ns, ARG_cpc, ARG_signed, 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_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_cpc, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, + { MP_QSTR_signed, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { 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_match_pin, &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, dinit 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 = m_new_obj(machine_encoder_obj_t); + encoder_table[id] = self; + self->id = id; + self->input_a = 0; + self->input_b = 0; + self->base.type = &machine_counter_type; + self->instance = enc_instances[id + 1]; + self->cpc = 0; + self->status = 0; + self->irq = NULL; + self->match_pin = 0; + self->is_signed = true; + 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.enableTRIGGERClearPositionCounter = true; + 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_deinit), MP_ROM_PTR(&machine_encoder_deinit_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_init), MP_ROM_PTR(&machine_counter_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_irq), MP_ROM_PTR(&machine_encoder_irq_obj) }, + { MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&machine_encoder_status_obj) }, + { MP_ROM_QSTR(MP_QSTR_id), MP_ROM_PTR(&machine_encoder_id_obj) }, + + { MP_ROM_QSTR(MP_QSTR_IRQ_MATCH), MP_ROM_INT(ENCODER_TRIGGER_MATCH) }, + { MP_ROM_QSTR(MP_QSTR_IRQ_ROLL_UNDER), MP_ROM_INT(ENCODER_TRIGGER_ROLL_UNDER) }, + { MP_ROM_QSTR(MP_QSTR_IRQ_ROLL_OVER), MP_ROM_INT(ENCODER_TRIGGER_ROLL_OVER) }, + { 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); + +const mp_obj_type_t machine_counter_type = { + { &mp_type_type }, + .name = MP_QSTR_Counter, + .print = mp_machine_encoder_print, + .make_new = mp_machine_counter_make_new, + .locals_dict = (mp_obj_dict_t *)&machine_counter_locals_dict, +}; + +#endif // MICROPY_PY_MACHINE_QECNT diff --git a/ports/mimxrt/machine_pin.c b/ports/mimxrt/machine_pin.c index 4f623214bc728..61b540076c142 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 7166171f17c2d..5054b60991694 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); @@ -175,6 +176,9 @@ int main(void) { machine_uart_deinit_all(); machine_pwm_deinit_all(); 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 ad078ff3ac6d2..491e21ee708c5 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 d6694badbad95..842cc6c86a7a4 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -50,6 +50,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) From 34916efc3e779f92e7ee49f348b49810ce4e33f5 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Fri, 11 Feb 2022 16:29:22 +0100 Subject: [PATCH 2/9] mimxrt: Add missing definition for the OLIMEX_RT1010. Signed-off-by: robert-hh --- ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.h b/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.h index 1ad2e9df5ebad..4d488647191f4 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 From 314984b7a7828c67b5326e53209dd0c39dc533f3 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Thu, 10 Mar 2022 20:57:00 +0100 Subject: [PATCH 3/9] mimxrt: Add settings for MIMXRT1015. The MIMXRT1015 MCU has only one encoder/counter unit. Signed-off-by: robert-hh --- ports/mimxrt/boards/MIMXRT1015_EVK/mpconfigboard.h | 2 ++ ports/mimxrt/boards/MIMXRT1015_EVK/pins.csv | 2 ++ ports/mimxrt/machine_encoder.c | 6 ++++++ 3 files changed, 10 insertions(+) diff --git a/ports/mimxrt/boards/MIMXRT1015_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1015_EVK/mpconfigboard.h index f47bbf4fa138b..1303aade72976 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 2b50c7ca7682f..d41dc30aa0969 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/machine_encoder.c b/ports/mimxrt/machine_encoder.c index 411a6b04f8c7c..2e89742b55ebd 100644 --- a/ports/mimxrt/machine_encoder.c +++ b/ports/mimxrt/machine_encoder.c @@ -94,6 +94,8 @@ static encoder_xbar_signal_t xbar_signal_table[FSL_FEATURE_SOC_ENC_COUNT] = { kXBARA1_OutputEnc1Trigger, kXBARA1_InputEnc1PosMatch }, + #if FSL_FEATURE_SOC_ENC_COUNT > 1 + { kXBARA1_OutputEnc2PhaseAInput, kXBARA1_OutputEnc2PhaseBInput, kXBARA1_OutputEnc2Index, @@ -118,6 +120,7 @@ static encoder_xbar_signal_t xbar_signal_table[FSL_FEATURE_SOC_ENC_COUNT] = { kXBARA1_InputEnc4PosMatch }, #endif + #endif }; static machine_encoder_obj_t *encoder_table[FSL_FEATURE_SOC_ENC_COUNT]; @@ -144,6 +147,8 @@ __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); } @@ -157,6 +162,7 @@ __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); From c2da804b7c1b0c275f3d6c42679176e6c54bf851 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Tue, 19 Apr 2022 17:59:27 +0200 Subject: [PATCH 4/9] mimxrt: Add the documentation for the Encoder/Counter class. This is the MIMXRT specific version, compliant to the documentation of PR #8072 in the basic methods. Signed-off-by: robert-hh --- docs/library/index.rst | 10 + docs/library/mimxrt.Encoder.rst | 379 ++++++++++++++++++++++++++++++++ docs/mimxrt/quickref.rst | 56 +++++ 3 files changed, 445 insertions(+) create mode 100644 docs/library/mimxrt.Encoder.rst diff --git a/docs/library/index.rst b/docs/library/index.rst index 2919378ce13b2..7cd753b355c73 100644 --- a/docs/library/index.rst +++ b/docs/library/index.rst @@ -194,6 +194,16 @@ The following libraries are specific to the Zephyr port. .. _micropython_lib_extending: +Libraries specific to MIMXRT devices +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following libraries are specific MIMXRT devices. + +.. toctree:: + :maxdepth: 2 + + mimxrt.Encoder.rst + Extending built-in libraries from Python ---------------------------------------- diff --git a/docs/library/mimxrt.Encoder.rst b/docs/library/mimxrt.Encoder.rst new file mode 100644 index 0000000000000..e397c85df5363 --- /dev/null +++ b/docs/library/mimxrt.Encoder.rst @@ -0,0 +1,379 @@ +.. currentmodule:: machine +.. _mimxrt_machine.Encoder: + +class Encoder -- Quadrature Encoder for i.MXRT MCUs +==================================================== + +This class provides the Quadrature Encoder Service. + +Example usage:: + + # Samples for Teensy + # + + from machine import Pin, Encoder + + qe = Encoder(0, Pin(0), Pin(1)) # create Quadrature Encoder object + qe.value() # get current counter values + qe.value(0) # Set value and cycles to 0 + qe.init(cpc=128) # Specify 128 counts/cycle + qe.init(index=Pin("D3")) # Specify Pin 3 as Index pulse input + qe.deinit() # turn off the Quadrature Encoder + qe.irq(qe.IRQ_MATCH, value=100, handler=handler) + # Call the function handler at a match event + + qe # show the Encoder object properties + +Constructors +------------ + +.. class:: Encoder(id, phase_a, phase_b, *, home, match_pin, filter_ns, cpc, signed, index) + :no-index: + + Construct and return a new quadrature encoder object using the following parameters: + + - id: The number of the Encoder. The range is board-specific, starting with 0. + For i.MX RT1015 and i.MX RT1021 based boards, this is 0..1, for i.MX RT1052, + i.MX RT106x and i.MX RT11xx based boards it is 0..3. + - *phase_a*. *phase_a* tells to which pin the phase a of the + encoder is connected. This is usually a :ref:`machine.Pin ` + object, but a port may allow other values, like integers or strings, + which designate a Pin in the machine.PIN class. + - *phase_b*. *phase_b* tells to which pin the phase b of the + encoder is connected. This is usually a :ref:`machine.Pin ` + object, but a port may allow other values, like integers or strings, + which designate a Pin in the machine.PIN class. + + Keyword arguments: + + - *phase_a*\=value and *phase_b*\=value may be assigned by keyword arguments as well. + - *home*\=value. A Pin specifier telling to which pin the home pulse is connected. + At a rising slope of the home pulse the position counter is set to the init + value, but the cycles counter is not changed. A *value* of *None* disables the home input. + - *match_pin*\=value. A Pin specifier telling to which pin the match output is connected. + This output will have a high level as long as the position counter matches the + match value. The signal is generated by the encoder logic and requires no + further software support. The pulse width is defined by the input signal frequency + and can be very short, like 20ns, or stay, if the counter stops at the match position. + A *value* of *None* disables the match output. + - *filter_ns*\=value. Specifies a ns-value for the minimal time a signal has to be stable + at the input to be recognized. The code does the best effort to configure the filter. + The largest value is 20400 for the i.MXRT102x and 17000 for the i.MXRT105x/i.MXRT106x + (1000000000 * 2550 * 4 / CPU_CLK). A value of 0 sets the filter off. + - *cpc*\=value. Specify the number of counts per cycle. Since the + Encoder counts all four phases of the input signal, the cpc value has to be four + times the ppr value given in the encoder data sheet. The position counter will count up + from the 0 up to cpc - 1, and then reset to the init value of 0 and increase + the cycles counter by one. The default is: no cpc set. In that case the + position counter overflows at 2**32 - 1. When counting down, the cycles counter changes + at the transition from 0 to cpc - 1. + - *signed*\=False|True tells, whether the value return by Encoder.value() is signed or + unsigned. The default is ``True``. + - *index*\=value. A Pin specifier telling to which pin the index pulse is connected. + At a rising slope of the index pulse the position counter is set to the init value + and the cycles counter is increased by one. A *value* of *None* disables the index input. + +The arguments phase_a, phase_b and filter_ns are generic across ports, all other arguments are port-specific. + +Methods +------- + +.. method:: Encoder.init(*, phase_a, phase_b, home, match_pin, filter_ns, cpc, signed, index) + :no-index: + + Modify settings for the Encoder object. See the above constructor for details + about the parameters. + +.. method:: Encoder.deinit() + :no-index: + + Stops the Encoder, disables interrupts and releases the resources used by the encoder. On + Soft Reset, all instances of Encoder and Counter are deinitialized. + +.. method:: value=Encoder.value([value]) + + Get or set the current position counter of the Encoder as signed or unsigned 32 bit integer, + depending on the signed=xxx keyword option of init(). + + With no arguments the actual position counter value is returned. + + With a single *value* argument the position counter is set to that value and the + cycles counter is cleared. The methods returns the previous value. + +.. method:: cycles=Encoder.cycles([value]) + + Get or set the current cycles counter of the Encoder as signed 16 bit integer. + + With no arguments the actual cycles counter value is returned. + + With a single *value* argument the cycles counter is set to that value. The + position counter is not changed. The methods returns the previous value. + + If the value returned by Encoder.value() is unsigned, + the total position can be calculated as cycles() * cpc + value(). + If the total position range is still too small, you can create your own cycles + counter using the irq() callback method. + +.. method:: Encoder.irq(trigger=event, value=nnn, handler=handler, hard=False) + + Specifies, that the *handler* is called when the respective *event* happens. + + *event* may be: + - Encoder.IRQ_MATCH Triggered when the position counter matches the match value. + - Encoder.IRQ_ROLL_OVER Triggered when the position counter rolls over from the highest + to the lowest value. + - Encoder.IRQ_ROLL_UNDER Triggered when the position counter rolls under from the lowest + to the highest value. + + The callback is called, when the Encoder is at *value*. For fas signals, the actual counter + value may be different from the trigger value. + The callback function *handler* receives a single argument, which is the Encoder object. All + events share the same callback. The event which triggers the callback can be identified + with the Encoder.status() method. The argument *hard* specifies, whether the callback is called + as a hard interrupt or as regular scheduled function. Hard interrupts have always a short latency, + but are limited in that they must not allocate memory. Regular scheduled functions are not limited + in what can be used, but depending on the load of the device execution may be delayed. + Under low load, the difference in latency is minor. + + The default arguments values are trigger=0, handler=None, hard=False. The callback will be + disabled, when called with handler=None. + + The position match event is triggered as long as the position and match value are identical. + Therefore the position match callback is run in a one-shot fashion, and has to be enabled + again when the position has changed. + +.. method:: Encoder.status() + + Returns the event status flags of the recent handled Encoder interrupt as a bitmap. + The assignment of events to the bits are: + + - 0: Transition at the HOME signal. (*) + - 1: Transition at the INDEX signal. (*) + - 2: Watchdog event. (*) + - 3 or Encoder.IRQ_MATCH: Position match event. + - 4: Phase_A and Phase_B changed at the same time. (*) + - 5 or Encoder.IRQ_ROLL_OVER: Roll-Over event of the counter. + - 6 or Encoder.IRQ_ROLL_UNDER: Roll-Under event of the counter. + - 7: Direction of the last count. 1 for counting up, 0 for counting down. + + (*) These flags are defined, but not (yet) enabled. + +.. method:: Encoder.id() + + Return the id of the Encoder. That may be helpful for interrupt callback functions. + +The Encoder was tested to work up to 25MHz on a Teensy. It may work at +higher frequencies as well, but that was the limit of the test set-up. + + +.. _mimxrt_machine.Counter: + +class Counter-- Signal counter for i.MXRT MCUs +============================================== + +This class provides a Counter service using the Quadrature Encoder module + +Example usage:: + + # Samples for Teensy + # + + from machine import Pin, Counter + + counter = Counter(0, Pin(0)) # create Counter object + counter.value() # get current counter value + counter.value(0) # Set the counter to 0 + counter.init(cpc=128) # Specify 128 counts/cycle + counter.deinit() # turn off the Counter + counter.irq(Counter.IRQ_MATCH, handler) # Call the function handler at a counter match + + counter # show the Counter object properties + +Constructors +------------ + +.. class:: Counter(id, src, *, direction, match_pin, filter_ns, cpc, match, signed, index) + :no-index: + + Construct and return a new Counter object using the following parameters: + + - id: The number of the Counter. The range is board-specific, starting with 0. + For i.MX RT1015 and i.MX RT1021 based boards, this is 0..1, for i.MX RT1052, + i.MX RT106x and i.MX RT11xx based boards it is 0..3. + - *src*. *src* tells to which pin the counter + is connected. This is usually a :ref:`machine.Pin ` + object, but a port may allow other values, like integers or strings, + which designate a Pin in the machine.PIN class. + + Keyword arguments: + + - *src*\=value may be assigned by a keyword argument as well. + - *direction*\=value. Specifying the direction of counting. Suitable values are: + + - Counter.UP: Count up, with a roll-over to 0 at 2**48-1. + - Counter.DOWN: Count down, with a roll-under to 2**48-1 at 0. + - a :ref:`machine.Pin ` object. The level at that pin controls + the counting direction. Low: Count up, High: Count down. + + - *match_pin*\=value. A Pin specifier telling to which pin the match output is connected. + This output will have a high level as long as the lower 32 bit of the counter value + matches the match value. The signal is generated by the encoder logic and + requires no further software support. A *value* of *None* disables the match output. + - *filter_ns*\=value. Specifies a ns-value for the minimal time a signal has to be stable + at the input to be recognized. The code does the best effort to configure the filter. + The largest value is 20400 for the i.MXRT102x and 17000 for the i.MXRT105x/i.MXRT106x + (1000000000 * 2550 * 4 / CPU_CLK). A value of 0 sets the filter off. + - *cpc*\=value. Specify the number of counts per cycle.The counter will count up + from the 0 up to cpc - 1, and then reset to 0 and increase + the cycles counter by one. The default is: no cpc set. In that case the + counter overflows at 2**32 - 1. If the counting direction is DOWN, then the cycles + counter is decreased when counting from 0 to cpc-1. + - *signed*\=False|True tells, whether the value returned by Counter.value() is signed or + unsigned. The default is ``True``. + - *index*\=value. A Pin specifier telling to which pin the index pulse is connected. + At a rising slope of the index pulse the counter is set to 0 + and the cycles counter is increased by one. A *value* of *None* disables the index input. + +The arguments input, direction and filter are generic across ports, all other arguments are port-specific. + + +Methods +------- + +.. method:: Counter.init( *, src, direction, match_pin, filter_ns, cpc, match, signed, index) + :no-index: + + Modify settings for the Counter object. See the above constructor for details + about the parameters. + +.. method:: Counter.deinit() + :no-index: + + Stops the Counter, disables interrupts and releases the resources used by the encoder. On + Soft Reset, all instances of Encoder and Counter are deinitialized. + +.. method:: value=Counter.value([value]) + + Get or set the current event value of the Counter. The value is returned as a signed or + unsigned 32 bit integer, as defined with the signed=True/False option of init() + + With a single *value* argument the counter is set to the lower 32 bits of that value, + and the cycles counter to the bits 32-47 of the supplied number. The methods returns the + previous value. + +.. method:: cycles=Counter.cycles([value]) + + Get or set the current cycles counter of the counter as signed 16 bit integer. + The value represents the overflow or underflow events of the 32bit basic counter. + A total count can be calculated as cycles() * 0x100000000 + value(). + If the total count range is still too small, you can create your own overflow + counter using the irq() callback method. + + With no arguments the actual cycles counter value is returned. + + With a single *value* argument the cycles counter is set to that value. The + base counter is not changed. The methods returns the previous value. + +.. method:: Counter.irq(trigger=event, value=nnn, handler=handler, hard=False) + + Specifies, that the *handler* is called when the respective *event* happens. + + *event* may be: + - Counter.IRQ_COMPARE Triggered when the positions counter matches the match value. + - Counter.IRQ_ROLL_OVER Triggered when the position counter rolls over from the highest + to the lowest value. + - Counter.IRQ_ROLL_UNDER Triggered when the position counter rolls under from the lowest + to the highest value. + + The callback is called, when the Counter is at *value*. For fast signals, the actual counter + value may be different from the trigger value. + The callback function *handler* receives a single argument, which is the Counter object. All + events share the same callback. The event which triggers the callback can be identified + with the Counter.status() method. The argument *hard* specifies, whether the callback is called + as a hard interrupt or as regular scheduled function. Hard interrupts have always a short latency, + but are limited in that they must not allocate memory. Regular scheduled functions are not limited + in what can be used, but depending on the load of the device execution may be delayed. + Under low load, the difference in latency is minor. + + The default arguments values are trigger=0, handler=None, hard=False. The callback will be + disabled, when called with handler=None. + + The counter match event is triggered as long as the lower 32 bit of the counter and match + value match. Therefore the counter match callback is run in a one-shot fashion, and has to be enabled + again when the counter value has changed. + +.. method:: Counter.status() + + Returns the event status flags of the recent handled Counter interrupt as a bitmap. + The assignment of events to the bits are: + + - 0: Transition at the HOME signal. (*) + - 1: Transition at the INDEX signal. (*) + - 2: Watchdog event. (*) + - 3 or Counter.IRQ_MATCH: Position match event. + - 4: Phase_A and Phase_B changed at the same time. (*) + - 5 or Counter.IRQ_ROLL_OVER: Roll-Over event of the counter. + - 6 or Counter.IRQ_ROLL_UNDER: Roll-Under event of the counter. + - 7: Direction of the last count. 1 for counting up, 0 for counting down. + + (*) These flags are defined, but not (yet) enabled. + +.. method:: Counter.id() + + Return the id of the Counter. That may be helpful for interrupt callback functions. + + +The counter was tested up to 50MHz. It may work at higher frequencies +as well, but that was the limit of the test set-up. + +Pin Assignment +-------------- + +Pins are specified in the same way as for the Pin class. The pins available for an +assignment to the Encoder or Counter are: + +**IMXRT1010_EVK**, **iMX RT1011 Nano Kit**, **Olimex RT1010Py**: + + Not supported. + +**IMXRT1015_EVK**: + + J30, pins 1 and 3, with the pin names "ENC1" and "ENC2". + +**IMXRT1020_EVK**: + + Pins D0 and D1. + +**IMXRT1050_EVK**, **IMXRT1050_EVKB**, **IMXRT1060_EVK**, **IMXRT1064_EBK**: + + Pins D2, D4, D5, D8, D9, D10, D11, D12, D13, D14, D15, A4, A5. + Depending on the board configuration, not all pins may be wired. + Pins D2, D4 and D5 cannot be used for the match output. + +**IMXRT1170_EVK**: + + Pins D0, D1, D2. + + D2 is connected to the 1G PHY chip as well. So levels may be distorted. + +**Teensy 4.0**: + + Pins 0, 1, 2, 3, 4, 5, 7, 8, 26, 27, 30, 31, 32, 33. Pin 0 and 5 share the + same signal and cannot be used independently. + Pins 26, 27, 30 and 31 cannot be used for the match output. + +**Teensy 4.1**: + + Pins 0, 1, 2, 3, 4, 5, 7, 8, 26, 27, 30, 31, 32, 33, 37, 42, 43, 44, 45, 46 and 47. + Pins 26, 27, 30 and 31 cannot be used for the match output. + Some pins are assigned to the same signal and cannot be used independently. These are: + + - Pin 0, 5 and 37, + - Pin 2 and 43, + - Pin 3 and 42, and + - Pin 4 and 47. + +**Seeed ARCH MIX** + + Pins J3_14, J3_15, J4_19, J4_20, J5_15, J5_16, J5_17, J5_22, J5_23, J5_24, J5_25 and J5_26. + Pins J3_14 and J3_15 cannot be used for the match output. diff --git a/docs/mimxrt/quickref.rst b/docs/mimxrt/quickref.rst index 9f1efd4ffcad2..186c94e7face1 100644 --- a/docs/mimxrt/quickref.rst +++ b/docs/mimxrt/quickref.rst @@ -556,6 +556,62 @@ port and LAN(1) for the 1G port. For details of the network interface refer to the class :ref:`network.LAN `. +class Encoder -- Quadrature Encoder for i.MXRT MCUs +--------------------------------------------------- + +This class provides the Quadrature Encoder Service. + +Example usage:: + + # Samples for Teensy + # + + from machine import Pin, Encoder + + qe = Encoder(0, Pin(0), Pin(1)) # create Quadrature Encoder object + qe.value() # get current counter values + qe.value(0) # set value and cycles to 0 + qe.init(cpc=128) # specify 128 counts/cycle + qe.init(index=Pin("D3")) # specify Pin 3 as Index pulse input + qe.deinit() # turn off the Quadrature Encoder + qe.init(match=64) # Set a match event at count 64 + qe.irq(qe.IRQ_MATCH, value=100, handler=handler) + # Call the function handler at a match event + + qe # show the Encoder object properties + +The Quadrature Encoder is hardware based. It available on all MIMXRT devices exept the ones +based on the i.MX RT 1010 MCU. For details about using the Encoder with a MIMXRT board +see :ref:`machine.Encoder `: + + +class Counter-- Signal counter for i.MXRT MCUs +---------------------------------------------- + +This class provides a Counter service using the Quadrature Encoder module + +Example usage:: + + # Samples for Teensy + # + + from machine import Pin, Counter + + counter = Counter(0, Pin(0)) # create Counter object + counter.value() # get current counter value + counter.value(0) # Set the counter to 0 + counter.init(cpc=128) # Specify 128 counts/cycle + counter.deinit() # turn off the Counter + counter.init(match=1000) # Create a match event at count 1000 + counter.irq(Counter.IRQ_MATCH, handler) # Call the function handler at a counter match + + counter # show the Counter object properties + +The Quadrature Encoder is hardware based. It available on all MIMXRT devices exept the ones +based on the i.MX RT 1010 MCU. For details about using the Counter with a MIMXRT board +see :ref:`machine.Counter `: + + Transferring files ------------------ From 149bf34e0f3af098a618d8d9cdaa8716e5d3b0e1 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Mon, 19 Sep 2022 13:48:49 +0200 Subject: [PATCH 5/9] mimxrt: Adapt for PR8813 - Optimise mp_obj_type_t storage. Signed-off-by: robert-hh --- ports/mimxrt/machine_encoder.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/ports/mimxrt/machine_encoder.c b/ports/mimxrt/machine_encoder.c index 2e89742b55ebd..d0d56c0357f56 100644 --- a/ports/mimxrt/machine_encoder.c +++ b/ports/mimxrt/machine_encoder.c @@ -568,13 +568,15 @@ STATIC const mp_rom_map_elem_t machine_encoder_locals_dict_table[] = { }; STATIC MP_DEFINE_CONST_DICT(machine_encoder_locals_dict, machine_encoder_locals_dict_table); -const mp_obj_type_t machine_encoder_type = { - { &mp_type_type }, - .name = MP_QSTR_Encoder, - .print = mp_machine_encoder_print, - .make_new = mp_machine_encoder_make_new, - .locals_dict = (mp_obj_dict_t *)&machine_encoder_locals_dict, -}; +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 ---------- @@ -693,12 +695,13 @@ STATIC const mp_rom_map_elem_t machine_counter_locals_dict_table[] = { }; STATIC MP_DEFINE_CONST_DICT(machine_counter_locals_dict, machine_counter_locals_dict_table); -const mp_obj_type_t machine_counter_type = { - { &mp_type_type }, - .name = MP_QSTR_Counter, - .print = mp_machine_encoder_print, - .make_new = mp_machine_counter_make_new, - .locals_dict = (mp_obj_dict_t *)&machine_counter_locals_dict, -}; +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 + ); #endif // MICROPY_PY_MACHINE_QECNT From a61e8a2e948614eaeb53c36d9509bbcba12bd185 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Thu, 17 Nov 2022 18:33:01 +0100 Subject: [PATCH 6/9] mimxrt: Support Encoder/Counter for the MIMXRT117x family. Signed-off-by: robert-hh --- docs/mimxrt/quickref.rst | 16 ++-- ports/mimxrt/machine_encoder.c | 135 ++++++++++++++++++++++++++++----- 2 files changed, 124 insertions(+), 27 deletions(-) diff --git a/docs/mimxrt/quickref.rst b/docs/mimxrt/quickref.rst index 186c94e7face1..6b357def177d5 100644 --- a/docs/mimxrt/quickref.rst +++ b/docs/mimxrt/quickref.rst @@ -574,13 +574,13 @@ Example usage:: qe.init(cpc=128) # specify 128 counts/cycle qe.init(index=Pin("D3")) # specify Pin 3 as Index pulse input qe.deinit() # turn off the Quadrature Encoder - qe.init(match=64) # Set a match event at count 64 + qe.init(match=64) # set a match event at count 64 qe.irq(qe.IRQ_MATCH, value=100, handler=handler) - # Call the function handler at a match event + # call the function handler at a match event qe # show the Encoder object properties -The Quadrature Encoder is hardware based. It available on all MIMXRT devices exept the ones +The Quadrature Encoder is hardware based. It is available at all MIMXRT devices except the ones based on the i.MX RT 1010 MCU. For details about using the Encoder with a MIMXRT board see :ref:`machine.Encoder `: @@ -599,15 +599,15 @@ Example usage:: counter = Counter(0, Pin(0)) # create Counter object counter.value() # get current counter value - counter.value(0) # Set the counter to 0 - counter.init(cpc=128) # Specify 128 counts/cycle + counter.value(0) # set the counter to 0 + counter.init(cpc=128) # specify 128 counts/cycle counter.deinit() # turn off the Counter - counter.init(match=1000) # Create a match event at count 1000 - counter.irq(Counter.IRQ_MATCH, handler) # Call the function handler at a counter match + counter.init(match=1000) # create a match event at count 1000 + counter.irq(Counter.IRQ_MATCH, handler) # call the function handler at a counter match counter # show the Counter object properties -The Quadrature Encoder is hardware based. It available on all MIMXRT devices exept the ones +The Counter is hardware based. It is available at all MIMXRT devices except the ones based on the i.MX RT 1010 MCU. For details about using the Counter with a MIMXRT board see :ref:`machine.Counter `: diff --git a/ports/mimxrt/machine_encoder.c b/ports/mimxrt/machine_encoder.c index d0d56c0357f56..f234bb2fcef7a 100644 --- a/ports/mimxrt/machine_encoder.c +++ b/ports/mimxrt/machine_encoder.c @@ -42,6 +42,7 @@ 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; @@ -69,13 +70,6 @@ typedef struct _encoder_xbar_signal_t { #define ENCODER_TRIGGER_ROLL_UNDER (kENC_PositionRollUnderFlag) #define ENCODER_ALL_INTERRUPTS (0x7f) -#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_IN (1) #define XBAR_OUT (0) @@ -86,6 +80,58 @@ typedef struct _encoder_xbar_signal_t { 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, @@ -122,6 +168,7 @@ static encoder_xbar_signal_t xbar_signal_table[FSL_FEATURE_SOC_ENC_COUNT] = { #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; @@ -172,7 +219,7 @@ STATIC void mp_machine_encoder_print(const mp_print_t *print, mp_obj_t self_in, self->id, self->cpc, self->enc_config.positionCompareValue, self->filter); } -// Utililty functions +// Utility functions // STATIC void encoder_set_iomux(const machine_pin_obj_t *pin, const machine_pin_af_obj_t *af) { @@ -185,13 +232,14 @@ STATIC const machine_pin_af_obj_t *af_name_decode_xbar(const machine_pin_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_INOUT - if (len < 12 || strncmp(str, "XBAR_INOUT", 10) != 0) { + // 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[10] - '0') * 10 + (str[11] - '0'); + *io_number = (str[xlen] - '0') * 10 + (str[xlen + 1] - '0'); return af_obj; } @@ -215,12 +263,26 @@ STATIC uint8_t connect_pin_to_encoder(mp_obj_t desc, xbar_output_signal_t encode 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; } @@ -239,7 +301,12 @@ STATIC void clear_encoder_registers(machine_encoder_obj_t *self) { // 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 @@ -277,7 +344,15 @@ STATIC void mp_machine_encoder_init_helper_common(machine_encoder_obj_t *self, 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 } } @@ -321,6 +396,7 @@ STATIC void mp_machine_encoder_init_helper_common(machine_encoder_obj_t *self, ENC_Init(self->instance, enc_config); clear_encoder_registers(self); ENC_ClearStatusFlags(self->instance, 0xff); // Clear all status flags + self->active = true; } STATIC void mp_machine_encoder_init_helper(machine_encoder_obj_t *self, @@ -420,15 +496,26 @@ STATIC mp_obj_t mp_machine_encoder_make_new(const mp_obj_type_t *type, size_t n_ } STATIC void encoder_deinit_single(machine_encoder_obj_t *self) { - if (self->irq->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. - IOMUXC_GPR->XBAR_ENC_DIR_REGISTER &= ~(1 << (self->match_pin + XBAR_ENC_DIR_OFFSET)); + if (self->active) { + if (self->irq && self->irq->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); } - ENC_Deinit(self->instance); + self->active = false; } // encoder_deinit_all() @@ -458,6 +545,9 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_encoder_status_obj, machine_encoder_sta // 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) { // Set the encoder position value and clear the rev counter. @@ -481,6 +571,10 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_encoder_value_obj, 1, 2, mach // 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 @@ -502,6 +596,9 @@ STATIC mp_obj_t machine_encoder_irq(size_t n_args, const mp_obj_t *pos_args, mp_ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); machine_encoder_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + if (!self->active) { + mp_raise_ValueError(MP_ERROR_TEXT("device stopped")); + } if (self->irq == NULL) { self->irq = m_new_obj(mp_irq_obj_t); From 1b5b32fbe7a3418d4c6e1ad79c1e34894b5a5358 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Fri, 3 Nov 2023 15:44:46 +0100 Subject: [PATCH 7/9] mimxrt: Adapt for the changed pin file creation of PR 12211. and replace "STATIC" with "static". Signed-off-by: robert-hh --- docs/library/mimxrt.Encoder.rst | 23 ++++++------ docs/mimxrt/quickref.rst | 4 +-- ports/mimxrt/boards/make-pins.py | 6 ++-- ports/mimxrt/machine_encoder.c | 61 ++++++++++++++++---------------- 4 files changed, 49 insertions(+), 45 deletions(-) diff --git a/docs/library/mimxrt.Encoder.rst b/docs/library/mimxrt.Encoder.rst index e397c85df5363..61e57586cca25 100644 --- a/docs/library/mimxrt.Encoder.rst +++ b/docs/library/mimxrt.Encoder.rst @@ -13,7 +13,7 @@ Example usage:: from machine import Pin, Encoder - qe = Encoder(0, Pin(0), Pin(1)) # create Quadrature Encoder object + qe = Encoder(0, Pin("D0"), Pin("D1")) # create Quadrature Encoder object qe.value() # get current counter values qe.value(0) # Set value and cycles to 0 qe.init(cpc=128) # Specify 128 counts/cycle @@ -180,7 +180,7 @@ Example usage:: from machine import Pin, Counter - counter = Counter(0, Pin(0)) # create Counter object + counter = Counter(0, Pin("D0")) # create Counter object counter.value() # get current counter value counter.value(0) # Set the counter to 0 counter.init(cpc=128) # Specify 128 counts/cycle @@ -358,20 +358,21 @@ assignment to the Encoder or Counter are: **Teensy 4.0**: - Pins 0, 1, 2, 3, 4, 5, 7, 8, 26, 27, 30, 31, 32, 33. Pin 0 and 5 share the - same signal and cannot be used independently. - Pins 26, 27, 30 and 31 cannot be used for the match output. + Pins D0, D1, D2, D3, D4, D5, D7, D8, D26, D27, D30, D31, D32, D33. + Pin D0 and D5 share the same signal and cannot be used independently. + Pins D26, D27, D30 and D31 cannot be used for the match output. **Teensy 4.1**: - Pins 0, 1, 2, 3, 4, 5, 7, 8, 26, 27, 30, 31, 32, 33, 37, 42, 43, 44, 45, 46 and 47. - Pins 26, 27, 30 and 31 cannot be used for the match output. + Pins D0, D1, D2, D3, D4, D5, D7, D8, D26, D27, D30, D31, D32, D33, + D37, D42, D43, D44, D45, D46 and D47. + Pins D26, D27, D30 and D31 cannot be used for the match output. Some pins are assigned to the same signal and cannot be used independently. These are: - - Pin 0, 5 and 37, - - Pin 2 and 43, - - Pin 3 and 42, and - - Pin 4 and 47. + - Pins D0, D5 and D37, + - Pins D2 and D43, + - Pins D3 and D42, and + - Pins D4 and D47. **Seeed ARCH MIX** diff --git a/docs/mimxrt/quickref.rst b/docs/mimxrt/quickref.rst index 6b357def177d5..2bc74e85846f4 100644 --- a/docs/mimxrt/quickref.rst +++ b/docs/mimxrt/quickref.rst @@ -568,7 +568,7 @@ Example usage:: from machine import Pin, Encoder - qe = Encoder(0, Pin(0), Pin(1)) # create Quadrature Encoder object + qe = Encoder(0, Pin("D0"), Pin("D1")) # create Quadrature Encoder object qe.value() # get current counter values qe.value(0) # set value and cycles to 0 qe.init(cpc=128) # specify 128 counts/cycle @@ -597,7 +597,7 @@ Example usage:: from machine import Pin, Counter - counter = Counter(0, Pin(0)) # create Counter object + counter = Counter(0, Pin("D0")) # create Counter object counter.value() # get current counter value counter.value(0) # set the counter to 0 counter.init(cpc=128) # specify 128 counts/cycle diff --git a/ports/mimxrt/boards/make-pins.py b/ports/mimxrt/boards/make-pins.py index 55c7f5bd02738..3a76306e9a81d 100644 --- a/ports/mimxrt/boards/make-pins.py +++ b/ports/mimxrt/boards/make-pins.py @@ -26,7 +26,7 @@ r"IOMUXC_(?PGPIO_SNVS_\d\d_DIG)_(?P\w+) (?P\w+), (?P\w+), (?P\w+), (?P\w+), (?P\w+)", ] -SUPPORTED_AF_FNS = {"GPIO", "USDHC", "FLEXPWM", "TMR"} +SUPPORTED_AF_FNS = {"GPIO", "USDHC", "FLEXPWM", "TMR", "XBAR"} class MimxrtPin(boardgen.Pin): @@ -183,7 +183,7 @@ def load_inputs(self, out_source): # the CMD pin of the USDHC1 function on the GPIO_SD_B0_00 pin. def print_module_instances(self, out_header): print(file=out_header) - for match_fn in ("USDHC", "FLEXPWM", "TMR"): + for match_fn in ("USDHC", "FLEXPWM", "TMR", "XBAR"): module_instances = defaultdict(list) for pin in self.available_pins(): for i, (_af_idx, _input_reg, _input_daisy, instance, fn, af) in enumerate( @@ -200,6 +200,8 @@ def print_module_instances(self, out_header): print("#define {:s}_AVAIL (1)".format(k), file=out_header) if match_fn == "FLEXPWM": print("#define {:s} {:s}".format(k, k[-4:]), file=out_header) + if match_fn == "XBAR": + print("#define {:s} {:s}A1".format(k, k[:4]), file=out_header) for i in v: print(i, file=out_header) diff --git a/ports/mimxrt/machine_encoder.c b/ports/mimxrt/machine_encoder.c index f234bb2fcef7a..173eacfeb0d20 100644 --- a/ports/mimxrt/machine_encoder.c +++ b/ports/mimxrt/machine_encoder.c @@ -31,6 +31,7 @@ #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" @@ -78,7 +79,7 @@ typedef struct _encoder_xbar_signal_t { #define MODE_ENCODER (0) #define MODE_COUNTER (1) -STATIC void encoder_deinit_single(machine_encoder_obj_t *self); +static void encoder_deinit_single(machine_encoder_obj_t *self); #if defined MIMXRT117x_SERIES @@ -211,7 +212,7 @@ __attribute__((section(".ram_functions"))) void ENC4_IRQHandler(void) { #endif #endif -STATIC void mp_machine_encoder_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { +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); mp_printf(print, self->is_signed ? "%s %d cpc=%lu match=%ld filter=%luns>\n" : "%s %d cpc=%lu match=%lu filter=%luns>\n", @@ -222,13 +223,13 @@ STATIC void mp_machine_encoder_print(const mp_print_t *print, mp_obj_t self_in, // Utility functions // -STATIC void encoder_set_iomux(const machine_pin_obj_t *pin, const machine_pin_af_obj_t *af) { +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, +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; @@ -243,7 +244,7 @@ STATIC const machine_pin_af_obj_t *af_name_decode_xbar(const machine_pin_af_obj_ 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) { +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); @@ -254,7 +255,7 @@ STATIC const machine_pin_af_obj_t *find_xbar_af(const machine_pin_obj_t *pin, xb 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) { +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); @@ -287,7 +288,7 @@ STATIC uint8_t connect_pin_to_encoder(mp_obj_t desc, xbar_output_signal_t encode return xbar_pin; } -STATIC void clear_encoder_registers(machine_encoder_obj_t *self) { +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_trigger); XBARA_SetSignalsConnection(XBARA1, kXBARA1_InputLogicLow, xbar_signal_table[self->id].enc_trigger); @@ -299,7 +300,7 @@ STATIC void clear_encoder_registers(machine_encoder_obj_t *self) { // 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) { +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; @@ -333,7 +334,7 @@ STATIC uint32_t calc_filter(uint32_t filter_ns, uint16_t *count, uint16_t *perio // Micropython API functions // -STATIC void mp_machine_encoder_init_helper_common(machine_encoder_obj_t *self, +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_match_pin, ARG_filter_ns, ARG_cpc, ARG_signed, ARG_index }; @@ -399,7 +400,7 @@ STATIC void mp_machine_encoder_init_helper_common(machine_encoder_obj_t *self, self->active = true; } -STATIC void mp_machine_encoder_init_helper(machine_encoder_obj_t *self, +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_home, ARG_match_pin, ARG_filter_ns, ARG_cpc, ARG_signed, ARG_index}; @@ -448,7 +449,7 @@ STATIC void mp_machine_encoder_init_helper(machine_encoder_obj_t *self, } // 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) { +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); @@ -495,7 +496,7 @@ STATIC mp_obj_t mp_machine_encoder_make_new(const mp_obj_type_t *type, size_t n_ return MP_OBJ_FROM_PTR(self); } -STATIC void encoder_deinit_single(machine_encoder_obj_t *self) { +static void encoder_deinit_single(machine_encoder_obj_t *self) { if (self->active) { if (self->irq && self->irq->handler) { DisableIRQ(enc_irqn[self->id + 1]); @@ -529,21 +530,21 @@ void machine_encoder_deinit_all(void) { } // encoder.deinit() -STATIC mp_obj_t machine_encoder_deinit(mp_obj_t self_in) { +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); +static MP_DEFINE_CONST_FUN_OBJ_1(machine_encoder_deinit_obj, machine_encoder_deinit); // encoder.status() mp_obj_t machine_encoder_status(mp_obj_t self_in) { machine_encoder_obj_t *self = MP_OBJ_TO_PTR(self_in); return MP_OBJ_NEW_SMALL_INT(self->status & (self->requested_irq | kENC_LastCountDirectionFlag)); } -STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_encoder_status_obj, machine_encoder_status); +static MP_DEFINE_CONST_FUN_OBJ_1(machine_encoder_status_obj, machine_encoder_status); // encoder.value([value]) -STATIC mp_obj_t machine_encoder_value(size_t n_args, const mp_obj_t *args) { +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")); @@ -566,10 +567,10 @@ STATIC mp_obj_t machine_encoder_value(size_t n_args, const mp_obj_t *args) { return mp_obj_new_int_from_uint(actual_value); } } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_encoder_value_obj, 1, 2, machine_encoder_value); +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) { +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")); @@ -582,10 +583,10 @@ STATIC mp_obj_t machine_encoder_cycles(size_t n_args, const mp_obj_t *args) { } return MP_OBJ_NEW_SMALL_INT(cycles); } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_encoder_cycles_obj, 1, 2, machine_encoder_cycles); +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_encoder_cycles_obj, 1, 2, machine_encoder_cycles); // encoder.irq(trigger=ENCODER.IRQ_MATCH, value=nnn, 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) { +static mp_obj_t machine_encoder_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_trigger, ARG_value, ARG_handler, ARG_hard }; static const mp_arg_t allowed_args[] = { { MP_QSTR_trigger, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, @@ -637,20 +638,20 @@ STATIC mp_obj_t machine_encoder_irq(size_t n_args, const mp_obj_t *pos_args, mp_ 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) { +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); // encoder.id() -STATIC mp_obj_t machine_encoder_id(mp_obj_t self_in) { +static mp_obj_t machine_encoder_id(mp_obj_t self_in) { machine_encoder_obj_t *self = MP_OBJ_TO_PTR(self_in); return MP_OBJ_NEW_SMALL_INT(self->id); } -STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_encoder_id_obj, machine_encoder_id); +static MP_DEFINE_CONST_FUN_OBJ_1(machine_encoder_id_obj, machine_encoder_id); -STATIC const mp_rom_map_elem_t machine_encoder_locals_dict_table[] = { +static const mp_rom_map_elem_t machine_encoder_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&machine_encoder_deinit_obj) }, { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_encoder_init_obj) }, { MP_ROM_QSTR(MP_QSTR_irq), MP_ROM_PTR(&machine_encoder_irq_obj) }, @@ -663,7 +664,7 @@ STATIC const mp_rom_map_elem_t machine_encoder_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_IRQ_ROLL_UNDER), MP_ROM_INT(ENCODER_TRIGGER_ROLL_UNDER) }, { MP_ROM_QSTR(MP_QSTR_IRQ_ROLL_OVER), MP_ROM_INT(ENCODER_TRIGGER_ROLL_OVER) }, }; -STATIC MP_DEFINE_CONST_DICT(machine_encoder_locals_dict, machine_encoder_locals_dict_table); +static MP_DEFINE_CONST_DICT(machine_encoder_locals_dict, machine_encoder_locals_dict_table); MP_DEFINE_CONST_OBJ_TYPE( machine_encoder_type, @@ -677,7 +678,7 @@ MP_DEFINE_CONST_OBJ_TYPE( // --- Counter class code ---------- -STATIC void mp_machine_counter_init_helper(machine_encoder_obj_t *self, +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_match_pin, ARG_filter_ns, ARG_cpc, ARG_signed, ARG_index }; static const mp_arg_t allowed_args[] = { @@ -717,7 +718,7 @@ STATIC void mp_machine_counter_init_helper(machine_encoder_obj_t *self, } // 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) { +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); @@ -769,13 +770,13 @@ STATIC mp_obj_t mp_machine_counter_make_new(const mp_obj_type_t *type, size_t n_ } // counter.init([kwargs]) -STATIC mp_obj_t machine_counter_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { +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[] = { +static const mp_rom_map_elem_t machine_counter_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&machine_encoder_deinit_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) }, @@ -790,7 +791,7 @@ STATIC const mp_rom_map_elem_t machine_counter_locals_dict_table[] = { { 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); +static MP_DEFINE_CONST_DICT(machine_counter_locals_dict, machine_counter_locals_dict_table); MP_DEFINE_CONST_OBJ_TYPE( machine_counter_type, From 82bf5b2042332a12df0c37bf08bebd34127c41dc Mon Sep 17 00:00:00 2001 From: robert-hh Date: Tue, 5 Aug 2025 10:12:16 +0200 Subject: [PATCH 8/9] tests/extmod_hardware/machine_encoder: Add a MIMXRT configuration. For Teensy 4.x. The connectivity tests and the falling edge of the counter test are skipped. Signed-off-by: robert-hh --- tests/extmod_hardware/machine_counter.py | 7 +++++++ tests/extmod_hardware/machine_encoder.py | 17 +++++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/tests/extmod_hardware/machine_counter.py b/tests/extmod_hardware/machine_counter.py index 62ac1fed47ce7..22acef07c038f 100644 --- a/tests/extmod_hardware/machine_counter.py +++ b/tests/extmod_hardware/machine_counter.py @@ -16,6 +16,11 @@ id = 0 out_pin = 4 in_pin = 5 +elif sys.platform == "mimxrt": + if "Teensy" in sys.implementation._machine: + id = 0 + out_pin = "D2" + in_pin = "D3" else: print("Please add support for this test on this platform.") raise SystemExit @@ -43,6 +48,7 @@ def tearDown(self): def assertCounter(self, value): self.assertEqual(self.counter.value(), value) + @unittest.skipIf(sys.platform == "mimxrt", "cannot read back the pin") def test_connections(self): # Test the hardware connections are correct. If this test fails, all tests will fail. out_pin(1) @@ -73,6 +79,7 @@ def test_change_directions(self): toggle(25) self.assertCounter(75) + @unittest.skipIf(sys.platform == "mimxrt", "FALLING edge not supported") def test_count_falling(self): self.counter.init(in_pin, direction=Counter.UP, edge=Counter.FALLING) toggle(20) diff --git a/tests/extmod_hardware/machine_encoder.py b/tests/extmod_hardware/machine_encoder.py index 9bd2bb464178c..3f014e4aa7d14 100644 --- a/tests/extmod_hardware/machine_encoder.py +++ b/tests/extmod_hardware/machine_encoder.py @@ -19,6 +19,15 @@ in0_pin = 5 out1_pin = 12 in1_pin = 13 + start_value = -1 +elif sys.platform == "mimxrt": + if "Teensy" in sys.implementation._machine: + id = 0 + out0_pin = "D2" + in0_pin = "D3" + out1_pin = "D5" + in1_pin = "D4" + start_value = 0 else: print("Please add support for this test on this platform.") raise SystemExit @@ -35,8 +44,9 @@ class TestEncoder(unittest.TestCase): def setUp(self): out0_pin(0) out1_pin(0) - self.enc = Encoder(id, in0_pin, in1_pin) + self.enc = Encoder(id, in0_pin, in1_pin, phases=1) self.pulses = 0 # track the expected encoder position in software + self.pulses = start_value # track the expected encoder position in software def tearDown(self): self.enc.deinit() @@ -52,6 +62,7 @@ def rotate(self, pulses): def assertPosition(self, value): self.assertEqual(self.enc.value(), value) + @unittest.skipIf(sys.platform == "mimxrt", "cannot read back the pin") def test_connections(self): # Test the hardware connections are correct. If this test fails, all tests will fail. for ch, outp, inp in ((0, out0_pin, in0_pin), (1, out1_pin, in1_pin)): @@ -76,7 +87,9 @@ def test_partial(self): self.rotate(1) self.assertPosition(0) self.rotate(1) - self.assertPosition(1) # only 3 pulses to count first rotation? + self.assertPosition(0) + self.rotate(1) + self.assertPosition(1) # 4 pulses to count first rotation? self.rotate(1) self.assertPosition(1) self.rotate(1) From cfe12cbf465d2bf4fc967bcbb9b4baecefbd557f Mon Sep 17 00:00:00 2001 From: robert-hh Date: Tue, 5 Aug 2025 10:10:53 +0200 Subject: [PATCH 9/9] mimxrt/machine_encoder: Emulate the phases argument. By dividing the internal counter by 1, 2 or 4. The default is 4, matching the hardware. The phases=1 and phase=2 result are calulated for positive counter values by: ret = (counter + 3) / 4 for phases = 1 and ret = (counter + 1) / 2 for phase = 2 Negative values are just divided by 4 or 2. That way it matches the ESP32 behavior when the start level of both inputs is 1. Signed-off-by: robert-hh --- docs/library/mimxrt.Encoder.rst | 6 +++++- ports/mimxrt/machine_encoder.c | 21 +++++++++++++++++---- tests/extmod_hardware/machine_encoder.py | 1 - 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/docs/library/mimxrt.Encoder.rst b/docs/library/mimxrt.Encoder.rst index 61e57586cca25..768a96145c37f 100644 --- a/docs/library/mimxrt.Encoder.rst +++ b/docs/library/mimxrt.Encoder.rst @@ -27,7 +27,7 @@ Example usage:: Constructors ------------ -.. class:: Encoder(id, phase_a, phase_b, *, home, match_pin, filter_ns, cpc, signed, index) +.. class:: Encoder(id, phase_a, phase_b, *, home, match_pin, filter_ns, cpc, signed, index, phases) :no-index: Construct and return a new quadrature encoder object using the following parameters: @@ -72,6 +72,10 @@ Constructors - *index*\=value. A Pin specifier telling to which pin the index pulse is connected. At a rising slope of the index pulse the position counter is set to the init value and the cycles counter is increased by one. A *value* of *None* disables the index input. + - *phases* specifies the number of signal edges to count and thus the + granularity of the decoding. e.g. 4 phases corresponds to "4x quadrature + decoding", and will result in four counts per pulse. Ports may support + either 1, 2, or 4 phases and the default is 4 phase. *(Supported on MIMXRT)* The arguments phase_a, phase_b and filter_ns are generic across ports, all other arguments are port-specific. diff --git a/ports/mimxrt/machine_encoder.c b/ports/mimxrt/machine_encoder.c index 173eacfeb0d20..ba4d7179e125f 100644 --- a/ports/mimxrt/machine_encoder.c +++ b/ports/mimxrt/machine_encoder.c @@ -49,6 +49,7 @@ typedef struct _machine_encoder_obj_t { uint8_t mode; bool is_signed; uint8_t match_pin; + uint8_t phases_inv; uint32_t cpc; uint32_t filter; uint16_t status; @@ -403,7 +404,7 @@ static void mp_machine_encoder_init_helper_common(machine_encoder_obj_t *self, 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_home, - ARG_match_pin, ARG_filter_ns, ARG_cpc, ARG_signed, ARG_index}; + ARG_match_pin, ARG_filter_ns, ARG_cpc, ARG_signed, ARG_index, ARG_phases}; static const mp_arg_t allowed_args[] = { { MP_QSTR_phase_a, MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, @@ -414,6 +415,7 @@ static void mp_machine_encoder_init_helper(machine_encoder_obj_t *self, { MP_QSTR_cpc, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, { MP_QSTR_signed, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_index, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_INT(-1)} }, + { MP_QSTR_phases, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 4} }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, @@ -442,6 +444,12 @@ static void mp_machine_encoder_init_helper(machine_encoder_obj_t *self, } } + // Get the Phases argument + 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_match_pin, &self->enc_config); @@ -552,7 +560,7 @@ static mp_obj_t machine_encoder_value(size_t n_args, const mp_obj_t *args) { uint32_t actual_value = ENC_GetPositionValue(self->instance); if (n_args > 1) { // Set the encoder position value and clear the rev counter. - uint32_t value = mp_obj_int_get_truncated(args[1]); + uint32_t value = mp_obj_int_get_truncated(args[1]) * self->phases_inv; clear_encoder_registers(self); // Set the position and rev register ENC_SetInitialPositionValue(self->instance, value); @@ -562,9 +570,13 @@ static mp_obj_t machine_encoder_value(size_t n_args, const mp_obj_t *args) { } // Get the position as signed or unsigned 32 bit value. if (self->is_signed) { - return mp_obj_new_int((int32_t)actual_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); } else { - return mp_obj_new_int_from_uint(actual_value); + return mp_obj_new_int_from_uint((actual_value + (self->phases_inv - 1))/ self->phases_inv); } } static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_encoder_value_obj, 1, 2, machine_encoder_value); @@ -751,6 +763,7 @@ static mp_obj_t mp_machine_counter_make_new(const mp_obj_type_t *type, size_t n_ self->irq = NULL; self->match_pin = 0; self->is_signed = true; + self->phases_inv = 1; self->mode = MODE_COUNTER; // Set defaults for ENC Config diff --git a/tests/extmod_hardware/machine_encoder.py b/tests/extmod_hardware/machine_encoder.py index 3f014e4aa7d14..668a3aea4e2f4 100644 --- a/tests/extmod_hardware/machine_encoder.py +++ b/tests/extmod_hardware/machine_encoder.py @@ -45,7 +45,6 @@ def setUp(self): out0_pin(0) out1_pin(0) self.enc = Encoder(id, in0_pin, in1_pin, phases=1) - self.pulses = 0 # track the expected encoder position in software self.pulses = start_value # track the expected encoder position in software def tearDown(self):