upm/src/mcp2515/mcp2515.c
Jon Trulson a70629e3b2 mcp2515: fix return from rx_status_filtermatch; Travis CI (clang)
Signed-off-by: Jon Trulson <jtrulson@ics.com>
2016-12-20 13:01:04 -07:00

1110 lines
29 KiB
C

/*
* Author: Jon Trulson <jtrulson@ics.com>
* Copyright (c) 2016 Intel Corporation.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include "mcp2515.h"
#include <upm_platform.h>
#include <upm_utilities.h>
// in ms
#define MCP2515_MAX_TIMEOUT (5000)
// static array of cnf1-3 value for setting speed. These values were
// taken from the Seeed code at:
// https://github.com/Seeed-Studio/CAN_BUS_Shield/
struct _mcp2515_config {
uint8_t cnf1;
uint8_t cnf2;
uint8_t cnf3;
};
// NOTE: These indexes must match the enum MCP2515_SPEED_T values in
// mcp2515_regs.h! Add any new values to the end, with a corresponding
// enum value in MCP2515_SPEED_T.
static struct _mcp2515_config mcp2515_configs[] = {
{ 0x3f, 0xff, 0x87 }, // 0 - 5kbps
{ 0x1f, 0xff, 0x87 }, // 1 - 10kbps
{ 0x0f, 0xff, 0x87 }, // 2 - 20kbps
{ 0x0f, 0xba, 0x07 }, // 3 - 25kbps
{ 0x0f, 0xf1, 0x85 }, // 4 - 31k25kbps
{ 0x09, 0xbe, 0x07 }, // 5 - 33kbps
{ 0x07, 0xff, 0x87 }, // 6 - 40kbps
{ 0x07, 0xfa, 0x87 }, // 7 - 50kbps
{ 0x03, 0xff, 0x87 }, // 8 - 80kbps
{ 0x03, 0xbe, 0x07 }, // 9 - 83kbps
{ 0x03, 0xad, 0x07 }, // 10 - 95kbps
{ 0x03, 0xfa, 0x87 }, // 11 - 100kbps
{ 0x03, 0xf0, 0x86 }, // 12 - 125kbps
{ 0x01, 0xfa, 0x87 }, // 13 - 200kbps
{ 0x41, 0xf1, 0x85 }, // 14 - 250kbps
{ 0x00, 0xf0, 0x86 }, // 15 - 500kbps
{ 0x00, 0xa0, 0x04 }, // 16 - 666kbps
{ 0x00, 0xd0, 0x82 }, // 17 - 1000kbps
};
// For SPI, these are our CS on/off functions, if needed
static void mcp2515_cs_on(const mcp2515_context dev)
{
assert(dev != NULL);
if (dev->gpio)
mraa_gpio_write(dev->gpio, 0);
}
static void mcp2515_cs_off(const mcp2515_context dev)
{
assert(dev != NULL);
if (dev->gpio)
mraa_gpio_write(dev->gpio, 1);
}
// convert an integer id into the 4-byte breakout format used by the
// device.
static void mcp2515_int_to_id(const mcp2515_context dev, int id, bool ext,
bool is_filter, MCP2515_ID_T *did)
{
assert(dev != NULL);
assert(did != NULL);
// mask off all but the lower 29 bits
id &= 0x1fffffff;
did->SIDH = (id >> 3) & 0xff;
did->SIDL = ((id & 0x07) << 5);
did->EID8 = 0;
did->EID0 = 0;
// This is a sort of special case (ext vs. filter). When using
// this function to create a filter, but the msg type is standard,
// the filtering/masking engine can use the first 16 bits of
// extended filter data (EID8, EID0) to compare against the first
// 16 bits of the data payload (D0 and D1). For a standard id,
// the extended bits 16 and 17 are ignored, so we only include
// these (along with the extended bit) on true extended ids
// regardless of filtering.
if (ext || is_filter)
{
if (ext)
{
// add ext bits 17 and 16
did->SIDL |= ((id & 0x18000000) >> 27) & 0x03;
// set the extended id bit
did->SIDL |= MCP2515_SIDL_EXIDE;
}
// now fill in the extended bits
did->EID8 = (id >> 19) & 0xff;
did->EID0 = (id >> 11) & 0xff;
}
}
// convert the 4-byte breakout format used by the device into an integer id
static int mcp2515_id_to_int(const mcp2515_context dev, bool *ext,
MCP2515_ID_T *did)
{
assert(dev != NULL);
assert(did != NULL);
int id = 0;
*ext = false;
id |= (did->SIDH << 3);
id |= (did->SIDL >> 5) & 0x07;
// is it an extended id?
if (did->SIDL & MCP2515_SIDL_EXIDE)
{
*ext = true;
id |= ((did->SIDL & 0x03) << 27);
id |= (did->EID8 << 19);
id |= (did->EID0 << 11);
}
return id;
}
// init...
mcp2515_context mcp2515_init(int bus, int cs_pin)
{
mcp2515_context dev =
(mcp2515_context)malloc(sizeof(struct _mcp2515_context));
if (!dev)
return NULL;
// zero out context
memset((void *)dev, 0, sizeof(struct _mcp2515_context));
// make sure MRAA is initialized
int mraa_rv;
if ((mraa_rv = mraa_init()) != MRAA_SUCCESS)
{
printf("%s: mraa_init() failed (%d).\n", __FUNCTION__, mraa_rv);
mcp2515_close(dev);
return NULL;
}
if (!(dev->spi = mraa_spi_init(bus)))
{
printf("%s: mraa_spi_init() failed.\n", __FUNCTION__);
mcp2515_close(dev);
return NULL;
}
// Only create cs context if we are actually using a valid pin.
// A hardware controlled pin should specify cs as -1.
if (cs_pin >= 0)
{
if (!(dev->gpio = mraa_gpio_init(cs_pin)))
{
printf("%s: mraa_gpio_init() failed.\n", __FUNCTION__);
mcp2515_close(dev);
return NULL;
}
mraa_gpio_dir(dev->gpio, MRAA_GPIO_OUT);
mcp2515_cs_off(dev);
}
if (mraa_spi_mode(dev->spi, MRAA_SPI_MODE0))
{
printf("%s: mraa_spi_mode() failed.\n", __FUNCTION__);
mcp2515_close(dev);
return NULL;
}
if (mraa_spi_frequency(dev->spi, 10000000))
{
printf("%s: mraa_spi_frequency() failed.\n", __FUNCTION__);
mcp2515_close(dev);
return NULL;
}
// reset
if (mcp2515_reset(dev))
{
printf("%s: mcp2515_reset() failed.\n", __FUNCTION__);
mcp2515_close(dev);
return NULL;
}
// make sure the mode is config (should be after a reset), set a
// default speed, and then set normal mode.
if (mcp2515_set_opmode(dev, MCP2515_OPMODE_CONFIG))
{
printf("%s: mcp2515_set_opmode(config) failed.\n", __FUNCTION__);
mcp2515_close(dev);
return NULL;
}
if (mcp2515_set_speed(dev, MCP2515_SPEED_50KBPS))
{
printf("%s: mcp2515_set_speed() failed.\n", __FUNCTION__);
mcp2515_close(dev);
return NULL;
}
// clear all filters and masks
if (mcp2515_set_filter(dev, MCP2515_RX_FILTER0, false, 0)
|| mcp2515_set_filter(dev, MCP2515_RX_FILTER1, false, 0)
|| mcp2515_set_filter(dev, MCP2515_RX_FILTER2, false, 0)
|| mcp2515_set_filter(dev, MCP2515_RX_FILTER3, false, 0)
|| mcp2515_set_filter(dev, MCP2515_RX_FILTER4, false, 0)
|| mcp2515_set_filter(dev, MCP2515_RX_FILTER5, false, 0))
{
printf("%s: mcp2515_set_filter() failed.\n", __FUNCTION__);
mcp2515_close(dev);
return NULL;
}
if (mcp2515_set_mask(dev, MCP2515_RX_MASK0, false, 0)
|| mcp2515_set_mask(dev, MCP2515_RX_MASK1, false, 0))
{
printf("%s: mcp2515_set_mask() failed.\n", __FUNCTION__);
mcp2515_close(dev);
return NULL;
}
// set the mode for any filter. With the above settings, all
// packets will be received since all of the mode bits are 0, so
// the filter bits are ignored.
if (mcp2515_set_rx_buffer_mode(dev, MCP2515_RX_BUFFER0,
MCP2515_RXMODE_ANY_FILTER)
|| mcp2515_set_rx_buffer_mode(dev, MCP2515_RX_BUFFER1,
MCP2515_RXMODE_ANY_FILTER))
{
printf("%s: mcp2515_set_rx_buffer_mode() failed.\n", __FUNCTION__);
mcp2515_close(dev);
return NULL;
}
// turn off all interrupt enables
if (mcp2515_set_intr_enables(dev, 0))
{
printf("%s: mcp2515_set_intr_enables() failed.\n", __FUNCTION__);
mcp2515_close(dev);
return NULL;
}
// switch to normal operating mode
if (mcp2515_set_opmode(dev, MCP2515_OPMODE_NORMAL))
{
printf("%s: mcp2515_set_opmode(normal) failed.\n", __FUNCTION__);
mcp2515_close(dev);
return NULL;
}
return dev;
}
void mcp2515_close(mcp2515_context dev)
{
assert(dev != NULL);
mcp2515_uninstall_isr(dev);
if (dev->spi)
mraa_spi_stop(dev->spi);
if (dev->gpio)
mraa_gpio_close(dev->gpio);
free(dev);
}
// bus read and write functions
upm_result_t mcp2515_bus_read(const mcp2515_context dev, uint8_t cmd,
uint8_t *args, unsigned int arglen,
uint8_t *data, uint8_t len)
{
int buflen = len + 1 + arglen;
uint8_t sbuf[buflen];
memset((void *)sbuf, 0, buflen);
int index = 0;
sbuf[index++] = cmd;
if (args && arglen)
{
for (int i=0; i<arglen; i++)
sbuf[index++] = args[i];
}
mcp2515_cs_on(dev);
if (mraa_spi_transfer_buf(dev->spi, sbuf, sbuf, buflen))
{
mcp2515_cs_off(dev);
printf("%s: mraa_spi_transfer_buf() failed.\n", __FUNCTION__);
return UPM_ERROR_OPERATION_FAILED;
}
mcp2515_cs_off(dev);
// now copy it into user buffer
for (int i=0; i<len; i++)
data[i] = sbuf[i + 1 + arglen];
return UPM_SUCCESS;
}
upm_result_t mcp2515_bus_write(const mcp2515_context dev, uint8_t cmd,
uint8_t *data, uint8_t len)
{
assert(dev != NULL);
uint8_t sbuf[len + 1];
memset((void *)sbuf, 0, len + 1);
sbuf[0] = cmd;
// copy in the data to write...
if (data && len)
{
for (int i=0; i<len; i++)
sbuf[i + 1] = data[i];
}
mcp2515_cs_on(dev);
if (mraa_spi_transfer_buf(dev->spi, sbuf, sbuf, len + 1))
{
mcp2515_cs_off(dev);
printf("%s: mraa_spi_transfer_buf() failed.\n", __FUNCTION__);
return UPM_ERROR_OPERATION_FAILED;
}
mcp2515_cs_off(dev);
return UPM_SUCCESS;
}
upm_result_t mcp2515_write_reg(const mcp2515_context dev, uint8_t reg,
uint8_t value)
{
assert(dev != NULL);
uint8_t arg[2]; // reg addr, value
arg[0] = reg;
arg[1] = value;
return mcp2515_bus_write(dev, MCP2515_CMD_WRITE, arg, 2);
}
upm_result_t mcp2515_write_regs(const mcp2515_context dev, uint8_t reg,
uint8_t *buffer, int len)
{
assert(dev != NULL);
assert(buffer != NULL);
int writeLen = len + 1;
uint8_t arg[1+len]; // reg addr, data...
arg[0] = reg;
for (int i=0; i<len; i++)
arg[i+1] = buffer[i];
return mcp2515_bus_write(dev, MCP2515_CMD_WRITE, arg, writeLen);
}
upm_result_t mcp2515_read_reg(const mcp2515_context dev, uint8_t reg,
uint8_t *value)
{
assert(dev != NULL);
uint8_t arg = reg;
return mcp2515_bus_read(dev, MCP2515_CMD_READ, &arg, 1, value, 1);
}
upm_result_t mcp2515_reset(const mcp2515_context dev)
{
assert(dev != NULL);
upm_result_t rv = mcp2515_bus_write(dev, MCP2515_CMD_RESET, NULL, 0);
upm_delay_ms(100);
return rv;
}
upm_result_t mcp2515_bit_modify(const mcp2515_context dev, uint8_t addr,
uint8_t mask, uint8_t value)
{
assert(dev != NULL);
// build the command and send it
uint8_t args[3]; // addr, mask, value
args[0] = addr;
args[1] = mask;
args[2] = value;
return mcp2515_bus_write(dev, MCP2515_CMD_BIT_MODIFY, args, 3);
}
upm_result_t mcp2515_set_opmode(const mcp2515_context dev,
MCP2515_OPMODE_T opmode)
{
assert(dev != NULL);
// make it so
upm_result_t rv =
mcp2515_bit_modify(dev, MCP2515_REG_CANCTRL,
(_MCP2515_CANCTRL_REQOP_MASK
<< _MCP2515_CANCTRL_REQOP_SHIFT),
(opmode
<< _MCP2515_CANCTRL_REQOP_SHIFT));
if (rv)
{
printf("%s: mcp2515_bit_modify() failed\n", __FUNCTION__);
return rv;
}
// now spin until the mode is set (there can be a delay if the
// chip is busy sending or receiving packets, or, secretly
// communicating with our alien overlords).
// We set an upper limit of 5 seconds before we decide to timeout.
const uint8_t shiftmask = (_MCP2515_CANSTAT_OPMODE_MASK
<< _MCP2515_CANSTAT_OPMODE_SHIFT);
bool done = false;
upm_clock_t clock;
upm_clock_init(&clock);
do
{
uint8_t reg;
rv = mcp2515_read_reg(dev, MCP2515_REG_CANSTAT, &reg);
if (rv)
{
printf("%s: mcp2515_bus_read() failed\n", __FUNCTION__);
return rv;
}
if ((reg & shiftmask) == (opmode << _MCP2515_CANSTAT_OPMODE_SHIFT))
done = true;
else
upm_delay_ms(10);
} while (!done && (upm_elapsed_ms(&clock) < MCP2515_MAX_TIMEOUT));
if (!done)
return UPM_ERROR_TIMED_OUT;
else
return UPM_SUCCESS;
}
upm_result_t mcp2515_set_speed(const mcp2515_context dev,
MCP2515_SPEED_T speed)
{
assert(dev != NULL);
upm_result_t rv;
if ((rv = mcp2515_write_reg(dev, MCP2515_REG_CNF1,
mcp2515_configs[speed].cnf1)))
return rv;
if ((rv = mcp2515_write_reg(dev, MCP2515_REG_CNF2,
mcp2515_configs[speed].cnf2)))
return rv;
if ((rv = mcp2515_write_reg(dev, MCP2515_REG_CNF3,
mcp2515_configs[speed].cnf3)))
return rv;
return UPM_SUCCESS;
}
upm_result_t mcp2515_load_tx_buffer(const mcp2515_context dev,
MCP2515_TX_BUFFER_T bufnum,
int id, bool ext, bool rtr,
uint8_t *payload, int len)
{
assert(dev != NULL);
// The first thing to do is make sure that the tx buffer is
// actually available (ie: does not already contain a packet
// awaiting transmit).
if (!mcp2515_tx_buffer_is_free(dev, bufnum))
{
printf("%s: buffer is busy\n", __FUNCTION__);
return UPM_ERROR_NO_RESOURCES;
}
uint8_t cmd = 0;
switch (bufnum)
{
case MCP2515_TX_BUFFER0:
cmd = MCP2515_CMD_LOAD_TXBUF_TXB0SIDH;
break;
case MCP2515_TX_BUFFER1:
cmd = MCP2515_CMD_LOAD_TXBUF_TXB1SIDH;
break;
case MCP2515_TX_BUFFER2:
cmd = MCP2515_CMD_LOAD_TXBUF_TXB2SIDH;
break;
default:
printf("%s: invalid buffer specified\n", __FUNCTION__);
return UPM_ERROR_INVALID_PARAMETER;
}
// start preparing for the load
MCP2515_ID_T idBuf;
MCP2515_PKT_T packet;
memset(&packet, 0, sizeof(MCP2515_PKT_T));
// first add the id converted to the 4-byte id the device requires
// mask off all but the lower 29 bits
id &= 0x1fffffff;
mcp2515_int_to_id(dev, id, ext, false, &idBuf);
// copy in the [device]id bytes, then fill in the DLC reg.
packet.SIDH = idBuf.SIDH;
packet.SIDL = idBuf.SIDL;
packet.EID8 = idBuf.EID8;
packet.EID0 = idBuf.EID0;
// DLC register
if (len > MCP2515_MAX_PAYLOAD_DATA)
len = MCP2515_MAX_PAYLOAD_DATA;
if (len < 0)
len = 0;
packet.DLC = (len & _MCP2515_TXBDLC_MASK) << _MCP2515_TXBDLC_SHIFT;
if (rtr)
packet.DLC |= MCP2515_TXBDLC_RTR;
// now the payload
for (int i=0; i<len; i++)
packet.data[i+MCP2515_PKT_D0] = payload[i];
// load the buffer
if (mcp2515_bus_write(dev, cmd, packet.data, MCP2515_MAX_PKT_DATA))
{
printf("%s: mcp2515_bus_write() failed\n", __FUNCTION__);
return UPM_ERROR_OPERATION_FAILED;
}
return UPM_SUCCESS;
}
upm_result_t mcp2515_transmit_buffer(const mcp2515_context dev,
MCP2515_TX_BUFFER_T bufnum,
bool wait)
{
assert(dev != NULL);
uint8_t waitreg = 0;
uint8_t rtscmd = 0;
switch (bufnum)
{
case MCP2515_TX_BUFFER0:
waitreg = MCP2515_REG_TXB0CTRL;
rtscmd = MCP2515_CMD_RTS_BUFFER0;
break;
case MCP2515_TX_BUFFER1:
waitreg = MCP2515_REG_TXB1CTRL;
rtscmd = MCP2515_CMD_RTS_BUFFER1;
break;
case MCP2515_TX_BUFFER2:
waitreg = MCP2515_REG_TXB2CTRL;
rtscmd = MCP2515_CMD_RTS_BUFFER2;
break;
default:
return UPM_ERROR_INVALID_PARAMETER;
}
// now transmit the requested buffer
if (mcp2515_bus_write(dev, rtscmd, NULL, 0))
return UPM_ERROR_OPERATION_FAILED;
// if we are not waiting for transmission to complete, we are done
// here.
if (!wait)
return UPM_SUCCESS;
// now spin with timeout waiting for it to be transmitted
upm_clock_t clock;
upm_clock_init(&clock);
bool done = false;
do
{
uint8_t txbctrl = 0;
if (mcp2515_read_reg(dev, waitreg, &txbctrl))
return UPM_ERROR_OPERATION_FAILED;
if (!(txbctrl & MCP2515_TXBCTRL_TXREQ))
done = true;
upm_delay_ms(1);
} while (!done && (upm_elapsed_ms(&clock) < MCP2515_MAX_TIMEOUT));
if (!done)
return UPM_ERROR_TIMED_OUT;
else
return UPM_SUCCESS;
}
bool mcp2515_tx_buffer_is_free(const mcp2515_context dev,
MCP2515_TX_BUFFER_T bufnum)
{
assert(dev != NULL);
uint8_t chkreg = 0;
switch (bufnum)
{
case MCP2515_TX_BUFFER0:
chkreg = MCP2515_REG_TXB0CTRL;
break;
case MCP2515_TX_BUFFER1:
chkreg = MCP2515_REG_TXB1CTRL;
break;
case MCP2515_TX_BUFFER2:
chkreg = MCP2515_REG_TXB2CTRL;
break;
default:
return false;
}
uint8_t txbctrl = 0;
if (mcp2515_read_reg(dev, chkreg, &txbctrl))
return false;
if (!(txbctrl & MCP2515_TXBCTRL_TXREQ))
return true;
else
return false;
}
MCP2515_TX_BUFFER_T mcp2515_find_free_tx_buffer(const mcp2515_context dev)
{
assert(dev != NULL);
// check each of the tx buffers and return one if it's free.
if (mcp2515_tx_buffer_is_free(dev, MCP2515_TX_BUFFER0))
return MCP2515_TX_BUFFER0;
if (mcp2515_tx_buffer_is_free(dev, MCP2515_TX_BUFFER1))
return MCP2515_TX_BUFFER1;
if (mcp2515_tx_buffer_is_free(dev, MCP2515_TX_BUFFER2))
return MCP2515_TX_BUFFER2;
return MCP2515_TX_NO_BUFFERS;
}
// can only be set in CONFIG mode
upm_result_t mcp2515_set_filter(const mcp2515_context dev,
MCP2515_RX_FILTER_T filter, bool ext, int id)
{
assert(dev != NULL);
MCP2515_ID_T idBuf;
mcp2515_int_to_id(dev, id, ext, true, &idBuf);
uint8_t reg = 0;
switch (filter)
{
case MCP2515_RX_FILTER0:
reg = MCP2515_REG_FILTER0;
break;
case MCP2515_RX_FILTER1:
reg = MCP2515_REG_FILTER1;
break;
case MCP2515_RX_FILTER2:
reg = MCP2515_REG_FILTER2;
break;
case MCP2515_RX_FILTER3:
reg = MCP2515_REG_FILTER3;
break;
case MCP2515_RX_FILTER4:
reg = MCP2515_REG_FILTER4;
break;
case MCP2515_RX_FILTER5:
reg = MCP2515_REG_FILTER5;
break;
default:
return UPM_ERROR_INVALID_PARAMETER;
}
return mcp2515_write_regs(dev, reg, idBuf.data, MCP2515_MAX_ID_BYTES);
}
// can only be set in CONFIG mode
upm_result_t mcp2515_set_mask(const mcp2515_context dev,
MCP2515_RX_MASK_T mask, bool ext, int id)
{
assert(dev != NULL);
MCP2515_ID_T idBuf;
mcp2515_int_to_id(dev, id, ext, true, &idBuf);
uint8_t reg = 0;
switch (mask)
{
case MCP2515_RX_MASK0:
reg = MCP2515_REG_MASK0;
break;
case MCP2515_RX_MASK1:
reg = MCP2515_REG_MASK1;
break;
default:
return UPM_ERROR_INVALID_PARAMETER;
}
return mcp2515_write_regs(dev, reg, idBuf.data, MCP2515_MAX_ID_BYTES);
}
upm_result_t mcp2515_set_rx_buffer_mode(const mcp2515_context dev,
MCP2515_RX_BUFFER_T bufnum,
MCP2515_RXMODE_T rxm)
{
assert(dev != NULL);
uint8_t reg = 0;
uint8_t mask = 0, shift = 0;
switch (bufnum)
{
case MCP2515_RX_BUFFER0:
reg = MCP2515_REG_RXB0CTRL;
mask = _MCP2515_RXB0CTRL_RXMODE_MASK;
shift = _MCP2515_RXB0CTRL_RXMODE_SHIFT;
break;
case MCP2515_RX_BUFFER1:
reg = MCP2515_REG_RXB1CTRL;
mask = _MCP2515_RXB1CTRL_RXMODE_MASK;
shift = _MCP2515_RXB1CTRL_RXMODE_SHIFT;
break;
default:
return UPM_ERROR_INVALID_PARAMETER;
}
return mcp2515_bit_modify(dev, reg, mask << shift, rxm << shift);
}
upm_result_t mcp2515_set_tx_buffer_priority(const mcp2515_context dev,
MCP2515_TX_BUFFER_T bufnum,
MCP2515_TXP_T priority)
{
assert(dev != NULL);
uint8_t reg = 0;
uint8_t mask = _MCP2515_TXBCTRL_TXP_MASK;
uint8_t shift = _MCP2515_TXBCTRL_TXP_SHIFT;
switch (bufnum)
{
case MCP2515_TX_BUFFER0:
reg = MCP2515_REG_TXB0CTRL;
break;
case MCP2515_TX_BUFFER1:
reg = MCP2515_REG_TXB1CTRL;
break;
case MCP2515_TX_BUFFER2:
reg = MCP2515_REG_TXB2CTRL;
break;
default:
printf("%s: invalid buffer specified\n", __FUNCTION__);
return UPM_ERROR_INVALID_PARAMETER;
}
return mcp2515_bit_modify(dev, reg, mask << shift, priority << shift);
}
MCP2515_RXMSG_T mcp2515_rx_status_msgs(const mcp2515_context dev)
{
assert(dev != NULL);
uint8_t rx_status_byte;
if (mcp2515_bus_read(dev, MCP2515_CMD_RX_STATUS, NULL, 0,
&rx_status_byte, 1))
return MCP2515_RXMSG_NONE;
MCP2515_RXMSG_T buffers =
(rx_status_byte &
(_MCP2515_RXSTATUS_RXMSG_MASK << _MCP2515_RXSTATUS_RXMSG_SHIFT))
>> _MCP2515_RXSTATUS_RXMSG_SHIFT;
return buffers;
}
MCP2515_MSGTYPE_T mcp2515_rx_status_msg_type(const mcp2515_context dev)
{
assert(dev != NULL);
uint8_t rx_status_byte;
if (mcp2515_bus_read(dev, MCP2515_CMD_RX_STATUS, NULL, 0,
&rx_status_byte, 1))
return MCP2515_MSGTYPE_STD; // not really, but....
MCP2515_MSGTYPE_T msgtype =
(rx_status_byte &
(_MCP2515_RXSTATUS_MSGTYPE_MASK << _MCP2515_RXSTATUS_MSGTYPE_SHIFT))
>> _MCP2515_RXSTATUS_MSGTYPE_SHIFT;
return msgtype;
}
MCP2515_FILTERMATCH_T mcp2515_rx_status_filtermatch(const mcp2515_context dev)
{
assert(dev != NULL);
uint8_t rx_status_byte;
if (mcp2515_bus_read(dev, MCP2515_CMD_RX_STATUS, NULL, 0,
&rx_status_byte, 1))
return MCP2515_FILTERMATCH_RXF0; // not really, but....
MCP2515_FILTERMATCH_T fm =
(rx_status_byte &
(_MCP2515_RXSTATUS_FILTERMATCH_MASK
<< _MCP2515_RXSTATUS_FILTERMATCH_SHIFT))
>> _MCP2515_RXSTATUS_FILTERMATCH_SHIFT;
return fm;
}
upm_result_t mcp2515_get_rx_msg(const mcp2515_context dev,
MCP2515_RX_BUFFER_T bufnum,
MCP2515_MSG_T *msg)
{
assert(dev != NULL);
assert(msg != NULL);
uint8_t cmd = 0;
uint8_t rxbctrl_reg = 0;
switch (bufnum)
{
case MCP2515_RX_BUFFER0:
cmd = MCP2515_CMD_READ_RXBUF_RXB0SIDH;
rxbctrl_reg = MCP2515_REG_RXB0CTRL;
break;
case MCP2515_RX_BUFFER1:
cmd = MCP2515_CMD_READ_RXBUF_RXB1SIDH;
rxbctrl_reg = MCP2515_REG_RXB1CTRL;
break;
default:
return UPM_ERROR_INVALID_PARAMETER;
}
// first see if we have a message waiting
MCP2515_RXMSG_T rxmsgs = mcp2515_rx_status_msgs(dev);
bool msgavail = false;
if (rxmsgs == MCP2515_RXMSG_BOTH)
msgavail = true;
else if (rxmsgs == MCP2515_RXMSG_RXB0 && bufnum == MCP2515_RX_BUFFER0)
msgavail = true;
else if (rxmsgs == MCP2515_RXMSG_RXB1 && bufnum == MCP2515_RX_BUFFER1)
msgavail = true;
// nope
if (!msgavail)
return UPM_ERROR_NO_RESOURCES;
// get the control reg
upm_result_t rv;
uint8_t rxbctrl;
if ((rv = mcp2515_read_reg(dev, rxbctrl_reg, &rxbctrl)))
return rv;
// go ahead and grab the message and fill in the details
memset(msg, 0, sizeof(MCP2515_MSG_T));
if ((rv = mcp2515_bus_read(dev, cmd, NULL, 0,
msg->pkt.data, MCP2515_MAX_PKT_DATA)))
{
printf("mcp2515_bus_read failed\n");
return UPM_ERROR_OPERATION_FAILED;
}
// if we are here, we got the packet, so decode and determine some
// things.
// id & ext
MCP2515_ID_T did;
did.SIDH = msg->pkt.SIDH;
did.SIDL = msg->pkt.SIDL;
did.EID8 = msg->pkt.EID8;
did.EID0 = msg->pkt.EID0;
msg->id = mcp2515_id_to_int(dev, &(msg->ext), &did);
// rtr
if (!msg->ext)
{
// The RTR flag bit is the same for both the RXB0CTRL and
// RXB1CTRL registers, so it's safe to just compare against
// the first regardless of the buffer we are interested in.
if (rxbctrl & MCP2515_RXB0CTRL_RXRTR)
msg->rtr = true;
}
else
{
// ext message stores rtr separately
if (msg->pkt.DLC & MCP2515_RXBDLC_RTR)
msg->rtr = true;
}
// filter num
if (bufnum == MCP2515_RX_BUFFER0)
{
if (rxbctrl & MCP2515_RXB0CTRL_FILHIT)
msg->filter_num = 1;
else
msg->filter_num = 0;
}
else
{
msg->filter_num =
(int)((rxbctrl &
(_MCP2515_RXB1CTRL_FILHIT_MASK
<< _MCP2515_RXB1CTRL_FILHIT_SHIFT))
>> _MCP2515_RXB1CTRL_FILHIT_SHIFT);
}
// now the payload length
msg->len = ((msg->pkt.DLC & (_MCP2515_RXBDLC_MASK << _MCP2515_RXBDLC_SHIFT))
>> _MCP2515_RXBDLC_SHIFT);
return UPM_SUCCESS;
}
void mcp2515_print_msg(const mcp2515_context dev, MCP2515_MSG_T *msg)
{
assert(dev != NULL);
assert(msg != NULL);
// print it out
printf("id %08x ext %d rtr %d filt %d len %d\n",
msg->id, msg->ext, msg->rtr, msg->filter_num, msg->len);
printf("\tpayload: ");
for (int i=0; i<msg->len; i++)
printf("0x%02x ", msg->pkt.data[MCP2515_PKT_D0 + i]);
printf("\n");
}
upm_result_t mcp2515_abort_tx(const mcp2515_context dev,
MCP2515_TX_BUFFER_T bufnum)
{
assert(dev != NULL);
uint8_t reg;
switch (bufnum)
{
case MCP2515_TX_BUFFER0:
reg = MCP2515_REG_TXB0CTRL;
break;
case MCP2515_TX_BUFFER1:
reg = MCP2515_REG_TXB1CTRL;
break;
case MCP2515_TX_BUFFER2:
reg = MCP2515_REG_TXB2CTRL;
break;
default:
return UPM_ERROR_INVALID_PARAMETER;
}
// Clear the TXREQ bit.
return mcp2515_bit_modify(dev, reg, MCP2515_TXBCTRL_TXREQ, 0);
}
upm_result_t mcp2515_install_isr(const mcp2515_context dev, int pin,
void (*isr)(void *), void *arg)
{
assert(dev != NULL);
mcp2515_uninstall_isr(dev);
if ( !(dev->intr = mraa_gpio_init(pin)) )
{
printf("%s: mraa_gpio_init failed.\n", __FUNCTION__);
return UPM_ERROR_OPERATION_FAILED;
}
mraa_gpio_dir(dev->intr, MRAA_GPIO_IN);
// install our interrupt handler
mraa_gpio_isr(dev->intr, MRAA_GPIO_EDGE_FALLING,
isr, arg);
return UPM_SUCCESS;
}
void mcp2515_uninstall_isr(const mcp2515_context dev)
{
assert(dev != NULL);
if (!dev->intr)
return;
mraa_gpio_isr_exit(dev->intr);
mraa_gpio_close(dev->intr);
dev->intr = NULL;
}
upm_result_t mcp2515_set_intr_enables(const mcp2515_context dev,
uint8_t enables)
{
assert(dev != NULL);
return mcp2515_write_reg(dev, MCP2515_REG_CANINTE, enables);
}
upm_result_t mcp2515_get_intr_flags(const mcp2515_context dev, uint8_t *flags)
{
assert(dev != NULL);
return mcp2515_read_reg(dev, MCP2515_REG_CANINTF, flags);
}
upm_result_t mcp2515_set_intr_flags(const mcp2515_context dev, uint8_t flags)
{
assert(dev != NULL);
return mcp2515_bit_modify(dev, MCP2515_REG_CANINTF, flags, 0xff);
}
upm_result_t mcp2515_clear_intr_flags(const mcp2515_context dev, uint8_t flags)
{
assert(dev != NULL);
return mcp2515_bit_modify(dev, MCP2515_REG_CANINTF, flags, 0);
}
upm_result_t mcp2515_get_error_flags(const mcp2515_context dev, uint8_t *flags)
{
assert(dev != NULL);
return mcp2515_read_reg(dev, MCP2515_REG_EFLG, flags);
}
upm_result_t mcp2515_clear_error_flags(const mcp2515_context dev,
uint8_t flags)
{
assert(dev != NULL);
return mcp2515_bit_modify(dev, MCP2515_REG_EFLG, flags, 0);
}