From 9f4dfe41cbe0551372364b55bbf4e230958001c6 Mon Sep 17 00:00:00 2001 From: MIT4NI Date: Thu, 21 Nov 2024 17:51:08 +0900 Subject: [PATCH] Add:MCP2562FD --- MCP2562FD 1.0.0/src/CAN.cpp | 36 +++ MCP2562FD 1.0.0/src/CAN.h | 18 ++ MCP2562FD 1.0.0/src/CANController.cpp | 216 ++++++++++++++ MCP2562FD 1.0.0/src/CANController.h | 71 +++++ MCP2562FD 1.0.0/src/ESP32SJA1000.cpp | 414 ++++++++++++++++++++++++++ MCP2562FD 1.0.0/src/ESP32SJA1000.h | 64 ++++ 6 files changed, 819 insertions(+) create mode 100644 MCP2562FD 1.0.0/src/CAN.cpp create mode 100644 MCP2562FD 1.0.0/src/CAN.h create mode 100644 MCP2562FD 1.0.0/src/CANController.cpp create mode 100644 MCP2562FD 1.0.0/src/CANController.h create mode 100644 MCP2562FD 1.0.0/src/ESP32SJA1000.cpp create mode 100644 MCP2562FD 1.0.0/src/ESP32SJA1000.h diff --git a/MCP2562FD 1.0.0/src/CAN.cpp b/MCP2562FD 1.0.0/src/CAN.cpp new file mode 100644 index 0000000..7ad4051 --- /dev/null +++ b/MCP2562FD 1.0.0/src/CAN.cpp @@ -0,0 +1,36 @@ +// CAN CREATE Library + +#include "CAN.h" + +//---------------------------- +// return 0:ACK ERROR +//---------------------------- +int CAN_CREATE::sendPacket(int id, char data) { + // set data + beginPacket(id); + write(data); + // send data + return endPacket(); +} +int CAN_CREATE::sendBytes(int id, uint8_t* tx, size_t size) { + beginPacket(id); + write(tx, size); + return endPacket(); +} + +int CAN_CREATE::available() { + int packetSize = parsePacket(); // Datafield value (DLC) + if (packetRtr()) { // RTR 0:data 1:remote + return 0; + } + return CANControllerClass::available(); // number of bytes available for + // reading +} + +int CAN_CREATE::read(long* pID) { + if (!CANControllerClass::available()) { + return -1; + } + *pID = packetId(); + return _rxData[_rxIndex++]; +} \ No newline at end of file diff --git a/MCP2562FD 1.0.0/src/CAN.h b/MCP2562FD 1.0.0/src/CAN.h new file mode 100644 index 0000000..2dbef3d --- /dev/null +++ b/MCP2562FD 1.0.0/src/CAN.h @@ -0,0 +1,18 @@ +// Copyright (c) Sandeep Mistry. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full +// license information. + +#ifndef CAN_H +#define CAN_H + +#include "ESP32SJA1000.h" + +class CAN_CREATE : public ESP32SJA1000Class { + public: + int sendPacket(int id, char data); // send data + int available(); // receive data + int read(long* pID); + int sendBytes(int id, uint8_t* tx, size_t size); +}; + +#endif diff --git a/MCP2562FD 1.0.0/src/CANController.cpp b/MCP2562FD 1.0.0/src/CANController.cpp new file mode 100644 index 0000000..0890eec --- /dev/null +++ b/MCP2562FD 1.0.0/src/CANController.cpp @@ -0,0 +1,216 @@ +// Copyright (c) Sandeep Mistry. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "CANController.h" + +CANControllerClass::CANControllerClass() : + _onReceive(NULL), + + _packetBegun(false), + _txId(-1), + _txExtended(-1), + _txRtr(false), + _txDlc(0), + _txLength(0), + + _rxId(-1), + _rxExtended(false), + _rxRtr(false), + _rxDlc(0), + _rxLength(0), + _rxIndex(0) +{ + // overide Stream timeout value + setTimeout(0); +} + +CANControllerClass::~CANControllerClass() +{ +} + +int CANControllerClass::begin(long /*baudRate*/) +{ + _packetBegun = false; + _txId = -1; + _txRtr =false; + _txDlc = 0; + _txLength = 0; + + _rxId = -1; + _rxRtr = false; + _rxDlc = 0; + _rxLength = 0; + _rxIndex = 0; + + return 1; +} + +void CANControllerClass::end() +{ +} + +int CANControllerClass::beginPacket(int id, int dlc, bool rtr) +{ + if (id < 0 || id > 0x7FF) { + return 0; + } + + if (dlc > 8) { + return 0; + } + + _packetBegun = true; + _txId = id; + _txExtended = false; + _txRtr = rtr; + _txDlc = dlc; + _txLength = 0; + + memset(_txData, 0x00, sizeof(_txData)); + + return 1; +} + +int CANControllerClass::beginExtendedPacket(long id, int dlc, bool rtr) +{ + if (id < 0 || id > 0x1FFFFFFF) { + return 0; + } + + if (dlc > 8) { + return 0; + } + + _packetBegun = true; + _txId = id; + _txExtended = true; + _txRtr = rtr; + _txDlc = dlc; + _txLength = 0; + + memset(_txData, 0x00, sizeof(_txData)); + + return 1; +} + +int CANControllerClass::endPacket() +{ + if (!_packetBegun) { + return 0; + } + _packetBegun = false; + + if (_txDlc >= 0) { + _txLength = _txDlc; + } + + return 1; +} + +int CANControllerClass::parsePacket() +{ + return 0; +} + +long CANControllerClass::packetId() +{ + return _rxId; +} + +bool CANControllerClass::packetExtended() +{ + return _rxExtended; +} + +bool CANControllerClass::packetRtr() +{ + return _rxRtr; +} + +int CANControllerClass::packetDlc() +{ + return _rxDlc; +} + +size_t CANControllerClass::write(uint8_t byte) +{ + return write(&byte, sizeof(byte)); +} + +size_t CANControllerClass::write(const uint8_t *buffer, size_t size) +{ + if (!_packetBegun) { + return 0; + } + + if (size > (sizeof(_txData) - _txLength)) { + size = sizeof(_txData) - _txLength; + } + + memcpy(&_txData[_txLength], buffer, size); + _txLength += size; + + return size; +} + +int CANControllerClass::available() +{ + return (_rxLength - _rxIndex); +} + +int CANControllerClass::read() +{ + if (!available()) { + return -1; + } + + return _rxData[_rxIndex++]; +} + +int CANControllerClass::peek() +{ + if (!available()) { + return -1; + } + + return _rxData[_rxIndex]; +} + +void CANControllerClass::flush() +{ +} + +void CANControllerClass::onReceive(void(*callback)(int)) +{ + _onReceive = callback; +} + +int CANControllerClass::filter(int /*id*/, int /*mask*/) +{ + return 0; +} + +int CANControllerClass::filterExtended(long /*id*/, long /*mask*/) +{ + return 0; +} + +int CANControllerClass::observe() +{ + return 0; +} + +int CANControllerClass::loopback() +{ + return 0; +} + +int CANControllerClass::sleep() +{ + return 0; +} + +int CANControllerClass::wakeup() +{ + return 0; +} diff --git a/MCP2562FD 1.0.0/src/CANController.h b/MCP2562FD 1.0.0/src/CANController.h new file mode 100644 index 0000000..cdaeb94 --- /dev/null +++ b/MCP2562FD 1.0.0/src/CANController.h @@ -0,0 +1,71 @@ +// Copyright (c) Sandeep Mistry. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef CAN_CONTROLLER_H +#define CAN_CONTROLLER_H + +#include + +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(); + + virtual int parsePacket(); + long packetId(); + bool packetExtended(); + bool packetRtr(); + int packetDlc(); + + // from Print + virtual size_t write(uint8_t byte); + virtual size_t write(const uint8_t *buffer, size_t size); + + // from Stream + virtual int available(); + virtual int read(); + virtual int peek(); + virtual void flush(); + + virtual void onReceive(void(*callback)(int)); + + virtual int filter(int id) { return filter(id, 0x7ff); } + virtual int filter(int id, int mask); + virtual int filterExtended(long id) { return filterExtended(id, 0x1fffffff); } + virtual int filterExtended(long id, long mask); + + virtual int observe(); + virtual int loopback(); + virtual int sleep(); + virtual int wakeup(); + +protected: + CANControllerClass(); + virtual ~CANControllerClass(); + +protected: + void (*_onReceive)(int); + + bool _packetBegun; + long _txId; + bool _txExtended; + bool _txRtr; + int _txDlc; + int _txLength; + uint8_t _txData[8]; + + long _rxId; + bool _rxExtended; + bool _rxRtr; + int _rxDlc; + int _rxLength; + int _rxIndex; + uint8_t _rxData[8]; +}; + +#endif diff --git a/MCP2562FD 1.0.0/src/ESP32SJA1000.cpp b/MCP2562FD 1.0.0/src/ESP32SJA1000.cpp new file mode 100644 index 0000000..040f200 --- /dev/null +++ b/MCP2562FD 1.0.0/src/ESP32SJA1000.cpp @@ -0,0 +1,414 @@ +// Copyright (c) Sandeep Mistry. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifdef ARDUINO_ARCH_ESP32 + +#include "esp_intr.h" +#include "soc/dport_reg.h" +#include "driver/gpio.h" + +#include "ESP32SJA1000.h" + +#define REG_BASE 0x3ff6b000 + +#define REG_MOD 0x00 +#define REG_CMR 0x01 +#define REG_SR 0x02 +#define REG_IR 0x03 +#define REG_IER 0x04 + +#define REG_BTR0 0x06 +#define REG_BTR1 0x07 +#define REG_OCR 0x08 + +#define REG_ALC 0x0b +#define REG_ECC 0x0c +#define REG_EWLR 0x0d +#define REG_RXERR 0x0e +#define REG_TXERR 0x0f +#define REG_SFF 0x10 +#define REG_EFF 0x10 +#define REG_ACRn(n) (0x10 + n) +#define REG_AMRn(n) (0x14 + n) + +#define REG_CDR 0x1F + + +ESP32SJA1000Class::ESP32SJA1000Class() : + CANControllerClass(), + _rxPin(DEFAULT_CAN_RX_PIN), + _txPin(DEFAULT_CAN_TX_PIN), + _loopback(false), + _intrHandle(NULL) +{ +} + +ESP32SJA1000Class::~ESP32SJA1000Class() +{ +} + +int ESP32SJA1000Class::begin(long baudRate) +{ + CANControllerClass::begin(baudRate); + + _loopback = false; + + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_CAN_RST); + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_CAN_CLK_EN); + + // RX pin + gpio_set_direction(_rxPin, GPIO_MODE_INPUT); + gpio_matrix_in(_rxPin, CAN_RX_IDX, 0); + gpio_pad_select_gpio(_rxPin); + + // TX pin + gpio_set_direction(_txPin, GPIO_MODE_OUTPUT); + gpio_matrix_out(_txPin, CAN_TX_IDX, 0, 0); + 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 + + 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); + 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 + 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 + // reset error counters + writeRegister(REG_TXERR, 0x00); + writeRegister(REG_RXERR, 0x00); + + // clear errors and interrupts + readRegister(REG_ECC); + readRegister(REG_IR); + + // normal mode + modifyRegister(REG_MOD, 0x08, 0x08); + modifyRegister(REG_MOD, 0x17, 0x00); + + return 1; +} + +void ESP32SJA1000Class::end() +{ + if (_intrHandle) { + esp_intr_free(_intrHandle); + _intrHandle = NULL; + } + + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_CAN_RST); + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_CAN_CLK_EN); + + CANControllerClass::end(); +} + +int ESP32SJA1000Class::endPacket() +{ + if (!CANControllerClass::endPacket()) { + return PAR_ERROR; + } + // wait for TX buffer to free + while ((readRegister(REG_SR) & 0x04) != 0x04) { + yield(); + } + + int dataReg; + + if (_txExtended) {// Extended packet + writeRegister(REG_EFF, 0x80 | (_txRtr ? 0x40 : 0x00) | (0x0f & _txLength)); + writeRegister(REG_EFF + 1, _txId >> 21); + writeRegister(REG_EFF + 2, _txId >> 13); + writeRegister(REG_EFF + 3, _txId >> 5); + writeRegister(REG_EFF + 4, _txId << 3); + + dataReg = REG_EFF + 5; + } else {// NORMAL packet + writeRegister(REG_SFF, (_txRtr ? 0x40 : 0x00) | (0x0f & _txLength)); + writeRegister(REG_SFF + 1, _txId >> 3); + writeRegister(REG_SFF + 2, _txId << 5); + + dataReg = REG_SFF + 3; + } + + for (int i = 0; i < _txLength; i++) { + writeRegister(dataReg + i, _txData[i]); + } + + if ( _loopback) { + // self reception request + modifyRegister(REG_CMR, 0x1f, 0x10); + } else { + // transmit request + modifyRegister(REG_CMR, 0x1f, 0x01); + } + + // wait for TX complete + + while ((readRegister(REG_SR) & 0x08) != 0x08) {// not bus-off + //ECC check + if (readRegister(REG_ECC) == 0xd9) { + modifyRegister(REG_CMR, 0x1f, 0x02); + return ACK_ERROR; + } + yield(); + } + + return CAN_OK; +} + +int ESP32SJA1000Class::parsePacket() +{ + if ((readRegister(REG_SR) & 0x01) != 0x01) { + // no packet + return 0; + } + + _rxExtended = (readRegister(REG_SFF) & 0x80) ? true : false; + _rxRtr = (readRegister(REG_SFF) & 0x40) ? true : false; + _rxDlc = (readRegister(REG_SFF) & 0x0f); + _rxIndex = 0; + + int dataReg; + + if (_rxExtended) { + _rxId = (readRegister(REG_EFF + 1) << 21) | + (readRegister(REG_EFF + 2) << 13) | + (readRegister(REG_EFF + 3) << 5) | + (readRegister(REG_EFF + 4) >> 3); + + dataReg = REG_EFF + 5; + } else { + _rxId = (readRegister(REG_SFF + 1) << 3) | ((readRegister(REG_SFF + 2) >> 5) & 0x07); + + dataReg = REG_SFF + 3; + } + + if (_rxRtr) { + _rxLength = 0; + } else { + _rxLength = _rxDlc; + + for (int i = 0; i < _rxLength; i++) { + _rxData[i] = readRegister(dataReg + i); + } + } + + // release RX buffer + modifyRegister(REG_CMR, 0x04, 0x04); + + return _rxDlc; +} + +void ESP32SJA1000Class::onReceive(void(*callback)(int)) +{ + CANControllerClass::onReceive(callback); + + if (_intrHandle) { + esp_intr_free(_intrHandle); + _intrHandle = NULL; + } + + if (callback) { + esp_intr_alloc(ETS_CAN_INTR_SOURCE, 0, ESP32SJA1000Class::onInterrupt, this, &_intrHandle); + } +} + +int ESP32SJA1000Class::filter(int id, int mask) +{ + id &= 0x7ff; + mask = ~(mask & 0x7ff); + + modifyRegister(REG_MOD, 0x17, 0x01); // reset + + writeRegister(REG_ACRn(0), id >> 3); + writeRegister(REG_ACRn(1), id << 5); + writeRegister(REG_ACRn(2), 0x00); + writeRegister(REG_ACRn(3), 0x00); + + writeRegister(REG_AMRn(0), mask >> 3); + writeRegister(REG_AMRn(1), (mask << 5) | 0x1f); + writeRegister(REG_AMRn(2), 0xff); + writeRegister(REG_AMRn(3), 0xff); + + modifyRegister(REG_MOD, 0x17, 0x00); // normal + + return 1; +} + +int ESP32SJA1000Class::filterExtended(long id, long mask) +{ + id &= 0x1FFFFFFF; + mask &= ~(mask & 0x1FFFFFFF); + + modifyRegister(REG_MOD, 0x17, 0x01); // reset + + writeRegister(REG_ACRn(0), id >> 21); + writeRegister(REG_ACRn(1), id >> 13); + writeRegister(REG_ACRn(2), id >> 5); + writeRegister(REG_ACRn(3), id << 3); + + writeRegister(REG_AMRn(0), mask >> 21); + writeRegister(REG_AMRn(1), mask >> 13); + writeRegister(REG_AMRn(2), mask >> 5); + writeRegister(REG_AMRn(3), (mask << 3) | 0x1f); + + modifyRegister(REG_MOD, 0x17, 0x00); // normal + + return 1; +} + +int ESP32SJA1000Class::observe() +{ + modifyRegister(REG_MOD, 0x17, 0x01); // reset + modifyRegister(REG_MOD, 0x17, 0x02); // observe + + return 1; +} + +int ESP32SJA1000Class::loopback() +{ + _loopback = true; + + modifyRegister(REG_MOD, 0x17, 0x01); // reset + modifyRegister(REG_MOD, 0x17, 0x04); // self test mode + + return 1; +} + +int ESP32SJA1000Class::sleep() +{ + modifyRegister(REG_MOD, 0x1f, 0x10); + + return 1; +} + +int ESP32SJA1000Class::wakeup() +{ + modifyRegister(REG_MOD, 0x1f, 0x00); + + return 1; +} + +void ESP32SJA1000Class::setPins(int rx, int tx) +{ + _rxPin = (gpio_num_t)rx; + _txPin = (gpio_num_t)tx; +} + +void ESP32SJA1000Class::dumpRegisters(Stream& out) +{ + for (int i = 0; i < 32; i++) { + byte b = readRegister(i); + + out.print("0x"); + if (i < 16) { + out.print('0'); + } + out.print(i, HEX); + out.print(": 0x"); + if (b < 16) { + out.print('0'); + } + out.println(b, HEX); + } +} + +void ESP32SJA1000Class::handleInterrupt() +{ + uint8_t ir = readRegister(REG_IR); + + if (ir & 0x01) { + // received packet, parse and call callback + parsePacket(); + + _onReceive(available()); + } +} + +uint8_t ESP32SJA1000Class::readRegister(uint8_t address) +{ + volatile uint32_t* reg = (volatile uint32_t*)(REG_BASE + address * 4); + + return *reg; +} + +void ESP32SJA1000Class::modifyRegister(uint8_t address, uint8_t mask, uint8_t value) +{ + volatile uint32_t* reg = (volatile uint32_t*)(REG_BASE + address * 4); + + *reg = (*reg & ~mask) | value; +} + +void ESP32SJA1000Class::writeRegister(uint8_t address, uint8_t value) +{ + volatile uint32_t* reg = (volatile uint32_t*)(REG_BASE + address * 4); + + *reg = value; +} + +void ESP32SJA1000Class::onInterrupt(void* arg) +{ + ((ESP32SJA1000Class*)arg)->handleInterrupt(); +} + +#endif diff --git a/MCP2562FD 1.0.0/src/ESP32SJA1000.h b/MCP2562FD 1.0.0/src/ESP32SJA1000.h new file mode 100644 index 0000000..81c271f --- /dev/null +++ b/MCP2562FD 1.0.0/src/ESP32SJA1000.h @@ -0,0 +1,64 @@ +// Copyright (c) Sandeep Mistry. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef ESP32_SJA1000_H +#define ESP32_SJA1000_H + +#include "CANController.h" + + +#define PAR_ERROR 0 +#define ACK_ERROR 1 +#define CAN_OK 2 + +#define DEFAULT_CAN_RX_PIN GPIO_NUM_4 +#define DEFAULT_CAN_TX_PIN GPIO_NUM_5 + +class ESP32SJA1000Class : public CANControllerClass { + +public: + ESP32SJA1000Class(); + virtual ~ESP32SJA1000Class(); + + virtual int begin(long baudRate); + virtual void end(); + + virtual int endPacket(); + + virtual int parsePacket(); + + virtual void onReceive(void(*callback)(int)); + + using CANControllerClass::filter; + virtual int filter(int id, int mask); + using CANControllerClass::filterExtended; + virtual int filterExtended(long id, long mask); + + virtual int observe(); + virtual int loopback(); + virtual int sleep(); + virtual int wakeup(); + + void setPins(int rx, int tx); + + void dumpRegisters(Stream& out); + +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); + + static void onInterrupt(void* arg); + +private: + gpio_num_t _rxPin; + gpio_num_t _txPin; + bool _loopback; + intr_handle_t _intrHandle; +}; + +#endif