Skip to content

Commit eadb9c1

Browse files
committed
addressing feedback from @jepler
1 parent bafc6db commit eadb9c1

File tree

5 files changed

+104
-74
lines changed

5 files changed

+104
-74
lines changed

adafruit_mcp2515/__init__.py

Lines changed: 38 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
`adafruit_mcp2515`
66
================================================================================
77
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
910
1011
1112
* Author(s): Bryan Siepert
@@ -20,8 +21,7 @@
2021
* Adafruit CircuitPython firmware for the supported boards:
2122
https://github.com/adafruit/circuitpython/releases
2223
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
2525
"""
2626

2727
from collections import namedtuple
@@ -30,6 +30,7 @@
3030
from micropython import const
3131
import adafruit_bus_device.spi_device as spi_device
3232
from .canio import *
33+
from .timer import Timer
3334

3435
__version__ = "0.0.0-auto.0"
3536
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_MCP2515.git"
@@ -162,12 +163,11 @@
162163
"TransmitBuffer",
163164
["CTRL_REG", "STD_ID_REG", "INT_FLAG_MASK", "LOAD_CMD", "SEND_CMD"],
164165
)
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-
)
169166

167+
# This is magic, don't disturb the dragon
168+
# expects a 16Mhz crystal
170169
_BAUD_RATES = {
170+
# CNF1, CNF2, CNF3
171171
1000000: (0x00, 0xD0, 0x82),
172172
500000: (0x00, 0xF0, 0x86),
173173
250000: (0x41, 0xF1, 0x85),
@@ -209,7 +209,24 @@ def _tx_buffer_status_decode(status_byte):
209209

210210

211211
class MCP2515: # pylint:disable=too-many-instance-attributes
212-
"""Library for the MCP2515 CANbus controller"""
212+
"""
213+
A common shared-bus protocol.
214+
215+
:param ~busio.SPI spi: The SPI bus used to communicate with the MCP2515
216+
:param ~digitalio.DigitalInOut cs_pin: SPI bus enable pin
217+
:param int baudrate: The bit rate of the bus in Hz, using a 16Mhz crystal. All devices on\
218+
the bus must agree on this value. Defaults to 250000.
219+
:param bool loopback: Receive only packets sent from this device, and send only to\
220+
this device. Requires that `silent` is also set to `False`, but only prevents\
221+
transimssion to other devices. Otherwise the send/receive behavior is normal.
222+
:param bool silent:When `True` the controller does not transmit and all messages\
223+
are received, ignoring errors and filters. This mode can be used to “sniff” a CAN\
224+
bus without interfering. Defaults to `False`.
225+
:param bool auto_restart: **Not supported by hardware. An `AttributeError`
226+
will be raised if `auto_restart` is set to `True`** If `True`, will restart\
227+
communications after entering bus-off state. Defaults to `False`.
228+
:param bool debug: If `True`, will enable printing debug information. Defaults to `False`.
229+
"""
213230

214231
def __init__(
215232
self,
@@ -222,24 +239,12 @@ def __init__(
222239
auto_restart: bool = False,
223240
debug: bool = False
224241
):
225-
"""[summary]
226242

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.
240-
"""
241-
if loopback and silent:
242-
raise AttributeError("only loopback or silent mode can bet set, not both")
243+
if loopback and not silent:
244+
raise AttributeError("Loopback mode requires silent to be set")
245+
if auto_restart:
246+
raise AttributeError("`auto-restart` is not supported by hardware")
247+
243248
self._auto_restart = auto_restart
244249
self._debug = debug
245250
self._bus_device_obj = spi_device.SPIDevice(spi_bus, cs_pin)
@@ -331,6 +336,7 @@ def send(self, message_obj, wait_sent=True):
331336
Args:
332337
message (canio.Message): The message to send. Must be a valid `canio.Message`
333338
"""
339+
334340
# TODO: Timeout
335341
tx_buff = self._get_tx_buffer() # info = addr.
336342

@@ -350,7 +356,7 @@ def send(self, message_obj, wait_sent=True):
350356
if send_confirmed:
351357
return True
352358

353-
raise RuntimeError("Timeout occoured waiting for transmit confirmation")
359+
raise RuntimeError("Timeout occurred waiting for transmit confirmation")
354360

355361
@property
356362
def unread_message_count(self):
@@ -830,7 +836,7 @@ def listen(self, matches=None, *, timeout: float = 10):
830836
831837
An empty filter list causes all messages to be accepted.
832838
833-
Timeout dictates how long ``receive()`` and ``next()`` will block.
839+
Timeout dictates how long ``receive()`` will block.
834840
835841
Args:
836842
match (Optional[Sequence[Match]], optional): [description]. Defaults to None.
@@ -841,6 +847,11 @@ def listen(self, matches=None, *, timeout: float = 10):
841847
"""
842848
if matches is None:
843849
matches = []
850+
elif self.silent and not self.loopback:
851+
raise AttributeError(
852+
"Hardware does not support setting `matches` in when\
853+
`silent`==`True` and `loopback` == `False`"
854+
)
844855

845856
for match in matches:
846857
self._dbg("match:", match)

adafruit_mcp2515/canio/__init__.py

Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
"""Python implementation of the CircuitPython core `canio` API"""
55
# pylint:disable=too-few-public-methods, invalid-name, redefined-builtin
66
import time
7+
from ..timer import Timer
78

89

910
class Message:
10-
"""A class representing a message to send on a `canio` bus
11-
"""
11+
"""A class representing a CANbus data frame"""
1212

1313
# pylint:disable=too-many-arguments,invalid-name,redefined-builtin
1414
def __init__(self, id, data, extended=False):
@@ -50,10 +50,10 @@ def data(self, new_data):
5050

5151

5252
class RemoteTransmissionRequest:
53-
"""RRTTTTRRRR """
53+
"""A class representing a CANbus remote frame"""
5454

5555
def __init__(self, id: int, length: int, *, extended: bool = False):
56-
"""Construct a Message to send on a CAN bus
56+
"""Construct a RemoteTransmissionRequest to send on a CAN bus
5757
5858
Args:
5959
id (int): The numeric ID of the requested message
@@ -205,29 +205,3 @@ def __init__(self, address: int, *, mask: int = 0, extended: bool = False):
205205
self.address = address
206206
self.mask = mask
207207
self.extended = extended
208-
209-
210-
############# non-api classes and methods
211-
class Timer:
212-
"""A class to track timeouts, like an egg timer
213-
"""
214-
215-
def __init__(self, timeout=0.0):
216-
self._timeout = None
217-
self._start_time = None
218-
if timeout:
219-
self.rewind_to(timeout)
220-
221-
@property
222-
def expired(self):
223-
"""Returns the expiration status of the timer
224-
225-
Returns:
226-
bool: True if more than `timeout` seconds has past since it was set
227-
"""
228-
return (time.monotonic() - self._start_time) > self._timeout
229-
230-
def rewind_to(self, new_timeout):
231-
"""Re-wind the timer to a new timeout and start ticking"""
232-
self._timeout = float(new_timeout)
233-
self._start_time = time.monotonic()

adafruit_mcp2515/timer.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2020 Bryan Siepert for Adafruit Industries
2+
#
3+
# SPDX-License-Identifier: MIT
4+
"""Provides a simple timer class; see `Timer`"""
5+
from time import monotonic
6+
7+
8+
class Timer:
9+
"""A reusable class to track timeouts, like an egg timer"""
10+
11+
def __init__(self, timeout=0.0):
12+
self._timeout = None
13+
self._start_time = None
14+
if timeout:
15+
self.rewind_to(timeout)
16+
17+
@property
18+
def expired(self):
19+
"""Returns the expiration status of the timer
20+
21+
Returns:
22+
bool: True if more than `timeout` seconds has past since it was set
23+
"""
24+
return (monotonic() - self._start_time) > self._timeout
25+
26+
def rewind_to(self, new_timeout):
27+
"""Re-wind the timer to a new timeout and start ticking"""
28+
self._timeout = float(new_timeout)
29+
self._start_time = monotonic()

examples/canio_test.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
# SPDX-FileCopyrightText: Copyright (c) 2020 Jeff Epler for Adafruit Industries
22
#
33
# SPDX-License-Identifier: MIT
4+
5+
###################### Board/ Config selection #################
6+
# This is is only on way to make this test work for other boards.
7+
# As an alternative, CAN_TYPE could be set manually and
8+
# board-specific expectations can be if/else'd, and support for a
9+
# new CAN controller or peripheral would start with defining the
10+
# controller-specifc bits
11+
412
CAN_TYPE = None
513
try:
614
from canio import (
@@ -37,8 +45,10 @@ def builtin_bus_factory():
3745
def builtin_bus_factory():
3846
cs = DigitalInOut(board.D5)
3947
cs.switch_to_output()
40-
return CAN(board.SPI(), cs, baudrate=1000000, loopback=True)
48+
return CAN(board.SPI(), cs, baudrate=1000000, loopback=True, silent=True)
49+
4150

51+
################################################################
4252

4353
max_standard_id = 0x7FF
4454
max_extended_id = 0x1FFFFFFF

examples/mcp2515_simpletest.py

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,31 @@
11
# SPDX-FileCopyrightText: Copyright (c) 2020 Bryan Siepert for Adafruit Industries
22
#
33
# SPDX-License-Identifier: MIT
4-
54
from time import sleep
6-
from board import SPI, D5 as CS_PIN
7-
import digitalio
8-
import adafruit_mcp2515
5+
import board
6+
import busio
7+
from digitalio import DigitalInOut
98
from adafruit_mcp2515.canio import Message
9+
from adafruit_mcp2515 import MCP2515 as CAN
10+
1011

11-
cs = digitalio.DigitalInOut(CS_PIN)
12+
cs = DigitalInOut(board.D5)
1213
cs.switch_to_output()
13-
mcp = adafruit_mcp2515.MCP2515(SPI(), cs)
14+
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
1415

15-
max_ext_id = 0x3FFFF
16-
mb1 = [0xDE, 0xAD, 0xBE, 0xEF]
17-
mb2 = [0xCA, 0xFE, 0xFA, 0xDE]
18-
can_id = 0b100000000000000001
16+
can_bus = CAN(
17+
spi, cs, loopback=True, silent=True
18+
) # use loopback to test without another device
1919
while True:
20-
mb1.insert(0, mb2.pop())
21-
mb2.insert(0, mb1.pop())
22-
message = Message(id=0xFFAA, data=bytes(mb1 + mb2), extended=True)
23-
mcp.send(message)
24-
sleep(0.3)
25-
print("*" * 40)
20+
with can_bus.listen(timeout=1.0) as listener:
21+
22+
message = Message(id=0x1234ABCD, data=b"adafruit", extended=True)
23+
can_bus.send(message)
24+
message_count = listener.in_waiting()
25+
print(message_count, "messages available")
26+
for _i in range(message_count):
27+
msg = listener.receive()
28+
print("Message from ", hex(msg.id))
29+
print("message data:", msg.data)
30+
print("")
31+
sleep(1)

0 commit comments

Comments
 (0)