diff --git a/cpu/stm32_common/include/can_params.h b/cpu/stm32_common/include/can_params.h new file mode 100644 index 0000000000..ff4f80fd88 --- /dev/null +++ b/cpu/stm32_common/include/can_params.h @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2016 OTA keys S.A. + * + * 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 candev_stm32 + * @{ + * + * @file + * @brief STM32 CAN controller driver (bxCAN) default parameters + * + * @author Vincent Dupont + * @} + */ + +#ifndef CANDEV_STM32_PARAMS_H +#define CANDEV_STM32_PARAMS_H + +#include "can/device.h" +#include "periph/can.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Default STM32 CAN devices config */ +static const can_conf_t candev_conf[] = { + { +#if defined(CPU_FAM_STM32F0) + .can = CAN, + .rcc_mask = RCC_APB1ENR_CANEN, + .rx_pin = GPIO_PIN(PORT_A, 11), + .tx_pin = GPIO_PIN(PORT_A, 12), + .af = GPIO_AF4, + .irqn = CEC_CAN_IRQn, +#else + .can = CAN1, + .rcc_mask = RCC_APB1ENR_CAN1EN, + .can_master = CAN1, + .master_rcc_mask = RCC_APB1ENR_CAN1EN, + .first_filter = 0, + .nb_filters = 14, +#ifndef CPU_FAM_STM32F1 + .rx_pin = GPIO_PIN(PORT_D, 0), + .tx_pin = GPIO_PIN(PORT_D, 1), + .af = GPIO_AF9, +#else + .rx_pin = GPIO_PIN(PORT_A, 11), + .tx_pin = GPIO_PIN(PORT_A, 12), +#endif + .tx_irqn = CAN1_TX_IRQn, + .rx0_irqn = CAN1_RX0_IRQn, + .rx1_irqn = CAN1_RX1_IRQn, + .sce_irqn = CAN1_SCE_IRQn, +#endif + .ttcm = 0, + .abom = 1, + .awum = 1, + .nart = 0, + .rflm = 0, + .txfp = 0, + }, +#if (CANDEV_STM32_CHAN_NUMOF >= 2) && (CAN_DLL_NUMOF >= 2) + { + .can = CAN2, + .rcc_mask = RCC_APB1ENR_CAN2EN, + .can_master = CAN1, + .master_rcc_mask = RCC_APB1ENR_CAN1EN, + .first_filter = 14, + .nb_filters = 14, + .rx_pin = GPIO_PIN(PORT_B, 5), + .tx_pin = GPIO_PIN(PORT_B, 6), +#ifndef CPU_FAM_STM32F1 + .af = GPIO_AF9, +#endif + .tx_irqn = CAN2_TX_IRQn, + .rx0_irqn = CAN2_RX0_IRQn, + .rx1_irqn = CAN2_RX1_IRQn, + .sce_irqn = CAN2_SCE_IRQn, + .ttcm = 0, + .abom = 1, + .awum = 1, + .nart = 0, + .rflm = 0, + .txfp = 0, + }, +#endif +#if (CANDEV_STM32_CHAN_NUMOF >= 3) && (CAN_DLL_NUMOF >= 3) + { + .can = CAN3, + .rcc_mask = RCC_APB1ENR_CAN3EN, + .can_master = CAN3, + .master_rcc_mask = RCC_APB1ENR_CAN3EN, + .first_filter = 0, + .nb_filters = 14, + .rx_pin = GPIO_PIN(PORT_B, 3), + .tx_pin = GPIO_PIN(PORT_B, 4), + .af = GPIO_AF11, + .tx_irqn = CAN3_TX_IRQn, + .rx0_irqn = CAN3_RX0_IRQn, + .rx1_irqn = CAN3_RX1_IRQn, + .sce_irqn = CAN3_SCE_IRQn, + .ttcm = 0, + .abom = 1, + .awum = 1, + .nart = 0, + .rflm = 0, + .txfp = 0, + }, +#endif +}; + +/** Default STM32 CAN devices parameters */ +static const candev_params_t candev_params[] = { + { + .name = "can_stm32_0", + }, +#if (CANDEV_STM32_CHAN_NUMOF >= 2) && (CAN_DLL_NUMOF >= 2) + { + .name = "can_stm32_1", + }, +#endif +#if (CANDEV_STM32_CHAN_NUMOF >= 3) && (CAN_DLL_NUMOF >= 3) + { + .name = "can_stm32_2", + }, +#endif +}; + +#ifdef __cplusplus +} +#endif + +#endif /* CANDEV_STM32_PARAMS_H */ diff --git a/cpu/stm32_common/include/candev_stm32.h b/cpu/stm32_common/include/candev_stm32.h new file mode 100644 index 0000000000..c4ec14860f --- /dev/null +++ b/cpu/stm32_common/include/candev_stm32.h @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2016 OTA keys S.A. + * + * 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_stm32_common + * @ingroup drivers_can + * @defgroup candev_stm32 STM32 CAN controller + * @brief STM32 CAN controller driver (bxCAN) + * + * The STM32Fx microcontroller can have an integrated CAN controller (bxCAN) + * + * This driver has been tested with a STM32F0,STM32F2 and STM32F4 MCU + * but should work on others + * @{ + * + * @file + * @brief bxCAN specific definitions + * + * @author Vincent Dupont + * @} + */ + +#ifndef CANDEV_STM32_H +#define CANDEV_STM32_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "can/candev.h" + +#if defined(CPU_LINE_STM32F413xx) || defined(CPU_LINE_STM32F423xx) +#define CANDEV_STM32_CHAN_NUMOF 3 +#elif defined(CPU_FAM_STM32F1) || defined(CPU_FAM_STM32F2) || defined(CPU_FAM_STM32F4) +#define CANDEV_STM32_CHAN_NUMOF 2 +#elif defined(CPU_FAM_STM32F0) || defined(CPU_FAM_STM32F3) || DOXYGEN +/** Number of channels in the device (up to 3) */ +#define CANDEV_STM32_CHAN_NUMOF 1 +#else +#error "CAN STM32: CPU not supported" +#endif + +#if defined(CPU_FAM_STM32F1) +#define ISR_CAN1_TX isr_usb_hp_can1_tx +#define ISR_CAN1_RX0 isr_usb_lp_can1_rx0 +#define ISR_CAN1_RX1 isr_can1_rx1 +#define ISR_CAN1_SCE isr_can1_sce +#else +#define ISR_CAN1_TX isr_can1_tx +#define ISR_CAN1_RX0 isr_can1_rx0 +#define ISR_CAN1_RX1 isr_can1_rx1 +#define ISR_CAN1_SCE isr_can1_sce +#define ISR_CAN2_TX isr_can2_tx +#define ISR_CAN2_RX0 isr_can2_rx0 +#define ISR_CAN2_RX1 isr_can2_rx1 +#define ISR_CAN2_SCE isr_can2_sce +#define ISR_CAN3_TX isr_can3_tx +#define ISR_CAN3_RX0 isr_can3_rx0 +#define ISR_CAN3_RX1 isr_can3_rx1 +#define ISR_CAN3_SCE isr_can3_sce +#endif + +#if CANDEV_STM32_CHAN_NUMOF > 1 || DOXYGEN +/** The maximum number of filters: 28 for dual channel, 14 for single channel */ +#define CAN_STM32_NB_FILTER 28 +#else +#define CAN_STM32_NB_FILTER 14 +#endif + +#ifndef CANDEV_STM32_DEFAULT_BITRATE +/** Default bitrate */ +#define CANDEV_STM32_DEFAULT_BITRATE 500000U +#endif + +#ifndef CANDEV_STM32_DEFAULT_SPT +/** Default sampling-point */ +#define CANDEV_STM32_DEFAULT_SPT 875 +#endif + +/** bxCAN device configuration */ +typedef struct { + CAN_TypeDef *can; /**< CAN device */ + uint32_t rcc_mask; /**< RCC mask to enable clock */ + gpio_t rx_pin; /**< RX pin */ + gpio_t tx_pin; /**< TX pin */ +#ifndef CPU_FAM_STM32F1 + gpio_af_t af; /**< Alternate pin function to use */ +#endif +#if CANDEV_STM32_CHAN_NUMOF > 1 || defined(DOXYGEN) + CAN_TypeDef *can_master; /**< Master CAN device */ + uint32_t master_rcc_mask; /**< Master device RCC mask */ + /** First filter in the bank. For a master channel it must be 0. + * For a slave channel, it is used without checking with the master channel, + * beware bot to overwrite the master config. */ + uint8_t first_filter; + /** Number of filters to use. Must be less or equal + * to CAN_STM32_NB_FILTER - first_filter */ + uint8_t nb_filters; +#endif +#if defined(CPU_FAM_STM32F0) + uint8_t irqn; /**< CAN common IRQ channel */ +#else + uint8_t tx_irqn; /**< TX IRQ channel */ + uint8_t rx0_irqn; /**< RX0 IRQ channel */ + uint8_t rx1_irqn; /**< RX1 IRQ channel */ + uint8_t sce_irqn; /**< SCE IRQ channel */ +#endif + uint8_t ttcm : 1; /**< Time triggered communication mode */ + uint8_t abom : 1; /**< Automatic bus-off management */ + uint8_t awum : 1; /**< Automatic wakeup mode */ + uint8_t nart : 1; /**< No automatic retransmission */ + uint8_t rflm : 1; /**< Receive FIFO locked mode */ + uint8_t txfp : 1; /**< Transmit FIFO priority */ + uint8_t lbkm : 1; /**< Loopback mode */ + uint8_t silm : 1; /**< Silent mode */ +} can_conf_t; +#define HAVE_CAN_CONF_T + +/** The number of transmit mailboxes */ +#define CAN_STM32_TX_MAILBOXES 3 +/** The number of receive FIFO */ +#define CAN_STM32_RX_MAILBOXES 2 + + +#ifndef CAN_STM32_RX_MAIL_FIFO +/** This is the maximum number of frame the driver can receive simultaneously */ +#define CAN_STM32_RX_MAIL_FIFO 12 +#endif + +/** bxCAN candev descriptor */ +typedef struct can can_t; +#define HAVE_CAN_T + +/** This structure holds anything related to the receive part */ +typedef struct candev_stm32_rx_fifo { + struct can_frame frame[CAN_STM32_RX_MAIL_FIFO]; /**< Receive FIFO */ + int write_idx; /**< Write index in the receive FIFO */ + int read_idx; /**< Read index in the receive FIFO*/ + int is_full; /**< Flag set when the FIFO is full */ +} candev_stm32_rx_fifo_t; + +/** Internal interrupt flags */ +typedef struct candev_stm32_isr { + int isr_tx : 3; /**< Tx mailboxes interrupt */ + int isr_rx : 2; /**< Rx FIFO interrupt */ + int isr_wkup : 1; /**< Wake up interrupt */ +} candev_stm32_isr_t; + +/** STM32 CAN device descriptor */ +struct can { + candev_t candev; /**< Common candev struct */ + const can_conf_t *conf; /**< Configuration */ + gpio_t rx_pin; /**< RX pin */ + gpio_t tx_pin; /**< TX pin */ + gpio_af_t af; /**< Alternate pin function to use */ + /** Tx mailboxes */ + const struct can_frame *tx_mailbox[CAN_STM32_TX_MAILBOXES]; + candev_stm32_rx_fifo_t rx_fifo; /**< Rx FIFOs */ + candev_stm32_isr_t isr_flags; /**< ISR flags */ +}; + +#ifndef CPU_FAM_STM32F1 +/** + * @brief Set the pins of an stm32 CAN device + * + * @param[in,out] dev the device to set pins + * @param[in] tx_pin tx pin + * @param[in] rx_pin rx pin + * @param[in] af alternate function + */ +void candev_stm32_set_pins(can_t *dev, gpio_t tx_pin, gpio_t rx_pin, + gpio_af_t af); +#else +/** + * @brief Set the pins of an stm32 CAN device + * + * @param[in,out] dev the device to set pins + * @param[in] tx_pin tx pin + * @param[in] rx_pin rx pin + */ +void candev_stm32_set_pins(can_t *dev, gpio_t tx_pin, gpio_t rx_pin); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* CANDEV_STM32_H */ diff --git a/cpu/stm32_common/include/periph_cpu_common.h b/cpu/stm32_common/include/periph_cpu_common.h index 2debe87737..19e1bc060a 100644 --- a/cpu/stm32_common/include/periph_cpu_common.h +++ b/cpu/stm32_common/include/periph_cpu_common.h @@ -704,6 +704,10 @@ int dma_configure(dma_t dma, int chan, const volatile void *src, volatile void * #endif /* MODULE_PERIPH_DMA */ +#ifdef MODULE_PERIPH_CAN +#include "candev_stm32.h" +#endif + #ifdef __cplusplus } #endif diff --git a/cpu/stm32_common/periph/can.c b/cpu/stm32_common/periph/can.c new file mode 100644 index 0000000000..19717cd2d9 --- /dev/null +++ b/cpu/stm32_common/periph/can.c @@ -0,0 +1,1263 @@ +/* + * Copyright (C) 2016-2018 OTA keys S.A. + * + * 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_stm32_common + * @{ + * + * @file + * @brief Implementation of the CAN controller driver + * + * @author Vincent Dupont + * @} + */ + +#include +#include +#include +#include + +#include "periph/can.h" +#include "periph/gpio.h" +#include "can/device.h" +#include "can/common.h" +#include "periph_conf.h" +#include "pm_layered.h" +#include "thread.h" +#include "sched.h" +#include "mutex.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#define CAN_MAX_WAIT_CHANGE (10000U) +#define CAN_TIxR_SFF_SHIFT (21U) +#define CAN_TIxR_EFF_SHIFT (3U) + +#define CAN_MCR_BIT_DONT_TOUCH (0xFFFFFF03) + +typedef enum { + MODE_NORMAL, + MODE_SLEEP, + MODE_INIT, +} can_mode_t; + +static int _init(candev_t *candev); +static void _isr(candev_t *candev); +static int _send(candev_t *candev, const struct can_frame *frame); +static int _abort(candev_t *candev, const struct can_frame *frame); +static int _set(candev_t *candev, canopt_t opt, void *value, size_t value_len); +static int _get(candev_t *candev, canopt_t opt, void *value, size_t max_len); +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 void tx_irq_handler(can_t *dev); +static void tx_isr(can_t *dev); +static void tx_conf(can_t *dev, int mailbox); +static void rx_irq_handler(can_t *dev, int mailbox); +static void rx_isr(can_t *dev); +static void sce_irq_handler(can_t *dev); + +static inline void set_bit_timing(can_t *dev); + +static inline can_mode_t get_mode(CAN_TypeDef *can); +static int set_mode(CAN_TypeDef *can, can_mode_t mode); + +static const candev_driver_t candev_stm32_driver = { + .send = _send, + .init = _init, + .isr = _isr, + .get = _get, + .set = _set, + .abort = _abort, + .set_filter = _set_filter, + .remove_filter = _remove_filter, +}; + +static const struct can_bittiming_const bittiming_const = { + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 1, + .brp_min = 1, + .brp_max = 1024, + .brp_inc = 1, +}; + +enum { + STATUS_NOT_USED, + STATUS_ON, + STATUS_READY_FOR_SLEEP, + STATUS_SLEEP, +}; + +static uint8_t _status[CANDEV_STM32_CHAN_NUMOF]; + +static can_t *_can[CANDEV_STM32_CHAN_NUMOF]; + +static inline int get_channel(CAN_TypeDef *can) +{ +#if CANDEV_STM32_CHAN_NUMOF == 1 + (void)can; + return 0; +#else + return (int) (((uint32_t)can - (uint32_t)CAN1) >> 10); +#endif +} + +static inline can_mode_t get_mode(CAN_TypeDef *can) +{ + if ((can->MCR & CAN_MCR_SLEEP) == CAN_MCR_SLEEP) { + return MODE_SLEEP; + } + else if ((can->MSR & CAN_MSR_INAK) == CAN_MSR_INAK) { + return MODE_INIT; + } + else { + return MODE_NORMAL; + } +} + +static int set_mode(CAN_TypeDef *can, can_mode_t mode) +{ + int max_loop = CAN_MAX_WAIT_CHANGE; + int res = 0; + + switch (mode) { + case MODE_NORMAL: + can->MCR &= ~(CAN_MCR_SLEEP | CAN_MCR_INRQ); + /* wait for hardare confirmation */ + while (((can->MSR & CAN_MSR_INAK) || (can->MSR & CAN_MSR_SLAK)) && max_loop != 0) { + max_loop--; + } + break; + case MODE_SLEEP: + /* set sleep mode */ + can->MCR &= ~CAN_MCR_INRQ; + can->MCR |= CAN_MCR_SLEEP; + while (((can->MSR & CAN_MSR_INAK) || !(can->MSR & CAN_MSR_SLAK)) && max_loop != 0) { + max_loop--; + } + break; + case MODE_INIT: + can->MCR &= ~CAN_MCR_SLEEP; + can->MCR |= CAN_MCR_INRQ; + /* wait for hardare confirmation */ + while ((!(can->MSR & CAN_MSR_INAK) || (can->MSR & CAN_MSR_SLAK)) && max_loop != 0) { + max_loop--; + } + break; + default: + DEBUG("unsupported mode\n"); + res = -1; + break; + } + + if (max_loop == 0) { + DEBUG("candev_stm32: didn't switch mode %d\n", mode); + res = -1; + } + return res; +} + +static inline int filter_is_set(CAN_TypeDef *master, uint8_t filter) +{ + return (master->FA1R & (1 << filter)) >> filter; +} + +void can_init(can_t *dev, const can_conf_t *conf) +{ + dev->candev.driver = &candev_stm32_driver; + + struct can_bittiming timing = { .bitrate = CANDEV_STM32_DEFAULT_BITRATE, + .sample_point = CANDEV_STM32_DEFAULT_SPT }; + can_device_calc_bittiming(CLOCK_APB1, &bittiming_const, &timing); + + memcpy(&dev->candev.bittiming, &timing, sizeof(timing)); + dev->conf = conf; + + dev->rx_pin = GPIO_UNDEF; + dev->tx_pin = GPIO_UNDEF; +} + +static void set_filter(CAN_TypeDef *can, uint32_t fr1, uint32_t fr2, uint8_t filter, uint8_t fifo) +{ + /* Set filter/mask */ + if ((fr1 & CAN_EFF_FLAG) == CAN_EFF_FLAG) { + can->sFilterRegister[filter].FR1 = ((fr1 & CAN_EFF_MASK) << CAN_TIxR_EFF_SHIFT) + | CAN_TI0R_IDE; + can->sFilterRegister[filter].FR2 = ((fr2 & CAN_EFF_MASK) << CAN_TIxR_EFF_SHIFT) + | CAN_TI0R_IDE; + } + else { + can->sFilterRegister[filter].FR1 = (fr1 & CAN_SFF_MASK) << CAN_TIxR_SFF_SHIFT; + can->sFilterRegister[filter].FR2 = (fr2 & CAN_SFF_MASK) << CAN_TIxR_SFF_SHIFT; + } + if (fifo == 0) { + can->FFA1R &= ~(1 << filter); /* To FIFO 0 */ + } + else { + can->FFA1R |= (1 << filter); /* To FIFO 1 */ + } + can->FM1R &= ~(1 << filter); /* Identifier Mask mode */ + can->FS1R |= (1 << filter); /* Single 32-bit scale config */ + can->FA1R |= (1 << filter); /* Activate filer */ +} + +static void get_can_filter(CAN_TypeDef *can, uint8_t filter_id, uint32_t *filter, uint32_t *mask) +{ + uint32_t fr1 = can->sFilterRegister[filter_id].FR1; + uint32_t fr2 = can->sFilterRegister[filter_id].FR2; + + if ((fr1 & CAN_TI0R_IDE) == CAN_TI0R_IDE) { + *filter = ((fr1 >> CAN_TIxR_EFF_SHIFT) & CAN_EFF_MASK) | CAN_EFF_FLAG; + *mask = ((fr2 >> CAN_TIxR_EFF_SHIFT) & CAN_EFF_MASK) | CAN_EFF_FLAG; + } + else { + *filter = (fr1 >> CAN_TIxR_SFF_SHIFT) & CAN_SFF_MASK; + *mask = (fr2 >> CAN_TIxR_SFF_SHIFT) & CAN_SFF_MASK; + } + if (fr1 & CAN_TI0R_RTR) { + *filter |= CAN_RTR_FLAG; + } + if (fr2 & CAN_TI0R_RTR) { + *mask |= CAN_RTR_FLAG; + } +} + +static inline void unset_filter(CAN_TypeDef *can, uint8_t filter) +{ + can->FA1R &= ~(1 << filter); +} + +#ifndef CPU_FAM_STM32F1 +void candev_stm32_set_pins(can_t *dev, gpio_t tx_pin, gpio_t rx_pin, + gpio_af_t af) +#else +void candev_stm32_set_pins(can_t *dev, gpio_t tx_pin, gpio_t rx_pin) +#endif +{ + if (dev->tx_pin != GPIO_UNDEF) { + gpio_init(dev->tx_pin, GPIO_IN); + gpio_init_analog(dev->tx_pin); + } + if (dev->rx_pin != GPIO_UNDEF) { + gpio_init(dev->rx_pin, GPIO_IN); + gpio_init_analog(dev->rx_pin); + } + dev->tx_pin = tx_pin; + dev->rx_pin = rx_pin; + /* configure pins */ + gpio_init(rx_pin, GPIO_IN); + gpio_init(tx_pin, GPIO_OUT); +#ifndef CPU_FAM_STM32F1 + dev->af = af; + gpio_init_af(rx_pin, af); + gpio_init_af(tx_pin, af); +#else + gpio_init_af(tx_pin, GPIO_AF_OUT_PP); +#endif +} + +static int _init(candev_t *candev) +{ + can_t *dev = (can_t *)candev; + int res = 0; + + _can[get_channel(dev->conf->can)] = dev; + + memset(dev->tx_mailbox, 0, sizeof(dev->tx_mailbox)); + memset(&dev->rx_fifo, 0, sizeof(dev->rx_fifo)); + + dev->isr_flags.isr_tx = 0; + dev->isr_flags.isr_rx = 0; + +#if CANDEV_STM32_CHAN_NUMOF > 1 + /* Enable master clock */ + periph_clk_en(APB1, dev->conf->master_rcc_mask); +#endif + /* Enable device clock */ + periph_clk_en(APB1, dev->conf->rcc_mask); + + _status[get_channel(dev->conf->can)] = STATUS_ON; + + /* configure pins */ +#ifndef CPU_FAM_STM32F1 + candev_stm32_set_pins(dev, dev->conf->tx_pin, dev->conf->rx_pin, dev->conf->af); +#else + candev_stm32_set_pins(dev, dev->conf->tx_pin, dev->conf->rx_pin); +#endif + + set_mode(dev->conf->can, MODE_INIT); + + /* Set configuration */ + uint32_t mask_config = (CAN_MCR_TTCM * dev->conf->ttcm) | (CAN_MCR_ABOM * dev->conf->abom) | + (CAN_MCR_AWUM * dev->conf->awum) | (CAN_MCR_NART * dev->conf->nart) | + (CAN_MCR_RFLM * dev->conf->rflm) | (CAN_MCR_TXFP * dev->conf->txfp); + dev->conf->can->MCR |= mask_config; + dev->conf->can->MCR &= (mask_config | CAN_MCR_BIT_DONT_TOUCH); + + set_bit_timing(dev); + + /* Loopback and/or silent mode */ + dev->conf->can->BTR |= (CAN_BTR_LBKM * dev->conf->lbkm) | (CAN_BTR_SILM * dev->conf->silm); + + /* Default filter config: No rx frame */ +#if CANDEV_STM32_CHAN_NUMOF > 1 + if (dev->conf->can == dev->conf->can_master) { + + assert(dev->conf->first_filter == 0); + assert(dev->conf->nb_filters <= CAN_STM32_NB_FILTER); +#endif + + dev->conf->can->FMR = CAN_FMR_FINIT; +#if CANDEV_STM32_CHAN_NUMOF > 1 + /* Clear start bank */ + dev->conf->can->FMR &= ~CAN_FMR_CAN2SB; + /* Set start filter */ + dev->conf->can->FMR |= dev->conf->nb_filters << 8; + + for (int i = 0; i < dev->conf->nb_filters; i++) { +#else + for (int i = 0; i < CAN_STM32_NB_FILTER; i++) { +#endif + dev->conf->can->FA1R &= ~(1 << i); + } + dev->conf->can->FMR &= ~CAN_FMR_FINIT; +#if CANDEV_STM32_CHAN_NUMOF > 1 +} +else { + can_mode_t mode = get_mode(dev->conf->can_master); + set_mode(dev->conf->can_master, MODE_INIT); + dev->conf->can_master->FMR = CAN_FMR_FINIT; + dev->conf->can_master->FMR &= ~CAN_FMR_CAN2SB; + dev->conf->can_master->FMR |= dev->conf->first_filter << 8; + for (int i = 0; i < dev->conf->nb_filters; i++) { + dev->conf->can_master->FA1R &= ~(1 << (i + dev->conf->first_filter)); + } + dev->conf->can_master->FMR &= ~CAN_FMR_FINIT; + set_mode(dev->conf->can_master, mode); +} +#endif + + /* Clear interrupt flags */ + dev->conf->can->TSR |= CAN_TSR_RQCP0 | CAN_TSR_RQCP1 | CAN_TSR_RQCP2; + dev->conf->can->RF0R |= CAN_RF0R_FMP0 | CAN_RF0R_FULL0 | CAN_RF0R_FOVR0; + dev->conf->can->RF1R |= CAN_RF1R_FMP1 | CAN_RF1R_FULL1 | CAN_RF1R_FOVR1; + dev->conf->can->ESR |= CAN_ESR_EWGF | CAN_ESR_EPVF | CAN_ESR_BOFF; + dev->conf->can->MSR |= CAN_MSR_WKUI; + + dev->conf->can->IER = CAN_IER_WKUIE | + CAN_IER_EPVIE | CAN_IER_EWGIE | CAN_IER_ERRIE | CAN_IER_BOFIE | + CAN_IER_FOVIE1 | CAN_IER_FMPIE1 | CAN_IER_FOVIE0 | CAN_IER_FMPIE0 | + CAN_IER_TMEIE; + /* Enable interrupts */ +#if defined(CPU_FAM_STM32F0) + NVIC_EnableIRQ(dev->conf->irqn); +#else + NVIC_EnableIRQ(dev->conf->tx_irqn); + NVIC_EnableIRQ(dev->conf->rx0_irqn); + NVIC_EnableIRQ(dev->conf->rx1_irqn); + NVIC_EnableIRQ(dev->conf->sce_irqn); +#endif + + res = set_mode(dev->conf->can, MODE_NORMAL); +#ifdef STM32_PM_STOP + pm_block(STM32_PM_STOP); +#endif + + return res; +} + +static inline void set_bit_timing(can_t *dev) +{ + /* Set bit timing */ + dev->conf->can->BTR = (((uint32_t)(dev->candev.bittiming.sjw - 1) << 24) & CAN_BTR_SJW) | + (((uint32_t)(dev->candev.bittiming.phase_seg2 - 1) << 20) & CAN_BTR_TS2) | + (((uint32_t)((dev->candev.bittiming.phase_seg1 + + dev->candev.bittiming.prop_seg) + - 1) << 16) & CAN_BTR_TS1) | + ((uint32_t)(dev->candev.bittiming.brp - 1) & CAN_BTR_BRP); +} + +static int _send(candev_t *candev, const struct can_frame *frame) +{ + can_t *dev = (can_t *)candev; + CAN_TypeDef *can = dev->conf->can; + int mailbox = 0; + + DEBUG("_send: candev=%p, frame=%p\n", (void *) candev, (void *) frame); + + for (mailbox = 0; mailbox < CAN_STM32_TX_MAILBOXES; mailbox++) { + if (dev->tx_mailbox[mailbox] == NULL) { + break; + } + } + if (mailbox == CAN_STM32_TX_MAILBOXES) { + return -EBUSY; + } + dev->tx_mailbox[mailbox] = frame; + + if ((frame->can_id & CAN_EFF_FLAG) == CAN_EFF_FLAG) { + can->sTxMailBox[mailbox].TIR = (frame->can_id & CAN_EFF_MASK) << CAN_TIxR_EFF_SHIFT + | CAN_TI0R_IDE; + } + else { + can->sTxMailBox[mailbox].TIR = (frame->can_id & CAN_SFF_MASK) << CAN_TIxR_SFF_SHIFT; + } + can->sTxMailBox[mailbox].TDTR = frame->can_dlc & CAN_TDT0R_DLC; + + can->sTxMailBox[mailbox].TDLR = 0; + can->sTxMailBox[mailbox].TDHR = 0; + for (int j = 0; j < 4 && frame->can_dlc > j; j++) { + can->sTxMailBox[mailbox].TDLR |= (uint32_t)(frame->data[j] << (8 * j)); + } + for (int j = 4; j < 8 && frame->can_dlc > j; j++) { + can->sTxMailBox[mailbox].TDHR |= (uint32_t)(frame->data[j] << (8 * (j - 4))); + } + + can->sTxMailBox[mailbox].TIR |= CAN_TI0R_TXRQ; + + return mailbox; +} + +static int _abort(candev_t *candev, const struct can_frame *frame) +{ + can_t *dev = (can_t *)candev; + CAN_TypeDef *can = dev->conf->can; + int mailbox = 0; + + for (mailbox = 0; mailbox < CAN_STM32_TX_MAILBOXES; mailbox++) { + if (dev->tx_mailbox[mailbox] == frame) { + break; + } + } + if (mailbox == CAN_STM32_TX_MAILBOXES) { + return -EOVERFLOW; + } + + can->TSR |= CAN_TSR_ABRQ0 << (8 * mailbox); + dev->tx_mailbox[mailbox] = NULL; + + return 0; +} + +#define CAN_RIxR_SFF_SHIFT 21 +#define CAN_RIxR_EFF_SHIFT 3 +#define CAN_RDTxR_FMI_SHIFT 8 + +static int read_frame(can_t *dev, struct can_frame *frame, int mailbox) +{ + CAN_TypeDef *can = dev->conf->can; + + /* Get frame ID */ + if ((can->sFIFOMailBox[mailbox].RIR & CAN_RI0R_IDE) == CAN_RI0R_IDE) { + frame->can_id = can->sFIFOMailBox[mailbox].RIR >> CAN_RIxR_EFF_SHIFT; + frame->can_id |= CAN_EFF_FLAG; + } + else { + frame->can_id = can->sFIFOMailBox[mailbox].RIR >> CAN_RIxR_SFF_SHIFT; + } + if ((can->sFIFOMailBox[mailbox].RIR & CAN_RI0R_RTR) == CAN_RI0R_RTR) { + frame->can_id |= CAN_RTR_FLAG; + } + + /* Get DLC */ + frame->can_dlc = can->sFIFOMailBox[mailbox].RDTR & CAN_RDT0R_DLC; + + /* Get Data */ + for (int j = 0; j < 4; j++) { + frame->data[j] = (can->sFIFOMailBox[mailbox].RDLR >> (j * 8)) & 0xFF; + } + for (int j = 4; j < 8; j++) { + frame->data[j] = (can->sFIFOMailBox[mailbox].RDHR >> ((j - 4) * 8)) & 0xFF; + } + + /* filter number matching the reveived frame */ + /* filter = (can->sFIFOMailBox[mailbox].RDTR & CAN_RDT0R_FMI) >> CAN_RDTxR_FMI_SHIFT; */ + + /* Release input mailbox */ + if (mailbox == 0) { + can->RF0R |= CAN_RF0R_RFOM0; + } + else { + can->RF1R |= CAN_RF1R_RFOM1; + } + + return 0; +} + +static void _isr(candev_t *candev) +{ + can_t *dev = (can_t *)candev; + + if (dev->isr_flags.isr_tx) { + tx_isr(dev); + } + + if (dev->isr_flags.isr_wkup) { + if (dev->candev.event_callback) { + dev->candev.event_callback(&dev->candev, CANDEV_EVENT_WAKE_UP, NULL); + } + } + + unsigned int irq; + + irq = irq_disable(); + if (dev->isr_flags.isr_rx & 1) { + dev->isr_flags.isr_rx &= ~1; + irq_restore(irq); + rx_isr(dev); + } + else { + irq_restore(irq); + } + + irq = irq_disable(); + if (dev->isr_flags.isr_rx & 2) { + dev->isr_flags.isr_rx &= ~2; + irq_restore(irq); + rx_isr(dev); + } + else { + irq_restore(irq); + } +} + +static inline int get_first_filter(can_t *dev) +{ +#if CANDEV_STM32_CHAN_NUMOF == 1 + (void)dev; + return 0; +#else + return dev->conf->first_filter; +#endif +} + +static inline int get_nb_filter(can_t *dev) +{ +#if CANDEV_STM32_CHAN_NUMOF == 1 + (void)dev; + return CAN_STM32_NB_FILTER; +#else + return dev->conf->nb_filters; +#endif +} + +static inline CAN_TypeDef *get_master(can_t *dev) +{ +#if CANDEV_STM32_CHAN_NUMOF == 1 + return dev->conf->can; +#else + return dev->conf->can_master; +#endif +} + +static inline int is_master(can_t *dev) +{ +#if CANDEV_STM32_CHAN_NUMOF == 1 + (void)dev; + return 1; +#else + return dev->conf->can_master == dev->conf->can; +#endif +} + +static void _wkup_cb(void *arg) +{ + can_t *dev = arg; + gpio_irq_disable(dev->rx_pin); + + DEBUG("int wkup: %p\n", arg); + + dev->isr_flags.isr_wkup = 1; + + if (dev->candev.event_callback) { + dev->candev.event_callback(&dev->candev, CANDEV_EVENT_ISR, NULL); + } +} + +#if CANDEV_STM32_CHAN_NUMOF > 1 +static void enable_int(can_t *dev, int master_from_slave) +{ + DEBUG("EN int (%d) (%p)\n", master_from_slave, (void *)dev); + + if (master_from_slave) { + can_t *master = _can[get_channel(get_master(dev))]; + gpio_init_int(master->rx_pin, GPIO_IN, GPIO_FALLING, _wkup_cb, master); + } + else { + gpio_init_int(dev->rx_pin, GPIO_IN, GPIO_FALLING, _wkup_cb, dev); + } +} +#endif + +static void disable_int(can_t *dev, int master_from_slave) +{ + DEBUG("DIS int (%d) (%p)\n", master_from_slave, (void *)dev); + + if (master_from_slave) { +#if CANDEV_STM32_CHAN_NUMOF > 1 + can_t *master = _can[get_channel(get_master(dev))]; + gpio_irq_disable(master->rx_pin); +#ifndef CPU_FAM_STM32F1 + candev_stm32_set_pins(master, master->tx_pin, master->rx_pin, master->af); +#else + candev_stm32_set_pins(master, master->tx_pin, master->rx_pin); +#endif +#endif + } + else { + gpio_irq_disable(dev->rx_pin); +#ifndef CPU_FAM_STM32F1 + candev_stm32_set_pins(dev, dev->tx_pin, dev->rx_pin, dev->af); +#else + candev_stm32_set_pins(dev, dev->tx_pin, dev->rx_pin); +#endif + } +} + +static void turn_off(can_t *dev) +{ + DEBUG("turn off (%p)\n", (void *)dev); + + unsigned irq = irq_disable(); +#if CANDEV_STM32_CHAN_NUMOF > 1 + if (is_master(dev)) { + int chan = get_channel(dev->conf->can); + if (chan < CANDEV_STM32_CHAN_NUMOF - 1 && _status[chan + 1] != STATUS_SLEEP) { + /* a slave exists and is not sleeping */ + _status[chan] = STATUS_READY_FOR_SLEEP; + } + else { + /* no slave or slave already sleeping */ + if (_status[get_channel(dev->conf->can)] != STATUS_SLEEP) { +#ifdef STM32_PM_STOP + pm_unblock(STM32_PM_STOP); +#endif + } + _status[chan] = STATUS_SLEEP; + periph_clk_dis(APB1, dev->conf->rcc_mask); + enable_int(dev, 0); + } + } + else { + int master_chan = get_channel(get_master(dev)); + switch (_status[master_chan]) { + case STATUS_READY_FOR_SLEEP: + _status[master_chan] = STATUS_SLEEP; +#ifdef STM32_PM_STOP + pm_unblock(STM32_PM_STOP); +#endif + /* Fall through */ + case STATUS_NOT_USED: + periph_clk_dis(APB1, dev->conf->master_rcc_mask); + break; + } + periph_clk_dis(APB1, dev->conf->rcc_mask); + if (_status[get_channel(dev->conf->can)] != STATUS_SLEEP) { +#ifdef STM32_PM_STOP + pm_unblock(STM32_PM_STOP); +#endif + } + _status[get_channel(dev->conf->can)] = STATUS_SLEEP; + if (_status[master_chan] == STATUS_SLEEP) { + enable_int(dev, 1); + } + enable_int(dev, 0); + } +#else + if (_status[get_channel(dev->conf->can)] != STATUS_SLEEP) { +#ifdef STM32_PM_STOP + pm_unblock(STM32_PM_STOP); +#endif + } + _status[get_channel(dev->conf->can)] = STATUS_SLEEP; + periph_clk_dis(APB1, dev->conf->rcc_mask); + gpio_init_int(dev->rx_pin, GPIO_IN, GPIO_FALLING, _wkup_cb, dev); +#endif + irq_restore(irq); +} + +static void turn_on(can_t *dev) +{ + DEBUG("turn on (%p)\n", (void *)dev); + + unsigned irq = irq_disable(); +#if CANDEV_STM32_CHAN_NUMOF > 1 + if (!is_master(dev)) { + int master_chan = get_channel(get_master(dev)); + switch (_status[master_chan]) { + case STATUS_SLEEP: + _status[master_chan] = STATUS_READY_FOR_SLEEP; + disable_int(dev, 1); +#ifdef STM32_PM_STOP + pm_block(STM32_PM_STOP); +#endif + /* Fall through */ + case STATUS_NOT_USED: + periph_clk_en(APB1, dev->conf->master_rcc_mask); + break; + } + } +#endif + if (_status[get_channel(dev->conf->can)] == STATUS_SLEEP) { +#ifdef STM32_PM_STOP + pm_block(STM32_PM_STOP); +#endif + disable_int(dev, 0); + periph_clk_en(APB1, dev->conf->rcc_mask); + } + _status[get_channel(dev->conf->can)] = STATUS_ON; + + irq_restore(irq); +} + +static int _wake_up(can_t *dev) +{ + turn_on(dev); + return set_mode(dev->conf->can, MODE_NORMAL); +} + +static int _sleep(can_t *dev) +{ + int res = set_mode(dev->conf->can, MODE_SLEEP); + turn_off(dev); + return res; +} + +static int _set(candev_t *candev, canopt_t opt, void *value, size_t value_len) +{ + can_t *dev = (can_t *)candev; + CAN_TypeDef *can = dev->conf->can; + int res = 0; + can_mode_t mode; + + switch (opt) { + case CANOPT_BITTIMING: + if (value_len < sizeof(dev->candev.bittiming)) { + res = -EOVERFLOW; + } + else { + memcpy(&dev->candev.bittiming, value, sizeof(dev->candev.bittiming)); + mode = get_mode(can); + if (mode == MODE_SLEEP) { + res = _wake_up(dev); + if (res != 0) { + res = -EBUSY; + break; + } + } + res = set_mode(can, MODE_INIT); + if (res == 0) { + set_bit_timing(dev); + res = sizeof(dev->candev.bittiming); + } + if (mode == MODE_SLEEP) { + if (_sleep(dev) < 0) { + res = -EBUSY; + } + } + else if (set_mode(can, mode) < 0) { + res = -EBUSY; + } + } + break; + case CANOPT_STATE: + if (value_len < sizeof(canopt_state_t)) { + res = -EOVERFLOW; + } + else { + switch (*((canopt_state_t *)value)) { + case CANOPT_STATE_OFF: + case CANOPT_STATE_SLEEP: + DEBUG("candev_stm32 %p: power down\n", (void *)dev); + res = _sleep(dev); + break; + case CANOPT_STATE_ON: + DEBUG("candev_stm32 %p: power up\n", (void *)dev); + res = _wake_up(dev); + break; + case CANOPT_STATE_LISTEN_ONLY: + mode = get_mode(can); + res = set_mode(can, MODE_INIT); + can->BTR |= CAN_BTR_SILM; + res += set_mode(can, mode); + break; + } + } + break; + default: + res = -ENOTSUP; + break; + } + + return res; +} + +#define CAN_ESR_REC_SHIFT 24 +#define CAN_ESR_TEC_SHIFT 16 + +static int _get(candev_t *candev, canopt_t opt, void *value, size_t max_len) +{ + can_t *dev = (can_t *)candev; + CAN_TypeDef *can = dev->conf->can; + int res = 0; + + switch (opt) { + case CANOPT_BITTIMING: + if (max_len < sizeof(dev->candev.bittiming)) { + res = -EOVERFLOW; + } + else { + memcpy(value, &dev->candev.bittiming, sizeof(dev->candev.bittiming)); + res = sizeof(dev->candev.bittiming); + } + break; + case CANOPT_BITTIMING_CONST: + if (max_len < sizeof(bittiming_const)) { + res = -EOVERFLOW; + } + else { + memcpy(value, &bittiming_const, sizeof(bittiming_const)); + res = sizeof(bittiming_const); + } + break; + case CANOPT_CLOCK: + if (max_len < sizeof(uint32_t)) { + res = -EOVERFLOW; + } + else { + *((uint32_t *)value) = CLOCK_APB1; + res = sizeof(uint32_t); + } + break; + case CANOPT_RX_FILTERS: + if (max_len % sizeof(struct can_filter) != 0) { + res = -EOVERFLOW; + } + else { + unsigned en_filt = 0; + unsigned base_filter = get_first_filter(dev); + unsigned last_filter = base_filter + get_nb_filter(dev); + for (unsigned i = base_filter; i < last_filter; i++) { + if ((get_master(dev)->FA1R & (1u << i)) == (1u << i)) { + en_filt++; + } + } + if (max_len / sizeof(struct can_filter) < en_filt) { + res = -EOVERFLOW; + } + else { + for (unsigned i = 0; i < en_filt; i++) { + struct can_filter *filter = (struct can_filter *)value + i; + get_can_filter(get_master( + dev), i + base_filter, &filter->can_id, + &filter->can_mask); + } + res = en_filt * sizeof(struct can_filter); + } + } + break; + case CANOPT_TEC: + if (max_len != sizeof(uint16_t)) { + res = -EOVERFLOW; + } + else { + uint16_t *tec = (uint16_t *)value; + *tec = (can->ESR & CAN_ESR_TEC) >> CAN_ESR_TEC_SHIFT; + res = sizeof(uint16_t); + } + break; + case CANOPT_REC: + if (max_len != sizeof(uint16_t)) { + res = -EOVERFLOW; + } + else { + uint16_t *rec = (uint16_t *)value; + *rec = (can->ESR & CAN_ESR_REC) >> CAN_ESR_REC_SHIFT; + res = sizeof(uint16_t); + } + break; + default: + res = -ENOTSUP; + break; + } + + return res; +} + +static int _set_filter(candev_t *candev, const struct can_filter *filter) +{ + can_t *dev = (can_t *)candev; + + DEBUG("_set_filter: dev=%p, filter=0x%" PRIx32 "\n", (void *)candev, filter->can_id); + + int first_filter = get_first_filter(dev); + int last_filter = first_filter + get_nb_filter(dev); + + uint8_t i; + for (i = first_filter; i < last_filter; i++) { + if (!filter_is_set(get_master(dev), i)) { + get_master(dev)->FMR = CAN_FMR_FINIT; + set_filter(get_master( + dev), filter->can_id, filter->can_mask, i, i % CAN_STM32_RX_MAILBOXES); + get_master(dev)->FMR &= ~CAN_FMR_FINIT; + break; + } + } + if (i == last_filter) { + return -EOVERFLOW; + } + + return i - first_filter; +} + +static int _remove_filter(candev_t *candev, const struct can_filter *filter) +{ + can_t *dev = (can_t *)candev; + + int first_filter = get_first_filter(dev); + int last_filter = first_filter + get_nb_filter(dev); + + uint8_t i; + + for (i = first_filter; i < last_filter; i++) { + if (filter_is_set(get_master(dev), i)) { + uint32_t filt, mask; + /* Clear RTR and ERR flags as they are ignored by the set/get filter */ + get_can_filter(get_master(dev), i, &filt, &mask); + DEBUG("_remove_filter: filter=0x%" PRIx32 ",0x%" PRIx32 ", nb=%d, " + "dev_filter=0x%" PRIx32 ",0x%" PRIx32 "\n", + filter->can_id, filter->can_mask, (int)i, filt, mask); + if ((filt == filter->can_id) && /* ID match */ + /* Filter match (extended case */ + (((filt & CAN_EFF_FLAG) && + ((mask & CAN_EFF_MASK) == (filter->can_mask & CAN_EFF_MASK))) + /* Filter match (standard case */ + || (!(filt & CAN_EFF_FLAG) && + ((mask & CAN_SFF_MASK) == (filter->can_mask & CAN_SFF_MASK))))) { + get_master(dev)->FMR = CAN_FMR_FINIT; + unset_filter(get_master(dev), i); + get_master(dev)->FMR &= ~CAN_FMR_FINIT; + break; + } + } + } + if (i == last_filter) { + return -EOVERFLOW; + } + + return 0; +} + +static void tx_conf(can_t *dev, int mailbox) +{ + candev_t *candev = (candev_t *) dev; + const struct can_frame *frame = dev->tx_mailbox[mailbox]; + + dev->tx_mailbox[mailbox] = NULL; + + DEBUG("_tx_conf: device=%p, mb=%d\n", (void *)dev, mailbox); + + if (frame && dev->candev.event_callback) { + dev->candev.event_callback(candev, CANDEV_EVENT_TX_CONFIRMATION, + (void *) frame); + } +} + +static void tx_irq_handler(can_t *dev) +{ + CAN_TypeDef *can = dev->conf->can; + int flags = dev->isr_flags.isr_tx; + + DEBUG("tx irq\n"); + + if (can->TSR & CAN_TSR_RQCP0) { + if (can->TSR & CAN_TSR_TXOK0) { + dev->isr_flags.isr_tx |= 1; + } + can->TSR |= CAN_TSR_RQCP0; + } + if (can->TSR & CAN_TSR_RQCP1) { + if (can->TSR & CAN_TSR_TXOK1) { + dev->isr_flags.isr_tx |= 2; + } + can->TSR |= CAN_TSR_RQCP1; + } + if (can->TSR & CAN_TSR_RQCP2) { + if (can->TSR & CAN_TSR_TXOK2) { + dev->isr_flags.isr_tx |= 4; + } + can->TSR |= CAN_TSR_RQCP2; + } + + if (dev->candev.event_callback && flags != dev->isr_flags.isr_tx) { + dev->candev.event_callback(&(dev->candev), CANDEV_EVENT_ISR, NULL); + } +} + +static void tx_isr(can_t *dev) +{ + unsigned int irq; + + irq = irq_disable(); + if (dev->isr_flags.isr_tx & 1) { + dev->isr_flags.isr_tx &= ~1; + irq_restore(irq); + tx_conf(dev, 0); + } + else { + irq_restore(irq); + } + + irq = irq_disable(); + if (dev->isr_flags.isr_tx & 2) { + dev->isr_flags.isr_tx &= ~2; + irq_restore(irq); + tx_conf(dev, 1); + } + else { + irq_restore(irq); + } + + irq = irq_disable(); + if (dev->isr_flags.isr_tx & 4) { + dev->isr_flags.isr_tx &= ~4; + irq_restore(irq); + tx_conf(dev, 2); + } + else { + irq_restore(irq); + } +} + +static void rx_irq_handler(can_t *dev, int mailbox) +{ + CAN_TypeDef *can = dev->conf->can; + candev_t *candev = (candev_t *) dev; + + DEBUG("rx irq\n"); + if ((can->RF0R & CAN_RF0R_FOVR0) == CAN_RF0R_FOVR0) { + can->RF0R |= CAN_RF0R_FOVR0; + if (dev->candev.event_callback) { + dev->candev.event_callback(candev, CANDEV_EVENT_RX_ERROR, NULL); + } + return; + } + if ((can->RF1R & CAN_RF1R_FOVR1) == CAN_RF1R_FOVR1) { + can->RF1R |= CAN_RF1R_FOVR1; + if (dev->candev.event_callback) { + dev->candev.event_callback(candev, CANDEV_EVENT_RX_ERROR, NULL); + } + return; + } + + if (!dev->rx_fifo.is_full) { + int i = dev->rx_fifo.write_idx; + read_frame(dev, &(dev->rx_fifo.frame[i]), mailbox); + + if (!dev->isr_flags.isr_rx) { + dev->isr_flags.isr_rx |= mailbox + 1; + if (dev->candev.event_callback) { + dev->candev.event_callback(candev, CANDEV_EVENT_ISR, NULL); + } + } + + dev->rx_fifo.write_idx++; + if (dev->rx_fifo.write_idx == CAN_STM32_RX_MAIL_FIFO) { + dev->rx_fifo.write_idx = 0; + } + if (dev->rx_fifo.write_idx == dev->rx_fifo.read_idx) { + dev->rx_fifo.is_full = 1; + } + } + else { + if (dev->candev.event_callback) { + dev->candev.event_callback(candev, CANDEV_EVENT_RX_ERROR, NULL); + } + } +} + +static void rx_isr(can_t *dev) +{ + DEBUG("_rx_isr: device=%p\n", (void *)dev); + + while (dev->rx_fifo.is_full || dev->rx_fifo.read_idx != dev->rx_fifo.write_idx) { + int i = dev->rx_fifo.read_idx; + + if (dev->candev.event_callback) { + dev->candev.event_callback(&dev->candev, + CANDEV_EVENT_RX_INDICATION, + &dev->rx_fifo.frame[i]); + } + + dev->rx_fifo.read_idx++; + if (dev->rx_fifo.read_idx == CAN_STM32_RX_MAIL_FIFO) { + dev->rx_fifo.read_idx = 0; + } + + dev->rx_fifo.is_full = 0; + } +} + +static void sce_irq_handler(can_t *dev) +{ + CAN_TypeDef *can = dev->conf->can; + candev_t *candev = (candev_t *) dev; + + DEBUG("sce irq: "); + + if ((can->MSR & CAN_MSR_ERRI) == CAN_MSR_ERRI) { + can->MSR = CAN_MSR_ERRI; + if ((can->ESR & CAN_ESR_BOFF) == CAN_ESR_BOFF) { + DEBUG("bus-off\n"); + if (!dev->conf->abom) { + set_mode(can, MODE_INIT); + } + if (dev->candev.event_callback) { + dev->candev.event_callback(candev, CANDEV_EVENT_BUS_OFF, NULL); + } + } + else if ((can->ESR & CAN_ESR_EPVF) == CAN_ESR_EPVF) { + DEBUG("error passive\n"); + if (dev->candev.event_callback) { + dev->candev.event_callback(candev, CANDEV_EVENT_ERROR_PASSIVE, NULL); + } + } + else if ((can->ESR & CAN_ESR_EWGF) == CAN_ESR_EWGF) { + DEBUG("error warning\n"); + if (dev->candev.event_callback) { + dev->candev.event_callback(candev, CANDEV_EVENT_ERROR_WARNING, NULL); + } + } + } + else if ((can->MSR & CAN_MSR_WKUI) == CAN_MSR_WKUI) { + can->MSR = CAN_MSR_WKUI; + DEBUG("wakeup\n"); + if (dev->candev.event_callback) { + dev->candev.event_callback(candev, CANDEV_EVENT_WAKE_UP, NULL); + } + } +} + +#if defined(CPU_FAM_STM32F0) +#define CAN_MSR_INT_MASK 0x0000001C +#define CAN_TSR_INT_MASK 0x000F0F0F +#define CAN_RFxR_INT_MASK 0x0000001B +#define CAN_ESR_INT_MASK 0x00000077 + +void isr_cec_can(void) +{ + DEBUG("bxCAN irq\n"); + + if ((CAN->ESR & CAN_ESR_INT_MASK) || (CAN->MSR & CAN_MSR_INT_MASK)) { + sce_irq_handler(_can[0]); + } + if (CAN->TSR & CAN_TSR_INT_MASK) { + tx_irq_handler(_can[0]); + } + if (CAN->RF0R & CAN_RFxR_INT_MASK) { + rx_irq_handler(_can[0], 0); + } + if (CAN->RF1R & CAN_RFxR_INT_MASK) { + rx_irq_handler(_can[0], 1); + } + + cortexm_isr_end(); +} +#else +void ISR_CAN1_TX(void) +{ + tx_irq_handler(_can[0]); + + cortexm_isr_end(); +} + +void ISR_CAN1_RX0(void) +{ + rx_irq_handler(_can[0], 0); + + cortexm_isr_end(); +} + +void ISR_CAN1_RX1(void) +{ + rx_irq_handler(_can[0], 1); + + cortexm_isr_end(); +} + +void ISR_CAN1_SCE(void) +{ + sce_irq_handler(_can[0]); + + cortexm_isr_end(); +} + +#if CANDEV_STM32_CHAN_NUMOF > 1 +void ISR_CAN2_TX(void) +{ + tx_irq_handler(_can[1]); + + cortexm_isr_end(); +} + +void ISR_CAN2_RX0(void) +{ + rx_irq_handler(_can[1], 0); + + cortexm_isr_end(); +} + +void ISR_CAN2_RX1(void) +{ + rx_irq_handler(_can[1], 1); + + cortexm_isr_end(); +} + +void ISR_CAN2_SCE(void) +{ + sce_irq_handler(_can[1]); + + cortexm_isr_end(); +} +#endif + +#if CANDEV_STM32_CHAN_NUMOF > 2 +void ISR_CAN3_TX(void) +{ + tx_irq_handler(_can[2]); + + cortexm_isr_end(); +} + +void ISR_CAN3_RX0(void) +{ + rx_irq_handler(_can[2], 0); + + cortexm_isr_end(); +} + +void ISR_CAN3_RX1(void) +{ + rx_irq_handler(_can[2], 1); + + cortexm_isr_end(); +} + +void ISR_CAN3_SCE(void) +{ + sce_irq_handler(_can[2]); + + cortexm_isr_end(); +} +#endif +#endif diff --git a/drivers/include/periph/can.h b/drivers/include/periph/can.h new file mode 100644 index 0000000000..08abbf5b16 --- /dev/null +++ b/drivers/include/periph/can.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2018 OTA keys S.A. + * + * 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. + */ + +/** + * @defgroup drivers_periph_can CAN + * @ingroup drivers_periph + * @brief Low-level CAN peripheral driver interface + * + * This is a basic abstract peripheral to be used with candev. + * + * The @p can_t type should be compatible with @p candev_t to be used by the + * candev interface. + * + * See auto init for more details how the can_t is used. + * + * @{ + * + * @file + * @brief Low-level CAN peripheral driver interface definitions + * + * @author Vincent Dupont + */ + +#ifndef PERIPH_CAN_H +#define PERIPH_CAN_H + +#include "periph_cpu.h" +#include "can/candev.h" + +#ifndef HAVE_CAN_T +/** + * @brief CAN device descriptor identifier + */ +typedef candev_t can_t; +#endif + +#ifndef HAVE_CAN_CONF_T +/** + * @brief CAN configuration identifier + */ +typedef void can_conf_t; +#endif + +/** + * @brief Initialize a periph CAN device with the given configuration + * + * @param[in,out] dev device descriptor + * @param[in] conf device vonfiguration + */ +void can_init(can_t *dev, const can_conf_t *conf); + +#endif /* PERIPH_CAN_H */ diff --git a/sys/auto_init/can/auto_init_can.c b/sys/auto_init/can/auto_init_can.c index c340dfcc67..6496413731 100644 --- a/sys/auto_init/can/auto_init_can.c +++ b/sys/auto_init/can/auto_init_can.c @@ -54,8 +54,18 @@ void auto_init_candev(void) auto_init_can_native(); #endif +#ifdef MODULE_PERIPH_CAN + extern void auto_init_periph_can(void); + auto_init_periph_can(); +#endif + #ifdef MODULE_ESP_CAN extern void auto_init_esp_can(void); auto_init_esp_can(); #endif + +#ifdef MODULE_CAN_STM32 + extern void auto_init_can_stm32(void); + auto_init_can_stm32(); +#endif } diff --git a/sys/auto_init/can/auto_init_periph_can.c b/sys/auto_init/can/auto_init_periph_can.c new file mode 100644 index 0000000000..69d941649a --- /dev/null +++ b/sys/auto_init/can/auto_init_periph_can.c @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2016-2018 OTA keys S.A. + * + * 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 auto_init + * @{ + * @file + * @brief initializes periph_can devices + * + * @author Vincent Dupont + * @} + */ + +#ifdef MODULE_PERIPH_CAN +#include "can/device.h" +#include "can_params.h" + +#define CANDEV_NUMOF ((sizeof(candev_params) / sizeof(candev_params[0]))) + +#ifndef CANDEV_STACKSIZE +#define CANDEV_STACKSIZE (THREAD_STACKSIZE_DEFAULT + THREAD_EXTRA_STACKSIZE_PRINTF) +#endif + +#ifndef CANDEV_BASE_PRIORITY +#define CANDEV_BASE_PRIORITY (THREAD_PRIORITY_MAIN - CANDEV_NUMOF - 2) +#endif + +static candev_dev_t candev_dev[CANDEV_NUMOF]; +static char _can_stacks[CANDEV_NUMOF][CANDEV_STACKSIZE]; +static can_t candev[CANDEV_NUMOF]; + +void auto_init_periph_can(void) { + + for (size_t i = 0; i < CANDEV_NUMOF; i++) { + can_init(&candev[i], &candev_conf[i]); + candev_dev[i].dev = (candev_t *)&candev[i]; + candev_dev[i].name = candev_params[i].name; +#ifdef MODULE_CAN_TRX + candev_dev[i].trx = candev_params[i].trx; +#endif +#ifdef MODULE_CAN_PM + candev_dev[i].rx_inactivity_timeout = candev_params[i].rx_inactivity_timeout; + candev_dev[i].tx_wakeup_timeout = candev_params[i].tx_wakeup_timeout; +#endif + + can_device_init(_can_stacks[i], CANDEV_STACKSIZE, CANDEV_BASE_PRIORITY + i, + candev_params[i].name, &candev_dev[i]); + } +} +#else +typedef int dont_be_pedantic; +#endif