From d209839fb90756da34d1b3daf5c10d17ae75c5a3 Mon Sep 17 00:00:00 2001 From: "Bryan C. Geraghty" Date: Mon, 16 Jun 2025 19:31:02 +0000 Subject: [PATCH 01/17] Converted callbacks to std::function and added error checking --- src/CANController.cpp | 2 +- src/CANController.h | 6 ++++-- src/ESP32SJA1000.cpp | 40 +++++++++++++++++++++++++++++----------- src/ESP32SJA1000.h | 2 +- 4 files changed, 35 insertions(+), 15 deletions(-) diff --git a/src/CANController.cpp b/src/CANController.cpp index 0890eec..a1d9fdb 100644 --- a/src/CANController.cpp +++ b/src/CANController.cpp @@ -180,7 +180,7 @@ void CANControllerClass::flush() { } -void CANControllerClass::onReceive(void(*callback)(int)) +void CANControllerClass::onReceive(TCallback callback) { _onReceive = callback; } diff --git a/src/CANController.h b/src/CANController.h index cdaeb94..afd4ea9 100644 --- a/src/CANController.h +++ b/src/CANController.h @@ -6,6 +6,8 @@ #include +typedef std::function TCallback; + class CANControllerClass : public Stream { public: @@ -32,7 +34,7 @@ class CANControllerClass : public Stream { virtual int peek(); virtual void flush(); - virtual void onReceive(void(*callback)(int)); + void onReceive(TCallback callback); virtual int filter(int id) { return filter(id, 0x7ff); } virtual int filter(int id, int mask); @@ -49,7 +51,7 @@ class CANControllerClass : public Stream { virtual ~CANControllerClass(); protected: - void (*_onReceive)(int); + TCallback _onReceive; bool _packetBegun; long _txId; diff --git a/src/ESP32SJA1000.cpp b/src/ESP32SJA1000.cpp index 309e030..9ef9378 100644 --- a/src/ESP32SJA1000.cpp +++ b/src/ESP32SJA1000.cpp @@ -33,6 +33,10 @@ #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,7 +71,9 @@ 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_BTR0, 0xc0, 0x40); // SJW = 2 + // modifyRegister(REG_BTR0, 0xc0, 0x80); // SJW = 3 + // modifyRegister(REG_BTR0, 0xc0, 0x00); // SJW = 1 modifyRegister(REG_BTR1, 0x70, 0x10); // TSEG2 = 1 switch (baudRate) { @@ -169,7 +175,7 @@ int ESP32SJA1000Class::endPacket() } // wait for TX buffer to free - while ((readRegister(REG_SR) & 0x04) != 0x04) { + while (!(readRegister(REG_SR) & SR_TBS)) { yield(); } @@ -199,19 +205,31 @@ int ESP32SJA1000Class::endPacket() // self reception request modifyRegister(REG_CMR, 0x1f, 0x10); } else { + // Check one more time to be sure the tx buffer is free + while (!(readRegister(REG_SR) & SR_TBS)) { + yield(); // Allow background tasks to happen + } + + // Wait for bus to be recessive before transmitting + while (readRegister(REG_SR) & 0x10) { // SR_RS = Receive Status = 1 means dominant + delayMicroseconds(10); + } + // 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 and buffer to be free, then check for errors + while (!((readRegister(REG_SR) & (SR_TCS | SR_TBS)) == (SR_TCS | SR_TBS))) { + if (readRegister(REG_IR) & IR_EI) { // Error interrupt must be set to check for an error code + if (readRegister(REG_ECC) != 0x00) { // Check error code + modifyRegister(REG_CMR, 0x1F, 0x02); // Abort transmission + return 0; + } + } + yield(); // Allow multitasking } - + return 1; } @@ -258,7 +276,7 @@ int ESP32SJA1000Class::parsePacket() return _rxDlc; } -void ESP32SJA1000Class::onReceive(void(*callback)(int)) +void ESP32SJA1000Class::onReceive(TCallback callback) { CANControllerClass::onReceive(callback); diff --git a/src/ESP32SJA1000.h b/src/ESP32SJA1000.h index b83cddc..696eabf 100644 --- a/src/ESP32SJA1000.h +++ b/src/ESP32SJA1000.h @@ -24,7 +24,7 @@ class ESP32SJA1000Class : public CANControllerClass { virtual int parsePacket(); - virtual void onReceive(void(*callback)(int)); + virtual void onReceive(TCallback callback); using CANControllerClass::filter; virtual int filter(int id, int mask); From 56c87bfed445126c5e12ceab41ce4d6871883dd9 Mon Sep 17 00:00:00 2001 From: "Bryan C. Geraghty" Date: Tue, 17 Jun 2025 22:30:46 +0000 Subject: [PATCH 02/17] Added error handling --- src/CANController.cpp | 6 ++++++ src/CANController.h | 3 +++ src/ESP32SJA1000.cpp | 7 ++++--- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/CANController.cpp b/src/CANController.cpp index a1d9fdb..18b643b 100644 --- a/src/CANController.cpp +++ b/src/CANController.cpp @@ -5,6 +5,7 @@ CANControllerClass::CANControllerClass() : _onReceive(NULL), + _onError(NULL), _packetBegun(false), _txId(-1), @@ -185,6 +186,11 @@ 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 afd4ea9..6e7cbc4 100644 --- a/src/CANController.h +++ b/src/CANController.h @@ -7,6 +7,7 @@ #include typedef std::function TCallback; +typedef std::function TErrorCallback; class CANControllerClass : public Stream { @@ -35,6 +36,7 @@ class CANControllerClass : public Stream { virtual void flush(); void onReceive(TCallback callback); + void onError(TErrorCallback callback); virtual int filter(int id) { return filter(id, 0x7ff); } virtual int filter(int id, int mask); @@ -52,6 +54,7 @@ class CANControllerClass : public Stream { protected: TCallback _onReceive; + TErrorCallback _onError; bool _packetBegun; long _txId; diff --git a/src/ESP32SJA1000.cpp b/src/ESP32SJA1000.cpp index 9ef9378..e70dd40 100644 --- a/src/ESP32SJA1000.cpp +++ b/src/ESP32SJA1000.cpp @@ -394,12 +394,13 @@ void ESP32SJA1000Class::handleInterrupt() { uint8_t ir = readRegister(REG_IR); - if (ir & 0x01) { + if (ir & IR_EI) { // Error interrupt must be set to check for an error code + _onError(readRegister(REG_EIR), readRegister(REG_ECC)); + } else if (ir & 0x01) { // received packet, parse and call callback parsePacket(); - _onReceive(available()); - } + } } uint8_t ESP32SJA1000Class::readRegister(uint8_t address) From 36ca4d94eca82e34f984ddd4e7adfca2741b5d86 Mon Sep 17 00:00:00 2001 From: "Bryan C. Geraghty" Date: Tue, 17 Jun 2025 22:32:02 +0000 Subject: [PATCH 03/17] Exposed readRegister --- src/ESP32SJA1000.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ESP32SJA1000.h b/src/ESP32SJA1000.h index 696eabf..b432dee 100644 --- a/src/ESP32SJA1000.h +++ b/src/ESP32SJA1000.h @@ -39,13 +39,13 @@ class ESP32SJA1000Class : public CANControllerClass { void setPins(int rx, int tx); void dumpRegisters(Stream& out); + uint8_t readRegister(uint8_t address); 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); From ccfeb5bfefea8826f093f24587b84ea0b2e09873 Mon Sep 17 00:00:00 2001 From: "Bryan C. Geraghty" Date: Tue, 17 Jun 2025 22:33:08 +0000 Subject: [PATCH 04/17] REG_EIR definition for error handling --- src/ESP32SJA1000.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ESP32SJA1000.cpp b/src/ESP32SJA1000.cpp index e70dd40..81e0560 100644 --- a/src/ESP32SJA1000.cpp +++ b/src/ESP32SJA1000.cpp @@ -31,6 +31,7 @@ #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 From 71886f940465b5dc6321ae46c0a585ddcf8949c0 Mon Sep 17 00:00:00 2001 From: "Bryan C. Geraghty" Date: Sat, 21 Jun 2025 01:34:05 +0000 Subject: [PATCH 05/17] Timing and processing tweaks --- src/ESP32SJA1000.cpp | 215 +++++++++++++++++++++++++++---------------- src/ESP32SJA1000.h | 11 +++ 2 files changed, 145 insertions(+), 81 deletions(-) diff --git a/src/ESP32SJA1000.cpp b/src/ESP32SJA1000.cpp index 81e0560..a51c091 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 @@ -72,75 +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 = 2 - // modifyRegister(REG_BTR0, 0xc0, 0x80); // SJW = 3 - // modifyRegister(REG_BTR0, 0xc0, 0x00); // 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 packet, 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 packet 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); @@ -149,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; @@ -169,16 +191,33 @@ void ESP32SJA1000Class::end() CANControllerClass::end(); } +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::endPacket() { if (!CANControllerClass::endPacket()) { return 0; } - // wait for TX buffer to free - while (!(readRegister(REG_SR) & SR_TBS)) { - yield(); - } + // while (_transmitting == true) { + // yield(); + // } + + _transmitting = true; int dataReg; @@ -206,31 +245,20 @@ int ESP32SJA1000Class::endPacket() // self reception request modifyRegister(REG_CMR, 0x1f, 0x10); } else { - // Check one more time to be sure the tx buffer is free - while (!(readRegister(REG_SR) & SR_TBS)) { - yield(); // Allow background tasks to happen - } - - // Wait for bus to be recessive before transmitting - while (readRegister(REG_SR) & 0x10) { // SR_RS = Receive Status = 1 means dominant - delayMicroseconds(10); - } + while (!isReadyToTransmit()) { + // yield(); // Allow background tasks to happen + } // transmit request modifyRegister(REG_CMR, 0x1f, 0x01); } - // wait for TX complete and buffer to be free, then check for errors - while (!((readRegister(REG_SR) & (SR_TCS | SR_TBS)) == (SR_TCS | SR_TBS))) { - if (readRegister(REG_IR) & IR_EI) { // Error interrupt must be set to check for an error code - if (readRegister(REG_ECC) != 0x00) { // Check error code - modifyRegister(REG_CMR, 0x1F, 0x02); // Abort transmission - return 0; - } - } - yield(); // Allow multitasking + // wait for TX complete, then return + while (!isTxComplete()) { + // yield(); } + _transmitting = true; return 1; } @@ -393,15 +421,25 @@ void ESP32SJA1000Class::dumpRegisters(Stream& out) void ESP32SJA1000Class::handleInterrupt() { - uint8_t ir = readRegister(REG_IR); - - if (ir & IR_EI) { // Error interrupt must be set to check for an error code - _onError(readRegister(REG_EIR), readRegister(REG_ECC)); - } else 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 packet, parse and call callback + parsePacket(); + _onReceive(available()); // Trigger packet buffering callback + } + } while (ir); } uint8_t ESP32SJA1000Class::readRegister(uint8_t address) @@ -430,6 +468,21 @@ void ESP32SJA1000Class::onInterrupt(void* arg) ((ESP32SJA1000Class*)arg)->handleInterrupt(); } +SJA1000Status ESP32SJA1000Class::getStatus() { + SJA1000Status status; + status.apb_freq = ((READ_PERI_REG(RTC_APB_FREQ_REG)) & UINT16_MAX) << 12; + status.mode = CAN.readRegister(REG_MOD); + status.clk_div = CAN.readRegister(REG_CDR) & 0x07; // Bits 0-2 of CDR + status.btr0 = CAN.readRegister(REG_BTR0); + status.btr1 = CAN.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 b432dee..d266361 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: @@ -40,6 +45,11 @@ class ESP32SJA1000Class : public CANControllerClass { void dumpRegisters(Stream& out); uint8_t readRegister(uint8_t address); + bool isTxBufferFree(); + bool isTxComplete(); + bool isBusRecessive(); + bool isReadyToTransmit(); + SJA1000Status getStatus(); private: void reset(); @@ -56,6 +66,7 @@ class ESP32SJA1000Class : public CANControllerClass { gpio_num_t _txPin; bool _loopback; intr_handle_t _intrHandle; + bool _transmitting; }; extern ESP32SJA1000Class CAN; From 1b6f6d5e46b1298da2c030c3a4cd9c81da91756c Mon Sep 17 00:00:00 2001 From: "Bryan C. Geraghty" Date: Sun, 22 Jun 2025 15:46:26 +0000 Subject: [PATCH 06/17] Disentangled ESP and MCP loading and added isReadyToTransmit() --- src/ESP32SJA1000.cpp | 10 +++++----- src/MCP2515.cpp | 16 ++++++---------- src/MCP2515.h | 5 +---- 3 files changed, 12 insertions(+), 19 deletions(-) diff --git a/src/ESP32SJA1000.cpp b/src/ESP32SJA1000.cpp index a51c091..191676a 100644 --- a/src/ESP32SJA1000.cpp +++ b/src/ESP32SJA1000.cpp @@ -471,10 +471,10 @@ void ESP32SJA1000Class::onInterrupt(void* arg) SJA1000Status ESP32SJA1000Class::getStatus() { SJA1000Status status; status.apb_freq = ((READ_PERI_REG(RTC_APB_FREQ_REG)) & UINT16_MAX) << 12; - status.mode = CAN.readRegister(REG_MOD); - status.clk_div = CAN.readRegister(REG_CDR) & 0x07; // Bits 0-2 of CDR - status.btr0 = CAN.readRegister(REG_BTR0); - status.btr1 = CAN.readRegister(REG_BTR1); + 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", @@ -483,6 +483,6 @@ SJA1000Status ESP32SJA1000Class::getStatus() { return status; } -ESP32SJA1000Class CAN; +// ESP32SJA1000Class CAN; #endif diff --git a/src/MCP2515.cpp b/src/MCP2515.cpp index 36c160d..8054086 100644 --- a/src/MCP2515.cpp +++ b/src/MCP2515.cpp @@ -1,8 +1,6 @@ // 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 @@ -260,6 +258,10 @@ int MCP2515Class::parsePacket() return _rxDlc; } +bool MCP2515Class::isReadyToTransmit() { + return true; +} + void MCP2515Class::onReceive(void(*callback)(int)) { CANControllerClass::onReceive(callback); @@ -267,13 +269,9 @@ void MCP2515Class::onReceive(void(*callback)(int)) 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 } } @@ -489,9 +487,7 @@ void MCP2515Class::writeRegister(uint8_t address, uint8_t value) void MCP2515Class::onInterrupt() { - CAN.handleInterrupt(); + CAN.handleInterrupt(); } -MCP2515Class CAN; - -#endif +// MCP2515Class CAN; diff --git a/src/MCP2515.h b/src/MCP2515.h index 2f0444f..bbc5315 100644 --- a/src/MCP2515.h +++ b/src/MCP2515.h @@ -1,8 +1,6 @@ // 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 @@ -35,6 +33,7 @@ class MCP2515Class : public CANControllerClass { virtual int parsePacket(); virtual void onReceive(void(*callback)(int)); + bool isReadyToTransmit(); using CANControllerClass::filter; virtual int filter(int id, int mask); @@ -73,5 +72,3 @@ class MCP2515Class : public CANControllerClass { extern MCP2515Class CAN; #endif - -#endif From f5305b20fd603e11487926a959531371adf9d0dd Mon Sep 17 00:00:00 2001 From: "Bryan C. Geraghty" Date: Sun, 22 Jun 2025 22:15:57 +0000 Subject: [PATCH 07/17] Cleaned up dumpRegisters, added some init return codes, and renamed reset to sendReset --- src/MCP2515.cpp | 20 ++++++++++++-------- src/MCP2515.h | 2 +- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/MCP2515.cpp b/src/MCP2515.cpp index 8054086..fb69247 100644 --- a/src/MCP2515.cpp +++ b/src/MCP2515.cpp @@ -74,13 +74,13 @@ int MCP2515Class::begin(long baudRate) pinMode(_csPin, OUTPUT); // start SPI - SPI.begin(); + SPI.begin(18, 19, 23, 5); // SCK, MISO, MOSI, SS - reset(); + sendReset(); writeRegister(REG_CANCTRL, 0x80); if (readRegister(REG_CANCTRL) != 0x80) { - return 0; + return -1; } const struct { @@ -127,7 +127,7 @@ int MCP2515Class::begin(long baudRate) } if (cnf == NULL) { - return 0; + return -2; } writeRegister(REG_CNF1, cnf[0]); @@ -142,7 +142,7 @@ int MCP2515Class::begin(long baudRate) writeRegister(REG_CANCTRL, 0x00); if (readRegister(REG_CANCTRL) != 0x00) { - return 0; + return -3; } return 1; @@ -417,15 +417,19 @@ 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() +void MCP2515Class::sendReset() { SPI.beginTransaction(_spiSettings); digitalWrite(_csPin, LOW); diff --git a/src/MCP2515.h b/src/MCP2515.h index bbc5315..d78505d 100644 --- a/src/MCP2515.h +++ b/src/MCP2515.h @@ -52,7 +52,7 @@ class MCP2515Class : public CANControllerClass { void dumpRegisters(Stream& out); private: - void reset(); + void sendReset(); void handleInterrupt(); From ba8a3b2add49fc6dc9feb158a9384b43a0ce14c8 Mon Sep 17 00:00:00 2001 From: "Bryan C. Geraghty" Date: Mon, 23 Jun 2025 19:07:17 +0000 Subject: [PATCH 08/17] SPI debugging --- src/MCP2515.cpp | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/MCP2515.cpp b/src/MCP2515.cpp index fb69247..13f972b 100644 --- a/src/MCP2515.cpp +++ b/src/MCP2515.cpp @@ -6,6 +6,7 @@ #define REG_BFPCTRL 0x0c #define REG_TXRTSCTRL 0x0d +#define REG_CANSTAT 0x0e #define REG_CANCTRL 0x0f #define REG_CNF3 0x28 @@ -74,15 +75,19 @@ int MCP2515Class::begin(long baudRate) pinMode(_csPin, OUTPUT); // start SPI - SPI.begin(18, 19, 23, 5); // SCK, MISO, MOSI, SS - + SPI.begin(18, 19, 23, _csPin); // SCK, MISO, MOSI, SS + delay(2000); + sendReset(); + // 0x80 = Config mode writeRegister(REG_CANCTRL, 0x80); - if (readRegister(REG_CANCTRL) != 0x80) { + if ((readRegister(REG_CANSTAT) & 0xE0) != 0x80) { return -1; } + writeRegister(REG_CANCTRL, 0x04); // Enable CLKOUT + const struct { long clockFrequency; long baudRate; @@ -120,16 +125,18 @@ 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 -2; + return -2; } + // uint8_t cnf[] = { 0x00, 0xB1, 0x04 }; + writeRegister(REG_CNF1, cnf[0]); writeRegister(REG_CNF2, cnf[1]); writeRegister(REG_CNF3, cnf[2]); @@ -140,8 +147,10 @@ int MCP2515Class::begin(long baudRate) writeRegister(REG_RXBnCTRL(0), FLAG_RXM1 | FLAG_RXM0); writeRegister(REG_RXBnCTRL(1), FLAG_RXM1 | FLAG_RXM0); + // 0x00 = Normal mode writeRegister(REG_CANCTRL, 0x00); - if (readRegister(REG_CANCTRL) != 0x00) { + + if ((readRegister(REG_CANSTAT) & 0xE0) != 0x00) { return -3; } @@ -269,7 +278,7 @@ void MCP2515Class::onReceive(void(*callback)(int)) pinMode(_intPin, INPUT); if (callback) { - attachInterrupt(digitalPinToInterrupt(_intPin), MCP2515Class::onInterrupt, LOW); + attachInterrupt(digitalPinToInterrupt(_intPin), MCP2515Class::onInterrupt, FALLING); } else { detachInterrupt(digitalPinToInterrupt(_intPin)); } @@ -436,8 +445,7 @@ void MCP2515Class::sendReset() SPI.transfer(0xc0); digitalWrite(_csPin, HIGH); SPI.endTransaction(); - - delayMicroseconds(10); + delayMicroseconds(100); } void MCP2515Class::handleInterrupt() @@ -487,6 +495,7 @@ void MCP2515Class::writeRegister(uint8_t address, uint8_t value) SPI.transfer(value); digitalWrite(_csPin, HIGH); SPI.endTransaction(); + delay(100); } void MCP2515Class::onInterrupt() From 35bc4cc2c30a4d74253d1f8fc33e933f5e7c49e6 Mon Sep 17 00:00:00 2001 From: "Bryan C. Geraghty" Date: Mon, 23 Jun 2025 21:20:04 +0000 Subject: [PATCH 09/17] Working implementation --- src/MCP2515.cpp | 60 +++++++++++++++++++++++++++++++------------------ src/MCP2515.h | 2 +- 2 files changed, 39 insertions(+), 23 deletions(-) diff --git a/src/MCP2515.cpp b/src/MCP2515.cpp index 13f972b..b5050dc 100644 --- a/src/MCP2515.cpp +++ b/src/MCP2515.cpp @@ -54,10 +54,17 @@ #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 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) @@ -77,17 +84,12 @@ int MCP2515Class::begin(long baudRate) // start SPI SPI.begin(18, 19, 23, _csPin); // SCK, MISO, MOSI, SS delay(2000); - - sendReset(); - // 0x80 = Config mode - writeRegister(REG_CANCTRL, 0x80); - if ((readRegister(REG_CANSTAT) & 0xE0) != 0x80) { - return -1; + // Reset and verify that CANSTAT is in Config mode + if (sendReset() != 1) { + return -4; } - writeRegister(REG_CANCTRL, 0x04); // Enable CLKOUT - const struct { long clockFrequency; long baudRate; @@ -135,11 +137,12 @@ int MCP2515Class::begin(long baudRate) return -2; } - // uint8_t cnf[] = { 0x00, 0xB1, 0x04 }; - 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); @@ -148,11 +151,12 @@ int MCP2515Class::begin(long baudRate) writeRegister(REG_RXBnCTRL(1), FLAG_RXM1 | FLAG_RXM0); // 0x00 = Normal mode - writeRegister(REG_CANCTRL, 0x00); + writeRegister(REG_CANCTRL, CANSTAT_NORMAL); - if ((readRegister(REG_CANSTAT) & 0xE0) != 0x00) { + if ((readRegister(REG_CANSTAT) & 0xE0) != CANSTAT_NORMAL) { return -3; } + return 1; } @@ -438,14 +442,28 @@ void MCP2515Class::dumpRegisters(Stream& out) } } -void MCP2515Class::sendReset() +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(100); + + // CANSTAT 0x80 == config mode + if (canstat != CANSTAT_CONFIG) { + return -1; + } + + return 1; } void MCP2515Class::handleInterrupt() @@ -462,15 +480,13 @@ void MCP2515Class::handleInterrupt() 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; } @@ -478,7 +494,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); @@ -490,12 +506,12 @@ 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(100); + // delay(10); } void MCP2515Class::onInterrupt() diff --git a/src/MCP2515.h b/src/MCP2515.h index d78505d..4c611b5 100644 --- a/src/MCP2515.h +++ b/src/MCP2515.h @@ -52,7 +52,7 @@ class MCP2515Class : public CANControllerClass { void dumpRegisters(Stream& out); private: - void sendReset(); + int sendReset(); void handleInterrupt(); From 85bbf20b5a9f0e0b99fca867cf78164acd54812a Mon Sep 17 00:00:00 2001 From: "Bryan C. Geraghty" Date: Tue, 24 Jun 2025 02:56:28 +0000 Subject: [PATCH 10/17] Simplify callback definitions --- src/CANController.h | 6 ++++-- src/MCP2515.cpp | 10 ++-------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/CANController.h b/src/CANController.h index 6e7cbc4..8a7b6a1 100644 --- a/src/CANController.h +++ b/src/CANController.h @@ -6,8 +6,10 @@ #include -typedef std::function TCallback; -typedef std::function TErrorCallback; +// typedef std::function TCallback; +// typedef std::function TErrorCallback; +typedef void (*TCallback)(int); +typedef void (*TErrorCallback)(int, int); class CANControllerClass : public Stream { diff --git a/src/MCP2515.cpp b/src/MCP2515.cpp index b5050dc..8791075 100644 --- a/src/MCP2515.cpp +++ b/src/MCP2515.cpp @@ -275,17 +275,11 @@ bool MCP2515Class::isReadyToTransmit() { return true; } -void MCP2515Class::onReceive(void(*callback)(int)) +void MCP2515Class::onReceive(TCallback callback) { CANControllerClass::onReceive(callback); - pinMode(_intPin, INPUT); - - if (callback) { - attachInterrupt(digitalPinToInterrupt(_intPin), MCP2515Class::onInterrupt, FALLING); - } else { - detachInterrupt(digitalPinToInterrupt(_intPin)); - } + attachInterrupt(digitalPinToInterrupt(_intPin), MCP2515Class::onInterrupt, FALLING); } int MCP2515Class::filter(int id, int mask) From eaa80726e9a0cd2e6a118b9574ac4ec9fbc72410 Mon Sep 17 00:00:00 2001 From: "Bryan C. Geraghty" Date: Tue, 24 Jun 2025 14:32:13 +0000 Subject: [PATCH 11/17] Debug handler --- src/MCP2515.cpp | 10 ++++++++++ src/MCP2515.h | 3 ++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/MCP2515.cpp b/src/MCP2515.cpp index 8791075..87c1deb 100644 --- a/src/MCP2515.cpp +++ b/src/MCP2515.cpp @@ -62,6 +62,8 @@ #define CANSTAT_NORMAL 0x00 #define CANSTAT_CONFIG 0x80 +Stream* MCP2515Class::_debug; + MCP2515Class::MCP2515Class() : CANControllerClass(), _spiSettings(1E6, MSBFIRST, SPI_MODE0), @@ -397,6 +399,10 @@ int MCP2515Class::wakeup() return 1; } + +void MCP2515Class::setDebugOutput(Stream* debug) { + _debug = debug; +} void MCP2515Class::setPins(int cs, int irq) { @@ -462,6 +468,10 @@ int MCP2515Class::sendReset() void MCP2515Class::handleInterrupt() { + if (_debug != nullptr) { + _debug->println("handleInterrupt called"); + } + if (readRegister(REG_CANINTF) == 0) { return; } diff --git a/src/MCP2515.h b/src/MCP2515.h index 4c611b5..e7fc0cc 100644 --- a/src/MCP2515.h +++ b/src/MCP2515.h @@ -5,7 +5,6 @@ #define MCP2515_H #include - #include "CANController.h" #define MCP2515_DEFAULT_CLOCK_FREQUENCY 16e6 @@ -48,6 +47,7 @@ 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); @@ -64,6 +64,7 @@ class MCP2515Class : public CANControllerClass { private: SPISettings _spiSettings; + static Stream* _debug; int _csPin; int _intPin; long _clockFrequency; From c45851b38c83f78b69c995ee9737160bb9e5ced3 Mon Sep 17 00:00:00 2001 From: "Bryan C. Geraghty" Date: Tue, 24 Jun 2025 19:41:19 +0000 Subject: [PATCH 12/17] Only call parsePacket once per interrupt --- src/MCP2515.cpp | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/MCP2515.cpp b/src/MCP2515.cpp index 87c1deb..7605c41 100644 --- a/src/MCP2515.cpp +++ b/src/MCP2515.cpp @@ -62,6 +62,10 @@ #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 + Stream* MCP2515Class::_debug; MCP2515Class::MCP2515Class() : @@ -231,9 +235,9 @@ int MCP2515Class::parsePacket() 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; @@ -255,6 +259,7 @@ int MCP2515Class::parsePacket() _rxId = idA; _rxRtr = (readRegister(REG_RXBnSIDL(n)) & FLAG_SRR) ? true : false; } + _rxDlc = readRegister(REG_RXBnDLC(n)) & 0x0f; _rxIndex = 0; @@ -472,13 +477,22 @@ void MCP2515Class::handleInterrupt() _debug->println("handleInterrupt called"); } - if (readRegister(REG_CANINTF) == 0) { - return; + if (!(readRegister(REG_CANINTF) & (FLAG_RXnIF(0) | FLAG_RXnIF(1)))) { + return; // no frame in RXB0 or RXB1 } - while (parsePacket() || _rxId != -1) { + int result = parsePacket(); + + if (result > 0) { + if (_debug != nullptr) { + _debug->print("parsePacket result:"); + _debug->println(result); + } _onReceive(available()); } + + _rxId = -1; // Mark buffer as consumed + } uint8_t MCP2515Class::readRegister(uint8_t address) From bd7d6b4ae0191cdf258d13840a84690b0bc770cc Mon Sep 17 00:00:00 2001 From: "Bryan C. Geraghty" Date: Tue, 24 Jun 2025 23:54:04 +0000 Subject: [PATCH 13/17] State checking and error reporting --- src/MCP2515.cpp | 67 +++++++++++++++++++++++++++++++++++++++---------- src/MCP2515.h | 1 + 2 files changed, 55 insertions(+), 13 deletions(-) diff --git a/src/MCP2515.cpp b/src/MCP2515.cpp index 7605c41..adf72f8 100644 --- a/src/MCP2515.cpp +++ b/src/MCP2515.cpp @@ -66,6 +66,12 @@ #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() : @@ -180,8 +186,21 @@ int MCP2515Class::endPacket() 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)); @@ -204,29 +223,40 @@ 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; + } + 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)); + _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() @@ -425,6 +455,17 @@ void MCP2515Class::setClockFrequency(long clockFrequency) _clockFrequency = clockFrequency; } +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); + _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++) { diff --git a/src/MCP2515.h b/src/MCP2515.h index e7fc0cc..cd06f65 100644 --- a/src/MCP2515.h +++ b/src/MCP2515.h @@ -50,6 +50,7 @@ class MCP2515Class : public CANControllerClass { static void setDebugOutput(Stream* debug); void dumpRegisters(Stream& out); + void dumpErrors(); private: int sendReset(); From 479b10a17fe5a5754d08161fd4ab7e3b00d1b836 Mon Sep 17 00:00:00 2001 From: "Bryan C. Geraghty" Date: Wed, 25 Jun 2025 00:52:10 +0000 Subject: [PATCH 14/17] Disabled some debugging --- src/MCP2515.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/MCP2515.cpp b/src/MCP2515.cpp index adf72f8..b9b2217 100644 --- a/src/MCP2515.cpp +++ b/src/MCP2515.cpp @@ -455,6 +455,7 @@ 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) { @@ -514,9 +515,9 @@ int MCP2515Class::sendReset() void MCP2515Class::handleInterrupt() { - if (_debug != nullptr) { - _debug->println("handleInterrupt called"); - } + // if (_debug != nullptr) { + // _debug->println("handleInterrupt called"); + // } if (!(readRegister(REG_CANINTF) & (FLAG_RXnIF(0) | FLAG_RXnIF(1)))) { return; // no frame in RXB0 or RXB1 @@ -525,10 +526,10 @@ void MCP2515Class::handleInterrupt() int result = parsePacket(); if (result > 0) { - if (_debug != nullptr) { - _debug->print("parsePacket result:"); - _debug->println(result); - } + // if (_debug != nullptr) { + // _debug->print("parsePacket result:"); + // _debug->println(result); + // } _onReceive(available()); } From 5ef4ac9847943d1a99976b03d6ddd61b6b416044 Mon Sep 17 00:00:00 2001 From: "Bryan C. Geraghty" Date: Thu, 26 Jun 2025 16:44:18 +0000 Subject: [PATCH 15/17] Remove yields --- src/MCP2515.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MCP2515.cpp b/src/MCP2515.cpp index b9b2217..58a9948 100644 --- a/src/MCP2515.cpp +++ b/src/MCP2515.cpp @@ -238,12 +238,12 @@ int MCP2515Class::endPacket() if (millis() - start > 10) { // force‐abort if no ACK modifyRegister(REG_CANCTRL, 0x10, 0x10); // ABAT=1 - while (readRegister(REG_TXBnCTRL(n)) & 0x08) yield(); + while (readRegister(REG_TXBnCTRL(n)) & 0x08) // yield(); modifyRegister(REG_CANCTRL, 0x10, 0x00); // ABAT=0 aborted = true; break; } - yield(); + // yield(); } // Print the status From bf18f8b1066260c2157023e32ea7d09565e68292 Mon Sep 17 00:00:00 2001 From: "Bryan C. Geraghty" Date: Thu, 26 Jun 2025 17:15:58 +0000 Subject: [PATCH 16/17] Added nullptr checks --- src/MCP2515.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/MCP2515.cpp b/src/MCP2515.cpp index 58a9948..5891607 100644 --- a/src/MCP2515.cpp +++ b/src/MCP2515.cpp @@ -248,11 +248,11 @@ int MCP2515Class::endPacket() // Print the status uint8_t s = readRegister(REG_TXBnCTRL(n)); - _debug->printf("TXB%d CTRL=0x%02X TXREQ=%d ABTF=%d MLOA=%d\n", - n, s, - !!(s & 0x08), - !!(s & 0x10), - !!(s & 0x20)); + 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); @@ -462,7 +462,11 @@ void MCP2515Class::dumpErrors() { uint8_t eflg = readRegister(REG_EFLG); uint8_t tec = readRegister(REG_TEC); uint8_t rec = readRegister(REG_REC); - _debug->printf("ERRIF! EFLG=0x%02X TEC=%u REC=%u\n", eflg, tec, rec); + + if (_debug != nullptr) { + _debug->printf("ERRIF! EFLG=0x%02X TEC=%u REC=%u\n", eflg, tec, rec); + } + modifyRegister(REG_CANINTF, FLAG_ERRIF, 0x00); } } From 21c56a113e2dc6617d095dd1badacc0e9e7d8743 Mon Sep 17 00:00:00 2001 From: "Bryan C. Geraghty" Date: Thu, 26 Jun 2025 17:56:42 +0000 Subject: [PATCH 17/17] Renamed packet to frame for clarity --- src/CANController.cpp | 30 +++++++++++++++--------------- src/CANController.h | 20 ++++++++++---------- src/ESP32SJA1000.cpp | 18 +++++++++--------- src/ESP32SJA1000.h | 4 ++-- src/MCP2515.cpp | 11 +++++------ src/MCP2515.h | 4 ++-- 6 files changed, 43 insertions(+), 44 deletions(-) diff --git a/src/CANController.cpp b/src/CANController.cpp index 18b643b..b503b18 100644 --- a/src/CANController.cpp +++ b/src/CANController.cpp @@ -7,7 +7,7 @@ CANControllerClass::CANControllerClass() : _onReceive(NULL), _onError(NULL), - _packetBegun(false), + _frameBegun(false), _txId(-1), _txExtended(-1), _txRtr(false), @@ -31,7 +31,7 @@ CANControllerClass::~CANControllerClass() int CANControllerClass::begin(long /*baudRate*/) { - _packetBegun = false; + _frameBegun = false; _txId = -1; _txRtr =false; _txDlc = 0; @@ -50,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; @@ -60,7 +60,7 @@ int CANControllerClass::beginPacket(int id, int dlc, bool rtr) return 0; } - _packetBegun = true; + _frameBegun = true; _txId = id; _txExtended = false; _txRtr = rtr; @@ -72,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; @@ -82,7 +82,7 @@ int CANControllerClass::beginExtendedPacket(long id, int dlc, bool rtr) return 0; } - _packetBegun = true; + _frameBegun = true; _txId = id; _txExtended = true; _txRtr = rtr; @@ -94,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; @@ -108,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; } @@ -140,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; } diff --git a/src/CANController.h b/src/CANController.h index 8a7b6a1..798e23c 100644 --- a/src/CANController.h +++ b/src/CANController.h @@ -6,7 +6,7 @@ #include -// typedef std::function TCallback; +// typedef std::function TCallback; // typedef std::function TErrorCallback; typedef void (*TCallback)(int); typedef void (*TErrorCallback)(int, int); @@ -17,15 +17,15 @@ class CANControllerClass : public Stream { 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); @@ -58,7 +58,7 @@ class CANControllerClass : public Stream { 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 191676a..ee01f87 100644 --- a/src/ESP32SJA1000.cpp +++ b/src/ESP32SJA1000.cpp @@ -108,7 +108,7 @@ int ESP32SJA1000Class::begin(long baudRate) // 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 packet, sends response, scaner acks, triggers CAN error on next scanner tx bit + // 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; @@ -116,7 +116,7 @@ int ESP32SJA1000Class::begin(long baudRate) // 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 packet CRC + // 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; @@ -207,9 +207,9 @@ bool ESP32SJA1000Class::isReadyToTransmit() { return isTxBufferFree() && isBusRecessive(); } -int ESP32SJA1000Class::endPacket() +int ESP32SJA1000Class::endFrame() { - if (!CANControllerClass::endPacket()) { + if (!CANControllerClass::endFrame()) { return 0; } @@ -262,10 +262,10 @@ int ESP32SJA1000Class::endPacket() return 1; } -int ESP32SJA1000Class::parsePacket() +int ESP32SJA1000Class::parseFrame() { if ((readRegister(REG_SR) & 0x01) != 0x01) { - // no packet + // no frame return 0; } @@ -435,9 +435,9 @@ void ESP32SJA1000Class::handleInterrupt() } if (ir & 0x01) { - // received packet, parse and call callback - parsePacket(); - _onReceive(available()); // Trigger packet buffering callback + // received frame, parse and call callback + parseFrame(); + _onReceive(available()); // Trigger frame buffering callback } } while (ir); } diff --git a/src/ESP32SJA1000.h b/src/ESP32SJA1000.h index d266361..5389532 100644 --- a/src/ESP32SJA1000.h +++ b/src/ESP32SJA1000.h @@ -25,9 +25,9 @@ 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(TCallback callback); diff --git a/src/MCP2515.cpp b/src/MCP2515.cpp index 5891607..9ab82a0 100644 --- a/src/MCP2515.cpp +++ b/src/MCP2515.cpp @@ -180,9 +180,9 @@ void MCP2515Class::end() CANControllerClass::end(); } -int MCP2515Class::endPacket() +int MCP2515Class::endFrame() { - if (!CANControllerClass::endPacket()) { + if (!CANControllerClass::endFrame()) { return 0; } @@ -252,14 +252,13 @@ int MCP2515Class::endPacket() _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 aborted ? 0 : 1; } -int MCP2515Class::parsePacket() +int MCP2515Class::parseFrame() { int n; @@ -527,11 +526,11 @@ void MCP2515Class::handleInterrupt() return; // no frame in RXB0 or RXB1 } - int result = parsePacket(); + int result = parseFrame(); if (result > 0) { // if (_debug != nullptr) { - // _debug->print("parsePacket result:"); + // _debug->print("parseFrame result:"); // _debug->println(result); // } _onReceive(available()); diff --git a/src/MCP2515.h b/src/MCP2515.h index cd06f65..f3c4c1c 100644 --- a/src/MCP2515.h +++ b/src/MCP2515.h @@ -27,9 +27,9 @@ 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();