mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
3f9866e709
drivers/mcp2515: enable filtering The current driver implementation initializes the driver in a way to receive all the CAN messages without matching the filters. This commit changes that by adding a macro definition that will be enabling or disabling the filtering and accordingly set the appropriate mcp2515 acceptance mode
425 lines
12 KiB
C
425 lines
12 KiB
C
/*
|
|
* 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 "ztimer.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;
|
|
}
|
|
if (gpio_is_valid(dev->conf->rst_pin)) {
|
|
gpio_init(dev->conf->rst_pin, GPIO_OUT);
|
|
}
|
|
|
|
res = mcp2515_spi_init(dev);
|
|
if (res < 0) {
|
|
return -1;
|
|
}
|
|
|
|
uint8_t cmd;
|
|
if (IS_ACTIVE(MCP2515_RECV_FILTER_EN)) {
|
|
DEBUG_PUTS("filtering enabled");
|
|
cmd = MCP2515_RXB0CTRL_MODE_RECV_FILTER;
|
|
}
|
|
else {
|
|
DEBUG_PUTS("filtering disabled");
|
|
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)
|
|
{
|
|
if (gpio_is_valid(dev->conf->rst_pin)) {
|
|
gpio_clear(dev->conf->rst_pin);
|
|
ztimer_sleep(ZTIMER_USEC, RESET_DELAY_US);
|
|
gpio_set(dev->conf->rst_pin);
|
|
}
|
|
else {
|
|
mcp2515_spi_reset(dev);
|
|
}
|
|
ztimer_sleep(ZTIMER_USEC, _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);
|
|
ztimer_sleep(ZTIMER_USEC, _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);
|
|
}
|