5
5
`adafruit_mcp2515`
6
6
================================================================================
7
7
8
- A CircuitPython library for working with the MCP2515 CAN bus controller
8
+ A CircuitPython library for working with the MCP2515 CAN bus controller using the
9
+ CircuitPython `canio` API
9
10
10
11
11
12
* Author(s): Bryan Siepert
20
21
* Adafruit CircuitPython firmware for the supported boards:
21
22
https://github.com/adafruit/circuitpython/releases
22
23
23
- # * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
24
- # * Adafruit's Register library: https://github.com/adafruit/Adafruit_CircuitPython_Register
24
+ * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
25
25
"""
26
26
27
27
from collections import namedtuple
30
30
from micropython import const
31
31
import adafruit_bus_device .spi_device as spi_device
32
32
from .canio import *
33
+ from .timer import Timer
33
34
34
35
__version__ = "0.0.0-auto.0"
35
36
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_MCP2515.git"
162
163
"TransmitBuffer" ,
163
164
["CTRL_REG" , "STD_ID_REG" , "INT_FLAG_MASK" , "LOAD_CMD" , "SEND_CMD" ],
164
165
)
165
- # perhaps this will be stateful later? #TODO : dedup with above
166
- FilterMask = namedtuple (
167
- "FilterMask" , ["CTRL_REG" , "STD_ID_REG" , "INT_FLAG_MASK" , "LOAD_CMD" , "SEND_CMD" ],
168
- )
169
166
167
+ # This is magic, don't disturb the dragon
168
+ # expects a 16Mhz crystal
170
169
_BAUD_RATES = {
170
+ # CNF1, CNF2, CNF3
171
171
1000000 : (0x00 , 0xD0 , 0x82 ),
172
172
500000 : (0x00 , 0xF0 , 0x86 ),
173
173
250000 : (0x41 , 0xF1 , 0x85 ),
@@ -209,7 +209,7 @@ def _tx_buffer_status_decode(status_byte):
209
209
210
210
211
211
class MCP2515 : # pylint:disable=too-many-instance-attributes
212
- """Library for the MCP2515 CANbus controller """
212
+ """A common shared-bus protocol. """
213
213
214
214
def __init__ (
215
215
self ,
@@ -222,29 +222,35 @@ def __init__(
222
222
auto_restart : bool = False ,
223
223
debug : bool = False
224
224
):
225
- """[summary]
226
-
227
- Args:
228
- spi_bus (busio.SPI): The SPI bus used to communicate with the MCP2515
229
- cs_pin (digitalio.DigitalInOut): SPI bus enable pin
230
- baudrate (int, optional): The bit rate of the bus in Hz. All devices on the bus must \
231
- agree on this value. Defaults to 250000.
232
- loopback (bool, optional): When True the rx pin’s value is ignored, and the device \
233
- receives the packets it sends. Defaults to False.
234
- silent (bool, optional): When True the tx pin is always driven to the high logic level.\
235
- This mode can be used to “sniff” a CAN bus without interfering.. Defaults to False.
236
- auto_restart (bool, optional): If True, will restart communications after entering \
237
- bus-off state. Defaults to False.
238
- debug (bool, optional): If True, will enable printing debug information. Defaults to \
239
- False.
225
+ """A common shared-bus protocol.
226
+
227
+ :param ~busio.SPI spi: The SPI bus used to communicate with the MCP2515
228
+ :param ~digitalio.DigitalInOut cs_pin: SPI bus enable pin
229
+ :param int baudrate: The bit rate of the bus in Hz, using a 16Mhz crystal. All devices on\
230
+ the bus must agree on this value. Defaults to 250000.
231
+ :param bool loopback: Receive only packets sent from this device, and send only to this\
232
+ device. Requires that `silent` is also set to `True`, but only prevents transmission to\
233
+ other devices. Otherwise the send/receive behavior is normal.
234
+ :param bool silent: When `True` the controller does not transmit and all messages are\
235
+ received, ignoring errors and filters. This mode can be used to “sniff” a CAN bus without\
236
+ interfering. Defaults to `False`.
237
+ :param bool auto_restart: **Not supported by hardware. An `AttributeError` will be raised\
238
+ if `auto_restart` is set to `True`** If `True`, will restart communications after entering\
239
+ bus-off state. Defaults to `False`.
240
+
241
+ :param bool debug: If `True`, will enable printing debug information. Defaults to `False`.
240
242
"""
241
- if loopback and silent :
242
- raise AttributeError ("only loopback or silent mode can bet set, not both" )
243
+
244
+ if loopback and not silent :
245
+ raise AttributeError ("Loopback mode requires silent to be set" )
246
+ if auto_restart :
247
+ raise AttributeError ("`auto-restart` is not supported by hardware" )
248
+
243
249
self ._auto_restart = auto_restart
244
250
self ._debug = debug
245
251
self ._bus_device_obj = spi_device .SPIDevice (spi_bus , cs_pin )
246
252
self ._cs_pin = cs_pin
247
- self ._buffer = bytearray (255 )
253
+ self ._buffer = bytearray (20 )
248
254
self ._id_buffer = bytearray (4 )
249
255
self ._unread_message_queue = []
250
256
self ._timer = Timer ()
@@ -324,33 +330,20 @@ def initialize(self):
324
330
325
331
self ._set_mode (new_mode )
326
332
327
- def send (self , message_obj , wait_sent = True ):
333
+ def send (self , message_obj ):
328
334
"""Send a message on the bus with the given data and id. If the message could not be sent
329
335
due to a full fifo or a bus error condition, RuntimeError is raised.
330
336
331
337
Args:
332
338
message (canio.Message): The message to send. Must be a valid `canio.Message`
333
339
"""
340
+
334
341
# TODO: Timeout
335
342
tx_buff = self ._get_tx_buffer () # info = addr.
343
+ if tx_buff is None :
344
+ raise RuntimeError ("No transmit buffer available to send" )
336
345
337
- # TODO: set buffer priority
338
- self ._write_message (tx_buff , message_obj )
339
- if not wait_sent :
340
- return True
341
- sleep (0.010 )
342
-
343
- send_confirmed = False
344
- self ._timer .rewind_to (0.005 )
345
- while not self ._timer .expired :
346
- # the status register address is whatever tx_buff_n is, minus one?
347
- tx_buff_status = self ._read_register (tx_buff .CTRL_REG )
348
- self ._dbg (_tx_buffer_status_decode (tx_buff_status ))
349
- send_confirmed = (tx_buff_status & _TXB_TXREQ_M ) == 0
350
- if send_confirmed :
351
- return True
352
-
353
- raise RuntimeError ("Timeout occoured waiting for transmit confirmation" )
346
+ return self ._write_message (tx_buff , message_obj )
354
347
355
348
@property
356
349
def unread_message_count (self ):
@@ -375,6 +368,9 @@ def read_message(self):
375
368
return self ._unread_message_queue .pop (0 )
376
369
377
370
def _read_rx_buffer (self , read_command ):
371
+ for i in len (self ._buffer ):
372
+ self ._buffer [i ] = 0
373
+
378
374
# read from buffer
379
375
with self ._bus_device_obj as spi :
380
376
self ._buffer [0 ] = read_command
@@ -407,7 +403,6 @@ def _read_rx_buffer(self, read_command):
407
403
data = bytes (self ._buffer [5 : 5 + message_length ]),
408
404
extended = extended ,
409
405
)
410
-
411
406
self ._unread_message_queue .append (frame_obj )
412
407
413
408
def _read_from_rx_buffers (self ):
@@ -427,6 +422,8 @@ def _read_from_rx_buffers(self):
427
422
428
423
def _write_message (self , tx_buffer , message_obj ):
429
424
425
+ if tx_buffer is None :
426
+ raise RuntimeError ("No transmit buffer available to send" )
430
427
if isinstance (message_obj , RemoteTransmissionRequest ):
431
428
dlc = message_obj .length
432
429
else :
@@ -471,6 +468,7 @@ def _write_message(self, tx_buffer, message_obj):
471
468
472
469
# send the frame based on the current buffers
473
470
self ._start_transmit (tx_buffer )
471
+ return True
474
472
475
473
# TODO: Priority
476
474
def _start_transmit (self , tx_buffer ):
@@ -830,7 +828,7 @@ def listen(self, matches=None, *, timeout: float = 10):
830
828
831
829
An empty filter list causes all messages to be accepted.
832
830
833
- Timeout dictates how long ``receive()`` and ``next()`` will block.
831
+ Timeout dictates how long ``receive()`` will block.
834
832
835
833
Args:
836
834
match (Optional[Sequence[Match]], optional): [description]. Defaults to None.
@@ -841,6 +839,11 @@ def listen(self, matches=None, *, timeout: float = 10):
841
839
"""
842
840
if matches is None :
843
841
matches = []
842
+ elif self .silent and not self .loopback :
843
+ raise AttributeError (
844
+ "Hardware does not support setting `matches` in when\
845
+ `silent`==`True` and `loopback` == `False`"
846
+ )
844
847
845
848
for match in matches :
846
849
self ._dbg ("match:" , match )
0 commit comments