Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added docs/esp32/img/twai_blockdiag.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 25 additions & 0 deletions docs/esp32/quickref.rst
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,31 @@ users is encouraged. Based on this feedback, the I2S class API and implementati

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

CAN bus
-------

See :ref:`esp32.CAN <esp32.CAN>`

The CAN driver is based on hardware implementation.
Any available output-capablepins can be used for TX, RX, BUS-OFF, and CLKOUT signal lines.

.. image:: img/twai_blockdiag.png

The driver is accessed via the :ref:`esp32.CAN <esp32.CAN>` class::

from esp32 import CAN
can = CAN(0, tx=5, rx=4, mode=CAN.NORMAL, baudrate=500000)
can.setfilter(0, CAN.FILTER_ADDRESS, params=[0x123], extframe=False) # set a filter to receive messages with id = 0x102
can.send([1,2,3], 0x102, extframe=False) # send a message with id 123
can.recv() # receive message

can.any() # returns True if there are any message to receive
can.info() # get information about the controller’s error states and TX and RX buffers
can.deinit() # turn off the can bus
can.clear_rx_queue() # clear messages in the FIFO
can.clear_tx_queue() # clear messages in the transmit buffer


Real time clock (RTC)
---------------------

Expand Down
262 changes: 262 additions & 0 deletions docs/library/esp32.rst
Original file line number Diff line number Diff line change
Expand Up @@ -351,3 +351,265 @@ supports 32-bit signed integers and blobs.
.. method:: NVS.commit()

Commits changes made by *set_xxx* methods to flash.

.. _esp32.CAN:

class CAN -- controller area network communication bus
======================================================

CAN implements the standard CAN communications protocol. At
the physical level it consists of 2 lines: RX and TX. Note that
to connect the microcontroller to a CAN bus you must use a CAN transceiver
to convert the CAN logic signals from the microcontroller to the correct
voltage levels on the bus.

Example usage (works without anything connected)::

from esp32 import CAN
BAUDRATE_500k = 500000
can = CAN(0, tx=5, rx=4, mode=CAN.NORMAL, baudrate=BAUDRATE_500k)
can.setfilter(0, CAN.FILTER_ADDRESS, params=[0x123], extframe=False) # set a filter to receive messages with id = 0x102
can.send([1,2,3], 0x102, extframe=False) # send a message with id 123
if can.any():
can.recv() # receive message


Constructors
------------

.. class:: esp32.CAN(bus, ...)

Construct a CAN object on the given bus(controller). *bus* must be 0 for ESP32.
With no additional parameters, the CAN object is created but not
initialised (it has the settings from the last initialisation of
the bus, if any). If extra arguments are given, the bus is initialised.
See :meth:`CAN.init` for parameters of initialisation.

The physical pins of the CAN bus can be assigned during init.

Methods
-------

.. method:: CAN.init(mode, *, tx=5, rx=4, baudrate=500000, prescaler=8, sjw=3, bs1=15, bs2=4, auto_restart=False, tx_queue=1, rx_queue=1)

Initialise the CAN bus with the given parameters:

- *mode* is one of: NORMAL, LOOPBACK, SILENT, SILENT_LOOPBACK
- *tx* defines the gpio used for transmission
- *rx* defines the gpio used for receiving
- *baudrate* is used to define a standard speed. If it is defined, the *prescaler*, *sjw*, *bs1*, *bs2*
will be ignored. Standard speeds are 25000, 50000, 100000, 125000, 250000, 500000, 1000000. Some versions
of esp32 supports non-standard speeds: 1000, 5000, 10000, 12500, 16000, 20000.
- *prescaler* is used to set the duration of 1 time quanta; the time quanta
will be the input clock divided by the prescaler
- *sjw* is the resynchronisation jump width in units of the time quanta;
it can be 1, 2, 3, 4
- *bs1* defines the location of the sample point in units of the time quanta;
it can be between 1 and 1024 inclusive
- *bs2* defines the location of the transmit point in units of the time quanta;
it can be between 1 and 16 inclusive
- *bus_off* defines the gpio used for BUS-OFF signal line(optional)
- *clkout* defines the gpio used for CLKOUT signal line(optional)
- *tx_queue* defines the number of waiting tx messages can be stored
- *rx_queue* defines the number of received messages can be stored
- *auto_restart* sets whether the controller will automatically try and restart
communications after entering the bus-off state; if this is disabled then
:meth:`~CAN.restart()` can be used to leave the bus-off state.
This parameter is currently not implemented and it must be set to False


.. method:: CAN.deinit()

Turn off the CAN bus.

.. method:: CAN.restart()

Force a software restart of the CAN controller without resetting its
configuration.

If the controller enters the bus-off state then it will no longer participate
in bus activity. If the controller is not configured to automatically restart
(see :meth:`~CAN.init()`) then this method can be used to trigger a restart,
and the controller will follow the CAN protocol to leave the bus-off state and
go into the error active state.

.. method:: CAN.state()

Return the state of the controller. The return value can be one of:

- ``CAN.STOPPED`` -- the controller is completely off and reset;
- ``CAN.ERROR_ACTIVE`` -- the controller is on and in the Error Active state
(both TEC and REC are less than 96);
- ``CAN.BUS_OFF`` -- the controller is on but not participating in bus activity
(TEC overflowed beyond 255).
- ``CAN.RECOVERING`` -- the controller is under recover from bus-off state;


.. method:: CAN.info()

Get information about the controller's error states and TX and RX buffers.
If *list* is provided then it should be a list object with at least 8 entries,
which will be filled in with the information. Otherwise a new list will be
created and filled in. In both cases the return value of the method is the
populated list.

The values in the list are:

- TEC value
- REC value
- number of times the controller enterted the Error Warning state (wrapped around to 0 after 65535)
- number of times the controller enterted the Error Passive state (wrapped around to 0 after 65535)
- number of times the controller enterted the Bus Off state (wrapped around to 0 after 65535)
- number of pending TX messages
- number of pending RX messages


.. method:: CAN.setfilter(bank, mode, params, *, rtr=False, extframe=False)

Configure a filter bank:

- *bank* is the filter bank that is to be configured (esp32 supports only 0 bank)
- *mode* is the mode the filter should operate in.
- *params* is an array of values the defines the filter.
The contents of the array depends on the *mode* and *extframe* arguments.

+-----------------------+----------------------------------------------------------------------------+
| *mode* | contents of *params* array |
+=======================+============================================================================+
| CAN.FILTER_RAW_SINGLE | *params* will be copied in hardware variable |
| | and single_filter_mode will be selected |
| | In this mode, *bank* will be ignored |
+-----------------------+----------------------------------------------------------------------------+
| CAN.FILTER_RAW_DUAL | *params* will be copied in hardware variable |
| | and single_filter_mode will be cleared |
| | In this mode, *bank* will be ignored |
+-----------------------+----------------------------------------------------------------------------+
| CAN.FILTER_ADDRESS | *params* could be: |
| | |
| | If ``extframe=True`` and *params* length of 1 -- filter 29 bit identifier |
| | of message. |
| | |
| | if ``extframe=False``: |
| | |
| | * length of 1 filter 11 bit identifier of message |
| | * length of 2 filter 11 bit identifier and first byte of message |
| | * length of 3 filter 11 bit identifier first and second bytes of message |
+-----------------------+----------------------------------------------------------------------------+

- *rtr* For classic CAN controllers, this is an array of booleans that states if
a filter should accept a remote transmission request message. If this argument
is not given then it defaults to ``False`` for all entries.

.. method:: CAN.clearfilter(bank)

Clear and disables all filters

.. method:: CAN.any(fifo)

Return ``True`` if any message waiting on the FIFO, else ``False``.

.. method:: CAN.recv(list=None, *, timeout=5000)

Receive data on the bus:

- *list* is an optional list object to be used as the return value
- *timeout* is the timeout in milliseconds to wait for the receive.

Return value: A tuple containing four values.

- The id of the message.
- A boolean that indicates if the message is an RTR message.
- Reserved.
- An array containing the data.

If *list* is ``None`` then a new tuple will be allocated, as well as a new
bytes object to contain the data (as the fourth element in the tuple).

If *list* is not ``None`` then it should be a list object with at least four
elements. The fourth element should be a memoryview object which is created
from either a bytearray or an array of type 'B' or 'b', and this array must
have enough room for at least 8 bytes. The list object will then be
populated with the first three return values above, and the memoryview object
will be resized inplace to the size of the data and filled in with that data.
The same list and memoryview objects can be reused in subsequent calls to
this method, providing a way of receiving data without using the heap.
For example::

buf = bytearray(8)
lst = [0, 0, 0, memoryview(buf)]
# No heap memory is allocated in the following call
can.recv(lst, timeout=0)

*list* values are:

- identifier of can packet (int)
- extended packet (bool)
- rtr packet (bool)
- data frame (0..8 bytes)


.. method:: CAN.send(data, id, *, timeout=0, rtr=False, extframe=false)

Send a message on the bus:

- *data* is the data to send (an integer to send, or a buffer object).
- *id* is the id of the message to be sent.
- *timeout* is the timeout in milliseconds to wait for the send.
- *rtr* is a boolean that specifies if the message shall be sent as
a remote transmission request. If *rtr* is True then only the length
of *data* is used to fill in the DLC slot of the frame; the actual
bytes in *data* are unused.

If timeout is 0 the message is placed in a buffer and the method returns
immediately. If all three buffers are in use an exception is thrown.
If timeout is not 0, the method waits until the message is transmitted.
If the message can't be transmitted within the specified time an exception
is thrown.

Return value: ``None``.

.. method:: CAN.clear_tx_queue()

Clear all messages from transmitting queue.

.. method:: CAN.clear_rx_queue()

Clear all messages from receiving queue.


Constants
---------

.. data:: CAN.NORMAL
CAN.LOOPBACK
CAN.SILENT
CAN.SILENT_LOOPBACK


The mode of the CAN bus used in :meth:`~CAN.init()`.

+---------------------+---------------------------------------------+-------+-------+
| *mode* | \ | STM32 | ESP32 |
+=====================+=============================================+=======+=======+
| CAN.NORMAL | .. image:: img/can_mode_normal.png | + | + |
+---------------------+---------------------------------------------+-------+-------+
| CAN.LOOPBACK | .. image:: img/can_mode_loopback.png | + | + |
+---------------------+---------------------------------------------+-------+-------+
| CAN.SILENT | .. image:: img/can_mode_silent.png | + | + |
+---------------------+---------------------------------------------+-------+-------+
| CAN.SILENT_LOOPBACK | .. image:: img/can_mode_silent_loopback.png | + | + |
+---------------------+---------------------------------------------+-------+-------+


.. data:: CAN.STOPPED
CAN.ERROR_ACTIVE
CAN.BUS_OFF
CAN.RECOVERING

Possible states of the CAN controller returned from :meth:`~CAN.state()`.

.. data:: CAN.FILTER_RAW_SINGLE
CAN.FILTER_RAW_DUAL
CAN.FILTER_ADDRESS

The operation mode of a filter used in :meth:`~CAN.setfilter()`.
Binary file added docs/library/img/can_mode_loopback.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/library/img/can_mode_normal.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/library/img/can_mode_silent.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/library/img/can_mode_silent_loopback.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
60 changes: 60 additions & 0 deletions examples/esp32_can.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from esp32 import CAN
import time


def send_and_check(can_bus, name, id, expected_result=True, extended=False):
can_bus.clear_tx_queue()
can_bus.clear_rx_queue()
can_bus.send([], id, extframe=extended)
time.sleep_ms(100)
if can_bus.any() == expected_result:
print("{}: OK".format(name))
if expected_result:
can_bus.recv()
else:
print("{}: FAILED".format(name))


# 4 and 5 pins must be connected to each other, see documentation
dev = CAN(0, tx=5, rx=4, mode=CAN.SILENT_LOOPBACK, baudrate=50000)

# Test send/receive message
print("Loopback Test: no filter - STD")
send_and_check(dev, "No filter", 0x100, True)

# Set filter1
print("Loopback Test: one filter - STD")
dev.setfilter(0, CAN.FILTER_ADDRESS, [0x101, 0])
send_and_check(dev, "Passing Message", 0x101, True)
send_and_check(dev, "Blocked Message", 0x100, False)

# Set filter2
print("Loopback Test: second filter - STD")
dev.setfilter(0, CAN.FILTER_ADDRESS, [0x102, 0])
send_and_check(dev, "Passing Message - Bank 1", 0x102, True)
send_and_check(dev, "Passing Message - Bank 0", 0x101, True)
send_and_check(dev, "Blocked Message", 0x100, False)

# Remove filter
print("Loopback Test: clear filter - STD")
dev.clearfilter()
send_and_check(dev, "Passing Message - Bank 1", 0x102, True)
send_and_check(dev, "Passing Message - Bank 0", 0x101, True)
send_and_check(dev, "Passing any Message", 0x100, True)

# Extended message tests
# Test send/receive message
print("Loopback Test: no filter - Extd")
send_and_check(dev, "No filter", 0x100, True, extended=True)

# Set filter1
print("Loopback Test: one filter - Extd")
dev.setfilter(0, CAN.FILTER_ADDRESS, [0x101], extframe=True)
send_and_check(dev, "Passing Message", 0x101, True, extended=True)
send_and_check(dev, "Blocked Message", 0x100, False, extended=True)

# Remove filter
print("Loopback Test: clear filter - Extd")
dev.clearfilter()
send_and_check(dev, "Passing Message - Bank 0", 0x101, True, extended=True)
send_and_check(dev, "Passing any Message", 0x100, True, extended=True)
Loading