Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
Gerard Marull-Paretas d4a67e321b samples, tests: remove usage of space-separated lists 7 months ago
..
boards samples: modules: can openmode runs on stm32h573i_dk 2 years ago
objdict everywhere: fix typos 3 years ago
src dts: bindings: can: rename bus-speed/bus-speed-data properties 1 year ago
CMakeLists.txt samples: modules: canopennode: convert to sysbuild 3 years ago
Kconfig
README.rst boards: st: adopt new zephyr:board directive and role 9 months ago
prj.conf samples: canopennode: Select STREAM_FLASH for sysbuild build 2 years ago
prj_img_mgmt.conf samples: canopennode: Select STREAM_FLASH for sysbuild build 2 years ago
prj_no_storage.conf drivers: can: remove CAN_HAS_CANFD Kconfig helper 2 years ago
sample.yaml samples, tests: remove usage of space-separated lists 7 months ago

README.rst

.. zephyr:code-sample:: canopennode
:name: CANopenNode

Use the CANopenNode CANopen protocol stack in Zephyr.

Overview
********
This sample application shows how the `CANopenNode`_ CANopen protocol
stack can be used in Zephyr.

CANopen is an internationally standardized (`EN 50325-4`_, `CiA 301`_)
communication protocol and device specification for embedded
systems used in automation. CANopenNode is a 3rd party, open-source
CANopen protocol stack.

Apart from the CANopen protocol stack integration, this sample also
demonstrates the use of non-volatile storage for the CANopen object
dictionary and optionally program download over CANopen.

Requirements
************

* A board with CAN bus and flash support
* Host PC with CAN bus support

Building and Running
********************

First, ensure the optional CANopenNode module is enabled and available:

.. code-block:: console

west config manifest.project-filter +canopennode
west update canopennode

Building and Running for TWR-KE18F
==================================
The :zephyr:board:`twr_ke18f` board is equipped with an onboard CAN
transceiver. This board supports CANopen LED indicators (red and green
LEDs). The sample can be built and executed for the TWR-KE18F as
follows:

.. zephyr-app-commands::
:zephyr-app: samples/modules/canopennode
:board: twr_ke18f
:goals: build flash
:compact:

Pressing the button labelled ``SW3`` will increment the button press
counter object at index ``0x2102`` in the object dictionary.

Building and Running for FRDM-K64F
==================================
The :zephyr:board:`frdm_k64f` board does not come with an onboard CAN
transceiver. In order to use the CAN bus on the FRDM-K64F board, an
external CAN bus transceiver must be connected to ``PTB18``
(``CAN0_TX``) and ``PTB19`` (``CAN0_RX``). This board supports CANopen
LED indicators (red and green LEDs)

The sample can be built and executed for the FRDM-K64F as follows:

.. zephyr-app-commands::
:zephyr-app: samples/modules/canopennode
:board: frdm_k64f
:goals: build flash
:compact:

Pressing the button labelled ``SW3`` will increment the button press
counter object at index ``0x2102`` in the object dictionary.

Building and Running for STM32F072RB Discovery
==============================================
The :zephyr:board:`stm32f072b_disco` board does not come with an onboard CAN
transceiver. In order to use the CAN bus on the STM32F072RB Discovery board, an
external CAN bus transceiver must be connected to ``PB8`` (``CAN_RX``) and
``PB9`` (``CAN_TX``). This board supports CANopen LED indicators (red and green
LEDs)

The sample can be built and executed for the STM32F072RB Discovery as follows:

.. zephyr-app-commands::
:zephyr-app: samples/modules/canopennode
:board: stm32f072b_disco
:goals: build flash
:compact:

Pressing the button labelled ``USER`` will increment the button press counter
object at index ``0x2102`` in the object dictionary.

Building and Running for STM32F3 Discovery
==========================================
The :zephyr:board:`stm32f3_disco` board does not come with an onboard CAN
transceiver. In order to use the CAN bus on the STM32F3 Discovery board, an
external CAN bus transceiver must be connected to ``PD1`` (``CAN_TX``) and
``PD0`` (``CAN_RX``). This board supports CANopen LED indicators (red and green
LEDs)

The sample can be built and executed for the STM32F3 Discovery as follows:

.. zephyr-app-commands::
:zephyr-app: samples/modules/canopennode
:board: stm32f3_disco
:goals: build flash
:compact:

Pressing the button labelled ``USER`` will increment the button press counter
object at index ``0x2102`` in the object dictionary.

Building and Running for other STM32 boards
===========================================
The sample cannot run if the <erase-block-size> of the flash-controller exceeds 0x10000.
Typically nucleo_h743zi with erase-block-size = <DT_SIZE_K(128)>;


Building and Running for boards without storage partition
=========================================================
The sample can be built for boards without a flash storage partition by using a different configuration file:

.. zephyr-app-commands::
:zephyr-app: samples/modules/canopennode
:board: <your_board_name>
:conf: "prj_no_storage.conf"
:goals: build flash
:compact:

Testing CANopen Communication
*****************************
CANopen communication between the host PC and Zephyr can be
established using any CANopen compliant application on the host PC.
The examples here uses `CANopen for Python`_ for communicating between
the host PC and Zephyr. First, install python-canopen along with the
python-can backend as follows:

.. code-block:: console

pip3 install --user canopen python-can

Next, configure python-can to use your CAN adapter through its
configuration file. On GNU/Linux, the configuration looks similar to
this:

.. code-block:: console

cat << EOF > ~/.canrc
[default]
interface = socketcan
channel = can0
bitrate = 125000
EOF

Please refer to the `python-can`_ documentation for further details
and instructions.

Finally, bring up the CAN interface on the test PC. On GNU/Linux, this
can be done as follows:

.. code-block:: console

sudo ip link set can0 type can bitrate 125000 restart-ms 100
sudo ip link set up can0

To better understand the communication taking place in the following
examples, you can monitor the CAN traffic from the host PC. On
GNU/Linux, this can be accomplished using ``candump`` from the
`can-utils`_ package as follows:

.. code-block:: console

candump can0

NMT State Changes
=================
Changing the Network Management (NMT) state of the node can be
accomplished using the following Python code:

.. code-block:: py

import canopen
import os
import time

ZEPHYR_BASE = os.environ['ZEPHYR_BASE']
EDS = os.path.join(ZEPHYR_BASE, 'samples', 'modules', 'canopennode',
'objdict', 'objdict.eds')

NODEID = 10

network = canopen.Network()

network.connect()

node = network.add_node(NODEID, EDS)

# Green indicator LED will flash slowly
node.nmt.state = 'STOPPED'
time.sleep(5)

# Green indicator LED will flash faster
node.nmt.state = 'PRE-OPERATIONAL'
time.sleep(5)

# Green indicator LED will be steady on
node.nmt.state = 'OPERATIONAL'
time.sleep(5)

# Node will reset communication
node.nmt.state = 'RESET COMMUNICATION'
node.nmt.wait_for_heartbeat()

# Node will reset
node.nmt.state = 'RESET'
node.nmt.wait_for_heartbeat()

network.disconnect()

Running the above Python code will update the NMT state of the node
which is reflected on the indicator LEDs (if present).

SDO Upload
==========
Reading a Service Data Object (SDO) at a given index of the CANopen
object dictionary (here index ``0x1008``, the manufacturer device
name) can be accomplished using the following Python code:

.. code-block:: py

import canopen
import os

ZEPHYR_BASE = os.environ['ZEPHYR_BASE']
EDS = os.path.join(ZEPHYR_BASE, 'samples', 'modules', 'canopennode',
'objdict', 'objdict.eds')

NODEID = 10

network = canopen.Network()

network.connect()

node = network.add_node(NODEID, EDS)
name = node.sdo['Manufacturer device name']

print("Device name: '{}'".format(name.raw))

network.disconnect()

Running the above Python code should produce the following output:

.. code-block:: console

Device name: 'Zephyr RTOS/CANopenNode'

SDO Download
============
Writing to a Service Data Object (SDO) at a given index of the CANopen
object dictionary (here index ``0x1017``, the producer heartbeat time)
can be accomplished using the following Python code:

.. code-block:: py

import canopen
import os

ZEPHYR_BASE = os.environ['ZEPHYR_BASE']
EDS = os.path.join(ZEPHYR_BASE, 'samples', 'modules', 'canopennode',
'objdict', 'objdict.eds')

NODEID = 10

network = canopen.Network()

network.connect()

node = network.add_node(NODEID, EDS)
heartbeat = node.sdo['Producer heartbeat time']
reboots = node.sdo['Power-on counter']

# Set heartbeat interval without saving to non-volatile storage
print("Initial heartbeat time: {} ms".format(heartbeat.raw))
print("Power-on counter: {}".format(reboots.raw))
heartbeat.raw = 5000
print("Updated heartbeat time: {} ms".format(heartbeat.raw))

# Reset and read heartbeat interval again
node.nmt.state = 'RESET'
node.nmt.wait_for_heartbeat()
print("heartbeat time after reset: {} ms".format(heartbeat.raw))
print("Power-on counter: {}".format(reboots.raw))

# Set interval and store it to non-volatile storage
heartbeat.raw = 2000
print("Updated heartbeat time: {} ms".format(heartbeat.raw))
node.store()

# Reset and read heartbeat interval again
node.nmt.state = 'RESET'
node.nmt.wait_for_heartbeat()
print("heartbeat time after store and reset: {} ms".format(heartbeat.raw))
print("Power-on counter: {}".format(reboots.raw))

# Restore default values, reset and read again
node.restore()
node.nmt.state = 'RESET'
node.nmt.wait_for_heartbeat()
print("heartbeat time after restore and reset: {} ms".format(heartbeat.raw))
print("Power-on counter: {}".format(reboots.raw))

network.disconnect()

Running the above Python code should produce the following output:

.. code-block:: console

Initial heartbeat time: 1000 ms
Power-on counter: 1
Updated heartbeat time: 5000 ms
heartbeat time after reset: 1000 ms
Power-on counter: 2
Updated heartbeat time: 2000 ms
heartbeat time after store and reset: 2000 ms
Power-on counter: 3
heartbeat time after restore and reset: 1000 ms
Power-on counter: 4

Note that the power-on counter value may be different.

PDO Mapping
===========
Transmit Process Data Object (PDO) mapping for data at a given index
of the CANopen object dictionary (here index ``0x2102``, the button
press counter) can be accomplished using the following Python code:

.. code-block:: py

import canopen
import os

ZEPHYR_BASE = os.environ['ZEPHYR_BASE']
EDS = os.path.join(ZEPHYR_BASE, 'samples', 'modules', 'canopennode',
'objdict', 'objdict.eds')

NODEID = 10

network = canopen.Network()

network.connect()

node = network.add_node(NODEID, EDS)
button = node.sdo['Button press counter']

# Read current TPDO mapping
node.tpdo.read()

# Enter pre-operational state to map TPDO
node.nmt.state = 'PRE-OPERATIONAL'

# Map TPDO 1 to transmit the button press counter on changes
node.tpdo[1].clear()
node.tpdo[1].add_variable('Button press counter')
node.tpdo[1].trans_type = 254
node.tpdo[1].enabled = True

# Save TPDO mapping
node.tpdo.save()
node.nmt.state = 'OPERATIONAL'

# Reset button press counter
button.raw = 0

print("Press the button 10 times")
while True:
node.tpdo[1].wait_for_reception()
print("Button press counter: {}".format(node.tpdo['Button press counter'].phys))
if node.tpdo['Button press counter'].phys >= 10:
break

network.disconnect()

Running the above Python code should produce the following output:

.. code-block:: console

Press the button 10 times
Button press counter: 0
Button press counter: 1
Button press counter: 2
Button press counter: 3
Button press counter: 4
Button press counter: 5
Button press counter: 6
Button press counter: 7
Button press counter: 8
Button press counter: 9
Button press counter: 10

Testing CANopen Program Download
********************************

Building and Running for FRDM-K64F
==================================
The sample can be rebuilt with MCUboot and program download support
for the FRDM-K64F as follows:

#. Build the CANopenNode sample with MCUboot support:

.. zephyr-app-commands::
:tool: west
:zephyr-app: samples/modules/canopennode
:board: frdm_k64f
:goals: build
:west-args: --sysbuild
:gen-args: -Dcanopennode_CONF_FILE=prj_img_mgmt.conf -DSB_CONFIG_BOOTLOADER_MCUBOOT=y
:compact:

#. Flash the newly built MCUboot and CANopen sample binaries using west:

.. code-block:: console

west flash --skip-rebuild

#. Confirm the newly flashed firmware image using west:

.. code-block:: console

west flash --skip-rebuild --domain canopennode --runner canopen --confirm-only

#. Finally, perform a program download via CANopen:

.. code-block:: console

west flash --skip-rebuild --domain canopennode --runner canopen

Modifying the Object Dictionary
*******************************
The CANopen object dictionary used in this sample application can be
found under :zephyr_file:`samples/modules/canopennode/objdict` in
the Zephyr tree. The object dictionary can be modified using any
object dictionary editor supporting CANopenNode object dictionary code
generation.

A popular choice is the EDS editor from the `libedssharp`_
project. With that, the
:zephyr_file:`samples/modules/canopennode/objdict/objdict.xml`
project file can be opened and modified, and new implementation files
(:zephyr_file:`samples/modules/canopennode/objdict/CO_OD.h` and
:zephyr_file:`samples/modules/canopennode/objdict/CO_OD.c`) can be
generated. The EDS editor can also export an updated Electronic Data
Sheet (EDS) file
(:zephyr_file:`samples/modules/canopennode/objdict/objdict.eds`).

.. _CANopenNode:
https://github.com/CANopenNode/CANopenNode

.. _EN 50325-4:
https://can-cia.org/cia-groups/international-standardization/

.. _CiA 301:
https://can-cia.org/cia-groups/technical-documents/

.. _CANopen for Python:
https://github.com/christiansandberg/canopen

.. _python-can:
https://python-can.readthedocs.io/

.. _can-utils:
https://github.com/linux-can/can-utils

.. _libedssharp:
https://github.com/robincornelius/libedssharp