heres the Swordfish Module that ive put together for the MCP2515, the module takes care of the SPI interface aswell, so the user has only got to set his/hers MCU pins of the specific microchip they are using..
Code: Select all
{
*****************************************************************************
* Name : MCP2515 Module *
* Author : Gavin Wiggett *
* Notice : Licensed Bitfogav *
* : All Rights Reserved *
* Date : 11/10/2011 *
* Notes : Microchip MCP2515 - CAN Interface with Serial SPI *
* : SPI is taken care off by the Module - user set MCU pins *
* *
* Version : 1.0 *
* : The module is designed to use software Reset to Reset MCP2515 *
*****************************************************************************
}
Module MCP2515
Include "MCP2515_def" ' user lib Definitions support module for MCP2515
// map registers to SSP(x)
#if _mssp = 0
#error _device + " does not support MSSP"
// single SSP...
#elseif _mssp = 1
// default module option - user options will override these values...
#option SPI_SCK = PORTC.3 ' SPI clock to MCP2515 pin (PDIP/SOIC 13)
#option SPI_SDI = PORTC.4 ' SPI SDI master in/slave out from MCP2515 pin (PDIP/SOIC 15)
#option SPI_SDO = PORTC.5 ' SPI SDO master out/slave in to MCP2515 pin (PDIP/SOIC 14)
#option MCP_CSN = PORTA.5 ' SPI chip select to MCP2515 pin (PDIP/SOIC 16)
#option MCP_INT = PORTC.6 ' MCP2515 INT pin (PDIP/SOIC 12)
// validate SCK pin...
#if IsOption(SPI_SCK) And Not IsValidPortPin(SPI_SCK)
#error SPI_SCK, "Invalid option. SCK must be a valid port pin."
#endif
// validate SDI pin...
#if IsOption(SPI_SDI) And Not IsValidPortPin(SPI_SDI)
#error SPI_SDI, "Invalid option. SDI must be a valid port pin."
#endif
// validate SDO pin...
#if IsOption(SPI_SDO) And Not IsValidPortPin(SPI_SDO)
#error SPI_SDO, "Invalid option. SDO must be a valid port pin."
#endif
// validate CS pin...
#if IsOption(MCP_CSN) And Not IsValidPortPin(MCP_CSN)
#error MCP_CSN, "Invalid option. CS must be a valid port pin."
#endif
// validate CS pin...
#if IsOption(MCP_INT) And Not IsValidPortPin(MCP_INT)
#error MCP_INT, "Invalid option. INT must be a valid port pin."
#endif
// Declare port pins for SPI and MCP2515
Dim
SCK As SPI_SCK.SPI_SCK@, ' clock pin
SDI As SPI_SDI.SPI_SDI@, ' data in pin
SDO As SPI_SDO.SPI_SDO@, ' data out pin
CSN As MCP_CSN.MCP_CSN@, ' chip select pin
MCP2515_INT As MCP_INT.MCP_INT@ ' INT pin
// has more than one SSP module?...
#else
#error "MCP2515 module does not support MSSP2"
#endif
// CAN operating frequency, valid modes are: 125kbp, 250kbp, 500kbp, 1mb
// Obtained from the MCP2515 timing calculator (MBTime) with 20Mhz crystal
// config1 = CNF1 mcp2515 register
// config2 = CNF2 mcp2515 register
// config3 = CNF3 mcp2515 register
#if IsOption(CAN_BAUDRATE) And Not (CAN_BAUDRATE in (125, 250, 500, 1000))
#error CAN_BAUDRATE, "Invalid option, CAN_BAUDRATE must be 125, 250, 500, 1000."
#endif
#if Not IsOption(CAN_BAUDRATE)
#option CAN_BAUDRATE = 250 ' Default CAN operating frequency to 250Kbps
' Comment out the warning if you do not want it!
#warning "CAN_BAUDRATE option is set to default = 250Kbps"
#endif
#if CAN_BAUDRATE = 125
Public Const
config1 = $03,
config2 = $BA,
config3 = $07
#elseif CAN_BAUDRATE = 250
Public Const
config1 = $01,
config2 = $BA,
config3 = $07
#elseif CAN_BAUDRATE = 500
Public Const
config1 = $00,
config2 = $B6,
config3 = $04
#elseif CAN_BAUDRATE = 1000
Public Const
config1 = $00,
config2 = $A0,
config3 = $02
#endif
// SPI MSSP
Public Dim
SSPControl1 As SSPCON1, ' MSSP Control register 1
SSPStatus As SSPSTAT, ' MSSP Status register (SPI MODE)
SSPBuffer As SSPBUF ' Receive buffer/Transmit register
// SSPSTAT bitnames
Public Dim
BF As SSPStatus.booleans(0), ' buffer full (receive and transmit)
SMP As SSPStatus.7, ' read sample mode
CKE As SSPStatus.6 ' clock edge control
// SSPCON1 bitnames, master mode only...
Public Dim
WCOL As SSPControl1.booleans(7), ' write collision Detect
SSPOV As SSPControl1.booleans(6), ' receive overflow
SSPEN As SSPControl1.booleans(5), ' synchronous receive enable
CKP As SSPControl1.4, ' clock polarity
// synchronous mode select bits, %00XX for master mode
SSPM3 As SSPControl1.3, ' always zero
SSPM2 As SSPControl1.2, ' slave Mode
SSPM1 As SSPControl1.1, ' clock Mode (MSB)
SSPM0 As SSPControl1.0 ' clock Mode (LSB)
// interrupt flags
Public Dim
SSPIF As PIR1.3,
SSPIE As PIE1.3
// standard SPI bus modes
Public Const
SPI_MODE_0 = 0, ' ckp=0, cke=1... clock idle low, mosi changes on falling clk
SPI_MODE_1 = 1, ' ckp=0, cke=0... clock idle low, mosi changes on rising clk
SPI_MODE_2 = 2, ' ckp=1, cke=1... clock idle high, mosi changes on rising clk
SPI_MODE_3 = 3 ' ckp=1, cke=0... clock idle high, mosi changes on falling clock
Public Const
spiOscDiv4 = 0, ' master mode FOSC/4
spiOscDiv16 = 1, ' master mode FOSC/16
spiOscDiv64 = 2, ' master mode FOSC/64
spiOscTimer2 = 3, ' master mode TMR2 provides clock
// RX data sampling settings (SSPStatus.6 - SMP)
spiSampleEnd = $80, ' input data sampled at end of data output time
spiSampleMiddle = $00 ' input data sampled at middle of data output time
// SPI Mode, Set SPI to master mode, spiOscDiv4 (DEFAULT, 10mhz) And sets I/O for SPI to MCP2515
Sub spi_set_as_master(pOscMode As Byte = spiOscDiv4)
// disable mssp for the time being
SSPEN = false
// set master mode
SSPControl1 = SSPControl1 And $F0
SSPControl1 = SSPControl1 Or pOscMode
End Sub
// set io pins for SPI and MCP2515
Sub set_IO_ports()
Output(SDO)
Output(SCK)
Input(SDI)
Output(CSN)
Input(MCP2515_INT)
End Sub
// SPI sample, Sets when the SPI input data is sampled
Macro spi_set_sample(m)
If (m = 0) Then
SMP = 0 ' spiSampleMiddle
Else
SMP = 1 ' spiSampleEnd
EndIf
End Macro
// standard SPI bus modes, Set SPI idle state and data transmission clock edge
Sub spi_set_mode(m As Byte)
Select (m)
Case SPI_MODE_1
CKP = 0
CKE = 0
Case SPI_MODE_2
CKP = 1
CKE = 1
Case SPI_MODE_3
CKP = 1
CKE = 0
Else ' default to mode 0 (0,0)
CKP = 0
CKE = 1
End Select
End Sub
// Enables SPI and clears any flags
Inline Sub spi_enable_port()
SSPIE = 0 ' disable intr (polled mode only)
WREG = SSPBuffer ' clear buffer
SSPIF = 0 ' reset intr flag
SSPEN = true ' enable mssp
End Sub
// init MSSP to spi master, spi mode 0, transmission 10Mhz, set I/O
Public Sub init_spi()
spi_set_as_master(spiOscDiv4) ' spi speed (if spiOscDiv4 then you need to uncomment nop in mcp_cs_delay sub)
set_IO_ports() ' set io pins for SPI and MCP2515
spi_set_mode(SPI_MODE_0) ' set SPI idle state and data transmission clock edge
spi_set_sample(spiSampleMiddle) ' sample input data at middle of data out time
spi_enable_port()
End Sub
{
**************************************************************************************
* Name : spi_transfer *
* Purpose : transfer a byte (does a proper SPI transaction...write and read) *
* : Returns received spi byte *
**************************************************************************************
}
Public Function spi_transfer(b As WREG) As WREG
// reset buffer full and SSPIF
BF = false
SSPIF = 0
// send the byte and wait for it to transmit
SSPBuffer = b
Repeat
Until (SSPIF = 1)
// get the byte that just came in... this clears BF
result = SSPBuffer
// make sure write collision is cleared
WCOL = false
End Function
{
**************************************************************************************
* Name : mcp_cs_delay, mcp_assert_cs, mcp_deassert_cs *
* Purpose : delay for asserting CS pin *
* : Set CS Low for comms to mcp2515 *
* : Set CS High to stop comms to mcp2515 *
* *
* note: the timing specs below are worst case. they're actually less (~ 50ns?) *
* If you're running up to 40MHz, then there's enough overhead in the spi_transfer() *
* calls, and you can just comment the nop out. *
**************************************************************************************
}
Public Inline Sub mcp_cs_delay()
Asm '
nop ' comment out if not needed
End Asm '
End Sub
Public Inline Sub mcp_assert_cs()
CSN = 0
mcp_cs_delay() ' Tcss setup time = 50ns?
End Sub
Public Inline Sub mcp_deassert_cs()
mcp_cs_delay() ' Tcsh hold = 100ns?
CSN = 1
mcp_cs_delay() ' Tcsd cs disable time = 100ns?
End Sub
{
**************************************************************************************
* Name : mcp2515_Reset *
* Purpose : Software Reset mcp2515 *
* : reset mcp2515 via SPI and will put the mcp2515 into Configuration Mode *
**************************************************************************************
}
Public Sub mcp2515_Reset()
mcp_assert_cs() ' start comms to MCP2515
spi_transfer(SPI_RESET) ' send reset to mcp2515
mcp_deassert_cs() ' stop comms to MCP2515
DelayMS(20) ' wait for the MCP2515 to restart
End Sub
{
**************************************************************************************
* Name : mcp2515_CAN_Freq *
* Purpose : set CAN operation Freq, Can only be set in Configuration mode *
* : sets CNF1, CNF2, CNF3 registers *
**************************************************************************************
}
Public Sub mcp2515_CAN_Freq()
mcp_assert_cs() ' start comms to MCP2515
spi_transfer(SPI_WRITE) ' send write command
spi_transfer(CNF3) ' address
spi_transfer(config3) ' CNF3
spi_transfer(config2) ' CNF2
spi_transfer(config1) ' CNF1
mcp_deassert_cs() ' stop comms to MCP2515
End Sub
{
**************************************************************************************
* Name : mcp2515_read_register *
* Purpose : Read mcp2515 Registers at Address *
* : Returns received spi byte from mcp2515 *
**************************************************************************************
}
Public Function mcp2515_read_register(address As Byte) As Byte
mcp_assert_cs() ' start comms to MCP2515
spi_transfer(SPI_READ) ' send mcp2515 read instruction
spi_transfer(address) ' send address
result = spi_transfer($FF) ' receive spi data from mcp2515 send dummy byte
mcp_deassert_cs() ' stop comms to MCP2515
End Function
{
**************************************************************************************
* Name : mcp2515_write_register *
* Purpose : Write mcp2515 Registers *
* : Send spi data to mcp2515 at Address, Data *
**************************************************************************************
}
Public Sub mcp2515_write_register(address As Byte, data As Byte)
mcp_assert_cs() ' start comms to MCP2515
spi_transfer(SPI_WRITE) ' send write instruction
spi_transfer(address) ' send address
spi_transfer(data) ' send data byte
mcp_deassert_cs() ' stop comms to MCP2515
End Sub
{
**************************************************************************************
* Name : mcp2515_bit_modify *
* Purpose : Bit Modify provides a means for setting or clearing individual bits *
* : in registers *
* notes : not all registers are available for bit modify see datasheet *
* CANCTRL, TXRTSCTRL, BFPCTRL, EFLG, CANINTF, CANINTE, CNF1, CNF2, CNF3, TXB0CTRL *
* TXB1CTRL, TXB2CTRL, RXB0CTRL, RXB1CTRL - locations that allow the user to *
* manipulate individual bits using the Bit Modify command. *
**************************************************************************************
}
Public Sub mcp2515_bit_modify(address As Byte, mask As Byte, data As Byte)
mcp_assert_cs() ' start comms to MCP2515
spi_transfer(SPI_BIT_MODIFY) ' send bit modify instruction
spi_transfer(address) ' send address
spi_transfer(mask) ' send mask byte
spi_transfer(data) ' send data byte
mcp_deassert_cs() ' stop comms to MCP2515
End Sub
{
**************************************************************************************
* Name : mcp2515_read_status *
* Purpose : Reads all the important bits interrupts/buffers TX/RX *
* : same has if you used the standard read command *
* notes : Returns recieved status from mcp2515 *
* : see mcp215 datasheet TABLE 12-1: SPI INSTRUCTION SET *
**************************************************************************************
}
Public Function mcp2515_read_status(pStatus As Byte) As Byte
mcp_assert_cs() ' start comms to MCP2515
spi_transfer(pStatus) ' read status of register
result = spi_transfer($FF) ' receive spi data from mcp2515 send dummy byte
mcp_deassert_cs() ' stop comms to MCP2515
End Function
{
**************************************************************************************
* Name : mcp2515_CAN_Mode *
* Purpose : to configure/set CAN Mode *
* : *
* notes : CAN modes, Config, Normal, Sleep, Listen, Loopback *
**************************************************************************************
}
Public Sub mcp2515_CAN_Mode(cfg As Byte)
Dim mode, done As Byte ' temp var
done = 0
While (done = 0) ' keep sending mode untill set
mcp2515_write_register(CANCTRL, cfg)
DelayMS(1) ' delay
mode = mcp2515_read_register(CANSTAT) ' read the config mode back
mode = mode And $E0 ' mask off the config bits
If (mode = cfg) Then ' check to see if mode has been set
done = 1 ' it has been set correctly
End If
DelayMS(1) ' delay
Wend
End Sub
{
**************************************************************************************
* Name : RBX0F_IO_pin *
* Purpose : set or clear digital output pin on mcp2515, pin RX0BF *
* : *
* notes : Example: RBX0F_IO_pin(1) = pin high / RBX0F_IO_pin(0) = pin low *
* : must set register eg: (BFPCTRL = %00000100) *
**************************************************************************************
}
Public Sub RBX0F_IO_pin(pSet As Bit)
// activate the RXnBF Pins (RX0BF Pin active)
// LED connected to pin to show comms to mcp2515
If pSet = 1 Then
' register, mask bit, data bit
mcp2515_bit_modify(BFPCTRL, (1 << B0BFS), (1 << B0BFS))
Else
' register, mask bit, data bit
mcp2515_bit_modify(BFPCTRL, (1 << B0BFS), (0 << B0BFS))
EndIf
End Sub
{
**************************************************************************************
* Name : RBX1F_IO_pin *
* Purpose : set or clear digital output pin on mcp2515, pin RX1BF *
* : *
* notes : Example: RBX1F_IO_pin(1) = pin high / RBX1F_IO_pin(0) = pin low *
* : must set register eg: (BFPCTRL = %00001000) *
**************************************************************************************
}
Public Sub RBX1F_IO_pin(pSet As Bit)
// activate the RXnBF Pins (RX1BF Pin active)
// LED connected to pin to show comms to mcp2515
If pSet = 1 Then
' register, mask bit, data bit
mcp2515_bit_modify(BFPCTRL, (1 << B1BFS), (1 << B1BFS))
Else
' register, mask bit, data bit
mcp2515_bit_modify(BFPCTRL, (1 << B1BFS), (0 << B1BFS))
EndIf
End Sub
// init SPI and set MCP2515
init_spi()
End Module