Skip to content

Commit ace8938

Browse files
committed
Optimize MCP2515Class::endPacket()
1) Use LOAD TX BUFFER instruction to set TXBn* registers sequentially, as well as to write into the TX data registers. This is done with a single CS pull, and requires sending N+6 bytes over SPI. The old code was doing N+5 writeRegister() calls, each doing 1 CS pull and sending 3 bytes. 2) Use the 1-byte RTS SPI instruction instead of WRITEing to TXBnCTRL. This reduces the number of bytes sent over SPI by 2. For N = 8, - we now send 15 bytes over SPI before the while(...TXBnCTRL...) loop vs 42 (2.8x reduction) - we do just 2 CS pulls before the while(...TXBnCTRL...) loop vs 13 (6.5x reduction) We still do at least 3 CS pulls and send at least 10 bytes over SPI for the condition of the while(...TXBnCTRL...) loop, to clear TXnIF and to check for errors. That means for N = 8, in the case when we do zero iterations of the while loop, we end up sending 25 bytes over SPI instead of 52, which is >2x reduction.
1 parent 22bd262 commit ace8938

File tree

1 file changed

+57
-18
lines changed

1 file changed

+57
-18
lines changed

src/MCP2515.cpp

Lines changed: 57 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -169,52 +169,91 @@ int MCP2515Class::endPacket()
169169
return 0;
170170
}
171171

172+
// Currently, we don't need to use more than one TX buffer as we always wait
173+
// until the data has been fully transmitted. For the same reason, we don't
174+
// need to check in the beginning whether there is any data in the TX buffer
175+
// pending transmission. The performance can be optimized by utilizing all
176+
// three TX buffers of the MCP2515, but this will come at extra complexity.
172177
int n = 0;
173178

179+
// Pre-calculate values for all registers so that we can write them
180+
// sequentially via the LOAD TX BUFFER instruction.
181+
// TX BUFFER
182+
uint8_t regSIDH;
183+
uint8_t regSIDL;
184+
uint8_t regEID8;
185+
uint8_t regEID0;
174186
if (_txExtended) {
175-
writeRegister(REG_TXBnSIDH(n), _txId >> 21);
176-
writeRegister(REG_TXBnSIDL(n), (((_txId >> 18) & 0x07) << 5) | FLAG_EXIDE | ((_txId >> 16) & 0x03));
177-
writeRegister(REG_TXBnEID8(n), (_txId >> 8) & 0xff);
178-
writeRegister(REG_TXBnEID0(n), _txId & 0xff);
187+
regSIDH = _txId >> 21;
188+
regSIDL =
189+
(((_txId >> 18) & 0x07) << 5) | FLAG_EXIDE | ((_txId >> 16) & 0x03);
190+
regEID8 = (_txId >> 8) & 0xff;
191+
regEID0 = _txId & 0xff;
179192
} else {
180-
writeRegister(REG_TXBnSIDH(n), _txId >> 3);
181-
writeRegister(REG_TXBnSIDL(n), _txId << 5);
182-
writeRegister(REG_TXBnEID8(n), 0x00);
183-
writeRegister(REG_TXBnEID0(n), 0x00);
193+
regSIDH = _txId >> 3;
194+
regSIDL = _txId << 5;
195+
regEID8 = 0x00;
196+
regEID0 = 0x00;
184197
}
185198

199+
uint8_t regDLC;
186200
if (_txRtr) {
187-
writeRegister(REG_TXBnDLC(n), 0x40 | _txLength);
201+
regDLC = 0x40 | _txLength;
188202
} else {
189-
writeRegister(REG_TXBnDLC(n), _txLength);
203+
regDLC = _txLength;
204+
}
190205

191-
for (int i = 0; i < _txLength; i++) {
192-
writeRegister(REG_TXBnD0(n) + i, _txData[i]);
206+
SPI.beginTransaction(_spiSettings);
207+
digitalWrite(_csPin, LOW);
208+
// Send the LOAD TX BUFFER instruction to sequentially write registers,
209+
// starting from TXBnSIDH(n).
210+
SPI.transfer(0b01000000 | (n << 1));
211+
SPI.transfer(regSIDH);
212+
SPI.transfer(regSIDL);
213+
SPI.transfer(regEID8);
214+
SPI.transfer(regEID0);
215+
SPI.transfer(regDLC);
216+
if (!_txRtr) {
217+
for (uint8_t i = 0; i < _txLength; i++) {
218+
SPI.transfer(_txData[i]);
193219
}
194220
}
221+
digitalWrite(_csPin, HIGH);
222+
SPI.endTransaction();
195223

196-
writeRegister(REG_TXBnCTRL(n), 0x08);
224+
SPI.beginTransaction(_spiSettings);
225+
digitalWrite(_csPin, LOW);
226+
// Send the RTS instruction, which sets the TXREQ (TXBnCTRL[3]) bit for the
227+
// respective buffer, and clears the ABTF, MLOA and TXERR bits.
228+
SPI.transfer(0b10000000 | (1 << n));
229+
digitalWrite(_csPin, HIGH);
230+
SPI.endTransaction();
197231

232+
// Wait until the transmission completes, or gets aborted.
233+
// Transmission is pending while TXREQ (TXBnCTRL[3]) bit is set.
198234
bool aborted = false;
199-
200235
while (readRegister(REG_TXBnCTRL(n)) & 0x08) {
236+
// Read the TXERR (TXBnCTRL[4]) bit to check for errors.
201237
if (readRegister(REG_TXBnCTRL(n)) & 0x10) {
202-
// abort
203-
aborted = true;
204-
238+
// Abort on errors by setting the ABAT bit. The MCP2515 will should the
239+
// TXREQ bit shortly. We'll keep running the loop until TXREQ is cleared.
205240
modifyRegister(REG_CANCTRL, 0x10, 0x10);
241+
aborted = true;
206242
}
207243

208244
yield();
209245
}
210246

211247
if (aborted) {
212-
// clear abort command
248+
// Reset the ABAT bit.
213249
modifyRegister(REG_CANCTRL, 0x10, 0x00);
214250
}
215251

252+
// Clear the pending TX interrupt, if any.
216253
modifyRegister(REG_CANINTF, FLAG_TXnIF(n), 0x00);
217254

255+
// Report failure if either of the ABTF, MLOA or TXERR bits are set.
256+
// TODO: perhaps we can reuse the last value read from this register // earlier?
218257
return (readRegister(REG_TXBnCTRL(n)) & 0x70) ? 0 : 1;
219258
}
220259

0 commit comments

Comments
 (0)