Dependents: Kamal_CAN ReadFromSerial446 USNA-UMBC-KF-02_v3-noise USNA-UMBC-KF-01
Diff: mcp2515.cpp
- Revision:
- 0:d8f50b1e384f
- Child:
- 1:dbc44582f2f8
diff -r 000000000000 -r d8f50b1e384f mcp2515.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mcp2515.cpp Sat Jan 30 08:46:04 2010 +0000 @@ -0,0 +1,452 @@ +/****************************************************************************** + * + * 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); +}