diff --git a/docs/library/index.rst b/docs/library/index.rst index 2919378ce13b2..7cd753b355c73 100644 --- a/docs/library/index.rst +++ b/docs/library/index.rst @@ -194,6 +194,16 @@ The following libraries are specific to the Zephyr port. .. _micropython_lib_extending: +Libraries specific to MIMXRT devices +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following libraries are specific MIMXRT devices. + +.. toctree:: + :maxdepth: 2 + + mimxrt.Encoder.rst + Extending built-in libraries from Python ---------------------------------------- diff --git a/docs/library/mimxrt.Encoder.rst b/docs/library/mimxrt.Encoder.rst new file mode 100644 index 0000000000000..768a96145c37f --- /dev/null +++ b/docs/library/mimxrt.Encoder.rst @@ -0,0 +1,384 @@ +.. 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("D0"), Pin("D1")) # create Quadrature Encoder object + qe.value() # get current counter values + qe.value(0) # Set value and cycles to 0 + qe.init(cpc=128) # Specify 128 counts/cycle + qe.init(index=Pin("D3")) # Specify Pin 3 as Index pulse input + qe.deinit() # turn off the Quadrature Encoder + qe.irq(qe.IRQ_MATCH, value=100, handler=handler) + # Call the function handler at a match event + + qe # show the Encoder object properties + +Constructors +------------ + +.. class:: Encoder(id, phase_a, phase_b, *, home, match_pin, filter_ns, cpc, signed, index, phases) + :no-index: + + Construct and return a new quadrature encoder object using the following parameters: + + - id: The number of the Encoder. The range is board-specific, starting with 0. + For i.MX RT1015 and i.MX RT1021 based boards, this is 0..1, for i.MX RT1052, + i.MX RT106x and i.MX RT11xx based boards it is 0..3. + - *phase_a*. *phase_a* tells to which pin the phase a of the + encoder is connected. This is usually a :ref:`machine.Pin ` + object, but a port may allow other values, like integers or strings, + which designate a Pin in the machine.PIN class. + - *phase_b*. *phase_b* tells to which pin the phase b of the + encoder is connected. This is usually a :ref:`machine.Pin ` + object, but a port may allow other values, like integers or strings, + which designate a Pin in the machine.PIN class. + + Keyword arguments: + + - *phase_a*\=value and *phase_b*\=value may be assigned by keyword arguments as well. + - *home*\=value. A Pin specifier telling to which pin the home pulse is connected. + At a rising slope of the home pulse the position counter is set to the init + value, but the cycles counter is not changed. A *value* of *None* disables the home input. + - *match_pin*\=value. A Pin specifier telling to which pin the match output is connected. + This output will have a high level as long as the position counter matches the + match value. The signal is generated by the encoder logic and requires no + further software support. The pulse width is defined by the input signal frequency + and can be very short, like 20ns, or stay, if the counter stops at the match position. + A *value* of *None* disables the match output. + - *filter_ns*\=value. Specifies a ns-value for the minimal time a signal has to be stable + at the input to be recognized. The code does the best effort to configure the filter. + The largest value is 20400 for the i.MXRT102x and 17000 for the i.MXRT105x/i.MXRT106x + (1000000000 * 2550 * 4 / CPU_CLK). A value of 0 sets the filter off. + - *cpc*\=value. Specify the number of counts per cycle. Since the + Encoder counts all four phases of the input signal, the cpc value has to be four + times the ppr value given in the encoder data sheet. The position counter will count up + from the 0 up to cpc - 1, and then reset to the init value of 0 and increase + the cycles counter by one. The default is: no cpc set. In that case the + position counter overflows at 2**32 - 1. When counting down, the cycles counter changes + at the transition from 0 to cpc - 1. + - *signed*\=False|True tells, whether the value return by Encoder.value() is signed or + unsigned. The default is ``True``. + - *index*\=value. A Pin specifier telling to which pin the index pulse is connected. + At a rising slope of the index pulse the position counter is set to the init value + and the cycles counter is increased by one. A *value* of *None* disables the index input. + - *phases* specifies the number of signal edges to count and thus the + granularity of the decoding. e.g. 4 phases corresponds to "4x quadrature + decoding", and will result in four counts per pulse. Ports may support + either 1, 2, or 4 phases and the default is 4 phase. *(Supported on MIMXRT)* + +The arguments phase_a, phase_b and filter_ns are generic across ports, all other arguments are port-specific. + +Methods +------- + +.. method:: Encoder.init(*, phase_a, phase_b, home, match_pin, filter_ns, cpc, signed, index) + :no-index: + + Modify settings for the Encoder object. See the above constructor for details + about the parameters. + +.. method:: Encoder.deinit() + :no-index: + + Stops the Encoder, disables interrupts and releases the resources used by the encoder. On + Soft Reset, all instances of Encoder and Counter are deinitialized. + +.. method:: value=Encoder.value([value]) + + Get or set the current position counter of the Encoder as signed or unsigned 32 bit integer, + depending on the signed=xxx keyword option of init(). + + With no arguments the actual position counter value is returned. + + With a single *value* argument the position counter is set to that value and the + cycles counter is cleared. The methods returns the previous value. + +.. method:: cycles=Encoder.cycles([value]) + + Get or set the current cycles counter of the Encoder as signed 16 bit integer. + + With no arguments the actual cycles counter value is returned. + + With a single *value* argument the cycles counter is set to that value. The + position counter is not changed. The methods returns the previous value. + + If the value returned by Encoder.value() is unsigned, + the total position can be calculated as cycles() * cpc + value(). + If the total position range is still too small, you can create your own cycles + counter using the irq() callback method. + +.. method:: Encoder.irq(trigger=event, value=nnn, handler=handler, hard=False) + + Specifies, that the *handler* is called when the respective *event* happens. + + *event* may be: + - Encoder.IRQ_MATCH Triggered when the position counter matches the match value. + - Encoder.IRQ_ROLL_OVER Triggered when the position counter rolls over from the highest + to the lowest value. + - Encoder.IRQ_ROLL_UNDER Triggered when the position counter rolls under from the lowest + to the highest value. + + The callback is called, when the Encoder is at *value*. For fas signals, the actual counter + value may be different from the trigger value. + The callback function *handler* receives a single argument, which is the Encoder object. All + events share the same callback. The event which triggers the callback can be identified + with the Encoder.status() method. The argument *hard* specifies, whether the callback is called + as a hard interrupt or as regular scheduled function. Hard interrupts have always a short latency, + but are limited in that they must not allocate memory. Regular scheduled functions are not limited + in what can be used, but depending on the load of the device execution may be delayed. + Under low load, the difference in latency is minor. + + The default arguments values are trigger=0, handler=None, hard=False. The callback will be + disabled, when called with handler=None. + + The position match event is triggered as long as the position and match value are identical. + Therefore the position match callback is run in a one-shot fashion, and has to be enabled + again when the position has changed. + +.. method:: Encoder.status() + + Returns the event status flags of the recent handled Encoder interrupt as a bitmap. + The assignment of events to the bits are: + + - 0: Transition at the HOME signal. (*) + - 1: Transition at the INDEX signal. (*) + - 2: Watchdog event. (*) + - 3 or Encoder.IRQ_MATCH: Position match event. + - 4: Phase_A and Phase_B changed at the same time. (*) + - 5 or Encoder.IRQ_ROLL_OVER: Roll-Over event of the counter. + - 6 or Encoder.IRQ_ROLL_UNDER: Roll-Under event of the counter. + - 7: Direction of the last count. 1 for counting up, 0 for counting down. + + (*) These flags are defined, but not (yet) enabled. + +.. method:: Encoder.id() + + Return the id of the Encoder. That may be helpful for interrupt callback functions. + +The Encoder was tested to work up to 25MHz on a Teensy. It may work at +higher frequencies as well, but that was the limit of the test set-up. + + +.. _mimxrt_machine.Counter: + +class Counter-- Signal counter for i.MXRT MCUs +============================================== + +This class provides a Counter service using the Quadrature Encoder module + +Example usage:: + + # Samples for Teensy + # + + from machine import Pin, Counter + + counter = Counter(0, Pin("D0")) # create Counter object + counter.value() # get current counter value + counter.value(0) # Set the counter to 0 + counter.init(cpc=128) # Specify 128 counts/cycle + counter.deinit() # turn off the Counter + counter.irq(Counter.IRQ_MATCH, handler) # Call the function handler at a counter match + + counter # show the Counter object properties + +Constructors +------------ + +.. class:: Counter(id, src, *, direction, match_pin, filter_ns, cpc, match, signed, index) + :no-index: + + Construct and return a new Counter object using the following parameters: + + - id: The number of the Counter. The range is board-specific, starting with 0. + For i.MX RT1015 and i.MX RT1021 based boards, this is 0..1, for i.MX RT1052, + i.MX RT106x and i.MX RT11xx based boards it is 0..3. + - *src*. *src* tells to which pin the counter + is connected. This is usually a :ref:`machine.Pin ` + object, but a port may allow other values, like integers or strings, + which designate a Pin in the machine.PIN class. + + Keyword arguments: + + - *src*\=value may be assigned by a keyword argument as well. + - *direction*\=value. Specifying the direction of counting. Suitable values are: + + - Counter.UP: Count up, with a roll-over to 0 at 2**48-1. + - Counter.DOWN: Count down, with a roll-under to 2**48-1 at 0. + - a :ref:`machine.Pin ` object. The level at that pin controls + the counting direction. Low: Count up, High: Count down. + + - *match_pin*\=value. A Pin specifier telling to which pin the match output is connected. + This output will have a high level as long as the lower 32 bit of the counter value + matches the match value. The signal is generated by the encoder logic and + requires no further software support. A *value* of *None* disables the match output. + - *filter_ns*\=value. Specifies a ns-value for the minimal time a signal has to be stable + at the input to be recognized. The code does the best effort to configure the filter. + The largest value is 20400 for the i.MXRT102x and 17000 for the i.MXRT105x/i.MXRT106x + (1000000000 * 2550 * 4 / CPU_CLK). A value of 0 sets the filter off. + - *cpc*\=value. Specify the number of counts per cycle.The counter will count up + from the 0 up to cpc - 1, and then reset to 0 and increase + the cycles counter by one. The default is: no cpc set. In that case the + counter overflows at 2**32 - 1. If the counting direction is DOWN, then the cycles + counter is decreased when counting from 0 to cpc-1. + - *signed*\=False|True tells, whether the value returned by Counter.value() is signed or + unsigned. The default is ``True``. + - *index*\=value. A Pin specifier telling to which pin the index pulse is connected. + At a rising slope of the index pulse the counter is set to 0 + and the cycles counter is increased by one. A *value* of *None* disables the index input. + +The arguments input, direction and filter are generic across ports, all other arguments are port-specific. + + +Methods +------- + +.. method:: Counter.init( *, src, direction, match_pin, filter_ns, cpc, match, signed, index) + :no-index: + + Modify settings for the Counter object. See the above constructor for details + about the parameters. + +.. method:: Counter.deinit() + :no-index: + + Stops the Counter, disables interrupts and releases the resources used by the encoder. On + Soft Reset, all instances of Encoder and Counter are deinitialized. + +.. method:: value=Counter.value([value]) + + Get or set the current event value of the Counter. The value is returned as a signed or + unsigned 32 bit integer, as defined with the signed=True/False option of init() + + With a single *value* argument the counter is set to the lower 32 bits of that value, + and the cycles counter to the bits 32-47 of the supplied number. The methods returns the + previous value. + +.. method:: cycles=Counter.cycles([value]) + + Get or set the current cycles counter of the counter as signed 16 bit integer. + The value represents the overflow or underflow events of the 32bit basic counter. + A total count can be calculated as cycles() * 0x100000000 + value(). + If the total count range is still too small, you can create your own overflow + counter using the irq() callback method. + + With no arguments the actual cycles counter value is returned. + + With a single *value* argument the cycles counter is set to that value. The + base counter is not changed. The methods returns the previous value. + +.. method:: Counter.irq(trigger=event, value=nnn, handler=handler, hard=False) + + Specifies, that the *handler* is called when the respective *event* happens. + + *event* may be: + - Counter.IRQ_COMPARE Triggered when the positions counter matches the match value. + - Counter.IRQ_ROLL_OVER Triggered when the position counter rolls over from the highest + to the lowest value. + - Counter.IRQ_ROLL_UNDER Triggered when the position counter rolls under from the lowest + to the highest value. + + The callback is called, when the Counter is at *value*. For fast signals, the actual counter + value may be different from the trigger value. + The callback function *handler* receives a single argument, which is the Counter object. All + events share the same callback. The event which triggers the callback can be identified + with the Counter.status() method. The argument *hard* specifies, whether the callback is called + as a hard interrupt or as regular scheduled function. Hard interrupts have always a short latency, + but are limited in that they must not allocate memory. Regular scheduled functions are not limited + in what can be used, but depending on the load of the device execution may be delayed. + Under low load, the difference in latency is minor. + + The default arguments values are trigger=0, handler=None, hard=False. The callback will be + disabled, when called with handler=None. + + The counter match event is triggered as long as the lower 32 bit of the counter and match + value match. Therefore the counter match callback is run in a one-shot fashion, and has to be enabled + again when the counter value has changed. + +.. method:: Counter.status() + + Returns the event status flags of the recent handled Counter interrupt as a bitmap. + The assignment of events to the bits are: + + - 0: Transition at the HOME signal. (*) + - 1: Transition at the INDEX signal. (*) + - 2: Watchdog event. (*) + - 3 or Counter.IRQ_MATCH: Position match event. + - 4: Phase_A and Phase_B changed at the same time. (*) + - 5 or Counter.IRQ_ROLL_OVER: Roll-Over event of the counter. + - 6 or Counter.IRQ_ROLL_UNDER: Roll-Under event of the counter. + - 7: Direction of the last count. 1 for counting up, 0 for counting down. + + (*) These flags are defined, but not (yet) enabled. + +.. method:: Counter.id() + + Return the id of the Counter. That may be helpful for interrupt callback functions. + + +The counter was tested up to 50MHz. It may work at higher frequencies +as well, but that was the limit of the test set-up. + +Pin Assignment +-------------- + +Pins are specified in the same way as for the Pin class. The pins available for an +assignment to the Encoder or Counter are: + +**IMXRT1010_EVK**, **iMX RT1011 Nano Kit**, **Olimex RT1010Py**: + + Not supported. + +**IMXRT1015_EVK**: + + J30, pins 1 and 3, with the pin names "ENC1" and "ENC2". + +**IMXRT1020_EVK**: + + Pins D0 and D1. + +**IMXRT1050_EVK**, **IMXRT1050_EVKB**, **IMXRT1060_EVK**, **IMXRT1064_EBK**: + + Pins D2, D4, D5, D8, D9, D10, D11, D12, D13, D14, D15, A4, A5. + Depending on the board configuration, not all pins may be wired. + Pins D2, D4 and D5 cannot be used for the match output. + +**IMXRT1170_EVK**: + + Pins D0, D1, D2. + + D2 is connected to the 1G PHY chip as well. So levels may be distorted. + +**Teensy 4.0**: + + Pins D0, D1, D2, D3, D4, D5, D7, D8, D26, D27, D30, D31, D32, D33. + Pin D0 and D5 share the same signal and cannot be used independently. + Pins D26, D27, D30 and D31 cannot be used for the match output. + +**Teensy 4.1**: + + Pins D0, D1, D2, D3, D4, D5, D7, D8, D26, D27, D30, D31, D32, D33, + D37, D42, D43, D44, D45, D46 and D47. + Pins D26, D27, D30 and D31 cannot be used for the match output. + Some pins are assigned to the same signal and cannot be used independently. These are: + + - Pins D0, D5 and D37, + - Pins D2 and D43, + - Pins D3 and D42, and + - Pins D4 and D47. + +**Seeed ARCH MIX** + + Pins J3_14, J3_15, J4_19, J4_20, J5_15, J5_16, J5_17, J5_22, J5_23, J5_24, J5_25 and J5_26. + Pins J3_14 and J3_15 cannot be used for the match output. diff --git a/docs/mimxrt/quickref.rst b/docs/mimxrt/quickref.rst index 9f1efd4ffcad2..2bc74e85846f4 100644 --- a/docs/mimxrt/quickref.rst +++ b/docs/mimxrt/quickref.rst @@ -556,6 +556,62 @@ port and LAN(1) for the 1G port. For details of the network interface refer to the class :ref:`network.LAN `. +class Encoder -- Quadrature Encoder for i.MXRT MCUs +--------------------------------------------------- + +This class provides the Quadrature Encoder Service. + +Example usage:: + + # Samples for Teensy + # + + from machine import Pin, Encoder + + qe = Encoder(0, Pin("D0"), Pin("D1")) # create Quadrature Encoder object + qe.value() # get current counter values + qe.value(0) # set value and cycles to 0 + qe.init(cpc=128) # specify 128 counts/cycle + qe.init(index=Pin("D3")) # specify Pin 3 as Index pulse input + qe.deinit() # turn off the Quadrature Encoder + qe.init(match=64) # set a match event at count 64 + qe.irq(qe.IRQ_MATCH, value=100, handler=handler) + # call the function handler at a match event + + qe # show the Encoder object properties + +The Quadrature Encoder is hardware based. It is available at all MIMXRT devices except the ones +based on the i.MX RT 1010 MCU. For details about using the Encoder with a MIMXRT board +see :ref:`machine.Encoder `: + + +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("D0")) # create Counter object + counter.value() # get current counter value + counter.value(0) # set the counter to 0 + counter.init(cpc=128) # specify 128 counts/cycle + 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 Counter is hardware based. It is available at all MIMXRT devices except the ones +based on the i.MX RT 1010 MCU. For details about using the Counter with a MIMXRT board +see :ref:`machine.Counter `: + + Transferring files ------------------ diff --git a/extmod/modmachine.h b/extmod/modmachine.h index ef507aca7408c..53660a7b7ab64 100644 --- a/extmod/modmachine.h +++ b/extmod/modmachine.h @@ -217,6 +217,10 @@ extern const mp_obj_type_t machine_timer_type; extern const mp_obj_type_t machine_uart_type; extern const mp_obj_type_t machine_usbd_type; extern const mp_obj_type_t machine_wdt_type; +#if MICROPY_PY_MACHINE_QECNT +extern const mp_obj_type_t machine_encoder_type; +extern const mp_obj_type_t machine_counter_type; +#endif #if MICROPY_PY_MACHINE_SOFTI2C extern const mp_obj_type_t mp_machine_soft_i2c_type; diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index fea7e56da22f4..e6e7a1fa8e504 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -184,9 +184,16 @@ endif ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES), MIMXRT1015 MIMXRT1021 MIMXRT1052 MIMXRT1062 MIMXRT1064 MIMXRT1176)) SRC_HAL_IMX_C += \ $(MCUX_SDK_DIR)/drivers/qtmr_1/fsl_qtmr.c \ + $(MCUX_SDK_DIR)/drivers/enc/fsl_enc.c \ + $(MCUX_SDK_DIR)/drivers/xbara/fsl_xbara.c \ $(MCU_DIR)/drivers/fsl_romapi.c -INC_HAL_IMX += -I$(TOP)/$(MCUX_SDK_DIR)/drivers/qtmr_1 +INC_HAL_IMX += \ + -I$(TOP)/$(MCUX_SDK_DIR)/drivers/enc \ + -I$(TOP)/$(MCUX_SDK_DIR)/drivers/xbara \ + -I$(TOP)/$(MCUX_SDK_DIR)/drivers/qtmr_1 + +CFLAGS += -DMICROPY_PY_MACHINE_QECNT=1 endif # If not empty, then it is 10xx. @@ -251,6 +258,7 @@ SRC_C += \ machine_i2c.c \ machine_led.c \ machine_pin.c \ + machine_encoder.c \ machine_rtc.c \ machine_sdcard.c \ machine_spi.c \ diff --git a/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.h index a616fbec64a59..4950e70ef9dd3 100644 --- a/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.h @@ -82,3 +82,5 @@ I2S_GPIO(1, WS, TX, GPIO_07, IOMUXC_GPIO_07_SAI1_TX_SYNC), \ I2S_GPIO(1, SD, TX, GPIO_04, IOMUXC_GPIO_04_SAI1_TX_DATA00), \ } + +#define XBARA1 XBARA diff --git a/ports/mimxrt/boards/MIMXRT1011_af.csv b/ports/mimxrt/boards/MIMXRT1011_af.csv index be8a9c634dd55..1899c4f7a3867 100644 --- a/ports/mimxrt/boards/MIMXRT1011_af.csv +++ b/ports/mimxrt/boards/MIMXRT1011_af.csv @@ -20,14 +20,14 @@ GPIO_AD_03,LPSPI1_SDI,PIT_TRIGGER3,FLEXPWM1_PWM2_B,KPP_ROW2,GPT2_CLK,GPIO1_IO17, GPIO_AD_04,LPSPI1_SDO,PIT_TRIGGER2,FLEXPWM1_PWM2_A,KPP_COL2,GPT2_COMPARE1,GPIO1_IO18,SNVS_VIO_5_CTL,,,,ADC1_IN4,,ALT5 GPIO_AD_05,LPSPI1_PCS0,PIT_TRIGGER1,FLEXPWM1_PWM3_B,KPP_ROW1,GPT2_CAPTURE1,GPIO1_IO19,,,,,ADC1_IN5,,ALT5 GPIO_AD_06,LPSPI1_SCK,PIT_TRIGGER0,FLEXPWM1_PWM3_A,KPP_COL1,GPT2_COMPARE2,GPIO1_IO20,LPI2C1_HREQ,,,,ADC1_IN6,,ALT5 -GPIO_AD_07,LPI2C2_SDA,LPUART3_RXD,ARM_CM7_RXEV,LPUART2_RTS_B,GPT2_CAPTURE2,GPIO1_IO21,OCOTP_FUSE_LATCHED,XBAR1_INOUT03,,,ADC1_IN7,,ALT5 +GPIO_AD_07,LPI2C2_SDA,LPUART3_RXD,ARM_CM7_RXEV,LPUART2_RTS_B,GPT2_CAPTURE2,GPIO1_IO21,OCOTP_FUSE_LATCHED,XBAR_INOUT03,,,ADC1_IN7,,ALT5 GPIO_AD_08,LPI2C2_SCL,LPUART3_TXD,ARM_CM7_TXEV,LPUART2_CTS_B,GPT2_COMPARE3,GPIO1_IO22,EWM_OUT_B,JTAG_TRSTB,,,ADC1_IN8,,ALT7 GPIO_AD_09,LPSPI2_SDI,FLEXPWM1_PWM3_X,KPP_ROW2,ARM_TRACE_SWO,FLEXIO1_IO21,GPIO1_IO23,REF_CLK_32K,JTAG_TDO,,,ADC1_IN9,,ALT7 GPIO_AD_10,LPSPI2_SDO,FLEXPWM1_PWM2_X,KPP_COL2,PIT_TRIGGER3,FLEXIO1_IO22,GPIO1_IO24,USB_OTG1_ID,JTAG_TDI,,,ADC1_IN10,,ALT7 GPIO_AD_11,LPSPI2_PCS0,FLEXPWM1_PWM1_X,KPP_ROW1,PIT_TRIGGER2,FLEXIO1_IO23,GPIO1_IO25,WDOG1_B,JTAG_MOD,,,ADC1_IN11,,ALT7 GPIO_AD_12,LPSPI2_SCK,FLEXPWM1_PWM0_X,KPP_COL1,PIT_TRIGGER1,FLEXIO1_IO24,GPIO1_IO26,USB_OTG1_PWR,JTAG_TCK,,,ADC1_IN12,,ALT7 GPIO_AD_13,LPI2C1_SDA,LPUART3_RTS_B,KPP_ROW0,LPUART4_RTS_B,FLEXIO1_IO25,GPIO1_IO27,ARM_NMI,JTAG_TMS,,,ADC1_IN13,,ALT7 -GPIO_AD_14,LPI2C1_SCL,LPUART3_CTS_B,KPP_COL0,LPUART4_CTS_B,FLEXIO1_IO26,GPIO1_IO28,REF_CLK_24M,XBAR1_INOUT02,,,ADC1_IN14,,ALT5 +GPIO_AD_14,LPI2C1_SCL,LPUART3_CTS_B,KPP_COL0,LPUART4_CTS_B,FLEXIO1_IO26,GPIO1_IO28,REF_CLK_24M,XBAR_INOUT02,,,ADC1_IN14,,ALT5 GPIO_SD_00,FLEXSPI_B_SS0_B,SAI3_TX_SYNC,ARM_CM7_RXEV,CCM_STOP,FLEXIO1_IO06,GPIO2_IO00,SRC_BT_CFG2,,,,,,ALT5 GPIO_SD_01,FLEXSPI_B_DATA1,SAI3_TX_BCLK,FLEXPWM1_PWM0_B,CCM_CLKO2,FLEXIO1_IO07,GPIO2_IO01,SRC_BT_CFG1,,,,,,ALT5 GPIO_SD_02,FLEXSPI_B_DATA2,SAI3_TX_DATA,FLEXPWM1_PWM0_A,CCM_CLKO1,FLEXIO1_IO08,GPIO2_IO02,SRC_BT_CFG0,,,,,,ALT5 diff --git a/ports/mimxrt/boards/MIMXRT1015_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1015_EVK/mpconfigboard.h index f47bbf4fa138b..1303aade72976 100644 --- a/ports/mimxrt/boards/MIMXRT1015_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1015_EVK/mpconfigboard.h @@ -87,3 +87,5 @@ I2S_GPIO(1, WS, TX, GPIO_EMC_27, IOMUXC_GPIO_EMC_27_SAI1_TX_SYNC), \ I2S_GPIO(1, SD, TX, GPIO_EMC_25, IOMUXC_GPIO_EMC_25_SAI1_TX_DATA00), \ } + +#define XBARA1 XBARA diff --git a/ports/mimxrt/boards/MIMXRT1015_EVK/pins.csv b/ports/mimxrt/boards/MIMXRT1015_EVK/pins.csv index 2b50c7ca7682f..d41dc30aa0969 100644 --- a/ports/mimxrt/boards/MIMXRT1015_EVK/pins.csv +++ b/ports/mimxrt/boards/MIMXRT1015_EVK/pins.csv @@ -22,6 +22,8 @@ A4,GPIO_AD_B1_15 A5,GPIO_AD_B1_14 RX,GPIO_EMC_33 TX,GPIO_EMC_32 +ENC1,GPIO_EMC_06 +ENC2,GPIO_EMC_07 SDA,GPIO_AD_B1_15 SCL,GPIO_AD_B1_14 SCK,GPIO_AD_B0_10 diff --git a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h index b4f777cf93b00..5f41dfdfd9adf 100644 --- a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h @@ -181,3 +181,5 @@ { IOMUXC_GPIO_AD_B0_15_ENET_TDATA01, 0, 0xB0E9u }, \ { IOMUXC_GPIO_EMC_40_ENET_MDIO, 0, 0xB0E9u }, \ { IOMUXC_GPIO_EMC_41_ENET_MDC, 0, 0xB0E9u }, + +#define XBARA1 XBARA diff --git a/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.h b/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.h index 1ad2e9df5ebad..4d488647191f4 100644 --- a/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.h +++ b/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.h @@ -88,3 +88,5 @@ I2S_GPIO(3, WS, TX, GPIO_SD_00, IOMUXC_GPIO_SD_00_SAI3_TX_SYNC), /* pin D9 */ \ I2S_GPIO(3, SD, TX, GPIO_SD_02, IOMUXC_GPIO_SD_02_SAI3_TX_DATA) /* pin D11 */ \ } + +#define XBARA1 XBARA diff --git a/ports/mimxrt/boards/make-pins.py b/ports/mimxrt/boards/make-pins.py index 55c7f5bd02738..3a76306e9a81d 100644 --- a/ports/mimxrt/boards/make-pins.py +++ b/ports/mimxrt/boards/make-pins.py @@ -26,7 +26,7 @@ r"IOMUXC_(?PGPIO_SNVS_\d\d_DIG)_(?P\w+) (?P\w+), (?P\w+), (?P\w+), (?P\w+), (?P\w+)", ] -SUPPORTED_AF_FNS = {"GPIO", "USDHC", "FLEXPWM", "TMR"} +SUPPORTED_AF_FNS = {"GPIO", "USDHC", "FLEXPWM", "TMR", "XBAR"} class MimxrtPin(boardgen.Pin): @@ -183,7 +183,7 @@ def load_inputs(self, out_source): # the CMD pin of the USDHC1 function on the GPIO_SD_B0_00 pin. def print_module_instances(self, out_header): print(file=out_header) - for match_fn in ("USDHC", "FLEXPWM", "TMR"): + for match_fn in ("USDHC", "FLEXPWM", "TMR", "XBAR"): module_instances = defaultdict(list) for pin in self.available_pins(): for i, (_af_idx, _input_reg, _input_daisy, instance, fn, af) in enumerate( @@ -200,6 +200,8 @@ def print_module_instances(self, out_header): print("#define {:s}_AVAIL (1)".format(k), file=out_header) if match_fn == "FLEXPWM": print("#define {:s} {:s}".format(k, k[-4:]), file=out_header) + if match_fn == "XBAR": + print("#define {:s} {:s}A1".format(k, k[:4]), file=out_header) for i in v: print(i, file=out_header) diff --git a/ports/mimxrt/machine_encoder.c b/ports/mimxrt/machine_encoder.c new file mode 100644 index 0000000000000..ba4d7179e125f --- /dev/null +++ b/ports/mimxrt/machine_encoder.c @@ -0,0 +1,818 @@ +/* + * 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; + uint8_t phases_inv; + 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, ARG_phases}; + + 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_QSTR_phases, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 4} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, + 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); + } + } + + // Get the Phases argument + if ((args[ARG_phases].u_int != 1) && (args[ARG_phases].u_int != 2) && (args[ARG_phases].u_int != 4)) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid value for phases")); + } + self->phases_inv = 4 / args[ARG_phases].u_int; + + // Set the common options + mp_machine_encoder_init_helper_common(self, args + ARG_match_pin, &self->enc_config); + + 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]) * self->phases_inv; + 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) { + int32_t value = (int32_t)actual_value; + if (value > 0) { + value += (self->phases_inv - 1); + } + return mp_obj_new_int(value / self->phases_inv); + } else { + return mp_obj_new_int_from_uint((actual_value + (self->phases_inv - 1))/ self->phases_inv); + } +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_encoder_value_obj, 1, 2, machine_encoder_value); + +// encoder.cycles([value]) +static mp_obj_t machine_encoder_cycles(size_t n_args, const mp_obj_t *args) { + machine_encoder_obj_t *self = MP_OBJ_TO_PTR(args[0]); + if (!self->active) { + mp_raise_ValueError(MP_ERROR_TEXT("device stopped")); + } + + int16_t cycles = (int16_t)ENC_GetRevolutionValue(self->instance); + if (n_args > 1) { + // Set the revolution value + self->instance->REV = mp_obj_get_int(args[1]); + } + return MP_OBJ_NEW_SMALL_INT(cycles); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_encoder_cycles_obj, 1, 2, machine_encoder_cycles); + +// 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->phases_inv = 1; + 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 diff --git a/ports/mimxrt/machine_pin.c b/ports/mimxrt/machine_pin.c index 4f623214bc728..61b540076c142 100644 --- a/ports/mimxrt/machine_pin.c +++ b/ports/mimxrt/machine_pin.c @@ -93,7 +93,7 @@ int GPIO_get_instance(GPIO_Type *gpio) { return 0; } -void call_handler(GPIO_Type *gpio, int gpio_nr, int pin) { +__attribute__((section(".ram_functions"))) void call_handler(GPIO_Type *gpio, int gpio_nr, int pin) { uint32_t mask = 1 << pin; uint32_t isr = gpio->ISR & gpio->IMR; for (int i = 0; i < 16; i++, pin++, mask <<= 1) { @@ -122,43 +122,43 @@ void call_handler(GPIO_Type *gpio, int gpio_nr, int pin) { // 10 GPIO IRQ handlers, each covering 16 bits. -void GPIO1_Combined_0_15_IRQHandler(void) { +__attribute__((section(".ram_functions"))) void GPIO1_Combined_0_15_IRQHandler(void) { call_handler(gpiobases[1], 1, 0); } -void GPIO1_Combined_16_31_IRQHandler(void) { +__attribute__((section(".ram_functions"))) void GPIO1_Combined_16_31_IRQHandler(void) { call_handler(gpiobases[1], 1, 16); } -void GPIO2_Combined_0_15_IRQHandler(void) { +__attribute__((section(".ram_functions"))) void GPIO2_Combined_0_15_IRQHandler(void) { call_handler(gpiobases[2], 2, 0); } -void GPIO2_Combined_16_31_IRQHandler(void) { +__attribute__((section(".ram_functions"))) void GPIO2_Combined_16_31_IRQHandler(void) { call_handler(gpiobases[2], 2, 16); } -void GPIO3_Combined_0_15_IRQHandler(void) { +__attribute__((section(".ram_functions"))) void GPIO3_Combined_0_15_IRQHandler(void) { call_handler(gpiobases[3], 3, 0); } -void GPIO3_Combined_16_31_IRQHandler(void) { +__attribute__((section(".ram_functions"))) void GPIO3_Combined_16_31_IRQHandler(void) { call_handler(gpiobases[3], 3, 16); } -void GPIO4_Combined_0_15_IRQHandler(void) { +__attribute__((section(".ram_functions"))) void GPIO4_Combined_0_15_IRQHandler(void) { call_handler(gpiobases[4], 4, 0); } -void GPIO4_Combined_16_31_IRQHandler(void) { +__attribute__((section(".ram_functions"))) void GPIO4_Combined_16_31_IRQHandler(void) { call_handler(gpiobases[4], 4, 16); } -void GPIO5_Combined_0_15_IRQHandler(void) { +__attribute__((section(".ram_functions"))) void GPIO5_Combined_0_15_IRQHandler(void) { call_handler(gpiobases[5], 5, 0); } -void GPIO5_Combined_16_31_IRQHandler(void) { +__attribute__((section(".ram_functions"))) void GPIO5_Combined_16_31_IRQHandler(void) { call_handler(gpiobases[5], 5, 16); } diff --git a/ports/mimxrt/main.c b/ports/mimxrt/main.c index 7166171f17c2d..5054b60991694 100644 --- a/ports/mimxrt/main.c +++ b/ports/mimxrt/main.c @@ -60,6 +60,7 @@ #include "extmod/vfs.h" extern uint8_t _sstack, _estack, _gc_heap_start, _gc_heap_end; +extern void machine_encoder_deinit_all(void); void board_init(void); @@ -175,6 +176,9 @@ int main(void) { machine_uart_deinit_all(); machine_pwm_deinit_all(); soft_timer_deinit(); + #if MICROPY_PY_MACHINE_QECNT + machine_encoder_deinit_all(); + #endif gc_sweep_all(); mp_deinit(); } diff --git a/ports/mimxrt/modmachine.c b/ports/mimxrt/modmachine.c index ad078ff3ac6d2..491e21ee708c5 100644 --- a/ports/mimxrt/modmachine.c +++ b/ports/mimxrt/modmachine.c @@ -55,6 +55,13 @@ #else #define MICROPY_PY_MACHINE_SDCARD_ENTRY #endif +#if MICROPY_PY_MACHINE_QECNT +#define MICROPY_PY_MACHINE_ENCODER_ENTRY { MP_ROM_QSTR(MP_QSTR_Encoder), MP_ROM_PTR(&machine_encoder_type) }, +#define MICROPY_PY_MACHINE_COUNTER_ENTRY { MP_ROM_QSTR(MP_QSTR_Counter), MP_ROM_PTR(&machine_counter_type) }, +#else +#define MICROPY_PY_MACHINE_ENCODER_ENTRY +#define MICROPY_PY_MACHINE_COUNTER_ENTRY +#endif #define MICROPY_PY_MACHINE_EXTRA_GLOBALS \ MICROPY_PY_MACHINE_LED_ENTRY \ @@ -62,6 +69,8 @@ { MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&machine_timer_type) }, \ { MP_ROM_QSTR(MP_QSTR_RTC), MP_ROM_PTR(&machine_rtc_type) }, \ MICROPY_PY_MACHINE_SDCARD_ENTRY \ + MICROPY_PY_MACHINE_ENCODER_ENTRY \ + MICROPY_PY_MACHINE_COUNTER_ENTRY \ \ /* Reset reasons */ \ { MP_ROM_QSTR(MP_QSTR_PWRON_RESET), MP_ROM_INT(MP_PWRON_RESET) }, \ diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index d6694badbad95..842cc6c86a7a4 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -50,6 +50,8 @@ uint32_t trng_random_u32(void); // Optimisations +// Compiler configuration + // Python internal features #define MICROPY_TRACKED_ALLOC (MICROPY_SSL_MBEDTLS) #define MICROPY_READER_VFS (1) diff --git a/tests/extmod_hardware/machine_counter.py b/tests/extmod_hardware/machine_counter.py index 62ac1fed47ce7..22acef07c038f 100644 --- a/tests/extmod_hardware/machine_counter.py +++ b/tests/extmod_hardware/machine_counter.py @@ -16,6 +16,11 @@ id = 0 out_pin = 4 in_pin = 5 +elif sys.platform == "mimxrt": + if "Teensy" in sys.implementation._machine: + id = 0 + out_pin = "D2" + in_pin = "D3" else: print("Please add support for this test on this platform.") raise SystemExit @@ -43,6 +48,7 @@ def tearDown(self): def assertCounter(self, value): self.assertEqual(self.counter.value(), value) + @unittest.skipIf(sys.platform == "mimxrt", "cannot read back the pin") def test_connections(self): # Test the hardware connections are correct. If this test fails, all tests will fail. out_pin(1) @@ -73,6 +79,7 @@ def test_change_directions(self): toggle(25) self.assertCounter(75) + @unittest.skipIf(sys.platform == "mimxrt", "FALLING edge not supported") def test_count_falling(self): self.counter.init(in_pin, direction=Counter.UP, edge=Counter.FALLING) toggle(20) diff --git a/tests/extmod_hardware/machine_encoder.py b/tests/extmod_hardware/machine_encoder.py index 9bd2bb464178c..668a3aea4e2f4 100644 --- a/tests/extmod_hardware/machine_encoder.py +++ b/tests/extmod_hardware/machine_encoder.py @@ -19,6 +19,15 @@ in0_pin = 5 out1_pin = 12 in1_pin = 13 + start_value = -1 +elif sys.platform == "mimxrt": + if "Teensy" in sys.implementation._machine: + id = 0 + out0_pin = "D2" + in0_pin = "D3" + out1_pin = "D5" + in1_pin = "D4" + start_value = 0 else: print("Please add support for this test on this platform.") raise SystemExit @@ -35,8 +44,8 @@ class TestEncoder(unittest.TestCase): def setUp(self): out0_pin(0) out1_pin(0) - self.enc = Encoder(id, in0_pin, in1_pin) - self.pulses = 0 # track the expected encoder position in software + self.enc = Encoder(id, in0_pin, in1_pin, phases=1) + self.pulses = start_value # track the expected encoder position in software def tearDown(self): self.enc.deinit() @@ -52,6 +61,7 @@ def rotate(self, pulses): def assertPosition(self, value): self.assertEqual(self.enc.value(), value) + @unittest.skipIf(sys.platform == "mimxrt", "cannot read back the pin") def test_connections(self): # Test the hardware connections are correct. If this test fails, all tests will fail. for ch, outp, inp in ((0, out0_pin, in0_pin), (1, out1_pin, in1_pin)): @@ -76,7 +86,9 @@ def test_partial(self): self.rotate(1) self.assertPosition(0) self.rotate(1) - self.assertPosition(1) # only 3 pulses to count first rotation? + self.assertPosition(0) + self.rotate(1) + self.assertPosition(1) # 4 pulses to count first rotation? self.rotate(1) self.assertPosition(1) self.rotate(1)