diff --git a/boards/same54-xpro/Makefile.features b/boards/same54-xpro/Makefile.features index 2a0b7d1abcb88..ecf8e7b7f0b42 100644 --- a/boards/same54-xpro/Makefile.features +++ b/boards/same54-xpro/Makefile.features @@ -16,6 +16,7 @@ FEATURES_PROVIDED += periph_uart_hw_fc FEATURES_PROVIDED += periph_adc FEATURES_PROVIDED += periph_usbdev FEATURES_PROVIDED += periph_freqm +FEATURES_PROVIDED += periph_can # Put other features for this board (in alphabetical order) FEATURES_PROVIDED += riotboot diff --git a/boards/same54-xpro/include/board.h b/boards/same54-xpro/include/board.h index 50a966787fefb..a712f88c6ef94 100644 --- a/boards/same54-xpro/include/board.h +++ b/boards/same54-xpro/include/board.h @@ -75,6 +75,13 @@ extern "C" { #define BTN0_MODE GPIO_IN_PU /** @} */ +/** + * @name ATA6561 STANDBY pin definition + * @{ + */ +#define AT6561_STBY_PIN GPIO_PIN(PC, 13) +/** @} */ + /** * @name MTD configuration * @{ diff --git a/boards/same54-xpro/include/periph_conf.h b/boards/same54-xpro/include/periph_conf.h index 33dbb72cfd13d..d21ba169812d0 100644 --- a/boards/same54-xpro/include/periph_conf.h +++ b/boards/same54-xpro/include/periph_conf.h @@ -106,6 +106,27 @@ static const tc32_conf_t timer_config[] = { #define TIMER_NUMOF ARRAY_SIZE(timer_config) /** @} */ +/** + * @name CAN configuration + * @{ + */ +/** Available CAN interfaces */ +static const can_conf_t candev_conf[] = { + { + .can = CAN1, + .rx_pin = GPIO_PIN(PB, 13), + .tx_pin = GPIO_PIN(PB, 12), + .gclk_src = SAM0_GCLK_PERIPH, + } +}; + +/** CAN 1 configuration */ +#define ISR_CAN1 isr_can1 + +/** Number of CAN interfaces */ +#define CAN_NUMOF ARRAY_SIZE(candev_conf) +/** @} */ + /** * @name UART configuration * @{ diff --git a/cpu/samd5x/Makefile.dep b/cpu/samd5x/Makefile.dep index a51a8aa278be4..ae047ccf39c28 100644 --- a/cpu/samd5x/Makefile.dep +++ b/cpu/samd5x/Makefile.dep @@ -2,4 +2,8 @@ ifneq (,$(filter periph_gpio_tamper_wake,$(USEMODULE))) USEMODULE += periph_rtc_rtt endif +ifneq (,$(filter periph_can,$(USEMODULE))) + FEATURES_REQUIRED += can_rx_mailbox +endif + include $(RIOTCPU)/sam0_common/Makefile.dep diff --git a/cpu/samd5x/Makefile.features b/cpu/samd5x/Makefile.features index 7cba14d2183fe..53a3e99d333f1 100644 --- a/cpu/samd5x/Makefile.features +++ b/cpu/samd5x/Makefile.features @@ -7,5 +7,6 @@ FEATURES_PROVIDED += periph_gpio_tamper_wake FEATURES_PROVIDED += periph_rtc_mem FEATURES_PROVIDED += periph_spi_on_qspi FEATURES_PROVIDED += periph_uart_collision +FEATURES_PROVIDED += can_rx_mailbox include $(RIOTCPU)/sam0_common/Makefile.features diff --git a/cpu/samd5x/include/can_params.h b/cpu/samd5x/include/can_params.h new file mode 100644 index 0000000000000..ac29b9fc1cc2b --- /dev/null +++ b/cpu/samd5x/include/can_params.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2024 ML!PA Consulting GmbH + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_samd5x + * @brief CPU specific definitions for CAN controllers + * @{ + * + * @file + * @brief SAMD5x CAN controller (M_CAN Bosch) default device parameters + * + * @author Firas Hamdi + */ + +#ifndef CAN_PARAMS_H +#define CAN_PARAMS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "can/device.h" + +/** Default SAMD5x CAN devices parameters */ +static const candev_params_t candev_params[] = { + { + .name = "can_samd5x_0", + }, + { + .name = "can_samd5x_1", + } +}; + +#ifdef __cplusplus +} +#endif + +#endif /* CAN_PARAMS_H */ +/** @} */ diff --git a/cpu/samd5x/include/candev_samd5x.h b/cpu/samd5x/include/candev_samd5x.h new file mode 100644 index 0000000000000..3e065f93d8cfc --- /dev/null +++ b/cpu/samd5x/include/candev_samd5x.h @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2024 ML!PA Consulting GmbH + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_samd5x + * @brief CPU specific definitions for CAN controllers + * @{ + * + * @file + * @brief SAMD5x CAN controller (M_CAN Bosch) driver + * + * @author Firas Hamdi + */ + +#ifndef CANDEV_SAMD5X_H +#define CANDEV_SAMD5X_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(CAN_INST_NUM) +#include "can/candev.h" + +#ifndef CANDEV_SAMD5X_DEFAULT_BITRATE +/** Default bitrate */ +#define CANDEV_SAMD5X_DEFAULT_BITRATE 500000U +#endif + +#ifndef CANDEV_SAMD5X_DEFAULT_SPT +/** Default sampling-point */ +#define CANDEV_SAMD5X_DEFAULT_SPT 875 +#endif + +#ifndef CANDEV_SAMD5X_DEFAULT_STD_FILTER_NUM +#define CANDEV_SAMD5X_DEFAULT_STD_FILTER_NUM 3 +#endif + +#ifndef CANDEV_SAMD5X_DEFAULT_EXT_FILTER_NUM +#define CANDEV_SAMD5X_DEFAULT_EXT_FILTER_NUM 3 +#endif + +#ifndef CANDEV_SAMD5X_DEFAULT_RX_FIFO_0_ELTS_NUM +#define CANDEV_SAMD5X_DEFAULT_RX_FIFO_0_ELTS_NUM 32 +#endif + +#ifndef CANDEV_SAMD5X_DEFAULT_RX_FIFO_1_ELTS_NUM +#define CANDEV_SAMD5X_DEFAULT_RX_FIFO_1_ELTS_NUM 32 +#endif + +#ifndef CANDEV_SAMD5X_DEFAULT_TX_EVT_FIFO_ELTS_NUM +#define CANDEV_SAMD5X_DEFAULT_TX_EVT_FIFO_ELTS_NUM 16 +#endif + +#ifndef CANDEV_SAMD5X_DEFAULT_TX_BUFFER_NUM +#define CANDEV_SAMD5X_DEFAULT_TX_BUFFER_NUM 16 +#endif + +#ifndef CANDEV_SAMD5X_DEFAULT_TX_BUFFER_FIFO_QUEUE_NUM +#define CANDEV_SAMD5X_DEFAULT_TX_BUFFER_FIFO_QUEUE_NUM 16 +#endif + +/* unit: elements */ +#define CANDEV_SAMD5X_MAX_STD_FILTER 128 +#define CANDEV_SAMD5X_MAX_EXT_FILTER 64 +#define CANDEV_SAMD5X_MAX_RX_FIFO_0_ELTS 64 +#define CANDEV_SAMD5X_MAX_RX_FIFO_1_ELTS 64 +#define CANDEV_SAMD5X_MAX_RX_BUFFER 64 +#define CANDEV_SAMD5X_MAX_TX_EVT_FIFO_ELTS 32 +#define CANDEV_SAMD5X_MAX_TX_BUFFER 32 +#define CANDEV_SAMD5X_MSG_RAM_MAX_SIZE 448 + +/** + * @brief CAN device configuration descriptor + */ +typedef struct { + /** CAN device handler */ + Can *can; + /** CAN Rx pin */ + gpio_t rx_pin; + /** CAN Tx pin */ + gpio_t tx_pin; + /** GCLK source supplying the CAN controller */ + uint8_t gclk_src; +} can_conf_t; +#define HAVE_CAN_CONF_T + +/** + * @brief CAN message RAM accessible to the CAN controller + */ +typedef struct { + /** Standard filters space in the CAN message RAM */ + CanMramSidfe std_filter[CANDEV_SAMD5X_DEFAULT_STD_FILTER_NUM]; + /** Extended filters space in the CAN message RAM */ + CanMramXifde ext_filter[CANDEV_SAMD5X_DEFAULT_EXT_FILTER_NUM]; + /** Reception FIFO_0 space in the CAN message RAM */ + CanMramRxf0e rx_fifo_0[CANDEV_SAMD5X_DEFAULT_RX_FIFO_0_ELTS_NUM]; + /** Reception FIFO_1 space in the CAN message RAM */ + CanMramRxf1e rx_fifo_1[CANDEV_SAMD5X_DEFAULT_RX_FIFO_1_ELTS_NUM]; + /** Reception buffers space in the CAN message RAM */ + CanMramRxbe rx_buffer[CANDEV_SAMD5X_MAX_RX_BUFFER]; + /** Transmission events FIFO space in the CAN message RAM */ + CanMramTxefe tx_event_fifo[CANDEV_SAMD5X_DEFAULT_TX_EVT_FIFO_ELTS_NUM]; + /** Transmission FIFO space in the CAN message RAM */ + CanMramTxbe tx_fifo_queue[CANDEV_SAMD5X_DEFAULT_TX_BUFFER_FIFO_QUEUE_NUM]; +} msg_ram_conf_t; + +/** + * @brief CAN device descriptor + */ +typedef struct { + /** Structure to hold driver state */ + candev_t candev; + /** CAN device configuration descriptor */ + const can_conf_t *conf; + /** CAN message RAM accessible to the CAN controller */ + msg_ram_conf_t msg_ram_conf; + /** Enable/Disable Transceiver Delay Compensation */ + bool tdc_ctrl; + /** False to use Tx FIFO operation, True to use Tx Queue operation */ + bool tx_fifo_queue_ctrl; +} can_t; +#define HAVE_CAN_T + +/** + * @brief Enable/Disable the transmitter delay compensation + * + * @param[in] dev device descriptor + * + */ +void candev_samd5x_tdc_control(can_t *dev); + +/** + * @brief Enter the device into sleep mode + * + * @param[in] candev candev driver + * + */ +void candev_samd5x_enter_sleep_mode(candev_t *candev); + +/** + * @brief Wake up the device from sleep mode + * + * @param[in] candev candev driver + * + */ +void candev_samd5x_exit_sleep_mode(candev_t *candev); + +#endif /* CAN_INST_NUM */ + +#ifdef __cplusplus +} +#endif + +#endif /* CANDEV_SAMD5X_H */ +/** @} */ diff --git a/cpu/samd5x/include/periph_cpu.h b/cpu/samd5x/include/periph_cpu.h index 450941af1f466..26d509ca77a2e 100644 --- a/cpu/samd5x/include/periph_cpu.h +++ b/cpu/samd5x/include/periph_cpu.h @@ -25,6 +25,7 @@ #include "macros/units.h" #include "periph_cpu_common.h" +#include "candev_samd5x.h" #ifdef __cplusplus extern "C" { #endif diff --git a/cpu/samd5x/periph/can.c b/cpu/samd5x/periph/can.c new file mode 100644 index 0000000000000..acf201bac358f --- /dev/null +++ b/cpu/samd5x/periph/can.c @@ -0,0 +1,802 @@ +/* + * Copyright (C) 2023 ML!PA Consulting GmbH + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_samd5x + * @{ + * + * @file + * @brief Implementation of the CAN controller driver + * + * @author Firas Hamdi + * @} + */ + +#include +#include + +#include "periph/can.h" +#include "periph/gpio.h" +#include "can/device.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +/** + * @brief Value from SAMD5x/E5x Family datasheet, Tables 39-13 and 39-17 + */ +#define CANDEV_SAMD5X_CLASSIC_FILTER 0x02 +/** + * @brief Set to 1 to access the internal loopback mode. + * Check SAMD5x/E5x Family datasheet, Figure 39-4 + */ +#define CANDEV_SAMD5X_INTERNAL_LOOPBACK 0 + +/** + * @brief Specific configuration of the CAN filter + */ +enum { + CANDEV_SAMD5X_FILTER_DISABLE = 0x00, + CANDEV_SAMD5X_FILTER_RX_FIFO_0, + CANDEV_SAMD5X_FILTER_RX_FIFO_1 +}; + +/** + * @brief Configuration of how to handle frames not matching the CAN filters + */ +enum { + /* Direct frames not matching any CAN filters applied to Rx FIFO 0 */ + CAN_ACCEPT_RX_FIFO_0 = 0x00, + /* Direct frames not matching any CAN filters applied to Rx FIFO 1 */ + CAN_ACCEPT_RX_FIFO_1, + /* Reject all frames not matching any CAN filters applied */ + CAN_REJECT +}; + +typedef enum { + MODE_INIT, + MODE_TEST, +} can_mode_t; + +typedef struct { + /* Message Marker Put index */ + uint8_t put:4; + /* Message Marker Get index */ + uint8_t get:4; +} can_mm_t; + +/* Used to handle interrupts generated by the CAN controller 0 */ +static can_t *_can_0; +/* Used to handle interrupts generated by the CAN controller 1 */ +static can_t *_can_1; + +static int _init(candev_t *candev); +static int _send(candev_t *candev, const struct can_frame *frame); +static int _set_filter(candev_t *candev, const struct can_filter *filter); +static int _remove_filter(candev_t *candev, const struct can_filter *filter); +static int _set(candev_t *candev, canopt_t opt, void *value, size_t value_len); +static void _isr(candev_t *candev); + +static int _set_mode(Can *can, can_mode_t mode); + +static const candev_driver_t candev_samd5x_driver = { + .init = _init, + .send = _send, + .set_filter = _set_filter, + .remove_filter = _remove_filter, + .set = _set, + .isr = _isr, +}; + +/* Values taken from SAMD5x/E5x datasheet section 39.8.8 */ +static const struct can_bittiming_const bittiming_const = { + .tseg1_min = 1, + .tseg1_max = 256, + .tseg2_min = 1, + .tseg2_max = 128, + .sjw_max = 128, + .brp_min = 1, + .brp_max = 512, + .brp_inc = 1, +}; + +static int _power_on(can_t *dev) +{ + if (dev->conf->can == CAN0) { + DEBUG_PUTS("CAN0 controller is used"); + MCLK->AHBMASK.reg |= MCLK_AHBMASK_CAN0; + } + else if (dev->conf->can == CAN1) { + DEBUG_PUTS("CAN1 controller is used"); + MCLK->AHBMASK.reg |= MCLK_AHBMASK_CAN1; + } + else { + DEBUG_PUTS("Unsupported CAN channel"); + assert(0); + } + + return 0; +} + +static int _power_off(can_t *dev) +{ + if (dev->conf->can == CAN0) { + DEBUG_PUTS("CAN0 controller is used"); + MCLK->AHBMASK.reg &= ~MCLK_AHBMASK_CAN0; + } + else if (dev->conf->can == CAN1) { + DEBUG_PUTS("CAN1 controller is used"); + MCLK->AHBMASK.reg &= ~MCLK_AHBMASK_CAN1; + } + else { + DEBUG_PUTS("Unsupported CAN channel"); + return -1; + } + + return 0; +} + +static void _enter_init_mode(Can *can) +{ + can->CCCR.reg |= CAN_CCCR_INIT; + while (!(can->CCCR.reg & CAN_CCCR_INIT)) {} + DEBUG_PUTS("Device in init mode"); +} + +static void _exit_init_mode(Can *can) +{ + if (can->CCCR.reg & CAN_CCCR_INIT) { + can->CCCR.reg &= ~CAN_CCCR_INIT; + } + + while (can->CCCR.reg & CAN_CCCR_INIT) {} + DEBUG_PUTS("Device out of init mode"); +} + +static int _set_mode(Can *can, can_mode_t can_mode) +{ + switch (can_mode) { + case MODE_INIT: + _enter_init_mode(can); + can->CCCR.reg |= CAN_CCCR_CCE; + break; + case MODE_TEST: + DEBUG_PUTS("test mode"); + _enter_init_mode(can); + /* CCCR.TEST and CCCR.MON can be set only when CCCR.INIT and CCCR.CCE are set */ + can->CCCR.reg |= CAN_CCCR_CCE; + can->CCCR.reg |= CAN_CCCR_TEST; + can->TEST.reg |= CAN_TEST_LBCK; +#if IS_ACTIVE(CANDEV_SAMD5X_INTERNAL_LOOPBACK) + can->CCCR.reg |= CAN_CCCR_MON; +#endif + _exit_init_mode(can); + break; + default: + DEBUG_PUTS("Unsupported mode"); + return -1; + } + + return 0; +} + +static void _setup_clock(can_t *dev) +{ + if (dev->conf->can == CAN0) { + GCLK->PCHCTRL[CAN0_GCLK_ID].reg = GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN(dev->conf->gclk_src); + } + else if (dev->conf->can == CAN1) { + GCLK->PCHCTRL[CAN1_GCLK_ID].reg = GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN(dev->conf->gclk_src); + } + else { + DEBUG_PUTS("CAN channel not supported"); + } +} + +static void _set_bit_timing(can_t *dev) +{ + assert(dev->candev.bittiming.sjw >= 1); + assert(dev->candev.bittiming.phase_seg2 >= 1); + assert(dev->candev.bittiming.phase_seg1 + dev->candev.bittiming.prop_seg >= 1); + assert(dev->candev.bittiming.brp >= 1); + + DEBUG("bitrate=%" PRIu32 ", sample_point=%" PRIu32 ", brp=%" PRIu32 ", prop_seg=%" PRIu32 + ", phase_seg1=%" PRIu32 ", phase_seg2=%" PRIu32 ", sjw=%" PRIu32 "\n", + dev->candev.bittiming.bitrate, dev->candev.bittiming.sample_point, + dev->candev.bittiming.brp, dev->candev.bittiming.prop_seg, + dev->candev.bittiming.phase_seg1, dev->candev.bittiming.phase_seg2, + dev->candev.bittiming.sjw); + + /* Set bit timing */ + dev->conf->can->NBTP.reg = (uint32_t)((CAN_NBTP_NTSEG2(dev->candev.bittiming.phase_seg2 - 1)) + | (CAN_NBTP_NTSEG1(dev->candev.bittiming.phase_seg1 + dev->candev.bittiming.prop_seg - 1)) + | (CAN_NBTP_NBRP(dev->candev.bittiming.brp - 1)) + | (CAN_NBTP_NSJW(dev->candev.bittiming.sjw - 1))); +} + +static void _set_tx_fifo_data_size(can_t *dev, uint8_t size) { + assert(size < 0x8); + dev->conf->can->TXESC.reg |= CAN_TXESC_TBDS(size); +} + +static void _set_rx_buffer_data_size(can_t *dev, uint8_t size) { + assert(size < 0x8); + dev->conf->can->RXESC.reg |= CAN_RXESC_RBDS(size); +} + +static void _set_rx_fifo_0_data_size(can_t *dev, uint8_t size) { + assert(size < 0x8); + dev->conf->can->RXESC.reg |= CAN_RXESC_F0DS(size); +} + +static void _set_rx_fifo_1_data_size(can_t *dev, uint8_t size) { + assert(size < 0x8); + dev->conf->can->RXESC.reg |= CAN_RXESC_F1DS(size); +} + +static void _set_can_pins(can_t *dev) +{ + assert(dev->conf->tx_pin != GPIO_UNDEF); + assert(dev->conf->rx_pin != GPIO_UNDEF); + + gpio_init(dev->conf->tx_pin, GPIO_OUT); + gpio_init(dev->conf->rx_pin, GPIO_IN); + + if (dev->conf->can == CAN0) { + gpio_init_mux(dev->conf->tx_pin, GPIO_MUX_I); + gpio_init_mux(dev->conf->rx_pin, GPIO_MUX_I); + } + else if (dev->conf->can == CAN1) { + gpio_init_mux(dev->conf->tx_pin, GPIO_MUX_H); + gpio_init_mux(dev->conf->rx_pin, GPIO_MUX_H); + } + else { + DEBUG_PUTS("Unsupported can channel"); + } +} + +void candev_samd5x_enter_sleep_mode(candev_t *candev) +{ + can_t *dev = container_of(candev, can_t, candev); + + dev->conf->can->CCCR.reg |= CAN_CCCR_CSR; + while (!(dev->conf->can->CCCR.reg & CAN_CCCR_CSA)) {} + DEBUG_PUTS("Device in sleep mode"); +} + +void candev_samd5x_exit_sleep_mode(candev_t *candev) +{ + can_t *dev = container_of(candev, can_t, candev); + + dev->conf->can->CCCR.reg &= ~CAN_CCCR_CSR; + while (dev->conf->can->CCCR.reg & CAN_CCCR_CSA) {} + DEBUG_PUTS("Device out of sleep mode"); +} + +void candev_samd5x_tdc_control(can_t *dev) +{ + if (dev->tdc_ctrl) { + DEBUG_PUTS("Enable Transceiver Delay Compensation"); + dev->conf->can->DBTP.reg |= CAN_DBTP_TDC; + } + else { + DEBUG_PUTS("Disable Transceiver Delay Compensation"); + dev->conf->can->DBTP.reg &= ~(CAN_DBTP_TDC); + } +} + +void can_init(can_t *dev, const can_conf_t *conf) +{ + dev->candev.driver = &candev_samd5x_driver; + + struct can_bittiming timing = { .bitrate = CANDEV_SAMD5X_DEFAULT_BITRATE, + .sample_point = CANDEV_SAMD5X_DEFAULT_SPT }; + + uint32_t clk_freq = sam0_gclk_freq(conf->gclk_src); + can_device_calc_bittiming(clk_freq, &bittiming_const, &timing); + + memcpy(&dev->candev.bittiming, &timing, sizeof(timing)); + dev->conf = conf; +} + +static void _dump_msg_ram_section(can_t *dev) +{ + puts("start address|\tsize of section"); + printf("Standard filters|\t0x%02lx|\t%lu\n", (uint32_t)(dev->msg_ram_conf.std_filter), + (uint32_t)(ARRAY_SIZE(dev->msg_ram_conf.std_filter))); + printf("Extended filters|\t0x%02lx|\t%lu\n", (uint32_t)(dev->msg_ram_conf.ext_filter), + (uint32_t)(ARRAY_SIZE(dev->msg_ram_conf.ext_filter))); + printf("Rx FIFO 0|\t0x%02lx|\t%lu\n", (uint32_t)(dev->msg_ram_conf.rx_fifo_0), + (uint32_t)(ARRAY_SIZE(dev->msg_ram_conf.rx_fifo_0))); + printf("Rx FIFO 1|\t0x%02lx|\t%lu\n", (uint32_t)(dev->msg_ram_conf.rx_fifo_1), + (uint32_t)(ARRAY_SIZE(dev->msg_ram_conf.rx_fifo_1))); + printf("Rx buffer|\t0x%02lx|\t%lu\n", (uint32_t)(dev->msg_ram_conf.rx_buffer), + (uint32_t)(ARRAY_SIZE(dev->msg_ram_conf.rx_buffer))); + printf("Tx event FIFO|\t0x%02lx|\t%lu\n", (uint32_t)(dev->msg_ram_conf.tx_event_fifo), + (uint32_t)(ARRAY_SIZE(dev->msg_ram_conf.tx_event_fifo))); + printf("Tx buffer|\t0x%02lx|\t%lu\n", (uint32_t)(dev->msg_ram_conf.tx_fifo_queue), + (uint32_t)(ARRAY_SIZE(dev->msg_ram_conf.tx_fifo_queue))); +} + +static int _init(candev_t *candev) +{ + can_t *dev = container_of(candev, can_t, candev); + int res = 0; + + sam0_gclk_enable(dev->conf->gclk_src); + + _setup_clock(dev); + _power_on(dev); + + _set_can_pins(dev); + + res = _set_mode(dev->conf->can, MODE_INIT); + if (res != 0) { + return -1; + } + + _set_bit_timing(dev); + + candev_samd5x_tdc_control(dev); + + /*Configure the start addresses of the RAM message sections */ + dev->conf->can->SIDFC.reg = CAN_SIDFC_FLSSA((uint32_t)(dev->msg_ram_conf.std_filter)) + | CAN_SIDFC_LSS((uint32_t)(ARRAY_SIZE(dev->msg_ram_conf.std_filter))); + dev->conf->can->XIDFC.reg = CAN_XIDFC_FLESA((uint32_t)(dev->msg_ram_conf.ext_filter)) + | CAN_XIDFC_LSE((uint32_t)(ARRAY_SIZE(dev->msg_ram_conf.ext_filter))); + dev->conf->can->RXF0C.reg = CAN_RXF0C_F0SA((uint32_t)(dev->msg_ram_conf.rx_fifo_0)) + | CAN_RXF0C_F0S((uint32_t)(ARRAY_SIZE(dev->msg_ram_conf.rx_fifo_0))); + dev->conf->can->RXF1C.reg = CAN_RXF1C_F1SA((uint32_t)(dev->msg_ram_conf.rx_fifo_1)) + | CAN_RXF1C_F1S((uint32_t)(ARRAY_SIZE(dev->msg_ram_conf.rx_fifo_1))); + dev->conf->can->RXBC.reg = CAN_RXBC_RBSA((uint32_t)(dev->msg_ram_conf.rx_buffer)); + dev->conf->can->TXEFC.reg = CAN_TXEFC_EFSA((uint32_t)(dev->msg_ram_conf.tx_event_fifo)) + | CAN_TXEFC_EFS((uint32_t)(ARRAY_SIZE(dev->msg_ram_conf.tx_event_fifo))); + dev->conf->can->TXBC.reg = CAN_TXBC_TBSA((uint32_t)(dev->msg_ram_conf.tx_fifo_queue)) + | CAN_TXBC_TFQS((uint32_t)(ARRAY_SIZE(dev->msg_ram_conf.tx_fifo_queue))); + + /* In the vendor file, the data field size in CanMramTxbe is set to 64 bytes + although it can be configurable. That's why 64 bytes is used here by default */ + _set_tx_fifo_data_size(dev, CAN_RXESC_F1DS_DATA64_Val); + /* In the vendor file, the data field size in CanMramRxbe is set to 64 bytes + although it can be configurable. That's why 64 bytes is used here by default */ + _set_rx_buffer_data_size(dev, CAN_RXESC_RBDS_DATA64_Val); + /* In the vendor file, the data field size in CanMramRxf0e is set to 64 bytes + although it can be configurable. That's why 64 bytes is used here by default */ + _set_rx_fifo_0_data_size(dev, CAN_RXESC_F0DS_DATA64_Val); + /* In the vendor file, the data field size in CanMramRxf1e is set to 64 bytes + although it can be configurable. That's why 64 bytes is used here by default */ + _set_rx_fifo_1_data_size(dev, CAN_RXESC_F1DS_DATA64_Val); + + if (IS_ACTIVE(ENABLE_DEBUG)) { + _dump_msg_ram_section(dev); + } + /* Disable automatic retransmission by default */ + /* This can be added as a configuration parameter for the CAN controller */ + dev->conf->can->CCCR.reg |= CAN_CCCR_DAR; + + /* Reject all remote frames */ + dev->conf->can->GFC.reg |= CAN_GFC_RRFE | CAN_GFC_RRFS; + + /* Enable reception interrupts: reception on FIFO0 and FIFO1 */ + dev->conf->can->IE.reg |= CAN_IE_RF0NE | CAN_IE_RF1NE; + /* Enable transmission events interrupts */ + dev->conf->can->IE.reg |= CAN_IE_TEFNE; + /* Enable the interrupt lines */ + dev->conf->can->ILE.reg = CAN_ILE_EINT0 | CAN_ILE_EINT1; + + /* Enable the peripheral's interrupt */ + if (dev->conf->can == CAN0) { + NVIC_EnableIRQ(CAN0_IRQn); + _can_0 = dev; + } + else { + NVIC_EnableIRQ(CAN1_IRQn); + _can_1 = dev; + } + + /* Exit initialization mode */ + _exit_init_mode(dev->conf->can); + + return res; +} + +static uint8_t _form_message_marker(can_mm_t *can_mm) +{ + return can_mm->put | (can_mm->get << 4); +} + +static int _send(candev_t *candev, const struct can_frame *frame) +{ + can_t *dev = container_of(candev, can_t, candev); + + if (frame->can_dlc > CAN_MAX_DLEN) { + DEBUG_PUTS("CAN frame payload not supported"); + return -1; + } + + /* Check if the Tx FIFO is full */ + if (dev->conf->can->TXFQS.reg & CAN_TXFQS_TFQF) { + DEBUG_PUTS("Tx FIFO is full"); + return -1; + } + + can_mm_t can_mm = {0}; + uint8_t fifo_queue_put_idx = (dev->conf->can->TXFQS.reg >> CAN_TXFQS_TFQPI_Pos) & 0x1F; + DEBUG("Tx FIFO put index = %u\n", fifo_queue_put_idx); + uint8_t fifo_queue_get_idx = (dev->conf->can->TXFQS.reg >> CAN_TXFQS_TFGI_Pos) & 0x1F; + DEBUG("Tx FIFO get index = %u\n", fifo_queue_get_idx); + can_mm.put = fifo_queue_put_idx; + can_mm.get = fifo_queue_get_idx; + + if (frame->can_id & CAN_EFF_FLAG) { + DEBUG_PUTS("Extended ID"); + dev->msg_ram_conf.tx_fifo_queue[can_mm.put].TXBE_0.bit.ID = frame->can_id & CAN_EFF_MASK; + dev->msg_ram_conf.tx_fifo_queue[can_mm.put].TXBE_0.bit.XTD = 1; + } + else { + DEBUG_PUTS("Standard identifier"); + dev->msg_ram_conf.tx_fifo_queue[can_mm.put].TXBE_0.bit.ID = (frame->can_id & CAN_SFF_MASK) << 18; + dev->msg_ram_conf.tx_fifo_queue[can_mm.put].TXBE_0.bit.XTD = 0; + } + dev->msg_ram_conf.tx_fifo_queue[can_mm.put].TXBE_0.bit.RTR = (frame->can_id & CAN_RTR_FLAG) >> 30; + dev->msg_ram_conf.tx_fifo_queue[can_mm.put].TXBE_0.bit.ESI = 1; + dev->msg_ram_conf.tx_fifo_queue[can_mm.put].TXBE_1.bit.DLC = frame->can_dlc; + dev->msg_ram_conf.tx_fifo_queue[can_mm.put].TXBE_1.bit.EFC = 1; + dev->msg_ram_conf.tx_fifo_queue[can_mm.put].TXBE_1.bit.MM = _form_message_marker(&can_mm); + memcpy((void *)dev->msg_ram_conf.tx_fifo_queue[can_mm.put].TXBE_DATA, frame->data, frame->can_dlc); + + /* Request transmission */ + dev->conf->can->TXBAR.reg |= (1 << can_mm.put); + + return 0; +} + +static int16_t _find_filter(can_t *can, const struct can_filter *filter, bool is_std_filter) +{ + int16_t idx = -1; + + /* Standard filter */ + if (is_std_filter) { + /* Search for the standard filter in the CAN controller message RAM */ + for (uint8_t i = 0; i < ARRAY_SIZE(can->msg_ram_conf.std_filter); i++) { + if (((filter->can_id & CAN_SFF_MASK) == can->msg_ram_conf.std_filter[i].SIDFE_0.bit.SFID1)) { + idx = i; + break; + } + } + } + /* Extended filter */ + else { + /* Search for the extended filter in the CAN controller message RAM */ + for (uint8_t i = 0; i < ARRAY_SIZE(can->msg_ram_conf.ext_filter); i++) { + if (((filter->can_id & CAN_EFF_MASK) == can->msg_ram_conf.ext_filter[i].XIDFE_0.bit.EFID1)) { + idx = i; + break; + } + } + } + + return idx; +} + +static int _set_filter(candev_t *candev, const struct can_filter *filter) +{ + can_t *dev = container_of(candev, can_t, candev); + + int16_t idx = 0; + uint8_t filter_conf = 0; + switch (filter->target_mailbox) { + case 0: + filter_conf = CANDEV_SAMD5X_FILTER_RX_FIFO_0; + break; + case 1 : + filter_conf = CANDEV_SAMD5X_FILTER_RX_FIFO_1; + break; + default: + puts("Invalid target mailbox --> Do not apply filter"); + return -1; + } + if (filter->can_id & CAN_EFF_FLAG) { + DEBUG_PUTS("Extended filter to add in the extended filter section of the message RAM"); + /* Check if the filter already exists */ + idx = _find_filter(dev, filter, false); + if (idx != -1) { + DEBUG_PUTS("Extended filter already exists --> Update it"); + } + else { + /* Find a free slot where to save the filter */ + for (idx = 0; (uint16_t)idx < ARRAY_SIZE(dev->msg_ram_conf.ext_filter); idx++) { + if (dev->msg_ram_conf.ext_filter[idx].XIDFE_0.bit.EFEC == CANDEV_SAMD5X_FILTER_DISABLE) { + DEBUG_PUTS("empty slot"); + break; + } + } + } + + if (idx == ARRAY_SIZE(dev->msg_ram_conf.ext_filter)) { + DEBUG_PUTS("Reached maximum capacity of extended filters --> Could not add filter"); + return -1; + } + + DEBUG("Extended Filter to add at idx = %d\n", idx); + dev->msg_ram_conf.ext_filter[idx].XIDFE_0.bit.EFEC = filter_conf; + /* For now, only CLASSIC filters are supported */ + dev->msg_ram_conf.ext_filter[idx].XIDFE_1.bit.EFT = CANDEV_SAMD5X_CLASSIC_FILTER; + dev->msg_ram_conf.ext_filter[idx].XIDFE_0.bit.EFID1 = filter->can_id; + dev->msg_ram_conf.ext_filter[idx].XIDFE_1.bit.EFID2 = filter->can_mask & CAN_EFF_MASK; + DEBUG("Extended message ID filter element N°%d = 0x%02lx\n", idx, + (uint32_t)(dev->msg_ram_conf.std_filter[idx].SIDFE_0.reg)); + _set_mode(dev->conf->can, MODE_INIT); + /* Reject all extended frames that are not matching the filters applied */ + dev->conf->can->GFC.reg |= CAN_GFC_ANFE((uint32_t)CAN_REJECT); + _exit_init_mode(dev->conf->can); + } + else { + DEBUG_PUTS("Standard filter to add in the standard filter section of the message RAM"); + /* Check if the filter already exists */ + idx = _find_filter(dev, filter, true); + if (idx != -1) { + DEBUG_PUTS("Standard filter already exists --> Update it"); + } + else { + /* Find a free slot where to save the filter */ + for (idx = 0; (uint16_t)idx < ARRAY_SIZE(dev->msg_ram_conf.std_filter); idx++) { + if (dev->msg_ram_conf.std_filter[idx].SIDFE_0.bit.SFEC == CANDEV_SAMD5X_FILTER_DISABLE) { + DEBUG_PUTS("empty slot"); + break; + } + } + } + + if (idx == ARRAY_SIZE(dev->msg_ram_conf.std_filter)) { + DEBUG_PUTS("Reached maximum capacity of standard filters --> Could not add filter"); + return -1; + } + + DEBUG("Standard Filter to add at idx = %d\n", idx); + dev->msg_ram_conf.std_filter[idx].SIDFE_0.bit.SFEC = filter_conf; + /* For now, only CLASSIC filters are supported */ + dev->msg_ram_conf.std_filter[idx].SIDFE_0.bit.SFT = CANDEV_SAMD5X_CLASSIC_FILTER; + dev->msg_ram_conf.std_filter[idx].SIDFE_0.bit.SFID1 = filter->can_id & CAN_SFF_MASK; + dev->msg_ram_conf.std_filter[idx].SIDFE_0.bit.SFID2 = filter->can_mask & CAN_SFF_MASK; + DEBUG("Standard message ID filter element N°%d = 0x%02lx\n", idx, + (uint32_t)(dev->msg_ram_conf.std_filter[idx].SIDFE_0.reg)); + _set_mode(dev->conf->can, MODE_INIT); + /* Reject all standard frames that are not matching the filters applied */ + dev->conf->can->GFC.reg |= CAN_GFC_ANFS((uint32_t)CAN_REJECT); + _exit_init_mode(dev->conf->can); + } + + return idx; +} + +static int _remove_filter(candev_t *candev, const struct can_filter *filter) +{ + can_t *dev = container_of(candev, can_t, candev); + + int16_t idx = 0; + if (filter->can_id & CAN_EFF_FLAG) { + idx = _find_filter(dev, filter, false); + if (idx != -1) { + DEBUG("Extended filter to disable at idx = %d\n", idx); + dev->msg_ram_conf.ext_filter[idx].XIDFE_0.bit.EFEC = CANDEV_SAMD5X_FILTER_DISABLE; + } + else { + DEBUG_PUTS("Filter not found"); + return -1; + } + } + else { + idx = _find_filter(dev, filter, true); + if (idx != -1) { + DEBUG("Standard filter to disable at idx = %d\n", idx); + dev->msg_ram_conf.std_filter[idx].SIDFE_0.bit.SFEC = CANDEV_SAMD5X_FILTER_DISABLE; + } + else { + DEBUG_PUTS("Filter not found"); + return -1; + } + } + + return idx; +} + +static int _set(candev_t *candev, canopt_t opt, void *value, size_t value_len) +{ + can_t *dev = container_of(candev, can_t, candev); + int res = 0; + + switch (opt) { + case CANOPT_BITTIMING: + if (value_len < sizeof(struct can_bittiming)) { + return -1; + } + else { + memcpy(&candev->bittiming, value, sizeof(struct can_bittiming)); + uint32_t clk_freq = sam0_gclk_freq(dev->conf->gclk_src); + can_device_calc_bittiming(clk_freq, &bittiming_const, &candev->bittiming); + res = _init(candev); + if (res == 0) { + res = sizeof(candev->bittiming); + } + else { + return -1; + } + } + break; + case CANOPT_RX_FILTERS: + if (value_len < sizeof(struct can_filter)) { + return -1; + } + else { + res = _set_filter(candev, value); + if (res >= 0) { + res = sizeof(struct can_filter); + } + else { + return -1; + } + } + break; + case CANOPT_STATE: + if (value_len < sizeof(canopt_state_t)) { + return -1; + } + else { + switch (*((canopt_state_t *)value)) { + case CANOPT_STATE_OFF: + res = _power_off(dev); + if (res == 0) { + res = sizeof(canopt_state_t); + } + else { + return -1; + } + break; + case CANOPT_STATE_ON: + res = _power_on(dev); + if (res == 0) { + res = sizeof(canopt_state_t); + } + else { + return -1; + } + break; + case CANOPT_STATE_LOOPBACK: + res = _set_mode(dev->conf->can, MODE_TEST); + if (res == 0) { + res = sizeof(canopt_state_t); + } + else { + return -1; + } + break; + default: + break; + } + } + default: + break; + } + + return res; +} + +static void _isr(candev_t *candev) +{ + can_t *dev = container_of(candev, can_t, candev); + + uint32_t irq_reg = dev->conf->can->IR.reg; + DEBUG("isr: IR reg = 0x%02lx\n", irq_reg); + + /* Interrupt triggered due to reception of CAN frame on Rx_FIFO_0 */ + if (irq_reg & CAN_IR_RF0N) { + DEBUG_PUTS("New message in Rx FIFO 0"); + /* Clear the interrupt source flag */ + dev->conf->can->IR.reg |= CAN_IR_RF0N; + + uint16_t rx_get_idx = 0; + uint16_t rx_put_idx = 0; + rx_get_idx = (dev->conf->can->RXF0S.reg >> CAN_RXF0S_F0GI_Pos) & 0x3F; + DEBUG("rx get index = %u\n", rx_get_idx); + rx_put_idx = (dev->conf->can->RXF0S.reg >> CAN_RXF0S_F0PI_Pos) & 0x3F; + DEBUG("rx put index = %u\n", rx_put_idx); + + struct can_frame frame_received = {0}; + if (!dev->msg_ram_conf.rx_fifo_0[rx_get_idx].RXF0E_0.bit.XTD) { + DEBUG_PUTS("Received standard CAN frame"); + frame_received.can_id = dev->msg_ram_conf.rx_fifo_0[rx_get_idx].RXF0E_0.bit.ID >> 18; + } + else { + DEBUG_PUTS("Received extended CAN frame"); + frame_received.can_id = dev->msg_ram_conf.rx_fifo_0[rx_get_idx].RXF0E_0.bit.ID; + } + frame_received.can_dlc = dev->msg_ram_conf.rx_fifo_0[rx_get_idx].RXF0E_1.bit.DLC; + memcpy(frame_received.data, (uint32_t *)dev->msg_ram_conf.rx_fifo_0[rx_get_idx].RXF0E_DATA, frame_received.can_dlc); + + dev->conf->can->RXF0A.reg = CAN_RXF0A_F0AI(rx_get_idx); + if (dev->candev.event_callback) { + dev->candev.event_callback(&(dev->candev), CANDEV_EVENT_RX_INDICATION, &frame_received); + } + } + + /* Interrupt triggered due to reception of CAN frame on Rx_FIFO_1 */ + if (irq_reg & CAN_IR_RF1N) { + DEBUG_PUTS("New message in Rx FIFO 1"); + /* Clear the interrupt source flag */ + dev->conf->can->IR.reg |= CAN_IR_RF1N; + + uint16_t rx_get_idx = 0; + uint16_t rx_put_idx = 0; + rx_get_idx = (dev->conf->can->RXF1S.reg >> CAN_RXF1S_F1GI_Pos) & 0x3F; + DEBUG("rx get index = %u\n", rx_get_idx); + rx_put_idx = (dev->conf->can->RXF1S.reg >> CAN_RXF1S_F1PI_Pos) & 0x3F; + DEBUG("rx put index = %u\n", rx_put_idx); + + struct can_frame frame_received = {0}; + if (!dev->msg_ram_conf.rx_fifo_1[rx_get_idx].RXF1E_0.bit.XTD) { + DEBUG_PUTS("Received standard CAN frame"); + frame_received.can_id = dev->msg_ram_conf.rx_fifo_1[rx_get_idx].RXF1E_0.bit.ID >> 18; + } + else { + DEBUG_PUTS("Received extended CAN frame"); + frame_received.can_id = dev->msg_ram_conf.rx_fifo_1[rx_get_idx].RXF1E_0.bit.ID; + } + frame_received.can_dlc = dev->msg_ram_conf.rx_fifo_1[rx_get_idx].RXF1E_1.bit.DLC; + memcpy(frame_received.data, (uint32_t *)dev->msg_ram_conf.rx_fifo_1[rx_get_idx].RXF1E_DATA, frame_received.can_dlc); + + dev->conf->can->RXF1A.reg = CAN_RXF1A_F1AI(rx_get_idx); + if (dev->candev.event_callback) { + dev->candev.event_callback(&(dev->candev), CANDEV_EVENT_RX_INDICATION, &frame_received); + } + } + + /* Interrupt triggered due to new transmission event */ + if (irq_reg & CAN_IR_TEFN) { + DEBUG_PUTS("New Tx event FIFO entry"); + dev->conf->can->IR.reg |= CAN_IR_TEFN; + if (dev->candev.event_callback) { + dev->candev.event_callback(&(dev->candev), CANDEV_EVENT_TX_CONFIRMATION, NULL); + } + } + + /* Enable the peripheral's interrupt */ + if (dev->conf->can == CAN0) { + NVIC_EnableIRQ(CAN0_IRQn); + } + else { + NVIC_EnableIRQ(CAN1_IRQn); + } +} + +#ifdef ISR_CAN0 +void ISR_CAN0(void) +{ + DEBUG_PUTS("ISR CAN0"); + + /* Disable the peripheral's interrupt to avoid potential 'interrupt bouncing' */ + NVIC_DisableIRQ(CAN0_IRQn); + if (_can_0->candev.event_callback) { + _can_0->candev.event_callback(&(_can_0->candev), CANDEV_EVENT_ISR, NULL); + } +} +#endif + +#ifdef ISR_CAN1 +void ISR_CAN1(void) +{ + DEBUG_PUTS("ISR CAN1"); + + /* Disable the peripheral's interrupt to avoid potential 'interrupt bouncing' */ + NVIC_DisableIRQ(CAN1_IRQn); + if (_can_1->candev.event_callback) { + _can_1->candev.event_callback(&(_can_1->candev), CANDEV_EVENT_ISR, NULL); + } + +} +#endif diff --git a/dist/tools/doccheck/exclude_simple b/dist/tools/doccheck/exclude_simple index 13755d6795cc3..7b46ec40e2939 100644 --- a/dist/tools/doccheck/exclude_simple +++ b/dist/tools/doccheck/exclude_simple @@ -8424,3 +8424,4 @@ warning: Member LSM6DSXX_PARAM_GYRO_FIFO_DEC (macro definition) of file lsm6dsxx warning: Member LSM6DSXX_PARAMS (macro definition) of file lsm6dsxx_params.h is not documented. warning: Member LSM6DSXX_SAUL_INFO (macro definition) of file lsm6dsxx_params.h is not documented. RIOTDoxygenLayout.xml:8: warning: the type 'topics' is not supported for the entry tag within a navindex! Check your layout file! +warning: Member AT6561_STBY_PIN (macro definition) of file board.h is not documented. diff --git a/drivers/mcp2515/Makefile.dep b/drivers/mcp2515/Makefile.dep index e034a1e4ce8b8..6f5bb24591f6e 100644 --- a/drivers/mcp2515/Makefile.dep +++ b/drivers/mcp2515/Makefile.dep @@ -1,2 +1,3 @@ USEMODULE += ztimer_usec +USEMODULE += can_rx_mailbox FEATURES_REQUIRED += periph_gpio periph_spi periph_gpio_irq diff --git a/features.yaml b/features.yaml index 4b979f21c5a43..a5ca87313263d 100644 --- a/features.yaml +++ b/features.yaml @@ -651,6 +651,8 @@ groups: features: - name: periph_can help: A CAN peripheral is present. + - name: can_rx_mailbox + help: CAN controller RX mailbox is supported - name: periph_eth help: An Ethernet peripheral is present. diff --git a/makefiles/features_existing.inc.mk b/makefiles/features_existing.inc.mk index 5d4d7359cdabc..85c42d6850899 100644 --- a/makefiles/features_existing.inc.mk +++ b/makefiles/features_existing.inc.mk @@ -43,6 +43,7 @@ FEATURES_EXISTING := \ ble_phy_2mbit \ ble_phy_coded \ bootloader_stm32 \ + can_rx_mailbox \ cortexm_fpu \ cortexm_mpu \ cortexm_svc \ diff --git a/makefiles/features_modules.inc.mk b/makefiles/features_modules.inc.mk index 8b02fa96b2d21..a0a6d64ac54b2 100644 --- a/makefiles/features_modules.inc.mk +++ b/makefiles/features_modules.inc.mk @@ -59,6 +59,9 @@ DEFAULT_MODULE += $(PERIPH_INIT_MODULES) # select cpu_check_address pseudomodule if the corresponding feature is used USEMODULE += $(filter cpu_check_address, $(FEATURES_USED)) +# select can_rx_mailbox pseudomodule if the corresponding feature is used +USEMODULE += $(filter can_rx_mailbox, $(FEATURES_USED)) + # select bootloader_stm32 module if the feature is used USEMODULE += $(filter bootloader_stm32, $(FEATURES_USED)) diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index 2a3c4be80e606..13fc368bb4520 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -38,6 +38,7 @@ PSEUDOMODULES += arduino_serial_stdio PSEUDOMODULES += can_mbox PSEUDOMODULES += can_pm PSEUDOMODULES += can_raw +PSEUDOMODULES += can_rx_mailbox PSEUDOMODULES += ccn-lite-utils PSEUDOMODULES += cc2538_rf_obs_sig PSEUDOMODULES += conn_can_isotp_multi diff --git a/sys/include/can/can.h b/sys/include/can/can.h index 749e9a6f0b0e7..b0974aa743fb7 100644 --- a/sys/include/can/can.h +++ b/sys/include/can/can.h @@ -101,7 +101,7 @@ struct can_frame { struct can_filter { canid_t can_id; /**< CAN ID */ canid_t can_mask; /**< Mask */ -#if defined(MODULE_MCP2515) +#if (MODULE_CAN_RX_MAILBOX) uint8_t target_mailbox; /**< The mailbox to apply the filter to */ #endif }; diff --git a/tests/drivers/candev/Makefile.board.dep b/tests/drivers/candev/Makefile.board.dep index 46eda88fd426d..25c7a1d8ffff8 100644 --- a/tests/drivers/candev/Makefile.board.dep +++ b/tests/drivers/candev/Makefile.board.dep @@ -5,8 +5,16 @@ endif # define the CAN driver you want to use here CAN_DRIVER ?= MCP2515 +# define the CPU family used +CPU_USED ?= SAME54 + ifeq ($(CAN_DRIVER), PERIPH_CAN) FEATURES_REQUIRED += periph_can + ifeq (SAME54, $(CPU_USED)) + # You can increase the number of provided filters using this block. Default number : 3 + # CFLAGS += "-DCANDEV_SAMD5X_DEFAULT_STD_FILTER_NUM=5" + # CFLAGS += "-DCANDEV_SAMD5X_DEFAULT_EXT_FILTER_NUM=5" + endif else ifeq ($(CAN_DRIVER), MCP2515) USEMODULE += mcp2515 # Uncomment to enable MCP2515 reception filtering diff --git a/tests/drivers/candev/main.c b/tests/drivers/candev/main.c index e9f7f417372f5..48f778813a97a 100644 --- a/tests/drivers/candev/main.c +++ b/tests/drivers/candev/main.c @@ -33,6 +33,9 @@ #include "periph/can.h" #include "can_params.h" +#include "board.h" +#include "periph_conf.h" +#include "periph/gpio.h" static can_t periph_dev; @@ -127,7 +130,86 @@ static int _receive(int argc, char **argv) return 0; } +static int _set_bit_rate(int argc, char **argv) +{ + uint32_t bitrate = 250000; + uint32_t sample_point = 875; + int res = 0; + + if (argc == 2) { + bitrate = atoi(argv[1]); + if (!bitrate) { + puts("Usage error: Invalid bitrate"); + return -1; + } + } + if (argc == 3) { + bitrate = atoi(argv[1]); + sample_point = atoi(argv[2]); + if (!bitrate || !sample_point) { + puts("Usage error: Invalid bitrate or sample point"); + return -2; + } + } + + struct can_bittiming bit_timing = { + .bitrate = bitrate, + .sample_point = sample_point, + }; + + res = candev->driver->set(candev, CANOPT_BITTIMING, &bit_timing, sizeof(bit_timing)); + + return res; +} + +static int _set_can_filter(int argc, char **argv) +{ + (void)argc; + (void)argv; + int res = 0; + + struct can_filter filter = { + .can_mask = 0x7FF, + .can_id = 0x1aa, +#if (MODULE_CAN_RX_MAILBOX) + .target_mailbox = 1, +#endif + }; + + res = candev->driver->set(candev, CANOPT_RX_FILTERS, &filter, sizeof(struct can_filter)); + + return res; +} + +static int _power_off(int argc, char **argv) +{ + (void)argc; + (void)argv; + int res = 0; + + canopt_state_t state = CANOPT_STATE_OFF; + res = candev->driver->set(candev, CANOPT_STATE, &state, sizeof(canopt_state_t)); + + return res; +} + +static int _power_on(int argc, char **argv) +{ + (void)argc; + (void)argv; + int res = 0; + + canopt_state_t state = CANOPT_STATE_ON; + res = candev->driver->set(candev, CANOPT_STATE, &state, sizeof(canopt_state_t)); + + return res; +} + static const shell_command_t shell_commands[] = { + { "on", "Turn on the CAN controller", _power_on}, + { "off", "Turn off the CAN controller", _power_off}, + { "set_filter", "set CAN filters", _set_can_filter}, + { "set_bit_rate", "set CAN bit rate", _set_bit_rate}, { "send", "send some data", _send }, { "receive", "receive some data", _receive }, { NULL, NULL, NULL } @@ -157,7 +239,7 @@ static void _can_event_callback(candev_t *dev, candev_event_t event, void *arg) frame = (struct can_frame *)arg; - DEBUG(" id: %" PRIx32 " dlc: %" PRIx8 " data: ", frame->can_id & 0x1FFFFFFF, + DEBUG(" id: %" PRIx32 " dlc: %u data: ", frame->can_id & 0x1FFFFFFF, frame->can_dlc); for (uint8_t i = 0; i < frame->can_dlc; i++) { DEBUG("0x%X ", frame->data[i]); @@ -196,9 +278,13 @@ int main(void) isrpipe_init(&rxbuf, (uint8_t *)rx_ringbuf, sizeof(rx_ringbuf)); #if IS_USED(MODULE_PERIPH_CAN) puts("Initializing CAN periph device"); - can_init(&periph_dev, &(candev_conf[0])); /* vcan0 on native */ - candev = (candev_t *)&periph_dev; -#elif defined(MODULE_MCP2515) + can_init(&periph_dev, &(candev_conf[0])); + candev = &(periph_dev.candev); +#if defined(BOARD_SAME54_XPRO) + gpio_init(AT6561_STBY_PIN, GPIO_OUT); + gpio_clear(AT6561_STBY_PIN); +#endif +#elif defined(MODULE_MCP2515) puts("Initializing MCP2515"); candev_mcp2515_init(&mcp2515_dev, &candev_mcp2515_conf[0]); candev = (candev_t *)&mcp2515_dev; @@ -221,34 +307,45 @@ if (IS_ACTIVE(CONFIG_USE_LOOPBACK_MODE)) { candev->driver->set(candev, CANOPT_STATE, &mode, sizeof(mode)); } +/* Depending from the CAN controller used, this test example will provide different results. +- For MCP2515 standalone CAN controller, the last filter won't be applied +as the first reception mailbox supports up to two filters +- For SAMD5x/E5x CAN controller, and with keeping the default parameters in candev_samd5x.h, +the last filter won't be applied as the CAN controller supports up to 3 standard filters +- For SAMD5x/E5x CAN controller, if you increase the maximum capacity of the standard +filters (check Makefile.board.dep), the last filter can be applied correctly. */ +#if defined(MODULE_MCP2515) if (IS_ACTIVE(MCP2515_RECV_FILTER_EN)) { +#endif /* CAN filters examples */ struct can_filter filter[4]; filter[0].can_mask = 0x7FF; filter[0].can_id = 0x001; -#if defined(MODULE_MCP2515) - filter[0].target_mailbox = 0; /* messages with CAN ID 0x001 will be received in mailbox 0 */ +#if (MODULE_CAN_RX_MAILBOX) + filter[0].target_mailbox = 0; #endif filter[1].can_mask = 0x7FF; filter[1].can_id = 0x002; -#if defined(MODULE_MCP2515) - filter[1].target_mailbox = 1; /* messages with CAN ID 0x002 will be received in mailbox 1 */ +#if (MODULE_CAN_RX_MAILBOX) + filter[1].target_mailbox = 1; #endif filter[2].can_mask = 0x7FF; filter[2].can_id = 0x003; -#if defined(MODULE_MCP2515) - filter[2].target_mailbox = 0; /* messages with CAN ID 0x003 will be received in mailbox 0 */ +#if (MODULE_CAN_RX_MAILBOX) + filter[2].target_mailbox = 0; #endif filter[3].can_mask = 0x7FF; filter[3].can_id = 0x004; -#if defined(MODULE_MCP2515) - filter[3].target_mailbox = 0; /* this filter won't be applied. Reason is no space found in the first mailbox as it supports only two filters */ +#if (MODULE_CAN_RX_MAILBOX) + filter[3].target_mailbox = 1; #endif for (uint8_t i = 0; i < 4; i++) { candev->driver->set_filter(candev, &filter[i]); } /* All other messages won't be received */ +#if defined(MODULE_MCP2515) } +#endif char line_buf[SHELL_DEFAULT_BUFSIZE]; shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE);