mirror of
https://github.com/Seeed-Studio/Seeed_Arduino_CAN.git
synced 2026-09-15 11:03:48 +00:00
Add sleep and gpio support + examples
Added sleep support for MCP2515 + MCP2551. This way, you can reduce the power consumption of a complete node down to around 240uA! Changes: - Reintroduced gpio support from commits 43521de/bac66d1 (adlerweb, 15/01/2018) and corrected the errors - Introduced advanced sleep support based on https://github.com/coryjfowler/MCP_CAN_lib/pull/10/files - added example code (receive_sleep and send_sleep) - tested everything
This commit is contained in:
@@ -142,6 +142,15 @@ When frame is received you may check whether it was remote request and whether i
|
||||
|
||||
**return value** is '0' for a negative response and '1' for a positive
|
||||
|
||||
### 8. Sleep Mode
|
||||
|
||||
By setting the MCU, the CAN controller (MCP2515) and the transceiver (MCP2551) into sleep mode, you can reduce the power consumption of the whole setup from around 50mA down to 240uA (Arduino directly connected to 5V, regulator and power LED removed). The node will wake up when a new message arrives, process the message and go back to sleep afterwards.
|
||||
|
||||
Look at the examples "receive_sleep" and "send_sleep" for more info.
|
||||
|
||||
In order to set the MCP2551 CAN transceiver on the shield into sleep/standby mode, a small hardware modification is necessary. The Rs pin of the transceiver must either be connected to pin RX0BF of the MCP2515 or to a free output of the Arduino, both via the resistor R1 (17k). Cut the connection to ground after R1 and solder in a wire to one of the pins. Note however that from now on, you have to pull this pin low in software before using the transceiver. Pulling the pin high will set the transceiver into standby mode.
|
||||
|
||||
Without this modification, the transceiver will stay awake and the power consumption in sleep mode will be around 8mA - still a significant improvement!
|
||||
|
||||
<br>
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ void setup()
|
||||
}
|
||||
Serial.println("CAN init ok");
|
||||
|
||||
if(CAN.pinMode(MCP_TX2RTS, MCP_PIN_IN))
|
||||
if(CAN.mcpPinMode(MCP_TX2RTS, MCP_PIN_IN))
|
||||
{
|
||||
Serial.println("TX2RTS is now an input");
|
||||
}
|
||||
@@ -32,7 +32,7 @@ void setup()
|
||||
void loop()
|
||||
{
|
||||
Serial.print("TX2RTS is currently ");
|
||||
Serial.println(CAN.digitalRead(MCP_TX2RTS));
|
||||
Serial.println(CAN.mcpDigitalRead(MCP_TX2RTS));
|
||||
delay(500);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ void setup()
|
||||
}
|
||||
Serial.println("CAN init ok");
|
||||
|
||||
if(CAN.pinMode(MCP_RX0BF, MCP_PIN_OUT))
|
||||
if(CAN.mcpPinMode(MCP_RX0BF, MCP_PIN_OUT))
|
||||
{
|
||||
Serial.println("RX0BF is now an output");
|
||||
}
|
||||
@@ -28,7 +28,7 @@ void setup()
|
||||
Serial.println("Could not switch RX0BF");
|
||||
}
|
||||
|
||||
if(CAN.pinMode(MCP_RX1BF, MCP_PIN_OUT))
|
||||
if(CAN.mcpPinMode(MCP_RX1BF, MCP_PIN_OUT))
|
||||
{
|
||||
Serial.println("RX1BF is now an output");
|
||||
}
|
||||
@@ -41,12 +41,12 @@ void setup()
|
||||
void loop()
|
||||
{
|
||||
Serial.println("10");
|
||||
CAN.digitalWrite(MCP_RX0BF, HIGH);
|
||||
CAN.digitalWrite(MCP_RX1BF, LOW);
|
||||
CAN.mcpDigitalWrite(MCP_RX0BF, HIGH);
|
||||
CAN.mcpDigitalWrite(MCP_RX1BF, LOW);
|
||||
delay(500);
|
||||
Serial.println("01");
|
||||
CAN.digitalWrite(MCP_RX0BF, LOW);
|
||||
CAN.digitalWrite(MCP_RX1BF, HIGH);
|
||||
CAN.mcpDigitalWrite(MCP_RX0BF, LOW);
|
||||
CAN.mcpDigitalWrite(MCP_RX1BF, HIGH);
|
||||
delay(500);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
// demo: CAN Sleep Example - receive
|
||||
// by Kai, based on the receive_interrupt example by loovee and the additions from Zak Kemble (https://github.com/coryjfowler/MCP_CAN_lib/pull/10/files)
|
||||
//
|
||||
// By setting the MCU, the CAN controller (MCP2515) and the transceiver (MCP2551) into sleep mode, you can reduce
|
||||
// the power consumption of the whole setup from around 50mA down to 240uA (Arduino directly connected to 5V, regulator and
|
||||
// power LED removed). The node will wake up when a new message arrives, process the message and go back to sleep
|
||||
// afterwards.
|
||||
//
|
||||
// Known issues:
|
||||
// - Because it takes some time for the controller to wake up, the first message is usually lost. Look at the
|
||||
// send_sleep example on how to avoid this by sending a special wakeup message before the normal message.
|
||||
// - If you only have 2 devices on the CAN bus (the device running this sketch and some other device sending
|
||||
// messages), you may find that duplicate messages are received when waking up. This is because when
|
||||
// the MCP2515 wakes up it enters LISTENONLY mode where it does not send ACKs to messages, so the transmitter
|
||||
// will retransmit the same message a few times. See below for a simple solution to filter duplicate messages out.
|
||||
|
||||
#include <SPI.h>
|
||||
#include "mcp_can.h"
|
||||
#include <avr/sleep.h>
|
||||
|
||||
// the cs pin of the version after v1.1 is default to D9
|
||||
// v0.9b and v1.0 is default D10
|
||||
const int SPI_CS_PIN = 9;
|
||||
#define CAN_INT 2 // Set INT to pin 2
|
||||
|
||||
// To use the sleep mode of the transceiver (MCP2551), it's Rs pin must be connected to either the MCP2515 or
|
||||
// any free Arduino output.
|
||||
#define RS_TO_MCP2515 true // Set this to false if Rs is connected to your Arduino
|
||||
#define RS_OUTPUT MCP_RX0BF // RX0BF is a pin of the MCP2515. You can also define an Arduino pin here
|
||||
|
||||
MCP_CAN CAN(SPI_CS_PIN); // Set CS pin
|
||||
|
||||
#define KEEP_AWAKE_TIME 200 // time the controller will stay awake after the last activity on the bus (in ms)
|
||||
unsigned long lastBusActivity = millis();
|
||||
|
||||
unsigned char flagRecv = 0;
|
||||
unsigned char len = 0;
|
||||
unsigned char buf[8];
|
||||
|
||||
int lastLen = -1;
|
||||
unsigned char lastBuf[8]; // used for duplicate message check below
|
||||
unsigned long lastMsgTime = 0;
|
||||
#define DUPLICATE_TIMEOUT 20
|
||||
|
||||
char str[20];
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
|
||||
while (CAN_OK != CAN.begin(CAN_500KBPS, MCP_16MHz)) // init can bus : baudrate = 500k
|
||||
{
|
||||
Serial.println("CAN BUS Shield init fail");
|
||||
Serial.println(" Init CAN BUS Shield again");
|
||||
delay(100);
|
||||
}
|
||||
Serial.println("CAN BUS Shield init ok!");
|
||||
|
||||
// attach interrupt
|
||||
pinMode(CAN_INT, INPUT);
|
||||
attachInterrupt(digitalPinToInterrupt(CAN_INT), MCP2515_ISR, FALLING);
|
||||
|
||||
CAN.setSleepWakeup(1); // this tells the MCP2515 to wake up on incoming messages
|
||||
|
||||
// Pull the Rs pin of the MCP2551 transceiver low to enable it:
|
||||
if(RS_TO_MCP2515)
|
||||
{
|
||||
CAN.mcpPinMode(MCP_RX0BF, MCP_PIN_OUT);
|
||||
CAN.mcpDigitalWrite(RS_OUTPUT, LOW);
|
||||
} else {
|
||||
pinMode(RS_OUTPUT, OUTPUT);
|
||||
digitalWrite(RS_OUTPUT, LOW);
|
||||
}
|
||||
}
|
||||
|
||||
void MCP2515_ISR()
|
||||
{
|
||||
flagRecv = 1;
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
if(flagRecv)
|
||||
{ // check if get data
|
||||
|
||||
flagRecv = 0; // clear flag
|
||||
lastBusActivity = millis();
|
||||
|
||||
// iterate over all pending messages
|
||||
// If either the bus is saturated or the MCU is busy,
|
||||
// both RX buffers may be in use and reading a single
|
||||
// message does not clear the IRQ conditon.
|
||||
while (CAN_MSGAVAIL == CAN.checkReceive())
|
||||
{
|
||||
// read data, len: data length, buf: data buf
|
||||
CAN.readMsgBuf(&len, buf);
|
||||
|
||||
// check if this is a duplicate message (including a timeout, so that the same message is accepted again after a while)
|
||||
if((len != lastLen) || (millis() > lastMsgTime + DUPLICATE_TIMEOUT) || (memcmp((const void *)lastBuf, (const void *)buf, sizeof(buf)) != 0))
|
||||
{
|
||||
lastLen = len;
|
||||
memcpy(lastBuf, buf, sizeof(buf));
|
||||
lastMsgTime = millis();
|
||||
|
||||
// print the data
|
||||
for(int i = 0; i<len; i++)
|
||||
{
|
||||
Serial.print(buf[i]);Serial.print("\t");
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
}
|
||||
} else if(millis() > lastBusActivity + KEEP_AWAKE_TIME)
|
||||
{
|
||||
// Put MCP2515 into sleep mode
|
||||
Serial.println(F("CAN sleep"));
|
||||
CAN.sleep();
|
||||
|
||||
// Put the transceiver into standby (by pulling Rs high):
|
||||
if(RS_TO_MCP2515)
|
||||
CAN.mcpDigitalWrite(RS_OUTPUT, HIGH);
|
||||
else
|
||||
digitalWrite(RS_OUTPUT, HIGH);
|
||||
|
||||
// Put the MCU to sleep
|
||||
Serial.println(F("MCU sleep"));
|
||||
|
||||
// Clear serial buffers before sleeping
|
||||
Serial.flush();
|
||||
|
||||
cli(); // Disable interrupts
|
||||
if(!flagRecv) // Make sure we havn't missed an interrupt between the check above and now. If an interrupt happens between now and sei()/sleep_cpu() then sleep_cpu() will immediately wake up again
|
||||
{
|
||||
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
|
||||
sleep_enable();
|
||||
sleep_bod_disable();
|
||||
sei();
|
||||
sleep_cpu();
|
||||
// Now the Arduino sleeps until the next message arrives...
|
||||
sleep_disable();
|
||||
}
|
||||
sei();
|
||||
|
||||
CAN.wake(); // When the MCP2515 wakes up it will be in LISTENONLY mode, here we put it into the mode it was before sleeping
|
||||
|
||||
// Wake up the transceiver:
|
||||
if(RS_TO_MCP2515)
|
||||
CAN.mcpDigitalWrite(RS_OUTPUT, LOW);
|
||||
else
|
||||
digitalWrite(RS_OUTPUT, LOW);
|
||||
|
||||
Serial.println(F("Woke up"));
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************************
|
||||
END FILE
|
||||
*********************************************************************************************************/
|
||||
@@ -0,0 +1,136 @@
|
||||
// demo: CAN Sleep Example - send
|
||||
// by Kai, based on the send example by loovee and the additions from Zak Kemble (https://github.com/coryjfowler/MCP_CAN_lib/pull/10/files)
|
||||
//
|
||||
// See receive_sleep example for additional notes.
|
||||
|
||||
#include <mcp_can.h>
|
||||
#include <SPI.h>
|
||||
#include <avr/sleep.h>
|
||||
#include <avr/wdt.h>
|
||||
|
||||
// the cs pin of the version after v1.1 is default to D9
|
||||
// v0.9b and v1.0 is default D10
|
||||
const int SPI_CS_PIN = 9;
|
||||
|
||||
// To use the sleep mode of the transceiver (MCP2551), it's Rs pin must be connected to either the MCP2515 or
|
||||
// any free Arduino output.
|
||||
#define RS_TO_MCP2515 true // Set this to false if Rs is connected to your Arduino
|
||||
#define RS_OUTPUT MCP_RX0BF // RX0BF is a pin of the MCP2515. You can also define an Arduino pin here
|
||||
|
||||
MCP_CAN CAN(SPI_CS_PIN); // Set CS pin
|
||||
|
||||
// Watchdog interrupt, used to wake the MCU periodically.
|
||||
ISR (WDT_vect)
|
||||
{
|
||||
wdt_disable(); // disable watchdog
|
||||
}
|
||||
|
||||
void sleepMCU()
|
||||
// Sleep the MCU for one second.
|
||||
// See http://www.gammon.com.au/power for details.
|
||||
// (You can make your life a lot easyer by using one of the numeral low power libraries for Arduino.)
|
||||
{
|
||||
// disable ADC
|
||||
ADCSRA = 0;
|
||||
|
||||
// clear various "reset" flags
|
||||
MCUSR = 0;
|
||||
// allow changes, disable reset
|
||||
WDTCSR = bit (WDCE) | bit (WDE);
|
||||
// set interrupt mode and an interval
|
||||
WDTCSR = bit (WDIE) | bit (WDP2) | bit (WDP1); // set WDIE, and 1 second delay
|
||||
wdt_reset(); // pat the dog
|
||||
|
||||
set_sleep_mode (SLEEP_MODE_PWR_DOWN);
|
||||
noInterrupts (); // timed sequence follows
|
||||
sleep_enable();
|
||||
|
||||
// turn off brown-out enable in software
|
||||
MCUCR = bit (BODS) | bit (BODSE);
|
||||
MCUCR = bit (BODS);
|
||||
interrupts (); // guarantees next instruction executed
|
||||
sleep_cpu ();
|
||||
|
||||
// cancel sleep as a precaution
|
||||
sleep_disable();
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
|
||||
while (CAN_OK != CAN.begin(CAN_500KBPS, MCP_16MHz)) // init can bus : baudrate = 500k
|
||||
{
|
||||
Serial.println("CAN BUS Shield init fail");
|
||||
Serial.println(" Init CAN BUS Shield again");
|
||||
delay(100);
|
||||
}
|
||||
Serial.println("CAN BUS Shield init ok!");
|
||||
|
||||
CAN.setSleepWakeup(0); // the MCP2515 will NOT wake up on incoming messages,
|
||||
// making it a 'send only' node
|
||||
|
||||
// Pull the Rs pin of the MCP2551 transceiver low to enable it:
|
||||
if(RS_TO_MCP2515)
|
||||
{
|
||||
CAN.mcpPinMode(MCP_RX0BF, MCP_PIN_OUT);
|
||||
CAN.mcpDigitalWrite(RS_OUTPUT, LOW);
|
||||
} else {
|
||||
pinMode(RS_OUTPUT, OUTPUT);
|
||||
digitalWrite(RS_OUTPUT, LOW);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char stmp[8] = {0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
void loop()
|
||||
{
|
||||
Serial.println("Sending message");
|
||||
|
||||
CAN.sendMsgBuf(0x00, 0, 0, NULL); // Send empty wakeup message
|
||||
|
||||
delay(100); // give the receiving node some time to wake up
|
||||
|
||||
|
||||
// send data: id = 0x00, standard frame, data len = 8, stmp: data buf
|
||||
stmp[7] = stmp[7]+1;
|
||||
if(stmp[7] == 100)
|
||||
{
|
||||
stmp[7] = 0;
|
||||
stmp[6] = stmp[6] + 1;
|
||||
|
||||
if(stmp[6] == 100)
|
||||
{
|
||||
stmp[6] = 0;
|
||||
stmp[5] = stmp[6] + 1;
|
||||
}
|
||||
}
|
||||
|
||||
CAN.sendMsgBuf(0x00, 0, 8, stmp);
|
||||
|
||||
|
||||
// sleep
|
||||
Serial.println("Sleep");
|
||||
Serial.flush();
|
||||
|
||||
// Put MCP2515 into sleep mode
|
||||
CAN.sleep();
|
||||
|
||||
// Put the transceiver into standby (by pulling Rs high):
|
||||
if(RS_TO_MCP2515)
|
||||
CAN.mcpDigitalWrite(RS_OUTPUT, HIGH);
|
||||
else
|
||||
digitalWrite(RS_OUTPUT, HIGH);
|
||||
|
||||
// Put the MCU to sleep for one second
|
||||
sleepMCU();
|
||||
|
||||
// wake MCP2515 and transceiver
|
||||
CAN.wake();
|
||||
if(RS_TO_MCP2515)
|
||||
CAN.mcpDigitalWrite(RS_OUTPUT, LOW);
|
||||
else
|
||||
digitalWrite(RS_OUTPUT, LOW);
|
||||
}
|
||||
|
||||
// END FILE
|
||||
+377
-14
@@ -298,25 +298,120 @@ byte MCP_CAN::mcp2515_readStatus(void)
|
||||
return i;
|
||||
}
|
||||
|
||||
/*********************************************************************************************************
|
||||
** Function name: setSleepWakeup
|
||||
** Descriptions: Enable or disable the wake up interrupt (If disabled the MCP2515 will not be woken up by CAN bus activity)
|
||||
*********************************************************************************************************/
|
||||
void MCP_CAN::setSleepWakeup(const byte enable)
|
||||
{
|
||||
mcp2515_modifyRegister(MCP_CANINTE, MCP_WAKIF, enable ? MCP_WAKIF : 0);
|
||||
}
|
||||
|
||||
/*********************************************************************************************************
|
||||
** Function name: sleep
|
||||
** Descriptions: Put mcp2515 in sleep mode to save power
|
||||
*********************************************************************************************************/
|
||||
byte MCP_CAN::sleep()
|
||||
{
|
||||
if(getMode() != MODE_SLEEP)
|
||||
return mcp2515_setCANCTRL_Mode(MODE_SLEEP);
|
||||
else
|
||||
return CAN_OK;
|
||||
}
|
||||
|
||||
/*********************************************************************************************************
|
||||
** Function name: wake
|
||||
** Descriptions: wake MCP2515 manually from sleep. It will come back in the mode it was before sleeping.
|
||||
*********************************************************************************************************/
|
||||
byte MCP_CAN::wake()
|
||||
{
|
||||
byte currMode = getMode();
|
||||
if(currMode != mcpMode)
|
||||
return mcp2515_setCANCTRL_Mode(mcpMode);
|
||||
else
|
||||
return CAN_OK;
|
||||
}
|
||||
|
||||
/*********************************************************************************************************
|
||||
** Function name: setMode
|
||||
** Descriptions: Sets control mode
|
||||
*********************************************************************************************************/
|
||||
byte MCP_CAN::setMode(const byte opMode)
|
||||
{
|
||||
if(opMode != MODE_SLEEP) // if going to sleep, the value stored in opMode is not changed so that we can return to it later
|
||||
mcpMode = opMode;
|
||||
return mcp2515_setCANCTRL_Mode(opMode);
|
||||
}
|
||||
|
||||
/*********************************************************************************************************
|
||||
** Function name: getMode
|
||||
** Descriptions: Returns current control mode
|
||||
*********************************************************************************************************/
|
||||
byte MCP_CAN::getMode()
|
||||
{
|
||||
return mcp2515_readRegister(MCP_CANSTAT) & MODE_MASK;
|
||||
}
|
||||
|
||||
/*********************************************************************************************************
|
||||
** Function name: mcp2515_setCANCTRL_Mode
|
||||
** Descriptions: set control mode
|
||||
*********************************************************************************************************/
|
||||
byte MCP_CAN::mcp2515_setCANCTRL_Mode(const byte newmode)
|
||||
{
|
||||
byte i;
|
||||
// If the chip is asleep and we want to change mode then a manual wake needs to be done
|
||||
// This is done by setting the wake up interrupt flag
|
||||
// This undocumented trick was found at https://github.com/mkleemann/can/blob/master/can_sleep_mcp2515.c
|
||||
if((getMode()) == MODE_SLEEP && newmode != MODE_SLEEP)
|
||||
{
|
||||
// Make sure wake interrupt is enabled
|
||||
byte wakeIntEnabled = (mcp2515_readRegister(MCP_CANINTE) & MCP_WAKIF);
|
||||
if(!wakeIntEnabled)
|
||||
mcp2515_modifyRegister(MCP_CANINTE, MCP_WAKIF, MCP_WAKIF);
|
||||
|
||||
mcp2515_modifyRegister(MCP_CANCTRL, MODE_MASK, newmode);
|
||||
// Set wake flag (this does the actual waking up)
|
||||
mcp2515_modifyRegister(MCP_CANINTF, MCP_WAKIF, MCP_WAKIF);
|
||||
|
||||
i = mcp2515_readRegister(MCP_CANCTRL);
|
||||
i &= MODE_MASK;
|
||||
// Wait for the chip to exit SLEEP and enter LISTENONLY mode.
|
||||
|
||||
if ( i == newmode )
|
||||
{
|
||||
return MCP2515_OK;
|
||||
}
|
||||
// If the chip is not connected to a CAN bus (or the bus has no other powered nodes) it will sometimes trigger the wake interrupt as soon
|
||||
// as it's put to sleep, but it will stay in SLEEP mode instead of automatically switching to LISTENONLY mode.
|
||||
// In this situation the mode needs to be manually set to LISTENONLY.
|
||||
|
||||
return MCP2515_FAIL;
|
||||
if(mcp2515_requestNewMode(MODE_LISTENONLY) != MCP2515_OK)
|
||||
return MCP2515_FAIL;
|
||||
|
||||
// Turn wake interrupt back off if it was originally off
|
||||
if(!wakeIntEnabled)
|
||||
mcp2515_modifyRegister(MCP_CANINTE, MCP_WAKIF, 0);
|
||||
}
|
||||
|
||||
// Clear wake flag
|
||||
mcp2515_modifyRegister(MCP_CANINTF, MCP_WAKIF, 0);
|
||||
|
||||
return mcp2515_requestNewMode(newmode);
|
||||
}
|
||||
|
||||
/*********************************************************************************************************
|
||||
** Function name: mcp2515_requestNewMode
|
||||
** Descriptions: Set control mode
|
||||
*********************************************************************************************************/
|
||||
byte MCP_CAN::mcp2515_requestNewMode(const byte newmode)
|
||||
{
|
||||
unsigned long startTime = millis();
|
||||
|
||||
// Spam new mode request and wait for the operation to complete
|
||||
while(1)
|
||||
{
|
||||
// Request new mode
|
||||
// This is inside the loop as sometimes requesting the new mode once doesn't work (usually when attempting to sleep)
|
||||
mcp2515_modifyRegister(MCP_CANCTRL, MODE_MASK, newmode);
|
||||
|
||||
byte statReg = mcp2515_readRegister(MCP_CANSTAT);
|
||||
if((statReg & MODE_MASK) == newmode) // We're now in the new mode
|
||||
return MCP2515_OK;
|
||||
else if((millis() - startTime) > 200) // Wait no more than 200ms for the operation to complete
|
||||
return MCP2515_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************************
|
||||
@@ -640,7 +735,7 @@ byte MCP_CAN::mcp2515_init(const byte canSpeed, const byte clock)
|
||||
MCP_RXB_RX_STDEXT);
|
||||
#endif
|
||||
// enter normal mode
|
||||
res = mcp2515_setCANCTRL_Mode(MODE_NORMAL);
|
||||
res = setMode(MODE_NORMAL);
|
||||
if (res)
|
||||
{
|
||||
#if DEBUG_EN
|
||||
@@ -867,7 +962,7 @@ MCP_CAN::MCP_CAN(byte _CS) : nReservedTx(0)
|
||||
}
|
||||
|
||||
/*********************************************************************************************************
|
||||
** Function name: set CS
|
||||
** Function name: init_CS
|
||||
** Descriptions: init CS pin and set UNSELECTED
|
||||
*********************************************************************************************************/
|
||||
void MCP_CAN::init_CS(byte _CS)
|
||||
@@ -886,7 +981,8 @@ byte MCP_CAN::begin(byte speedset, const byte clockset)
|
||||
{
|
||||
pSPI->begin();
|
||||
byte res = mcp2515_init(speedset, clockset);
|
||||
return ((res == MCP2515_OK) ? CAN_OK : CAN_FAILINIT);
|
||||
|
||||
return ((res == MCP2515_OK) ? CAN_OK : CAN_FAILINIT);
|
||||
}
|
||||
|
||||
/*********************************************************************************************************
|
||||
@@ -937,7 +1033,7 @@ byte MCP_CAN::init_Mask(byte num, byte ext, unsigned long ulData)
|
||||
}
|
||||
else res = MCP2515_FAIL;
|
||||
|
||||
res = mcp2515_setCANCTRL_Mode(MODE_NORMAL);
|
||||
res = mcp2515_setCANCTRL_Mode(mcpMode);
|
||||
if (res > 0) {
|
||||
#if DEBUG_EN
|
||||
Serial.print("Enter normal mode fall\r\n");
|
||||
@@ -1007,7 +1103,7 @@ byte MCP_CAN::init_Filt(byte num, byte ext, unsigned long ulData)
|
||||
res = MCP2515_FAIL;
|
||||
}
|
||||
|
||||
res = mcp2515_setCANCTRL_Mode(MODE_NORMAL);
|
||||
res = mcp2515_setCANCTRL_Mode(mcpMode);
|
||||
if (res > 0)
|
||||
{
|
||||
#if DEBUG_EN
|
||||
@@ -1297,6 +1393,273 @@ byte MCP_CAN::isExtendedFrame(void)
|
||||
return ext_flg;
|
||||
}
|
||||
|
||||
/*********************************************************************************************************
|
||||
** Function name: mcpPinMode
|
||||
** Descriptions: switch supported pins between HiZ, interrupt, output or input
|
||||
*********************************************************************************************************/
|
||||
bool MCP_CAN::mcpPinMode(const byte pin, const byte mode)
|
||||
{
|
||||
byte res;
|
||||
bool ret=true;
|
||||
|
||||
switch(pin)
|
||||
{
|
||||
case MCP_RX0BF:
|
||||
switch(mode) {
|
||||
case MCP_PIN_HIZ:
|
||||
mcp2515_modifyRegister(MCP_BFPCTRL, B0BFE, 0);
|
||||
break;
|
||||
case MCP_PIN_INT:
|
||||
mcp2515_modifyRegister(MCP_BFPCTRL, B0BFM | B0BFE, B0BFM | B0BFE);
|
||||
break;
|
||||
case MCP_PIN_OUT:
|
||||
mcp2515_modifyRegister(MCP_BFPCTRL, B0BFM | B0BFE, B0BFE);
|
||||
break;
|
||||
default:
|
||||
#if DEBUG_EN
|
||||
Serial.print("Invalid pin mode request\r\n");
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
break;
|
||||
case MCP_RX1BF:
|
||||
switch(mode) {
|
||||
case MCP_PIN_HIZ:
|
||||
mcp2515_modifyRegister(MCP_BFPCTRL, B1BFE, 0);
|
||||
break;
|
||||
case MCP_PIN_INT:
|
||||
mcp2515_modifyRegister(MCP_BFPCTRL, B1BFM | B1BFE, B1BFM | B1BFE);
|
||||
break;
|
||||
case MCP_PIN_OUT:
|
||||
mcp2515_modifyRegister(MCP_BFPCTRL, B1BFM | B1BFE, B1BFE);
|
||||
break;
|
||||
default:
|
||||
#if DEBUG_EN
|
||||
Serial.print("Invalid pin mode request\r\n");
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
break;
|
||||
case MCP_TX0RTS:
|
||||
res = mcp2515_setCANCTRL_Mode(MODE_CONFIG);
|
||||
if(res > 0)
|
||||
{
|
||||
#if DEBUG_EN
|
||||
Serial.print("Entering Configuration Mode Failure...\r\n");
|
||||
#else
|
||||
delay(10);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
switch(mode) {
|
||||
case MCP_PIN_INT:
|
||||
mcp2515_modifyRegister(MCP_TXRTSCTRL, B0RTSM, B0RTSM);
|
||||
break;
|
||||
case MCP_PIN_IN:
|
||||
mcp2515_modifyRegister(MCP_TXRTSCTRL, B0RTSM, 0);
|
||||
break;
|
||||
default:
|
||||
#if DEBUG_EN
|
||||
Serial.print("Invalid pin mode request\r\n");
|
||||
#endif
|
||||
ret=false;
|
||||
}
|
||||
res = mcp2515_setCANCTRL_Mode(mcpMode);
|
||||
if(res)
|
||||
{
|
||||
#if DEBUG_EN
|
||||
Serial.print("`Setting ID Mode Failure...\r\n");
|
||||
#else
|
||||
delay(10);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
return ret;
|
||||
break;
|
||||
case MCP_TX1RTS:
|
||||
res = mcp2515_setCANCTRL_Mode(MODE_CONFIG);
|
||||
if(res > 0)
|
||||
{
|
||||
#if DEBUG_EN
|
||||
Serial.print("Entering Configuration Mode Failure...\r\n");
|
||||
#else
|
||||
delay(10);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
switch(mode) {
|
||||
case MCP_PIN_INT:
|
||||
mcp2515_modifyRegister(MCP_TXRTSCTRL, B1RTSM, B1RTSM);
|
||||
break;
|
||||
case MCP_PIN_IN:
|
||||
mcp2515_modifyRegister(MCP_TXRTSCTRL, B1RTSM, 0);
|
||||
break;
|
||||
default:
|
||||
#if DEBUG_EN
|
||||
Serial.print("Invalid pin mode request\r\n");
|
||||
#endif
|
||||
ret=false;
|
||||
}
|
||||
res = mcp2515_setCANCTRL_Mode(mcpMode);
|
||||
if(res)
|
||||
{
|
||||
#if DEBUG_EN
|
||||
Serial.print("`Setting ID Mode Failure...\r\n");
|
||||
#else
|
||||
delay(10);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
return ret;
|
||||
break;
|
||||
case MCP_TX2RTS:
|
||||
res = mcp2515_setCANCTRL_Mode(MODE_CONFIG);
|
||||
if(res > 0)
|
||||
{
|
||||
#if DEBUG_EN
|
||||
Serial.print("Entering Configuration Mode Failure...\r\n");
|
||||
#else
|
||||
delay(10);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
switch(mode) {
|
||||
case MCP_PIN_INT:
|
||||
mcp2515_modifyRegister(MCP_TXRTSCTRL, B2RTSM, B2RTSM);
|
||||
break;
|
||||
case MCP_PIN_IN:
|
||||
mcp2515_modifyRegister(MCP_TXRTSCTRL, B2RTSM, 0);
|
||||
break;
|
||||
default:
|
||||
#if DEBUG_EN
|
||||
Serial.print("Invalid pin mode request\r\n");
|
||||
#endif
|
||||
ret=false;
|
||||
}
|
||||
res = mcp2515_setCANCTRL_Mode(mcpMode);
|
||||
if(res)
|
||||
{
|
||||
#if DEBUG_EN
|
||||
Serial.print("`Setting ID Mode Failure...\r\n");
|
||||
#else
|
||||
delay(10);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
return ret;
|
||||
break;
|
||||
default:
|
||||
#if DEBUG_EN
|
||||
Serial.print("Invalid pin for mode request\r\n");
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************************
|
||||
** Function name: mcpDigitalWrite
|
||||
** Descriptions: write HIGH or LOW to RX0BF/RX1BF
|
||||
*********************************************************************************************************/
|
||||
bool MCP_CAN::mcpDigitalWrite(const byte pin, const byte mode) {
|
||||
switch(pin)
|
||||
{
|
||||
case MCP_RX0BF:
|
||||
switch(mode) {
|
||||
case HIGH:
|
||||
mcp2515_modifyRegister(MCP_BFPCTRL, B0BFS, B0BFS);
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
mcp2515_modifyRegister(MCP_BFPCTRL, B0BFS, 0);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case MCP_RX1BF:
|
||||
switch(mode) {
|
||||
case HIGH:
|
||||
mcp2515_modifyRegister(MCP_BFPCTRL, B1BFS, B1BFS);
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
mcp2515_modifyRegister(MCP_BFPCTRL, B1BFS, 0);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
#if DEBUG_EN
|
||||
Serial.print("Invalid pin for mcpDigitalWrite\r\n");
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************************
|
||||
** Function name: mcpDigitalRead
|
||||
** Descriptions: read HIGH or LOW from supported pins
|
||||
*********************************************************************************************************/
|
||||
byte MCP_CAN::mcpDigitalRead(const byte pin) {
|
||||
switch(pin)
|
||||
{
|
||||
case MCP_RX0BF:
|
||||
if((mcp2515_readRegister(MCP_BFPCTRL) & B0BFS) > 0)
|
||||
{
|
||||
return HIGH;
|
||||
}
|
||||
else
|
||||
{
|
||||
return LOW;
|
||||
}
|
||||
break;
|
||||
case MCP_RX1BF:
|
||||
if((mcp2515_readRegister(MCP_BFPCTRL) & B1BFS) > 0)
|
||||
{
|
||||
return HIGH;
|
||||
}
|
||||
else
|
||||
{
|
||||
return LOW;
|
||||
}
|
||||
break;
|
||||
case MCP_TX0RTS:
|
||||
if((mcp2515_readRegister(MCP_TXRTSCTRL) & B0RTS) > 0)
|
||||
{
|
||||
return HIGH;
|
||||
}
|
||||
else
|
||||
{
|
||||
return LOW;
|
||||
}
|
||||
break;
|
||||
case MCP_TX1RTS:
|
||||
if((mcp2515_readRegister(MCP_TXRTSCTRL) & B1RTS) > 0)
|
||||
{
|
||||
return HIGH;
|
||||
}
|
||||
else
|
||||
{
|
||||
return LOW;
|
||||
}
|
||||
break;
|
||||
case MCP_TX2RTS:
|
||||
if((mcp2515_readRegister(MCP_TXRTSCTRL) & B2RTS) > 0)
|
||||
{
|
||||
return HIGH;
|
||||
}
|
||||
else
|
||||
{
|
||||
return LOW;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
#if DEBUG_EN
|
||||
Serial.print("Invalid pin for mcpDigitalRead\r\n");
|
||||
#endif
|
||||
return LOW;
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************************
|
||||
END FILE
|
||||
*********************************************************************************************************/
|
||||
|
||||
@@ -62,6 +62,7 @@ class MCP_CAN
|
||||
byte SPICS;
|
||||
SPIClass *pSPI;
|
||||
byte nReservedTx; // Count of tx buffers for reserved send
|
||||
byte mcpMode; // Current controller mode
|
||||
|
||||
/*
|
||||
* mcp2515 driver function
|
||||
@@ -91,6 +92,7 @@ private:
|
||||
|
||||
byte mcp2515_readStatus(void); // read mcp2515's Status
|
||||
byte mcp2515_setCANCTRL_Mode(const byte newmode); // set mode
|
||||
byte mcp2515_requestNewMode(const byte newmode); // Set mode
|
||||
byte mcp2515_configRate(const byte canSpeed, const byte clock); // set baudrate
|
||||
byte mcp2515_init(const byte canSpeed, const byte clock); // mcp2515init
|
||||
|
||||
@@ -125,7 +127,12 @@ public:
|
||||
byte begin(byte speedset, const byte clockset = MCP_16MHz); // init can
|
||||
byte init_Mask(byte num, byte ext, unsigned long ulData); // init Masks
|
||||
byte init_Filt(byte num, byte ext, unsigned long ulData); // init filters
|
||||
byte sendMsgBuf(unsigned long id, byte ext, byte rtrBit, byte len, const byte *buf, bool wait_sent=true); // send buf
|
||||
void setSleepWakeup(byte enable); // Enable or disable the wake up interrupt (If disabled the MCP2515 will not be woken up by CAN bus activity, making it send only)
|
||||
byte sleep(); // Put the MCP2515 in sleep mode
|
||||
byte wake(); // Wake MCP2515 manually from sleep
|
||||
byte setMode(byte opMode); // Set operational mode
|
||||
byte getMode(); // Get operational mode
|
||||
byte sendMsgBuf(unsigned long id, byte ext, byte rtrBit, byte len, const byte *buf, bool wait_sent=true); // send buf
|
||||
byte sendMsgBuf(unsigned long id, byte ext, byte len, const byte *buf, bool wait_sent=true); // send buf
|
||||
byte readMsgBuf(byte *len, byte *buf); // read buf
|
||||
byte readMsgBufID(unsigned long *ID, byte *len, byte *buf); // read buf with object ID
|
||||
@@ -148,6 +155,10 @@ public:
|
||||
byte readRxTxStatus(void); // read has something send or received
|
||||
byte checkClearRxStatus(byte *status); // read and clear and return first found rx status bit
|
||||
byte checkClearTxStatus(byte *status, byte iTxBuf=0xff); // read and clear and return first found or buffer specified tx status bit
|
||||
|
||||
bool mcpPinMode(const byte pin, const byte mode); // switch supported pins between HiZ, interrupt, output or input
|
||||
bool mcpDigitalWrite(const byte pin, const byte mode); // write HIGH or LOW to RX0BF/RX1BF
|
||||
byte mcpDigitalRead(const byte pin); // read HIGH or LOW from supported pins
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -126,6 +126,8 @@
|
||||
#define MCP_RXF2SIDL 0x09
|
||||
#define MCP_RXF2EID8 0x0A
|
||||
#define MCP_RXF2EID0 0x0B
|
||||
#define MCP_BFPCTRL 0x0C
|
||||
#define MCP_TXRTSCTRL 0x0D
|
||||
#define MCP_CANSTAT 0x0E
|
||||
#define MCP_CANCTRL 0x0F
|
||||
#define MCP_RXF3SIDH 0x10
|
||||
@@ -249,6 +251,24 @@
|
||||
#define MCP_WAKIF 0x40
|
||||
#define MCP_MERRF 0x80
|
||||
|
||||
// BFPCTRL Register Bits
|
||||
|
||||
#define B1BFS 0x20
|
||||
#define B0BFS 0x10
|
||||
#define B1BFE 0x08
|
||||
#define B0BFE 0x04
|
||||
#define B1BFM 0x02
|
||||
#define B0BFM 0x01
|
||||
|
||||
// TXRTCTRL Register Bits
|
||||
|
||||
#define B2RTS 0x20
|
||||
#define B1RTS 0x10
|
||||
#define B0RTS 0x08
|
||||
#define B2RTSM 0x04
|
||||
#define B1RTSM 0x02
|
||||
#define B0RTSM 0x01
|
||||
|
||||
// clock
|
||||
|
||||
#define MCP_16MHz 1
|
||||
@@ -403,6 +423,17 @@
|
||||
|
||||
#define CANSENDTIMEOUT (200) // milliseconds
|
||||
|
||||
#define MCP_PIN_HIZ (0)
|
||||
#define MCP_PIN_INT (1)
|
||||
#define MCP_PIN_OUT (2)
|
||||
#define MCP_PIN_IN (3)
|
||||
|
||||
#define MCP_RX0BF (0)
|
||||
#define MCP_RX1BF (1)
|
||||
#define MCP_TX0RTS (2)
|
||||
#define MCP_TX1RTS (3)
|
||||
#define MCP_TX2RTS (4)
|
||||
|
||||
|
||||
// initial value of gCANAutoProcess
|
||||
|
||||
|
||||
Reference in New Issue
Block a user