Skip to content

Commit d1b0008

Browse files
committed
esp32\machine_can: Add docs, example and code.
1 parent 1e1be9a commit d1b0008

File tree

13 files changed

+1151
-127
lines changed

13 files changed

+1151
-127
lines changed

docs/esp32/img/twai_blockdiag.png

9.92 KB
Loading

docs/esp32/quickref.rst

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,30 @@ users is encouraged. Based on this feedback, the I2S class API and implementati
545545

546546
ESP32 has two I2S buses with id=0 and id=1
547547

548+
CAN bus
549+
-------
550+
551+
See :ref:`machine.CAN <machine.CAN>` ::
552+
553+
The CAN driver is based on hardware implementation.
554+
Any available output-capablepins can be used for TX, RX, BUS-OFF, and CLKOUT signal lines.
555+
556+
.. image:: img/twai_blockdiag.png
557+
558+
The driver is accessed via the :ref:`machine.CAN <machine.CAN>` class::
559+
560+
from machine import CAN
561+
can = CAN(0, tx=5, rx=4, mode=CAN.NORMAL, baudrate=500000)
562+
can.setfilter(0, CAN.FILTER_ADDRESS, [0x102]) # set a filter to receive messages with id = 0x102
563+
can.send([1,2,3], 0x102) # send a message with id 123
564+
can.recv() # receive message
565+
566+
can.any() # returns True if there are any message to receive
567+
can.info() # get information about the controller’s error states and TX and RX buffers
568+
can.deinit() # turn off the can bus
569+
can.clear_rx_queue() # clear messages in the FIFO
570+
can.clear_tx_queue() # clear messages in the transmit buffer
571+
548572
Real time clock (RTC)
549573
---------------------
550574

3.49 KB
Loading
3.58 KB
Loading
3.43 KB
Loading
3.55 KB
Loading
3.56 KB
Loading

docs/library/machine.CAN.rst

Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
.. currentmodule:: machine
2+
.. _machine.CAN:
3+
4+
class CAN -- controller area network communication bus
5+
======================================================
6+
7+
CAN implements the standard CAN communications protocol. At
8+
the physical level it consists of 2 lines: RX and TX. Note that
9+
to connect the microcontroller to a CAN bus you must use a CAN transceiver
10+
to convert the CAN logic signals from the microcontroller to the correct
11+
voltage levels on the bus.
12+
13+
Example usage (works without anything connected)::
14+
15+
from machine import CAN
16+
BAUDRATE_500k = 500
17+
can = CAN(0, extframe=True, mode=CAN.LOOPBACK, baudrate=BAUDRATE_500k)
18+
dev.setfilter(0, CAN.FILTER_ADDRESS, [0x102, 0]) # set a filter to receive messages with id = 0x102
19+
can.send([1,2,3], 0x102) # send a message with id 123
20+
can.recv() # receive message
21+
22+
23+
Constructors
24+
------------
25+
26+
.. class:: machine.CAN(bus, ...)
27+
28+
Construct a CAN object on the given bus(controller). *bus* can be 0 or 1 for STM32 and 0 for ESP32.
29+
With no additional parameters, the CAN object is created but not
30+
initialised (it has the settings from the last initialisation of
31+
the bus, if any). If extra arguments are given, the bus is initialised.
32+
See :meth:`CAN.init` for parameters of initialisation.
33+
34+
The physical pins of the CAN bus can be assigned during init.
35+
36+
Methods
37+
-------
38+
39+
.. method:: CAN.init(mode, extframe=False, baudrate, prescaler, \*, sjw=1, bs1=6, bs2=8, auto_restart=False)
40+
41+
Initialise the CAN bus with the given parameters:
42+
43+
- *mode* is one of: NORMAL, LOOPBACK, SILENT, SILENT_LOOPBACK
44+
- if *extframe* is True then the bus uses extended identifiers in the frames
45+
(29 bits); otherwise it uses standard 11 bit identifiers
46+
- *baudrate* is used to define a standard speed. If it is defined, the *prescaler*, *sjw*, *bs1*, *bs2*
47+
will be ignored. Standard speeds are 25, 50, 100, 125, 250, 500, 1000
48+
- *prescaler* is used to set the duration of 1 time quanta; the time quanta
49+
will be the input clock divided by the prescaler
50+
- *sjw* is the resynchronisation jump width in units of the time quanta;
51+
it can be 1, 2, 3, 4
52+
- *bs1* defines the location of the sample point in units of the time quanta;
53+
it can be between 1 and 1024 inclusive
54+
- *bs2* defines the location of the transmit point in units of the time quanta;
55+
it can be between 1 and 16 inclusive
56+
- *tx* defines the gpio used for transmission
57+
- *rx* defines the gpio used for receiving
58+
- *bus_off* defines the gpio used for BUS-OFF signal line(optional)
59+
- *clkout* defines the gpio used for CLKOUT signal line(optional)
60+
- *tx_queue* defines the number of waiting tx messages can be stored
61+
- *rx_queue* defines the number of received messages can be stored
62+
- *auto_restart* sets whether the controller will automatically try and restart
63+
communications after entering the bus-off state; if this is disabled then
64+
:meth:`~CAN.restart()` can be used to leave the bus-off state.
65+
This parameter is currently not implemented and it must be set to False
66+
67+
68+
.. method:: CAN.deinit()
69+
70+
Turn off the CAN bus.
71+
72+
.. method:: CAN.restart()
73+
74+
Force a software restart of the CAN controller without resetting its
75+
configuration.
76+
77+
If the controller enters the bus-off state then it will no longer participate
78+
in bus activity. If the controller is not configured to automatically restart
79+
(see :meth:`~CAN.init()`) then this method can be used to trigger a restart,
80+
and the controller will follow the CAN protocol to leave the bus-off state and
81+
go into the error active state.
82+
83+
.. method:: CAN.state()
84+
85+
Return the state of the controller. The return value can be one of:
86+
87+
- ``CAN.STOPPED`` -- the controller is completely off and reset;
88+
- ``CAN.ERROR_ACTIVE`` -- the controller is on and in the Error Active state
89+
(both TEC and REC are less than 96);
90+
- ``CAN.BUS_OFF`` -- the controller is on but not participating in bus activity
91+
(TEC overflowed beyond 255).
92+
- ``CAN.RECOVERING`` -- the controller is under recover from bus-off state;
93+
94+
95+
.. method:: CAN.info([list])
96+
97+
Get information about the controller's error states and TX and RX buffers.
98+
If *list* is provided then it should be a list object with at least 8 entries,
99+
which will be filled in with the information. Otherwise a new list will be
100+
created and filled in. In both cases the return value of the method is the
101+
populated list.
102+
103+
The values in the list are:
104+
105+
- TEC value
106+
- REC value
107+
- number of times the controller enterted the Error Warning state (wrapped
108+
around to 0 after 65535) - CURRENTLY NOT IMPLEMENTED
109+
- number of times the controller enterted the Error Passive state (wrapped
110+
around to 0 after 65535) - CURRENTLY NOT IMPLEMENTED
111+
- number of times the controller enterted the Bus Off state (wrapped
112+
around to 0 after 65535) - CURRENTLY NOT IMPLEMENTED
113+
- number of pending TX messages
114+
- number of pending RX messages
115+
- Reserved
116+
117+
.. method:: CAN.setfilter(bank, mode, fifo, params, \*, rtr)
118+
119+
Configure a filter bank:
120+
121+
- *bank* is the filter bank that is to be configured (0 for extended, 0 or 1 for standard msg)
122+
- *mode* is the mode the filter should operate in.
123+
- *params* is an array of two values that defines the filter.
124+
The first element will be the id to filter and the second element will be the mask to apply.
125+
mask bit implementation considers 1 as a don't care state and 0 as a check state.
126+
127+
+-----------------------+----------------------------------------------+
128+
| *mode* | contents of *params* array |
129+
+=======================+==============================================+
130+
| CAN.FILTER_RAW_SINGLE | *params* will be copied in hardware variable |
131+
| | and single_filter_mode will be selected |
132+
| | In this mode, *bank* will be ignored |
133+
+-----------------------+----------------------------------------------+
134+
| CAN.FILTER_RAW_DUAL | *params* will be copied in hardware variable |
135+
| | and single_filter_mode will be cleared |
136+
| | In this mode, *bank* will be ignored |
137+
+-----------------------+----------------------------------------------+
138+
| CAN.FILTER_ADDRESS | *params* will be set in hardware registers |
139+
| | according to *bank* selection |
140+
+-----------------------+----------------------------------------------+
141+
142+
- *rtr* is bool that states if a filter should accept a remote transmission request message.
143+
If this argument is not given then it defaults to ``False``.
144+
145+
.. method:: CAN.clearfilter(bank)
146+
147+
Clear and disables all filters
148+
149+
.. method:: CAN.any(fifo)
150+
151+
Return ``True`` if any message waiting on the FIFO, else ``False``.
152+
153+
.. method:: CAN.recv(list=None, \*, timeout=5000)
154+
155+
Receive data on the bus:
156+
157+
- *list* is an optional list object to be used as the return value
158+
- *timeout* is the timeout in milliseconds to wait for the receive.
159+
160+
Return value: A tuple containing four values.
161+
162+
- The id of the message.
163+
- A boolean that indicates if the message is an RTR message.
164+
- Reserved.
165+
- An array containing the data.
166+
167+
If *list* is ``None`` then a new tuple will be allocated, as well as a new
168+
bytes object to contain the data (as the fourth element in the tuple).
169+
170+
If *list* is not ``None`` then it should be a list object with a least four
171+
elements. The fourth element should be a memoryview object which is created
172+
from either a bytearray or an array of type 'B' or 'b', and this array must
173+
have enough room for at least 8 bytes. The list object will then be
174+
populated with the first three return values above, and the memoryview object
175+
will be resized inplace to the size of the data and filled in with that data.
176+
The same list and memoryview objects can be reused in subsequent calls to
177+
this method, providing a way of receiving data without using the heap.
178+
For example::
179+
180+
buf = bytearray(8)
181+
lst = [0, 0, 0, memoryview(buf)]
182+
# No heap memory is allocated in the following call
183+
can.recv(lst, timeout=0)
184+
185+
.. method:: CAN.send(data, id, \*, timeout=0, rtr=False)
186+
187+
Send a message on the bus:
188+
189+
- *data* is the data to send (an integer to send, or a buffer object).
190+
- *id* is the id of the message to be sent.
191+
- *timeout* is the timeout in milliseconds to wait for the send.
192+
- *rtr* is a boolean that specifies if the message shall be sent as
193+
a remote transmission request. If *rtr* is True then only the length
194+
of *data* is used to fill in the DLC slot of the frame; the actual
195+
bytes in *data* are unused.
196+
197+
If timeout is 0 the message is placed in a buffer and the method returns
198+
immediately. If all three buffers are in use an exception is thrown.
199+
If timeout is not 0, the method waits until the message is transmitted.
200+
If the message can't be transmitted within the specified time an exception
201+
is thrown.
202+
203+
Return value: ``None``.
204+
205+
.. method:: CAN.clear_tx_queue()
206+
207+
Clear all messages from transmitting queue.
208+
209+
.. method:: CAN.clear_rx_queue()
210+
211+
Clear all messages from receiving queue.
212+
213+
.. method:: CAN.get_alerts()
214+
215+
Read the alert status word directly from hardware.
216+
In order to save space in the firmware, the constants for the result decoding are not included on the :mod:`machine.CAN` module. Add the ones that you need from the list below to your program.
217+
218+
The event codes are::
219+
220+
from micropython import const
221+
CAN_ALERT_TX_IDLE = const(0x0001)
222+
CAN_ALERT_TX_SUCCESS = const(0x0002)
223+
CAN_ALERT_BELOW_ERR_WARN = const(0x0004)
224+
CAN_ALERT_ERR_ACTIVE = const(0x0008)
225+
CAN_ALERT_RECOVERY_IN_PROGRESS = const(0x0010)
226+
CAN_ALERT_BUS_RECOVERED = const(0x0020)
227+
CAN_ALERT_ARB_LOST = const(0x0040)
228+
CAN_ALERT_ABOVE_ERR_WARN = const(0x0080)
229+
CAN_ALERT_BUS_ERROR = const(0x0100)
230+
CAN_ALERT_TX_FAILED = const(0x0200)
231+
CAN_ALERT_RX_QUEUE_FULL = const(0x0400)
232+
CAN_ALERT_ERR_PASS = const(0x0800)
233+
CAN_ALERT_BUS_OFF = const(0x1000)
234+
235+
236+
Constants
237+
---------
238+
239+
.. data:: CAN.NORMAL
240+
CAN.LOOPBACK
241+
CAN.SILENT
242+
CAN.SILENT_LOOPBACK
243+
CAN.LISTEN_ONLY
244+
245+
246+
The mode of the CAN bus used in :meth:`~CAN.init()`.
247+
248+
+---------------------+---------------------------------------------+-------+-------+
249+
| *mode* | \ | STM32 | ESP32 |
250+
+=====================+=============================================+=======+=======+
251+
| CAN.NORMAL | .. image:: img/can_mode_normal.png | + | + |
252+
+---------------------+---------------------------------------------+-------+-------+
253+
| CAN.LOOPBACK | .. image:: img/can_mode_loopback.png | + | + |
254+
+---------------------+---------------------------------------------+-------+-------+
255+
| CAN.SILENT | .. image:: img/can_mode_silent.png | + | + |
256+
+---------------------+---------------------------------------------+-------+-------+
257+
| CAN.SILENT_LOOPBACK | .. image:: img/can_mode_silent_loopback.png | + | |
258+
+---------------------+---------------------------------------------+-------+-------+
259+
| CAN.LISTEN_ONLY | .. image:: img/can_mode_listen_only.png | | + |
260+
+---------------------+---------------------------------------------+-------+-------+
261+
262+
263+
.. data:: CAN.STOPPED
264+
CAN.ERROR_ACTIVE
265+
CAN.BUS_OFF
266+
CAN.RECOVERING
267+
268+
Possible states of the CAN controller returned from :meth:`~CAN.state()`.
269+
270+
.. data:: CAN.FILTER_RAW_SINGLE
271+
CAN.FILTER_RAW_DUAL
272+
CAN.FILTER_ADDRESS
273+
274+
The operation mode of a filter used in :meth:`~CAN.setfilter()`.
275+

docs/library/machine.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@ Classes
260260
machine.SPI.rst
261261
machine.I2C.rst
262262
machine.I2S.rst
263+
machine.CAN.rst
263264
machine.RTC.rst
264265
machine.Timer.rst
265266
machine.WDT.rst

examples/esp32_can.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
from machine import CAN
2+
import time
3+
4+
5+
def send_and_check(can_bus, name, id, expected_result=True, extended=False):
6+
can_bus.clear_tx_queue()
7+
can_bus.clear_rx_queue()
8+
can_bus.send([], id, extframe=extended)
9+
time.sleep_ms(100)
10+
if can_bus.any() == expected_result:
11+
print("{}: OK".format(name))
12+
if expected_result:
13+
can_bus.recv()
14+
else:
15+
print("{}: FAILED".format(name))
16+
17+
18+
# 4 and 5 pins must be connected to each other, see documentation
19+
dev = CAN(0, extframe=False, tx=5, rx=4, mode=CAN.SILENT_LOOPBACK, baudrate=50000, auto_restart=False)
20+
21+
# Test send/receive message
22+
print("Loopback Test: no filter - STD")
23+
send_and_check(dev, "No filter", 0x100, True)
24+
25+
# Set filter1
26+
print("Loopback Test: one filter - STD")
27+
dev.setfilter(0, CAN.FILTER_ADDRESS, [0x101, 0])
28+
send_and_check(dev, "Passing Message", 0x101, True)
29+
send_and_check(dev, "Blocked Message", 0x100, False)
30+
31+
# Set filter2
32+
print("Loopback Test: second filter - STD")
33+
dev.setfilter(0, CAN.FILTER_ADDRESS, [0x102, 0])
34+
send_and_check(dev, "Passing Message - Bank 1", 0x102, True)
35+
send_and_check(dev, "Passing Message - Bank 0", 0x101, True)
36+
send_and_check(dev, "Blocked Message", 0x100, False)
37+
38+
# Remove filter
39+
print("Loopback Test: clear filter - STD")
40+
dev.clearfilter()
41+
send_and_check(dev, "Passing Message - Bank 1", 0x102, True)
42+
send_and_check(dev, "Passing Message - Bank 0", 0x101, True)
43+
send_and_check(dev, "Passing any Message", 0x100, True)
44+
45+
# Extended message tests
46+
# Move to Extended
47+
dev = CAN(0,
48+
extframe=True,
49+
mode=CAN.SILENT_LOOPBACK,
50+
baudrate=CAN.BAUDRATE_500k,
51+
tx_io=18, rx_io=19, auto_restart=False)
52+
53+
# Test send/receive message
54+
print("Loopback Test: no filter - Extd")
55+
send_and_check(dev, "No filter", 0x100, True, extended=True)
56+
57+
# Set filter1
58+
print("Loopback Test: one filter - Extd")
59+
dev.setfilter(0, CAN.FILTER_ADDRESS, [0x101, 0], extframe=True)
60+
send_and_check(dev, "Passing Message", 0x101, True, extended=True)
61+
send_and_check(dev, "Blocked Message", 0x100, False, extended=True)
62+
63+
# Remove filter
64+
print("Loopback Test: clear filter - Extd")
65+
dev.clearfilter()
66+
send_and_check(dev, "Passing Message - Bank 0", 0x101, True, extended=True)
67+
send_and_check(dev, "Passing any Message", 0x100, True, extended=True)

0 commit comments

Comments
 (0)