From 8c18270f51d2086f7fce42871bef98e0e242de9b Mon Sep 17 00:00:00 2001 From: robert-hh Date: Fri, 24 Sep 2021 21:47:31 +0200 Subject: [PATCH 1/7] 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. --- ports/mimxrt/Makefile | 7 +- .../boards/MIMXRT1010_EVK/mpconfigboard.h | 2 + ports/mimxrt/boards/MIMXRT1011_af.csv | 4 +- .../boards/MIMXRT1020_EVK/mpconfigboard.h | 2 + ports/mimxrt/boards/make-pins.py | 6 +- ports/mimxrt/machine_encoder.c | 698 ++++++++++++++++++ ports/mimxrt/machine_pin.c | 22 +- ports/mimxrt/main.c | 4 + ports/mimxrt/modmachine.c | 4 + ports/mimxrt/modmachine.h | 6 + ports/mimxrt/mpconfigport.h | 2 + 11 files changed, 742 insertions(+), 15 deletions(-) create mode 100644 ports/mimxrt/machine_encoder.c diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index e08f2ad6025a4..880cacd9e965a 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -137,7 +137,11 @@ endif ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES), MIMXRT1015 MIMXRT1021 MIMXRT1052 MIMXRT1062 MIMXRT1064 MIMXRT1176)) SRC_HAL_IMX_C += \ - $(MCU_DIR)/drivers/fsl_qtmr.c + $(MCU_DIR)/drivers/fsl_qtmr.c \ + $(MCU_DIR)/drivers/fsl_enc.c \ + $(MCU_DIR)/drivers/fsl_xbara.c + +CFLAGS += -DMICROPY_PY_MACHINE_QECNT=1 endif ifeq ($(MCU_SERIES), MIMXRT1176) @@ -175,6 +179,7 @@ SRC_C += \ machine_i2s.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 73055526a0857..fdcf0a91e70b1 100644 --- a/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.h @@ -75,3 +75,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 11dd08546332b..21d57bd98f7e8 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 9a9befa126cd8..396ce08a1d669 100644 --- a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h @@ -173,3 +173,5 @@ { IOMUXC_GPIO_AD_B0_15_ENET_TDATA01, 0, 0xB0E9u }, \ { IOMUXC_GPIO_EMC_40_ENET_MDIO, 0, 0xB0E9u }, \ { IOMUXC_GPIO_EMC_41_ENET_MDC, 0, 0xB0E9u }, + +#define XBARA1 XBARA diff --git a/ports/mimxrt/boards/make-pins.py b/ports/mimxrt/boards/make-pins.py index 9d11feb13ac9c..16bc9054a9259 100644 --- a/ports/mimxrt/boards/make-pins.py +++ b/ports/mimxrt/boards/make-pins.py @@ -8,7 +8,7 @@ import csv import re -SUPPORTED_AFS = {"GPIO", "USDHC", "FLEXPWM", "TMR"} +SUPPORTED_AFS = {"GPIO", "USDHC", "FLEXPWM", "TMR", "XBAR"} MAX_AF = 10 # AF0 .. AF9 ADC_COL = 11 @@ -302,6 +302,8 @@ def print_header(self, hdr_filename): module_instance_factory(self.cpu_pins, hdr_file, "USDHC") module_instance_factory(self.cpu_pins, hdr_file, "FLEXPWM") module_instance_factory(self.cpu_pins, hdr_file, "TMR") + # module_instance_factory(self.cpu_pins, hdr_file, "SEMC") + module_instance_factory(self.cpu_pins, hdr_file, "XBAR") def module_instance_factory(pins, output_file, name): @@ -326,6 +328,8 @@ def module_instance_factory(pins, output_file, name): output_file.write(f"#define {k}_AVAIL (1)\n") if name == "FLEXPWM": output_file.write(f"#define {k} {k[-4:]}\n") + if name == "XBAR": + output_file.write(f"#define {k} {k}A1\n") for i in v: output_file.write(i + "\n") 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 d44e649964f72..dead241ebfc34 100644 --- a/ports/mimxrt/machine_pin.c +++ b/ports/mimxrt/machine_pin.c @@ -90,7 +90,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) { @@ -109,43 +109,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 907de373dee6a..b499a1d29d437 100644 --- a/ports/mimxrt/main.c +++ b/ports/mimxrt/main.c @@ -48,6 +48,7 @@ #include "extmod/modnetwork.h" extern uint8_t _sstack, _estack, _gc_heap_start, _gc_heap_end; +extern void machine_encoder_deinit_all(void); void board_init(void); @@ -123,6 +124,9 @@ int main(void) { #endif 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 9097289e79bdc..6b8516277c634 100644 --- a/ports/mimxrt/modmachine.c +++ b/ports/mimxrt/modmachine.c @@ -124,6 +124,10 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&machine_pin_type) }, { MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&machine_adc_type) }, { MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&machine_timer_type) }, + #if MICROPY_PY_MACHINE_QECNT + { MP_ROM_QSTR(MP_QSTR_Encoder), MP_ROM_PTR(&machine_encoder_type) }, + { MP_ROM_QSTR(MP_QSTR_Counter), MP_ROM_PTR(&machine_counter_type) }, + #endif { MP_ROM_QSTR(MP_QSTR_RTC), MP_ROM_PTR(&machine_rtc_type) }, #if MICROPY_PY_MACHINE_SDCARD { MP_ROM_QSTR(MP_QSTR_SDCard), MP_ROM_PTR(&machine_sdcard_type) }, diff --git a/ports/mimxrt/modmachine.h b/ports/mimxrt/modmachine.h index ed1c4e34a07c9..02ddd9a130c04 100644 --- a/ports/mimxrt/modmachine.h +++ b/ports/mimxrt/modmachine.h @@ -39,6 +39,12 @@ extern const mp_obj_type_t machine_spi_type; 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_wdt_type; +extern const mp_obj_type_t machine_sdcard_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 + void machine_adc_init(void); void machine_pin_irq_deinit(void); diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index 911fd41b46206..7615adc366cd4 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -48,6 +48,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 f4abd6d8d4d55fb156801d293cc113202be062d1 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Fri, 11 Feb 2022 16:29:22 +0100 Subject: [PATCH 2/7] mimxrt: Add missing definition for the OLIMEX_RT1010. --- 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 d63c0dc403900..a8c5fbbe2c240 100644 --- a/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.h +++ b/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.h @@ -81,3 +81,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 efe046be8debe933744c00ae0008427e10a10acf Mon Sep 17 00:00:00 2001 From: robert-hh Date: Thu, 10 Mar 2022 20:57:00 +0100 Subject: [PATCH 3/7] mimxrt: Add settings for MIMXRT1015. The MIMXRT1015 MCU has only one encoder/counter unit. --- 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 f37ba824f4859..00142fe998157 100644 --- a/ports/mimxrt/boards/MIMXRT1015_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1015_EVK/mpconfigboard.h @@ -80,3 +80,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 f86921c26c4227bbc4c3c5a679fb37dcd7abe2d4 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Tue, 19 Apr 2022 17:59:27 +0200 Subject: [PATCH 4/7] 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. --- docs/library/index.rst | 11 + docs/library/machine.QECNT.rst | 379 +++++++++++++++++++++++++++++++++ docs/mimxrt/quickref.rst | 56 +++++ 3 files changed, 446 insertions(+) create mode 100644 docs/library/machine.QECNT.rst diff --git a/docs/library/index.rst b/docs/library/index.rst index 59ed1127a7836..8f6ea3eabab9b 100644 --- a/docs/library/index.rst +++ b/docs/library/index.rst @@ -176,6 +176,17 @@ The following libraries are specific to the Zephyr port. zephyr.rst + +Libraries specific to MIMXRT devices +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following libraries are specific MIMXRT devices. + +.. toctree:: + :maxdepth: 2 + + machine.QECNT.rst + Extending built-in libraries from Python ---------------------------------------- diff --git a/docs/library/machine.QECNT.rst b/docs/library/machine.QECNT.rst new file mode 100644 index 0000000000000..e84af87f80fd4 --- /dev/null +++ b/docs/library/machine.QECNT.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(3)) # 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 + +Constructors +------------ + +.. class:: Encoder(id, phase_a, phase_b, *, home, match_pin, filter_ns, cpc, signed, 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 + time 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, reverse, match_pin, filter_ns, cpc, match, signed, index) + + Modify settings for the Encoder object. See the above constructor for details + about the parameters. + +.. method:: Encoder.deinit() + + 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.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 + +Constructors +------------ + +.. class:: Counter(id, src, *, direction, match_pin, filter_ns, cpc, match, signed, 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) + + Modify settings for the Counter object. See the above constructor for details + about the parameters. + +.. method:: Counter.deinit() + + 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**: + + 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. + +**Olimex RT1010Py** + + Not supported. \ No newline at end of file diff --git a/docs/mimxrt/quickref.rst b/docs/mimxrt/quickref.rst index 06f91f7f50312..4d1f09e0534b3 100644 --- a/docs/mimxrt/quickref.rst +++ b/docs/mimxrt/quickref.rst @@ -540,6 +540,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(3)) # 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 1810d97e605627e7a0cfeb4152731b9a605fc13d Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sat, 9 Jul 2022 08:18:50 +0200 Subject: [PATCH 5/7] mimxrt: Fixes to the documentation of the Encoder/Counter class. Thanks to @androiddrew for testing the code and finding the inconsitencies to the documentation. --- docs/library/machine.QECNT.rst | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/library/machine.QECNT.rst b/docs/library/machine.QECNT.rst index e84af87f80fd4..143fe249aff18 100644 --- a/docs/library/machine.QECNT.rst +++ b/docs/library/machine.QECNT.rst @@ -19,7 +19,6 @@ Example usage:: qe.init(cpc=128) # Specify 128 counts/cycle qe.init(index=Pin(3)) # 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 @@ -62,7 +61,7 @@ Constructors (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 - time the ppr value given in the encoder data sheet. The position counter will count up + 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 @@ -78,7 +77,7 @@ The arguments phase_a, phase_b and filter_ns are generic across ports, all other Methods ------- -.. method:: Encoder.init(*, phase_a, phase_b, home, reverse, match_pin, filter_ns, cpc, match, signed, index) +.. method:: Encoder.init(*, phase_a, phase_b, home, match_pin, filter_ns, cpc, signed, index) Modify settings for the Encoder object. See the above constructor for details about the parameters. @@ -183,7 +182,6 @@ Example usage:: 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 From dd38eeb51980e3ef50bd8652553ee2a7d31fad96 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Mon, 19 Sep 2022 13:48:49 +0200 Subject: [PATCH 6/7] mimxrt: Adapt for PR8813 - Optimise mp_obj_type_t storage. --- 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 4f2d497d958a8a70c01872ebb3e8c12ba6c40eab Mon Sep 17 00:00:00 2001 From: robert-hh Date: Thu, 17 Nov 2022 18:33:01 +0100 Subject: [PATCH 7/7] mimxrt: Support Encoder/Counter for the MIMXRT117x family. --- ports/mimxrt/boards/make-pins.py | 2 +- ports/mimxrt/machine_encoder.c | 133 ++++++++++++++++++++++++++----- 2 files changed, 116 insertions(+), 19 deletions(-) diff --git a/ports/mimxrt/boards/make-pins.py b/ports/mimxrt/boards/make-pins.py index 16bc9054a9259..cb2caa1503d6e 100644 --- a/ports/mimxrt/boards/make-pins.py +++ b/ports/mimxrt/boards/make-pins.py @@ -329,7 +329,7 @@ def module_instance_factory(pins, output_file, name): if name == "FLEXPWM": output_file.write(f"#define {k} {k[-4:]}\n") if name == "XBAR": - output_file.write(f"#define {k} {k}A1\n") + output_file.write(f"#define {k} {k[:4]}A1\n") for i in v: output_file.write(i + "\n") diff --git a/ports/mimxrt/machine_encoder.c b/ports/mimxrt/machine_encoder.c index d0d56c0357f56..b87d5e6c00085 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; @@ -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); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy