1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

Merge pull request #13624 from wosym/pr/candev_mcp2515

tests/candev: add mcp2515 + driver/mcp2515: add driver
This commit is contained in:
Francisco 2020-10-08 09:45:40 +02:00 committed by GitHub
commit 3918d714ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 2565 additions and 62 deletions

View File

@ -77,6 +77,7 @@
/drivers/dose/ @jue89
/drivers/ds18/ @leandrolanzieri
/drivers/itg320x/ @gschorcht
/drivers/mcp2515/ @wosym
/drivers/mrf24j40/ @bergzand
/drivers/pca9685/ @gschorcht
/drivers/sht3x/ @gschorcht

View File

@ -6,7 +6,7 @@ ifneq (,$(filter mtd,$(USEMODULE)))
USEMODULE += mtd_native
endif
ifneq (,$(filter can,$(USEMODULE)))
ifneq (,$(filter periph_can,$(FEATURES_USED)))
ifeq ($(OS),Linux)
USEMODULE += can_linux
CFLAGS += -DCAN_DLL_NUMOF=2

View File

@ -72,6 +72,11 @@ ifneq (,$(filter ltc4150_%,$(USEMODULE)))
USEMODULE += ltc4150
endif
ifneq (,$(filter mcp2515,$(USEMODULE)))
USEMODULE += xtimer
FEATURES_REQUIRED += periph_gpio periph_spi periph_gpio_irq
endif
ifneq (,$(filter mhz19_%,$(USEMODULE)))
USEMODULE += mhz19
endif

View File

@ -0,0 +1,137 @@
/*
* 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 <toon.stegen@altran.com>
* @author Vincent Dupont <vincent@otakeys.com>
* @author Wouter Symons <wosym@airsantelmo.com>
*/
#ifndef CANDEV_MCP2515_H
#define CANDEV_MCP2515_H
#include <stdbool.h>
#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
/**
* Default CAN bitrate
*/
#ifndef CANDEV_MCP2515_DEFAULT_BITRATE
#define CANDEV_MCP2515_DEFAULT_BITRATE 125000
#endif
/**
* Default sampling point setup
*/
#ifndef CANDEV_MCP2515_DEFAULT_SPT
#define CANDEV_MCP2515_DEFAULT_SPT 875
#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 */
/** @} */

1
drivers/mcp2515/Makefile Normal file
View File

@ -0,0 +1 @@
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,2 @@
USEMODULE_INCLUDES_mcp2515 := $(LAST_MAKEFILEDIR)/include
USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_mcp2515)

View File

@ -0,0 +1,729 @@
/*
* 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
* @{
*
* @file
* @brief Implementation of the CAN controller driver
*
* @author Toon Stegen <toon.stegen@altran.com>
* @author Vincent Dupont <vincent@otakeys.com>
* @author Wouter Symons <wosym@airsantelmo.com>
* @}
*/
#include <errno.h>
#include <limits.h>
#include <stdbool.h>
#include <string.h>
#include "candev_mcp2515.h"
#include "can/common.h"
#include "can/device.h"
#include "mcp2515.h"
#include "mutex.h"
#include "periph_conf.h"
#include "thread.h"
#include "sched.h"
#include "xtimer.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
static mutex_t _mcp_mutex;
static int _neednewisr = 0;
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, /**< Time segment 1 = prop_seg + phase_seg1, min value */
.tseg1_max = 16, /**< Time segment 1, max value */
.tseg2_min = 2, /**< Time segment 2 = phase_seg2, min value */
.tseg2_max = 8, /**< Time segment 2, max value */
.sjw_max = 4, /**< Synchronisation jump width */
.brp_min = 1, /**< Bit-rate prescaler, min value */
.brp_max = 64, /**< Bit-rate prescaler, max value */
.brp_inc = 1, /**< Bit-rate prescaler, increment */
};
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 };
/* f_quantum = f_osc / 2 */
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);
if (mutex_trylock(&_mcp_mutex)) {
/* 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);
mutex_unlock(&_mcp_mutex);
}
else {
/* locking failed */
DEBUG("failed to lock mutex_init");
return -1;
}
return res;
}
static int _send(candev_t *candev, const struct can_frame *frame)
{
candev_mcp2515_t *dev = (candev_mcp2515_t *)candev;
int box;
int ret = 0;
enum mcp2515_mode mode;
if (frame->can_id > 0x1FFFFFFF) {
DEBUG("Illegal CAN-ID!\n");
return -EINVAL;
}
if (mutex_trylock(&_mcp_mutex)) {
mode = mcp2515_get_mode(dev);
mutex_unlock(&_mcp_mutex);
}
else {
DEBUG("failed to lock mutex_send\n");
return -1;
}
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;
if (mutex_trylock(&_mcp_mutex)) {
ret = mcp2515_send(dev, frame, box);
mutex_unlock(&_mcp_mutex);
}
else {
DEBUG("send_failed to lock mutex\n");
return -1;
}
if (ret < 0) {
return -1;
}
if (_neednewisr) {
DEBUG("Calling _isr() again on request\n");
_isr(candev);
_neednewisr = 0;
}
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;
}
if (mutex_trylock(&_mcp_mutex)) {
mcp2515_abort(dev, box);
mutex_unlock(&_mcp_mutex);
}
else {
DEBUG("abort_Failed to lock mutex\n");
return -1;
}
dev->tx_mailbox[box] = NULL;
return 0;
}
static void _isr(candev_t *candev)
{
uint8_t flag;
candev_mcp2515_t *dev = (candev_mcp2515_t *)candev;
if (mutex_trylock(&_mcp_mutex)) {
flag = mcp2515_get_irq(dev);
mutex_unlock(&_mcp_mutex);
}
else {
DEBUG("isr: Failed to lock mutex\n");
_neednewisr = 1;
return;
}
while ((flag)) {
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);
}
if (mutex_trylock(&_mcp_mutex)) {
flag = mcp2515_get_irq(dev);
mutex_unlock(&_mcp_mutex);
}
else {
DEBUG("isr2: Failed to lock mutex\n");
_neednewisr = 1;
return;
}
/* clear all flags except for RX flags, which are cleared by receiving */
if (mutex_trylock(&_mcp_mutex)) {
mcp2515_clear_irq(dev, flag & ~(INT_RX0 | INT_RX1));
mutex_unlock(&_mcp_mutex);
}
else {
DEBUG("isr3: failed to lock mutex\n");
_neednewisr = 1;
return;
}
}
}
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:
if (mutex_trylock(&_mcp_mutex)) {
res = mcp2515_set_mode(dev, MODE_LISTEN_ONLY);
mutex_unlock(&_mcp_mutex);
}
else {
DEBUG("set1_Failed to lock mutex\n");
return -1;
}
break;
case CANOPT_STATE_OFF:
case CANOPT_STATE_SLEEP:
if (mutex_trylock(&_mcp_mutex)) {
res = mcp2515_set_mode(dev, MODE_SLEEP);
mutex_unlock(&_mcp_mutex);
}
else {
DEBUG("set2_Failed to lock mutex\n");
return -1;
}
break;
case CANOPT_STATE_ON:
if (mutex_trylock(&_mcp_mutex)) {
res = mcp2515_set_mode(dev, MODE_NORMAL);
mutex_unlock(&_mcp_mutex);
}
else {
DEBUG("set3_Failed to lock mutex\n");
return -1;
}
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 {
/* Not implemented (yet...) */
res = -ENOTSUP;
}
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");
bool filter_added = true;
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 */
}
if (mutex_trylock(&_mcp_mutex)) {
mode = mcp2515_get_mode(dev_mcp);
res = mcp2515_set_mode(dev_mcp, MODE_CONFIG);
mutex_unlock(&_mcp_mutex);
}
else {
DEBUG("setfilt_Failed to lock mutex\n");
return -1;
}
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) {
/* 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 = true;
}
/* 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 */
if (mutex_trylock(&_mcp_mutex)) {
mcp2515_set_filter(dev_mcp,
MCP2515_FILTERS_MB0 * mailbox_index + filter_pos,
f.can_id);
mutex_unlock(&_mcp_mutex);
}
else {
DEBUG("setfilt2_Failed to lock mutex");
return -1;
}
/* save filter */
dev_mcp->filter_ids[mailbox_index][filter_pos] = f.can_id;
/* function succeeded */
filter_added = true;
}
}
mailbox_index++;
}
if (mutex_trylock(&_mcp_mutex)) {
mcp2515_set_mode(dev_mcp, mode);
mutex_unlock(&_mcp_mutex);
}
else {
DEBUG("setfilt3_Failed to lock mutex");
return -1;
}
return filter_added;
}
static int _remove_filter(candev_t *dev, const struct can_filter *filter)
{
DEBUG("inside _remove_filter of MCP2515\n");
bool filter_removed;
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 */
}
if (mutex_trylock(&_mcp_mutex)) {
mode = mcp2515_get_mode(dev_mcp);
res = mcp2515_set_mode(dev_mcp, MODE_CONFIG);
mutex_unlock(&_mcp_mutex);
}
else {
DEBUG("remfilt_Failed to lock mutex\n");
return -1;
}
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) {
/* 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 */
if (mutex_trylock(&_mcp_mutex)) {
mcp2515_set_filter(dev_mcp,
MCP2515_FILTERS_MB0 * mailbox_index + filter_pos,
CAN_EFF_MASK);
mutex_unlock(&_mcp_mutex);
}
else {
DEBUG("remfilt2_Failed to lock mutex\n");
return -1;
}
/* 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++;
}
if (mutex_trylock(&_mcp_mutex)) {
mcp2515_set_mode(dev_mcp, mode);
mutex_unlock(&_mcp_mutex);
}
else {
DEBUG("remfilt3_Failed to lock mutex\n");
return -1;
}
return filter_removed;
}
static void _irq_rx(candev_mcp2515_t *dev, int box)
{
DEBUG("Inside mcp2515 rx irq, box=%d\n", box);
if (mutex_trylock(&_mcp_mutex)) {
mcp2515_receive(dev, &dev->rx_buf[box], box);
mutex_unlock(&_mcp_mutex);
}
else {
DEBUG("irqrx_Failed to lock mutex\n");
return;
}
_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");
if (mutex_trylock(&_mcp_mutex)) {
err = mcp2515_get_errors(dev);
mutex_unlock(&_mcp_mutex);
}
else {
DEBUG("erqerr_Failed to lock mutex\n");
return;
}
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("Bus 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)
{
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);
if (mutex_trylock(&_mcp_mutex)) {
mcp2515_abort(dev, box);
mutex_unlock(&_mcp_mutex);
}
else {
DEBUG("irqmsg_Failed to lock mutex\n");
return;
}
_send_event(dev, CANDEV_EVENT_TIMEOUT_TX_CONF,
NULL);
}
}
}
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);
}
}

View File

@ -0,0 +1,98 @@
/*
* 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
* @{
*
* @file
* @brief Parameters for the CAN driver implementation
*
* @author Vincent Dupont <vincent@otakeys.com>
* @author Wouter Symons <wosym@airsantelmo.com>
* @}
*/
#ifndef MCP2515_PARAMS_H
#define MCP2515_PARAMS_H
#ifdef __cplusplus
extern "C" {
#endif
#include "can/device.h"
#include "candev_mcp2515.h"
#include "board.h"
/**
* @name Set default configuration parameters for the MCP2515
* @{
*/
#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 GPIO_PIN(1, 9)
#endif
#ifndef MCP2515_PARAM_RST
#define MCP2515_PARAM_RST GPIO_PIN(0, 0)
#endif
#ifndef MCP2515_PARAM_INT
#define MCP2515_PARAM_INT GPIO_PIN(1, 8)
#endif
#ifndef MCP2515_PARAM_CLK
#define MCP2515_PARAM_CLK (8000000ul) /**< External clock frequency */
#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, \
}
/**@*/
/**
* @brief Set default configuration
*/
static const candev_mcp2515_conf_t candev_mcp2515_conf[] = {
MCP2515_DEFAULT_CONFIG
};
/**
* @brief set candev parameters
*/
static const candev_params_t candev_mcp2515_params[] = {
{
.name = "can_mcp2515_0",
},
};
#ifdef __cplusplus
}
#endif
#endif /* MCP2515_PARAMS_H */

410
drivers/mcp2515/mcp2515.c Normal file
View File

@ -0,0 +1,410 @@
/*
* 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 <toon.stegen@altran.com>
* @author Wouter Symons <wosym@airsantelmo.com>
* @}
*/
#include <string.h>
#include "xtimer.h"
#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_US 2
/* size of transmission and reception buffers in mcp2515 */
#define BUFFER_SIZE 13
/* macros for getting the TX and RX control registers */
#define MCP2515_TX_CTRL(mailbox) ((MCP2515_TXB0CTRL) + ((mailbox) << 4))
#define MCP2515_RX_CTRL(mailbox) ((MCP2515_RXB0CTRL) + ((mailbox) << 4))
/* length of the fixed part of a can message: 4 bytes can_id + 1 byte can_dlc */
#define CAN_FIXED_LEN 5
/* oscillator startup time
* 128 cycles @ clock freq + some extra */
static inline uint32_t _osc_startup(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;
res = gpio_init_int(dev->conf->int_pin, GPIO_IN_PU, GPIO_FALLING,
(gpio_cb_t)irq_handler_cb, (void *)dev);
if (res != 0) {
DEBUG("Error setting interrupt pin!\n");
return -1;
}
gpio_init(dev->conf->rst_pin, GPIO_OUT);
res = mcp2515_spi_init(dev);
if (res < 0) {
return -1;
}
uint8_t cmd = MCP2515_RXB0CTRL_MODE_RECV_ALL;
res = mcp2515_spi_write(dev, MCP2515_RXB0CTRL, &cmd, 1);
if (res < 0) {
DEBUG("failed to set acceptance mode\n");
return -1;
}
return 0;
}
void mcp2515_reset(candev_mcp2515_t *dev)
{
gpio_clear(dev->conf->rst_pin);
xtimer_usleep(RESET_DELAY_US);
gpio_set(dev->conf->rst_pin);
xtimer_usleep(_osc_startup(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) >> 13)
| (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;
uint8_t outbuf[BUFFER_SIZE];
uint8_t ctrl;
struct can_frame framebuf;
if (frame->can_dlc > CAN_MAX_DLEN) {
return -1;
}
framebuf.can_id = frame->can_id;
framebuf.can_dlc = frame->can_dlc;
for (int i = 0; i < framebuf.can_dlc; i++) {
framebuf.data[i] = frame->data[i];
}
mcp2515_spi_read(dev, MCP2515_TX_CTRL(mailbox), &ctrl, 1);
if (ctrl & MCP2515_TXBCTRL_TXREQ) {
DEBUG("Mailbox in use, TXB%dCTRL: 0x%02x\n", mailbox, ctrl);
return -1;
}
if (framebuf.can_id > CAN_SFF_MASK) {
framebuf.can_id |= CAN_EFF_FLAG;
}
if ((framebuf.can_id & CAN_EFF_FLAG) == CAN_EFF_FLAG) {
_fill_extended_id(framebuf.can_id, outbuf);
}
else {
_fill_standard_id(framebuf.can_id, outbuf);
}
outbuf[4] = framebuf.can_dlc;
memcpy(&outbuf[CAN_FIXED_LEN], framebuf.data, framebuf.can_dlc);
/* set mailbox priority */
mcp2515_spi_write(dev, MCP2515_TX_CTRL(mailbox), &prio, 1);
mcp2515_spi_write_txbuf(dev, mailbox, outbuf,
CAN_FIXED_LEN + framebuf.can_dlc);
_mcp2515_enable_irq(dev, MCP2515_CANINTE_TX0IE << mailbox);
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 = ((uint32_t)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, MCP2515_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) {
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;
/* allow for two retries to set the mode */
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(_osc_startup(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;
}
}
/* set Synchronization Jump Width Length */
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_ERRIE |
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, MCP2515_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, sizeof(buf));
}
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);
}

294
drivers/mcp2515/mcp2515.h Normal file
View File

@ -0,0 +1,294 @@
/*
* 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 <toon.stegen@altran.com>
*/
#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 information 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 information 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 */
/** @} */

View File

@ -0,0 +1,402 @@
/*
* 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 <toon.stegen@altran.com>
*/
#ifndef MCP2515_DEFINES_H
#define MCP2515_DEFINES_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @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 */
/** @} */

View File

@ -0,0 +1,164 @@
/*
* 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 <toon.stegen@altran.com>
* @}
*/
#include <stdio.h>
#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)
{
/* See TABLE 12-1:SPI INSTRUCTION SET in mcp2515 datasheet */
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;
}

View File

@ -0,0 +1,168 @@
/*
* 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 <toon.stegen@altran.com>
*/
#ifndef MCP2515_SPI_H
#define MCP2515_SPI_H
#include <stdint.h>
#include "candev_mcp2515.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Initialize SPI interface
*
* The device descriptor contains all information 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 the read status
*/
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 register 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 */
/** @} */

View File

@ -0,0 +1,60 @@
/*
* 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 sys_auto_init
* @{
* @file
* @brief initializes mcp2515 can device
*
* @author Vincent Dupont <vincent@otakeys.com>
* @}
*/
#ifdef MODULE_MCP2515
#include "can/device.h"
#include "mcp2515_params.h"
#define CANDEV_MCP2515_NUMOF ((ARRAY_SIZE(candev_mcp2515_params) / ARRAY_SIZE(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_CAN_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

View File

@ -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
}

View File

@ -4,14 +4,16 @@ USEMODULE += shell
USEMODULE += can
USEMODULE += isrpipe
FEATURES_OPTIONAL += periph_can
# define the CAN driver you want to use here
CAN_DRIVER ?= CAN_EXT
ifeq ($(CAN_DRIVER), CAN_EXT)
# external CAN driver modules/variables go here
CAN_DRIVER ?= MCP2515
ifeq ($(CAN_DRIVER), PERIPH_CAN)
FEATURES_REQUIRED += periph_can
else ifeq ($(CAN_DRIVER), MCP2515)
USEMODULE += mcp2515
else ifeq ($(CAN_DRIVER), CAN_ALT)
# other can modules can be defined here
endif
include $(RIOTBASE)/Makefile.include

View File

@ -1 +1,8 @@
FEATURES_BLACKLIST += arch_msp430
BOARD_INSUFFICIENT_MEMORY := \
arduino-duemilanove \
arduino-nano \
arduino-uno \
atmega328p \
stm32f030f4-demo \
#

View File

@ -5,6 +5,11 @@
This application is a test for using the candev abstraction directly.
Use this if you want to use a single CAN driver and thus don't need the CAN-DLL layer.
You can select the driver you want to use by redefining the CAN_DRIVER variable in the Makefile. Alternatively you can pass it to the make command.
The application will automatically adapt its initialization procedure to the selected driver.
By default the mcp2515 driver is used.
NOTE: When building for native, use PERIPH_CAN.
Native prerequisites
============
For using the can stack on top of socketCAN, available for linux, you need:

View File

@ -13,8 +13,8 @@
* @file
* @brief Test application for the candev abstraction
*
* @author Wouter Symons <wosym@airsantelmo.com>
* @author Toon Stegen <tstegen@nalys-group.com>
* @author Wouter Symons <wsymons@nalys-group.com>
*
* @}
*/
@ -23,10 +23,10 @@
#include <debug.h>
#include <errno.h>
#include <isrpipe.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <isrpipe.h>
#include "shell.h"
#include "can/device.h"
@ -37,8 +37,14 @@
static can_t periph_dev;
#elif defined(MODULE_MCP2515)
#include "candev_mcp2515.h"
#include "mcp2515_params.h"
static candev_mcp2515_t mcp2515_dev;
#else
/* add other candev drivers here */
/* add includes for other candev drivers here */
#endif
#define RX_RINGBUFFER_SIZE 128 /* Needs to be a power of 2! */
@ -101,12 +107,14 @@ static int _receive(int argc, char **argv)
puts("Reading from Rxbuf...");
isrpipe_read(&rxbuf, buf, 4); /* can-id */
can_id = ((uint32_t)buf[0] << 24) |
((uint32_t)buf[1] << 16) |
((uint32_t)buf[2] << 8) |
((uint32_t)buf[3]);
((uint32_t)buf[1] << 16) |
((uint32_t)buf[2] << 8) |
((uint32_t)buf[3]);
isrpipe_read(&rxbuf, buf, 1); /* can-dlc */
can_dlc = buf[0];
isrpipe_read(&rxbuf, buf, can_dlc); /* data */
if (can_dlc > 0) {
isrpipe_read(&rxbuf, buf, can_dlc); /* data */
}
printf("id: %" PRIx32 " dlc: %" PRIx8 " Data: \n", can_id, can_dlc);
for (int i = 0; i < can_dlc; i++) {
@ -130,60 +138,60 @@ static void _can_event_callback(candev_t *dev, candev_event_t event, void *arg)
struct can_frame *frame;
switch (event) {
case CANDEV_EVENT_ISR:
DEBUG("_can_event: CANDEV_EVENT_ISR\n");
dev->driver->isr(candev);
break;
case CANDEV_EVENT_WAKE_UP:
DEBUG("_can_event: CANDEV_EVENT_WAKE_UP\n");
break;
case CANDEV_EVENT_TX_CONFIRMATION:
DEBUG("_can_event: CANDEV_EVENT_TX_CONFIRMATION\n");
break;
case CANDEV_EVENT_TX_ERROR:
DEBUG("_can_event: CANDEV_EVENT_TX_ERROR\n");
break;
case CANDEV_EVENT_RX_INDICATION:
DEBUG("_can_event: CANDEV_EVENT_RX_INDICATION\n");
case CANDEV_EVENT_ISR:
DEBUG("_can_event: CANDEV_EVENT_ISR\n");
dev->driver->isr(candev);
break;
case CANDEV_EVENT_WAKE_UP:
DEBUG("_can_event: CANDEV_EVENT_WAKE_UP\n");
break;
case CANDEV_EVENT_TX_CONFIRMATION:
DEBUG("_can_event: CANDEV_EVENT_TX_CONFIRMATION\n");
break;
case CANDEV_EVENT_TX_ERROR:
DEBUG("_can_event: CANDEV_EVENT_TX_ERROR\n");
break;
case CANDEV_EVENT_RX_INDICATION:
DEBUG("_can_event: CANDEV_EVENT_RX_INDICATION\n");
frame = (struct can_frame *)arg;
frame = (struct can_frame *)arg;
DEBUG("\tid: %" PRIx32 " dlc: %" PRIx8 " Data: \n\t", frame->can_id,
frame->can_dlc);
for (uint8_t i = 0; i < frame->can_dlc; i++) {
DEBUG("0x%X ", frame->data[i]);
}
DEBUG(" ");
DEBUG("\tid: %" PRIx32 " dlc: %" PRIx8 " Data: \n\t", frame->can_id,
frame->can_dlc);
for (uint8_t i = 0; i < frame->can_dlc; i++) {
DEBUG("0x%X ", frame->data[i]);
}
DEBUG(" ");
/* Store in buffer until user requests the data */
isrpipe_write_one(&rxbuf,
(uint8_t)((frame->can_id & 0x1FFFFFFF) >> 24));
isrpipe_write_one(&rxbuf,
(uint8_t)((frame->can_id & 0xFF0000) >> 16));
isrpipe_write_one(&rxbuf, (uint8_t)((frame->can_id & 0xFF00) >> 8));
isrpipe_write_one(&rxbuf, (uint8_t)((frame->can_id & 0xFF)));
/* Store in buffer until user requests the data */
isrpipe_write_one(&rxbuf,
(uint8_t)((frame->can_id & 0x1FFFFFFF) >> 24));
isrpipe_write_one(&rxbuf,
(uint8_t)((frame->can_id & 0xFF0000) >> 16));
isrpipe_write_one(&rxbuf, (uint8_t)((frame->can_id & 0xFF00) >> 8));
isrpipe_write_one(&rxbuf, (uint8_t)((frame->can_id & 0xFF)));
isrpipe_write_one(&rxbuf, frame->can_dlc);
for (uint8_t i = 0; i < frame->can_dlc; i++) {
isrpipe_write_one(&rxbuf, frame->data[i]);
}
isrpipe_write_one(&rxbuf, frame->can_dlc);
for (uint8_t i = 0; i < frame->can_dlc; i++) {
isrpipe_write_one(&rxbuf, frame->data[i]);
}
break;
case CANDEV_EVENT_RX_ERROR:
DEBUG("_can_event: CANDEV_EVENT_RX_ERROR\n");
break;
case CANDEV_EVENT_BUS_OFF:
dev->state = CAN_STATE_BUS_OFF;
break;
case CANDEV_EVENT_ERROR_PASSIVE:
dev->state = CAN_STATE_ERROR_PASSIVE;
break;
case CANDEV_EVENT_ERROR_WARNING:
dev->state = CAN_STATE_ERROR_WARNING;
break;
default:
DEBUG("_can_event: unknown event\n");
break;
break;
case CANDEV_EVENT_RX_ERROR:
DEBUG("_can_event: CANDEV_EVENT_RX_ERROR\n");
break;
case CANDEV_EVENT_BUS_OFF:
dev->state = CAN_STATE_BUS_OFF;
break;
case CANDEV_EVENT_ERROR_PASSIVE:
dev->state = CAN_STATE_ERROR_PASSIVE;
break;
case CANDEV_EVENT_ERROR_WARNING:
dev->state = CAN_STATE_ERROR_WARNING;
break;
default:
DEBUG("_can_event: unknown event\n");
break;
}
}
@ -197,6 +205,11 @@ int main(void)
puts("Initializing CAN periph device");
can_init(&periph_dev, &(candev_conf[0])); /* vcan0 on native */
candev = (candev_t *)&periph_dev;
#elif defined(MODULE_MCP2515)
puts("Initializing MCP2515");
candev_mcp2515_init(&mcp2515_dev, &candev_mcp2515_conf[0]);
candev = (candev_t *)&mcp2515_dev;
#else
/* add initialization for other candev drivers here */
#endif