diff --git a/drivers/include/candev_mcp2515.h b/drivers/include/candev_mcp2515.h new file mode 100644 index 0000000000..5d01ccde64 --- /dev/null +++ b/drivers/include/candev_mcp2515.h @@ -0,0 +1,121 @@ +/* + * 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. + */ + +/** + * @defgroup drivers_mcp2515 MCP2515 + * @ingroup drivers_can + * @brief Driver for the Microchip MCP2515 can controller. + * + * @{ + * + * @file + * @brief Definition of the implementation of the CAN controller driver. + * + * + * @author Toon Stegen + * @author Vincent Dupont + */ + +#ifndef CANDEV_MCP2515_H +#define CANDEV_MCP2515_H + +#include + +#include "can/candev.h" +#include "cpu_conf.h" +#include "periph/gpio.h" +#include "periph/spi.h" +#include "mutex.h" +#include "xtimer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Number of transmit mailboxes + */ +#define MCP2515_TX_MAILBOXES 3 + +/** + * @name Receive mailboxes and filters number + * @{ + * for RX buffers: the MCP2515 managed 6 acceptance filters in 2 mailboxes: + * - MB0 contains 2 acceptance filters in relation with 1 acceptance mask + * - MB1 contains 4 acceptance filters in relation with 1 acceptance mask + * + * MB0 MB1 + * +------+ +------+ + * mask 0 | RXM0 | | RXM1 | mask 1 + * +======+ +======+ + * filter 0 | RXF0 | | RXF2 | filter 2 + * +------+ +------+ + * filter 1 | RXF1 | | RXF3 | filter 3 + * +------+ +------+ + * | RXF4 | filter 4 + * +------+ + * | RXF5 | filter 5 + * +------+ + */ +#define MCP2515_RX_MAILBOXES 2 +#define MCP2515_FILTERS_MB0 2 +#define MCP2515_FILTERS_MB1 4 +#define MCP2515_FILTERS (MCP2515_FILTERS_MB0 + MCP2515_FILTERS_MB1) +/** @} */ + +/** MCP2515 candev descriptor */ +typedef struct candev_mcp2515 candev_mcp2515_t; + +/** + * @brief MCP2515 configuration descriptor + */ +typedef struct candev_mcp2515_conf { + spi_t spi; /**< SPI bus */ + spi_mode_t spi_mode; /**< SPI mode */ + spi_clk_t spi_clk; /**< SPI clock speed */ + gpio_t cs_pin; /**< Slave select pin */ + gpio_t rst_pin; /**< Reset pin */ + gpio_t int_pin; /**< Interrupt pin */ + uint32_t clk; /**< External clock frequency */ +} candev_mcp2515_conf_t; + +/** + * @brief MCP2515 device descriptor + */ +struct candev_mcp2515 { + /** candev driver */ + candev_t candev; + /** driver configuration */ + const candev_mcp2515_conf_t *conf; + /** tx mailboxes local copy */ + const struct can_frame *tx_mailbox[MCP2515_TX_MAILBOXES]; + /** rx mailboxes local copy */ + struct can_frame rx_buf[MCP2515_RX_MAILBOXES]; + /** masks list */ + uint32_t masks[MCP2515_RX_MAILBOXES]; + /** filters list */ + canid_t filter_ids[MCP2515_RX_MAILBOXES][MCP2515_FILTERS_MB1]; + /** wakeup source */ + int wakeup_src; +}; + +/** + * @brief Initialize a mcp2515 device by assigning a @p timing and an SPI + * configuration @p conf. + * + * @param[out] dev mcp2515 device descriptor + * @param[in] conf mcp2515 configuration + */ +void candev_mcp2515_init(candev_mcp2515_t *dev, const candev_mcp2515_conf_t *conf); + +#ifdef __cplusplus +} +#endif + +#endif /* CANDEV_MCP2515_H */ +/** @} */ diff --git a/drivers/mcp2515/Makefile b/drivers/mcp2515/Makefile new file mode 100644 index 0000000000..d686bef0e4 --- /dev/null +++ b/drivers/mcp2515/Makefile @@ -0,0 +1,3 @@ +MODULE = mcp2515 + +include $(RIOTBASE)/Makefile.base diff --git a/drivers/mcp2515/candev_mcp2515.c b/drivers/mcp2515/candev_mcp2515.c new file mode 100644 index 0000000000..449bc453c3 --- /dev/null +++ b/drivers/mcp2515/candev_mcp2515.c @@ -0,0 +1,570 @@ +/* + * 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 drivers_mcp2515 Stand-Alone CAN Controller With SPI Interface + * @{ + * + * @file + * @brief Implementation of the CAN controller driver + * + * @author Toon Stegen + * @author Vincent Dupont + * @} + */ + +#include +#include +#include +#include + +#include "candev_mcp2515.h" +#include "can/common.h" +#include "can/device.h" +#include "mcp2515.h" +#include "periph_conf.h" +#include "thread.h" +#include "sched.h" +#include "mutex.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#define MAILBOX_USED 1 +#define MAILBOX_EMPTY 0 + +#define MAILBOX_TO_INTR(mailbox) (INT_RX0 + (mailbox)) + +#ifndef CANDEV_MCP2515_DEFAULT_BITRATE +#define CANDEV_MCP2515_DEFAULT_BITRATE 500000 +#endif + +#ifndef CANDEV_MCP2515_DEFAULT_SPT +#define CANDEV_MCP2515_DEFAULT_SPT 875 +#endif + +static int _init(candev_t *candev); +static int _send(candev_t *candev, const struct can_frame *frame); +static void _isr(candev_t *candev); +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 _abort(candev_t *candev, const struct can_frame *frame); +static int _set_filter(candev_t *dev, const struct can_filter * filter); +static int _remove_filter(candev_t *dev, const struct can_filter * filter); + +static void _irq_rx(candev_mcp2515_t *dev, int handle); +static void _irq_tx(candev_mcp2515_t *dev, int handle); +static void _irq_error(candev_mcp2515_t *dev); +static void _irq_message_error(candev_mcp2515_t *dev); +static void _irq_wakeup(const candev_mcp2515_t *dev); +static void _send_event(const candev_mcp2515_t *dev, candev_event_t event, void *arg); + +static const candev_driver_t candev_mcp2515_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 = 3, + .tseg1_max = 16, + .tseg2_min = 2, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 64, + .brp_inc = 1, +}; + +static inline int _max_filters(int mailbox) +{ + return mailbox == 0 ? MCP2515_FILTERS_MB0 : MCP2515_FILTERS_MB1; +} + +void candev_mcp2515_init(candev_mcp2515_t *dev, const candev_mcp2515_conf_t *conf) +{ + memset(dev, 0, sizeof(*dev)); + dev->candev.driver = &candev_mcp2515_driver; + + struct can_bittiming timing = { .bitrate = CANDEV_MCP2515_DEFAULT_BITRATE, + .sample_point = CANDEV_MCP2515_DEFAULT_SPT }; + can_device_calc_bittiming(conf->clk / 2, &bittiming_const, &timing); + + memcpy(&dev->candev.bittiming, &timing, sizeof(timing)); + /* configure filters to be closed */ + for (int mailbox = 0; mailbox < MCP2515_RX_MAILBOXES; mailbox++) { + dev->masks[mailbox] = 0; + for (int filter_id = 0; filter_id < MCP2515_FILTERS_MB1; filter_id++) { + dev->filter_ids[mailbox][filter_id] = 0; + } + } + + dev->conf = conf; +} + +static void _mcp2515_irq_handler(void *arg) +{ + candev_mcp2515_t *candev = (candev_mcp2515_t *) arg; + _send_event(candev, CANDEV_EVENT_ISR, NULL); +} + +static int _init(candev_t *candev) +{ + int res = 0; + candev_mcp2515_t *dev = (candev_mcp2515_t *)candev; + + memset(dev->tx_mailbox, 0, sizeof(dev->tx_mailbox)); + + mcp2515_init(dev, _mcp2515_irq_handler); + mcp2515_reset(dev); + mcp2515_set_mode(dev, MODE_CONFIG); + mcp2515_configure_bittiming(dev); + mcp2515_init_irqs(dev); + + /* configure filters to be closed */ + for (int mailbox = 0; mailbox < MCP2515_RX_MAILBOXES; mailbox++) { + mcp2515_set_mask(dev, mailbox, dev->masks[mailbox]); + for (int filter = 0; filter < _max_filters(mailbox); filter++) { + mcp2515_set_filter(dev, mailbox * MCP2515_FILTERS_MB0 + filter, + dev->filter_ids[mailbox][filter]); + } + } + res = mcp2515_set_mode(dev, MODE_NORMAL); + return res; +} + +static int _send(candev_t *candev, const struct can_frame *frame) +{ + candev_mcp2515_t *dev = (candev_mcp2515_t *)candev; + int box; + enum mcp2515_mode mode; + + mode = mcp2515_get_mode(dev); + if (mode != MODE_NORMAL && mode != MODE_LOOPBACK) { + return -EINVAL; + } + + DEBUG("Inside mcp2515 send\n"); + + for (box = 0; box < MCP2515_TX_MAILBOXES; box++) { + if (dev->tx_mailbox[box] == NULL) { + break; + } + } + + if (box == MCP2515_TX_MAILBOXES) { + return -EBUSY; + } + + dev->tx_mailbox[box] = frame; + + mcp2515_send(dev, frame, box); + + return box; +} + +static int _abort(candev_t *candev, const struct can_frame *frame) +{ + candev_mcp2515_t *dev = (candev_mcp2515_t *)candev; + int box; + + DEBUG("Inside mcp2515 abort\n"); + + for (box = 0; box < MCP2515_TX_MAILBOXES; box++) { + if (dev->tx_mailbox[box] == frame) { + break; + } + } + + if (box == MCP2515_TX_MAILBOXES) { + return -EBUSY; + } + + mcp2515_abort(dev, box); + dev->tx_mailbox[box] = NULL; + + return 0; +} + +static void _isr(candev_t *candev) +{ + uint8_t flag; + candev_mcp2515_t *dev = (candev_mcp2515_t *)candev; + + while((flag = mcp2515_get_irq(dev))) { + if (flag & INT_WAKEUP) { + if (dev->wakeup_src == MCP2515_WKUP_SRC_INTERNAL) { + dev->wakeup_src = 0; + } + else { + _irq_wakeup(dev); + } + } + if (flag & INT_ERROR) { + _irq_error(dev); + } + if (flag & INT_RX0) { + _irq_rx(dev, 0); + } + if (flag & INT_RX1) { + _irq_rx(dev, 1); + } + if (flag & INT_TX0) { + _irq_tx(dev, 0); + } + if (flag & INT_TX1) { + _irq_tx(dev, 1); + } + if (flag & INT_TX2) { + _irq_tx(dev, 2); + } + if (flag & INT_MESSAGE_ERROR) { + _irq_message_error(dev); + } + + /* clear all flags except for RX flags, which are cleared by receiving */ + mcp2515_clear_irq(dev, flag & ~INT_RX0 & ~INT_RX1); + } +} + +static int _set(candev_t *candev, canopt_t opt, void *value, size_t value_len) +{ + candev_mcp2515_t *dev = (candev_mcp2515_t *)candev; + int res = 0; + + DEBUG("Inside mcp2515 set opt=%d\n", opt); + switch (opt) { + case CANOPT_BITTIMING: /**< bit timing parameter */ + if (value_len < sizeof(candev->bittiming)) { + res = -EOVERFLOW; + } + else { + memcpy(&candev->bittiming, value, sizeof(candev->bittiming)); + res = _init(candev); + if (res == 0) { + res = sizeof(candev->bittiming); + } + + } + break; + case CANOPT_STATE: + if (value_len < sizeof(uint8_t)) { + res = -EOVERFLOW; + } + else { + switch (*((canopt_state_t *)value)) { + case CANOPT_STATE_LISTEN_ONLY: + res = mcp2515_set_mode(dev, MODE_LISTEN_ONLY); + break; + case CANOPT_STATE_OFF: + case CANOPT_STATE_SLEEP: + res = mcp2515_set_mode(dev, MODE_SLEEP); + break; + case CANOPT_STATE_ON: + res = mcp2515_set_mode(dev, MODE_NORMAL); + break; + default: + res = -ENOTSUP; + break; + } + } + break; + default: + res = -ENOTSUP; + } + + return res; +} + +static int _get(candev_t *candev, canopt_t opt, void *value, size_t max_len) +{ + candev_mcp2515_t *dev = (candev_mcp2515_t *)candev; + int res = 0; + + DEBUG("Inside mcp2515 get opt=%d\n", opt); + switch (opt) { + case CANOPT_BITTIMING: + if (max_len < sizeof(candev->bittiming)) { + res = -EOVERFLOW; + } + else { + memcpy(value, &candev->bittiming, sizeof(candev->bittiming)); + res = sizeof(candev->bittiming); + } + break; + case CANOPT_RX_FILTERS: /**< rx filters */ + if (max_len % sizeof(struct can_filter) != 0) { + res = -EOVERFLOW; + } + else { + res = 1; + } + 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) = (uint32_t)(dev->conf->clk / 2); + res = sizeof(uint32_t); + } + break; + default: + res = -ENOTSUP; + break; + } + + return res; +} + +static int _set_filter(candev_t *dev, const struct can_filter *filter) +{ + DEBUG("inside _set_filter of MCP2515\n"); + int filter_added = -1; + struct can_filter f = *filter; + int res = -1; + enum mcp2515_mode mode; + + candev_mcp2515_t *dev_mcp = (candev_mcp2515_t *) dev; + + if (f.can_mask == 0) { + return -EINVAL; /* invalid mask */ + } + + mode = mcp2515_get_mode(dev_mcp); + res = mcp2515_set_mode(dev_mcp, MODE_CONFIG); + if (res != MODE_CONFIG) { + return -1; + } + + if ((f.can_id & CAN_EFF_FLAG) == CAN_EFF_FLAG) { + f.can_mask &= CAN_EFF_MASK; + } + else { + f.can_mask &= CAN_SFF_MASK; + } + + /* Browse on each mailbox to find an empty space */ + int mailbox_index = 0; + while (mailbox_index < MCP2515_RX_MAILBOXES && filter_added == -1) { + /* mask unused */ + if (dev_mcp->masks[mailbox_index] == 0) { + /* set mask */ + mcp2515_set_mask(dev_mcp, mailbox_index, f.can_mask); + /* set filter */ + mcp2515_set_filter(dev_mcp, MCP2515_FILTERS_MB0 * mailbox_index, f.can_id); + + /* save filter */ + dev_mcp->masks[mailbox_index] = f.can_mask; + dev_mcp->filter_ids[mailbox_index][0] = f.can_id; + + /* function succeeded */ + filter_added = 1; + } + + /* mask existed and same mask */ + else if (dev_mcp->masks[mailbox_index] == f.can_mask) { + /* find en empty space if it exist */ + int filter_pos = 1; /* first one is already filled */ + /* stop at the end of mailbox or an empty space found */ + while (filter_pos < _max_filters(mailbox_index) && + dev_mcp->filter_ids[mailbox_index][filter_pos] != 0) { + filter_pos++; + } + + /* an empty space is found */ + if (filter_pos < _max_filters(mailbox_index)) { + /* set filter on this memory space */ + mcp2515_set_filter(dev_mcp, MCP2515_FILTERS_MB0 * mailbox_index + filter_pos, f.can_id); + + /* save filter */ + dev_mcp->filter_ids[mailbox_index][filter_pos] = f.can_id; + + /* function succeeded */ + filter_added = 1; + } + } + mailbox_index++; + } + + mcp2515_set_mode(dev_mcp, mode); + + return filter_added; +} + +static int _remove_filter(candev_t *dev, const struct can_filter *filter) +{ + DEBUG("inside _remove_filter of MCP2515\n"); + int filter_removed = -1; + struct can_filter f = *filter; + int res = 0; + enum mcp2515_mode mode; + + candev_mcp2515_t *dev_mcp = (candev_mcp2515_t *) dev; + + if (f.can_mask == 0) { + return -1; /* invalid mask */ + } + + mode = mcp2515_get_mode(dev_mcp); + res = mcp2515_set_mode(dev_mcp, MODE_CONFIG); + if(res < 0) { + return -1; + } + + if ((f.can_id & CAN_EFF_FLAG) == CAN_EFF_FLAG) { + f.can_mask &= CAN_EFF_MASK; + } + else { + f.can_mask &= CAN_SFF_MASK; + } + + int mailbox_index = 0; + /* Browse on each mailbox to find the right filter id */ + while (mailbox_index < MCP2515_RX_MAILBOXES && filter_removed == -1) { + /* same mask */ + if (dev_mcp->masks[mailbox_index] == f.can_mask) { + int filter_pos = 0; + /* stop at the end of mailbox or filter_id found */ + while (filter_pos < _max_filters(mailbox_index) && + dev_mcp->filter_ids[mailbox_index][filter_pos] != f.can_id) { + filter_pos++; + } + + /* filter id found */ + if (filter_pos < _max_filters(mailbox_index)) { + /* remove filter */ + mcp2515_set_filter(dev_mcp, MCP2515_FILTERS_MB0 * mailbox_index + filter_pos, CAN_EFF_MASK); + /* save modification */ + dev_mcp->filter_ids[mailbox_index][filter_pos] = 0; + + /* check mailbox empty */ + int nb_item = 0; + for (int i = 0; i < _max_filters(mailbox_index); i++) { + if (dev_mcp->filter_ids[mailbox_index][i] == 0) { + nb_item++; + } + } + + /* mailbox empty */ + if (nb_item == _max_filters(mailbox_index)) { + /* remove mask */ + mcp2515_set_mask(dev_mcp, mailbox_index, CAN_EFF_MASK); + /* save modification */ + dev_mcp->masks[mailbox_index] = 0; + } + + filter_removed = 0; + } + } + mailbox_index++; + } + mcp2515_set_mode(dev_mcp, mode); + + return filter_removed; +} + +static void _irq_rx(candev_mcp2515_t *dev, int box) +{ + DEBUG("Inside mcp2515 rx irq, box=%d\n", box); + + mcp2515_receive(dev, &dev->rx_buf[box], box); + + _send_event(dev, CANDEV_EVENT_RX_INDICATION, &dev->rx_buf[box]); +} + +static void _irq_tx(candev_mcp2515_t *dev, int box) +{ + DEBUG("Inside mcp2515 tx irq\n"); + const struct can_frame *frame = dev->tx_mailbox[box]; + dev->tx_mailbox[box] = NULL; + + _send_event(dev, CANDEV_EVENT_TX_CONFIRMATION, (void *)frame); +} + +static void _irq_error(candev_mcp2515_t *dev) +{ + uint8_t err; + DEBUG("Inside mcp2515 error irq\n"); + + err = mcp2515_get_errors(dev); + + if(err & (ERR_WARNING | ERR_RX_WARNING | ERR_TX_WARNING)) { + DEBUG("Error Warning\n"); + _send_event(dev, CANDEV_EVENT_ERROR_WARNING, NULL); + } + else if(err & (ERR_RX_PASSIVE | ERR_TX_PASSIVE)) { + DEBUG("Error Passive\n"); + _send_event(dev, CANDEV_EVENT_ERROR_PASSIVE, NULL); + } + else if(err & ERR_TX_BUS_OFF) { + DEBUG("Buss Off\n"); + _send_event(dev, CANDEV_EVENT_BUS_OFF, NULL); + } + else if(err & (ERR_RX_0_OVERFLOW | ERR_RX_1_OVERFLOW)) { + DEBUG("RX overflow\n"); + _send_event(dev, CANDEV_EVENT_RX_ERROR, NULL); + } +} + +static void _irq_message_error(candev_mcp2515_t *dev) +{ + (void) dev; +#if(0) + int box; + + DEBUG("Inside mcp2515 message error irq\n"); + + for (box = 0; box < MCP2515_TX_MAILBOXES; box++) { + if (mcp2515_tx_err_occurred(dev, box)) { + DEBUG("Box: %d\n", box); + + mutex_lock(&dev->tx_mutex); + mcp2515_abort(dev, box); + xtimer_remove(&dev->tx_mailbox[box].timer); + mutex_unlock(&dev->tx_mutex); + + _send_event(dev, CANDEV_EVENT_TIMEOUT_TX_CONF, (void *) dev->tx_mailbox[box].pkt); + + mutex_lock(&dev->tx_mutex); + dev->tx_mailbox[box].pkt = NULL; + mutex_unlock(&dev->tx_mutex); + } + } +#endif +} + +static void _irq_wakeup(const candev_mcp2515_t *dev) +{ + DEBUG("Inside mcp2515 wakeup irq\n"); + + _send_event(dev, CANDEV_EVENT_WAKE_UP, NULL); +} + +static void _send_event(const candev_mcp2515_t *dev, candev_event_t event, void *arg) +{ + candev_t *candev = (candev_t *) dev; + + if (candev->event_callback) { + candev->event_callback(candev, event, arg); + } +} diff --git a/drivers/mcp2515/include/mcp2515_params.h b/drivers/mcp2515/include/mcp2515_params.h new file mode 100644 index 0000000000..bb2362edba --- /dev/null +++ b/drivers/mcp2515/include/mcp2515_params.h @@ -0,0 +1,66 @@ +#ifndef MCP2515_PARAMS_H +#define MCP2515_PARAMS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "can/device.h" +#include "candev_mcp2515.h" + +#include "board.h" + +#ifndef MCP2515_PARAM_SPI +#define MCP2515_PARAM_SPI SPI_DEV(0) +#endif + +#ifndef MCP2515_PARAM_SPI_MODE +#define MCP2515_PARAM_SPI_MODE SPI_MODE_0 +#endif + +#ifndef MCP2515_PARAM_SPI_CLK +#define MCP2515_PARAM_SPI_CLK SPI_CLK_10MHZ +#endif + +#ifndef MCP2515_PARAM_CS +#define MCP2515_PARAM_CS SPI_HWCS(0) +#endif + +#ifndef MCP2515_PARAM_RST +#define MCP2515_PARAM_RST GPIO_PIN(0, 0) +#endif + +#ifndef MCP2515_PARAM_INT +#define MCP2515_PARAM_INT GPIO_PIN(0, 1) +#endif + +#ifndef MCP2515_PARAM_CLK +#define MCP2515_PARAM_CLK (8000000ul) +#endif + +#define MCP2515_DEFAULT_CONFIG \ +{ \ + .spi = MCP2515_PARAM_SPI, \ + .spi_mode = MCP2515_PARAM_SPI_MODE, \ + .spi_clk =MCP2515_PARAM_SPI_CLK, \ + .cs_pin = MCP2515_PARAM_CS, \ + .rst_pin = MCP2515_PARAM_RST, \ + .int_pin = MCP2515_PARAM_INT, \ + .clk = MCP2515_PARAM_CLK, \ +} + +const static candev_mcp2515_conf_t candev_mcp2515_conf[] = { + MCP2515_DEFAULT_CONFIG +}; + +const static candev_params_t candev_mcp2515_params[] = { + { + .name = "can_cmp2515_0", + }, +}; + +#ifdef __cplusplus +} +#endif + +#endif /* MCP2515_PARAMS_H */ diff --git a/drivers/mcp2515/mcp2515.c b/drivers/mcp2515/mcp2515.c new file mode 100644 index 0000000000..eee7fc3908 --- /dev/null +++ b/drivers/mcp2515/mcp2515.c @@ -0,0 +1,364 @@ +/* + * Copyright (C) 2016 OTA keys + * + * 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 drivers_mcp2515 + * @{ + * + * @file + * @brief mcp2515 can spi driver + * + * @author Toon Stegen + * @} + */ + +#include "xtimer.h" +#include + +#include "mcp2515.h" +#include "mcp2515_spi.h" +#include "mcp2515_defines.h" + +#include "can/candev.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/* reset delay should be at least 2 microseconds */ +#define RESET_DELAY 2 + +/* size of transmission and reception buffers in mcp2515 */ +#define BUFFER_SIZE 13 + +/* macros for getting the TX and RX control registers */ +#define TX_CTRL(mailbox) ((MCP2515_TXB0CTRL) + ((mailbox) << 4)) +#define RX_CTRL(mailbox) ((MCP2515_RXB0CTRL) + ((mailbox) << 4)) + +/* oscillator startup time + * 128 cycles @ clock freq + some extra */ +static inline uint32_t ost_delay(candev_mcp2515_t *dev) +{ + return (128 / (dev->conf->clk / 1000000) + 2); +} + +/** + * @brief enable MCP2515 interrupt @p irq + * + * @param[in] dev device descriptor + * @param[in] irq interrupt to enable + * + * @return 0 on success + * @return <0 on error + */ +static int mcp2515_enable_irq(candev_mcp2515_t *dev, uint8_t irq); + +/** + * @brief Fill tx/rx standard buffer instruction from filter identifier @p id + * + * The Read RX Buffer instruction provides a means to quickly address a receive + * buffer for reading. This instruction reduces the SPI overhead by one byte, + * the address byte. The command byte actually has four possible values that + * determine the address pointer location. + * + * The Load TX Buffer instruction eliminates the eight-bit address required by + * a normal write command. The eight-bit instruction sets the address pointer + * to one of six addresses to quickly write to a transmit buffer that points to + * the “ID” or “data” address of any of the three transmit buffers. + * + * @param[in] id filter identifier in the MCP2515 mailbox + * @param|out] bytebuf buffer instruction + */ +static void fill_standard_id(uint32_t id, uint8_t *bytebuf); + +/** + * @brief Fill tx/rx extended buffer instruction from filter identifier @p id + * + * for more details see fill_standard_id. + * + * @param[in] id filter identifier in the MCP2515 mailbox + * @param|out] bytebuf buffer instruction + */ +static void fill_extended_id(uint32_t id, uint8_t *bytebuf); + +int mcp2515_init(candev_mcp2515_t *dev, void(*irq_handler_cb)(void*)) +{ + int res; + gpio_init_int(dev->conf->int_pin, GPIO_IN_PU, GPIO_FALLING, (gpio_cb_t)irq_handler_cb, (void *)dev); + gpio_init(dev->conf->rst_pin, GPIO_OUT); + /*the CS pin should be initialized & set in board.c to avoid conflict with other SPI devices */ + + res = mcp2515_spi_init(dev); + if (res < 0){ + return -1; + } + return 0; +} + +void mcp2515_reset(candev_mcp2515_t *dev) +{ + gpio_clear(dev->conf->rst_pin); + xtimer_usleep(RESET_DELAY); + gpio_set(dev->conf->rst_pin); + xtimer_usleep(ost_delay(dev)); +} + +static void fill_standard_id(uint32_t id, uint8_t *bytebuf) +{ + bytebuf[0] = (uint8_t) ((id & 0x000007F8UL) >> 3); /* T/RXBnSIDH */ + bytebuf[1] = (uint8_t) ((id & 0x00000007UL) << 5); /* T/RXBnSIDL */ + bytebuf[2] = (uint8_t) ((id & 0xFF000000UL) >> 24); /* T/RXBnEID8 */ + bytebuf[3] = (uint8_t) ((id & 0x00FF0000UL) >> 16); /* T/RXBnEID0 */ +} + +static void fill_extended_id(uint32_t id, uint8_t *bytebuf) +{ + bytebuf[0] = (uint8_t) ((id & 0x1FE00000UL) >> 21); /* T/RXBnSIDH */ + bytebuf[1] = (uint8_t) ((id & 0x001C0000UL) >> 12) + | (uint8_t) ((id & 0x00030000UL) >> 16) | 0x08; /* T/RXBnSIDL */ + bytebuf[2] = (uint8_t) ((id & 0x0000FF00UL) >> 8); /* T/RXBnEID8 */ + bytebuf[3] = (uint8_t) (id & 0x000000FFUL); /* T/RXBnEID0 */ +} + +int mcp2515_send(candev_mcp2515_t *dev, const struct can_frame *frame, int mailbox) +{ + uint8_t prio = 1; /* TODO: adjust priority */ + uint8_t outbuf[BUFFER_SIZE]; + uint8_t ctrl; + + //TODO: for speedup, remove this check + mcp2515_spi_read(dev, TX_CTRL(mailbox), &ctrl, 1); + if (ctrl & MCP2515_TXBCTRL_TXREQ) { + DEBUG("Mailbox in use, TXB%dCTRL: 0x%02x\n", mailbox, ctrl); + return -1; + } + + if ((frame->can_id & CAN_EFF_FLAG) == CAN_EFF_FLAG) { + fill_extended_id(frame->can_id, outbuf); + } + else { + fill_standard_id(frame->can_id, outbuf); + } + + outbuf[4] = frame->can_dlc; + memcpy(outbuf+5, frame->data, frame->can_dlc); + + /* set mailbox priority */ + mcp2515_spi_write(dev, TX_CTRL(mailbox), &prio, 1); + + mcp2515_spi_write_txbuf(dev, mailbox, outbuf, 5 + frame->can_dlc); + mcp2515_enable_irq(dev, MCP2515_CANINTE_TX0IE << mailbox); + //mcp2515_spi_bitmod(dev, TX_CTRL(mailbox), MCP2515_TXBCTRL_TXREQ, MCP2515_TXBCTRL_TXREQ); + mcp2515_spi_rts(dev, mailbox); + + return mailbox; +} + +int mcp2515_receive(candev_mcp2515_t *dev, struct can_frame *frame, int mailbox) +{ + uint8_t inbuf[BUFFER_SIZE]; + + mcp2515_spi_read_rxbuf(dev, mailbox, inbuf, BUFFER_SIZE); + + /* extended id */ + if (inbuf[1] & MCP2515_RX_IDE) { + frame->can_id = (inbuf[0] << 21) + + (((uint32_t)inbuf[1] & 0xE0) << 13) + + (((uint32_t)inbuf[1] & 0x03) << 16) + + ((uint32_t)inbuf[2] << 8) + + inbuf[3]; + frame->can_id |= CAN_EFF_FLAG; + } + + /* standard id */ + else { + frame->can_id = ((uint32_t)inbuf[0] << 3) + (((uint32_t)inbuf[1] & 0xE0) >> 5); + } + + frame->can_dlc = inbuf[4]; + memcpy(frame->data, inbuf + 5, frame->can_dlc); + return mailbox; +} + +int mcp2515_abort(candev_mcp2515_t *dev, int mailbox) +{ + return mcp2515_spi_bitmod(dev, TX_CTRL(mailbox), MCP2515_TXBCTRL_TXREQ, 0); +} + +enum mcp2515_mode mcp2515_get_mode(candev_mcp2515_t *dev) +{ + enum mcp2515_mode result = MODE_UNKNOWN; + uint8_t mode; + + int res = mcp2515_spi_read(dev, MCP2515_CANSTAT, &mode, 1); + if (res == 0) { + //TODO: error handling of extra information in mode result + result = (enum mcp2515_mode) mode & MCP2515_CANSTAT_OPMOD_MASK; + } + + DEBUG("mcp2515_get_mode: mode=%x\n", result); + + return result; +} + +enum mcp2515_mode mcp2515_set_mode(candev_mcp2515_t *dev, enum mcp2515_mode mode) +{ + DEBUG("mcp2515_set_mode: mode=%x\n", mode); + + if (mode == MODE_UNKNOWN) { + return -1; + } + + enum mcp2515_mode cur_mode = mcp2515_get_mode(dev); + if (cur_mode == mode) { + return mode; + } + if (cur_mode == MODE_SLEEP) { + mcp2515_wake_up(dev); + } + + mcp2515_spi_bitmod(dev, MCP2515_CANCTRL, MCP2515_CANCTRL_REQOP_MASK, (uint8_t) mode); + int cnt = 0; + while (cur_mode != mode && cnt++ < 2) { + cur_mode = mcp2515_get_mode(dev); + } + return cur_mode; +} + +void mcp2515_wake_up(candev_mcp2515_t *dev) +{ + dev->wakeup_src = MCP2515_WKUP_SRC_INTERNAL; + mcp2515_spi_bitmod(dev, MCP2515_CANINTF, MCP2515_CANINTF_WAKIF, MCP2515_CANINTF_WAKIF); + xtimer_usleep(ost_delay(dev)); + + uint8_t flag = mcp2515_get_irq(dev); + if (flag & INT_WAKEUP) { + DEBUG("wakeup, irq raised\n"); + dev->wakeup_src = 0; + mcp2515_clear_irq(dev, INT_WAKEUP); + } +} + +static int mcp2515_enable_irq(candev_mcp2515_t *dev, uint8_t irq) +{ + return mcp2515_spi_bitmod(dev, MCP2515_CANINTE, irq, irq); +} + +int mcp2515_configure_bittiming(candev_mcp2515_t *dev) +{ + struct can_bittiming *tim = &(dev->candev.bittiming); + int res = 0; + uint8_t c; + enum mcp2515_mode mode; + + DEBUG("mcp2515_configure_bittiming: brp=%" PRIu32 ", prop_seg=%" PRIu32 + ", phase_seg1=%" PRIu32 ", phase_seg2=%" PRIu32 "\n", tim->brp, tim->prop_seg, + tim->phase_seg1, tim->phase_seg2); + + mode = mcp2515_get_mode(dev); + if (mode != MODE_CONFIG) { + if (mcp2515_set_mode(dev, MODE_CONFIG) != MODE_CONFIG) { + return -1; + } + } + + c = ((tim->brp - 1) & 0x3F) | ((tim->sjw - 1) << 6); + mcp2515_spi_write(dev, MCP2515_CNF1, &c, 1); + + + mcp2515_spi_bitmod(dev, MCP2515_CNF2, MCP2515_CNF2_PRSEG_MASK | + MCP2515_CNF2_PHSEG_MASK | MCP2515_CNF2_BTLMODE, + MCP2515_CNF2_BTLMODE | (tim->prop_seg - 1) | ((tim->phase_seg1 - 1) << 3)); + mcp2515_spi_bitmod(dev, MCP2515_CNF3, MCP2515_CNF3_PHSEG_MASK | MCP2515_CNF3_WAKFIL, + (tim->phase_seg2 - 1) | MCP2515_CNF3_WAKFIL); + + if (mode != MODE_CONFIG) { + mcp2515_set_mode(dev, mode); + } + + return res; +} + +int mcp2515_init_irqs(candev_mcp2515_t *dev) +{ + return mcp2515_enable_irq(dev, + MCP2515_CANINTE_RX0IE | + MCP2515_CANINTE_RX1IE | + //MCP2515_CANINTE_TX0IE | + //MCP2515_CANINTE_TX1IE | + //MCP2515_CANINTE_TX2IE | + MCP2515_CANINTE_ERRIE | + //MCP2515_CANINTE_MERRE | + MCP2515_CANINTE_WAKIE); +} + +enum mcp2515_interrupt mcp2515_get_irq(candev_mcp2515_t *dev) +{ + uint8_t flag; + mcp2515_spi_read(dev, MCP2515_CANINTF, &flag, 1); + return (enum mcp2515_interrupt) flag; +} + +int mcp2515_clear_irq(candev_mcp2515_t *dev, enum mcp2515_interrupt irq) +{ + if(!irq) { /* no irq's to be cleared */ + return 0; + } + else { + return mcp2515_spi_bitmod(dev, MCP2515_CANINTF, irq, 0); + } +} + +int mcp2515_tx_err_occurred(candev_mcp2515_t *dev, int mailbox) +{ + uint8_t ctrl_reg; + mcp2515_spi_read(dev, TX_CTRL(mailbox), &ctrl_reg, 1); + if (ctrl_reg & MCP2515_TXBCTRL_TXERR) { + return 1; + } + else { + return 0; + } +} + +uint8_t mcp2515_get_errors(candev_mcp2515_t *dev) +{ + uint8_t eflg; + mcp2515_spi_read(dev, MCP2515_EFLG, &eflg, 1); + return eflg; +} + +int mcp2515_set_filter(candev_mcp2515_t *dev, int filter_id, uint32_t filter) +{ + uint8_t buf[4]; + uint8_t reg; + if ((filter & CAN_EFF_FLAG) == CAN_EFF_FLAG) { + fill_extended_id(filter, buf); + } + else { + fill_standard_id(filter, buf); + } + if(filter_id < 3) { + reg = MCP2515_RXF0SIDH + (filter_id << 2); + } + else { + reg = MCP2515_RXF3SIDH + ((filter_id - 3) << 2); + } + return mcp2515_spi_write(dev, reg, buf, 4); +} + +int mcp2515_set_mask(candev_mcp2515_t *dev, int mailbox, uint32_t mask) +{ + uint8_t buf[4]; + if ((mask & CAN_EFF_FLAG) == CAN_EFF_FLAG) { + fill_extended_id(mask, buf); + } + else { + fill_standard_id(mask, buf); + } + return mcp2515_spi_write(dev, MCP2515_RXM0SIDH + (mailbox << 2), buf, 4); +} diff --git a/drivers/mcp2515/mcp2515.h b/drivers/mcp2515/mcp2515.h new file mode 100644 index 0000000000..5941750d11 --- /dev/null +++ b/drivers/mcp2515/mcp2515.h @@ -0,0 +1,291 @@ +/* + * 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 drivers_mcp2515 + * @brief Driver for the Microchip MCP2515 can controller. + * + * @{ + * + * @file + * @brief Definition of the MCP2515 CAN controller driver. + * + * + * @author Toon Stegen + */ +#ifndef MCP2515_H +#define MCP2515_H + +#include "mcp2515_defines.h" +#include "candev_mcp2515.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief MCP2515 mode + */ +enum mcp2515_mode { + MODE_NORMAL = MCP2515_CANSTAT_OPMOD_NORMAL, + MODE_SLEEP = MCP2515_CANSTAT_OPMOD_SLEEP, + MODE_LOOPBACK = MCP2515_CANSTAT_OPMOD_LOOPBACK, + MODE_LISTEN_ONLY = MCP2515_CANSTAT_OPMOD_LISTEN_ONLY, + MODE_CONFIG = MCP2515_CANSTAT_OPMOD_CONFIGURATION, + MODE_UNKNOWN = -1 +}; + +/** + * @brief MCP2515 interrupt + */ +enum mcp2515_interrupt { + INT_RX0 = MCP2515_CANINTF_RX0IF, + INT_RX1 = MCP2515_CANINTF_RX1IF, + INT_TX0 = MCP2515_CANINTF_TX0IF, + INT_TX1 = MCP2515_CANINTF_TX1IF, + INT_TX2 = MCP2515_CANINTF_TX2IF, + INT_ERROR = MCP2515_CANINTF_ERRIF, + INT_WAKEUP = MCP2515_CANINTF_WAKIF, + INT_MESSAGE_ERROR = MCP2515_CANINTF_MERRF, +}; + +/** + * @brief MCP2515 error + */ +enum mcp2515_error { + ERR_WARNING = MCP2515_EFLG_EWARN, + ERR_RX_WARNING = MCP2515_EFLG_RXWAR, + ERR_TX_WARNING = MCP2515_EFLG_TXWAR, + ERR_RX_PASSIVE = MCP2515_EFLG_RXEP, + ERR_TX_PASSIVE = MCP2515_EFLG_TXEP, + ERR_TX_BUS_OFF = MCP2515_EFLG_TXBO, + ERR_RX_0_OVERFLOW = MCP2515_EFLG_RX0OVR, + ERR_RX_1_OVERFLOW = MCP2515_EFLG_RX1OVR, +}; + +/** Wake up source */ +#define MCP2515_WKUP_SRC_INTERNAL 1 + +/** + * @brief Initialize pins and SPI interface + * + * The device descriptor contains all informations related to pins and SPI + * interface. This function initialize all corresponding fields and relies + * the @p irq_cb callback function to the pin interruption. The pin + * interruption should be configured in the device descriptor. + * + * @param[out] dev device descriptor + * @param[in] irq_cb callback function called when an interrupt is raised + * from the MCP2515. The MCP2515 makes the interruption + * through the interruption pin. + * + * @return 0 on success + * @return <0 on error + */ +int mcp2515_init(candev_mcp2515_t *dev, void(*irq_cb)(void*)); + +/** + * @brief Reset MCP2515 device with dedicated pin + * + * The MCP2515 device is reset by toggling the rst pin. + * + * @param[in] dev device descriptor + */ +void mcp2515_reset(candev_mcp2515_t *dev); + +/** + * @brief Initialize MCP2515 interrupts + * + * @param[in] dev device descriptor + * + * @return 0 on success + * @return <0 on error + */ +int mcp2515_init_irqs(candev_mcp2515_t *dev); + +/** + * @brief Send frame through the corresponding tx @p mailbox. + * + * @param[in] dev device descriptor + * @param[in] frame the frame to send + * @param[in] mailbox tx mailbox + * + * @return 0 on success + * @return <0 on error + */ +int mcp2515_send(candev_mcp2515_t *dev, const struct can_frame *frame, int mailbox); + +/** + * @brief Receive frame from the corresponding rx @p mailbox. + * + * @param[in] dev device descriptor + * @param[out] frame the receive frame + * @param[in] mailbox rx mailbox + * + * @return 0 on success + * @return <0 on error + */ +int mcp2515_receive(candev_mcp2515_t *dev, struct can_frame *frame, int mailbox); + +/** + * @brief Abort communication. + * + * @param[in] dev device descriptor + * @param[in] mailbox mailbox + * + * @return 0 on success + * @return <0 on error + */ +int mcp2515_abort(candev_mcp2515_t *dev, int mailbox); + +/** + * @brief Get MCP2515 mode of operation. + * + * @param[in] dev device descriptor + * + * @return mcp2515_mode enum + */ +enum mcp2515_mode mcp2515_get_mode(candev_mcp2515_t *dev); + +/** + * @brief Set MCP2515 mode of operation. + * + * @param[in] dev device descriptor + * @param[in] mode mode of operation to set + * + * @return The mode actually set + */ +enum mcp2515_mode mcp2515_set_mode(candev_mcp2515_t *dev, enum mcp2515_mode mode); + +/** + * @brief Wake up MCP2515 + * + * @param[in] dev device descriptor + */ +void mcp2515_wake_up(candev_mcp2515_t *dev); + +/** + * @brief Get MCP2515 interrupt type. + * + * @param[in] dev device descriptor + * + * @return mcp2515_interrupt enum + */ +enum mcp2515_interrupt mcp2515_get_irq(candev_mcp2515_t *dev); + +/** + * @brief Clear MCP2515 interrupt. + * + * @param[in] dev device descriptor + * @param[in] irq interrupt to clear + * + * @return 0 on success + * @return <0 on error + */ +int mcp2515_clear_irq(candev_mcp2515_t *dev, enum mcp2515_interrupt irq); + +/** + * @brief Get if an tx error occurred on MCP2515. + * + * @param[in] dev device descriptor + * @param[in] mailbox tx mailbox + * + * @return 0 on success + * @return <0 on error + */ +int mcp2515_tx_err_occurred(candev_mcp2515_t *dev, int mailbox); + +/** + * @brief Configure the bit timing of the MCP2515. + * + * The informations about the bit timing should be contained in dev descriptor. + * + * @param[in] dev device descriptor + * + * @return 0 on success + * @return <0 on error + */ +int mcp2515_configure_bittiming(candev_mcp2515_t *dev); + +/** + * @brief Get the error flags. + * + * @param[in] dev device descriptor + * + * @return EFLG error flags + */ +uint8_t mcp2515_get_errors(candev_mcp2515_t *dev); + +/** + * @brief Set the @ filter_id to the position in the mailbox. + * + * @p filter_id corresponds to the position in the MCP2515 mailbox as follow: + * - [0; 1]: mailbox RXB0 + * - [2; 5]: mailbox RXB1 + * + * The MCP2515 managed 6 acceptance filters in 2 rx mailboxes: + * - MB0 contains 2 acceptance filters in relation with 1 acceptance mask + * - MB1 contains 4 acceptance filters in relation with 1 acceptance mask + * + * MB0 MB1 + * +------+ +------+ + * mask 0 | RXM0 | | RXM1 | mask 1 + * +======+ +======+ + * filter 0 | RXF0 | | RXF2 | filter 2 + * +------+ +------+ + * filter 1 | RXF1 | | RXF3 | filter 3 + * +------+ +------+ + * | RXF4 | filter 4 + * +------+ + * | RXF5 | filter 5 + * +------+ + * + * @param[in] dev device descriptor + * @param[in] filter_id filter identifier in the MCP2515 mailbox + * @param[in] filter acceptance filter can identifier + * + * @return 0 on success + * @return <0 on error + */ +int mcp2515_set_filter(candev_mcp2515_t *dev, int filter_id, uint32_t filter); + +/** + * @brief Set the @ mask to the mailbox. + * + * The MCP2515 managed 6 acceptance filters in 2 rx mailboxes: + * - MB0 contains 2 acceptance filters in relation with 1 acceptance mask + * - MB1 contains 4 acceptance filters in relation with 1 acceptance mask + * + * MB0 MB1 + * +------+ +------+ + * mask 0 | RXM0 | | RXM1 | mask 1 + * +======+ +======+ + * filter 0 | RXF0 | | RXF2 | filter 2 + * +------+ +------+ + * filter 1 | RXF1 | | RXF3 | filter 3 + * +------+ +------+ + * | RXF4 | filter 4 + * +------+ + * | RXF5 | filter 5 + * +------+ + * + * @param[in] dev device descriptor + * @param[in] mailbox rx mailbox + * @param[in] mask acceptance mask + * + * @return 0 on success + * @return <0 on error + */ +int mcp2515_set_mask(candev_mcp2515_t *dev, int mailbox, uint32_t mask); + +#ifdef __cplusplus +} +#endif + +#endif /* MCP2515_H */ +/** @} */ diff --git a/drivers/mcp2515/mcp2515_defines.h b/drivers/mcp2515/mcp2515_defines.h new file mode 100644 index 0000000000..e615ea5360 --- /dev/null +++ b/drivers/mcp2515/mcp2515_defines.h @@ -0,0 +1,419 @@ +/* + * 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 drivers_mcp2515 + * @brief Driver for the Microchip MCP2515 can controller. + * + * @{ + * + * @file + * @brief Defines for the MCP2515 can controller driver. + * + * MCP2515 SPI CAN Controller Register & Configuration Constant + * + * @author Toon Stegen + */ +#ifndef MCP2515_DEFINES_H +#define MCP2515_DEFINES_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * User configuration + * @{ + */ +#define CAN_SPI_CS_PORTBIT BIT4 +#define CAN_SPI_CS_PORTOUT P2OUT +#define CAN_SPI_CS_PORTDIR P2DIR + +#define CAN_IRQ_PORTBIT BIT3 +#define CAN_IRQ_PORTOUT P1OUT +#define CAN_IRQ_PORTDIR P1DIR +#define CAN_IRQ_PORTREN P1REN +#define CAN_IRQ_PORTIES P1IES +#define CAN_IRQ_PORTIE P1IE +#define CAN_IRQ_PORTIFG P1IFG +/** @} */ + +/** + * BoosterPack contains 8MHz crystal w/ 22pF load caps + */ +#define CAN_OSC_FREQUENCY 8000000 + +/** + * @name MCP2515 Register Memory Map + * { + */ +#define MCP2515_RXF0SIDH 0x00 +#define MCP2515_RXF0SIDL 0x01 +#define MCP2515_RXF0EID8 0x02 +#define MCP2515_RXF0EID0 0x03 + +#define MCP2515_RXF1SIDH 0x04 +#define MCP2515_RXF1SIDL 0x05 +#define MCP2515_RXF1EID8 0x06 +#define MCP2515_RXF1EID0 0x07 + +#define MCP2515_RXF2SIDH 0x08 +#define MCP2515_RXF2SIDL 0x09 +#define MCP2515_RXF2EID8 0x0A +#define MCP2515_RXF2EID0 0x0B + +#define MCP2515_BFPCTRL 0x0C +#define MCP2515_TXRTSCTRL 0x0D +#define MCP2515_CANSTAT 0x0E +#define MCP2515_CANCTRL 0x0F + +#define MCP2515_RXF3SIDH 0x10 +#define MCP2515_RXF3SIDL 0x11 +#define MCP2515_RXF3EID8 0x12 +#define MCP2515_RXF3EID0 0x13 + +#define MCP2515_RXF4SIDH 0x14 +#define MCP2515_RXF4SIDL 0x15 +#define MCP2515_RXF4EID8 0x16 +#define MCP2515_RXF4EID0 0x17 + +#define MCP2515_RXF5SIDH 0x18 +#define MCP2515_RXF5SIDL 0x19 +#define MCP2515_RXF5EID8 0x1A +#define MCP2515_RXF5EID0 0x1B + +#define MCP2515_TEC 0x1C +#define MCP2515_REC 0x1D + +#define MCP2515_RXM0SIDH 0x20 +#define MCP2515_RXM0SIDL 0x21 +#define MCP2515_RXM0EID8 0x22 +#define MCP2515_RXM0EID0 0x23 + +#define MCP2515_RXM1SIDH 0x24 +#define MCP2515_RXM1SIDL 0x25 +#define MCP2515_RXM1EID8 0x26 +#define MCP2515_RXM1EID0 0x27 + +#define MCP2515_CNF3 0x28 +#define MCP2515_CNF2 0x29 +#define MCP2515_CNF1 0x2A +#define MCP2515_CANINTE 0x2B + +#define MCP2515_CANINTF 0x2C +#define MCP2515_EFLG 0x2D + +#define MCP2515_TXB0CTRL 0x30 +#define MCP2515_TXB0SIDH 0x31 +#define MCP2515_TXB0SIDL 0x32 +#define MCP2515_TXB0EID8 0x33 +#define MCP2515_TXB0EID0 0x34 +#define MCP2515_TXB0DLC 0x35 +#define MCP2515_TXB0D0 0x36 +#define MCP2515_TXB0D1 0x37 +#define MCP2515_TXB0D2 0x38 +#define MCP2515_TXB0D3 0x39 +#define MCP2515_TXB0D4 0x3A +#define MCP2515_TXB0D5 0x3B +#define MCP2515_TXB0D6 0x3C +#define MCP2515_TXB0D7 0x3D + +#define MCP2515_TXB1CTRL 0x40 +#define MCP2515_TXB1SIDH 0x41 +#define MCP2515_TXB1SIDL 0x42 +#define MCP2515_TXB1EID8 0x43 +#define MCP2515_TXB1EID0 0x44 +#define MCP2515_TXB1DLC 0x45 +#define MCP2515_TXB1D0 0x46 +#define MCP2515_TXB1D1 0x47 +#define MCP2515_TXB1D2 0x48 +#define MCP2515_TXB1D3 0x49 +#define MCP2515_TXB1D4 0x4A +#define MCP2515_TXB1D5 0x4B +#define MCP2515_TXB1D6 0x4C +#define MCP2515_TXB1D7 0x4D + +#define MCP2515_TXB2CTRL 0x50 +#define MCP2515_TXB2SIDH 0x51 +#define MCP2515_TXB2SIDL 0x52 +#define MCP2515_TXB2EID8 0x53 +#define MCP2515_TXB2EID0 0x54 +#define MCP2515_TXB2DLC 0x55 +#define MCP2515_TXB2D0 0x56 +#define MCP2515_TXB2D1 0x57 +#define MCP2515_TXB2D2 0x58 +#define MCP2515_TXB2D3 0x59 +#define MCP2515_TXB2D4 0x5A +#define MCP2515_TXB2D5 0x5B +#define MCP2515_TXB2D6 0x5C +#define MCP2515_TXB2D7 0x5D + +#define MCP2515_RXB0CTRL 0x60 +#define MCP2515_RXB0SIDH 0x61 +#define MCP2515_RXB0SIDL 0x62 +#define MCP2515_RXB0EID8 0x63 +#define MCP2515_RXB0EID0 0x64 +#define MCP2515_RXB0DLC 0x65 +#define MCP2515_RXB0D0 0x66 +#define MCP2515_RXB0D1 0x67 +#define MCP2515_RXB0D2 0x68 +#define MCP2515_RXB0D3 0x69 +#define MCP2515_RXB0D4 0x6A +#define MCP2515_RXB0D5 0x6B +#define MCP2515_RXB0D6 0x6C +#define MCP2515_RXB0D7 0x6D + +#define MCP2515_RXB1CTRL 0x70 +#define MCP2515_RXB1SIDH 0x71 +#define MCP2515_RXB1SIDL 0x72 +#define MCP2515_RXB1EID8 0x73 +#define MCP2515_RXB1EID0 0x74 +#define MCP2515_RXB1DLC 0x75 +#define MCP2515_RXB1D0 0x76 +#define MCP2515_RXB1D1 0x77 +#define MCP2515_RXB1D2 0x78 +#define MCP2515_RXB1D3 0x79 +#define MCP2515_RXB1D4 0x7A +#define MCP2515_RXB1D5 0x7B +#define MCP2515_RXB1D6 0x7C +#define MCP2515_RXB1D7 0x7D +/** @} */ + +/** + * @name MCP2515 Control Register bits + * { + */ +#define MCP2515_BFPCTRL_B0BFM 0x01 +#define MCP2515_BFPCTRL_B1BFM 0x02 +#define MCP2515_BFPCTRL_B0BFE 0x04 +#define MCP2515_BFPCTRL_B1BFE 0x08 + +#define MCP2515_BFPCTRL_B0BFS 0x10 +#define MCP2515_BFPCTRL_B1BFS 0x20 + +#define MCP2515_TXRTSCTRL_B0RTSM 0x01 +#define MCP2515_TXRTSCTRL_B1RTSM 0x02 +#define MCP2515_TXRTSCTRL_B2RTSM 0x04 +#define MCP2515_TXRTSCTRL_B0RTS 0x08 +#define MCP2515_TXRTSCTRL_B1RTS 0x10 +#define MCP2515_TXRTSCTRL_B2RTS 0x20 + +#define MCP2515_CANSTAT_ICOD0 0x02 +#define MCP2515_CANSTAT_ICOD1 0x04 +#define MCP2515_CANSTAT_ICOD2 0x08 +#define MCP2515_CANSTAT_OPMOD0 0x20 +#define MCP2515_CANSTAT_OPMOD1 0x40 +#define MCP2515_CANSTAT_OPMOD2 0x80 + +#define MCP2515_CANSTAT_ICOD_MASK 0x0E + +#define MCP2515_CANSTAT_OPMOD_MASK 0xE0 +#define MCP2515_CANSTAT_OPMOD_CONFIGURATION MCP2515_CANSTAT_OPMOD2 +#define MCP2515_CANSTAT_OPMOD_NORMAL 0x00 +#define MCP2515_CANSTAT_OPMOD_SLEEP MCP2515_CANSTAT_OPMOD0 +#define MCP2515_CANSTAT_OPMOD_LOOPBACK MCP2515_CANSTAT_OPMOD1 +#define MCP2515_CANSTAT_OPMOD_LISTEN_ONLY (MCP2515_CANSTAT_OPMOD1 | MCP2515_CANSTAT_OPMOD0) + +#define MCP2515_CANCTRL_CLKPRE0 0x01 +#define MCP2515_CANCTRL_CLKPRE1 0x02 +#define MCP2515_CANCTRL_CLKEN 0x04 +#define MCP2515_CANCTRL_OSM 0x08 +#define MCP2515_CANCTRL_ABAT 0x10 +#define MCP2515_CANCTRL_REQOP0 0x20 +#define MCP2515_CANCTRL_REQOP1 0x40 +#define MCP2515_CANCTRL_REQOP2 0x80 + +#define MCP2515_CANCTRL_CLKPRE_MASK (MCP2515_CANCTRL_CLKPRE1 | MCP2515_CANCTRL_CLKPRE0) + +#define MCP2515_CANCTRL_REQOP_MASK 0xE0 +#define MCP2515_CANCTRL_REQOP_CONFIGURATION MCP2515_CANCTRL_REQOP2 +#define MCP2515_CANCTRL_REQOP_NORMAL 0x00 +#define MCP2515_CANCTRL_REQOP_SLEEP MCP2515_CANCTRL_REQOP0 +#define MCP2515_CANCTRL_REQOP_LOOPBACK MCP2515_CANCTRL_REQOP1 +#define MCP2515_CANCTRL_REQOP_LISTEN_ONLY (MCP2515_CANCTRL_REQOP1 | MCP2515_CANCTRL_REQOP0) + +#define MCP2515_CNF3_PHSEG20 0x01 +#define MCP2515_CNF3_PHSEG21 0x02 +#define MCP2515_CNF3_PHSEG22 0x04 +#define MCP2515_CNF3_WAKFIL 0x40 +#define MCP2515_CNF3_SOF 0x80 + +#define MCP2515_CNF3_PHSEG_MASK 0x07 + +#define MCP2515_CNF2_PRSEG0 0x01 +#define MCP2515_CNF2_PRSEG1 0x02 +#define MCP2515_CNF2_PRSEG2 0x04 +#define MCP2515_CNF2_PHSEG10 0x08 +#define MCP2515_CNF2_PHSEG11 0x10 +#define MCP2515_CNF2_PHSEG12 0x20 +#define MCP2515_CNF2_SAM 0x40 +#define MCP2515_CNF2_BTLMODE 0x80 + +#define MCP2515_CNF2_PRSEG_MASK 0x07 +#define MCP2515_CNF2_PHSEG_MASK 0x38 + +#define MCP2515_CNF1_BRP0 0x01 +#define MCP2515_CNF1_BRP1 0x02 +#define MCP2515_CNF1_BRP2 0x04 +#define MCP2515_CNF1_BRP3 0x08 +#define MCP2515_CNF1_BRP4 0x10 +#define MCP2515_CNF1_BRP5 0x20 +#define MCP2515_CNF1_SJW0 0x40 +#define MCP2515_CNF1_SJW1 0x80 + +#define MCP2515_CNF1_BRP_MASK 0x3F +#define MCP2515_CNF1_SJW_MASK 0xC0 + +#define MCP2515_CANINTE_RX0IE 0x01 +#define MCP2515_CANINTE_RX1IE 0x02 +#define MCP2515_CANINTE_TX0IE 0x04 +#define MCP2515_CANINTE_TX1IE 0x08 +#define MCP2515_CANINTE_TX2IE 0x10 +#define MCP2515_CANINTE_ERRIE 0x20 +#define MCP2515_CANINTE_WAKIE 0x40 +#define MCP2515_CANINTE_MERRE 0x80 + +#define MCP2515_CANINTF_RX0IF 0x01 +#define MCP2515_CANINTF_RX1IF 0x02 +#define MCP2515_CANINTF_TX0IF 0x04 +#define MCP2515_CANINTF_TX1IF 0x08 +#define MCP2515_CANINTF_TX2IF 0x10 +#define MCP2515_CANINTF_ERRIF 0x20 +#define MCP2515_CANINTF_WAKIF 0x40 +#define MCP2515_CANINTF_MERRF 0x80 +/** @} */ + +/** + * @name MCP2515 error flags + * { + */ +#define MCP2515_EFLG_EWARN 0x01 +#define MCP2515_EFLG_RXWAR 0x02 +#define MCP2515_EFLG_TXWAR 0x04 +#define MCP2515_EFLG_RXEP 0x08 +#define MCP2515_EFLG_TXEP 0x10 +#define MCP2515_EFLG_TXBO 0x20 +#define MCP2515_EFLG_RX0OVR 0x40 +#define MCP2515_EFLG_RX1OVR 0x80 +/** @} */ + +/** + * @name MCP2515 Transmit and receive flags + * @{ + */ +#define MCP2515_TXBCTRL_TXP0 0x01 +#define MCP2515_TXBCTRL_TXP1 0x02 +#define MCP2515_TXBCTRL_TXREQ 0x08 +#define MCP2515_TXBCTRL_TXERR 0x10 +#define MCP2515_TXBCTRL_MLOA 0x20 +#define MCP2515_TXBCTRL_ABTF 0x40 + +#define MCP2515_RXB0CTRL_FILHIT0 0x01 +#define MCP2515_RXB0CTRL_BUKT1 0x02 +#define MCP2515_RXB0CTRL_BUKT 0x04 +#define MCP2515_RXB0CTRL_RXRTR 0x08 +#define MCP2515_RXB0CTRL_RXM0 0x20 +#define MCP2515_RXB0CTRL_RXM1 0x40 +#define MCP2515_RXB0CTRL_MODE_RECV_STD_OR_EXT 0x00 +#define MCP2515_RXB0CTRL_MODE_RECV_STD MCP2515_RXB0CTRL_RXM0 +#define MCP2515_RXB0CTRL_MODE_RECV_EXT MCP2515_RXB0CTRL_RXM1 +#define MCP2515_RXB0CTRL_MODE_RECV_ALL (MCP2515_RXB0CTRL_RXM1 | MCP2515_RXB0CTRL_RXM0) + +#define MCP2515_RXB1CTRL_FILHIT0 0x01 +#define MCP2515_RXB1CTRL_FILHIT1 0x02 +#define MCP2515_RXB1CTRL_FILHIT2 0x04 +#define MCP2515_RXB1CTRL_RXRTR 0x08 +#define MCP2515_RXB1CTRL_RXM0 0x20 +#define MCP2515_RXB1CTRL_RXM1 0x40 +#define MCP2515_RXB1CTRL_MODE_RECV_STD_OR_EXT 0x00 +#define MCP2515_RXB1CTRL_MODE_RECV_STD MCP2515_RXB1CTRL_RXM0 +#define MCP2515_RXB1CTRL_MODE_RECV_EXT MCP2515_RXB1CTRL_RXM1 +#define MCP2515_RXB1CTRL_MODE_RECV_ALL (MCP2515_RXB1CTRL_RXM1 | MCP2515_RXB1CTRL_RXM0) +/** @} */ + +/** + * @name MCP2515 SPI commands + * { + */ +#define MCP2515_SPI_RESET 0xC0 +#define MCP2515_SPI_READ 0x03 +#define MCP2515_SPI_READ_RXBUF 0x90 +#define MCP2515_SPI_WRITE 0x02 +#define MCP2515_SPI_LOAD_TXBUF 0x40 +#define MCP2515_SPI_RTS 0x80 +#define MCP2515_SPI_READ_STATUS 0xA0 +#define MCP2515_SPI_RX_STATUS 0xB0 +#define MCP2515_SPI_BITMOD 0x05 +/** @} */ + +/** + * @name MCP2515 RX buffer id + * { + */ +#define MCP2515_RXBUF_RXB0SIDH 0x00 +#define MCP2515_RXBUF_RXB0D0 0x02 +#define MCP2515_RXBUF_RXB1SIDH 0x04 +#define MCP2515_RXBUF_RXB1D0 0x06 +/** @} */ + +/** + * @name MCP2515 TX buffer id + * { + */ +#define MCP2515_TXBUF_TXB0SIDH 0x00 +#define MCP2515_TXBUF_TXB0D0 0x01 +#define MCP2515_TXBUF_TXB1SIDH 0x02 +#define MCP2515_TXBUF_TXB1D0 0x03 +#define MCP2515_TXBUF_TXB2SIDH 0x04 +#define MCP2515_TXBUF_TXB2D0 0x05 +/** @} */ + +/** + * @name MCP2515 option ID for ioctl function + * { + */ +#define MCP2515_OPTION_ROLLOVER 1 +#define MCP2515_OPTION_ONESHOT 2 +#define MCP2515_OPTION_ABORT 3 +#define MCP2515_OPTION_CLOCKOUT 4 +#define MCP2515_OPTION_LOOPBACK 5 +#define MCP2515_OPTION_LISTEN_ONLY 6 +#define MCP2515_OPTION_SLEEP 7 +#define MCP2515_OPTION_MULTISAMPLE 8 +#define MCP2515_OPTION_SOFOUT 9 +#define MCP2515_OPTION_WAKE_GLITCH_FILTER 10 +#define MCP2515_OPTION_WAKE 11 +/** @} */ + +/** + * @name MCP2515 IRQ handling + * { + */ +#define MCP2515_IRQ_FLAGGED 0x80 +#define MCP2515_IRQ_HANDLED 0x40 +#define MCP2515_IRQ_RX 0x01 +#define MCP2515_IRQ_TX 0x02 +#define MCP2515_IRQ_ERROR 0x04 +#define MCP2515_IRQ_WAKEUP 0x08 +/** @} */ + +/** + * @name MCP2515 Extended ID bit + * { + */ +#define MCP2515_RX_IDE 0x08 +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* MCP2515_DEFINES_H */ +/** @} */ diff --git a/drivers/mcp2515/mcp2515_spi.c b/drivers/mcp2515/mcp2515_spi.c new file mode 100644 index 0000000000..2eff521b4c --- /dev/null +++ b/drivers/mcp2515/mcp2515_spi.c @@ -0,0 +1,138 @@ + +/* + * Copyright (C) 2016 OTA keys + * + * 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 drivers_mcp2515 + * @{ + * + * @file + * @brief MCP2515 can spi driver + * + * @author Toon Stegen + * @} + */ + +#include + +#include "mcp2515_spi.h" +#include "mcp2515_defines.h" + +#include "periph/gpio.h" +#include "periph/spi.h" + +#include "xtimer.h" +#include "irq.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +int mcp2515_spi_init(const candev_mcp2515_t *dev) +{ + int res; + /* Configure SPI */ + res = spi_init_cs(dev->conf->spi, dev->conf->cs_pin); + if (res != SPI_OK) { + DEBUG("spi_init_master: error initializing SPI_%i device (code %i)\n", + dev->conf->spi, res); + return -1; + } + return 0; +} + +int mcp2515_spi_reset(const candev_mcp2515_t *dev) +{ + spi_acquire(dev->conf->spi, dev->conf->cs_pin, dev->conf->spi_mode, dev->conf->spi_clk); + spi_transfer_byte(dev->conf->spi, dev->conf->cs_pin, false, MCP2515_SPI_RESET); + spi_release(dev->conf->spi); + return 0; +} + +int mcp2515_spi_read(const candev_mcp2515_t *dev, uint8_t addr, uint8_t *buf, + unsigned int len) +{ + spi_acquire(dev->conf->spi, dev->conf->cs_pin, dev->conf->spi_mode, dev->conf->spi_clk); + spi_transfer_byte(dev->conf->spi, dev->conf->cs_pin, true, MCP2515_SPI_READ); + spi_transfer_regs(dev->conf->spi, dev->conf->cs_pin, addr, NULL, (void *)buf, len); + spi_release(dev->conf->spi); + return 0; +} + +int mcp2515_spi_read_rxbuf(const candev_mcp2515_t *dev, uint8_t mailbox, + void *buf, uint8_t len) +{ + spi_acquire(dev->conf->spi, dev->conf->cs_pin, dev->conf->spi_mode, dev->conf->spi_clk); + spi_transfer_byte(dev->conf->spi, dev->conf->cs_pin, true, MCP2515_SPI_READ_RXBUF | (mailbox << 2)); + spi_transfer_bytes(dev->conf->spi, dev->conf->cs_pin, false, NULL, (void *)buf, len); + spi_release(dev->conf->spi); + return 0; +} + +int mcp2515_spi_write(const candev_mcp2515_t *dev, uint8_t addr, uint8_t *buf, + unsigned int len) +{ + spi_acquire(dev->conf->spi, dev->conf->cs_pin, dev->conf->spi_mode, dev->conf->spi_clk); + spi_transfer_byte(dev->conf->spi, dev->conf->cs_pin, true, MCP2515_SPI_WRITE); + spi_transfer_regs(dev->conf->spi, dev->conf->cs_pin, addr, (void *)buf, NULL, len); + spi_release(dev->conf->spi); + return 0; +} + +int mcp2515_spi_write_txbuf(const candev_mcp2515_t *dev, uint8_t mailbox, + void *buf, uint8_t len) +{ + spi_acquire(dev->conf->spi, dev->conf->cs_pin, dev->conf->spi_mode, dev->conf->spi_clk); + spi_transfer_byte(dev->conf->spi, dev->conf->cs_pin, true, MCP2515_SPI_LOAD_TXBUF | (mailbox << 1)); + spi_transfer_bytes(dev->conf->spi, dev->conf->cs_pin, false, (void *)buf, NULL, len); + spi_release(dev->conf->spi); + return 0; +} + +int mcp2515_spi_rts(const candev_mcp2515_t *dev, uint8_t mailbox) +{ + spi_acquire(dev->conf->spi, dev->conf->cs_pin, dev->conf->spi_mode, dev->conf->spi_clk); + spi_transfer_byte(dev->conf->spi, dev->conf->cs_pin, false, MCP2515_SPI_RTS | (1 << mailbox)); + spi_release(dev->conf->spi); + return 0; +} + +uint8_t mcp2515_spi_read_status(const candev_mcp2515_t *dev) +{ + uint8_t status; + spi_acquire(dev->conf->spi, dev->conf->cs_pin, dev->conf->spi_mode, dev->conf->spi_clk); + spi_transfer_byte(dev->conf->spi, dev->conf->cs_pin, true, MCP2515_SPI_READ_STATUS); + status = spi_transfer_byte(dev->conf->spi, dev->conf->cs_pin, false, 0); + spi_release(dev->conf->spi); + return status; +} + +int mcp2515_spi_rx_status(const candev_mcp2515_t *dev) +{ + uint8_t status; + spi_acquire(dev->conf->spi, dev->conf->cs_pin, dev->conf->spi_mode, dev->conf->spi_clk); + spi_transfer_byte(dev->conf->spi, dev->conf->cs_pin, true, MCP2515_SPI_RX_STATUS); + status = spi_transfer_byte(dev->conf->spi, dev->conf->cs_pin, false, 0); + spi_release(dev->conf->spi); + return status; +} + +int mcp2515_spi_bitmod(const candev_mcp2515_t *dev, uint8_t addr, uint8_t mask, + uint8_t buf) +{ + uint8_t msg[2]; + + spi_acquire(dev->conf->spi, dev->conf->cs_pin, dev->conf->spi_mode, dev->conf->spi_clk); + spi_transfer_byte(dev->conf->spi, dev->conf->cs_pin, true, MCP2515_SPI_BITMOD); + + msg[0] = mask; + msg[1] = buf; + + spi_transfer_regs(dev->conf->spi, dev->conf->cs_pin, addr, (const void*)msg, NULL, sizeof(msg)); + spi_release(dev->conf->spi); + return 0; +} diff --git a/drivers/mcp2515/mcp2515_spi.h b/drivers/mcp2515/mcp2515_spi.h new file mode 100644 index 0000000000..30726197c6 --- /dev/null +++ b/drivers/mcp2515/mcp2515_spi.h @@ -0,0 +1,169 @@ +/* + * 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 drivers_mcp2515 + * @brief Driver for the Microchip MCP2515 can controller. + * + * @{ + * + * @file + * @brief Definition of the MCP2515 SPI driver. + * + * + * @author Toon Stegen + */ +#ifndef MCP2515_SPI_H +#define MCP2515_SPI_H + +#include +#include "candev_mcp2515.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initialize SPI interface + * + * The device descriptor contains all informations related to the SPI interface. + * + * @param[out] dev device descriptor + * + * @return 0 on success + * @return <0 on error + */ +int mcp2515_spi_init(const candev_mcp2515_t *dev); + +/** + * @brief Reset MCP2515 device though SPI interface + * + * The MCP2515 device is reset by sending the right message over SPI interface. + * + * @param[in] dev device descriptor + * + * @return 0 on success + * @return <0 on error + */ +int mcp2515_spi_reset(const candev_mcp2515_t *dev); + +/** + * @brief Read the register value corresponding to @p addr + * + * @param[in] dev device descriptor + * @param[in] addr register addr + * @param[out] buf buffer to receive register value + * @param[in] len length of register value + * + * @return 0 on success + * @return <0 on error + */ +int mcp2515_spi_read(const candev_mcp2515_t *dev, uint8_t addr, uint8_t *buf, + unsigned int len); + +/** + * @brief Read the can data received in the rx @p mailbox + * + * @param[in] dev device descriptor + * @param[in] mailbox rx mailbox + * @param[out] buf buffer to receive can data + * @param[in] len length of can data + * + * @return 0 on success + * @return <0 on error + */ +int mcp2515_spi_read_rxbuf(const candev_mcp2515_t *dev, uint8_t mailbox, + void *buf, uint8_t len); + +/** + * @brief Send the register value corresponding to @p addr + * + * @param[in] dev device descriptor + * @param[in] addr address to write + * @param[in] buf buffer containing register data + * @param[in] len buffer length + * + * @return 0 on success + * @return <0 on error + */ +int mcp2515_spi_write(const candev_mcp2515_t *dev, uint8_t addr, uint8_t *buf, + unsigned int len); + +/** + * @brief Send the can data to the tx @p mailbox + * + * @param[in] dev device descriptor + * @param[in] mailbox tx mailbox + * @param[in] buf buffer containing can data + * @param[in] len buffer length + * + * @return 0 on success + * @return <0 on error + */ +int mcp2515_spi_write_txbuf(const candev_mcp2515_t *dev, uint8_t mailbox, + void *buf, uint8_t len); + +/** + * @brief Initiate message transmission. + * + * The RTS command can be used to initiate message transmission for one or more + * of the transmit buffers. + * + * @param[in] dev device descriptor + * @param[in] mailbox mailbox to enable transmission + * + * @return 0 on success + * @return <0 on error + */ +int mcp2515_spi_rts(const candev_mcp2515_t *dev, uint8_t mailbox); + +/** + * @brief Read MCP2515 status over SPI interface. + * + * @param[in] dev device descriptor + * + * @return 0 on success + * @return <0 on error + */ +uint8_t mcp2515_spi_read_status(const candev_mcp2515_t *dev); + +/** + * @brief Read MCP2515 receive status over SPI interface. + * + * @param[in] dev device descriptor + * + * @return 0 on success + * @return <0 on error + */ +int mcp2515_spi_rx_status(const candev_mcp2515_t *dev); + +/** + * @brief Bit modify instruction. + * + * The Bit Modify instruction provides a means for setting or clearing + * individual bits in specific status and control registers. + * This command is not available for all registers. Executing this command on + * registers that are not bit-modifiable will force the mask to FFh. + * + * @param[in] dev device descriptor + * @param[in] addr register address + * @param[in] mask mask to modify individual bit + * @param[in] buf regsiter value + * + * @return 0 on success + * @return <0 on error + */ +int mcp2515_spi_bitmod(const candev_mcp2515_t *dev, uint8_t addr, uint8_t mask, + uint8_t buf); + +#ifdef __cplusplus +} +#endif + +#endif /* MCP2515_SPI_H */ +/** @} */ diff --git a/sys/auto_init/can/auto_init_mcp2515.c b/sys/auto_init/can/auto_init_mcp2515.c new file mode 100644 index 0000000000..90527eeaea --- /dev/null +++ b/sys/auto_init/can/auto_init_mcp2515.c @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2017 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 mcp2515 can device + * + * @author Vincent Dupont + * @} + */ + +#ifdef MODULE_MCP2515 +#include "can/device.h" +#include "mcp2515_params.h" + +#define CANDEV_MCP2515_NUMOF ((sizeof(candev_mcp2515_params) / sizeof(candev_params_t))) + +#ifndef CANDEV_MCP2515_STACKSIZE +#define CANDEV_MCP2515_STACKSIZE (THREAD_STACKSIZE_DEFAULT + THREAD_EXTRA_STACKSIZE_PRINTF) +#endif + +#ifndef CANDEV_MCP2515_BASE_PRIORITY +#define CANDEV_MCP2515_BASE_PRIORITY (THREAD_PRIORITY_MAIN - CANDEV_MCP2515_NUMOF - 2) +#endif + +static candev_dev_t candev_dev_mcp2515[CANDEV_MCP2515_NUMOF]; +static char _can_mcp2515_stacks[CANDEV_MCP2515_NUMOF][CANDEV_MCP2515_STACKSIZE]; +static candev_mcp2515_t candev_mcp2515[CANDEV_MCP2515_NUMOF]; + +void auto_init_can_mcp2515(void) { + + for (size_t i = 0; i < CANDEV_MCP2515_NUMOF; i++) { + candev_mcp2515_init(&candev_mcp2515[i], &candev_mcp2515_conf[i]); + candev_dev_mcp2515[i].dev = (candev_t *)&candev_mcp2515[i]; + candev_dev_mcp2515[i].name = candev_mcp2515_params[i].name; +#ifdef MODULE_TRX + candev_dev_mcp2515[i].trx = candev_mcp2515_params[i].trx; +#endif +#ifdef MODULE_CAN_PM + candev_dev_mcp2515[i].rx_inactivity_timeout = candev_mcp2515_params[i].rx_inactivity_timeout; + candev_dev_mcp2515[i].tx_wakeup_timeout = candev_mcp2515_params[i].tx_wakeup_timeout; +#endif + + can_device_init(_can_mcp2515_stacks[i], CANDEV_MCP2515_STACKSIZE, CANDEV_MCP2515_BASE_PRIORITY + i, + candev_mcp2515_params[i].name, &candev_dev_mcp2515[i]); + } +} +#else +typedef int dont_be_pedantic; +#endif diff --git a/sys/auto_init/can/init.c b/sys/auto_init/can/init.c index 8f80e390d6..7ce060490b 100644 --- a/sys/auto_init/can/init.c +++ b/sys/auto_init/can/init.c @@ -53,4 +53,9 @@ void auto_init_candev(void) extern void auto_init_periph_can(void); auto_init_periph_can(); #endif + +#ifdef MODULE_MCP2515 + extern void auto_init_can_mcp2515(void); + auto_init_can_mcp2515(); +#endif }