MCP2515 bandwidth issues at 500K and 1Mbps -> SPI command optimizations
I made a few optimizations to improve the performance of the interface for the MCP2515. There are a couple of commands to reduce the overhead of the SPI to increase the data throughput to and from the MCP2515; LOAD RX BUFFER, RTS, and READ RX BUFFER. I added it endPacket() even though it doesn't matter as much since it waits for the packet to complete sending before returning (there is still an improvement), the improvement in parsePacket() is nice.
int MCP2515Class::parsePacket()
{
int n;
uint8_t intf = readRegister(REG_CANINTF);
if (intf & FLAG_RXnIF(0))
{
n = 0;
}
else if (intf & FLAG_RXnIF(1))
{
n = 1;
}
else
{
_rxId = -1;
_rxRtr = false;
_rxLength = 0;
return 0;
}
uint8_t SIDH = readRegister(REG_RXBnSIDH(n));
uint8_t SIDL = readRegister(REG_RXBnSIDL(n));
uint32_t idA = ((SIDH << 3) & 0x07f8) | ((SIDL >> 5) & 0x07);
_rxId = idA;
_rxRtr = (SIDL & FLAG_SRR) ? true : false;
_rxDlc = readRegister(REG_RXBnDLC(n)) & 0x0f;
_rxIndex = 0;
if (_rxRtr)
{
_rxLength = 0;
}
else
{
_rxLength = _rxDlc;
SPI.beginTransaction(_spiSettings);
digitalWrite(_csPin, LOW);
SPI.transfer(0b10010010 | (n << 2)); //Set to stream RX data from Buffer n
for (uint8_t x = 0; x < _rxLength; x++)
_rxData[x] = SPI.transfer(0x00); //Stream data form RX buffer
digitalWrite(_csPin, HIGH); //unsetting CS pin will reset IRQ flag
SPI.endTransaction();
}
return _rxRtr ? true : _rxDlc;
}
int MCP2515Class::endPacket()
{
if (!CANControllerClass::endPacket())
{
return 0;
}
int n = 0;
writeRegister(REG_TXBnSIDH(n), _txId >> 3);
writeRegister(REG_TXBnSIDL(n), _txId << 5);
writeRegister(REG_TXBnEID8(n), 0x00);
writeRegister(REG_TXBnEID0(n), 0x00);
if (_txRtr)
{
writeRegister(REG_TXBnDLC(n), 0x40 | _txLength);
}
else
{
writeRegister(REG_TXBnDLC(n), _txLength); //set packet length
SPI.beginTransaction(_spiSettings);
digitalWrite(_csPin, LOW);
SPI.transfer(0b01000000 | (n * 2 + 1)); //set TX buffer for stream
for (uint8_t x = 0; x < _txLength; x++)
SPI.transfer(_txData[x]); //stream data to buffer
digitalWrite(_csPin, HIGH);
SPI.endTransaction();
}
SPI.beginTransaction(_spiSettings);
digitalWrite(_csPin, LOW);
SPI.transfer(0b10000000 | (1 << n)); //set RTS for TX Buffer n
digitalWrite(_csPin, HIGH);
SPI.endTransaction();
bool aborted = false;
while (readRegister(REG_TXBnCTRL(n)) & 0x08)
{
if (readRegister(REG_TXBnCTRL(n)) & 0x10)
{
// abort
aborted = true;
modifyRegister(REG_CANCTRL, 0x10, 0x10);
}
yield();
}
if (aborted) {
// clear abort command
modifyRegister(REG_CANCTRL, 0x10, 0x00);
}
modifyRegister(REG_CANINTF, FLAG_TXnIF(n), 0x00);
return (readRegister(REG_TXBnCTRL(n)) & 0x70) ? 0 : 1;
}
Thanks for filing this!
Once I realized there is room for improvement, I've decided to take a closer look at the datasheet and this is what I came up with for parsePacket(). I'm sure something similar can be done for endPacket().
Links to PRs: parsePacket(): https://github.com/sandeepmistry/arduino-CAN/pull/46 endPacket(): https://github.com/sandeepmistry/arduino-CAN/pull/49
Thank you @timurrrr ! With your fix I got 0% packets loss from over 30% when I tried multiple messages with the same time cycle. I wonder why it's still not merged though...