dev(none): Снимок библиотеки p-net
41
profinet_stack/p-net/.clang-format
Normal 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
|
||||
12
profinet_stack/p-net/.git-blame-ignore-revs
Normal 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
@ -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
|
||||
35
profinet_stack/p-net/.github/workflows/build.yml
vendored
Normal 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
|
||||
69
profinet_stack/p-net/.github/workflows/clang-analysis.yml
vendored
Normal 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
|
||||
46
profinet_stack/p-net/.github/workflows/codeql-analysis.yml
vendored
Normal 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
@ -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
@ -0,0 +1,3 @@
|
||||
[submodule "cmake/tools"]
|
||||
path = cmake/tools
|
||||
url = https://github.com/rtlabs-com/cmake-tools
|
||||
125
profinet_stack/p-net/CHANGELOG.md
Normal 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.
|
||||
265
profinet_stack/p-net/CMakeLists.txt
Normal 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)
|
||||
23
profinet_stack/p-net/LICENSE.md
Normal 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.
|
||||
147
profinet_stack/p-net/README.md
Normal 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)
|
||||
|
||||
|
||||
[](https://github.com/rtlabs-com/p-net/actions?workflow=Build)
|
||||
[](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).
|
||||
37
profinet_stack/p-net/cmake/FindNetSNMP.cmake
Normal 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()
|
||||
37
profinet_stack/p-net/cmake/FindNetSNMPAgent.cmake
Normal 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()
|
||||
116
profinet_stack/p-net/cmake/Linux.cmake
Normal 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()
|
||||
83
profinet_stack/p-net/cmake/STM32Cube.cmake
Normal 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()
|
||||
83
profinet_stack/p-net/cmake/rt-kernel.cmake
Normal 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()
|
||||
34
profinet_stack/p-net/doc/Makefile
Normal 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
|
||||
375
profinet_stack/p-net/doc/abbreviations.rst
Normal 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
|
||||
389
profinet_stack/p-net/doc/additional_linux_details.rst
Normal 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.
|
||||
182
profinet_stack/p-net/doc/api_documentation.rst
Normal 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:
|
||||
140
profinet_stack/p-net/doc/applications_and_porting.rst
Normal 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.
|
||||
263
profinet_stack/p-net/doc/capturing_packets.rst
Normal 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.
|
||||
3
profinet_stack/p-net/doc/codespell_ignore.txt
Normal file
@ -0,0 +1,3 @@
|
||||
ba
|
||||
fo
|
||||
inout
|
||||
224
profinet_stack/p-net/doc/codesys_details.rst
Normal 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.
|
||||
911
profinet_stack/p-net/doc/compliancetest.rst
Normal 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.
|
||||
94
profinet_stack/p-net/doc/conf.py
Normal 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
|
||||
818
profinet_stack/p-net/doc/creating_gsdml_files.rst
Normal 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.
|
||||
8
profinet_stack/p-net/doc/doc_requirements.txt
Normal file
@ -0,0 +1,8 @@
|
||||
sphinx
|
||||
recommonmark
|
||||
sphinx-rtd-theme
|
||||
breathe
|
||||
sphinxcontrib-googleanalytics
|
||||
sphinxcontrib-spelling
|
||||
rst2pdf
|
||||
codespell
|
||||
380
profinet_stack/p-net/doc/getting_started_LAN9662.rst
Normal 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.
|
||||
|
||||
50
profinet_stack/p-net/doc/getting_started_freertos.rst
Normal 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.
|
||||
380
profinet_stack/p-net/doc/getting_started_rtkernel.rst
Normal 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.
|
||||
@ -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>
|
||||
BIN
profinet_stack/p-net/doc/illustrations/AlarmStateMachines.png
Normal file
|
After Width: | Height: | Size: 451 KiB |
BIN
profinet_stack/p-net/doc/illustrations/Cyclecounter.png
Normal file
|
After Width: | Height: | Size: 89 KiB |
BIN
profinet_stack/p-net/doc/illustrations/FlasherFunctionBlock.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
profinet_stack/p-net/doc/illustrations/NetworkLoad.png
Normal file
|
After Width: | Height: | Size: 70 KiB |
BIN
profinet_stack/p-net/doc/illustrations/PlcProgramLadderLogic.png
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
profinet_stack/p-net/doc/illustrations/RaspberryPiLedButtons.jpg
Normal file
|
After Width: | Height: | Size: 132 KiB |
BIN
profinet_stack/p-net/doc/illustrations/SimaticIoDevice.jpg
Normal file
|
After Width: | Height: | Size: 71 KiB |
@ -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>
|
||||
BIN
profinet_stack/p-net/doc/illustrations/TutorialOverview.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
profinet_stack/p-net/doc/illustrations/periodicity.png
Normal file
|
After Width: | Height: | Size: 269 KiB |
BIN
profinet_stack/p-net/doc/illustrations/proneta_example.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
profinet_stack/p-net/doc/illustrations/rdrecLadderlogic.png
Normal file
|
After Width: | Height: | Size: 43 KiB |
1026
profinet_stack/p-net/doc/implementation_details.rst
Normal file
50
profinet_stack/p-net/doc/index.rst
Normal 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`
|
||||
116
profinet_stack/p-net/doc/linuxtiming.rst
Normal 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.
|
||||
272
profinet_stack/p-net/doc/multiple_ports.rst
Normal 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.
|
||||
1376
profinet_stack/p-net/doc/network_topology_detection.rst
Normal file
382
profinet_stack/p-net/doc/prepare_raspberrypi.rst
Normal 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
|
||||
376
profinet_stack/p-net/doc/profinet_basics.rst
Normal 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)
|
||||
643
profinet_stack/p-net/doc/profinet_details.rst
Normal 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
|
||||
237
profinet_stack/p-net/doc/sampleapp_details.rst
Normal 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 | |
|
||||
| | | | -----------> | |
|
||||
| +----------+ | | |
|
||||
| | | |
|
||||
+----------------+ +-----------+
|
||||
223
profinet_stack/p-net/doc/spelling_wordlist.txt
Normal 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
|
||||
3
profinet_stack/p-net/doc/static/css/change_header_size.css
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/* Reduce subheading size */
|
||||
|
||||
h3{font-size: 110%;}
|
||||
15
profinet_stack/p-net/doc/static/css/fix_table_width.css
vendored
Normal 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;
|
||||
}
|
||||
}
|
||||
565
profinet_stack/p-net/doc/tutorial.rst
Normal 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.
|
||||
699
profinet_stack/p-net/doc/use_with_siematic.rst
Normal 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.
|
||||
178
profinet_stack/p-net/doc/using_codesys.rst
Normal 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.
|
||||
43
profinet_stack/p-net/doc/writing_documentation.rst
Normal 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/
|
||||
21
profinet_stack/p-net/doc/yoctobuild.rst
Normal 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.
|
||||
2257
profinet_stack/p-net/include/pnet_api.h
Normal file
246
profinet_stack/p-net/options.h.in
Normal 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 */
|
||||
33
profinet_stack/p-net/pnet_options.h.in
Normal 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 */
|
||||
41
profinet_stack/p-net/pnet_version.h.in
Normal 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 */
|
||||
21
profinet_stack/p-net/samples/pn_dev/CMakeLists.txt
Normal 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)
|
||||
BIN
profinet_stack/p-net/samples/pn_dev/GSDML-RT-LABS-STACK.bmp
Normal file
|
After Width: | Height: | Size: 47 KiB |
@ -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>
|
||||
315
profinet_stack/p-net/samples/pn_dev/app_data.c
Normal 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;
|
||||
}
|
||||
140
profinet_stack/p-net/samples/pn_dev/app_data.h
Normal 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 */
|
||||
260
profinet_stack/p-net/samples/pn_dev/app_gsdml.c
Normal 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;
|
||||
}
|
||||
184
profinet_stack/p-net/samples/pn_dev/app_gsdml.h
Normal 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 */
|
||||
52
profinet_stack/p-net/samples/pn_dev/app_log.c
Normal 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");
|
||||
}
|
||||
}
|
||||
75
profinet_stack/p-net/samples/pn_dev/app_log.h
Normal 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 */
|
||||
741
profinet_stack/p-net/samples/pn_dev/app_utils.c
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
428
profinet_stack/p-net/samples/pn_dev/app_utils.h
Normal 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 */
|
||||
1622
profinet_stack/p-net/samples/pn_dev/sampleapp_common.c
Normal file
156
profinet_stack/p-net/samples/pn_dev/sampleapp_common.h
Normal 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 */
|
||||
23
profinet_stack/p-net/samples/pn_dev_lan9662/CMakeLists.txt
Normal 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)
|
||||
@ -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>
|
||||
@ -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 $?
|
||||
442
profinet_stack/p-net/samples/pn_dev_lan9662/app_data.c
Normal 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;
|
||||
}
|
||||
121
profinet_stack/p-net/samples/pn_dev_lan9662/app_data.h
Normal 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 */
|
||||
394
profinet_stack/p-net/samples/pn_dev_lan9662/app_gsdml.c
Normal 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;
|
||||
}
|
||||
199
profinet_stack/p-net/samples/pn_dev_lan9662/app_gsdml.h
Normal 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 */
|
||||
52
profinet_stack/p-net/samples/pn_dev_lan9662/app_log.c
Normal 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");
|
||||
}
|
||||
}
|
||||
75
profinet_stack/p-net/samples/pn_dev_lan9662/app_log.h
Normal 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 */
|
||||
318
profinet_stack/p-net/samples/pn_dev_lan9662/app_shm.c
Normal 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;
|
||||
}
|
||||
104
profinet_stack/p-net/samples/pn_dev_lan9662/app_shm.h
Normal 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 */
|
||||
722
profinet_stack/p-net/samples/pn_dev_lan9662/app_utils.c
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
382
profinet_stack/p-net/samples/pn_dev_lan9662/app_utils.h
Normal 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 */
|
||||
1235
profinet_stack/p-net/samples/pn_dev_lan9662/sampleapp_common.c
Normal file
148
profinet_stack/p-net/samples/pn_dev_lan9662/sampleapp_common.h
Normal 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 */
|
||||
@ -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
|
||||
|
||||
|
||||
17
profinet_stack/p-net/samples/pn_shm_tool/CMakeLists.txt
Normal 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)
|
||||
509
profinet_stack/p-net/samples/pn_shm_tool/pn_shm_tool.c
Normal 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);
|
||||
}
|
||||
31
profinet_stack/p-net/samples/pn_shm_tool/shm_LED_handler.sh
Normal 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
|
||||
@ -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
|
||||
15
profinet_stack/p-net/samples/pn_shm_tool/shm_echo_all.sh
Normal 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
|
||||