dev(none): Снимок библиотеки p-net

This commit is contained in:
svad05 2022-07-08 16:04:01 +03:00
parent 7b457c065d
commit 890193582a
293 changed files with 101139 additions and 0 deletions

View File

@ -0,0 +1,41 @@
AlignAfterOpenBracket: AlwaysBreak
AlignConsecutiveAssignments: false
AlignConsecutiveMacros: true
AllowAllArgumentsOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: Never
BinPackArguments: false
BinPackParameters: false
BreakBeforeBraces: Custom
BraceWrapping:
AfterCaseLabel: true
AfterClass: true
AfterControlStatement: true
AfterEnum: true
AfterFunction: true
AfterNamespace: true
AfterStruct: true
AfterUnion: true
BeforeCatch: true
BeforeElse: true
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: false
AfterExternBlock: false
ColumnLimit: 80
ContinuationIndentWidth: 3
IndentCaseLabels: false
IndentWidth: 3
PenaltyBreakAssignment: 10
PenaltyBreakBeforeFirstCallParameter: 30
PenaltyBreakComment: 10
PenaltyBreakString: 100
PenaltyExcessCharacter: 100
PenaltyReturnTypeOnItsOwnLine: 100000
PointerAlignment: Middle
SortIncludes: false
SpaceBeforeParens: NonEmptyParentheses
UseTab: Never

View File

@ -0,0 +1,12 @@
# This file contains a list of commits that are not likely what you
# are looking for in a blame, such as mass reformatting or renaming.
# You can set this file as a default ignore file for blame by running
# the following command.
#
# $ git config blame.ignoreRevsFile .git-blame-ignore-revs
# Run clang-format
723c3d2ea620328554e3f4ca6718d7359a807bdf
# Clang format of earlier modifications
71e73f071cb6c42a3c8ec8a209e7e979a43cce5a

24
profinet_stack/p-net/.gitattributes vendored Normal file
View File

@ -0,0 +1,24 @@
# Auto detect text files and perform LF normalization
* text=auto
# Do not perform LF normalization on bash scripts
*.service text eol=lf
*.raspberrypi text eol=lf
*.sh text eol=lf
src/ports/linux/set_network_parameters text eol=lf
src/ports/linux/set_profinet_leds text eol=lf
# Custom for Visual Studio
*.cs diff=csharp
# Standard to msysgit
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain

View File

@ -0,0 +1,35 @@
name: Build
on: [push, pull_request]
env:
BUILD_TYPE: Release
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Apt update
run: sudo apt-get update
- name: Install dependencies
run: sudo apt-get install doxygen libsnmp-dev
- name: Configure
shell: bash
run: |
cmake -E make_directory ${{github.workspace}}/build
cmake -B ${{github.workspace}}/build -S ${{github.workspace}} \
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
-DPNET_OPTION_SNMP=ON
- name: Build
shell: bash
run: |
cmake --build ${{github.workspace}}/build --target all docs install -j4
- name: Test
shell: bash
run: |
cmake --build ${{github.workspace}}/build --target check

View File

@ -0,0 +1,69 @@
name: Clang
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '23 13 * * 3'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-20.04
steps:
- name: Install dependencies
run: sudo apt-get install clang-tools libsnmp-dev
- name: Checkout repository
uses: actions/checkout@v2
with:
submodules: true
# Run clang analyzer.
#
# Detect if issues were found and upload SARIF report only in that
# case, because reports without entries are not accepted. See
# https://github.com/github/codeql-action/issues/390. The analyzer
# is run twice in order to get a SARIF report as well as an exit
# status when issues are detected, as the scan-build --status-bugs
# parameter doesn't work when the output format is SARIF.
- name: Run analyzer
id: analyze
continue-on-error: true
run: |
cmake -E make_directory ${{github.workspace}}/build.clang
scan-build cmake \
-B ${{github.workspace}}/build.clang \
-S ${{github.workspace}} \
-DPNET_OPTION_SNMP=ON \
-DBUILD_TESTING=OFF \
-DCMAKE_BUILD_TYPE=Release
scan-build \
-sarif \
-o ${{github.workspace}}/sarif \
make \
-C ${{github.workspace}}/build.clang \
-j4
cmake --build ${{github.workspace}}/build.clang --target clean
scan-build \
--status-bugs \
make \
-C ${{github.workspace}}/build.clang \
-j4
- name: Merge results if any
if: steps.analyze.outcome == 'failure'
uses: microsoft/sarif-actions@v0.1
with:
command: 'merge sarif/*/*.sarif'
- name: Upload results if any
if: steps.analyze.outcome == 'failure'
uses: github/codeql-action/upload-sarif@v1
with:
sarif_file: ${{github.workspace}}/merged.sarif

View File

@ -0,0 +1,46 @@
name: "CodeQL"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '23 13 * * 3'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: [ 'cpp' ]
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
submodules: true
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
- name: Build
run: |
cmake -E make_directory ${{github.workspace}}/build.lgtm
cmake -B ${{github.workspace}}/build.lgtm -S ${{github.workspace}} \
-DCMAKE_VERBOSE_MAKEFILE=ON \
-DCMAKE_BUILD_TYPE=Release
cmake --build ${{github.workspace}}/build.lgtm -j4
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

53
profinet_stack/p-net/.gitignore vendored Normal file
View File

@ -0,0 +1,53 @@
build*/
*~
*#
CMakeFiles/
CMakeCache.txt
install
doc/_build
doc/_copied
.cproject
.project
# Object files
*.o
*.ko
*.obj
*.elf
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Editor files
.vscode/
# Sampleapp dummy LED files
pnet_led*.txt
# Sampleapp dummy button files (typically named by user)
*button*.txt
# Persistent data storage
pnet_data*.bin

3
profinet_stack/p-net/.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "cmake/tools"]
path = cmake/tools
url = https://github.com/rtlabs-com/cmake-tools

View File

@ -0,0 +1,125 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
## [Unreleased]
## 2021-03-31
### Changed
- Update GSDML file to conformance class B
## 2021-03-24
### Added
- Possibility to configure number of ports, without recompiling.
## 2021-03-03
### Changed
- Set thread priority and stack size via configuration
## 2021-02-19
### Changed
- Split options.h into two files.
- Simplify configuration
## 2020-02-03
### Added
- Improved support for multiple ports
## 2021-01-29
### Changed
- Move remaining Linux sample app files to src/ports/linux
## 2020-01-11
### Added
- Support for multiple ports
## 2021-01-05
### Added
- Additional API functions for low-level diagnostic handling
## 2020-12-22
### Changed
- Remove link status from configuration
## 2020-12-15
### Changed
- Use Profinet version 2.4 for the GSDML file
- Changed default installation path for sample_app on Raspberry Pi
## 2020-12-11
### Added
- Runtime data for multiple ports
- Functionality to search for scripts in multiple locations for Linux
## 2020-12-07
### Changed
- Rename pnet_set_state() to pnet_set_primary_state()
## 2020-12-01
### Added
- Changed informative callbacks to be void-functions.
## 2020-11-30
### Added
- SNMP functionality for Linux
## 2020-11-20
### Added
- Additional log level FATAL
## 2020-11-19
### Added
- SNMP functionality for rt-kernel
## 2020-11-16
### Added
- Support for multiple ports in configuration
## 2020-11-10
### Changed
- Remove LLDP TTL from configuration
- Remove LLDP chassis ID from configuration
## 2020-11-03
### Changed
- Improve API for adding diagnosis
- Use rtlabs-com/osal and rtlabs-com/cmake-tools as submodules
## 2020-10-28
### Changed
- Rename compile time constants for number of slots and subslots
## 2020-09-19
### Changed
- Modified public API for sending alarm ACK.
## 2020-08-24
### Changed
- Renamed configuration members related to I&M data
## 2020-08-18
### Added
- Add min_device_interval to the configuration
- Use cmake to set configurable build options
## 2020-06-15
### Added
- New user callback for LED state change.
## 2020-06-11
### Added
- New API function for application to perform a factory reset
## 2020-04-09
### Added
- New user callback to indicate reset requests from the IO-controller.
## 2020-04-06
### Changed
- Changed the user API to use a handle to the p-net stack, and to allow user
arguments to callbacks.
## 2020-03-27
### Changed
- Read MAC address also from Linux hardware.

View File

@ -0,0 +1,265 @@
#********************************************************************
# _ _ _
# _ __ | |_ _ | | __ _ | |__ ___
# | '__|| __|(_)| | / _` || '_ \ / __|
# | | | |_ _ | || (_| || |_) |\__ \
# |_| \__|(_)|_| \__,_||_.__/ |___/
#
# www.rt-labs.com
# Copyright 2018 rt-labs AB, Sweden.
#
# This software is dual-licensed under GPLv3 and a commercial
# license. See the file LICENSE.md distributed with this software for
# full license information.
#*******************************************************************/
cmake_minimum_required (VERSION 3.14)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/tools")
project (PROFINET VERSION 0.2.0)
# Default settings if this is the main project
if (CMAKE_PROJECT_NAME STREQUAL PROFINET)
include(CTest)
# Make option visible in ccmake, cmake-gui
option (BUILD_SHARED_LIBS "Build shared library" OFF)
# Default to release build with debug info
if (NOT CMAKE_BUILD_TYPE)
set (CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING
"Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel."
FORCE)
endif (NOT CMAKE_BUILD_TYPE)
# Default to installing in build directory
if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX ${PROFINET_BINARY_DIR}/install
CACHE PATH "Default install path" FORCE)
endif()
message(STATUS "Current build type is: ${CMAKE_BUILD_TYPE}")
message(STATUS "Current install path is: ${CMAKE_INSTALL_PREFIX}")
message(STATUS "Building for ${CMAKE_SYSTEM_NAME}")
endif()
include(AddOsal)
include(GenerateExportHeader)
include(CMakeDependentOption)
include(GetGitRevision)
# Always use standard .o suffix
set(CMAKE_C_OUTPUT_EXTENSION_REPLACE 1)
set(CMAKE_CXX_OUTPUT_EXTENSION_REPLACE 1)
# Options. See options.h.in for more information.
option (PNET_OPTION_FAST_STARTUP "" ON)
option (PNET_OPTION_PARAMETER_SERVER "" ON)
option (PNET_OPTION_IR "" ON)
option (PNET_OPTION_SR "" ON)
option (PNET_OPTION_REDUNDANCY "" ON)
option (PNET_OPTION_AR_VENDOR_BLOCKS "" ON)
option (PNET_OPTION_RS "" ON)
option (PNET_OPTION_MC_CR "" ON)
option (PNET_OPTION_SRL "" OFF)
option (PNET_OPTION_SNMP "" OFF)
option (PNET_OPTION_DRIVER_ENABLE "Enable drivers. Specific driver must be enabled." OFF )
# TODO: this should be handled in cc.h
option (PNET_USE_ATOMICS "Enable use of atomic operations (stdatomic.h)" OFF)
set(PNET_MAX_AR 2
CACHE STRING "Number of connections. Must be > 0. 'Automated RT Tester' uses 2, but only 1 connection AR is supported.")
set(PNET_MAX_API 1
CACHE STRING "Number of Application Processes. Must be > 0")
set(PNET_MAX_CR 2
CACHE STRING "Per AR. At least 2 (1 input and 1 output). If unsure, use 2.")
set(PNET_MAX_SLOTS 5
CACHE STRING "Per API. Should be > 1 to allow at least one I/O module")
set(PNET_MAX_SUBSLOTS 3
CACHE STRING "Per slot (DAP requires 2 + PNET_MAX_PHYSICAL_PORTS)")
set(PNET_MAX_DFP_IOCR 2
CACHE STRING "Allowed values are 0 (zero) or 2")
set(PNET_MAX_PHYSICAL_PORTS 1
CACHE STRING "Max number of physical ports")
set(PNET_MAX_LOG_BOOK_ENTRIES 16
CACHE STRING "")
set(PNET_MAX_ALARMS 6
CACHE STRING "Per AR and queue. One queue for high and one for low priority alarms. 'Automated RT Tester' uses 6.")
set(PNET_MAX_ALARM_PAYLOAD_DATA_SIZE 28
CACHE STRING "Min 24 or PNET_MAX_DIAG_MANUF_DATA_SIZE + 12. Max is 1408")
set(PNET_MAX_DIAG_ITEMS 200
CACHE STRING "Total, per device. Max is 65534 items")
set(PNET_MAX_DIAG_MANUF_DATA_SIZE 16
CACHE STRING "Min 5 for tests. Max is 1396")
set(PNET_MAX_MC_CR 1
CACHE STRING "Per AR")
set(PNET_MAX_AR_VENDOR_BLOCKS 1
CACHE STRING "Must be > 0")
set(PNET_MAX_AR_VENDOR_BLOCK_DATA_LENGTH 512
CACHE STRING "")
set(PNET_MAX_MAN_SPECIFIC_FAST_STARTUP_DATA_LENGTH 0
CACHE STRING "or 512 (bytes")
set(PNET_MAX_SESSION_BUFFER_SIZE 4500
CACHE STRING "Max fragmented RPC request/response length. Max value 65535")
set(PNET_MAX_DIRECTORYPATH_SIZE 240
CACHE STRING "Max size of directory path, including termination")
set(PNET_MAX_FILENAME_SIZE 30
CACHE STRING "Max size of filename, including termination")
set(PNET_MAX_PORT_DESCRIPTION_SIZE 60
CACHE STRING "Max size of port description, including termination")
set(LOG_STATE_VALUES "ON;OFF")
set(LOG_LEVEL_VALUES "DEBUG;INFO;WARNING;ERROR;FATAL")
set(LOG_LEVEL FATAL CACHE STRING "default log level")
set_property(CACHE LOG_LEVEL PROPERTY STRINGS ${LOG_LEVEL_VALUES})
set(PF_ETH_LOG ON CACHE STRING "pf_eth log")
set_property(CACHE PF_ETH_LOG PROPERTY STRINGS ${LOG_STATE_VALUES})
set(PF_LLDP_LOG ON CACHE STRING "pf_lldp log")
set_property(CACHE PF_LLDP_LOG PROPERTY STRINGS ${LOG_STATE_VALUES})
set(PF_SNMP_LOG ON CACHE STRING "pf_snmp log")
set_property(CACHE PF_SNMP_LOG PROPERTY STRINGS ${LOG_STATE_VALUES})
set(PF_CPM_LOG ON CACHE STRING "pf_cpm log")
set_property(CACHE PF_CPM_LOG PROPERTY STRINGS ${LOG_STATE_VALUES})
set(PF_PPM_LOG ON CACHE STRING "pf_ppm log")
set_property(CACHE PF_PPM_LOG PROPERTY STRINGS ${LOG_STATE_VALUES})
set(PF_DCP_LOG ON CACHE STRING "pf_dcp log")
set_property(CACHE PF_DCP_LOG PROPERTY STRINGS ${LOG_STATE_VALUES})
set(PF_RPC_LOG ON CACHE STRING "pf_rpc log")
set_property(CACHE PF_RPC_LOG PROPERTY STRINGS ${LOG_STATE_VALUES})
set(PF_ALARM_LOG ON CACHE STRING "pf_alarm log")
set_property(CACHE PF_ALARM_LOG PROPERTY STRINGS ${LOG_STATE_VALUES})
set(PF_AL_BUF_LOG ON CACHE STRING "pf_alarm_buffer log")
set_property(CACHE PF_AL_BUF_LOG PROPERTY STRINGS ${LOG_STATE_VALUES})
set(PF_PNAL_LOG ON CACHE STRING "PNAL log")
set_property(CACHE PF_PNAL_LOG PROPERTY STRINGS ${LOG_STATE_VALUES})
set(PNET_LOG ON CACHE STRING "PNET log")
set_property(CACHE PNET_LOG PROPERTY STRINGS ${LOG_STATE_VALUES})
# Generate version numbers
configure_file (
pnet_version.h.in
${PROFINET_BINARY_DIR}/include/pnet_version.h
)
# Generate config options
configure_file (
options.h.in
${PROFINET_BINARY_DIR}/src/options.h
)
configure_file (
pnet_options.h.in
${PROFINET_BINARY_DIR}/include/pnet_options.h
)
# Add platform-dependent targets early, so they can be configured by
# platform
add_library(profinet "")
add_executable(pn_dev "")
if (PNET_OPTION_DRIVER_ENABLE)
include(${CMAKE_CURRENT_SOURCE_DIR}/src/drivers/drivers.cmake)
endif ()
if (CMAKE_PROJECT_NAME STREQUAL PROFINET AND BUILD_TESTING)
add_executable(pf_test "")
endif()
# Platform configuration
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/${CMAKE_SYSTEM_NAME}.cmake)
generate_export_header(profinet
BASE_NAME pnet
EXPORT_FILE_NAME ${PROFINET_BINARY_DIR}/include/pnet_export.h
)
set_target_properties (profinet pn_dev
PROPERTIES
C_STANDARD 99
)
target_compile_features(profinet PUBLIC c_std_99)
target_include_directories(profinet
PUBLIC
$<BUILD_INTERFACE:${PROFINET_SOURCE_DIR}/include>
$<BUILD_INTERFACE:${PROFINET_BINARY_DIR}/include>
$<INSTALL_INTERFACE:include>
PRIVATE
${PROFINET_BINARY_DIR}/src
src
src/common
src/device
)
target_link_libraries(profinet PUBLIC osal)
install (
TARGETS profinet
EXPORT ProfinetConfig
DESTINATION lib
)
install(
EXPORT ProfinetConfig
DESTINATION cmake
)
install (FILES
include/pnet_api.h
${PROFINET_BINARY_DIR}/include/pnet_export.h
${PROFINET_BINARY_DIR}/include/pnet_options.h
${PROFINET_BINARY_DIR}/include/pnet_version.h
DESTINATION include
)
add_subdirectory (src)
add_subdirectory (samples/pn_dev)
if (CMAKE_PROJECT_NAME STREQUAL PROFINET AND BUILD_TESTING)
add_subdirectory (test)
include(AddGoogleTest)
add_gtest(pf_test)
endif()
# Doxygen configuration
cmake_policy(SET CMP0057 NEW)
find_package(Doxygen)
if (CMAKE_PROJECT_NAME STREQUAL PROFINET AND DOXYGEN_FOUND)
set(DOXYGEN_OPTIMIZE_OUTPUT_FOR_C YES)
set(DOXYGEN_TYPEDEF_HIDES_STRUCT YES)
set(DOXYGEN_STRIP_CODE_COMMENTS NO)
set(DOXYGEN_MACRO_EXPANSION YES)
set(DOXYGEN_EXPAND_ONLY_PREDEF YES)
set(DOXYGEN_PREDEFINED PNET_EXPORT)
set(DOXYGEN_EXPAND_AS_DEFINED PNET_EXPORT)
set(DOXYGEN_COLLABORATION_GRAPH NO)
set(DOXYGEN_INCLUDE_GRAPH NO)
set(DOXYGEN_INCLUDED_BY_GRAPH NO)
set(DOXYGEN_RECURSIVE YES)
set(DOXYGEN_GENERATE_XML YES)
set(DOXYGEN_USE_MDFILE_AS_MAINPAGE README.md)
doxygen_add_docs(docs
README.md
include
src
)
endif()
include (InstallRequiredSystemLibraries)
set (CPACK_RESOURCE_FILE_LICENSE "${PROFINET_SOURCE_DIR}/LICENSE.md")
set (CPACK_PACKAGE_CONTACT info.profinet@rt-labs.com)
include (CPack)

View File

@ -0,0 +1,23 @@
# License
This software is dual-licensed.
## GPL version 3
This software is distributed under GPLv3. You are allowed to use this
software for an open-source project with a compatible license.
Use of IPC interface(s) provided by the project is understood to be
ordinary use, not within the meaning of "covered work" as defined in
the GPLv3 license.
[GNU GPL license v3](https://www.gnu.org/licenses/gpl-3.0.html)
## Commercial license
This software is also available under a commercial license with
options for support and maintenance. Please contact sales@rt-labs.com
for further details.
If you intend to use this stack in a commercial product, you likely need to
buy a license.

View File

@ -0,0 +1,147 @@
p-net Profinet device stack
===========================
Web resources
-------------
* Source repository: [https://github.com/rtlabs-com/p-net](https://github.com/rtlabs-com/p-net)
* Documentation: [https://rt-labs.com/docs/p-net](https://rt-labs.com/docs/p-net)
* Continuous integration: [https://github.com/rtlabs-com/p-net/actions](https://github.com/rtlabs-com/p-net/actions)
* RT-Labs (stack integration, certification services and training): [https://rt-labs.com](https://rt-labs.com)
[![Build Status](https://github.com/rtlabs-com/p-net/workflows/Build/badge.svg?branch=master)](https://github.com/rtlabs-com/p-net/actions?workflow=Build)
[![CodeQL](https://github.com/rtlabs-com/p-net/workflows/CodeQL/badge.svg?branch=master)](https://github.com/rtlabs-com/p-net/actions?workflow=CodeQL)
p-net
-----
Profinet device stack implementation. Key features:
* Profinet v2.4
* Conformance Class A and B
* Real Time Class 1
* Multiple Ethernet ports
* Easy to use
* Extensive documentation and instructions on how to get started.
* Build and run sample application on Raspberry Pi in 30 minutes.
* Portable
* Written in C.
* Linux, RTOS or bare metal.
* Sources for supported port layers provided.
The RT-Labs Profinet stack p-net is used for Profinet device
implementations. It is easy to use and provides a small footprint. It
is especially well suited for embedded systems where resources are
limited and efficiency is crucial.
The stack is supplied with full sources including porting
layers and a sample application.
Also C++ (any version) is supported for application development.
The main requirement on the platform
is that it can send and receive raw Ethernet Layer 2 frames.
Features:
* Multiple Ethernet ports (for Linux only, so far)
* TCP/IP
* LLDP
* SNMP
* RT (real-time)
* Address resolution
* Parameterization
* Process IO data exchange
* Alarm handling
* Configurable number of modules and sub-modules
* Bare-metal or OS
* Porting layer provided
* Supports I&M0 - I&M4. The I&M data is supported for the device, but not for
individual modules.
Limitations or not yet implemented:
* This is a device stack, which means that the IO-controller/master/PLC side is
not supported.
* No media redundancy (No MRP support)
* Legacy startup mode is not fully implemented
* No support for RT_CLASS_UDP
* No support for DHCP
* No fast start-up
* No MC multicast device-to-device
* No support of shared device (connection to multiple controllers)
* Supports only full connections, not the limited "DeviceAccess" connection type.
* No iPar (parameter server) support
* No support for time synchronization
* No UDP frames at alarm (just the default alarm mechanism is implemented)
* No ProfiDrive or ProfiSafe profiles.
This software is dual-licensed, with GPL version 3 and a commercial license.
If you intend to use this stack in a commercial product, you likely need to
buy a license. See LICENSE.md for more details.
Requirements
------------
The platform must be able to send and receive raw Ethernet Layer 2 frames,
and the Ethernet driver must be able to handle full size frames. It
should also avoid copying data, for performance reasons.
* cmake 3.14 or later
For Linux:
* gcc 4.6 or later
* See the "Real-time properties of Linux" page in the documentation on how to
improve Linux timing
For rt-kernel:
* Workbench 2020.1 or later
An example of microcontroller we have been using is the Infineon XMC4800,
which has an ARM Cortex-M4 running at 144 MHz, with 2 MB Flash and 352 kB RAM.
It runs rt-kernel, and we have tested it with 9 Profinet slots each
having 8 digital inputs and 8 digital outputs (one bit each). The values are
sent and received each millisecond (PLC watchdog setting 3 ms).
Getting started
---------------
See the tutorial in the documentation: [https://rt-labs.com/docs/p-net/tutorial.html](https://rt-labs.com/docs/p-net/tutorial.html)
Note that you need to include submodules when cloning:
```
git clone --recurse-submodules https://github.com/rtlabs-com/p-net.git
```
Dependencies
------------
Some of the platform-dependent parts are located in the OSAL repository and the
cmake-tools repository.
* [https://github.com/rtlabs-com/osal](https://github.com/rtlabs-com/osal)
* [https://github.com/rtlabs-com/cmake-tools](https://github.com/rtlabs-com/cmake-tools)
Those are downloaded automatically during install.
The p-net stack contains no third party components. Its external dependencies are:
* C-library
* An operating system (if used)
* For conformance class B you need an SNMP implementation. On Linux is
net-snmp (BSD License) used [http://www.net-snmp.org](http://www.net-snmp.org)
Tools used for building, testing and documentation (not shipped in the resulting binaries):
* cmake (BSD 3-clause License) [https://cmake.org](https://cmake.org)
* gtest (BSD-3-Clause License) [https://github.com/google/googletest](https://github.com/google/googletest)
* Sphinx (BSD license) [https://www.sphinx-doc.org](https://www.sphinx-doc.org)
* Doxygen (GPL v2) [https://www.doxygen.nl](https://www.doxygen.nl/index.html)
* clang-format (Apache License 2.0) [https://clang.llvm.org](https://clang.llvm.org/docs/ClangFormat.html)
Contributions
--------------
Contributions are welcome. If you want to contribute you will need to
sign a Contributor License Agreement and send it to us either by
e-mail or by physical mail. More information is available
on [https://rt-labs.com/contribution](https://rt-labs.com/contribution).

View File

@ -0,0 +1,37 @@
#********************************************************************
# _ _ _
# _ __ | |_ _ | | __ _ | |__ ___
# | '__|| __|(_)| | / _` || '_ \ / __|
# | | | |_ _ | || (_| || |_) |\__ \
# |_| \__|(_)|_| \__,_||_.__/ |___/
#
# www.rt-labs.com
# Copyright 2020 rt-labs AB, Sweden.
#
# This software is dual-licensed under GPLv3 and a commercial
# license. See the file LICENSE.md distributed with this software for
# full license information.
#*******************************************************************/
include(FindPackageHandleStandardArgs)
# Find Net-SNMP
find_path(NetSNMP_INCLUDE_DIR net-snmp-includes.h
PATH_SUFFIXES net-snmp
)
find_library(NetSNMP_LIBRARY netsnmp)
mark_as_advanced(NetSNMP_INCLUDE_DIR NetSNMP_LIBRARY)
find_package_handle_standard_args(NetSNMP
REQUIRED_VARS NetSNMP_LIBRARY NetSNMP_INCLUDE_DIR
)
if (NetSNMP_FOUND AND NOT TARGET NetSNMP::NetSNMP)
add_library(NetSNMP::NetSNMP UNKNOWN IMPORTED)
set_target_properties(NetSNMP::NetSNMP PROPERTIES
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
IMPORTED_LOCATION "${NetSNMP_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${NetSNMP_INCLUDE_DIRS}"
)
endif()

View File

@ -0,0 +1,37 @@
#********************************************************************
# _ _ _
# _ __ | |_ _ | | __ _ | |__ ___
# | '__|| __|(_)| | / _` || '_ \ / __|
# | | | |_ _ | || (_| || |_) |\__ \
# |_| \__|(_)|_| \__,_||_.__/ |___/
#
# www.rt-labs.com
# Copyright 2020 rt-labs AB, Sweden.
#
# This software is dual-licensed under GPLv3 and a commercial
# license. See the file LICENSE.md distributed with this software for
# full license information.
#*******************************************************************/
include(FindPackageHandleStandardArgs)
# Find Net-SNMP agent
find_path(NetSNMPAgent_INCLUDE_DIR net-snmp-agent-includes.h
PATH_SUFFIXES net-snmp/agent
)
find_library(NetSNMPAgent_LIBRARY netsnmpagent)
mark_as_advanced(NetSNMPAgent_INCLUDE_DIR NetSNMPAgent_LIBRARY)
find_package_handle_standard_args(NetSNMPAgent
REQUIRED_VARS NetSNMPAgent_LIBRARY NetSNMPAgent_INCLUDE_DIR
)
if (NetSNMPAgent_FOUND AND NOT TARGET NetSNMP::NetSNMPAgent)
add_library(NetSNMP::NetSNMPAgent UNKNOWN IMPORTED)
set_target_properties(NetSNMP::NetSNMPAgent PROPERTIES
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
IMPORTED_LOCATION "${NetSNMPAgent_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${NetSNMPAgent_INCLUDE_DIRS}"
)
endif()

View File

@ -0,0 +1,116 @@
#********************************************************************
# _ _ _
# _ __ | |_ _ | | __ _ | |__ ___
# | '__|| __|(_)| | / _` || '_ \ / __|
# | | | |_ _ | || (_| || |_) |\__ \
# |_| \__|(_)|_| \__,_||_.__/ |___/
#
# www.rt-labs.com
# Copyright 2018 rt-labs AB, Sweden.
#
# This software is dual-licensed under GPLv3 and a commercial
# license. See the file LICENSE.md distributed with this software for
# full license information.
#*******************************************************************/
if (PNET_OPTION_SNMP)
find_package(NetSNMP REQUIRED)
find_package(NetSNMPAgent REQUIRED)
endif()
target_include_directories(profinet
PRIVATE
src/ports/linux
)
target_sources(profinet
PRIVATE
src/ports/linux/pnal.c
src/ports/linux/pnal_eth.c
src/ports/linux/pnal_udp.c
src/ports/linux/pnal_filetools.c
$<$<BOOL:${PNET_OPTION_SNMP}>:src/ports/linux/pnal_snmp.c>
$<$<BOOL:${PNET_OPTION_SNMP}>:src/ports/linux/mib/system_mib.c>
$<$<BOOL:${PNET_OPTION_SNMP}>:src/ports/linux/mib/lldpLocalSystemData.c>
$<$<BOOL:${PNET_OPTION_SNMP}>:src/ports/linux/mib/lldpLocPortTable.c>
$<$<BOOL:${PNET_OPTION_SNMP}>:src/ports/linux/mib/lldpConfigManAddrTable.c>
$<$<BOOL:${PNET_OPTION_SNMP}>:src/ports/linux/mib/lldpLocManAddrTable.c>
$<$<BOOL:${PNET_OPTION_SNMP}>:src/ports/linux/mib/lldpRemTable.c>
$<$<BOOL:${PNET_OPTION_SNMP}>:src/ports/linux/mib/lldpRemManAddrTable.c>
$<$<BOOL:${PNET_OPTION_SNMP}>:src/ports/linux/mib/lldpXdot3LocPortTable.c>
$<$<BOOL:${PNET_OPTION_SNMP}>:src/ports/linux/mib/lldpXdot3RemPortTable.c>
$<$<BOOL:${PNET_OPTION_SNMP}>:src/ports/linux/mib/lldpXPnoLocTable.c>
$<$<BOOL:${PNET_OPTION_SNMP}>:src/ports/linux/mib/lldpXPnoRemTable.c>
)
target_compile_options(profinet
PRIVATE
-Wall
-Wextra
-Werror
-Wno-unused-parameter
-ffunction-sections
-fdata-sections
INTERFACE
$<$<CONFIG:Coverage>:--coverage>
)
target_link_libraries(profinet
PUBLIC
$<$<BOOL:${PNET_OPTION_SNMP}>:NetSNMP::NetSNMPAgent>
$<$<BOOL:${PNET_OPTION_SNMP}>:NetSNMP::NetSNMP>
INTERFACE
$<$<CONFIG:Coverage>:--coverage>
)
target_include_directories(pn_dev
PRIVATE
samples/pn_dev
src/ports/linux
)
target_sources(pn_dev
PRIVATE
samples/pn_dev/sampleapp_common.c
samples/pn_dev/app_utils.c
samples/pn_dev/app_log.c
samples/pn_dev/app_gsdml.c
samples/pn_dev/app_data.c
src/ports/linux/sampleapp_main.c
)
target_compile_options(pn_dev
PRIVATE
-Wall
-Wextra
-Werror
-Wno-unused-parameter
-ffunction-sections
-fdata-sections
)
target_link_options(pn_dev
PRIVATE
-Wl,--gc-sections
)
install (FILES
src/ports/linux/pnal_config.h
DESTINATION include
)
file(COPY
src/ports/linux/set_network_parameters
src/ports/linux/set_profinet_leds
src/ports/linux/set_profinet_leds.raspberrypi
DESTINATION
${PROFINET_BINARY_DIR}/
)
if (BUILD_TESTING)
set(GOOGLE_TEST_INDIVIDUAL TRUE)
target_include_directories(pf_test
PRIVATE
src/ports/linux
)
endif()

View File

@ -0,0 +1,83 @@
#********************************************************************
# _ _ _
# _ __ | |_ _ | | __ _ | |__ ___
# | '__|| __|(_)| | / _` || '_ \ / __|
# | | | |_ _ | || (_| || |_) |\__ \
# |_| \__|(_)|_| \__,_||_.__/ |___/
#
# www.rt-labs.com
# Copyright 2021 rt-labs AB, Sweden.
#
# This software is dual-licensed under GPLv3 and a commercial
# license. See the file LICENSE.md distributed with this software for
# full license information.
#*******************************************************************/
target_include_directories(profinet
PRIVATE
src/ports/STM32Cube
)
target_sources(profinet
PRIVATE
src/ports/STM32Cube/pnal.c
src/ports/STM32Cube/pnal_eth.c
src/ports/STM32Cube/pnal_udp.c
)
target_compile_options(profinet
PRIVATE
-Wall
-Wextra
-Werror
-Wno-unused-parameter
)
target_include_directories(pn_dev
PRIVATE
samples/pn_dev
src/ports/STM32Cube
)
if (EXISTS ${PROFINET_SOURCE_DIR}/src/ports/STM32Cube/sampleapp_${BOARD}.c)
set(BOARD_SOURCE sampleapp_${BOARD}.c)
else()
set(BOARD_SOURCE sampleapp_board.c)
endif()
target_sources(pn_dev
PRIVATE
samples/pn_dev/sampleapp_common.c
samples/pn_dev/app_utils.c
samples/pn_dev/app_log.c
samples/pn_dev/app_gsdml.c
samples/pn_dev/app_data.c
src/ports/STM32Cube/sampleapp_main.c
src/ports/STM32Cube/${BOARD_SOURCE}
)
target_compile_options(pn_dev
PRIVATE
-Wall
-Wextra
-Werror
-Wno-unused-parameter
)
target_link_libraries(pn_dev PRIVATE cube-bsp)
install (FILES
src/ports/STM32Cube/pnal_config.h
DESTINATION include
)
generate_bin(pn_dev)
if (BUILD_TESTING)
target_include_directories(pf_test
PRIVATE
src/ports/STM32Cube
)
target_link_libraries(pf_test PRIVATE cube-bsp)
generate_bin(pf_test)
endif()

View File

@ -0,0 +1,83 @@
#********************************************************************
# _ _ _
# _ __ | |_ _ | | __ _ | |__ ___
# | '__|| __|(_)| | / _` || '_ \ / __|
# | | | |_ _ | || (_| || |_) |\__ \
# |_| \__|(_)|_| \__,_||_.__/ |___/
#
# www.rt-labs.com
# Copyright 2018 rt-labs AB, Sweden.
#
# This software is dual-licensed under GPLv3 and a commercial
# license. See the file LICENSE.md distributed with this software for
# full license information.
#*******************************************************************/
target_include_directories(profinet
PRIVATE
src/ports/rt-kernel
)
target_sources(profinet
PRIVATE
src/ports/rt-kernel/pnal.c
src/ports/rt-kernel/pnal_eth.c
src/ports/rt-kernel/pnal_udp.c
src/ports/rt-kernel/pnal_snmp.c
src/ports/rt-kernel/mib/mib2_system.c
src/ports/rt-kernel/mib/lldp-mib.c
src/ports/rt-kernel/mib/lldp-ext-pno-mib.c
src/ports/rt-kernel/mib/lldp-ext-dot3-mib.c
src/ports/rt-kernel/mib/rowindex.c
)
target_compile_options(profinet
PRIVATE
-Wall
-Wextra
-Werror
-Wno-unused-parameter
)
target_include_directories(pn_dev
PRIVATE
samples/pn_dev
src/ports/rt-kernel
)
if (EXISTS ${PROFINET_SOURCE_DIR}/src/ports/rt-kernel/sampleapp_${BSP}.c)
set(BSP_SOURCE sampleapp_${BSP}.c)
else()
set(BSP_SOURCE sampleapp_bsp.c)
endif()
target_sources(pn_dev
PRIVATE
samples/pn_dev/sampleapp_common.c
samples/pn_dev/app_utils.c
samples/pn_dev/app_log.c
samples/pn_dev/app_gsdml.c
samples/pn_dev/app_data.c
src/ports/rt-kernel/sampleapp_main.c
src/ports/rt-kernel/${BSP_SOURCE}
)
target_compile_options(pn_dev
PRIVATE
-Wall
-Wextra
-Werror
-Wno-unused-parameter
)
install (FILES
src/ports/rt-kernel/pnal_config.h
DESTINATION include
)
if (BUILD_TESTING)
target_include_directories(pf_test
PRIVATE
src/ports/rt-kernel
)
endif()

View File

@ -0,0 +1,34 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = .
BUILDDIR = _build
htmlzipfile := pnet_docs_$(shell git describe --dirty --always --tags).zip
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
# Note that we remove the build status badge image before PDF creation.
%: Makefile
@echo "Cleaning ..."
@rm -rf _copied/
@rm -rf _build/
@echo "Copying markdown files to _copied subdir"
@mkdir -p _copied
@cp ../README.md _copied/
@cp ../LICENSE.md _copied/
@cp ../CHANGELOG.md _copied/
@(test $@ != pdf) || sed -i '/Build Status/d' _copied/README.md
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
@(test $@ != html) || cd _build; echo "Zipping ..."; zip -r $(htmlzipfile) html/ > /dev/null

View File

@ -0,0 +1,375 @@
Terminology and abbreviations
=============================
Abstract syntax
Describes the parts (and their subparts) of different messages. See also "Transfer syntax".
Configuration
IO-device and IO-controller definition in an engineering tool.
Commissioning
Device initialization (Configuration is downloaded to IO-controller).
Engineering tool
A desktop program for configuring PLC. For example Siemens TIA portal.
Parameterization
Write parameter values to all submodules. Parameterization is done via
the Write and Control commands.
Record data
Data that can be accessed with acyclic Profinet Read and Write. Addressed by
slot, subslot and index.
Transfer syntax
Coding of the different fields in a message. See also "Abstract syntax".
Abbreviations
-------------
ACK
Positively acknowledge
AKA
Also Known As
ALPMI
Alarm Protocol Machine Initiator. Trigger alarms.
ALPMR
Alarm Protocol Machine Responder. Responds to incoming alarms.
ALPMX
General term for ALPMI and ALPMR
AMR
Asset Management Record
AP
Application Process
APDU
Application (layer) Protocol Data Unit
API
Application Process Identifier (uint32). Used to differentiate between for example user profiles. Sometimes named "Profile ID".
API
Application Programming Interface. Application implementers use the API of the p-net Profinet stack.
APMR
Acyclic Protocol Machine Receiver. Receives incoming alarm frames.
APMS
Acyclic Protocol Machine Sender. Sends alarm frames.
APMX
General term for APMR and APMS
APO
Application Process Object
AR
Application Relation. Consists of several communication relations (CR). Typically an IO AR, which is a connection to a PLC, but can also be an Supervisor AR.
ARP
Address Resolution Protocol, used to translate from an IP address to a MAC address.
AREP
Application Relationship End Point (uint32), pretty much an index into an array of AR.
ASDU
Application Service Data Unit
ASE
Application Service Element. For example logbook, time and diagnosis.
CBA
Component Based Automation, for the legacy Profinet CBA system. It used communication profile CP 3/3.
CC
Conformance Class (Performance level A to D)
CDML
Controller Description Markup Language. An XML file describing a controller, for certification.
CE
A marking for European Union declaration of conformity
CIDR
Classless Inter-Domain Routing. The CIDR notation ``/24`` tells how many bits of the netmask that should be enabled.
CIM
Communication Interface Management
CiR
Configuration in Run
CM
Context Management
CMDEV
Context Management Protocol Machine Device
CMI
CM Initiator
CMINA
Context Management Ip and Name Assignment protocol machine
CMIO
Context Management Input Output protocol machine
CMPBE
Context Management Parameter Begin End protocol machine
CMRDR
Context Management Read Record Responder protocol machine, responds to parameter read from the IO-controller.
CMRPC
Context Management RPC protocol machine
CMWRR
Context Management Write Record Responder protocol machine, responds to parameter write from the IO-controller.
CMSM
Context Management Surveillance protocol Machine, monitors the establishment of a connection. Also written as CM Server Protocol machine.
CP
Communication Profile. For example Profinet conformance class B is also known as CP 3/5.
CPF
Communication Profile Family. Profinet and Profibus is CPF = 3, Ethercat is CPF = 12.
CPM
Consumer Protocol Machine, for receiving cyclic data.
CPU
Central Processing Unit
CR
Communication Relation (Part of AR).
CREP
Communication Relationship EndPoint (uint32), pretty much an index into an array of input and output CR.
DA
Destination Address. It is the MAC address of the receiver.
DAP
Device Access Point
DCE
Distributed Computing Environment. Used with RPC.
DCP
Discovery and basic Configuration Protocol. Runs over Ethernet layer 2 (not IP or UDP).
DFP
Dynamic Frame Packing. Used with IRT protocol.
DHCP
Dynamic Host Configuration Protocol, for allocating IP addresses to devices.
DHT
Data Hold Timer
DLL
Data Link Layer
DLPDU
?
DNS
Domain Name System, for converting from host name to IP address.
DT
Device Tool
DUT
Device Under Test
EMC
ElectroMagnetic Compatibility
EPM
EndPoint Mapper
ES
Engineering System
FACK
Fragment acknowledge
FAL
Fieldbus Application Layer
FD
Full Duplex
FDB
Forwarding Database, used in MRP
FO
Fiber Optics
FSPM
FAL Service Protocol Machines
FSU
Fast Start Up (Store communication parameters in IO devices)
GAP
?
GSD
General Station Description. An XML file describing an IO-Device.
GSDML
GSD Markup Language
GUI
Graphical User Interface
HTTP
Hypertext Transfer Protocol
I&M
Identification & Maintenance. Has different blocks; IM0 to IM??.
ICMP
Internet Control Message Protocol. (Sent in an IP packet)
IDNA
Internationalized Domain Names for Applications
IE
Industrial Ethernet
IGMP
Internet Group Management Protocol. For multicast groups. Used in IPv4.
IO
Input Output
IOC
IO Controller. Typically a PLC.
IOD
IO Device. An input-output device controlled by a PLC via Profinet communication.
IOCS
IO Consumer Status. Reported by IO-device (for output data) and IO-controller (for input data), per subslot. (uint8)
IOCR
IO Communication Relation
IOPS
IO Provider Status. Describes validity of IO data per subslot. Sent by IO-device (for input data) or IO-controller (for output data) together with data. (uint8)
IOxS
General term for IOCS and IOPS.
IOS
IO Supervisor. Typically an engineering tool running on a personal computer.
IP
Internet Protocol
IP
Ingress Protection. For example IP65 is a housing class suitable for outdoor installation.
iPar
Individual Parameters. Backups of these parameters are typically stored in a separate parameter server.
IRT
Isochronous Real-Time
LAN
Local Area Network
LED
Light Emitting Diode
LLC
?
LLDP
Link Layer Discovery Protocol, for neighbourhood detection.
LMPM
data Link layer Mapping Protocol Machine. Receives Ethernet frames.
LT
Length and Type field in Ethernet frame. Also known as EtherType.
MAC
Media Access Control
MAU
Medium Attachment Unit. Ethernet transceiver type. 0x0 = radio, 0x10 = Media type copper 100BaseTXFD
MC
Multicast (as opposed to unicast)
MC
Multicore (Codesys runtime variant for Raspberry Pi)
MCR
Multicast Communication Relationship
MDNS
Multicast DNS. A UDS based protocol for resolving hostname to IP address. Implemented by Bonjour and Avahi.
MIB
Management Information Base. A text file format describing Object Identifiers (OID) for Simple Network Management Protocol (SNMP) frames.
MIC
MRP interconnection Client. Routes traffic between two Ethernet rings (which are using MRP).
MIM
MRP Interconnection Manager. Controls the traffic between two Ethernet rings (which are using MRP).
MRA
Media redundancy manager with auto negotiation
MRC
Media redundancy client
MRM
Media redundancy manager
MRP
Media Redundancy Protocol. Use a normally off path in the Ethernet network, to form a ring topology.
MRPIC
Media Redundancy Protocol Interconnect (connects two MRP rings). Nodes have MIM or MIC roles.
MRPD
Media Redundancy for Planned Duplication. Send similar frames in both directions on an Ethernet ring.
MTU
Maximum Transfer Unit. The largest packet a network interface can handle. Typically 1500 bytes. This includes the IP header, but not the Ethernet header.
NACK
Negatively acknowledge
NDR
Network Data Representation. A header as first part of the DCE/RPC payload (sent via UDP). Contains info on how large the payload is, and how large responses that can be accepted.
NME
Network Management Engine
NRT
Non-Real Time Ethernet frames
NVM
Non-Volatile Memory. Typically flash memory chips.
OID
Object Identifier. Describes a field in a Simple Network Management Protocol (SNMP) frame.
OS
Operating System
OUI
Organizationally Unique Identifier. This is the three first bytes of the MAC address. The value for Profinet Multicast is 01:0E:CF.
PA
Process Automation (as opposed to production automation)
PCA
Provider, Consumer or Alarm.
PCF
Polymer Clad Fiber
PCP
Priority Code Point, for VLAN
PD
Physical Device. This is information related to an Ethernet port.
PDEV
Physical Device management. Physical interface and switch ports of a Profinet field device.
PDF
Portable Document Format
PDU
Protocol Data Unit
PI
PROFIBUS & PROFINET International. The Profinet interest group. See also PNO.
PICO
PI Certification Office
PITL
PI Test Laboratories. Performs certification testing.
PLC
Programmable Logic Controller. Often used as a Profinet IO-controller.
PN
See PROFINET
PNI
Primary Network Initialization. Siemens SinecPni is a tool for configuration of Profinet equipment.
PNIO
Profinet IO protocol
PNO
PROFIBUS Nutzerorganisation e.V, located in Germany. See also PI.
POF
Plastic Optical Fiber
PPM
Cyclic Provider Protocol Machine
PROFINET
Process Field Net
PS
?
PTCP
Precision Transparent Clock Protocol
RED
Redundancy
RPC
Remote Procedure Call. The protocol DCE/RPC runs on UDP and is used for configuration of the IO-Device during startup.
RS
Reporting system
RSI
Remote Service Interface
RTA
RealTime Acyclic protocol, for alarms.
RTC
Real Time Class
RTC
RealTime Cyclic protocol
RTE
Real Time Ethernet
RTOS
Real Time Operating System
SA
Source Address. It is the MAC address of the sender.
SAM
Source Address of ? Uses to restrict incoming DCP communication to a single remote MAC address (for 3 seconds).
SCL
Structured Control Language. Siemens name for the structured text (ST) programming language for PLCs.
SDU
Service Data Unit. This is the payload of a PDU.
SMA
Sample Moving Average. A smoothing function in the Wireshark Ethernet frame capture software.
SNMP
Simple Network Management Protocol. For network topology detection.
SOE
Sequence Of Events
ST
Structured Text. A programming language for PLCs.
STP
Spanning Tree Protocol. An alternative to MRP (but slower)
STX
Structured Text. See ST.
TACK
Transport Acknowledge. Used for alarm transmission.
TED
Topology and Engineering Discovery
TIA
Totally Integrated Automation. An automation portal (engineering tool) by Siemens.
TIAP
See TIA.
TCI
Tool Calling Interface (The engineering tool can call specialized device-related tools)
TCP
Transmission Control Protocol, used on top of IP.
TLV
Type-Length-Value. A data structure in an LLDP Ethernet frame.
TPID
Tag protocol identifier, for VLAN.
TSDU
?
TSN
Time-Sensitive Networking
TTL
Time to live. A field in an LLDP Ethernet frame.
UC
Unicast (as opposed to multicast)
UDP
User Datagram Protocol, used on top of IP.
USI
User Structure Identifier (unit16) Describes alarm payloads.
UUID
Universally Unique Identifier. A 128-bit number for uniquely identifying information.
VLAN
Virtual LAN
VID
VLAN identifier
WLAN
Wireless LAN
XML
eXtended Markup Language

View File

@ -0,0 +1,389 @@
Additional Linux Details
========================
The tutorial gives most information required for getting started on a Linux
machine. Some additional information is given here.
Detailed dependency installation info
-------------------------------------
You need to have cmake version at least 3.14 installed. It is available in
Ubuntu 19.04 and later.
To install on Ubuntu 16.04 or 18.04, follow the instructions here:
https://www.kitware.com/ubuntu-cmake-repository-now-available/
It is also possible to install it via snap::
sudo snap install cmake --classic
When using it, you might need to replace ``cmake`` with ``snap run cmake``.
If uninstalling the default ``cmake``, the snap-installed version should be
used by default after reboot.
Optional dependencies (for development and documentation of the p-net stack)::
sudo apt install -y \
clang-tools \
clang-format-10 \
doxygen \
graphviz
Run tests and generate documentation
------------------------------------
Run tests (if you told cmake to configure it)::
cmake --build build --target check
Run a single test file::
build/pf_test --gtest_filter=CmrpcTest.CmrpcConnectReleaseTest
Create Doxygen documentation::
cmake --build build --target docs
The Doxygen documentation ends up in ``build/html/index.html``
See the "Writing documentation" page if you would like to install
the toolchain to build the entire Sphinx documentation.
The clang static analyzer can also be used if installed. Create a new
build directory by running::
scan-build cmake -B build.scan-build -S p-net
scan-build cmake --build build
Setting Linux ephemeral port range
----------------------------------
This is the range of random source ports used when sending UDP messages.
Profinet requires that the UDP source port should be >= 0xC000, which is 49152
in decimal numbers.
To change the ephemeral port range::
echo "49152 60999" > /proc/sys/net/ipv4/ip_local_port_range
This should typically be done at system start up.
File size and memory usage on Linux
-----------------------------------
The resulting file size of the sample application binary is heavily dependent
on the compile time options, for example whether to include debug information.
In this example we use::
BUILD_SHARED_LIBS ON
CMAKE_BUILD_TYPE Release
LOG_LEVEL Warning
PNET_MAX_AR 2
PNET_MAX_SLOTS 5
PNET_MAX_SUBSLOTS 3
To get an estimate of the binary size, partially link it (use release, without
standard libraries)::
p-net/build$ make all
p-net/build$ /usr/bin/cc -O3 -DNDEBUG CMakeFiles/pn_dev.dir/samples/pn_dev/sampleapp_common.o CMakeFiles/pn_dev.dir/src/ports/linux/sampleapp_main.o -o pn_dev libprofinet.a -nostdlib -r
Resulting size::
p-net/build$ size pn_dev
text data bss dec hex filename
244481 72 8 244561 3bb51 pn_dev
See https://linux.die.net/man/1/size for information on how to use the command.
Also the rt-kernel page in this documentation has some description on how to
interpret the output.
The size of the p-net stack can be estimated from the size of libprofinet,
built with the options given above::
p-net/build$ size libprofinet.so
text data bss dec hex filename
230888 3304 8 234200 392d8 libprofinet.so
An estimate of the p-net RAM usage can be made from the size of the ``pnet_t`` struct.
The sample application has a command line option to show this value, for the used
compile time options (for example the maximum number of modules allowed).
Debug intermittent segmentation faults during tests on Linux
------------------------------------------------------------
Enable core dumps::
ulimit -c unlimited
Run a test case until the problem occurs (in the build directory)::
while ./pf_test --gtest_filter=DiagTest.DiagRunTest; do :; done
Study the resulting core::
gdb pf_test core
SNMP (Conformance class B)
--------------------------
Conformance class B requires SNMP support. Linux uses net-snmp as agent,
see http://www.net-snmp.org/. The name of the SNMP agent binary is ``snmpd``.
P-Net for Linux implements a Net-SNMP subagent that handles the Profinet
mandatory MIB:s. Also
see :ref:`network-topology-detection` for information regarding SNMP.
Enable SNMP by setting ``PNET_OPTION_SNMP`` to ``ON`` in the p-net compilation
options. Net-SNMP also needs to
be installed. On Ubuntu you can install the required packages using::
sudo apt install -y snmpd libsnmp-dev
To show the installed version of ``snmpd``, use::
snmpd -v
Change snmpd command line arguments
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The p-net SNMP subagent will handle the system objects, so the default
SNMP "system" module should be disabled by adding the snmpd argument
``-I -system_mib``. Otherwise the subagent will complain about
"registering pdu failed" at startup. If you use systemd init system (for
example on Ubuntu Linux) you should change
``/lib/systemd/system/snmpd.service`` to read::
[Unit]
Description=Simple Network Management Protocol (SNMP) Daemon.
After=network.target
ConditionPathExists=/etc/snmp/snmpd.conf
[Service]
Type=simple
ExecStartPre=/bin/mkdir -p /var/run/agentx
ExecStart=/usr/sbin/snmpd -LOw -u Debian-snmp -g Debian-snmp -I -system_mib,smux,mteTrigger,mteTriggerConf -f -p /run/snmpd.pid
ExecReload=/bin/kill -HUP $MAINPID
[Install]
WantedBy=multi-user.target
To see the status of the service::
systemctl status snmpd.service
journalctl -u snmpd.service -f
To restart the service after modification::
sudo systemctl daemon-reload
sudo systemctl restart snmpd.service
The command line arguments ``-u`` and ``-g`` are for the user id and group id
of the snmpd process will use after its initial startup.
They are not necessary to use snmpd.
However snmpd must be started with permissions to open relevant sockets,
typically root permissions.
If you use "system V init" instead of systemd, then snmpd is typically started
by a script file named ``/etc/init.d/snmpd``. Change the snmpd command line
arguments in the file, typically via ``SNMPDOPTS``. Stop and start the
service with::
sudo /etc/init.d/snmpd stop
sudo /etc/init.d/snmpd start
Configuration file for snmpd
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The file ``snmpd.conf`` controls access to the snmp agent. It should be
set to listen on all interfaces and allow read-write access to the
Profinet MIB:s. On Ubuntu Linux you should change
``/etc/snmp/snmpd.conf`` to read::
master agentx
agentaddress udp:161
view systemonly included .1.3.6.1.2.1.1
view systemonly included .1.3.6.1.2.1.2.2
view systemonly included .1.0.8802.1.1.2
rocommunity public default -V systemonly
rwcommunity private default -V systemonly
If your Linux distribution does give a long description for ``ifDesc`` you can
override it by adding a line to the ``snmpd.conf`` file. Adapt the interface
index (last digit in OID) and the interface name::
override 1.3.6.1.2.1.2.2.1.2.3 octet_str "enp0s31f6"
See :ref:`network-topology-detection` for more details on SNMP and how to
verify the SNMP communication to the p-net stack.
Start your application after snmpd
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You probably would like your application to wait for the ``snmpd`` application to
be up and running. If you use systemd as init system, modify this line in
the ``[Unit]`` part of your ``.service`` file::
After=network.target snmpd.service
You might also need to add this to the ``[Service]`` part of your
``.service`` file::
ExecStartPre=/usr/bin/sleep 0.3
If the p-net application fails to connect to snmpd, a message "Warning: Failed
to connect to the agentx master agent" will be written to the p-net sample
app log file every 15 seconds.
Debugging snmpd settings
^^^^^^^^^^^^^^^^^^^^^^^^
The command line argument ``-LOw`` for snmpd sets the snmpd logging to
standard out with log level "warning".
To change to log level debug, use ``-LOd``. You should also use the ``-D`` flag
to select which debug messages you are interested in. Use the ``-Dagentx``
command line argument to debug the agentx communication between the snmpd and
the subagent in the p-net stack. A log example when asked for a single OID::
Connection from UDP: [192.168.0.30]:43833->[192.168.0.50]:161
agentx/master: agentx master handler starting, mode = 0xa0
agentx/master: request for variable (iso.3.6.1.2.1.1.4.0)
agentx/master: sending pdu (req=0x8,trans=0x7,sess=0x6)
agentx_build: packet built okay
agentx/master: got response errstat=0, (req=0x8,trans=0x7,sess=0x6)
agentx/master: agentx_got_response() beginning...
agentx/master: handle_agentx_response: processing: iso.3.6.1.2.1.1.4.0
agentx/master: handle_agentx_response() finishing...
Note that there might be a warning message "pcilib: Cannot open /proc/bus/pci"
in the snmpd log if you specify that it should use all interfaces.
That is because it will look also for (possibly non-existing) PCI interfaces.
To trouble-shoot snmpd issues, verify that no other snmpd instances are running::
ps -ef | grep snmpd
and verify that no other process is using UDP port 161::
sudo lsof -i udp -n -P
snmpd in a Yocto build
----------------------
In an embedded Linux Yocto build, you would include the ``snmpd`` daemon by
using the ``net-snmp`` recipe.
Persistent logs
---------------
To make the journalctl logs persistent between restarts::
sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal
Remove all contents of the journalctl logs::
sudo journalctl --rotate
sudo journalctl --vacuum-time=1s
The configurations for journalctl are located in ``/etc/systemd/journald.conf``.
If you do experiments with frequent reboots, it might be useful to change some
values::
SyncIntervalSec=10s
MaxRetentionSec=4h
Boot time optimization
----------------------
To improve the startup time of your Linux device, it is useful to study what
is delaying the start. If you use the "systemd" init system, you can use these
commands to analyze the startup::
systemd-analyze
systemd-analyze blame
systemd-analyze critical-chain pnet-sampleapp.service
To decrease the startup time, disable services you don't use. On a Raspberry Pi
it might be for example::
sudo systemctl disable triggerhappy triggerhappy.socket apt-daily.timer apt-daily-upgrade.timer logrotate.timer rpi-display-backlight lightdm bluetooth hciuart rsync cups cups-browsed alsa-state avahi-daemon
Other applications that you might disable for experimentation::
sudo systemctl disable snapd snapd.socket wpa_supplicant systemd-timesyncd dhcpcd
Debug the sample application on a Linux Laptop
----------------------------------------------
It can be convenient to be able to run the sample application and the p-net
stack in a debugger tool. It is easy using gdb and the Visual Studio Code
editor.
First make sure you can run the application from a terminal on your Linux
laptop.
Next step is to be able to run it from the terminal within Visual Studio Code.
In case of compilation error messages, you can click on the code line given
in the terminal (within Visual Studio Code) and the corresponding file will
be opened. Hold the CTRL key while clicking on the line.
To use debug features while running (for example breakpoints) you need to adapt
the settings file for Visual Studio Code. Click the "Run and Debug" icon
in the left side tool bar. Then click "Create a launch.json file". In the
"Select environment", use "C++ (GDB/LLDB)".
Modify the ``launch.json`` file to point at the correct executable, working
directory and to use correct command line arguments.
If you need to run the application with root permissions, you need to add a path in the
``"miDebuggerPath"`` field. It should point to a text file typically named
``gdb``, with this content::
pkexec /usr/bin/gdb "$@"
Put the ``gdb`` file for example in the ``.vscode`` subdirectory within
the ``p-net`` directory. Set the executable flag::
chmod +x gdb
An example of a ``launch.json`` file::
{
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/pn_dev",
"args": ["-vv", "-i", "enp0s31f6"],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/build/",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"miDebuggerPath": "${workspaceFolder}/.vscode/gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}
The given ``"args"`` command line arguments in the example is for
increasing the verbosity level and to set the Ethernet interface name.
Adapt those and also paths to your particular setup.
Use the ``CMAKE_BUILD_TYPE`` setting as ``Debug`` when running the executable
via the debugger.
Start the debugging by clicking on the small green "Run" icon on the
"Run and Debug" page. It will stop at any breakpoint. Set a breakpoint in
any file by clicking on a line to the left of the line number.

View File

@ -0,0 +1,182 @@
API documentation
=================
Here is a brief overview given for some of the most important functions,
structs etc in the public API for the p-net Profinet stack.
For detailed documentation, read the ``include/pnet_api.h`` header file. It
also contains error code definitions etc.
You can also build the Doxygen documentation for all functions by following
the instructions given on the page "Additional Linux Details".
Study samples/pn_dev to get more information on how to use the API.
General
-------
All functions return either ``0`` (zero) for a successful call or ``-1`` for an
unsuccessful call.
Initialization and handling
---------------------------
.. doxygenfunction:: pnet_init
.. doxygenfunction:: pnet_application_ready
.. doxygenfunction:: pnet_handle_periodic
.. doxygenfunction:: pnet_get_ar_error_codes
.. doxygenfunction:: pnet_ar_abort
.. doxygenfunction:: pnet_factory_reset
.. doxygenfunction:: pnet_show
Plug and pull modules/submodules
--------------------------------
.. doxygenfunction:: pnet_plug_module
.. doxygenfunction:: pnet_plug_submodule
.. doxygenfunction:: pnet_pull_module
.. doxygenfunction:: pnet_pull_submodule
Periodic data
-------------
.. doxygenfunction:: pnet_input_set_data_and_iops
.. doxygenfunction:: pnet_input_get_iocs
.. doxygenfunction:: pnet_output_get_data_and_iops
.. doxygenfunction:: pnet_output_set_iocs
.. doxygenfunction:: pnet_set_provider_state
Redundant state etc
-------------------
.. doxygenfunction:: pnet_set_primary_state
.. doxygenfunction:: pnet_set_redundancy_state
Alarms and diagnostics
----------------------
.. doxygenfunction:: pnet_create_log_book_entry
.. doxygenfunction:: pnet_alarm_send_process_alarm
.. doxygenfunction:: pnet_alarm_send_ack
.. doxygenfunction:: pnet_diag_std_add
.. doxygenfunction:: pnet_diag_std_update
.. doxygenfunction:: pnet_diag_std_remove
.. doxygenfunction:: pnet_diag_usi_add
.. doxygenfunction:: pnet_diag_usi_update
.. doxygenfunction:: pnet_diag_usi_remove
Low-level diagnostic functions
------------------------------
These are used internally in the functions above for handling diagnosis in
standard or in USI format. However they can be useful in situations where
detailed control is required.
.. doxygenfunction:: pnet_diag_add
.. doxygenfunction:: pnet_diag_update
.. doxygenfunction:: pnet_diag_remove
Callbacks
---------
The application should define call-back functions which are called by
the stack when specific events occurs within the stack.
Note that most of these functions are mandatory in the sense that they must
exist and return ``0`` for a functioning stack. Some functions are required
to perform specific functionality.
.. doxygentypedef:: pnet_connect_ind
.. doxygentypedef:: pnet_release_ind
.. doxygentypedef:: pnet_dcontrol_ind
.. doxygentypedef:: pnet_ccontrol_cnf
.. doxygentypedef:: pnet_state_ind
.. doxygentypedef:: pnet_reset_ind
.. doxygentypedef:: pnet_signal_led_ind
.. doxygentypedef:: pnet_read_ind
.. doxygentypedef:: pnet_write_ind
.. doxygentypedef:: pnet_exp_module_ind
.. doxygentypedef:: pnet_exp_submodule_ind
.. doxygentypedef:: pnet_new_data_status_ind
.. doxygentypedef:: pnet_alarm_ind
.. doxygentypedef:: pnet_alarm_cnf
.. doxygentypedef:: pnet_alarm_ack_cnf
Selected enums
--------------
.. doxygenenum:: pnet_event_values
.. doxygenenum:: pnet_ioxs_values
.. doxygenenum:: pnet_submodule_dir
.. doxygenenum:: pnet_control_command
.. doxygenenum:: pnet_data_status_bits
.. doxygenenum:: pnet_diag_ch_prop_type_values
.. doxygenenum:: pnet_diag_ch_prop_dir_values
.. doxygenenum:: pnet_diag_ch_prop_maint_values
.. doxygenenum:: pnet_diag_ch_group_values
.. doxygenenum:: pnet_im_supported_values
.. doxygenenum:: pnal_eth_mau
Selected structs
----------------
Network and device configuration.
Configuration of the stack is performed by transferring a structure
in the call to ``pnet_init()``.
Along with the configuration the initial (default) values of the
I&M data records are conveyed as well as the values used for
sending LLDP frames.
.. doxygenstruct:: pnet_im_0
:members:
:undoc-members:
.. doxygenstruct:: pnet_im_1
:members:
:undoc-members:
.. doxygenstruct:: pnet_im_2
:members:
:undoc-members:
.. doxygenstruct:: pnet_im_3
:members:
:undoc-members:
.. doxygenstruct:: pnet_im_4
:members:
:undoc-members:
.. doxygenstruct:: pnet_cfg_device_id
:members:
:undoc-members:
.. doxygenstruct:: pnet_if_cfg
:members:
:undoc-members:
.. doxygenstruct:: pnet_port_cfg
:members:
:undoc-members:
.. doxygenstruct:: pnet_ip_cfg
:members:
:undoc-members:
.. doxygenstruct:: pnet_cfg
:members:
:undoc-members:
.. doxygenstruct:: pnet_alarm_spec
:members:
:undoc-members:
.. doxygenstruct:: pnet_alarm_argument
:members:
:undoc-members:
.. doxygenstruct:: pnet_diag_source
:members:
:undoc-members:

View File

@ -0,0 +1,140 @@
Creating applications and porting to new hardware
=================================================
Create your own application
---------------------------
If you prefer not to implement some of the callbacks, set the corresponding
fields in the configuration struct to NULL instead of a function pointer.
See the API documentation on which callbacks that are optional.
To read output data from the PLC, use ``pnet_output_get_data_and_iops()``.
To write data to the PLC (which is input data in Profinet terminology), use
``pnet_input_set_data_and_iops()``.
Adapt to other hardware
-----------------------
You need to adapt the implementation to your specific hardware.
This includes for example:
* For Linux, adjust the networking and LED scripts in PNAL
* Implement the callback to control the "Signal" LED.
Required features of the hardware or operating system
-----------------------------------------------------
* It should be possible to send and receive raw (layer 2) Ethernet frames
* It should be possible to store data between runs, in a file system or some nonvolatile memory
* For conformance class B, there needs to be an SNMP implementation that
the p-net stack can use.
General requirements
--------------------
It should be possible to do a factory reset without an engineering tool. For
example a hardware switch can be used. Routing of Ethernet frames must not be
affected by a factory reset.
A Profinet signal LED must be visible on the IO-Device. A callback is available
from p-net to control the signal LED, so you can implement your board specific
hardware.
Connectors and cables should fulfill the requirements in "PROFINET Cabling and
Interconnection Technology". For recommended diagnosis indicators (for example
LEDs) see "Diagnosis for PROFINET".
Consider the housing (often IP65) and grounding of the device.
In general the MAC address of the device should be visible when installed.
Hardware requirements for the Ethernet ports:
* At least 100 Mbit/s full duplex
* Standard and crossover cables should be handled
* Auto polarity
* Auto negotiation (should be possible to turn off for fast startup)
Interacting with other parts of your application software
---------------------------------------------------------
When the PLC sends a "factory reset" command, not only the p-net stack but
also the rest of your application should be reset to factory settings.
If there is a method to do a factory reset of your application, it should also
reset the p-net stack.
It is possible to set the IP-address, netmask and gateway address via Profinet
from a PLC. Consider how this affects other aspects of your application.
Serial number, software version, model name etc should be available for the
p-net stack to use.
Minimum cycle time for your application and hardware
----------------------------------------------------
At PLC configuration, there are two adjustable parameters regarding the
cyclic data timing for an IO-device. One basically controls the
frame-to-frame transmission interval (given as a multiple of the cycle time),
and the other one controls the PLC watchdog timeout setting.
The watchdog triggers an alarm when a number of consecutive cyclic data
frames from the IO-device are missing.
During conformance testing, the PLC watchdog timeout setting is 3 x
frame-to-frame interval times, ie the PLC will trigger an alarm if three
consecutive cyclic data frames are missing. Thus the smallest allowed
frame-to-frame interval given in the GSDML file must take the smallest
useful PLC watchdog timeout setting into account.
There can be different factors limiting the ability to send Profinet frames at
a regular pace:
* The initial work at Profinet communication startup should not cause
delays in the cyclic data transmission once started.
* The hardware needs to keep up with the steady-state sending and receiving
of Profinet cyclic data.
* Other work done by the Profinet application, for example work done in
callbacks, might slow down the communication.
* Operating system jitter, which basically is that the operating system is
busy doing other work, will cause frame delays.
* Using a network interface connected via USB can cause additional frame
delays.
For real-time operating systems (RTOS) for example rt-kernel, the
frame-to-frame transmission will have little jitter.
For non-real-time operating systems, for example Linux, the limiting factor
is typically the jitter caused by the operating system.
See the page on Linux timing in this documentation for ideas on improving
Linux real-time properties.
Measure Profinet frame transmission jitter
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Set the PLC watchdog time to 100 x frame interval, to avoid any alarms
from the PLC.
Use Wireshark to record cyclic data transmission for a few minutes.
Plot the maximum frame-to-frame transmission interval (see the page on
capturing Ethernet packets in this documentation).
This will show which PLC watchdog setting you need in order not to trigger
"missing frame" alarms.
For example if the transmission interval is 1 ms but there sometimes is a 10 ms
delay between the frames, then you need to use the next standard PLC timeout
value which is 12 ms. In order to handle that with a watchdog setting of 3 x
frame interval, you need to use a minimum cycle time of 4 ms (as
will be stated in your GSDML file).
Example measurement results
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Measurements with p-net on a XMC4800 board running rt-kernel shows that the
hardware is able to send frames with a periodicity of 1 ms with little jitter.
The liming factor is that the PLC watchdog timeout value needs to be 12 ms,
due to the time it takes for the first cyclic data frame to be sent from
the IO-device.
A GSDML file should state that the smallest allowed cycle time is 4 ms.
Similar measurements on a Raspberry Pi 3B+ show that it is possible to send
frames with a cycle time of 1 ms. However there is sometimes a frame-to-frame
interval of up to 40 ms, resulting in that a GSDML file should state that the
minimum frame interval is 16 ms.

View File

@ -0,0 +1,263 @@
Capturing and analyzing Ethernet packets
========================================
In order to understand the Profinet traffic, it is useful to capture network
packets and analyze them in a tool like Wireshark. You typically run
Wireshark on your laptop, but in order to capture packets on for example
a Raspberry Pi you can use the program tcpdump (described below).
Install Wireshark on Linux
--------------------------
To install a relatively new Wireshark version on Ubuntu::
sudo add-apt-repository ppa:wireshark-dev/stable
sudo apt update
sudo apt -y install wireshark
In order to be able to capture packets you need to add yourself to the
``wireshark`` user group, or run the program as root::
sudo wireshark
For details on how to add yourself to the ``wireshark`` user group, see
https://linuxhint.com/install_wireshark_ubuntu/
Wireshark is also available on Windows. Download it from https://www.wireshark.org/
Install Wireshark on Windows
----------------------------
Download Wireshark from https://www.wireshark.org/download.html
Filtering frames in Wireshark based on protocol
-----------------------------------------------
To show only frames with specific protocols, enter this into the "display
filter" text box::
lldp or snmp or pn_dcp or dcerpc or pn_io or syslog
To hide frames with specific protocols::
not (pn_ptcp or arp or mdns or llmnr or ssdp or dhcp or dhcpv6 or icmpv6 or igmp or nbns or browser or ipv6)
+--------------------------+----------------------------------------------------+
| Filter expression | Wireshark protocol names |
+==========================+====================================================+
| arp | ARP |
+--------------------------+----------------------------------------------------+
| epm | EPM |
+--------------------------+----------------------------------------------------+
| lldp | LLDP |
+--------------------------+----------------------------------------------------+
| pn_dcp | PN-DCP |
+--------------------------+----------------------------------------------------+
| pn_io | PNIO and PNIO_PS and PNIO-AL |
+--------------------------+----------------------------------------------------+
| pn_io_controller | PNIO-CM (application ready) |
+--------------------------+----------------------------------------------------+
| pn_io_device | PNIO-CM (connect, read, read implicit, write, |
| | param end, release) |
+--------------------------+----------------------------------------------------+
| dcerpc | PNIO-CM and EPM |
+--------------------------+----------------------------------------------------+
| pn_ptcp | PN-PTCP |
+--------------------------+----------------------------------------------------+
| snmp | SNMP |
+--------------------------+----------------------------------------------------+
| syslog | Syslog |
+--------------------------+----------------------------------------------------+
Filter frames in Wireshark based on frame contents
--------------------------------------------------
To show Profinet read and write commands for a specific index::
pn_io.index == 0x8071
To show Profinet read and write commands for a specific subslot::
pn_io.subslot_nr == 0x8001
To look for a specific OID in SNMP messages::
snmp.name == 1.3.6.1.2.1.1.3.0
Select frames with a specific DCE RPC Activity UUID::
dcerpc.dg_act_id == 38353218-0002-1010-ae64-ac6417366047
Coloring rules in Wireshark
---------------------------
To modify the coloring rules in Wireshark, use the menu View > "Coloring rules".
Deselect the rules you don't need.
Create a new rule by clicking the ``+`` symbol in the lower left corner.
Enter the filter expression and the name of the new rule. Enable it using the
checkbox. With the rule still marked, click the "Background" button to modify
the background color.
+------------+----------------------------------------------------------+
| Rule name | Filter |
+============+==========================================================+
| LLDP | lldp |
+------------+----------------------------------------------------------+
| SNMP | snmp |
+------------+----------------------------------------------------------+
| Syslog | syslog |
+------------+----------------------------------------------------------+
| DCP | pn_dcp |
+------------+----------------------------------------------------------+
| DCERPC | dcerpc |
+------------+----------------------------------------------------------+
| PNIO ALARM | ``pn_rt.frame_id == 0xfc01 || pn_rt.frame_id == 0xfe01`` |
+------------+----------------------------------------------------------+
| PNIO | pn_io |
+------------+----------------------------------------------------------+
Move the most specific lines to the top.
Parsing Profinet cyclic data with Wireshark
-------------------------------------------
It is possible to load a GSDML file into recent versions of Wireshark, for
parsing the cyclic data.
In the Wireshark menu, select Edit > Preferences > Protocols > PNIO.
Enter the directory where you have your GSDML file.
For this functionality to work, the Wireshark capture must include the start-up
sequence. When a packet is interpreted according to a GSDML file, the name of
the GSDML file is displayed in the detail view of the packet.
Diagnosis data in Wireshark
---------------------------
Note that Wireshark can not yet decode QualifiedChannelDiagnosisData.
See the ``dissect_AlarmUserStructure ()`` function in
the ``packet-dcerpc-pn-io.c`` file in the source code for Wireshark.
Show wall time in Wireshark
---------------------------
In order to correlate captured Wireshark frames to other logs, it can be
convenient to display wall time in the time column in Wireshark.
Right-click the column header, and select "Edit Column".
For "Type" use "Time (format as specified)". Click OK.
In the main menu View > "Time Format Display" select "Time of day".
Show transmission time periodicity using Wireshark
--------------------------------------------------
In order to study the periodicity of sent frames, in the filter heading on the
main screen select the MAC address of the p-net IO-device, for example::
eth.src == 54:ee:75:ff:95:a6 and pn_io
In the column header, right-click and select "Column Preferences ...". Press "+"
to add a new column. Change "Title" to "Delta displayed" and "Type" to
"Delta time displayed".
Plot transmission time periodicity using Wireshark
--------------------------------------------------
To plot the periodicity of sent frames, you first need to filter the displayed
frames in the main Wireshark window, as described above.
Then use the menu "Statistics" -> "I/O Graph".
* Display filter: ``eth.src == 54:ee:75:ff:95:a6 and pn_io``
* Y Axis: AVG(Y Field)
* Y Field: ``frame.time_delta_displayed``
* SMA (sample moving average) Period: None
* Interval: 10 or 100 ms.
Adapt the MAC address to your p-net IO-device.
You need to zoom the Y-axis to an interesting range, maybe 0-10 ms.
It is also interesting to add a line "MAX(Y Field)" and a line "MIN(Y Field)"
in the same graph as the first line. See below for an example where the
average frame interval is 1 ms.
.. image:: illustrations/periodicity.png
The lines should be interpreted as the average, minimum and maximum
packet-to-packet times during the interval (for example 100 ms).
Plot Profinet cycle counter
---------------------------
The cycle counter should increase between each frame. You can use Wireshark to verify that.
Use the menu “Statistics” -> “I/O Graph”.
* Display filter: ``eth.src == 54:ee:75:ff:95:a6``
* Style: Dot
* Y Axis: AVG(Y Field)
* Y Field: ``pn_rt.cycle_counter``
* SMA (sample moving average) Period: None
* Interval: 1 ms
Adapt the MAC address to your p-net IO-device.
Zoom to the relevant time period, and y-axis range of approx 0 to 70000.
The frame cycle counter increases up to max 65535 before it wraps to 0.
It is important that the samples are evenly distributed in time (on the
horizontal axis). See the image below (captured during a trouble-shooting
session).
.. image:: illustrations/Cyclecounter.png
Plot network load
-----------------
Use the menu “Statistics” -> “I/O Graph”.
* Display filter: (none)
* Style: Line
* Y Axis: Bits
* Y Field: (none)
* SMA (sample moving average) Period: None
* Interval: 100 ms or 1 s
See below for an example captured during a "Security Level 1" network load test,
where the network load is close to 100 Mbit/s (1E8 bits/s).
.. image:: illustrations/NetworkLoad.png
tcpdump
-------
When running on an embedded Linux board, it can be convenient to run without
a graphical user interface. To capture packets for later display in Wireshark,
use the tool ``tcpdump``.
Install it, for example like::
sudo apt-get install tcpdump
Run it with::
sudo tcpdump -i enp0s31f6 -n -w outputfile.pcap
Use the ``-i`` argument to specify Ethernet interface.
Transfer the resulting file to your laptop, where you can open it in Wireshark.
Note that Wireshark and tcpdump modifies the network interface settings on Linux.
The promiscuous bit will be enabled, as seen when looking at
``/sys/class/net/eth0/flags``. It will not be visible via ``ifconfig`` though.
Hardware for capturing packets on network
-----------------------------------------
Profinet is a point-to-point protocol. If the Profinet controller or device
software is running on your machine, you can use Wireshark (or tcpdump)
directly to capture the packets.
If you would like to capture packets between other units (Profinet
controllers/devices) you need special hardware to do the capturing. A network
tap is a network switch with packet monitoring to send a copy of each packet
to another Ethernet connector. Connect the tap on the network link between the
IO-device and IO-controller. Connect the mirroring port to the machine where
you run Wireshark or tcpdump.

View File

@ -0,0 +1,3 @@
ba
fo
inout

View File

@ -0,0 +1,224 @@
Codesys Details
===============
This page contains additional information on how to use the Codesys soft PLC.
Adjust PLC timing settings
--------------------------
It is possible to adjust the cycle time that the IO-controller (PLC) is using
for cyclic data communication with an IO-device.
In the left menu, double-click the "P_Net_Sample_App", and open the "General"
tab. The "Communication" section shows the send clock in milliseconds, as read
from the GSDML file. By using the "Reduction ratio" you can slow down the
communication, by multiplying the cycle time by the factor given in the
dropdown.
It is also possible to increase the watchdog time, after which the PLC will set
an alarm for missing incoming cyclic data. The watchdog will also shut down the
communication, and trigger a subsequent restart of communication.
In case of problems, increase the reduction ratio (and timeout) value a lot,
and then gradually reduce it to find the smallest usable value.
Displaying errors
-----------------
Click on the IO-device in the tree structure in the left part of the screen.
Use the "Log" tab to display errors.
Connection status
-----------------
Go to the Profinet IO-device page, and see the "PNIO IEC objects" tab. Expand
the topmost row. The states of these boolean fields are shown:
* xRunning: Periodic data is sent
* xBusy: The controller is trying to connect to the IO-device
* xError: Failure to connect to the IO-device
If there is no connection at all to the IO-device, the state will shift to
xBusy from xError once every 5 seconds.
Change IP address of IO-device
------------------------------
Change the IP address by double-clicking the "P_Net_Sample_App" node
in the left menu, using the "General" tab. Set the IP-address to new value.
The IO-controller will send the new IP address in a "DCP Set" message to the
IO-device having the given station name. Then it will use ARP messages to
the IO-device to find its MAC address, and to detect IP address collisions.
Scan for devices, assign IP address, reset devices and change station name
--------------------------------------------------------------------------
Scan
^^^^
In the left side menu, right-click the PN_Controller and select "Scan for
devices". The running IO-devices will show up, and it is possible to see which
modules are plugged into which slot.
Note that the texts in the columns "Device name" and "Device type" are from the
available GSDML files in Codesys. If a corresponding GSDML file not has been
loaded, the text "Attention: The device was not found in the repository" is
shown together with the Vendor-ID and Product-ID.
This is implemented in Codesys by sending the "Ident request, all" DCP
message from the IO-controller.
It works also if there are no IO-devices loaded in the left side hierarchy menu.
An IO-device will respond with the "Ident OK" DCP message. Then the IO-controller
will do a "Read implicit request" for "APIData", on which the IO device
responds with the APIs it supports. A similar request for
"RealIdentificationData for one API" is done by the IO-controller, on which the
IO-device responds which modules (and submodules) are plugged into which slots
(and subslots).
Factory reset
^^^^^^^^^^^^^
To factory reset a device, select it in the list of scanned devices and click
the "reset" button.
At a factory reset, the IO-controller sends a "Set request" DCP message
with suboption "Reset factory settings". After sending a response, the
IO-device will do the factory reset and also send a LLDP message with the
new values. Then the IO-controller sends a "Ident request, all", to which
the IO-device responds.
Set name and IP
^^^^^^^^^^^^^^^
To modify the station name or IP address, change the corresponding fields
in the list of scanned devices, and the click "Set name and IP".
The IO-controller sends a "Set request" DCP message
with suboptions "Name of station" and "IP parameter". After sending a
response, the IO-device will change IP address and station name. It will
also send a LLDP message with the new values. Then the
IO-controller sends a "Ident request, all", to which the IO-device responds.
Flash LED
^^^^^^^^^
There is functionality to flash a LED on an IO-device. Select your device in
the list of scanned devices, and click the "Blink LED" button. The button
remains activated until you click it again.
LED-blinking is done by sending the "Set request" DCP message with suboption
"Signal" once every 5 seconds as long as the corresponding button is activated.
Identification & Maintenance (I&M) data
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In order the read Identification & Maintenance (I&M) data, the device needs to
be present as an IO-device connected to the IO-controller in the left side menu.
Select the device in the list of scanned devices, and click the "I&M" button.
Reading I&M data is done by the IO-controller by sending four "Read implicit"
request DCP messages, one for each of I&M0 to I&M3.
When writing I&M data from Codesys, it will send a connect, write and release.
Writing and reading IO-device parameters
----------------------------------------
The parameters for different submodules are written as part of normal startup.
To adjust the values sent at startup, you must first be offline.
Then double-click the DIO_8xLogicLevel device in the left menu. Use the
General tab, and the Settings part of the page. Click the corresponding
value in the table, and adjust it.
While offline (with regards to the PLC, it is possible to manually trigger
parameter reading and writing. Right-click the parameter you would like
to send, and select "Write to device". It is also possible to read a
parameter similarly.
To read all or write all parameters, use the corresponding buttons.
When clicking the "Write All Values" icon, one write request is sent per
parameter.
Forcing cyclic data values
--------------------------
To force an output value from the PLC, open the PLC_PRG window while online.
In the table on the top of the page, on the line you would like to force click the "Prepared value"
until it shows "TRUE". Use the line "out_pin_LED" if running the sample application.
Right-click on the line, and select "Force all values ...". The new value is sent from the PLC to the IO-device.
A red `F` symbol is shown.
To stop forcing the value, right-click it and select "Unforce all values ...".
Displaying alarms sent from IO-device
-------------------------------------
Incoming process alarms and diagnosis alarms appear on multiple places in
the Codesys desktop application.
* Codesys Raspberry Pi: The "Log" tab show process and diagnosis alarms.
* PN_Controller: The "Log" tab show process and diagnosis alarms.
* IO-device: The "Log" tab show process and diagnosis alarms. The
“Status” tab shows alarms related to built-in (DAP) modules.
* Plugged module in IO-device: Process and diagnosis alarms are displayed on
the “Status” tab.
Setting output producer status (IOPS)
-------------------------------------
Normally Codesys will set the Output PS to GOOD (0x80 = 128) when running.
Clicking the "Output PS" checkbox on the "IOxS" tab on the Profinet IO-device
sets the value to BAD (0).
Enabling checking of peer stationname and port ID
-------------------------------------------------
It is possible to have the IO-device verify that it is connected to the
correct neighbour (peer) by checking its station name and port ID (as sent
in LLDP frames by the neighbour).
Double-click the “P_Net_Sample_App” node in the left menu. On the "options"
tab in the resulting window, use the fields "Peer station" and "port". It
seems only possible to select station names from other devices or controllers
already available in the project.
During startup the PLC will send the given values to the IO-Device via a
write command. If the correct neighbour is not present, an alarm will be sent
by the IO-device to the PLC.
Writing PLC programs
--------------------
Documentation of available function blocks is found at
https://help.codesys.com/webapp/_pnio_f_profinet_io_configuration;product=core_ProfinetIO_Configuration_Editor
Using the Echo module
---------------------
The echo module will receive an integer and a float from the PLC, and multiply them with a constant
value before sending them back to the PLC. The multiplier is module parameter, and can be adjusted
at startup. The integer is an unsigned 32 bit integer, and the float is a single precision float
(32 bits).
To test it, unplug any existing modules, and plug one Echo module into slot 1.
Enter this program::
PROGRAM PLC_PRG
VAR
out_echo_int: UDINT;
out_echo_float: REAL;
in_echo_int: UDINT;
in_echo_float: REAL;
temp_int: UDINT;
temp_float: REAL;
END_VAR
out_echo_int := 1000001;
out_echo_float := 1001.23456;
temp_int := in_echo_int;
temp_float := in_echo_float;
On the Echo_module page, use the "PNIO Module I/O Mapping" to connect the
four program variables to the corresponding channels. Connect "in_echo_float" to
"Input float to controller" etc.
Start the PLC, and go online to follow the values. You can force (described elsewhere)
the ``out_echo_int`` and ``out_echo_float`` values to study how the resulting
input values from the IO-device changes.

View File

@ -0,0 +1,911 @@
Compliance testing of IO-devices
================================
All Profinet products need to be certified. That is done after testing by an
accredited PROFIBUS & PROFINET International test lab (PITL).
The full hardware, communication stack and application software is tested
together. This is also known as certification testing or conformance testing.
Each manufacturer of Profinet equipment needs a Vendor ID, which is obtained
from the PI Certification Office.
The tests checks the GSD file, the hardware interface and the Profinet
behavior. The tests should be performed on a device from series production.
The Profinet equipment also needs to fulfill parts of the IEEE802 standards,
for example uniqueness of MAC addresses. This is not tested by the test lab.
A detailed description of the certification process can be downloaded from
https://www.profibus.com/download/how-to-get-a-certificate-for-a-profinet-device/
See also the general requirements on the page "Creating applications and
porting to new hardware" in this documentation.
Needed for certification:
* EMC test report
* European Union declaration of conformity (for CE marking)
* The user manual must be available.
* A valid GSD file
Tool for pretesting
--------------------
For Profinet members, the "Automated RT tester" tool is available for download.
It is useful for pretesting before a full compliance test.
Profinet devices must fulfill "Security Level 1" with regards to the net
load. This is tested by a separate tool (not the "Automated RT tester").
Installation of Automated RT tester on a Windows PC
---------------------------------------------------
Unzip the downloaded file, and double-click the
"AutomatedRtTester_VERSION_Setup.exe" file.
Verify that you use the latest available version.
Adjust the Ethernet network card on a Windows PC
------------------------------------------------
To use the "Automated RT tester" tool you need to adjust the Ethernet interface
settings of your PC. In the properties for the driver, go to the "Advanced"
tab. For the "Packet priority & VLAN" select "Packet priority & VLAN Disable".
More details are given in the "Product Documentation" document for the tool.
You also need to turn off LLDP protocol for the selected network interface
on the laptop running the "Automated RT tester" tool,
otherwise the test case “Different access ways port-to-port” will fail.
Both Windows and Simatic TIA ("PROFINET IO Protocol (DCP/LLDP)") can have
LLDP implemented.
Adjust the settings of the Ethernet card of your personal computer to use
100 Mbit/s full duplex (otherwise the test case "Different access ways
port-to-port" will fail).
Set the IP address to ``192.168.0.25`` and netmask to ``255.255.255.0``.
Use a separate network for running tests with Advanced RT tester
(avoid running it on a network with unrelated devices).
Supported GSD versions
----------------------
The "Automated RT tester" tool is compatible only with a few versions of GSDML
files. See the document "PN_Versions_for_certifications" included in the
download.
Create a project
----------------
Use the menu File > New > “Device Test Project”. Follow the wizard.
Enter the MAC address of the IO-Device. If you use the Profinet switch
(device "B") you can press the button "Get MAC Address", as the ART-tester
queries the network for the alias ``port-003.b``.
On the “Profinet settings” page, select the GSDML file for the IO-Device.
Populate modules/submodules in slots/subslots by
marking a module in the left column, and then click the -> arrow. To insert a
submodule, mark the relevant submodule in the left column and mark the
appropriate slot in the right column.
To remove from a slot or subslot, mark it (in the right column) and press the
<- arrow.
Adjust settings
---------------
You need to adjust the device MAC address. This is done via the menu
Tools > Options > DUT. Enter the value and press OK.
If the device does not have support for remote change of values, you might
need to adjust the device station name, IP address and subnet mask.
This is done via the menu Tools > Options > Setting. You might need to
click the "Show expert settings".
Run tests
---------
Select the tests (in the left side menu) to run. Failing tests are time
consuming, so start with a single test to verify the communication. Disable all
tests by the left side menu. Press the "Deselect all" icon. Then open "Automated
test cases" > "Standard Setup" > DCP, and enable "DCP - DCP_IDN". Use the menu
Project > Run.
When communication is verified, enable all relevant test cases.
The ART tester tool stores Wireshark files (.pcap files) in the
project directory. See the ``EthernetDump`` subdirectory.
To export test reports as PDF, click the small printer icon in the top
toolbar.
Checklist before starting ART Tester tests. Use correct:
* Cabling
* Software version and compile time options
* Ethernet interface settings for ART Tester laptop
* GSDML file
* Plugged modules in ART Tester
* Make sure no other device uses the IP address ``192.168.0.50``
Additional hardware
-------------------
Some of the test cases requires additional hardware; a Profinet-enabled switch
("Device B") and an IO-controller ("Device A"). Also a remote controlled
power outlet can be used to simplify the tests.
+-------------------------+-----------------------------+------------------------+
| Item | IP address | Description |
+=========================+=============================+========================+
| Device under test (DUT) | 192.168.0.50 | |
+-------------------------+-----------------------------+------------------------+
| ART tester on PC | 192.168.0.25, 192.168.1.143 | |
+-------------------------+-----------------------------+------------------------+
| PLC ("Device A") | 192.168.0.100 | |
+-------------------------+-----------------------------+------------------------+
| Switch (“Device B”) | 192.168.0.99 | |
+-------------------------+-----------------------------+------------------------+
| Neighbour (“Device D”) | 192.168.0.98 | To port 2 of DUT |
+-------------------------+-----------------------------+------------------------+
| Neighbour (“Device E”) | 192.168.0.97 | To highest port of DUT |
+-------------------------+-----------------------------+------------------------+
| Power outlet | 192.168.1.244 | Separate network |
+-------------------------+-----------------------------+------------------------+
Profinet-enabled switch
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Some of the test cases for the Automated RT Tester requires an Profinet-enabled
switch. It is called "Device B" in the test setup documentation.
The test specification of version V 2.41 recommends the use of a
Siemens Scalance X204IRT (article number 6GK5204-0BA00-2BA3).
The ART Tester is rather strict regarding the model of Siemens Profinet switch.
It should have IP address ``192.168.0.99``, netmask ``255.255.255.0`` and station name "b".
Use for example Codesys to scan for the device, and to adjust the IP settings.
Alternatively, use SinecPni to change the IP address (see the Simatic
page in this documentation).
The switch has a web interface, but it is not necessary to do any setting
adjustments via the web interface.
Log in to the web interface by directing your web browser to its IP address.
User name "admin", factory default password "admin".
Connection of the switch ports is described in the table below:
+-------------+-----------------------------------------------+
| Switch port | Connected to |
+=============+===============================================+
| P1 | Personal computer running Automated RT Tester |
+-------------+-----------------------------------------------+
| P2 | IO-controller ("Device A" port X1 P1) |
+-------------+-----------------------------------------------+
| P3 | Device under test (DUT) running p-net |
+-------------+-----------------------------------------------+
The Automated RT tester will detect "Device B" by itself. No configuration is
required in the Automated RT tester menu.
The setting "Use IEC V2.2 LLDP mode" available via the STEP7 Profinet setup
tool controls the format of the sent portID in LLDP frames.
With the "Use IEC V2.2 LLDP mode" enabled the portID is sent as ``port-001``,
while it is sent as ``port-001.b`` if disabled. The latter format is used in
Profinet 2.3 and newer. The ART tester requires the LLDP format to be in the
2.2 format, otherwise it will complain about portID length.
One way to restore the behavior to the 2.2 format is to do a factory reset
of the switch via the web interface or by pressing the SET button for more than
20 seconds (if the button not is disabled in the web interface).
Remote controlled power outlet
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The Automated RT Tester can control an "Anel Net-PwrCtrl" power outlet via Ethernet.
It must be connected via a separate Ethernet
interface on the personal computer. Use a static IP address ``192.168.1.243`` with
subnet mask to ``255.255.255.0`` on that interface.
The Power outlet has a default IP address of ``192.168.0.244``, and it has a
built-in web server. Enter its IP address in your web browser to log in
(username and password printed on the hardware).
(You might need to temporary set your Ethernet interface to IP ``192.168.0.1``
and subnet mask to ``255.255.255.0``)
Modify the IP settings (on the "Einstellung" page) to use a static IP address
of ``192.168.1.244``.
On the "Steuerung" page you can control the individual power outputs.
Connect power for your device under test to connector number 3 on the power outlet.
Test the functionality from Automated RT Tester by clicking on the symbol to the
left of the "PowerOutlet" text in the tool bar. The symbol to the right of the
"PowerOutlet" text shows a green check mark when the outputs are on, and a
black cross when the outputs are off (or when the power outlet not is connected).
+--------------+------------------------------------------------------------+
| Power outlet | Connected to |
+==============+============================================================+
| 1 | PLC "A" |
+--------------+------------------------------------------------------------+
| 2 | Profinet enabled switch "B" |
+--------------+------------------------------------------------------------+
| 3 | Device under test (DUT) running p-net |
+--------------+------------------------------------------------------------+
| 4 | Neighbour device "D", connected to DUT port 2 |
+--------------+------------------------------------------------------------+
| 5 | Neighbour device "E", connected to DUT highest port number |
+--------------+------------------------------------------------------------+
Hardware naming
^^^^^^^^^^^^^^^
Different types of Siemens hardware are used for the conformance test.
In order to simplify how the different units should be connected together,
a list of Siemens naming conventions is provided here:
* AI: Analog input module
* AQ: Analog output module
* BA: Basic
* BA: Busadapter (with RJ45 or fiber optic connectors)
* BU: BaseUnit (for mounting input and output modules)
* CM: Communication module
* CU: Ethernet connector (copper wires)
* DI: Digital input module
* DP: Profibus DP
* DQ: Digital output module
* F-: Fail safe
* FC: Fast Connect (A bus adapter for network cables)
* HF: High feature
* HS: High speed
* IM: Interface Module
* L+: +24 V DC
* M: Ground connection
* MLFB: Article number (order number) Maschinen Lesbare Fabrikate Bezeichnung
* MP: ?
* P: Port
* PN: Profinet
* R: Ring port for media redundancy
* SM: Special module
* SP: Scalable Peripherals
* ST: Standard
* TM: Technology module
* X: Interface
Siemens IO-device for verification of multi-port devices
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
It is called "Device D" in the test setup documentation.
+--------------------------------------+-------------------------------------------+
| Part | Comments |
+======================================+===========================================+
| Interface module ET200 IM155-6PN/2HF | |
+--------------------------------------+-------------------------------------------+
| Digital output module DQ 132 | In slot 1 (closest to interface module) |
+--------------------------------------+-------------------------------------------+
| Digital input module DI 131 | In slot 2 |
+--------------------------------------+-------------------------------------------+
| Base uint A0 (24 VDC, light colored) | One for each input/output module |
+--------------------------------------+-------------------------------------------+
| Bus adapter | With two RJ45 connectors |
+--------------------------------------+-------------------------------------------+
| Server module | Delivered with the interface module. Put |
| | it in slot 3. |
+--------------------------------------+-------------------------------------------+
See the Profinet test specification for part numbers.
Light-colored bus adapters are used for supply voltage distribution.
The cyan-colored (auxiliary) terminals on bus-adapters are all connected together.
If you only use light-colored bus adapters, then the cyan-colored terminals on
one bus adapter are isolated from the corresponding terminals on other bus adapters.
Connect +24 V to the red terminals of the interface module and the base units.
Connect 0 V to the blue terminals of the interface module and the base units.
Connect a button via wires to the digital input (DI) module. Connect it between
DI.7 (pin 18) and and +24 V. The LED ".7" on the input module will be green
when the button is pressed.
The LED ".7" on the digital output module (DQ) will be green when the output
is high (+24 V).
Use the Ethernet connector P1R.
.. image:: illustrations/SimaticIoDevice.jpg
See the page on setting up a Simatic PLC in this documentation for
instructions on usage.
Make sure that the LLDP frames are in the legacy LLDP format, where the PortId
is "port-001" instead of "port-001.d". This is done via the setting
"Use IEC V2.2 LLDP mode", available in the STEP7 Profinet setup tool for PLCs.
Set up Cisco SF352-08P switch
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
For multiport Profinet devices, also SNMP-communication to non-Profinet
devices is verified. This Cisco switch can be used for that purpose.
Replaces "Device D" in the test setup.
The system LED flashes during startup, and lights steady when the switch is
properly set up and running.
Note that the boot time can be around 100 seconds.
Connect an Ethernet cable to port G1.
Set your laptop IP address to ``192.168.1.143`` and netmask to be ``255.255.255.0``.
Log in to ``192.168.1.254``. Default username is ``cisco`` and password is ``cisco``.
Change password when prompted.
Set the IP address via the left side menu "IP configuration" -> "IPv4 Management and Interfaces" -> "IPv4 Interface".
Click "Add" and enter the static IP address ``192.168.0.98``. Use netmask ``255.255.255.0``.
The switch will change IP address to a new subnet, so you might need to change your
laptop network setting before connecting to the new IP address.
Adjust LLDP settings via menu Administration -> "Discovery - LLDP" -> Properties.
In the page top bar, set "Display mode" to Advanced. Set "Chassis ID Advertisement"
to "MAC Address".
Via Administration -> "Discovery - LLDP" -> "Port settings" select port FE1 and
click Edit. Enable SNMP notification. Select the optional TLVs that start with "802.3".
Via the menu Security -> "TCP/UDP Services", enable "SNMP Service".
In the page top bar, set "Display mode" to Advanced.
Add a SNMP community via the left side menu SNMP -> Communities and
click Add. The community string should be "public". Set "SNMP Management
Station" to "All". Click "Apply" and "Close".
In the top of the page click the "Save" icon.
For the actual measurements, use the port 1 on the Cisco switch.
Verify the SNMP communication to the Cisco switch::
snmpwalk -v1 -c public 192.168.0.98
Tips and ideas
--------------
If you end up with ``Pass with Hint "The device made a EPM Request from a
not Profinet port"``, that means that wrong source port was used when sending
UDP messages. See the page on Linux in this documentation on how to adjust the
ephemeral port range.
If your software version indicates that it is a prototype version (letter "P")
the Automated RT Tester will mark this as pass with hint.
The Automated RT Tester has a convenient feature for remotely setting the
station name, IP address, netmask and gateway of the device under test (DUT).
Use the menu Tools > "Set DUT name and IP".
It will change the settings of the IO device via DCP communication. It is also
possible to do a factory reset of the IO device.
It can be useful to show the ART tester log messages in the target output.
On your Linux target run (replace interface name if necessary)::
tcpdump -i br0 udp port 514 -v -a
Reduce timeout values to speed up testing
-----------------------------------------
It is possible to reduce the timeout values used by Automated RT Tester. This
can be convenient during development, in order to speed up the tests.
Use the menu Tools > Options, and enable "Show expert settings". The time
settings are found on the "Expert Settings" tab.
The times are given in milliseconds.
Remember to use the default values when doing pre-certification testing.
These values have large impact on test execution times:
* ApplicationReadyReqTimeout
* ConnectResTimeout
* DcpResetToFactoryTestSetupTime
* DutBootUpTime
* ReleaseArResTimeout
* StandardTestSetupBootTime
* WriteResTimeout
Relevant test cases for conformance class A
-------------------------------------------
+-------------------------------------------------+-----------------------------------------------------------------+
| Test case | Notes |
+=================================================+=================================================================+
| DCP_1 | Power cycle 8 times. |
+-------------------------------------------------+-----------------------------------------------------------------+
| DCP_2 | Power cycle 2 times. |
+-------------------------------------------------+-----------------------------------------------------------------+
| DCP_3 | Power cycle 2 times. |
+-------------------------------------------------+-----------------------------------------------------------------+
| DCP_4 | Fast |
+-------------------------------------------------+-----------------------------------------------------------------+
| DCP_ALIAS | Requires additional hardware ("Device B") |
+-------------------------------------------------+-----------------------------------------------------------------+
| DCP_IDN | Fast. |
+-------------------------------------------------+-----------------------------------------------------------------+
| DCP_NAME_1 | Power cycle 4 times. |
+-------------------------------------------------+-----------------------------------------------------------------+
| DCP_NAME_2 | Power cycle 4 times. |
+-------------------------------------------------+-----------------------------------------------------------------+
| DCP_ResetToFactory | |
+-------------------------------------------------+-----------------------------------------------------------------+
| DCP_OPTIONS_SUBOPTIONS | |
+-------------------------------------------------+-----------------------------------------------------------------+
| DCP_Router | |
+-------------------------------------------------+-----------------------------------------------------------------+
| DCP_Access | Fast. |
+-------------------------------------------------+-----------------------------------------------------------------+
| DCP_VLAN | Power cycle 2 times |
+-------------------------------------------------+-----------------------------------------------------------------+
| DCP IP-parameter Remanence | Power cycle 4 times. |
+-------------------------------------------------+-----------------------------------------------------------------+
| Behavior Scenario 1 to 9 | Power cycle |
+-------------------------------------------------+-----------------------------------------------------------------+
| Behavior Scenario 10 | Power cycle 7 times. |
+-------------------------------------------------+-----------------------------------------------------------------+
| Behavior Scenario 11 | |
+-------------------------------------------------+-----------------------------------------------------------------+
| Different Access Ways | Requires additional hardware ("Device B") |
+-------------------------------------------------+-----------------------------------------------------------------+
| PDEV_CHECK_ONEPORT | Requires additional hardware ("Device B"). Power cycle 3 times. |
+-------------------------------------------------+-----------------------------------------------------------------+
| Diagnosis | Requires additional hardware ("Device B"). Power cycle twice. |
+-------------------------------------------------+-----------------------------------------------------------------+
| Alarm | Requires additional hardware ("Device B") |
+-------------------------------------------------+-----------------------------------------------------------------+
| AR-ASE | Power cycle |
+-------------------------------------------------+-----------------------------------------------------------------+
| IP_UDP_RPC_I&M_EPM | Power cycle |
+-------------------------------------------------+-----------------------------------------------------------------+
| RTC | Requires additional hardware ("Device B") |
+-------------------------------------------------+-----------------------------------------------------------------+
| VLAN | Fast. Use port-to-port set up. |
+-------------------------------------------------+-----------------------------------------------------------------+
| Different access ways port-to-port | Use port-to-port set up |
+-------------------------------------------------+-----------------------------------------------------------------+
| Manual: DCP_Signal | Flash Signal LED. Fast. |
+-------------------------------------------------+-----------------------------------------------------------------+
| Manual: Behavior of ResetToFactory | Power cycle 4 times. |
+-------------------------------------------------+-----------------------------------------------------------------+
| Manual: Checking of sending RTC frames | Fast |
+-------------------------------------------------+-----------------------------------------------------------------+
| Not automated: DataHoldTimer | PLC required. Use network tap at DUT. |
+-------------------------------------------------+-----------------------------------------------------------------+
| Not automated: Interoperability | PLC required |
+-------------------------------------------------+-----------------------------------------------------------------+
| Not automated: Interoperability with controller | PLC required |
+-------------------------------------------------+-----------------------------------------------------------------+
| Security Level 1 | PLC required |
+-------------------------------------------------+-----------------------------------------------------------------+
Relevant test cases for conformance class B
-------------------------------------------
Set the GSDML file attributes ``ConformanceClass="B"`` and
``SupportedProtocols="SNMP;LLDP"``.
* Behavior scenario 10
* Topology discovery check, standard setup. Requires additional hardware (“Device B”).
* Topology discovery check, non-Profinet-neighbour setup
* Port-to-port
* Behavior of reset to factory (manual)
Additional test cases for multi-port devices
--------------------------------------------
Requires additional hardware ("Device B", “Device D” and Cisco switch).
* PDEV_RECORDS
* Topology discovery check, standard setup.
* Topology discovery check, non-Profinet setup. Uses Cisco switch. Power cycle twice.
For "port-to-port" testing on devices with multiple ports, connect the port
directly to the ART tester laptop. Leave other ports on the device not
connected.
Relevant test cases for legacy startup mode
-------------------------------------------
Legacy startup mode is defined in Profinet version 2.2 and earlier.
Set the attribute ``StartupMode`` in the GSDML file to ``"Legacy;Advanced"``.
Also the attributes ``PNIO_Version`` and ``NumberOfAR`` affects the ART tester
behavior.
* SM_Legacy
* Different Access Ways
* Different Access Ways port-to-port
* DCP
* AR-ASE
* IP_UDP_RPC_I&M_EPM
* Behavior
* FSU (if also supporting fast startup)
* Interoperability (use a legacy PLC)
* Interoperability with controller (use a legacy PLC)
Relevant test cases for fast startup (FSU)
------------------------------------------
Set the parameters ``ParameterizationSpeedupSupported="true"`` and
``DCP_HelloSupported="true"``. The attribute ``PowerOnToCommReady="700"``
describes the startup time in milliseconds.
* FSU
* Different Access Ways
* Manual FSU test case
* Hardware (no auto-negotiation)
Relevant test cases for DHCP
----------------------------
In the GSDML file, set the ``AddressAssignment`` attribute to ``"DCP;DHCP"``.
* DHCP
Relevant test cases for MRP
---------------------------
In the GSDML file, adjust the ``MediaRedundancy`` and ``SupportedRole``
attributes.
* MRP_1
* MRP_2
* MRP_chk1
* MRP_chk2
* MRP_OFF1
* MRP_OFF2
* MRP_On1
* MRP_On2
These are named "MRP_Pdev", and are described in
"TCS_00004126_MRP_Pdev_ctrl_not_automated_V2.42.1.pdf".
The PLC (CPU 1516-3 PN/DP) and the neighbour device (Interface module IM
155-6PN/2) used in the standard setup both have support for MRP.
Other tests
-----------
Your GSDML file should pass the verification with the "GSDMLcheck" tool.
Details on tests with PLC
-------------------------
Load PLC program
^^^^^^^^^^^^^^^^
Verify that the sample application PLC program is working properly with your
IO-device. Button1 should be able to control the state of data LED (LED1).
Interoperability
^^^^^^^^^^^^^^^^
Run with PLC for 10 minutes without errors.
If the device under test has more than one port, there should be 5 IO-devices
connected to the non-PLC port.
For the cyclic data, use one input module and one output module.
The PLC should be programmed to continuously read out record data.
The timing should be the fastest allowed according to the GSDML
file, and use 3 "accepted update cycles without IO data".
Record startup and data exchange using Wireshark.
In the Wireshark file, make sure IOPS and IOCS in the cyclic data from the
IO-device have the value GOOD after it has sent the "application ready"
message.
Also verify that there have been no alarms (sort the frames by protocol).
* "Record data"?
* ExpectedIdentification is equal to the RealIdentification?
* How to create additional net load? (using DCP Identify all)
* Implicit read?
Data Hold Timer
^^^^^^^^^^^^^^^
Run with PLC. The timing should be the fastest allowed according to the GSDML
file, and use 3 "accepted update cycles without IO data".
Record startup and data exchange using Wireshark.
Unplug network cable from the PLC.
In the Wireshark file:
* Count the number of cyclic data frames sent by the IO-device before the
alarm frame appears. It is allowed that 3-6 data frames are sent before
the alarm frame.
* At startup the first valid data frame should be sent within the data
hold time.
* The IOCS in the cyclic data from the IO-device should have the value GOOD
after the "application ready" message has been sent.
* Verify the data cycle time.
Repeat the cable unplugging measurements with reduction ratios (1), 2, 4, 8
and 16. With a cycle time of for example 1 ms this corresponds to a frame
send interval of 1 ms to 16 ms, and a data hold time of 3 ms to 48 ms.
Check that a LLDP frame is sent within 5 seconds, and then every 5 seconds.
The TTL value in the LLDP frame should be 20 seconds.
The MAUtype, "autonegotiation supported" and "autonegotiation enabled" must
be correct.
Interoperability with controller
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Run with PLC. The timing should be the fastest allowed according to the GSDML
file, and use 3 "accepted update cycles without IO data".
Record startup and data exchange using Wireshark.
Verify that the outputs are according to the manual of your IO-device when
you do these actions (repeat several times):
* PLC powered off
* PLC powered on. The program should be running.
* Switch the PLC to stop.
* Switch the PLC to run.
* Disconnect cable from PLC.
* Reconnect the PLC cable.
In the Wireshark file, make sure IOPS and IOCS in the cyclic data from the
IO-device have the value GOOD after it has sent the "application ready"
message.
* Record data?
Security Level 1 tester
-----------------------
A PLC program is used to both establish cyclic data communication, and to
continuously read out parameter values from the IO-device under test (DUT).
If the DUT has more than one port, a neighbour device "D" is connected to
port 2, and the PLC will control the digital inputs and outputs of device D.
A program running on a Linux laptop will generate additional network load.
Depending on the result, the DUT will be assigned net load class 1 to 3.
See also the guideline "PROFINET IO Security Level 1".
It can be useful to run the other PLC-based tests before, to find out the
shortest cycle time useful when there is no additional network load.
PLC program
^^^^^^^^^^^
Use the STEP7 project from the test bundle, and import it into the TIA portal.
In Siemens TIA Portal, open the file "normal_d_V2.40.0_V15.1.zap15_1" as
an existing project. Give the path to a local directory that will be used
for the project.
The block "Main [OB0]" will call the "FC001_Test_Programm", and it will also
read and set the digital inputs and outputs on neighbour device D.
Data block "DB003_RECORD_Index" contains an array of record numbers (parameters)
that are to be read from the IO-device. It also contains a counter value
keeping track of which entry in the array that is being used right now.
The block "Startup" will initialize this counter value.
The "FC001_Test_Programm" function will call "FB001_RDREC", and then
will the counter value be increased.
The "FB001_RDREC" function block will read out a parameter from the IO-device
and store any error value. It should be maximum 100 ms from one response
until the next request is sent out by the PLC.
Data block "DB002_RECORD_data" has a large array of bytes for storing the
record data read from the IO-device.
Device D should have a digital input module and a digital output module. It
should have a cycle time of 1 ms and an IP address ``192.168.0.98``.
If the device "D" is not exactly the variant you have, you need to replace it
with another IO-device with digital inputs and outputs. Default connection:
* Input ``%I0.0`` Ix_Req (Enables continuous readout of parameter values)
* Input ``%I0.1`` Ix_ACK (Acknowledges errors)
* Output ``%Q0.0`` Qx_Error
* Output ``%Q0.1`` Qx_Error_RDREC
Delete the existing "dut" device.
Import the GSDML file of your device (the DUT), and insert your device.
Plug relevant modules into the slots.
Give it the station name ``dut``, and it should use the IP address
``192.168.0.50``. Connect it to the PLC via the "Network view".
In the "Device view" select the DUT, and in the "Device overview" select the
DUT line. In Properties > "System constants" find the hardware identifier
number for the line "dut~HEAD".
Adjust cyclic time setting of the DUT.
In the "Main [OB1]" block make sure that the hardware identifier is set to
the relevant value::
Ihw_ID := "dut~HEAD",
or to the value found above (for example)::
Ihw_ID := 261,
Create a watch table for the relevant entries.
Compile the hardware configuration and the software, and download to the PLC.
If the DUT only has one port the neighbour device "D" is not needed.
Normally one of the digital inputs of device "D" is enabling the continuous
reading out of parameter values, but you need instead to force the corresponding
PLC program variable to a high level. In the left side menu use "Watch and
force tables" (below the PLC), and add a new entry by clicking an empty row
in the "Name" column, and select "Ix_Req". When later in online mode, click
the small "Show/hide all modify columns" to show the column. Enter "TRUE" in
the "Modify value" on the line for "Ix_Req". Select that line by enabling
the corresponding check box. Press the "Modify all selected values once
and now".
Tester software for additional network load
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Install the tester software on an Ubuntu machine, or in a virtual Ubuntu
machine running on Windows. The IP address should be ``192.168.0.30``.
See the PDF in the "Security Level 1"/"tester" folder in the downloaded
test bundle. The program ends up in ``/root/Netload``. See the PDF
how to start the program.
The SL1-tester has a number of template ``.pcap`` files, and rewrites those
files with the MAC address of the SL1-tester laptop and the DUT.
Rewriting is done using the Bittwist editor tool (``bittwiste``).
The MAC of the DUT is found with the ``arping`` Linux command.
Actual sending of frames is done with the ``packETHcli`` Linux command.
Set up hardware
^^^^^^^^^^^^^^^
Set the station name of the DUT to ``dut`` and the IP address to ``192.168.0.50``.
Use a temporary station name, to be able to detect device reboots.
Set the station name of device D to ``d`` and the IP address to ``192.168.0.98``.
The digital input "Ix_Req" is used to enable continuous read out of parameter
values. Set it to high level to start the readout.
Verify that there is cyclic communication, and that there is repeated
acyclic data read out.
Use a non-Profinet switch (no LLDP packet filtering) to connect the device
under test (DUT, port 1), the PLC and the personal computer running the Security
Level 1 tester software. Connect neighbour device D to port 2 of the DUT.
Synchronize clock of the PLC with the the clock of the laptop running the
tester software, as we later read the diagnostic log of the PLC.
Run the tests
^^^^^^^^^^^^^
For a class B device with two ports you need to run one "normal" mode test and
one "faulty" mode test. After the "faulty" mode the communication should be
good again after the additional network load has stopped.
During the "normal" mode the communication should not be lost, and this is
verified by studying the diagnostic log of the PLC afterwards.
+----------------+---------------+---------------+-----------------------------------+
| Operation mode | Run time | Test cases | Bus load |
+================+===============+===============+===================================+
| Normal | 1 hour 40 min | 101-118, 1-63 | Up to 10%, based on netload class |
+----------------+---------------+---------------+-----------------------------------+
| Faulty | 1 hour 20 min | 1-63 | 100% |
+----------------+---------------+---------------+-----------------------------------+
For the "faulty" test set up, there is no difference between the different netload classes.
In the TIA portal, make sure you are "Offline" with the PLC (otherwise there
will be even more additional network load).
Make sure that the software you run on the DUT has the correct compile time settings,
for example log level.
* Verify that the PLC clock setting is correct.
* Set the device name to ``dut`` with temporary setting
* Start the PLC program, and verify that the parameter readout is running
(using Wireshark).
* Start the SL1-tester in "faulty" mode.
* Verify that the PLC communication still is good after the SL1-tester is done.
* Start the SL1-tester in "normal" mode.
* Verify that there hasn't been any communication breakdown, by looking in
the PLC diagnostic buffer (via TIA portal).
If the device under test has a 100 Mbit/s, use the 100 Mbit/s setting in the test
program otherwise 1 Gbit/s.
Each test case has a duration of 1 minute, except case 15 - 16 and 116 - 117
which runs for 3 minutes each. Test case 101 and 102 runs until the sequence
is completed. If the tests take longer than that, wrong settings have been
used for the SL1-tester.
Test cases for Netload class I. Duration is 1 minute, except where noted otherwise:
========= ===================================== ================== ==================
Test case Description "Normal" packets/s "Faulty" packets/s
========= ===================================== ================== ==================
101 DCP scanning (manual review) Approx 5 -
102 ARP scanning (manual review) Approx 5 -
103-104 Ping 10 -
105-108 UDP malformed 10 -
109 IP malformed 10 -
110-114 SNMP 10 -
115 UDP malformed 10 -
1-6 Configuration test protocol "CRAP" 820-15000 8000-150000
7-8 Ping 10-15 110000-150000
9-11 TCP 1-15 8000-150000
12-14 UDP 8-150 8000-150000
15-16 ARP, 3 minutes 50 150000
17 ARP 15 150000
18-20 UDP 0.8-15 8000-150000
21 LLDP 8 87000
22-23 Ping 10000-15000 110000-150000
24-26 TCP 800-15000 8000-150000
27-29 UDP 800-15000 8000-150000
30 DHCPv6 7 70000
31-33 ICMPv6 11-14 110000-140000
34 LLMNR 10 98000
35 MDNS 11 110000
36 SSDP 6 62000
37-42 Profinet cyclic data (RTC) 0.8-15 8000-150000
43-44 Profinet alarm (RTA) 15 150000
45-50 Profinet cyclic data (RTC) 800-15000 8000-150000
51-52 Profinet alarm (RTA) 14000-15000 150000
53-54 DCP 50-70 100000-150000
55 PTCP 14 140000
56-60 MRP 1400-1500 140000-150000
61-63 PTCP 15-140 1100000-150000
========= ===================================== ================== ==================
Calculation example for 100 Mbit/s, which allows approximately 10 MByte/s payload:
============= ==========
Packet size Packets/s
============= ==========
10 Bytes 1000000
100 Bytes 100000
1000 Bytes 10000
============= ==========
If there are problems during the "normal" mode, study the error LED on the
PLC to detect which scenario that is causing the malfunction.
To find which of the test case that is causing any problems in the "Faulty"
test suite, you can increase the time between the test cases to 30 seconds.
Then the communication will have time to recover between the test cases,
and you can verify communication using the LEDs of the PLC.
If the error LED (on the PLC) does not turn off after a specific test case, then
you have found the test case that causes permanent communication breakdown.
Modify this line in the ``/root/Netload/Scripts/Netload-Tester.sh`` script::
# -> Delay between the individual frame sequences
Delay="30s"
Troubleshooting
---------------
For the "Get MAC Address" button in ART tester to work, you need to
have the "Device b" connected.
If the test case "Different access ways port-to-port" fails,
verify that your laptop Ethernet interface speed is set to 100 Mbit/s and
that any Windows or Siemens (TIA portal) LLDP implementation on the
Ethernet interface is disabled.
If you in the test case "Different access ways port-to-port" end up with
the error "NumberOfPeers is not valid" make sure that the device "D" is
disconnected from port 2 of your device under test.
If the "Alarm" test case fails with the message "No valid Transport ACK on
the Alarm ACK was received", then the Ethernet receive task probably
has too low priority or too limited resources.
Note that if increasing the priority, several alarm frames might be
queued up before the main application have a chance to
handle them. As the ART Tester sends a burst of 6 alarm frames in the APMS
scenario, make sure that your alarm input queue can hold at least that number.
If there are problems in test case Behavior scenario 10 regarding reading and
writing via SNMP after power cycling, verify that there is proper connection to
the SNMP daemon immediately at startup.
If you get the error "Device did answer ping request although it shouldn't", the device is probably not powered
off during the test as required. Make sure your device is not powered via some USB cable, for example.
If the Alarm test case fails with the message "No valid Transport ACK on the Alarm ACK was received",
then the Ethernet receieve task might have too low priority.

View File

@ -0,0 +1,94 @@
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
import time
# Workaround for issue https://github.com/sphinx-contrib/googleanalytics/issues/2
# Note that a warning still will be issued "unsupported object from its setup() function"
# Remove this workaround when the issue has been resolved upstream
import sphinx.application
import sphinx.errors
sphinx.application.ExtensionError = sphinx.errors.ExtensionError
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- Project information -----------------------------------------------------
project = 'p-net'
copyright = '2020, rt-labs'
author = 'rt-labs'
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
"breathe",
"recommonmark",
"rst2pdf.pdfbuilder",
"sphinx_rtd_theme",
"sphinxcontrib.spelling",
"sphinxcontrib.googleanalytics",
]
needs_sphinx = "1.8"
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
spelling_word_list_filename = "spelling_wordlist.txt"
googleanalytics_id = "UA-4171737-2"
googleanalytics_enabled = True
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = "sphinx_rtd_theme"
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['static']
html_last_updated_fmt = '%Y-%m-%d %H:%M'
breathe_projects = { project: "../build/xml/" }
breathe_default_project = project
html_css_files = [
'../../css/custom_rtd.css', # Requested by web developer
'css/fix_table_width.css',
'css/change_header_size.css'
]
# -- Options for PDF output -------------------------------------------------
pdf_filename = "{}_{}".format(project.replace("-", ""), time.strftime("%Y-%m-%d"))
pdf_title = "{} Profinet device stack".format(project)
pdf_documents = [('index', pdf_filename, pdf_title, author),]
pdf_break_level = 2

View File

@ -0,0 +1,818 @@
Creating GSD (GSDML) files
==========================
A GSD (General Station Description) file is an XML file describing a
Profinet IO-Device. The XML-based language is called GSDML (GSD Markup Language).
A single GSDML file describes a single VendorID and ProductID combination.
A family of devices can be described in the same file; Then they will share
the ProductID but have different DAP (head module) ID values.
For Profinet members, the "Profinet GSD Checker" tool is available for
download. It contains GSD example files showing different aspects of the file
format.
To create a custom GSD file, start from one of the examples. Edit the file in
an XML editor. Then run the resulting file in the "Profinet GSD Checker" tool
to verify its structure.
Below is a brief introduction to the most common elements and attributes in
GSDML files given. It contains simplifications and does not describe special
cases. For full details on GSDML file syntax, see the official GSDML
specification.
An example GSDML file is available in the samples/pn_dev directory of the p-net
repository: https://github.com/rtlabs-com/p-net/tree/master/samples/pn_dev
A GSDML file is used to describe an IO-device. The corresponding file
for describing a controller is a CDML (Controller Description Markup Language)
file (with file ending ``.xml``). The CDML is used during controller certification
(not discussed here).
XML editors
-----------
It can be helpful to use an XML editor when working with GSD files. A few
examples are given here.
**QXmlEdit**:
http://qxmledit.org/ (Windows and Linux)
It can be installed via Ubuntu Software Store.
**CAM editor**:
http://camprocessor.sourceforge.net/wiki/ (Windows and Linux)
**XML Notepad**:
https://github.com/microsoft/xmlnotepad (Windows)
Using "Profinet GSD Checker" tool on a Windows PC
-------------------------------------------------
The tool is available for Profinet members.
The file is part of the conformance test tool download. Unzip the file
PROFINET_GSD_Checker_2.34.zip, and double-click the "setup" file. After
installation, enter ``profinet`` in the Windows search bar to start the program.
Use the menu File > Open and select your GSD file. An overview of the file
is shown. In the left menu different parts of the file can be selected.
To verify the file, use the menu File > Check. Click the "GSDCheck" bar at
the bottom of the screen to open a document check view in the right part of
the screen. Use the Icons to show the details of any error messages.
If you later need to uninstall the tool, do that via Windows settings > Apps &
features.
File name
---------
The current GSD version (as of writing) is 2.4.
The file name format is specified in section 5.1 of the GSD specification. For example
``GSDML-V2.35-Vendor-Device-20171231.xml``. You can give it an optional timestamp
in the filename: ``-20171231-235900.xml``.
Note that version for example 2.3 is newer than version 2.25. From the GSDML
specification: "V2.2 < V2.25 < V2.3 < V2.31"
In-file comments
----------------
.. highlight:: xml
XML comments are written::
<!-- Comment text -->
<!-- Multi-line
comment
text -->
.. highlight:: none
Entering values
---------------
Values are written in double quotes. Different attributes have different types:
* ValueList: To enter several values (including a range of values), write ``"0..4 7 9"``.
* TokenList1: Semicolon separated values, for example ``"Legacy;Advanced"``.
File structure
--------------
The GSD files are structured like::
ISO15745Profile
|
+--ProfileHeader
+--ProfileBody
|
+--DeviceIdentity
| |
| +--InfoText
| +--VendorName
|
+--DeviceFunction
| |
| +--Family
|
+--ApplicationProcess
|
+--DeviceAccessPointList
+--ModuleList
+--SubmoduleList
+--ValueList
+--LogBookEntryList
+--CategoryList
+--ChannelDiagList
+--ChannelProcessAlarmList
+--UnitDiagTypeList
+--GraphicsList
+--ExternalTextList
The encoding is given in the first line of the XML file. Use ``utf-8`` if using non-ASCII characters in any of the text fields.
Do not change the ``<ProfileHeader>`` contents.
In the ``<DeviceIdentity>`` adjust the attributes ``VendorID``, ``DeviceID``.
Adjust the ``Value`` in ``<VendorName>``.
In the ``<Family>`` adjust the attributes for ``MainFamily`` (typically "I/O", should be from the list of allowed values)
and ``ProductFamily`` which is vendor specific.
Those values are for example used by the engineering tool to display the device
in the hardware catalog. The catalog is typically sorted by
"Profinet IO"/<MainFamily>/<VendorName>/<ProductFamily>/<IDT_MODULE_NAME_DAP1>
Details on the DeviceAccessPoint
--------------------------------
This part of the file deals mainly with communication settings.
DeviceAccessPointList element hierarchy::
DeviceAccessPointList
|
+--DeviceAccessPointItem
|
+--ModuleInfo
| |
| +--Name
| +--InfoText
| +--VendorName
| +--OrderNumber
| +--HardwareRelease
| +--SoftwareRelease
|
+--CertificationInfo
+--SubslotList
| |
| +--SubslotItem
|
+--IOConfigData
+--UseableModules
| |
| +--ModuleItemRef
|
+--ARVendorBlock
| |
| +--Request
| |
| +--Const
|
+--VirtualSubmoduleList
| |
| +--VirtualSubmoduleItem
| |
| +--ModuleInfo
| | |
| | +--Name
| | +--InfoText
| |
| +--IOData
| +--RecordDataList
| |
| +--ParameterRecordDataItem
| |
| +--Name
| +--Const
| +--Ref
|
+--SystemDefinedSubmoduleList
| |
| +--InterfaceSubmoduleItem
| | |
| | +--ApplicationRelations
| | |
| | +--TimingProperties
| |
| +--PortSubmoduleItem
| |
| +--MAUTypeList
| |
| +--MAUTypeItem
|
+--Graphics
|
+--GraphicItemRef
The ``<DeviceAccessPointItem>`` element has the attributes:
* ``ID="IDD_1"``
* ``PNIO_Version="V2.4"`` Which version of Profinet specification it is
certified against.
* ``PhysicalSlots="0..4"`` Slot 0 is always used by the DAP (bus interface)
module. Relates to the ``PNET_MAX_SLOTS`` value in the p-net stack.
* ``ModuleIdentNumber="0x00000001"`` Unsigned32hex.
* ``MinDeviceInterval="32"`` Minimum cyclic data update interval, in number
of 31.25 us ticks. A value 32 corresponds to cyclic data sending and
receiving every millisecond. Unsigned16. It should match the value
``min_device_interval`` in the p-net configuration. (It must be possible to
achieve this time using the values in the ``SendClock`` and ``ReductionRatio``
attributes in another element).
* ``DNS_CompatibleName="pno-example-dap"`` (Default station name)
* ``FixedInSlots="0"`` The DAP module is always in slot 0
* ``ObjectUUID_LocalIndex="0"``
* ``DeviceAccessSupported="false"`` If a limited version of AR connection is allowed.
* ``NumberOfDeviceAccessAR="1"`` Number of Device Access connections. Should only
be given if ``DeviceAccessSupported`` is ``true``. Dependent on the ``PNET_MAX_AR``
value in the p-net stack.
* ``MultipleWriteSupported="true"`` Multiple writes in a single request.
Mandatory ``true`` since V2.31.
* ``SharedDeviceSupported="false"`` False if not given
* ``SharedInputSupported="false"`` False if not given
* ``RequiredSchemaVersion="V2.3"`` This file has features requiring this schema
version. It must be at least 2.3 if legacy startup mode not is supported.
* ``CheckDeviceID_Allowed="true"`` If the VendorID and DeviceID are fine grained
enough to verify that the same type of device is used at replacement.
* ``NameOfStationNotTransferable="false"``
* ``LLDP_NoD_Supported="true"`` Mandatory ``true`` since V2.31.
* ``ResetToFactoryModes="1..2"`` Bits describing reset possibilities. At least
"2" should be present. Reset modes 1 and 2 are supported by p-net.
* ``ParameterizationSpeedupSupported="true"`` For fast startup.
* ``PowerOnToCommReady="700"`` For fast startup, time to first data exchange
in milliseconds. Unsigned32.
* ``AddressAssignment="DCP"`` Can also be ``"DHCP"`` and ``"LOCAL"``. Defaults
to DCP if not given.
General info on the Profinet IO-Device is given in ``<ModuleInfo>``
subelements. For example the vendor name and order number are given.
The ``<CertificationInfo>`` element has the attributes:
* ``ConformanceClass="B"``
* ``ApplicationClass=""`` Typically empty, but can be for example "FunctionalSafety"
* ``NetloadClass="I"``
With ``<SubslotItem>`` elements it is possible to give names to subslots. Each
element has the attributes ``SubslotNumber`` and ``TextId``.
The ``<IOConfigData>`` element has the attributes:
* ``MaxInputLength="24"`` Unsigned16, valid 0..1440
* ``MaxOutputLength="24"`` Unsigned16, valid 0..1440
* ``MaxDataLength="40"`` Defaults to MaxInputLength + MaxOutputLength. Unsigned16.
The values are in bytes and are for all submodules. For details on how to
calculate these, see the GSDML specification.
Which modules that can be used in the slots are given by the
``<ModuleItemRef>`` elements. Each has the attribute ``ModuleItemTarget``,
which is a reference to a module (as described below). The attribute
``AllowedInSlots`` is a space separated list of slots that module type can be
used in. If the module type is permanently fixed in slots, then the attribute
``FixedInSlots`` is used instead.
The ``<ARVendorBlock>`` element is optional, and is used for global parameters.
These are sent from the IO-controller (PLC) during communication start.
The ``<Request>`` element has the attributes ``Length`` (in bytes) and
``APStructureIdentifier="0"``.
Data is stored in the ``<Const>`` element, with the attribute
``Data="0x00,0x00,0x00,0x01"``.
The DAP (bus interface) module can have (non-removable = virtual) submodules.
See ``<SubmoduleItem>`` below for a general description on submodules.
One specific detail for a DAP virtual submodule is that it has the
``Writeable_IM_Records="1 2 3"`` attribute, which informs about writable
Identification & Maintenance (I&M) records. Note that record 0 and 5 are
read only, so they should never appear in this list.
You must support writing to I&M1-3 for at least one of the DAP submodules.
Other special submodules for DAP modules are ``<InterfaceSubmoduleItem>`` and
``<PortSubmoduleItem>``, both subelements to ``<SystemDefinedSubmoduleList>``.
Each interface defines for example clock synchronization, and the ports (of that
interface) define for example if they use radio or 100 Mbit/s copper cables.
The subslot number for the first interface is 0x8000, and next interface (if
any) has subslot number 0x8100. Note that p-net only supports one interface.
The first port of the first interface has subslot 0x8001,
and next port of that interface has subslot number 0x8002.
Interfaces are described using the ``<InterfaceSubmoduleItem>`` element, which
has these attributes:
* ``ID="IDS_I"``
* ``SubmoduleIdentNumber="0x00000002"`` Unsigned32hex.
* ``SubslotNumber="32768"`` This is first interface (0x8000). Unsigned16.
* ``TextId="IDT_NAME_IS"``
* ``SupportedRT_Classes="RT_CLASS_1"``
* ``SupportedProtocols="SNMP;LLDP"`` Conformance class B must support SNMP.
* ``PTP_BoundarySupported="true"``
* ``DCP_BoundarySupported="true"``
* ``DCP_HelloSupported="true"`` Used for fast startup.
If the ``DCP_HelloSupported`` attribute is set to ``"true"``, you must also
set the ``PowerOnToCommReady`` attribute of the ``<DeviceAccessPointItem>``
element.
The communication startup is described in the element ``<ApplicationRelations>``
with the attribute ``StartupMode``, which typically should be "Advanced" (the
alternative is "Legacy"). If supporting both modes, use a semicolon separated
list. The ``NumberOfAR`` attribute defaults to 1 if not given.
There is typically one input-CR and one output-CR per AR, but in the GSDML file
it is possible to set the attributes ``NumberOfAdditionalInputCR``,
``NumberOfAdditionalOutputCR``, ``NumberOfAdditionalMulticastProviderCR`` and
``NumberOfMulticastConsumerCR``. Those attributes are not supported by p-net.
The ``<TimingProperties>`` element describes the sending of cyclic IO data.
The ``SendClock`` attribute contains a list of all supported send cycle times,
in units of 31.25 us. Defaults to "32", which corresponds to 1 ms. Note that the
list must contain the value ``32``.
The attribute ``ReductionRatio`` defines how much the sending can be slowed down,
and it seems that it must be “1 2 4 8 16 32 64 128 256 512”.
The actual speed of the device should be set by the ``MinDeviceInterval`` attribute in another element.
Ethernet port properties are descried using the ``<PortSubmoduleItem>``, which
has these attributes:
* ``ID="IDS_P2"``
* ``SubmoduleIdentNumber="0x00000003"`` Unsigned32hex.
* ``SubslotNumber="32770"`` This is second port on first interface (0x8002). Unsigned16.
* ``TextId="IDT_NAME_PS2"``
* ``MaxPortRxDelay="350"`` Time delay in ns needed for receiving frames. Unsigned16.
* ``MaxPortTxDelay="160"`` Time delay in ns needed for sending frames. Unsigned16.
Use an ``<MAUTypeItem>`` element to describe the Medium Attachment Unit type,
which can be radio (0), copper at 100 Mbit/s (16), copper at 1000 Mbit/s (30)
or fiber optics etc.
With the ``<GraphicItemRef>`` element it is possible to add a logo or other symbol
to your device. It will appear in the engineering tool. Use a file in the .BMP file format.
The attribute ``Type`` should typically be ``DeviceSymbol``, and the ``GraphicItemTarget``
is a reference to the file name given in the ``<GraphicsList>`` (see below).
Additional ports
----------------
Additional physical ports are created by adding ``<PortSubmoduleItem>`` nodes
to the ``<SystemDefinedSubmoduleList>`` node.
The ID, submodule identity number and subslot number shall be unique for
all ports.
Details on the module list
--------------------------
Profinet field devices can have different hardware modules, therefore there is
a need to be able to describe those modules. There are also field devices with
non-modifiable hardware, and they are sometimes called compact devices. Also
they are described using modules (fixed in slots, as mentioned above).
ModuleList element hierarchy::
ModuleList
|
+--ModuleItem
|
+--ModuleInfo
| |
| +--Name
| +--TextId
| +--InfoText
| +--OrderNumber
| +--HardwareRelease
| +--SoftwareRelease
|
+--UseableSubmodules
| |
| +--SubmoduleItemRef
|
+--VirtualSubmoduleList
|
+--VirtualSubmoduleItem
|
+--ModuleInfo
| |
| +--Name
| +--InfoText
|
+--IOData
|
+--Input
| |
| +--DataItem
|
+--Output
|
+--DataItem
|
+--BitDataItem
Each ``<ModuleItem>`` element has the attributes ``ID`` (for example "IDM_1"),
``ModuleIdentNumber`` and ``PhysicalSubslots``. The last attribute is a space
separated list of its subslot numbers.
The element ``<ModuleInfo>`` has information on the module name in its
subelements. The elements ``<HardwareRelease>`` and ``<SoftwareRelease>`` have
``Value`` attributes.
The value for <SoftwareRelease> should correspond to the configuration values
``im_sw_revision_prefix``, ``im_sw_revision_functional_enhancement``,
``im_sw_revision_bug_fix`` and ``im_sw_revision_internal_change``.
Each ``<SubmoduleItemRef>`` element has the attributes ``SubmoduleItemTarget``
(which is a reference to a submodule) and ``AllowedInSubslots`` (which is a
space separated list of subslot numbers).
Virtual submodules are submodules that are built-in into a module (no physical
submodule can be removed). If only virtual submodules are available, the
``PhysicalSubslots`` attribute is not given in ``<ModuleItem>``.
For details on ``<VirtualSubmoduleItem>``, see ``<SubmoduleItem>`` below.
The configuration value PNET_MAX_SUBSLOTS defines the maximum number of
submodules (for each module) that the p-net stack can handle.
Details on the submodule list
-----------------------------
Some submodules are permanent parts of modules, and are then called virtual
submodules.
SubmoduleList element hierarchy::
SubmoduleList
|
+--SubmoduleItem
|
+--ModuleInfo
| |
| +--Name
| +--InfoText
| +--OrderNumber
|
+--IOData
| |
| +--Input
| | |
| | +--DataItem
| |
| +--Output
| |
| +--DataItem
| |
| +--BitDataItem
|
+--RecordDataList
|
+--ParameterRecordDataItem
|
+--Name
+--Ref
+--Const
+--MenuList
|
+--MenuItem
|
+--Name
+--MenuRef
+--ParameterRef
Each ``<SubmoduleItem>`` has the attributes ``ID`` (for example "IDS_1"),
``SubmoduleIdentNumber`` and ``MayIssueProcessAlarm`` (which can be "true" or
"false"). The element ``<ModuleInfo>`` might have an attribute ``CategoryRef``,
and also has subelements with information on the submodule name etc.
The ``<Input>`` and ``<Output>`` elements have the optional attribute
``Consistency``, which can be "Item consistency" (default if not given) or
"All items consistency" (consistency between all input/output fields for
the submodule).
The ``<DataItem>`` elements have the attributes ``TextId`` and ``DataType``
(which can be for example "Unsigned8", "Unsigned64", "Float32", "Integer8",
"Date", "VisibleString", "Boolean" or "TimeStamp"). The optional
attribute ``UseAsBits="true"`` is used when individual bits are to be displayed
in the engineering tool (only for the unsigned ``DataType`` variants). It is
recommended to use Unsigned8 when packing booleans.
Use ``<BitDataItem>`` elements to name the individual bits, by setting the
attributes ``TextId`` and ``BitOffset`` (which is a string, for example "0").
The least significant bit has offset 0.
A module parameter is typically adjustable from the IO-controller, and could
be used to set for example an input delay time. To describe parameters use
``<ParameterRecordDataItem>`` elements. They have the attributes
``Index="123"`` and ``Length="4"`` (in bytes).
Use the ``<Name>`` subelement to give it a name.
To initialize the content, use the ``<Const>`` element (if subelement
``<Ref>`` not is given).
The subelement ``<Ref>`` has these attributes:
* ``DataType="Unsigned32"``
* ``ByteOffset="0"``
* ``DefaultValue="0"``
* ``AllowedValues="0..99"``
* ``Changeable="true"`` Whether changes of this parameter is allowed.
* ``Visible="true"`` Whether it should be visible in the engineering tool.
* ``TextId="DEMO_1"``
* ``ValueItemTarget="IDV_InputDelay"`` Optional, to reference an enum (see ``<ValueItem>``).
Each module parameter can contain several ``<Ref>`` subelements, as long as
they fit in the total ``Length`` size.
It is possible to connect parameter values to enums for use in menus in
engineering tools. This is done via the ``<MenuItem>`` element (and
subelements).
============================= ============== ============ =============== ======================
Data type GSDML name Size (bytes) Name in Codesys Name in Simatic Step 7
============================= ============== ============ =============== ======================
Bit (part of lager data) (none) (none) BIT
Boolean (added in ver 2.32) Boolean 1 BOOL
Integer 8 bits Integer8 1 SINT SINT
Integer 16 bits Integer16 2 INT INT
Integer 32 bits Integer32 3 DINT DINT
Integer 64 bits Integer64 4 LINT LINT
Unsigned int 8 bits Unsigned8 1 USINT, BYTE USINT, BYTE
Unsigned int 16 bits Unsigned16 2 UINT, WORD UINT, WORD
Unsigned int 32 bits Unsigned32 4 UDINT, DWORD UDINT, DWORD
Unsigned int 64 bits Unsigned64 8 ULINT, LWORD ULINT, LWORD
Float Float32 4 REAL REAL
Double precision float Float64 8 LREAL LREAL
============================= ============== ============ =============== ======================
Details on the value list
-------------------------
The value list is optional. It is a storage of enum values.
ValueList element hierarchy::
ValueList
|
+--ValueItem
|
+--Help
|
+--Assignments
|
+--Assign
Each enum is described in a ``<ValueItem>`` element with an ``ID`` attribute.
Each enum value is then given in an ``<Assign>`` element with attributes
``TextId`` and ``Content`` (with a numerical value given as a string,
for example ``"5"``).
It is also possible to give a help text by using the ``<Help>`` element with
a ``TextId`` attribute.
Details on the LogBook entry list
----------------------------------
This is optional, and is used to give human-readable descriptions to
manufacturer-specific error codes.
LogBookEntryList element hierarchy::
LogBookEntryList
|
+--LogBookEntryItem
|
+--ErrorCode2Value
| |
| +--Name
|
+--ErrorCode2List
|
+--ErrorCode2Item
|
+--Name
A ``<LogBookEntryItem>`` has an attribute ``Status="2130432"`` that is the
decimal version of the (hex) status value 0x208200. Those are the bytes
ErrorCode, ErrorDecode and ErrorCode1. The subelements ``<ErrorCode2Value>``
and ``<Name>`` connects it to a text entry.
Some error conditions also require information from the ErrorCode2 byte. Then
the ``<ErrorCode2Item>`` element with attribute ``ErrorCode2="4"`` is used.
Details on the category list
----------------------------
The category list is optional. It can be useful for storing categories like
"Digital input" and "Digital output".
CategoryList element hierarchy::
CategoryList
|
+--CategoryItem
|
+--InfoText
Each ``<CategoryItem>`` element has the attributes ``ID`` and ``TextId``.
It has subelements ``<InfoText>`` with the attribute ``TextId``.
The category information is used in other elements by setting the attribute
``CategoryRef`` with the value given in the ``ID`` here. For example
``<ModuleInfo>`` elements can use category information. If a more detailed
categorization is required, then also the attribute ``SubCategory1Ref`` can
be used.
Details on the external text list
---------------------------------
Human readable text strings are located here, and referenced to from the rest
of the XML file. This is for the strings to be easy to translate to other
languages.
Remember to update the contents of all relevant texts when updating a GSDML
file.
ExternalTextList element hierarchy::
ExternalTextList
|
+--PrimaryLanguage
| |
| +--Text
|
+--Language
|
+--Text
Within each ``<Text>`` element, the attributes ``TextId`` and ``Value``
are used to store the information.
Only ``<PrimaryLanguage>`` is mandatory, and corresponds to English.
If ``<Language>`` is given, the actual
language is set by for example a ``xml:lang="fr"`` attribute.
This is how the different text fields are used in engineering tools:
======================= ===================================================================================================================
Item TIA portal and Codesys
======================= ===================================================================================================================
DeviceIdentity infotext Unclear?
DAP module name Device name in hardware catalog, on icon in network view and in Network overview table. Typically some model name.
DAP module infotext Device detailed description in hardware catalog. Use up to a few hundred characters.
DAP submodule name Codesys show this in the tree on the Device/IOxS page
DAP submodule infotext Unclear?
InterfaceSubmodule Interface name
PortSubmodule Port name
Module name List of plugged modules in Device Overview
Module infotext Catalog information in Properties -> General
Submodule name Codesys show this in the tree on the Device/IOxS page
Submodule infotext Unclear?
======================= ===================================================================================================================
Details on diagnosis
--------------------
The elements ``<ChannelDiagList>`` and ``<UnitDiagTypeList>`` (with
subelements) are used to specify diagnosis functionality.
Use the ``<ChannelDiagList>`` element to describe diagnosis sent in the
standard format::
ChannelDiagList
|
+--ChannelDiagItem
| |
| +--Name
| +--ExtChannelDiagList
| |
| +--ExtChannelDiagItem
| |
| +--Name
|
+--SystemDefinedChannelDiagItem
| |
| +--ExtChannelDiagList
| |
| +--ExtChannelDiagItem
| | |
| | +--Name
| | +--ExtChannelAddValue
| | |
| | +--DataItem
| |
| +--ProfileExtChannelDiagItem
| |
| +--Name
|
+--ProfileChannelDiagItem
|
+--Name
+--ExtChannelDiagList
|
+--ProfileExtChannelDiagItem
|
+--Name
To add a diagnosis with ChannelErrorType in the manufacturer specific range,
use the ``<ChannelDiagItem>`` element. Set the ChannelErrorType with the
attribute ``ErrorType="999"``, for example. Describe it using the
``<Name>`` element. In the ``<ExtChannelDiagItem>`` element, use the
``ErrorType`` attribute for the ExtChannelErrorType. Describe the
ExtChannelErrorType using the ``<Name>`` element.
It is also possible to add your own ExtChannelErrorType to a standard
ChannelErrorType. Use the ``<SystemDefinedChannelDiagItem>`` element,
with the attribute ``ErrorType`` to specify the ChannelErrorType. Add
``<ExtChannelDiagItem>`` elements as we described in the previous paragraph.
The ExtChannelAddValue is specified with the ``<ExtChannelAddValue>`` element,
and ``<DataItem>`` subelements. Use ``Id`` and ``DataType`` attributes in the
subelements.
Similarly use the ``<ProfileChannelDiagItem>`` element to add
ExtChannelErrorType to diagnosis items defined in a profile.
For diagnosis sent in the USI format (also known as manufacturer specific
format), use the ``<UnitDiagTypeList>`` element::
UnitDiagTypeList
|
+--UnitDiagTypeItem
| |
| +--Name
| +--Ref
|
+--ProfileUnitDiagTypeItem
|
+--Name
+--Ref
Add the USI value to the ``<UnitDiagTypeItem>`` element by using the
``UserStructureIdentifier`` attribute.
Specify the data content by setting attributes to the ``<Ref>`` element,
for example ``ByteOffset``, ``DataType``, ``DefaultValue`` and ``TextId``.
It is also possible to add diagnosis in USI format for profiles, by using
the ``<ProfileUnitDiagTypeItem>`` element. Set the ``UserStructureIdentifier``
and ``API`` attributes.
Details on the process alarm list
---------------------------------
This is optional, and is used to give human-readable descriptions to
manufacturer-specific process alarms.
ChannelProcessAlarmList element hierarchy::
ChannelProcessAlarmList
|
+--ChannelProcessAlarmItem
| |
| +--Name
| +--Help
| +--ExtChannelProcessAlarmList
| |
| +--ExtChannelProcessAlarmItem
|
+--SystemDefinedChannelProcessAlarmItem
| |
| +--ExtChannelProcessAlarmList
| |
| +--ExtChannelProcessAlarmItem
| +--ProfileExtChannelProcessAlarmItem
|
+--ProfileChannelProcessAlarmItem
|
+--Name
+--Help
+--ExtChannelProcessAlarmList
|
+--ExtChannelProcessAlarmItem
+--ProfileExtChannelProcessAlarmItem
The element ``<ChannelProcessAlarmItem>`` is used to describe custom process
alarms.
An extension to system defined process alarms is created by the element
``<SystemDefinedChannelProcessAlarmItem>``. Profiles can define process alarms
using the ``<ProfileChannelProcessAlarmItem>`` element.
Details on the graphics list
---------------------------------
File names for graphic items are mapped to IDs here.
GraphicsList element hierarchy::
GraphicsList
|
+--GraphicItem
Within each ``<GraphicItem>`` element, the attributes ``ID`` and ``GraphicFile``
are used to store the information.
The ``GraphicFile`` is the filename without the BMP file extension.

View File

@ -0,0 +1,8 @@
sphinx
recommonmark
sphinx-rtd-theme
breathe
sphinxcontrib-googleanalytics
sphinxcontrib-spelling
rst2pdf
codespell

View File

@ -0,0 +1,380 @@
LAN9662
=======
Introduction
------------
Add description of LAN9662 when datasheet is available.
For links to Microchip LAN9662 resources see section :ref:`lan9662-resources`.
Known limitations:
- One byte write operations are not supported from
LAN9662 RTE to the I/O FPGA. This means the sample submodule
Digital Output 1x8 is not working in full rte mode.
- Submodule default values are not applied on AR teardown
in full rte mode.
LAN9662 Profinet Solution
-------------------------
P-Net implements support for the networking hardware offload features in the LAN9662.
When hardware offload is enabled, Profinet cyclic data frames are handled by the LAN9662 Real
Time Engine (RTE). During AR / connection establishment the RTE is configured by the P-Net stack and
sending and receiving cyclic data is handed over the RTE. The P-Net stack still handles
all non-cyclic Profinet frames.
The cyclic process data is defined by the Profinet device application.
In Profinet the device input data is sent to the controller using the Producer Protocol Machine (PPM) and
the output data is received using the the Consumer Protocol Machine (CPM).
For the PPM side the RTE is configured to read input data sources and build a frame
that is periodically sent to the controller.
For the CPM side the RTE is configured to extract output data in received
frames and write it to configured output sinks.
The RTE process data can be mapped to SRAM or a QSPI HW device.
When hardware offload is enabled all process data by default is mapped to SRAM
by P-Net and the application uses the P-Net API the same way as usual.
To map the process data to a QSPI interface the application uses the P-Net API functions
``pnet_mera_input_set_rte_config()`` for input data and
``pnet_mera_output_set_rte_config()`` for output data.
When a QSPI data source or sink is configured the process data
is handled by the configured HW device and not by the application.
The P-Net stack still keeps a copy of the process data in SRAM
and the application can read the process data from this copy.
All P-Net interactions with the LAN9662 RTE is done through the MERA library provided by Microchip.
Detailed information about the LAN9662 and the RTE can be found in the references in section :ref:`lan9662-resources`.
Table below shows how the the P-Net API for handling the process data is affected when RTE is enabled.
+---------------------------------+-----------+------------------------------------------------------+
| API Function | RTE-SRAM | RTE-QSPI |
+=================================+===========+======================================================+
| pnet_input_set_data_and_iops | Supported | Not supported. Input IO data from QSPI. |
+---------------------------------+-----------+------------------------------------------------------+
| pnet_input_get_data_and_iops | Supported | Supported |
+---------------------------------+-----------+------------------------------------------------------+
| net_input_get_iocs | Supported | Supported |
+---------------------------------+-----------+------------------------------------------------------+
| pnet_output_get_data_and_iops | Supported | Supported for reading output IO data mapped to QSPI. |
+---------------------------------+-----------+------------------------------------------------------+
| pnet_output_set_iocs | Supported | Supported |
+---------------------------------+-----------+------------------------------------------------------+
| pnet_mera_output_set_rte_config | Not used | Used for configuration |
+---------------------------------+-----------+------------------------------------------------------+
| pnet_mera_input_set_rte_config | Not used | Used for configuration |
+---------------------------------+-----------+------------------------------------------------------+
Build-time configuration
------------------------
Support for LAN9962 features in the P-Net stack is enabled using the following CMake options:
- PNET_OPTION_DRIVER_ENABLE - Enable the P-Net support for hardware drivers in general
- PNET_OPTION_DRIVER_LAN9662 - Enable the support for the LAN9662 driver
- PNET_OPTION_LAN9662_SHOW_RTE_INFO - Print the RTE configuration to the application log
The LAN9662 sample is built with the following P-Net options::
set(HAS_PNET ON)
set(BUILD_TESTING OFF)
set(PNET_OPTION_DRIVER_ENABLE ON)
set(PNET_OPTION_DRIVER_LAN9662 ON)
set(PNET_OPTION_LAN9662_SHOW_RTE_INFO OFF)
set(PNET_MAX_PHYSICAL_PORTS "2" CACHE STRING "LAN9962 2 Ports" FORCE)
set(PNET_MAX_SLOTS "13" CACHE STRING "LAN9962 sample application" FORCE)
set(PNET_MAX_SUBSLOTS "4" CACHE STRING "LAN9962 2 Ports" FORCE)
set(LOG_LEVEL "FATAL" CACHE STRING "Enable logging" FORCE)
Run-time configuration
----------------------
The build-time configuration described in previous section enables the support for the LAN9662 driver.
The application decides if the hardware offload shall be used when p-net stack is initialized.
The hardware offload feature is enabled using the driver_enable parameter part of
the configuration (pnet_cfg_t) passed to P-Net during initialization.
When set to true, LAN9662 specific CPM and PPM drivers are used in the P-Net stack and handling of cyclic data frames
is handed off to the LAN9662 RTE during AR establishment.
The driver_config parameter contains base IDs for various resources in the LAN9662 RTE.
In a system where P-Net is the only feature using the RTE these don't need to be considered.
If not, the IDs must be set not to conflict with other parts of the system.
Input data is mapped to QSPI using the operation ``pnet_input_set_rte_config()``.
Output data is mapped to QSPI using the operation ``pnet_output_set_rte_config()``.
EVB-LAN9662
-----------
Both the default P-Net sample application and the LAN9662 sample application can be run on the EVB-LAN9662.
Utility scripts for configuration of LEDs and buttons and starting the applications are provided in the ``p-net/samples/pn_dev_lan9662/`` directory.
EVB-LAN9662 Features used by P-Net samples:
- Shell is available on the EVB-LAN9662 USB connector marked ``CONSOLE``. Use 115200 baud, no flow control.
- The io-fpga is connected to the EVB-LAN9662-Carrier USB connector marked ``FPGA SPI``.
- Two ethernet ports are supported. Sample scripts show how to configure a network bridge.
LAN9662 Sample Application
--------------------------
The application focus on the process data and its mapping to the RTE.
The source code is is found in ``/p-net/samples/pn_dev_lan9662/``.
The sample application builds for and runs on the EVB-LAN9662.
It supports the following I/O-data:
============== ======================= =========================================== ============
[Slot,Subslot] Name Shared memory area FPGA addr [func]
============== ======================= =========================================== ============
[1,1] Digital Input 1x8 /dev/shm/pnet-in-1-1-digital_input_1x8 0x100 [mem]
[2,1] Digital Output 1x8 /dev/shm/pnet-out-2-1-digital_output_1x8 0x104 [mem]
[3,1] Digital Input 1x64 /dev/shm/pnet-in-3-1-digital_input_1x64 0x108 [mem]
[4,1] Digital Input 2x32a /dev/shm/pnet-in-4-1-digital_input_2x32_a 0x110 [mem]
[5,1] Digital Input 2x32b /dev/shm/pnet-in-5-1-digital_input_2x32_b 0x118 [mem]
[6,1] Digital Input 1x800 /dev/shm/pnet-in-6-1-digital_input_1x800 0x120 [mem]
[7,1] Digital Output 1x64 /dev/shm/pnet-out-7-1-digital_out_1x64 0x184 [mem]
[8,1] Digital Output 2x32a /dev/shm/pnet-out-8-1-digital_output_2x32_a 0x18c [mem]
[9,1] Digital Output 2x32b /dev/shm/pnet-out-9-1-digital_output_2x32_b 0x194 [mem]
[10,1] Digital Output 1x800 /dev/shm/pnet-out-10-1-digital_output_1x800 0x19c [mem]
[11,1] Digital Input Port A Not supported 0x200 [gpios]
[12,1] Digital Output Port A Not supported 0x10 [gpios]
============== ======================= =========================================== ============
Note that the I/Os on slots 11 and 12 are available at the EVB-LAN9662 pin lists IN-A and OUT-A.
The sample application gsdml file is available at ``/p-net/samples/pn_dev_lan9662/``.
The application has three modes of operation. The mode is a runtime configuration defined by the mode (-m) argument:
-m none RTE disabled. Application process data mapped to shared memory.
-m cpu RTE enabled. RTE maps process data to SRAM. Application process data mapped to shared memory.
-m full RTE enabled. RTE maps process data to QSPI. Application process data mapped to io-fpga.
**Mode none**
In this mode the input/output data is mapped to shared memory.
The shared memory can be accessed using the pn_shm_tool or by another
application in the system. HW offload is disabled and the default data
path of the P-Net stack is used.
The shared memory is accessed using the pn_shm_tool. Run ``/usr/bin/pn_shm_tool -h`` for further details.
**Mode cpu**
This mode shows how to use the P-Net with LAN9662 RTE SRAM data.
Also in this mode the input/output data is mapped to shared memory.
The shared memory can be accessed using the pn_shm_tool or by another
application in the system. HW offload is enabled and the cyclic data is
is handled by the LAN9662 RTE. To a user of the application it is no
difference to the "none"-mode. However P-Net copies application process
data to SRAM which is mapped to the cyclic data frames handled by the RTE.
The shared memory is accessed using the pn_shm_tool. Run ``/usr/bin/pn_shm_tool -h`` for further details.
**Mode full**
This mode shows how to use P-Net with LAN9662 RTE QSPI data.
Shared memory is not used. The input/output data is mapped to
the IO-FPGA on the EVB-LAN9662.
The fpga is accessed using the mera-iofpga-rw tool. Run ``mera-iofpga-rw -h`` for further details.
Note that Port A outputs and Port A inputs can be accessed without the mera-iofpga-rw tool since they are physically available on the EVB-LAN9662.
Note that the mera-iofpga-rw tool is run on a host system, not on the LAN9662.
See :ref:`lan9662-resources` for further information on the io-fpga tool.
Running the LAN9662 Sample Application
--------------------------------------
Start the LAN992 sample application using the script ``switchdev-profinet-example.sh``.
The log from a scenario with a PLC using input port A and output port A is shown below.
log::
switchdev-profinet-example.sh
Starting switchdev-profinet-example
[ 31.365807] EXT4-fs (mmcblk0p2): recovery complete
[ 31.371838] EXT4-fs (mmcblk0p2): mounted filesystem with ordered data mode. Opts: (null). Quota mode: disabled.
ANA_PGID[61] = 0x0000010f -> 0x000001ff
net.ipv6.conf.br0.disable_ipv6 = 1
[ 31.647312] lan966x-switch e2000000.switch eth0: PHY [e200413c.mdio-mii:01] driver [Microchip INDY Gigabit Internal] (irq=POLL)
[ 31.658804] lan966x-switch e2000000.switch eth0: configuring for phy/gmii link mode
[ 31.786815] 8021q: adding VLAN 0 to HW filter on device eth0
[ 31.877381] lan966x-switch e2000000.switch eth1: PHY [e200413c.mdio-mii:02] driver [Microchip INDY Gigabit Internal] (irq=POLL)
[ 31.888861] lan966x-switch e2000000.switch eth1: configuring for phy/gmii link mode
[ 32.016817] 8021q: adding VLAN 0 to HW filter on device eth1
[ 32.032693] br0: port 1(eth0) entered blocking state
[ 32.037722] br0: port 1(eth0) entered disabled state
[ 32.042987] device eth0 entered promiscuous mode
[ 32.047909] br0: port 1(eth0) entered blocking state
[ 32.052820] br0: port 1(eth0) entered forwarding state
[ 32.068835] br0: port 2(eth1) entered blocking state
[ 32.073767] br0: port 2(eth1) entered disabled state
[ 32.079243] device eth1 entered promiscuous mode
[ 32.084021] br0: port 2(eth1) entered blocking state
[ 32.088977] br0: port 2(eth1) entered forwarding state
QSYS_SW_PORT_MODE[4] = 0x00005002 -> 0x00045000
Starting LAN9662 Profinet sample application
RTE mode: full
** Starting P-Net sample application 0.2.0 **
Number of slots: 13 (incl slot for DAP module)
P-net log level: 2 (DEBUG=0, FATAL=4)
App log level: 0 (DEBUG=0, FATAL=4)
Max number of ports: 2
Network interfaces: br0,eth0,eth1
Button1 file:
Button2 file:
Default station name: lan9662-dev
Management port: br0 12:A9:2D:16:93:83
Physical port [1]: eth0 12:A9:2D:16:93:81
Physical port [2]: eth1 12:A9:2D:16:93:82
Hostname: vcoreiii
IP address: 0.0.0.0
Netmask: 0.0.0.0
Gateway: 0.0.0.0
Storage directory: /tmp/pn_data
Application RTE mode "full"
Slot [1,1] Digital Input 1x8 mapped to FPGA address 0x100
Slot [2,1] Digital Output 1x8 mapped to FPGA address 0x104
Slot [3,1] Digital Input 1x64 mapped to FPGA address 0x108
Slot [4,1] Digital Input 2x32 a mapped to FPGA address 0x110
Slot [5,1] Digital Input 2x32 b mapped to FPGA address 0x118
Slot [6,1] Digital Input 1x800 mapped to FPGA address 0x120
Slot [7,1] Digital Output 1x64 mapped to FPGA address 0x184
Slot [8,1] Digital Output 2x32 a mapped to FPGA address 0x18c
Slot [9,1] Digital Output 2x32 b mapped to FPGA address 0x194
Slot [10,1] Digital Output 1x800 mapped to FPGA address 0x19c
Slot [11,1] Digital Input Port A mapped to FPGA address 0x200
Slot [12,1] Digital Output Port A mapped to FPGA address 0x10
Profinet signal LED indication. New state: 0
LED 2 new state 0
Network script for br0: Set IP 0.0.0.0 Netmask 0.0.0.0 Gateway 0.0.0.0 Permanent: 1 Hostname: lan9662-dev Skip setting hostname: true
No valid default gateway given. Skipping setting default gateway.
LED 1 new state 0
Plug DAP module and its submodules
Module plug indication API 0
[0] Pull old module
[0] Plug module. Module ID: 0x1 "DAP 1"
Submodule plug indication API 0
[0,1] Pull old submodule.
[0,1] Plug submodule. Submodule ID: 0x1 Data Dir: NO_IO In: 0 Out: 0 "DAP Identity 1"
Submodule plug indication API 0
[0,32768] Pull old submodule.
[0,32768] Plug submodule. Submodule ID: 0x8000 Data Dir: NO_IO In: 0 Out: 0 "DAP Interface 1"
Submodule plug indication API 0
[0,32769] Pull old submodule.
[0,32769] Plug submodule. Submodule ID: 0x8001 Data Dir: NO_IO In: 0 Out: 0 "DAP Port 1"
Submodule plug indication API 0
[0,32770] Pull old submodule.
[0,32770] Plug submodule. Submodule ID: 0x8002 Data Dir: NO_IO In: 0 Out: 0 "DAP Port 2"
Waiting for PLC connect request
[ 32.544399] br0: port 1(eth0) entered disabled state
[ 32.555613] br0: port 2(eth1) entered disabled state
[ 34.164904] lan966x-switch e2000000.switch eth1: Link is Up - 100Mbps/Full - flow control off
[ 34.173514] IPv6: ADDRCONF(NETDEV_CHANGE): eth1: link becomes ready
[ 34.179964] br0: port 2(eth1) entered blocking state
[ 34.184904] br0: port 2(eth1) entered forwarding state
[ 34.884950] lan966x-switch e2000000.switch eth0: Link is Up - 1Gbps/Full - flow control rx/tx
[ 34.893743] IPv6: ADDRCONF(NETDEV_CHANGE): eth0: link becomes ready
[ 34.900201] br0: port 1(eth0) entered blocking state
[ 34.905155] br0: port 1(eth0) entered forwarding state
[ 37.214719] NOHZ tick-stop error: Non-RCU local softirq work is pending, handler #08!!!
Network script for br0: Set IP 192.168.0.50 Netmask 255.255.255.0 Gateway 192.168.0.50 Permanent: 0 Hostname: lan9662-dev Skip setting hostname: true
[ 39.225858] NOHZ tick-stop error: Non-RCU local softirq work is pending, handler #08!!!
[ 39.235956] NOHZ tick-stop error: Non-RCU local softirq work is pending, handler #08!!!
Module plug indication API 0
[11] Pull old module
[11] Plug module. Module ID: 0x100b "DI Port A"
Submodule plug indication API 0
[11,1] Pull old submodule.
[11,1] Plug submodule. Submodule ID: 0x200b Data Dir: INPUT In: 4 Out: 0 "Digital Input Port A"
[11,1,"Digital Input Port A"] Set RTE QSPI address 0x200
Module plug indication API 0
[12] Pull old module
[12] Plug module. Module ID: 0x100c "DO Port A"
Submodule plug indication API 0
[12,1] Pull old submodule.
[12,1] Plug submodule. Submodule ID: 0x200c Data Dir: OUTPUT In: 0 Out: 4 "Digital Output Port A"
[12,1,"Digital Output Port A"] Set RTE QSPI address 0x10
PLC connect indication. AREP: 1
ANA_RT_VLAN_PCP[1].PCP_MASK = 0x00000000 -> 0x000000ff
ANA_RT_VLAN_PCP[1].VLAN_ID = 0x00000000 -> 0x00000000
ANA_RT_VLAN_PCP[1].VLAN_PCP_ENA = 0x00000000 -> 0x00000001
vcap add is1 10 3 s1_rt first 0 rt_vlan_idx 1 0x7 l2_mac 12:A9:2D:16:93:83 ff:ff:ff:ff:ff:ff rt_type 1 0x3 rt_frmid 32769 0xffff s1_rt rtp_id 5 fwd_ena 1 fwd_mas0
key field first: value: 0
key field rt_vlan_idx: value: 01 mask: 07
key field l2_mac: value: 8393162da912 mask: ffffffffffff
key field rt_type: value: 01[ 39.549942] NOHZ tick-stop error: Non-RCU local softirq work is pending, handler #08!!!
mask: 03
key field rt_frmid: value: 00008001 mask: 0000ffff
act field rtp_id: val[ 39.565901] NOHZ tick-stop error: Non-RCU local softirq work is pending, handler #08!!!
ue: 0x5
act field fwd_ena: value: 0x1
act field fwd_mask: value: 0x10
Event indication PNET_EVENT_STARTUP AREP: 1
PLC dcontrol message. AREP: 1 Command: PRM_END
Event indication PNET_EVENT_PRMEND AREP: 1
[0,1,"DAP Identity 1"] Set input data and IOPS. Size: 0 IOPS: GOOD
[0,32768,"DAP Interface 1"] Set input data and IOPS. Size: 0 IOPS: GOOD
[0,32769,"DAP Port 1"] Set input data and IOPS. Size: 0 IOPS: GOOD
[0,32770,"DAP Port 2"] Set input data and IOPS. Size: 0 IOPS: GOOD
[11,1,"Digital Input Port A"] Set input data and IOPS. Size: 4 IOPS: GOOD
[12,1,"Digital Output Port A"] Set output IOCS: GOOD
vcap add is1 10 2 s1_rt first 0 l2_mac 12:A9:2D:16:93:83 ff:ff:ff:ff:ff:ff rt_vlan_idx 0 0x7 rt_frmid 32768 0xffff s1_rt rtp_id 4 rtp_subid 0 rte_inb_upd 1 fwd_e0
key field first: value: 0
key field l2_mac: value: 8393162da912 mask: ffffffffffff
key field rt_vlan_idx: value: 00 mask: 07
key field rt_frmid: value: 00008000 mask: 0000ffff
act field rtp_id: value: 0x4
act field rtp_subid: value: 0x0
act field rte_inb_upd: value: 0x1
act field fwd_ena: value: 0x1
act field fwd_mask: value: 0x10
[11,1,"Digital Input Port A"] PLC reports Consumer Status (IOCS) GOOD
Application will signal that it is ready for data, for AREP 1.
Event indication PNET_EV[ 39.883951] NOHZ tick-stop error: Non-RCU local softirq work is pending, handler #08!!!
ENT_APPLRDY AREP: 1
Event indication PNET_EVENT_DATA AREP: 1
Cyclic data transmission started
PLC ccontrol message confirmation. AREP: 1 Status codes: 0 0 0 0
[12,1,"Digital Output Port A"] PLC reports Provider Status (IOPS) GOOD
Building the LAN9662 Sample Application
---------------------------------------
Add step by step guide describing how to build the LAN9662
sample application and which Microchip resources to download
when that information is available.
P-Net on LAN9962 Application Summary
------------------------------------
- To map process data to QSPI the application must use the operations ``pnet_output_set_rte_config()`` and ``pnet_input_set_rte_config()``
- If process data is handled by the application and not mapped to QSPI the hardware offload can be enabled and used without any change in the application. API usage is identical except that the hardware offload is enabled during stack initialization.
- ``p-net/samples/pn_dev_lan9662/switchdev-profinet-example.sh`` shows the required systems configurations for a 2 port Profinet device application.
.. _lan9662-resources:
LAN9662 Resources
-----------------
Microchip provides pre-built buildroot images for the LAN9662 which is used
to run the Profinet sample application:
- http://mscc-ent-open-source.s3-eu-west-1.amazonaws.com/index.html?prefix=public_root/
To build the Profinet sample application the following bsp and toolchain are used:
- http://mscc-ent-open-source.s3-eu-west-1.amazonaws.com/public_root/bsp/mscc-brsdk-arm-2021.09.tar.gz
- http://mscc-ent-open-source.s3-eu-west-1.amazonaws.com/public_root/toolchain/mscc-toolchain-bin-2021.02-090.tar.gz
Documentation and data sheets:
- http://mscc-ent-open-source.s3-eu-west-1.amazonaws.com/public_root/bsp/mscc-brsdk-doc-2021.09.html
- Add more documents when LAN9662 documentation is available.

View File

@ -0,0 +1,50 @@
Getting started using STM32Cube / FreeRTOS / lwIP
=================================================
This tutorial uses the STM32F769i discovery kit, see
https://www.st.com/en/evaluation-tools/32f769idiscovery.html
Preparations
------------
Run the build on a Linux x86 machine. The build chain uses an ARM cross compiler. Install it::
sudo apt install gcc-arm-none-eabi
Install ST-link on your Linux machine to have access to the serial port of the device.
Cloning STM32Cube firmware
--------------------------
First, clone the relevant STM32Cube repo for your platform. For
instance, to build for STM32F7 (fetching a specific tag only)::
git clone https://github.com/STMicroelectronics/STM32CubeF7.git \
-b v1.16.1 --single-branch --depth 1
Download and compile p-net
--------------------------
Clone the source::
git clone --recurse-submodules https://github.com/rtlabs-com/p-net.git
This will clone the repository with submodules. If you already cloned
the repository without the ``--recurse-submodules`` flag then run this
in the ``p-net`` folder::
git submodule update --init --recursive
Then, when configuring specify CPU, board and path to the cloned git
repository::
CPU=cortex-m7fd BOARD=STM32F769I-DISCO CUBE_DIR=/path/to/cube cmake \
-B build.cube \
-DCMAKE_TOOLCHAIN_FILE=cmake/tools/toolchain/stm32cube.cmake \
-G "Unix Makefiles"
cmake --build build.cube
When the build completes you can find the sample-app binary in
``build.cube/pn_dev.bin``. Flash this binary to your board by
uploading it to the USB disk as usual for STM32 boards.
A serial port will also be available via the USB cable.

View File

@ -0,0 +1,380 @@
Getting started using rt-kernel
===============================
Evaluation board details
------------------------
This example uses the Infineon evaluation board "XMC4800 Relax EtherCAT Kit".
It has a Cortex®-M4 running at 144 MHz, and it has 352 kB RAM and 2 MB flash.
Details are found on
https://www.infineon.com/cms/en/product/evaluation-boards/kit_xmc48_relax_ecat_v1/
You can use other evaluation boards supported by rt-kernel, but you need to
adapt paths etc accordingly.
Install tools on a Linux Laptop
-------------------------------
Install (see description elsewhere):
* Workbench (Version 2020.1 or later)
* Compiler
* rt-kernel, use variant ``rt-kernel-xmc4``
* Segger J-link
* Run ``pip install pyyaml``
* cmake (version 3.14 or later)
Copy the rt-kernel sources
--------------------------
Run (adapt paths)::
cp -r /opt/rt-tools/rt-kernel-xmc4 /home/jonas/projects/profinetstack/rtkernelcopy/
Patch the source by using the patch file available in the p-net repository.
Verify that you have the version of rt-kernel that the patch file is intended for.
Run this in the root folder of the rt-kernel directory::
patch -p1 < PATH_TO_PNET/src/ports/rt-kernel/0001-rtkernel-for-Profinet.patch
You may want to change IP settings in ``rt-kernel-xmc4/bsp/xmc48relax/include/config.h``.
In the root folder of the rt-kernel directory::
source setup.sh xmc48relax
make clean
make -j
Download and compile p-net
--------------------------
Clone the source::
git clone --recurse-submodules https://github.com/rtlabs-com/p-net.git
This will clone the repository with submodules. If you already cloned
the repository without the ``--recurse-submodules`` flag then run this
in the ``p-net`` folder::
git submodule update --init --recursive
The CMake build files for rt-kernel need to know which BSP you want to
use, as well as the path to your rt-kernel source tree. Run the
following to create and configure the build::
RTK=<PATH TO YOUR MODIFIED>/rt-kernel-xmc4 \
BSP=xmc48relax \
cmake -B build -S p-net \
-DCMAKE_TOOLCHAIN_FILE=cmake/tools/toolchain/rt-kernel.cmake \
-DCMAKE_ECLIPSE_EXECUTABLE=/opt/rt-tools/workbench/Workbench \
-DCMAKE_ECLIPSE_GENERATE_SOURCE_PROJECT=TRUE -G "Eclipse CDT4 - Unix Makefiles"
If you are using Windows, start the program "Command line" found in
the rt-labs rt-collab Toolbox installation (adapt paths to your setup)::
cd /c/Users/rtlfrm/Documents/Profinet
RTK=/c/Users/rtlfrm/Documents/Profinet/rt-kernel-xmc4 \
BSP=xmc48relax \
/c/Program\ Files/CMake/bin/cmake.exe -B build -S p-net \
-DCMAKE_TOOLCHAIN_FILE=cmake/tools/toolchain/rt-kernel.cmake \
-DCMAKE_ECLIPSE_EXECUTABLE=/opt/rt-tools/workbench/Workbench \
-DCMAKE_ECLIPSE_GENERATE_SOURCE_PROJECT=TRUE -G "Eclipse CDT4 - Unix Makefiles"
Depending on how you installed cmake, you might need to run ``snap run
cmake`` instead of ``cmake``. This creates build files in a folder
named ``build``. You can choose any name for the build folder, for
instance if you want to build for multiple targets.
After running ``cmake`` you can run ``ccmake build`` or ``cmake-gui``
in the build folder to change settings.
Start Workbench::
/opt/rt-tools/workbench/Workbench
Two Workbench projects have been created by cmake. One project is for building
the code, the other is for the p-net source code. You need at least to
import the project for building the code.
Use the menu "File > Import". Select "General > Existing Projects". Click
"Browse" and select the ``build`` directory that was created earlier.
Use the menu "Project > Build All" to build it.
An OSAL layer hosted in a separate directory is used by p-net, and is installed
automatically during setup. For details see the tutorial page
in this documentation.
More Workbench settings
-----------------------
If you intend to edit the p-net source code in the Workbench tool, you
should also import the p-net source project. The files ``.project``
and ``.cproject`` have been created in the p-net repo by cmake.
Use the menu "File > Import". Select "General > Existing Projects". Click
"Browse" and select the ``p-net`` repo directory.
After importing, right-click on the project and choose "New > Convert
to a C/C++ project". This will setup the project so that the indexer
works correctly and the Workbench revision control tools can be used.
Run on target
-------------
Install J-link from https://www.segger.com/
Start Segger J-link GDB debug server::
JLinkGDBServerExe
Select "USB" and target device "XMC4800-2048" in the GUI. Use "Little Endian",
Target interface "SWD" and speed "Auto Selection".
Run the compiled code on target by right-clicking the Profinet build project,
and selecting "Debug as > Hardware debugging". Select J-Link.
On the "Startup" tab enter ``monitor reset 0`` in the "Run commands".
Click Apply and Close. Select ``pn_dev.elf`` and click OK.
The download progress pop-up window should appear.
The resulting ``.elf`` file contains the sample application, the p-net stack,
the rt-kernel, lwip and drivers.
If you need to adjust debugger settings later, right-click the Profinet build
project, and select "Debug as > Debug configurations". Select the "Profinet... "
node. You might need to double click "Hardware Debugging" if the child node
does not appear. Typically these values have been automatically entered:
* Tab "Main" C/C++ application: ``pn_dev.elf``.
* Tab "Debugger". Debugger type J-Link. GDB command:
``${COMPILERS}/arm-eabi/bin/arm-eabi-gdb``.
To be able to view register content, use the MMR tab in the debug view. Select
core "XMC4800".
Open a terminal to view the debug output from the target, which will appear as
for example ``/dev/ttyACM0``. An example of a terminal program is picocom
(add yourself to the ``dialout`` user group to avoid using sudo)::
sudo picocom -b 115200 /dev/ttyACM0
You can step-debug in the Workbench GUI. Press the small "Resume" icon to have
the target run continuously.
Adjust log level
----------------
In order to learn the Profinet communication model, it is very informative to
adjust the log level to see the incoming and outgoing messages. See the
tutorial page for details on how to adjust the log level.
However note that printing out log strings is slow, so you probably need
to decrease the cyclic data frequency (see PLC timing settings below).
It is recommended to use log level ERROR when running with short cycle times
on a microcontroller, in order not to interfere with the real-time
requirements of the Profinet communication.
Standalone rt-kernel project
----------------------------
This creates standalone makefiles.
Configure the build::
RTK=<PATH TO YOUR MODIFIED>/rt-kernel-xmc4 \
BSP=xmc48relax \
cmake -B build -S p-net \
-DCMAKE_TOOLCHAIN_FILE=cmake/tools/toolchain/rt-kernel.cmake \
-G "Unix Makefiles"
Build the code::
cmake --build build
Serial port baud rate
---------------------
If you like to increase the baud rate of the serial port, change the value in
the file ``bsp/xmc48relax/src/xmc48relax.c``. For example change
``.baudrate = 115200,`` to ``.baudrate = 460800,``.
To be able to run debug logging via serial cable, you need to increase the
baudrate to 460800 bits/s.
PLC timing settings
-------------------
The send clock is 1 ms in the GSDML file.
If you do lots of printouts (which are slow) from the application on the
XMC4800 board, you might need to increase the reduction ratio in the PLC
settings to avoid timeout errors.
In case of problems, increase the reduction ratio (and timeout) value a lot,
and then gradually reduce it to find the smallest usable value.
Using the built-in rt-kernel shell
----------------------------------
Press Enter key to enter the built-in rt-kernel shell via the serial console.
To view a list of available commands, use::
help
Example commands::
ls /disk1
hexdump /disk1/pnet_data_ip.bin
rm /disk1/pnet_data_ip.bin
pnio_factory_reset
pnio_remove_files
pnio_show
Memory requirements for the tests
---------------------------------
Note that the tests require a stack of at least 6 kB. You may have to increase
``CFG_MAIN_STACK_SIZE`` in your BSP ``include/config.h`` file.
Examining flash and RAM usage
-----------------------------
The flash and RAM usage is shown by the tool ``arm-eabi-size``.
In this example we use::
CMAKE_BUILD_TYPE Release
LOG_LEVEL Warning
PNET_MAX_AR 1
PNET_MAX_SLOTS 5
PNET_MAX_SUBSLOTS 3
To estimate the binary size, link partially (without standard
libraries). This example is for cortex-m4f MCU:s, such as XMC4800::
build$ make all
build$ /opt/rt-tools/compilers/arm-eabi/bin/arm-eabi-gcc -O3 -DNDEBUG -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 CMakeFiles/pn_dev.dir/samples/pn_dev/sampleapp_common.o CMakeFiles/pn_dev.dir/src/ports/rt-kernel/sampleapp_main.o -o pn_dev.elf libprofinet.a -nostartfiles -nostdlib -r
Study the resulting executable::
build$ arm-eabi-size pn_dev.elf
text data bss dec hex filename
127421 16 1388 128825 1f739 pn_dev.elf
Values in bytes (including the rt-kernel RTOS).
* text: code in flash
* data: Memory, statically initialized
* bss: Memory, zero-initialized. For example the stack.
* dec = text + data + bss
* hex = text + data + bss (in hexadecimal)
The flash usage is text + data, as the RAM initialization values are stored in flash.
Run tests on XMC4800 target
---------------------------
In order to compile the test code, make sure to use ``BUILD_TESTING`` and that
``TEST_DEBUG`` is disabled. Reduce ``PNET_MAX_FILENAME_SIZE`` to 30 bytes.
This is done via ccmake, which should be started in the build directory::
ccmake .
Set ``CFG_MAIN_STACK_SIZE`` to at least 8192 in ``rt-kernel-xmc4/bsp/xmc48relax/include/config.h``
The resulting file after compiling is named ``pf_test.elf``
Add a new hardware debugging configuration, where the C/C++ application on the
"Main" tab is set to ``pn_dev.elf``.
The test will run on the target board when starting hardware debugging.
You might need to press the Play button in the Workbench if you have enabled
breakpoints.
Run tests on the QEMU emulator
------------------------------
On a Linux laptop, install the package ``rt-collab-qemu``.
Patch the rt-kernel. Use the BSP "integrator", which is intended for emulation.
You need to increase the main stack size in ``rt-kernel/bsp/integrator/include/config.h``.
Modify ``CFG_MAIN_STACK_SIZE``.
In the root folder of the rt-kernel directory::
source setup.sh integrator
make clean
make -j
In the parent directory of ``p-net``, configure a new build directory::
RTK=<PATH TO YOUR MODIFIED>/rt-kernel \
BSP=integrator \
cmake -B build.integrator -S p-net \
-DCMAKE_TOOLCHAIN_FILE=cmake/tools/toolchain/rt-kernel.cmake \
-G "Unix Makefiles"
If necessary adjust the settings::
ccmake build.integrator/
Build only the ``pf_test`` binary::
cmake --build build.integrator/ -j --target pf_test
Start the emulator::
/opt/rt-tools/qemu/bin/qemu-system-arm -M integratorcp -nographic -semihosting -kernel build.integrator/pf_test.elf
If you add ``-s`` it it possible to connect with ``gdb`` to port 1234 from
Workbench. By adding ``-S`` qemu will wait for gdb to connect.
To send a command line argument to the gtest binary, add ``--append "<gtest_command"``.
For example ``--append "--gtest_filter=AlarmUnitTest*"`` or
``--append "--gtest_filter=CmrpcTest.CmrpcConnectReleaseTest"``.
Exit QEMU with CTRL-A X (not CTRL-A CTRL-X).
SNMP
----
To enable SNMP support, set the ``PNET_OPTION_SNMP`` value to ``ON``.
See :ref:`network-topology-detection` for more details on SNMP and how to
verify the SNMP communication to the p-net stack.
IP-stack LWIP
-------------
The rt-kernel uses the "lwip" IP stack.
To enable logging in lwip, modify the file
``rt-kernel-xmc4/lwip/src/include/lwip/lwipopts.h``.
Make sure general logging is enabled::
#define LWIP_DEBUG 1
#define LWIP_DBG_MIN_LEVEL LWIP_DBG_LEVEL_ALL
#define LWIP_DBG_TYPES_ON LWIP_DBG_ON
And enable debug logging of the modules you are interested in::
#define PBUF_DEBUG LWIP_DBG_OFF
#define IP_DEBUG LWIP_DBG_ON
#define IGMP_DEBUG LWIP_DBG_ON
#define TCPIP_DEBUG LWIP_DBG_ON
Rebuild rt-kernel.
Increase LWIP resources
-----------------------
In order to handle incoming data, you might need to increase buffer sizes for
the lwip IP stack.
In the file ``lwip/src/include/lwip/lwipopts.h`` or in
``lwip/src/include/lwip/opt.h`` (which holds the default values), increase the
values for ``MEMP_NUM_NETBUF`` and ``PBUF_POOL_SIZE``.
It can also be beneficial to increase the values ``eth_cfg.rx_buffers``
and ``eth_cfg.rx_task_prio`` found in the ``bsp/xmc48relax/src/lwip.c`` file.
For debugging you can enable ``LWIP_STATS_DISPLAY`` in the ``lwipopts.h`` file,
and then trigger the ``stats_display()`` function.

View File

@ -0,0 +1 @@
<mxfile host="app.diagrams.net" modified="2020-08-13T05:56:15.749Z" agent="5.0 (X11)" etag="inc1hbK5Dvu5yFF7klPa" version="13.6.2" type="device"><diagram id="VVN3v7ZgQKKg46ZfP3pY" name="Page-1">7Vzbl5o4HP5rPGf6IAcSbj462tvZaet2utvdpzmoUdkisZCZcfrXbwJByUXRFlA748MMCRjI9/3uiXTgYLl+mwSrxQc8RVEHmNN1Bw47AFjQceg/1vOU93iemXfMk3DKL9p23IY/EO8sLrsPpygVLiQYRyRciZ0THMdoQoS+IEnwo3jZDEfiXVfBHCkdt5MgUnu/hlOy4L2W29ueeIfC+YLf2gdefmIZFBfzmaSLYIofS13wdQcOEoxJfrRcD1DEwCtwyb/3ZsfZzYMlKCaHfOGrP/x08+f443C4+PY5HQcPQYK7Hh/mIYju+Yz505KnAoIE38dTxEaxOvD6cRESdLsKJuzsIyWd9i3IMuKnp0G62Fw7wzHhlFI+aTuMogGOcEI7YhzT/usgmfArAP/2KCAEJXE2BDDZMOpMi6dGCUHrUhef+VuEl4gkT/QSftaFnAUuhlbByuOWVMfkfYsSn3YhiAEXpPlm7C3W9IDDfQT00H0e0Nvnh7wCfP9m9OH9HvTNavRrQEqGCvR0UKlIAfN4pPD4P2YugRkFYxRxFFarKJwEJMRxPt5eFN3SXDjqJexoBzXRqBXcHBE26ALDUYDzNMBB52jcaJNDd4TA+VqB+3xygfN9SeB8VeBcncA1ppo9FanRh9uTAwV71ZqpEzCnKZwKAkSgTi9RMlC2fWqg1ADnLIDyrHOTKHCeqicDdXqJcrQSdde/GwYkMMJ4qmBGQ/8VO0zDeB6hPktLRJwyJzzCaZj5XjicULwQDdWuGU7UJUc30gUEM6SDKJxrL+/zE2NMCF5qwsE6iJGdh0aCLV1gBxpjRvUeB1JRIcYzGsu848dZUlmkgqYBiy6OrmlYpm25pY9XUxjtVuvBjuiwIbiLTFcOa+76UZAs7z5iEs54OHkhWpFgEvChYE1a0pODU1MNTdvVErAjGuW09SffjAR9vyy2uhldZTVlx3/X54VkGm14amMHVGN3lbugu0k8e9UBbkQf4Xqc0KM5O2LevHBSl0Fwmc8GHJbOgrbLITyg+obiKediOImCNA0nkm+SQBOqQPTLpRIPWofkH3bGMCHk7X8zn+U5RXu45pFd1ngqNUYoCem0GYXDvcYxxffJBB0QaqKpUHtViauoexR9CYqoIXgQK7Y6svgdRjiMSUkupJzFlUPHfEL8W6BUYZUG8j1fFLCiXQxEgmSOiDJQJjubaf+COOlD+ELpqVlQpYtScJNXgMpCVamzuWonKA1/BONsMCYSKzazbK7OdccZaqTzaBuyNQ57jcF+9ZJNxGa5gD97p1yR15kO07CBQK1Vi+R16cCQmpztR7xL1zJ64ph4NktRM8KjVkV/N39Sgw8BBxSCWvYh9u/oQ87ENUDfM3xbYByCnuE6ve3H+zlnAXrAsOzSOMpdBMvgtutJtDWOZ+xJCiU7V0/CxoUlYeqJkUiXnjZ7wqc1v6Kmm3QSfdqT5ZvaygDFmIhilJIEf0PSAqZuTbNK3JbhdMpuoy35iLXNXeupwGunVLBZein7Ft1yDGzKtxSiWuIOlrlL0tVVB9C5v+m+unASu7WxCKorBZtooB0W1cjOEljUBG8XRp5dE3lySdvTFOva1UA1urPL3FFPfHXpqleX5nmeIUVRtq3bCWD1WmVQE0qx3Sa6KvllpFGK4k3DhEZAec8jSolUfq25jGdJW7kgOHVF3T5gJ9f50Vi9kFUPX/LClS5lbnfhylYXrhS+GkiZiyS4SJf3Zss7ga+urp5XCu1IFRMoW9pDE2bb6RmaDGY7rFEupLWcMDtqJb+iepafvLzsp65Kmu1JcbJmX6h271lTVsFRw+TDrMIRpmAnbmeirMoyJ5B2SB6qrJYp7WWFvsRb0wqp+mR5lVtXxboA3SuHX8CpKWndxVY5prJa1Ua1bNSoj47pQ2+cNGuUvTRrb9101qr007l87/PT4KxU33YdUfU9R3Co0oCHGgLPNOVhW7UDhQsp24Hc9V5e2aMp17vZP3kq1+vqN+le1pbKmtNceaXx9LtVXKB1qdpKhs63nh9lchFqTyGjzHBNamgByeJa9qkZVquNz95YuvaZ5SmuWlD86VTzd1m0rW2jj+uZuqhHXpE7YouPOELXFgdobt3V1SRAz12XlZrDob93a0yXNVvonztJssGFmgXUdknSbJh/MakHm1TH3GdSlXJwfbRpfkv6QltNntCWy3m10eapWcaX/uCPF8Iq9czfq2dyJbc+wtSk4YWwwwjTVtg2hTLQFGFqAlHO3emZ0fCvCws+5D0ldQWMvmeISTrQpH+g+E1fKxtKPE3MyNQtZ818IS7TLWkXHtTt07c1UWRztOkWNPJUfWEJjLnf7zHhKHXTDCa24QuA1Xp7skjuvyThfI6SlD0yK8Op6X/RQ5+a3UjsXB1yZwtq7/y0YrIQMdTJAtG/7z91h+ghpEKw7yFW277aJDWlohfGc9rhbFtfmOMYdsGu9xRgKk6zKFs+WlBBRvF+odW/IKmJBEgUXUv3U1PdGw+8pkTX1/1M8ddF9zMKJoQJLsFnIr0D+s0ERxE1dsC8Gt0MNNW0F0k+JpV3KkVZ6zx/Irv49Rda+aA0mdO+0coy5dcGOCpyzbzRija37yTMi5HbNzvC1/8D</diagram></mxfile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 451 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

View File

@ -0,0 +1 @@
<mxfile host="app.diagrams.net" modified="2020-11-26T14:30:30.069Z" agent="5.0 (X11)" etag="U9fMjwx7IcZsPsHZ0MTZ" version="13.10.3" type="device"><diagram id="Sqc9-NYhzWLnmHblXexb" name="Page-1">7VpJc6M4GP01rpo5mGK1zbGzTTKV1LgrXZPOKSWDDJrIiAh561/fkhGLANt0bCdUj30x+pCEeO/paUE963K2+ouCOHwgPsQ9U/dXPeuqZ5qG5Tj8T0TWaWQ41NNAQJEvMxWBR/QDymCWbY58mCgZGSGYoVgNeiSKoMeUGKCULNVsU4LVp8YggLXAowdwPfqEfBam0ZGjF/FbiIIwe7KhyzszkGWWgSQEPlmWQtZ1z7qkhLD0ara6hFiAl+GSlrvZcjdvGIURa1Pg/s79dndFl/EAG1+f3cl47j30zUFazQLguXxj2Vq2ziAIKJnH9afJBiwgZXDVxAWYZDUUr8t1AskMMrrm+WQpy9A1XZc6kTKxLYnasgDdcNNQWMbbdjTdkGWB5DrIH1HAwS8kIr+CznA/OjFBEYP0esGBEc83etaFwARxDd2DCcRjkiCGSMTvTQhjZMYz5FrQecIHSQh9mQAYBSKrB0WtPBCyGa5U+0XmYSRO64pFW2arQHRBDWKOPRX55CXpz6AXgmgTiudJ+JIsEfPCl4iIpyPKM6XtiwgVYF8kvNBrrndO3UUMKeKYQvoYAw9FgWxtXRW7VVbVSl0TmU2UJLFHAkOhgFMJYLRfADDyvwij2eAXQZWyOpJTErEbMENYvOktxAsoOJU3pP+ZoiiHlK6/y3o2iWeBujays/TVStKQptbl1Dgj7H1MMUADuDOj7BrQV/yzTmiJvcw0y+xlMQoxYGihum4Tn/IJY9HtCg8xHM11Hb34VSwhIXPqQVmobJKVevqWEJ7tFj/Vl0a6Wm+KU63ejdhyEA7Qn9kBe7ZdrQpDN+zZObvzUdzZ3CKVzruzsV8Av5c7my3d2Tmb8weYs/755mwPbdWYnXq3tBqYdU1tNCgxcqouau2HqCt2TGJGXqQnczdONAz9l427NowhZV/m8t3iy227tb5FDlsN2KkPv00s24LlEzGbmf2nmq+umq8zPKX56i3N1+qW+VrvNFvD1RVrsU7mrt5i+QC8r39bb974+ftbv//646nfgZmvk2H3+ebaCNHZXFXid+qoo97a2Gb797XWXRyVnXWX4M/OetC8tcWmJjfWyN84gdDEMkQMiqWluLvkXVtV2xRhfEkwoZuylg/gaOrlKizdGXgjOJnW9Gk1CmWbi29163z6n6Hq1jtynkexa30784etT/8fQBvmZwOdfSwq+yP3iEeZlAZZYC2sSWymkIBEfGwkYvTaIPwfZGwt7RDMGVHxhyvENtaomY5MPmfux68LYxQJxRfLpTJPNfYYqspduq2Qe/4EE+/1W4iiNHyDBGBbLFcSnNrTfl/Yv/Xb0oVb2+th3BsfzP2wzL3Rlvuhwr3eNe7bbiy13fU/cGi1bNVibL1iHW3H2mpF+dfYDxprjRZzdG9OF/kQUOLZwyBJkLdvapfwV8incGkNMmWUp/TGLn10ZEbl6BW2nHfSPhhUKqoOPVto59CDdSnbZnWS/EKDR/rOdjlb2lWoLm3BcTXYYjlx1mDuGBWKcgc5VINW1cOOpMFag/XdGrTtnflPpMGmcyYDzOQKlN+ZAjlADd7m4kjMRbFKzUP8KhD/T4jCJAT0NauDtymtJr1fkzef2bImDWdzZjlLKE+wZai2C1Ld+pgh3xePaZy8q9OOKUbxben6X9mW0hqc47RvsS7fKj+AdMBMfjBstq9yV3Mbulp1wXq8yVzT2jSl2EeLjN67f/oeR4ISjDklpv7H+P7yz5IUSlkN19SMwUjTtfxgVvdk0XZv5giMW5bKuGE3fMZtWrtVXfB4lNeP2HB+fbhAHDHJ6YTW+XTOdAo6K+cyjM+m06x/FrpCAWJgUzKK5+lQ1kXeuubO5nCkua7Cr9Ng0IZl5EcsyhRXVzrH+6ZbP3ZRUEzm7Mxxe46rk7ETE8yTxYHjdG5XHNu2rn8C</diagram></mxfile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,50 @@
.. p-net documentation master file, created by
sphinx-quickstart on Fri Jan 31 12:13:48 2020.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to p-net's documentation!
=================================
Here you will find a tutorial, the API reference as well as other detailed
documentation.
.. toctree::
:maxdepth: 1
:caption: Contents:
_copied/README.md
profinet_basics.rst
tutorial.rst
additional_linux_details.rst
getting_started_rtkernel.rst
getting_started_freertos.rst
using_codesys.rst
codesys_details.rst
use_with_siematic.rst
capturing_packets.rst
sampleapp_details.rst
profinet_details.rst
prepare_raspberrypi.rst
multiple_ports.rst
creating_gsdml_files.rst
network_topology_detection.rst
applications_and_porting.rst
linuxtiming.rst
compliancetest.rst
yoctobuild.rst
getting_started_LAN9662.rst
abbreviations.rst
api_documentation.rst
_copied/LICENSE.md
_copied/CHANGELOG.md
implementation_details.rst
writing_documentation.rst
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

View File

@ -0,0 +1,116 @@
Real-time properties of Linux
=============================
Regular Linux is not a real-time operating system. Profinet have rather strict
timing constraints. For example a Simatic PLC (with default settings) will
send an alarm if no incoming Profinet frame is received for 7-8 ms. So it is
important that Linux is responsive enough.
There are a few methods that can be used to improve the Linux responsiveness.
The most important is to use FIFO scheduling and to isolate the application on
a separate CPU core.
Use USE_SCHED_FIFO option
-------------------------
Select the FIFO Linux kernel scheduling option. This is done by passing
``-DUSE_SCHED_FIFO=ON`` command line argument to cmake.
Display real time properties of a process (should typically be ``SCHED_FIFO``
for best result)::
pi@pndevice-pi:~$ chrt -p $(pidof pn_dev)
pid 438's current scheduling policy: SCHED_OTHER
pid 438's current scheduling priority: 0
To show all threads, with scheduling mechanism::
ps -efLc
or for the sample application::
ps -efLc | grep pn_dev
The column "NLWP" shows number of threads, and the column "LWP" shows the thread ID.
In the column "CLS" is the thread scheduling policy shown. It can be for example
``TS`` for time sharing, ``RR`` for round robin or ``FF`` for FIFO scheduling.
To show the names of the threads, use custom output of the ``ps`` command::
pi@raspberrypi:~$ ps H -o 'pid ppid nlwp tid lwp comm cls pri psr time' $(pidof pn_dev)
PID PPID NLWP TID LWP COMMAND CLS PRI PSR TIME
1018 1 8 1018 1018 pn_dev TS 19 2 00:00:00
1018 1 8 1020 1020 os_eth_task FF 50 0 00:00:00
1018 1 8 1021 1021 os_eth_task FF 50 0 00:00:00
1018 1 8 1022 1022 os_eth_task FF 50 0 00:00:00
1018 1 8 1023 1023 p-net_bg_worker FF 45 0 00:00:08
1018 1 8 1029 1029 pn_snmp FF 41 1 00:00:00
1018 1 8 1030 1030 os_timer FF 70 3 00:01:37
1018 1 8 1031 1031 pn_dev FF 55 2 00:01:54
The "PSR" column shows which processor (core) the thread is running on.
Run the application on a separate processor core
------------------------------------------------
It is possible to tell the Linux kernel not to put any processes on a specific
processor core. This assumes that you have more than one core in your CPU.
Check it using::
cat /proc/cpuinfo
By setting the ``isolcpus=2`` Linux boot command, the kernel will not put any
processes on CPU core number 2 automatically. This option is typically set from
the boot loader.
The best way to check which CPU cores that are currently isolated::
cat /sys/devices/system/cpu/isolated
To see which kernel command line options that have been used::
cat /proc/cmdline
Put your Profinet application on the isolated CPU core. It is done using::
taskset -c 2 pn_dev
where ``-c 2`` tells which CPU core to use.
Display which CPU core a process is running on::
pi@pndevice-pi:~$ taskset -c -p $(pidof pn_dev)
pid 443's current affinity list: 2
Real-time patches
-----------------
By applying the real-time patches (PREEMPT_RT) the real-time properties can
be improved.
For more details, see:
* https://wiki.linuxfoundation.org/realtime/start
* https://rt.wiki.kernel.org/index.php/Main_Page
It is important to write you application so that it can use the benefits of
the real-time patches. This includes running in a separate thread and setting
the priorities properly.
For the real-time patches to have an effect on p-net, set the ``USE_SCHED_FIFO``
cmake option.
Increase application cycle time
-------------------------------
For testing, you can increase the cycle time from the PLC in order to reduce
the time-out problems. Also the allowed number of missed frames can be
increased in the PLC settings.
Network interface hardware
--------------------------
If your Ethernet network interface is connected via USB, there can be an
additional latency. This can affect the frame-to-frame interval for
transmitted Profinet frames.
For example the Ethernet interface in a Raspberry Pi 3 is connected via
an USB bus internally.

View File

@ -0,0 +1,272 @@
Multiple ports
==============
This section describes how to configure the p-net stack, sample application
and system for multiple network interfaces or ports.
So far, multiple port has only been tested using the Linux port.
Terminology
-----------
Interface
Abstract group of ports. In Profinet context, interface typically doesn't mean a
specific network interface. This is a common cause of confusion.
Port
Network interface. The physical connectors are referred to as "physical ports".
A "management port" is the network interface to which a controller / PLC connects.
In the example described in this section, ``br0`` is the management port
and ``eth0`` and ``eth1`` are the physical ports. The interface consists of
``br0``, ``eth0`` and ``eth1``.
Configuration of bridge using Linux
-----------------------------------
Tested with Raspberry PI 3B+ and USB Ethernet dongle USB3GIG.
+-------------+
| br0 |
+------+------+
| eth0 | eth1 |
+------+------+
p-net supports multiple Ethernet ports. To use multiple ports, these
shall be grouped into a bridge. In such a configuration the management port / main network interface
is the bridge and the Ethernet interfaces are named physical ports.
To create a bridge and add network interfaces to it, create the following files and content::
# /etc/systemd/network/bridge-br0.netdev
[NetDev]
Name=br0
Kind=bridge
# /etc/systemd/network/bridge-br0.network
[Match]
Name=br0
# /etc/systemd/network/br0-member-eth0.network
[Match]
Name=eth0
[Network]
Bridge=br0
# /etc/systemd/network/br0-member-eth1.network
[Match]
Name=eth1
[Network]
Bridge=br0
Enable and start network daemon::
sudo systemctl enable systemd-networkd
sudo systemctl start systemd-networkd
Run ``ifconfig -a`` to check that the bridge is up and its network interfaces are all up::
pi@pndevice-pi:~$ ifconfig -a
br0: flags=4419<UP,BROADCAST,RUNNING,PROMISC,MULTICAST> mtu 1500
inet 192.168.0.50 netmask 255.255.255.0 broadcast 0.0.0.0
ether 3e:9b:37:c6:3e:ee txqueuelen 1000 (Ethernet)
RX packets 209875 bytes 11260238 (10.7 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 201263 bytes 12927207 (12.3 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
eth0: flags=4419<UP,BROADCAST,RUNNING,PROMISC,MULTICAST> mtu 1500
inet6 fe80::ba27:ebff:fef3:9ab2 prefixlen 64 scopeid 0x20<link>
ether b8:27:eb:f3:9a:b2 txqueuelen 1000 (Ethernet)
RX packets 209581 bytes 12999322 (12.3 MiB)
RX errors 0 dropped 2285 overruns 0 frame 0
TX packets 205044 bytes 14541592 (13.8 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
eth1: flags=4419<UP,BROADCAST,RUNNING,PROMISC,MULTICAST> mtu 1500
inet6 fe80::a2ce:c8ff:fe1e:1745 prefixlen 64 scopeid 0x20<link>
ether a0:ce:c8:1e:17:45 txqueuelen 1000 (Ethernet)
RX packets 6019 bytes 1764099 (1.6 MiB)
RX errors 0 dropped 2320 overruns 0 frame 0
TX packets 6662 bytes 721472 (704.5 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 38 bytes 3952 (3.8 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 38 bytes 3952 (3.8 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
wlan0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.42.92 netmask 255.255.255.0 broadcast 192.168.42.255
inet6 fe80::d4bf:5c2c:48c0:bf1e prefixlen 64 scopeid 0x20<link>
ether b8:27:eb:a6:cf:e7 txqueuelen 1000 (Ethernet)
RX packets 1883 bytes 135753 (132.5 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 126 bytes 20075 (19.6 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
No IP address has to be assigned to the bridge or any of the Ethernet interfaces
in order for the bridge to forward packets from one Ethernet interface to
the other. However the bridge needs to be in ``UP`` state.
To show bridge status::
pi@pndevice-pi:~$ bridge -d link
2: eth0: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1500 master br0 state forwarding priority 32 cost 4
hairpin off guard off root_block off fastleave off learning on flood on mcast_flood on neigh_suppress off vlan_tunnel off isolated off
3: br0: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1500 master br0
5: eth1: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1500 master br0 state forwarding priority 32 cost 19
hairpin off guard off root_block off fastleave off learning on flood on mcast_flood on neigh_suppress off vlan_tunnel off isolated off
You can also use the ``brctl`` Linux command from the ``bridge-utils`` package::
pi@pndevice-pi:~$ brctl show
bridge name bridge id STP enabled interfaces
br0 8000.3e9b37c63eee no eth0
eth1
Another useful Linux command is ``networkctl``::
pi@pndevice-pi:~$ networkctl
IDX LINK TYPE OPERATIONAL SETUP
1 lo loopback carrier unmanaged
2 eth0 ether degraded configured
3 br0 bridge degraded unmanaged
4 eth1 ether degraded configured
5 wlan0 wlan routable unmanaged
5 links listed.
To disable the creation of the bridge at reboot::
sudo systemctl disable systemd-networkd
If you do not use systemd, then a script ``enable_bridge.sh`` might be handy::
ip link add name br0 type bridge
ip link set br0 up
ip link set eth0 up
ip link set eth1 up
ip link set eth0 master br0
ip link set eth1 master br0
And correspondingly ``disable_bridge.sh``::
ip link set eth0 nomaster
ip link set eth1 nomaster
ip link set br0 down
ip link delete br0 type bridge
Configuration of p-net stack and sample application
---------------------------------------------------------
To run p-net and the sample application with multiple ports a couple
of things need to be done. Note that the settings described in the
following sections are changed by running ``ccmake .`` in the build folder,
and then ``options.h`` will be regenerated.
Reconfigure setting ``PNET_MAX_PHYSICAL_PORTS`` to the actual number of physical
ports available in the system. For this example the value shall be set to 2.
Reconfigure setting ``PNET_MAX_SUBSLOTS``. Each additional port will require an
additional subslot. For this example the value should be be set to 4.
Another way to set the options is to set them on the cmake command line::
-DPNET_MAX_PHYSICAL_PORTS=2 -DPNET_MAX_SUBSLOTS=4
The configuration field ``.num_physical_ports`` must be set accordingly. For
the Linux sample application this is set automatically by parsing the command
line arguments, which need to be adjusted.
Example of initial log when starting the demo application with a multi port configuration::
pi@pndevice-pi:~/profinet/build $ sudo ./pn_dev -v
** Starting Profinet demo application **
Number of slots: 5 (incl slot for DAP module)
P-net log level: 0 (DEBUG=0, FATAL=4)
App verbosity level: 1
Nbr of ports: 2
Network interfaces: br0,eth0,eth1
Button1 file:
Button2 file:
Station name: rt-labs-dev
Management port: br0 C2:38:F3:A6:0A:66
Physical port [1]: eth0 B8:27:EB:67:14:8A
Physical port [2]: eth1 58:EF:68:B5:11:0F
Current hostname: pndevice-pi
Current IP address: 192.168.0.50
Current Netmask: 255.255.255.0
Current Gateway: 192.168.0.1
Storage directory: /home/pi/profinet/build
Update GSDML file
-----------------
The sample app GSDML file contains a commented out block that defines
a second physical port. In the sample application GSDML file, search for "IDS_P2"
and enable commented out lines as described in the GSDML file.
Note that you will have to the reload GSDML file in all tools you are using and
also the Automated RT tester any time the file is changed.
Running ART tester with multiple ports
--------------------------------------
Use the MAC-address of ``br0`` when running ART tester.
Routing traffic with multiple ports on Linux
---------------------------------------------
To see the MAC addresses and IP addresses of the neighbours, use the ``arp``
Linux command::
pi@pndevice-pi:~$ arp
Address HWtype HWaddress Flags Mask Iface
192.168.0.99 ether 20:87:56:ff:aa:83 C br0
192.168.42.1 ether b4:fb:e4:51:09:72 C wlan0
192.168.0.98 ether ac:4a:56:f4:02:89 C br0
192.168.0.30 ether 54:ee:75:ff:95:a6 C br0
To see the IP routing table, use the ``route`` Linux command::
pi@pndevice-pi:~$ route
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
default 192.168.42.1 0.0.0.0 UG 305 0 0 wlan0
192.168.0.0 0.0.0.0 255.255.255.0 U 0 0 0 br0
192.168.42.0 0.0.0.0 255.255.255.0 U 305 0 0 wlan0
Adding multiple Ethernet ports on a microcontroller
---------------------------------------------------
Typically a microcontroller has only one Ethernet controller built in. One way
to provide more Ethernet ports is to use an external Ethernet switch chip.
However the Profinet stack must be able to control from which Ethernet port
a frame is sent from. This is because the outgoing LLDP frames should have
different contents on different ports (important for neighbourhood detection).
Similarly, the stack must be able to figure out on which port an incoming
LLDP frame did arrive, in order to determine the closest neighbour for each
Ethernet port.
Traffic not intended for the device should be routed between the external
Ethernet ports.
There are Ethernet switch chips with functionality to route traffic to
different Ethernet ports from a single Ethernet controller in a microcontroller.
This is done by "tail tagging", where a few additional bytes are put in the
end of outgoing frames. The switch chip will remove those bytes, but will
use the information to route the frame to the correct outgoing Ethernet port.
A detailed description of the concept is given on
https://www.segger.com/products/connectivity/emnet/technology/port-multiplication/
Examples of useful Ethernet switch chips (not tested):
* Microchip KSZ8863
* Microchip KSZ8873
* Microchip KSZ8895
The p-net stack does not implement support for tail tagging by default.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,382 @@
.. _prepare_raspberry:
Installation and configuration of Raspberry Pi
==============================================
This page consists an installation guide to use if you have a Linux laptop,
and a separate guide to use with a Windows laptop.
The p-net stack and sample application has been tested with:
* Raspberry Pi 3 Model B+
To avoid problems it is recommended to start with a fresh
Raspberry Pi OS image.
When running the Raspberry Pi as a Profinet IO-device using p-net, the
network settings of the Raspberry Pi will be changed by Profinet.
Therefore it is highly recommended to use a keyboard, mouse and monitor or
a serial cable.
If you use ssh you may end up in a situation were you have difficulties
connecting to, or recovering your device. If you currently do not have a
suitable USB-to-serial cable you can still build and run p-net with the
sample application but you might run into problem when a PLC is used for
configuration.
Installation using a Linux laptop
---------------------------------
Burn a SD-card with Raspberry Pi OS, by using the "Raspberry Pi imager"
software and a SD card reader.
A card size of 16 - 32 GByte is recommended.
Follow the instructions on the page https://www.raspberrypi.com/software/
Select the standard "Raspberry Pi OS" operating system.
Unplug, and reinsert your SD-card to mount it. To enable SSH logging in on the
Raspberry Pi, create an empty file on the named ``ssh`` in the boot partition::
touch ssh
To enable the serial port console add this line to the
file ``config.txt`` in the boot partition::
enable_uart=1
The DHCP client daemon will adjust the network interface settings automatically.
This interferes with the p-net control of the Ethernet interface. So if you
run your Raspberry Pi as a Profinet IO-Device (NOT if you use it as a PLC)
and have a serial cable, you need to add this line to ``/etc/dhcpcd.conf``
in the root file system::
denyinterfaces eth*
If you would like to change hostname from ``raspberrypi`` to ``pndevice-pi``, change
the texts in the files ``etc/hostname`` and ``etc/hosts`` in the rootfs
partition.
To make sure that you subsequently are logging in to the correct Raspberry Pi,
you can create a file in the home directory in the rootfs partition. Change
name to something informative, for example::
touch home/pi/IAmAProfinetDevice
Unmount the SD-card, and plug it in into your Raspberry Pi. Power up the
Raspberry Pi. Log in to it via a serial cable (see below).
Use the username ``pi`` and the default password ``raspberry``.
If you do not have any serial cable (and not have disabled DHCP), connect
the Raspberry Pi to your local network.
Find the IP address of it by running this on a Linux machine on the network
(replace the hostname if you have changed it)::
ping raspberrypi.local
Verify that it is the correct machine by checking that is disappears when the
power is disconnected.
Log in to it::
ssh pi@<IP>
Enter the password mentioned just above.
If you would connect your Raspberry Pi to a WiFi network, follow the
guide in https://www.raspberrypi.com/documentation/computers/configuration.html
You might also want to disable the splash screen and to expand the file system,
by using the ``raspi-config`` utility.
.. note:: If you are following the tutorial and are setting up the IO-device,
you should head back now. See :ref:`tutorial`.
Installation using a Windows laptop
-----------------------------------
This section describes how to install the Raspberry Pi OS
and how to enable ssh and serial console so that the Raspberry Pi can be
used in headless mode without a display and keyboard connected.
Step 1. Write Raspberry Pi OS image to SD card using Raspberry Pi Imager.
* Download and install Raspberry Pi Imager from
https://www.raspberrypi.com/software/
* Start Raspberry Pi Imager
* In the Select OS dialog choose full version
* Select SD-card
* Press Write
Step 2. Initial configuration of Raspberry Pi OS.
* Eject SD-card
* Reinsert SD-card in windows PC. The SD-card will be shown as external drive named ``boot``.
* Enable ssh by creating an empty file named ``ssh`` in the root folder of ``boot``.
The windows file explorer can be used for this.
Note that the file ``ssh`` shall not have a txt file extension.
* Enable serial port console.
Open ``config.txt`` in root folder of ``boot`` using Notepad.
Add the line ``enable_uart=1`` to the end of the file.
Save file and close Notepad.
* Eject SD-card
Step 3. Start Raspberry Pi
* Insert SD-card and power on Raspberry Pi.
* Login (preferably using serial console) with default user ``pi`` and password ``raspberry``.
Step 4. Network configuration.
Use the nano editor to edit the configuration files as described below.
For example to edit the ``/etc/dhcpcd.conf``::
sudo nano /etc/dhcpcd.conf
Save the file in nano by pressing ``CTRL-X``, then ``Y`` and ``Enter``.
The DHCP client daemon will adjust the network interface settings automatically.
This interferes with the p-net control of the Ethernet interface. So if you
run your Raspberry Pi as a Profinet IO-Device (NOT if you use it as a PLC)
and have a serial cable, you should add the line below to ``/etc/dhcpcd.conf``::
denyinterfaces eth*
Optionally, to change hostname from ``raspberrypi`` to ``pndevice-pi``, change
the configuration in the files ``/etc/hostname`` and ``/etc/hosts``.
To make sure that you subsequently are logging in to the correct Raspberry Pi,
you can create a file in the home directory in the rootfs partition. Change
name to something informative, for example::
touch /home/pi/IAmAProfinetDevice
Reboot and the Raspberry Pi is now ready to run the p-net sample application::
sudo reboot
If you would connect your Raspberry Pi to a WiFi network, follow the
guide in https://www.raspberrypi.com/documentation/computers/configuration.html
You might also want to disable the splash screen and to expand the file system,
by using the ``raspi-config`` utility.
.. note:: If you are following the tutorial and are setting up the IO-device,
you should head back now. See :ref:`tutorial`.
Optionally connect a serial cable to Raspberry Pi
-------------------------------------------------
The p-net Profinet stack will change the IP-address of the Raspberry Pi when
running it as an IO-Device (as requested by the PLC), why it can be
inconvenient to connect to it via ssh. You can use a keyboard, mouse and a
monitor to connect to the Raspberry Pi. Using a serial cable to connect it to
your laptop can then be helpful if a keyboard etc not is available.
Use a USB-to-serial adapter cable with 3.3 V logic levels. For example
Adafruit sells a popular version of those cables. Connect the USB end to your
laptop and the other end to the header connector on the Raspberry Pi.
If not already done, enable the serial port console by writing the line
``enable_uart=1`` in the file ``/boot/config.txt``.
The serial port within the Raspberry Pi will be named ``/dev/ttyS0``.
+-----+-----------+---------------------+-----------------------+
| Pin | Name | Terminal on cable | Adafruit cable color |
+=====+===========+=====================+=======================+
| 6 | GND | GND | Black |
+-----+-----------+---------------------+-----------------------+
| 8 | UART0_TXD | RX | White |
+-----+-----------+---------------------+-----------------------+
| 10 | UART0_RXD | TX | Green |
+-----+-----------+---------------------+-----------------------+
Use a communication program with a baud rate of 115200.
Before connecting the serial cable to your Raspberry Pi you can verify the
functionality of the cable by connecting the USB connector to your Laptop,
and connect the RX-terminal to the TX terminal of the cable. Use a communication
program to verify that text that you enter is echoed back. When removing
the RX-to-TX connection the echo should stop.
Optionally connect LEDs and buttons to Raspberry Pi
---------------------------------------------------
You need these components:
+-----------------------+-----------------+
| Component | Number required |
+=======================+=================+
| LED | 2 |
+-----------------------+-----------------+
| Button switch | 2 |
+-----------------------+-----------------+
| Resistor 220 Ohm | 4 |
+-----------------------+-----------------+
Connect them like:
+------+---------+-----------------------------------------------------+
| Pin | Name | Description |
+======+=========+=====================================================+
| 9 | GND | |
+------+---------+-----------------------------------------------------+
| 11 | GPIO17 | Connect LED1 (application data) to GND via 220 Ohm |
+------+---------+-----------------------------------------------------+
| 13 | GPIO27 | Connect Button1 to 3.3V via 220 Ohm |
+------+---------+-----------------------------------------------------+
| 15 | GPIO22 | Connect Button2 to 3.3V via 220 Ohm |
+------+---------+-----------------------------------------------------+
| 16 | GPIO23 | Connect LED2 (Profinet signal) to GND via 220 Ohm |
+------+---------+-----------------------------------------------------+
| 17 | 3.3V | |
+------+---------+-----------------------------------------------------+
The resistors for the buttons are to limit the consequences of connecting the
wires to wrong pins.
Set up the GPIO pins for the buttons::
echo 22 > /sys/class/gpio/export
echo 27 > /sys/class/gpio/export
and for the LEDs::
echo 17 > /sys/class/gpio/export
echo 23 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio17/direction
echo out > /sys/class/gpio/gpio23/direction
Turn on and off a LED::
echo 1 > /sys/class/gpio/gpio17/value
echo 0 > /sys/class/gpio/gpio17/value
Show state of buttons::
cat /sys/class/gpio/gpio22/value
cat /sys/class/gpio/gpio27/value
.. image:: illustrations/RaspberryPiLedButtons.jpg
Adjust IP address if using the Raspberry Pi as a PLC
----------------------------------------------------
If running your Raspberry Pi as a PLC (Profinet IO-Controller). you would like
to have a static IP address (it will not work if running as a Profinet IO-Device).
Instead modify the file ``/etc/dhcpcd.conf`` to include these lines::
interface eth0
static ip_address=192.168.0.100/24
You can still ping the <hostname>.local address to find it on the network.
To re-enable DHCP, remove the lines again from ``/etc/dhcpcd.conf``.
Once you have prepared the IP address etc on the Raspberry Pi intended for
use as a PLC, it is time to install the Codesys runtime on it. See
:ref:`using-codesys`.
Advanced users only: Automatic start of sample application
----------------------------------------------------------
Use systemd to automatically start the p-net sample application at boot on a
Raspberry Pi.
Place a systemd unit file here: ``/lib/systemd/system/pnet-sampleapp.service``
An example file is available in the ``samples/pn_dev/`` directory of this
repository. It assumes that the code is checked out into
``/home/pi/profinet/p-net/`` on your Raspberry Pi.
Install the files::
sudo cp /home/pi/profinet/p-net/src/ports/linux/pnet-sampleapp.service /lib/systemd/system/
sudo cp /home/pi/profinet/p-net/src/ports/linux/enable-rpi-gpio-pins.sh /usr/bin/
sudo chmod +x /usr/bin/enable-rpi-gpio-pins.sh
Adapt the contents to your paths and hardware.
Enable automatic startup::
sudo systemctl daemon-reload
sudo systemctl enable pnet-sampleapp.service
Start service::
sudo systemctl start pnet-sampleapp.service
To see the status of the process, and the log output::
systemctl status pnet-sampleapp.service
journalctl -u pnet-sampleapp -f
If using a serial cable, you might need to adjust the number of visible columns::
stty cols 150 rows 40
You can for example add it to your ``.bashrc`` file on the Raspberry Pi.
In order to speed up the boot time, you might want to disable some functionality
not necessary for Profinet applications. For example::
sudo systemctl disable cups-browsed.service
sudo systemctl disable cups.service
See the section "Boot time optimization" elsewhere in this documentation.
Advanced users only: Control of built-in LEDs
---------------------------------------------
The Raspberry Pi board has LEDs on the board, typically a red PWR LED and a
green ACT (activity) LED.
Manually control the green LED (ACT = ``led0``) on Raspberry Pi 3::
echo none > /sys/class/leds/led0/trigger
echo 1 > /sys/class/leds/led0/brightness
And to turn it off::
echo 0 > /sys/class/leds/led0/brightness
Note that you need root privileges to control the LEDs.
Similarly for the red (power) LED, which is called ``led1``.
Advanced users only: Control Linux real-time properties
-------------------------------------------------------
See the page on Linux timing in this documentation for an introduction to
the subject.
Add this to the first (and only) line in ``/boot/cmdline.txt``::
isolcpus=2
Run the sample application on a specific CPU core, by modifying the
autostart file ``/lib/systemd/system/pnet-sampleapp.service`` (if installed)::
ExecStart=taskset -c 2 /home/pi/profinet/build/pn_dev -v -b /sys/class/gpio/gpio27/value -d /sys/class/gpio/gpio22/value
SD-card problems
----------------
If you have problems with the CPU freezing for a few seconds now and then,
the SD-card might be damaged. There is a SD-card test program available
for Raspberry Pi::
sudo apt update
sudo apt install agnostics
Start the test program::
pi@raspberrypi:~$ sh /usr/share/agnostics/sdtest.sh
Example output for a damaged SD-card::
Run 1
prepare-file;0;0;2029;3
seq-write;0;0;2944;5
rand-4k-write;0;0;705;176
rand-4k-read;7444;1861;0;0
Sequential write speed 2944 KB/sec (target 10000) - FAIL
Note that sequential write speed declines over time as a card is used - your card may require reformatting
Random write speed 176 IOPS (target 500) - FAIL
Random read speed 1861 IOPS (target 1500) - PASS

View File

@ -0,0 +1,376 @@
Profinet basics
===============
Profinet is a field bus that communicates over Ethernet, typically at a speed
of 100 Mbit/s. For details on Profinet, see
https://en.wikipedia.org/wiki/PROFINET
A detailed introduction to Profinet is given in the book "Industrial
communication with PROFINET" by Manfred Popp.
It is available to Profinet members.
Profinet devices (IO-devices) are controlled by an PLC (Programmable Logic
Controller), also known as an IO-controller.
GSD files
---------
A GSD (General Station Description) file is an XML file describing a Profinet
IO-Device. The XML-based language is called GSDML (GSD Markup Language).
Note that the GSD file is not used by the p-net stack or application. It is
a machine readable file describing the capabilities, hardware- and software
versions etc, and is used by the engineering tool to adjust the PLC settings.
The GSD file describes for example which types of pluggable hardware modules
that can be used in the IO-device. The GSD file is loaded into the
engineering tool (typically running on a personal computer),
and a user can then in the tool
describe which modules that actually should be plugged into the IO-device.
This is called configuration. Later this information is downloaded to the PLC
(IO-controller), in a process called commissioning. At startup the PLC will
tell the IO-device what type of modules it expects to be plugged in.
If the correct modules not are plugged into the IO-device, the IO-device will
send an error message back to the PLC.
Startup description
-------------------
In the startup of an Profinet IO device (before the IP address has been set) the
DCP protocol is used. It is like DHCP, but without using a central server.
The PLC sends a DCP broadcast message, and all IO Devices on the subnet answer
with their MAC addresses. The PLC sends a DCP message to the IO Device with
a specific MAC address, containing which IP address and station name that the
IO Device should use. The IO Device sets its IP address and station name
accordingly.
Then the PLC starts the actual configuration of the IO device, using the
DCE/RPC protocol that runs on UDP over IP.
After the configuration is done, cyclic data is continuously exchanged between
the IO-device and the PLC. This communication runs on Ethernet layer 2, i.e.
the MAC addresses are used for routing frames (the IP protocol is not used in
those frames).
Nodes classes and device details
--------------------------------
Profinet defines three node classes:
.. table::
:widths: 25 50 25
+---------------+------------------------------------------------------------------+----------------------------+
| Node class | Description | | Supported by |
| | | | this software |
+===============+==================================================================+============================+
| IO-Device | "Slave" | Yes |
+---------------+------------------------------------------------------------------+----------------------------+
| IO-Controller | "Master", often a PLC. | No |
+---------------+------------------------------------------------------------------+----------------------------+
| IO-Supervisor | For commissioning and diagnostics, typically a personal computer | No |
+---------------+------------------------------------------------------------------+----------------------------+
Depending on the capabilities, different conformance classes are assigned.
+------------------------+---------------------------------------------+----------------------------+----------------------------+
| | Conformance | Description | | Supported by | | Communication |
| | class (CC) | | | this software | | profile (CP) |
+========================+=============================================+============================+============================+
| A | Wireless is only allowed for class A | Yes | CP 3/4 |
+------------------------+---------------------------------------------+----------------------------+----------------------------+
| B | Class A + SNMP (network topology detection) | Yes | CP 3/5 |
+------------------------+---------------------------------------------+----------------------------+----------------------------+
| B (PA) | Class B + System redundancy | No | |
+------------------------+---------------------------------------------+----------------------------+----------------------------+
| C | Class B + IRT communication | No | CP 3/6 |
+------------------------+---------------------------------------------+----------------------------+----------------------------+
| D | Uses time sensitive networking Ethernet | No | CP 3/7 |
+------------------------+---------------------------------------------+----------------------------+----------------------------+
The first digit in the Communication Profile (CP) is the Communication Profile Family (CPF). Profinet and Profibus are CPF 3,
while for example Ethercat is CPF 12.
Communication profiles CP 3/1 and CP 3/2 are for Profibus. The CP 3/3 was used for the legacy Profinet CBA.
Real Time Class:
* Real Time Class 1 Mandatory for conformance class A, B, C
* Real Time Class 2 (legacy) Only for legacy startup mode for conformance class C.
* Real Time Class 3 = IRT (Isochronous Real Time) Mandatory for conformance class C
* Real Time Class UDP For cross-network real time communication. Optional for conformance class A, B, C
* Real Time Class STREAM for conformance class D.
This software supports Real Time Class 1.
MAC addresses
-------------
If your device has two Ethernet connectors (ports), then it typically uses an Ethernet
switch chip with three data connections::
Port X1 P1 Port X1 P2
| |
| |
| |
+--+-----------------+--+
| |
| Ethernet Switch |
| |
+----------+------------+
|
|
|
Profinet interface X1
The device has one interface and two ports.
For Profinet conformance class B and higher, there are three assigned MAC addresses:
* Profinet interface X1 (main MAC address or device MAC address)
* Port X1 P1
* Port X1 P2
The MAC addresses for X1 P1 and X1 P2 are used by the LLDP protocol for neighbor detection.
There are examples of Profinet controllers with more than one interface (having more than one port each).
Each interface has its own IP address. Each port has its own MAC address.
Communication
-------------
Profinet uses three protocol levels:
+-----------------------------+--------------------+-------------+---------------+----------------------------+
| Protocol | | Typical | Description | Ethertype | | Supported by |
| | | cycle time | | | | this software |
+=============================+====================+=============+===============+============================+
| TCP/IP (UDP actually) | 100 ms | | 0x0800 = IPv4 | Yes |
+-----------------------------+--------------------+-------------+---------------+----------------------------+
| RT (Real Time) | 10 ms | | 0x8892 | Yes |
+-----------------------------+--------------------+-------------+---------------+----------------------------+
| IRT (Isochronous Real-Time) | 1 ms | | 0x8892? | No |
+-----------------------------+--------------------+-------------+---------------+----------------------------+
Profinet uses IPv4 only (not IPv6).
Overview of all protocols used in a Profinet Application
--------------------------------------------------------
A typical Profinet application needs to handle a multitude of protocols.
The p-net stack implements some of them, while others are optional.
For example HTTP is used by some IO-devices for additional configuration via
a web page, but should be implemented outside the Profinet stack itself.
+-------+----------+-------------+-----------------+-----------------------+---------------------------------+
| Layer | Protocol | Addressing | | Content | Protocol | Notes |
| | | | | description | | |
+=======+==========+=============+=================+=======================+=================================+
| 4 | TCP | Port | SSH = 22 | Remote login on Linux |
| | | +-----------------------+---------------------------------+
| | | | HTTP = 80 + 443 | Web server for configuration |
+-------+----------+-------------------------------+-----------------------+---------------------------------+
| 4 | UDP | Port | DHCP = 67 + 68 | IP address assignment |
| | | +-----------------------+---------------------------------+
| | | | SNMP = 161 + 162 | Network topology query |
| | | +-----------------------+---------------------------------+
| | | | PNIO-CM = 34964 | Profinet start-up, uses DCE/RPC |
+-------+----------+-------------+-----------------+-----------------------+---------------------------------+
| 3 | IPv4 | IP address | Protocol number | ICMP = 1 | Used by ping |
| | | | +-----------------------+---------------------------------+
| | | | | TCP = 6 | Transmission control protocol |
| | | | +-----------------------+---------------------------------+
| | | | | UDP = 17 | User Datagram Protocol |
+-------+----------+-------------+-----------------+-----------------------+---------------------------------+
| 2 | Ethernet | MAC address | Ethertype | LLDP = 0x88CC | Link layer discovery |
| | | | +-----------------------+---------------------------------+
| | | | | ARP = 0x0806 | IP address lookup |
| | | | +-----------------------+---------------------------------+
| | | | | IPv4 = 0x0800 | Internet protocol |
| | | | +-----------------------+---------------------------------+
| | | | | PN-DCP = 0x8892 | Profinet start-up |
| | | | +-----------------------+---------------------------------+
| | | | | Profinet RT = 0x8892 | PNIO_PS = Cyclic IO data |
| | | | +-----------------------+---------------------------------+
| | | | | (VLAN = 0x8100) | Not really a protocol |
| | | | +-----------------------+---------------------------------+
| | | | | (VLAN = 0x9100) | For double tagged frames |
+-------+----------+-------------+-----------------+-----------------------+---------------------------------+
Profinet Profiles
-----------------
There are a number of Profinet application profiles defined, for example
PROFIenergy and PROFIdrive. These defines for example the cyclic data should
be interpreted.
Profiles use the API (Application Program Identifier) concept for telling
that profile-specific data is transferred.
Slots and modules
-----------------
A Profinet IO-device has typically a number of slots where (hardware) modules
can be placed. A module can have subslots where submodules are placed.
Each submodule have a number of channels (for example digital inputs).
Each API has its own collection of slots.
TODO Example
* Module
* Submodule
* Channels
Channels are always connected to submodules (rather than to modules).
Addressing a channel
* Slot
* Subslot
* Index
First usable slot is slot number 1. Slot 0 is used for the IO-device itself,
and does not have any input/output data. Instead it has diagnostic information
for the IO-device.
If compile time setting PNET_MAX_SLOTS is 5, then the slots are numbered 0-4.
The setting PNET_MAX_SUBSLOTS controls the number of subslots per slot,
but there is no fixed relation to which subslot numbers will be used.
Subslot numbers in the range 0-0x9FFF might be used.
The GSD file pretty much describes the hardware: slots (and subslots), and
the modules (and submodules) that can be placed in the slots. The file does
not describe which modules that actually have been placed in each slot for
each device. That is done during the setup (configuration) in the engineering
tool during PLC programming.
Also in the GSD file is description on the data exchange?
+----------------------+-------------------------------------+
| Type | Description |
+======================+=====================================+
| Compact field device | Not possible to change modules etc? |
+----------------------+-------------------------------------+
| Modular field device | Change modules at configuration? |
+----------------------+-------------------------------------+
Payload
-------
The Profinet payload is sent as big endian (network endian) on the wire.
The maximum data (and status) size in total in each direction for a device is 1440 bytes.
If this is consumed by a single subslot the maximum data size is 1438 byte (plus one or two bytes for IOPS and IOCS).
See the IOData entry in the GSDML specification.
If the IOPS (producer status) for a subslot goes to BAD, the PLC will indicate an error
"User data failure of hardware component".
It will persist until the communication is restarted to the IO-device.
I&M data records
----------------
This is Identification & Maintenance records, which are human-readable
information about the model type, software version etc.
Some fields are writable, for example where the device is located.
Up to I&M15 is described in the standard. The p-net stack supports I&M0 - I&M4.
You must support writing to I&M1-3 for at least one of the DAP submodules.
+-------------+-----------+-----------------------+--------------------------------------------------------------+
| Data record | Mandatory | Controller can write? | Description |
+=============+===========+=======================+==============================================================+
| I&M0 | Yes | No | Vendor ID, serial number. Hardware and software version etc. |
+-------------+-----------+-----------------------+--------------------------------------------------------------+
| I&M1 | No | Yes | Tag function and location |
+-------------+-----------+-----------------------+--------------------------------------------------------------+
| I&M2 | No | Yes | Date. Format "1995-02-04 16:23" |
+-------------+-----------+-----------------------+--------------------------------------------------------------+
| I&M3 | No | Yes | Descriptor |
+-------------+-----------+-----------------------+--------------------------------------------------------------+
| I&M4 | No | Yes | Signature. Only for functional safety. |
+-------------+-----------+-----------------------+--------------------------------------------------------------+
There is also I&M0 Filterdata, which is read only.
Startup modes
-------------
The startup mode was changed in Profinet 2.3, to "Advanced". The previous
startup mode is now called "Legacy".
Net load class
--------------
* I
* II
* III
Alarm types
-----------
A process alarm describes conditions in the monitored process, for example
too high temperature.
A diagnostic alarm describes conditions in the IO Device itself, for example
a faulty channel or short circuit. Diagnostic alarms are also stored in the IO-Device.
Description of supported alarm types:
+------------------------------------------+--------+-----------------------------------------------------------+
| Name | Hex | Description |
+==========================================+========+===========================================================+
| Diagnosis | 0x0001 | There is something wrong with the IO device itself |
+------------------------------------------+--------+-----------------------------------------------------------+
| Process | 0x0002 | There is something wrong with the process, for |
| | | example too high temperature. High priority. |
+------------------------------------------+--------+-----------------------------------------------------------+
| Pull | 0x0003 | Submodule pulled from subslot. |
+------------------------------------------+--------+-----------------------------------------------------------+
| Plug | 0x0004 | Module/submodule plugged into slot/subslot. |
+------------------------------------------+--------+-----------------------------------------------------------+
| Controlled by supervisor | 0x0008 | |
+------------------------------------------+--------+-----------------------------------------------------------+
| Released | 0x0009 | |
+------------------------------------------+--------+-----------------------------------------------------------+
| Plug wrong submodule | 0x000A | Wrong module/submodule plugged into slot/subslot. |
+------------------------------------------+--------+-----------------------------------------------------------+
| Return of submodule | 0x000B | |
+------------------------------------------+--------+-----------------------------------------------------------+
| Diagnosis disappears | 0x000C | A kind of diagnosis alarm |
+------------------------------------------+--------+-----------------------------------------------------------+
| Port data change notification | 0x000E | Port up, or peer changes name. A kind of diagnosis alarm. |
+------------------------------------------+--------+-----------------------------------------------------------+
| Pull module | 0x001F | Module pulled from slot. |
+------------------------------------------+--------+-----------------------------------------------------------+
Only process alarms are sent with high prio, all other alarms use low prio.
Diagnosis
---------
Diagnosis alarms are sent to indicate for example short-circuit on an output.
To localize the diagnosis source, these values are required:
* API
* Slot
* Subslot
* Channel number (Use 0x8000 for "whole submodule")
* Accumulative (true when describing a channel group)
* Direction (in or out. Use "manufacturer specific" for "whole submodule")
Do not update diagnosis information at a higher frequency than 1 Hz.
Logbook
-------
A logbook is a circular buffer with at least 16 entries. The IO-controller can
read out entire logbook. Each entry contains:
* A timestamp
* Error codes (4 bytes)
* A manufacturer specific entry detail (uint32_t)

View File

@ -0,0 +1,643 @@
Profinet details
================
Technical details on some of the protocols used in a Profinet Application
--------------------------------------------------------------------------
+---------------------------+--------------+----------------+--------------------------------------+----------------------------------------------------+
| Layer | | Header | | Footer | Header contents | Footer contents |
| | | size | | size | | |
+===========================+==============+================+======================================+====================================================+
| Ethernet (layer 2) | 14 or 18 | | MAC 6+6, Ethertype 2 (VLAN 4) | |
+---------------------------+--------------+----------------+--------------------------------------+----------------------------------------------------+
| IPv4 | 20 | | IP addr 4+4, len 2, protocol 1, etc | |
+---------------------------+--------------+----------------+--------------------------------------+----------------------------------------------------+
| UDP | 8 | | Port 2+2, len 2, checksum 2 | |
+---------------------------+--------------+----------------+--------------------------------------+----------------------------------------------------+
| DCE/RPC | 80 | | UUID 16+16+16, etc | |
+---------------------------+--------------+----------------+--------------------------------------+----------------------------------------------------+
| Profinet cyclic realtime | 2 | 4 | FrameId 2 | Cycle counter 2, data status 1, transfer status 1 |
+---------------------------+--------------+----------------+--------------------------------------+----------------------------------------------------+
| Profinet acyclic realtime | 2 | | FrameId 2 | |
+---------------------------+--------------+----------------+--------------------------------------+----------------------------------------------------+
| Profinet DCP | 10 | | ServiceID 1, ServiceType 1, Xid, Len | |
+---------------------------+--------------+----------------+--------------------------------------+----------------------------------------------------+
Sizes are given in bytes.
Note that "Profinet cyclic realtime" and "Profinet acyclic realtime" run
directly on Ethernet layer 2 (they do not use IP or UDP).
Profinet DCP runs via "Profinet acyclic realtime".
Note that the length field in the UDP header includes the size of the header itself.
Profinet cyclic and acyclic realtime protocol via Ethernet layer 2
------------------------------------------------------------------
+----------+-----------------------+-------------------+
| Frame ID | Protocol | Description |
+==========+=======================+===================+
| 0x8000 | Profinet cyclic | Output CR |
+----------+-----------------------+-------------------+
| 0x8001 | Profinet cyclic | Input CR |
+----------+-----------------------+-------------------+
| 0xFC01 | | ALARM_HIGH |
+----------+-----------------------+-------------------+
| 0xFE01 | | ALARM_LOW |
+----------+-----------------------+-------------------+
| 0xFEFC | Profinet acyclic, DCP | HELLO |
+----------+-----------------------+-------------------+
| 0xFEFD | Profinet acyclic, DCP | GET_SET |
+----------+-----------------------+-------------------+
| 0xFEFE | Profinet acyclic, DCP | Identify request |
+----------+-----------------------+-------------------+
| 0xFEFE | Profinet acyclic, DCP | Identify response |
+----------+-----------------------+-------------------+
PNIO status (4 bytes):
* Error code
* Error decode
* Error code 1
* Error code 2
DCP protocol via Ethernet layer 2
---------------------------------
Uses Profinet cyclic realtime protocol.
This is used for example for assigning station name and IP address to devices.
+--------------+------------------+
| Service Type | Description |
+==============+==================+
| 0 | Request |
+--------------+------------------+
| 1 | Response Success |
+--------------+------------------+
+------------+-------------+
| Service ID | Description |
+============+=============+
| 3 | Get |
+------------+-------------+
| 4 | Set |
+------------+-------------+
| 5 | Identify |
+------------+-------------+
| 6 | Hello |
+------------+-------------+
+-------------+--------+-----------+------------------------+------------------------------------+
| Service IDs | Option | Suboption | Description | Contains |
+=============+========+===========+========================+====================================+
| 3 | 1 | 1 | MAC address | |
+-------------+--------+-----------+------------------------+------------------------------------+
| 3, 4, 5, 6 | 1 | 2 | IP parameter | IP address, netmask, gateway |
+-------------+--------+-----------+------------------------+------------------------------------+
| 3, 4, 5, 6 | 1 | 3 | Full IP suite | IP address, netmask, gateway, DNS |
+-------------+--------+-----------+------------------------+------------------------------------+
| 3, 5 | 2 | 1 | Type of station | Device vendor |
+-------------+--------+-----------+------------------------+------------------------------------+
| 3, 4, 5, 6 | 2 | 2 | Name of station | Also permanent/temporary |
+-------------+--------+-----------+------------------------+------------------------------------+
| 3, 5, 6 | 2 | 3 | Device ID | VendorID, DeviceID |
+-------------+--------+-----------+------------------------+------------------------------------+
| 3, 5 | 2 | 4 | Device role | ? |
+-------------+--------+-----------+------------------------+------------------------------------+
| 3, 5 | 2 | 5 | Device options | Which options are available |
+-------------+--------+-----------+------------------------+------------------------------------+
| Filter only | 2 | 6 | Alias name | |
+-------------+--------+-----------+------------------------+------------------------------------+
| 6 | 2 | 8 | OEM device ID | |
+-------------+--------+-----------+------------------------+------------------------------------+
| 4 | 5 | 1 | Start transaction | |
+-------------+--------+-----------+------------------------+------------------------------------+
| 4 | 5 | 2 | End transaction | |
+-------------+--------+-----------+------------------------+------------------------------------+
| 4 | 5 | 3 | Signal (Flash LED) | Flash once |
+-------------+--------+-----------+------------------------+------------------------------------+
| 4 | 5 | 4 | Response | |
+-------------+--------+-----------+------------------------+------------------------------------+
| 4 | 5 | 6 | Reset to factory | Type of reset |
+-------------+--------+-----------+------------------------+------------------------------------+
| 5 | 255 | 255 | All | |
+-------------+--------+-----------+------------------------+------------------------------------+
| 6 | 6 | 1 | Device initiative | Issues Hello at power on |
+-------------+--------+-----------+------------------------+------------------------------------+
Setting the station name and IP address etc:
* Permanent: The values should be used after power cycling
* Temporary: After power cycling the station name should be ``""`` and the IP address ``0.0.0.0``
DCE/RPC protocol via UDP
------------------------
In the connect request, the IO-controller (PLC) tells the IO-device how it
believes that the IO-device hardware is set up. If that not is correct, the
IO-device will complain.
Message types:
* "Request" sent from system A
* "Indication" when it is received in system B
* "Response" sent back from system B
* "Confirmation" when received in system A
The "Response" and "Confirmation" can contain a positive value (+, ACK) or negative
value (-, NACK, indicating an error).
Most often (DCE/RPC) requests are sent from the IO-controller, but CControl
request and a few alarm requests are sent from the IO-device.
The section 5.2.40 "PDU checking rules" in the standard describes what to check in
incoming DCE/RPC messages via UDP.
Messages from controller to device:
* Connect request
* Parameter end request ?
* Application ready response
* Read IM0 request
* Release request
* DControl request
* CControl confirmation
* IODRead request
* IODWrite request
Where:
* DControl: Request to IO-device (End of parameterization)
* CControl: Request to IO-controller (Application ready)
Operations:
* 0: Connect
* 1: Release
* 2: Read
* 3: Write
* 4: Control
* 5: Read Implicit
* 6: Reject
* 9: Fragment acknowledge
UDP ports:
* 0x8892 = 34962 Port for RT_CLASS_UDP
* 0x8894 = 34964 Listening port for incoming requests, both on IO-device and IO-controller.
* 0xC000 = 49152 and up Ephemeral port range
* 0xC001 = 49153 Ephemeral port for CControl sending???
UDP port numbers are described in Profinet 2.4 section 4.13.3.1.2.4
NDR header in DCE/RPC payload
-----------------------------
The first part of the DCE/RPC payload is the NDR (Network Data Representation) header. For requests, it contains five uint32 values:
* Args Maximum: Buffer size available for the response
* Args Length: Number of bytes payload after the NDR header
* Maximum Count: In requests this it the same values as the Args Maximum. For responses this is the Args Maximum from the request.
* Offset: Always 0.
* Actual Count: Same as Args Length
The Maximum Count, Offset and Actual Count are known as the "Array" block.
In responses there is no Args Maximum field. Instead there is a status field, with these subfields:
* code
* decode
* code1
* code2
DCE/RPC payload
---------------
Examples of block identifiers:
* 0x0001 AlarmNotificationHigh
* 0x0002 AlarmNotificationLow
* 0x0008 IODWriteReqHeader
* 0x0009 IODReadReqHeader
* 0x0010 DiagnosisData
* 0x0019 LogBookData
* 0x0020 I&M0
* 0x0021 I&M1
* 0x0101 ARBlockReq
* 0x0102 IOCRBlockReq
* 0x0103 AlarmCRBlockReq
* 0x0104 ExpectedSubmoduleBlockReq
* 0x0110 IODControlReq
* 0x8001 AlarmAckHigh
* 0x8002 AlarmAckLow
* 0x8008 IODWriteResHeader
* 0x8009 IODReadResHeader
DCE/RPC UUID entities
---------------------
+-----------------------+-------------+-------------------------------------------------------------------------------+
| UUID variant | Section | Description |
+=======================+=============+===============================================================================+
| Object UUID | 4.10.3.2.8 | ``dea00000-6c97-11d1-8271-`` then vendorID, device ID etc |
+-----------------------+-------------+-------------------------------------------------------------------------------+
| PNIO Interface UUID | 4.10.3.2.9 | ``dea00001-6c97-11d1-8271-00a02442df7d`` for IO devices |
+-----------------------+-------------+-------------------------------------------------------------------------------+
| Activity UUID | 4.10.3.2.10 | Same in request and response for one group of Connect-Write-DControl-Ccontrol |
+-----------------------+-------------+-------------------------------------------------------------------------------+
| AR UUID | 5.2.5.47 | Can be the same between several connect attempts. |
+-----------------------+-------------+-------------------------------------------------------------------------------+
| CMInitiatorObjectUUID | 5.1.2 | ``dea00000-6c97-11d1-8271-`` then InstanceID and deviceIdentNumber. |
| | | Only available in Connect requests. |
+-----------------------+-------------+-------------------------------------------------------------------------------+
The section column refers to the Profinet 2.4 Protocol document.
UDP message fragmentation
-------------------------
Profinet has a mechanism (part of DCE/RPC via UDP) to split large frames
(for start-up messages) into smaller fragments. Operating systems, for example
Linux, have a competing mechanism to split frames into fragments.
If sending a large chunk of data via UDP in Linux, it is automatically split
into fragments. The maximum transfer unit (MTU) is often 1500 bytes,
including the IP header (but not the Ethernet header). An IP header is
typically 20 bytes, but some rarely used options would make it larger.
Without any IP header options, the largest IP payload would then be 1480 bytes
and the largest UDP payload would be 1472 bytes. It seems that for Linux, the
largest UDP payload is 1464 bytes before the kernel fragments the message.
Communication relations
-----------------------
+-----------------------------+----------------------------------------------------------------------------------------------+
| Communication Relation (CR) | Description |
+=============================+==============================================================================================+
| IO data CR | Real-time cyclic data. Unacknowledged. |
+-----------------------------+----------------------------------------------------------------------------------------------+
| Record data CR | Non-real time configuration data, for example parameter assignment and device identification |
+-----------------------------+----------------------------------------------------------------------------------------------+
| Alarm CR | Real-time alarms |
+-----------------------------+----------------------------------------------------------------------------------------------+
Details on slots and modules
----------------------------
Subslots 0x8000-0xFFFF are reserved by the Profinet standard.
Subslots in the DAP module:
* 0x1 The device itself
* 0x8000 (32768) Interface 1 (typically named X1)
* 0x8001 (32769) Port 1 of interface 1 (typically named X1 P1)
* 0x8002 (32770) Port 2 of interface 1 (typically named X1 P2)
* 0x8100 (33024) Interface 2
* 0x8101 (33025) Port 1 of interface 2
* 0x8102 (33026) Port 2 of interface 2
Read and write indexes
----------------------
User defined indexes are in the range 0x?? to 0x??
Examples of pre-defined indexes:
* 0x802B PDPortDataCheck for one subslot
* 0xAFF0 I&M0
* 0xAFF1 I&M1
* 0xAFF2 I&M2
* 0xAFF3 I&M3
* 0xF830 LogBookData
* 0xF840 I&M0FilterData
* 0xF841 PDRealData
+-----------------------------+--------------------------------------------------------+
| Data record | Description |
+=============================+========================================================+
| APIData | |
+-----------------------------+--------------------------------------------------------+
| ARData | |
+-----------------------------+--------------------------------------------------------+
| ARFSUDataAdjust | |
+-----------------------------+--------------------------------------------------------+
| AutoConfiguration | |
+-----------------------------+--------------------------------------------------------+
| CombinedObjectContainer | |
+-----------------------------+--------------------------------------------------------+
| ExpectedIdentificationData | |
+-----------------------------+--------------------------------------------------------+
| I&M0FilterData | |
+-----------------------------+--------------------------------------------------------+
| LogBookData | |
+-----------------------------+--------------------------------------------------------+
| ModuleDiffBlock | |
+-----------------------------+--------------------------------------------------------+
| PDExpectedData | |
+-----------------------------+--------------------------------------------------------+
| PDInterfaceAdjust | Use standard LLDP mode or legacy LLDP mode. |
+-----------------------------+--------------------------------------------------------+
| PDInterfaceDataReal | Actual station name (not ChassisID), interface MAC |
| | address, IP address etc. |
+-----------------------------+--------------------------------------------------------+
| PDInterfaceFSUDataAdjust | |
+-----------------------------+--------------------------------------------------------+
| PDInterfaceSecurityAdjust | |
+-----------------------------+--------------------------------------------------------+
| PDIRData | |
+-----------------------------+--------------------------------------------------------+
| PDIRSubframeData | |
+-----------------------------+--------------------------------------------------------+
| PDNCDataCheck | |
+-----------------------------+--------------------------------------------------------+
| PDPortDataAdjust | Turn on and off LLDP transmission. |
+-----------------------------+--------------------------------------------------------+
| PDPortDataCheck | Wanted peer chassisID, port ID etc. |
+-----------------------------+--------------------------------------------------------+
| PDPortDataReal | Actual PortID, peer PortID, peer ChassisID, peer MAC |
| | address, MAU type, link state etc. |
+-----------------------------+--------------------------------------------------------+
| PDPortDataRealExtended | |
+-----------------------------+--------------------------------------------------------+
| PDPortStatistic | Sent and received bytes. Errors and discards. |
+-----------------------------+--------------------------------------------------------+
| PDRealData | Actual values and statistics for interface and port. |
+-----------------------------+--------------------------------------------------------+
| PDSyncData | |
+-----------------------------+--------------------------------------------------------+
| PDTimeData | |
+-----------------------------+--------------------------------------------------------+
| PE_EntityFilterData | |
+-----------------------------+--------------------------------------------------------+
| PE_EntityStatusData | |
+-----------------------------+--------------------------------------------------------+
| PE_EntityStatusData | |
+-----------------------------+--------------------------------------------------------+
| RealIdentificationData | Which modules (and submodules) are plugged into which |
| | slots (and subslots). |
+-----------------------------+--------------------------------------------------------+
| SubstituteValue | |
+-----------------------------+--------------------------------------------------------+
Allowed station name
--------------------
The specification is found i Profinet 2.4 section 4.3.1.4.16
Diagnosis details
-----------------
There are several formats in which the diagnosis information can be sent to
the PLC. The formats are described by the USI value.
Channel diagnosis (USI 0x8000, one of the standard formats):
* Channel number
* Channel properties (direction)
* Channel error type
Extended channel diagnosis (USI 0x8002, one of the standard formats):
* Same as for "Channel diagnosis", and also
* ExtChannelErrorType
* ExtChannelErrorAddValue
Qualified channel diagnosis (USI 0x8003, one of the standard formats):
* Same as for "Extended channel diagnosis", and also
* Qualified sub severities
USI format (USI 0x0000 to 0x7FFF):
* Manufacturer data
Storage of diagnosis info
^^^^^^^^^^^^^^^^^^^^^^^^^
There is at most one diagnosis
entry stored for each ChannelErrortype, extChannelErrorType combination.
LLDP
----
A protocol for neighbourhood detection. LLDP frames are not forwarded by managed
switches, so the frames are useful to detect which neighbour the device is
connected to.
An LLDP frame is sent by a Profinet device every 5 seconds, to indicate
the IP address etc.
A Profinet device also receives LLDP frames. It uses the chassis ID and the
frame ID of the frame from its neighbour, to set the alias name.
The LLDP frame is a layer 2 Ethernet frame with the payload consisting of a
number of Type-Length-Value (TLV) blocks. The first 16 bits of each block
contains info on the block type and block payload length.
It is followed by the block payload data.
Different TLV block types may have subtypes defined (within the payload).
The frame is broadcast to MAC address ``01:80:c2:00:00:0e`` and
has an Ethertype of ``0x88cc``.
TLV types:
* 0: (End of LLDP frame indicator)
* 1: Chassis ID. Subtypes 4: MAC address. 7: Locally assigned name
* 2: Port ID. Subtypes 3: MAC address. 7: Locally assigned name
* 3: Time to live in seconds
* 4: Port description
* 5: System name
* 7: Capabilities (Router, Bridge, Telephone etc)
* 8: Management address (optional for LLDP, mandatory in Profinet). Includes IP
address and interface number. Address subtype 1: IPv4 2: IPv6
* 127: Organisation specific (optional for LLDP. See below.). Has an
organisation unique code, and a subtype.
Organisation unique code ``00:0e:cf`` belongs to Profibus Nutzerorganisation,
and supports these subtypes:
* 1: Measured delay values
* 2: Port status. Contains RTClass2 and RTClass3 port status.
* 5: Chassis MAC address
Organisation unique code ``00:12:0f`` belongs to the IEEE 802.3 organisation,
and supports these subtypes:
* 1: MAC/PHY configuration status. Shows autonegotiation support, and which
speeds are supported. Also MAU type.
* 4: Maximum frame size
Autonegotiation:
* Bit 0: Supported
* Bit 1: Enabled
Speed:
* Bit 0: 1000BASE-T Full duplex
* Bit 1: 1000BASE-T Half duplex
* Bit 10: 100BASE-TX Full duplex
* Bit 11: 100BASE-TX Half duplex
* Bit 13: 10BASE-T Full duplex
* Bit 14: 10BASE-T Half duplex
* Bit 15: Unknown speed
MAU types:
* 0x00 Radio
* 0x10 Copper 100BaseTX Full duplex
* 0x1E Copper 1000BaseT Full duplex
Standard and legacy LLDP name format
------------------------------------
The contents of the LLDP fields PortID and ChassisID were changed in
Profinet v 2.3. An example will show the differences:
+-------------------------------+------------+
| NameOfStation (for interface) | "dut" |
+-------------------------------+------------+
| NameOfPort | "port-001" |
+-------------------------------+------------+
| NameOfStation of neighbour | "b" |
+-------------------------------+------------+
| NameOfPort of neighbour | "port-003" |
+-------------------------------+------------+
We have an alias name for each of our ports, and it is constructed from
the information about the connected neighbour. In this example, our
alias name for our port would be "port-003.b".
Legacy LLDP mode (up to and including Profinet v 2.2):
+-----------------+------------+
| LLDP_PortID | "port-001" |
+-----------------+------------+
| LLDP_ChassisID | "dut" |
+-----------------+------------+
Standard LLDP mode (Profinet v 2.3 and later):
+-----------------+----------------------------------------+
| LLDP_PortID | "port-001.dut" |
+-----------------+----------------------------------------+
| LLDP_ChassisID | devicetype+orderID+serialNumber etc |
+-----------------+----------------------------------------+
If NameOfStation not yet is set, we instead use our device MAC address
(as a string) when constructing the LLDP strings.
The legacy LLDP mode is used by devices conformant to Profinet v2.2 and earlier.
Later Profinet versions uses the standard LLDP mode, but an IO-device can
be ordered by the PLC at startup to use the legacy LLDP mode.
By setting the ``MultipleInterfaceMode`` to ``1`` (instead of ``0``) the PLC
will ask the IO-device to use the standard LLDP mode. This is done by writing
to the PDInterfaceAdjust index.
Media redundancy
----------------
With media redundancy an Ethernet ring topology is used, so that Ethernet frames
can reach all devices in the case of a link failure. To prevent frames to
circulate in the ring forever, a manager will keep one of its links blocked
for normal packets.
If the manager detects a failure in the ring, it will open both its links.
The detection of the ring integrity is done using the Media Redundancy Protocol
(MRP). The manager sends MRP_Test frames in both direction, and verifies that
it self can receive them. In case of ring failure, it will send a
MRP_TopologyChange frame to all clients to inform them that they should clean
their routing tables. Clients can also tell the manager that a link has failed,
by sending a MRP_LinkChange frame.
The MRP implementation will open and close network ports, but will not
affect the Profinet class A and B operation.
Devices supporting media redundancy needs to have at least two Ethernet ports
and an integrated switch.
For Profinet class C there is also Media redundancy with duplicated frames
(MRPD), also known as seamless or bumpless redundancy. It sends two similar
frames with cyclic data in the different ring directions. The frames have
identical FrameID but use different VLAN IDs. In case of a link failure there
is cyclic data available from the other direction.
Profinet also has the concept of system redundancy, where two PLCs with
automatic switchover are used. There are different levels of system
redundancy, named R1, R2, S1 and S2.
====================== ================= ===============
Media redundancy class Conformance class Comments
====================== ================= ===============
RED_CLASS_1 A and B Uses MRP
RED_CLASS_3 C Uses MRPD
RED_CLASS_STREAM D
====================== ================= ===============
An "interconnect ring" connects two MRP rings together, and has four nodes (two
from each normal MRP ring). This requires 3 Ethernet ports on each device.
The interconnect functionality have two modes:
* LC: Link Check (used by Profinet). The status of each link is monitored by the
Ethernet hardware
* RC: Ring Check. Test frames are sent in the interconnect ring.
In summary, these are the available MRP roles:
* MRP client (MRC). Mandatory if MRP is used for Profinet.
* MRP manager (MRM)
* MRP manager with auto negotiation (MRA)
* MRP interconnection client (MIC)
* MRP interconnection manager (MIM)
The nodes participating in the interconnection ring will also have a role in
the normal ring. Thus a node can be for example bot a MRC and a MIM
simultaneusly.
Profinet defines alarms for different events in the MRP implementation, for
example if multiple MRP managers exist in the same ring or if the neighbour
belongs to wrong MRP ring.
It is possible to to read out the MRP status via the SNMP protocol. This is
done using the MRP-MIB. It is optional for Profinet devices to implement this.
See IEC 62439-2 for MRP protocol details.
Relevant standards
------------------
* IEC IEEE 60802 TSN Profile for Industrial Automation
* IEC 61158-5-10 PROFINET IO: Application Layer services for decentralized periphery (Also known as PNO-2.712)
* IEC 61158-6-10 PROFINET IO: Application Layer protocol for decentralized periphery (Also known as PNO-2.722)
* IEC 61784 Describes several fieldbuses, for example Foundation Fieldbus, Profibus and Profinet.
* IEC 61784-2 Profiles for decentralized periphery (Also known as PNO-2.742)
* IEC 62439-2 Media Redundancy Protocol
* IEEE 802 LANs
* IEEE 802.1 Higher Layer LAN Protocols
* IEEE 802.1AB LLDP (A topology detection protocol)
* IEEE 802.1AS Time synchronization
* IEEE 802.1Q Virtual LANs (VLAN)
* IEEE 802.3 Ethernet
* IEEE 802.11 WiFi
* IETF :rfc:`768` UDP
* IETF :rfc:`791` IP
* IETF :rfc:`792` ICMP
* IETF :rfc:`826` ARP
* IETF :rfc:`1034` DNS
* IETF :rfc:`1157` SNMP
* IETF :rfc:`1213` Management Information Base v 2 (MIB-II)
* IETF :rfc:`2131` DHCP
* IETF :rfc:`2132` DHCP Options
* IETF :rfc:`2741` AgentX protocol for SNMP subagents
* IETF :rfc:`2863` The Interfaces Group MIB
* IETF :rfc:`3414` SNMPv3
* IETF :rfc:`3418` Management Information Base (MIB) for SNMP
* IETF :rfc:`3635` Definitions of Managed Objects for the Ethernet-like Interface Types
* IETF :rfc:`5890` Internationalized Domain Names for Applications (IDNA)
* ISO/IEC 7498-1 ?
* ISO 8859-1 ?
* ISO 15745 ?
* Open Group C706 Remote procedure calls (RPC) DCE 1.1 https://publications.opengroup.org/c706

View File

@ -0,0 +1,237 @@
Sample app details
==================
Cyclic data timing
------------------
The sample application GSDML file has the element ``<TimingProperties>``, which
has an attribute ``SendClock="32"`` in units of 1/32 milliseconds.
It corresponds to 1 ms, and thus the IO-device will send cyclic
data every millisecond. Note that the sample application does not read
the GSDML file, the file just describes the hard coded value used
in the sample application.
The sample application timer uses ``APP_TICK_INTERVAL_US`` which is
1000 microseconds = 1 millisecond. To be able to tell this information to
others, the value ``32`` is given in the ``min_device_interval`` field of the
device configuration.
In the PLC you can set the reduction ratio to slow down the data exchange,
and this information is sent to the IO-device on startup.
For example a reduction ratio of 4 will cause the cyclic data to
be sent with 4 times longer interval (i.e. every 4 ms) from the IO-device.
Using more modules
------------------
If necessary, increase the values for ``PNET_MAX_SLOTS`` and
``PNET_MAX_SUBSLOTS`` in ``include/pnet_api.h``.
In the sample app, the input data is written to all input modules ("8 bit in +
8 bit out" and "8 bit in"). The LED is controlled by the output module ("8 bit
in + 8 bit out" or "8 bit out") with lowest slot number.
The alarm triggered by button 2 is sent from the input module with lowest slot
number (if any).
In order to handle larger incoming DCE/RPC messages (split in several frames),
you might need to increase PNET_MAX_SESSION_BUFFER_SIZE. Note that this will
increase the memory consumption.
Sample app data payload
-----------------------
This example is for when using the “8 bit in + 8 bit out” module in slot 1.
The periodic data sent from the sample application IO-device to IO-controller
is one byte:
* Lowest 7 bits: A counter which has its value updated every 10 ms
* Most significant bit: Button1
When looking in Wireshark, look at the "Profinet IO Cyclic Service Data Unit",
which is 40 bytes. The relevant byte it the fourth byte from left in this
block.
Details of the 40 bytes from IO-device to IO-controller:
* IOPS from slot 0, subslot 1
* IOPS from slot 0, subslot 0x8000
* IOPS from slot 0, subslot 0x8001
* IO data from slot 1, subslot 1 (input part of the module)
* IOPS from slot 1, subslot 1 (input part of the module)
* IOCS from slot 1, subslot 1 (output part of the module)
* (Then 34 bytes of padding, which is 0)
From the IO-controller to IO-device is one data byte sent:
* Most significant bit: LED
When looking in Wireshark, look at the "Profinet IO Cyclic Service Data Unit",
which is 40 bytes. The relevant byte is the fifth byte from left in this
block. The value toggles between 0x80 and 0x00.
Details of the 40 bytes from IO-controller to IO-device:
* IOCS to slot 0, subslot 1
* IOCS to slot 0, subslot 0x8000
* IOCS to slot 0, subslot 0x8001
* IOCS to slot 1, subslot 1 (input part of the module)
* IO data to slot 1, subslot 1 (output part of the module)
* IOPS to slot 1, subslot 1 (output part of the module)
* (Then 34 bytes of padding, which is 0)
Note that Wireshark can help parsing the cyclic data by using the GSDML file.
See the Wireshark page in this document.
Ethernet frames sent during start-up
------------------------------------
For this example, the IO-controller is started first, and then the IO-device.
+---------------------------+----------+----------------------------------------------------------------------------------------------------------------------+
| Sender | Protocol | Content |
+===========================+==========+======================================================================================================================+
| IO-controller (broadcast) | LLDP | Name, MAC, IP address, port name (sent every 5 seconds) |
+---------------------------+----------+----------------------------------------------------------------------------------------------------------------------+
| IO-controller (broadcast) | PN-DCP | "Ident req". Looking for "rt-labs-dev" (sent every 2.5 seconds) |
+---------------------------+----------+----------------------------------------------------------------------------------------------------------------------+
| IO-controller (broadcast) | ARP | Is someone else using my IP? |
+---------------------------+----------+----------------------------------------------------------------------------------------------------------------------+
| IO-device (broadcast) | LLDP | Name, MAC, IP address, port name (sent every 5 seconds??) |
+---------------------------+----------+----------------------------------------------------------------------------------------------------------------------+
| IO-device (broadcast) | PN-DCP | "Hello req". Station name "rt-labs-dev", IP address, gateway, vendor, device (sent every 3 seconds) |
+---------------------------+----------+----------------------------------------------------------------------------------------------------------------------+
| IO-device | PN-DCP | "Ident OK" Identify response. Station name "rt-labs-dev", IP address, netmask, gateway, VendorID, DeviceID, options |
+---------------------------+----------+----------------------------------------------------------------------------------------------------------------------+
| IO-controller | PN-DCP | "Set Req" Set IP request. Use IP address and gateway. |
+---------------------------+----------+----------------------------------------------------------------------------------------------------------------------+
| IO-device | PN-DCP | "Set OK" Status. |
+---------------------------+----------+----------------------------------------------------------------------------------------------------------------------+
| IO-controller (broadcast) | ARP | Who has <IO-device IP address>? |
+---------------------------+----------+----------------------------------------------------------------------------------------------------------------------+
| IO-device | ARP | IP <IO-device IP address> is at <IO-device MAC address> |
+---------------------------+----------+----------------------------------------------------------------------------------------------------------------------+
| IO-controller | PNIO-CM | "Connect request" Controller MAC, timeout, input + output data (CR), modules + submodules in slots |
+---------------------------+----------+----------------------------------------------------------------------------------------------------------------------+
| IO-device | PNIO-CM | "Connect response" MAC address, UDP port, input + output + alarm CR, station name |
+---------------------------+----------+----------------------------------------------------------------------------------------------------------------------+
| IO-device | PNIO-PS | FrameID 0x8001. Cycle counter, provider stopped. 40 bytes data. |
+---------------------------+----------+----------------------------------------------------------------------------------------------------------------------+
| IO-controller | PNIO-PS | FrameID 0x8000. Cycle counter, provider running. 40 bytes data. |
+---------------------------+----------+----------------------------------------------------------------------------------------------------------------------+
| IO-controller | PNIO-CM | "Write request" API, slot, subslot, data. |
+---------------------------+----------+----------------------------------------------------------------------------------------------------------------------+
| IO-device | PNIO-CM | "Write response" API, slot, subslot, status. |
+---------------------------+----------+----------------------------------------------------------------------------------------------------------------------+
| IO-controller | PNIO-CM | "Control request" (DControl). Command: ParameterEnd. |
+---------------------------+----------+----------------------------------------------------------------------------------------------------------------------+
| IO-device | PNIO-CM | "Control response" Command: Done |
+---------------------------+----------+----------------------------------------------------------------------------------------------------------------------+
| IO-device | PNIO-PS | FrameID 0x8001. Cycle counter, provider running. 40 bytes data. |
+---------------------------+----------+----------------------------------------------------------------------------------------------------------------------+
| IO-device | PNIO-CM | "Control request" (CControl). Command: ApplicationReady |
+---------------------------+----------+----------------------------------------------------------------------------------------------------------------------+
| IO-controller | PNIO-CM | "Control response" Command: ApplicationReadyDone |
+---------------------------+----------+----------------------------------------------------------------------------------------------------------------------+
The order of the PNIO-PS frames is somewhat random in relation to PNIO-CM frames.
+------------+---------------------------------+
| Protocol | Description |
+============+=================================+
| LLDP | |
+------------+---------------------------------+
| ARP | |
+------------+---------------------------------+
| PN-DCP | Acyclic real-time data |
+------------+---------------------------------+
| PNIO-PS | Cyclic real-time data |
+------------+---------------------------------+
| PNIO-AL | Acyclic real-time alarm |
+------------+---------------------------------+
| PNIO-CM | UDP, port 34964 = 0x8892 |
+------------+---------------------------------+
Ethernet frames sent at alarm
-----------------------------
Frames sent when pressing button 2.
+---------------+----------+----------------------------------------------------------------------------------------+
| Sender | Protocol | Content |
+===============+==========+========================================================================================+
| IO-device | PN-AL | Alarm notification high, slot, subslot, module, submodule, sequence, 1 byte user data |
+---------------+----------+----------------------------------------------------------------------------------------+
| IO-controller | PN-AL | ACK-RTA-PDU |
+---------------+----------+----------------------------------------------------------------------------------------+
| IO-controller | PN-AL | Alarm ack high, alarm type Process, slot, subslot, Status OK |
+---------------+----------+----------------------------------------------------------------------------------------+
| IO-device | PN-AL | ACK-RTA-PDU |
+---------------+----------+----------------------------------------------------------------------------------------+
Cyclic data for the different slots
-----------------------------------
This is an example if you populate slot 1 to 3 with different modules.
+------+---------+--------------------------------------------+-----------------------------------------+--------------------------------------------+
| Slot | Subslot | Description | | Contents of Input CR | | Contents of Output CR |
| | | | | (to IO-controller) | | (from IO-controller) |
+======+=========+============================================+=========================================+============================================+
| 0 | 1 | The IO-Device itself | (data) + IOPS | IOCS |
+------+---------+--------------------------------------------+-----------------------------------------+--------------------------------------------+
| 0 | 0x8000 | Interface 1 | (data) + IOPS | IOCS |
+------+---------+--------------------------------------------+-----------------------------------------+--------------------------------------------+
| 0 | 0x8001 | Port 0 of interface 1 | (data) + IOPS | IOCS |
+------+---------+--------------------------------------------+-----------------------------------------+--------------------------------------------+
| 1 | 1 | Input part of "8-bit in 8-bit out" module | data (1 byte) + IOPS | IOCS |
| | +--------------------------------------------+-----------------------------------------+--------------------------------------------+
| | | Output part of "8-bit in 8-bit out" module | IOCS | data (1 byte) + IOPS |
+------+---------+--------------------------------------------+-----------------------------------------+--------------------------------------------+
| 2 | 1 | "8-bit in" module | data (1 byte) + IOPS | IOCS |
+------+---------+--------------------------------------------+-----------------------------------------+--------------------------------------------+
| 3 | 1 | "8-bit out" module | IOCS | data (1 byte) + IOPS |
+------+---------+--------------------------------------------+-----------------------------------------+--------------------------------------------+
Note that the submodules (in subslots) in slot 0 do not send any cyclic data,
but they behave as inputs (as they send cyclic IOPS).
Output (from PLC)::
+----------------+ +-----------+
| IO-device | | PLC |
| +----------+ | DATA | |
| | | | <----------- | |
| | CPM | | | |
| | | | IOPS | |
| | | | <----------- | |
| +----------+ | | |
| | | |
| +----------+ | | |
| | | | | |
| | PPM | | | |
| | | | IOCS | |
| | | | -----------> | |
| +----------+ | | |
| | | |
+----------------+ +-----------+
Input (to PLC)::
+----------------+ +-----------+
| IO-device | | PLC |
| +----------+ | IOCS | |
| | | | <----------- | |
| | CPM | | | |
| | | | | |
| | | | | |
| +----------+ | | |
| | | |
| +----------+ | DATA | |
| | | | -----------> | |
| | PPM | | | |
| | | | IOPS | |
| | | | -----------> | |
| +----------+ | | |
| | | |
+----------------+ +-----------+

View File

@ -0,0 +1,223 @@
ack
acyclic
Adafruit
AgentX
Anel
Args
autonegotiation
Avahi
BaseT
BaseTX
BaseTXFD
baudrate
Bittwist
Bonjour
Bool
boolean
booleans
breakpoint
bsp
bss
Buildroot
bumpless
ccmake
CControl
ccontrol
Changelog
chassisID
CiR
CMake
cmake
cnf
Codesys
collab
Commonmark
comparator
conformant
COntroller
customise
Datagram
DControl
dcontrol
DCP
defence
Democontroller
dev
DEvelopment
DInt
Doxygen
dropdown
dut
DWord
Einstellung
endian
Endian
endianness
enums
Ethercat
Ethertype
exe
eXtended
Fieldbus
fieldbuses
FilterData
Gbit
GByte
gcc
gdb
Github
gtest
hostname
Icinga
Ident
ident
IEC
ifAdminStatus
ifDescr
ifDescr
ifEntry
ifIndex
ifIndex
ifInDiscards
ifInErrors
ifInOctets
ifMtu
ifOperStatus
ifOutDiscards
ifOutErrors
ifOutOctets
ifPhysAddress
ifPhysAddress
ifSpeed
ifTable
ifType
imager
implementers
Infineon
init
Interoperability
IOxS
Ip
iPar
IPv
IPv4
iso
Isochronous
J-link
jitter
journalctl
kB
libprofinet
libreNMS
lldpConfigManAddrPortsTxEnable
lldpd
lldpLocPortDesc
lwip
lwIP
makefiles
Mbit
MByte
MIB
mib
microcontroller
multicast
multicore
nano
neighbour
neighbourhood
neighbours
netload
netmask
nmap
ns
Nutzerorganisation
orderID
organisation
osal
Parameterization
parameterization
persistency
pcap
picocom
PLC
pluggable
pnet
Popp
prio
PRMEND
Profibus
Profinet
Proneta
PwrCtrl
qemu
Raspbian
realtime
remanence
repo
req
resends
Responder
reST
reStructuredText
rfc
rootfs
rtlabs
runtime
Scalance
Segger
severities
Simatic
snmp
snmpd
snmpwalk
softplc
stationname
Steuerung
structs
subagent
subdirectory
subelement
subelements
subfields
subframes
subitem
submodule
submodules
subnet
suboption
suboptions
subparts
subslot
subslots
subtab
subtree
subtrees
subtype
subtypes
sudo
sysContact
sysDescr
sysLocation
sysName
sysObjectId
sysServices
sysUpTime
tcpdump
toolchain
txt
uint
UInt
unicast
Unmount
uptime
vendorID
Virtualbox
WiFi
Wireshark
xBusy
xError
xmc
xml
xRunning
Yocto
Zabbix

View File

@ -0,0 +1,3 @@
/* Reduce subheading size */
h3{font-size: 110%;}

View File

@ -0,0 +1,15 @@
/* From https://rackerlabs.github.io/docs-rackspace/tools/rtd-tables.html */
/* override table width restrictions */
@media screen and (min-width: 500px) {
.wy-table-responsive table td {
/* !important prevents the common CSS stylesheets from overriding
this as on RTD they are loaded after this stylesheet */
white-space: normal !important;
}
.wy-table-responsive {
overflow: visible !important;
}
}

View File

@ -0,0 +1,565 @@
.. _tutorial:
Tutorial
========
In this tutorial we will run the p-net Profinet device stack and its
sample application on a Raspberry Pi, which is an embedded Linux board.
You can optionally connect two LEDs and two buttons to the Raspberry Pi
for easier interaction with the sample application.
We will use a second Raspberry Pi as a PLC (Programmable Logic Controller =
IO controller) running Codesys soft PLC.
Necessary hardware to complete the tutorial:
* 1 Raspberry Pi as IO-device
* 1 Raspberry Pi as IO-controller (alternatively a Siemens PLC).
* 1 Ethernet switch
* 3 Ethernet cables
Optional hardware:
* Keyboard, mouse and monitor for the Raspberry Pi running as IO-device. If
this not is available it is helpful with a USB-to-serial cable for talking
to the Raspberry Pi (as it will automatically change IP address now and then).
* LEDs and buttons for connecting to the Raspberry Pi running as IO-device.
It will take approximately 30 minutes to get p-net running on a Raspberry Pi.
After this the sample application will wait for incoming connects.
An additional hour is required to get another Raspberry Pi up and running
as an IO-controller (PLC), and to study the sample application data.
Sample app description
----------------------
The sample application implements a Profinet IO-device, having an
IO-module with 8 digital inputs and 8 digital outputs. The example uses
two buttons connected to inputs and one LED connected to one of the outputs
(we call this the "data LED").
A second LED is connected as the Profinet "Signal LED", which can be flashed in
order to identify a particular IO-device if you have many devices.
.. image:: illustrations/TutorialOverview.png
The LED1 ("data LED") on the IO-device is controlled by the IO-controller
(PLC), and is normally flashing. With Button1 on the IO-device it is
possible to tell the PLC to turn on and off the flashing of the LED.
Button2 triggers sending an alarm from the IO-device to the PLC.
* Button1: Setting cyclic data value
* Button2: Trigger alarm, setting diagnosis etc
* LED1: Data LED
* LED2: Signal LED
The resulting Ethernet traffic can be studied (see below).
Notes to advanced users
-----------------------
The IO-device sample application can be running on:
* Raspberry Pi (as described in this tutorial)
* Some other embedded Linux board
* A Linux laptop (or a Linux guest in Virtualbox)
* An embedded board running an RTOS, such as RT-kernel
Instead of a Codesys soft PLC running on a second Raspberry Pi, you can
use a Siemens Simatic PLC. See another page of this documentation.
Available files
---------------
The ``samples/pn_dev`` directory in the p-net repository contains the source code
for this example. It also contains a GSD file (written in GSDML), which tells
the IO-controller how to communicate with the IO-device.
Those parts of the sample application that are dependent on whether you run
Linux or an RTOS are located in ``src/ports``.
Modules and slots
-----------------
Slots are locations where you can put modules.
The GSDML file for the sample app defines these modules:
+----------------------+-------------------------------+----------------------------------+
| Module | Input data (to IO-controller) | Output data (from IO-controller) |
+======================+===============================+==================================+
| 8 bit in + 8 bit out | 1 byte | 1 byte |
+----------------------+-------------------------------+----------------------------------+
| 8 bit in | 1 byte | |
+----------------------+-------------------------------+----------------------------------+
| 8 bit out | | 1 byte |
+----------------------+-------------------------------+----------------------------------+
There are 4 slots for the sample app (in addition to slot 0 which is used by the
DAP module), and for this sample app any of the modules fit in any of
the slots 1 to 4.
In this example we will use the "8 bit in + 8 bit out" module in slot 1.
Set up the IO-device Raspberry Pi for running p-net
---------------------------------------------------
As the PLC typically will change the IP address of the IO-device,
we recommend that you connect a keyboard, mouse and monitor to the Raspberry
Pi running the p-net sample application. Alternatively you can use a
USB-to-serial cable to communicate with the Raspberry Pi from your laptop.
To setup Raspbian on a Raspberry Pi, and optionally connect buttons and LEDs,
see :ref:`prepare_raspberry`.
A LED is controlled by the Linux sample app by writing to a file, for example
``/sys/class/gpio/gpio17/value``. A ``0`` or ``1`` will be written to the
file upon LED state changes. This is done by a script, for easy adaptation to
your hardware.
If you do not have a physical LED, you can use an alternate script that
writes to plain text files instead. Usage is described below.
Install dependencies
--------------------
Your Raspberry Pi needs to be connected to Internet via LAN or WiFi to be
able to download software.
In order to compile p-net on Raspberry Pi, you need a recent version of
``cmake``. Install it::
sudo apt update
sudo apt install snapd
sudo reboot
sudo snap install cmake --classic
Verify the installed version::
cmake --version
Compare the installed version with the minimum version required for p-net
(see first page).
You also need ``git`` to download p-net. Install it using::
sudo apt install git
Download and compile p-net
--------------------------
Create a directory::
mkdir /home/pi/profinet/
cd /home/pi/profinet/
Clone the source::
git clone --recurse-submodules https://github.com/rtlabs-com/p-net.git
This will clone the repository with submodules.
Then create and configure the build::
cmake -B build -S p-net
Build the code::
cmake --build build --target install
We used the ``install`` target to install scripts for manipulating IP
settings, control LEDs etc.
Instead of controlling real LEDs, the default behavior is to write LED output
to regular files. If you have connected real LEDs to your Raspberry Pi,
enable the LED control script::
mv build/set_profinet_leds build/set_profinet_leds.disabled
mv build/set_profinet_leds.raspberrypi build/set_profinet_leds
Notes to advanced users
-----------------------
If you already cloned the repository without the ``--recurse-submodules``
flag then run this in the ``p-net`` folder::
git submodule update --init --recursive
Alternate cmake command to also adjust some settings::
cmake -B build -S p-net -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTING=OFF -DBUILD_SHARED_LIBS=ON -DUSE_SCHED_FIFO=ON
You can choose any name for the build folder, for instance if you want
to build different configurations.
You can use the ``-j`` flag to ``make`` if you like to enable parallel build.
Depending on how you installed cmake, you might need to run ``snap run cmake``
instead of ``cmake``.
It is possible to specify the location of the submodule repositories.
See the end of this page for details.
Run the sample application
------------------------------
Run the sample app in the build directory::
cd build
Usage of the IO-device sample application:
.. code-block:: none
pi@pndevice-pi:~/profinet/build$ ./pn_dev -h
Sample application for p-net Profinet device stack.
Wait for connection from IO-controller.
Then read buttons (input) and send to controller.
Listen for application LED output (from controller) and set application LED state.
It will also send a counter value (useful also without buttons and LED).
Button1 value is sent in the periodic data.
Button2 cycles through triggering an alarm, setting diagnosis and creating logbook entries.
Also the mandatory Profinet signal LED is controlled by this application.
The LEDs are controlled by the script set_profinet_leds
located in the same directory as the application binary.
A version for Raspberry Pi is available, and also a version writing
to plain text files (useful for demo if no LEDs are available).
Assumes the default gateway is found on .1 on same subnet as the IP address.
Optional arguments:
--help Show this help text and exit
-h Show this help text and exit
-v Incresase verbosity. Can be repeated.
-f Reset to factory settings, and store to file. Exit.
-r Remove stored files and exit.
-g Show stack details and exit. Repeat for more details.
-i INTERF Name of Ethernet interface to use. Defaults to eth0
-s NAME Set station name. Defaults to rt-labs-dev Only used
if not already available in storage file.
-b FILE Path (absolute or relative) to read Button1. Defaults to not read Button1.
-d FILE Path (absolute or relative) to read Button2. Defaults to not read Button2.
-p PATH Absolute path to storage directory. Defaults to use current directory.
p-net revision: 0.1.0+bb4177a
Enable the Ethernet interface and set the initial IP address::
sudo ifconfig eth0 192.168.0.50 netmask 255.255.255.0 up
Run the sample application::
sudo ./pn_dev -v
Example output:
.. code-block:: none
pi@pndevice-pi:~/profinet/build$ sudo ./pn_dev -v
** Starting Profinet sample application 0.1.0+bb4177a **
Number of slots: 5 (incl slot for DAP module)
P-net log level: 3 (DEBUG=0, FATAL=4)
App verbosity level: 1
Number of ports: 1
Network interfaces: eth0
Button1 file:
Button2 file:
Station name: rt-labs-dev
Management port: eth0
Physical port [1]: eth0
Current hostname: pndevice-pi
Current IP address: 192.168.0.50
Current Netmask: 255.255.255.0
Current Gateway: 192.168.0.1
Storage directory: /home/pi/profinet/build
Profinet signal LED call-back. New state: 0
Network script for eth0: Set IP 192.168.0.50 Netmask 255.255.255.0 Gateway 192.168.0.1 Permanent: 1 Hostname: rt-labs-dev Skip setting hostname: true
Module plug call-back
Pull old module. API: 0 Slot: 0 Slot was empty.
Plug module. API: 0 Slot: 0 Module ID: 0x1
Submodule plug call-back.
Pull old submodule. API: 0 Slot: 0 Subslot: 1 Subslot was empty.
Plug submodule. API: 0 Slot: 0 Module ID: 0x1 Subslot: 1 Submodule ID: 0x1 "DAP Identity 1"
Data Dir: NO_IO In: 0 Out: 0 (Exp Data Dir: NO_IO In: 0 Out: 0)
Submodule plug call-back.
Pull old submodule. API: 0 Slot: 0 Subslot: 32768 Subslot was empty.
Plug submodule. API: 0 Slot: 0 Module ID: 0x1 Subslot: 32768 Submodule ID: 0x8000 "DAP Interface 1"
Data Dir: NO_IO In: 0 Out: 0 (Exp Data Dir: NO_IO In: 0 Out: 0)
Submodule plug call-back.
Pull old submodule. API: 0 Slot: 0 Subslot: 32769 Subslot was empty.
Plug submodule. API: 0 Slot: 0 Module ID: 0x1 Subslot: 32769 Submodule ID: 0x8001 "DAP Port 1"
Data Dir: NO_IO In: 0 Out: 0 (Exp Data Dir: NO_IO In: 0 Out: 0)
Waiting for connect request from IO-controller
The IP settings are stored to file. If you accidentally have run the application
when IP settings were wrong, use this command to remove the stored settings::
sudo ./pn_dev -r
Now you have installed the sample app on the Raspberry Pi, congratulations!
In order to see it in action, you need to connect it to a PLC.
Set up the PLC
--------------
We suggest that you use Codesys soft PLC.
Install Raspberry Pi OS on the second Raspberry Pi. No serial cable or LEDs are
required.
See :ref:`prepare_raspberry` and :ref:`using-codesys` for how to set it up as an IO-controller (PLC).
Connect the two Raspberry Pi boards and your laptop via an Ethernet switch.
Print-out from Linux sample application when connecting to PLC
--------------------------------------------------------------
.. highlight:: none
This is the typical output from the Linux sample application at startup if
you enable verbose output::
pi@pndevice-pi:~/profinet/build$ sudo ./pn_dev -v -b /sys/class/gpio/gpio22/value -d /sys/class/gpio/gpio27/value
** Starting Profinet sample application 0.1.0+bb4177a **
Number of slots: 5 (incl slot for DAP module)
P-net log level: 3 (DEBUG=0, FATAL=4)
App verbosity level: 1
Number of ports: 1
Network interfaces: eth0
Button1 file: /sys/class/gpio/gpio22/value
Button2 file: /sys/class/gpio/gpio27/value
Station name: rt-labs-dev
Management port: eth0
Physical port [1]: eth0
Current hostname: pndevice-pi
Current IP address: 192.168.0.50
Current Netmask: 255.255.255.0
Current Gateway: 192.168.0.1
Storage directory: /home/pi/profinet/build
Profinet signal LED call-back. New state: 0
Network script for eth0: Set IP 0.0.0.0 Netmask 0.0.0.0 Gateway 0.0.0.0 Permanent: 1 Hostname: rt-labs-dev Skip setting hostname: true
No valid default gateway given. Skipping setting default gateway.
Module plug call-back
Pull old module. API: 0 Slot: 0 Slot was empty.
Plug module. API: 0 Slot: 0 Module ID: 0x1
Submodule plug call-back.
Pull old submodule. API: 0 Slot: 0 Subslot: 1 Subslot was empty.
Plug submodule. API: 0 Slot: 0 Module ID: 0x1 Subslot: 1 Submodule ID: 0x1 "DAP Identity 1"
Data Dir: NO_IO In: 0 Out: 0 (Exp Data Dir: NO_IO In: 0 Out: 0)
Submodule plug call-back.
Pull old submodule. API: 0 Slot: 0 Subslot: 32768 Subslot was empty.
Plug submodule. API: 0 Slot: 0 Module ID: 0x1 Subslot: 32768 Submodule ID: 0x8000 "DAP Interface 1"
Data Dir: NO_IO In: 0 Out: 0 (Exp Data Dir: NO_IO In: 0 Out: 0)
Submodule plug call-back.
Pull old submodule. API: 0 Slot: 0 Subslot: 32769 Subslot was empty.
Plug submodule. API: 0 Slot: 0 Module ID: 0x1 Subslot: 32769 Submodule ID: 0x8001 "DAP Port 1"
Data Dir: NO_IO In: 0 Out: 0 (Exp Data Dir: NO_IO In: 0 Out: 0)
Waiting for connect request from IO-controller
Network script for eth0: Set IP 192.168.0.50 Netmask 255.255.255.0 Gateway 0.0.0.0 Permanent: 0 Hostname: rt-labs-dev Skip setting hostname: true
No valid default gateway given. Skipping setting default gateway.
Module plug call-back
Pull old module. API: 0 Slot: 1 Slot was empty.
Plug module. API: 0 Slot: 1 Module ID: 0x32
Submodule plug call-back.
Pull old submodule. API: 0 Slot: 1 Subslot: 1 Subslot was empty.
Plug submodule. API: 0 Slot: 1 Module ID: 0x32 Subslot: 1 Submodule ID: 0x1 "Input 8 bits output 8 bits"
Data Dir: INPUT_OUTPUT In: 1 Out: 1 (Exp Data Dir: INPUT_OUTPUT In: 1 Out: 1)
Connect call-back. AREP: 1 Status codes: 0 0 0 0
Callback on event PNET_EVENT_STARTUP AREP: 1
New data status callback. AREP: 1 Data status changes: 0x35 Data status: 0x35
Run, Valid, Primary, Normal operation, Evaluate data status
Parameter write call-back. AREP: 1 API: 0 Slot: 1 Subslot: 1 Index: 123 Sequence: 2 Length: 4
Bytes: 00 00 00 05
Parameter write call-back. AREP: 1 API: 0 Slot: 1 Subslot: 1 Index: 124 Sequence: 3 Length: 4
Bytes: 00 00 00 06
Dcontrol call-back. AREP: 1 Command: PRM_END
Callback on event PNET_EVENT_PRMEND AREP: 1
Set input data and IOPS for slot 0 subslot 1 "DAP Identity 1" size 0 IOXS_GOOD
Set input data and IOPS for slot 0 subslot 32768 "DAP Interface 1" size 0 IOXS_GOOD
Set input data and IOPS for slot 0 subslot 32769 "DAP Port 1" size 0 IOXS_GOOD
Set input data and IOPS for slot 1 subslot 1 "Input 8 bits output 8 bits" size 1 IOXS_GOOD
Set output IOCS for slot 1 subslot 1 "Input 8 bits output 8 bits"
Application will signal that it is ready for data.
Callback on event PNET_EVENT_APPLRDY AREP: 1
Setting outputs to default values.
Ccontrol confirmation call-back. AREP: 1 Status codes: 0 0 0 0
Callback on event PNET_EVENT_DATA AREP: 1
Setting outputs to default values.
The exact output will depend on for example which modules you use when setting
up the PLC.
Input buttons and LEDs, or files for simulation
-----------------------------------------------
If you use plain files as output instead of LEDs, use this to study the file
for the "Data LED"::
watch -n 0.1 cat /home/pi/profinet/build/pnet_led_1.txt
If you would like to use physical input buttons you must set up the
GPIO files for buttons properly first::
echo 22 > /sys/class/gpio/export
echo 27 > /sys/class/gpio/export
Then::
sudo ./pn_dev -v -b /sys/class/gpio/gpio27/value -d /sys/class/gpio/gpio22/value
It is possible to use plain files as inputs instead of physical buttons::
touch /home/pi/profinet/build/button1.txt
touch /home/pi/profinet/build/button2.txt
sudo ./pn_dev -v -b /home/pi/profinet/build/button1.txt -d /home/pi/profinet/build/button2.txt
Manually write ``1`` or ``0`` to a file to simulate the button press and
release::
echo 1 > /home/pi/profinet/build/button1.txt
echo 0 > /home/pi/profinet/build/button1.txt
If you only have one terminal, you need to run ``pn_dev`` in the background
to be able to run these commands.
That is done by adding a ``&`` at the end of the command to start ``pn_dev`` .
Later on kill the ``pn_dev`` process by using ``sudo pkill pn_dev``.
Study the resulting communication
---------------------------------
LED1 should be flashing by default. Press Button1 to toggle LED1
flashing.
By pressing Button2 you can trigger alarms, add diagnosis etc. See the
printout in the console.
On the page "Capturing and analyzing Ethernet packets" is a description given
on how to study the network traffic. If you are interested in the different
packets sent during startup or the cyclic data payload, see the page
"Sample app details".
Adjust log level
----------------
There is logging available in the p-net stack describing the interaction
with the PLC.
If you would like to change the p-net stack log level, run ``ccmake .`` in
the ``build`` directory. It will start a menu program. Move to the
LOG_LEVEL entry, and press Enter to change to DEBUG. Press ``c`` to save
and ``q`` to exit.
You need to re-build the project for the changes to take effect.
Next steps
----------
Great! You managed to get the sample application running.
Try flashing the Profinet signal LED. See description on the page "Using
Codesys soft PLC".
To enable automatic start of the sample application on power on, see the page
"Install Raspberry Pi OS on the Raspberry Pi".
For Profinet members the "ART tester" tool is available for conformance
testing. Run the conformance tests against the sample app to verify that the
stack is compliant. See a separate page on conformance testing in this
documentation.
To experiment with the SNMP features of conformance class B, see the page
"Network topology detection".
Now it is time for you to start developing your own applications. You can use
the sample app as a starting template.
Experiment by modifying the available modules, and the data types they send
and receive. Modify your GSDML file accordingly to explain the IO-device
behavior to the PLC configuration tool.
A separate page is available with a few ideas on how to write you application.
Remember to run the "ART tester" now and then to verify that you stay compliant.
Timing issues
-------------
If running on a Linux machine without realtime patches, you might face timeout
problems. It can look like::
Callback on event PNET_EVENT_ABORT. Error class: 253 Error code: 6
where the error code most often is 5 or 6.
See the "Real-time properties of Linux" page in this document for solutions, and
the "Using Codesys soft PLC" page for workarounds.
Debug compilation issues
------------------------
To show more details on the compilation, use::
cmake --build build -v
Troubleshooting
---------------
If you have network problems on your IO-device Raspberry Pi, re-run the
``ifconfig`` command given above.
If you have problems establishing a connection to your PLC, connect it
directly to your laptop and run the program Wireshark on the corresponding
Ethernet interface. Study the DCP and LLDP frames to see the current PLC
settings. See another page in this documentation for details on Wireshark usage.
The "Management Address" block in a LLDP frame shows the IP address of the PLC.
There are also other blocks describing the MAC address and the port ID.
You can find the expected IO-device station name in some DCP frames.
Advanced users: OSAL
--------------------
OSAL is a generic OS abstraction library that may be used by multiple
projects in a system. To avoid issues with multiple copies of the
library, possibly of conflicting versions, it has been moved to its
own repository.
``cmake-tools`` is a repository that contains common CMake utilities for
RT-Labs projects. It contains a CMake script ``AddOsal.cmake`` that
simplifies use of OSAL. It supports two different use-cases:
1) Automatic download and build of OSAL
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
During CMake configuration, if OSAL is not found in the system it will
be downloaded and built automatically. For most users this will be the
default.
Run CMake configuration by issuing e.g.::
cmake -B build -S p-net
2) External OSAL
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
During CMake configuration, if OSAL is found then p-net will just link
against the external library.
CMake will find the external OSAL library if it is installed in a
default location such as ``/usr/include`` or ``/usr/local/include``. This
could be the case for a native build or a cross-compiled Linux system
with a staging folder.
CMake can also be told of the path to an installed version of OSAL by
setting ``Osal_DIR`` during configuration, like so::
cmake -B build -S p-net -DOsal_DIR=/path/to/osal/install/cmake
The install folder is produced when running::
make install
or similar in the OSAL build directory.

View File

@ -0,0 +1,699 @@
Using a Simatic PLC as IO controller
====================================
This have been tested with:
* Simatic S7-1200 CPU1215C DC/DC/DC 6ES7 215-1AG40-0xXB0 Firmware version 4.2
It uses a 24 V DC supply voltage.
Install Siemens TIA on a Windows PC
-----------------------------------
Install TIA (SIMATIC STEP 7 and WinCC) V15.1
Download the files from
`https://support.industry.siemens.com/cs/document/109752566/simatic-step-7-and-wincc-v15-trial-download-?dti=0&lc=en-US <https://support.industry.siemens.com/cs/document/109752566/simatic-step-7-and-wincc-v15-trial-download-?dti=0&lc=en-US>`_
* "SIMATIC STEP 7 Basic V15.1 is the easy-to-use engineering system for the
small modular SIMATIC S7-1200 controller and the associated I/Os."
* "SIMATIC STEP 7 Professional V15.1 is the high-performance, integrated
engineering system for the latest SIMATIC controllers S7-1500, S7-1200,
S7-300, S7-400, WinAC and ET 200 CPU."
"SIMATIC WinCC is a supervisory control and data acquisition (SCADA) and
human-machine interface (HMI) system from Siemens."
See https://en.wikipedia.org/wiki/WinCC
Download "WinCC Professional". Use all files from "DVD 1 Setup". The "DVD 2"
files are not necessary.
The installer is split in a number of files, each 2 GByte.
Put the files in a directory on your Windows PC.
To start the installation, double-click the exe file. Restart of your computer
is required.
To show installed software version, use the "Portal view" and the "Start" tab
to the left of the screen. Click "Installed software".
After installation, make sure "PROFINET IO protocol (DCP/LLDP)" is enabled
for your Ethernet interface on your laptop. Otherwise you will have problems
communicating to Profinet devices. Remember however that this setting is
interfering with the "ART Tester" compliance verification tool, so disable
it before starting the "ART Tester" on your laptop.
Using Siemens TIA on a Windows PC: Add devices
----------------------------------------------
Start the application by searching for "TIA portal" in the Windows start menu.
In the start screen, select "Create new project" and enter the details.
Also on the start screen, select "Devices and networks", "Add new device" and
select your PLC model (a CPU that will act as IO-controller).
If you only know the series of your PLC, but not the exact model, then select
for example "Controller" > "SIMATIC S7-1200" > "CPU" > "Unspecified CPU 1200" >
"6ES7 2XX-". Click Add. In the "Device view" you will be notified that the
device not is specified. Click "detect". Make sure your PLC is powered on.
Select "PN/IE" and your Ethernet interface and "Start search". Mark the found
PLC and click "Detect".
Import a GSD file by using the menu "Options > Manage GSD files". Browse to
the directory with your GSD file. Mark the line with the file, and click
"Install".
In the project view, in the left menu select the PLC and the subitem "Device
configuration". Click the "Network view" tab. At the right edge open the
"Hardware catalog". Select "Other field devices" > Profinet IO > I/O >
RT-Labs > "P-Net Samples" > "P-Net Sample App".
Double-click it, and it will appear in the main window.
Use the "Network view" tab, and right-click "Not assigned" on the rt-labs-dev
icon. Select "Assign to new IO-controller" and "PLC_1.PROFINETinterface_1".
Set IP addresses and connect physically
---------------------------------------
Connect to the Siemens PLC directly via an Ethernet cable to your laptop. For
the Simatic S7-1200 PLC you can use any of the two X1 RJ45 connectors. Your
laptop should have a manual IP address in the same subnet range as the PLC
(first three groups in IP address should be the same). Changing the subnet
mask size does not seem to have an impact.
In the project view, in the left menu select the PLC and the subitem "Device
configuration". In the "Properties" tab, use the "General" sub-tab. Select
"PROFINET interface [X1]" and "Ethernet addresses". Enter the IP address
``192.168.0.100`` for the PLC. The subnet mask should be ``255.255.255.0``
and subnet "PN/IE_1".
Right-click the icon of the PLC, and select "Go online". Use type of
interface "PN/IE", your Ethernet network card and "Direct at slot 1 X1".
Select "Show accessible devices" in the dropdown. Click "Start search".
The table should be updated with "Device type" = "CPU 1215C..."
if the connection is OK.
To enter the IP-address of the IO-device, go to "Device view" for the IO-device
and click the IO-device icon. in the "Properties" tab, select the "General"
sub-tab. Select
"PROFINET interface [X1]" and "Ethernet addresses". Enter the IP address
``192.168.0.50`` for the IO-device.
Also in the “PROFINET interface [X1]” section, set the IO cycle update time
to "Set update time manually" and select 16 ms. (Depending on your hardware
you can try faster updates later on).
Add modules to IO-device
------------------------
In the device view, select the "rt-labs-dev" device (by using the dropdown).
Add a module to the "Device overview" tab. That is done by dragging a module
from the "Hardware catalog", which is located to the right of screen.
The modules are found in "Other field devices" > Profinet IO > I/O > rt-labs >
rt-labs dev > Module. Drop the module in the correct slot row in the "Device
overview table". For the tutorial use a "DIO 8xLogicLevel" module in slot 1.
Only modules that fit in the respective slot seems to stick.
Run the application
-------------------
Connect one Ethernet cable between the PLC and the Windows laptop, and one
Ethernet cable between the PLC and the IO-device.
In the network view, right-click the PLC icon and select Compile > "Hardware
(rebuild all)". Repeat for Compile > "Software (rebuild all)". Then right-click
and select "Download to device" > "Hardware configuration".
Click Load in the pop-up window, and then Finish.
Repeat for "Download to device" > "Software (all)".
In the main menu, use Online > "Go online". In the right part of the screen,
use "Online tools" to see the PLC LED states and to go to RUN and STOP modes.
There will be cyclic communication regardless whether the PLC is in RUN or
STOP mode, and the run state information is available in the cyclic data.
No PLC program is necessary for the cyclic communication to take place, but
the payload from the PLC is probably zeros.
Change IO-device station name
-----------------------------
To change which IO-device name the PLC will connect to, use the network view
and click the icon of the IO-device.
Select and change the
name (on top row) in the icon. Do a download (hardware and software) to the
PLC. You can verify the result by looking at the PN-DCP frames in Wireshark.
Connect inputs and outputs
--------------------------
First find the address of the IO-device input byte and output byte. In the
“Device view” for the IO-device, look in the “Device overview” table.
The module "8 bits I
8 bits O" should appear (if previously inserted). Look for the I (input)
address and Q (output) address. The value can be for example 2.
Note that the IO-device must be connected to the PLC (as seen for example in
the network view) for the IO-device to have a PLC address assigned.
In the Project tree, select PLC_1 > PLC tags > Show all tags.
Create a new tag by clicking the first line and enter the name ``ButtonIn``.
Use "Default tag table" and DataType Bool. The address should be of operand
identifier "I" (input). Use address from the "Device view" as described above.
The bit number should be 7. This is written as ``%I2.7``.
Create a new tag ``LEDout``, also of Bool type. The bit number should be 7, so
the address should be for example ``%Q2.7``.
In order to study the values while running, you need to create an watch table.
In the Project tree, select PLC_1 > "Watch and force table" > "Add new watch
table". In the first empty line, double-click on the small icon on the Name field.
Select "ButtonIn". Repeat on next line with "LEDout".
When running, in order to study the values, connect to the PLC ("Online"). On
the "Watch table_1" page, click the small "Monitor all" icon. The values on the
page will be continuously updated.
Enter PLC program
-----------------
In the project tree, under PLC1 > Program blocks > Add new block. In the pop-up
window, select "Function block" and language SCL. Give it the name ``Flasher``.
Click "OK".
Note that Structured Control Language (SCL) is Siemens name for Structured Text
(ST) programming language.
In the "Flasher [FB1]" window upper part, add an input pin in the "Input"
section. Name it ``in_pin_button_LED``, and give it the data type Bool. In the
"Output" section, add a ``out_pin_LED`` which also should be Bool.
In the "Static" section, add:
* ``in_pin_button_LED_previous``: BOOL
* ``flashing``: BOOL
* ``oscillator_state``: BOOL
* ``oscillator_cycles``: Int
.. highlight:: none
In the program part of the window, insert this::
#oscillator_cycles := #oscillator_cycles + 1;
IF #oscillator_cycles > 400 THEN
#oscillator_cycles := 0;
#oscillator_state := NOT #oscillator_state;
END_IF;
IF #in_pin_button_LED = TRUE THEN
IF #in_pin_button_LED_previous = FALSE THEN
#flashing := NOT #flashing;
END_IF;
#out_pin_LED := TRUE;
ELSIF #flashing = TRUE THEN
#out_pin_LED := #oscillator_state;
ELSE
#out_pin_LED := FALSE;
END_IF;
#in_pin_button_LED_previous := #in_pin_button_LED;
In the "Main [OB1]" block, drag the "Flasher" icon to the "Network 1" line.
The icon is found in the left-side menu "PLC_1" > "Program blocks".
In the pop up asking for data block, select "Flasher_DB".
Connect the input on the "Flasher" block by double-clicking it. Select "ButtonIn".
Similarly connect the output to "LEDout".
.. image:: illustrations/FlasherFunctionBlock.png
The block "Flasher_DB [DB1]" has been created automatically. All the input-,
output- and static variables should appear inside it.
Compile the program and download it to the PLC. Verify that it works as
intended.
Adjust data cycle time and watchdog
-----------------------------------
In the device view, select the IO-device in the dropdown. Use the "Properties"
tag and "General" subtab. Select "PROFINET interface [X1]" > Advanced options
> Real time settings. Adjust "Update time" in ms and the watchdog time (which
is the allowed number of cycles without data before an alarm is triggered).
Adjust parameter values for a slot
----------------------------------
Use the "Device view" for the IO-device while you are offline from the PLC.
In the "Device overview", select the line for the relevant slot.
In the "Properties" tab and the "General" subtab, select "Module parameters".
A list of parameters is seen. Set the value in the text box.
Forcing output signals
----------------------
In the Project tree, select PLC_1 > "Watch and force table" > "Force table".
In the first empty line, double-click on the small icon on the Name field.
Select "LEDout". In the "Force value" column enter `TRUE`, and enable
the checkbox in the "F" column.
Go online, and then click the small "Start or replace forcing ..." icon.
Confirm in the pop-up window. To stop forcing the signal, press the small
"Stops forcing of ..." icon. Note that it not possible to download software
to the PLC while it is forcing output signals.
Opening an archived project
----------------------------
Open an archived project by using the project view menu Project > Retrieve
and select the ``.zap15_1`` file. Create a new empty folder when asked for
target directory.
Setting date and time on PLC
----------------------------
To set the time zone, in the "Device view" select Properties > General >
"Time of day". Use appropriate time zone.
To set the date and time, go online to the PLC. In the Project tree on
the "PLC_1", right-click and select "Online & diagnostics". Use Functions >
"Set time".
Alarm when IO-device is terminated
----------------------------------
Approximately 7-8 ms after the last cyclic data frame is received from the
IO-device, the Simatic PLC will send an alarm frame about missing data (if
using default values).
The Wireshark tool will display::
Status: Error: "RTA error", "PNIO", "RTA_ERR_CLS_PROTOCOL", "AR consumer DHT/WDT expired (RTA_ERR_ABORT)"
Display alarms in the PLC
-------------------------
While online, right-click on the PLC and enable "Receive alarms". Alarms are
then visible in "Device view" > Diagnostics > "Alarm display".
Show connection errors to IO-device
-----------------------------------
If you are connected to the PLC ("online"), then it is possible to see if
there are communication problems to the IO-device. In the project tree > PLC_1
> Distributed I/O > Profinet IO-System > rt-labs-dev. In case of communication
errors, the hover text on the small icon is showing "Not reachable".
Display diagnosis in the PLC
----------------------------
To see diagnosis in standard format for an IO-device, use the "Device view" for
the IO-device while you are "Online" with the PLC.
Press the "Go online" in the menu bar.
In the "Device overview"
there is a table of slots and subslot. A red icon will be shown for the slot
with the diagnosis. Double-click the icon. In the new window select
"Diagnostics" > "Channel diagnostics". The error will be described and it is
possible to see for which channel it is reported.
To see a list of previous communication failures for the PLC, make sure you
are online with the PLC. In the Project tree on the "PLC_1", right-click and
select "Online & diagnostics".
Select Diagnostics -> "Diagnostics buffer" in the left part of the new window.
A list of previous problems is seen. Click on relevant row to see details.
There is also a "Diagnosis status" page that shows a summary of the current
status.
Scan for devices from TIA portal
--------------------------------
In the "Portal view", click "Online & Diagnostics" and then "Accessible devices".
In the pop-up window, select "PN/IE" and the name of the Ethernet interface
of your laptop. Click "Start search".
You can flash a LED on your device by marking the relevant line in the
result table, and mark the "Flash LED" check-box.
To change network settings for a device, mark the relevant line in the table
of found devices, and click "Show". The "Project tree" will open, and under
"Online access" in the left menu, select the relevant Ethernet interface.
If necessary click "Update accessible devices". Select relevant found device,
and use the small triangle to access its "Online & diagnostics" button.
Double-click it. A new window will open, and in its "Functions" sections it is
possible to assign IP address and to assign Profinet device name.
It is also possible to reach the tool to scan for devices via the main top menu
Online > "Accessible devices".
Troubleshooting
---------------
Note that the PLC might complain if there is a non-Profinet switch between the
IO-device and the IO-controller. This is probably due to wrong port names in LLDP messages.
Replace a device or a PLC
-------------------------
To replace an IO-device or an IO-controller, right-click on it in the left
side menu and select "Change device". Follow the wizard.
Using the Echo module
---------------------
The echo module will receive an integer and a float from the PLC, and multiply them with a constant
value before sending them back to the PLC. The multiplier is module parameter, and can be adjusted
at startup. The integer is an unsigned 32 bit integer, and the float is a single precision float
(32 bits).
To test it, unplug any existing modules, and plug one Echo module into slot 1.
Check the resulting addresses for the inputs and outputs of the module (by looking in the
"Device overview"). Typically the addresses are "0..7" for both the input (I) and output (Q) addresses.
Assuming these addresses, add these tags:
============== =========== =======
Name Data type Address
============== =========== =======
EchoFloatIn LReal %ID0
EchoIntIn UDInt %ID4
EchoFloatOut LReal %QD0
EchoIntOut UDInt %QD4
============== =========== =======
In a program block, define these values:
======== =========== ============
Section Name Data type
======== =========== ============
Input in_float Real
Input in_int UDInt
Output out_float Real
Output out_int UDInt
Temp temp_float Real
Temp temp_int UDInt
======== =========== ============
and enter this program::
#out_float := 1001.2345;
#out_int := 16;
#temp_float := #in_float;
#temp_int := #in_int;
It is not clear why the IO data signals not are shown in the user interface.
Add the program block to the "Main" program block. Connect "in_float" to "EchoFloatIn" etc.
Add "EchoFloatIn" and the three other signals to the watch table.
Add "EchoFloatOut" and "EchoIntOut" to the Force table. See description elsewhere on how to force the values.
Start the PLC, and go online. Study the values sent to and from the IO-device.
Reload an GSDML file
--------------------
When the GSDML file is updated and needs to be reloaded in the Siemens environment:
1. Delete all devices in your project that are based on the GSDML that shall be changed.
2. Check the "Force" table.
* Stop forcing all values.
* Delete entries in table.
3. Open "Options/Manage general station description files"
* In "Installed GSDs" tab, delete GSDML file
* in "GSDs in the project", "Find unused GSDs" and delete
4. Save project
5. Restart TIA and add your updated GSDML file and recreate your device.
If you you are still facing problems you can try repeat the described
sequence and also completely remove all tags.
Factory reset of Simatic ET200SP CPU
------------------------------------
Use the mode switch on the front panel to do a factory reset. See the user
manual for details. This will reset also the IP address.
Connect the PLC to your laptop, and run Wireshark to figure out the IP address.
It is given inside the LLDP frame. Also the detailed model name, firmware
version etc are given in the LLDP frame.
Upgrade firmware on a Siemens PLC
---------------------------------
Select the proper firmware to use for your PLC from the Siemens downloads
page. With the latest firmware you need a recent version of the STEP7 software.
Download the file, which is in ``.zip`` format. Unzip the file.
In TIA Portal, right-click the PLC and select "Online and Diagnostics". In
the "Functions" sections, use "Firmware update". Browse to the downloaded file
(in ``.upd`` format) and start the update.
Using a Simatic ET200SP IO-device for conformance test
------------------------------------------------------
See the page on conformance testing in this documentation for hardware details.
This type of hardware is used when testing multiple-port IO-devices, to verify
that it is possible to communicate with other IO-devices via the ports of the
device-under-test.
You can also use this hardware for communication reference instead of the
p-net sample app. For that use case, configure the Simatic IO-device similarly
as described for the p-net sample application above. Select the proper device
from the hardware catalog in the STEP7 software.
In STEP7, add the digital output module (DQ) in slot 1 and the digital input
module (DI) in slot 2. The server module should be inserted into slot 3.
Step7 naming
------------
* DB - Data block for storage
* FB - Function block, that uses data blocks.
* FC - Function without any storage
* LGF - Library of general functions
* OB - Organisation block. A callback called by the PLC's operating system in different situations.
Data types in Step7
-------------------
A few of the available data types:
* Bool
* Int - 16 bit
* UInt - 16 bit unsigned
* DInt - 32 bit
* UDInt - 32 bit unsigned
* Word - 16 bits
* DWord - 32 bits
* Real - 32 bit floating point number
SCL programming basics
-----------------------
Line comments are written like this::
// Line comment
Hexadecimal literals start with ``16#``.
Assignment::
temporary_value := 0;
Running the sample application via a ladder logic PLC program
-------------------------------------------------------------
It is possible to program the PLC in the programming language "Ladder logic"
instead of in SCL.
In order to flash the LED we use an available clock bit. In the "Device view"
for the PLC, use the "Properties" tab and "General" sub-tab. Select "System
and clock memory", and enable the checkbox "Enable the use of clock memory
byte". Enter the value 100 in the "Address of clock memory byte" text box.
This results in the "Clock_1.25Hz" having the address ``%M100.4``.
For this change to take effect in the PLC, you need to compile the hardware
configuration and to download the hardware configuration to the PLC.
First create two internal (memory) tags via the left menu PLC_1 > "PLC tags" >
"Show all tags". Add a new tag "CounterValue" with data type "Int" and address
``%MW200``. Similarly a new tag "Flashing" with data type "Int" and address
``%MW201``.
.. image:: illustrations/PlcProgramLadderLogic.png
With your program by using the left side menu "PLC_1" > "Program blocks" >
"Main [OB1]". In order to make the button toggle the state between off and
flashing, we will use a counter and the modulo operator.
From the right-side menu "Instructions" > "Basic instructions" > "Bit logic
operations" drag the "Normally open contact" icon to the "Network 1" line.
Double-click the question marks on top of the icon, and select "Button In".
Drag a "CTU" counter from Instructions" > "Basic instructions" > "Counter
operations". Accept the data block name in the pop-up window. Double-click the
question marks on the PV input and enter ``0``. Double-click the value connected
to the CV output, and select "CounterValue".
The last item on this network line is a modulo operator. You find it in
"Instructions" > "Basic instructions" > "Math functions" > "MOD".
For "IN1" use "CounterValue", and for "IN2" use ``2``. Connect the output to
"Flashing".
Insert a new network by using the small "Insert network" icon in the top of the
"Main [OB1]" window. Add a "Normally open contact" which you use with
"Clock_1.25Hz", and then add a "Instructions" > "Basic instructions" >
"Comparator operations" > "CMP>". For the top row of question marks, use
"Flashing" and use ``0`` for the bottom row of question marks. Finally add a
"Instructions" > "Basic instructions" > "Bit logic operations" > "Assignment"
and connect it to "LEDout".
Compile and download the program to your PLC. Button1 on the sample app will
turn on and off the flashing of the LED1.
Reading parameter data from the sample app using ladder logic
-------------------------------------------------------------
Reading parameter data from the IO-device is done with the RDREC command.
It is a asynchronous PLC command, meaning that the command is started in
one PLC execution cycle and the result is available in some later PLC execution
cycle.
Find the ID of the relevant "DIO 8xLogicLevel" by using the left side menu
"PLC_1" > "PLC tags" > "Show all tags" and use the "System constants" tab.
The value for "rt-labs-dev~DIO 8xLogicLevel" can be for example 264.
Create a data block using the left side menu "PLC_1" > "Program blocks" >
"Add new block". Use a data block of type "Global DB" and name it "data".
In the block create these a tag "param_value" of type "UDInt.
In the "Main [OB1]" block, insert a RDREC block, and keep the default name. The
block is found in the right-side menu "Instructions" > "Extended instructions"
> "Distributed I/O".
The value at the REQ input should be ``true`` and MLEN (number of bytes to read)
should be ``4``. The INDEX input should be ``123`` as given in the sample app
GSDML file. For the ID input, use the value you did find out above.
Connect the outputs RECORD to ``"data".param_value``.
To study the result, compile and download the program to the PLC. Go online,
and enable monitoring by clicking the small glasses-icon. The parameter value
will be seen in the ladder logic diagram. The PLC will read out the parameter
value many times per second. The VALID and BUSY outputs are switching on
and off rapidly.
.. image:: illustrations/rdrecLadderlogic.png
Reading parameter data from the sample app using the SCL language
-----------------------------------------------------------------
Create a new function block "RecReader" with language SCL.
In the "Static" section, add these tags:
* valid - Bool
* busy - Bool
* error - Bool
* status - DWord
* len - UInt
In the code part::
"RDREC_DB_1"(REQ := TRUE,
ID := 264,
INDEX := 123,
MLEN := 4,
VALID => #valid,
BUSY => #busy,
ERROR => #error,
STATUS => #status,
LEN => #len,
RECORD := "data".param_value);
In the "Main [OB1]" window, drag the "RecReader" block to the Network1 line.
Accept the name of the datablock.
The result is the same as above.
Using Siemens SinecPni (Primary Network Initialization)
-------------------------------------------------------
This is a tool for configuration of Profinet network equipment. Use it
to adjust the IP address of your PLC.
Download the program from the Siemens homepage, and unzip the file.
Start the program by double clicking the SinecPni executable.
To be able to change IP address on a Siemens PLC, make sure that the
"mode switch" on the front panel is in the "STOP" position.
Click the “Settings” tab, and enable “PROFINET devices” for “Scan Protocol”, and
click “Save”. On the “Device list” tab, click “Start network scan”.
Select the “S7-1500” line, and click “Configure Device”. Adjust the IP address
to ``192.168.0.100`` and netmask to ``255.255.255.0``.
Click “Load” to store the settings.
Setting up a Simatic HMI
------------------------
This example uses a "HMI KTP400 Basic", which is a 4 inch touch screen.
The sample application LED state will be shown on the screen. A
touch-screen button will start and stop the LED blinking on the IO-device (and
on the screen).
Make sure you have added your PLC to the project first.
Add a new tag to your PLC. In the left menu go to "PLC_1" > "PLC tags" >
"Show all tags". On an empty line add the name "ButtonHmiIn", and give it the
address ``%M1.1`` (which indicates that it is in-memory only).
In the "Portal view" select "Devices and networks" and "Add new device".
Click "HMI" and select the correct model in the tree view. Click "Add".
Follow the device wizard. When you reach the "Screens" setting use only the
default "Root" screen. For "System screens" enable these:
* Project information
* PLC system diagnostics
* System information
For "Buttons" select to use button area to the left.
Click "Finish".
In the "Device view" select the "HMI_1", and click on the image of the HMI.
In the "Properties" tab and "General" sub-tab select "PROFINET Interface [X1]" >
"Ethernet addresses". Set the IP address to ``192.168.0.51`` and subnet mask to
``255.255.255.0``.
In the left menu select "HMI_1" > "Screens" and double-click "Root screen".
The screen editor will be seen. Drag the "Welcome to HMI_1 ..." text box to
make place for other elements. From the Toolbox menu on the right side of the
screen drag a "Button" icon, found in the "Elements" section.
Double-click the button to modify its text to "Press Me!".
Select the button icon in the graphical editor, and select the "Properties" tab
and "Events" sub-tab. Select "Press" and on the "<Add function>" select
"System functions" > "Edit bits" > "Set bit". In the resulting line press the
icon with three dots, and select "PLC_1" > "PLC tags" > "Default tag table" >
"ButtonHmiIn".
Similarly for the "Release" use "Reset bit" for "ButtonHmiIn".
To modify the button color when pressed, use the "Animations" sub-tab. Use
"Display" and double-click "Add new animation". Select "Appearance", and in the
Tag field select "PLC_1" > "PLC tags" > "Default tag table" > "ButtonHmiIn".
The line "0" is already available, so add a line for the value "1". Modify the
background color to something different.
Add a LED to the screen by dragging a circle from the right side menu "Toolbox" >
"Basic objects". Select the circle and use the "Properties" "Animations" sub-tab.
Use the method described above to set the background color to black when the PLC
tag "LEDout" has the value 0, and red when it has the value 1.
Update the sample app PLC program to also take the button on the HMI screen
into account.
Compile the software for the HMI screen by right-clicking it in the "Device view",
and select Compile > "Hardware (rebuild all)" and then Compile > "Software
(rebuild all)". Then right-click on it and select "Download to device" >
"Software (all)". In the new window select "PN/IE" and your Ethernet interface.
Click "Start search" and select the device when it appears in the list. Click
"load".
Also compile and download hardware settings and software to the PLC.
Run the sample application. The state of the physical LED connected to your
Raspberry Pi will be reflected on the LED (drawn circle) on the HMI. Use
the touch screen button on the HMI to start and stop flashing of the LED.

View File

@ -0,0 +1,178 @@
.. _using-codesys:
Using Codesys soft PLC
======================
We run the Codesys ("COntroller DEvelopment SYStem") runtime on a Raspberry Pi,
and the setup is done using a Windows-based software (Codesys Development
System). Your Windows laptop is used to make changes to the PLC (IO-controller)
settings, but it does not act as an IO-controller.
Download and installation of Codesys Development System on a Windows PC
-----------------------------------------------------------------------
The program can be downloaded from https://store.codesys.com/codesys.html
A trial version is available. Registration is required.
Download “CODESYS Development System V3", in the latest available version.
The file is named :file:`CODESYS 64 <VERSION>.exe`. Install it on a Windows
machine by double clicking the icon.
From within Codesys, install the "Codesys control for Raspberry Pi"
by using the menu Tools/"Codesys installer". In the "AddOns" section
use "Browse" and search for "raspberry". Select the relevant row in the
results, and click "Install". When completed, there should be an entry
"Update Raspberry Pi" available in the Tools menu.
Restart the program after the installation.
Scan network to find your PLC Raspberry Pi, and install the Codesys runtime onto it
-----------------------------------------------------------------------------------
Make sure your Windows machine and the Raspberry Pi are connected to the
same local network.
In Codesys on Windows, use the menu Tools -> "Update Raspberry Pi".
Click "Scan" to find the IP address.
In this tutorial the IP address of the PLC Raspberry Pi is ``192.168.0.100``.
It is possible to install the runtime on the PLC Raspberry Pi as long as you
know its IP address, even if it doesn't show up during the scan.
Click "Install" for the Codesys Runtime package. Use "Standard" runtime
in the pop-up window.
The runtime will be installed in the :file:`/opt/codesys` directory on the Raspberry Pi.
Create a project in Codesys
---------------------------
On the Windows machine, first create a suitable project area on your hard
drive. For example Documents/Codesys/Democontroller.
From the Codesys menu, create a new project. Use the recently created
directory, and "Standard Project". Name it "Democontroller".
Select device "Codesys Control for Raspberry Pi SL", and select to program in
"Structured Text (ST)"
It is important that you select the same version of the runtime (single core =
"SL" or multicore = "SL MC") both in the runtime and in the project, otherwise the
controller will not be found when you try to use it.
Verify that the "Device" in the left hand menu shows as "Codesys Control for
Raspberry Pi SL". Double-click the "Device". Click "Scan network" tab,
and select the Raspberry Pi. The marker on the image should turn green. Use
tab "Device" and "Send Echo Service" to verify the communication.
In the Codesys menu "Tools", select "Device Repository". Click "Install" and
select the GSDML file from your hard drive.
For the sample application the GSDML file is available in the
:file:`samples/pn_dev` folder in the repository that you have cloned, or
from https://github.com/rtlabs-com/p-net/tree/master/samples/pn_dev
* On the “Device (CODESY Control for Raspberry Pi SL)” in the left hand panel,
right-click and select Add Device. Use “Ethernet adapter”, “Ethernet”.
* On the "Ethernet", right-click and select Add Device.
Use "Profinet IO master", "PN-Controller".
* On the "PN_Controller", right-click and select Add Device. Use "P-Net Sample App".
* On the "P_Net_Sample_App", right-click and select Add Device. Use "DIO 8xLogicLevel".
Double-click the "Ethernet" node in the left menu. Select interface "eth0".
The IP address will be updated accordingly.
Double-click the "PN_controller" node in the left menu. Adjust the IP range
using "First IP" and "Last IP" to both have the existing IP-address of your
IO-device (for example a Linux laptop or embedded Linux board running the
sample_app). For this tutorial we use the “First IP” ``192.168.0.50``
and also the “Last IP” ``192.168.0.50``.
Double-click the "P_Net_Sample_App" node in the left menu. Set the
IP-address to the existing address of your IO-device.
In this tutorial we use ``192.168.0.50``.
Structured Text programming language for PLCs
---------------------------------------------
Structured Text (ST) is a text based programming language for PLCs.
Read about it on https://en.wikipedia.org/wiki/Structured_text
A tutorial is found here: https://www.plcacademy.com/structured-text-tutorial/
Create controller application
-----------------------------
Enter program in "PLC_PRG".
Variables section::
PROGRAM PLC_PRG
VAR
in_pin_button_LED: BOOL;
out_pin_LED: BOOL;
in_pin_button_LED_previous: BOOL;
flashing: BOOL := TRUE;
oscillator_state: BOOL := FALSE;
oscillator_cycles: UINT := 0;
END_VAR
Program section::
oscillator_cycles := oscillator_cycles + 1;
IF oscillator_cycles > 200 THEN
oscillator_cycles := 0;
oscillator_state := NOT oscillator_state;
END_IF
IF in_pin_button_LED = TRUE THEN
IF in_pin_button_LED_previous = FALSE THEN
flashing := NOT flashing;
END_IF
out_pin_LED := TRUE;
ELSIF flashing = TRUE THEN
out_pin_LED := oscillator_state;
ELSE
out_pin_LED := FALSE;
END_IF
in_pin_button_LED_previous := in_pin_button_LED;
On the "DIO_8xLogicLevel" node in the left-side menu,
right-click and select "Edit IO mapping".
Open the "Input 8 bits" row by clicking the small ``+`` sign.
Double-click the icon on the row that you would like the edit.
Map "Input Bit 7" to "in_pin_button_LED" (found via Application/PLC_PRG),
and "Output Bit 7" to "out_pin_LED".
In the "Application -> MainTask" select "Cyclic" with 4 ms.
In the "Application -> Profinet_CommunicationTask" select "Cyclic" with 10 ms.
Use priority 14.
Transfer controller application to (controller) Raspberry Pi
------------------------------------------------------------
* In the top menu, use Build -> "Generate Code".
* Transfer the application to the Raspberry Pi by using the top menu
Online -> Login. Press "Yes" in the pop-up window.
* In the top menu, use Debug -> Start
You can follow the controller log by using the top menu Tools -> "Update
Raspberry Pi". Click the "System info" button, and look in the "Runtime Info"
text box. It will show an error message if it can't find the IO-device on
the network.
Use Wireshark to verify that the controller sends LLDP packets every 5 seconds.
Every 15 seconds it will send an ARP packet to ask for the (first?) IO-device
IP address, and a PN-DCP packet to ask for the IO-device with the name
"rt-labs-dev".
Running the application
-----------------------
Now it is time to run the application, so head back to the :ref:`tutorial` page.
Once the Codesys softplc running on the Raspberry Pi has been configured,
you can turn off the personal computer (running the Codesys desktop application)
used to configure it.
Remember that you need to power cycle the Raspberry Pi running the softplc every
two hours, if using the trial version.

View File

@ -0,0 +1,43 @@
Writing documentation
=====================
Documentation can be written in Markdown or reStructuredText (reST).
Use restructured text when more advanced featured are required, for example
inserting a table of contents.
Add new documentation files to ``doc/index.rst``.
Markdown
--------
Use ``.md`` file name extension.
Parsing is done with the Commonmark specification,
see https://commonmark.org/
Restructured text
-----------------
Use ``.rst`` file name extension.
For details on the syntax, see
https://www.sphinx-doc.org/en/master/usage/restructuredtext/index.html
Installing documentation toolchain
----------------------------------
Install Sphinx by running this in the ``doc`` subdirectory::
doc$ sudo apt install python3-pip
doc$ pip3 install -r doc_requirements.txt
Build documentation (in the ``doc`` subdirectory)::
doc$ cmake --build ../build --target docs
doc$ make html
doc$ make pdf
Run checks::
doc$ make spelling
doc$ make linkcheck
$ codespell -I doc/codespell_ignore.txt -S "*.patch" include/ samples/pn_dev/ src/ test/

View File

@ -0,0 +1,21 @@
Build p-net for Linux using Yocto or Buildroot
==============================================
Yocto
-----
A Yocto layer for building p-net on embedded Linux is available
on https://github.com/rtlabs-com/meta-rtlabs. Usage details are given there.
Add ``p-net-demo`` to your image to include the Profinet IO-device sample
application.
Buildroot
---------
It is possible to use Buildroot for including p-net in your embedded Linux
product.
A Buildroot recipe for p-net is available as one of the packages in
https://github.com/rtlabs-com/br2-rtlabs See the ``README.md`` file for usage
instructions. You might need to adjust ``P_NET_VERSION`` in the ``p-net.mk``
file to have the latest version.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,246 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* www.rt-labs.com
* Copyright 2018 rt-labs AB, Sweden.
*
* This software is dual-licensed under GPLv3 and a commercial
* license. See the file LICENSE.md distributed with this software for
* full license information.
********************************************************************/
#ifndef OPTIONS_H
#define OPTIONS_H
/**
* # Profinet Stack Options
*
* The defines named `PNET_OPTION_*` may be used to extend or reduce
* the functionality of the stack. Setting these values to 0 excludes
* the specific function and may also reduce the memory usage of the
* Profinet stack.
*
* Note that none of these options are currently supported (even if
* enabled by setting the value to 1), except for parsing the RPC
* Connect request message and generating the connect RPC Connect
* response message.
*/
#if !defined (PNET_OPTION_FAST_STARTUP)
#cmakedefine01 PNET_OPTION_FAST_STARTUP
#endif
#if !defined (PNET_OPTION_PARAMETER_SERVER)
#cmakedefine01 PNET_OPTION_PARAMETER_SERVER
#endif
#if !defined (PNET_OPTION_IR)
#cmakedefine01 PNET_OPTION_IR
#endif
#if !defined (PNET_OPTION_SR)
#cmakedefine01 PNET_OPTION_SR
#endif
#if !defined (PNET_OPTION_REDUNDANCY)
#cmakedefine01 PNET_OPTION_REDUNDANCY
#endif
#if !defined (PNET_OPTION_AR_VENDOR_BLOCKS)
#cmakedefine01 PNET_OPTION_AR_VENDOR_BLOCKS
#endif
#if !defined (PNET_OPTION_RS)
#cmakedefine01 PNET_OPTION_RS
#endif
#if !defined (PNET_OPTION_MC_CR)
#cmakedefine01 PNET_OPTION_MC_CR
#endif
#if !defined (PNET_OPTION_SRL)
#cmakedefine01 PNET_OPTION_SRL
#endif
#if !defined (PNET_OPTION_SNMP)
#cmakedefine01 PNET_OPTION_SNMP
#endif
/**
* Disable use of atomic operations (stdatomic.h).
* If the compiler supports it then set this define to 1.
*/
/* TODO: compiler abstraction should be handled by cc.h */
#if !defined (PNET_USE_ATOMICS)
#cmakedefine01 PNET_USE_ATOMICS
#endif
/**
* # Memory Usage
*
* Memory usage is controlled by the `PNET_MAX_*` defines. Define the
* required number of supported items.
*
* These values directly affect the memory usage of the
* implementation. Sometimes in complicated ways.
*
* Please note that some defines have minimum requirements.
* Only a few of the defines are validated.
*/
#if !defined (PNET_MAX_AR)
/** Number of connections. Must be > 0. "Automated RT Tester" uses 2 */
#define PNET_MAX_AR @PNET_MAX_AR@
#endif
#if !defined (PNET_MAX_API)
/** Number of Application Processes. Must be > 0. */
#define PNET_MAX_API @PNET_MAX_API@
#endif
#if !defined (PNET_MAX_CR)
/** Per AR. 1 input and 1 output. */
#define PNET_MAX_CR @PNET_MAX_CR@
#endif
#if !defined (PNET_MAX_SLOTS)
/** Per API. Should be > 1 to allow at least one I/O module. */
#define PNET_MAX_SLOTS @PNET_MAX_SLOTS@
#endif
#if !defined (PNET_MAX_SUBSLOTS)
/** Per slot (3 needed for DAP). */
#define PNET_MAX_SUBSLOTS @PNET_MAX_SUBSLOTS@
#endif
#if !defined (PNET_MAX_DFP_IOCR)
/** Allowed values are 0 (zero) or 2. */
#define PNET_MAX_DFP_IOCR @PNET_MAX_DFP_IOCR@
#endif
#if !defined (PNET_MAX_LOG_BOOK_ENTRIES)
#define PNET_MAX_LOG_BOOK_ENTRIES @PNET_MAX_LOG_BOOK_ENTRIES@
#endif
#if !defined (PNET_MAX_ALARMS)
/** Per AR and queue. One queue for hi and one for lo alarms. */
#define PNET_MAX_ALARMS @PNET_MAX_ALARMS@
#endif
#if !defined (PNET_MAX_ALARM_PAYLOAD_DATA_SIZE)
/** Max size of alarm payload */
#define PNET_MAX_ALARM_PAYLOAD_DATA_SIZE @PNET_MAX_ALARM_PAYLOAD_DATA_SIZE@
#endif
#if !defined (PNET_MAX_DIAG_ITEMS)
/** Total, per device. Max is 65534 items. */
#define PNET_MAX_DIAG_ITEMS @PNET_MAX_DIAG_ITEMS@
#endif
#if !defined (PNET_MAX_DIAG_MANUF_DATA_SIZE)
/** Max size of manufacturer specific diagnosis data */
#define PNET_MAX_DIAG_MANUF_DATA_SIZE @PNET_MAX_DIAG_MANUF_DATA_SIZE@
#endif
#if PNET_OPTION_MC_CR
#if !defined (PNET_MAX_MC_CR)
/**< Per AR. */
#define PNET_MAX_MC_CR @PNET_MAX_MC_CR@
#endif
#endif /* PNET_OPTION_MC_CR */
#if PNET_OPTION_AR_VENDOR_BLOCKS
#if !defined (PNET_MAX_AR_VENDOR_BLOCKS)
/**< Must be > 0 */
#define PNET_MAX_AR_VENDOR_BLOCKS @PNET_MAX_AR_VENDOR_BLOCKS@
#endif
#if !defined (PNET_MAX_AR_VENDOR_BLOCK_DATA_LENGTH)
#define PNET_MAX_AR_VENDOR_BLOCK_DATA_LENGTH @PNET_MAX_AR_VENDOR_BLOCK_DATA_LENGTH@
#endif
#endif /* PNET_OPTION_AR_VENDOR_BLOCKS */
#if !defined (PNET_MAX_MAN_SPECIFIC_FAST_STARTUP_DATA_LENGTH)
/** or 512 (bytes) */
#define PNET_MAX_MAN_SPECIFIC_FAST_STARTUP_DATA_LENGTH @PNET_MAX_MAN_SPECIFIC_FAST_STARTUP_DATA_LENGTH@
#endif
#if !defined (PNET_MAX_SESSION_BUFFER_SIZE)
/** Max fragmented RPC request/response length */
#define PNET_MAX_SESSION_BUFFER_SIZE @PNET_MAX_SESSION_BUFFER_SIZE@
#endif
#if !defined (PNET_MAX_FILENAME_SIZE)
/** Max filename size, including termination */
#define PNET_MAX_FILENAME_SIZE @PNET_MAX_FILENAME_SIZE@
#endif
#if !defined (PNET_MAX_PORT_DESCRIPTION_SIZE)
/** Max port description size, including termination */
#define PNET_MAX_PORT_DESCRIPTION_SIZE @PNET_MAX_PORT_DESCRIPTION_SIZE@
#endif
/**
* # Logging
*
* The following options controlling logging.
*/
#ifndef LOG_LEVEL
#define LOG_LEVEL (LOG_LEVEL_@LOG_LEVEL@)
#endif
#ifndef PF_ETH_LOG
#define PF_ETH_LOG (LOG_STATE_@PF_ETH_LOG@)
#endif
#ifndef PF_LLDP_LOG
#define PF_LLDP_LOG (LOG_STATE_@PF_LLDP_LOG@)
#endif
#ifndef PF_SNMP_LOG
#define PF_SNMP_LOG (LOG_STATE_@PF_SNMP_LOG@)
#endif
#ifndef PF_CPM_LOG
#define PF_CPM_LOG (LOG_STATE_@PF_CPM_LOG@)
#endif
#ifndef PF_PPM_LOG
#define PF_PPM_LOG (LOG_STATE_@PF_PPM_LOG@)
#endif
#ifndef PF_DCP_LOG
#define PF_DCP_LOG (LOG_STATE_@PF_DCP_LOG@)
#endif
#ifndef PF_RPC_LOG
#define PF_RPC_LOG (LOG_STATE_@PF_RPC_LOG@)
#endif
#ifndef PF_ALARM_LOG
#define PF_ALARM_LOG (LOG_STATE_@PF_ALARM_LOG@)
#endif
#ifndef PF_AL_BUF_LOG
#define PF_AL_BUF_LOG (LOG_STATE_@PF_AL_BUF_LOG@)
#endif
#ifndef PF_PNAL_LOG
#define PF_PNAL_LOG (LOG_STATE_@PF_PNAL_LOG@)
#endif
#ifndef PNET_LOG
#define PNET_LOG (LOG_STATE_@PNET_LOG@)
#endif
#endif /* OPTIONS_H */

View File

@ -0,0 +1,33 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* www.rt-labs.com
* Copyright 2018 rt-labs AB, Sweden.
*
* This software is dual-licensed under GPLv3 and a commercial
* license. See the file LICENSE.md distributed with this software for
* full license information.
********************************************************************/
#ifndef PNET_OPTIONS_H
#define PNET_OPTIONS_H
#if !defined (PNET_MAX_PHYSICAL_PORTS)
/** Max number of physical ports */
#define PNET_MAX_PHYSICAL_PORTS @PNET_MAX_PHYSICAL_PORTS@
#endif
#if !defined (PNET_MAX_DIRECTORYPATH_SIZE)
/** Max directory path size, including termination */
#define PNET_MAX_DIRECTORYPATH_SIZE @PNET_MAX_DIRECTORYPATH_SIZE@
#endif
#if !defined (PNET_OPTION_DRIVER_ENABLE)
#cmakedefine01 PNET_OPTION_DRIVER_ENABLE
#endif
#endif /* PNET_OPTIONS_H */

View File

@ -0,0 +1,41 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* www.rt-labs.com
* Copyright 2018 rt-labs AB, Sweden.
*
* This software is dual-licensed under GPLv3 and a commercial
* license. See the file LICENSE.md distributed with this software for
* full license information.
********************************************************************/
#ifndef PNET_VERSION_H
#define PNET_VERSION_H
#cmakedefine PROFINET_GIT_REVISION "@PROFINET_GIT_REVISION@"
#if !defined(PNET_VERSION_BUILD) && defined(PROFINET_GIT_REVISION)
#define PNET_VERSION_BUILD PROFINET_GIT_REVISION
#endif
/* clang-format-off */
#define PNET_VERSION_MAJOR @PROFINET_VERSION_MAJOR@
#define PNET_VERSION_MINOR @PROFINET_VERSION_MINOR@
#define PNET_VERSION_PATCH @PROFINET_VERSION_PATCH@
#if defined(PNET_VERSION_BUILD)
#define PNET_VERSION \
"@PROFINET_VERSION_MAJOR@.@PROFINET_VERSION_MINOR@.@PROFINET_VERSION_PATCH@+" PNET_VERSION_BUILD
#else
#define PNET_VERSION \
"@PROFINET_VERSION_MAJOR@.@PROFINET_VERSION_MINOR@.@PROFINET_VERSION_PATCH@"
#endif
/* clang-format-on */
#endif /* PNET_VERSION_H */

View File

@ -0,0 +1,21 @@
#********************************************************************
# _ _ _
# _ __ | |_ _ | | __ _ | |__ ___
# | '__|| __|(_)| | / _` || '_ \ / __|
# | | | |_ _ | || (_| || |_) |\__ \
# |_| \__|(_)|_| \__,_||_.__/ |___/
#
# http://www.rt-labs.com
# Copyright 2017 rt-labs AB, Sweden.
# See LICENSE file in the project root for full license information.
#*******************************************************************/
target_include_directories(pn_dev
PRIVATE
${PROFINET_SOURCE_DIR}/src
${PROFINET_BINARY_DIR}/src
)
target_link_libraries (pn_dev PUBLIC profinet)
install (TARGETS pn_dev DESTINATION bin)

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

@ -0,0 +1,301 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<ISO15745Profile xmlns="http://www.profibus.com/GSDML/2003/11/DeviceProfile" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.profibus.com/GSDML/2003/11/DeviceProfile ..\xsd\GSDML-DeviceProfile-V2.4.xsd">
<!-- ProfileHeader definition as defined in ISO 15745-1. Please do not change the content. -->
<ProfileHeader>
<ProfileIdentification>PROFINET Device Profile</ProfileIdentification>
<ProfileRevision>1.00</ProfileRevision>
<ProfileName>Device Profile for PROFINET Devices</ProfileName>
<ProfileSource>PROFIBUS Nutzerorganisation e. V. (PNO)</ProfileSource>
<ProfileClassID>Device</ProfileClassID>
<ISO15745Reference>
<ISO15745Part>4</ISO15745Part>
<ISO15745Edition>1</ISO15745Edition>
<ProfileTechnology>GSDML</ProfileTechnology>
</ISO15745Reference>
</ProfileHeader>
<ProfileBody>
<DeviceIdentity VendorID="0x0493" DeviceID="0x0002">
<InfoText TextId="IDT_INFO_Device"/>
<VendorName Value="RT-Labs"/>
</DeviceIdentity>
<DeviceFunction>
<Family MainFamily="I/O" ProductFamily="P-Net Samples"/>
</DeviceFunction>
<ApplicationProcess>
<DeviceAccessPointList>
<DeviceAccessPointItem ID="IDD_1" PNIO_Version="V2.4" PhysicalSlots="0..4" ModuleIdentNumber="0x00000001" MinDeviceInterval="32" DNS_CompatibleName="rt-labs-dev" FixedInSlots="0" ObjectUUID_LocalIndex="1" DeviceAccessSupported="false" MultipleWriteSupported="true" CheckDeviceID_Allowed="true" NameOfStationNotTransferable="false" LLDP_NoD_Supported="true" ResetToFactoryModes="1..2">
<ModuleInfo>
<Name TextId="IDT_MODULE_NAME_DAP1"/>
<InfoText TextId="IDT_INFO_DAP1"/>
<VendorName Value="RT-Labs"/>
<OrderNumber Value="123456 Abcdefghijkl"/>
<HardwareRelease Value="A1.0"/>
<SoftwareRelease Value="V0.1.0"/>
</ModuleInfo>
<CertificationInfo ConformanceClass="B" ApplicationClass="" NetloadClass="I"/>
<IOConfigData MaxInputLength="244" MaxOutputLength="244"/>
<UseableModules>
<ModuleItemRef ModuleItemTarget="IDM_30" AllowedInSlots="1..4"/>
<ModuleItemRef ModuleItemTarget="IDM_31" AllowedInSlots="1..4"/>
<ModuleItemRef ModuleItemTarget="IDM_32" AllowedInSlots="1..4"/>
<ModuleItemRef ModuleItemTarget="IDM_40" AllowedInSlots="1..4"/>
</UseableModules>
<VirtualSubmoduleList>
<VirtualSubmoduleItem ID="IDS_1" SubmoduleIdentNumber="0x00000001" Writeable_IM_Records="1 2 3" MayIssueProcessAlarm="false">
<IOData/>
<ModuleInfo>
<Name TextId="IDT_MODULE_NAME_DAP1"/>
<InfoText TextId="IDT_INFO_DAP1"/>
</ModuleInfo>
</VirtualSubmoduleItem>
</VirtualSubmoduleList>
<SystemDefinedSubmoduleList>
<InterfaceSubmoduleItem ID="IDS_I" SubmoduleIdentNumber="0x00008000" SubslotNumber="32768" TextId="IDT_NAME_IS" SupportedRT_Classes="RT_CLASS_1" SupportedProtocols="SNMP;LLDP" NetworkComponentDiagnosisSupported="false" PTP_BoundarySupported="true" DCP_BoundarySupported="true">
<ApplicationRelations StartupMode="Advanced">
<TimingProperties SendClock="32" ReductionRatio="1 2 4 8 16 32 64 128 256 512"/>
</ApplicationRelations>
</InterfaceSubmoduleItem>
<PortSubmoduleItem ID="IDS_P1" SubmoduleIdentNumber="0x00008001" SubslotNumber="32769" TextId="IDT_NAME_PS1" MaxPortRxDelay="350" MaxPortTxDelay="160">
<MAUTypeList>
<!--
MAUTypeItems shall match the actual network interfaces of the device.
Current list works for Raspberry Pi, Linksys usb/ethernet dongle and xmc sample targets
-->
<MAUTypeItem Value="30"/>
<MAUTypeItem Value="16"/>
<MAUTypeItem Value="5"/>
</MAUTypeList>
</PortSubmoduleItem>
<!--
Enable to support additional port. (PNET_MAX_PHYSICAL_PORTS == 2)
Add additional PortSubmoduleItems to support additional ports
-->
<!--
<PortSubmoduleItem ID="IDS_P2" SubmoduleIdentNumber="0x00008002" SubslotNumber="32770" TextId="IDT_NAME_PS2" MaxPortRxDelay="350" MaxPortTxDelay="160">
<MAUTypeList>
<MAUTypeItem Value="30"/>
<MAUTypeItem Value="16"/>
<MAUTypeItem Value="5"/>
</MAUTypeList>
</PortSubmoduleItem>
-->
</SystemDefinedSubmoduleList>
<Graphics>
<GraphicItemRef Type="DeviceSymbol" GraphicItemTarget="RT-LabsStackImage"/>
</Graphics>
</DeviceAccessPointItem>
</DeviceAccessPointList>
<ModuleList>
<ModuleItem ID="IDM_30" ModuleIdentNumber="0x00000030">
<ModuleInfo>
<Name TextId="TOK_Name_Module_I8"/>
<InfoText TextId="TOK_InfoText_Module_I8"/>
<HardwareRelease Value="1.0"/>
<SoftwareRelease Value="1.0"/>
</ModuleInfo>
<VirtualSubmoduleList>
<VirtualSubmoduleItem ID="IDSM_130" SubmoduleIdentNumber="0x0130" MayIssueProcessAlarm="true">
<IOData>
<Input Consistency="All items consistency">
<DataItem DataType="Unsigned8" TextId="TOK_Input_DataItem_8" UseAsBits="true">
<BitDataItem BitOffset="0" TextId="TOK_Input_DataItem_Bit0"/>
<BitDataItem BitOffset="1" TextId="TOK_Input_DataItem_Bit1"/>
<BitDataItem BitOffset="2" TextId="TOK_Input_DataItem_Bit2"/>
<BitDataItem BitOffset="3" TextId="TOK_Input_DataItem_Bit3"/>
<BitDataItem BitOffset="4" TextId="TOK_Input_DataItem_Bit4"/>
<BitDataItem BitOffset="5" TextId="TOK_Input_DataItem_Bit5"/>
<BitDataItem BitOffset="6" TextId="TOK_Input_DataItem_Bit6"/>
<BitDataItem BitOffset="7" TextId="TOK_Input_DataItem_Bit7"/>
</DataItem>
</Input>
</IOData>
<ModuleInfo>
<Name TextId="TOK_Name_Module_I8"/>
<InfoText TextId="TOK_InfoText_Module_I8"/>
</ModuleInfo>
</VirtualSubmoduleItem>
</VirtualSubmoduleList>
</ModuleItem>
<ModuleItem ID="IDM_31" ModuleIdentNumber="0x00000031">
<ModuleInfo>
<Name TextId="TOK_Name_Module_O8"/>
<InfoText TextId="TOK_InfoText_Module_O8"/>
<HardwareRelease Value="1.0"/>
<SoftwareRelease Value="1.0"/>
</ModuleInfo>
<VirtualSubmoduleList>
<VirtualSubmoduleItem ID="IDSM_131" SubmoduleIdentNumber="0x0131" MayIssueProcessAlarm="true">
<IOData>
<Output Consistency="All items consistency">
<DataItem DataType="Unsigned8" TextId="TOK_Output_DataItem_8" UseAsBits="true">
<BitDataItem BitOffset="0" TextId="TOK_Output_DataItem_Bit0"/>
<BitDataItem BitOffset="1" TextId="TOK_Output_DataItem_Bit1"/>
<BitDataItem BitOffset="2" TextId="TOK_Output_DataItem_Bit2"/>
<BitDataItem BitOffset="3" TextId="TOK_Output_DataItem_Bit3"/>
<BitDataItem BitOffset="4" TextId="TOK_Output_DataItem_Bit4"/>
<BitDataItem BitOffset="5" TextId="TOK_Output_DataItem_Bit5"/>
<BitDataItem BitOffset="6" TextId="TOK_Output_DataItem_Bit6"/>
<BitDataItem BitOffset="7" TextId="TOK_Output_DataItem_Bit7"/>
</DataItem>
</Output>
</IOData>
<ModuleInfo>
<Name TextId="TOK_Name_Module_O8"/>
<InfoText TextId="TOK_InfoText_Module_O8"/>
</ModuleInfo>
</VirtualSubmoduleItem>
</VirtualSubmoduleList>
</ModuleItem>
<ModuleItem ID="IDM_32" ModuleIdentNumber="0x00000032">
<ModuleInfo>
<Name TextId="TOK_Name_Module_I8O8"/>
<InfoText TextId="TOK_InfoText_Module_I8O8"/>
<HardwareRelease Value="1.0"/>
<SoftwareRelease Value="1.0"/>
</ModuleInfo>
<VirtualSubmoduleList>
<VirtualSubmoduleItem ID="IDSM_132" SubmoduleIdentNumber="0x0132" MayIssueProcessAlarm="true">
<IOData>
<Input>
<DataItem DataType="Unsigned8" UseAsBits="true" TextId="TOK_Input_DataItem_8">
<BitDataItem BitOffset="0" TextId="TOK_Input_DataItem_Bit0"/>
<BitDataItem BitOffset="1" TextId="TOK_Input_DataItem_Bit1"/>
<BitDataItem BitOffset="2" TextId="TOK_Input_DataItem_Bit2"/>
<BitDataItem BitOffset="3" TextId="TOK_Input_DataItem_Bit3"/>
<BitDataItem BitOffset="4" TextId="TOK_Input_DataItem_Bit4"/>
<BitDataItem BitOffset="5" TextId="TOK_Input_DataItem_Bit5"/>
<BitDataItem BitOffset="6" TextId="TOK_Input_DataItem_Bit6"/>
<BitDataItem BitOffset="7" TextId="TOK_Input_DataItem_Bit7"/>
</DataItem>
</Input>
<Output Consistency="All items consistency">
<DataItem DataType="Unsigned8" TextId="TOK_Output_DataItem_8" UseAsBits="true">
<BitDataItem BitOffset="0" TextId="TOK_Output_DataItem_Bit0"/>
<BitDataItem BitOffset="1" TextId="TOK_Output_DataItem_Bit1"/>
<BitDataItem BitOffset="2" TextId="TOK_Output_DataItem_Bit2"/>
<BitDataItem BitOffset="3" TextId="TOK_Output_DataItem_Bit3"/>
<BitDataItem BitOffset="4" TextId="TOK_Output_DataItem_Bit4"/>
<BitDataItem BitOffset="5" TextId="TOK_Output_DataItem_Bit5"/>
<BitDataItem BitOffset="6" TextId="TOK_Output_DataItem_Bit6"/>
<BitDataItem BitOffset="7" TextId="TOK_Output_DataItem_Bit7"/>
</DataItem>
</Output>
</IOData>
<RecordDataList>
<ParameterRecordDataItem Index="123" Length="4">
<Name TextId="TOK_sample_parameter_1"/>
<Ref DataType="Unsigned32" ByteOffset="0" DefaultValue="1" AllowedValues="0..99" Changeable="true" Visible="true" TextId="TOK_Demo_1"/>
</ParameterRecordDataItem>
<ParameterRecordDataItem Index="124" Length="4">
<Name TextId="TOK_sample_parameter_2"/>
<Ref DataType="Unsigned32" ByteOffset="0" DefaultValue="2" AllowedValues="0..999" Changeable="true" Visible="true" TextId="TOK_Demo_2"/>
</ParameterRecordDataItem>
</RecordDataList>
<ModuleInfo>
<Name TextId="TOK_Name_Module_I8O8"/>
<InfoText TextId="TOK_InfoText_Module_I8O8"/>
</ModuleInfo>
</VirtualSubmoduleItem>
</VirtualSubmoduleList>
</ModuleItem>
<ModuleItem ID="IDM_40" ModuleIdentNumber="0x00000040">
<ModuleInfo>
<Name TextId="TOK_Name_Module_Echo"/>
<InfoText TextId="TOK_InfoText_Module_Echo"/>
<HardwareRelease Value="1.0"/>
<SoftwareRelease Value="1.0"/>
</ModuleInfo>
<VirtualSubmoduleList>
<VirtualSubmoduleItem ID="IDSM_140" SubmoduleIdentNumber="0x0140" MayIssueProcessAlarm="true">
<IOData>
<Input Consistency="All items consistency">
<DataItem DataType="Float32" TextId="TOK_Input_DataItem_Echo_Float32"/>
<DataItem DataType="Unsigned32" TextId="TOK_Input_DataItem_Echo_Unsigned32"/>
</Input>
<Output Consistency="All items consistency">
<DataItem DataType="Float32" TextId="TOK_Output_DataItem_Echo_Float32"/>
<DataItem DataType="Unsigned32" TextId="TOK_Output_DataItem_Echo_Unsigned32"/>
</Output>
</IOData>
<RecordDataList>
<ParameterRecordDataItem Index="125" Length="4">
<Name TextId="TOK_sample_parameter_Echo"/>
<Ref DataType="Unsigned32" ByteOffset="0" DefaultValue="2" AllowedValues="1..4" Changeable="true" Visible="true" TextId="TOK_Echo_Gain"/>
</ParameterRecordDataItem>
</RecordDataList>
<ModuleInfo>
<Name TextId="TOK_Name_Module_Echo"/>
<InfoText TextId="TOK_InfoText_Module_Echo"/>
</ModuleInfo>
</VirtualSubmoduleItem>
</VirtualSubmoduleList>
</ModuleItem>
</ModuleList>
<LogBookEntryList>
<LogBookEntryItem Status="2130510">
<!--Custom log book entry for sample application-->
<!--Error code 0x20 Error decode 0x82 Error code 1 0x4E-->
<ErrorCode2Value>
<Name TextId="IDT_CUSTOM_LOGBOOK_1"/>
</ErrorCode2Value>
</LogBookEntryItem>
</LogBookEntryList>
<GraphicsList>
<GraphicItem ID="RT-LabsStackImage" GraphicFile="GSDML-RT-LABS-STACK"/>
</GraphicsList>
<ExternalTextList>
<PrimaryLanguage>
<Text TextId="IDT_INFO_Device" Value="https://github.com/rtlabs-com/p-net"/>
<Text TextId="IDT_MODULE_NAME_DAP1" Value="P-Net multi-module sample app"/>
<Text TextId="IDT_INFO_DAP1" Value="Profinet device sample app https://github.com/rtlabs-com/p-net"/>
<Text TextId="IDT_CUSTOM_DIAG_1" Value="Custom diagnosis in USI format"/>
<Text TextId="IDT_CUSTOM_DIAG_1_VALUE" Value="Custom diagnosis value"/>
<Text TextId="IDT_CUSTOM_LOGBOOK_1" Value="Custom Logbook entry"/>
<Text TextId="IDT_NAME_IS" Value="X1"/>
<Text TextId="IDT_NAME_PS1" Value="X1 P1"/>
<Text TextId="IDT_NAME_PS2" Value="X1 P2"/>
<!--module name-->
<Text TextId="TOK_Name_Module_I8" Value="DI 8xLogicLevel"/>
<Text TextId="TOK_Name_Module_O8" Value="DO 8xLogicLevel"/>
<Text TextId="TOK_Name_Module_I8O8" Value="DIO 8xLogicLevel"/>
<Text TextId="TOK_Name_Module_Echo" Value="Echo Module"/>
<!--module info -->
<Text TextId="TOK_InfoText_Module_I8" Value="Digital In 8xLogicLevel"/>
<Text TextId="TOK_InfoText_Module_O8" Value="Digital Out 8xLogicLevel"/>
<Text TextId="TOK_InfoText_Module_I8O8" Value="Digital In+Out 8xLogicLevel"/>
<Text TextId="TOK_InfoText_Module_Echo" Value="Echo with adjustable gain"/>
<!--dataitem name-->
<Text TextId="TOK_Input_DataItem_8" Value="Input 8 bits"/>
<Text TextId="TOK_Output_DataItem_8" Value="Output 8 bits"/>
<Text TextId="TOK_Input_DataItem_Bit0" Value="Input Bit 0"/>
<Text TextId="TOK_Input_DataItem_Bit1" Value="Input Bit 1"/>
<Text TextId="TOK_Input_DataItem_Bit2" Value="Input Bit 2"/>
<Text TextId="TOK_Input_DataItem_Bit3" Value="Input Bit 3"/>
<Text TextId="TOK_Input_DataItem_Bit4" Value="Input Bit 4"/>
<Text TextId="TOK_Input_DataItem_Bit5" Value="Input Bit 5"/>
<Text TextId="TOK_Input_DataItem_Bit6" Value="Input Bit 6"/>
<Text TextId="TOK_Input_DataItem_Bit7" Value="Input Bit 7"/>
<Text TextId="TOK_Output_DataItem_Bit0" Value="Output Bit 0"/>
<Text TextId="TOK_Output_DataItem_Bit1" Value="Output Bit 1"/>
<Text TextId="TOK_Output_DataItem_Bit2" Value="Output Bit 2"/>
<Text TextId="TOK_Output_DataItem_Bit3" Value="Output Bit 3"/>
<Text TextId="TOK_Output_DataItem_Bit4" Value="Output Bit 4"/>
<Text TextId="TOK_Output_DataItem_Bit5" Value="Output Bit 5"/>
<Text TextId="TOK_Output_DataItem_Bit6" Value="Output Bit 6"/>
<Text TextId="TOK_Output_DataItem_Bit7" Value="Output Bit 7"/>
<Text TextId="TOK_Input_DataItem_Echo_Float32" Value="Input float to controller (output from controller multiplied by gain)"/>
<Text TextId="TOK_Input_DataItem_Echo_Unsigned32" Value="Input int to controller (output from controller multiplied by gain)"/>
<Text TextId="TOK_Output_DataItem_Echo_Float32" Value="Output float from controller"/>
<Text TextId="TOK_Output_DataItem_Echo_Unsigned32" Value="Output int from controller"/>
<!--ParameterRecordDataItem name-->
<Text TextId="TOK_sample_parameter_1" Value="Parameter 1"/>
<Text TextId="TOK_sample_parameter_2" Value="Parameter 2"/>
<Text TextId="TOK_sample_parameter_Echo" Value="Gain for echo module"/>
<Text TextId="TOK_Demo_1" Value="Demo 1"/>
<Text TextId="TOK_Demo_2" Value="Demo 2"/>
<Text TextId="TOK_Echo_Gain" Value="Gain"/>
</PrimaryLanguage>
</ExternalTextList>
</ApplicationProcess>
</ProfileBody>
</ISO15745Profile>

View File

@ -0,0 +1,315 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* www.rt-labs.com
* Copyright 2021 rt-labs AB, Sweden.
*
* This software is dual-licensed under GPLv3 and a commercial
* license. See the file LICENSE.md distributed with this software for
* full license information.
********************************************************************/
#include "app_data.h"
#include "app_utils.h"
#include "app_gsdml.h"
#include "app_log.h"
#include "sampleapp_common.h"
#include "osal.h"
#include "pnal.h"
#include <pnet_api.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define APP_DATA_DEFAULT_OUTPUT_DATA 0
/* Parameter data for digital submodules
* The stored value is shared between all digital submodules in this example.
*
* Todo: Data is always in pnio data format. Add conversion to uint32_t.
*/
static uint32_t app_param_1 = 0; /* Network endianness */
static uint32_t app_param_2 = 0; /* Network endianness */
/* Parameter data for echo submodules
* The stored value is shared between all echo submodules in this example.
*
* Todo: Data is always in pnio data format. Add conversion to uint32_t.
*/
static uint32_t app_param_echo_gain = 1; /* Network endianness */
/* Digital submodule process data
* The stored value is shared between all digital submodules in this example. */
static uint8_t inputdata[APP_GSDML_INPUT_DATA_DIGITAL_SIZE] = {0};
static uint8_t outputdata[APP_GSDML_OUTPUT_DATA_DIGITAL_SIZE] = {0};
static uint8_t counter = 0;
/* Network endianness */
static uint8_t echo_inputdata[APP_GSDML_INPUT_DATA_ECHO_SIZE] = {0};
static uint8_t echo_outputdata[APP_GSDML_OUTPUT_DATA_ECHO_SIZE] = {0};
CC_PACKED_BEGIN
typedef struct CC_PACKED app_echo_data
{
/* Network endianness.
Used as a float, but we model it as a 4-byte integer to easily
do endianness conversion */
uint32_t echo_float_bytes;
/* Network endianness */
uint32_t echo_int;
} app_echo_data_t;
CC_PACKED_END
CC_STATIC_ASSERT (sizeof (app_echo_data_t) == APP_GSDML_INPUT_DATA_ECHO_SIZE);
CC_STATIC_ASSERT (sizeof (app_echo_data_t) == APP_GSDML_OUTPUT_DATA_ECHO_SIZE);
/**
* Set LED state.
*
* Compares new state with previous state, to minimize system calls.
*
* Uses the hardware specific app_set_led() function.
*
* @param led_state In: New LED state
*/
static void app_handle_data_led_state (bool led_state)
{
static bool previous_led_state = false;
if (led_state != previous_led_state)
{
app_set_led (APP_DATA_LED_ID, led_state);
}
previous_led_state = led_state;
}
uint8_t * app_data_get_input_data (
uint16_t slot_nbr,
uint16_t subslot_nbr,
uint32_t submodule_id,
bool button_pressed,
uint16_t * size,
uint8_t * iops)
{
float inputfloat;
float outputfloat;
uint32_t hostorder_inputfloat_bytes;
uint32_t hostorder_outputfloat_bytes;
app_echo_data_t * p_echo_inputdata = (app_echo_data_t *)&echo_inputdata;
app_echo_data_t * p_echo_outputdata = (app_echo_data_t *)&echo_outputdata;
if (size == NULL || iops == NULL)
{
return NULL;
}
if (
submodule_id == APP_GSDML_SUBMOD_ID_DIGITAL_IN ||
submodule_id == APP_GSDML_SUBMOD_ID_DIGITAL_IN_OUT)
{
/* Prepare digital input data
* Lowest 7 bits: Counter Most significant bit: Button
*/
inputdata[0] = counter++;
if (button_pressed)
{
inputdata[0] |= 0x80;
}
else
{
inputdata[0] &= 0x7F;
}
*size = APP_GSDML_INPUT_DATA_DIGITAL_SIZE;
*iops = PNET_IOXS_GOOD;
return inputdata;
}
if (submodule_id == APP_GSDML_SUBMOD_ID_ECHO)
{
/* Calculate echodata input (to the PLC)
* by multiplying the output (from the PLC) with a gain factor
*/
/* Integer */
p_echo_inputdata->echo_int = CC_TO_BE32 (
CC_FROM_BE32 (p_echo_outputdata->echo_int) *
CC_FROM_BE32 (app_param_echo_gain));
/* Float */
/* Use memcopy to avoid strict-aliasing rule warnings */
hostorder_outputfloat_bytes =
CC_FROM_BE32 (p_echo_outputdata->echo_float_bytes);
memcpy (&outputfloat, &hostorder_outputfloat_bytes, sizeof (outputfloat));
inputfloat = outputfloat * CC_FROM_BE32 (app_param_echo_gain);
memcpy (&hostorder_inputfloat_bytes, &inputfloat, sizeof (outputfloat));
p_echo_inputdata->echo_float_bytes =
CC_TO_BE32 (hostorder_inputfloat_bytes);
*size = APP_GSDML_INPUT_DATA_ECHO_SIZE;
*iops = PNET_IOXS_GOOD;
return echo_inputdata;
}
/* Automated RT Tester scenario 2 - unsupported (sub)module */
return NULL;
}
int app_data_set_output_data (
uint16_t slot_nbr,
uint16_t subslot_nbr,
uint32_t submodule_id,
uint8_t * data,
uint16_t size)
{
bool led_state;
if (data == NULL)
{
return -1;
}
if (
submodule_id == APP_GSDML_SUBMOD_ID_DIGITAL_OUT ||
submodule_id == APP_GSDML_SUBMOD_ID_DIGITAL_IN_OUT)
{
if (size == APP_GSDML_OUTPUT_DATA_DIGITAL_SIZE)
{
memcpy (outputdata, data, size);
/* Most significant bit: LED */
led_state = (outputdata[0] & 0x80) > 0;
app_handle_data_led_state (led_state);
return 0;
}
}
else if (submodule_id == APP_GSDML_SUBMOD_ID_ECHO)
{
if (size == APP_GSDML_OUTPUT_DATA_ECHO_SIZE)
{
memcpy (echo_outputdata, data, size);
return 0;
}
}
return -1;
}
int app_data_set_default_outputs (void)
{
outputdata[0] = APP_DATA_DEFAULT_OUTPUT_DATA;
app_handle_data_led_state (false);
return 0;
}
int app_data_write_parameter (
uint16_t slot_nbr,
uint16_t subslot_nbr,
uint32_t submodule_id,
uint32_t index,
const uint8_t * data,
uint16_t length)
{
const app_gsdml_param_t * par_cfg;
par_cfg = app_gsdml_get_parameter_cfg (submodule_id, index);
if (par_cfg == NULL)
{
APP_LOG_WARNING (
"PLC write request unsupported submodule/parameter. "
"Submodule id: %u Index: %u\n",
(unsigned)submodule_id,
(unsigned)index);
return -1;
}
if (length != par_cfg->length)
{
APP_LOG_WARNING (
"PLC write request unsupported length. "
"Index: %u Length: %u Expected length: %u\n",
(unsigned)index,
(unsigned)length,
par_cfg->length);
return -1;
}
if (index == APP_GSDML_PARAMETER_1_IDX)
{
memcpy (&app_param_1, data, length);
}
else if (index == APP_GSDML_PARAMETER_2_IDX)
{
memcpy (&app_param_2, data, length);
}
else if (index == APP_GSDML_PARAMETER_ECHO_IDX)
{
memcpy (&app_param_echo_gain, data, length);
}
APP_LOG_DEBUG (" Writing parameter \"%s\"\n", par_cfg->name);
app_log_print_bytes (APP_LOG_LEVEL_DEBUG, data, length);
return 0;
}
int app_data_read_parameter (
uint16_t slot_nbr,
uint16_t subslot_nbr,
uint32_t submodule_id,
uint32_t index,
uint8_t ** data,
uint16_t * length)
{
const app_gsdml_param_t * par_cfg;
par_cfg = app_gsdml_get_parameter_cfg (submodule_id, index);
if (par_cfg == NULL)
{
APP_LOG_WARNING (
"PLC read request unsupported submodule/parameter. "
"Submodule id: %u Index: %u\n",
(unsigned)submodule_id,
(unsigned)index);
return -1;
}
if (*length < par_cfg->length)
{
APP_LOG_WARNING (
"PLC read request unsupported length. "
"Index: %u Length: %u Expected length: %u\n",
(unsigned)index,
(unsigned)*length,
par_cfg->length);
return -1;
}
APP_LOG_DEBUG (" Reading \"%s\"\n", par_cfg->name);
if (index == APP_GSDML_PARAMETER_1_IDX)
{
*data = (uint8_t *)&app_param_1;
*length = sizeof (app_param_1);
}
else if (index == APP_GSDML_PARAMETER_2_IDX)
{
*data = (uint8_t *)&app_param_2;
*length = sizeof (app_param_2);
}
else if (index == APP_GSDML_PARAMETER_ECHO_IDX)
{
*data = (uint8_t *)&app_param_echo_gain;
*length = sizeof (app_param_echo_gain);
}
app_log_print_bytes (APP_LOG_LEVEL_DEBUG, *data, *length);
return 0;
}

View File

@ -0,0 +1,140 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* www.rt-labs.com
* Copyright 2018 rt-labs AB, Sweden.
*
* This software is dual-licensed under GPLv3 and a commercial
* license. See the file LICENSE.md distributed with this software for
* full license information.
********************************************************************/
#ifndef APP_DATA_H
#define APP_DATA_H
/**
* @file
* @brief Sample application data interface
*
* Functions for:
* - Getting input data (Button 1 and counter value)
* - Setting output data (LED 1)
* - Setting default output state. This should be
* part of all device implementations for setting
* defined state when device is not connected to PLC
* - Reading and writing parameters
*/
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdbool.h>
/**
* Get application specific PNIO input data (for sending to PLC)
*
* The main sample application keeps track
* of button so it is a parameter to this function.
*
* This function is not called for the DAP submodules (slot_nbr==0).
*
* @param slot_nbr In: Slot number
* @param subslot_nbr In: Subslot number
* @param submodule_id In: Submodule id
* @param button_state In: State of button 1
* @param size Out: Size of pnio data.
* Not modified on error.
* @param iops Out: Provider status. If for example
* a sensor is failing or a short
* circuit is detected on digital
* input this shall be set to BAD.
* Not modified on error.
* @return Reference to PNIO data, NULL on error
*/
uint8_t * app_data_get_input_data (
uint16_t slot_nbr,
uint16_t subslot_nbr,
uint32_t submodule_id,
bool button_state,
uint16_t * size,
uint8_t * iops);
/**
* Set application specific PNIO output data (received from PLC)
*
* This function is not called for the DAP submodules (slot_nbr==0).
*
* @param slot_nbr In: Slot number
* @param subslot_nbr In: Subslot number
* @param submodule_id In: Submodule id
* @param data In: Reference to output data
* @param size In: Length of output data
* @return 0 on success, -1 on error
*/
int app_data_set_output_data (
uint16_t slot_nbr,
uint16_t subslot_nbr,
uint32_t submodule_id,
uint8_t * data,
uint16_t size);
/**
* Set default outputs for all subslots.
*
* For the sample application this means that
* LED 1 is turned off.
*
* @return 0 on success, -1 on error
*/
int app_data_set_default_outputs (void);
/**
* Write parameter index for a subslot
*
* @param slot_nbr In: Slot number
* @param subslot_nbr In: Subslot number
* @param submodule_id In: Submodule id
* @param index In: Parameter index
* @param data In: New parameter value
* @param write_length In: Length of parameter data
* @return 0 on success, -1 on error
*/
int app_data_write_parameter (
uint16_t slot_nbr,
uint16_t subslot_nbr,
uint32_t submodule_id,
uint32_t index,
const uint8_t * data,
uint16_t write_length);
/**
* Read parameter index from a subslot
*
* @param slot_nbr In: Slot number
* @param subslot_nbr In: Subslot number
* @param submodule_id In: Submodule id
* @param index In: Parameter index
* @param data In: Reference to parameter data
* @param length InOut: The maximum (in) and actual (out) length in
* bytes of the data.
* @return 0 on success, -1 on error
*/
int app_data_read_parameter (
uint16_t slot_nbr,
uint16_t subslot_nbr,
uint32_t submodule_id,
uint32_t index,
uint8_t ** data,
uint16_t * length);
#ifdef __cplusplus
}
#endif
#endif /* APP_DATA_H */

View File

@ -0,0 +1,260 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* www.rt-labs.com
* Copyright 2021 rt-labs AB, Sweden.
*
* This software is dual-licensed under GPLv3 and a commercial
* license. See the file LICENSE.md distributed with this software for
* full license information.
********************************************************************/
#include "sampleapp_common.h"
#include "app_utils.h"
#include "app_gsdml.h"
#include "app_log.h"
#include "osal.h"
#include "pnal.h"
#include <pnet_api.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/******************* Supported modules ***************************/
static const app_gsdml_module_t dap_1 = {
.id = PNET_MOD_DAP_IDENT,
.name = "DAP 1",
.submodules = {
PNET_SUBMOD_DAP_IDENT,
PNET_SUBMOD_DAP_INTERFACE_1_PORT_1_IDENT,
PNET_SUBMOD_DAP_INTERFACE_1_PORT_2_IDENT,
PNET_SUBMOD_DAP_INTERFACE_1_PORT_3_IDENT,
PNET_SUBMOD_DAP_INTERFACE_1_PORT_4_IDENT,
0}};
static const app_gsdml_module_t module_digital_in = {
.id = APP_GSDML_MOD_ID_8_0_DIGITAL_IN,
.name = "DI 8xLogicLevel",
.submodules = {APP_GSDML_SUBMOD_ID_DIGITAL_IN, 0},
};
static const app_gsdml_module_t module_digital_out = {
.id = APP_GSDML_MOD_ID_0_8_DIGITAL_OUT,
.name = "DO 8xLogicLevel",
.submodules = {APP_GSDML_SUBMOD_ID_DIGITAL_OUT, 0}};
static const app_gsdml_module_t module_digital_in_out = {
.id = APP_GSDML_MOD_ID_8_8_DIGITAL_IN_OUT,
.name = "DIO 8xLogicLevel",
.submodules = {APP_GSDML_SUBMOD_ID_DIGITAL_IN_OUT, 0}};
static const app_gsdml_module_t module_echo = {
.id = APP_GSDML_MOD_ID_ECHO,
.name = "Echo module",
.submodules = {APP_GSDML_SUBMOD_ID_ECHO, 0}};
/******************* Supported submodules ************************/
static const app_gsdml_submodule_t dap_indentity_1 = {
.name = "DAP Identity 1",
.api = APP_GSDML_API,
.id = PNET_SUBMOD_DAP_IDENT,
.data_dir = PNET_DIR_NO_IO,
.insize = 0,
.outsize = 0,
.parameters = {0}};
static const app_gsdml_submodule_t dap_interface_1 = {
.name = "DAP Interface 1",
.api = APP_GSDML_API,
.id = PNET_SUBMOD_DAP_INTERFACE_1_IDENT,
.data_dir = PNET_DIR_NO_IO,
.insize = 0,
.outsize = 0,
.parameters = {0}};
static const app_gsdml_submodule_t dap_port_1 = {
.name = "DAP Port 1",
.api = APP_GSDML_API,
.id = PNET_SUBMOD_DAP_INTERFACE_1_PORT_1_IDENT,
.data_dir = PNET_DIR_NO_IO,
.insize = 0,
.outsize = 0,
.parameters = {0}};
static const app_gsdml_submodule_t dap_port_2 = {
.name = "DAP Port 2",
.api = APP_GSDML_API,
.id = PNET_SUBMOD_DAP_INTERFACE_1_PORT_2_IDENT,
.data_dir = PNET_DIR_NO_IO,
.insize = 0,
.outsize = 0,
.parameters = {0}};
static const app_gsdml_submodule_t dap_port_3 = {
.name = "DAP Port 3",
.api = APP_GSDML_API,
.id = PNET_SUBMOD_DAP_INTERFACE_1_PORT_3_IDENT,
.data_dir = PNET_DIR_NO_IO,
.insize = 0,
.outsize = 0,
.parameters = {0}};
static const app_gsdml_submodule_t dap_port_4 = {
.name = "DAP Port 4",
.api = APP_GSDML_API,
.id = PNET_SUBMOD_DAP_INTERFACE_1_PORT_4_IDENT,
.data_dir = PNET_DIR_NO_IO,
.insize = 0,
.outsize = 0,
.parameters = {0}};
static const app_gsdml_submodule_t submod_digital_in = {
.id = APP_GSDML_SUBMOD_ID_DIGITAL_IN,
.name = "Digital Input",
.api = APP_GSDML_API,
.data_dir = PNET_DIR_INPUT,
.insize = APP_GSDML_INPUT_DATA_DIGITAL_SIZE,
.outsize = 0,
.parameters = {0}};
static const app_gsdml_submodule_t submod_digital_out = {
.id = APP_GSDML_SUBMOD_ID_DIGITAL_OUT,
.name = "Digital Output",
.api = APP_GSDML_API,
.data_dir = PNET_DIR_OUTPUT,
.insize = 0,
.outsize = APP_GSDML_OUTPUT_DATA_DIGITAL_SIZE,
.parameters = {0}};
static const app_gsdml_submodule_t submod_digital_inout = {
.id = APP_GSDML_SUBMOD_ID_DIGITAL_IN_OUT,
.name = "Digital Input/Output",
.api = APP_GSDML_API,
.data_dir = PNET_DIR_IO,
.insize = APP_GSDML_INPUT_DATA_DIGITAL_SIZE,
.outsize = APP_GSDML_OUTPUT_DATA_DIGITAL_SIZE,
.parameters = {APP_GSDML_PARAMETER_1_IDX, APP_GSDML_PARAMETER_2_IDX, 0}};
static const app_gsdml_submodule_t submod_echo = {
.id = APP_GSDML_SUBMOD_ID_ECHO,
.name = "Echo submodule",
.api = APP_GSDML_API,
.data_dir = PNET_DIR_IO,
.insize = APP_GSDML_INPUT_DATA_ECHO_SIZE,
.outsize = APP_GSDML_OUTPUT_DATA_ECHO_SIZE,
.parameters = {APP_GSDML_PARAMETER_ECHO_IDX, 0}};
/** List of supported modules */
static const app_gsdml_module_t * app_gsdml_modules[] = {
&dap_1,
&module_digital_in,
&module_digital_out,
&module_digital_in_out,
&module_echo};
/** List of supported submodules */
static const app_gsdml_submodule_t * app_gsdml_submodules[] = {
&dap_indentity_1,
&dap_interface_1,
&dap_port_1,
&dap_port_2,
&dap_port_3,
&dap_port_4,
&submod_digital_in,
&submod_digital_out,
&submod_digital_inout,
&submod_echo,
};
/* List of supported parameters.
* Note that parameters are submodule attributes.
* This list contain all parameters while each
* submodule list its supported parameters using
* their indexes.
*/
static app_gsdml_param_t app_gsdml_parameters[] = {
{
.index = APP_GSDML_PARAMETER_1_IDX,
.name = "Demo 1",
.length = APP_GSDML_PARAMETER_LENGTH,
},
{
.index = APP_GSDML_PARAMETER_2_IDX,
.name = "Demo 2",
.length = APP_GSDML_PARAMETER_LENGTH,
},
{
.index = APP_GSDML_PARAMETER_ECHO_IDX,
.name = "Echo gain setting",
.length = APP_GSDML_PARAMETER_LENGTH,
}};
const app_gsdml_module_t * app_gsdml_get_module_cfg (uint32_t id)
{
uint32_t i;
for (i = 0; i < NELEMENTS (app_gsdml_modules); i++)
{
if (app_gsdml_modules[i]->id == id)
{
return app_gsdml_modules[i];
}
}
return NULL;
}
const app_gsdml_submodule_t * app_gsdml_get_submodule_cfg (uint32_t id)
{
uint32_t i;
for (i = 0; i < NELEMENTS (app_gsdml_submodules); i++)
{
if (app_gsdml_submodules[i]->id == id)
{
return app_gsdml_submodules[i];
}
}
return NULL;
}
const app_gsdml_param_t * app_gsdml_get_parameter_cfg (
uint32_t submodule_id,
uint32_t index)
{
uint16_t i;
uint16_t j;
const app_gsdml_submodule_t * submodule_cfg =
app_gsdml_get_submodule_cfg (submodule_id);
if (submodule_cfg == NULL)
{
/* Unsupported submodule id */
return NULL;
}
/* Search for parameter index in submodule configuration */
for (i = 0; submodule_cfg->parameters[i] != 0; i++)
{
if (submodule_cfg->parameters[i] == index)
{
/* Find parameter configuration */
for (j = 0; j < NELEMENTS (app_gsdml_parameters); j++)
{
if (app_gsdml_parameters[j].index == index)
{
return &app_gsdml_parameters[j];
}
}
}
}
return NULL;
}

View File

@ -0,0 +1,184 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* www.rt-labs.com
* Copyright 2018 rt-labs AB, Sweden.
*
* This software is dual-licensed under GPLv3 and a commercial
* license. See the file LICENSE.md distributed with this software for
* full license information.
********************************************************************/
#ifndef APP_GSDML_H
#define APP_GSDML_H
/**
* @file
* @brief Device properties defined by the GSDML device definition
*
* Functions for getting module, submodule and parameter
* configurations using their ids.
*
* Important:
* Any change in this file may require an update of the GSDML file.
* Note that when the GSDML file is updated it has to be reloaded
* in your Profinet engineering tool. PLC applications may be affected.
*
* Design requires unique submodule IDs and unique parameter indexes.
*/
#ifdef __cplusplus
extern "C" {
#endif
#include <pnet_api.h>
#define APP_GSDML_API 0
#define APP_GSDML_DEFAULT_STATION_NAME "rt-labs-dev"
/* GSDML tag: VendorID */
#define APP_GSDML_VENDOR_ID 0x0493
/* GSDML tag: DeviceID */
#define APP_GSDML_DEVICE_ID 0x0002
/* Used in DCP communication */
#define APP_GSDML_OEM_VENDOR_ID 0xcafe
#define APP_GSDML_OEM_DEVICE_ID 0xee02
/* Used in I&M0 */
#define APP_GSDML_IM_HARDWARE_REVISION 3
#define APP_GSDML_IM_VERSION_MAJOR 1
#define APP_GSDML_IM_VERSION_MINOR 2
/* Allowed: 'V', 'R', 'P', 'U', 'T' */
#define APP_GSDML_SW_REV_PREFIX 'V'
#define APP_GSDML_PROFILE_ID 0x1234
#define APP_GSDML_PROFILE_SPEC_TYPE 0x5678
#define APP_GSDML_IM_REVISION_COUNTER 0 /* Typically 0 */
/* Note: You need to read out the actual hardware serial number instead */
#define APP_GSDML_EXAMPLE_SERIAL_NUMBER "007"
/* Initial values. Can be overwritten by PLC */
#define APP_GSDML_TAG_FUNCTION "my function"
#define APP_GSDML_TAG_LOCATION "my location"
#define APP_GSDML_IM_DATE "2022-03-01 10:03"
#define APP_GSDML_DESCRIPTOR "my descriptor"
#define APP_GSDML_SIGNATURE ""
/* GSDML tag: Writeable_IM_Records */
#define APP_GSDML_IM_SUPPORTED \
(PNET_SUPPORTED_IM1 | PNET_SUPPORTED_IM2 | PNET_SUPPORTED_IM3)
/* GSDML tag: OrderNumber */
#define APP_GSDML_ORDER_ID "12345 Abcdefghijk"
/* GSDML tag: ModuleInfo / Name */
#define APP_GSDML_PRODUCT_NAME "P-Net Sample Application"
/* GSDML tag: MinDeviceInterval */
#define APP_GSDML_MIN_DEVICE_INTERVAL 32 /* 1 ms */
#define APP_GSDML_DIAG_CUSTOM_USI 0x1234
/* See "Specification for GSDML" 8.26 LogBookEntryItem for allowed values */
#define APP_GSDML_LOGBOOK_ERROR_CODE 0x20 /* Manufacturer specific */
#define APP_GSDML_LOGBOOK_ERROR_DECODE 0x82 /* Manufacturer specific */
#define APP_GSDML_LOGBOOK_ERROR_CODE_1 PNET_ERROR_CODE_1_FSPM
#define APP_GSDML_LOGBOOK_ERROR_CODE_2 0x00 /* Manufacturer specific */
#define APP_GSDML_LOGBOOK_ENTRY_DETAIL 0xFEE1DEAD /* Manufacturer specific */
#define APP_GSDML_PARAMETER_1_IDX 123
#define APP_GSDML_PARAMETER_2_IDX 124
#define APP_GSDML_PARAMETER_ECHO_IDX 125
/* Use same size for all parameters in example */
#define APP_GSDML_PARAMETER_LENGTH 4
#define APP_GSDML_DEFAULT_MAUTYPE 0x10 /* Copper 100 Mbit/s Full duplex */
typedef struct app_gsdml_module
{
uint32_t id;
/** Module name */
const char * name;
/** Submodule IDs. Variable length, ends with 0. */
uint32_t submodules[];
} app_gsdml_module_t;
typedef struct app_gsdml_submodule
{
uint32_t id;
/** Submodule name */
const char * name;
uint32_t api;
pnet_submodule_dir_t data_dir;
uint16_t insize;
uint16_t outsize;
/** Parameter indexes. See app_gsdml_parameters.
* Variable length, ends with 0. */
uint16_t parameters[];
} app_gsdml_submodule_t;
typedef struct
{
uint32_t index;
const char * name;
uint16_t length;
} app_gsdml_param_t;
#define APP_GSDML_MOD_ID_8_0_DIGITAL_IN 0x00000030
#define APP_GSDML_MOD_ID_0_8_DIGITAL_OUT 0x00000031
#define APP_GSDML_MOD_ID_8_8_DIGITAL_IN_OUT 0x00000032
#define APP_GSDML_MOD_ID_ECHO 0x00000040
#define APP_GSDML_SUBMOD_ID_DIGITAL_IN 0x00000130
#define APP_GSDML_SUBMOD_ID_DIGITAL_OUT 0x00000131
#define APP_GSDML_SUBMOD_ID_DIGITAL_IN_OUT 0x00000132
#define APP_GSDML_SUBMOD_ID_ECHO 0x00000140
#define APP_GSDML_INPUT_DATA_DIGITAL_SIZE 1 /* bytes */
#define APP_GSDML_OUTPUT_DATA_DIGITAL_SIZE 1 /* bytes */
#define APP_GSDML_INPUT_DATA_ECHO_SIZE 8 /* bytes */
#define APP_GSDML_OUTPUT_DATA_ECHO_SIZE APP_GSDML_INPUT_DATA_ECHO_SIZE
#define APP_GSDML_ALARM_PAYLOAD_SIZE 1 /* bytes */
/**
* Get module configuration from module ID
* @param module_id In: Module ID
* @return Module configuration, NULL if not found
*/
const app_gsdml_module_t * app_gsdml_get_module_cfg (uint32_t module_id);
/**
* Get submodule module configuration from submodule ID
* @param submodule_id In: Submodule ID
* @return Submodule configuration, NULL if not found
*/
const app_gsdml_submodule_t * app_gsdml_get_submodule_cfg (
uint32_t submodule_id);
/**
* Get parameter configuration from parameter index
* @param submodule_id In: Submodule ID
* @param index In: Parameters index
* @return Parameter configuration, NULL if not found
*/
const app_gsdml_param_t * app_gsdml_get_parameter_cfg (
uint32_t submodule_id,
uint32_t index);
#ifdef __cplusplus
}
#endif
#endif /* APP_GSDML_H */

View File

@ -0,0 +1,52 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* www.rt-labs.com
* Copyright 2021 rt-labs AB, Sweden.
*
* This software is dual-licensed under GPLv3 and a commercial
* license. See the file LICENSE.md distributed with this software for
* full license information.
********************************************************************/
#include "app_log.h"
#include <stdarg.h>
#include <stdio.h>
static int32_t log_level = APP_DEFAULT_LOG_LEVEL;
void app_log_set_log_level (int32_t level)
{
log_level = level;
}
void app_log (int32_t level, const char * fmt, ...)
{
va_list list;
if (level >= log_level)
{
va_start (list, fmt);
vprintf (fmt, list);
va_end (list);
fflush (stdout);
}
}
void app_log_print_bytes (int32_t level, const uint8_t * bytes, uint32_t len)
{
if (level >= log_level)
{
printf (" Bytes: ");
for (uint32_t i = 0; i < len; i++)
{
printf ("%02X ", bytes[i]);
}
printf ("\n");
}
}

View File

@ -0,0 +1,75 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* www.rt-labs.com
* Copyright 2018 rt-labs AB, Sweden.
*
* This software is dual-licensed under GPLv3 and a commercial
* license. See the file LICENSE.md distributed with this software for
* full license information.
********************************************************************/
#ifndef APP_LOG_H
#define APP_LOG_H
/**
* @file
* @brief Application debug log utility
*
* Runtime configurable debug log using printf()
* Levels matches levels used in P-Net.
*/
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#define APP_LOG_LEVEL_DEBUG 0x00
#define APP_LOG_LEVEL_INFO 0x01
#define APP_LOG_LEVEL_WARNING 0x02
#define APP_LOG_LEVEL_ERROR 0x03
#define APP_LOG_LEVEL_FATAL 0x04
#define APP_DEFAULT_LOG_LEVEL APP_LOG_LEVEL_FATAL
#define APP_LOG(level, ...) app_log (level, __VA_ARGS__)
#define APP_LOG_DEBUG(...) APP_LOG (APP_LOG_LEVEL_DEBUG, __VA_ARGS__)
#define APP_LOG_INFO(...) APP_LOG (APP_LOG_LEVEL_INFO, __VA_ARGS__)
#define APP_LOG_WARNING(...) APP_LOG (APP_LOG_LEVEL_WARNING, __VA_ARGS__)
#define APP_LOG_ERROR(...) APP_LOG (APP_LOG_LEVEL_ERROR, __VA_ARGS__)
#define APP_LOG_FATAL(...) APP_LOG (APP_LOG_LEVEL_FATAL, __VA_ARGS__)
/**
* Print log message depending on level
* Use the APP_LOG_xxxxx macros instead of this function.
* @param level In: Message log level
* @param fmt In: Log message format string
*/
void app_log (int32_t level, const char * fmt, ...);
/**
* Log an array of bytes
* @param level In: Log level
* @param bytes In: Array of bytes
* @param length In: Length of array
*/
void app_log_print_bytes (int32_t level, const uint8_t * bytes, uint32_t length);
/**
* Set log level
* @param level In: Log level
*/
void app_log_set_log_level (int32_t level);
#ifdef __cplusplus
}
#endif
#endif /* APP_LOG_H */

View File

@ -0,0 +1,741 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* www.rt-labs.com
* Copyright 2021 rt-labs AB, Sweden.
*
* This software is dual-licensed under GPLv3 and a commercial
* license. See the file LICENSE.md distributed with this software for
* full license information.
********************************************************************/
#define _GNU_SOURCE /* For asprintf() */
#include "app_utils.h"
#include "app_log.h"
#include "app_gsdml.h"
#include "sampleapp_common.h"
#include "osal.h"
#include "osal_log.h" /* For LOG_LEVEL */
#include "pnal.h"
#include <pnet_api.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define GET_HIGH_BYTE(id) ((id >> 8) & 0xFF)
#define GET_LOW_BYTE(id) (id & 0xFF)
void app_utils_ip_to_string (pnal_ipaddr_t ip, char * outputstring)
{
snprintf (
outputstring,
PNAL_INET_ADDRSTR_SIZE,
"%u.%u.%u.%u",
(uint8_t)((ip >> 24) & 0xFF),
(uint8_t)((ip >> 16) & 0xFF),
(uint8_t)((ip >> 8) & 0xFF),
(uint8_t)(ip & 0xFF));
}
void app_utils_mac_to_string (pnet_ethaddr_t mac, char * outputstring)
{
snprintf (
outputstring,
PNAL_ETH_ADDRSTR_SIZE,
"%02X:%02X:%02X:%02X:%02X:%02X",
mac.addr[0],
mac.addr[1],
mac.addr[2],
mac.addr[3],
mac.addr[4],
mac.addr[5]);
}
const char * app_utils_submod_dir_to_string (pnet_submodule_dir_t direction)
{
const char * s = "<error>";
switch (direction)
{
case PNET_DIR_NO_IO:
s = "NO_IO";
break;
case PNET_DIR_INPUT:
s = "INPUT";
break;
case PNET_DIR_OUTPUT:
s = "OUTPUT";
break;
case PNET_DIR_IO:
s = "INPUT_OUTPUT";
break;
}
return s;
}
const char * app_utils_ioxs_to_string (pnet_ioxs_values_t ioxs)
{
const char * s = "<error>";
switch (ioxs)
{
case PNET_IOXS_BAD:
s = "IOXS_BAD";
break;
case PNET_IOXS_GOOD:
s = "IOXS_GOOD";
break;
}
return s;
}
void app_utils_get_error_code_strings (
uint16_t err_cls,
uint16_t err_code,
const char ** err_cls_str,
const char ** err_code_str)
{
if (err_cls_str == NULL || err_cls_str == NULL)
{
return;
}
*err_cls_str = "Not decoded";
*err_code_str = "Not decoded";
switch (err_cls)
{
case PNET_ERROR_CODE_1_RTA_ERR_CLS_PROTOCOL:
*err_cls_str = "Real-Time Acyclic Protocol";
switch (err_code)
{
case PNET_ERROR_CODE_2_ABORT_AR_CONSUMER_DHT_EXPIRED:
*err_code_str = "Device missed cyclic data "
"deadline, device terminated AR";
break;
case PNET_ERROR_CODE_2_ABORT_AR_CMI_TIMEOUT:
*err_code_str = "Communication initialization "
"timeout, device terminated AR";
break;
case PNET_ERROR_CODE_2_ABORT_AR_RELEASE_IND_RECEIVED:
*err_code_str = "AR release indication received";
break;
case PNET_ERROR_CODE_2_ABORT_DCP_STATION_NAME_CHANGED:
*err_code_str = "DCP station name changed, "
"device terminated AR";
break;
case PNET_ERROR_CODE_2_ABORT_DCP_RESET_TO_FACTORY:
*err_code_str = "DCP reset to factory or factory "
"reset, device terminated AR";
break;
}
break;
case PNET_ERROR_CODE_1_CTLDINA:
*err_cls_str = "CTLDINA = Name and IP assignment from controller";
switch (err_code)
{
case PNET_ERROR_CODE_2_CTLDINA_ARP_MULTIPLE_IP_ADDRESSES:
*err_code_str = "Multiple users of same IP address";
break;
}
break;
}
}
void app_utils_copy_ip_to_struct (
pnet_cfg_ip_addr_t * destination_struct,
pnal_ipaddr_t ip)
{
destination_struct->a = ((ip >> 24) & 0xFF);
destination_struct->b = ((ip >> 16) & 0xFF);
destination_struct->c = ((ip >> 8) & 0xFF);
destination_struct->d = (ip & 0xFF);
}
const char * app_utils_dcontrol_cmd_to_string (
pnet_control_command_t control_command)
{
const char * s = NULL;
switch (control_command)
{
case PNET_CONTROL_COMMAND_PRM_BEGIN:
s = "PRM_BEGIN";
break;
case PNET_CONTROL_COMMAND_PRM_END:
s = "PRM_END";
break;
case PNET_CONTROL_COMMAND_APP_RDY:
s = "APP_RDY";
break;
case PNET_CONTROL_COMMAND_RELEASE:
s = "RELEASE";
break;
default:
s = "<error>";
break;
}
return s;
}
const char * app_utils_event_to_string (pnet_event_values_t event)
{
const char * s = "<error>";
switch (event)
{
case PNET_EVENT_ABORT:
s = "PNET_EVENT_ABORT";
break;
case PNET_EVENT_STARTUP:
s = "PNET_EVENT_STARTUP";
break;
case PNET_EVENT_PRMEND:
s = "PNET_EVENT_PRMEND";
break;
case PNET_EVENT_APPLRDY:
s = "PNET_EVENT_APPLRDY";
break;
case PNET_EVENT_DATA:
s = "PNET_EVENT_DATA";
break;
}
return s;
}
int app_utils_pnet_cfg_init_default (pnet_cfg_t * cfg)
{
memset (cfg, 0, sizeof (pnet_cfg_t));
cfg->tick_us = APP_TICK_INTERVAL_US;
/* Identification & Maintenance */
cfg->im_0_data.im_vendor_id_hi = GET_HIGH_BYTE (APP_GSDML_VENDOR_ID);
cfg->im_0_data.im_vendor_id_lo = GET_LOW_BYTE (APP_GSDML_VENDOR_ID);
cfg->im_0_data.im_hardware_revision = APP_GSDML_IM_HARDWARE_REVISION;
cfg->im_0_data.im_sw_revision_prefix = APP_GSDML_SW_REV_PREFIX;
cfg->im_0_data.im_sw_revision_functional_enhancement = PNET_VERSION_MAJOR;
cfg->im_0_data.im_sw_revision_bug_fix = PNET_VERSION_MINOR;
cfg->im_0_data.im_sw_revision_internal_change = PNET_VERSION_PATCH;
cfg->im_0_data.im_revision_counter = APP_GSDML_IM_REVISION_COUNTER;
cfg->im_0_data.im_profile_id = APP_GSDML_PROFILE_ID;
cfg->im_0_data.im_profile_specific_type = APP_GSDML_PROFILE_SPEC_TYPE;
cfg->im_0_data.im_version_major = 1; /** Always 1 */
cfg->im_0_data.im_version_minor = 1; /** Always 1 */
cfg->im_0_data.im_supported = APP_GSDML_IM_SUPPORTED;
snprintf (
cfg->im_0_data.im_order_id,
sizeof (cfg->im_0_data.im_order_id),
"%s",
APP_GSDML_ORDER_ID);
snprintf (
cfg->im_0_data.im_serial_number,
sizeof (cfg->im_0_data.im_serial_number),
"%s",
APP_GSDML_EXAMPLE_SERIAL_NUMBER);
snprintf (
cfg->im_1_data.im_tag_function,
sizeof (cfg->im_1_data.im_tag_function),
"%s",
APP_GSDML_TAG_FUNCTION);
snprintf (
cfg->im_1_data.im_tag_location,
sizeof (cfg->im_1_data.im_tag_location),
"%s",
APP_GSDML_TAG_LOCATION);
snprintf (
cfg->im_2_data.im_date,
sizeof (cfg->im_2_data.im_date),
"%s",
APP_GSDML_IM_DATE);
snprintf (
cfg->im_3_data.im_descriptor,
sizeof (cfg->im_3_data.im_descriptor),
"%s",
APP_GSDML_DESCRIPTOR);
snprintf (
cfg->im_4_data.im_signature,
sizeof (cfg->im_4_data.im_signature),
"%s",
APP_GSDML_SIGNATURE);
/* Device configuration */
cfg->device_id.vendor_id_hi = GET_HIGH_BYTE (APP_GSDML_VENDOR_ID);
cfg->device_id.vendor_id_lo = GET_LOW_BYTE (APP_GSDML_VENDOR_ID);
cfg->device_id.device_id_hi = GET_HIGH_BYTE (APP_GSDML_DEVICE_ID);
cfg->device_id.device_id_lo = GET_LOW_BYTE (APP_GSDML_DEVICE_ID);
cfg->oem_device_id.vendor_id_hi = GET_HIGH_BYTE (APP_GSDML_OEM_VENDOR_ID);
cfg->oem_device_id.vendor_id_lo = GET_LOW_BYTE (APP_GSDML_OEM_VENDOR_ID);
cfg->oem_device_id.device_id_hi = GET_HIGH_BYTE (APP_GSDML_OEM_DEVICE_ID);
cfg->oem_device_id.device_id_lo = GET_LOW_BYTE (APP_GSDML_OEM_DEVICE_ID);
snprintf (
cfg->product_name,
sizeof (cfg->product_name),
"%s",
APP_GSDML_PRODUCT_NAME);
cfg->send_hello = true;
/* Timing */
cfg->min_device_interval = APP_GSDML_MIN_DEVICE_INTERVAL;
/* Should be set by application as part of network configuration. */
cfg->num_physical_ports = 1;
snprintf (
cfg->station_name,
sizeof (cfg->station_name),
"%s",
APP_GSDML_DEFAULT_STATION_NAME);
/* Diagnosis mechanism */
/* We prefer using "Extended channel diagnosis" instead of
* "Qualified channel diagnosis" format on the wire,
* as this is better supported by Wireshark.
*/
cfg->use_qualified_diagnosis = false;
return 0;
}
int app_utils_get_netif_namelist (
const char * arg_str,
uint16_t max_port,
app_utils_netif_namelist_t * p_if_list,
uint16_t * p_num_ports)
{
int ret = 0;
uint16_t i = 0;
uint16_t j = 0;
uint16_t if_index = 0;
uint16_t number_of_given_names = 1;
uint16_t if_list_size = max_port + 1;
char c;
if (max_port == 0)
{
printf ("Error: max_port is 0.\n");
return -1;
}
memset (p_if_list, 0, sizeof (*p_if_list));
c = arg_str[i++];
while (c != '\0')
{
if (c != ',')
{
if (if_index < if_list_size)
{
p_if_list->netif[if_index].name[j++] = c;
}
}
else
{
if (if_index < if_list_size)
{
p_if_list->netif[if_index].name[j++] = '\0';
j = 0;
if_index++;
}
number_of_given_names++;
}
c = arg_str[i++];
}
if (max_port == 1 && number_of_given_names > 1)
{
printf ("Error: Only 1 network interface expected as max_port is 1.\n");
return -1;
}
if (number_of_given_names == 2)
{
printf ("Error: It is illegal to give 2 interface names. Use 1, or one "
"more than the number of physical interfaces.\n");
return -1;
}
if (number_of_given_names > max_port + 1)
{
printf (
"Error: You have given %u interface names, but max is %u as "
"PNET_MAX_PHYSICAL_PORTS is %u.\n",
number_of_given_names,
max_port + 1,
max_port);
return -1;
}
if (number_of_given_names == 1)
{
if (strlen (p_if_list->netif[0].name) == 0)
{
printf ("Error: Zero length network interface name.\n");
return -1;
}
else
{
p_if_list->netif[1] = p_if_list->netif[0];
*p_num_ports = 1;
}
}
else
{
for (i = 0; i < number_of_given_names; i++)
{
if (strlen (p_if_list->netif[i].name) == 0)
{
printf ("Error: Zero length network interface name (%d).\n", i);
return -1;
}
}
*p_num_ports = number_of_given_names - 1;
}
return ret;
}
int app_utils_pnet_cfg_init_netifs (
const char * netif_list_str,
app_utils_netif_namelist_t * if_list,
uint16_t * number_of_ports,
pnet_if_cfg_t * if_cfg)
{
int ret = 0;
int i = 0;
pnal_ipaddr_t ip;
pnal_ipaddr_t netmask;
pnal_ipaddr_t gateway;
ret = app_utils_get_netif_namelist (
netif_list_str,
PNET_MAX_PHYSICAL_PORTS,
if_list,
number_of_ports);
if (ret != 0)
{
return ret;
}
if_cfg->main_netif_name = if_list->netif[0].name;
for (i = 1; i <= *number_of_ports; i++)
{
if_cfg->physical_ports[i - 1].netif_name = if_list->netif[i].name;
if_cfg->physical_ports[i - 1].default_mau_type =
APP_GSDML_DEFAULT_MAUTYPE;
}
/* Read IP, netmask, gateway from operating system */
ip = pnal_get_ip_address (if_cfg->main_netif_name);
netmask = pnal_get_netmask (if_cfg->main_netif_name);
gateway = pnal_get_gateway (if_cfg->main_netif_name);
app_utils_copy_ip_to_struct (&if_cfg->ip_cfg.ip_addr, ip);
app_utils_copy_ip_to_struct (&if_cfg->ip_cfg.ip_gateway, gateway);
app_utils_copy_ip_to_struct (&if_cfg->ip_cfg.ip_mask, netmask);
return ret;
}
static void app_utils_print_mac_address (const char * netif_name)
{
pnal_ethaddr_t pnal_mac_addr;
if (pnal_get_macaddress (netif_name, &pnal_mac_addr) == 0)
{
APP_LOG_INFO (
"%02X:%02X:%02X:%02X:%02X:%02X\n",
pnal_mac_addr.addr[0],
pnal_mac_addr.addr[1],
pnal_mac_addr.addr[2],
pnal_mac_addr.addr[3],
pnal_mac_addr.addr[4],
pnal_mac_addr.addr[5]);
}
else
{
APP_LOG_ERROR ("Failed read mac address\n");
}
}
void app_utils_print_network_config (
pnet_if_cfg_t * if_cfg,
uint16_t number_of_ports)
{
uint16_t i;
char hostname_string[PNAL_HOSTNAME_MAX_SIZE]; /* Terminated string */
APP_LOG_INFO ("Management port: %s ", if_cfg->main_netif_name);
app_utils_print_mac_address (if_cfg->main_netif_name);
for (i = 1; i <= number_of_ports; i++)
{
APP_LOG_INFO (
"Physical port [%u]: %s ",
i,
if_cfg->physical_ports[i - 1].netif_name);
app_utils_print_mac_address (if_cfg->physical_ports[i - 1].netif_name);
}
if (pnal_get_hostname (hostname_string) != 0)
{
hostname_string[0] = '\0';
}
APP_LOG_INFO ("Hostname: %s\n", hostname_string);
APP_LOG_INFO (
"IP address: %u.%u.%u.%u\n",
if_cfg->ip_cfg.ip_addr.a,
if_cfg->ip_cfg.ip_addr.b,
if_cfg->ip_cfg.ip_addr.c,
if_cfg->ip_cfg.ip_addr.d);
APP_LOG_INFO (
"Netmask: %u.%u.%u.%u\n",
if_cfg->ip_cfg.ip_mask.a,
if_cfg->ip_cfg.ip_mask.b,
if_cfg->ip_cfg.ip_mask.c,
if_cfg->ip_cfg.ip_mask.d);
APP_LOG_INFO (
"Gateway: %u.%u.%u.%u\n",
if_cfg->ip_cfg.ip_gateway.a,
if_cfg->ip_cfg.ip_gateway.b,
if_cfg->ip_cfg.ip_gateway.c,
if_cfg->ip_cfg.ip_gateway.d);
}
void app_utils_print_ioxs_change (
const app_subslot_t * subslot,
const char * ioxs_str,
uint8_t iocs_current,
uint8_t iocs_new)
{
if (iocs_current != iocs_new)
{
if (iocs_new == PNET_IOXS_BAD)
{
APP_LOG_DEBUG (
"PLC reports %s BAD for slot %u subslot %u \"%s\"\n",
ioxs_str,
subslot->slot_nbr,
subslot->subslot_nbr,
subslot->submodule_name);
}
else if (iocs_new == PNET_IOXS_GOOD)
{
APP_LOG_DEBUG (
"PLC reports %s GOOD for slot %u subslot %u \"%s\".\n",
ioxs_str,
subslot->slot_nbr,
subslot->subslot_nbr,
subslot->submodule_name);
}
else if (iocs_new != PNET_IOXS_GOOD)
{
APP_LOG_DEBUG (
"PLC reports %s %u for input slot %u subslot %u \"%s\".\n"
" Is the PLC in STOP mode?\n",
ioxs_str,
iocs_new,
subslot->slot_nbr,
subslot->subslot_nbr,
subslot->submodule_name);
}
}
}
int app_utils_plug_module (
app_api_t * p_api,
uint16_t slot_nbr,
uint32_t id,
const char * name)
{
if (slot_nbr >= PNET_MAX_SLOTS)
{
return -1;
}
p_api->slots[slot_nbr].module_id = id;
p_api->slots[slot_nbr].plugged = true;
p_api->slots[slot_nbr].name = name;
return 0;
}
int app_utils_pull_module (app_api_t * p_api, uint16_t slot_nbr)
{
if (slot_nbr >= PNET_MAX_SLOTS)
{
return -1;
}
p_api->slots[slot_nbr].plugged = false;
return 0;
}
app_subslot_t * app_utils_plug_submodule (
app_api_t * p_api,
uint16_t slot_nbr,
uint16_t subslot_nbr,
uint32_t submodule_ident,
const pnet_data_cfg_t * p_data_cfg,
const char * submodule_name,
app_utils_cyclic_callback cyclic_callback,
void * tag)
{
uint16_t subslot_ix;
if (slot_nbr >= PNET_MAX_SLOTS || p_api == NULL || p_data_cfg == NULL)
{
return NULL;
}
/** Find a free subslot */
for (subslot_ix = 0; subslot_ix < PNET_MAX_SUBSLOTS; subslot_ix++)
{
if (p_api->slots[slot_nbr].subslots[subslot_ix].used == false)
{
app_subslot_t * p_subslot =
&p_api->slots[slot_nbr].subslots[subslot_ix];
p_subslot->used = true;
p_subslot->plugged = true;
p_subslot->slot_nbr = slot_nbr;
p_subslot->subslot_nbr = subslot_nbr;
p_subslot->submodule_name = submodule_name;
p_subslot->submodule_id = submodule_ident;
p_subslot->data_cfg = *p_data_cfg;
p_subslot->cyclic_callback = cyclic_callback;
p_subslot->tag = tag;
p_subslot->indata_iocs = PNET_IOXS_BAD;
p_subslot->outdata_iops = PNET_IOXS_BAD;
return p_subslot;
}
}
return NULL;
}
int app_utils_pull_submodule (
app_api_t * p_api,
uint16_t slot_nbr,
uint16_t subslot_nbr)
{
app_subslot_t * p_subslot = NULL;
if (slot_nbr >= PNET_MAX_SUBSLOTS || p_api == NULL)
{
return -1;
}
p_subslot = app_utils_subslot_get (p_api, slot_nbr, subslot_nbr);
if (p_subslot == NULL)
{
return -1;
}
memset (p_subslot, 0, sizeof (app_subslot_t));
p_subslot->used = false;
return 0;
}
app_subslot_t * app_utils_subslot_get (
app_api_t * p_api,
uint16_t slot_nbr,
uint16_t subslot_nbr)
{
uint16_t subslot_ix;
if (slot_nbr >= PNET_MAX_SLOTS || p_api == NULL)
{
return NULL;
}
for (subslot_ix = 0; subslot_ix < PNET_MAX_SUBSLOTS; subslot_ix++)
{
if (p_api->slots[slot_nbr].subslots[subslot_ix].subslot_nbr == subslot_nbr)
{
return &p_api->slots[slot_nbr].subslots[subslot_ix];
}
}
return NULL;
}
bool app_utils_subslot_is_input (const app_subslot_t * p_subslot)
{
if (p_subslot == NULL || p_subslot->used == false)
{
return false;
}
if (
p_subslot->data_cfg.data_dir == PNET_DIR_INPUT ||
p_subslot->data_cfg.data_dir == PNET_DIR_IO)
{
return true;
}
return false;
}
bool app_utils_subslot_is_no_io (const app_subslot_t * p_subslot)
{
if (p_subslot == NULL || p_subslot->used == false)
{
return false;
}
return p_subslot->data_cfg.data_dir == PNET_DIR_NO_IO;
}
bool app_utils_subslot_is_output (const app_subslot_t * p_subslot)
{
if (p_subslot == NULL || p_subslot->used == false)
{
return false;
}
if (
p_subslot->data_cfg.data_dir == PNET_DIR_OUTPUT ||
p_subslot->data_cfg.data_dir == PNET_DIR_IO)
{
return true;
}
return false;
}
void app_utils_cyclic_data_poll (app_api_t * p_api)
{
uint16_t slot_nbr;
uint16_t subslot_index;
app_subslot_t * p_subslot;
for (slot_nbr = 0; slot_nbr < PNET_MAX_SLOTS; slot_nbr++)
{
for (subslot_index = 0; subslot_index < PNET_MAX_SUBSLOTS;
subslot_index++)
{
p_subslot = &p_api->slots[slot_nbr].subslots[subslot_index];
if (p_subslot->plugged && p_subslot->cyclic_callback != NULL)
{
p_subslot->cyclic_callback (p_subslot, p_subslot->tag);
}
}
}
}

View File

@ -0,0 +1,428 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* www.rt-labs.com
* Copyright 2018 rt-labs AB, Sweden.
*
* This software is dual-licensed under GPLv3 and a commercial
* license. See the file LICENSE.md distributed with this software for
* full license information.
********************************************************************/
#ifndef APP_UTILS_H
#define APP_UTILS_H
/**
* @file
* @brief Application utilities and helper functions
*
* Functions for getting string representation of
* P-Net events, error codes and more.
*
* API, slot and subslot administration.
*
* Initialization of P-Net configuration from app_gsdml.h.
*/
#ifdef __cplusplus
extern "C" {
#endif
#include "osal.h"
#include "pnal.h"
#include <pnet_api.h>
typedef struct app_utils_netif_name
{
char name[PNET_INTERFACE_NAME_MAX_SIZE];
} app_utils_netif_name_t;
typedef struct app_utils_netif_namelist
{
app_utils_netif_name_t netif[PNET_MAX_PHYSICAL_PORTS + 1];
} app_utils_netif_namelist_t;
/* Forward declaration */
typedef struct app_subslot app_subslot_t;
/**
* Callback for updated cyclic data
*
* @param subslot InOut: Subslot structure
* @param tag InOut: Typically a handle to a submodule
*/
typedef void (*app_utils_cyclic_callback) (app_subslot_t * subslot, void * tag);
/**
* Information of submodule plugged into a subslot.
*
* Note that submodule data is not stored here but must
* be handled by the submodule implementation.
*
* All parameters are initialized by the app_utils_plug_submodule()
* function.
*
* The cyclic_callback is used when app_utils_cyclic_data_poll()
* is called. Typically on the tick event in the main task.
* The \a tag parameter is passed with the cyclic_callback and
* is typically a handle to a submodule on application.
*/
typedef struct app_subslot
{
/** True when the position in the subslot array is occupied */
bool used;
/** True when the subslot is plugged */
bool plugged;
uint16_t slot_nbr;
uint16_t subslot_nbr;
uint32_t submodule_id;
const char * submodule_name;
pnet_data_cfg_t data_cfg;
/** Status indicator from PLC */
uint8_t indata_iocs;
/** Status indicator from PLC */
uint8_t outdata_iops;
/** Callback for cyclic input- or output data, or NULL if not implemented */
app_utils_cyclic_callback cyclic_callback;
void * tag;
} app_subslot_t;
/**
* Information of module plugged into a slot,
* and array of subslots for admin of submodules.
*/
typedef struct app_slot
{
bool plugged;
uint32_t module_id;
const char * name; /** Module name */
/** Subslots. Use a separate index, as the subslot number might be large.
* For example the subslot for DAP port 1 has number 0x8001 */
app_subslot_t subslots[PNET_MAX_SUBSLOTS];
} app_slot_t;
/**
* Profinet API state for application
*
* Used to manage plugged modules into slots (and submodules into subslots).
*/
typedef struct app_api_t
{
uint32_t api_id;
uint32_t arep;
/** Slots. Use slot number as index */
app_slot_t slots[PNET_MAX_SLOTS];
} app_api_t;
/**
* Convert IP address to string
* @param ip In: IP address
* @param outputstring Out: Resulting string buffer. Should have size
* PNAL_INET_ADDRSTR_SIZE.
*/
void app_utils_ip_to_string (pnal_ipaddr_t ip, char * outputstring);
/**
* Get string description of data direction
* @param direction In: Submodule data direction
* @return String represention of data direction
*/
const char * app_utils_submod_dir_to_string (pnet_submodule_dir_t direction);
/**
* Get string description of PNIO producer or consumer status
* @param ioxs In: Producer or consumer status (IOPS/IOCS)
* @return String represention of ioxs (IOPS/IOCS)
*/
const char * app_utils_ioxs_to_string (pnet_ioxs_values_t ioxs);
/**
* Convert MAC address to string
* @param mac In: MAC address
* @param outputstring Out: Resulting string buffer. Should have size
* PNAL_ETH_ADDRSTR_SIZE.
*/
void app_utils_mac_to_string (pnet_ethaddr_t mac, char * outputstring);
/**
* Convert error code to string format
* Only common error codes supported.
* Todo: Add rest of error codes.
*
* @param err_cls In: The error class. See PNET_ERROR_CODE_1_*
* @param err_code In: The error code. See PNET_ERROR_CODE_2_*
* @param err_cls_str Out: The error class string
* @param err_code_str Out: The error code string
*/
void app_utils_get_error_code_strings (
uint16_t err_cls,
uint16_t err_code,
const char ** err_cls_str,
const char ** err_code_str);
/**
* Copy an IP address (as an integer) to a struct
* @param destination_struct Out: Destination
* @param ip In: IP address
*/
void app_utils_copy_ip_to_struct (
pnet_cfg_ip_addr_t * destination_struct,
pnal_ipaddr_t ip);
/**
* Return a string representation of
* the given dcontrol command.
* @param event In: control_command
* @return A string representing the command
*/
const char * app_utils_dcontrol_cmd_to_string (
pnet_control_command_t control_command);
/**
* Return a string representation of the given event.
* @param event In: event
* @return A string representing the event
*/
const char * app_utils_event_to_string (pnet_event_values_t event);
/**
* Update network configuration from a string
* defining a list of network interfaces examples:
* "eth0" or "br0,eth0,eth1"
*
* Read IP, netmask etc from operating system.
*
* @param netif_list_str In: Comma separated string of network ifs
* @param if_list Out: Array of network ifs
* @param number_of_ports Out: Number of ports
* @param if_cfg Out: P-Net network configuration to be updated
* @return 0 on success, -1 on error
*/
int app_utils_pnet_cfg_init_netifs (
const char * netif_list_str,
app_utils_netif_namelist_t * if_list,
uint16_t * number_of_ports,
pnet_if_cfg_t * if_cfg);
/**
* Parse a comma separated list of network interfaces and check
* that the number of interfaces match the PNET_MAX_PHYSICAL_PORTS
* configuration.
*
* For a single Ethernet interface, the \a arg_str should consist of
* one name. For two Ethernet interfaces, the \a arg_str should consist of
* three names, as we also need a bridge interface.
*
* Does only consider the number of comma separated names. No check of the
* names themselves are done.
*
* Examples:
* arg_str num_ports
* "eth0" 1
* "eth0,eth1" error (We need a bridge as well)
* "br0,eth0,eth1" 2
*
* @param arg_str In: Network interface list as comma separated,
* terminated string. For example "eth0" or
* "br0,eth0,eth1".
* @param max_port In: PNET_MAX_PHYSICAL_PORTS, passed as argument to
* allow test.
* @param p_if_list Out: List of network interfaces
* @param p_num_ports Out: Resulting number of physical ports
* @return 0 on success
* -1 on error
*/
int app_utils_get_netif_namelist (
const char * arg_str,
uint16_t max_port,
app_utils_netif_namelist_t * p_if_list,
uint16_t * p_num_ports);
/**
* Print network configuration using APP_LOG_INFO().
*
* @param if_cfg In: Network configuration
* @param number_of_ports In: Number of used ports
*/
void app_utils_print_network_config (
pnet_if_cfg_t * if_cfg,
uint16_t number_of_ports);
/**
* Print message if IOXS has changed.
*
* Uses APP_LOG_INFO()
*
* @param subslot In: Subslot
* @param ioxs_str In: String description Producer or Consumer
* @param ioxs_current In: Current status
* @param ioxs_new In: New status
*/
void app_utils_print_ioxs_change (
const app_subslot_t * subslot,
const char * ioxs_str,
uint8_t ioxs_current,
uint8_t ioxs_new);
/**
* Init the p-net configuration to default values.
*
* Most values are picked from app_gsdml.h
*
* Network configuration not initialized.
* This means that \a '.if_cfg' must be set by application.
*
* Use this function to init P-Net configuration before
* before passing config to app_init().
*
* @param pnet_cfg Out: Configuration for use by p-net
* @return 0 if the operation succeeded.
* -1 if an error occurred.
*/
int app_utils_pnet_cfg_init_default (pnet_cfg_t * pnet_cfg);
/**
* Plug application module
*
* This is for the application to remember which slots are
* populated in the p-net stack.
*
* @param p_api InOut: API
* @param slot_nbr In: Slot number
* @param id In: Module identity
* @param name In: Module name
* @return 0 on success, -1 on error
*/
int app_utils_plug_module (
app_api_t * p_api,
uint16_t slot_nbr,
uint32_t id,
const char * name);
/**
* Pull any application module in given slot.
*
* This is for the application to remember which slots are
* populated in the p-net stack.
*
* @param p_api InOut: API
* @param slot_nbr In: Slot number
* @return 0 on success, -1 on error
*/
int app_utils_pull_module (app_api_t * p_api, uint16_t slot_nbr);
/**
* Plug application submodule.
*
* This is for the application to remember which subslots are
* populated in the p-net stack.
*
* @param p_api InOut: API
* @param slot_nbr In: Slot number
* @param subslot_nbr In: Subslot number
* @param submodule_id In: Submodule identity
* @param p_data_cfg In: Data configuration,
* direction, in and out sizes
* @param submodule_name In: Submodule name
* @param cyclic_callback In: Submodule data callback
* @param tag In: Tag passed in cyclic callback
* Typically application or
* submodule handle
* @return Reference to allocated subslot,
* NULL if no free subslot is available. This should
* never happen if application is aligned with p-net state.
*/
app_subslot_t * app_utils_plug_submodule (
app_api_t * p_api,
uint16_t slot_nbr,
uint16_t subslot_nbr,
uint32_t submodule_id,
const pnet_data_cfg_t * p_data_cfg,
const char * submodule_name,
app_utils_cyclic_callback cyclic_callback,
void * tag);
/**
* Unplug any application submodule from given subslot.
*
* This is for the application to remember which subslots are
* populated in the p-net stack.
*
* @param p_api InOut: API
* @param slot_nbr In: Slot number
* @param subslot_nbr In: Subslot number
* @return 0 on success, -1 on error.
*/
int app_utils_pull_submodule (
app_api_t * p_api,
uint16_t slot_nbr,
uint16_t subslot_nbr);
/**
* Trigger data callback for all plugged submodules in all slots.
*
* The callbacks given in \a app_utils_plug_submodule() are used.
*
* @param p_api In: API
*/
void app_utils_cyclic_data_poll (app_api_t * p_api);
/**
* Get subslot application information.
*
* @param p_appdata InOut: Application state.
* @param slot_nbr In: Slot number.
* @param subslot_nbr In: Subslot number. Range 0 - 0x9FFF.
* @return Reference to application subslot,
* NULL if subslot is not found/plugged.
*/
app_subslot_t * app_utils_subslot_get (
app_api_t * p_api,
uint16_t slot_nbr,
uint16_t subslot_nbr);
/**
* Return true if subslot is input.
*
* @param p_subslot In: Reference to subslot.
* @return true if subslot is input or input/output.
* false if not.
*/
bool app_utils_subslot_is_input (const app_subslot_t * p_subslot);
/**
* Return true if subslot is neither input or output.
*
* This is applies for DAP submodules/slots
*
* @param p_subslot In: Reference to subslot.
* @return true if subslot is input or input/output.
* false if not.
*/
bool app_utils_subslot_is_no_io (const app_subslot_t * p_subslot);
/**
* Return true if subslot is output.
*
* @param p_subslot In: Reference to subslot.
* @return true if subslot is output or input/output,
* false if not.
*/
bool app_utils_subslot_is_output (const app_subslot_t * p_subslot);
#ifdef __cplusplus
}
#endif
#endif /* APP_UTILS_H */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,156 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* www.rt-labs.com
* Copyright 2018 rt-labs AB, Sweden.
*
* This software is dual-licensed under GPLv3 and a commercial
* license. See the file LICENSE.md distributed with this software for
* full license information.
********************************************************************/
#ifndef SAMPLEAPP_COMMON_H
#define SAMPLEAPP_COMMON_H
#include "osal.h"
#include "pnal.h"
#include <pnet_api.h>
#ifdef __cplusplus
extern "C" {
#endif
#define APP_TICK_INTERVAL_US 1000 /* 1 ms */
/* Thread configuration for targets where sample
* event loop is run in a separate thread (not main).
* This applies for linux sample app implementation.
*/
#define APP_MAIN_THREAD_PRIORITY 15
#define APP_MAIN_THREAD_STACKSIZE 4096 /* bytes */
#define APP_DATA_LED_ID 1
#define APP_PROFINET_SIGNAL_LED_ID 2
#define APP_TICKS_READ_BUTTONS 10
#define APP_TICKS_UPDATE_DATA 100
/** HW Offload configuration. */
typedef enum
{
MODE_HW_OFFLOAD_NONE = 0,
MODE_HW_OFFLOAD_CPU,
MODE_HW_OFFLOAD_FULL,
} app_mode_t;
/** Command line arguments for sample application */
typedef struct app_args
{
char path_button1[PNET_MAX_FILE_FULLPATH_SIZE]; /** Terminated string */
char path_button2[PNET_MAX_FILE_FULLPATH_SIZE]; /** Terminated string */
char path_storage_directory[PNET_MAX_DIRECTORYPATH_SIZE]; /** Terminated */
char station_name[PNET_STATION_NAME_MAX_SIZE]; /** Terminated string */
char eth_interfaces
[PNET_INTERFACE_NAME_MAX_SIZE * (PNET_MAX_PHYSICAL_PORTS + 1) +
PNET_MAX_PHYSICAL_PORTS]; /** Terminated string */
int verbosity;
int show;
bool factory_reset;
bool remove_files;
app_mode_t mode;
} app_args_t;
typedef enum
{
RUN_IN_SEPARATE_THREAD,
RUN_IN_MAIN_THREAD
} app_run_in_separate_task_t;
typedef struct app_data_t app_data_t;
/** Partially initialise config values, and use proper callbacks
*
* @param pnet_cfg Out: Configuration to be updated
*/
void app_pnet_cfg_init_default (pnet_cfg_t * pnet_cfg);
/**
* Initialize P-Net stack and application.
*
* The \a pnet_cfg argument shall have been initialized using
* \a app_pnet_cfg_init_default() before this function is
* called.
*
* @param pnet_cfg In: P-Net configuration
* @param app_args In: Application arguments
* @return Application handle, NULL on error
*/
app_data_t * app_init (const pnet_cfg_t * pnet_cfg, const app_args_t * app_args);
/**
* Start application main loop
*
* Application must have been initialized using \a app_init() before
* this function is called.
*
* If \a task_config parameters is set to RUN_IN_SEPARATE_THREAD a
* thread execution the \a app_loop_forever() function is started.
* If task_config is set to RUN_IN_MAIN_THREAD no such thread is
* started and the caller must call the \a app_loop_forever() after
* calling this function.
*
* RUN_IN_MAIN_THREAD is intended for rt-kernel targets.
* RUN_IN_SEPARATE_THREAD is intended for linux targets.
*
* @param app In: Application handle
* @param task_config In: Defines if stack and application
* is run in main or separate task.
* @return 0 on success, -1 on error
*/
int app_start (app_data_t * app, app_run_in_separate_task_t task_config);
/**
* Application task definition. Handles events in eternal loop.
*
* @param arg In: Application handle
*/
void app_loop_forever (void * arg);
/**
* Get P-Net instance from application
*
* @param app In: Application handle
* @return P-Net instance, NULL on failure
*/
pnet_t * app_get_pnet_instance (app_data_t * app);
/**
* Set LED state
* Hardware specific. Implemented in sample app main file for
* each supported platform.
*
* @param id In: LED number, starting from 0.
* @param led_state In: LED state. Use true for on and false for off.
*/
void app_set_led (uint16_t id, bool led_state);
/**
* Read button state
*
* Hardware specific. Implemented in sample app main file for
* each supported platform.
*
* @param id In: Button number, starting from 0.
* @return true if button is pressed, false if not
*/
bool app_get_button (uint16_t id);
#ifdef __cplusplus
}
#endif
#endif /* SAMPLEAPP_COMMON_H */

View File

@ -0,0 +1,23 @@
#********************************************************************
# _ _ _
# _ __ | |_ _ | | __ _ | |__ ___
# | '__|| __|(_)| | / _` || '_ \ / __|
# | | | |_ _ | || (_| || |_) |\__ \
# |_| \__|(_)|_| \__,_||_.__/ |___/
#
# http://www.rt-labs.com
# Copyright 2017 rt-labs AB, Sweden.
# See LICENSE file in the project root for full license information.
#*******************************************************************/
target_include_directories(pn_lan9662
PRIVATE
${PROFINET_SOURCE_DIR}/src
${PROFINET_SOURCE_DIR}/src/drivers/lan9662
${PROFINET_BINARY_DIR}/src
)
target_link_libraries (pn_lan9662 PUBLIC profinet mera)
install (TARGETS pn_lan9662 DESTINATION bin)

View File

@ -0,0 +1,417 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<ISO15745Profile xmlns="http://www.profibus.com/GSDML/2003/11/DeviceProfile" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.profibus.com/GSDML/2003/11/DeviceProfile ..\xsd\GSDML-DeviceProfile-V2.4.xsd">
<!-- ProfileHeader definition as defined in ISO 15745-1. Please do not change the content. -->
<ProfileHeader>
<ProfileIdentification>PROFINET Device Profile</ProfileIdentification>
<ProfileRevision>1.00</ProfileRevision>
<ProfileName>Device Profile for PROFINET Devices</ProfileName>
<ProfileSource>PROFIBUS Nutzerorganisation e. V. (PNO)</ProfileSource>
<ProfileClassID>Device</ProfileClassID>
<ISO15745Reference>
<ISO15745Part>4</ISO15745Part>
<ISO15745Edition>1</ISO15745Edition>
<ProfileTechnology>GSDML</ProfileTechnology>
</ISO15745Reference>
</ProfileHeader>
<ProfileBody>
<DeviceIdentity VendorID="0x0493" DeviceID="0x9662">
<InfoText TextId="IDT_INFO_Device"/>
<VendorName Value="RT-Labs"/>
</DeviceIdentity>
<DeviceFunction>
<Family MainFamily="I/O" ProductFamily="P-Net Samples"/>
</DeviceFunction>
<ApplicationProcess>
<DeviceAccessPointList>
<DeviceAccessPointItem ID="IDD_1" PNIO_Version="V2.4" PhysicalSlots="0..12" ModuleIdentNumber="0x00000001" MinDeviceInterval="32" DNS_CompatibleName="lan9662-dev" FixedInSlots="0" ObjectUUID_LocalIndex="1" DeviceAccessSupported="false" MultipleWriteSupported="true" CheckDeviceID_Allowed="true" NameOfStationNotTransferable="false" LLDP_NoD_Supported="true" ResetToFactoryModes="1..2">
<ModuleInfo>
<Name TextId="IDT_MODULE_NAME_DAP1"/>
<InfoText TextId="IDT_INFO_DAP1"/>
<VendorName Value="RT-Labs"/>
<OrderNumber Value="12345 Abcdefghijk"/>
<HardwareRelease Value="A1.0"/>
<SoftwareRelease Value="V0.1.0"/>
</ModuleInfo>
<CertificationInfo ConformanceClass="A" ApplicationClass="" NetloadClass="I"/>
<IOConfigData MaxInputLength="1440" MaxOutputLength="1440"/>
<UseableModules>
<ModuleItemRef ModuleItemTarget="IDM_1" AllowedInSlots="1"/>
<ModuleItemRef ModuleItemTarget="IDM_2" AllowedInSlots="2"/>
<ModuleItemRef ModuleItemTarget="IDM_3" AllowedInSlots="3"/>
<ModuleItemRef ModuleItemTarget="IDM_4" AllowedInSlots="4"/>
<ModuleItemRef ModuleItemTarget="IDM_5" AllowedInSlots="5"/>
<ModuleItemRef ModuleItemTarget="IDM_6" AllowedInSlots="6"/>
<ModuleItemRef ModuleItemTarget="IDM_7" AllowedInSlots="7"/>
<ModuleItemRef ModuleItemTarget="IDM_8" AllowedInSlots="8"/>
<ModuleItemRef ModuleItemTarget="IDM_9" AllowedInSlots="9"/>
<ModuleItemRef ModuleItemTarget="IDM_10" AllowedInSlots="10"/>
<ModuleItemRef ModuleItemTarget="IDM_11" AllowedInSlots="11"/>
<ModuleItemRef ModuleItemTarget="IDM_12" AllowedInSlots="12"/>
</UseableModules>
<VirtualSubmoduleList>
<VirtualSubmoduleItem ID="IDS_1" SubmoduleIdentNumber="0x00000001" Writeable_IM_Records="1 2 3" MayIssueProcessAlarm="false">
<IOData/>
<ModuleInfo>
<Name TextId="IDT_MODULE_NAME_DAP1"/>
<InfoText TextId="IDT_INFO_DAP1"/>
</ModuleInfo>
</VirtualSubmoduleItem>
</VirtualSubmoduleList>
<SystemDefinedSubmoduleList>
<InterfaceSubmoduleItem ID="IDS_I" SubmoduleIdentNumber="0x00008000" SubslotNumber="32768" TextId="IDT_NAME_IS" SupportedRT_Classes="RT_CLASS_1" SupportedProtocols="LLDP" NetworkComponentDiagnosisSupported="false" PTP_BoundarySupported="true" DCP_BoundarySupported="true">
<ApplicationRelations StartupMode="Advanced">
<TimingProperties SendClock="32" ReductionRatio="1 2 4 8 16 32 64 128 256 512"/>
</ApplicationRelations>
</InterfaceSubmoduleItem>
<PortSubmoduleItem ID="IDS_P1" SubmoduleIdentNumber="0x00008001" SubslotNumber="32769" TextId="IDT_NAME_PS1" MaxPortRxDelay="350" MaxPortTxDelay="160">
<MAUTypeList>
<MAUTypeItem Value="30"/>
<MAUTypeItem Value="16"/>
<MAUTypeItem Value="5"/>
</MAUTypeList>
</PortSubmoduleItem>
<PortSubmoduleItem ID="IDS_P2" SubmoduleIdentNumber="0x00008002" SubslotNumber="32770" TextId="IDT_NAME_PS2" MaxPortRxDelay="350" MaxPortTxDelay="160">
<MAUTypeList>
<MAUTypeItem Value="30"/>
<MAUTypeItem Value="16"/>
<MAUTypeItem Value="5"/>
</MAUTypeList>
</PortSubmoduleItem>
</SystemDefinedSubmoduleList>
</DeviceAccessPointItem>
</DeviceAccessPointList>
<ModuleList>
<ModuleItem ID="IDM_1" ModuleIdentNumber="0x00001001">
<ModuleInfo>
<Name TextId="TOK_Name_Module_1"/>
<InfoText TextId="TOK_InfoText_Module_1"/>
<HardwareRelease Value="1.0"/>
<SoftwareRelease Value="1.0"/>
</ModuleInfo>
<VirtualSubmoduleList>
<VirtualSubmoduleItem ID="IDSM_1" SubmoduleIdentNumber="0x00002001" MayIssueProcessAlarm="false">
<IOData>
<Input Consistency="All items consistency">
<DataItem DataType="Unsigned8" TextId="TOK_Dummy" UseAsBits="false">
</DataItem>
</Input>
</IOData>
<ModuleInfo>
<Name TextId="TOK_Name_Module_1"/>
<InfoText TextId="TOK_InfoText_Module_1"/>
</ModuleInfo>
</VirtualSubmoduleItem>
</VirtualSubmoduleList>
</ModuleItem>
<ModuleItem ID="IDM_2" ModuleIdentNumber="0x00001002">
<ModuleInfo>
<Name TextId="TOK_Name_Module_2"/>
<InfoText TextId="TOK_InfoText_Module_2"/>
<HardwareRelease Value="1.0"/>
<SoftwareRelease Value="1.0"/>
</ModuleInfo>
<VirtualSubmoduleList>
<VirtualSubmoduleItem ID="IDSM_2" SubmoduleIdentNumber="0x00002002" MayIssueProcessAlarm="false">
<IOData>
<Output Consistency="All items consistency">
<DataItem DataType="Unsigned8" TextId="TOK_Dummy" UseAsBits="false">
</DataItem>
</Output>
</IOData>
<ModuleInfo>
<Name TextId="TOK_Name_Module_2"/>
<InfoText TextId="TOK_InfoText_Module_2"/>
</ModuleInfo>
</VirtualSubmoduleItem>
</VirtualSubmoduleList>
</ModuleItem>
<ModuleItem ID="IDM_3" ModuleIdentNumber="0x00001003">
<ModuleInfo>
<Name TextId="TOK_Name_Module_3"/>
<InfoText TextId="TOK_InfoText_Module_3"/>
<HardwareRelease Value="1.0"/>
<SoftwareRelease Value="1.0"/>
</ModuleInfo>
<VirtualSubmoduleList>
<VirtualSubmoduleItem ID="IDSM_3" SubmoduleIdentNumber="0x00002003" MayIssueProcessAlarm="false">
<IOData>
<Input Consistency="All items consistency">
<DataItem DataType="Unsigned64" TextId="TOK_Dummy" UseAsBits="false">
</DataItem>
</Input>
</IOData>
<ModuleInfo>
<Name TextId="TOK_Name_Module_3"/>
<InfoText TextId="TOK_InfoText_Module_3"/>
</ModuleInfo>
</VirtualSubmoduleItem>
</VirtualSubmoduleList>
</ModuleItem>
<ModuleItem ID="IDM_4" ModuleIdentNumber="0x00001004">
<ModuleInfo>
<Name TextId="TOK_Name_Module_4"/>
<InfoText TextId="TOK_InfoText_Module_4"/>
<HardwareRelease Value="1.0"/>
<SoftwareRelease Value="1.0"/>
</ModuleInfo>
<VirtualSubmoduleList>
<VirtualSubmoduleItem ID="IDSM_4" SubmoduleIdentNumber="0x00002004" MayIssueProcessAlarm="false">
<IOData>
<Input Consistency="All items consistency">
<DataItem DataType="Unsigned32" TextId="TOK_Dummy" UseAsBits="false">
</DataItem>
<DataItem DataType="Unsigned32" TextId="TOK_Dummy" UseAsBits="false">
</DataItem>
</Input>
</IOData>
<ModuleInfo>
<Name TextId="TOK_Name_Module_4"/>
<InfoText TextId="TOK_InfoText_Module_4"/>
</ModuleInfo>
</VirtualSubmoduleItem>
</VirtualSubmoduleList>
</ModuleItem>
<ModuleItem ID="IDM_5" ModuleIdentNumber="0x00001005">
<ModuleInfo>
<Name TextId="TOK_Name_Module_5"/>
<InfoText TextId="TOK_InfoText_Module_5"/>
<HardwareRelease Value="1.0"/>
<SoftwareRelease Value="1.0"/>
</ModuleInfo>
<VirtualSubmoduleList>
<VirtualSubmoduleItem ID="IDSM_5" SubmoduleIdentNumber="0x00002005" MayIssueProcessAlarm="false">
<IOData>
<Input Consistency="All items consistency">
<DataItem DataType="Unsigned32" TextId="TOK_Dummy" UseAsBits="false">
</DataItem>
<DataItem DataType="Unsigned32" TextId="TOK_Dummy" UseAsBits="false">
</DataItem>
</Input>
</IOData>
<ModuleInfo>
<Name TextId="TOK_Name_Module_5"/>
<InfoText TextId="TOK_InfoText_Module_5"/>
</ModuleInfo>
</VirtualSubmoduleItem>
</VirtualSubmoduleList>
</ModuleItem>
<ModuleItem ID="IDM_6" ModuleIdentNumber="0x00001006">
<ModuleInfo>
<Name TextId="TOK_Name_Module_6"/>
<InfoText TextId="TOK_InfoText_Module_6"/>
<HardwareRelease Value="1.0"/>
<SoftwareRelease Value="1.0"/>
</ModuleInfo>
<VirtualSubmoduleList>
<VirtualSubmoduleItem ID="IDSM_6" SubmoduleIdentNumber="0x00002006" MayIssueProcessAlarm="false">
<IOData>
<Input Consistency="All items consistency">
<DataItem DataType="OctetString" Length="100" TextId="TOK_Dummy" UseAsBits="false">
</DataItem>
</Input>
</IOData>
<ModuleInfo>
<Name TextId="TOK_Name_Module_6"/>
<InfoText TextId="TOK_InfoText_Module_6"/>
</ModuleInfo>
</VirtualSubmoduleItem>
</VirtualSubmoduleList>
</ModuleItem>
<ModuleItem ID="IDM_7" ModuleIdentNumber="0x00001007">
<ModuleInfo>
<Name TextId="TOK_Name_Module_7"/>
<InfoText TextId="TOK_InfoText_Module_7"/>
<HardwareRelease Value="1.0"/>
<SoftwareRelease Value="1.0"/>
</ModuleInfo>
<VirtualSubmoduleList>
<VirtualSubmoduleItem ID="IDSM_7" SubmoduleIdentNumber="0x00002007" MayIssueProcessAlarm="false">
<IOData>
<Output Consistency="All items consistency">
<DataItem DataType="Unsigned64" TextId="TOK_Dummy" UseAsBits="false">
</DataItem>
</Output>
</IOData>
<ModuleInfo>
<Name TextId="TOK_Name_Module_7"/>
<InfoText TextId="TOK_InfoText_Module_7"/>
</ModuleInfo>
</VirtualSubmoduleItem>
</VirtualSubmoduleList>
</ModuleItem>
<ModuleItem ID="IDM_8" ModuleIdentNumber="0x00001008">
<ModuleInfo>
<Name TextId="TOK_Name_Module_8"/>
<InfoText TextId="TOK_InfoText_Module_8"/>
<HardwareRelease Value="1.0"/>
<SoftwareRelease Value="1.0"/>
</ModuleInfo>
<VirtualSubmoduleList>
<VirtualSubmoduleItem ID="IDSM_8" SubmoduleIdentNumber="0x00002008" MayIssueProcessAlarm="false">
<IOData>
<Output Consistency="All items consistency">
<DataItem DataType="Unsigned32" TextId="TOK_Dummy" UseAsBits="false">
</DataItem>
<DataItem DataType="Unsigned32" TextId="TOK_Dummy" UseAsBits="false">
</DataItem>
</Output>
</IOData>
<ModuleInfo>
<Name TextId="TOK_Name_Module_8"/>
<InfoText TextId="TOK_InfoText_Module_8"/>
</ModuleInfo>
</VirtualSubmoduleItem>
</VirtualSubmoduleList>
</ModuleItem>
<ModuleItem ID="IDM_9" ModuleIdentNumber="0x00001009">
<ModuleInfo>
<Name TextId="TOK_Name_Module_9"/>
<InfoText TextId="TOK_InfoText_Module_9"/>
<HardwareRelease Value="1.0"/>
<SoftwareRelease Value="1.0"/>
</ModuleInfo>
<VirtualSubmoduleList>
<VirtualSubmoduleItem ID="IDSM_9" SubmoduleIdentNumber="0x00002009" MayIssueProcessAlarm="false">
<IOData>
<Output Consistency="All items consistency">
<DataItem DataType="Unsigned32" TextId="TOK_Dummy" UseAsBits="false">
</DataItem>
<DataItem DataType="Unsigned32" TextId="TOK_Dummy" UseAsBits="false">
</DataItem>
</Output>
</IOData>
<ModuleInfo>
<Name TextId="TOK_Name_Module_9"/>
<InfoText TextId="TOK_InfoText_Module_9"/>
</ModuleInfo>
</VirtualSubmoduleItem>
</VirtualSubmoduleList>
</ModuleItem>
<ModuleItem ID="IDM_10" ModuleIdentNumber="0x0000100A">
<ModuleInfo>
<Name TextId="TOK_Name_Module_10"/>
<InfoText TextId="TOK_InfoText_Module_10"/>
<HardwareRelease Value="1.0"/>
<SoftwareRelease Value="1.0"/>
</ModuleInfo>
<VirtualSubmoduleList>
<VirtualSubmoduleItem ID="IDSM_10" SubmoduleIdentNumber="0x0000200A" MayIssueProcessAlarm="false">
<IOData>
<Output Consistency="All items consistency">
<DataItem DataType="OctetString" Length="100" TextId="TOK_Dummy" UseAsBits="false">
</DataItem>
</Output>
</IOData>
<ModuleInfo>
<Name TextId="TOK_Name_Module_10"/>
<InfoText TextId="TOK_InfoText_Module_10"/>
</ModuleInfo>
</VirtualSubmoduleItem>
</VirtualSubmoduleList>
</ModuleItem>
<ModuleItem ID="IDM_11" ModuleIdentNumber="0x0000100B">
<ModuleInfo>
<Name TextId="TOK_Name_Module_11"/>
<InfoText TextId="TOK_InfoText_Module_11"/>
<HardwareRelease Value="1.0"/>
<SoftwareRelease Value="1.0"/>
</ModuleInfo>
<VirtualSubmoduleList>
<VirtualSubmoduleItem ID="IDSM_11" SubmoduleIdentNumber="0x0000200B" MayIssueProcessAlarm="false">
<IOData>
<Input Consistency="All items consistency">
<DataItem DataType="Unsigned32" TextId="TOK_Dummy" UseAsBits="false">
</DataItem>
</Input>
</IOData>
<ModuleInfo>
<Name TextId="TOK_Name_Module_11"/>
<InfoText TextId="TOK_InfoText_Module_11"/>
</ModuleInfo>
</VirtualSubmoduleItem>
</VirtualSubmoduleList>
</ModuleItem>
<ModuleItem ID="IDM_12" ModuleIdentNumber="0x0000100C">
<ModuleInfo>
<Name TextId="TOK_Name_Module_12"/>
<InfoText TextId="TOK_InfoText_Module_12"/>
<HardwareRelease Value="1.0"/>
<SoftwareRelease Value="1.0"/>
</ModuleInfo>
<VirtualSubmoduleList>
<VirtualSubmoduleItem ID="IDSM_12" SubmoduleIdentNumber="0x0000200C" MayIssueProcessAlarm="false">
<IOData>
<Output Consistency="All items consistency">
<DataItem DataType="Unsigned32" TextId="TOK_Dummy" UseAsBits="false">
</DataItem>
</Output>
</IOData>
<ModuleInfo>
<Name TextId="TOK_Name_Module_12"/>
<InfoText TextId="TOK_InfoText_Module_12"/>
</ModuleInfo>
</VirtualSubmoduleItem>
</VirtualSubmoduleList>
</ModuleItem>
</ModuleList>
<LogBookEntryList>
<LogBookEntryItem Status="2130510">
<!--Custom log book entry for sample application-->
<!--Error code 0x20 Error decode 0x82 Error code 1 0x4E-->
<ErrorCode2Value>
<Name TextId="IDT_CUSTOM_LOGBOOK_1"/>
</ErrorCode2Value>
</LogBookEntryItem>
</LogBookEntryList>
<GraphicsList>
<GraphicItem ID="RT-LabsStackImage" GraphicFile="GSDML-RT-LABS-STACK" />
</GraphicsList>
<ExternalTextList>
<PrimaryLanguage>
<Text TextId="IDT_INFO_Device" Value="https://github.com/rtlabs-com/p-net"/>
<Text TextId="IDT_MODULE_NAME_DAP1" Value="P-Net LAN9662 Sample"/>
<Text TextId="IDT_INFO_DAP1" Value="Profinet LAN9662 sample https://github.com/rtlabs-com/p-net"/>
<Text TextId="IDT_CUSTOM_DIAG_1" Value="Custom diagnosis in USI format"/>
<Text TextId="IDT_CUSTOM_DIAG_1_VALUE" Value="Custom diagnosis value"/>
<Text TextId="IDT_CUSTOM_LOGBOOK_1" Value="Custom Logbook entry"/>
<Text TextId="IDT_NAME_IS" Value="X1"/>
<Text TextId="IDT_NAME_PS1" Value="X1 P1"/>
<Text TextId="IDT_NAME_PS2" Value="X1 P2"/>
<!--module names-->
<Text TextId="TOK_Name_Module_1" Value="DI 1x8"/>
<Text TextId="TOK_Name_Module_2" Value="DO 1x8"/>
<Text TextId="TOK_Name_Module_3" Value="DI 1x64"/>
<Text TextId="TOK_Name_Module_4" Value="DI 2x32a"/>
<Text TextId="TOK_Name_Module_5" Value="DI 2x32b"/>
<Text TextId="TOK_Name_Module_6" Value="DI 1x800"/>
<Text TextId="TOK_Name_Module_7" Value="DO 1x64"/>
<Text TextId="TOK_Name_Module_8" Value="DO 2x32a"/>
<Text TextId="TOK_Name_Module_9" Value="DO 2x32b"/>
<Text TextId="TOK_Name_Module_10" Value="DO 1x800"/>
<Text TextId="TOK_Name_Module_11" Value="DI Port A"/>
<Text TextId="TOK_Name_Module_12" Value="DO Port A"/>
<!--module info -->
<Text TextId="TOK_InfoText_Module_1" Value="Digital In 1x8"/>
<Text TextId="TOK_InfoText_Module_2" Value="Digital Out 1x8"/>
<Text TextId="TOK_InfoText_Module_3" Value="Digital In 1x64"/>
<Text TextId="TOK_InfoText_Module_4" Value="Digital In 2x32"/>
<Text TextId="TOK_InfoText_Module_5" Value="Digital In 2x32"/>
<Text TextId="TOK_InfoText_Module_6" Value="Digital In 1x800"/>
<Text TextId="TOK_InfoText_Module_7" Value="Digital Out 1x64"/>
<Text TextId="TOK_InfoText_Module_8" Value="Digital Out 2x32"/>
<Text TextId="TOK_InfoText_Module_9" Value="Digital Out 2x32"/>
<Text TextId="TOK_InfoText_Module_10" Value="Digital Out 1x800"/>
<Text TextId="TOK_InfoText_Module_11" Value="Digital In Port A"/>
<Text TextId="TOK_InfoText_Module_12" Value="Digital Out Port A"/>
<!--dataitem name-->
<Text TextId="TOK_Dummy" Value="Dummy"/>
</PrimaryLanguage>
</ExternalTextList>
</ApplicationProcess>
</ProfileBody>
</ISO15745Profile>

View File

@ -0,0 +1,68 @@
#!/bin/sh
#
# Initialization script for automatic
# start of the pn_lan9662 sample application.
# Copy this script to overlay/etc/init.d/
# to start the application automatically.
#
start() {
# Assume a valid partion on /dev/mmcblk0p2
mkdir /tmp/pn_data
mount -o sync /dev/mmcblk0p2 /tmp/pn_data
echo "Configure network settings for pn_lan9662 profinet sample application"
# Create bridge
ip link add name br0 type bridge
symreg ANA_PGID[61] 0x1ff
ip link set br0 address 12:A9:2D:16:93:89
ip link set br0 up
sysctl -w net.ipv6.conf.br0.disable_ipv6=1
ip link set eth0 master br0
ip link set eth1 master br0
ip link set eth0 up
ip link set eth1 up
# Enable forwarding to chip port 4 (RTE)
symreg qsys_sw_port_mode[4] 0x45000
# Start Profinet application
# Supported modes: none, cpu, full
# Read mode from file
mode="full"
if test -f /tmp/pn_data/mode.txt; then
mode=$(cat /tmp/pn_data/mode.txt)
fi
echo "Starting LAN9662 Profinet sample application"
echo "RTE mode: $mode"
/usr/bin/pn_lan9662 -m $mode -vvvv -p /tmp/pn_data &
# Enable for interleaved ART-Tester logs
#tcpdump -i br0 udp port 514 -v -a &
}
stop() {
printf "Todo - Stop Profinet application"
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart|reload)
stop
start
;;
*)
echo "Usage: $0 {start|stop|restart}"
exit 1
esac
exit $?

View File

@ -0,0 +1,442 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* www.rt-labs.com
* Copyright 2021 rt-labs AB, Sweden.
*
* This software is dual-licensed under GPLv3 and a commercial
* license. See the file LICENSE.md distributed with this software for
* full license information.
********************************************************************/
#include "app_data.h"
#include "app_shm.h"
#include "app_utils.h"
#include "app_gsdml.h"
#include "app_log.h"
#include "sampleapp_common.h"
#include "osal.h"
#include "pnal.h"
#include <pnet_api.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/**
* IO FPGA address mapping.
*/
#define APP_DATA_FPGA_START_ADDRESS 0x100
#define ALIGN32(x) ((((x)) + 3) & ~3)
#define APP_FPGA_ADDRESS_DIGITAL_IN_1x8 (APP_DATA_FPGA_START_ADDRESS)
#define APP_FPGA_ADDRESS_DIGITAL_OUT_1x8 \
(APP_FPGA_ADDRESS_DIGITAL_IN_1x8 + ALIGN32 (APP_GSDML_SIZE_DIGITAL_IN_1x8))
#define APP_FPGA_ADDRESS_DIGITAL_IN_1x64 \
(APP_FPGA_ADDRESS_DIGITAL_OUT_1x8 + ALIGN32 (APP_GSDML_SIZE_DIGITAL_OUT_1x8))
#define APP_FPGA_ADDRESS_DIGITAL_IN_2x32a \
(APP_FPGA_ADDRESS_DIGITAL_IN_1x64 + ALIGN32 (APP_GSDML_SIZE_DIGITAL_IN_1x64))
#define APP_FPGA_ADDRESS_DIGITAL_IN_2x32b \
(APP_FPGA_ADDRESS_DIGITAL_IN_2x32a + \
ALIGN32 (APP_GSDML_SIZE_DIGITAL_IN_2x32a))
#define APP_FPGA_ADDRESS_DIGITAL_IN_1x800 \
(APP_FPGA_ADDRESS_DIGITAL_IN_2x32b + \
ALIGN32 (APP_GSDML_SIZE_DIGITAL_IN_2x32b))
#define APP_FPGA_ADDRESS_DIGITAL_OUT_1x64 \
(APP_FPGA_ADDRESS_DIGITAL_IN_1x800 + \
ALIGN32 (APP_GSDML_SIZE_DIGITAL_IN_1x800))
#define APP_FPGA_ADDRESS_DIGITAL_OUT_2x32a \
(APP_FPGA_ADDRESS_DIGITAL_OUT_1x64 + \
ALIGN32 (APP_GSDML_SIZE_DIGITAL_OUT_1x64))
#define APP_FPGA_ADDRESS_DIGITAL_OUT_2x32b \
(APP_FPGA_ADDRESS_DIGITAL_OUT_2x32a + \
ALIGN32 (APP_GSDML_SIZE_DIGITAL_OUT_2x32a))
#define APP_FPGA_ADDRESS_DIGITAL_OUT_1x800 \
(APP_FPGA_ADDRESS_DIGITAL_OUT_2x32b + \
ALIGN32 (APP_GSDML_SIZE_DIGITAL_OUT_2x32b))
#define APP_FPGA_ADDRESS_DIGITAL_IN_PORT_A 0x200
#define APP_FPGA_ADDRESS_DIGITAL_OUT_PORT_A 0x10
typedef struct submodule_fpga_config
{
uint16_t address;
const uint8_t * default_data;
} submodule_fpga_config_t;
typedef struct submodule
{
const app_gsdml_submodule_t * info;
/* shm -> pnet*/
app_shm_t * indata_shm;
uint8_t * in_data;
/* pnet -> shm */
app_shm_t * outdata_shm;
uint8_t * out_data;
/* FPGA address for full offload mode */
const submodule_fpga_config_t * fpga_config;
} submodule_t;
typedef struct fpga_map
{
uint32_t submodule_id;
submodule_fpga_config_t fpga_config;
} fpga_map_t;
static const uint8_t app_data_default[APP_GSDML_MAX_SUBMODULE_DATA_SIZE] = {0};
/**
* Configuration of FPGA addresses for the different
* submodules when running full offload.
*/
static const fpga_map_t fpga[] = {
{.submodule_id = APP_GSDML_SUBMOD_ID_DIGITAL_IN_1x8,
.fpga_config =
{.address = APP_FPGA_ADDRESS_DIGITAL_IN_1x8,
.default_data = app_data_default}},
{.submodule_id = APP_GSDML_SUBMOD_ID_DIGITAL_OUT_1x8,
.fpga_config =
{.address = APP_FPGA_ADDRESS_DIGITAL_OUT_1x8,
.default_data = app_data_default}},
{.submodule_id = APP_GSDML_SUBMOD_ID_DIGITAL_IN_1x64,
.fpga_config =
{.address = APP_FPGA_ADDRESS_DIGITAL_IN_1x64,
.default_data = app_data_default}},
{.submodule_id = APP_GSDML_SUBMOD_ID_DIGITAL_IN_2x32a,
.fpga_config =
{.address = APP_FPGA_ADDRESS_DIGITAL_IN_2x32a,
.default_data = app_data_default}},
{.submodule_id = APP_GSDML_SUBMOD_ID_DIGITAL_IN_2x32b,
.fpga_config =
{.address = APP_FPGA_ADDRESS_DIGITAL_IN_2x32b,
.default_data = app_data_default}},
{.submodule_id = APP_GSDML_SUBMOD_ID_DIGITAL_IN_1x800,
.fpga_config =
{.address = APP_FPGA_ADDRESS_DIGITAL_IN_1x800,
.default_data = app_data_default}},
{.submodule_id = APP_GSDML_SUBMOD_ID_DIGITAL_OUT_1x64,
.fpga_config =
{.address = APP_FPGA_ADDRESS_DIGITAL_OUT_1x64,
.default_data = app_data_default}},
{.submodule_id = APP_GSDML_SUBMOD_ID_DIGITAL_OUT_2x32a,
.fpga_config =
{.address = APP_FPGA_ADDRESS_DIGITAL_OUT_2x32a,
.default_data = app_data_default}},
{.submodule_id = APP_GSDML_SUBMOD_ID_DIGITAL_OUT_2x32b,
.fpga_config =
{.address = APP_FPGA_ADDRESS_DIGITAL_OUT_2x32b,
.default_data = app_data_default}},
{.submodule_id = APP_GSDML_SUBMOD_ID_DIGITAL_OUT_1x800,
.fpga_config =
{.address = APP_FPGA_ADDRESS_DIGITAL_OUT_1x800,
.default_data = app_data_default}},
{.submodule_id = APP_GSDML_SUBMOD_ID_DIGITAL_IN_PORT_A,
.fpga_config =
{.address = APP_FPGA_ADDRESS_DIGITAL_IN_PORT_A,
.default_data = app_data_default}},
{.submodule_id = APP_GSDML_SUBMOD_ID_DIGITAL_OUT_PORT_A,
.fpga_config =
{.address = APP_FPGA_ADDRESS_DIGITAL_OUT_PORT_A,
.default_data = app_data_default}},
};
submodule_t submodules[APP_GSDML_MAX_SUBMODULES] = {0};
/**
* Get a submodule using its submodule id
* @param id In: Submodule id
* @return Reference to submodule. NULL if id is not found.
**/
static submodule_t * get_submodule_by_id (uint32_t id)
{
uint16_t i;
for (i = 0; i < NELEMENTS (submodules); i++)
{
if (submodules[i].info != NULL && submodules[i].info->id == id)
{
return &submodules[i];
}
}
return NULL;
}
/**
* Get submodule fpga config using its id.
* @param id In: Submodule id
* @return Reference to fgpa mapping. NULL if id is not found.
**/
static const submodule_fpga_config_t * get_fpga_config_by_id (uint32_t id)
{
uint32_t i = 0;
while (i < NELEMENTS (fpga))
{
if (fpga[i].submodule_id == id)
{
return &fpga[i].fpga_config;
}
i++;
}
return NULL;
}
int app_data_get_fpga_info_by_id (
uint32_t id,
uint16_t * address,
const uint8_t ** default_data)
{
const submodule_fpga_config_t * config = get_fpga_config_by_id (id);
if (config != NULL)
{
*address = config->address;
*default_data = config->default_data;
return 0;
}
return -1;
}
/**
* Allocate a submodule from submodules array.
* The returned submodule is uninitialized.
* @return Reference to submodule. NULL if allocation fails.
**/
static submodule_t * alloc_submodule (void)
{
uint32_t i = 0;
while (i < NELEMENTS (submodules))
{
if (submodules[i].info == NULL)
{
return &submodules[i];
}
i++;
}
return NULL;
}
/**
* Initialize submodule
* Set module and submodule configurations.
* Configure shared memory regions.
* Allocate buffers.
* Will generate assert on failure.
* @param module_cfg In: Module configuration
* @param submodule_cfg In: Submodule configuration
* @param app_mode In: Application mode
**/
static void app_data_init_submodule (
const app_gsdml_module_t * module_cfg,
const app_gsdml_submodule_t * submodule_cfg,
app_mode_t app_mode)
{
submodule_t * submodule;
CC_ASSERT (module_cfg != NULL);
CC_ASSERT (submodule_cfg != NULL);
submodule = alloc_submodule();
CC_ASSERT (submodule != NULL);
submodule->info = submodule_cfg;
if (app_mode == MODE_HW_OFFLOAD_FULL)
{
submodule->fpga_config = get_fpga_config_by_id (submodule_cfg->id);
CC_ASSERT (submodule->fpga_config != NULL);
}
else
{
submodule->fpga_config = NULL;
}
if (submodule->info->insize > 0)
{
submodule->in_data = malloc (submodule->info->insize);
CC_ASSERT (submodule->in_data != NULL);
if (app_mode != MODE_HW_OFFLOAD_FULL)
{
submodule->indata_shm = app_shm_create_input (
submodule->info->name,
module_cfg->fixed_slot,
submodule_cfg->fixed_subslot,
submodule->info->insize);
CC_ASSERT (submodule->indata_shm != NULL);
}
else
{
APP_LOG_INFO (
"[%u,%u,\"%s\"] mapped to FPGA address 0x%x\n",
module_cfg->fixed_slot,
submodule_cfg->fixed_subslot,
submodule->info->name,
submodule->fpga_config->address);
}
}
if (submodule->info->outsize > 0)
{
submodule->out_data = malloc (submodule->info->outsize);
CC_ASSERT (submodule->out_data != NULL);
if (app_mode != MODE_HW_OFFLOAD_FULL)
{
submodule->outdata_shm = app_shm_create_output (
submodule->info->name,
module_cfg->fixed_slot,
submodule_cfg->fixed_subslot,
submodule->info->outsize);
CC_ASSERT (submodule->outdata_shm != NULL);
}
else
{
APP_LOG_INFO (
"[%u,%u,\"%s\"] mapped to FPGA address 0x%x\n",
module_cfg->fixed_slot,
submodule_cfg->fixed_subslot,
submodule->info->name,
submodule->fpga_config->address);
}
}
}
int app_data_init (app_mode_t app_mode)
{
uint16_t i, j;
const app_gsdml_module_t * module_cfg;
const app_gsdml_submodule_t * submodule_cfg;
uint32_t modules_array_len;
const app_gsdml_module_t ** app_gsdml_modules;
app_gsdml_modules = app_gsdml_get_modules (&modules_array_len);
for (i = 0; i < modules_array_len; i++)
{
module_cfg = app_gsdml_modules[i];
j = 0;
while (module_cfg->submodules[j] != 0)
{
submodule_cfg =
app_gsdml_get_submodule_cfg (module_cfg->submodules[j]);
app_data_init_submodule (module_cfg, submodule_cfg, app_mode);
j++;
}
}
return 0;
}
uint8_t * app_data_get_input_data (
uint32_t submodule_id,
uint16_t * size,
uint8_t * iops)
{
int error = -1;
submodule_t * submodule;
if (size == NULL || iops == NULL)
{
return NULL;
}
submodule = get_submodule_by_id (submodule_id);
if (submodule == NULL)
{
/* Handle unsupported submodules part of process data */
*iops = PNET_IOXS_BAD;
return NULL;
}
*size = submodule->info->insize;
if (submodule->fpga_config == NULL)
{
error = app_shm_read (submodule->indata_shm, submodule->in_data, *size);
CC_ASSERT (error == 0);
}
else
{
/* Input is mapped to FPGA. Data is not available to
* application. Use default value set status.
* P-Net needs an initial value during initialization.
*/
memcpy (submodule->in_data, submodule->fpga_config->default_data, *size);
}
*iops = PNET_IOXS_GOOD;
return submodule->in_data;
}
uint8_t * app_data_get_output_data_buffer (
uint32_t submodule_id,
uint16_t * size)
{
submodule_t * submodule = get_submodule_by_id (submodule_id);
CC_ASSERT (size != NULL);
if (submodule == NULL)
{
*size = 0;
return NULL;
}
*size = submodule->info->outsize;
return submodule->out_data;
}
int app_data_set_output_data (
uint32_t submodule_id,
uint8_t * data,
uint16_t size)
{
int error = 0;
submodule_t * submodule = get_submodule_by_id (submodule_id);
if (data == NULL || submodule == NULL || size == 0)
{
return -1;
}
if (submodule->fpga_config == NULL)
{
/* Only write shared memory if output is not mapped
* to FPGA.
*/
error = app_shm_write (submodule->outdata_shm, data, size);
}
return error;
}
int app_data_set_default_outputs (void)
{
uint16_t i;
for (i = 0; i < NELEMENTS (submodules); i++)
{
if (submodules[i].outdata_shm != NULL)
{
if (submodules[i].fpga_config == NULL)
{
/* Only write shared memory if output is not mapped
* to FPGA.
*/
app_shm_reset (submodules[i].outdata_shm);
}
}
}
return 0;
}

View File

@ -0,0 +1,121 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* www.rt-labs.com
* Copyright 2021 rt-labs AB, Sweden.
*
* This software is dual-licensed under GPLv3 and a commercial
* license. See the file LICENSE.md distributed with this software for
* full license information.
********************************************************************/
#ifndef APP_DATA_H
#define APP_DATA_H
#include "sampleapp_common.h"
/**
* @file
* @brief Sample application data interface
*
* Functions for:
* - Getting input data
* - Setting ouput data
* - Setting default output state
*
* In this LAN9662 sample application the submodule data
* is mapped to shared memory found in /dev/shm on Linux.
*/
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdbool.h>
/**
* Initialize data submodules.
* During the initialization the shared memory areas
* supported by the application are created and the
* mapping between the shared memory and the submodules
* is configured.
* @param app_mode In: Application mode
* @return 0 on success, -1 on error
*/
int app_data_init (app_mode_t app_mode);
/**
* Get PNIO input data using submodule id.
* When this function is called the mapped shared
* memory is copied to the submodule input buffer.
* A reference to the the input buffer is returned.
* @param submodule_id In: Submodule id
* @param size Out: Size of pnio data
* @param iops Out: Provider status. If for example
* a sensor is failing or a short
* circuit detected on digital
* input this shall be set to BAD.
* Set to BAD for unsupported submodules
* or data can not be provided.
* @return Reference to PNIO data, NULL on error
*/
uint8_t * app_data_get_input_data (
uint32_t submodule_id,
uint16_t * size,
uint8_t * iops);
/**
* Get FPGA data start address for submodule data
* @param id In: Submodule id
* @param address Out: FPGA start address
* @param default_data Out: Submodule default data
* @return 0 on success, -1 on error
*/
int app_data_get_fpga_info_by_id (
uint32_t id,
uint16_t * address,
const uint8_t ** default_data);
/**
* Get PNIO output data buffer using submodule id.
* @param submodule_id In: Submodule id
* @param size Out: Size of pnio data. If submodule
* is not supported size is set to 0.
* @return Reference to PNIO data, NULL on error
*/
uint8_t * app_data_get_output_data_buffer (
uint32_t submodule_id,
uint16_t * size);
/**
* Set PNIO output data using module id.
* When this operation is called the data is
* written the mapped shared memory.
* @param submodule_id In: Submodule id
* @param data In: Reference to output data
* @param size In: Length of output data
* @return 0 on success, -1 on error
*/
int app_data_set_output_data (
uint32_t submodule_id,
uint8_t * data,
uint16_t size);
/**
* Set default values for all outputs.
* When this operation is called the mapped
* shared memory is set to 0.
* @return 0 on success, -1 on error
*/
int app_data_set_default_outputs (void);
#ifdef __cplusplus
}
#endif
#endif /* APP_DATA_H */

View File

@ -0,0 +1,394 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* www.rt-labs.com
* Copyright 2021 rt-labs AB, Sweden.
*
* This software is dual-licensed under GPLv3 and a commercial
* license. See the file LICENSE.md distributed with this software for
* full license information.
********************************************************************/
/**
* @file
* @brief Device properties defined by the GSDML device definition
*
* Functions for getting module, submodule and parameter
* configurations using their IDs.
*
* Important:
* Any change in this file may require an update of the GSDML file.
* Note that when the GSDML file is updated it has to be reloaded
* in your Profinet engineering tool. PLC applications may be affected.
*
* Design requires unique submodule IDs.
*/
#include "sampleapp_common.h"
#include "app_utils.h"
#include "app_gsdml.h"
#include "app_log.h"
#include "osal.h"
#include "pnal.h"
#include <pnet_api.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/************************* Modules ***************************/
static const app_gsdml_module_t dap_1 = {
.id = PNET_MOD_DAP_IDENT,
.name = "DAP 1",
.fixed_slot = 0,
.submodules = {
PNET_SUBMOD_DAP_IDENT,
PNET_SUBMOD_DAP_INTERFACE_1_IDENT,
PNET_SUBMOD_DAP_INTERFACE_1_PORT_1_IDENT,
PNET_SUBMOD_DAP_INTERFACE_1_PORT_2_IDENT,
0}};
static const app_gsdml_module_t module_di_1x8 = {
.id = APP_GSDML_MOD_ID_DIGITAL_IN_1x8,
.name = "DI 1x8",
.fixed_slot = 1,
.submodules = {APP_GSDML_SUBMOD_ID_DIGITAL_IN_1x8, 0},
};
static const app_gsdml_module_t module_do_1x8 = {
.id = APP_GSDML_MOD_ID_DIGITAL_OUT_1x8,
.name = "DO 1x8",
.fixed_slot = 2,
.submodules = {APP_GSDML_SUBMOD_ID_DIGITAL_OUT_1x8, 0}};
static const app_gsdml_module_t module_di_1x64 = {
.id = APP_GSDML_MOD_ID_DIGITAL_IN_1x64,
.name = "DI 1x64",
.fixed_slot = 3,
.submodules = {APP_GSDML_SUBMOD_ID_DIGITAL_IN_1x64, 0},
};
static const app_gsdml_module_t module_di_2x32a = {
.id = APP_GSDML_MOD_ID_DIGITAL_IN_2x32a,
.name = "DI 2x32a",
.fixed_slot = 4,
.submodules = {APP_GSDML_SUBMOD_ID_DIGITAL_IN_2x32a, 0},
};
static const app_gsdml_module_t module_di_2x32b = {
.id = APP_GSDML_MOD_ID_DIGITAL_IN_2x32b,
.name = "DI 2x32b",
.fixed_slot = 5,
.submodules = {APP_GSDML_SUBMOD_ID_DIGITAL_IN_2x32b, 0},
};
static const app_gsdml_module_t module_di_1x800 = {
.id = APP_GSDML_MOD_ID_DIGITAL_IN_1x800,
.name = "DI 1x800",
.fixed_slot = 6,
.submodules = {APP_GSDML_SUBMOD_ID_DIGITAL_IN_1x800, 0},
};
static const app_gsdml_module_t module_do_1x64 = {
.id = APP_GSDML_MOD_ID_DIGITAL_OUT_1x64,
.name = "DO 1x64",
.fixed_slot = 7,
.submodules = {APP_GSDML_SUBMOD_ID_DIGITAL_OUT_1x64, 0},
};
static const app_gsdml_module_t module_do_2x32a = {
.id = APP_GSDML_MOD_ID_DIGITAL_OUT_2x32a,
.name = "DO 2x32a",
.fixed_slot = 8,
.submodules = {APP_GSDML_SUBMOD_ID_DIGITAL_OUT_2x32a, 0},
};
static const app_gsdml_module_t module_do_2x32b = {
.id = APP_GSDML_MOD_ID_DIGITAL_OUT_2x32b,
.name = "DO 2x32b",
.fixed_slot = 9,
.submodules = {APP_GSDML_SUBMOD_ID_DIGITAL_OUT_2x32b, 0},
};
static const app_gsdml_module_t module_do_1x800 = {
.id = APP_GSDML_MOD_ID_DIGITAL_OUT_1x800,
.name = "DO 1x800",
.fixed_slot = 10,
.submodules = {APP_GSDML_SUBMOD_ID_DIGITAL_OUT_1x800, 0},
};
static const app_gsdml_module_t module_di_port_a = {
.id = APP_GSDML_MOD_ID_DIGITAL_IN_PORT_A,
.name = "DI Port A",
.fixed_slot = 11,
.submodules = {APP_GSDML_SUBMOD_ID_DIGITAL_IN_PORT_A, 0},
};
static const app_gsdml_module_t module_do_port_a = {
.id = APP_GSDML_MOD_ID_DIGITAL_OUT_PORT_A,
.name = "DO Port A",
.fixed_slot = 12,
.submodules = {APP_GSDML_SUBMOD_ID_DIGITAL_OUT_PORT_A, 0},
};
/************************* Submodules ***************************/
static const app_gsdml_submodule_t dap_identity_1 = {
.name = "DAP Identity 1",
.api = APP_GSDML_API,
.id = PNET_SUBMOD_DAP_IDENT,
.fixed_subslot = PNET_SUBSLOT_DAP_IDENT,
.data_dir = PNET_DIR_NO_IO,
.insize = 0,
.outsize = 0,
.parameters = {0}};
static const app_gsdml_submodule_t dap_interface_1 = {
.name = "DAP Interface 1",
.api = APP_GSDML_API,
.id = PNET_SUBMOD_DAP_INTERFACE_1_IDENT,
.fixed_subslot = PNET_SUBSLOT_DAP_INTERFACE_1_IDENT,
.data_dir = PNET_DIR_NO_IO,
.insize = 0,
.outsize = 0,
.parameters = {0}};
static const app_gsdml_submodule_t dap_port_1 = {
.name = "DAP Port 1",
.api = APP_GSDML_API,
.id = PNET_SUBMOD_DAP_INTERFACE_1_PORT_1_IDENT,
.fixed_subslot = PNET_SUBSLOT_DAP_INTERFACE_1_PORT_1_IDENT,
.data_dir = PNET_DIR_NO_IO,
.insize = 0,
.outsize = 0,
.parameters = {0}};
static const app_gsdml_submodule_t dap_port_2 = {
.name = "DAP Port 2",
.api = APP_GSDML_API,
.id = PNET_SUBMOD_DAP_INTERFACE_1_PORT_2_IDENT,
.fixed_subslot = PNET_SUBSLOT_DAP_INTERFACE_1_PORT_2_IDENT,
.data_dir = PNET_DIR_NO_IO,
.insize = 0,
.outsize = 0,
.parameters = {0}};
static const app_gsdml_submodule_t dap_port_3 = {
.name = "DAP Port 3",
.api = APP_GSDML_API,
.id = PNET_SUBMOD_DAP_INTERFACE_1_PORT_3_IDENT,
.fixed_subslot = PNET_SUBSLOT_DAP_INTERFACE_1_PORT_3_IDENT,
.data_dir = PNET_DIR_NO_IO,
.insize = 0,
.outsize = 0,
.parameters = {0}};
static const app_gsdml_submodule_t dap_port_4 = {
.name = "DAP Port 4",
.api = APP_GSDML_API,
.id = PNET_SUBMOD_DAP_INTERFACE_1_PORT_4_IDENT,
.fixed_subslot = PNET_SUBSLOT_DAP_INTERFACE_1_PORT_4_IDENT,
.data_dir = PNET_DIR_NO_IO,
.insize = 0,
.outsize = 0,
.parameters = {0}};
static const app_gsdml_submodule_t submodule_di_1x8 = {
.id = APP_GSDML_SUBMOD_ID_DIGITAL_IN_1x8,
.fixed_subslot = 1,
.name = "Digital Input 1x8",
.api = APP_GSDML_API,
.data_dir = PNET_DIR_INPUT,
.insize = APP_GSDML_SIZE_DIGITAL_IN_1x8,
.outsize = 0,
.parameters = {0}};
static const app_gsdml_submodule_t submodule_do_1x8 = {
.id = APP_GSDML_SUBMOD_ID_DIGITAL_OUT_1x8,
.fixed_subslot = 1,
.name = "Digital Output 1x8",
.api = APP_GSDML_API,
.data_dir = PNET_DIR_OUTPUT,
.insize = 0,
.outsize = APP_GSDML_SIZE_DIGITAL_OUT_1x8,
.parameters = {0}};
static const app_gsdml_submodule_t submodule_di_1x64 = {
.id = APP_GSDML_SUBMOD_ID_DIGITAL_IN_1x64,
.fixed_subslot = 1,
.name = "Digital Input 1x64",
.api = APP_GSDML_API,
.data_dir = PNET_DIR_INPUT,
.insize = APP_GSDML_SIZE_DIGITAL_IN_1x64,
.outsize = 0,
.parameters = {0}};
static const app_gsdml_submodule_t submodule_di_2x32a = {
.id = APP_GSDML_SUBMOD_ID_DIGITAL_IN_2x32a,
.fixed_subslot = 1,
.name = "Digital Input 2x32 a",
.api = APP_GSDML_API,
.data_dir = PNET_DIR_INPUT,
.insize = 8,
.outsize = 0,
.parameters = {0}};
static const app_gsdml_submodule_t submodule_di_2x32b = {
.id = APP_GSDML_SUBMOD_ID_DIGITAL_IN_2x32b,
.fixed_subslot = 1,
.name = "Digital Input 2x32 b",
.api = APP_GSDML_API,
.data_dir = PNET_DIR_INPUT,
.insize = APP_GSDML_SIZE_DIGITAL_IN_2x32b,
.outsize = 0,
.parameters = {0}};
static const app_gsdml_submodule_t submodule_di_1x800 = {
.id = APP_GSDML_SUBMOD_ID_DIGITAL_IN_1x800,
.fixed_subslot = 1,
.name = "Digital Input 1x800",
.api = APP_GSDML_API,
.data_dir = PNET_DIR_INPUT,
.insize = APP_GSDML_SIZE_DIGITAL_IN_1x800,
.outsize = 0,
.parameters = {0}};
static const app_gsdml_submodule_t submodule_do_1x64 = {
.id = APP_GSDML_SUBMOD_ID_DIGITAL_OUT_1x64,
.fixed_subslot = 1,
.name = "Digital Output 1x64",
.api = APP_GSDML_API,
.data_dir = PNET_DIR_OUTPUT,
.insize = 0,
.outsize = APP_GSDML_SIZE_DIGITAL_OUT_1x64,
.parameters = {0}};
static const app_gsdml_submodule_t submodule_do_2x32a = {
.id = APP_GSDML_SUBMOD_ID_DIGITAL_OUT_2x32a,
.fixed_subslot = 1,
.name = "Digital Output 2x32 a",
.api = APP_GSDML_API,
.data_dir = PNET_DIR_OUTPUT,
.insize = 0,
.outsize = APP_GSDML_SIZE_DIGITAL_OUT_2x32a,
.parameters = {0}};
static const app_gsdml_submodule_t submodule_do_2x32b = {
.id = APP_GSDML_SUBMOD_ID_DIGITAL_OUT_2x32b,
.fixed_subslot = 1,
.name = "Digital Output 2x32 b",
.api = APP_GSDML_API,
.data_dir = PNET_DIR_OUTPUT,
.insize = 0,
.outsize = APP_GSDML_SIZE_DIGITAL_OUT_2x32b,
.parameters = {0}};
static const app_gsdml_submodule_t submodule_do_1x800 = {
.id = APP_GSDML_SUBMOD_ID_DIGITAL_OUT_1x800,
.fixed_subslot = 1,
.name = "Digital Output 1x800",
.api = APP_GSDML_API,
.data_dir = PNET_DIR_OUTPUT,
.insize = 0,
.outsize = APP_GSDML_SIZE_DIGITAL_OUT_1x800,
.parameters = {0}};
static const app_gsdml_submodule_t submodule_di_port_a = {
.id = APP_GSDML_SUBMOD_ID_DIGITAL_IN_PORT_A,
.fixed_subslot = 1,
.name = "Digital Input Port A",
.api = APP_GSDML_API,
.data_dir = PNET_DIR_INPUT,
.insize = APP_GSDML_SIZE_DIGITAL_IN_PORT_A,
.outsize = 0,
.parameters = {0}};
static const app_gsdml_submodule_t submodule_do_port_a = {
.id = APP_GSDML_SUBMOD_ID_DIGITAL_OUT_PORT_A,
.fixed_subslot = 1,
.name = "Digital Output Port A",
.api = APP_GSDML_API,
.data_dir = PNET_DIR_OUTPUT,
.insize = 0,
.outsize = APP_GSDML_SIZE_DIGITAL_OUT_PORT_A,
.parameters = {0}};
/* List of supported modules */
static const app_gsdml_module_t * app_gsdml_modules[] = {
&dap_1,
&module_di_1x8,
&module_do_1x8,
&module_di_1x64,
&module_di_2x32a,
&module_di_2x32b,
&module_di_1x800,
&module_do_1x64,
&module_do_2x32a,
&module_do_2x32b,
&module_do_1x800,
&module_di_port_a,
&module_do_port_a};
/* List of supported submodules */
static const app_gsdml_submodule_t * app_gsdml_submodules[] = {
&dap_identity_1,
&dap_interface_1,
&dap_port_1,
&dap_port_2,
&dap_port_3,
&dap_port_4,
&submodule_di_1x8,
&submodule_do_1x8,
&submodule_di_1x64,
&submodule_di_2x32a,
&submodule_di_2x32b,
&submodule_di_1x800,
&submodule_do_1x64,
&submodule_do_2x32a,
&submodule_do_2x32b,
&submodule_do_1x800,
&submodule_di_port_a,
&submodule_do_port_a};
const app_gsdml_module_t * app_gsdml_get_module_cfg (uint32_t id)
{
uint32_t i;
for (i = 0; i < NELEMENTS (app_gsdml_modules); i++)
{
if (app_gsdml_modules[i]->id == id)
{
return app_gsdml_modules[i];
}
}
return NULL;
}
const app_gsdml_module_t ** app_gsdml_get_modules (uint32_t * array_len)
{
*array_len = NELEMENTS (app_gsdml_modules);
return app_gsdml_modules;
}
const app_gsdml_submodule_t ** app_gsdml_get_submodules (uint32_t * array_len)
{
*array_len = NELEMENTS (app_gsdml_submodules);
return app_gsdml_submodules;
}
const app_gsdml_submodule_t * app_gsdml_get_submodule_cfg (uint32_t id)
{
uint32_t i;
for (i = 0; i < NELEMENTS (app_gsdml_submodules); i++)
{
if (app_gsdml_submodules[i]->id == id)
{
return app_gsdml_submodules[i];
}
}
return NULL;
}

View File

@ -0,0 +1,199 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* www.rt-labs.com
* Copyright 2021 rt-labs AB, Sweden.
*
* This software is dual-licensed under GPLv3 and a commercial
* license. See the file LICENSE.md distributed with this software for
* full license information.
********************************************************************/
#ifndef APP_GSDML_H
#define APP_GSDML_H
/**
* @file
* @brief Device properties defined by the GSDML device definition
*
* Functions for getting module, submodule and parameter
* configurations using their IDs.
*
* Important:
* Any change in this file may require an update of the GSDML file.
* Note that when the GSDML file is updated it has to be reloaded
* in your Profinet engineering tool. PLC applications may be affected.
*
* Design requires unique submodule IDs.
*/
#ifdef __cplusplus
extern "C" {
#endif
#include <pnet_api.h>
#define APP_GSDML_API 0
#define APP_GSDML_DEFAULT_STATION_NAME "lan9662-dev"
/* GSDML tag: VendorID */
#define APP_GSDML_VENDOR_ID 0x0493
/* GSDML tag: DeviceID */
#define APP_GSDML_DEVICE_ID 0x9662
/* Allowed: 'V', 'R', 'P', 'U', 'T' */
#define APP_GSDML_SW_REV_PREFIX 'V'
#define APP_GSDML_PROFILE_ID 0x1234
#define APP_GSDML_PROFILE_SPEC_TYPE 0x5678
/* GSDML tag: Writeable_IM_Records */
#define APP_GSDML_IM_SUPPORTED \
(PNET_SUPPORTED_IM1 | PNET_SUPPORTED_IM2 | PNET_SUPPORTED_IM3)
/* GSDML tag: OrderNumber */
#define APP_GSDML_ORDER_ID "12345 Abcdefghijk"
/* GSDML tag: ModuleInfo / Name */
#define APP_GSDML_PRODUCT_NAME "P-Net LAN9662 Sample"
/* GSDML tag: MinDeviceInterval */
#define APP_GSDML_MIN_DEVICE_INTERVAL 32 /* 1ms */
#define APP_GSDML_DIAG_CUSTOM_USI 0x1234
/* See "Specification for GSDML" 8.26 LogBookEntryItem for allowed values */
#define APP_GSDML_LOGBOOK_ERROR_CODE 0x20 /* Manufacturer specific */
#define APP_GSDML_LOGBOOK_ERROR_DECODE 0x82 /* Manufacturer specific */
#define APP_GSDML_LOGBOOK_ERROR_CODE_1 PNET_ERROR_CODE_1_FSPM
#define APP_GSDML_LOGBOOK_ERROR_CODE_2 0x00 /* Manufacturer specific */
#define APP_GSDML_LOGBOOK_ENTRY_DETAIL 0xFEE1DEAD /* Manufacturer specific */
#define APP_GSDML_MAX_SUBMODULES 20
#define APP_GSDML_ALARM_PAYLOAD_SIZE 1 /* bytes */
#define APP_GSDML_MOD_ID_DIGITAL_IN_1x8 0x00001001
#define APP_GSDML_MOD_ID_DIGITAL_OUT_1x8 0x00001002
#define APP_GSDML_MOD_ID_DIGITAL_IN_1x64 0x00001003
#define APP_GSDML_MOD_ID_DIGITAL_IN_2x32a 0x00001004
#define APP_GSDML_MOD_ID_DIGITAL_IN_2x32b 0x00001005
#define APP_GSDML_MOD_ID_DIGITAL_IN_1x800 0x00001006
#define APP_GSDML_MOD_ID_DIGITAL_OUT_1x64 0x00001007
#define APP_GSDML_MOD_ID_DIGITAL_OUT_2x32a 0x00001008
#define APP_GSDML_MOD_ID_DIGITAL_OUT_2x32b 0x00001009
#define APP_GSDML_MOD_ID_DIGITAL_OUT_1x800 0x0000100A
#define APP_GSDML_MOD_ID_DIGITAL_IN_PORT_A 0x0000100B
#define APP_GSDML_MOD_ID_DIGITAL_OUT_PORT_A 0x0000100C
#define APP_GSDML_SUBMOD_ID_DIGITAL_IN_1x8 0x00002001
#define APP_GSDML_SUBMOD_ID_DIGITAL_OUT_1x8 0x00002002
#define APP_GSDML_SUBMOD_ID_DIGITAL_IN_1x64 0x00002003
#define APP_GSDML_SUBMOD_ID_DIGITAL_IN_2x32a 0x00002004
#define APP_GSDML_SUBMOD_ID_DIGITAL_IN_2x32b 0x00002005
#define APP_GSDML_SUBMOD_ID_DIGITAL_IN_1x800 0x00002006
#define APP_GSDML_SUBMOD_ID_DIGITAL_OUT_1x64 0x00002007
#define APP_GSDML_SUBMOD_ID_DIGITAL_OUT_2x32a 0x00002008
#define APP_GSDML_SUBMOD_ID_DIGITAL_OUT_2x32b 0x00002009
#define APP_GSDML_SUBMOD_ID_DIGITAL_OUT_1x800 0x0000200A
#define APP_GSDML_SUBMOD_ID_DIGITAL_IN_PORT_A 0x0000200B
#define APP_GSDML_SUBMOD_ID_DIGITAL_OUT_PORT_A 0x0000200C
#define APP_GSDML_SIZE_DIGITAL_IN_1x8 1
#define APP_GSDML_SIZE_DIGITAL_OUT_1x8 1
#define APP_GSDML_SIZE_DIGITAL_IN_1x64 8
#define APP_GSDML_SIZE_DIGITAL_IN_2x32a 8
#define APP_GSDML_SIZE_DIGITAL_IN_2x32b 8
#define APP_GSDML_SIZE_DIGITAL_IN_1x800 100
#define APP_GSDML_SIZE_DIGITAL_OUT_1x64 8
#define APP_GSDML_SIZE_DIGITAL_OUT_2x32a 8
#define APP_GSDML_SIZE_DIGITAL_OUT_2x32b 8
#define APP_GSDML_SIZE_DIGITAL_OUT_1x800 100
#define APP_GSDML_SIZE_DIGITAL_IN_PORT_A 4
#define APP_GSDML_SIZE_DIGITAL_OUT_PORT_A 4
#define APP_GSDML_MAX_SUBMODULE_DATA_SIZE 100
/* TODO support virtual and pluggable submodules. */
typedef struct cfg_module
{
uint32_t id;
const char * name;
uint32_t fixed_slot; /* Set to UINT32_MAX if not fixed */
uint32_t submodules[]; /* Variable length, ends with 0*/
} app_gsdml_module_t;
typedef struct app_gsdml_submodule
{
uint32_t id;
const char * name;
uint32_t api;
uint32_t fixed_subslot; /* Set to UINT32_MAX if not fixed */
pnet_submodule_dir_t data_dir;
uint16_t insize;
uint16_t outsize;
uint16_t parameters[]; /* Variable length, ends with 0 */
} app_gsdml_submodule_t;
typedef struct app_gsdml_param
{
uint32_t index;
const char * name;
uint16_t length;
} app_gsdml_param_t;
/**
* Get array of supported modules
* @param array_len Out: Length of array
* @return Modules array
*/
const app_gsdml_module_t ** app_gsdml_get_modules (uint32_t * array_len);
/**
* Get array of supported submodules
* @param array_len Out: Length of array
* @return Submodules array
*/
const app_gsdml_submodule_t ** app_gsdml_get_submodules (uint32_t * array_len);
/**
* Get array of supported parameters
* @param array_len Out: Length of array
* @return Parameters array
*/
const app_gsdml_param_t ** app_gsdml_get_parameters (uint32_t * array_len);
/**
* Get module configuration from module id
* @param module_id In: Module id
* @return Module configuration, NULL if not found
*/
const app_gsdml_module_t * app_gsdml_get_module_cfg (uint32_t module_id);
/**
* Get submodule module configuration from submodule id
* @param submodule_id In: Submodule id
* @return Submodule configuration, NULL if not found
*/
const app_gsdml_submodule_t * app_gsdml_get_submodule_cfg (
uint32_t submodule_id);
/**
* Get parameter configuration from parameter index
* @param submodule_id In: Submodule id
* @param index In: Parameters index
* @return Parameter configuration, NULL if not found
*/
const app_gsdml_param_t * app_gsdml_get_parameter_cfg (
uint32_t submodule_id,
uint32_t index);
#ifdef __cplusplus
}
#endif
#endif /* APP_GSDML_H */

View File

@ -0,0 +1,52 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* www.rt-labs.com
* Copyright 2021 rt-labs AB, Sweden.
*
* This software is dual-licensed under GPLv3 and a commercial
* license. See the file LICENSE.md distributed with this software for
* full license information.
********************************************************************/
#include "app_log.h"
#include <stdarg.h>
#include <stdio.h>
static int32_t log_level = APP_DEFAULT_LOG_LEVEL;
void app_log_set_log_level (int32_t level)
{
log_level = level;
}
void app_log (int32_t level, const char * fmt, ...)
{
va_list list;
if (level >= log_level)
{
va_start (list, fmt);
vprintf (fmt, list);
va_end (list);
fflush (stdout);
}
}
void app_log_print_bytes (int32_t level, const uint8_t * bytes, uint32_t len)
{
if (level >= log_level)
{
printf (" Bytes: ");
for (uint32_t i = 0; i < len; i++)
{
printf ("%02X ", bytes[i]);
}
printf ("\n");
}
}

View File

@ -0,0 +1,75 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* www.rt-labs.com
* Copyright 2021 rt-labs AB, Sweden.
*
* This software is dual-licensed under GPLv3 and a commercial
* license. See the file LICENSE.md distributed with this software for
* full license information.
********************************************************************/
#ifndef APP_LOG_H
#define APP_LOG_H
/**
* @file
* @brief Application debug log utility
*
* Runtime configurable debug log using printf()
* Levels matches levels used in P-Net.
*/
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#define APP_LOG_LEVEL_DEBUG 0x00
#define APP_LOG_LEVEL_INFO 0x01
#define APP_LOG_LEVEL_WARNING 0x02
#define APP_LOG_LEVEL_ERROR 0x03
#define APP_LOG_LEVEL_FATAL 0x04
#define APP_DEFAULT_LOG_LEVEL APP_LOG_LEVEL_FATAL
#define APP_LOG(level, ...) app_log (level, __VA_ARGS__)
#define APP_LOG_DEBUG(...) APP_LOG (APP_LOG_LEVEL_DEBUG, __VA_ARGS__)
#define APP_LOG_INFO(...) APP_LOG (APP_LOG_LEVEL_INFO, __VA_ARGS__)
#define APP_LOG_WARNING(...) APP_LOG (APP_LOG_LEVEL_WARNING, __VA_ARGS__)
#define APP_LOG_ERROR(...) APP_LOG (APP_LOG_LEVEL_ERROR, __VA_ARGS__)
#define APP_LOG_FATAL(...) APP_LOG (APP_LOG_LEVEL_FATAL, __VA_ARGS__)
/**
* Print log message depending on level
* Use the APP_LOG_xxxxx macros instead of this function.
* @param level In: Message log level
* @param fmt In: Log message format string
*/
void app_log (int32_t level, const char * fmt, ...);
/**
* Log an array of bytes
* @param level In: Log level
* @param bytes In: Array of bytes
* @param length In: Length of array
*/
void app_log_print_bytes (int32_t level, const uint8_t * bytes, uint32_t length);
/**
* Set log level
* @param level In: Log level
*/
void app_log_set_log_level (int32_t level);
#ifdef __cplusplus
}
#endif
#endif /* APP_LOG_H */

View File

@ -0,0 +1,318 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* www.rt-labs.com
* Copyright 2021 rt-labs AB, Sweden.
*
* This software is dual-licensed under GPLv3 and a commercial
* license. See the file LICENSE.md distributed with this software for
* full license information.
********************************************************************/
#include "app_shm.h"
#include "app_log.h"
#include <semaphore.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <ctype.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define APP_SHM_MAX_FILE_NAME_LEN 40
typedef struct app_shm_t
{
char name[APP_SHM_MAX_FILE_NAME_LEN];
int fd;
caddr_t mem_address;
sem_t * sem;
size_t size;
} app_shm_t;
/**
* Format name string
*
* Replace space " " with "-"
* Convert uppercase characters to lowercase.
* @param name In: NULL terminated string
* @return 0 on success, -1 on error
**/
static int format_name (char * name)
{
uint32_t i;
if (name == NULL)
{
return -1;
}
for (i = 0; name[i] != '\0'; i++)
{
name[i] = tolower (name[i]);
if (name[i] == ' ')
{
name[i] = '_';
}
}
return 0;
}
/**
* Initialise an existing shared memory area handle
*
* @param handle In: Handle
* @param name In: Name
* @param slot In: Slot number
* @param subslot In: Subslot number
* @param size In: Size in bytes
* @param is_output In: True if the shared memory area is an output
* @return 0 on success, -1 on error
**/
static int app_shm_init (
app_shm_t * handle,
const char * name,
uint32_t slot,
uint32_t subslot,
uint32_t size,
bool is_output)
{
const int shm_flags = O_RDWR | O_CREAT;
if (handle == NULL || name == NULL || size == 0)
{
return -1;
}
memset (handle, 0, sizeof (app_shm_t));
/* Only include slot and subslot numbers if both module and
submodules are fixed in slot and subslot */
if (slot != UINT32_MAX && subslot != UINT32_MAX)
{
snprintf (
handle->name,
APP_SHM_MAX_FILE_NAME_LEN - 1,
"pnet-%s-%d-%d-%s",
is_output ? "out" : "in",
slot,
subslot,
name);
}
else
{
snprintf (
handle->name,
APP_SHM_MAX_FILE_NAME_LEN - 1,
"pnet-%s-%s",
is_output ? "out" : "in",
name);
}
format_name (handle->name);
handle->size = size;
handle->fd = shm_open (handle->name, shm_flags, 0666);
if (handle->fd < 0)
{
APP_LOG_ERROR (
"Failed to open shared memory\n"
" file name: %s\n"
" Input files is expected to be created by data generating "
" application\n",
handle->name);
return -1;
}
if (ftruncate (handle->fd, handle->size) != 0)
{
return -1;
}
handle->mem_address = mmap (
NULL,
handle->size,
PROT_READ | PROT_WRITE,
MAP_SHARED,
handle->fd,
0);
if (handle->mem_address == MAP_FAILED)
{
return -1;
}
handle->sem = sem_open (handle->name, O_CREAT, 0666, 1);
if (handle->sem == NULL)
{
return -1;
}
/* sem_unlink (handle->name); TODO */
APP_LOG_DEBUG ("shared memory area created\n");
APP_LOG_DEBUG (" name=%s\n", handle->name);
APP_LOG_DEBUG (" size=%lu\n", handle->size);
APP_LOG_DEBUG (" backing file = /dev/shm/%s\n", handle->name);
return 0;
}
app_shm_t * app_shm_create_input (
const char * name,
int slot,
int subslot,
int size)
{
app_shm_t * handle = (app_shm_t *)malloc (sizeof (app_shm_t));
if (handle != NULL)
{
if (app_shm_init (handle, name, slot, subslot, size, false) != 0)
{
app_shm_destroy (handle);
handle = NULL;
}
}
return handle;
}
app_shm_t * app_shm_create_output (
const char * name,
int slot,
int subslot,
int size)
{
app_shm_t * handle = (app_shm_t *)malloc (sizeof (app_shm_t));
if (handle != NULL)
{
if (app_shm_init (handle, name, slot, subslot, size, true) != 0)
{
app_shm_destroy (handle);
handle = NULL;
}
}
return handle;
}
/**
* Free resources related to a shared memory area.
*
* Does not free the memory allocated for the handle itself.
*
* @param handle In: Handle
**/
static void app_shm_free_resources (app_shm_t * handle)
{
if (handle == NULL)
{
return;
}
if (handle->mem_address != 0)
{
munmap (handle->mem_address, handle->size);
}
if (handle->fd >= 0)
{
close (handle->fd);
}
if (handle->sem != NULL)
{
sem_close (handle->sem);
/* sem_unlink TODO */
}
unlink (handle->name);
}
void app_shm_destroy (app_shm_t * handle)
{
if (handle != NULL)
{
app_shm_free_resources (handle);
free (handle);
}
}
int app_shm_read (app_shm_t * handle, void * data, size_t size)
{
if (handle == NULL || size == 0)
{
return -1;
}
if (sem_wait (handle->sem) != 0)
{
return -1;
}
memcpy (data, handle->mem_address, size);
if (sem_post (handle->sem) != 0)
{
return -1;
}
return 0;
}
int app_shm_write (app_shm_t * handle, void * data, size_t size)
{
if (handle == NULL || data == NULL || size > handle->size)
{
return -1;
}
if (sem_wait (handle->sem) != 0)
{
return -1;
}
memcpy (handle->mem_address, data, size);
if (sem_post (handle->sem) != 0)
{
return -1;
}
return 0;
}
int app_shm_reset (app_shm_t * handle)
{
if (handle == NULL)
{
return -1;
}
if (sem_wait (handle->sem) != 0)
{
return -1;
}
memset (handle->mem_address, 0, handle->size);
if (sem_post (handle->sem) != 0)
{
return -1;
}
return 0;
}

View File

@ -0,0 +1,104 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* www.rt-labs.com
* Copyright 2021 rt-labs AB, Sweden.
*
* This software is dual-licensed under GPLv3 and a commercial
* license. See the file LICENSE.md distributed with this software for
* full license information.
********************************************************************/
#ifndef APP_SHM_H
#define APP_SHM_H
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct app_shm_t app_shm_t;
/**
* Create a shared memory area used as input.
*
* Shared memory is read by this application.
* Intended for submodule input data.
* The slot and and subslot info is used to
* generate the name of the shared memory area.
*
* @param name In: Submodule id
* @param slot In: Slot number
* @param subslot In: Subslot number
* @param size In: Size of area
* @return Handle to created shared memory area. NULL on error.
*/
app_shm_t * app_shm_create_input (
const char * name,
int slot,
int subslot,
int size);
/**
* Create a shared memory area used as output.
*
* Shared memory is written by this application.
* Intended for submodule output data.
* The slot and and subslot info is used to
* generate the name of the shared memory area.
*
* @param name In: Submodule id
* @param slot In: Slot number
* @param subslot In: Subslot number
* @param size In: Size of area
* @return Handle to created shared memory area. NULL on error.
*/
app_shm_t * app_shm_create_output (
const char * name,
int slot,
int subslot,
int size);
/**
* Free the resources of a shared memory area
*
* @param handle In: Shared memory object handle
*/
void app_shm_destroy (app_shm_t * handle);
/**
* Read shared memory area into a buffer
*
* @param handle In: Shared memory object handle
* @param data In: Start of buffer
* @param size In: Number of bytes to read
* @return 0 on success, -1 on error
*/
int app_shm_read (app_shm_t * handle, void * data, size_t size);
/**
* Write to shared memory
*
* @param handle In: Shared memory object handle
* @param data In: Start of buffer
* @param size In: Number of bytes to write
* @return 0 on success, -1 on error
*/
int app_shm_write (app_shm_t * handle, void * data, size_t size);
/**
* Set shared memory to zero
*
* @param handle In: Shared memory object handle
* @return 0 on success, -1 on error
*/
int app_shm_reset (app_shm_t * handle);
#endif /* APP_SHM_H */

View File

@ -0,0 +1,722 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* www.rt-labs.com
* Copyright 2021 rt-labs AB, Sweden.
*
* This software is dual-licensed under GPLv3 and a commercial
* license. See the file LICENSE.md distributed with this software for
* full license information.
********************************************************************/
#define _GNU_SOURCE /* For asprintf() */
#include "app_utils.h"
#include "app_log.h"
#include "app_gsdml.h"
#include "sampleapp_common.h"
#include "osal.h"
#include "osal_log.h" /* For LOG_LEVEL */
#include "pnal.h"
#include <pnet_api.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define GET_HIGH_BYTE(id) ((id >> 8) & 0xFF)
#define GET_LOW_BYTE(id) (id & 0xFF)
void app_utils_ip_to_string (pnal_ipaddr_t ip, char * outputstring)
{
snprintf (
outputstring,
PNAL_INET_ADDRSTR_SIZE,
"%u.%u.%u.%u",
(uint8_t) ((ip >> 24) & 0xFF),
(uint8_t) ((ip >> 16) & 0xFF),
(uint8_t) ((ip >> 8) & 0xFF),
(uint8_t) (ip & 0xFF));
}
void app_utils_mac_to_string (pnet_ethaddr_t mac, char * outputstring)
{
snprintf (
outputstring,
PNAL_ETH_ADDRSTR_SIZE,
"%02X:%02X:%02X:%02X:%02X:%02X",
mac.addr[0],
mac.addr[1],
mac.addr[2],
mac.addr[3],
mac.addr[4],
mac.addr[5]);
}
const char * app_utils_submod_dir_to_string (pnet_submodule_dir_t direction)
{
const char * s = "<error>";
switch (direction)
{
case PNET_DIR_NO_IO:
s = "NO_IO";
break;
case PNET_DIR_INPUT:
s = "INPUT";
break;
case PNET_DIR_OUTPUT:
s = "OUTPUT";
break;
case PNET_DIR_IO:
s = "INPUT_OUTPUT";
break;
}
return s;
}
const char * app_utils_ioxs_to_string (pnet_ioxs_values_t ioxs)
{
const char * s = "<error>";
switch (ioxs)
{
case PNET_IOXS_BAD:
s = "BAD";
break;
case PNET_IOXS_GOOD:
s = "GOOD";
break;
}
return s;
}
char * app_utils_get_subslot_string (const app_subslot_t * subslot)
{
static char buf[40];
snprintf (
buf,
sizeof (buf),
"[%u,%u,\"%s\"]",
subslot->slot_nbr,
subslot->subslot_nbr,
subslot->submodule_name);
return buf;
}
void app_utils_get_error_code_strings (
uint16_t err_cls,
uint16_t err_code,
const char ** err_cls_str,
const char ** err_code_str)
{
if (err_cls_str == NULL || err_cls_str == NULL)
{
return;
}
*err_cls_str = "Not decoded";
*err_code_str = "Not decoded";
switch (err_cls)
{
case PNET_ERROR_CODE_1_RTA_ERR_CLS_PROTOCOL:
*err_cls_str = "Real-Time Acyclic Protocol";
switch (err_code)
{
case PNET_ERROR_CODE_2_ABORT_AR_CONSUMER_DHT_EXPIRED:
*err_code_str = "Device missed cyclic data "
"deadline, device terminated AR";
break;
case PNET_ERROR_CODE_2_ABORT_AR_CMI_TIMEOUT:
*err_code_str = "Communication initialization "
"timeout, device terminated AR";
break;
case PNET_ERROR_CODE_2_ABORT_AR_RELEASE_IND_RECEIVED:
*err_code_str = "AR release indication received";
break;
case PNET_ERROR_CODE_2_ABORT_DCP_STATION_NAME_CHANGED:
*err_code_str = "DCP station name changed, "
"device terminated AR";
break;
case PNET_ERROR_CODE_2_ABORT_DCP_RESET_TO_FACTORY:
*err_code_str = "DCP reset to factory or factory "
"reset, device terminated AR";
break;
}
break;
case PNET_ERROR_CODE_1_CTLDINA:
*err_cls_str = "CTLDINA = Name and IP assignment from controller";
switch (err_code)
{
case PNET_ERROR_CODE_2_CTLDINA_ARP_MULTIPLE_IP_ADDRESSES:
*err_code_str = "Multiple users of same IP address";
break;
}
break;
}
}
void app_utils_copy_ip_to_struct (
pnet_cfg_ip_addr_t * destination_struct,
pnal_ipaddr_t ip)
{
destination_struct->a = ((ip >> 24) & 0xFF);
destination_struct->b = ((ip >> 16) & 0xFF);
destination_struct->c = ((ip >> 8) & 0xFF);
destination_struct->d = (ip & 0xFF);
}
const char * app_utils_dcontrol_cmd_to_string (
pnet_control_command_t control_command)
{
const char * s = NULL;
switch (control_command)
{
case PNET_CONTROL_COMMAND_PRM_BEGIN:
s = "PRM_BEGIN";
break;
case PNET_CONTROL_COMMAND_PRM_END:
s = "PRM_END";
break;
case PNET_CONTROL_COMMAND_APP_RDY:
s = "APP_RDY";
break;
case PNET_CONTROL_COMMAND_RELEASE:
s = "RELEASE";
break;
default:
s = "<error>";
break;
}
return s;
}
const char * app_utils_event_to_string (pnet_event_values_t event)
{
const char * s = "<error>";
switch (event)
{
case PNET_EVENT_ABORT:
s = "PNET_EVENT_ABORT";
break;
case PNET_EVENT_STARTUP:
s = "PNET_EVENT_STARTUP";
break;
case PNET_EVENT_PRMEND:
s = "PNET_EVENT_PRMEND";
break;
case PNET_EVENT_APPLRDY:
s = "PNET_EVENT_APPLRDY";
break;
case PNET_EVENT_DATA:
s = "PNET_EVENT_DATA";
break;
}
return s;
}
int app_utils_pnet_cfg_init_default (pnet_cfg_t * cfg)
{
memset (cfg, 0, sizeof (pnet_cfg_t));
cfg->tick_us = APP_TICK_INTERVAL_US;
/* Identification & Maintenance */
cfg->im_0_data.im_vendor_id_hi = GET_HIGH_BYTE (APP_GSDML_VENDOR_ID);
cfg->im_0_data.im_vendor_id_lo = GET_LOW_BYTE (APP_GSDML_VENDOR_ID);
cfg->im_0_data.im_hardware_revision = 1;
cfg->im_0_data.im_sw_revision_prefix = APP_GSDML_SW_REV_PREFIX;
cfg->im_0_data.im_sw_revision_functional_enhancement = PNET_VERSION_MAJOR;
cfg->im_0_data.im_sw_revision_bug_fix = PNET_VERSION_MINOR;
cfg->im_0_data.im_sw_revision_internal_change = PNET_VERSION_PATCH;
cfg->im_0_data.im_revision_counter = 0; /* Only 0 allowed according
to standard */
cfg->im_0_data.im_profile_id = APP_GSDML_PROFILE_ID;
cfg->im_0_data.im_profile_specific_type = APP_GSDML_PROFILE_SPEC_TYPE;
cfg->im_0_data.im_version_major = 1;
cfg->im_0_data.im_version_minor = 1;
cfg->im_0_data.im_supported = APP_GSDML_IM_SUPPORTED;
strcpy (cfg->im_0_data.im_order_id, APP_GSDML_ORDER_ID);
strcpy (cfg->im_0_data.im_serial_number, "00001");
strcpy (cfg->im_1_data.im_tag_function, "my function");
strcpy (cfg->im_1_data.im_tag_location, "my location");
strcpy (cfg->im_2_data.im_date, "2020-09-03 13:53");
strcpy (cfg->im_3_data.im_descriptor, "my descriptor");
strcpy (cfg->im_4_data.im_signature, ""); /* Functional safety */
/* Device configuration */
cfg->device_id.vendor_id_hi = GET_HIGH_BYTE (APP_GSDML_VENDOR_ID);
cfg->device_id.vendor_id_lo = GET_LOW_BYTE (APP_GSDML_VENDOR_ID);
cfg->device_id.device_id_hi = GET_HIGH_BYTE (APP_GSDML_DEVICE_ID);
cfg->device_id.device_id_lo = GET_LOW_BYTE (APP_GSDML_DEVICE_ID);
cfg->oem_device_id.vendor_id_hi = 0xc0;
cfg->oem_device_id.vendor_id_lo = 0xff;
cfg->oem_device_id.device_id_hi = 0xee;
cfg->oem_device_id.device_id_lo = 0x01;
strcpy (cfg->product_name, APP_GSDML_PRODUCT_NAME);
cfg->send_hello = true;
/* Timing */
cfg->min_device_interval = APP_GSDML_MIN_DEVICE_INTERVAL;
/* Should be set by application as part of network configuration. */
cfg->num_physical_ports = 1;
snprintf (
cfg->station_name,
sizeof (cfg->station_name),
APP_GSDML_DEFAULT_STATION_NAME);
/* Diagnosis mechanism */
/* Use "Extended channel diagnosis" instead of
* "Qualified channel diagnosis" format on the wire,
* as this is better supported by Wireshark.
*/
cfg->use_qualified_diagnosis = false;
#if PNET_OPTION_DRIVER_ENABLE
cfg->driver_enable = false;
cfg->driver_config.mera.vcam_base_id = PNET_LAN9662_VCAM_BASE;
cfg->driver_config.mera.rtp_base_id = PNET_LAN9662_RTP_BASE;
cfg->driver_config.mera.wal_base_id = PNET_LAN9662_WAL_BASE;
cfg->driver_config.mera.ral_base_id = PNET_LAN9662_RAL_BASE;
#endif
return 0;
}
int app_utils_get_netif_namelist (
const char * arg_str,
uint16_t max_port,
app_utils_netif_namelist_t * p_if_list,
uint16_t * p_num_ports)
{
int ret = 0;
uint16_t i = 0;
uint16_t j = 0;
uint16_t if_index = 0;
uint16_t number_of_given_names = 1;
uint16_t if_list_size = max_port + 1;
char c;
if (max_port == 0)
{
printf ("Error: max_port is 0.\n");
return -1;
}
memset (p_if_list, 0, sizeof (*p_if_list));
c = arg_str[i++];
while (c != '\0')
{
if (c != ',')
{
if (if_index < if_list_size)
{
p_if_list->netif[if_index].name[j++] = c;
}
}
else
{
if (if_index < if_list_size)
{
p_if_list->netif[if_index].name[j++] = '\0';
j = 0;
if_index++;
}
number_of_given_names++;
}
c = arg_str[i++];
}
if (max_port == 1 && number_of_given_names > 1)
{
printf ("Error: Only 1 network interface expected as max_port is 1.\n");
return -1;
}
if (number_of_given_names == 2)
{
printf ("Error: It is illegal to give 2 interface names. Use 1, or one "
"more than the number of physical interfaces.\n");
return -1;
}
if (number_of_given_names > max_port + 1)
{
printf (
"Error: You have given %u interface names, but max is %u as "
"PNET_MAX_PHYSICAL_PORTS is %u.\n",
number_of_given_names,
max_port + 1,
max_port);
return -1;
}
if (number_of_given_names == 1)
{
if (strlen (p_if_list->netif[0].name) == 0)
{
printf ("Error: Zero length network interface name.\n");
return -1;
}
else
{
p_if_list->netif[1] = p_if_list->netif[0];
*p_num_ports = 1;
}
}
else
{
for (i = 0; i < number_of_given_names; i++)
{
if (strlen (p_if_list->netif[i].name) == 0)
{
printf ("Error: Zero length network interface name (%d).\n", i);
return -1;
}
}
*p_num_ports = number_of_given_names - 1;
}
return ret;
}
int app_utils_pnet_cfg_init_netifs (
const char * netif_list_str,
app_utils_netif_namelist_t * if_list,
uint16_t * number_of_ports,
pnet_if_cfg_t * if_cfg)
{
int ret = 0;
int i = 0;
pnal_ipaddr_t ip;
pnal_ipaddr_t netmask;
pnal_ipaddr_t gateway;
ret = app_utils_get_netif_namelist (
netif_list_str,
PNET_MAX_PHYSICAL_PORTS,
if_list,
number_of_ports);
if (ret != 0)
{
return ret;
}
if_cfg->main_netif_name = if_list->netif[0].name;
for (i = 1; i <= *number_of_ports; i++)
{
if_cfg->physical_ports[i - 1].netif_name = if_list->netif[i].name;
}
/* Read IP, netmask, gateway from operating system */
ip = pnal_get_ip_address (if_cfg->main_netif_name);
netmask = pnal_get_netmask (if_cfg->main_netif_name);
gateway = pnal_get_gateway (if_cfg->main_netif_name);
app_utils_copy_ip_to_struct (&if_cfg->ip_cfg.ip_addr, ip);
app_utils_copy_ip_to_struct (&if_cfg->ip_cfg.ip_gateway, gateway);
app_utils_copy_ip_to_struct (&if_cfg->ip_cfg.ip_mask, netmask);
return ret;
}
static void app_utils_print_mac_address (const char * netif_name)
{
pnal_ethaddr_t pnal_mac_addr;
if (pnal_get_macaddress (netif_name, &pnal_mac_addr) == 0)
{
APP_LOG_INFO (
"%02X:%02X:%02X:%02X:%02X:%02X\n",
pnal_mac_addr.addr[0],
pnal_mac_addr.addr[1],
pnal_mac_addr.addr[2],
pnal_mac_addr.addr[3],
pnal_mac_addr.addr[4],
pnal_mac_addr.addr[5]);
}
else
{
APP_LOG_ERROR ("Failed read mac address\n");
}
}
void app_utils_print_network_config (
pnet_if_cfg_t * if_cfg,
uint16_t number_of_ports)
{
uint16_t i;
char hostname_string[PNAL_HOSTNAME_MAX_SIZE]; /* Terminated string */
APP_LOG_INFO ("Management port: %s ", if_cfg->main_netif_name);
app_utils_print_mac_address (if_cfg->main_netif_name);
for (i = 1; i <= number_of_ports; i++)
{
APP_LOG_INFO (
"Physical port [%u]: %s ",
i,
if_cfg->physical_ports[i - 1].netif_name);
app_utils_print_mac_address (if_cfg->physical_ports[i - 1].netif_name);
}
if (pnal_get_hostname (hostname_string) != 0)
{
hostname_string[0] = '\0';
}
APP_LOG_INFO ("Hostname: %s\n", hostname_string);
APP_LOG_INFO (
"IP address: %u.%u.%u.%u\n",
if_cfg->ip_cfg.ip_addr.a,
if_cfg->ip_cfg.ip_addr.b,
if_cfg->ip_cfg.ip_addr.c,
if_cfg->ip_cfg.ip_addr.d);
APP_LOG_INFO (
"Netmask: %u.%u.%u.%u\n",
if_cfg->ip_cfg.ip_mask.a,
if_cfg->ip_cfg.ip_mask.b,
if_cfg->ip_cfg.ip_mask.c,
if_cfg->ip_cfg.ip_mask.d);
APP_LOG_INFO (
"Gateway: %u.%u.%u.%u\n",
if_cfg->ip_cfg.ip_gateway.a,
if_cfg->ip_cfg.ip_gateway.b,
if_cfg->ip_cfg.ip_gateway.c,
if_cfg->ip_cfg.ip_gateway.d);
}
void app_utils_print_ioxs_change (
app_subslot_t * subslot,
const char * ioxs_str,
uint8_t ioxs_current,
uint8_t ioxs_new)
{
if (ioxs_current != ioxs_new)
{
if (ioxs_new == PNET_IOXS_BAD || ioxs_new == PNET_IOXS_GOOD)
{
APP_LOG_DEBUG (
" %-40s PLC reports %s %s \n",
app_utils_get_subslot_string (subslot),
ioxs_str,
app_utils_ioxs_to_string (ioxs_new));
}
else if (ioxs_new != PNET_IOXS_GOOD)
{
APP_LOG_WARNING (
" %-40s PLC reports %s 0x%x Is it in STOP mode?\n",
app_utils_get_subslot_string (subslot),
ioxs_str,
ioxs_new);
}
}
}
int app_utils_plug_module (
app_api_t * p_api,
uint16_t slot_nbr,
uint32_t id,
const char * name)
{
if (slot_nbr >= PNET_MAX_SLOTS)
{
return -1;
}
p_api->slots[slot_nbr].module_id = id;
p_api->slots[slot_nbr].plugged = true;
p_api->slots[slot_nbr].name = name;
return 0;
}
int app_utils_pull_module (app_api_t * p_api, uint16_t slot_nbr)
{
if (slot_nbr >= PNET_MAX_SLOTS)
{
return -1;
}
p_api->slots[slot_nbr].plugged = false;
return 0;
}
app_subslot_t * app_utils_plug_submodule (
app_api_t * p_api,
uint16_t slot_nbr,
uint16_t subslot_nbr,
uint32_t submodule_ident,
const pnet_data_cfg_t * p_data_cfg,
const char * submodule_name,
app_utils_cyclic_callback cyclic_callback,
void * tag)
{
uint16_t subslot_ix;
if (slot_nbr >= PNET_MAX_SLOTS || p_api == NULL || p_data_cfg == NULL)
{
return NULL;
}
for (subslot_ix = 0; subslot_ix < PNET_MAX_SUBSLOTS; subslot_ix++)
{
if (p_api->slots[slot_nbr].subslots[subslot_ix].used == false)
{
app_subslot_t * p_subslot =
&p_api->slots[slot_nbr].subslots[subslot_ix];
p_subslot->used = true;
p_subslot->plugged = true;
p_subslot->slot_nbr = slot_nbr;
p_subslot->subslot_nbr = subslot_nbr;
p_subslot->submodule_name = submodule_name;
p_subslot->submodule_id = submodule_ident;
p_subslot->data_cfg = *p_data_cfg;
p_subslot->cyclic_callback = cyclic_callback;
p_subslot->tag = tag;
p_subslot->iocs = 0;
return p_subslot;
}
}
return NULL;
}
int app_utils_pull_submodule (
app_api_t * p_api,
uint16_t slot_nbr,
uint16_t subslot_nbr)
{
app_subslot_t * p_subslot = NULL;
if (slot_nbr >= PNET_MAX_SUBSLOTS || p_api == NULL)
{
return -1;
}
p_subslot = app_utils_subslot_get (p_api, slot_nbr, subslot_nbr);
if (p_subslot != NULL)
{
memset (p_subslot, 0, sizeof (app_subslot_t));
p_subslot->used = false;
return 0;
}
return -1;
}
/**
* Get subslot application information.
* @param p_appdata InOut: Application state.
* @param slot_nbr In: Slot number.
* @param subslot_nbr In: Subslot number. Range 0 - 0x9FFF.
* @return Reference to application subslot,
* NULL if subslot is not found/plugged.
*/
app_subslot_t * app_utils_subslot_get (
app_api_t * p_api,
uint16_t slot_nbr,
uint16_t subslot_nbr)
{
uint16_t subslot_ix;
if (slot_nbr >= PNET_MAX_SLOTS || p_api == NULL)
{
return NULL;
}
for (subslot_ix = 0; subslot_ix < PNET_MAX_SUBSLOTS; subslot_ix++)
{
if (p_api->slots[slot_nbr].subslots[subslot_ix].subslot_nbr == subslot_nbr)
{
return &p_api->slots[slot_nbr].subslots[subslot_ix];
}
}
return NULL;
}
bool app_utils_subslot_is_input (const app_subslot_t * p_subslot)
{
if (
p_subslot != NULL && (p_subslot->data_cfg.data_dir == PNET_DIR_INPUT ||
p_subslot->data_cfg.data_dir == PNET_DIR_IO))
{
return true;
}
return false;
}
/**
* Return true if subslot is neither input or output.
*
* This is applies for DAP submodules/slots
* @param p_subslot In: Reference to subslot.
* @return true if subslot is input or input/output.
* false if not.
*/
bool app_utils_subslot_is_no_io (const app_subslot_t * p_subslot)
{
if (p_subslot != NULL && p_subslot->data_cfg.data_dir == PNET_DIR_NO_IO)
{
return true;
}
return false;
}
/**
* Return true if subslot is output.
*
* @param p_subslot In: Reference to subslot.
* @return true if subslot is output or input/output,
* false if not.
*/
bool app_utils_subslot_is_output (const app_subslot_t * p_subslot)
{
if (
p_subslot != NULL && (p_subslot->data_cfg.data_dir == PNET_DIR_OUTPUT ||
p_subslot->data_cfg.data_dir == PNET_DIR_IO))
{
return true;
}
return false;
}
void app_utils_cyclic_data_poll (app_api_t * p_api)
{
uint16_t slot;
uint16_t subslot;
app_subslot_t * p_subslot;
for (slot = 0; slot < PNET_MAX_SLOTS; slot++)
{
for (subslot = 0; subslot < PNET_MAX_SUBSLOTS; subslot++)
{
p_subslot = &p_api->slots[slot].subslots[subslot];
if (p_subslot->plugged && p_subslot->cyclic_callback != NULL)
{
p_subslot->cyclic_callback (p_subslot, p_subslot->tag);
}
}
}
}

View File

@ -0,0 +1,382 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* www.rt-labs.com
* Copyright 2021 rt-labs AB, Sweden.
*
* This software is dual-licensed under GPLv3 and a commercial
* license. See the file LICENSE.md distributed with this software for
* full license information.
********************************************************************/
#ifndef APP_UTILS_H
#define APP_UTILS_H
/**
* @file
* @brief Application utilities and helper functions
*
* Functions for getting string representation of
* P-Net events, error codes and more.
*
* API, slot and subslot administration.
*
* Initialization of P-Net configuration from app_gsdml.h.
*/
#ifdef __cplusplus
extern "C" {
#endif
#include "osal.h"
#include "pnal.h"
#include <pnet_api.h>
typedef struct app_utils_netif_name
{
char name[PNET_INTERFACE_NAME_MAX_SIZE];
} app_utils_netif_name_t;
typedef struct app_utils_netif_namelist
{
app_utils_netif_name_t netif[PNET_MAX_PHYSICAL_PORTS + 1];
} app_utils_netif_namelist_t;
typedef struct app_subslot app_subslot_t;
typedef void (*app_utils_cyclic_callback) (app_subslot_t * subslot, void * tag);
/**
* Information of submodule plugged into a subslot.
* Note that submodule data is not stored here but must
* be handled by the submodule implementation.
* All parameters are initialized by the app_utils_plug_submodule()
* function.
* The cyclic_callback is generated when app_utils_cyclic_data_poll()
* is called. Typically on the tick event in the main task.
* tag parameter is passed with the cyclic_callback and
* is typically a handle to a submodule on application.
*/
typedef struct app_subslot
{
bool used;
bool plugged;
uint16_t slot_nbr;
uint16_t subslot_nbr;
uint32_t submodule_id;
const char * submodule_name;
pnet_data_cfg_t data_cfg;
uint8_t iocs;
uint8_t iops;
app_utils_cyclic_callback cyclic_callback;
void * tag;
} app_subslot_t;
/**
* Information of module plugged into
* slot and array of subslots for admin
* of submodules.
*/
typedef struct app_slot
{
bool plugged;
uint32_t module_id;
const char * name;
app_subslot_t subslots[PNET_MAX_SUBSLOTS];
} app_slot_t;
/**
* Profinet API state
* Used to admin modules is plugged into
* slots and submodules into subslots.
*/
typedef struct app_api_t
{
uint32_t api_id;
uint32_t arep;
app_slot_t slots[PNET_MAX_SLOTS];
} app_api_t;
/**
* Convert IP address to string
* @param ip In: IP address
* @param outputstring Out: Resulting string buffer. Should have size
* PNAL_INET_ADDRSTR_SIZE.
*/
void app_utils_ip_to_string (pnal_ipaddr_t ip, char * outputstring);
/**
* Get string description of data direction
* @param direction In: Submodule data direction
* @return String representation of data direction
*/
const char * app_utils_submod_dir_to_string (pnet_submodule_dir_t direction);
/**
* Get string description of pnio produced or consumer status
* @param ioxs In: Producer or consumer status (iops/iocs)
* @return String representation of ioxs (iops/iocs)
*/
const char * app_utils_ioxs_to_string (pnet_ioxs_values_t ioxs);
/**
* Get string representation of application subslot in format
* [slot,subslot,module name]
* This function has an internal buffer for temp storage
* of the generated string.
* @param subslot In: Subslot
* @return String representation of subslot.
*/
char * app_utils_get_subslot_string (const app_subslot_t * subslot);
/**
* Convert MAC address to string
* @param mac In: MAC address
* @param outputstring Out: Resulting string buffer. Should have size
* PNAL_ETH_ADDRSTR_SIZE.
*/
void app_utils_mac_to_string (pnet_ethaddr_t mac, char * outputstring);
/**
* Convert error code to string format
* Only common error codes supported.
* Todo: Add rest of error codes.
*
* @param err_cls In: The error class. See PNET_ERROR_CODE_1_*
* @param err_code In: The error code. See PNET_ERROR_CODE_2_*
* @param err_cls_str Out: The error class string
* @param err_code_str Out: The error code string
*/
void app_utils_get_error_code_strings (
uint16_t err_cls,
uint16_t err_code,
const char ** err_cls_str,
const char ** err_code_str);
/**
* Copy an IP address (as an integer) to a struct
* @param destination_struct Out: Destination
* @param ip In: IP address
*/
void app_utils_copy_ip_to_struct (
pnet_cfg_ip_addr_t * destination_struct,
pnal_ipaddr_t ip);
/**
* Return a string representation of
* the given dcontrol command.
* @param event In: control_command
* @return A string representing the command
*/
const char * app_utils_dcontrol_cmd_to_string (
pnet_control_command_t control_command);
/**
* Return a string representation of the given event.
* @param event In: event
* @return A string representing the event
*/
const char * app_utils_event_to_string (pnet_event_values_t event);
/**
* Get network configuration from a string
* defining a list of network interfaces examples:
* "eth0" or "br0,eth0,eth1"
* @param netif_list_str In: list of network ifs
* @param if_list Out: Array of network ifs
* @param number_of_ports Out: Number of ports
* @param if_cfg Out: P-Net network configuration
* @return 0 on success, -1 on error
*/
int app_utils_pnet_cfg_init_netifs (
const char * netif_list_str,
app_utils_netif_namelist_t * if_list,
uint16_t * number_of_ports,
pnet_if_cfg_t * if_cfg);
/**
* Parse a comma separated list of network interfaces and check
* that the number of interfaces match the PNET_MAX_PHYSICAL_PORTS
* configuration.
*
* For a single Ethernet interface, the \a arg_str should consist of
* one name. For two Ethernet interfaces, the \a arg_str should consist of
* three names, as we also need a bridge interface.
*
* Does only consider the number of comma separated names. No check of the
* names themselves are done.
*
* Examples:
* arg_str num_ports
* "eth0" 1
* "eth0,eth1" error (We need a bridge as well)
* "br0,eth0,eth1" 2
*
* @param arg_str In: Network interface list as comma separated,
* terminated string. For example "eth0" or
* "br0,eth0,eth1".
* @param max_port In: PNET_MAX_PHYSICAL_PORTS, passed as argument to
* allow test.
* @param p_if_list Out: List of network interfaces
* @param p_num_ports Out: Resulting number of physical ports
* @return 0 on success
* -1 on error
*/
int app_utils_get_netif_namelist (
const char * arg_str,
uint16_t max_port,
app_utils_netif_namelist_t * p_if_list,
uint16_t * p_num_ports);
/**
* Print network configuration using APP_LOG_INFO().
* @param if_cfg In: Network configuration
* @param number_of_ports In: Number of used ports
*/
void app_utils_print_network_config (
pnet_if_cfg_t * if_cfg,
uint16_t number_of_ports);
/**
* Print iocs warning message if ioxs is changed.
* @param subslot In: Subslot
* @param ioxs_str In: String description Producer or Consumer
* @param ioxs_current In: Current status
* @param ioxs_new In: New status
*/
void app_utils_print_ioxs_change (
app_subslot_t * subslot,
const char * ioxs_str,
uint8_t ioxs_current,
uint8_t ioxs_new);
/**
* Init the p-net configuration to default values.
* Some values hardcoded, some picked from app_gsdml.h
* Network configuration not initialized.
* This means'.if_cfg' must be set by application.
* Use this function to init P-Net configuration before
* before passing config to app_init().
* @param pnet_cfg Out: Configuration for use by p-net
* @return 0 if the operation succeeded.
* -1 if an error occurred.
*/
int app_utils_pnet_cfg_init_default (pnet_cfg_t * pnet_cfg);
/**
* Plug application module.
* @param p_api InOut: API
* @param slot_nbr In: Slot number
* @param module_id In: Module identity
* @return 0 on success, -1 on error
*/
int app_utils_plug_module (
app_api_t * p_api,
uint16_t slot_nbr,
uint32_t id,
const char * name);
/**
* Pull any application module in given slot.
* @param p_api InOut: API
* @param slot_nbr In: Slot number
* @return 0 on success, -1 on error
*/
int app_utils_pull_module (app_api_t * p_api, uint16_t slot_nbr);
/**
* Plug application submodule.
* @param p_api InOut: API
* @param slot_nbr In: Slot number
* @param subslot_nbr In: Subslot number
* @param submodule_id In: Submodule identity
* @param p_data_cfg In: Data configuration,
* direction, in and out sizes
* @param submodule_name In: Submodule name
* @param cyclic_callback In: Submodule data callback
* @param tag In: Tag passed in cyclic callback
* Typically application or
* submodule handle
*
* @return Reference to allocated subslot,
* NULL if no free subslot is available. This should
* never happen if application is aligned with p-net state.
*/
app_subslot_t * app_utils_plug_submodule (
app_api_t * p_api,
uint16_t slot_nbr,
uint16_t subslot_nbr,
uint32_t submodule_id,
const pnet_data_cfg_t * p_data_cfg,
const char * submodule_name,
app_utils_cyclic_callback cyclic_callback,
void * tag);
/**
* Unplug any application submodule from given subslot.
* @param p_api InOut: API
* @param slot_nbr In: Slot number
* @param subslot_nbr In: Subslot number
* @return 0 on success, -1 on error.
*/
int app_utils_pull_submodule (
app_api_t * p_api,
uint16_t slot_nbr,
uint16_t subslot_nbr);
/**
* Generate data callback for all plugged
* submodules. The callback passed in
* app_utils_plug_submodule is used.
* @param p_api In: API
*/
void app_utils_cyclic_data_poll (app_api_t * p_api);
/**
* Get subslot application information.
*
* @param p_appdata InOut: Application state.
* @param slot_nbr In: Slot number.
* @param subslot_nbr In: Subslot number. Range 0 - 0x9FFF.
* @return Reference to application subslot,
* NULL if subslot is not found/plugged.
*/
app_subslot_t * app_utils_subslot_get (
app_api_t * p_api,
uint16_t slot_nbr,
uint16_t subslot_nbr);
/**
* Return true if subslot is input.
* @param p_subslot In: Reference to subslot.
* @return true if subslot is input or input/output.
* false if not.
*/
bool app_utils_subslot_is_input (const app_subslot_t * p_subslot);
/**
* Return true if subslot is neither input or output.
* This is applies for DAP submodules/slots
* @param p_subslot In: Reference to subslot.
* @return true if subslot is input or input/output.
* false if not.
*/
bool app_utils_subslot_is_no_io (const app_subslot_t * p_subslot);
/**
* Return true if subslot is output.
* @param p_subslot In: Reference to subslot.
* @return true if subslot is output or input/output,
* false if not.
*/
bool app_utils_subslot_is_output (const app_subslot_t * p_subslot);
#ifdef __cplusplus
}
#endif
#endif /* APP_UTILS_H */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,148 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* www.rt-labs.com
* Copyright 2021 rt-labs AB, Sweden.
*
* This software is dual-licensed under GPLv3 and a commercial
* license. See the file LICENSE.md distributed with this software for
* full license information.
********************************************************************/
#ifndef SAMPLEAPP_COMMON_H
#define SAMPLEAPP_COMMON_H
#include "osal.h"
#include "pnal.h"
#include <pnet_api.h>
#ifdef __cplusplus
extern "C" {
#endif
#define APP_TICK_INTERVAL_US 1000 /* 1 ms */
/* Thread configuration for targets where sample
* event loop is run in a separate thread (not main).
* This applies for linux sample app implementation.
*/
#define APP_MAIN_THREAD_PRIORITY 15
#define APP_MAIN_THREAD_STACKSIZE 4096 /* bytes */
#define APP_DATA_LED_ID 1
#define APP_PROFINET_SIGNAL_LED_ID 2
#define APP_TICKS_UPDATE_DATA 100
typedef enum
{
MODE_HW_OFFLOAD_NONE = 0,
MODE_HW_OFFLOAD_CPU,
MODE_HW_OFFLOAD_FULL,
} app_mode_t;
typedef struct app_args
{
char path_button1[PNET_MAX_FILE_FULLPATH_SIZE]; /** Terminated string */
char path_button2[PNET_MAX_FILE_FULLPATH_SIZE]; /** Terminated string */
char path_storage_directory[PNET_MAX_DIRECTORYPATH_SIZE]; /** Terminated */
char station_name[PNET_STATION_NAME_MAX_SIZE]; /** Terminated string */
char eth_interfaces
[PNET_INTERFACE_NAME_MAX_SIZE * (PNET_MAX_PHYSICAL_PORTS + 1) +
PNET_MAX_PHYSICAL_PORTS]; /** Terminated string */
int verbosity;
int show;
bool factory_reset;
bool remove_files;
app_mode_t mode;
} app_args_t;
typedef enum
{
RUN_IN_SEPARATE_THREAD,
RUN_IN_MAIN_THREAD
} app_run_in_separate_task_t;
typedef struct app_data_t app_data_t;
void app_pnet_cfg_init_default (pnet_cfg_t * pnet_cfg);
/**
* Initialize application
*
* Initialize P-Net stack and application.
* The pnet_cfg argument shall have been initialized using
* app_pnet_cfg_init_default() before this function is
* called.
* @param pnet_cfg In: P-Net start configuration
* @return Application handle, NULL on error
*/
app_data_t * app_init (pnet_cfg_t * pnet_cfg, const app_args_t * app_args);
/**
* Start application
*
* Application must have been initialized using app_init() before
* app_start() is called.
* If task_config parameters is set to RUN_IN_SEPARATE_THREAD a
* thread execution the app_loop_forever() function is started.
* If task_config is set to RUN_IN_MAIN_THREAD no such thread is
* started and the caller must call the app_loop_forever() after
* calling this function.
* RUN_IN_MAIN_THREAD is intended for rt-kernel targets.
* RUN_IN_SEPARATE_THREAD is intended for linux targets.
* @param app In: Application handle
* @param task_config In: Defines if stack and application
* is run in main or separate task.
*
* @return 0 on success, -1 on error
*/
int app_start (app_data_t * app, app_run_in_separate_task_t task_config);
/**
* Application task definition. Handles events
* in eternal loop.
* @param arg In: Application handle
* return Should not
*/
void app_loop_forever (void * arg);
/**
* Get P-Net instance from application
*
* @param app In: Application handle
*
* @return P-Net instance
*/
pnet_t * app_get_pnet_instance (app_data_t * app);
/**
* Set LED state
* Hardware specific. Implemented in sample app main file for
* each supported platform.
*
* @param id In: LED number, starting from 0.
* @param led_state In: LED state. Use true for on and false for off.
*/
void app_set_led (uint16_t id, bool led_state);
/**
* Read button state
*
* Hardware specific. Implemented in sample app main file for
* each supported platform.
*
* @param id In: Button number, starting from 0.
* @return true if button is pressed, false if not
*/
bool app_get_button (uint16_t id);
#ifdef __cplusplus
}
#endif
#endif /* SAMPLEAPP_COMMON_H */

View File

@ -0,0 +1,79 @@
#!/bin/sh
#
# Start Profinet sample application
# By default RTE mode is set to full / RTE-FGPA
# The RTE mode can be selected by passing
# "none", "cpu" or "full" to this script.
#
# Application RTE modes
# none
# - LAN9662 RTE: Disabled
# - I/O Data mapping: Shared Memory
# - P-Net data path: P-Net default
# cpu
# - LAN9662 RTE: Enabled
# - I/O Data mapping: Shared Memory
# - P-Net data path: RTE - SRAM
# full
# - LAN9662 RTE: Enabled
# - I/O Data mapping: iofpga
# - P-Net data path: RTE - QSPI
#
# P-Net profinet requires a file system for
# configuration storage. This example assumes
# a rw file system mounted on /tmp/pn_data
#
echo "Starting switchdev-profinet-example"
# Assume a valid partion on /dev/mmcblk0p2
mkdir /tmp/pn_data
mount -o sync /dev/mmcblk0p2 /tmp/pn_data
# Configure bridge
ip link add name br0 type bridge
# Needed for bridge to work
symreg ANA_PGID[61] 0x1ff
ip link set br0 address 12:A9:2D:16:93:89
ip link set br0 up
sysctl -w net.ipv6.conf.br0.disable_ipv6=1
ip link set eth0 up
ip link set eth1 up
ip link set eth0 master br0
ip link set eth1 master br0
# Enable forwarding to chip port 4 (RTE)
symreg qsys_sw_port_mode[4] 0x45000
# Start Profinet application
if [[ $# -eq 0 ]] ; then
mode=full
else
mode=$1
fi
echo "Starting LAN9662 Profinet sample application"
echo "RTE mode: $mode"
/usr/bin/pn_lan9662 -m "$mode" -vvvv -p /tmp/pn_data &
# When enabling log level "DEBUG" for the P-Net stack
# it may be useful to write log to file. The log file
# can be uploaded from target using scp:
# scp root@192.168.0.50:/tmp/pn_dev_log.txt pn_dev_log.txt
#echo "Setup dropbear to support scp"
#mkdir -p /var/run/dropbear/
#dropbear -R -B
# Enable to to write p-net log to file.
#touch /tmp/pn_dev_log.txt
#chmod a+w /tmp/pn_dev_log.txt
#echo " Application log will be stored to /tmp/pn_dev_log.txt"
#/usr/bin/pn_lan9662 -m "$mode" -vvvv -p /tmp/pn_data > /tmp/pn_dev_log.txt

View File

@ -0,0 +1,17 @@
#********************************************************************
# _ _ _
# _ __ | |_ _ | | __ _ | |__ ___
# | '__|| __|(_)| | / _` || '_ \ / __|
# | | | |_ _ | || (_| || |_) |\__ \
# |_| \__|(_)|_| \__,_||_.__/ |___/
#
# http://www.rt-labs.com
# Copyright 2017 rt-labs AB, Sweden.
# See LICENSE file in the project root for full license information.
#*******************************************************************/
target_include_directories(pn_shm_tool
PRIVATE
)
install (TARGETS pn_shm_tool DESTINATION bin)

View File

@ -0,0 +1,509 @@
/*********************************************************************
* _ _ _
* _ __ | |_ _ | | __ _ | |__ ___
* | '__|| __|(_)| | / _` || '_ \ / __|
* | | | |_ _ | || (_| || |_) |\__ \
* |_| \__|(_)|_| \__,_||_.__/ |___/
*
* www.rt-labs.com
* Copyright 2021 rt-labs AB, Sweden.
*
* This software is dual-licensed under GPLv3 and a commercial
* license. See the file LICENSE.md distributed with this software for
* full license information.
********************************************************************/
/* Linux application intended to be used
* to read and write shared memory areas supported
* by the lan9662 sample application.
* See show_usage() for a functional description.
*/
#include <getopt.h>
#include <semaphore.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define MAX_FILE_NAME 200
#define MAX_STDIO_BUFFER 10000
#define DEBUG_LOG (1)
#if DEBUG_LOG == 1
#define LOG_ERR(...) \
fprintf (stderr, "Line:%d ", __LINE__); \
fprintf (stderr, __VA_ARGS__);
#else
#define LOG_ERR(...)
#endif
typedef struct app
{
enum
{
UNDEFINED = 0,
READ,
WRITE,
CREATE,
READ_BIT
} mode;
char name[MAX_FILE_NAME]; /* Shared memory file name*/
int fd; /* Shared memory file descriptor */
caddr_t mem_address; /* Address of mapped shared memory */
sem_t * sem; /* Named semaphore */
size_t shm_size; /* The size of an existing shared memory area */
size_t create_size; /* Size argument used when creating a shared memory
area*/
uint32_t bitnumber; /* Bit number to read */
uint8_t buffer[MAX_STDIO_BUFFER];
} app_t;
static void show_usage (void)
{
printf ("\nTool for accessing shared memory files\n");
printf ("Intended to be used in combination with the \n"
"pn_lan9662 sample application. \n");
printf ("Arguments:\n");
printf (" -h Show help and exit\n");
printf (
" -r <shm_name> Read shared memory and write content to stdout \n");
printf (" -w <shm_name> Write content on stdin to shared memory \n");
printf (" -c <shm_name> Create shared memory area\n");
printf (" -s <size> Size of shared memory, only used with create\n");
printf (" -b <shm_name> Read bit from shared memory. Write '1' or '0' "
"to stdout\n");
printf (
" -n <bitnumber> Bit number, only used with read bit. Defaults to\n");
printf (
" bit number 0 (least significant bit in first byte)\n");
printf (
"Partial write is allowed. Offsets are not supported. If write data\n");
printf ("is larger than shared memory, data is truncated.\n");
printf ("\nExample usage:\n");
printf ("Read slot and passing data to hexdump:\n");
printf ("pn_shm_tool -r pnet-in-1-1-digital_input_1x8 | hexdump -C\n");
printf ("Write slot:\n");
printf ("echo -n -e \"\\x02\" | pn_shm_tool -w "
"pnet-in-1-1-digital_input_1x8\n");
printf ("Create 4 bytes shared memory area (for testing this tool):\n");
printf ("pn_shm_tool -c test_shm -s 4\n");
}
/**
* Open an existing shared memory area
* figure out its size and map it.
* @param app InOut: Application context
* @return 0 on success, -1 on error
*/
int open_shm (app_t * app)
{
struct stat st;
if (app->mode == CREATE)
{
return -1;
}
app->fd = shm_open (app->name, O_RDWR, 0666);
if (app->fd < 0)
{
LOG_ERR ("\nFailed to open %s %s\n", app->name, strerror (errno));
return -1;
}
if (fstat (app->fd, &st))
{
LOG_ERR ("\nfstat error: [%s]\n", strerror (errno));
close (app->fd);
return -1;
}
app->shm_size = st.st_size;
if (ftruncate (app->fd, app->shm_size) != 0)
{
return -1;
}
app->mem_address =
mmap (NULL, app->shm_size, PROT_READ | PROT_WRITE, MAP_SHARED, app->fd, 0);
if (app->mem_address == MAP_FAILED)
{
LOG_ERR ("\nmmap error: [%s]\n", strerror (errno));
return -1;
}
app->sem = sem_open (app->name, 0, 0644, 1);
if (app->sem == NULL)
{
LOG_ERR ("\nFailed to open semaphore: [%s]\n", strerror (errno));
return -1;
}
return 0;
}
/**
* Create a new shared memory area
* @param app InOut: Application context
* @return 0 on success, -1 on error
*/
int create_shm (app_t * app)
{
if (app->mode != CREATE)
{
return -1;
}
app->fd = shm_open (app->name, O_RDWR | O_CREAT, 0666);
if (app->fd < 0)
{
LOG_ERR ("\nFailed to create %s %s\n", app->name, strerror (errno));
return -1;
}
if (ftruncate (app->fd, app->create_size) != 0)
{
return -1;
}
app->mem_address = mmap (
NULL,
app->create_size,
PROT_READ | PROT_WRITE,
MAP_SHARED,
app->fd,
0);
if (app->mem_address == MAP_FAILED)
{
LOG_ERR ("\nmmap error: [%s]\n", strerror (errno));
return -1;
}
app->sem = sem_open (app->name, O_CREAT, 0644, 1);
if (app->sem == NULL)
{
LOG_ERR ("\nFailed to open semaphore: [%s]\n", strerror (errno));
return -1;
}
return 0;
}
/**
* Close a shared memory area and its
* open resources.
* @param app InOut: Application context
* @return 0 always
*/
int close_shm (app_t * app)
{
if (app->mem_address != 0)
{
munmap (app->mem_address, app->shm_size);
app->mem_address = 0;
}
if (app->fd >= 0)
{
close (app->fd);
app->fd = -1;
}
if (app->sem != NULL)
{
sem_close (app->sem);
app->sem = NULL;
}
return 0;
}
/**
* Read a shared memory area and print its content
* to STDOUT.
* @param app InOut: Application context
* @return 0 on success, -1 on error
*/
int read_shm (app_t * app)
{
int write_len;
if (sem_wait (app->sem) != 0)
{
LOG_ERR ("\nFailed to take semaphore: [%s]\n", strerror (errno));
return -1;
}
memcpy (app->buffer, app->mem_address, app->shm_size);
if (sem_post (app->sem) != 0)
{
LOG_ERR ("\nFailed to release semaphore: [%s]\n", strerror (errno));
return -1;
}
write_len = write (STDOUT_FILENO, app->buffer, app->shm_size);
if (write_len != (int)app->shm_size)
{
LOG_ERR ("\nFailed to write stdout\n");
return -1;
}
return 0;
}
/**
* Read a bit from shared memory area and print "0" or "1" to STDOUT.
* @param app InOut: Application context
* @return 0 on success, -1 on error
*/
int read_bit_shm (app_t * app)
{
int write_len = 0;
uint32_t byte_number = 0;
uint8_t bitnumber_in_byte = 0;
uint8_t byte_value = 0;
uint8_t bit_value = 0;
if (app->shm_size == 0)
{
LOG_ERR ("\nShared memory size is zero\n");
return -1;
}
if (app->bitnumber >= MAX_STDIO_BUFFER * 8)
{
LOG_ERR (
"\nToo large bitnumber. Given: %u (Buffer size %u bytes)\n",
app->bitnumber,
MAX_STDIO_BUFFER);
return -1;
}
if (app->bitnumber >= app->shm_size * 8)
{
LOG_ERR (
"\nToo large bitnumber. Given: %u (Memory size %zu bytes)\n",
app->bitnumber,
app->shm_size);
return -1;
}
if (sem_wait (app->sem) != 0)
{
LOG_ERR ("\nFailed to take semaphore: [%s]\n", strerror (errno));
return -1;
}
memcpy (app->buffer, app->mem_address, app->shm_size);
if (sem_post (app->sem) != 0)
{
LOG_ERR ("\nFailed to release semaphore: [%s]\n", strerror (errno));
return -1;
}
byte_number = app->bitnumber / 8; /* byte_number 0 is first */
byte_value = app->buffer[byte_number];
bitnumber_in_byte = app->bitnumber % 8;
bit_value = (byte_value >> bitnumber_in_byte) & 0x01;
write_len = write (STDOUT_FILENO, bit_value ? "1\n" : "0\n", 2);
if (write_len != 2)
{
LOG_ERR ("\nFailed to write stdout\n");
return -1;
}
return 0;
}
/**
* Read STDIN and copy the data to shared memory area.
* @param app InOut: Application context
* @return 0 on success, -1 on error
*/
int write_shm (app_t * app)
{
int len = read (STDIN_FILENO, app->buffer, app->shm_size);
if (len > (int)app->shm_size)
{
/* Truncate len of data to match shared memory */
len = app->shm_size;
}
if (len <= 0)
{
LOG_ERR ("\nFailed to read stdin: [%s]\n", strerror (errno));
return -1;
}
if (sem_wait (app->sem) != 0)
{
LOG_ERR ("\nFailed to take semaphore: [%s]\n", strerror (errno));
return -1;
}
memcpy (app->mem_address, app->buffer, len);
if (sem_post (app->sem) != 0)
{
LOG_ERR ("\nFailed to release semaphore: [%s]\n", strerror (errno));
return -1;
}
return 0;
}
/**
* Execute a command given the configuration
* set in the app variable.
* Creation, read and write of shared memory regions
* are supported.
* @param app InOut: Application context
* @return 0 on success, -1 on error
*/
static int run (app_t * app)
{
int error = 0;
if (app->mode == CREATE)
{
error = create_shm (app);
close_shm (app);
return error;
}
error = open_shm (app);
if (!error)
{
if (app->mode == READ)
{
error = read_shm (app);
}
else if (app->mode == READ_BIT)
{
error = read_bit_shm (app);
}
else if (app->mode == WRITE)
{
error = write_shm (app);
}
error |= close_shm (app);
}
return error;
}
/**
* Parse command line arguments and execute selected command.
* @param argc In: Number of args
* @param argv In: Arguments
* @return 0 on success, 1 on error
*/
int main (int argc, char * argv[])
{
int error = 0;
int option = 0;
app_t app = {0};
if (argc < 2)
{
show_usage();
exit (EXIT_FAILURE);
}
while ((option = getopt (argc, argv, "hw:r:c:s:b:n:")) != -1)
{
switch (option)
{
case 'r':
if (app.mode != UNDEFINED)
{
show_usage();
printf ("\nOnly one operation allowed\n");
exit (EXIT_FAILURE);
}
strncpy (app.name, optarg, MAX_FILE_NAME - 1);
app.mode = READ;
break;
case 'b':
if (app.mode != UNDEFINED)
{
show_usage();
printf ("\nOnly one operation allowed\n");
exit (EXIT_FAILURE);
}
strncpy (app.name, optarg, MAX_FILE_NAME - 1);
app.mode = READ_BIT;
break;
case 'w':
if (app.mode != UNDEFINED)
{
show_usage();
printf ("\nOnly one operation allowed\n");
exit (EXIT_FAILURE);
}
strncpy (app.name, optarg, MAX_FILE_NAME - 1);
app.mode = WRITE;
break;
case 'c':
if (app.mode != UNDEFINED)
{
show_usage();
printf ("\nOnly one operation allowed\n");
exit (EXIT_FAILURE);
}
strncpy (app.name, optarg, MAX_FILE_NAME - 1);
app.mode = CREATE;
break;
case 's':
app.create_size = atoi (optarg);
break;
case 'n':
app.bitnumber = atoi (optarg);
break;
case 'h':
show_usage();
exit (EXIT_SUCCESS);
default:
show_usage();
exit (EXIT_FAILURE);
}
}
if (app.mode == CREATE)
{
if (app.create_size <= 0 || app.create_size > MAX_STDIO_BUFFER)
{
show_usage();
printf ("\nSize must be set when creating shared mem area\n");
printf ("Min size 1, Max size %d\n", MAX_STDIO_BUFFER);
exit (EXIT_FAILURE);
}
}
error = run (&app);
if (error != 0)
{
fprintf (stderr, "Operation failed.\n");
exit (EXIT_FAILURE);
}
exit (EXIT_SUCCESS);
}

View File

@ -0,0 +1,31 @@
#!/bin/sh
#
# Read shared memory and write to EVB-LAN9662 LED
# Utility script using the pn_shm_tool. Intended to be
# used for testing the pn_lan9662 application.
#
MEMORY_NAME="pnet-out-2-1-digital_output_1x8"
BIT_NUMBER=7
LED_NAME="twr1:yellow"
echo "Starting LED handler for Profinet data LED"
sleep 1
while true
do
sleep 0.1
led_state=$(pn_shm_tool -b "$MEMORY_NAME" -n "$BIT_NUMBER")
exitcode=$?
if [ $exitcode -ne 0 ]; then
echo "Failed to get LED state from shared memory"
exit 1
fi
echo ${led_state} > "/sys/class/leds/${LED_NAME}/brightness"
done

View File

@ -0,0 +1,39 @@
#!/bin/sh
#
# Read button on GPIO pin and write to shared memory
# Utility script using the pn_shm_tool. Intended to be
# used for testing the pn_lan9662 application.
#
MEMORY_NAME="pnet-in-1-1-digital_input_1x8"
# Set up digital input on "Raspberry Pi"-type header
# Button1, pin 7, use GND at pin 9
GPIO_NUMBER=39
echo "Starting button state monitor for GPIO $GPIO_NUMBER"
echo "$GPIO_NUMBER" > /sys/class/gpio/export
echo "1" > "/sys/class/gpio/gpio$GPIO_NUMBER/active_low"
sleep 1
while true
do
sleep 0.1
button_state=$(cat "/sys/class/gpio/gpio$GPIO_NUMBER/value")
exitcode=$?
if [ $exitcode -ne 0 ]; then
echo "Failed to read button state"
exit 1
fi
if [ "$button_state" = "1" ]; then
echo -n -e '\x80' | pn_shm_tool -w "$MEMORY_NAME"
else
echo -n -e '\x00' | pn_shm_tool -w "$MEMORY_NAME"
fi
done

View File

@ -0,0 +1,15 @@
#!/bin/sh
# Write shm outputs to inputs
# For test running this command forever
# may be handy:
# while true; do /usr/bin/shm_echo_all.sh; done
# Utility script using the pn_shm_tool. Intended to be
# used for testing the pn_lan9662 application.
#
pn_shm_tool -r pnet-out-2-1-digital_output_1x8 | pn_shm_tool -w pnet-in-1-1-digital_input_1x8
pn_shm_tool -r pnet-out-7-1-digital_out_1x64 | pn_shm_tool -w pnet-in-3-1-digital_input_1x64
pn_shm_tool -r pnet-out-8-1-digital_output_2x32_a | pn_shm_tool -w pnet-in-4-1-digital_input_2x32_a
pn_shm_tool -r pnet-out-9-1-digital_output_2x32_b | pn_shm_tool -w pnet-in-5-1-digital_input_2x32_b
pn_shm_tool -r pnet-out-10-1-digital_output_1x8000 | pn_shm_tool -w pnet-in-6-1-digital_input_1x8000

Some files were not shown because too many files have changed in this diff Show More