1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/drivers/mcp2515/mcp2515.c
Firas Hamdi 3f9866e709 drivers/mcp2515: fix to add filters
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
2022-06-26 17:38:23 +02:00

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);
}