summaryrefslogtreecommitdiff
path: root/ports/atmel-samd/common-hal/canio
diff options
context:
space:
mode:
authorfoamyguy <foamyguy@gmail.com>2020-11-08 18:32:15 -0600
committerGitHub <noreply@github.com>2020-11-08 18:32:15 -0600
commitcf21c4da601e00a614efd8ade774a0c8e5444c64 (patch)
tree118a157edbc1884f502de8e9e26f79d178ed8c9a /ports/atmel-samd/common-hal/canio
parent7611e71a1bc49bcb426e0854519d5143eb262a17 (diff)
parenteec9821fdc19ca81c2d0811a6b6c5909a54b8c81 (diff)
Merge pull request #1 from adafruit/main
merge from adafruit
Diffstat (limited to 'ports/atmel-samd/common-hal/canio')
-rw-r--r--ports/atmel-samd/common-hal/canio/CAN.c421
-rw-r--r--ports/atmel-samd/common-hal/canio/CAN.h52
-rw-r--r--ports/atmel-samd/common-hal/canio/Listener.c394
-rw-r--r--ports/atmel-samd/common-hal/canio/Listener.h45
-rw-r--r--ports/atmel-samd/common-hal/canio/__init__.c25
-rw-r--r--ports/atmel-samd/common-hal/canio/__init__.h66
6 files changed, 1003 insertions, 0 deletions
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..76599a9a7
--- /dev/null
+++ b/ports/atmel-samd/common-hal/canio/CAN.c
@@ -0,0 +1,421 @@
+/*
+ * 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)];
+
+void common_hal_canio_can_construct(canio_can_obj_t *self, mcu_pin_obj_t *tx, mcu_pin_obj_t *rx, 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);
+
+ 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 = tx ? common_hal_mcu_pin_number(tx) : COMMON_HAL_MCU_NO_PIN;
+ 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;
+ self->silent = silent;
+
+ // 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);
+ }
+
+ // A "data bit" is a data bit :) with dula 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_buffer,
+ .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 = 0,
+ .bit.ANFS = CAN_GFC_ANFS_REJECT_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->standard_rx_filter
+ };
+ hri_can_write_SIDFC_reg(self->hw, dfc.reg);
+ }
+
+ {
+ CAN_XIDFC_Type dfc = {
+ .bit.LSE = COMMON_HAL_CANIO_RX_FILTER_SIZE,
+ .bit.FLESA = (uint32_t)self->state->extended_rx_filter
+ };
+ hri_can_write_XIDFC_reg(self->hw, dfc.reg);
+ }
+
+ {
+ CAN_IE_Type ie = {
+ .bit.EWE = 1,
+ .bit.EPE = 1,
+ .bit.BOE = 1,
+ };
+ hri_can_write_IE_reg(self->hw, ie.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;
+}
+
+bool 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 self->hw->ECR.bit.TEC;
+}
+
+int common_hal_canio_can_receive_error_count_get(canio_can_obj_t *self)
+{
+ return self->hw->ECR.bit.REC;
+}
+
+canio_bus_state_t common_hal_canio_can_state_get(canio_can_obj_t *self) {
+ CAN_PSR_Type psr = self->hw->PSR;
+ if (psr.bit.BO) {
+ return BUS_STATE_OFF;
+ }
+ if (psr.bit.EP) {
+ return BUS_STATE_ERROR_PASSIVE;
+ }
+ if (psr.bit.EW) {
+ return BUS_STATE_ERROR_WARNING;
+ }
+ return BUS_STATE_ERROR_ACTIVE;
+}
+
+void common_hal_canio_can_restart(canio_can_obj_t *self) {
+ if (!self->hw->PSR.bit.BO) {
+ return;
+ }
+
+ hri_can_clear_CCCR_INIT_bit(self->hw);
+ while (hri_can_get_CCCR_INIT_bit(self->hw)) {
+ }
+}
+
+bool common_hal_canio_can_auto_restart_get(canio_can_obj_t *self) {
+ return self->auto_restart;
+}
+
+void common_hal_canio_can_auto_restart_set(canio_can_obj_t *self, bool value) {
+ self->auto_restart = value;
+}
+
+static void maybe_auto_restart(canio_can_obj_t *self) {
+ if (self->auto_restart) {
+ common_hal_canio_can_restart(self);
+ }
+}
+
+void common_hal_canio_can_send(canio_can_obj_t *self, mp_obj_t message_in)
+{
+ maybe_auto_restart(self);
+
+ canio_message_obj_t *message = message_in;;
+ // We have just one dedicated TX buffer, use it!
+ canio_can_tx_buffer_t *ent = &self->state->tx_buffer[0];
+
+ bool rtr = message->base.type == &canio_remote_transmission_request_type;
+ ent->txb0.bit.ESI = false;
+ ent->txb0.bit.XTD = message->extended;
+ ent->txb0.bit.RTR = rtr;
+ if (message->extended) {
+ ent->txb0.bit.ID = message->id;
+ } else {
+ 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 (!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_silent_get(canio_can_obj_t *self) {
+ return self->silent;
+}
+
+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);
+
+ /* 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..15ac0f4b7
--- /dev/null
+++ b/ports/atmel-samd/common-hal/canio/CAN.h
@@ -0,0 +1,52 @@
+/*
+ * 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 "shared-bindings/canio/__init__.h"
+#include "shared-bindings/canio/CAN.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 canio_can_obj {
+ mp_obj_base_t base;
+ Can *hw;
+ canio_can_state_t *state;
+ int baudrate;
+ uint8_t rx_pin_number:8;
+ uint8_t tx_pin_number:8;
+ bool loopback:1;
+ bool silent:1;
+ bool auto_restart:1;
+ bool fifo0_in_use:1;
+ bool fifo1_in_use:1;
+} canio_can_obj_t;
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..b27c8cd1b
--- /dev/null
+++ b/ports/atmel-samd/common-hal/canio/Listener.c
@@ -0,0 +1,394 @@
+/*
+ * 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/canio/Listener.h"
+#include "shared-bindings/util.h"
+#include "supervisor/shared/tick.h"
+#include "component/can.h"
+
+STATIC void allow_config_change(canio_can_obj_t *can) {
+ can->hw->CCCR.bit.INIT = 1;
+ while (!can->hw->CCCR.bit.INIT) {
+ }
+ can->hw->CCCR.bit.CCE = 1;
+}
+
+STATIC void prevent_config_change(canio_can_obj_t *can) {
+ can->hw->CCCR.bit.CCE = 0;
+ can->hw->CCCR.bit.INIT = 0;
+ while (can->hw->CCCR.bit.INIT) {
+ }
+}
+
+__attribute__((unused))
+STATIC void static_assertions(void) {
+ MP_STATIC_ASSERT(CAN_GFC_ANFE_RXF0_Val + 1 == CAN_GFC_ANFE_RXF1_Val);
+ MP_STATIC_ASSERT(CAN_GFC_ANFS_RXF0_Val + 1 == CAN_GFC_ANFS_RXF1_Val);
+ MP_STATIC_ASSERT(CAN_SIDFE_0_SFEC_STF0M_Val + 1 == CAN_SIDFE_0_SFEC_STF1M_Val);
+ MP_STATIC_ASSERT(CAN_XIDFE_0_EFEC_STF0M_Val + 1 == CAN_XIDFE_0_EFEC_STF1M_Val);
+}
+
+STATIC bool single_id_filter(canio_match_obj_t *match) {
+ return match->mask == 0 || match->mask == match->id;
+}
+
+STATIC bool standard_filter_in_use(CanMramSidfe *filter) {
+ return filter->SIDFE_0.bit.SFEC != CAN_SIDFE_0_SFEC_DISABLE_Val;
+}
+
+STATIC bool extended_filter_in_use(CanMramXidfe *filter) {
+ return filter->XIDFE_0.bit.EFEC != CAN_XIDFE_0_EFEC_DISABLE_Val;
+}
+
+STATIC size_t num_filters_needed(size_t nmatch, canio_match_obj_t **matches, bool extended) {
+ size_t num_half_filters_needed = 1;
+ for(size_t i=0; i<nmatch; i++) {
+ if (extended != matches[i]->extended) {
+ continue;
+ }
+ if (single_id_filter(matches[i])) {
+ num_half_filters_needed += 1;
+ } else {
+ num_half_filters_needed += 2;
+ }
+ }
+ return num_half_filters_needed / 2;
+}
+
+STATIC size_t num_filters_available(canio_can_obj_t *can, bool extended) {
+ size_t available = 0;
+ if (extended) {
+ for(size_t i = 0; i < MP_ARRAY_SIZE(can->state->extended_rx_filter); i++) {
+ if (!extended_filter_in_use(&can->state->extended_rx_filter[i])) {
+ available++;
+ }
+ }
+ } else {
+ for(size_t i = 0; i < MP_ARRAY_SIZE(can->state->standard_rx_filter); i++) {
+ if (!standard_filter_in_use(&can->state->standard_rx_filter[i])) {
+ available++;
+ }
+ }
+ }
+ return available;
+}
+
+STATIC void clear_filters(canio_listener_obj_t *self) {
+ canio_can_obj_t *can = self->can;
+ int fifo = self->fifo_idx;
+
+ // If it was a global accept, clear it
+ allow_config_change(can);
+ if (can->hw->GFC.bit.ANFS == CAN_GFC_ANFS_RXF0 + fifo) {
+ can->hw->GFC.bit.ANFS = CAN_GFC_ANFS_REJECT_Val;
+ }
+ if (can->hw->GFC.bit.ANFE == CAN_GFC_ANFE_RXF0 + fifo) {
+ can->hw->GFC.bit.ANFE = CAN_GFC_ANFE_REJECT_Val;
+ }
+ prevent_config_change(can);
+
+ // For each filter entry, if it pointed at this FIFO set it to DISABLE
+ for(size_t i = 0; i < MP_ARRAY_SIZE(can->state->extended_rx_filter); i++) {
+ int val = CAN_XIDFE_0_EFEC_STF0M_Val + fifo;
+ if (can->state->extended_rx_filter[i].XIDFE_0.bit.EFEC == val) {
+ can->state->extended_rx_filter[i].XIDFE_0.bit.EFEC = CAN_XIDFE_0_EFEC_DISABLE_Val;
+ }
+ }
+ for(size_t i = 0; i < MP_ARRAY_SIZE(can->state->standard_rx_filter); i++) {
+ int val = CAN_SIDFE_0_SFEC_STF1M_Val + fifo;
+ if (can->state->standard_rx_filter[i].SIDFE_0.bit.SFEC == val) {
+ can->state->standard_rx_filter[i].SIDFE_0.bit.SFEC = CAN_SIDFE_0_SFEC_DISABLE_Val;
+ }
+ }
+}
+
+STATIC CanMramXidfe *next_extended_filter(canio_listener_obj_t *self, CanMramXidfe *start) {
+ CanMramXidfe *end = &self->can->state->extended_rx_filter[MP_ARRAY_SIZE(self->can->state->extended_rx_filter)];
+ if (start == NULL) {
+ start = self->can->state->extended_rx_filter;
+ } else {
+ start = start + 1;
+ }
+ while (extended_filter_in_use(start)) {
+ if (start == end) {
+ return NULL;
+ }
+ start = start + 1;
+ }
+ return start;
+}
+
+STATIC CanMramSidfe *next_standard_filter(canio_listener_obj_t *self, CanMramSidfe *start) {
+ CanMramSidfe *end = &self->can->state->standard_rx_filter[MP_ARRAY_SIZE(self->can->state->standard_rx_filter)];
+ if (start == NULL) {
+ start = self->can->state->standard_rx_filter;
+ } else {
+ start = start + 1;
+ }
+ while (standard_filter_in_use(start)) {
+ if (start == end) {
+ return NULL;
+ }
+ start = start + 1;
+ }
+ return start;
+}
+
+STATIC void install_standard_filter(CanMramSidfe *standard, int id1, int id2, int sfec, int sft) {
+ assert(standard);
+ CAN_SIDFE_0_Type val = {
+ .bit.SFID1 = id1,
+ .bit.SFID2 = id2,
+ .bit.SFEC = sfec,
+ .bit.SFT = sft,
+ };
+ standard->SIDFE_0 = val;
+}
+
+STATIC void install_extended_filter(CanMramXidfe *extended, int id1, int id2, int efec, int eft) {
+ assert(extended);
+ CAN_XIDFE_0_Type val0 = {
+ .bit.EFID1 = id1,
+ .bit.EFEC = efec,
+ };
+ CAN_XIDFE_1_Type val1 = {
+ .bit.EFID2 = id2,
+ .bit.EFT = eft,
+ };
+ // Set entry 0 second, because it has the enable bits (XIDFE_0_EFEC)
+ extended->XIDFE_1 = val1;
+ extended->XIDFE_0 = val0;
+}
+
+
+#define NO_ID (-1)
+void set_filters(canio_listener_obj_t *self, size_t nmatch, canio_match_obj_t **matches) {
+ int fifo = self->fifo_idx;
+
+ if (!nmatch) {
+ allow_config_change(self->can);
+ self->can->hw->GFC.bit.ANFS = CAN_GFC_ANFS_RXF0_Val + fifo;
+ self->can->hw->GFC.bit.ANFE = CAN_GFC_ANFE_RXF0_Val + fifo;
+ self->can->hw->CCCR.bit.CCE = 0;
+ prevent_config_change(self->can);
+ return;
+ }
+
+ CanMramSidfe *standard = next_standard_filter(self, NULL);
+ CanMramXidfe *extended = next_extended_filter(self, NULL);
+
+ int first_id = NO_ID;
+
+ // step 1: single id standard matches
+ // we have to gather up pairs and stuff them in a single filter entry
+ for(size_t i = 0; i<nmatch; i++) {
+ canio_match_obj_t *match = matches[i];
+ if (match->extended) {
+ continue;
+ }
+ if (!single_id_filter(match)) {
+ continue;
+ }
+ if (first_id != NO_ID) {
+ install_standard_filter(standard, first_id, match->id, CAN_SIDFE_0_SFEC_STF0M_Val + fifo, CAN_SIDFE_0_SFT_DUAL_Val);
+ first_id = NO_ID;
+ standard = next_standard_filter(self, standard);
+ } else {
+ first_id = match->id;
+ }
+ }
+ // step 1.5. odd single id standard match
+ if (first_id != NO_ID) {
+ install_standard_filter(standard, first_id, first_id, CAN_SIDFE_0_SFEC_STF0M_Val + fifo, CAN_SIDFE_0_SFT_DUAL_Val);
+ standard = next_standard_filter(self, standard);
+ first_id = NO_ID;
+ }
+
+ // step 2: standard mask filter
+ for(size_t i = 0; i<nmatch; i++) {
+ canio_match_obj_t *match = matches[i];
+ if (match->extended) {
+ continue;
+ }
+ if (single_id_filter(match)) {
+ continue;
+ }
+ install_standard_filter(standard, match->id, match->mask, CAN_SIDFE_0_SFEC_STF0M_Val + fifo, CAN_SIDFE_0_SFT_CLASSIC_Val);
+ standard = next_standard_filter(self, standard);
+ }
+
+ // step 3: single id extended matches
+ // we have to gather up pairs and stuff them in a single filter entry
+ for(size_t i = 0; i<nmatch; i++) {
+ canio_match_obj_t *match = matches[i];
+ if (!match->extended) {
+ continue;
+ }
+ if (!single_id_filter(match)) {
+ continue;
+ }
+ if (first_id != NO_ID) {
+ install_extended_filter(extended, first_id, match->id, CAN_XIDFE_0_EFEC_STF0M_Val + fifo, CAN_XIDFE_1_EFT_DUAL_Val);
+ first_id = NO_ID;
+ extended = next_extended_filter(self, extended);
+ } else {
+ first_id = match->id;
+ }
+ }
+ // step 3.5. odd single id standard match
+ if (first_id != NO_ID) {
+ install_extended_filter(extended, first_id, first_id, CAN_XIDFE_0_EFEC_STF0M_Val + fifo, CAN_XIDFE_1_EFT_DUAL_Val);
+ extended = next_extended_filter(self, extended);
+ first_id = NO_ID;
+ }
+
+ // step 4: extended mask filters
+ for(size_t i = 0; i<nmatch; i++) {
+ canio_match_obj_t *match = matches[i];
+ if (!match->extended) {
+ continue;
+ }
+ if (single_id_filter(match)) {
+ continue;
+ }
+ install_extended_filter(extended, match->id, match->mask, CAN_XIDFE_0_EFEC_STF0M_Val + fifo, CAN_XIDFE_1_EFT_CLASSIC_Val);
+ extended = next_extended_filter(self, extended);
+ }
+
+ // phew, easy(!)
+}
+
+
+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 (!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"));
+ }
+
+ if (!nmatch) {
+ if (can->hw->GFC.bit.ANFS == CAN_GFC_ANFS_RXF1_Val - self->fifo_idx) {
+ mp_raise_ValueError(translate("Already have all-matches listener"));
+ }
+ if (can->hw->GFC.bit.ANFE == CAN_GFC_ANFE_RXF1_Val - self->fifo_idx) {
+ mp_raise_ValueError(translate("Already have all-matches listener"));
+ }
+ }
+
+ if (num_filters_needed(nmatch, matches, false) > num_filters_available(can, false)) {
+ mp_raise_ValueError(translate("Filters too complex"));
+ }
+
+ if (num_filters_needed(nmatch, matches, true) > num_filters_available(can, true)) {
+ mp_raise_ValueError(translate("Filters too complex"));
+ }
+
+ // Nothing can fail now so it's safe to assign self->can
+ self->can = can;
+ set_filters(self, nmatch, matches);
+ 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;
+}
+
+mp_obj_t common_hal_canio_listener_receive(canio_listener_obj_t *self) {
+ if (!common_hal_canio_listener_in_waiting(self)) {
+ uint64_t deadline = supervisor_ticks_ms64() + self->timeout_ms;
+ do {
+ if (supervisor_ticks_ms64() > deadline) {
+ return NULL;
+ }
+ } while (!common_hal_canio_listener_in_waiting(self));
+ }
+ int index = self->hw->RXFS.bit.F0GI;
+ canio_can_rx_fifo_t *hw_message = &self->fifo[index];
+ bool rtr = hw_message->rxf0.bit.RTR;
+ canio_message_obj_t *message = m_new_obj(canio_message_obj_t);
+ message->base.type = rtr ? &canio_remote_transmission_request_type : &canio_message_type;
+ message->extended = hw_message->rxf0.bit.XTD;
+ if (message->extended) {
+ message->id = hw_message->rxf0.bit.ID;
+ } else {
+ message->id = hw_message->rxf0.bit.ID >> 18; // short ids are left-justified
+ }
+ message->size = hw_message->rxf1.bit.DLC;
+ if (!rtr) {
+ memcpy(message->data, hw_message->data, message->size);
+ }
+ self->hw->RXFA.bit.F0AI = index;
+ return message;
+}
+
+void common_hal_canio_listener_deinit(canio_listener_obj_t *self) {
+ if (self->can) {
+ clear_filters(self);
+ 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..237dca870
--- /dev/null
+++ b/ports/atmel-samd/common-hal/canio/Listener.h
@@ -0,0 +1,45 @@
+/*
+ * 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 canio_listener_obj {
+ mp_obj_base_t base;
+ canio_can_obj_t *can;
+ canio_can_rx_fifo_t *fifo;
+ canio_rxfifo_reg_t *hw;
+ uint32_t timeout_ms;
+ uint8_t fifo_idx;
+} canio_listener_obj_t;
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..32adc5bf9
--- /dev/null
+++ b/ports/atmel-samd/common-hal/canio/__init__.h
@@ -0,0 +1,66 @@
+/*
+ * 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)
+
+// This appears to be a typo (transposition error) in the ASF4 headers
+// It's called the "Extended ID Filter Entry"
+typedef CanMramXifde CanMramXidfe;
+
+typedef struct canio_listener canio_listener_t;
+typedef struct canio_can canio_can_t;
+
+typedef struct {
+ CAN_TXBE_0_Type txb0;
+ CAN_TXBE_1_Type txb1;
+ COMPILER_ALIGNED(4)
+ uint8_t data[COMMON_HAL_CANIO_MAX_MESSAGE_LENGTH];
+} canio_can_tx_buffer_t;
+
+typedef struct {
+ CAN_RXF0E_0_Type rxf0;
+ CAN_RXF0E_1_Type rxf1;
+ COMPILER_ALIGNED(4)
+ uint8_t data[COMMON_HAL_CANIO_MAX_MESSAGE_LENGTH];
+} canio_can_rx_fifo_t;
+
+typedef uint32_t canio_can_filter_t;
+
+typedef struct {
+ canio_can_tx_buffer_t tx_buffer[COMMON_HAL_CANIO_TX_FIFO_SIZE];
+ canio_can_rx_fifo_t rx0_fifo[COMMON_HAL_CANIO_RX_FIFO_SIZE];
+ canio_can_rx_fifo_t rx1_fifo[COMMON_HAL_CANIO_RX_FIFO_SIZE];
+ CanMramSidfe standard_rx_filter[COMMON_HAL_CANIO_RX_FILTER_SIZE];
+ CanMramXifde extended_rx_filter[COMMON_HAL_CANIO_RX_FILTER_SIZE];
+} canio_can_state_t;