• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..--

boards/03-Aug-2024-6856

objdict/03-Aug-2024-3,9333,317

src/03-Aug-2024-308201

CMakeLists.txtD03-Aug-2024348 149

KconfigD03-Aug-2024217 1512

README.rstD03-Aug-202413.5 KiB471342

prj.confD03-Aug-2024319 2215

prj_img_mgmt.confD03-Aug-2024360 2417

prj_no_storage.confD03-Aug-2024192 149

sample.yamlD03-Aug-2024705 3331

README.rst

1.. _canopennode-sample:
2
3CANopenNode
4###########
5
6Overview
7********
8This sample application shows how the `CANopenNode`_ CANopen protocol
9stack can be used in Zephyr.
10
11CANopen is an internationally standardized (`EN 50325-4`_, `CiA 301`_)
12communication protocol and device specification for embedded
13systems used in automation. CANopenNode is a 3rd party, open-source
14CANopen protocol stack.
15
16Apart from the CANopen protocol stack integration, this sample also
17demonstrates the use of non-volatile storage for the CANopen object
18dictionary and optionally program download over CANopen.
19
20Requirements
21************
22
23* A board with CAN bus and flash support
24* Host PC with CAN bus support
25
26Building and Running
27********************
28
29First, ensure the optional CANopenNode module is enabled and available:
30
31   .. code-block:: console
32
33      west config manifest.project-filter +canopennode
34      west update canopennode
35
36Building and Running for TWR-KE18F
37==================================
38The :ref:`twr_ke18f` board is equipped with an onboard CAN
39transceiver. This board supports CANopen LED indicators (red and green
40LEDs). The sample can be built and executed for the TWR-KE18F as
41follows:
42
43.. zephyr-app-commands::
44   :zephyr-app: samples/modules/canopennode
45   :board: twr_ke18f
46   :goals: build flash
47   :compact:
48
49Pressing the button labelled ``SW3`` will increment the button press
50counter object at index ``0x2102`` in the object dictionary.
51
52Building and Running for FRDM-K64F
53==================================
54The :ref:`frdm_k64f` board does not come with an onboard CAN
55transceiver. In order to use the CAN bus on the FRDM-K64F board, an
56external CAN bus transceiver must be connected to ``PTB18``
57(``CAN0_TX``) and ``PTB19`` (``CAN0_RX``). This board supports CANopen
58LED indicators (red and green LEDs)
59
60The sample can be built and executed for the FRDM-K64F as follows:
61
62.. zephyr-app-commands::
63   :zephyr-app: samples/modules/canopennode
64   :board: frdm_k64f
65   :goals: build flash
66   :compact:
67
68Pressing the button labelled ``SW3`` will increment the button press
69counter object at index ``0x2102`` in the object dictionary.
70
71Building and Running for STM32F072RB Discovery
72==============================================
73The :ref:`stm32f072b_disco_board` board does not come with an onboard CAN
74transceiver. In order to use the CAN bus on the STM32F072RB Discovery board, an
75external CAN bus transceiver must be connected to ``PB8`` (``CAN_RX``) and
76``PB9`` (``CAN_TX``). This board supports CANopen LED indicators (red and green
77LEDs)
78
79The sample can be built and executed for the STM32F072RB Discovery as follows:
80
81.. zephyr-app-commands::
82   :zephyr-app: samples/modules/canopennode
83   :board: stm32f072b_disco
84   :goals: build flash
85   :compact:
86
87Pressing the button labelled ``USER`` will increment the button press counter
88object at index ``0x2102`` in the object dictionary.
89
90Building and Running for STM32F3 Discovery
91==========================================
92The :ref:`stm32f3_disco_board` board does not come with an onboard CAN
93transceiver. In order to use the CAN bus on the STM32F3 Discovery board, an
94external CAN bus transceiver must be connected to ``PD1`` (``CAN_TX``) and
95``PD0`` (``CAN_RX``). This board supports CANopen LED indicators (red and green
96LEDs)
97
98The sample can be built and executed for the STM32F3 Discovery as follows:
99
100.. zephyr-app-commands::
101   :zephyr-app: samples/modules/canopennode
102   :board: stm32f3_disco
103   :goals: build flash
104   :compact:
105
106Pressing the button labelled ``USER`` will increment the button press counter
107object at index ``0x2102`` in the object dictionary.
108
109Building and Running for other STM32 boards
110===========================================
111The sample cannot run if the <erase-block-size> of the flash-controller exceeds 0x10000.
112Typically nucleo_h743zi with erase-block-size = <DT_SIZE_K(128)>;
113
114
115Building and Running for boards without storage partition
116=========================================================
117The sample can be built for boards without a flash storage partition by using a different configuration file:
118
119.. zephyr-app-commands::
120   :zephyr-app: samples/modules/canopennode
121   :board: <your_board_name>
122   :conf: "prj_no_storage.conf"
123   :goals: build flash
124   :compact:
125
126Testing CANopen Communication
127*****************************
128CANopen communication between the host PC and Zephyr can be
129established using any CANopen compliant application on the host PC.
130The examples here uses `CANopen for Python`_ for communicating between
131the host PC and Zephyr.  First, install python-canopen along with the
132python-can backend as follows:
133
134.. code-block:: console
135
136   pip3 install --user canopen python-can
137
138Next, configure python-can to use your CAN adapter through its
139configuration file. On GNU/Linux, the configuration looks similar to
140this:
141
142.. code-block:: console
143
144   cat << EOF > ~/.canrc
145   [default]
146   interface = socketcan
147   channel = can0
148   bitrate = 125000
149   EOF
150
151Please refer to the `python-can`_ documentation for further details
152and instructions.
153
154Finally, bring up the CAN interface on the test PC. On GNU/Linux, this
155can be done as follows:
156
157.. code-block:: console
158
159   sudo ip link set can0 type can bitrate 125000 restart-ms 100
160   sudo ip link set up can0
161
162To better understand the communication taking place in the following
163examples, you can monitor the CAN traffic from the host PC. On
164GNU/Linux, this can be accomplished using ``candump`` from the
165`can-utils`_ package as follows:
166
167.. code-block:: console
168
169   candump can0
170
171NMT State Changes
172=================
173Changing the Network Management (NMT) state of the node can be
174accomplished using the following Python code:
175
176.. code-block:: py
177
178   import canopen
179   import os
180   import time
181
182   ZEPHYR_BASE = os.environ['ZEPHYR_BASE']
183   EDS = os.path.join(ZEPHYR_BASE, 'samples', 'modules', 'canopennode',
184                   'objdict', 'objdict.eds')
185
186   NODEID = 10
187
188   network = canopen.Network()
189
190   network.connect()
191
192   node = network.add_node(NODEID, EDS)
193
194   # Green indicator LED will flash slowly
195   node.nmt.state = 'STOPPED'
196   time.sleep(5)
197
198   # Green indicator LED will flash faster
199   node.nmt.state = 'PRE-OPERATIONAL'
200   time.sleep(5)
201
202   # Green indicator LED will be steady on
203   node.nmt.state = 'OPERATIONAL'
204   time.sleep(5)
205
206   # Node will reset communication
207   node.nmt.state = 'RESET COMMUNICATION'
208   node.nmt.wait_for_heartbeat()
209
210   # Node will reset
211   node.nmt.state = 'RESET'
212   node.nmt.wait_for_heartbeat()
213
214   network.disconnect()
215
216Running the above Python code will update the NMT state of the node
217which is reflected on the indicator LEDs (if present).
218
219SDO Upload
220==========
221Reading a Service Data Object (SDO) at a given index of the CANopen
222object dictionary (here index ``0x1008``, the manufacturer device
223name) can be accomplished using the following Python code:
224
225.. code-block:: py
226
227   import canopen
228   import os
229
230   ZEPHYR_BASE = os.environ['ZEPHYR_BASE']
231   EDS = os.path.join(ZEPHYR_BASE, 'samples', 'modules', 'canopennode',
232                   'objdict', 'objdict.eds')
233
234   NODEID = 10
235
236   network = canopen.Network()
237
238   network.connect()
239
240   node = network.add_node(NODEID, EDS)
241   name = node.sdo['Manufacturer device name']
242
243   print("Device name: '{}'".format(name.raw))
244
245   network.disconnect()
246
247Running the above Python code should produce the following output:
248
249.. code-block:: console
250
251   Device name: 'Zephyr RTOS/CANopenNode'
252
253SDO Download
254============
255Writing to a Service Data Object (SDO) at a given index of the CANopen
256object dictionary (here index ``0x1017``, the producer heartbeat time)
257can be accomplished using the following Python code:
258
259.. code-block:: py
260
261   import canopen
262   import os
263
264   ZEPHYR_BASE = os.environ['ZEPHYR_BASE']
265   EDS = os.path.join(ZEPHYR_BASE, 'samples', 'modules', 'canopennode',
266                   'objdict', 'objdict.eds')
267
268   NODEID = 10
269
270   network = canopen.Network()
271
272   network.connect()
273
274   node = network.add_node(NODEID, EDS)
275   heartbeat = node.sdo['Producer heartbeat time']
276   reboots = node.sdo['Power-on counter']
277
278   # Set heartbeat interval without saving to non-volatile storage
279   print("Initial heartbeat time: {} ms".format(heartbeat.raw))
280   print("Power-on counter: {}".format(reboots.raw))
281   heartbeat.raw = 5000
282   print("Updated heartbeat time: {} ms".format(heartbeat.raw))
283
284   # Reset and read heartbeat interval again
285   node.nmt.state = 'RESET'
286   node.nmt.wait_for_heartbeat()
287   print("heartbeat time after reset: {} ms".format(heartbeat.raw))
288   print("Power-on counter: {}".format(reboots.raw))
289
290   # Set interval and store it to non-volatile storage
291   heartbeat.raw = 2000
292   print("Updated heartbeat time: {} ms".format(heartbeat.raw))
293   node.store()
294
295   # Reset and read heartbeat interval again
296   node.nmt.state = 'RESET'
297   node.nmt.wait_for_heartbeat()
298   print("heartbeat time after store and reset: {} ms".format(heartbeat.raw))
299   print("Power-on counter: {}".format(reboots.raw))
300
301   # Restore default values, reset and read again
302   node.restore()
303   node.nmt.state = 'RESET'
304   node.nmt.wait_for_heartbeat()
305   print("heartbeat time after restore and reset: {} ms".format(heartbeat.raw))
306   print("Power-on counter: {}".format(reboots.raw))
307
308   network.disconnect()
309
310Running the above Python code should produce the following output:
311
312.. code-block:: console
313
314   Initial heartbeat time: 1000 ms
315   Power-on counter: 1
316   Updated heartbeat time: 5000 ms
317   heartbeat time after reset: 1000 ms
318   Power-on counter: 2
319   Updated heartbeat time: 2000 ms
320   heartbeat time after store and reset: 2000 ms
321   Power-on counter: 3
322   heartbeat time after restore and reset: 1000 ms
323   Power-on counter: 4
324
325Note that the power-on counter value may be different.
326
327PDO Mapping
328===========
329Transmit Process Data Object (PDO) mapping for data at a given index
330of the CANopen object dictionary (here index ``0x2102``, the button
331press counter) can be accomplished using the following Python code:
332
333.. code-block:: py
334
335   import canopen
336   import os
337
338   ZEPHYR_BASE = os.environ['ZEPHYR_BASE']
339   EDS = os.path.join(ZEPHYR_BASE, 'samples', 'modules', 'canopennode',
340                   'objdict', 'objdict.eds')
341
342   NODEID = 10
343
344   network = canopen.Network()
345
346   network.connect()
347
348   node = network.add_node(NODEID, EDS)
349   button = node.sdo['Button press counter']
350
351   # Read current TPDO mapping
352   node.tpdo.read()
353
354   # Enter pre-operational state to map TPDO
355   node.nmt.state = 'PRE-OPERATIONAL'
356
357   # Map TPDO 1 to transmit the button press counter on changes
358   node.tpdo[1].clear()
359   node.tpdo[1].add_variable('Button press counter')
360   node.tpdo[1].trans_type = 254
361   node.tpdo[1].enabled = True
362
363   # Save TPDO mapping
364   node.tpdo.save()
365   node.nmt.state = 'OPERATIONAL'
366
367   # Reset button press counter
368   button.raw = 0
369
370   print("Press the button 10 times")
371   while True:
372       node.tpdo[1].wait_for_reception()
373       print("Button press counter: {}".format(node.tpdo['Button press counter'].phys))
374       if node.tpdo['Button press counter'].phys >= 10:
375           break
376
377   network.disconnect()
378
379Running the above Python code should produce the following output:
380
381.. code-block:: console
382
383   Press the button 10 times
384   Button press counter: 0
385   Button press counter: 1
386   Button press counter: 2
387   Button press counter: 3
388   Button press counter: 4
389   Button press counter: 5
390   Button press counter: 6
391   Button press counter: 7
392   Button press counter: 8
393   Button press counter: 9
394   Button press counter: 10
395
396Testing CANopen Program Download
397********************************
398
399Building and Running for FRDM-K64F
400==================================
401The sample can be rebuilt with MCUboot and program download support
402for the FRDM-K64F as follows:
403
404#. Build the CANopenNode sample with MCUboot support:
405
406   .. zephyr-app-commands::
407      :tool: west
408      :app: samples/modules/canopennode
409      :board: frdm_k64f
410      :goals: build
411      :west-args: --sysbuild
412      :gen-args: -Dcanopennode_CONF_FILE=prj_img_mgmt.conf -DSB_CONFIG_BOOTLOADER_MCUBOOT=y
413      :compact:
414
415#. Flash the newly built MCUboot and CANopen sample binaries using west:
416
417   .. code-block:: console
418
419      west flash --skip-rebuild
420
421#. Confirm the newly flashed firmware image using west:
422
423   .. code-block:: console
424
425      west flash --skip-rebuild --domain canopennode --runner canopen --confirm-only
426
427#. Finally, perform a program download via CANopen:
428
429   .. code-block:: console
430
431      west flash --skip-rebuild --domain canopennode --runner canopen
432
433Modifying the Object Dictionary
434*******************************
435The CANopen object dictionary used in this sample application can be
436found under :zephyr_file:`samples/modules/canopennode/objdict` in
437the Zephyr tree. The object dictionary can be modified using any
438object dictionary editor supporting CANopenNode object dictionary code
439generation.
440
441A popular choice is the EDS editor from the `libedssharp`_
442project. With that, the
443:zephyr_file:`samples/modules/canopennode/objdict/objdict.xml`
444project file can be opened and modified, and new implementation files
445(:zephyr_file:`samples/modules/canopennode/objdict/CO_OD.h` and
446:zephyr_file:`samples/modules/canopennode/objdict/CO_OD.c`) can be
447generated. The EDS editor can also export an updated Electronic Data
448Sheet (EDS) file
449(:zephyr_file:`samples/modules/canopennode/objdict/objdict.eds`).
450
451.. _CANopenNode:
452   https://github.com/CANopenNode/CANopenNode
453
454.. _EN 50325-4:
455   https://can-cia.org/cia-groups/international-standardization/
456
457.. _CiA 301:
458   https://can-cia.org/cia-groups/technical-documents/
459
460.. _CANopen for Python:
461   https://github.com/christiansandberg/canopen
462
463.. _python-can:
464   https://python-can.readthedocs.io/
465
466.. _can-utils:
467   https://github.com/linux-can/can-utils
468
469.. _libedssharp:
470   https://github.com/robincornelius/libedssharp
471