mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-04 18:29:48 +02:00
176 lines
9.1 KiB
C++
176 lines
9.1 KiB
C++
//··································································································
|
|
// 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 ;
|
|
}
|
|
|
|
//··································································································
|