kopia lustrzana https://github.com/micropython/micropython
806 wiersze
31 KiB
C
806 wiersze
31 KiB
C
/*
|
|
* 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 ? "<Encoder" : "<Counter",
|
|
self->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
|