diff --git a/API.md b/API.md index cbe9cac..0232c2a 100644 --- a/API.md +++ b/API.md @@ -247,6 +247,18 @@ if ((packetId & mask) == id) { Returns `1` on success, `0` on failure. +The MCP2515 implementation also supports setting mask/filter registers directly +for more advanced usage: + +``` +CAN.setFilterRegisters( + mask0, filter0, filter1, + mask1, filter2, filter3, filter4, filter5, + allowRollover); +``` + +Please see the MCP2515 data sheet for more details on how these work. + ## Other modes ### Loopback mode diff --git a/src/MCP2515.cpp b/src/MCP2515.cpp index aa46051..015be2e 100644 --- a/src/MCP2515.cpp +++ b/src/MCP2515.cpp @@ -21,10 +21,11 @@ #define FLAG_RXnIF(n) (0x01 << n) #define FLAG_TXnIF(n) (0x04 << n) -#define REG_RXFnSIDH(n) (0x00 + (n * 4)) -#define REG_RXFnSIDL(n) (0x01 + (n * 4)) -#define REG_RXFnEID8(n) (0x02 + (n * 4)) -#define REG_RXFnEID0(n) (0x03 + (n * 4)) +// There is a 4-register gap between RXF2EID0 and RXF3SIDH. +#define REG_RXFnSIDH(n) (0x00 + ((n + (n >= 3)) * 4)) +#define REG_RXFnSIDL(n) (0x01 + ((n + (n >= 3)) * 4)) +#define REG_RXFnEID8(n) (0x02 + ((n + (n >= 3)) * 4)) +#define REG_RXFnEID0(n) (0x03 + ((n + (n >= 3)) * 4)) #define REG_RXMnSIDH(n) (0x20 + (n * 0x04)) #define REG_RXMnSIDL(n) (0x21 + (n * 0x04)) @@ -51,6 +52,7 @@ #define FLAG_SRR 0x10 #define FLAG_RTR 0x40 #define FLAG_EXIDE 0x08 +#define FLAG_RXB0CTRL_BUKT 0x04 #define FLAG_RXM0 0x20 #define FLAG_RXM1 0x40 @@ -288,6 +290,10 @@ int MCP2515Class::filter(int id, int mask) for (int n = 0; n < 2; n++) { // standard only + // TODO: This doesn't look correct. According to the datasheet, the RXM0 and + // RMX1 should either both be unset (in which case filters are active), or + // both be unset (in which case all filters are ignored). + // Either way, it's unclear why we write to the same register twice here. writeRegister(REG_RXBnCTRL(n), FLAG_RXM0); writeRegister(REG_RXBnCTRL(n), FLAG_RXM0); @@ -313,6 +319,55 @@ int MCP2515Class::filter(int id, int mask) return 1; } +boolean MCP2515Class::setFilterRegisters( + uint16_t mask0, uint16_t filter0, uint16_t filter1, + uint16_t mask1, uint16_t filter2, uint16_t filter3, uint16_t filter4, uint16_t filter5, + bool allowRollover) +{ + mask0 &= 0x7ff; + filter0 &= 0x7ff; + filter1 &= 0x7ff; + mask1 &= 0x7ff; + filter2 &= 0x7ff; + filter3 &= 0x7ff; + filter4 &= 0x7ff; + filter5 &= 0x7ff; + + // config mode + writeRegister(REG_CANCTRL, 0x80); + if (readRegister(REG_CANCTRL) != 0x80) { + return false; + } + + writeRegister(REG_RXBnCTRL(0), allowRollover ? FLAG_RXB0CTRL_BUKT : 0); + writeRegister(REG_RXBnCTRL(1), 0); + for (int n = 0; n < 2; n++) { + uint8_t mask = (n == 0) ? mask0 : mask1; + writeRegister(REG_RXMnSIDH(n), mask >> 3); + writeRegister(REG_RXMnSIDL(n), mask << 5); + writeRegister(REG_RXMnEID8(n), 0); + writeRegister(REG_RXMnEID0(n), 0); + } + + uint8_t filter_array[6] = + {filter0, filter1, filter2, filter3, filter4, filter5}; + for (int n = 0; n < 6; n++) { + uint8_t id = filter_array[n]; + writeRegister(REG_RXFnSIDH(n), id >> 3); + writeRegister(REG_RXFnSIDL(n), id << 5); + writeRegister(REG_RXFnEID8(n), 0); + writeRegister(REG_RXFnEID0(n), 0); + } + + // normal mode + writeRegister(REG_CANCTRL, 0x00); + if (readRegister(REG_CANCTRL) != 0x00) { + return false; + } + + return true; +} + int MCP2515Class::filterExtended(long id, long mask) { id &= 0x1FFFFFFF; @@ -326,6 +381,10 @@ int MCP2515Class::filterExtended(long id, long mask) for (int n = 0; n < 2; n++) { // extended only + // TODO: This doesn't look correct. According to the datasheet, the RXM0 and + // RMX1 should either both be unset (in which case filters are active), or + // both be unset (in which case all filters are ignored). + // Either way, it's unclear why we write to the same register twice here. writeRegister(REG_RXBnCTRL(n), FLAG_RXM1); writeRegister(REG_RXBnCTRL(n), FLAG_RXM1); diff --git a/src/MCP2515.h b/src/MCP2515.h index 2f0444f..cffaaf2 100644 --- a/src/MCP2515.h +++ b/src/MCP2515.h @@ -38,8 +38,21 @@ class MCP2515Class : public CANControllerClass { using CANControllerClass::filter; virtual int filter(int id, int mask); + + // mask0 is applied to filter0 and filter1 for RXB0. + // mask1 is applied to filter2, filter3, filter4, filter5 for RXB1. + // allowRollover controls whether messages that would otherwise overflow RXB0 + // should be put in RXB1 instead. + // + // See the MCP2515 datasheet for more info. + boolean setFilterRegisters( + uint16_t mask0, uint16_t filter0, uint16_t filter1, + uint16_t mask1, uint16_t filter2, uint16_t filter3, uint16_t filter4, uint16_t filter5, + bool allowRollover); + using CANControllerClass::filterExtended; virtual int filterExtended(long id, long mask); + // TODO: add setFilterRegistersExtended(). virtual int observe(); virtual int loopback();