AgIsoStack++
A control-function-focused implementation of the major ISOBUS and J1939 protocols
Loading...
Searching...
No Matches
mcp2515_can_interface.cpp
Go to the documentation of this file.
1//================================================================================================
8//================================================================================================
11#include "isobus/utility/system_timing.hpp"
12#include "isobus/utility/to_string.hpp"
13
14#include <iostream>
15#include <thread>
16
17namespace isobus
18{
19 MCP2515CANInterface::MCP2515CANInterface(SPIHardwarePlugin *transactionHandler, const std::uint8_t cfg1, const std::uint8_t cfg2, const std::uint8_t cfg3) :
20 transactionHandler(transactionHandler),
21 cfg1(cfg1),
22 cfg2(cfg2),
23 cfg3(cfg3)
24 {
25 }
26
31
33 {
34 return (transactionHandler) && (initialized);
35 }
36
38 {
39 initialized = false;
40 }
41
43 {
44 if (reset())
45 {
46 if (set_mode(MCPMode::CONFIG))
47 {
48 if ((write_register(MCPRegister::CNF1, cfg1)) &&
49 (write_register(MCPRegister::CNF2, cfg2)) &&
50 (write_register(MCPRegister::CNF3, cfg3)))
51 {
52 if (set_mode(MCPMode::NORMAL))
53 {
54 initialized = true;
55 }
56 }
57 }
58 }
59 }
60
62 {
63 bool retVal = false;
64 if (write_reset())
65 {
66 // Depends on oscillator & capacitors used
67 std::this_thread::sleep_for(std::chrono::milliseconds(10));
68 if ((write_register(MCPRegister::RXB0CTRL, 0x65)) &&
69 (write_register(MCPRegister::RXB1CTRL, 0x65)) &&
70 (write_register(MCPRegister::CANINTE, 0x03)))
71 {
72 retVal = true;
73 }
74 }
75 return retVal;
76 }
77
78 bool MCP2515CANInterface::get_read_status(std::uint8_t &status)
79 {
80 bool retVal = false;
82 {
83 const std::vector<std::uint8_t> txBuffer = { static_cast<std::uint8_t>(MCPInstruction::READ_STATUS), 0x00 };
84 SPITransactionFrame frame(&txBuffer, true);
88 {
89 retVal = true;
90 frame.read_byte(1, status);
91 }
92 }
93 return retVal;
94 }
95 bool MCP2515CANInterface::read_register(const MCPRegister address, std::uint8_t &data)
96 {
97 bool retVal = false;
99 {
100 const std::vector<std::uint8_t> txBuffer = { static_cast<std::uint8_t>(MCPInstruction::READ),
101 static_cast<std::uint8_t>(address),
102 0x00 };
103 SPITransactionFrame frame(&txBuffer, true);
104
108 {
109 retVal = true;
110 frame.read_byte(2, data);
111 }
112 }
113 return retVal;
114 }
115
116 bool MCP2515CANInterface::read_register(const MCPRegister address, std::uint8_t *data, const std::size_t length)
117 {
118 bool retVal = false;
120 {
121 std::vector<std::uint8_t> txBuffer(2 + length, 0x00);
122 txBuffer[0] = static_cast<std::uint8_t>(MCPInstruction::READ);
123 txBuffer[1] = static_cast<std::uint8_t>(address);
124 SPITransactionFrame frame(&txBuffer, true);
128 {
129 retVal = true;
130 frame.read_bytes(2, data, length);
131 }
132 }
133 return retVal;
134 }
135
136 bool MCP2515CANInterface::modify_register(const MCPRegister address, const std::uint8_t mask, const std::uint8_t data)
137 {
138 bool retVal = false;
140 {
141 const std::vector<std::uint8_t> txBuffer = { static_cast<std::uint8_t>(MCPInstruction::BITMOD),
142 static_cast<std::uint8_t>(address),
143 mask,
144 data };
145 SPITransactionFrame frame(&txBuffer);
149 }
150 return retVal;
151 }
152
154 {
155 bool retVal = false;
157 {
158 const std::vector<std::uint8_t> txBuffer = { static_cast<std::uint8_t>(MCPInstruction::RESET) };
159 SPITransactionFrame frame(&txBuffer);
163 }
164 return retVal;
165 }
166
167 bool MCP2515CANInterface::write_register(const MCPRegister address, const std::uint8_t data)
168 {
169 bool retVal = false;
171 {
172 const std::vector<std::uint8_t> txBuffer = { static_cast<std::uint8_t>(MCPInstruction::WRITE),
173 static_cast<std::uint8_t>(address),
174 data };
175 SPITransactionFrame frame(&txBuffer);
179 }
180 return retVal;
181 }
182
183 bool MCP2515CANInterface::write_register(const MCPRegister address, const std::uint8_t data[], const std::size_t length)
184 {
185 bool retVal = false;
187 {
188 std::vector<std::uint8_t> txBuffer(2 + length, 0x00);
189 txBuffer[0] = static_cast<std::uint8_t>(MCPInstruction::WRITE);
190 txBuffer[1] = static_cast<std::uint8_t>(address);
191 memcpy(txBuffer.data() + 2, data, length);
192 SPITransactionFrame frame(&txBuffer);
193
197 }
198 return retVal;
199 }
200
202 {
203 bool retVal = false;
204 if (modify_register(MCPRegister::CANCTRL, 0xE0, static_cast<std::uint8_t>(mode)))
205 {
206 // Wait for the mode to be set within 10ms
207 const auto start = isobus::SystemTiming::get_timestamp_ms();
208 while (isobus::SystemTiming::get_time_elapsed_ms(start) < 10)
209 {
210 std::uint8_t newMode;
211 if (read_register(MCPRegister::CANSTAT, newMode))
212 {
213 if ((newMode & 0xE0) == static_cast<std::uint8_t>(mode))
214 {
215 retVal = true;
216 break;
217 }
218 }
219 }
220 }
221 return retVal;
222 }
223
225 const MCPRegister ctrlRegister,
226 const MCPRegister dataRegister,
227 const std::uint8_t intfMask)
228 {
229 bool retVal = false;
230
231 std::uint8_t buffer[6];
232 if (read_register(ctrlRegister, buffer, 6))
233 {
234 canFrame.identifier = (buffer[1] << 3) + (buffer[2] >> 5);
235
236 if (0x08 == (buffer[2] & 0x08))
237 {
238 canFrame.identifier = (canFrame.identifier << 2) + (buffer[2] & 0x03);
239 canFrame.identifier = (canFrame.identifier << 8) + buffer[3];
240 canFrame.identifier = (canFrame.identifier << 8) + buffer[4];
241 canFrame.isExtendedFrame = true;
242 }
243
244 std::uint8_t ctrl = buffer[0];
245 if (ctrl & 0x08)
246 {
247 // TODO: Handle remote frames
248 }
249
250 canFrame.dataLength = (buffer[5] & 0x0F);
251 if (isobus::CAN_DATA_LENGTH >= canFrame.dataLength)
252 {
253 if ((read_register(dataRegister, canFrame.data, canFrame.dataLength)) &&
254 (modify_register(MCPRegister::CANINTF, intfMask, 0)))
255 {
256 retVal = true;
257 }
258 }
259 }
260 return retVal;
261 }
262
264 {
265 bool retVal = false;
266
267 std::uint8_t status;
268
269 while (get_is_valid())
270 {
271 if (get_read_status(status))
272 {
273 // Check if any of the buffers have a message
274 if (status & 0x01 && rxIndex == 0)
275 {
276 retVal = read_frame(canFrame, MCPRegister::RXB0CTRL, MCPRegister::RXB0DATA, 0x01);
277 get_read_status(status);
278 if (status & 0x02)
279 {
280 rxIndex = 1;
281 }
282 break;
283 }
284 else if (status & 0x02)
285 {
286 retVal = read_frame(canFrame, MCPRegister::RXB1CTRL, MCPRegister::RXB1DATA, 0x02);
287 rxIndex = 0;
288 break;
289 }
290 }
291 std::this_thread::sleep_for(std::chrono::milliseconds(6));
292 }
293
294 return retVal;
295 }
296
298 const MCPRegister ctrlRegister,
299 const MCPRegister sidhRegister)
300 {
301 bool retVal = false;
302
303 // Check if the write buffer is empty
304 std::uint8_t ctrl;
305 if (read_register(ctrlRegister, ctrl))
306 {
307 if ((ctrl & 0x08) == 0)
308 {
309 // Check if last message was sent successfully
310 if (0 != (ctrl & (0x40 | 0x10)))
311 {
312 LOG_ERROR("[MCP2515] Failed to send last message, please verify your connection/setup:");
313 if (0 != (ctrl & 0x40))
314 {
315 LOG_ERROR("\t- Message was aborted.");
316 }
317 if (0 != (ctrl & 0x10))
318 {
319 LOG_ERROR("\t- A bus error occurred while the message was being transmitted.");
320 }
321 }
322
323 // Buffer is empty now we can write the buffer
324 std::uint8_t buffer[13];
325 if (canFrame.isExtendedFrame)
326 {
327 buffer[3] = static_cast<std::uint8_t>(canFrame.identifier & 0xFF); //< EID0
328 buffer[2] = static_cast<std::uint8_t>((canFrame.identifier >> 8) & 0xFF); //< EID8
329 buffer[1] = static_cast<std::uint8_t>((canFrame.identifier >> 16) & 0x03); //< SIDL EID17-16
330 buffer[1] |= static_cast<std::uint8_t>((canFrame.identifier >> 13) & 0xE0); //< SIDL SID0-2
331 buffer[1] |= 0x08; //< SIDL exide mask
332 buffer[0] = static_cast<std::uint8_t>((canFrame.identifier >> 21) & 0xFF); //< SIDH
333 }
334 else
335 {
336 buffer[1] = static_cast<std::uint8_t>((canFrame.identifier << 5) & 0xE0); // SIDL
337 buffer[0] = static_cast<std::uint8_t>(canFrame.identifier >> 3); // SIDH
338 }
339
340 buffer[4] = canFrame.dataLength; //< DLC
341 memcpy(&buffer[5], canFrame.data, canFrame.dataLength); //< Data
342 if (write_register(sidhRegister, buffer, 5 + canFrame.dataLength))
343 {
344 // Now indicate that the buffer is ready to be sent
345 if (modify_register(ctrlRegister, 0x08 | 0x03, 0x08 | txPriority))
346 {
347 retVal = true;
348 }
349 }
350 }
351 else
352 {
353 LOG_WARNING("[MCP2515] Failed to send message, buffer is not empty.");
354 }
355 }
356
357 return retVal;
358 }
359
361 {
362 bool retVal = false;
363 std::uint8_t retries = 100;
364 std::uint8_t status;
365 while (!retVal && retries > 0)
366 {
367 get_read_status(status);
368 if (0 == (status & 0x04) && txIndex == 0)
369 {
370 retVal = write_frame(canFrame, MCPRegister::TXB0CTRL, MCPRegister::TXB0SIDH);
371 if (retVal)
372 {
373 txIndex = 2;
374 }
375 }
376 else if (0 == (status & 0x10) && txIndex == 1)
377 {
378 retVal = write_frame(canFrame, MCPRegister::TXB1CTRL, MCPRegister::TXB1SIDH);
379 if (retVal)
380 {
381 txIndex = 0;
382 }
383 }
384 else if (0 == (status & 0x40) && txIndex == 2)
385 {
386 if (status & (0x10 | 0x04))
387 {
388 // If there are messages in one of the other buffers, lower the priority of this message if possible, otherwise raise the priority of the other messages
389 if (txPriority > 0)
390 {
391 txPriority--;
392 }
393 else
394 {
395 txPriority = 2;
396 modify_register(MCPRegister::TXB1CTRL, 0x03, 0x03);
397 modify_register(MCPRegister::TXB0CTRL, 0x03, 0x03);
398 }
399 }
400 retVal = write_frame(canFrame, MCPRegister::TXB2CTRL, MCPRegister::TXB2SIDH);
401 if (retVal)
402 {
403 txIndex = 1;
404 }
405 }
406 retries--;
407 }
408
409 if (!retVal)
410 {
411 LOG_ERROR("[MCP2515] Failed to send message, buffer has been full for too long.");
412 }
413 return retVal;
414 }
415}
A class that acts as a logging sink. The intent is that someone could make their own derived class of...
A CAN frame for interfacing with a hardware layer, like socket CAN or other interface.
std::uint8_t dataLength
The length of the data used in the frame.
std::uint32_t identifier
The 32 bit identifier of the frame.
bool isExtendedFrame
Denotes if the frame is extended format.
std::uint8_t data[8]
The data payload of the frame.
std::uint8_t rxIndex
The index of the rx buffer to read from next.
bool reset()
Resets the MCP2515.
bool write_frame(const isobus::CANMessageFrame &canFrame) override
Writes a frame to the bus (synchronous)
bool write_register(const MCPRegister address, const std::uint8_t data)
write a single byte register of the mcp2515
MCPMode
The different modes of the MCP2515 associated with their internal bits.
MCPRegister
Some essential registers of the MCP2515.
const std::uint8_t cfg2
Configuration value for CFG2 register.
void close() override
Closes the socket.
bool write_reset()
Reset the mcp2515 internally.
const std::uint8_t cfg1
Configuration value for CFG1 register.
std::uint8_t txIndex
The index of the tx buffer to write to next, start with 2 as it is the buffer with the highest priori...
std::uint8_t txPriority
The priority of the next tx frame.
bool get_is_valid() const override
Returns if the socket connection is valid.
const std::uint8_t cfg3
Configuration value for CFG3 register.
bool read_register(const MCPRegister address, std::uint8_t &data)
read a single byte register of the mcp2515
bool set_mode(const MCPMode mode)
set the mode of the mcp2515
bool modify_register(const MCPRegister address, const std::uint8_t mask, const std::uint8_t data)
modify a register of the mcp2515
virtual ~MCP2515CANInterface()
The destructor for SocketCANInterface.
bool read_frame(isobus::CANMessageFrame &canFrame) override
Returns a frame from the hardware (synchronous), or false if no frame can be read.
SPIHardwarePlugin * transactionHandler
The SPI transaction handler.
bool get_read_status(std::uint8_t &status)
Read the rx status of the mcp2515.
MCP2515CANInterface(SPIHardwarePlugin *transactionHandler, const std::uint8_t cfg1, const std::uint8_t cfg2, const std::uint8_t cfg3)
Constructor for the socket CAN driver.
void open() override
Connects to the socket.
bool initialized
If the mcp2515 has been initialized and no errors have occurred.
An abstract base class for SPI communication.
virtual void transmit(SPITransactionFrame *frame)=0
Write a frame to the SPI bus. This should only be called after begin_transaction()....
virtual void begin_transaction()
Begin a transaction on the SPI bus. This should be called before any of the read/write operations.
virtual bool end_transaction()=0
End a transaction on the SPI bus. This must be called after all write operations and before any read ...
A class containing the data for a single SPI transaction.
bool read_byte(std::size_t index, std::uint8_t &byte) const
Read a byte from the response buffer.
bool read_bytes(std::size_t index, std::uint8_t *buffer, std::size_t length) const
Read multiple bytes from the response buffer.
An interface for using the MCP2515 can controller.
This namespace encompasses all of the ISO11783 stack's functionality to reduce global namespace pollu...
constexpr std::uint8_t CAN_DATA_LENGTH
The length of a classical CAN frame.