/* * 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 "extmod/modmachine.h" #include "modmachine.h" #include "fsl_clock.h" #include "fsl_enc.h" #include "fsl_xbara.h" #include "fsl_iomuxc.h" #include "fsl_gpio.h" typedef struct _machine_encoder_obj_t { mp_obj_base_t base; ENC_Type *instance; int8_t id; bool active; uint8_t input_a; uint8_t input_b; uint8_t mode; 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) #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); #if defined MIMXRT117x_SERIES #define XBAR_ENC_DIR_OFFSET_1 (4) #define XBAR_ENC_DIR_REGISTER_1 GPR20 #define XBAR_ENC_DIR_OFFSET_2 (32) #define XBAR_ENC_DIR_REGISTER_2 GPR21 #define XBAR_OUT_MIN (4) #define XBAR_OUT_MAX (42) #define XBAR_STRING "XBAR1_INOUT" #define XBAR_STRING_LEN strlen(XBAR_STRING) static encoder_xbar_signal_t xbar_signal_table[FSL_FEATURE_SOC_ENC_COUNT] = { { kXBARA1_OutputDec1Phasea, kXBARA1_OutputDec1Phaseb, kXBARA1_OutputDec1Index, kXBARA1_OutputDec1Home, kXBARA1_OutputDec1Trigger, kXBARA1_InputDec1PosMatch }, { kXBARA1_OutputDec2Phasea, kXBARA1_OutputDec2Phaseb, kXBARA1_OutputDec2Index, kXBARA1_OutputDec2Home, kXBARA1_OutputDec2Trigger, kXBARA1_InputDec2PosMatch }, { kXBARA1_OutputDec3Phasea, kXBARA1_OutputDec3Phaseb, kXBARA1_OutputDec3Index, kXBARA1_OutputDec3Home, kXBARA1_OutputDec3Trigger, kXBARA1_InputDec3PosMatch }, { kXBARA1_OutputDec4Phasea, kXBARA1_OutputDec4Phaseb, kXBARA1_OutputDec4Index, kXBARA1_OutputDec4Home, kXBARA1_OutputDec4Trigger, kXBARA1_InputDec4PosMatch }, }; #else // defined MIMXRT117x_SERIES #if !defined(XBAR_ENC_DIR_OFFSET) #define XBAR_ENC_DIR_OFFSET (12) #define XBAR_ENC_DIR_REGISTER GPR6 #define XBAR_OUT_MIN (4) #define XBAR_OUT_MAX (19) #endif #define XBAR_STRING "XBAR_INOUT" #define XBAR_STRING_LEN strlen(XBAR_STRING) static encoder_xbar_signal_t xbar_signal_table[FSL_FEATURE_SOC_ENC_COUNT] = { { kXBARA1_OutputEnc1PhaseAInput, kXBARA1_OutputEnc1PhaseBInput, kXBARA1_OutputEnc1Index, kXBARA1_OutputEnc1Home, kXBARA1_OutputEnc1Trigger, kXBARA1_InputEnc1PosMatch }, #if FSL_FEATURE_SOC_ENC_COUNT > 1 { kXBARA1_OutputEnc2PhaseAInput, kXBARA1_OutputEnc2PhaseBInput, kXBARA1_OutputEnc2Index, kXBARA1_OutputEnc2Home, kXBARA1_OutputEnc2Trigger, kXBARA1_InputEnc2PosMatch }, #if FSL_FEATURE_SOC_ENC_COUNT > 2 { kXBARA1_OutputEnc3PhaseAInput, kXBARA1_OutputEnc3PhaseBInput, kXBARA1_OutputEnc3Index, kXBARA1_OutputEnc3Home, kXBARA1_OutputEnc3Trigger, kXBARA1_InputEnc3PosMatch }, { kXBARA1_OutputEnc4PhaseAInput, kXBARA1_OutputEnc4PhaseBInput, kXBARA1_OutputEnc4Index, kXBARA1_OutputEnc4Home, kXBARA1_OutputEnc4Trigger, kXBARA1_InputEnc4PosMatch }, #endif #endif }; #endif // defined MIMXRT117x_SERIES static machine_encoder_obj_t *encoder_table[FSL_FEATURE_SOC_ENC_COUNT]; static ENC_Type *enc_instances[] = ENC_BASE_PTRS; static IRQn_Type enc_irqn[] = ENC_COMPARE_IRQS; __attribute__((section(".ram_functions"))) void irq_callback(int irq_num) { machine_encoder_obj_t *self = encoder_table[irq_num]; if (self != NULL) { self->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); } #if FSL_FEATURE_SOC_ENC_COUNT > 1 __attribute__((section(".ram_functions"))) void ENC2_IRQHandler(void) { irq_callback(1); } #if FSL_FEATURE_SOC_ENC_COUNT > 2 __attribute__((section(".ram_functions"))) void ENC3_IRQHandler(void) { irq_callback(2); } __attribute__((section(".ram_functions"))) void ENC4_IRQHandler(void) { irq_callback(3); } #endif #endif static void mp_machine_encoder_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { machine_encoder_obj_t *self = MP_OBJ_TO_PTR(self_in); 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); } // Utility functions // static void encoder_set_iomux(const machine_pin_obj_t *pin, const machine_pin_af_obj_t *af) { IOMUXC_SetPinMux(pin->muxRegister, af->af_mode, af->input_register, af->input_daisy, pin->configRegister, 0U); IOMUXC_SetPinConfig(pin->muxRegister, af->af_mode, af->input_register, af->input_daisy, pin->configRegister, 0x10B0U); } // decode the AF objects module and Port numer. Returns NULL if it is not a XBAR object static const machine_pin_af_obj_t *af_name_decode_xbar(const machine_pin_af_obj_t *af_obj, xbar_input_signal_t *io_number) { const char *str; size_t len; size_t xlen = XBAR_STRING_LEN; str = (char *)qstr_data(af_obj->name, &len); // test for the name starting with XBAR if (len < (xlen + 2) || strncmp(str, XBAR_STRING, xlen) != 0) { return NULL; } // Get I/O number, e.g. XBAR_INOUT03 *io_number = (str[xlen] - '0') * 10 + (str[xlen + 1] - '0'); return af_obj; } static const machine_pin_af_obj_t *find_xbar_af(const machine_pin_obj_t *pin, xbar_input_signal_t *io_number) { const machine_pin_af_obj_t *af = NULL; for (int i = 0; i < pin->af_list_len; ++i) { af = af_name_decode_xbar(&(pin->af_list[i]), io_number); if (af != NULL) { return af; } } mp_raise_ValueError(MP_ERROR_TEXT("invalid input Pin")); } static uint8_t connect_pin_to_encoder(mp_obj_t desc, xbar_output_signal_t encoder_signal, uint8_t direction) { xbar_input_signal_t xbar_pin; const machine_pin_obj_t *pin = pin_find(desc); const machine_pin_af_obj_t *af = find_xbar_af(pin, &xbar_pin); encoder_set_iomux(pin, af); if (direction == XBAR_IN) { XBARA_SetSignalsConnection(XBARA1, xbar_pin, encoder_signal); } else { // No API here, so do basic Register access. #if defined MIMXRT117x_SERIES if (xbar_pin >= XBAR_OUT_MIN && xbar_pin <= XBAR_OUT_MAX) { if (xbar_pin < XBAR_ENC_DIR_OFFSET_2) { IOMUXC_GPR->XBAR_ENC_DIR_REGISTER_1 |= 1 << (xbar_pin - XBAR_ENC_DIR_OFFSET_1); XBARA_SetSignalsConnection(XBARA1, encoder_signal, xbar_pin); } else { IOMUXC_GPR->XBAR_ENC_DIR_REGISTER_2 |= 1 << (xbar_pin - XBAR_ENC_DIR_OFFSET_2); XBARA_SetSignalsConnection(XBARA1, encoder_signal, xbar_pin); } } else { mp_raise_ValueError(MP_ERROR_TEXT("invalid match Pin")); } #else if (xbar_pin >= XBAR_OUT_MIN && xbar_pin <= XBAR_OUT_MAX) { IOMUXC_GPR->XBAR_ENC_DIR_REGISTER |= 1 << (xbar_pin + XBAR_ENC_DIR_OFFSET); // Compare the offset 12 with other MCU XBARA_SetSignalsConnection(XBARA1, encoder_signal, xbar_pin); } else { mp_raise_ValueError(MP_ERROR_TEXT("invalid match Pin")); } #endif // defined MIMXRT117x_SERIES } return xbar_pin; } static void clear_encoder_registers(machine_encoder_obj_t *self) { // Create a High pulse on the Trigger input, clearing Position, Revolution and Hold registers. XBARA_SetSignalsConnection(XBARA1, kXBARA1_InputLogicHigh, xbar_signal_table[self->id].enc_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) { #if defined MIMXRT117x_SERIES uint32_t freq_khz = CLOCK_GetRootClockFreq(kCLOCK_Root_Bus) / 1000; #else uint32_t freq_khz = CLOCK_GetIpgFreq() / 1000; #endif uint32_t cycles = (filter_ns * (freq_khz / 1000)) / 1000; if (cycles == 0) { // Set filter off *count = 0; *period = 0; } else { uint16_t pmax = cycles / 10; if (pmax > 255) { pmax = 255; } if (pmax == 0) { pmax = 1; } uint16_t cnt; cnt = cycles / pmax; if (cnt > 10) { cnt = 10; } *count = cnt >= 3 ? cnt - 3 : 0; *period = pmax; } return ((1000000000 / freq_khz) + 1) * (*count + 3) * *period / 1000; } // Micropython API functions // static void mp_machine_encoder_init_helper_common(machine_encoder_obj_t *self, mp_arg_val_t args[], enc_config_t *enc_config) { enum { ARG_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. #if defined MIMXRT117x_SERIES if (self->match_pin < XBAR_ENC_DIR_OFFSET_2) { IOMUXC_GPR->XBAR_ENC_DIR_REGISTER_1 &= ~(1 << (self->match_pin - XBAR_ENC_DIR_OFFSET_1)); } else { IOMUXC_GPR->XBAR_ENC_DIR_REGISTER_2 &= ~(1 << (self->match_pin - XBAR_ENC_DIR_OFFSET_2)); } #else IOMUXC_GPR->XBAR_ENC_DIR_REGISTER &= ~(1 << (self->match_pin + XBAR_ENC_DIR_OFFSET)); #endif } } if (args[ARG_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 self->active = true; } static void mp_machine_encoder_init_helper(machine_encoder_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_phase_a, ARG_phase_b, ARG_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->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); } self->active = false; } // encoder_deinit_all() void machine_encoder_deinit_all(void) { for (int i = 0; i < ARRAY_SIZE(encoder_table); i++) { if (encoder_table[i] != NULL) { encoder_deinit_single(encoder_table[i]); encoder_table[i] = NULL; } } } // encoder.deinit() static mp_obj_t machine_encoder_deinit(mp_obj_t self_in) { encoder_deinit_single(MP_OBJ_TO_PTR(self_in)); return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_1(machine_encoder_deinit_obj, machine_encoder_deinit); // encoder.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]); 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. 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]); if (!self->active) { mp_raise_ValueError(MP_ERROR_TEXT("device stopped")); } int16_t cycles = (int16_t)ENC_GetRevolutionValue(self->instance); if (n_args > 1) { // Set the revolution value self->instance->REV = mp_obj_get_int(args[1]); } return MP_OBJ_NEW_SMALL_INT(cycles); } static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_encoder_cycles_obj, 1, 2, machine_encoder_cycles); // 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->active) { mp_raise_ValueError(MP_ERROR_TEXT("device stopped")); } 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); MP_DEFINE_CONST_OBJ_TYPE( machine_encoder_type, MP_QSTR_Encoder, MP_TYPE_FLAG_NONE, make_new, mp_machine_encoder_make_new, print, mp_machine_encoder_print, locals_dict, &machine_encoder_locals_dict ); // --- Counter class code ---------- static void mp_machine_counter_init_helper(machine_encoder_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_src, ARG_direction, ARG_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); 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