File content as of revision 0:d8f50b1e384f:
/******************************************************************************
*
* Controller Area Network (CAN) Demo-Application
* Atmel AVR with Microchip MCP2515
*
* Copyright (C) 2005 Martin THOMAS, Kaiserslautern, Germany
* <eversmith@heizung-thomas.de>
* http://www.siwawi.arubi.uni-kl.de/avr_projects
*
*****************************************************************************
*
* File : mcp2515.c
* Version : 0.9
*
* Summary : MCP2515 "low-level" driver
*
* Parts of this code are adapted from a MCP2510 sample-application
* by KVASER AB, http://www.kvaser.com (KVASER-code is marked as free)
*
* This code-module is free to use but you have to keep the copyright
* notice.
*
*
*****************************************************************************
*
* File : mcp2515.cpp (mbed LPC1768 version)
* Version : 0.1
*
* All credits to the nerds above, this source has been adapted for the
* LPC1768 platform by J.Engelman. And does'nt require and of the copyrighted
* SPI or AVR controller code that Martin or co have excluded copyright.
* This module remains free.
*
*
*****************************************************************************/
#include "mcp2515.h"
#include <mbed.h>
#include "mcp2515_can.h"
#include "mcp2515_defs.h"
#include "mcp2515_bittime.h"
#define SPI_NULL (0x00)
mcp2515::mcp2515(PinName mosi, PinName miso, PinName clk, PinName ncs)
: _spi(mosi, miso, clk), _ncs(ncs) {
// _spi.format(8,0);
// _spi.frequency(10000000);
//_spi.frequency(5000000);
}
void mcp2515::_reset() {
_select();
_spi_readwrite(MCP_RESET);
_deselect();
wait(0.001);
}
void mcp2515::setRegister(const uint8_t address, const uint8_t value)
{
_select();
_spi_readwrite(MCP_WRITE);
_spi_readwrite(address);
_spi_readwrite(value);
_deselect();
}
uint8_t mcp2515::configRate(const uint8_t canSpeed)
{
uint8_t set, cfg1, cfg2, cfg3;
set = 0;
switch (canSpeed) {
case (CAN_125KBPS) :
cfg1 = MCP_4MHz_125kBPS_CFG1 ;
cfg2 = MCP_4MHz_125kBPS_CFG2 ;
cfg3 = MCP_4MHz_125kBPS_CFG3 ;
set = 1;
break;
case (CAN_20KBPS) :
cfg1 = MCP_4MHz_20kBPS_CFG1 ;
cfg2 = MCP_4MHz_20kBPS_CFG2 ;
cfg3 = MCP_4MHz_20kBPS_CFG3 ;
set = 1;
break;
default:
set = 0;
break;
}
if (set) {
setRegister(MCP_CNF1, cfg1);
setRegister(MCP_CNF2, cfg2);
setRegister(MCP_CNF3, cfg3);
return MCP2515_OK;
}
else {
return MCP2515_FAIL;
}
}
uint8_t mcp2515::readRegister(const uint8_t address)
{
uint8_t ret;
_select();
_spi_readwrite(MCP_READ);
_spi_readwrite(address);
ret = _spi_read();
_deselect();
return ret;
}
void mcp2515::readRegisterS(const uint8_t address,
uint8_t values[], const uint8_t n)
{
uint8_t i;
_select();
_spi_readwrite(MCP_READ);
_spi_readwrite(address);
// mcp2515 has auto-increment of address-pointer
for (i=0; i<n; i++) {
values[i] = _spi_read();
}
_deselect();
}
void mcp2515::modifyRegister(const uint8_t address,
const uint8_t mask, const uint8_t data)
{
_select();
_spi_readwrite(MCP_BITMOD);
_spi_readwrite(address);
_spi_readwrite(mask);
_spi_readwrite(data);
_deselect();
}
uint8_t mcp2515::readXXStatus_helper(const uint8_t cmd)
{
uint8_t i;
_select();
_spi_readwrite(cmd);
i = _spi_read();
_deselect();
return i;
}
uint8_t mcp2515::readStatus(void)
{
return readXXStatus_helper(MCP_READ_STATUS);
}
uint8_t mcp2515::RXStatus(void)
{
return readXXStatus_helper(MCP_RX_STATUS);
}
// read-modify-write - better: Bit Modify Instruction
uint8_t mcp2515::setCANCTRL_Mode(uint8_t newmode)
{
uint8_t i;
i = readRegister(MCP_CANCTRL);
i &= ~(MODE_MASK);
i |= newmode;
setRegister(MCP_CANCTRL, i);
// verify as advised in datasheet
i = readRegister(MCP_CANCTRL);
i &= MODE_MASK;
if ( i == newmode ) {
return MCP2515_OK;
}
else {
return MCP2515_FAIL;
}
}
void mcp2515::setRegisterS(const uint8_t address,
const uint8_t values[], const uint8_t n)
{
uint8_t i;
_select();
_spi_readwrite(MCP_WRITE);
_spi_readwrite(address);
// mcp2515 has auto-increment of address-pointer
for (i=0; i<n; i++) {
_spi_readwrite(values[i]);
}
_deselect();
}
void mcp2515::read_can_id( const uint8_t mcp_addr,
uint8_t* ext, uint32_t* can_id )
{
uint8_t tbufdata[4];
*ext = 0;
*can_id = 0;
readRegisterS( mcp_addr, tbufdata, 4 );
*can_id = (tbufdata[MCP_SIDH]<<3) + (tbufdata[MCP_SIDL]>>5);
if ( (tbufdata[MCP_SIDL] & MCP_TXB_EXIDE_M) == MCP_TXB_EXIDE_M ) {
// extended id
*can_id = (*can_id<<2) + (tbufdata[MCP_SIDL] & 0x03);
*can_id <<= 16;
*can_id = *can_id +(tbufdata[MCP_EID8]<<8) + tbufdata[MCP_EID0];
*ext = 1;
}
}
// Buffer can be MCP_RXBUF_0 or MCP_RXBUF_1
void mcp2515::read_canMsg( const uint8_t buffer_sidh_addr,
CANMessage* msg)
{
/*
uint8_t mcp_addr, ctrl;
mcp_addr = buffer_sidh_addr;
read_can_id( mcp_addr, &(msg->extended_identifier),
&(msg->identifier) );
ctrl = readRegister( mcp_addr-1 );
msg->dlc = readRegister( mcp_addr+4 );
//if ((*dlc & RTR_MASK) || (ctrl & 0x08)) {
if ((ctrl & 0x08)) {
msg->rtr = 1;
} else {
msg->rtr = 0;
}
msg->dlc &= MCP_DLC_MASK;
readRegisterS( mcp_addr+5, &(msg->dta[0]), msg->dlc );
*/
}
void mcp2515::write_can_id( const uint8_t mcp_addr,
const uint8_t ext, const uint32_t can_id )
{
uint16_t canid;
uint8_t tbufdata[4];
canid = (uint16_t)(can_id & 0x0FFFF);
if ( ext == 1) {
tbufdata[MCP_EID0] = (uint8_t) (canid & 0xFF);
tbufdata[MCP_EID8] = (uint8_t) (canid / 256);
canid = (uint16_t)( can_id / 0x10000L );
tbufdata[MCP_SIDL] = (uint8_t) (canid & 0x03);
tbufdata[MCP_SIDL] += (uint8_t) ((canid & 0x1C )*8);
tbufdata[MCP_SIDL] |= MCP_TXB_EXIDE_M;
tbufdata[MCP_SIDH] = (uint8_t) (canid / 32 );
}
else {
tbufdata[MCP_SIDH] = (uint8_t) (canid / 8 );
tbufdata[MCP_SIDL] = (uint8_t) ((canid & 0x07 )*32);
tbufdata[MCP_EID0] = 0;
tbufdata[MCP_EID8] = 0;
}
setRegisterS( mcp_addr, tbufdata, 4 );
}
// Buffer can be MCP_TXBUF_0 MCP_TXBUF_1 or MCP_TXBUF_2
void mcp2515::write_canMsg( const uint8_t buffer_sidh_addr,
CANMessage* msg)
{
uint8_t mcp_addr, dlc;
mcp_addr = buffer_sidh_addr;
dlc = msg->len;
setRegisterS(mcp_addr+5, &(msg->data[0]), dlc ); // write data bytes
write_can_id( mcp_addr, msg->format,
msg->id ); // write CAN id
if ( msg->type == 1) dlc |= MCP_RTR_MASK; // if RTR set bit in byte
setRegister( (mcp_addr+4), dlc ); // write the RTR and DLC
}
void mcp2515::start_transmit(const uint8_t buffer_sidh_addr)
{
// TXBnCTRL_addr = TXBnSIDH_addr - 1
modifyRegister( buffer_sidh_addr-1 , MCP_TXB_TXREQ_M,
MCP_TXB_TXREQ_M );
}
uint8_t mcp2515::getNextFreeTXBuf(uint8_t *txbuf_n)
{
uint8_t res, i, ctrlval;
uint8_t ctrlregs[MCP_N_TXBUFFERS] = { MCP_TXB0CTRL, MCP_TXB1CTRL, MCP_TXB2CTRL };
res = MCP_ALLTXBUSY;
*txbuf_n = 0x00;
// check all 3 TX-Buffers
for (i=0; i<MCP_N_TXBUFFERS; i++) {
ctrlval = readRegister( ctrlregs[i] );
if ( (ctrlval & MCP_TXB_TXREQ_M) == 0 ) {
*txbuf_n = ctrlregs[i]+1; // return SIDH-address of Buffer
res = MCP2515_OK;
return res; /* ! function exit */
}
}
return res;
}
void mcp2515::initCANBuffers(void)
{
uint8_t i, a1, a2, a3;
// TODO: check why this is needed to receive extended
// and standard frames
// Mark all filter bits as don't care:
write_can_id(MCP_RXM0SIDH, 0, 0);
write_can_id(MCP_RXM1SIDH, 0, 0);
// Anyway, set all filters to 0:
write_can_id(MCP_RXF0SIDH, 1, 0); // RXB0: extended
write_can_id(MCP_RXF1SIDH, 0, 0); // AND standard
write_can_id(MCP_RXF2SIDH, 1, 0); // RXB1: extended
write_can_id(MCP_RXF3SIDH, 0, 0); // AND standard
write_can_id(MCP_RXF4SIDH, 0, 0);
write_can_id(MCP_RXF5SIDH, 0, 0);
// Clear, deactivate the three transmit buffers
// TXBnCTRL -> TXBnD7
a1 = MCP_TXB0CTRL;
a2 = MCP_TXB1CTRL;
a3 = MCP_TXB2CTRL;
for (i = 0; i < 14; i++) { // in-buffer loop
setRegister(a1, 0);
setRegister(a2, 0);
setRegister(a3, 0);
a1++;
a2++;
a3++;
}
// and clear, deactivate the two receive buffers.
setRegister(MCP_RXB0CTRL, 0);
setRegister(MCP_RXB1CTRL, 0);
}
uint8_t mcp2515::init(const uint8_t canSpeed)
{
uint8_t res;
_deselect();
//MCP_CS_DDR |= ( 1 << MCP_CS_BIT );
_reset();
res = setCANCTRL_Mode(MODE_CONFIG);
if ( res == MCP2515_FAIL ){
printf("FAIL here");
return res; /* function exit on error */
}
res = configRate(canSpeed);
if ( res == MCP2515_OK ) {
initCANBuffers();
// enable both receive-buffers to receive messages
// with std. and ext. identifiers
// and enable rollover
modifyRegister(MCP_RXB0CTRL,
MCP_RXB_RX_MASK | MCP_RXB_BUKT_MASK,
MCP_RXB_RX_STDEXT | MCP_RXB_BUKT_MASK );
modifyRegister(MCP_RXB1CTRL, MCP_RXB_RX_MASK,
MCP_RXB_RX_STDEXT);
}
return res;
}
/*
* Select function
*/
void mcp2515::_select() {
//printf("{");
_ncs = 0;
}
/*
* Deselect function
*/
void mcp2515::_deselect() {
_ncs = 1;
//printf("}");
}
int mcp2515::status() {
int status = 0;
_select();
_spi.write(0xd7);
status = (_spi.write(0x00) << 8 );
status |= _spi.write(0x00);
_deselect();
return status;
}
void mcp2515::_pollbusy() {
volatile int busy = 1;
while (busy) {
// if bit 7 is set, we can proceed
if ( status() & 0x80 ) {
busy = 0;
}
}
}
uint8_t mcp2515::_spi_readwrite(uint8_t data)
{
//printf("W0x%x ", data);
uint8_t ret = _spi.write(data);
// printf("R0x%x,", ret);
return ret;
}
uint8_t mcp2515::_spi_read(void)
{
return _spi_readwrite(SPI_NULL);
}