diff --git a/src/CANController.cpp b/src/CANController.cpp index 0890eec..b503b18 100644 --- a/src/CANController.cpp +++ b/src/CANController.cpp @@ -5,8 +5,9 @@ CANControllerClass::CANControllerClass() : _onReceive(NULL), + _onError(NULL), - _packetBegun(false), + _frameBegun(false), _txId(-1), _txExtended(-1), _txRtr(false), @@ -30,7 +31,7 @@ CANControllerClass::~CANControllerClass() int CANControllerClass::begin(long /*baudRate*/) { - _packetBegun = false; + _frameBegun = false; _txId = -1; _txRtr =false; _txDlc = 0; @@ -49,7 +50,7 @@ void CANControllerClass::end() { } -int CANControllerClass::beginPacket(int id, int dlc, bool rtr) +int CANControllerClass::beginFrame(int id, int dlc, bool rtr) { if (id < 0 || id > 0x7FF) { return 0; @@ -59,7 +60,7 @@ int CANControllerClass::beginPacket(int id, int dlc, bool rtr) return 0; } - _packetBegun = true; + _frameBegun = true; _txId = id; _txExtended = false; _txRtr = rtr; @@ -71,7 +72,7 @@ int CANControllerClass::beginPacket(int id, int dlc, bool rtr) return 1; } -int CANControllerClass::beginExtendedPacket(long id, int dlc, bool rtr) +int CANControllerClass::beginExtendedFrame(long id, int dlc, bool rtr) { if (id < 0 || id > 0x1FFFFFFF) { return 0; @@ -81,7 +82,7 @@ int CANControllerClass::beginExtendedPacket(long id, int dlc, bool rtr) return 0; } - _packetBegun = true; + _frameBegun = true; _txId = id; _txExtended = true; _txRtr = rtr; @@ -93,12 +94,12 @@ int CANControllerClass::beginExtendedPacket(long id, int dlc, bool rtr) return 1; } -int CANControllerClass::endPacket() +int CANControllerClass::endFrame() { - if (!_packetBegun) { + if (!_frameBegun) { return 0; } - _packetBegun = false; + _frameBegun = false; if (_txDlc >= 0) { _txLength = _txDlc; @@ -107,27 +108,27 @@ int CANControllerClass::endPacket() return 1; } -int CANControllerClass::parsePacket() +int CANControllerClass::parseFrame() { return 0; } -long CANControllerClass::packetId() +long CANControllerClass::frameId() { return _rxId; } -bool CANControllerClass::packetExtended() +bool CANControllerClass::frameExtended() { return _rxExtended; } -bool CANControllerClass::packetRtr() +bool CANControllerClass::frameRtr() { return _rxRtr; } -int CANControllerClass::packetDlc() +int CANControllerClass::frameDlc() { return _rxDlc; } @@ -139,7 +140,7 @@ size_t CANControllerClass::write(uint8_t byte) size_t CANControllerClass::write(const uint8_t *buffer, size_t size) { - if (!_packetBegun) { + if (!_frameBegun) { return 0; } @@ -180,11 +181,16 @@ void CANControllerClass::flush() { } -void CANControllerClass::onReceive(void(*callback)(int)) +void CANControllerClass::onReceive(TCallback callback) { _onReceive = callback; } +void CANControllerClass::onError(TErrorCallback callback) +{ + _onError = callback; +} + int CANControllerClass::filter(int /*id*/, int /*mask*/) { return 0; diff --git a/src/CANController.h b/src/CANController.h index cdaeb94..798e23c 100644 --- a/src/CANController.h +++ b/src/CANController.h @@ -6,21 +6,26 @@ #include +// typedef std::function TCallback; +// typedef std::function TErrorCallback; +typedef void (*TCallback)(int); +typedef void (*TErrorCallback)(int, int); + class CANControllerClass : public Stream { public: virtual int begin(long baudRate); virtual void end(); - int beginPacket(int id, int dlc = -1, bool rtr = false); - int beginExtendedPacket(long id, int dlc = -1, bool rtr = false); - virtual int endPacket(); + int beginFrame(int id, int dlc = -1, bool rtr = false); + int beginExtendedFrame(long id, int dlc = -1, bool rtr = false); + virtual int endFrame(); - virtual int parsePacket(); - long packetId(); - bool packetExtended(); - bool packetRtr(); - int packetDlc(); + virtual int parseFrame(); + long frameId(); + bool frameExtended(); + bool frameRtr(); + int frameDlc(); // from Print virtual size_t write(uint8_t byte); @@ -32,7 +37,8 @@ class CANControllerClass : public Stream { virtual int peek(); virtual void flush(); - virtual void onReceive(void(*callback)(int)); + void onReceive(TCallback callback); + void onError(TErrorCallback callback); virtual int filter(int id) { return filter(id, 0x7ff); } virtual int filter(int id, int mask); @@ -49,9 +55,10 @@ class CANControllerClass : public Stream { virtual ~CANControllerClass(); protected: - void (*_onReceive)(int); + TCallback _onReceive; + TErrorCallback _onError; - bool _packetBegun; + bool _frameBegun; long _txId; bool _txExtended; bool _txRtr; diff --git a/src/ESP32SJA1000.cpp b/src/ESP32SJA1000.cpp index 309e030..ee01f87 100644 --- a/src/ESP32SJA1000.cpp +++ b/src/ESP32SJA1000.cpp @@ -7,6 +7,11 @@ #include "soc/dport_reg.h" #include "driver/gpio.h" +// These are to read the APB clock speed +#include +#include +#include + #include "ESP32SJA1000.h" #define REG_BASE 0x3ff6b000 @@ -31,8 +36,13 @@ #define REG_ACRn(n) (0x10 + n) #define REG_AMRn(n) (0x14 + n) +#define REG_EIR 0x1C #define REG_CDR 0x1F +#define IR_EI 0x02 // Error Interrupt flag + +#define SR_TCS 0x08 // Transmit Complete Status +#define SR_TBS 0x04 // Transmit Buffer Status (free) ESP32SJA1000Class::ESP32SJA1000Class() : CANControllerClass(), @@ -67,73 +77,88 @@ int ESP32SJA1000Class::begin(long baudRate) gpio_pad_select_gpio(_txPin); modifyRegister(REG_CDR, 0x80, 0x80); // pelican mode - modifyRegister(REG_BTR0, 0xc0, 0x40); // SJW = 1 - modifyRegister(REG_BTR1, 0x70, 0x10); // TSEG2 = 1 + // Masks: + // 0xc0: bits 6-7 + // 0x3f: bits 0-5 + // + // 0x80: bit 7 + // 0x70: bits 4-6 + // 0x0f: bits 0-3 + // + // Validate with python: + // format(ord(chr(0xff & ~0x07 | 0x40)), 'b').zfill(8) + + // Final SJW, BRP, TSEG1, and TSEG2 values are regval + 1 + uint8_t sjw, brp, ts, tseg1, tseg2; + switch (baudRate) { - case (long)1000E3: - modifyRegister(REG_BTR1, 0x0f, 0x04); - modifyRegister(REG_BTR0, 0x3f, 4); - break; - case (long)500E3: - modifyRegister(REG_BTR1, 0x0f, 0x0c); - modifyRegister(REG_BTR0, 0x3f, 4); - break; - - case (long)250E3: - modifyRegister(REG_BTR1, 0x0f, 0x0c); - modifyRegister(REG_BTR0, 0x3f, 9); - break; - - case (long)200E3: - modifyRegister(REG_BTR1, 0x0f, 0x0c); - modifyRegister(REG_BTR0, 0x3f, 12); - break; - - case (long)125E3: - modifyRegister(REG_BTR1, 0x0f, 0x0c); - modifyRegister(REG_BTR0, 0x3f, 19); + // Target config is 500kbps, 75% sample point, triple sampling disabled + // ESP32 has an 80 Mhz APB clock (79998976 hz to be exact), + // 80E6 / 500E3 = 160 + // So theoretically, (BRP +1) * (1 + TSEG1+1 + TSEG2+1) must equal 160 + // The TWAI library 500k settings are brp=8, tseg_1=15, tseg_2=4, sjw=3, triple_sampling=false + // Which is 8 * (1 + 15 + 4) = 160 + // But that doesn't seem to work at all + // The ESP32-WROOM-32E datasheet says the crystal on this board is 40 Mhz + // The TJA1000 datasheet says the clock is 24 Mhz + // The clock divider is set to 0x00 + // The TJA1000 datasheet says CLKDIV 0x00 = 2 and 0x01 = 1 + // So let's do some experiments + + // 20 Mhz = 20E6/500E3 = 40 = 2x20; brp=2; 20x.75=15 (75% sample point); tseg1=15; 20-15=5; tseg2=5; + // Acks first scanner frame, sends response, scaner acks, triggers CAN error on next scanner tx bit + sjw = 2; ts = 0; brp = 2, tseg1 = 15, tseg2 = 5; + + // Same: sjw = 2; ts = 0; brp = 2, tseg1 = 14, tseg2 = 6; + // Nope: sjw = 2; ts = 0; brp = 2, tseg1 = 15, tseg2 = 6; + // sjw = 2; ts = 0; brp = 2, tseg1 = 16, tseg2 = 4; + + // 24 Mhz = 24E6/500E3 = 48 = 3x16; brp=3; 16x.75=12, tseg1=12; 16-12=4; tseg2=4; + // Triggers error on first scanner frame CRC + // sjw = 3; ts = 0; brp = 3, tseg1 = 12, tseg2 = 4; + + // 12 Mhz = 12E6/500E3 = 24 = 1x24; brp=1; 24*.75=18; tseg1=18; 24-18=6; tseg2=6; + // Triggers CAN error after first scanner tx bit + // sjw = 3; ts = 0; brp = 1, tseg1 = 18, tseg2 = 6; + + // 40 Mhz = 40E6/500E3 = 80 = 4x20; brp=4; 20x.75=15 (75% sample point); tseg1=15; 20-15=5; tseg2=5; + // Triggers error after first scanner tx data bit + // sjw = 3; ts = 0; brp = 4, tseg1 = 15, tseg2 = 5; + + // 80 Mhz = 80E6/500E3 = 160 = 8x20; brp=8; 20x.75=15; tseg1=15; 20-15=5; tseg2=5; + // Triggers error after first scanner tx data bit + // sjw = 3; ts = 0; brp = 8, tseg1 = 15, tseg2 = 5; + + modifyRegister(REG_BTR0, 0xc0, sjw -1 << 6); // SJW (bits 6-7) -1 because final value is regval+1 + modifyRegister(REG_BTR0, 0x3f, brp -1); // BRP (bits 0-5) -1 because final value is regval+1 + modifyRegister(REG_BTR1, 0x80, ts << 7); // Triple Sampling (bit 7) + modifyRegister(REG_BTR1, 0x70, tseg2 -1 << 4); // TSEG2 (bits 4-6) -1 because final value is regval+1 + modifyRegister(REG_BTR1, 0x0f, tseg1 -2); // TSEG1 (bits 0-3) -2 to account for regval+1 + SYNC bit break; - case (long)100E3: - modifyRegister(REG_BTR1, 0x0f, 0x0c); - modifyRegister(REG_BTR0, 0x3f, 24); - break; - - case (long)80E3: - modifyRegister(REG_BTR1, 0x0f, 0x0c); - modifyRegister(REG_BTR0, 0x3f, 30); - break; - - case (long)50E3: - modifyRegister(REG_BTR1, 0x0f, 0x0c); - modifyRegister(REG_BTR0, 0x3f, 49); - break; - -/* - Due to limitations in ESP32 hardware and/or RTOS software, baudrate can't be lower than 50kbps. - See https://esp32.com/viewtopic.php?t=2142 -*/ default: return 0; break; } - modifyRegister(REG_BTR1, 0x80, 0x80); // SAM = 1 writeRegister(REG_IER, 0xff); // enable all interrupts - // set filter to allow anything + // set filters to allow anything writeRegister(REG_ACRn(0), 0x00); writeRegister(REG_ACRn(1), 0x00); writeRegister(REG_ACRn(2), 0x00); writeRegister(REG_ACRn(3), 0x00); + writeRegister(REG_AMRn(0), 0xff); writeRegister(REG_AMRn(1), 0xff); writeRegister(REG_AMRn(2), 0xff); writeRegister(REG_AMRn(3), 0xff); - modifyRegister(REG_OCR, 0x03, 0x02); // normal output mode + // normal output mode + modifyRegister(REG_OCR, 0x03, 0x02); + // reset error counters writeRegister(REG_TXERR, 0x00); writeRegister(REG_RXERR, 0x00); @@ -142,8 +167,12 @@ int ESP32SJA1000Class::begin(long baudRate) readRegister(REG_ECC); readRegister(REG_IR); - // normal mode - modifyRegister(REG_MOD, 0x08, 0x08); + // 0x17 = 0b00010111 → mask for: + // Bit 0: RM (Reset Mode) + // Bit 1: LOM (Listen Only Mode) + // Bit 2: STM (Self-Test Mode) + // Bit 4: AFM (Acceptance Filter Mode) + // Value 0x00 sets them all to 0x00 modifyRegister(REG_MOD, 0x17, 0x00); return 1; @@ -162,16 +191,33 @@ void ESP32SJA1000Class::end() CANControllerClass::end(); } -int ESP32SJA1000Class::endPacket() +bool ESP32SJA1000Class::isTxComplete() { + return readRegister(REG_SR) & SR_TCS; +} + +bool ESP32SJA1000Class::isTxBufferFree() { + return readRegister(REG_SR) & SR_TBS; +} + +bool ESP32SJA1000Class::isBusRecessive() { + return (readRegister(REG_SR) & (1 << 4)) != 0; // Bit 4 is SR_RX +} + +bool ESP32SJA1000Class::isReadyToTransmit() { + return isTxBufferFree() && isBusRecessive(); +} + +int ESP32SJA1000Class::endFrame() { - if (!CANControllerClass::endPacket()) { + if (!CANControllerClass::endFrame()) { return 0; } - // wait for TX buffer to free - while ((readRegister(REG_SR) & 0x04) != 0x04) { - yield(); - } + // while (_transmitting == true) { + // yield(); + // } + + _transmitting = true; int dataReg; @@ -199,26 +245,27 @@ int ESP32SJA1000Class::endPacket() // self reception request modifyRegister(REG_CMR, 0x1f, 0x10); } else { + while (!isReadyToTransmit()) { + // yield(); // Allow background tasks to happen + } + // transmit request modifyRegister(REG_CMR, 0x1f, 0x01); } - // wait for TX complete - while ((readRegister(REG_SR) & 0x08) != 0x08) { - if (readRegister(REG_ECC) == 0xd9) { - modifyRegister(REG_CMR, 0x1f, 0x02); // error, abort - return 0; - } - yield(); + // wait for TX complete, then return + while (!isTxComplete()) { + // yield(); } - + + _transmitting = true; return 1; } -int ESP32SJA1000Class::parsePacket() +int ESP32SJA1000Class::parseFrame() { if ((readRegister(REG_SR) & 0x01) != 0x01) { - // no packet + // no frame return 0; } @@ -258,7 +305,7 @@ int ESP32SJA1000Class::parsePacket() return _rxDlc; } -void ESP32SJA1000Class::onReceive(void(*callback)(int)) +void ESP32SJA1000Class::onReceive(TCallback callback) { CANControllerClass::onReceive(callback); @@ -374,14 +421,25 @@ void ESP32SJA1000Class::dumpRegisters(Stream& out) void ESP32SJA1000Class::handleInterrupt() { - uint8_t ir = readRegister(REG_IR); - - if (ir & 0x01) { - // received packet, parse and call callback - parsePacket(); - - _onReceive(available()); - } + uint8_t ir; + + // Loop to catch all interrupts + do { + uint8_t ir = readRegister(REG_IR); // Read interrupts + + if (ir & IR_EI) { // Check error interrupt + uint8_t ecc = readRegister(REG_ECC); // Get error code + if (ecc != 0x00) { + _onError(ir, ecc); // Trigger error buffering callback + } + } + + if (ir & 0x01) { + // received frame, parse and call callback + parseFrame(); + _onReceive(available()); // Trigger frame buffering callback + } + } while (ir); } uint8_t ESP32SJA1000Class::readRegister(uint8_t address) @@ -410,6 +468,21 @@ void ESP32SJA1000Class::onInterrupt(void* arg) ((ESP32SJA1000Class*)arg)->handleInterrupt(); } -ESP32SJA1000Class CAN; +SJA1000Status ESP32SJA1000Class::getStatus() { + SJA1000Status status; + status.apb_freq = ((READ_PERI_REG(RTC_APB_FREQ_REG)) & UINT16_MAX) << 12; + status.mode = readRegister(REG_MOD); + status.clk_div = readRegister(REG_CDR) & 0x07; // Bits 0-2 of CDR + status.btr0 = readRegister(REG_BTR0); + status.btr1 = readRegister(REG_BTR1); + // char bitrate_message[80]; + // sprintf(bitrate_message, + // "Mode: 0x%02x, APB: %i, CLK DIV: 0x%02x, BTR0: 0x%02x, BTR1: 0x%02x", + // mode, apb_freq, clock_divider, btr0, btr1 + // ); + return status; +} + +// ESP32SJA1000Class CAN; #endif diff --git a/src/ESP32SJA1000.h b/src/ESP32SJA1000.h index b83cddc..5389532 100644 --- a/src/ESP32SJA1000.h +++ b/src/ESP32SJA1000.h @@ -11,6 +11,11 @@ #define DEFAULT_CAN_RX_PIN GPIO_NUM_4 #define DEFAULT_CAN_TX_PIN GPIO_NUM_5 +struct SJA1000Status { + uint32_t apb_freq; + uint8_t mode, clk_div, btr0, btr1; +}; + class ESP32SJA1000Class : public CANControllerClass { public: @@ -20,11 +25,11 @@ class ESP32SJA1000Class : public CANControllerClass { virtual int begin(long baudRate); virtual void end(); - virtual int endPacket(); + virtual int endFrame(); - virtual int parsePacket(); + virtual int parseFrame(); - virtual void onReceive(void(*callback)(int)); + virtual void onReceive(TCallback callback); using CANControllerClass::filter; virtual int filter(int id, int mask); @@ -39,13 +44,18 @@ class ESP32SJA1000Class : public CANControllerClass { void setPins(int rx, int tx); void dumpRegisters(Stream& out); + uint8_t readRegister(uint8_t address); + bool isTxBufferFree(); + bool isTxComplete(); + bool isBusRecessive(); + bool isReadyToTransmit(); + SJA1000Status getStatus(); private: void reset(); void handleInterrupt(); - uint8_t readRegister(uint8_t address); void modifyRegister(uint8_t address, uint8_t mask, uint8_t value); void writeRegister(uint8_t address, uint8_t value); @@ -56,6 +66,7 @@ class ESP32SJA1000Class : public CANControllerClass { gpio_num_t _txPin; bool _loopback; intr_handle_t _intrHandle; + bool _transmitting; }; extern ESP32SJA1000Class CAN; diff --git a/src/MCP2515.cpp b/src/MCP2515.cpp index 36c160d..9ab82a0 100644 --- a/src/MCP2515.cpp +++ b/src/MCP2515.cpp @@ -1,13 +1,12 @@ // Copyright (c) Sandeep Mistry. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -#ifndef ARDUINO_ARCH_ESP32 - #include "MCP2515.h" #define REG_BFPCTRL 0x0c #define REG_TXRTSCTRL 0x0d +#define REG_CANSTAT 0x0e #define REG_CANCTRL 0x0f #define REG_CNF3 0x28 @@ -55,10 +54,29 @@ #define FLAG_RXM0 0x20 #define FLAG_RXM1 0x40 +#define CMD_WRITE 0x02 +#define CMD_READ 0x03 +#define CMD_UPDATE 0x05 +#define CMD_RESET 0xC0 + +#define CANSTAT_NORMAL 0x00 +#define CANSTAT_CONFIG 0x80 + +#define FLAG_TX0IF 0x04 // Bit 2 +#define FLAG_TX1IF 0x08 // Bit 3 +#define FLAG_TX2IF 0x10 // Bit 4 + +#define FLAG_ERRIF 0x20 // bit 5 of REG_CANINTF (0x2C) +#define REG_EFLG 0x2D // Error Flag Register +#define REG_TEC 0x1C // Transmit Error Counter +#define REG_REC 0x1D // Receive Error Counter + + +Stream* MCP2515Class::_debug; MCP2515Class::MCP2515Class() : CANControllerClass(), - _spiSettings(10E6, MSBFIRST, SPI_MODE0), + _spiSettings(1E6, MSBFIRST, SPI_MODE0), _csPin(MCP2515_DEFAULT_CS_PIN), _intPin(MCP2515_DEFAULT_INT_PIN), _clockFrequency(MCP2515_DEFAULT_CLOCK_FREQUENCY) @@ -76,13 +94,12 @@ int MCP2515Class::begin(long baudRate) pinMode(_csPin, OUTPUT); // start SPI - SPI.begin(); - - reset(); + SPI.begin(18, 19, 23, _csPin); // SCK, MISO, MOSI, SS + delay(2000); - writeRegister(REG_CANCTRL, 0x80); - if (readRegister(REG_CANCTRL) != 0x80) { - return 0; + // Reset and verify that CANSTAT is in Config mode + if (sendReset() != 1) { + return -4; } const struct { @@ -122,19 +139,22 @@ int MCP2515Class::begin(long baudRate) const uint8_t* cnf = NULL; for (unsigned int i = 0; i < (sizeof(CNF_MAPPER) / sizeof(CNF_MAPPER[0])); i++) { - if (CNF_MAPPER[i].clockFrequency == _clockFrequency && CNF_MAPPER[i].baudRate == baudRate) { - cnf = CNF_MAPPER[i].cnf; - break; - } + if (CNF_MAPPER[i].clockFrequency == _clockFrequency && CNF_MAPPER[i].baudRate == baudRate) { + cnf = CNF_MAPPER[i].cnf; + break; + } } if (cnf == NULL) { - return 0; + return -2; } writeRegister(REG_CNF1, cnf[0]); + if (readRegister(REG_CNF1) != cnf[0]) { return -5; } writeRegister(REG_CNF2, cnf[1]); + if (readRegister(REG_CNF2) != cnf[1]) { return -6; } writeRegister(REG_CNF3, cnf[2]); + if (readRegister(REG_CNF3) != cnf[2]) { return -7; } writeRegister(REG_CANINTE, FLAG_RXnIE(1) | FLAG_RXnIE(0)); writeRegister(REG_BFPCTRL, 0x00); @@ -142,10 +162,13 @@ int MCP2515Class::begin(long baudRate) writeRegister(REG_RXBnCTRL(0), FLAG_RXM1 | FLAG_RXM0); writeRegister(REG_RXBnCTRL(1), FLAG_RXM1 | FLAG_RXM0); - writeRegister(REG_CANCTRL, 0x00); - if (readRegister(REG_CANCTRL) != 0x00) { - return 0; + // 0x00 = Normal mode + writeRegister(REG_CANCTRL, CANSTAT_NORMAL); + + if ((readRegister(REG_CANSTAT) & 0xE0) != CANSTAT_NORMAL) { + return -3; } + return 1; } @@ -157,14 +180,27 @@ void MCP2515Class::end() CANControllerClass::end(); } -int MCP2515Class::endPacket() +int MCP2515Class::endFrame() { - if (!CANControllerClass::endPacket()) { + if (!CANControllerClass::endFrame()) { return 0; } - int n = 0; + // 1) pick a free mailbox + int n = -1; + for (int i = 0; i < 3; i++) { + if (!(readRegister(REG_TXBnCTRL(i)) & 0x08)) { // TXREQ==0? + n = i; + break; + } + } + + if (n < 0) { + // all three mailboxes busy! + return 0; + } + // 2) load ID/DLC/data if (_txExtended) { writeRegister(REG_TXBnSIDH(n), _txId >> 21); writeRegister(REG_TXBnSIDL(n), (((_txId >> 18) & 0x07) << 5) | FLAG_EXIDE | ((_txId >> 16) & 0x03)); @@ -187,40 +223,50 @@ int MCP2515Class::endPacket() } } + // 3) request transmit writeRegister(REG_TXBnCTRL(n), 0x08); + // 4) wait for TXREQ to clear, or abort on MLOA/ABTF/timeout + unsigned long start = millis(); bool aborted = false; - while (readRegister(REG_TXBnCTRL(n)) & 0x08) { - if (readRegister(REG_TXBnCTRL(n)) & 0x10) { - // abort + uint8_t s = readRegister(REG_TXBnCTRL(n)); + if (s & (0x10 /*ABTF*/ | 0x20 /*MLOA*/)) { aborted = true; - - modifyRegister(REG_CANCTRL, 0x10, 0x10); + break; } - - yield(); + if (millis() - start > 10) { + // force‐abort if no ACK + modifyRegister(REG_CANCTRL, 0x10, 0x10); // ABAT=1 + while (readRegister(REG_TXBnCTRL(n)) & 0x08) // yield(); + modifyRegister(REG_CANCTRL, 0x10, 0x00); // ABAT=0 + aborted = true; + break; + } + // yield(); } - if (aborted) { - // clear abort command - modifyRegister(REG_CANCTRL, 0x10, 0x00); + // Print the status + uint8_t s = readRegister(REG_TXBnCTRL(n)); + if (_debug != nullptr) { + _debug->printf("TXB%d CTRL=0x%02X TXREQ=%d ABTF=%d MLOA=%d\n", + n, s, !!(s & 0x08), !!(s & 0x10), !!(s & 0x20)); } + // 5) clear interrupts & return modifyRegister(REG_CANINTF, FLAG_TXnIF(n), 0x00); - - return (readRegister(REG_TXBnCTRL(n)) & 0x70) ? 0 : 1; + return aborted ? 0 : 1; } -int MCP2515Class::parsePacket() +int MCP2515Class::parseFrame() { int n; uint8_t intf = readRegister(REG_CANINTF); - if (intf & FLAG_RXnIF(0)) { + if (intf & FLAG_RXnIF(0)) { // CAN message is available in RX buffer 0 n = 0; - } else if (intf & FLAG_RXnIF(1)) { + } else if (intf & FLAG_RXnIF(1)) { // CAN message is available in RX buffer 1 n = 1; } else { _rxId = -1; @@ -242,6 +288,7 @@ int MCP2515Class::parsePacket() _rxId = idA; _rxRtr = (readRegister(REG_RXBnSIDL(n)) & FLAG_SRR) ? true : false; } + _rxDlc = readRegister(REG_RXBnDLC(n)) & 0x0f; _rxIndex = 0; @@ -260,21 +307,15 @@ int MCP2515Class::parsePacket() return _rxDlc; } -void MCP2515Class::onReceive(void(*callback)(int)) +bool MCP2515Class::isReadyToTransmit() { + return true; +} + +void MCP2515Class::onReceive(TCallback callback) { CANControllerClass::onReceive(callback); - pinMode(_intPin, INPUT); - - if (callback) { - SPI.usingInterrupt(digitalPinToInterrupt(_intPin)); - attachInterrupt(digitalPinToInterrupt(_intPin), MCP2515Class::onInterrupt, LOW); - } else { - detachInterrupt(digitalPinToInterrupt(_intPin)); -#ifdef SPI_HAS_NOTUSINGINTERRUPT - SPI.notUsingInterrupt(digitalPinToInterrupt(_intPin)); -#endif - } + attachInterrupt(digitalPinToInterrupt(_intPin), MCP2515Class::onInterrupt, FALLING); } int MCP2515Class::filter(int id, int mask) @@ -392,6 +433,10 @@ int MCP2515Class::wakeup() return 1; } + +void MCP2515Class::setDebugOutput(Stream* debug) { + _debug = debug; +} void MCP2515Class::setPins(int cs, int irq) { @@ -409,6 +454,22 @@ void MCP2515Class::setClockFrequency(long clockFrequency) _clockFrequency = clockFrequency; } +// ERRIF! EFLG=0x0B TEC=0 REC=128 +void MCP2515Class::dumpErrors() { + uint8_t intf = readRegister(REG_CANINTF); + if (intf & FLAG_ERRIF) { + uint8_t eflg = readRegister(REG_EFLG); + uint8_t tec = readRegister(REG_TEC); + uint8_t rec = readRegister(REG_REC); + + if (_debug != nullptr) { + _debug->printf("ERRIF! EFLG=0x%02X TEC=%u REC=%u\n", eflg, tec, rec); + } + + modifyRegister(REG_CANINTF, FLAG_ERRIF, 0x00); + } +} + void MCP2515Class::dumpRegisters(Stream& out) { for (int i = 0; i < 128; i++) { @@ -419,48 +480,76 @@ void MCP2515Class::dumpRegisters(Stream& out) out.print('0'); } out.print(i, HEX); - out.print(": 0x"); + out.print(":0x"); if (b < 16) { out.print('0'); } - out.println(b, HEX); + out.print(b, HEX); + out.print(" "); + if (i % 8 == 7) { + out.println(); + } } } -void MCP2515Class::reset() +int MCP2515Class::sendReset() { SPI.beginTransaction(_spiSettings); digitalWrite(_csPin, LOW); - SPI.transfer(0xc0); + SPI.transfer(CMD_RESET); + digitalWrite(_csPin, HIGH); + delay(10); + + // Read CANSTAT (cmd 0x03, address 0x0E) + digitalWrite(_csPin, LOW); + SPI.transfer(CMD_READ); // READ command + SPI.transfer(REG_CANSTAT); // CANSTAT address + uint8_t canstat = SPI.transfer(0x00); // read value digitalWrite(_csPin, HIGH); SPI.endTransaction(); - delayMicroseconds(10); + // CANSTAT 0x80 == config mode + if (canstat != CANSTAT_CONFIG) { + return -1; + } + + return 1; } void MCP2515Class::handleInterrupt() { - if (readRegister(REG_CANINTF) == 0) { - return; + // if (_debug != nullptr) { + // _debug->println("handleInterrupt called"); + // } + + if (!(readRegister(REG_CANINTF) & (FLAG_RXnIF(0) | FLAG_RXnIF(1)))) { + return; // no frame in RXB0 or RXB1 } - while (parsePacket() || _rxId != -1) { + int result = parseFrame(); + + if (result > 0) { + // if (_debug != nullptr) { + // _debug->print("parseFrame result:"); + // _debug->println(result); + // } _onReceive(available()); } + + _rxId = -1; // Mark buffer as consumed + } uint8_t MCP2515Class::readRegister(uint8_t address) { uint8_t value; - SPI.beginTransaction(_spiSettings); digitalWrite(_csPin, LOW); - SPI.transfer(0x03); + SPI.transfer(CMD_READ); SPI.transfer(address); value = SPI.transfer(0x00); digitalWrite(_csPin, HIGH); SPI.endTransaction(); - return value; } @@ -468,7 +557,7 @@ void MCP2515Class::modifyRegister(uint8_t address, uint8_t mask, uint8_t value) { SPI.beginTransaction(_spiSettings); digitalWrite(_csPin, LOW); - SPI.transfer(0x05); + SPI.transfer(CMD_UPDATE); SPI.transfer(address); SPI.transfer(mask); SPI.transfer(value); @@ -480,18 +569,17 @@ void MCP2515Class::writeRegister(uint8_t address, uint8_t value) { SPI.beginTransaction(_spiSettings); digitalWrite(_csPin, LOW); - SPI.transfer(0x02); + SPI.transfer(CMD_WRITE); SPI.transfer(address); SPI.transfer(value); digitalWrite(_csPin, HIGH); SPI.endTransaction(); + // delay(10); } void MCP2515Class::onInterrupt() { - CAN.handleInterrupt(); + CAN.handleInterrupt(); } -MCP2515Class CAN; - -#endif +// MCP2515Class CAN; diff --git a/src/MCP2515.h b/src/MCP2515.h index 2f0444f..f3c4c1c 100644 --- a/src/MCP2515.h +++ b/src/MCP2515.h @@ -1,13 +1,10 @@ // Copyright (c) Sandeep Mistry. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -#ifndef ARDUINO_ARCH_ESP32 - #ifndef MCP2515_H #define MCP2515_H #include - #include "CANController.h" #define MCP2515_DEFAULT_CLOCK_FREQUENCY 16e6 @@ -30,11 +27,12 @@ class MCP2515Class : public CANControllerClass { virtual int begin(long baudRate); virtual void end(); - virtual int endPacket(); + virtual int endFrame(); - virtual int parsePacket(); + virtual int parseFrame(); virtual void onReceive(void(*callback)(int)); + bool isReadyToTransmit(); using CANControllerClass::filter; virtual int filter(int id, int mask); @@ -49,11 +47,13 @@ class MCP2515Class : public CANControllerClass { void setPins(int cs = MCP2515_DEFAULT_CS_PIN, int irq = MCP2515_DEFAULT_INT_PIN); void setSPIFrequency(uint32_t frequency); void setClockFrequency(long clockFrequency); + static void setDebugOutput(Stream* debug); void dumpRegisters(Stream& out); + void dumpErrors(); private: - void reset(); + int sendReset(); void handleInterrupt(); @@ -65,6 +65,7 @@ class MCP2515Class : public CANControllerClass { private: SPISettings _spiSettings; + static Stream* _debug; int _csPin; int _intPin; long _clockFrequency; @@ -73,5 +74,3 @@ class MCP2515Class : public CANControllerClass { extern MCP2515Class CAN; #endif - -#endif