diff options
| author | foamyguy <foamyguy@gmail.com> | 2020-11-08 18:32:15 -0600 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-11-08 18:32:15 -0600 |
| commit | cf21c4da601e00a614efd8ade774a0c8e5444c64 (patch) | |
| tree | 118a157edbc1884f502de8e9e26f79d178ed8c9a /ports/atmel-samd/common-hal/canio | |
| parent | 7611e71a1bc49bcb426e0854519d5143eb262a17 (diff) | |
| parent | eec9821fdc19ca81c2d0811a6b6c5909a54b8c81 (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.c | 421 | ||||
| -rw-r--r-- | ports/atmel-samd/common-hal/canio/CAN.h | 52 | ||||
| -rw-r--r-- | ports/atmel-samd/common-hal/canio/Listener.c | 394 | ||||
| -rw-r--r-- | ports/atmel-samd/common-hal/canio/Listener.h | 45 | ||||
| -rw-r--r-- | ports/atmel-samd/common-hal/canio/__init__.c | 25 | ||||
| -rw-r--r-- | ports/atmel-samd/common-hal/canio/__init__.h | 66 |
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; |
