Fix multiple defines, add ACAN locally

This commit is contained in:
Daniel 2023-08-27 22:47:11 +03:00
parent 284c32cda8
commit 188812a565
11 changed files with 1993 additions and 11 deletions

957
Software/ACAN2515.cpp Normal file
View file

@ -0,0 +1,957 @@
//··································································································
// A CAN driver for MCP2515
// by Pierre Molinaro
// https://github.com/pierremolinaro/acan2515
//··································································································
#include "ACAN2515.h"
//··································································································
// MCP2515 COMMANDS
//··································································································
static const uint8_t RESET_COMMAND = 0xC0 ;
static const uint8_t WRITE_COMMAND = 0x02 ;
static const uint8_t READ_COMMAND = 0x03 ;
static const uint8_t BIT_MODIFY_COMMAND = 0x05 ;
static const uint8_t LOAD_TX_BUFFER_COMMAND = 0x40 ;
static const uint8_t REQUEST_TO_SEND_COMMAND = 0x80 ;
static const uint8_t READ_FROM_RXB0SIDH_COMMAND = 0x90 ;
static const uint8_t READ_FROM_RXB1SIDH_COMMAND = 0x94 ;
static const uint8_t READ_STATUS_COMMAND = 0xA0 ;
static const uint8_t RX_STATUS_COMMAND = 0xB0 ;
//··································································································
// MCP2515 REGISTERS
//··································································································
static const uint8_t BFPCTRL_REGISTER = 0x0C ;
static const uint8_t TXRTSCTRL_REGISTER = 0x0D ;
static const uint8_t CANSTAT_REGISTER = 0x0E ;
static const uint8_t CANCTRL_REGISTER = 0x0F ;
static const uint8_t TEC_REGISTER = 0x1C ;
static const uint8_t REC_REGISTER = 0x1D ;
static const uint8_t RXM0SIDH_REGISTER = 0x20 ;
static const uint8_t RXM1SIDH_REGISTER = 0x24 ;
static const uint8_t CNF3_REGISTER = 0x28 ;
static const uint8_t CNF2_REGISTER = 0x29 ;
static const uint8_t CNF1_REGISTER = 0x2A ;
static const uint8_t CANINTF_REGISTER = 0x2C ;
static const uint8_t EFLG_REGISTER = 0x2D ;
static const uint8_t TXB0CTRL_REGISTER = 0x30 ;
static const uint8_t TXB1CTRL_REGISTER = 0x40 ;
static const uint8_t TXB2CTRL_REGISTER = 0x50 ;
static const uint8_t RXB0CTRL_REGISTER = 0x60 ;
static const uint8_t RXB1CTRL_REGISTER = 0x70 ;
static const uint8_t RXFSIDH_REGISTER [6] = {0x00, 0x04, 0x08, 0x10, 0x14, 0x18} ;
//··································································································
// Note about ESP32
//··································································································
//
// It appears that Arduino ESP32 interrupts are managed in a completely different way
// from "usual" Arduino:
// - SPI.usingInterrupt is not implemented;
// - noInterrupts() and interrupts() are NOPs;
// - interrupt service routines should be fast, otherwise you get smothing like
// "Guru Meditation Error: Core 1 panic'ed (Interrupt wdt timeout on CPU1)".
// So we handle the ESP32 interrupt in the following way:
// - interrupt service routine performs a xSemaphoreGive on mISRSemaphore of can driver
// - this activates the myESP32Task task that performs "isr_core" that is done
// by interrupt service routine in "usual" Arduino;
// - as this task runs in parallel with setup / loop routines, SPI access is natively
// protected by the beginTransaction / endTransaction pair, that manages a mutex.
//··································································································
#ifdef ARDUINO_ARCH_ESP32
static void myESP32Task (void * pData) {
ACAN2515 * canDriver = (ACAN2515 *) pData ;
while (1) {
canDriver->attachMCP2515InterruptPin () ;
xSemaphoreTake (canDriver->mISRSemaphore, portMAX_DELAY) ;
bool loop = true ;
while (loop) {
loop = canDriver->isr_core () ;
}
}
}
#endif
//··································································································
#ifdef ARDUINO_ARCH_ESP32
void ACAN2515::attachMCP2515InterruptPin (void) {
attachInterrupt (digitalPinToInterrupt (mINT), mInterruptServiceRoutine, ONLOW) ;
}
#endif
//··································································································
// CONSTRUCTOR, HARDWARE SPI
//··································································································
ACAN2515::ACAN2515 (const uint8_t inCS, // CS input of MCP2515
SPIClass & inSPI, // Hardware SPI object
const uint8_t inINT) : // INT output of MCP2515
mSPI (inSPI),
mSPISettings (10UL * 1000UL * 1000UL, MSBFIRST, SPI_MODE0), // 10 MHz, UL suffix is required for Arduino Uno
mCS (inCS),
mINT (inINT),
mRolloverEnable (false),
#ifdef ARDUINO_ARCH_ESP32
mISRSemaphore (xSemaphoreCreateCounting (10, 0)),
#endif
mReceiveBuffer (),
mCallBackFunctionArray (),
mTXBIsFree () {
for (uint8_t i=0 ; i<6 ; i++) {
mCallBackFunctionArray [i] = NULL ;
}
}
//··································································································
// BEGIN
//··································································································
uint16_t ACAN2515::begin (const ACAN2515Settings & inSettings,
void (* inInterruptServiceRoutine) (void)) {
return beginWithoutFilterCheck (inSettings, inInterruptServiceRoutine, ACAN2515Mask (), ACAN2515Mask (), NULL, 0) ;
}
//··································································································
uint16_t ACAN2515::begin (const ACAN2515Settings & inSettings,
void (* inInterruptServiceRoutine) (void),
const ACAN2515Mask inRXM0,
const ACAN2515AcceptanceFilter inAcceptanceFilters [],
const uint8_t inAcceptanceFilterCount) {
uint16_t errorCode = 0 ;
if (inAcceptanceFilterCount == 0) {
errorCode = kOneFilterMaskRequiresOneOrTwoAcceptanceFilters ;
}else if (inAcceptanceFilterCount > 2) {
errorCode = kOneFilterMaskRequiresOneOrTwoAcceptanceFilters ;
}else if (inAcceptanceFilters == NULL) {
errorCode = kAcceptanceFilterArrayIsNULL ;
}else{
errorCode = beginWithoutFilterCheck (inSettings, inInterruptServiceRoutine,
inRXM0, inRXM0, inAcceptanceFilters, inAcceptanceFilterCount) ;
}
return errorCode ;
}
//··································································································
uint16_t ACAN2515::begin (const ACAN2515Settings & inSettings,
void (* inInterruptServiceRoutine) (void),
const ACAN2515Mask inRXM0,
const ACAN2515Mask inRXM1,
const ACAN2515AcceptanceFilter inAcceptanceFilters [],
const uint8_t inAcceptanceFilterCount) {
uint16_t errorCode = 0 ;
if (inAcceptanceFilterCount < 3) {
errorCode = kTwoFilterMasksRequireThreeToSixAcceptanceFilters ;
}else if (inAcceptanceFilterCount > 6) {
errorCode = kTwoFilterMasksRequireThreeToSixAcceptanceFilters ;
}else if (inAcceptanceFilters == NULL) {
errorCode = kAcceptanceFilterArrayIsNULL ;
}else{
errorCode = beginWithoutFilterCheck (inSettings, inInterruptServiceRoutine,
inRXM0, inRXM1, inAcceptanceFilters, inAcceptanceFilterCount) ;
}
return errorCode ;
}
//··································································································
uint16_t ACAN2515::beginWithoutFilterCheck (const ACAN2515Settings & inSettings,
void (* inInterruptServiceRoutine) (void),
const ACAN2515Mask inRXM0,
const ACAN2515Mask inRXM1,
const ACAN2515AcceptanceFilter inAcceptanceFilters [],
const uint8_t inAcceptanceFilterCount) {
uint16_t errorCode = 0 ; // Means no error
//----------------------------------- Check mINT has interrupt capability
const int8_t itPin = digitalPinToInterrupt (mINT) ;
if ((mINT != 255) && (itPin == NOT_AN_INTERRUPT)) {
errorCode = kINTPinIsNotAnInterrupt ;
}
//----------------------------------- Check interrupt service routine is not null
if ((mINT != 255) && (inInterruptServiceRoutine == NULL)) {
errorCode |= kISRIsNull ;
}
//----------------------------------- Check consistency between ISR and INT pin
if ((mINT == 255) && (inInterruptServiceRoutine != NULL)) {
errorCode |= kISRNotNullAndNoIntPin ;
}
//----------------------------------- if no error, configure port and MCP2515
if (errorCode == 0) {
//--- Configure ports
if (mINT != 255) { // 255 means interrupt is not used
pinMode (mINT, INPUT_PULLUP) ;
}
pinMode (mCS, OUTPUT) ;
digitalWrite (mCS, HIGH) ; // CS is high outside a command
//--- Send software reset to MCP2515
mSPI.beginTransaction (mSPISettings) ;
select () ;
mSPI.transfer (RESET_COMMAND) ;
unselect () ;
mSPI.endTransaction () ;
//--- DS20001801J, page 55: The Oscillator Start-up Timer keeps the device in a Reset
// state for 128 OSC1 clock cycles after the occurrence of a Power-on Reset, SPI Reset,
// after the assertion of the RESET pin, and after a wake-up from Sleep mode
// Fot a 1 MHz clock --> 128 µs
// So delayMicroseconds (10) is too short --> use delay (2)
// delayMicroseconds (10) ; // Removed in release 2.1.2
delay (2) ; // Added in release 2.1.2
//--- Internal begin
errorCode = internalBeginOperation (inSettings,
inRXM0,
inRXM1,
inAcceptanceFilters,
inAcceptanceFilterCount) ;
}
//--- Configure interrupt only if no error (thanks to mvSarma)
if (errorCode == 0) {
if (mINT != 255) { // 255 means interrupt is not used
#ifdef ARDUINO_ARCH_ESP32
mInterruptServiceRoutine = inInterruptServiceRoutine ;
#else
mSPI.usingInterrupt (itPin) ; // usingInterrupt is not implemented in Arduino ESP32
attachInterrupt (itPin, inInterruptServiceRoutine, LOW) ;
#endif
}
#ifdef ARDUINO_ARCH_ESP32
xTaskCreate (myESP32Task, "ACAN2515Handler", 1024, this, 256, NULL) ;
#endif
}
//----------------------------------- Return
return errorCode ;
}
//··································································································
// MESSAGE RECEPTION
//··································································································
bool ACAN2515::available (void) {
#ifdef ARDUINO_ARCH_ESP32
mSPI.beginTransaction (mSPISettings) ; // For ensuring mutual exclusion access
#else
noInterrupts () ;
#endif
const bool hasReceivedMessage = mReceiveBuffer.count () > 0 ;
#ifdef ARDUINO_ARCH_ESP32
mSPI.endTransaction () ;
#else
interrupts () ;
#endif
return hasReceivedMessage ;
}
//··································································································
bool ACAN2515::receive (CANMessage & outMessage) {
#ifdef ARDUINO_ARCH_ESP32
mSPI.beginTransaction (mSPISettings) ; // For ensuring mutual exclusion access
#else
noInterrupts () ;
#endif
const bool hasReceivedMessage = mReceiveBuffer.remove (outMessage) ;
#ifdef ARDUINO_ARCH_ESP32
mSPI.endTransaction () ;
#else
interrupts () ;
#endif
//---
return hasReceivedMessage ;
}
//··································································································
bool ACAN2515::dispatchReceivedMessage (const tFilterMatchCallBack inFilterMatchCallBack) {
CANMessage receivedMessage ;
const bool hasReceived = receive (receivedMessage) ;
if (hasReceived) {
const uint8_t filterIndex = receivedMessage.idx ;
if (NULL != inFilterMatchCallBack) {
inFilterMatchCallBack (filterIndex) ;
}
ACANCallBackRoutine callBackFunction = mCallBackFunctionArray [filterIndex] ;
if (NULL != callBackFunction) {
callBackFunction (receivedMessage) ;
}
}
return hasReceived ;
}
//··································································································
// INTERRUPTS ARE DISABLED WHEN THESE FUNCTIONS ARE EXECUTED
//··································································································
uint16_t ACAN2515::internalBeginOperation (const ACAN2515Settings & inSettings,
const ACAN2515Mask inRXM0,
const ACAN2515Mask inRXM1,
const ACAN2515AcceptanceFilter inAcceptanceFilters [],
const uint8_t inAcceptanceFilterCount) {
uint16_t errorCode = 0 ; // Ok be default
//----------------------------------- Check if MCP2515 is accessible
mSPI.beginTransaction (mSPISettings) ;
write2515Register (CNF1_REGISTER, 0x55) ;
bool ok = read2515Register (CNF1_REGISTER) == 0x55 ;
if (ok) {
write2515Register (CNF1_REGISTER, 0xAA) ;
ok = read2515Register (CNF1_REGISTER) == 0xAA ;
}
if (!ok) {
errorCode = kNoMCP2515 ;
}
mSPI.endTransaction () ;
//----------------------------------- Check if settings are correct
if (!inSettings.mBitRateClosedToDesiredRate) {
errorCode |= kTooFarFromDesiredBitRate ;
}
if (inSettings.CANBitSettingConsistency () != 0) {
errorCode |= kInconsistentBitRateSettings ;
}
//----------------------------------- Allocate buffer
if (!mReceiveBuffer.initWithSize (inSettings.mReceiveBufferSize)) {
errorCode |= kCannotAllocateReceiveBuffer ;
}
if (!mTransmitBuffer [0].initWithSize (inSettings.mTransmitBuffer0Size)) {
errorCode |= kCannotAllocateTransmitBuffer0 ;
}
if (!mTransmitBuffer [1].initWithSize (inSettings.mTransmitBuffer1Size)) {
errorCode |= kCannotAllocateTransmitBuffer1 ;
}
if (!mTransmitBuffer [2].initWithSize (inSettings.mTransmitBuffer2Size)) {
errorCode |= kCannotAllocateTransmitBuffer2 ;
}
mTXBIsFree [0] = true ;
mTXBIsFree [1] = true ;
mTXBIsFree [2] = true ;
//----------------------------------- If ok, perform configuration
if (errorCode == 0) {
mSPI.beginTransaction (mSPISettings) ;
//----------------------------------- Set CNF3, CNF2, CNF1 and CANINTE registers
select () ;
mSPI.transfer (WRITE_COMMAND) ;
mSPI.transfer (CNF3_REGISTER) ;
//--- Register CNF3:
// Bit 7: SOF
// bit 6 --> 0: No Wake-up Filter bit
// Bit 5-3: -
// Bit 2-0: PHSEG2 - 1
const uint8_t cnf3 =
((inSettings.mCLKOUT_SOF_pin == ACAN2515Settings::SOF) << 6) /* SOF */ |
((inSettings.mPhaseSegment2 - 1) << 0) /* PHSEG2 */
;
mSPI.transfer (cnf3) ;
//--- Register CNF2:
// Bit 7 --> 1: BLTMODE
// bit 6: SAM
// Bit 5-3: PHSEG1 - 1
// Bit 2-0: PRSEG - 1
const uint8_t cnf2 =
0x80 /* BLTMODE */ |
(inSettings.mTripleSampling << 6) /* SAM */ |
((inSettings.mPhaseSegment1 - 1) << 3) /* PHSEG1 */ |
((inSettings.mPropagationSegment - 1) << 0) /* PRSEG */
;
mSPI.transfer (cnf2) ;
//--- Register CNF1:
// Bit 7-6: SJW - 1
// Bit 5-0: BRP - 1
const uint8_t cnf1 =
((inSettings.mSJW - 1) << 6) /* SJW */ | // Incorrect SJW setting fixed in 2.0.1
((inSettings.mBitRatePrescaler - 1) << 0) /* BRP */
;
mSPI.transfer (cnf1) ;
//--- Register CANINTE: activate interrupts
// Bit 7 --> 0: MERRE
// Bit 6 --> 0: WAKIE
// Bit 5 --> 0: ERRIE
// Bit 4 --> 1: TX2IE
// Bit 3 --> 1: TX1IE
// Bit 2 --> 1: TX0IE
// Bit 1 --> 1: RX1IE
// Bit 0 --> 1: RX0IE
mSPI.transfer (0x1F) ;
unselect () ;
//----------------------------------- Deactivate the RXnBF Pins (High Impedance State)
write2515Register (BFPCTRL_REGISTER, 0) ;
//----------------------------------- Set TXnRTS as inputs
write2515Register (TXRTSCTRL_REGISTER, 0);
//----------------------------------- RXBnCTRL
mRolloverEnable = inSettings.mRolloverEnable ;
const uint8_t acceptAll = (inAcceptanceFilterCount == 0) ? 0x60 : 0x00 ;
write2515Register (RXB0CTRL_REGISTER, acceptAll | (uint8_t (inSettings.mRolloverEnable) << 2)) ;
write2515Register (RXB1CTRL_REGISTER, acceptAll) ;
//----------------------------------- Setup mask registers
setupMaskRegister (inRXM0, RXM0SIDH_REGISTER) ;
setupMaskRegister (inRXM1, RXM1SIDH_REGISTER) ;
if (inAcceptanceFilterCount > 0) {
uint8_t idx = 0 ;
while (idx < inAcceptanceFilterCount) {
setupMaskRegister (inAcceptanceFilters [idx].mMask, RXFSIDH_REGISTER [idx]) ;
mCallBackFunctionArray [idx] = inAcceptanceFilters [idx].mCallBack ;
idx += 1 ;
}
while (idx < 6) {
setupMaskRegister (inAcceptanceFilters [inAcceptanceFilterCount-1].mMask, RXFSIDH_REGISTER [idx]) ;
mCallBackFunctionArray [idx] = inAcceptanceFilters [inAcceptanceFilterCount-1].mCallBack ;
idx += 1 ;
}
}
//----------------------------------- Set TXBi priorities
write2515Register (TXB0CTRL_REGISTER, inSettings.mTXBPriority & 3) ;
write2515Register (TXB1CTRL_REGISTER, (inSettings.mTXBPriority >> 2) & 3) ;
write2515Register (TXB2CTRL_REGISTER, (inSettings.mTXBPriority >> 4) & 3) ;
mSPI.endTransaction () ;
//----------------------------------- Reset device to requested mode
uint8_t canctrl = inSettings.mOneShotModeEnabled ? (1 << 3) : 0 ;
switch (inSettings.mCLKOUT_SOF_pin) {
case ACAN2515Settings::CLOCK :
canctrl |= 0x04 | 0x00 ; // Same as default setting
break ;
case ACAN2515Settings::CLOCK2 :
canctrl |= 0x04 | 0x01 ;
break ;
case ACAN2515Settings::CLOCK4 :
canctrl |= 0x04 | 0x02 ;
break ;
case ACAN2515Settings::CLOCK8 :
canctrl |= 0x04 | 0x03 ;
break ;
case ACAN2515Settings::SOF :
canctrl |= 0x04 ;
break ;
case ACAN2515Settings::HiZ :
break ;
}
//--- Request mode
const uint8_t requestedMode = (uint8_t) inSettings.mRequestedMode ;
errorCode |= setRequestedMode (canctrl | requestedMode) ;
}
//-----------------------------------
return errorCode ;
}
//··································································································
// setRequestedMode (private method)
//··································································································
uint16_t ACAN2515::setRequestedMode (const uint8_t inCANControlRegister) {
uint16_t errorCode = 0 ;
//--- Request mode
mSPI.beginTransaction (mSPISettings) ;
write2515Register (CANCTRL_REGISTER, inCANControlRegister) ;
mSPI.endTransaction () ;
//--- Wait until requested mode is reached (during 1 or 2 ms)
bool wait = true ;
const uint32_t deadline = millis () + 2 ;
while (wait) {
mSPI.beginTransaction (mSPISettings) ;
const uint8_t actualMode = read2515Register (CANSTAT_REGISTER) & 0xE0 ;
mSPI.endTransaction () ;
wait = actualMode != (inCANControlRegister & 0xE0) ;
if (wait && (millis () >= deadline)) {
errorCode |= kRequestedModeTimeOut ;
wait = false ;
}
}
//---
return errorCode ;
}
//··································································································
// Change Mode
//··································································································
uint16_t ACAN2515::changeModeOnTheFly (const ACAN2515Settings::RequestedMode inRequestedMode) {
//--- Read current mode register (for saving settings of bits 0 ... 4)
mSPI.beginTransaction (mSPISettings) ;
const uint8_t currentMode = read2515Register (CANCTRL_REGISTER) ;
mSPI.endTransaction () ;
//--- New mode
const uint8_t newMode = (currentMode & 0x1F) | (uint8_t) inRequestedMode ;
//--- Set new mode
const uint16_t errorCode = setRequestedMode (newMode) ;
//---
return errorCode ;
}
//··································································································
// Set filters on the fly
//··································································································
uint16_t ACAN2515::setFiltersOnTheFly (void) {
return internalSetFiltersOnTheFly (ACAN2515Mask (), ACAN2515Mask (), NULL, 0) ;
}
//··································································································
uint16_t ACAN2515::setFiltersOnTheFly (const ACAN2515Mask inRXM0,
const ACAN2515AcceptanceFilter inAcceptanceFilters [],
const uint8_t inAcceptanceFilterCount) {
uint16_t errorCode = 0 ;
if (inAcceptanceFilterCount == 0) {
errorCode = kOneFilterMaskRequiresOneOrTwoAcceptanceFilters ;
}else if (inAcceptanceFilterCount > 2) {
errorCode = kOneFilterMaskRequiresOneOrTwoAcceptanceFilters ;
}else if (inAcceptanceFilters == NULL) {
errorCode = kAcceptanceFilterArrayIsNULL ;
}else{
errorCode = internalSetFiltersOnTheFly (inRXM0, ACAN2515Mask (), inAcceptanceFilters, inAcceptanceFilterCount) ;
}
return errorCode ;
}
//··································································································
uint16_t ACAN2515::setFiltersOnTheFly (const ACAN2515Mask inRXM0,
const ACAN2515Mask inRXM1,
const ACAN2515AcceptanceFilter inAcceptanceFilters [],
const uint8_t inAcceptanceFilterCount) {
uint16_t errorCode = 0 ;
if (inAcceptanceFilterCount < 3) {
errorCode = kTwoFilterMasksRequireThreeToSixAcceptanceFilters ;
}else if (inAcceptanceFilterCount > 6) {
errorCode = kTwoFilterMasksRequireThreeToSixAcceptanceFilters ;
}else if (inAcceptanceFilters == NULL) {
errorCode = kAcceptanceFilterArrayIsNULL ;
}else{
errorCode = internalSetFiltersOnTheFly (inRXM0, inRXM1, inAcceptanceFilters, inAcceptanceFilterCount) ;
}
return errorCode ;
}
//··································································································
uint16_t ACAN2515::internalSetFiltersOnTheFly (const ACAN2515Mask inRXM0,
const ACAN2515Mask inRXM1,
const ACAN2515AcceptanceFilter inAcceptanceFilters [],
const uint8_t inAcceptanceFilterCount) {
//--- Read current mode register
mSPI.beginTransaction (mSPISettings) ;
const uint8_t currentMode = read2515Register (CANCTRL_REGISTER) ;
mSPI.endTransaction () ;
//--- Request configuration mode
const uint8_t configurationMode = (currentMode & 0x1F) | (0b100 << 5) ; // Preserve bits 0 ... 4
uint16_t errorCode = setRequestedMode (configurationMode) ;
//--- Setup mask registers
if (errorCode == 0) {
const uint8_t acceptAll = (inAcceptanceFilterCount == 0) ? 0x60 : 0x00 ;
write2515Register (RXB0CTRL_REGISTER, acceptAll | (uint8_t (mRolloverEnable) << 2)) ;
write2515Register (RXB1CTRL_REGISTER, acceptAll) ;
setupMaskRegister (inRXM0, RXM0SIDH_REGISTER) ;
setupMaskRegister (inRXM1, RXM1SIDH_REGISTER) ;
if (inAcceptanceFilterCount > 0) {
uint8_t idx = 0 ;
while (idx < inAcceptanceFilterCount) {
setupMaskRegister (inAcceptanceFilters [idx].mMask, RXFSIDH_REGISTER [idx]) ;
mCallBackFunctionArray [idx] = inAcceptanceFilters [idx].mCallBack ;
idx += 1 ;
}
while (idx < 6) {
setupMaskRegister (inAcceptanceFilters [inAcceptanceFilterCount-1].mMask, RXFSIDH_REGISTER [idx]) ;
mCallBackFunctionArray [idx] = inAcceptanceFilters [inAcceptanceFilterCount-1].mCallBack ;
idx += 1 ;
}
}
}
//--- Restore saved mode
if (errorCode == 0) {
errorCode = setRequestedMode (currentMode) ;
}
//---
return errorCode ;
}
//··································································································
// end
//··································································································
void ACAN2515::end (void) {
//--- Remove interrupt capability of mINT pin
if (mINT != 255) {
detachInterrupt (digitalPinToInterrupt (mINT)) ;
}
//--- Request configuration mode
const uint8_t configurationMode = (0b100 << 5) ;
const uint16_t errorCode __attribute__((unused)) = setRequestedMode (configurationMode) ;
//--- Deallocate driver buffers
mTransmitBuffer [0].free () ;
mTransmitBuffer [1].free () ;
mTransmitBuffer [2].free () ;
mReceiveBuffer.free () ;
}
//··································································································
// POLLING (ESP32)
//··································································································
#ifdef ARDUINO_ARCH_ESP32
void ACAN2515::poll (void) {
xSemaphoreGive (mISRSemaphore) ;
}
#endif
//··································································································
// POLLING (other than ESP32)
//··································································································
#ifndef ARDUINO_ARCH_ESP32
void ACAN2515::poll (void) {
noInterrupts () ;
while (isr_core ()) {}
interrupts () ;
}
#endif
//··································································································
// INTERRUPT SERVICE ROUTINE (ESP32)
// https://stackoverflow.com/questions/51750377/how-to-disable-interrupt-watchdog-in-esp32-or-increase-isr-time-limit
//··································································································
#ifdef ARDUINO_ARCH_ESP32
void IRAM_ATTR ACAN2515::isr (void) {
detachInterrupt (digitalPinToInterrupt (mINT)) ;
BaseType_t xHigherPriorityTaskWoken = pdFALSE ;
xSemaphoreGiveFromISR (mISRSemaphore, &xHigherPriorityTaskWoken) ;
if (xHigherPriorityTaskWoken) {
portYIELD_FROM_ISR () ;
}
}
#endif
//··································································································
// INTERRUPT SERVICE ROUTINE (other than ESP32)
//··································································································
#ifndef ARDUINO_ARCH_ESP32
void ACAN2515::isr (void) {
isr_core () ;
}
#endif
//··································································································
bool ACAN2515::isr_core (void) {
bool handled = false ;
mSPI.beginTransaction (mSPISettings) ;
uint8_t itStatus = read2515Register (CANSTAT_REGISTER) & 0x0E ;
while (itStatus != 0) {
handled = true ;
switch (itStatus) {
case 0 : // No interrupt
break ;
case 1 << 1 : // Error interrupt
bitModify2515Register (CANINTF_REGISTER, 0x20, 0) ; // Ack interrupt
break ;
case 2 << 1 : // Wake-up interrupt
bitModify2515Register (CANINTF_REGISTER, 0x40, 0) ; // Ack interrupt
break ;
case 3 << 1 : // TXB0 interrupt
handleTXBInterrupt (0) ;
break ;
case 4 << 1 : // TXB1 interrupt
handleTXBInterrupt (1) ;
break ;
case 5 << 1 : // TXB2 interrupt
handleTXBInterrupt (2) ;
break ;
case 6 << 1 : // RXB0 interrupt
case 7 << 1 : // RXB1 interrupt
handleRXBInterrupt () ;
break ;
}
itStatus = read2515Register (CANSTAT_REGISTER) & 0x0E ;
}
mSPI.endTransaction () ;
return handled ;
}
//··································································································
// This function is called by ISR when a MCP2515 receive buffer becomes full
void ACAN2515::handleRXBInterrupt (void) {
const uint8_t rxStatus = read2515RxStatus () ; // Bit 6: message in RXB0, bit 7: message in RXB1
const bool received = (rxStatus & 0xC0) != 0 ;
if (received) { // Message in RXB0 and / or RXB1
const bool accessRXB0 = (rxStatus & 0x40) != 0 ;
CANMessage message ;
//--- Set idx field to matching receive filter
message.idx = rxStatus & 0x07 ;
if (message.idx > 5) {
message.idx -= 6 ;
}
//---
select () ;
mSPI.transfer (accessRXB0 ? READ_FROM_RXB0SIDH_COMMAND : READ_FROM_RXB1SIDH_COMMAND) ;
//--- SIDH
message.id = mSPI.transfer (0) ;
message.id <<= 3 ;
//--- SIDL
const uint32_t sidl = mSPI.transfer (0) ;
message.id |= sidl >> 5 ;
message.rtr = (sidl & 0x10) != 0 ; // Only significant for standard frame
message.ext = (sidl & 0x08) != 0 ;
//--- EID8
const uint32_t eid8 = mSPI.transfer (0) ;
if (message.ext) {
message.id <<= 2 ;
message.id |= (sidl & 0x03) ;
message.id <<= 8 ;
message.id |= eid8 ;
}
//--- EID0
const uint32_t eid0 = mSPI.transfer (0) ;
if (message.ext) {
message.id <<= 8 ;
message.id |= eid0 ;
}
//--- DLC
const uint8_t dlc = mSPI.transfer (0) ;
message.len = dlc & 0x0F ;
if (message.ext) { // Added in 2.1.1 (thanks to Achilles)
message.rtr = (dlc & 0x40) != 0 ; // RTR bit in DLC is significant only for extended frame
}
//--- Read data
for (int i=0 ; i<message.len ; i++) {
message.data [i] = mSPI.transfer (0) ;
}
//---
unselect () ;
//--- Free receive buffer command
bitModify2515Register (CANINTF_REGISTER, accessRXB0 ? 0x01 : 0x02, 0) ;
//--- Enter received message in receive buffer (if not full)
mReceiveBuffer.append (message) ;
}
}
//··································································································
// This function is called by ISR when a MCP2515 transmit buffer becomes empty
void ACAN2515::handleTXBInterrupt (const uint8_t inTXB) { // inTXB value is 0, 1 or 2
//--- Acknowledge interrupt
bitModify2515Register (CANINTF_REGISTER, 0x04 << inTXB, 0) ;
//--- Send an other message ?
CANMessage message ;
const bool ok = mTransmitBuffer [inTXB].remove (message) ;
if (ok) {
internalSendMessage (message, inTXB) ;
}else{
mTXBIsFree [inTXB] = true ;
}
}
//··································································································
void ACAN2515::internalSendMessage (const CANMessage & inFrame, const uint8_t inTXB) { // inTXB is 0, 1 or 2
//--- Send command
// send via TXB0: 0x81
// send via TXB1: 0x82
// send via TXB2: 0x84
const uint8_t sendCommand = REQUEST_TO_SEND_COMMAND | (1 << inTXB) ;
//--- Load TX buffer command
// Load TXB0, start at TXB0SIDH: 0x40
// Load TXB1, start at TXB1SIDH: 0x42
// Load TXB2, start at TXB2SIDH: 0x44
const uint8_t loadTxBufferCommand = LOAD_TX_BUFFER_COMMAND | (inTXB << 1) ;
//--- Send message
select () ;
mSPI.transfer (loadTxBufferCommand) ;
if (inFrame.ext) { // Extended frame
uint32_t v = inFrame.id >> 21 ;
mSPI.transfer ((uint8_t) v) ; // ID28 ... ID21 --> SIDH
v = (inFrame.id >> 13) & 0xE0 ; // ID20, ID19, ID18 in bits 7, 6, 5
v |= (inFrame.id >> 16) & 0x03 ; // ID17, ID16 in bits 1, 0
v |= 0x08 ; // Extended bit
mSPI.transfer ((uint8_t) v) ; // ID20, ID19, ID18, -, 1, -, ID17, ID16 --> SIDL
v = (inFrame.id >> 8) & 0xFF ; // ID15, ..., ID8
mSPI.transfer ((uint8_t) v) ; // ID15, ID14, ID13, ID12, ID11, ID10, ID9, ID8 --> EID8
v = inFrame.id & 0xFF ; // ID7, ..., ID0
mSPI.transfer ((uint8_t) v) ; // ID7, ID6, ID5, ID4, ID3, ID2, ID1, ID0 --> EID0
}else{ // Standard frame
uint32_t v = inFrame.id >> 3 ;
mSPI.transfer ((uint8_t) v) ; // ID10 ... ID3 --> SIDH
v = (inFrame.id << 5) & 0xE0 ; // ID2, ID1, ID0 in bits 7, 6, 5
mSPI.transfer ((uint8_t) v) ; // ID2, ID1, ID0, -, 0, -, 0, 0 --> SIDL
mSPI.transfer (0x00) ; // any value --> EID8
mSPI.transfer (0x00) ; // any value --> EID0
}
//--- DLC
uint8_t v = inFrame.len ;
if (v > 8) {
v = 8 ;
}
if (inFrame.rtr) {
v |= 0x40 ;
}
mSPI.transfer (v) ;
//--- Send data
if (!inFrame.rtr) {
for (uint8_t i=0 ; i<inFrame.len ; i++) {
mSPI.transfer (inFrame.data [i]) ;
}
}
unselect () ;
//--- Write send command
select () ;
mSPI.transfer (sendCommand) ;
unselect () ;
}
//··································································································
// INTERNAL SPI FUNCTIONS
//··································································································
void ACAN2515::write2515Register (const uint8_t inRegister, const uint8_t inValue) {
select () ;
mSPI.transfer (WRITE_COMMAND) ;
mSPI.transfer (inRegister) ;
mSPI.transfer (inValue) ;
unselect () ;
}
//··································································································
uint8_t ACAN2515::read2515Register (const uint8_t inRegister) {
select () ;
mSPI.transfer (READ_COMMAND) ;
mSPI.transfer (inRegister) ;
const uint8_t readValue = mSPI.transfer (0) ;
unselect () ;
return readValue ;
}
//··································································································
uint8_t ACAN2515::read2515Status (void) {
select () ;
mSPI.transfer (READ_STATUS_COMMAND) ;
const uint8_t readValue = mSPI.transfer (0) ;
unselect () ;
return readValue ;
}
//··································································································
uint8_t ACAN2515::read2515RxStatus (void) {
select () ;
mSPI.transfer (RX_STATUS_COMMAND) ;
const uint8_t readValue = mSPI.transfer (0) ;
unselect () ;
return readValue ;
}
//··································································································
void ACAN2515::bitModify2515Register (const uint8_t inRegister,
const uint8_t inMask,
const uint8_t inData) {
select () ;
mSPI.transfer (BIT_MODIFY_COMMAND) ;
mSPI.transfer (inRegister) ;
mSPI.transfer (inMask) ;
mSPI.transfer (inData) ;
unselect () ;
}
//··································································································
void ACAN2515::setupMaskRegister (const ACAN2515Mask inMask, const uint8_t inRegister) {
select () ;
mSPI.transfer (WRITE_COMMAND) ;
mSPI.transfer (inRegister) ;
mSPI.transfer (inMask.mSIDH) ;
mSPI.transfer (inMask.mSIDL) ;
mSPI.transfer (inMask.mEID8) ;
mSPI.transfer (inMask.mEID0) ;
unselect () ;
}
//··································································································
// MCP2515 controller state
//··································································································
uint8_t ACAN2515::transmitErrorCounter (void) {
mSPI.beginTransaction (mSPISettings) ;
const uint8_t result = read2515Register (TEC_REGISTER) ;
mSPI.endTransaction () ;
return result ;
}
//··································································································
uint8_t ACAN2515::receiveErrorCounter (void) {
mSPI.beginTransaction (mSPISettings) ;
const uint8_t result = read2515Register (REC_REGISTER) ;
mSPI.endTransaction () ;
return result ;
}
//··································································································
uint8_t ACAN2515::errorFlagRegister (void) {
mSPI.beginTransaction (mSPISettings) ;
const uint8_t result = read2515Register (EFLG_REGISTER) ;
mSPI.endTransaction () ;
return result ;
}
//··································································································
// MESSAGE EMISSION
//··································································································
bool ACAN2515::sendBufferNotFullForIndex (const uint32_t inIndex) {
//--- Fix send buffer index
uint8_t idx = inIndex ;
if (idx > 2) {
idx = 0 ;
}
#ifndef ARDUINO_ARCH_ESP32
noInterrupts () ;
#endif
mSPI.beginTransaction (mSPISettings) ;
const bool ok = mTXBIsFree [idx] || !mTransmitBuffer [idx].isFull () ;
mSPI.endTransaction () ;
#ifndef ARDUINO_ARCH_ESP32
interrupts () ;
#endif
return ok ;
}
//··································································································
bool ACAN2515::tryToSend (const CANMessage & inMessage) {
//--- Fix send buffer index
uint8_t idx = inMessage.idx ;
if (idx > 2) {
idx = 0 ;
}
//--- Bug fix in 2.0.6 (thanks to Fergus Duncan): interrupts were only disabled for Teensy boards
#ifndef ARDUINO_ARCH_ESP32
noInterrupts () ;
#endif
//---
mSPI.beginTransaction (mSPISettings) ;
bool ok = mTXBIsFree [idx] ;
if (ok) { // Transmit buffer and TXB are both free: transmit immediatly
mTXBIsFree [idx] = false ;
internalSendMessage (inMessage, idx) ;
}else{ // Enter in transmit buffer, if not full
ok = mTransmitBuffer [idx].append (inMessage) ;
}
mSPI.endTransaction () ;
#ifndef ARDUINO_ARCH_ESP32
interrupts () ;
#endif
return ok ;
}
//··································································································

280
Software/ACAN2515.h Normal file
View file

@ -0,0 +1,280 @@
//··································································································
// A CAN driver for MCP2515
// by Pierre Molinaro
// https://github.com/pierremolinaro/acan2515
//··································································································
#pragma once
//··································································································
#include "ACAN2515_Buffer16.h"
#include "ACAN2515Settings.h"
#include "MCP2515ReceiveFilters.h"
#include <SPI.h>
//··································································································
class ACAN2515 {
//··································································································
// Constructor: using hardware SPI
//··································································································
public: ACAN2515 (const uint8_t inCS, // CS input of MCP2515
SPIClass & inSPI, // Hardware SPI object
const uint8_t inINT) ; // INT output of MCP2515
//··································································································
// Initialisation: returns 0 if ok, otherwise see error codes below
//··································································································
public: uint16_t begin (const ACAN2515Settings & inSettings,
void (* inInterruptServiceRoutine) (void)) ;
public: uint16_t begin (const ACAN2515Settings & inSettings,
void (* inInterruptServiceRoutine) (void),
const ACAN2515Mask inRXM0,
const ACAN2515AcceptanceFilter inAcceptanceFilters [],
const uint8_t inAcceptanceFilterCount) ;
public: uint16_t begin (const ACAN2515Settings & inSettings,
void (* inInterruptServiceRoutine) (void),
const ACAN2515Mask inRXM0,
const ACAN2515Mask inRXM1,
const ACAN2515AcceptanceFilter inAcceptanceFilters [],
const uint8_t inAcceptanceFilterCount) ;
//··································································································
// Error codes returned by begin
//··································································································
public: static const uint16_t kNoMCP2515 = 1 << 0 ;
public: static const uint16_t kTooFarFromDesiredBitRate = 1 << 1 ;
public: static const uint16_t kInconsistentBitRateSettings = 1 << 2 ;
public: static const uint16_t kINTPinIsNotAnInterrupt = 1 << 3 ;
public: static const uint16_t kISRIsNull = 1 << 4 ;
public: static const uint16_t kRequestedModeTimeOut = 1 << 5 ;
public: static const uint16_t kAcceptanceFilterArrayIsNULL = 1 << 6 ;
public: static const uint16_t kOneFilterMaskRequiresOneOrTwoAcceptanceFilters = 1 << 7 ;
public: static const uint16_t kTwoFilterMasksRequireThreeToSixAcceptanceFilters = 1 << 8 ;
public: static const uint16_t kCannotAllocateReceiveBuffer = 1 << 9 ;
public: static const uint16_t kCannotAllocateTransmitBuffer0 = 1 << 10 ;
public: static const uint16_t kCannotAllocateTransmitBuffer1 = 1 << 11 ;
public: static const uint16_t kCannotAllocateTransmitBuffer2 = 1 << 12 ;
public: static const uint32_t kISRNotNullAndNoIntPin = 1 << 13 ;
//··································································································
// Change Mode on the fly
//··································································································
public: uint16_t changeModeOnTheFly (const ACAN2515Settings::RequestedMode inRequestedMode) ;
//··································································································
// Set filters on the fly
//··································································································
public: uint16_t setFiltersOnTheFly (void) ;
public: uint16_t setFiltersOnTheFly (const ACAN2515Mask inRXM0,
const ACAN2515AcceptanceFilter inAcceptanceFilters [],
const uint8_t inAcceptanceFilterCount) ;
public: uint16_t setFiltersOnTheFly (const ACAN2515Mask inRXM0,
const ACAN2515Mask inRXM1,
const ACAN2515AcceptanceFilter inAcceptanceFilters [],
const uint8_t inAcceptanceFilterCount) ;
//··································································································
// end
//··································································································
public: void end (void) ;
//··································································································
// Receiving messages
//··································································································
public: bool available (void) ;
public: bool receive (CANMessage & outFrame) ;
public: typedef void (*tFilterMatchCallBack) (const uint8_t inFilterIndex) ;
public: bool dispatchReceivedMessage (const tFilterMatchCallBack inFilterMatchCallBack = NULL) ;
//··································································································
// Handling messages to send and receiving messages
//··································································································
public: void isr (void) ;
public: bool isr_core (void) ;
private: void handleTXBInterrupt (const uint8_t inTXB) ;
private: void handleRXBInterrupt (void) ;
//··································································································
// Properties
//··································································································
private: SPIClass & mSPI ;
private: const SPISettings mSPISettings ;
private: const uint8_t mCS ;
private: const uint8_t mINT ;
private: bool mRolloverEnable ;
#ifdef ARDUINO_ARCH_ESP32
public: SemaphoreHandle_t mISRSemaphore ;
private: void (* mInterruptServiceRoutine) (void) = nullptr ;
#endif
//··································································································
// Receive buffer
//··································································································
private: ACAN2515_Buffer16 mReceiveBuffer ;
//··································································································
// Receive buffer size
//··································································································
public: inline uint16_t receiveBufferSize (void) const {
return mReceiveBuffer.size () ;
}
//··································································································
// Receive buffer count
//··································································································
public: inline uint16_t receiveBufferCount (void) const {
return mReceiveBuffer.count () ;
}
//··································································································
// Receive buffer peak count
//··································································································
public: inline uint16_t receiveBufferPeakCount (void) const {
return mReceiveBuffer.peakCount () ;
}
//··································································································
// Call back function array
//··································································································
private: ACANCallBackRoutine mCallBackFunctionArray [6] ;
//··································································································
// Transmitting messages
//··································································································
public: bool sendBufferNotFullForIndex (const uint32_t inIndex) ; // 0 ... 2
public: bool tryToSend (const CANMessage & inMessage) ;
//··································································································
// Driver transmit buffer
//··································································································
private: ACAN2515_Buffer16 mTransmitBuffer [3] ;
private: bool mTXBIsFree [3] ;
public: inline uint16_t transmitBufferSize (const uint8_t inIndex) const {
return mTransmitBuffer [inIndex].size () ;
}
public: inline uint16_t transmitBufferCount (const uint8_t inIndex) const {
return mTransmitBuffer [inIndex].count () ;
}
public: inline uint16_t transmitBufferPeakCount (const uint8_t inIndex) const {
return mTransmitBuffer [inIndex].peakCount () ;
}
private: void internalSendMessage (const CANMessage & inFrame, const uint8_t inTXB) ;
//··································································································
// Polling
//··································································································
public: void poll (void) ;
//··································································································
// Private methods
//··································································································
private: inline void select (void) { digitalWrite (mCS, LOW) ; }
private: inline void unselect (void) { digitalWrite (mCS, HIGH) ; }
private: uint16_t beginWithoutFilterCheck (const ACAN2515Settings & inSettings,
void (* inInterruptServiceRoutine) (void),
const ACAN2515Mask inRXM0,
const ACAN2515Mask inRXM1,
const ACAN2515AcceptanceFilter inAcceptanceFilters [],
const uint8_t inAcceptanceFilterCount) ;
private: uint16_t internalBeginOperation (const ACAN2515Settings & inSettings,
const ACAN2515Mask inRXM0,
const ACAN2515Mask inRXM1,
const ACAN2515AcceptanceFilter inAcceptanceFilters [],
const uint8_t inAcceptanceFilterCount) ;
private: void write2515Register (const uint8_t inRegister, const uint8_t inValue) ;
private: uint8_t read2515Register (const uint8_t inRegister) ;
private: uint8_t read2515Status (void) ;
private: uint8_t read2515RxStatus (void) ;
private: void bitModify2515Register (const uint8_t inRegister, const uint8_t inMask, const uint8_t inData) ;
private: void setupMaskRegister (const ACAN2515Mask inMask, const uint8_t inRegister) ;
private: uint16_t setRequestedMode (const uint8_t inCANControlRegister) ;
private: uint16_t internalSetFiltersOnTheFly (const ACAN2515Mask inRXM0,
const ACAN2515Mask inRXM1,
const ACAN2515AcceptanceFilter inAcceptanceFilters [],
const uint8_t inAcceptanceFilterCount) ;
#ifdef ARDUINO_ARCH_ESP32
public: void attachMCP2515InterruptPin (void) ;
#endif
//··································································································
// MCP2515 controller state
//··································································································
public: uint8_t receiveErrorCounter (void) ;
public: uint8_t transmitErrorCounter (void) ;
public: uint8_t errorFlagRegister (void) ;
//··································································································
// No Copy
//··································································································
private: ACAN2515 (const ACAN2515 &) = delete ;
private: ACAN2515 & operator = (const ACAN2515 &) = delete ;
//··································································································
} ;
//··································································································

View file

@ -0,0 +1,176 @@
//··································································································
// A CAN driver for MCP2515
// by Pierre Molinaro
// https://github.com/pierremolinaro/acan2515
//··································································································
#include "ACAN2515Settings.h"
//··································································································
// CAN Settings
//··································································································
ACAN2515Settings::ACAN2515Settings (const uint32_t inQuartzFrequency,
const uint32_t inWhishedBitRate,
const uint32_t inTolerancePPM) :
mQuartzFrequency (inQuartzFrequency) {
if (mDesiredBitRate != inWhishedBitRate) {
mDesiredBitRate = inWhishedBitRate ;
const uint32_t clock = mQuartzFrequency / 2 ;
uint32_t TQCount = 25 ; // TQCount: 5 ... 25
uint32_t smallestError = UINT32_MAX ;
uint32_t bestBRP = 64 ; // Setting for slowest bit rate
uint32_t bestTQCount = 25 ; // Setting for slowest bit rate
uint32_t BRP = clock / inWhishedBitRate / TQCount ;
//--- Loop for finding best BRP and best TQCount
while ((TQCount >= 5) && (BRP <= 64)) {
//--- Compute error using BRP (caution: BRP should be > 0)
if (BRP > 0) {
const uint32_t error = clock - inWhishedBitRate * TQCount * BRP ; // error is always >= 0
if (error < smallestError) {
smallestError = error ;
bestBRP = BRP ;
bestTQCount = TQCount ;
}
}
//--- Compute error using BRP+1 (caution: BRP+1 should be <= 64)
if (BRP < 64) {
const uint32_t error = inWhishedBitRate * TQCount * (BRP + 1) - clock ; // error is always >= 0
if (error < smallestError) {
smallestError = error ;
bestBRP = BRP + 1 ;
bestTQCount = TQCount ;
}
}
//--- Continue with next value of TQCount
TQCount -- ;
BRP = clock / (inWhishedBitRate * TQCount) ;
}
//--- Set the BRP
mBitRatePrescaler = (uint8_t) bestBRP ;
//--- Compute PS2
const uint32_t PS2 = (bestTQCount + 1) / 3 ; // Always 2 <= PS2 <= 8
mPhaseSegment2 = (uint8_t) PS2 ;
//--- Compute the remaining number of TQ once PS2 and SyncSeg are removed
const uint32_t propSegmentPlusPhaseSegment1 = bestTQCount - PS2 - 1 /* Sync Seg */ ;
//--- Set PS1 to half of remaining TQCount
const uint32_t PS1 = propSegmentPlusPhaseSegment1 / 2 ; // Always 1 <= PS1 <= 8
mPhaseSegment1 = (uint8_t) PS1 ;
//--- Set PS to what is left
mPropagationSegment = (uint8_t) (propSegmentPlusPhaseSegment1 - PS1) ; // Always 1 <= PropSeg <= 8
//--- Set SJW to PS2, with a maximum value of 4
mSJW = (mPhaseSegment2 > 4) ? 4 : (mPhaseSegment2 - 1) ; // Always 2 <= RJW <= 4, and RJW <= mPhaseSegment2
//--- Triple sampling ?
mTripleSampling = (inWhishedBitRate <= 125000) && (mPhaseSegment1 >= 2) ;
//--- Final check of the configuration
const uint32_t W = bestTQCount * mDesiredBitRate * mBitRatePrescaler ;
const uint64_t diff = (clock > W) ? (clock - W) : (W - clock) ;
const uint64_t ppm = (uint64_t) (1000UL * 1000UL) ; // UL suffix is required for Arduino Uno
mBitRateClosedToDesiredRate = (diff * ppm) <= (((uint64_t) W) * inTolerancePPM) ;
}
} ;
//··································································································
static uint32_t bitrateFrom (const uint32_t inQuartzFrequency, // In Hertz
const uint8_t inBitRatePrescaler, // 1...64
const uint8_t inPropagationSegment, // 1...8
const uint8_t inPhaseSegment1, // 1...8
const uint8_t inPhaseSegment2) {// 2...8
const uint8_t TQ = 1 + inPropagationSegment + inPhaseSegment1 + inPhaseSegment2 ;
return inQuartzFrequency / inBitRatePrescaler / TQ / 2 ;
}
//··································································································
ACAN2515Settings::ACAN2515Settings (const uint32_t inQuartzFrequency, // In Hertz
const uint8_t inBitRatePrescaler, // 1...64
const uint8_t inPropagationSegment, // 1...8
const uint8_t inPhaseSegment1, // 1...8
const uint8_t inPhaseSegment2, // 2...8
const uint8_t inSJW) : // 1...4
mQuartzFrequency (inQuartzFrequency),
mDesiredBitRate (bitrateFrom (inQuartzFrequency, inBitRatePrescaler, inPropagationSegment, inPhaseSegment1, inPhaseSegment2)),
mPropagationSegment (inPropagationSegment),
mPhaseSegment1 (inPhaseSegment1),
mPhaseSegment2 (inPhaseSegment2),
mSJW (inSJW),
mBitRatePrescaler (inBitRatePrescaler),
mBitRateClosedToDesiredRate (true) {
} ;
//··································································································
uint32_t ACAN2515Settings::actualBitRate (void) const {
const uint32_t TQCount = 1 /* Sync Seg */ + mPropagationSegment + mPhaseSegment1 + mPhaseSegment2 ;
return mQuartzFrequency / mBitRatePrescaler / TQCount / 2 ;
}
//··································································································
bool ACAN2515Settings::exactBitRate (void) const {
const uint32_t TQCount = 1 /* Sync Seg */ + mPropagationSegment + mPhaseSegment1 + mPhaseSegment2 ;
return mQuartzFrequency == (mDesiredBitRate * mBitRatePrescaler * TQCount * 2) ;
}
//··································································································
uint32_t ACAN2515Settings::ppmFromDesiredBitRate (void) const {
const uint32_t TQCount = 1 /* Sync Seg */ + mPropagationSegment + mPhaseSegment1 + mPhaseSegment2 ;
const uint32_t W = TQCount * mDesiredBitRate * mBitRatePrescaler * 2 ;
const uint64_t diff = (mQuartzFrequency > W) ? (mQuartzFrequency - W) : (W - mQuartzFrequency) ;
const uint64_t ppm = (uint64_t) (1000UL * 1000UL) ; // UL suffix is required for Arduino Uno
return (uint32_t) ((diff * ppm) / W) ;
}
//··································································································
uint32_t ACAN2515Settings::samplePointFromBitStart (void) const {
const uint32_t TQCount = 1 /* Sync Seg */ + mPropagationSegment + mPhaseSegment1 + mPhaseSegment2 ;
const uint32_t samplePoint = 1 /* Sync Seg */ + mPropagationSegment + mPhaseSegment1 - mTripleSampling ;
const uint32_t partPerCent = 100 ;
return (samplePoint * partPerCent) / TQCount ;
}
//··································································································
uint16_t ACAN2515Settings::CANBitSettingConsistency (void) const {
uint16_t errorCode = 0 ; // Means no error
if (mBitRatePrescaler == 0) {
errorCode |= kBitRatePrescalerIsZero ;
}else if (mBitRatePrescaler > 64) {
errorCode |= kBitRatePrescalerIsGreaterThan64 ;
}
if (mPropagationSegment == 0) {
errorCode |= kPropagationSegmentIsZero ;
}else if (mPropagationSegment > 8) {
errorCode |= kPropagationSegmentIsGreaterThan8 ;
}
if (mPhaseSegment1 == 0) {
errorCode |= kPhaseSegment1IsZero ;
}else if ((mPhaseSegment1 == 1) && mTripleSampling) {
errorCode |= kPhaseSegment1Is1AndTripleSampling ;
}else if (mPhaseSegment1 > 8) {
errorCode |= kPhaseSegment1IsGreaterThan8 ;
}
if (mPhaseSegment2 < 2) {
errorCode |= kPhaseSegment2IsLowerThan2 ;
}else if (mPhaseSegment2 > 8) {
errorCode |= kPhaseSegment2IsGreaterThan8 ;
}
if (mSJW == 0) {
errorCode |= kSJWIsZero ;
}else if (mSJW > 4) {
errorCode |= kSJWIsGreaterThan4 ;
}
if (mSJW >= mPhaseSegment2) {
errorCode |= kSJWIsGreaterThanOrEqualToPhaseSegment2 ;
}
if (mPhaseSegment2 > (mPropagationSegment + mPhaseSegment1)) {
errorCode |= kPhaseSegment2IsGreaterThanPSPlusPS1 ;
}
return errorCode ;
}
//··································································································

182
Software/ACAN2515Settings.h Normal file
View file

@ -0,0 +1,182 @@
//··································································································
// A CAN driver for MCP2515
// by Pierre Molinaro
// https://github.com/pierremolinaro/acan2515
//··································································································
#pragma once
//··································································································
#include <stdint.h>
//··································································································
class ACAN2515Settings {
//··································································································
// Enumerations
//··································································································
public: typedef enum : uint8_t {
NormalMode = 0 << 5,
SleepMode = 1 << 5,
LoopBackMode = 2 << 5,
ListenOnlyMode = 3 << 5
} RequestedMode ;
//··································································································
public: typedef enum : uint8_t {CLOCK, CLOCK2, CLOCK4, CLOCK8, SOF, HiZ} CLKOUT_SOF ;
//··································································································
// Constructor for a given baud rate
//··································································································
public: explicit ACAN2515Settings (const uint32_t inQuartzFrequency, // In Hertz
const uint32_t inDesiredBitRate,
const uint32_t inTolerancePPM = 1000) ;
//··································································································
// Constructor with explicit bit settings
//··································································································
public: explicit ACAN2515Settings (const uint32_t inQuartzFrequency, // In Hertz
const uint8_t inBitRatePrescaler, // 1...64
const uint8_t inPropagationSegment, // 1...8
const uint8_t inPhaseSegment1, // 1...8
const uint8_t inPhaseSegment2, // 2...8
const uint8_t inSJW) ; // 1...4
//··································································································
// CAN bit timing properties
//··································································································
public: const uint32_t mQuartzFrequency ;
public: uint32_t mDesiredBitRate = mQuartzFrequency / 64 ; // In kb/s
public: uint8_t mPropagationSegment = 5 ; // 1...8
public: uint8_t mPhaseSegment1 = 5 ; // 1...8
public: uint8_t mPhaseSegment2 = 5 ; // 2...8
public: uint8_t mSJW = 4 ; // 1...4
public: uint8_t mBitRatePrescaler = 32 / (1 + mPropagationSegment + mPhaseSegment1 + mPhaseSegment2) ; // 1...64
public: bool mTripleSampling = false ; // true --> triple sampling, false --> single sampling
public: bool mBitRateClosedToDesiredRate = true ; // The above configuration is correct
//··································································································
// One shot mode
// true --> Enabled; messages will only attempt to transmit one time
// false --> Disabled; messages will reattempt transmission if required
//··································································································
public: bool mOneShotModeEnabled = false ;
//··································································································
// MCP2515 TXBi priorities
// bits 7-6: unused
// bits 5-4: TXB2 priority
// bits 3-2: TXB1 priority
// bits 1-0: TXB0 priority
//··································································································
public: uint8_t mTXBPriority = 0 ;
//··································································································
// Requested mode
//··································································································
public: RequestedMode mRequestedMode = NormalMode ;
//··································································································
// Signal on CLKOUT/SOF pin
//··································································································
public: CLKOUT_SOF mCLKOUT_SOF_pin = CLOCK ;
//··································································································
// Rollover Enable Bit (is set to the BUKT bit of the RXB0CTRL register)
// true --> RXB0 message will roll over and be written to RXB1 if RXB0 is full
// false --> Rollover is disabled
//··································································································
public : bool mRolloverEnable = true ;
//··································································································
// Receive buffer size
//··································································································
public: uint16_t mReceiveBufferSize = 32 ;
//··································································································
// Transmit buffer sizes
//··································································································
public: uint16_t mTransmitBuffer0Size = 16 ;
public: uint16_t mTransmitBuffer1Size = 0 ;
public: uint16_t mTransmitBuffer2Size = 0 ;
//··································································································
// Compute actual bit rate
//··································································································
public: uint32_t actualBitRate (void) const ;
//··································································································
// Exact bit rate ?
//··································································································
public: bool exactBitRate (void) const ;
//··································································································
// Distance between actual bit rate and requested bit rate (in ppm, part-per-million)
//··································································································
public: uint32_t ppmFromDesiredBitRate (void) const ;
//··································································································
// Distance of sample point from bit start (in ppc, part-per-cent, denoted by %)
//··································································································
public: uint32_t samplePointFromBitStart (void) const ;
//··································································································
// Bit settings are consistent ? (returns 0 if ok)
//··································································································
public: uint16_t CANBitSettingConsistency (void) const ;
//··································································································
// Constants returned by CANBitSettingConsistency
//··································································································
public: static const uint16_t kBitRatePrescalerIsZero = 1 << 0 ;
public: static const uint16_t kBitRatePrescalerIsGreaterThan64 = 1 << 1 ;
public: static const uint16_t kPropagationSegmentIsZero = 1 << 2 ;
public: static const uint16_t kPropagationSegmentIsGreaterThan8 = 1 << 3 ;
public: static const uint16_t kPhaseSegment1IsZero = 1 << 4 ;
public: static const uint16_t kPhaseSegment1IsGreaterThan8 = 1 << 5 ;
public: static const uint16_t kPhaseSegment2IsLowerThan2 = 1 << 6 ;
public: static const uint16_t kPhaseSegment2IsGreaterThan8 = 1 << 7 ;
public: static const uint16_t kPhaseSegment1Is1AndTripleSampling = 1 << 8 ;
public: static const uint16_t kSJWIsZero = 1 << 9 ;
public: static const uint16_t kSJWIsGreaterThan4 = 1 << 10 ;
public: static const uint16_t kSJWIsGreaterThanOrEqualToPhaseSegment2 = 1 << 11 ;
public: static const uint16_t kPhaseSegment2IsGreaterThanPSPlusPS1 = 1 << 12 ;
//··································································································
} ;
//··································································································

View file

@ -0,0 +1,130 @@
//--------------------------------------------------------------------------------------------------
#pragma once
//--------------------------------------------------------------------------------------------------
#include "CANMessage.h"
//--------------------------------------------------------------------------------------------------
class ACAN2515_Buffer16 {
//································································································
// Default constructor
//································································································
public: ACAN2515_Buffer16 (void) :
mBuffer (NULL),
mSize (0),
mReadIndex (0),
mCount (0),
mPeakCount (0) {
}
//································································································
// Destructor
//································································································
public: ~ ACAN2515_Buffer16 (void) {
delete [] mBuffer ;
}
//································································································
// Private properties
//································································································
private: CANMessage * mBuffer ;
private: uint16_t mSize ;
private: uint16_t mReadIndex ;
private: uint16_t mCount ;
private: uint16_t mPeakCount ; // > mSize if overflow did occur
//································································································
// Accessors
//································································································
public: inline bool isFull (void) const { return mCount == mSize ; }
public: inline uint16_t size (void) const { return mSize ; }
public: inline uint16_t count (void) const { return mCount ; }
public: inline uint16_t peakCount (void) const { return mPeakCount ; }
//································································································
// initWithSize
//································································································
public: bool initWithSize (const uint16_t inSize) {
delete [] mBuffer ;
mBuffer = new CANMessage [inSize] ;
const bool ok = mBuffer != NULL ;
mSize = ok ? inSize : 0 ;
mReadIndex = 0 ;
mCount = 0 ;
mPeakCount = 0 ;
return ok ;
}
//································································································
// append
//································································································
public: bool append (const CANMessage & inMessage) {
const bool ok = mCount < mSize ;
if (ok) {
uint16_t writeIndex = mReadIndex + mCount ;
if (writeIndex >= mSize) {
writeIndex -= mSize ;
}
mBuffer [writeIndex] = inMessage ;
mCount ++ ;
if (mPeakCount < mCount) {
mPeakCount = mCount ;
}
}
return ok ;
}
//································································································
// Remove
//································································································
public: bool remove (CANMessage & outMessage) {
const bool ok = mCount > 0 ;
if (ok) {
outMessage = mBuffer [mReadIndex] ;
mCount -= 1 ;
mReadIndex += 1 ;
if (mReadIndex == mSize) {
mReadIndex = 0 ;
}
}
return ok ;
}
//································································································
// Free
//································································································
public: void free (void) {
delete [] mBuffer ; mBuffer = nullptr ;
mSize = 0 ;
mReadIndex = 0 ;
mCount = 0 ;
mPeakCount = 0 ;
}
//································································································
// Reset Peak Count
//································································································
public: inline void resetPeakCount (void) { mPeakCount = mCount ; }
//································································································
// No copy
//································································································
private: ACAN2515_Buffer16 (const ACAN2515_Buffer16 &) ;
private: ACAN2515_Buffer16 & operator = (const ACAN2515_Buffer16 &) ;
} ;
//--------------------------------------------------------------------------------------------------

117
Software/ACANBuffer.h Normal file
View file

@ -0,0 +1,117 @@
//··································································································
// This file is not used any more by ACAN2515 driver.
// It is provided for compatibility with sketchs that use it.
//··································································································
#ifndef ACAN_BUFFER_CLASS_DEFINED
#define ACAN_BUFFER_CLASS_DEFINED
//··································································································
#include <CANMessage.h>
//··································································································
class ACANBuffer {
//··································································································
// Default constructor
//··································································································
public: ACANBuffer (void) :
mBuffer (NULL),
mSize (0),
mReadIndex (0),
mWriteIndex (0),
mCount (0),
mPeakCount (0) {
}
//··································································································
// Destructor
//··································································································
public: ~ ACANBuffer (void) {
delete [] mBuffer ;
}
//··································································································
// Private properties
//··································································································
private: CANMessage * mBuffer ;
private: uint32_t mSize ;
private: uint32_t mReadIndex ;
private: uint32_t mWriteIndex ;
private: uint32_t mCount ;
private: uint32_t mPeakCount ; // > mSize if overflow did occur
//··································································································
// Accessors
//··································································································
public: inline uint32_t size (void) const { return mSize ; }
public: inline uint32_t count (void) const { return mCount ; }
public: inline uint32_t peakCount (void) const { return mPeakCount ; }
//··································································································
// initWithSize
//··································································································
public: void initWithSize (const uint32_t inSize) {
mBuffer = new CANMessage [inSize] ;
mSize = inSize ;
mReadIndex = 0 ;
mWriteIndex = 0 ;
mCount = 0 ;
mPeakCount = 0 ;
}
//··································································································
// append
//··································································································
public: bool append (const CANMessage & inMessage) {
const bool ok = mCount < mSize ;
if (ok) {
mBuffer [mWriteIndex] = inMessage ;
mWriteIndex += 1 ;
if (mWriteIndex == mSize) {
mWriteIndex = 0 ;
}
mCount ++ ;
if (mPeakCount < mCount) {
mPeakCount = mCount ;
}
}
return ok ;
}
//··································································································
// Remove
//··································································································
public: bool remove (CANMessage & outMessage) {
const bool ok = mCount > 0 ;
if (ok) {
outMessage = mBuffer [mReadIndex] ;
mCount -= 1 ;
mReadIndex += 1 ;
if (mReadIndex == mSize) {
mReadIndex = 0 ;
}
}
return ok ;
}
//··································································································
// No copy
//··································································································
private: ACANBuffer (const ACANBuffer &) ;
private: ACANBuffer & operator = (const ACANBuffer &) ;
} ;
//··································································································
#endif

49
Software/CANMessage.h Normal file
View file

@ -0,0 +1,49 @@
//----------------------------------------------------------------------------------------------------------------------
// Generic CAN Message
// by Pierre Molinaro
//
// This file is common to the following libraries
// https://github.com/pierremolinaro/acan
// https://github.com/pierremolinaro/acan2515
// https://github.com/pierremolinaro/acan2517
// https://github.com/pierremolinaro/acan2517FD
//
//----------------------------------------------------------------------------------------------------------------------
#ifndef GENERIC_CAN_MESSAGE_DEFINED
#define GENERIC_CAN_MESSAGE_DEFINED
//----------------------------------------------------------------------------------------------------------------------
#include <Arduino.h>
//----------------------------------------------------------------------------------------------------------------------
class CANMessage {
public : uint32_t id = 0 ; // Frame identifier
public : bool ext = false ; // false -> standard frame, true -> extended frame
public : bool rtr = false ; // false -> data frame, true -> remote frame
public : uint8_t idx = 0 ; // This field is used by the driver
public : uint8_t len = 0 ; // Length of data (0 ... 8)
public : union {
uint64_t data64 ; // Caution: subject to endianness
int64_t data_s64 ; // Caution: subject to endianness
uint32_t data32 [2] ; // Caution: subject to endianness
int32_t data_s32 [2] ; // Caution: subject to endianness
float dataFloat [2] ; // Caution: subject to endianness
uint16_t data16 [4] ; // Caution: subject to endianness
int16_t data_s16 [4] ; // Caution: subject to endianness
int8_t data_s8 [8] ;
uint8_t data [8] = {0, 0, 0, 0, 0, 0, 0, 0} ;
} ;
} ;
//----------------------------------------------------------------------------------------------------------------------
typedef enum {kStandard, kExtended} tFrameFormat ;
typedef enum {kData, kRemote} tFrameKind ;
typedef void (*ACANCallBackRoutine) (const CANMessage & inMessage) ;
//----------------------------------------------------------------------------------------------------------------------
#endif

View file

@ -0,0 +1,91 @@
//··································································································
// MCP2515 Receive filter classes
// by Pierre Molinaro
// https://github.com/pierremolinaro/acan2515
//··································································································
#ifndef MCP2515_RECEIVE_FILTER_ENTITIES_DEFINED
#define MCP2515_RECEIVE_FILTER_ENTITIES_DEFINED
//··································································································
#include <Arduino.h>
//··································································································
class ACAN2515Mask {
//--- Default constructor
public: ACAN2515Mask (void) :
mSIDH (0),
mSIDL (0),
mEID8 (0),
mEID0 (0) {
}
//--- Properties
public: uint8_t mSIDH ;
public: uint8_t mSIDL ;
public: uint8_t mEID8 ;
public: uint8_t mEID0 ;
} ;
//··································································································
class ACAN2515AcceptanceFilter {
public: typedef void (*tCallBackRoutine) (const CANMessage & inMessage) ;
public: const ACAN2515Mask mMask ;
public: const tCallBackRoutine mCallBack ;
} ;
//··································································································
inline ACAN2515Mask standard2515Mask (const uint16_t inIdentifier,
const uint8_t inByte0,
const uint8_t inByte1) {
ACAN2515Mask result ;
result.mSIDH = (uint8_t) (inIdentifier >> 3) ;
result.mSIDL = (uint8_t) (inIdentifier << 5) ;
result.mEID8 = inByte0 ;
result.mEID0 = inByte1 ;
return result ;
}
//··································································································
inline ACAN2515Mask extended2515Mask (const uint32_t inIdentifier) {
ACAN2515Mask result ;
result.mSIDH = (uint8_t) (inIdentifier >> 21) ;
result.mSIDL = (uint8_t) (((inIdentifier >> 16) & 0x03) | ((inIdentifier >> 13) & 0xE0)) ;
result.mEID8 = (uint8_t) (inIdentifier >> 8) ;
result.mEID0 = (uint8_t) inIdentifier ;
return result ;
}
//··································································································
inline ACAN2515Mask standard2515Filter (const uint16_t inIdentifier,
const uint8_t inByte0,
const uint8_t inByte1) {
ACAN2515Mask result ;
result.mSIDH = (uint8_t) (inIdentifier >> 3) ;
result.mSIDL = (uint8_t) (inIdentifier << 5) ;
result.mEID8 = inByte0 ;
result.mEID0 = inByte1 ;
return result ;
}
//··································································································
inline ACAN2515Mask extended2515Filter (const uint32_t inIdentifier) {
ACAN2515Mask result ;
result.mSIDH = (uint8_t) (inIdentifier >> 21) ;
result.mSIDL = (uint8_t) (((inIdentifier >> 16) & 0x03) | ((inIdentifier >> 13) & 0xE0)) | 0x08 ;
result.mEID8 = (uint8_t) (inIdentifier >> 8) ;
result.mEID0 = (uint8_t) inIdentifier ;
return result ;
}
//··································································································
#endif

View file

@ -30,7 +30,7 @@ CAN_frame_t SOLAX_100A001 = {.FIR = {.B = {.DLC = 0,.FF = CAN_frame_ext,}},.MsgI
void CAN_WriteFrame(CAN_frame_t* tx_frame)
{
#ifdef DUAL_CAN
if(dual_can){
CANMessage MCP2515Frame; //Struct with ACAN2515 library format, needed to use the MCP2515 library
MCP2515Frame.id = tx_frame->MsgID;
MCP2515Frame.ext = tx_frame->FIR.B.FF;
@ -39,11 +39,10 @@ void CAN_WriteFrame(CAN_frame_t* tx_frame)
MCP2515Frame.data[i] = tx_frame->data.u8[i];
}
can.tryToSend(MCP2515Frame);
//Serial.println("Solax CAN Frame sent in Bus 2");
#else
}
else{
ESP32Can.CANWriteFrame(tx_frame);
//Serial.println("Solax CAN Frame sent in Bus 1");
#endif
}
}
void update_values_can_solax()

View file

@ -4,12 +4,9 @@
#include "ESP32CAN.h"
#include "config.h"
//#define DUAL_CAN //Enable this line to activate an isolated secondary CAN Bus using add-on MCP2515 controller (Needed for FoxESS inverters)
#ifdef DUAL_CAN
#include "ACAN2515.h" //If this file is missing, install the ACAN2515 library
#include "ACAN2515.h"
extern ACAN2515 can;
#endif
extern bool dual_can;
extern uint16_t SOC;
extern uint16_t StateOfHealth;

View file

@ -36,11 +36,15 @@
//CAN parameters
CAN_device_t CAN_cfg; // CAN Config
const int rx_queue_size = 10; // Receive Queue size
#ifdef DUAL_CAN
#include <ACAN2515.h>
const bool dual_can = 1;
#include "ACAN2515.h"
static const uint32_t QUARTZ_FREQUENCY = 8UL * 1000UL * 1000UL ; // 8 MHz
ACAN2515 can(MCP2515_CS, SPI, MCP2515_INT);
static ACAN2515_Buffer16 gBuffer;
#else
const bool dual_can = 0;
#endif
//Interval settings