From 1ba6ef50e17ce2eb8e17aa9a87eba272a45681f2 Mon Sep 17 00:00:00 2001 From: "Kwabena W. Agyeman" Date: Thu, 24 Aug 2023 14:07:16 -0700 Subject: [PATCH] ports/mimxrt: Add machine.CAN driver. Signed-off-by: Kwabena W. Agyeman --- docs/library/machine.CAN.rst | 251 +++++ docs/library/machine.rst | 1 + ports/mimxrt/Makefile | 7 + ports/mimxrt/README.md | 3 +- .../boards/MIMXRT1020_EVK/mpconfigboard.h | 6 + .../boards/MIMXRT1020_EVK/mpconfigboard.mk | 1 + .../boards/MIMXRT1050_EVK/mpconfigboard.h | 7 + .../boards/MIMXRT1050_EVK/mpconfigboard.mk | 1 + .../boards/MIMXRT1060_EVK/mpconfigboard.h | 7 + .../boards/MIMXRT1060_EVK/mpconfigboard.mk | 1 + .../boards/MIMXRT1064_EVK/mpconfigboard.h | 7 + .../boards/MIMXRT1064_EVK/mpconfigboard.mk | 1 + .../boards/MIMXRT1170_EVK/mpconfigboard.h | 8 + .../boards/MIMXRT1170_EVK/mpconfigboard.mk | 1 + .../boards/SEEED_ARCH_MIX/mpconfigboard.h | 7 + .../boards/SEEED_ARCH_MIX/mpconfigboard.mk | 1 + ports/mimxrt/boards/TEENSY40/mpconfigboard.h | 7 + ports/mimxrt/boards/TEENSY40/mpconfigboard.mk | 1 + ports/mimxrt/boards/TEENSY41/mpconfigboard.h | 8 + ports/mimxrt/boards/TEENSY41/mpconfigboard.mk | 1 + ports/mimxrt/machine_can.c | 927 ++++++++++++++++++ ports/mimxrt/main.c | 3 + ports/mimxrt/modmachine.c | 3 + ports/mimxrt/modmachine.h | 2 + 24 files changed, 1261 insertions(+), 1 deletion(-) create mode 100644 docs/library/machine.CAN.rst create mode 100644 ports/mimxrt/machine_can.c diff --git a/docs/library/machine.CAN.rst b/docs/library/machine.CAN.rst new file mode 100644 index 0000000000000..8217081ebe4e2 --- /dev/null +++ b/docs/library/machine.CAN.rst @@ -0,0 +1,251 @@ +.. currentmodule:: machine +.. _machine.CAN: + +class CAN -- controller area network communication bus +====================================================== + +CAN implements support for classic CAN controllers. +At the physical level CAN bus consists of 2 lines: RX and TX. Note that to connect the board to a +CAN bus you must use a CAN transceiver to convert the CAN logic signals from the board to the correct +voltage levels on the bus. + +Example usage for classic CAN controller in Loopback (transceiver-less) mode:: + + from machine import CAN + can = CAN(1, CAN.LOOPBACK) + can.setfilter(0, CAN.LIST32, 0, (123, 124)) # set a filter to receive messages with id=123 and 124 + can.send('message!', 123) # send a message with id 123 + can.recv(0) # receive message on FIFO 0 + +The following CAN module functions and their arguments are available +for classic CAN controllers, unless otherwise stated. + +Availability: IMXRT. + +Constructors +------------ + +.. class:: CAN(bus, ...) + + Construct a CAN object on the given bus. *bus* can be 1-3. + With no additional parameters, the CAN object is created but not + initialised (it has the settings from the last initialisation of + the bus, if any). If extra arguments are given, the bus is initialised. + See :meth:`CAN.init` for parameters of initialisation. + +Methods +------- + +.. method:: CAN.init(mode, *, auto_restart=False, baudrate=0) + + Initialise the CAN bus with the given parameters: + + - *mode* is one of: NORMAL, LOOPBACK, SILENT, SILENT_LOOPBACK + - *auto_restart* sets whether the controller will automatically try and restart + communications after entering the bus-off state; if this is disabled then + :meth:`~CAN.restart()` can be used to leave the bus-off state + - *baudrate* can be 33333, 50000, 62500, 83333, 100000, 125000, 250000, 500000, or 1000000. + +.. method:: CAN.deinit() + + Turn off the CAN bus. + +.. method:: CAN.restart() + + Force a software restart of the CAN controller without resetting its + configuration. + + If the controller enters the bus-off state then it will no longer participate + in bus activity. If the controller is not configured to automatically restart + (see :meth:`~CAN.init()`) then this method can be used to trigger a restart, + and the controller will follow the CAN protocol to leave the bus-off state and + go into the error active state. + +.. method:: CAN.state() + + Return the state of the controller. The return value can be one of: + + - ``CAN.STOPPED`` -- the controller is completely off and reset; + - ``CAN.ERROR_ACTIVE`` -- the controller is on and in the Error Active state + (both TEC and REC are less than 96); + - ``CAN.ERROR_WARNING`` -- the controller is on and in the Error Warning state + (at least one of TEC or REC is 96 or greater); + - ``CAN.ERROR_PASSIVE`` -- the controller is on and in the Error Passive state + (at least one of TEC or REC is 128 or greater); + - ``CAN.BUS_OFF`` -- the controller is on but not participating in bus activity + (TEC overflowed beyond 255). + +.. method:: CAN.info([list]) + + Get information about the controller's error states and TX and RX buffers. + If *list* is provided then it should be a list object with at least 8 entries, + which will be filled in with the information. Otherwise a new list will be + created and filled in. In both cases the return value of the method is the + populated list. + + The values in the list are: + + - TEC value + - REC value + - number of times the controller enterted the Error Warning state (wrapped + around to 0 after 65535) + - number of times the controller enterted the Error Passive state (wrapped + around to 0 after 65535) + - number of times the controller enterted the Bus Off state (wrapped + around to 0 after 65535) + - number of pending TX messages + - number of pending RX messages on fifo 0 + - number of pending RX messages on fifo 1 + +.. method:: CAN.setfilter(bank, mode, fifo, params, *, rtr, extframe=False) + + Configure a filter bank: + + - *bank* is the classic CAN controller filter bank to configure. + - *mode* is the mode the filter should operate in, see the tables below. + - *fifo* is which fifo (0 or 1) a message should be stored in, if it is accepted by this filter. + - *params* is an array of values the defines the filter. The contents of the array depends on the *mode* argument. + + +-----------------------+---------------------------------------------------------+ + |*mode* |Contents of *params* array for classic CAN controller | + +=======================+=========================================================+ + |CAN.LIST32 or CAN.DUAL |Two 32 bit ids that will be accepted | + +-----------------------+---------------------------------------------------------+ + + - *rtr* For classic CAN controllers, this is an array of booleans that states if + a filter should accept a remote transmission request message. If this argument + is not given then it defaults to ``False`` for all entries. The length of the + array depends on the *mode* argument. + + +-----------------------+----------------------+ + |*mode* |length of *rtr* array | + +=======================+======================+ + |CAN.LIST32 or CAN.DUAL |2 | + +-----------------------+----------------------+ + + - *extframe* If True the frame will have an extended identifier (29 bits), + otherwise a standard identifier (11 bits) is used. + + +.. method:: CAN.clearfilter(bank) + + Clear and disables a filter bank: + + - *bank* is the classic CAN controller filter bank to clear. + +.. method:: CAN.any(fifo) + + Return ``True`` if any message waiting on the FIFO, else ``False``. + +.. method:: CAN.recv(fifo, list=None, *, timeout=5000) + + Receive data on the bus: + + - *fifo* is an integer, which is the FIFO to receive on + - *list* is an optional list object to be used as the return value + - *timeout* is the timeout in milliseconds to wait for the receive. + + Return value: A tuple containing five values. + + - The id of the message. + - A boolean that indicates if the message ID is standard or extended. + - A boolean that indicates if the message is an RTR message. + - The FMI (Filter Match Index) value. + - An array containing the data. + + If *list* is ``None`` then a new tuple will be allocated, as well as a new + bytes object to contain the data (as the fifth element in the tuple). + + If *list* is not ``None`` then it should be a list object with a least five + elements. The fifth element should be a memoryview object which is created + from either a bytearray or an array of type 'B' or 'b', and this array must + have enough room for at least 8 bytes. The list object will then be + populated with the first four return values above, and the memoryview object + will be resized inplace to the size of the data and filled in with that data. + The same list and memoryview objects can be reused in subsequent calls to + this method, providing a way of receiving data without using the heap. + For example:: + + buf = bytearray(8) + lst = [0, 0, 0, 0, memoryview(buf)] + # No heap memory is allocated in the following call + can.recv(0, lst) + +.. method:: CAN.send(data, id, *, timeout=0, rtr=False, extframe=False) + + Send a message on the bus: + + - *data* is the data to send (an integer to send, or a buffer object). + - *id* is the id of the message to be sent. + - *timeout* is the timeout in milliseconds to wait for the send. + - *rtr* is a boolean that specifies if the message shall be sent as + a remote transmission request. If *rtr* is True then only the length + of *data* is used to fill in the DLC slot of the frame; the actual + bytes in *data* are unused. + - *extframe* if True the frame will have an extended identifier (29 bits), + otherwise a standard identifier (11 bits) is used. + + If timeout is 0 the message is placed in a buffer in one of three hardware + buffers and the method returns immediately. If all three buffers are in use + an exception is thrown. If timeout is not 0, the method waits until the + message is transmitted. If the message can't be transmitted within the + specified time an exception is thrown. + + Return value: ``None``. + +.. method:: CAN.rxcallback(fifo, fun) + + Register a function to be called when a message is accepted into a empty fifo: + + - *fifo* is the receiving fifo. + - *fun* is the function to be called when the fifo becomes non empty. + + The callback function takes two arguments the first is the can object it self the second is + a integer that indicates the reason for the callback. + + +--------+------------------------------------------------+ + | Reason | | + +========+================================================+ + | 0 | A message has been accepted into a empty FIFO. | + +--------+------------------------------------------------+ + | 1 | The FIFO is full | + +--------+------------------------------------------------+ + | 2 | A message has been lost due to a full FIFO | + +--------+------------------------------------------------+ + + Example use of rxcallback:: + + def cb0(bus, reason): + print('cb0') + if reason == 0: + print('pending') + if reason == 1: + print('full') + if reason == 2: + print('overflow') + + can = CAN(1, CAN.LOOPBACK) + can.rxcallback(0, cb0) + +Constants +--------- + +.. data:: CAN.NORMAL + CAN.LOOPBACK + CAN.SILENT + CAN.SILENT_LOOPBACK + + The mode of the CAN bus used in :meth:`~CAN.init()`. + +.. data:: CAN.STOPPED + CAN.ERROR_ACTIVE + CAN.ERROR_WARNING + CAN.ERROR_PASSIVE + CAN.BUS_OFF + + Possible states of the CAN controller returned from :meth:`~CAN.state()`. + +.. data:: CAN.LIST32 + CAN.DUAL + + The operation mode of a filter used in :meth:`~CAN.setfilter()` for classic CAN. diff --git a/docs/library/machine.rst b/docs/library/machine.rst index 3f5cd6f13c72b..61aad50486f5c 100644 --- a/docs/library/machine.rst +++ b/docs/library/machine.rst @@ -255,6 +255,7 @@ Classes machine.Signal.rst machine.ADC.rst machine.ADCBlock.rst + machine.CAN.rst machine.PWM.rst machine.UART.rst machine.SPI.rst diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index 8cb46245aec97..98e565a66d4ac 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -156,6 +156,10 @@ ifeq ($(MICROPY_PY_MACHINE_SDCARD),1) SRC_HAL_IMX_C += $(MCU_DIR)/drivers/fsl_usdhc.c endif +ifeq ($(MICROPY_HW_ENABLE_CAN),1) +SRC_HAL_IMX_C += $(MCU_DIR)/drivers/fsl_flexcan.c +endif + ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES), MIMXRT1015 MIMXRT1021 MIMXRT1052 MIMXRT1062 MIMXRT1064 MIMXRT1176)) SRC_HAL_IMX_C += \ $(MCU_DIR)/drivers/fsl_qtmr.c \ @@ -195,6 +199,7 @@ SRC_C += \ led.c \ machine_adc.c \ machine_bitstream.c \ + machine_can.c \ machine_i2c.c \ machine_i2s.c \ machine_led.c \ @@ -323,6 +328,7 @@ endif MICROPY_HW_SDRAM_AVAIL ?= 0 MICROPY_HW_SDRAM_SIZE ?= 0 MICROPY_PY_MACHINE_SDCARD ?= 0 +MICROPY_HW_ENABLE_CAN ?= 0 # Configure default compiler flags CFLAGS += \ @@ -343,6 +349,7 @@ CFLAGS += \ -DMICROPY_HW_SDRAM_AVAIL=$(MICROPY_HW_SDRAM_AVAIL) \ -DMICROPY_HW_SDRAM_SIZE=$(MICROPY_HW_SDRAM_SIZE) \ -DMICROPY_PY_MACHINE_SDCARD=$(MICROPY_PY_MACHINE_SDCARD) \ + -DMICROPY_HW_ENABLE_CAN=$(MICROPY_HW_ENABLE_CAN) \ -DSPI_RETRY_TIMES=1000000 \ -DUART_RETRY_TIMES=1000000 \ -DXIP_BOOT_HEADER_ENABLE=1 \ diff --git a/ports/mimxrt/README.md b/ports/mimxrt/README.md index de3de1e2817f4..a578db00261af 100644 --- a/ports/mimxrt/README.md +++ b/ports/mimxrt/README.md @@ -8,6 +8,7 @@ MIMXRT1064_EVK boards. Features: - REPL over USB VCP - machine.ADC + - machine.CAN - machine.I2C - machine.LED - machine.Pin @@ -27,7 +28,7 @@ Features: Known issues: TODO: - - More peripherals (Counter, I2S, CAN, etc) + - More peripherals (Counter, I2S, etc) - More Python options ## Build Instructions diff --git a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h index 8b7316c8dabfc..1615ff0b982da 100644 --- a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h @@ -77,6 +77,12 @@ { 0 }, { 0 }, \ { IOMUXC_GPIO_SD_B1_02_LPI2C4_SCL }, { IOMUXC_GPIO_SD_B1_03_LPI2C4_SDA }, +#define MICROPY_HW_CAN_INDEX { 1 } +#define MICROPY_HW_NUM_CAN_IRQS (1) + +#define IOMUX_TABLE_CAN \ + { IOMUXC_GPIO_SD_B1_00_FLEXCAN1_TX }, { IOMUXC_GPIO_SD_B1_01_FLEXCAN1_RX }, + #define MICROPY_PY_MACHINE_I2S (1) #define MICROPY_HW_I2S_NUM (1) #define I2S_CLOCK_MUX { 0, kCLOCK_Sai1Mux, kCLOCK_Sai2Mux } diff --git a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.mk index 547b88d57de43..369497c9d4515 100644 --- a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.mk @@ -3,6 +3,7 @@ MCU_VARIANT = MIMXRT1021DAG5A MICROPY_FLOAT_IMPL = double MICROPY_PY_MACHINE_SDCARD = 1 +MICROPY_HW_ENABLE_CAN = 1 MICROPY_HW_FLASH_TYPE = qspi_nor_flash MICROPY_HW_FLASH_SIZE = 0x800000 # 8MB diff --git a/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h index 74666562a81d5..8796ea512c577 100644 --- a/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h @@ -65,6 +65,13 @@ { 0 }, { 0 }, \ { IOMUXC_GPIO_AD_B1_07_LPI2C3_SCL }, { IOMUXC_GPIO_AD_B1_06_LPI2C3_SDA }, +#define MICROPY_HW_CAN_INDEX { 2 } +#define MICROPY_HW_NUM_CAN_IRQS (1) + +#define IOMUX_TABLE_CAN \ + { 0 }, { 0 }, \ + { IOMUXC_GPIO_AD_B0_14_FLEXCAN2_TX }, { IOMUXC_GPIO_AD_B0_15_FLEXCAN2_RX }, + #define MICROPY_PY_MACHINE_I2S (1) #define MICROPY_HW_I2S_NUM (1) #define I2S_CLOCK_MUX { 0, kCLOCK_Sai1Mux, kCLOCK_Sai2Mux } diff --git a/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.mk index 0cf2a348046e2..252cc7f2b2355 100644 --- a/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.mk @@ -3,6 +3,7 @@ MCU_VARIANT = MIMXRT1052DVL6B MICROPY_FLOAT_IMPL = double MICROPY_PY_MACHINE_SDCARD = 1 +MICROPY_HW_ENABLE_CAN = 1 MICROPY_HW_FLASH_TYPE = qspi_hyper_flash MICROPY_HW_FLASH_SIZE = 0x4000000 # 64MB diff --git a/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h index 824f9977c2710..90a79da02f60a 100644 --- a/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h @@ -65,6 +65,13 @@ { 0 }, { 0 }, \ { IOMUXC_GPIO_AD_B1_07_LPI2C3_SCL }, { IOMUXC_GPIO_AD_B1_06_LPI2C3_SDA }, +#define MICROPY_HW_CAN_INDEX { 2 } +#define MICROPY_HW_NUM_CAN_IRQS (1) + +#define IOMUX_TABLE_CAN \ + { 0 }, { 0 }, \ + { IOMUXC_GPIO_AD_B0_14_FLEXCAN2_TX }, { IOMUXC_GPIO_AD_B0_15_FLEXCAN2_RX }, + #define MICROPY_PY_MACHINE_I2S (1) #define MICROPY_HW_I2S_NUM (1) #define I2S_CLOCK_MUX { 0, kCLOCK_Sai1Mux, kCLOCK_Sai2Mux } diff --git a/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.mk index c2556a27248ac..fd3ded8383d76 100644 --- a/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.mk @@ -3,6 +3,7 @@ MCU_VARIANT = MIMXRT1062DVJ6A MICROPY_FLOAT_IMPL = double MICROPY_PY_MACHINE_SDCARD = 1 +MICROPY_HW_ENABLE_CAN = 1 MICROPY_HW_FLASH_TYPE = qspi_nor_flash MICROPY_HW_FLASH_SIZE = 0x800000 # 8MB diff --git a/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h index cb64d2b9d24f8..8d3a33f9cf717 100644 --- a/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h @@ -65,6 +65,13 @@ { 0 }, { 0 }, \ { IOMUXC_GPIO_AD_B1_07_LPI2C3_SCL }, { IOMUXC_GPIO_AD_B1_06_LPI2C3_SDA }, +#define MICROPY_HW_CAN_INDEX { 2 } +#define MICROPY_HW_NUM_CAN_IRQS (1) + +#define IOMUX_TABLE_CAN \ + { 0 }, { 0 }, \ + { IOMUXC_GPIO_AD_B0_14_FLEXCAN2_TX }, { IOMUXC_GPIO_AD_B0_15_FLEXCAN2_RX }, + #define MICROPY_PY_MACHINE_I2S (1) #define MICROPY_HW_I2S_NUM (1) #define I2S_CLOCK_MUX { 0, kCLOCK_Sai1Mux, kCLOCK_Sai2Mux } diff --git a/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk index d3ba752419e0a..114a5ada66240 100644 --- a/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk @@ -3,6 +3,7 @@ MCU_VARIANT = MIMXRT1064DVL6A MICROPY_FLOAT_IMPL = double MICROPY_PY_MACHINE_SDCARD = 1 +MICROPY_HW_ENABLE_CAN = 1 MICROPY_HW_FLASH_TYPE = internal MICROPY_HW_FLASH_SIZE = 0x400000 # 4MB diff --git a/ports/mimxrt/boards/MIMXRT1170_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1170_EVK/mpconfigboard.h index 8ce91adbaf23e..47b29c948b92d 100644 --- a/ports/mimxrt/boards/MIMXRT1170_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1170_EVK/mpconfigboard.h @@ -104,6 +104,14 @@ { IOMUXC_GPIO_LPSR_05_LPI2C5_SCL }, { IOMUXC_GPIO_LPSR_04_LPI2C5_SDA }, \ { IOMUXC_GPIO_LPSR_11_LPI2C6_SCL }, { IOMUXC_GPIO_LPSR_10_LPI2C6_SDA }, +#define MICROPY_HW_CAN_INDEX { 1, 3 } +#define MICROPY_HW_NUM_CAN_IRQS (2) + +#define IOMUX_TABLE_CAN \ + { IOMUXC_GPIO_AD_06_FLEXCAN1_TX }, { IOMUXC_GPIO_AD_07_FLEXCAN1_RX }, \ + { 0 }, { 0 }, \ + { IOMUXC_GPIO_LPSR_00_FLEXCAN3_TX }, { IOMUXC_GPIO_LPSR_01_FLEXCAN3_RX }, + #define MICROPY_PY_MACHINE_I2S (1) #define MICROPY_HW_I2S_NUM (1) #define I2S_CLOCK_MUX { 0, kCLOCK_Root_Sai1, kCLOCK_Root_Sai2, kCLOCK_Root_Sai3, kCLOCK_Root_Sai4 } diff --git a/ports/mimxrt/boards/MIMXRT1170_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1170_EVK/mpconfigboard.mk index 25747a52c6be7..f5b52e8a7d039 100644 --- a/ports/mimxrt/boards/MIMXRT1170_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1170_EVK/mpconfigboard.mk @@ -4,6 +4,7 @@ MCU_CORE = _cm7 MICROPY_FLOAT_IMPL = double MICROPY_PY_MACHINE_SDCARD = 1 +MICROPY_HW_ENABLE_CAN = 1 MICROPY_HW_FLASH_TYPE ?= qspi_nor_flash MICROPY_HW_FLASH_SIZE ?= 0x1000000 # 16MB MICROPY_HW_FLASH_RESERVED ?= 0x100000 # 1MB CM4 Code address space diff --git a/ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.h b/ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.h index edda72f2660d1..e6b0d3c25c3db 100644 --- a/ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.h +++ b/ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.h @@ -77,6 +77,13 @@ { IOMUXC_GPIO_B0_04_LPI2C2_SCL }, { IOMUXC_GPIO_B0_05_LPI2C2_SDA }, \ { IOMUXC_GPIO_AD_B1_07_LPI2C3_SCL }, { IOMUXC_GPIO_AD_B1_06_LPI2C3_SDA } +#define MICROPY_HW_CAN_INDEX { 2 } +#define MICROPY_HW_NUM_CAN_IRQS (1) + +#define IOMUX_TABLE_CAN \ + { 0 }, { 0 }, \ + { IOMUXC_GPIO_AD_B0_14_FLEXCAN2_TX }, { IOMUXC_GPIO_AD_B0_15_FLEXCAN2_RX }, + #define MICROPY_PY_MACHINE_I2S (1) #define MICROPY_HW_I2S_NUM (1) #define I2S_CLOCK_MUX { 0, kCLOCK_Sai1Mux } diff --git a/ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.mk b/ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.mk index ca27dff55f3a2..a705f34dc5a6e 100644 --- a/ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.mk +++ b/ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.mk @@ -3,6 +3,7 @@ MCU_VARIANT = MIMXRT1052DVL6B MICROPY_FLOAT_IMPL = double MICROPY_PY_MACHINE_SDCARD = 1 +MICROPY_HW_ENABLE_CAN = 1 MICROPY_HW_FLASH_TYPE = qspi_nor_flash MICROPY_HW_FLASH_SIZE = 0x800000 # 8MB diff --git a/ports/mimxrt/boards/TEENSY40/mpconfigboard.h b/ports/mimxrt/boards/TEENSY40/mpconfigboard.h index 1a6227a60e074..8a734a481551f 100644 --- a/ports/mimxrt/boards/TEENSY40/mpconfigboard.h +++ b/ports/mimxrt/boards/TEENSY40/mpconfigboard.h @@ -68,6 +68,13 @@ { IOMUXC_GPIO_AD_B1_07_LPI2C3_SCL }, { IOMUXC_GPIO_AD_B1_06_LPI2C3_SDA }, \ { IOMUXC_GPIO_AD_B0_12_LPI2C4_SCL }, { IOMUXC_GPIO_AD_B0_13_LPI2C4_SDA }, +#define MICROPY_HW_CAN_INDEX { 1, 2 } +#define MICROPY_HW_NUM_CAN_IRQS (2) + +#define IOMUX_TABLE_CAN \ + { IOMUXC_GPIO_B0_02_FLEXCAN1_TX }, { IOMUXC_GPIO_B0_03_FLEXCAN1_RX }, \ + { IOMUXC_GPIO_AD_B0_02_FLEXCAN2_TX }, { IOMUXC_GPIO_AD_B0_03_FLEXCAN2_RX }, + #define MICROPY_PY_MACHINE_I2S (1) #define MICROPY_HW_I2S_NUM (2) #define I2S_CLOCK_MUX { 0, kCLOCK_Sai1Mux, kCLOCK_Sai2Mux } diff --git a/ports/mimxrt/boards/TEENSY40/mpconfigboard.mk b/ports/mimxrt/boards/TEENSY40/mpconfigboard.mk index 4482c629f7dcc..f42128abf7767 100644 --- a/ports/mimxrt/boards/TEENSY40/mpconfigboard.mk +++ b/ports/mimxrt/boards/TEENSY40/mpconfigboard.mk @@ -3,6 +3,7 @@ MCU_VARIANT = MIMXRT1062DVJ6A MICROPY_FLOAT_IMPL = double MICROPY_PY_MACHINE_SDCARD = 1 +MICROPY_HW_ENABLE_CAN = 1 MICROPY_HW_FLASH_TYPE = qspi_nor_flash MICROPY_HW_FLASH_SIZE = 0x200000 # 2MB MICROPY_HW_FLASH_RESERVED ?= 0x1000 # 4KB diff --git a/ports/mimxrt/boards/TEENSY41/mpconfigboard.h b/ports/mimxrt/boards/TEENSY41/mpconfigboard.h index 09addbe52c219..14acb76bdc25e 100644 --- a/ports/mimxrt/boards/TEENSY41/mpconfigboard.h +++ b/ports/mimxrt/boards/TEENSY41/mpconfigboard.h @@ -70,6 +70,14 @@ { IOMUXC_GPIO_AD_B1_07_LPI2C3_SCL }, { IOMUXC_GPIO_AD_B1_06_LPI2C3_SDA }, \ { IOMUXC_GPIO_AD_B0_12_LPI2C4_SCL }, { IOMUXC_GPIO_AD_B0_13_LPI2C4_SDA }, +#define MICROPY_HW_CAN_INDEX { 1, 2, 3 } +#define MICROPY_HW_NUM_CAN_IRQS (3) + +#define IOMUX_TABLE_CAN \ + { IOMUXC_GPIO_B0_02_FLEXCAN1_TX }, { IOMUXC_GPIO_B0_03_FLEXCAN1_RX }, \ + { IOMUXC_GPIO_AD_B0_02_FLEXCAN2_TX }, { IOMUXC_GPIO_AD_B0_03_FLEXCAN2_RX }, \ + { IOMUXC_GPIO_EMC_36_FLEXCAN3_TX }, { IOMUXC_GPIO_EMC_37_FLEXCAN3_RX }, + #define MICROPY_PY_MACHINE_I2S (1) #define MICROPY_HW_I2S_NUM (2) #define I2S_CLOCK_MUX { 0, kCLOCK_Sai1Mux, kCLOCK_Sai2Mux } diff --git a/ports/mimxrt/boards/TEENSY41/mpconfigboard.mk b/ports/mimxrt/boards/TEENSY41/mpconfigboard.mk index cf07144668e15..e7ce03d223488 100755 --- a/ports/mimxrt/boards/TEENSY41/mpconfigboard.mk +++ b/ports/mimxrt/boards/TEENSY41/mpconfigboard.mk @@ -3,6 +3,7 @@ MCU_VARIANT = MIMXRT1062DVJ6A MICROPY_FLOAT_IMPL = double MICROPY_PY_MACHINE_SDCARD = 1 +MICROPY_HW_ENABLE_CAN = 1 MICROPY_HW_FLASH_TYPE = qspi_nor_flash MICROPY_HW_FLASH_SIZE = 0x800000 # 8MB MICROPY_HW_FLASH_RESERVED ?= 0x1000 # 4KB diff --git a/ports/mimxrt/machine_can.c b/ports/mimxrt/machine_can.c new file mode 100644 index 0000000000000..ed2752ed407e9 --- /dev/null +++ b/ports/mimxrt/machine_can.c @@ -0,0 +1,927 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023 Kwabena W. Agyeman + * + * 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. + */ + +#include "py/obj.h" +#include "py/objarray.h" +#include "py/runtime.h" +#include "py/gc.h" +#include "py/binary.h" +#include "py/stream.h" +#include "py/mperrno.h" +#include "py/mphal.h" +#include "shared/runtime/mpirq.h" +#include "modmachine.h" +#include CLOCK_CONFIG_H + +#include "fsl_iomuxc.h" + +#if MICROPY_HW_ENABLE_CAN +#include "fsl_flexcan.h" + +#define CAN_MAX_DATA_FRAME (8) + +#define CAN_NORMAL_MODE (0) +#define CAN_LOOPBACK_FLAG (1) +#define CAN_SILENT_FLAG (2) + +#define CAN_DUAL (0) + +// matches fsl_flexcan.c enum _flexcan_mb_code_tx +#define kFLEXCAN_TxMbDataOrRemote (0xC) + +enum { + CAN_STATE_STOPPED, + CAN_STATE_ERROR_ACTIVE, + CAN_STATE_ERROR_WARNING, + CAN_STATE_ERROR_PASSIVE, + CAN_STATE_BUS_OFF, +}; + +#define MICROPY_HW_CAN_NUM MP_ARRAY_SIZE(can_index_table) + +#define CTX (iomux_table[index]) +#define CRX (iomux_table[index + 1]) + +typedef struct _machine_can_obj_t { + mp_obj_base_t base; + uint8_t can_id; + uint8_t can_hw_id; + CAN_Type *can_inst; + flexcan_config_t *flexcan_config; + flexcan_rx_fifo_config_t *flexcan_rx_fifo_config; + uint8_t flexcan_txmb_start; + uint8_t flexcan_txmb_count; + mp_obj_t callback; + bool is_enabled; + uint16_t num_error_warning; + uint16_t num_error_passive; + uint16_t num_bus_off; +} machine_can_obj_t; + +typedef struct _iomux_table_t { + uint32_t muxRegister; + uint32_t muxMode; + uint32_t inputRegister; + uint32_t inputDaisy; + uint32_t configRegister; +} iomux_table_t; + +STATIC const uint8_t can_index_table[] = MICROPY_HW_CAN_INDEX; +#ifdef MIMXRT117x_SERIES +STATIC const uint32_t can_clock_index_table[] = { + BOARD_BOOTCLOCKRUN_CAN1_CLK_ROOT, + BOARD_BOOTCLOCKRUN_CAN2_CLK_ROOT, + BOARD_BOOTCLOCKRUN_CAN3_CLK_ROOT +}; +#endif +STATIC CAN_Type *can_base_ptr_table[] = CAN_BASE_PTRS; +static const iomux_table_t iomux_table[] = { + IOMUX_TABLE_CAN +}; + +bool can_set_iomux(int8_t can) { + int index = (can - 1) * 2; + + if (CTX.muxRegister != 0) { + IOMUXC_SetPinMux(CTX.muxRegister, CTX.muxMode, CTX.inputRegister, CTX.inputDaisy, CTX.configRegister, 0U); + IOMUXC_SetPinConfig(CTX.muxRegister, CTX.muxMode, CTX.inputRegister, CTX.inputDaisy, CTX.configRegister, + pin_generate_config(PIN_PULL_UP_100K, PIN_MODE_OUT, PIN_DRIVE_6, CTX.configRegister)); + + IOMUXC_SetPinMux(CRX.muxRegister, CRX.muxMode, CRX.inputRegister, CRX.inputDaisy, CRX.configRegister, 0U); + IOMUXC_SetPinConfig(CRX.muxRegister, CRX.muxMode, CRX.inputRegister, CRX.inputDaisy, CRX.configRegister, + pin_generate_config(PIN_PULL_UP_100K, PIN_MODE_IN, PIN_DRIVE_6, CRX.configRegister)); + + return true; + } else { + return false; + } +} + +void machine_can_handler(CAN_Type *base) { + machine_can_obj_t *self = NULL; + for (int i = 0; i < MICROPY_HW_NUM_CAN_IRQS; ++i) { + machine_can_obj_t *machine_can_obj = MP_STATE_PORT(machine_can_objects[i]); + if ((machine_can_obj != NULL) && (machine_can_obj->can_inst == base)) { + self = machine_can_obj; + break; + } + } + if (self != NULL) { + uint32_t result = FLEXCAN_GetStatusFlags(self->can_inst); + if (result & FLEXCAN_ERROR_AND_STATUS_INIT_FLAG) { + uint32_t flt_conf = (result & CAN_ESR1_FLTCONF_MASK) >> CAN_ESR1_FLTCONF_SHIFT; + if (flt_conf > 1) { + ++self->num_bus_off; + } else if (flt_conf == 1) { + ++self->num_error_passive; + } else if ((result & CAN_ESR1_RXWRN_MASK) || (result & CAN_ESR1_TXWRN_MASK)) { + ++self->num_error_warning; + } + FLEXCAN_ClearStatusFlags(self->can_inst, FLEXCAN_ERROR_AND_STATUS_INIT_FLAG); + } + + mp_int_t irq_reason = -1; + + if (FLEXCAN_GetMbStatusFlags(self->can_inst, (uint32_t)kFLEXCAN_RxFifoOverflowFlag)) { + FLEXCAN_DisableMbInterrupts(self->can_inst, + kFLEXCAN_RxFifoOverflowFlag | kFLEXCAN_RxFifoWarningFlag | kFLEXCAN_RxFifoFrameAvlFlag); + irq_reason = 2; + } else if (FLEXCAN_GetMbStatusFlags(self->can_inst, (uint32_t)kFLEXCAN_RxFifoWarningFlag)) { + FLEXCAN_DisableMbInterrupts(self->can_inst, kFLEXCAN_RxFifoWarningFlag | kFLEXCAN_RxFifoFrameAvlFlag); + irq_reason = 1; + } else if (FLEXCAN_GetMbStatusFlags(self->can_inst, (uint32_t)kFLEXCAN_RxFifoFrameAvlFlag)) { + FLEXCAN_DisableMbInterrupts(self->can_inst, kFLEXCAN_RxFifoFrameAvlFlag); + irq_reason = 0; + } + + if (irq_reason != -1 && self->callback != mp_const_none) { + mp_sched_lock(); + gc_lock(); + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + mp_call_function_2(self->callback, MP_OBJ_FROM_PTR(self), MP_OBJ_NEW_SMALL_INT(irq_reason)); + nlr_pop(); + } else { + // Uncaught exception; disable the callback so it doesn't run again. + self->callback = mp_const_none; + mp_printf(MICROPY_ERROR_PRINTER, "uncaught exception in CAN(%u) rx interrupt handler\n", self->can_id); + mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val)); + } + gc_unlock(); + mp_sched_unlock(); + } + } +} + +#if defined(CAN1) +void CAN1_IRQHandler(void) { + machine_can_handler(CAN1); +} +#endif + +#if defined(CAN2) +void CAN2_IRQHandler(void) { + machine_can_handler(CAN2); +} +#endif + +#if defined(CAN3) +void CAN3_IRQHandler(void) { + machine_can_handler(CAN3); +} +#endif + +static void machine_flexcan_deinit(machine_can_obj_t *self) { + if (MP_STATE_PORT(machine_can_objects[self->can_id]) != NULL) { + mp_uint_t instance = FLEXCAN_GetInstance(self->can_inst); + DisableIRQ(((IRQn_Type [])CAN_Rx_Warning_IRQS)[instance]); + DisableIRQ(((IRQn_Type [])CAN_Tx_Warning_IRQS)[instance]); + DisableIRQ(((IRQn_Type [])CAN_Error_IRQS)[instance]); + DisableIRQ(((IRQn_Type [])CAN_Bus_Off_IRQS)[instance]); + DisableIRQ(((IRQn_Type [])CAN_ORed_Message_buffer_IRQS)[instance]); + FLEXCAN_DisableInterrupts(self->can_inst, + kFLEXCAN_BusOffInterruptEnable | kFLEXCAN_ErrorInterruptEnable | + kFLEXCAN_RxWarningInterruptEnable | kFLEXCAN_TxWarningInterruptEnable); + FLEXCAN_DisableMbInterrupts(self->can_inst, + kFLEXCAN_RxFifoOverflowFlag | kFLEXCAN_RxFifoWarningFlag | kFLEXCAN_RxFifoFrameAvlFlag); + MP_STATE_PORT(machine_can_objects[self->can_id]) = NULL; + self->is_enabled = false; + FLEXCAN_Deinit(self->can_inst); + } +} + +// Deinit all can IRQ handlers. +void machine_can_irq_deinit(void) { + for (int i = 0; i < MICROPY_HW_NUM_CAN_IRQS; ++i) { + machine_can_obj_t *machine_can_obj = MP_STATE_PORT(machine_can_objects[i]); + if (machine_can_obj != NULL) { + machine_flexcan_deinit(machine_can_obj); + } + } +} + +STATIC void machine_can_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (!self->is_enabled) { + mp_printf(print, "CAN(%u)", self->can_id); + } else { + qstr mode = MP_QSTR_NORMAL; + if (self->flexcan_config->enableLoopBack) { + if (self->flexcan_config->enableListenOnlyMode) { + mode = MP_QSTR_SILENT_LOOPBACK; + } else { + mode = MP_QSTR_LOOPBACK; + } + } else if (self->flexcan_config->enableListenOnlyMode) { + mode = MP_QSTR_SILENT; + } + mp_printf(print, "CAN(%u, mode=CAN.%q, auto_restart=%q, baudrate=%u)", + self->can_id, + mode, + (self->can_inst->CTRL1 & CAN_CTRL1_BOFFREC_MASK) ? MP_QSTR_False : MP_QSTR_True, + self->flexcan_config->bitRate); + } +} + +STATIC mp_obj_t machine_can_init_helper(machine_can_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_mode, ARG_auto_restart, ARG_baudrate }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = CAN_NORMAL_MODE} }, + { MP_QSTR_auto_restart, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_baudrate, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + }; + + // Parse the arguments. + 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); + + machine_flexcan_deinit(self); + + // Initialise the CAN peripheral. + self->flexcan_config->enableLoopBack = args[ARG_mode].u_int & CAN_LOOPBACK_FLAG; + self->flexcan_config->enableListenOnlyMode = args[ARG_mode].u_int & CAN_SILENT_FLAG; + self->flexcan_config->bitRate = args[ARG_baudrate].u_int; + + // When selecting the CCM CAN clock source with CAN_CLK_SEL set to 2, the UART clock gate + // will not open and CAN_CLK_ROOT will be off. To avoid this issue, set CAN_CLK_SEL to 0 or + // 1 for CAN clock selection, or open the UART clock gate by configuring the CCM_CCGRx register. + // There are two workarounds: + // Set CAN_CLK_SEL to 0 or 1 for CAN clock selection, or if CAN_CLK_SEL is set to 2, + // then the CCM must open any of UART clock gate by configuring the CCM_CCGRx register. + #if (defined(FSL_FEATURE_CCM_HAS_ERRATA_50235) && FSL_FEATURE_CCM_HAS_ERRATA_50235) + CLOCK_EnableClock(kCLOCK_Lpuart1); + #endif // FSL_FEATURE_CCM_HAS_ERRATA_50235 + + uint32_t sourceClock_Hz; + #ifdef MIMXRT117x_SERIES + sourceClock_Hz = can_clock_index_table[self->can_hw_id]; + #else + sourceClock_Hz = BOARD_BOOTCLOCKRUN_CAN_CLK_ROOT; + #endif + + FLEXCAN_Init(self->can_inst, self->flexcan_config, sourceClock_Hz); + memset(self->flexcan_rx_fifo_config->idFilterTable, 0, + sizeof(uint32_t) * self->flexcan_rx_fifo_config->idFilterNum); + FLEXCAN_SetRxFifoConfig(self->can_inst, self->flexcan_rx_fifo_config, true); + + // Calculate the Number of Mailboxes occupied by RX Legacy FIFO and the filter. + mp_uint_t rffn = (uint8_t)((self->can_inst->CTRL2 & CAN_CTRL2_RFFN_MASK) >> CAN_CTRL2_RFFN_SHIFT); + self->flexcan_txmb_start = 6U + (rffn + 1U) * 2U; + #if ((defined(FSL_FEATURE_FLEXCAN_HAS_ERRATA_5641) && FSL_FEATURE_FLEXCAN_HAS_ERRATA_5641) || \ + (defined(FSL_FEATURE_FLEXCAN_HAS_ERRATA_5829) && FSL_FEATURE_FLEXCAN_HAS_ERRATA_5829)) + // the first valid MB should be occupied by ERRATA 5461 or 5829. + self->flexcan_txmb_start += 1U; + #endif + self->flexcan_txmb_count = self->flexcan_config->maxMbNum - self->flexcan_txmb_start; + + for (mp_uint_t i = 0; i < self->flexcan_txmb_count; i++) { + FLEXCAN_SetTxMbConfig(self->can_inst, self->flexcan_txmb_start + i, true); + } + + if (!args[ARG_auto_restart].u_bool) { + self->can_inst->CTRL1 |= CAN_CTRL1_BOFFREC_MASK; + } else { + self->can_inst->CTRL1 &= ~CAN_CTRL1_BOFFREC_MASK; + } + + self->callback = mp_const_none; + self->is_enabled = true; + self->num_error_warning = 0; + self->num_error_passive = 0; + self->num_bus_off = 0; + + MP_STATE_PORT(machine_can_objects[self->can_id]) = self; + + FLEXCAN_EnableMbInterrupts(self->can_inst, + kFLEXCAN_RxFifoOverflowFlag | kFLEXCAN_RxFifoWarningFlag | kFLEXCAN_RxFifoFrameAvlFlag); + + FLEXCAN_EnableInterrupts(self->can_inst, + kFLEXCAN_BusOffInterruptEnable | kFLEXCAN_ErrorInterruptEnable | + kFLEXCAN_RxWarningInterruptEnable | kFLEXCAN_TxWarningInterruptEnable); + + mp_uint_t instance = FLEXCAN_GetInstance(self->can_inst); + EnableIRQ(((IRQn_Type [])CAN_Rx_Warning_IRQS)[instance]); + EnableIRQ(((IRQn_Type [])CAN_Tx_Warning_IRQS)[instance]); + EnableIRQ(((IRQn_Type [])CAN_Error_IRQS)[instance]); + EnableIRQ(((IRQn_Type [])CAN_Bus_Off_IRQS)[instance]); + EnableIRQ(((IRQn_Type [])CAN_ORed_Message_buffer_IRQS)[instance]); + + return MP_OBJ_FROM_PTR(self); +} + +// CAN(bus, ...) +mp_obj_t machine_can_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + // check arguments + mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); + + // Get the CAN bus id. + int can_id = mp_obj_get_int(args[0]); + if (can_id < 0 || can_id >= MP_ARRAY_SIZE(can_index_table) || can_index_table[can_id] == 0) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("CAN(%d) doesn't exist"), can_id); + } + + // Get peripheral object. + mp_uint_t can_hw_id = can_index_table[can_id]; // the hw can number 1..n + machine_can_obj_t *self = mp_obj_malloc(machine_can_obj_t, &machine_can_type); + self->can_id = can_id; + self->can_inst = can_base_ptr_table[can_hw_id]; + self->can_hw_id = can_hw_id; + self->flexcan_config = m_new_obj(flexcan_config_t); + FLEXCAN_GetDefaultConfig(self->flexcan_config); + mp_uint_t maxMbNum = FSL_FEATURE_FLEXCAN_HAS_MESSAGE_BUFFER_MAX_NUMBERn(self->can_inst); + self->flexcan_config->maxMbNum = maxMbNum; + self->flexcan_config->disableSelfReception = true; + + self->flexcan_rx_fifo_config = m_new_obj(flexcan_rx_fifo_config_t); + mp_uint_t idFilterNum = maxMbNum * 2; + self->flexcan_rx_fifo_config->idFilterNum = idFilterNum; + self->flexcan_rx_fifo_config->idFilterType = kFLEXCAN_RxFifoFilterTypeA; + self->flexcan_rx_fifo_config->priority = kFLEXCAN_RxFifoPrioHigh; + self->flexcan_rx_fifo_config->idFilterTable = m_new0(uint32_t, idFilterNum); + + // Configure board-specific pin MUX based on the hardware device number. + bool can_present = can_set_iomux(can_hw_id); + + if (can_present) { + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + return machine_can_init_helper(self, n_args - 1, args + 1, &kw_args); + } else { + return mp_const_none; + } +} + +// can.init(mode, [kwargs]) +STATIC mp_obj_t machine_can_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + return machine_can_init_helper(args[0], n_args - 1, args + 1, kw_args); +} +MP_DEFINE_CONST_FUN_OBJ_KW(machine_can_init_obj, 1, machine_can_init); + +// deinit() +STATIC mp_obj_t machine_can_deinit(mp_obj_t self_in) { + machine_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + machine_flexcan_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_can_deinit_obj, machine_can_deinit); + +// Force a software restart of the controller, to allow transmission after a bus error +STATIC mp_obj_t machine_can_restart(mp_obj_t self_in) { + machine_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (!self->is_enabled) { + mp_raise_ValueError(MP_ERROR_TEXT("CAN bus not enabled")); + } + bool boff_rec = self->can_inst->CTRL1 & CAN_CTRL1_BOFFREC_MASK; + self->can_inst->CTRL1 &= ~CAN_CTRL1_BOFFREC_MASK; + while (self->can_inst->CTRL1 & CAN_CTRL1_BOFFREC_MASK) { + } + if (boff_rec) { + self->can_inst->CTRL1 |= CAN_CTRL1_BOFFREC_MASK; + while ((self->can_inst->CTRL1 & CAN_CTRL1_BOFFREC_MASK) == 0) { + } + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_can_restart_obj, machine_can_restart); + +// Get the state of the controller +STATIC mp_obj_t machine_can_state(mp_obj_t self_in) { + machine_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (self->is_enabled) { + uint32_t result = FLEXCAN_GetStatusFlags(self->can_inst); + uint32_t flt_conf = (result & CAN_ESR1_FLTCONF_MASK) >> CAN_ESR1_FLTCONF_SHIFT; + if (flt_conf > 1) { + return MP_OBJ_NEW_SMALL_INT(CAN_STATE_BUS_OFF); + } + if (flt_conf == 1) { + return MP_OBJ_NEW_SMALL_INT(CAN_STATE_ERROR_PASSIVE); + } + if ((result & CAN_ESR1_RXWRN_MASK) || (result & CAN_ESR1_TXWRN_MASK)) { + return MP_OBJ_NEW_SMALL_INT(CAN_STATE_ERROR_WARNING); + } + // flt_conf == 0 + return MP_OBJ_NEW_SMALL_INT(CAN_STATE_ERROR_ACTIVE); + } + return MP_OBJ_NEW_SMALL_INT(CAN_STATE_STOPPED); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_can_state_obj, machine_can_state); + +static mp_uint_t can_count_txmb_pending(machine_can_obj_t *self) { + mp_uint_t count = 0; + for (mp_uint_t i = 0; i < self->flexcan_txmb_count; i++) { + uint32_t cs_temp = self->can_inst->MB[self->flexcan_txmb_start + i].CS; + if ((cs_temp & CAN_CS_CODE_MASK) == CAN_CS_CODE(kFLEXCAN_TxMbDataOrRemote)) { + count++; + } + } + return count; +} + +// Get info about error states and TX/RX buffers +STATIC mp_obj_t machine_can_info(size_t n_args, const mp_obj_t *args) { + machine_can_obj_t *self = MP_OBJ_TO_PTR(args[0]); + if (!self->is_enabled) { + mp_raise_ValueError(MP_ERROR_TEXT("CAN bus not enabled")); + } + mp_obj_list_t *list; + if (n_args == 1) { + list = MP_OBJ_TO_PTR(mp_obj_new_list(8, NULL)); + } else { + if (!mp_obj_is_type(args[1], &mp_type_list)) { + mp_raise_TypeError(NULL); + } + list = MP_OBJ_TO_PTR(args[1]); + if (list->len < 8) { + mp_raise_ValueError(NULL); + } + } + + CAN_Type *can = self->can_inst; + list->items[0] = MP_OBJ_NEW_SMALL_INT((can->ECR & CAN_ECR_TXERRCNT_MASK) >> CAN_ECR_TXERRCNT_SHIFT); + list->items[1] = MP_OBJ_NEW_SMALL_INT((can->ECR & CAN_ECR_RXERRCNT_MASK) >> CAN_ECR_RXERRCNT_SHIFT); + list->items[2] = MP_OBJ_NEW_SMALL_INT(self->num_error_warning); + list->items[3] = MP_OBJ_NEW_SMALL_INT(self->num_error_passive); + list->items[4] = MP_OBJ_NEW_SMALL_INT(self->num_bus_off); + list->items[5] = MP_OBJ_NEW_SMALL_INT(can_count_txmb_pending(self)); + // only 0 or 1 + int n_rx_pending = FLEXCAN_GetMbStatusFlags(can, (uint32_t)kFLEXCAN_RxFifoFrameAvlFlag); + list->items[6] = MP_OBJ_NEW_SMALL_INT(n_rx_pending); + list->items[7] = MP_OBJ_NEW_SMALL_INT(0); + + return MP_OBJ_FROM_PTR(list); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_can_info_obj, 1, 2, machine_can_info); + +// any(fifo) - return `True` if any message waiting on the FIFO, else `False` +STATIC mp_obj_t machine_can_any(mp_obj_t self_in, mp_obj_t fifo_in) { + machine_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (!self->is_enabled) { + mp_raise_ValueError(MP_ERROR_TEXT("CAN bus not enabled")); + } + mp_int_t fifo = mp_obj_get_int(fifo_in); + if (fifo != 0) { + mp_raise_ValueError(MP_ERROR_TEXT("Fifo must be 0")); + } + if (FLEXCAN_GetMbStatusFlags(self->can_inst, (uint32_t)kFLEXCAN_RxFifoFrameAvlFlag)) { + return mp_const_true; + } + return mp_const_false; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(machine_can_any_obj, machine_can_any); + +static mp_uint_t can_find_txmb(machine_can_obj_t *self, flexcan_frame_t *frame) { + // See if this frame id has been used before. If so, re-use the same mailbox to keep message ordering. + for (mp_uint_t i = 0; i < self->flexcan_txmb_count; i++) { + uint32_t cs_temp = self->can_inst->MB[self->flexcan_txmb_start + i].CS; + if (((frame->format == kFLEXCAN_FrameFormatStandard) || (cs_temp & CAN_CS_IDE_MASK)) + && ((frame->type == kFLEXCAN_FrameTypeData) || (cs_temp & CAN_CS_RTR_MASK)) + && (self->can_inst->MB[self->flexcan_txmb_start + i].ID == frame->id)) { + if ((cs_temp & CAN_CS_CODE_MASK) != CAN_CS_CODE(kFLEXCAN_TxMbDataOrRemote)) { + return self->flexcan_txmb_start + i; + } else { + return 0; + } + } + } + // Frame id has never been used before so just pick the first empty mailbox. + for (mp_uint_t i = 0; i < self->flexcan_txmb_count; i++) { + uint32_t cs_temp = self->can_inst->MB[self->flexcan_txmb_start + i].CS; + if ((cs_temp & CAN_CS_CODE_MASK) != CAN_CS_CODE(kFLEXCAN_TxMbDataOrRemote)) { + return self->flexcan_txmb_start + i; + } + } + return 0; +} + +STATIC mp_obj_t machine_can_send(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_data, ARG_id, ARG_timeout, ARG_rtr, ARG_extframe }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_rtr, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_extframe, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + }; + + // parse args + machine_can_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + if (!self->is_enabled) { + mp_raise_ValueError(MP_ERROR_TEXT("CAN bus not enabled")); + } + 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); + + // get the buffer to send from + mp_buffer_info_t bufinfo; + uint8_t data[1]; + if (mp_obj_is_int(args[ARG_data].u_obj)) { + data[0] = mp_obj_get_int(args[ARG_data].u_obj); + bufinfo.buf = data; + bufinfo.len = 1; + bufinfo.typecode = 'B'; + } else { + mp_get_buffer_raise(args[ARG_data].u_obj, &bufinfo, MP_BUFFER_READ); + } + + if (bufinfo.len > CAN_MAX_DATA_FRAME) { + mp_raise_ValueError(MP_ERROR_TEXT("CAN data field too long")); + } + + flexcan_frame_t tx_msg; + + tx_msg.dataWord0 = 0; + tx_msg.dataWord1 = 0; + + if (bufinfo.len > 0) { + tx_msg.dataByte0 = ((byte *)bufinfo.buf)[0]; + } + if (bufinfo.len > 1) { + tx_msg.dataByte1 = ((byte *)bufinfo.buf)[1]; + } + if (bufinfo.len > 2) { + tx_msg.dataByte2 = ((byte *)bufinfo.buf)[2]; + } + if (bufinfo.len > 3) { + tx_msg.dataByte3 = ((byte *)bufinfo.buf)[3]; + } + if (bufinfo.len > 4) { + tx_msg.dataByte4 = ((byte *)bufinfo.buf)[4]; + } + if (bufinfo.len > 5) { + tx_msg.dataByte5 = ((byte *)bufinfo.buf)[5]; + } + if (bufinfo.len > 6) { + tx_msg.dataByte6 = ((byte *)bufinfo.buf)[6]; + } + if (bufinfo.len > 7) { + tx_msg.dataByte7 = ((byte *)bufinfo.buf)[7]; + } + + tx_msg.length = bufinfo.len; + tx_msg.type = args[ARG_rtr].u_bool; + tx_msg.format = args[ARG_extframe].u_bool; + + if (tx_msg.format) { + tx_msg.id = FLEXCAN_ID_EXT(args[ARG_id].u_int); + } else { + tx_msg.id = FLEXCAN_ID_STD(args[ARG_id].u_int); + } + + uint32_t timeout_ms = args[ARG_timeout].u_int; + uint32_t start = mp_hal_ticks_ms(); + + while (true) { + mp_uint_t mbIdx = can_find_txmb(self, &tx_msg); + if (mbIdx && (FLEXCAN_WriteTxMb(self->can_inst, mbIdx, &tx_msg) == kStatus_Success)) { + break; + } + if (timeout_ms == 0) { + mp_raise_OSError(MP_ETIMEDOUT); + } + // Check for the Timeout + if (timeout_ms != UINT32_MAX) { + if (mp_hal_ticks_ms() - start >= timeout_ms) { + mp_raise_OSError(MP_ETIMEDOUT); + } + } + MICROPY_EVENT_POLL_HOOK + } + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_can_send_obj, 1, machine_can_send); + +// recv(fifo, list=None, *, timeout=5000) +STATIC mp_obj_t machine_can_recv(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_fifo, ARG_list, ARG_timeout, ARG_fdf }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_fifo, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_list, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} }, + }; + + // parse args + machine_can_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + if (!self->is_enabled) { + mp_raise_ValueError(MP_ERROR_TEXT("CAN bus not enabled")); + } + 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); + + mp_uint_t fifo = args[ARG_fifo].u_int; + if (fifo != 0) { + mp_raise_TypeError(MP_ERROR_TEXT("fifo must be 0")); + } + + uint32_t timeout_ms = args[ARG_timeout].u_int; + uint32_t start = mp_hal_ticks_ms(); + + while (!FLEXCAN_GetMbStatusFlags(self->can_inst, (uint32_t)kFLEXCAN_RxFifoFrameAvlFlag)) { + if (timeout_ms == 0) { + mp_raise_OSError(MP_ETIMEDOUT); + } + // Check for the Timeout + if (timeout_ms != UINT32_MAX) { + if (mp_hal_ticks_ms() - start >= timeout_ms) { + mp_raise_OSError(MP_ETIMEDOUT); + } + } + MICROPY_EVENT_POLL_HOOK + } + + flexcan_frame_t rx_frame; + status_t status = FLEXCAN_ReadRxFifo(self->can_inst, &rx_frame); + FLEXCAN_ClearMbStatusFlags(self->can_inst, (uint32_t)kFLEXCAN_RxFifoFrameAvlFlag); + + FLEXCAN_EnableMbInterrupts(self->can_inst, + kFLEXCAN_RxFifoOverflowFlag | kFLEXCAN_RxFifoWarningFlag | kFLEXCAN_RxFifoFrameAvlFlag); + + if (status != kStatus_Success) { + mp_raise_OSError(MP_EIO); + } + + uint32_t rx_dlc = rx_frame.length; + uint8_t rx_data[CAN_MAX_DATA_FRAME] = {}; + + if (rx_dlc > 0) { + rx_data[0] = rx_frame.dataByte0; + } + if (rx_dlc > 1) { + rx_data[1] = rx_frame.dataByte1; + } + if (rx_dlc > 2) { + rx_data[2] = rx_frame.dataByte2; + } + if (rx_dlc > 3) { + rx_data[3] = rx_frame.dataByte3; + } + if (rx_dlc > 4) { + rx_data[4] = rx_frame.dataByte4; + } + if (rx_dlc > 5) { + rx_data[5] = rx_frame.dataByte5; + } + if (rx_dlc > 6) { + rx_data[6] = rx_frame.dataByte6; + } + if (rx_dlc > 7) { + rx_data[7] = rx_frame.dataByte7; + } + + // Create the tuple, or get the list, that will hold the return values + // Also populate the fifth element, either a new bytes or reuse existing memoryview + mp_obj_t ret_obj = args[ARG_list].u_obj; + mp_obj_t *items; + if (ret_obj == mp_const_none) { + ret_obj = mp_obj_new_tuple(5, NULL); + items = ((mp_obj_tuple_t *)MP_OBJ_TO_PTR(ret_obj))->items; + items[4] = mp_obj_new_bytes(rx_data, rx_dlc); + } else { + // User should provide a list of length at least 5 to hold the values + if (!mp_obj_is_type(ret_obj, &mp_type_list)) { + mp_raise_TypeError(NULL); + } + mp_obj_list_t *list = MP_OBJ_TO_PTR(ret_obj); + if (list->len < 5) { + mp_raise_ValueError(NULL); + } + items = list->items; + // Fifth element must be a memoryview which we assume points to a + // byte-like array which is large enough, and then we resize it inplace + if (!mp_obj_is_type(items[4], &mp_type_memoryview)) { + mp_raise_TypeError(NULL); + } + mp_obj_array_t *mv = MP_OBJ_TO_PTR(items[4]); + if (!(mv->typecode == (MP_OBJ_ARRAY_TYPECODE_FLAG_RW | BYTEARRAY_TYPECODE) + || (mv->typecode | 0x20) == (MP_OBJ_ARRAY_TYPECODE_FLAG_RW | 'b'))) { + mp_raise_ValueError(NULL); + } + mv->len = rx_dlc; + memcpy(mv->items, rx_data, rx_dlc); + } + + // Populate the first 4 values of the tuple/list + items[0] = MP_OBJ_NEW_SMALL_INT(rx_frame.id >> (rx_frame.format ? CAN_ID_EXT_SHIFT : CAN_ID_STD_SHIFT)); + items[1] = mp_obj_new_bool(rx_frame.format); + items[2] = mp_obj_new_bool(rx_frame.type); + items[3] = MP_OBJ_NEW_SMALL_INT(rx_frame.idhit); + + // Return the result + return ret_obj; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_can_recv_obj, 1, machine_can_recv); + +STATIC mp_obj_t machine_can_clearfilter(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_extframe }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_extframe, MP_ARG_BOOL, {.u_bool = false} }, + }; + + // parse args + machine_can_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + if (!self->is_enabled) { + mp_raise_ValueError(MP_ERROR_TEXT("CAN bus not enabled")); + } + mp_uint_t bank = mp_obj_get_int(pos_args[1]); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 2, pos_args + 2, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + if ((bank < 0) || (bank >= self->flexcan_config->maxMbNum)) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("%d maximum banks"), + self->flexcan_config->maxMbNum); + } + + self->flexcan_rx_fifo_config->idFilterTable[bank * 2] = 0; + self->flexcan_rx_fifo_config->idFilterTable[(bank * 2) + 1] = 0; + + FLEXCAN_SetRxFifoConfig(self->can_inst, self->flexcan_rx_fifo_config, true); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_can_clearfilter_obj, 2, machine_can_clearfilter); + +// setfilter(bank, mode, fifo, params, *, rtr) +STATIC mp_obj_t machine_can_setfilter(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_bank, ARG_mode, ARG_fifo, ARG_params, ARG_rtr, ARG_extframe }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_bank, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_fifo, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_params, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_rtr, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_extframe, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + }; + + // parse args + machine_can_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + if (!self->is_enabled) { + mp_raise_ValueError(MP_ERROR_TEXT("CAN bus not enabled")); + } + 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); + + mp_uint_t bank = args[ARG_bank].u_int; + if ((bank < 0) || (bank >= self->flexcan_config->maxMbNum)) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("%d maximum banks"), + self->flexcan_config->maxMbNum); + } + + if (args[ARG_fifo].u_int != 0) { + mp_raise_ValueError(MP_ERROR_TEXT("fifo must be 0")); + } + + size_t len; + size_t rtr_len; + mp_uint_t rtr_masks[2] = {0, 0}; + mp_obj_t *rtr_flags; + mp_obj_t *params; + mp_obj_get_array(args[ARG_params].u_obj, &len, ¶ms); + if (args[ARG_rtr].u_obj != MP_OBJ_NULL) { + mp_obj_get_array(args[ARG_rtr].u_obj, &rtr_len, &rtr_flags); + } + + if (args[ARG_mode].u_int == CAN_DUAL) { + if (len != 2) { + goto error; + } + if (args[ARG_rtr].u_obj != MP_OBJ_NULL) { + if (rtr_len != 2) { + goto error; + } + rtr_masks[0] = mp_obj_is_true(rtr_flags[0]); + rtr_masks[1] = mp_obj_is_true(rtr_flags[1]); + } + mp_uint_t id0 = mp_obj_get_int(params[0]); + mp_uint_t id1 = mp_obj_get_int(params[1]); + if (args[ARG_extframe].u_bool) { + id0 = FLEXCAN_ID_EXT(id0) | (1 << 29); + id1 = FLEXCAN_ID_EXT(id1) | (1 << 29); + } else { + id0 = FLEXCAN_ID_STD(id0); + id1 = FLEXCAN_ID_STD(id1); + } + id0 = ((rtr_masks[0] << 30) | id0) << 1; + id1 = ((rtr_masks[1] << 30) | id1) << 1; + self->flexcan_rx_fifo_config->idFilterTable[bank * 2] = id0; + self->flexcan_rx_fifo_config->idFilterTable[(bank * 2) + 1] = id1; + } else { + goto error; + } + + FLEXCAN_SetRxFifoConfig(self->can_inst, self->flexcan_rx_fifo_config, true); + + return mp_const_none; +error: + mp_raise_ValueError(MP_ERROR_TEXT("CAN filter parameter error")); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_can_setfilter_obj, 1, machine_can_setfilter); + +STATIC mp_obj_t machine_can_rxcallback(mp_obj_t self_in, mp_obj_t fifo_in, mp_obj_t callback_in) { + machine_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (!self->is_enabled) { + mp_raise_ValueError(MP_ERROR_TEXT("CAN bus not enabled")); + } + mp_int_t fifo = mp_obj_get_int(fifo_in); + if (fifo != 0) { + mp_raise_ValueError(MP_ERROR_TEXT("fifo must be 0")); + } + self->callback = callback_in; + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(machine_can_rxcallback_obj, machine_can_rxcallback); + +STATIC const mp_rom_map_elem_t machine_can_locals_dict_table[] = { + // instance methods + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_can_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&machine_can_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_restart), MP_ROM_PTR(&machine_can_restart_obj) }, + { MP_ROM_QSTR(MP_QSTR_state), MP_ROM_PTR(&machine_can_state_obj) }, + { MP_ROM_QSTR(MP_QSTR_info), MP_ROM_PTR(&machine_can_info_obj) }, + { MP_ROM_QSTR(MP_QSTR_any), MP_ROM_PTR(&machine_can_any_obj) }, + { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&machine_can_send_obj) }, + { MP_ROM_QSTR(MP_QSTR_recv), MP_ROM_PTR(&machine_can_recv_obj) }, + { MP_ROM_QSTR(MP_QSTR_setfilter), MP_ROM_PTR(&machine_can_setfilter_obj) }, + { MP_ROM_QSTR(MP_QSTR_clearfilter), MP_ROM_PTR(&machine_can_clearfilter_obj) }, + { MP_ROM_QSTR(MP_QSTR_rxcallback), MP_ROM_PTR(&machine_can_rxcallback_obj) }, + + // class constants + { MP_ROM_QSTR(MP_QSTR_NORMAL), MP_ROM_INT(CAN_NORMAL_MODE) }, + { MP_ROM_QSTR(MP_QSTR_LOOPBACK), MP_ROM_INT(CAN_LOOPBACK_FLAG) }, + { MP_ROM_QSTR(MP_QSTR_SILENT), MP_ROM_INT(CAN_SILENT_FLAG) }, + { MP_ROM_QSTR(MP_QSTR_SILENT_LOOPBACK), MP_ROM_INT(CAN_LOOPBACK_FLAG | CAN_SILENT_FLAG) }, + { MP_ROM_QSTR(MP_QSTR_DUAL), MP_ROM_INT(CAN_DUAL) }, + { MP_ROM_QSTR(MP_QSTR_MASK32), MP_ROM_INT(CAN_DUAL) }, + + // values for CAN.state() + { MP_ROM_QSTR(MP_QSTR_STOPPED), MP_ROM_INT(CAN_STATE_STOPPED) }, + { MP_ROM_QSTR(MP_QSTR_ERROR_ACTIVE), MP_ROM_INT(CAN_STATE_ERROR_ACTIVE) }, + { MP_ROM_QSTR(MP_QSTR_ERROR_WARNING), MP_ROM_INT(CAN_STATE_ERROR_WARNING) }, + { MP_ROM_QSTR(MP_QSTR_ERROR_PASSIVE), MP_ROM_INT(CAN_STATE_ERROR_PASSIVE) }, + { MP_ROM_QSTR(MP_QSTR_BUS_OFF), MP_ROM_INT(CAN_STATE_BUS_OFF) }, +}; +STATIC MP_DEFINE_CONST_DICT(machine_can_locals_dict, machine_can_locals_dict_table); + +STATIC mp_uint_t can_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) { + machine_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_uint_t ret; + if (self->is_enabled && request == MP_STREAM_POLL) { + uintptr_t flags = arg; + ret = 0; + + if ((flags & MP_STREAM_POLL_RD) + && FLEXCAN_GetMbStatusFlags(self->can_inst, (uint32_t)kFLEXCAN_RxFifoFrameAvlFlag)) { + ret |= MP_STREAM_POLL_RD; + } + + if ((flags & MP_STREAM_POLL_WR) + && can_count_txmb_pending(self)) { + ret |= MP_STREAM_POLL_WR; + } + } else { + *errcode = MP_EINVAL; + ret = -1; + } + return ret; +} + +STATIC const mp_stream_p_t can_stream_p = { + .ioctl = can_ioctl, + .is_text = false, +}; + +MP_DEFINE_CONST_OBJ_TYPE( + machine_can_type, + MP_QSTR_CAN, + MP_TYPE_FLAG_NONE, + make_new, machine_can_make_new, + print, machine_can_print, + protocol, &can_stream_p, + locals_dict, &machine_can_locals_dict + ); + +MP_REGISTER_ROOT_POINTER(void *machine_can_objects[MICROPY_HW_NUM_CAN_IRQS]); + +#endif // MICROPY_HW_ENABLE_CAN diff --git a/ports/mimxrt/main.c b/ports/mimxrt/main.c index 2d0760964c225..136db4bdb524b 100644 --- a/ports/mimxrt/main.c +++ b/ports/mimxrt/main.c @@ -117,6 +117,9 @@ int main(void) { soft_reset_exit: mp_printf(MP_PYTHON_PRINTER, "MPY: soft reboot\n"); + #if MICROPY_HW_ENABLE_CAN + machine_can_irq_deinit(); + #endif machine_pin_irq_deinit(); #if MICROPY_PY_MACHINE_I2S machine_i2s_deinit_all(); diff --git a/ports/mimxrt/modmachine.c b/ports/mimxrt/modmachine.c index 423a67d6095f2..c363d0dfd206e 100644 --- a/ports/mimxrt/modmachine.c +++ b/ports/mimxrt/modmachine.c @@ -153,6 +153,9 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_Signal), MP_ROM_PTR(&machine_signal_type) }, { MP_ROM_QSTR(MP_QSTR_SoftI2C), MP_ROM_PTR(&mp_machine_soft_i2c_type) }, { MP_ROM_QSTR(MP_QSTR_SoftSPI), MP_ROM_PTR(&mp_machine_soft_spi_type) }, + #if MICROPY_HW_ENABLE_CAN + { MP_ROM_QSTR(MP_QSTR_CAN), MP_ROM_PTR(&machine_can_type) }, + #endif { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&machine_i2c_type) }, #if MICROPY_PY_MACHINE_I2S { MP_ROM_QSTR(MP_QSTR_I2S), MP_ROM_PTR(&machine_i2s_type) }, diff --git a/ports/mimxrt/modmachine.h b/ports/mimxrt/modmachine.h index 1f669af09e962..cbc7132196ccb 100644 --- a/ports/mimxrt/modmachine.h +++ b/ports/mimxrt/modmachine.h @@ -30,6 +30,7 @@ #include "py/obj.h" extern const mp_obj_type_t machine_adc_type; +extern const mp_obj_type_t machine_can_type; extern const mp_obj_type_t machine_i2c_type; extern const mp_obj_type_t machine_i2s_type; extern const mp_obj_type_t machine_pwm_type; @@ -41,6 +42,7 @@ extern const mp_obj_type_t machine_uart_type; extern const mp_obj_type_t machine_wdt_type; void machine_adc_init(void); +void machine_can_irq_deinit(void); void machine_pin_irq_deinit(void); void machine_pwm_deinit_all(void); void machine_uart_deinit_all(void); pFad - Phonifier reborn

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

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


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy