summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xmain.c8
-rw-r--r--ports/atmel-samd/Makefile4
-rw-r--r--ports/atmel-samd/asf4_conf/same54/peripheral_clk_config.h82
-rw-r--r--ports/atmel-samd/boards/common.template.ld7
-rw-r--r--ports/atmel-samd/boards/same54_xplained/mpconfigboard.mk1
-rw-r--r--ports/atmel-samd/boards/same54_xplained/pins.c5
-rw-r--r--ports/atmel-samd/common-hal/_canio/CAN.c384
-rw-r--r--ports/atmel-samd/common-hal/_canio/CAN.h62
-rw-r--r--ports/atmel-samd/common-hal/_canio/Listener.c117
-rw-r--r--ports/atmel-samd/common-hal/_canio/Listener.h53
-rw-r--r--ports/atmel-samd/common-hal/_canio/__init__.c25
-rw-r--r--ports/atmel-samd/common-hal/_canio/__init__.h62
-rwxr-xr-xports/atmel-samd/tools/mkcandata.py31
-rw-r--r--py/circuitpy_defns.mk9
-rw-r--r--py/circuitpy_mpconfig.h8
-rw-r--r--py/circuitpy_mpconfig.mk3
-rw-r--r--shared-bindings/_canio/CAN.c338
-rw-r--r--shared-bindings/_canio/CAN.h31
-rw-r--r--shared-bindings/_canio/Listener.c197
-rw-r--r--shared-bindings/_canio/Listener.h31
-rw-r--r--shared-bindings/_canio/Match.c140
-rw-r--r--shared-bindings/_canio/Match.h33
-rw-r--r--shared-bindings/_canio/Message.c213
-rw-r--r--shared-bindings/_canio/Message.h31
-rw-r--r--shared-bindings/_canio/__init__.c73
-rw-r--r--shared-bindings/_canio/__init__.h29
-rw-r--r--shared-module/_canio/Match.c43
-rw-r--r--shared-module/_canio/Match.h41
-rw-r--r--shared-module/_canio/Message.c85
-rw-r--r--shared-module/_canio/Message.h47
30 files changed, 2190 insertions, 3 deletions
diff --git a/main.c b/main.c
index 5b719dc4b..f259fea3f 100755
--- a/main.c
+++ b/main.c
@@ -81,6 +81,10 @@
#include "supervisor/shared/bluetooth.h"
#endif
+#if CIRCUITPY_CANIO
+#include "common-hal/_canio/CAN.h"
+#endif
+
void do_str(const char *src, mp_parse_input_kind_t input_kind) {
mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0);
if (lex == NULL) {
@@ -226,6 +230,10 @@ void cleanup_after_vm(supervisor_allocation* heap) {
free_memory(heap);
supervisor_move_memory();
+ #ifdef CIRCUITPY_CANIO
+ common_hal_canio_reset();
+ #endif
+
reset_port();
#if CIRCUITPY_BOARD
reset_board_busses();
diff --git a/ports/atmel-samd/Makefile b/ports/atmel-samd/Makefile
index aba630dbb..1ee21679d 100644
--- a/ports/atmel-samd/Makefile
+++ b/ports/atmel-samd/Makefile
@@ -393,6 +393,10 @@ SRC_QSTR += $(HEADER_BUILD)/sdiodata.h
$(HEADER_BUILD)/sdiodata.h: tools/mksdiodata.py | $(HEADER_BUILD)
$(Q)$(PYTHON3) $< > $@
+SRC_QSTR += $(HEADER_BUILD)/candata.h
+$(HEADER_BUILD)/candata.h: tools/mkcandata.py | $(HEADER_BUILD)
+ $(Q)$(PYTHON3) $< > $@
+
SRC_QSTR += $(SRC_C) $(SRC_SUPERVISOR) $(SRC_COMMON_HAL_EXPANDED) $(SRC_SHARED_MODULE_EXPANDED)
# Sources that only hold QSTRs after pre-processing.
SRC_QSTR_PREPROCESSOR += peripherals/samd/$(PERIPHERALS_CHIP_FAMILY)/clocks.c
diff --git a/ports/atmel-samd/asf4_conf/same54/peripheral_clk_config.h b/ports/atmel-samd/asf4_conf/same54/peripheral_clk_config.h
index 59fe8730e..51173f6d3 100644
--- a/ports/atmel-samd/asf4_conf/same54/peripheral_clk_config.h
+++ b/ports/atmel-samd/asf4_conf/same54/peripheral_clk_config.h
@@ -1165,6 +1165,88 @@
#define CONF_SDHC1_SLOW_FREQUENCY 12000000
#endif
+// <h> CAN Clock Settings
+// <y> CAN Clock source
+
+// <GCLK_PCHCTRL_GEN_GCLK0_Val"> Generic clock generator 0
+
+// <GCLK_PCHCTRL_GEN_GCLK1_Val"> Generic clock generator 1
+
+// <GCLK_PCHCTRL_GEN_GCLK2_Val"> Generic clock generator 2
+
+// <GCLK_PCHCTRL_GEN_GCLK3_Val"> Generic clock generator 3
+
+// <GCLK_PCHCTRL_GEN_GCLK4_Val"> Generic clock generator 4
+
+// <GCLK_PCHCTRL_GEN_GCLK5_Val"> Generic clock generator 5
+
+// <GCLK_PCHCTRL_GEN_GCLK6_Val"> Generic clock generator 6
+
+// <GCLK_PCHCTRL_GEN_GCLK7_Val"> Generic clock generator 7
+
+// <GCLK_PCHCTRL_GEN_GCLK8_Val"> Generic clock generator 8
+
+// <GCLK_PCHCTRL_GEN_GCLK9_Val"> Generic clock generator 9
+
+// <GCLK_PCHCTRL_GEN_GCLK10_Val"> Generic clock generator 10
+
+// <GCLK_PCHCTRL_GEN_GCLK11_Val"> Generic clock generator 11
+
+// <i> Select the clock source for CAN.
+// <id> sdhc_gclk_selection
+#ifndef CONF_GCLK_CAN0_SRC
+#define CONF_GCLK_CAN0_SRC GCLK_PCHCTRL_GEN_GCLK0_Val
+#endif
+
+/**
+ * \def CAN FREQUENCY
+ * \brief CAN's Clock frequency
+ */
+#ifndef CONF_CAN0_FREQUENCY
+#define CONF_CAN0_FREQUENCY 120000000
+#endif
+
+// <h> CAN Clock Settings
+// <y> CAN Clock source
+
+// <GCLK_PCHCTRL_GEN_GCLK0_Val"> Generic clock generator 0
+
+// <GCLK_PCHCTRL_GEN_GCLK1_Val"> Generic clock generator 1
+
+// <GCLK_PCHCTRL_GEN_GCLK2_Val"> Generic clock generator 2
+
+// <GCLK_PCHCTRL_GEN_GCLK3_Val"> Generic clock generator 3
+
+// <GCLK_PCHCTRL_GEN_GCLK4_Val"> Generic clock generator 4
+
+// <GCLK_PCHCTRL_GEN_GCLK5_Val"> Generic clock generator 5
+
+// <GCLK_PCHCTRL_GEN_GCLK6_Val"> Generic clock generator 6
+
+// <GCLK_PCHCTRL_GEN_GCLK7_Val"> Generic clock generator 7
+
+// <GCLK_PCHCTRL_GEN_GCLK8_Val"> Generic clock generator 8
+
+// <GCLK_PCHCTRL_GEN_GCLK9_Val"> Generic clock generator 9
+
+// <GCLK_PCHCTRL_GEN_GCLK10_Val"> Generic clock generator 10
+
+// <GCLK_PCHCTRL_GEN_GCLK11_Val"> Generic clock generator 11
+
+// <i> Select the clock source for CAN.
+// <id> sdhc_gclk_selection
+#ifndef CONF_GCLK_CAN1_SRC
+#define CONF_GCLK_CAN1_SRC GCLK_PCHCTRL_GEN_GCLK0_Val
+#endif
+
+/**
+ * \def CAN FREQUENCY
+ * \brief CAN's Clock frequency
+ */
+#ifndef CONF_CAN1_FREQUENCY
+#define CONF_CAN1_FREQUENCY 120000000
+#endif
+
// <<< end of configuration section >>>
#endif // PERIPHERAL_CLK_CONFIG_H
diff --git a/ports/atmel-samd/boards/common.template.ld b/ports/atmel-samd/boards/common.template.ld
index 1054605c8..19e4753f8 100644
--- a/ports/atmel-samd/boards/common.template.ld
+++ b/ports/atmel-samd/boards/common.template.ld
@@ -51,6 +51,7 @@ SECTIONS
{
. = ALIGN(4);
_srelocate = .; /* create a global symbol at data start; used by startup code in order to initialize the .data section in RAM */
+ . = ALIGN(4);
*(.ramfunc)
*(.ramfunc*)
*(.data) /* .data sections */
@@ -61,11 +62,15 @@ SECTIONS
} >RAM
/* Uninitialized data section */
- .bss :
+ .bss (NOLOAD) :
{
. = ALIGN(4);
_sbss = .;
_szero = .; /* define a global symbol at bss start; used by startup code */
+ /* Data accessed by the CAN peripheral must be in the first 64kB RAM */
+ _scanram = .;
+ *(.canram)
+ _ecanram = .;
*(.bss)
*(.bss*)
*(COMMON)
diff --git a/ports/atmel-samd/boards/same54_xplained/mpconfigboard.mk b/ports/atmel-samd/boards/same54_xplained/mpconfigboard.mk
index 7ac126514..e4472e05d 100644
--- a/ports/atmel-samd/boards/same54_xplained/mpconfigboard.mk
+++ b/ports/atmel-samd/boards/same54_xplained/mpconfigboard.mk
@@ -12,3 +12,4 @@ EXTERNAL_FLASH_DEVICES = "N25Q256A"
LONGINT_IMPL = MPZ
CIRCUITPY_SDIOIO = 1
+CIRCUITPY_CANIO = 1
diff --git a/ports/atmel-samd/boards/same54_xplained/pins.c b/ports/atmel-samd/boards/same54_xplained/pins.c
index 8a864ab97..1ed431fba 100644
--- a/ports/atmel-samd/boards/same54_xplained/pins.c
+++ b/ports/atmel-samd/boards/same54_xplained/pins.c
@@ -48,8 +48,9 @@ STATIC const mp_rom_map_elem_t board_global_dict_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR_QT), MP_ROM_PTR(&pin_PA16) },
- { MP_OBJ_NEW_QSTR(MP_QSTR_CANRX), MP_ROM_PTR(&pin_PB12) },
- { MP_OBJ_NEW_QSTR(MP_QSTR_CANTX), MP_ROM_PTR(&pin_PB13) },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_CAN_RX), MP_ROM_PTR(&pin_PB13) },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_CAN_TX), MP_ROM_PTR(&pin_PB12) },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_CAN_STANDBY), MP_ROM_PTR(&pin_PC13) },
// EXT1 header
{ MP_OBJ_NEW_QSTR(MP_QSTR_PB04), MP_ROM_PTR(&pin_PB04) },
diff --git a/ports/atmel-samd/common-hal/_canio/CAN.c b/ports/atmel-samd/common-hal/_canio/CAN.c
new file mode 100644
index 000000000..dcd269495
--- /dev/null
+++ b/ports/atmel-samd/common-hal/_canio/CAN.c
@@ -0,0 +1,384 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler for Adafruit Industries
+ *
+ * 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 <string.h>
+
+#include "py/runtime.h"
+#include "py/mperrno.h"
+
+#include "peripheral_clk_config.h"
+
+#include "common-hal/_canio/CAN.h"
+#include "shared-bindings/microcontroller/Pin.h"
+#include "shared-bindings/util.h"
+#include "supervisor/port.h"
+
+#include "component/can.h"
+
+#include "genhdr/candata.h"
+
+STATIC Can * const can_insts[] = CAN_INSTS;
+
+STATIC canio_can_obj_t *can_objs[MP_ARRAY_SIZE(can_insts)];
+
+// This must be placed in the first 64kB of RAM
+STATIC COMPILER_SECTION(".canram") canio_can_state_t can_state[MP_ARRAY_SIZE(can_insts)];
+
+__attribute__((optimize("O0"), noinline))
+void common_hal_canio_can_construct(canio_can_obj_t *self, mcu_pin_obj_t *rx, mcu_pin_obj_t *tx, int baudrate, bool loopback, bool silent)
+{
+ mcu_pin_function_t *tx_function = mcu_find_pin_function(can_tx, tx, -1, MP_QSTR_tx);
+ int instance = tx_function->instance;
+
+ mcu_pin_function_t *rx_function = mcu_find_pin_function(can_rx, rx, instance, MP_QSTR_rx);
+
+ const uint32_t can_frequency = CONF_CAN0_FREQUENCY;
+
+#define DIV_ROUND(a, b) (((a) + (b)/2) / (b))
+#define DIV_ROUND_UP(a, b) (((a) + (b) - 1) / (b))
+
+ uint32_t clocks_per_bit = DIV_ROUND(can_frequency, baudrate);
+ uint32_t clocks_to_sample = DIV_ROUND(clocks_per_bit * 7, 8);
+ uint32_t clocks_after_sample = clocks_per_bit - clocks_to_sample;
+ uint32_t divisor = MAX(DIV_ROUND_UP(clocks_to_sample, 256), DIV_ROUND_UP(clocks_after_sample, 128));
+ if (divisor > 32) {
+ mp_raise_OSError(MP_EINVAL); // baudrate cannot be attained (16kHz or something is lower bound, should never happen)
+ }
+
+ gpio_set_pin_direction(tx_function->pin, GPIO_DIRECTION_OUT);
+ gpio_set_pin_function(tx_function->pin, tx_function->function);
+ common_hal_never_reset_pin(tx_function->obj);
+
+ if (rx_function) {
+ gpio_set_pin_direction(rx_function->pin, GPIO_DIRECTION_IN);
+ gpio_set_pin_function(rx_function->pin, rx_function->function);
+ common_hal_never_reset_pin(rx_function->obj);
+ }
+
+ self->tx_pin_number = common_hal_mcu_pin_number(tx);
+ self->rx_pin_number = rx ? common_hal_mcu_pin_number(rx) : COMMON_HAL_MCU_NO_PIN;
+ self->hw = can_insts[instance];
+ self->state = &can_state[instance];
+
+ self->loopback = loopback;
+
+ // Allow configuration change
+ hri_can_set_CCCR_INIT_bit(self->hw);
+ while (hri_can_get_CCCR_INIT_bit(self->hw) == 0) {
+ }
+ hri_can_set_CCCR_CCE_bit(self->hw);
+
+ if(instance == 0) {
+ hri_mclk_set_AHBMASK_CAN0_bit(MCLK);
+ hri_gclk_write_PCHCTRL_reg(GCLK, CAN0_GCLK_ID, CONF_GCLK_CAN0_SRC | (1 << GCLK_PCHCTRL_CHEN_Pos));
+
+ NVIC_DisableIRQ(CAN0_IRQn);
+ NVIC_ClearPendingIRQ(CAN0_IRQn);
+ NVIC_EnableIRQ(CAN0_IRQn);
+ hri_can_write_ILE_reg(self->hw, CAN_ILE_EINT0);
+#ifdef CAN1_GCLK_ID
+ } else if(instance == 1) {
+ hri_mclk_set_AHBMASK_CAN1_bit(MCLK);
+ hri_gclk_write_PCHCTRL_reg(GCLK, CAN1_GCLK_ID, CONF_GCLK_CAN1_SRC | (1 << GCLK_PCHCTRL_CHEN_Pos));
+
+ NVIC_DisableIRQ(CAN1_IRQn);
+ NVIC_ClearPendingIRQ(CAN1_IRQn);
+ NVIC_EnableIRQ(CAN1_IRQn);
+ hri_can_write_ILE_reg(self->hw, CAN_ILE_EINT0);
+#endif
+ }
+
+ self->hw->CCCR.bit.FDOE = 0; // neither FD nor Bit Rate Switch enabled
+ self->hw->CCCR.bit.BRSE = 0;
+
+ hri_can_write_MRCFG_reg(self->hw, CAN_MRCFG_QOS(CAN_MRCFG_QOS_DISABLE_Val)); // QoS disabled (no sensitive operation)
+
+ // A "nominal bit" is a header bit. With dual rate CAN FD, this is a slower rate
+ {
+ CAN_NBTP_Type btp = {
+ // 0 means "1 tq", but 2 is subtracted from NTSEG1 for the
+ // fixed 1 "SYNC" tq
+ .bit.NTSEG1 = DIV_ROUND(clocks_to_sample, divisor) - 2,
+ .bit.NTSEG2 = DIV_ROUND(clocks_after_sample, divisor) - 1,
+ .bit.NBRP = divisor - 1,
+ .bit.NSJW = DIV_ROUND(clocks_after_sample, divisor * 4),
+ };
+ hri_can_write_NBTP_reg(self->hw, btp.reg);
+ }
+
+ // we don't do the high bit rate yet, so do we need to set this register?
+ // hri_can_write_DBTP_reg(self->hw, ???);
+ // A "data bit" is a data bit :) with dul rate CAN FD, this is a higher
+ // rate. However, CAN FD is not implemented in CircuitPython, and this is the same rate as
+ // the "nominal rate".
+ {
+ CAN_DBTP_Type btp = {
+ .bit.DTSEG1 = DIV_ROUND(clocks_to_sample, divisor) - 1,
+ .bit.DTSEG2 = DIV_ROUND(clocks_after_sample, divisor) - 1,
+ .bit.DBRP = divisor - 1,
+ .bit.DSJW = DIV_ROUND(clocks_after_sample, divisor * 4),
+ };
+ hri_can_write_DBTP_reg(self->hw, btp.reg);
+ }
+
+ {
+ CAN_RXF0C_Type rxf = {
+ .bit.F0SA = (uint32_t)self->state->rx0_fifo,
+ .bit.F0S = COMMON_HAL_CANIO_RX_FIFO_SIZE,
+ };
+ hri_can_write_RXF0C_reg(self->hw, rxf.reg);
+ }
+
+ {
+ CAN_RXF1C_Type rxf = {
+ .bit.F1SA = (uint32_t)self->state->rx1_fifo,
+ .bit.F1S = COMMON_HAL_CANIO_RX_FIFO_SIZE,
+ };
+ hri_can_write_RXF1C_reg(self->hw, rxf.reg);
+ }
+
+ // All RX data has an 8 byte payload (max)
+ {
+ CAN_RXESC_Type esc = {
+ .bit.F0DS = CAN_RXESC_F0DS_DATA8_Val,
+ .bit.F1DS = CAN_RXESC_F1DS_DATA8_Val,
+ .bit.RBDS = CAN_RXESC_RBDS_DATA8_Val,
+ };
+ hri_can_write_RXESC_reg(self->hw, esc.reg);
+ }
+
+ // All TX data has an 8 byte payload (max)
+ {
+ CAN_TXESC_Type esc = {
+ .bit.TBDS = CAN_TXESC_TBDS_DATA8_Val,
+ };
+ hri_can_write_TXESC_reg(self->hw, esc.reg);
+ }
+
+ {
+ CAN_TXBC_Type bc = {
+ .bit.TBSA = (uint32_t)self->state->tx_fifo,
+ .bit.NDTB = COMMON_HAL_CANIO_TX_FIFO_SIZE,
+ .bit.TFQM = 0, // Messages are transmitted in the order submitted
+ };
+ hri_can_write_TXBC_reg(self->hw, bc.reg);
+ }
+
+ {
+ CAN_TXEFC_Type efc = {
+ .bit.EFS = 0,
+ };
+ hri_can_write_TXEFC_reg(self->hw, efc.reg);
+ }
+
+ {
+ CAN_GFC_Type gfc = {
+ .bit.RRFE = 1,
+ .bit.ANFS = CAN_GFC_ANFS_RXF0_Val,
+ .bit.ANFE = CAN_GFC_ANFE_REJECT_Val,
+ };
+ hri_can_write_GFC_reg(self->hw, gfc.reg);
+ }
+
+ {
+ CAN_SIDFC_Type dfc = {
+ .bit.LSS = COMMON_HAL_CANIO_RX_FILTER_SIZE,
+ .bit.FLSSA = (uint32_t)self->state->rx_filter
+ };
+ hri_can_write_SIDFC_reg(self->hw, dfc.reg);
+ }
+
+ {
+ CAN_XIDFC_Type dfc = {
+ .bit.LSE = 0,
+ };
+ hri_can_write_XIDFC_reg(self->hw, dfc.reg);
+ }
+
+ hri_can_write_XIDAM_reg(self->hw, CAN_XIDAM_RESETVALUE);
+
+// silent: The CAN is set in Bus Monitoring Mode by programming CCCR.MON to '1'. (tx pin unused)
+// external loopback: The CAN can be set in External Loop Back Mode by programming TEST.LBCK and CCCR.MON to '1'. (rx pin unused)
+// internal loopback (silent loopback): Internal Loop Back Mode is entered by programming bits TEST.LBCK and CCCR.MON to '1'. (tx, rx unused)
+ self->hw->CCCR.bit.MON = silent;
+ self->hw->CCCR.bit.TEST = loopback;
+ self->hw->TEST.bit.LBCK = loopback;
+
+ if(instance == 0) {
+ NVIC_DisableIRQ(CAN0_IRQn);
+ NVIC_ClearPendingIRQ(CAN0_IRQn);
+ NVIC_EnableIRQ(CAN0_IRQn);
+#ifdef CAN1_GCLK_ID
+ } else if(instance == 1) {
+ NVIC_DisableIRQ(CAN1_IRQn);
+ NVIC_ClearPendingIRQ(CAN1_IRQn);
+ NVIC_EnableIRQ(CAN1_IRQn);
+#endif
+ }
+
+ hri_can_write_ILE_reg(self->hw, CAN_ILE_EINT0);
+ // Prevent configuration change
+ hri_can_clear_CCCR_CCE_bit(self->hw);
+ hri_can_clear_CCCR_INIT_bit(self->hw);
+ while (hri_can_get_CCCR_INIT_bit(self->hw)) {
+ }
+
+ can_objs[instance] = self;
+}
+
+int common_hal_canio_can_loopback_get(canio_can_obj_t *self)
+{
+ return self->loopback;
+}
+
+int common_hal_canio_can_baudrate_get(canio_can_obj_t *self)
+{
+ return self->baudrate;
+}
+
+int common_hal_canio_can_transmit_error_count_get(canio_can_obj_t *self)
+{
+ return -1;
+}
+
+int common_hal_canio_can_receive_error_count_get(canio_can_obj_t *self)
+{
+ return -1;
+}
+
+int common_hal_canio_can_error_warning_state_count_get(canio_can_obj_t *self)
+{
+ return -1;
+}
+
+int common_hal_canio_can_error_passive_state_count_get(canio_can_obj_t *self)
+{
+ return -1;
+}
+
+int common_hal_canio_can_bus_off_state_count_get(canio_can_obj_t *self)
+{
+ return -1;
+}
+
+int common_hal_canio_can_state_get(canio_can_obj_t *self) {
+ return -1;
+}
+
+void common_hal_canio_can_send(canio_can_obj_t *self, canio_message_obj_t *message)
+{
+ // We have just one dedicated TX buffer, use it!
+ canio_can_fifo_t *ent = &self->state->tx_fifo[0];
+ ent->txb0.bit.ESI = false;
+ ent->txb0.bit.XTD = false;
+ ent->txb0.bit.RTR = message->rtr;
+ ent->txb0.bit.ID = message->id << 18; // short addresses are left-justified
+
+ ent->txb1.bit.MM = 0; // "message marker"
+ ent->txb1.bit.EFC = 0; // don't store fifo events to event queue
+ ent->txb1.bit.FDF = 0; // Classic CAN format
+ ent->txb1.bit.BRS = 0; // No bit rate switching
+ ent->txb1.bit.DLC = message->size;
+
+ if (!message->rtr) {
+ memcpy(ent->data, message->data, message->size);
+ }
+
+ // TX buffer add request
+ self->hw->TXBAR.reg = 1;
+
+ // wait 8ms (hard coded for now) for TX to occur
+ uint64_t deadline = port_get_raw_ticks(NULL) + 8;
+ while (port_get_raw_ticks(NULL) < deadline && !(self->hw->TXBTO.reg & 1)) {
+ RUN_BACKGROUND_TASKS;
+ }
+}
+
+bool common_hal_canio_can_deinited(canio_can_obj_t *self) {
+ return !self->hw;
+}
+
+void common_hal_canio_can_check_for_deinit(canio_can_obj_t *self) {
+ if(common_hal_canio_can_deinited(self)) {
+ raise_deinited_error();
+ }
+}
+
+void common_hal_canio_can_deinit(canio_can_obj_t *self)
+{
+ if (self->hw) {
+ hri_can_set_CCCR_INIT_bit(self->hw);
+ self->hw = 0;
+ }
+ if (self->rx_pin_number != COMMON_HAL_MCU_NO_PIN) {
+ reset_pin_number(self->rx_pin_number);
+ self->rx_pin_number = COMMON_HAL_MCU_NO_PIN;
+ }
+ if (self->tx_pin_number != COMMON_HAL_MCU_NO_PIN) {
+ reset_pin_number(self->tx_pin_number);
+ self->tx_pin_number = COMMON_HAL_MCU_NO_PIN;
+ }
+}
+
+void common_hal_canio_reset(void) {
+ memset(can_state, 0, sizeof(can_state));
+
+ for (size_t i=0; i<MP_ARRAY_SIZE(can_insts); i++) {
+ hri_can_set_CCCR_INIT_bit(can_insts[i]);
+ }
+
+ for (size_t i=0; i<MP_ARRAY_SIZE(can_objs); i++) {
+ if (can_objs[i]) {
+ common_hal_canio_can_deinit(can_objs[i]);
+ can_objs[i] = NULL;
+ }
+ }
+}
+
+
+STATIC void can_handler(int i) {
+ canio_can_obj_t *self = can_objs[i];
+ (void) self;
+
+ Can *hw = can_insts[i];
+ uint32_t ir = hri_can_read_IR_reg(hw);
+ /* Handle various interrupts */
+
+ /* Acknowledge interrupt */
+ hri_can_write_IR_reg(hw, ir);
+}
+
+__attribute__((used))
+void CAN0_Handler(void) {
+ can_handler(0);
+}
+
+#ifdef CAN1_GCLK_ID
+__attribute__((used))
+void CAN1_Handler(void) {
+ can_handler(1);
+}
+#endif
diff --git a/ports/atmel-samd/common-hal/_canio/CAN.h b/ports/atmel-samd/common-hal/_canio/CAN.h
new file mode 100644
index 000000000..0e637fc40
--- /dev/null
+++ b/ports/atmel-samd/common-hal/_canio/CAN.h
@@ -0,0 +1,62 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler for Adafruit Industries
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "py/obj.h"
+#include "component/can.h"
+#include "common-hal/microcontroller/Pin.h"
+#include "common-hal/_canio/__init__.h"
+#include "shared-module/_canio/Message.h"
+
+#define COMMON_HAL_CAN_RX_FIFO_LEN (2)
+#define COMMON_HAL_CAN_TX_FIFO_LEN (2)
+
+typedef struct {
+ mp_obj_base_t base;
+ Can *hw;
+ int baudrate;
+ uint8_t rx_pin_number, tx_pin_number;
+ bool loopback;
+ bool silent;
+ canio_can_state_t *state;
+ bool fifo0_in_use:1;
+ bool fifo1_in_use:1;
+} canio_can_obj_t;
+
+void common_hal_canio_can_construct(canio_can_obj_t *self, mcu_pin_obj_t *rx, mcu_pin_obj_t *tx, int baudrate, bool loopback, bool silent);
+int common_hal_canio_can_state_get(canio_can_obj_t *self);
+int common_hal_canio_can_baudrate_get(canio_can_obj_t *self);
+int common_hal_canio_can_transmit_error_count_get(canio_can_obj_t *self);
+int common_hal_canio_can_receive_error_count_get(canio_can_obj_t *self);
+int common_hal_canio_can_error_warning_state_count_get(canio_can_obj_t *self);
+int common_hal_canio_can_error_passive_state_count_get(canio_can_obj_t *self);
+int common_hal_canio_can_bus_off_state_count_get(canio_can_obj_t *self);
+void common_hal_canio_can_send(canio_can_obj_t *self, canio_message_obj_t *message);
+void common_hal_canio_can_deinit(canio_can_obj_t *self);
+void common_hal_canio_can_check_for_deinit(canio_can_obj_t *self);
+bool common_hal_canio_can_deinited(canio_can_obj_t *self);
+void common_hal_canio_reset(void);
diff --git a/ports/atmel-samd/common-hal/_canio/Listener.c b/ports/atmel-samd/common-hal/_canio/Listener.c
new file mode 100644
index 000000000..ddae61de9
--- /dev/null
+++ b/ports/atmel-samd/common-hal/_canio/Listener.c
@@ -0,0 +1,117 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler for Adafruit Industries
+ *
+ * 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 <math.h>
+#include <string.h>
+
+#include "py/obj.h"
+#include "py/runtime.h"
+
+#include "common-hal/_canio/__init__.h"
+#include "common-hal/_canio/Listener.h"
+#include "shared-bindings/util.h"
+#include "supervisor/shared/tick.h"
+
+void common_hal_canio_listener_construct(canio_listener_obj_t *self, canio_can_obj_t *can, size_t nmatch, canio_match_obj_t **matches, float timeout) {
+ if (nmatch) {
+ mp_raise_NotImplementedError(NULL);
+ }
+
+ if (!can->fifo0_in_use) {
+ self->fifo_idx = 0;
+ self->fifo = can->state->rx0_fifo;
+ self->hw = (canio_rxfifo_reg_t*)&can->hw->RXF0C;
+ can->hw->IR.reg = CAN_IR_RF0N | CAN_IR_RF0W | CAN_IR_RF0F | CAN_IR_RF0L;
+ can->fifo0_in_use = true;
+ } else if (!can->fifo1_in_use) {
+ self->fifo_idx = 1;
+ self->fifo = can->state->rx1_fifo;
+ self->hw = (canio_rxfifo_reg_t*)&can->hw->RXF1C;
+ can->fifo1_in_use = true;
+ can->hw->IR.reg = CAN_IR_RF1N | CAN_IR_RF1W | CAN_IR_RF1F | CAN_IR_RF1L;
+ } else {
+ mp_raise_ValueError(translate("All RX FIFOs in use"));
+ }
+
+ self->can = can;
+ common_hal_canio_listener_set_timeout(self, timeout);
+}
+
+void common_hal_canio_listener_set_timeout(canio_listener_obj_t *self, float timeout) {
+ self->timeout_ms = (int)MICROPY_FLOAT_C_FUN(ceil)(timeout * 1000);
+}
+
+float common_hal_canio_listener_get_timeout(canio_listener_obj_t *self) {
+ return self->timeout_ms / 1000.0f;
+}
+
+void common_hal_canio_listener_check_for_deinit(canio_listener_obj_t *self) {
+ if(!self->can) {
+ raise_deinited_error();
+ }
+ common_hal_canio_can_check_for_deinit(self->can);
+}
+
+int common_hal_canio_listener_in_waiting(canio_listener_obj_t *self) {
+ return self->hw->RXFS.bit.F0FL;
+}
+
+bool common_hal_canio_listener_readinto(canio_listener_obj_t *self, canio_message_obj_t *message) {
+ if (!common_hal_canio_listener_in_waiting(self)) {
+ uint64_t deadline = supervisor_ticks_ms64() + self->timeout_ms;
+ do {
+ if(supervisor_ticks_ms64() > deadline) {
+ return false;
+ }
+ } while (!common_hal_canio_listener_in_waiting(self));
+ }
+ int index = self->hw->RXFS.bit.F0GI;
+ canio_can_fifo_t *hw_message = &self->fifo[index];
+ message->id = hw_message->rxb0.bit.ID >> 18; // short addresses are left-justified
+ message->rtr = hw_message->rxb0.bit.RTR;
+ message->size = hw_message->rxb1.bit.DLC;
+ if(!message->rtr) {
+ memcpy(message->data, hw_message->data, message->size);
+ }
+ self->hw->RXFA.bit.F0AI = index;
+ return true;
+}
+
+void common_hal_canio_listener_deinit(canio_listener_obj_t *self) {
+ // free our FIFO, clear our matches, SOMETHING
+ if(self->can) {
+ if(self->fifo_idx == 0) {
+ self->can->fifo0_in_use = false;
+ }
+ if(self->fifo_idx == 1) {
+ self->can->fifo1_in_use = false;
+ }
+ }
+ self->fifo_idx = -1;
+ self->fifo = NULL;
+ self->can = NULL;
+ self->hw = NULL;
+}
diff --git a/ports/atmel-samd/common-hal/_canio/Listener.h b/ports/atmel-samd/common-hal/_canio/Listener.h
new file mode 100644
index 000000000..cc6770e6e
--- /dev/null
+++ b/ports/atmel-samd/common-hal/_canio/Listener.h
@@ -0,0 +1,53 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler for Adafruit Industries
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "common-hal/_canio/CAN.h"
+#include "shared-module/_canio/Match.h"
+
+typedef struct {
+ __IO CAN_RXF0C_Type RXFC; /**< \brief (R/W 32) Rx FIFO n Configuration */
+ __I CAN_RXF0S_Type RXFS; /**< \brief (R/ 32) Rx FIFO n Status */
+ __IO CAN_RXF0A_Type RXFA; /**< \brief (R/W 32) Rx FIFO n Acknowledge */
+} canio_rxfifo_reg_t;
+
+typedef struct {
+ mp_obj_base_t base;
+ canio_can_obj_t *can;
+ canio_can_fifo_t *fifo;
+ canio_rxfifo_reg_t *hw;
+ uint32_t timeout_ms;
+ uint8_t fifo_idx;
+} canio_listener_obj_t;
+
+void common_hal_canio_listener_construct(canio_listener_obj_t *self, canio_can_obj_t *can, size_t nmatch, canio_match_obj_t **matches, float timeout);
+void common_hal_canio_listener_check_for_deinit(canio_listener_obj_t *self);
+void common_hal_canio_listener_deinit(canio_listener_obj_t *self);
+bool common_hal_canio_listener_readinto(canio_listener_obj_t *self, canio_message_obj_t *message);
+int common_hal_canio_listener_in_waiting(canio_listener_obj_t *self);
+float common_hal_canio_listener_get_timeout(canio_listener_obj_t *self);
+void common_hal_canio_listener_set_timeout(canio_listener_obj_t *self, float timeout);
diff --git a/ports/atmel-samd/common-hal/_canio/__init__.c b/ports/atmel-samd/common-hal/_canio/__init__.c
new file mode 100644
index 000000000..7932bfc2d
--- /dev/null
+++ b/ports/atmel-samd/common-hal/_canio/__init__.c
@@ -0,0 +1,25 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler for Adafruit Industries
+ *
+ * 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.
+ */
diff --git a/ports/atmel-samd/common-hal/_canio/__init__.h b/ports/atmel-samd/common-hal/_canio/__init__.h
new file mode 100644
index 000000000..429369e18
--- /dev/null
+++ b/ports/atmel-samd/common-hal/_canio/__init__.h
@@ -0,0 +1,62 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler for Adafruit Industries
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "hal/utils/include/utils.h"
+#include "component/can.h"
+
+#define COMMON_HAL_CANIO_MAX_MESSAGE_LENGTH (8)
+#define COMMON_HAL_CANIO_RX_FIFO_SIZE (3)
+#define COMMON_HAL_CANIO_RX_FILTER_SIZE (4)
+#define COMMON_HAL_CANIO_TX_FIFO_SIZE (1)
+
+typedef struct canio_listener canio_listener_t;
+typedef struct canio_can canio_can_t;
+
+typedef struct {
+ union {
+ CAN_RXBE_0_Type rxb0;
+ CAN_TXBE_0_Type txb0;
+ CAN_RXF0E_0_Type rxf0;
+ };
+ union {
+ CAN_RXBE_1_Type rxb1;
+ CAN_TXBE_1_Type txb1;
+ CAN_RXF0E_1_Type rxf1;
+ };
+ COMPILER_ALIGNED(4)
+ uint8_t data[COMMON_HAL_CANIO_MAX_MESSAGE_LENGTH];
+} canio_can_fifo_t;
+
+typedef uint32_t canio_can_filter_t;
+
+typedef struct {
+ canio_can_fifo_t tx_fifo[COMMON_HAL_CANIO_TX_FIFO_SIZE];
+ canio_can_fifo_t rx0_fifo[COMMON_HAL_CANIO_RX_FIFO_SIZE];
+ canio_can_fifo_t rx1_fifo[COMMON_HAL_CANIO_RX_FIFO_SIZE];
+ canio_can_filter_t rx_filter[COMMON_HAL_CANIO_RX_FILTER_SIZE];
+} canio_can_state_t;
diff --git a/ports/atmel-samd/tools/mkcandata.py b/ports/atmel-samd/tools/mkcandata.py
new file mode 100755
index 000000000..9668d2208
--- /dev/null
+++ b/ports/atmel-samd/tools/mkcandata.py
@@ -0,0 +1,31 @@
+#!/usr/bin/python3
+
+def defines(name, suffix):
+ print(f'mcu_pin_function_t {name} [] = {{')
+ for instance in (0, 1):
+ for function in 'HI':
+ for port in 'ABCD':
+ for idx in range(32):
+ pin = f'P{port}{idx:02d}'
+ pinmux = f'PINMUX_{pin}{function}_CAN{instance}_{suffix}'
+ print(f'''\
+#if defined({pinmux}) && ! defined(IGNORE_PIN_{pin})
+ {{&pin_{pin}, {instance}, PIN_{pin}, {pinmux} & 0xffff}},
+#endif''')
+ print(f'{{NULL, 0, 0}}')
+ print(f'}};')
+ print()
+
+print('''\
+#include <stdint.h>
+#include "py/obj.h"
+#include "sam.h"
+#include "samd/pins.h"
+#include "mpconfigport.h"
+#include "atmel_start_pins.h"
+#include "hal/include/hal_gpio.h"
+#include "common-hal/microcontroller/Pin.h"
+''')
+
+defines('can_rx', 'RX')
+defines('can_tx', 'TX')
diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk
index 6e98af868..2614dc011 100644
--- a/py/circuitpy_defns.mk
+++ b/py/circuitpy_defns.mk
@@ -142,6 +142,9 @@ endif
ifeq ($(CIRCUITPY_CAMERA),1)
SRC_PATTERNS += camera/%
endif
+ifeq ($(CIRCUITPY_CANIO),1)
+SRC_PATTERNS += _canio/%
+endif
ifeq ($(CIRCUITPY_COUNTIO),1)
SRC_PATTERNS += countio/%
endif
@@ -315,6 +318,9 @@ SRC_COMMON_HAL_ALL = \
busio/__init__.c \
camera/__init__.c \
camera/Camera.c \
+ _canio/CAN.c \
+ _canio/Listener.c \
+ _canio/__init__.c \
countio/Counter.c \
countio/__init__.c \
digitalio/DigitalInOut.c \
@@ -384,6 +390,7 @@ $(filter $(SRC_PATTERNS), \
_bleio/Address.c \
_bleio/Attribute.c \
_bleio/ScanEntry.c \
+ _canio/Match.c \
_eve/__init__.c \
camera/ImageFormat.c \
digitalio/Direction.c \
@@ -402,6 +409,8 @@ SRC_SHARED_MODULE_ALL = \
_bleio/Attribute.c \
_bleio/ScanEntry.c \
_bleio/ScanResults.c \
+ _canio/Match.c \
+ _canio/Message.c \
_eve/__init__.c \
_pixelbuf/PixelBuf.c \
_pixelbuf/__init__.c \
diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h
index 4272de2ec..d1b12f354 100644
--- a/py/circuitpy_mpconfig.h
+++ b/py/circuitpy_mpconfig.h
@@ -336,6 +336,13 @@ extern const struct _mp_obj_module_t camera_module;
#define CAMERA_MODULE
#endif
+#if CIRCUITPY_CANIO
+extern const struct _mp_obj_module_t canio_module;
+#define CANIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR__canio), (mp_obj_t)&canio_module },
+#else
+#define CANIO_MODULE
+#endif
+
#if CIRCUITPY_COUNTIO
extern const struct _mp_obj_module_t countio_module;
#define COUNTIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_countio), (mp_obj_t)&countio_module },
@@ -766,6 +773,7 @@ extern const struct _mp_obj_module_t wifi_module;
BOARD_MODULE \
BUSIO_MODULE \
CAMERA_MODULE \
+ CANIO_MODULE \
COUNTIO_MODULE \
DIGITALIO_MODULE \
DISPLAYIO_MODULE \
diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk
index 54bdefc6d..9b9bd83b4 100644
--- a/py/circuitpy_mpconfig.mk
+++ b/py/circuitpy_mpconfig.mk
@@ -93,6 +93,9 @@ CFLAGS += -DCIRCUITPY_BUSIO=$(CIRCUITPY_BUSIO)
CIRCUITPY_CAMERA ?= 0
CFLAGS += -DCIRCUITPY_CAMERA=$(CIRCUITPY_CAMERA)
+CIRCUITPY_CANIO ?= 0
+CFLAGS += -DCIRCUITPY_CANIO=$(CIRCUITPY_CANIO)
+
CIRCUITPY_DIGITALIO ?= 1
CFLAGS += -DCIRCUITPY_DIGITALIO=$(CIRCUITPY_DIGITALIO)
diff --git a/shared-bindings/_canio/CAN.c b/shared-bindings/_canio/CAN.c
new file mode 100644
index 000000000..4c7ae3650
--- /dev/null
+++ b/shared-bindings/_canio/CAN.c
@@ -0,0 +1,338 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler for Adafruit Industries
+ *
+ * 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 "common-hal/_canio/CAN.h"
+#include "common-hal/_canio/Listener.h"
+#include "shared-bindings/_canio/CAN.h"
+#include "shared-bindings/_canio/Listener.h"
+#include "shared-bindings/_canio/Match.h"
+#include "shared-bindings/_canio/Message.h"
+#include "shared-bindings/microcontroller/Pin.h"
+
+#include "py/objproperty.h"
+#include "py/runtime.h"
+
+//|
+//| class CAN:
+//| """CAN bus protocol"""
+//|
+//| def __init__(self,
+//| rx: microcontroller.Pin,
+//| tx: Optional[microcontroller.Pin]=None,
+//| *,
+//| baudrate: int = 250000,
+//| loopback: bool = False,
+//| ):
+//| """A common shared-bus protocol. The rx and tx pins are generally
+//| connected to a transceiver which controls the H and L pins on a shared
+//| bus.
+//|
+//| :param ~microcontrller.Pin rx: the pin to receive with.
+//| :param ~microcontrller.Pin tx: the pin to transmit with, or None if the peripheral should operate in "silent" mode.
+//| :param int baudrate: The bit rate of the bus in Hz. All devices on the bus must agree on this value.
+//| :param bool loopback: True if the peripheral will be operated in loopback mode.
+//| """
+//| ...
+//|
+//## auto_restart: bool = False, # Whether to restart communications after entering bus-off state
+//## sample_point: float = .875, # When to sample within bit time (0.0-1.0)
+STATIC mp_obj_t canio_can_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ enum { ARG_rx, ARG_tx, ARG_baudrate, ARG_loopback, ARG_silent, NUM_ARGS };
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_rx, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = 0} },
+ { MP_QSTR_tx, MP_ARG_OBJ, {.u_obj = 0} },
+ { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 250000} },
+ { MP_QSTR_loopback, MP_ARG_BOOL, {.u_bool = false} },
+ { MP_QSTR_silent, MP_ARG_BOOL, {.u_bool = false} },
+ };
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ MP_STATIC_ASSERT( MP_ARRAY_SIZE(allowed_args) == NUM_ARGS );
+
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+mp_printf(&mp_plat_print, "ARG_rx=%d args[ARG_rx].u_obj=%p\n", ARG_rx, args[ARG_rx].u_obj);
+mp_printf(&mp_plat_print, "ARG_tx=%d args[ARG_tx].u_obj=%p\n", ARG_tx, args[ARG_tx].u_obj);
+
+ mcu_pin_obj_t *rx_pin = validate_obj_is_free_pin(args[ARG_rx].u_obj);
+mp_printf(&mp_plat_print, "rx_pin=%p\n", rx_pin);
+ mcu_pin_obj_t *tx_pin = validate_obj_is_free_pin_or_none(args[ARG_tx].u_obj);
+mp_printf(&mp_plat_print, "tx_pin=%p\n", tx_pin);
+
+ canio_can_obj_t *self = m_new_obj(canio_can_obj_t);
+ self->base.type = &canio_can_type;
+ common_hal_canio_can_construct(self, rx_pin, tx_pin, args[ARG_baudrate].u_int, args[ARG_loopback].u_bool, args[ARG_silent].u_bool);
+
+ return MP_OBJ_FROM_PTR(self);
+}
+
+
+//| baudrate: int
+//| """The baud rate(read-only)"""
+//|
+STATIC mp_obj_t canio_can_baudrate_get(mp_obj_t self_in) {
+ canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ common_hal_canio_can_check_for_deinit(self);
+ return MP_OBJ_NEW_SMALL_INT(common_hal_canio_can_baudrate_get(self));
+}
+MP_DEFINE_CONST_FUN_OBJ_1(canio_can_baudrate_get_obj, canio_can_baudrate_get);
+
+STATIC const mp_obj_property_t canio_can_baudrate_obj = {
+ .base.type = &mp_type_property,
+ .proxy = {(mp_obj_t)&canio_can_baudrate_get_obj,
+ (mp_obj_t)mp_const_none,
+ (mp_obj_t)mp_const_none},
+};
+
+//| state: State
+//| """The status of the hardware (read-only)"""
+//|
+STATIC mp_obj_t canio_can_state_get(mp_obj_t self_in) {
+ canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ common_hal_canio_can_check_for_deinit(self);
+ return MP_OBJ_NEW_SMALL_INT(common_hal_canio_can_state_get(self));
+}
+MP_DEFINE_CONST_FUN_OBJ_1(canio_can_state_get_obj, canio_can_state_get);
+
+STATIC const mp_obj_property_t canio_can_state_obj = {
+ .base.type = &mp_type_property,
+ .proxy = {(mp_obj_t)&canio_can_state_get_obj,
+ (mp_obj_t)mp_const_none,
+ (mp_obj_t)mp_const_none},
+};
+
+//| transmit_error_count: int
+//| """The number of transmit errors (read-only). Increased for a detected transmission error, decreased for successful transmission. Limited to the range from 0 to 255 inclusive. Also called TEC."""
+//|
+STATIC mp_obj_t canio_can_transmit_error_count_get(mp_obj_t self_in) {
+ canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ common_hal_canio_can_check_for_deinit(self);
+ return MP_OBJ_NEW_SMALL_INT(common_hal_canio_can_transmit_error_count_get(self));
+}
+MP_DEFINE_CONST_FUN_OBJ_1(canio_can_transmit_error_count_get_obj, canio_can_transmit_error_count_get);
+
+STATIC const mp_obj_property_t canio_can_transmit_error_count_obj = {
+ .base.type = &mp_type_property,
+ .proxy = {(mp_obj_t)&canio_can_transmit_error_count_get_obj,
+ (mp_obj_t)mp_const_none,
+ (mp_obj_t)mp_const_none},
+};
+
+//| receive_error_count: int
+//| """The number of receive errors (read-only). Increased for a detected reception error, decreased for successful reception. Limited to the range from 0 to 255 inclusive. Also called REC."""
+//|
+STATIC mp_obj_t canio_can_receive_error_count_get(mp_obj_t self_in) {
+ canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ common_hal_canio_can_check_for_deinit(self);
+ return MP_OBJ_NEW_SMALL_INT(common_hal_canio_can_receive_error_count_get(self));
+}
+MP_DEFINE_CONST_FUN_OBJ_1(canio_can_receive_error_count_get_obj, canio_can_receive_error_count_get);
+
+STATIC const mp_obj_property_t canio_can_receive_error_count_obj = {
+ .base.type = &mp_type_property,
+ .proxy = {(mp_obj_t)&canio_can_receive_error_count_get_obj,
+ (mp_obj_t)mp_const_none,
+ (mp_obj_t)mp_const_none},
+};
+
+//| error_warning_state_count: int
+//| """The number of times the controller enterted the Error Warning state (read-only). This number wraps around to 0 after an implementation-defined number of errors."""
+//|
+STATIC mp_obj_t canio_can_error_warning_state_count_get(mp_obj_t self_in) {
+ canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ common_hal_canio_can_check_for_deinit(self);
+ return MP_OBJ_NEW_SMALL_INT(common_hal_canio_can_error_warning_state_count_get(self));
+}
+MP_DEFINE_CONST_FUN_OBJ_1(canio_can_error_warning_state_count_get_obj, canio_can_error_warning_state_count_get);
+
+STATIC const mp_obj_property_t canio_can_error_warning_state_count_obj = {
+ .base.type = &mp_type_property,
+ .proxy = {(mp_obj_t)&canio_can_error_warning_state_count_get_obj,
+ (mp_obj_t)mp_const_none,
+ (mp_obj_t)mp_const_none},
+};
+
+//| error_passive_state_count: int
+//| """The number of times the controller enterted the Error Passive state (read-only). This number wraps around to 0 after an implementation-defined number of errors."""
+//|
+STATIC mp_obj_t canio_can_error_passive_state_count_get(mp_obj_t self_in) {
+ canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ common_hal_canio_can_check_for_deinit(self);
+ return MP_OBJ_NEW_SMALL_INT(common_hal_canio_can_error_passive_state_count_get(self));
+}
+MP_DEFINE_CONST_FUN_OBJ_1(canio_can_error_passive_state_count_get_obj, canio_can_error_passive_state_count_get);
+
+STATIC const mp_obj_property_t canio_can_error_passive_state_count_obj = {
+ .base.type = &mp_type_property,
+ .proxy = {(mp_obj_t)&canio_can_error_passive_state_count_get_obj,
+ (mp_obj_t)mp_const_none,
+ (mp_obj_t)mp_const_none},
+};
+
+//| bus_off_state_count: int
+//| """The number of times the controller enterted the Bus Off state (read-only). This number wraps around to 0 after an implementation-defined number of errors."""
+//|
+STATIC mp_obj_t canio_can_bus_off_state_count_get(mp_obj_t self_in) {
+ canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ common_hal_canio_can_check_for_deinit(self);
+ return MP_OBJ_NEW_SMALL_INT(common_hal_canio_can_bus_off_state_count_get(self));
+}
+MP_DEFINE_CONST_FUN_OBJ_1(canio_can_bus_off_state_count_get_obj, canio_can_bus_off_state_count_get);
+
+STATIC const mp_obj_property_t canio_can_bus_off_state_count_obj = {
+ .base.type = &mp_type_property,
+ .proxy = {(mp_obj_t)&canio_can_bus_off_state_count_get_obj,
+ (mp_obj_t)mp_const_none,
+ (mp_obj_t)mp_const_none},
+};
+
+#if 0
+//| # pending_tx_count: int
+//| # """The number of messages waiting to be transmitted. (read-only)"""
+#endif
+
+//| def listen(filters: Optional[Sequence[Filter]]=None, *, timeout: float=10) -> Listener:
+//| """Start receiving messages that match any one of the filters.
+//| Creating a listener is an expensive operation and can interfere with reception of messages by other listeners.
+//| There is an implementation-defined maximum number of listeners and limit to the complexity of the filters.
+//| If the hardware cannot support all the requested filters, a ValueError is raised. Note that generally there are some number of hardware filters shared among all fifos.
+//| A message can be received by at most one Listener.
+//| An empty filter list causes all messages to be accepted.
+//| Timeout dictates how long readinto, read and next() will block.
+//| Readinto will return false(), read will return None, and next() will raise StopIteration."""
+//| ...
+//|
+STATIC mp_obj_t canio_can_listen(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ canio_can_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
+ common_hal_canio_can_check_for_deinit(self);
+
+ enum { ARG_match, ARG_timeout, NUM_ARGS };
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_match, MP_ARG_OBJ, {.u_obj = 0} },
+ { MP_QSTR_timeout, MP_ARG_OBJ, {.u_obj = 0} },
+ };
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ MP_STATIC_ASSERT( MP_ARRAY_SIZE(allowed_args) == NUM_ARGS );
+
+ mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ size_t nmatch = 0;
+ mp_obj_t *match_objects = NULL;
+
+ if (args[ARG_match].u_obj) {
+ mp_obj_get_array(args[ARG_match].u_obj, &nmatch, &match_objects);
+ }
+
+ canio_match_obj_t *matches[nmatch];
+ for (size_t i=0; i<nmatch; i++) {
+ mp_obj_type_t *type = mp_obj_get_type(match_objects[i]);
+ if(type != &canio_match_type) {
+ mp_raise_TypeError_varg(translate("expected '%q' but got '%q'"), MP_QSTR_Match, type->name);
+ }
+ matches[i] = MP_OBJ_TO_PTR(match_objects[i]);
+ }
+
+ float timeout = args[ARG_timeout].u_obj ? mp_obj_get_float(args[ARG_timeout].u_obj) : 10.0f;
+ canio_listener_obj_t *listener = m_new_obj(canio_listener_obj_t);
+ listener->base.type = &canio_listener_type;
+ common_hal_canio_listener_construct(listener, self, nmatch, matches, timeout);
+ return listener;
+}
+MP_DEFINE_CONST_FUN_OBJ_KW(canio_can_listen_obj, 1, canio_can_listen);
+
+//| def send(message: Message) -> None:
+//| """Send a message on the bus with the given data and id.
+//| If the message could not be sent due to a full fifo or a bus error condition, RuntimeError is raised.
+//| """
+//| ...
+//|
+STATIC mp_obj_t canio_can_send(mp_obj_t self_in, mp_obj_t message_in) {
+ canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ common_hal_canio_can_check_for_deinit(self);
+ mp_obj_type_t *message_type = mp_obj_get_type(message_in);
+ if (message_type != &canio_message_type) {
+ mp_raise_TypeError_varg(translate("expected '%q' but got '%q'"), MP_QSTR_Message, message_type->name);
+ }
+
+ canio_message_obj_t *message = message_in;
+ common_hal_canio_can_send(self, message);
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_2(canio_can_send_obj, canio_can_send);
+
+//| def deinit(self) -> None:
+//| """Deinitialize this object, freeing its hardware resources"""
+//| ...
+//|
+STATIC mp_obj_t canio_can_deinit(mp_obj_t self_in) {
+ canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ common_hal_canio_can_deinit(self);
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(canio_can_deinit_obj, canio_can_deinit);
+
+//| def __enter__(self) -> CAN:
+//| """Returns self, to allow the object to be used in a `with` statement for resource control"""
+//| ...
+//|
+STATIC mp_obj_t canio_can_enter(mp_obj_t self_in) {
+ canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ common_hal_canio_can_check_for_deinit(self);
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(canio_can_enter_obj, canio_can_enter);
+
+//| def __exit__(self, unused1, unused2, unused3) -> None:
+//| """Calls deinit()"""
+//| ...
+STATIC mp_obj_t canio_can_exit(size_t num_args, const mp_obj_t args[]) {
+ canio_can_obj_t *self = MP_OBJ_TO_PTR(args[0]);
+ common_hal_canio_can_deinit(self);
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(canio_can_exit_obj, 4, 4, canio_can_exit);
+
+STATIC const mp_rom_map_elem_t canio_can_locals_dict_table[] = {
+ { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&canio_can_enter_obj) },
+ { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&canio_can_exit_obj) },
+ { MP_ROM_QSTR(MP_QSTR_bus_off_state_count), MP_ROM_PTR(&canio_can_bus_off_state_count_obj) },
+ { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&canio_can_deinit_obj) },
+ { MP_ROM_QSTR(MP_QSTR_error_passive_state_count), MP_ROM_PTR(&canio_can_error_passive_state_count_obj) },
+ { MP_ROM_QSTR(MP_QSTR_error_warning_state_count), MP_ROM_PTR(&canio_can_error_warning_state_count_obj) },
+ { MP_ROM_QSTR(MP_QSTR_listen), MP_ROM_PTR(&canio_can_listen_obj) },
+ { MP_ROM_QSTR(MP_QSTR_receive_error_count), MP_ROM_PTR(&canio_can_receive_error_count_obj) },
+ { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&canio_can_send_obj) },
+ { MP_ROM_QSTR(MP_QSTR_state), MP_ROM_PTR(&canio_can_state_obj) },
+ { MP_ROM_QSTR(MP_QSTR_baudrate), MP_ROM_PTR(&canio_can_baudrate_obj) },
+ { MP_ROM_QSTR(MP_QSTR_transmit_error_count), MP_ROM_PTR(&canio_can_transmit_error_count_obj) },
+};
+STATIC MP_DEFINE_CONST_DICT(canio_can_locals_dict, canio_can_locals_dict_table);
+
+const mp_obj_type_t canio_can_type = {
+ { &mp_type_type },
+ .name = MP_QSTR_CAN,
+ .make_new = canio_can_make_new,
+ .locals_dict = (mp_obj_t)&canio_can_locals_dict,
+};
diff --git a/shared-bindings/_canio/CAN.h b/shared-bindings/_canio/CAN.h
new file mode 100644
index 000000000..81040cf23
--- /dev/null
+++ b/shared-bindings/_canio/CAN.h
@@ -0,0 +1,31 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler for Adafruit Industries
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "py/obj.h"
+
+extern const mp_obj_type_t canio_can_type;
diff --git a/shared-bindings/_canio/Listener.c b/shared-bindings/_canio/Listener.c
new file mode 100644
index 000000000..7f83c735f
--- /dev/null
+++ b/shared-bindings/_canio/Listener.c
@@ -0,0 +1,197 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler for Adafruit Industries
+ *
+ * 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 "shared-bindings/_canio/Listener.h"
+#include "shared-bindings/_canio/Message.h"
+#include "common-hal/_canio/Listener.h"
+
+#include "py/runtime.h"
+#include "py/objproperty.h"
+
+//| class Listener:
+//| """Listens for CAN message
+//|
+//| _canio.Listener is not constructed directly, but instead by calling the
+//| Listen method of a _canio.CAN object."""
+//|
+
+//| def read(self) -> Optional[Message]:
+//| """Returns a message, after waiting up to self.timeout seconds
+//|
+//| If no message is received in time, None is returned. Otherwise,
+//| a Message is returned."""
+//| ...
+//|
+STATIC mp_obj_t canio_listener_read(mp_obj_t self_in) {
+ canio_listener_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ common_hal_canio_listener_check_for_deinit(self);
+
+ canio_message_obj_t *message = m_new_obj(canio_message_obj_t);
+ self->base.type = &canio_message_type;
+
+ if (common_hal_canio_listener_readinto(self, message)) {
+ return message;
+ } else {
+ m_free(message); // message did not escape into vm
+ }
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(canio_listener_read_obj, canio_listener_read);
+
+//| def readinto(self, message: Message) -> bool:
+//| """Returns a message, after waiting up to self.timeout seconds
+//|
+//| Returns True (and modifies message) if a message was received,
+//| False otherwise."""
+//| ...
+//|
+STATIC mp_obj_t canio_listener_readinto(mp_obj_t self_in, mp_obj_t message) {
+ canio_listener_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ mp_obj_type_t *type = mp_obj_get_type(message);
+ if (type != &canio_message_type) {
+ mp_raise_TypeError_varg(translate("expected '%q' but got '%q'"), MP_QSTR_Message, type->name);
+ }
+ common_hal_canio_listener_check_for_deinit(self);
+ return mp_obj_new_bool(common_hal_canio_listener_readinto(self, message));
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(canio_listener_readinto_obj, canio_listener_readinto);
+
+//| def in_waiting(self) -> int:
+//| """Returns the number of messages waiting"""
+//| ...
+//|
+STATIC mp_obj_t canio_listener_in_waiting(mp_obj_t self_in) {
+ canio_listener_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ common_hal_canio_listener_check_for_deinit(self);
+ return MP_OBJ_NEW_SMALL_INT(common_hal_canio_listener_in_waiting(self));
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(canio_listener_in_waiting_obj, canio_listener_in_waiting);
+
+//| def __iter__(self):
+//| """Returns self, unless the object is deinitialized"""
+//| ...
+//|
+STATIC mp_obj_t canio_listener_iter(mp_obj_t self_in) {
+ canio_listener_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ common_hal_canio_listener_check_for_deinit(self);
+ return self;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(canio_listener_iter_obj, canio_listener_iter);
+
+//| def __next__(self):
+//| """Returns the next waiting message, if one is available
+//|
+//| If the object is deinitialized, raises ValueError.
+//|
+//| If no message is waiting raises StopIteration"""
+//| ...
+//|
+STATIC mp_obj_t canio_listener_next(mp_obj_t self_in) {
+ canio_listener_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ common_hal_canio_listener_check_for_deinit(self);
+ if(common_hal_canio_listener_in_waiting(self)) {
+ return canio_listener_read(self_in);
+ }
+ return self;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(canio_listener_next_obj, canio_listener_next);
+
+//| def deinit(self) -> None:
+//| """Deinitialize this object, freeing its hardware resources"""
+//| ...
+//|
+STATIC mp_obj_t canio_listener_deinit(mp_obj_t self_in) {
+ canio_listener_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ common_hal_canio_listener_deinit(self);
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(canio_listener_deinit_obj, canio_listener_deinit);
+
+//| def __enter__(self) -> CAN:
+//| """Returns self, to allow the object to be used in a `with` statement for resource control"""
+//| ...
+//|
+STATIC mp_obj_t canio_listener_enter(mp_obj_t self_in) {
+ canio_listener_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ common_hal_canio_listener_check_for_deinit(self);
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(canio_listener_enter_obj, canio_listener_enter);
+
+//| def __exit__(self, unused1, unused2, unused3) -> None:
+//| """Calls deinit()"""
+//| ...
+STATIC mp_obj_t canio_listener_exit(size_t num_args, const mp_obj_t args[]) {
+ canio_listener_obj_t *self = MP_OBJ_TO_PTR(args[0]);
+ common_hal_canio_listener_deinit(self);
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(canio_listener_exit_obj, 4, 4, canio_listener_exit);
+
+
+//| timeout : float
+STATIC mp_obj_t canio_listener_timeout_get(mp_obj_t self_in) {
+ canio_listener_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ common_hal_canio_listener_check_for_deinit(self);
+ return mp_obj_new_float(common_hal_canio_listener_get_timeout(self));
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(canio_listener_timeout_get_obj, canio_listener_timeout_get);
+
+STATIC mp_obj_t canio_listener_timeout_set(mp_obj_t self_in, mp_obj_t timeout_in) {
+ canio_listener_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ common_hal_canio_listener_check_for_deinit(self);
+ common_hal_canio_listener_set_timeout(self, mp_obj_get_float(timeout_in));
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(canio_listener_timeout_set_obj, canio_listener_timeout_set);
+
+STATIC const mp_obj_property_t canio_listener_timeout_obj = {
+ .base.type = &mp_type_property,
+ .proxy = {(mp_obj_t)&canio_listener_timeout_get_obj,
+ (mp_obj_t)&canio_listener_timeout_set_obj,
+ (mp_obj_t)mp_const_none},
+};
+
+
+
+STATIC const mp_rom_map_elem_t canio_listener_locals_dict_table[] = {
+ { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&canio_listener_enter_obj) },
+ { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&canio_listener_exit_obj) },
+ { MP_ROM_QSTR(MP_QSTR___iter__), MP_ROM_PTR(&canio_listener_iter_obj) },
+ { MP_ROM_QSTR(MP_QSTR___next__), MP_ROM_PTR(&canio_listener_next_obj) },
+ { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&canio_listener_deinit_obj) },
+ { MP_ROM_QSTR(MP_QSTR_in_waiting), MP_ROM_PTR(&canio_listener_in_waiting_obj) },
+ { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&canio_listener_read_obj) },
+ { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&canio_listener_readinto_obj) },
+ { MP_ROM_QSTR(MP_QSTR_timeout), MP_ROM_PTR(&canio_listener_timeout_obj) },
+};
+STATIC MP_DEFINE_CONST_DICT(canio_listener_locals_dict, canio_listener_locals_dict_table);
+
+const mp_obj_type_t canio_listener_type = {
+ { &mp_type_type },
+ .name = MP_QSTR_Listener,
+ .locals_dict = (mp_obj_dict_t*)&canio_listener_locals_dict,
+};
diff --git a/shared-bindings/_canio/Listener.h b/shared-bindings/_canio/Listener.h
new file mode 100644
index 000000000..eaa3490df
--- /dev/null
+++ b/shared-bindings/_canio/Listener.h
@@ -0,0 +1,31 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler for Adafruit Industries
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "py/obj.h"
+
+extern const mp_obj_type_t canio_listener_type;
diff --git a/shared-bindings/_canio/Match.c b/shared-bindings/_canio/Match.c
new file mode 100644
index 000000000..6c32aa539
--- /dev/null
+++ b/shared-bindings/_canio/Match.c
@@ -0,0 +1,140 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler for Adafruit Industries
+ *
+ * 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 "shared-bindings/_canio/Match.h"
+#include "shared-module/_canio/Match.h"
+
+#include "py/objproperty.h"
+#include "py/runtime.h"
+
+//| class Match:
+//| """Describe CAN bus messages to match"""
+//|
+//|
+//| def __init__(self, address: int, *, mask: int = 0, extended: bool = False):
+//| """Construct a Match with the given properties.
+//|
+//| If mask is nonzero, then the filter is for any sender which matches all
+//| the nonzero bits in mask. Otherwise, it matches exactly the given address.
+//| If extended is true then only extended addresses are matched, otherwise
+//| only standard addresses are matched."""
+//|
+
+STATIC mp_obj_t canio_match_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ enum { ARG_address, ARG_mask, ARG_extended, NUM_ARGS };
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_address, MP_ARG_INT | MP_ARG_REQUIRED, {.u_int = 0} },
+ { MP_QSTR_mask, MP_ARG_INT, {.u_int = 0} },
+ { MP_QSTR_extended, MP_ARG_BOOL, {.u_bool = false} },
+ };
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ MP_STATIC_ASSERT( MP_ARRAY_SIZE(allowed_args) == NUM_ARGS );
+
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ int address_bits = args[ARG_extended].u_bool ? 0x1fffffff : 0x7ff;
+ int address = args[ARG_address].u_int;
+ int mask = args[ARG_mask].u_int;
+
+ if(address & ~address_bits) {
+ mp_raise_ValueError_varg(translate("%q out of range"), MP_QSTR_address);
+ }
+
+ if(mask & ~address_bits) {
+ mp_raise_ValueError_varg(translate("%q out of range"), MP_QSTR_mask);
+ }
+
+ canio_match_obj_t *self = m_new_obj(canio_match_obj_t);
+ self->base.type = &canio_match_type;
+ common_hal_canio_match_construct(self, args[ARG_address].u_int, args[ARG_mask].u_int, args[ARG_extended].u_bool);
+ return self;
+}
+
+//| address: int
+//| """The address to match"""
+//|
+
+STATIC mp_obj_t canio_match_address_get(mp_obj_t self_in) {
+ canio_match_obj_t *self = self_in;
+ return MP_OBJ_NEW_SMALL_INT(common_hal_canio_match_address_get(self));
+}
+MP_DEFINE_CONST_FUN_OBJ_1(canio_match_address_get_obj, canio_match_address_get);
+
+const mp_obj_property_t canio_match_address_obj = {
+ .base.type = &mp_type_property,
+ .proxy = {(mp_obj_t)&canio_match_address_get_obj,
+ (mp_obj_t)&mp_const_none_obj,
+ (mp_obj_t)&mp_const_none_obj},
+};
+
+//|
+//| mask: int
+//| """The optional mask of addresses to match"""
+//|
+
+STATIC mp_obj_t canio_match_mask_get(mp_obj_t self_in) {
+ canio_match_obj_t *self = self_in;
+ return MP_OBJ_NEW_SMALL_INT(common_hal_canio_match_mask_get(self));
+}
+MP_DEFINE_CONST_FUN_OBJ_1(canio_match_mask_get_obj, canio_match_mask_get);
+
+const mp_obj_property_t canio_match_mask_obj = {
+ .base.type = &mp_type_property,
+ .proxy = {(mp_obj_t)&canio_match_mask_get_obj,
+ (mp_obj_t)&mp_const_none_obj,
+ (mp_obj_t)&mp_const_none_obj},
+};
+
+//| extended: bool
+//| """True to match extended addresses, False to match standard addresses"""
+//|
+
+STATIC mp_obj_t canio_match_extended_get(mp_obj_t self_in) {
+ canio_match_obj_t *self = self_in;
+ return mp_obj_new_bool(common_hal_canio_match_extended_get(self));
+}
+MP_DEFINE_CONST_FUN_OBJ_1(canio_match_extended_get_obj, canio_match_extended_get);
+
+const mp_obj_property_t canio_match_extended_obj = {
+ .base.type = &mp_type_property,
+ .proxy = {(mp_obj_t)&canio_match_extended_get_obj,
+ (mp_obj_t)&mp_const_none_obj,
+ (mp_obj_t)&mp_const_none_obj},
+};
+
+STATIC const mp_rom_map_elem_t canio_match_locals_dict_table[] = {
+ { MP_ROM_QSTR(MP_QSTR_address), MP_ROM_PTR(&canio_match_address_obj) },
+ { MP_ROM_QSTR(MP_QSTR_mask), MP_ROM_PTR(&canio_match_mask_obj) },
+ { MP_ROM_QSTR(MP_QSTR_extended), MP_ROM_PTR(&canio_match_extended_obj) },
+};
+STATIC MP_DEFINE_CONST_DICT(canio_match_locals_dict, canio_match_locals_dict_table);
+
+const mp_obj_type_t canio_match_type = {
+ { &mp_type_type },
+ .name = MP_QSTR_Match,
+ .make_new = canio_match_make_new,
+ .locals_dict = (mp_obj_dict_t*)&canio_match_locals_dict,
+};
diff --git a/shared-bindings/_canio/Match.h b/shared-bindings/_canio/Match.h
new file mode 100644
index 000000000..adf98eb86
--- /dev/null
+++ b/shared-bindings/_canio/Match.h
@@ -0,0 +1,33 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler for Adafruit Industries
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "py/obj.h"
+
+extern const mp_obj_type_t canio_match_type;
+
+// Nothing now.
diff --git a/shared-bindings/_canio/Message.c b/shared-bindings/_canio/Message.c
new file mode 100644
index 000000000..a8e588354
--- /dev/null
+++ b/shared-bindings/_canio/Message.c
@@ -0,0 +1,213 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler for Adafruit Industries
+ *
+ * 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 "shared-bindings/_canio/Message.h"
+#include "shared-module/_canio/Message.h"
+
+#include "py/obj.h"
+#include "py/objproperty.h"
+#include "py/runtime.h"
+
+//| class Message:
+//| def __init__(self, id: int=0, data: Optional[bytes] = None, *, size: Optional[int] = None, rtr: bool = False):
+//| """Construct a Message to send on a CAN bus
+//|
+//| :param int id: The numeric ID of the message
+//| :param bytes data: The content of the message
+//| :param int size: The amount of data requested, for an rtr
+//| :param bool rtr: True if the message represents an rtr (Remote Transmission Request)
+//|
+//| In CAN, messages can have a size from 0 to 8 bytes.
+//|
+//| For a non-rtr message, specify `data`. For an rtr-message, specify either `data` (a dummy buffer of the requested size) or `size`.
+//| """
+//| ...
+//|
+STATIC mp_obj_t canio_message_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ enum { ARG_id, ARG_data, ARG_size, ARG_rtr, NUM_ARGS };
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_id, MP_ARG_INT, {.u_obj = 0} },
+ { MP_QSTR_data, MP_ARG_OBJ, {.u_obj = 0} },
+ { MP_QSTR_size, MP_ARG_INT, {.u_int = -1} },
+ { MP_QSTR_rtr, MP_ARG_BOOL, {.u_bool = false} },
+ };
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ MP_STATIC_ASSERT( MP_ARRAY_SIZE(allowed_args) == NUM_ARGS );
+
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ bool rtr = args[ARG_rtr].u_bool;
+ size_t size = (size_t)args[ARG_size].u_int;
+ bool specified_size = (size != (size_t)-1);
+ bool specified_data = (args[ARG_data].u_obj != NULL);
+
+ if(specified_size && specified_data) {
+ mp_raise_TypeError(translate("specify size or data, but not both"));
+ }
+
+ mp_buffer_info_t data;
+ if (specified_data) {
+ mp_get_buffer_raise(args[ARG_data].u_obj, &data, MP_BUFFER_READ);
+ } else if (specified_size) {
+ data.buf = 0;
+ data.len = size;
+ } else {
+ data.buf = 0;
+ data.len = 0;
+ }
+
+ if(data.len > 8) {
+ mp_raise_ValueError(translate("Messages limited to 8 bytes"));
+ }
+
+ canio_message_obj_t *self = m_new_obj(canio_message_obj_t);
+ self->base.type = &canio_message_type;
+ common_hal_canio_message_construct(self, args[ARG_id].u_int, data.buf, data.len, rtr);
+ return self;
+}
+
+//| id: int
+//| """The numeric ID of the message"""
+//|
+STATIC mp_obj_t canio_message_id_get(const mp_obj_t self_in) {
+ canio_message_obj_t *self = self_in;
+ return MP_OBJ_NEW_SMALL_INT(common_hal_canio_message_id_get(self));
+}
+MP_DEFINE_CONST_FUN_OBJ_1(canio_message_id_get_obj, canio_message_id_get);
+
+STATIC mp_obj_t canio_message_id_set(const mp_obj_t self_in, const mp_obj_t id) {
+ canio_message_obj_t *self = self_in;
+ common_hal_canio_message_id_set(self, mp_obj_get_int(id));
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_2(canio_message_id_set_obj, canio_message_id_set);
+
+STATIC const mp_obj_property_t canio_message_id_obj = {
+ .base.type = &mp_type_property,
+ .proxy = {(mp_obj_t)&canio_message_id_get_obj,
+ (mp_obj_t)&canio_message_id_set_obj,
+ (mp_obj_t)&mp_const_none_obj},
+};
+
+//| data: bytes
+//| """The content of the message, or dummy content in the case of an rtr.
+//|
+//| Assigning to data sets the bytes to zero"""
+//|
+STATIC mp_obj_t canio_message_data_get(const mp_obj_t self_in) {
+ canio_message_obj_t *self = self_in;
+ return mp_obj_new_bytes((const byte*)common_hal_canio_message_data_get(self), common_hal_canio_message_size_get(self));
+}
+MP_DEFINE_CONST_FUN_OBJ_1(canio_message_data_get_obj, canio_message_data_get);
+
+STATIC mp_obj_t canio_message_data_set(const mp_obj_t self_in, const mp_obj_t data_in) {
+ canio_message_obj_t *self = self_in;
+ mp_buffer_info_t data;
+ mp_get_buffer_raise(data_in, &data, MP_BUFFER_READ);
+ if(data.len > 8) {
+ mp_raise_ValueError(translate("Messages limited to 8 bytes"));
+ }
+ common_hal_canio_message_data_set(self, data.buf, data.len);
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_2(canio_message_data_set_obj, canio_message_data_set);
+
+
+STATIC const mp_obj_property_t canio_message_data_obj = {
+ .base.type = &mp_type_property,
+ .proxy = {(mp_obj_t)&canio_message_data_get_obj,
+ (mp_obj_t)&canio_message_data_set_obj,
+ (mp_obj_t)&mp_const_none_obj},
+};
+
+
+//| size: int
+//| """The length of the message, or the length of the requested data in the case of an rtr"""
+//|
+STATIC mp_obj_t canio_message_size_get(const mp_obj_t self_in) {
+ canio_message_obj_t *self = self_in;
+ return MP_OBJ_NEW_SMALL_INT(common_hal_canio_message_size_get(self));
+}
+MP_DEFINE_CONST_FUN_OBJ_1(canio_message_size_get_obj, canio_message_size_get);
+
+STATIC mp_obj_t canio_message_size_set(const mp_obj_t self_in, const mp_obj_t size_in) {
+ canio_message_obj_t *self = self_in;
+ int size = mp_obj_get_int(size_in);
+ if(size > 8) {
+ mp_raise_ValueError(translate("Messages limited to 8 bytes"));
+ }
+ common_hal_canio_message_size_set(self, size);
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_2(canio_message_size_set_obj, canio_message_size_set);
+
+
+STATIC const mp_obj_property_t canio_message_size_obj = {
+ .base.type = &mp_type_property,
+ .proxy = {(mp_obj_t)&canio_message_size_get_obj,
+ (mp_obj_t)&canio_message_size_set_obj,
+ (mp_obj_t)&mp_const_none_obj},
+};
+
+//| rtr: bool
+//| """True if the message represents a remote transmission request (RTR)"""
+//|
+STATIC mp_obj_t canio_message_rtr_get(const mp_obj_t self_in) {
+ canio_message_obj_t *self = self_in;
+ return mp_obj_new_bool(common_hal_canio_message_rtr_get(self));
+}
+MP_DEFINE_CONST_FUN_OBJ_1(canio_message_rtr_get_obj, canio_message_rtr_get);
+
+STATIC mp_obj_t canio_message_rtr_set(const mp_obj_t self_in, const mp_obj_t rtr) {
+ canio_message_obj_t *self = self_in;
+ common_hal_canio_message_size_set(self, mp_obj_is_true(rtr));
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_2(canio_message_rtr_set_obj, canio_message_rtr_set);
+
+
+STATIC const mp_obj_property_t canio_message_rtr_obj = {
+ .base.type = &mp_type_property,
+ .proxy = {(mp_obj_t)&canio_message_rtr_get_obj,
+ (mp_obj_t)&canio_message_rtr_set_obj,
+ (mp_obj_t)&mp_const_none_obj},
+};
+
+
+STATIC const mp_rom_map_elem_t canio_message_locals_dict_table[] = {
+ { MP_ROM_QSTR(MP_QSTR_id), MP_ROM_PTR(&canio_message_id_obj) },
+ { MP_ROM_QSTR(MP_QSTR_data), MP_ROM_PTR(&canio_message_data_obj) },
+ { MP_ROM_QSTR(MP_QSTR_size), MP_ROM_PTR(&canio_message_size_obj) },
+ { MP_ROM_QSTR(MP_QSTR_rtr), MP_ROM_PTR(&canio_message_rtr_obj) },
+};
+STATIC MP_DEFINE_CONST_DICT(canio_message_locals_dict, canio_message_locals_dict_table);
+
+const mp_obj_type_t canio_message_type = {
+ { &mp_type_type },
+ .name = MP_QSTR_Message,
+ .make_new = canio_message_make_new,
+ .locals_dict = (mp_obj_t)&canio_message_locals_dict,
+};
diff --git a/shared-bindings/_canio/Message.h b/shared-bindings/_canio/Message.h
new file mode 100644
index 000000000..32d6801bc
--- /dev/null
+++ b/shared-bindings/_canio/Message.h
@@ -0,0 +1,31 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler for Adafruit Industries
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "py/obj.h"
+
+extern const mp_obj_type_t canio_message_type;
diff --git a/shared-bindings/_canio/__init__.c b/shared-bindings/_canio/__init__.c
new file mode 100644
index 000000000..4da008ff2
--- /dev/null
+++ b/shared-bindings/_canio/__init__.c
@@ -0,0 +1,73 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler for Adafruit Industries
+ *
+ * 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.
+ */
+
+//| """CAN bus access
+//|
+//| The `_canio` module contains low level classes to support the CAN bus
+//| protocol.
+//|
+//| All classes change hardware state and should be deinitialized when they
+//| are no longer needed if the program continues after use. To do so, either
+//| call :py:meth:`!deinit` or use a context manager. See
+//| :ref:`lifetime-and-contextmanagers` for more info.
+//|
+//| For example::
+//|
+//| import _canio
+//| from board import *
+//|
+//| can = _canio.BUS(board.CANRX, board.CANTX)
+//| can.write(408, b"adafruit")
+//| can.deinit()
+//|
+//| This example will write the data 'adafruit' onto the CAN bus to any
+//| device listening for message id 408."""
+//|
+
+#include "py/obj.h"
+
+#include "shared-bindings/_canio/__init__.h"
+#include "shared-bindings/_canio/CAN.h"
+#include "shared-bindings/_canio/Match.h"
+#include "shared-bindings/_canio/Message.h"
+
+STATIC const mp_rom_map_elem_t canio_module_globals_table[] = {
+ { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR__canio) },
+ { MP_ROM_QSTR(MP_QSTR_CAN), MP_ROM_PTR(&canio_can_type) },
+ { MP_ROM_QSTR(MP_QSTR_Match), MP_ROM_PTR(&canio_match_type) },
+ { MP_ROM_QSTR(MP_QSTR_Message), MP_ROM_PTR(&canio_message_type) },
+#if 0
+ { MP_ROM_QSTR(MP_QSTR_Listener), MP_ROM_PTR(&canio_listener_type) },
+#endif
+};
+
+
+STATIC MP_DEFINE_CONST_DICT(canio_module_globals, canio_module_globals_table);
+
+const mp_obj_module_t canio_module = {
+ .base = { &mp_type_module },
+ .globals = (mp_obj_dict_t*)&canio_module_globals,
+};
diff --git a/shared-bindings/_canio/__init__.h b/shared-bindings/_canio/__init__.h
new file mode 100644
index 000000000..45e76369c
--- /dev/null
+++ b/shared-bindings/_canio/__init__.h
@@ -0,0 +1,29 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler for Adafruit Industries
+ *
+ * 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.
+ */
+
+#pragma once
+
+// Nothing now.
diff --git a/shared-module/_canio/Match.c b/shared-module/_canio/Match.c
new file mode 100644
index 000000000..2b696addb
--- /dev/null
+++ b/shared-module/_canio/Match.c
@@ -0,0 +1,43 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler for Adafruit Industries
+ *
+ * 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 "shared-module/_canio/Match.h"
+
+void common_hal_canio_match_construct(canio_match_obj_t *self, int address, int mask, bool extended) {
+ self->address = address;
+ self->mask = mask;
+ self->extended = extended;
+}
+
+int common_hal_canio_match_address_get(const canio_match_obj_t *self) {
+ return self->address;
+}
+int common_hal_canio_match_mask_get(const canio_match_obj_t *self) {
+ return self->mask;
+}
+bool common_hal_canio_match_extended_get(const canio_match_obj_t *self) {
+ return self->extended;
+}
diff --git a/shared-module/_canio/Match.h b/shared-module/_canio/Match.h
new file mode 100644
index 000000000..099df976d
--- /dev/null
+++ b/shared-module/_canio/Match.h
@@ -0,0 +1,41 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler for Adafruit Industries
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "py/obj.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ int address;
+ int mask;
+ bool extended;
+} canio_match_obj_t;
+
+void common_hal_canio_match_construct(canio_match_obj_t *self, int address, int mask, bool extended);
+int common_hal_canio_match_address_get(const canio_match_obj_t *self);
+int common_hal_canio_match_mask_get(const canio_match_obj_t *self);
+bool common_hal_canio_match_extended_get(const canio_match_obj_t *self);
diff --git a/shared-module/_canio/Message.c b/shared-module/_canio/Message.c
new file mode 100644
index 000000000..df6a40a0a
--- /dev/null
+++ b/shared-module/_canio/Message.c
@@ -0,0 +1,85 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler for Adafruit Industries
+ *
+ * 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 "shared-module/_canio/Message.h"
+
+#include <string.h>
+
+void common_hal_canio_message_construct(canio_message_obj_t *self, int id, void *data, size_t size, bool rtr)
+{
+ self->id = id;
+ self->size = size;
+ self->rtr = rtr;
+ if (data) {
+ memcpy(self->data, data, size);
+ } else {
+ memset(self->data, 0, size);
+ }
+}
+
+int common_hal_canio_message_id_get(const canio_message_obj_t *self)
+{
+ return self->id;
+}
+
+void common_hal_canio_message_id_set(canio_message_obj_t *self, int id)
+{
+ self->id = id;
+}
+
+
+const void *common_hal_canio_message_data_get(const canio_message_obj_t *self)
+{
+ return self->data;
+}
+
+const void common_hal_canio_message_data_set(canio_message_obj_t *self, const void *data, size_t size)
+{
+ memcpy(self->data, data, size);
+}
+
+
+size_t common_hal_canio_message_size_get(const canio_message_obj_t *self)
+{
+ return self->size;
+}
+
+void common_hal_canio_message_size_set(canio_message_obj_t *self, size_t size)
+{
+ memset(self->data, 0, size);
+ self->size = size;
+}
+
+
+bool common_hal_canio_message_rtr_get(const canio_message_obj_t *self)
+{
+ return self->rtr;
+}
+
+void common_hal_canio_message_rtr_set(canio_message_obj_t *self, bool rtr)
+{
+ self->rtr = rtr;
+}
diff --git a/shared-module/_canio/Message.h b/shared-module/_canio/Message.h
new file mode 100644
index 000000000..e846f1095
--- /dev/null
+++ b/shared-module/_canio/Message.h
@@ -0,0 +1,47 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler for Adafruit Industries
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "py/obj.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ int id;
+ size_t size;
+ uint8_t data[8];
+ bool rtr;
+} canio_message_obj_t;
+
+void common_hal_canio_message_construct(canio_message_obj_t *self, int id, void *data, size_t size, bool rtr);
+bool common_hal_canio_message_rtr_get(const canio_message_obj_t *self);
+int common_hal_canio_message_id_get(const canio_message_obj_t *self);
+const void *common_hal_canio_message_data_get(const canio_message_obj_t *self);
+size_t common_hal_canio_message_size_get(const canio_message_obj_t *self);
+void common_hal_canio_message_rtr_set(canio_message_obj_t *self, bool rtr);
+void common_hal_canio_message_id_set(canio_message_obj_t *self, int id);
+void common_hal_canio_message_data_set(canio_message_obj_t *self, const void *data, size_t size);
+void common_hal_canio_message_size_set(canio_message_obj_t *self, size_t size);