diff --git a/.clang-format b/.clang-format index 1ac2aa8e..1e6be923 100644 --- a/.clang-format +++ b/.clang-format @@ -1,15 +1,193 @@ -# https://clang.llvm.org/docs/ClangFormatStyleOptions.html -BasedOnStyle: LLVM - -# indent 4 spaces +--- +Language: Cpp +# BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: None +AlignConsecutiveMacros: + Enabled: true + AcrossEmptyLines: true + AcrossComments: true +AlignConsecutiveAssignments: None +AlignConsecutiveBitFields: + Enabled: true + AcrossEmptyLines: true + AcrossComments: true +AlignConsecutiveDeclarations: None +AlignEscapedNewlines: Right +AlignOperands: Align +SortIncludes: false +InsertBraces: true # Control statements must have curly brackets +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortEnumsOnASingleLine: true +AllowShortBlocksOnASingleLine: Empty +AllowShortCaseLabelsOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: AllDefinitions +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +AttributeMacros: + - __capability +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeConceptDeclarations: true +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 120 +CommentPragmas: "^ IWYU pragma:" +QualifierAlignment: Leave +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DeriveLineEnding: true +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +PackConstructorInitializers: BinPack +BasedOnStyle: "" +ConstructorInitializerAllOnOneLineOrOnePerLine: false +AllowAllConstructorInitializersOnNextLine: true +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Preserve +IncludeCategories: + - Regex: "^<(.*)>" + Priority: 0 + - Regex: '^"(.*)"' + Priority: 1 + - Regex: "(.*)" + Priority: 2 +IncludeIsMainRegex: "(Test)?$" +IncludeIsMainSourceRegex: "" +IndentAccessModifiers: false +IndentCaseLabels: true +IndentCaseBlocks: false +IndentGotoLabels: true +IndentPPDirectives: None +IndentExternBlock: AfterExternBlock +IndentRequires: true IndentWidth: 4 +IndentWrappedFunctionNames: false +InsertTrailingCommas: None +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +LambdaBodyIndentation: Signature +MacroBlockBegin: "" +MacroBlockEnd: "" +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 50 +PenaltyBreakBeforeFirstCallParameter: 9 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PenaltyIndentedWhitespace: 0 +PointerAlignment: Left +PPIndentWidth: -1 +ReferenceAlignment: Pointer +ReflowComments: true +RemoveBracesLLVM: false +SeparateDefinitionBlocks: Always +ShortNamespaceLines: 1 +SortJavaStaticImport: Before +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeParens: ControlStatements +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterForeachMacros: true + AfterFunctionDefinitionName: false + AfterFunctionDeclarationName: false + AfterIfMacros: true + AfterOverloadedOperator: false + BeforeNonEmptyParentheses: false +SpaceAroundPointerQualifiers: Default +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never +SpacesInConditionalStatement: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false +BitFieldColonSpacing: Both +Standard: Latest +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseCRLF: false +UseTab: Never +WhitespaceSensitiveMacros: + - STRINGIZE + - PP_STRINGIZE + - BOOST_PP_STRINGIZE + - NS_SWIFT_NAME + - CF_SWIFT_NAME +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +--- -# break before function -BreakBeforeBraces: Linux - -# arguments and parameters in same line or one line for each -BinPackArguments: false -BinPackParameters: false - -# keep up to two empty lines -MaxEmptyLinesToKeep: 2 diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..437d8e6e --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # CANopenNode +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: ['paypal.me/jnz022'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.gitignore b/.gitignore index adacc76c..96202779 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,15 @@ -canopennode +/example/canopennode_blank +/canopend *.o +*.persist +*.persist.old -doc/ +doc/html/ #eclipse .cproject .project .settings/ +#kdevelop +*.kdev4 diff --git a/301/CO_Emergency.c b/301/CO_Emergency.c new file mode 100644 index 00000000..614b4d3c --- /dev/null +++ b/301/CO_Emergency.c @@ -0,0 +1,720 @@ +/* + * CANopen Emergency object. + * + * @file CO_Emergency.c + * @ingroup CO_Emergency + * @author Janez Paternoster + * @copyright 2020 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#include + +#include "301/CO_Emergency.h" + +/* verify configuration */ +#if CO_CONFIG_EM_ERR_STATUS_BITS_COUNT < (6U * 8U) || CO_CONFIG_EM_ERR_STATUS_BITS_COUNT > 256U \ + || (CO_CONFIG_EM_ERR_STATUS_BITS_COUNT % 8U) != 0 +#error CO_CONFIG_EM_ERR_STATUS_BITS_COUNT is not correct +#endif + +/* fifo buffer example for fifoSize = 7 (actual capacity = 6) * + * * + * 0 * * * * * + * 1 pp==wp fifoPpPtr fifoWrPtr * * + * 2 * * * * * + * 3 * * * fifoWrPtr * + * 4 * fifoWrPtr fifoPpPtr fifoPpPtr * + * 5 * * * * * + * 6 * * * * * + * * + * nothing 3 bytes 4 bytes buffer * + * to process to process to process full * + ******************************************************************************/ + +#if ((CO_CONFIG_EM)&CO_CONFIG_EM_PRODUCER) != 0 +#if ((CO_CONFIG_EM)&CO_CONFIG_EM_PROD_CONFIGURABLE) != 0 +/* + * Custom functions for read/write OD object "COB-ID EMCY" + * + * For more information see file CO_ODinterface.h, OD_IO_t. + */ +static ODR_t +OD_read_1014(OD_stream_t* stream, void* buf, OD_size_t count, OD_size_t* countRead) { + if ((stream == NULL) || (stream->subIndex != 0U) || (buf == NULL) || (count < sizeof(uint32_t)) + || (countRead == NULL)) { + return ODR_DEV_INCOMPAT; + } + + CO_EM_t* em = (CO_EM_t*)stream->object; + + uint16_t canId = (em->producerCanId == CO_CAN_ID_EMERGENCY) ? (CO_CAN_ID_EMERGENCY + em->nodeId) + : em->producerCanId; + uint32_t COB_IDEmergency32 = em->producerEnabled ? 0U : 0x80000000U; + COB_IDEmergency32 |= canId; + (void)CO_setUint32(buf, COB_IDEmergency32); + + *countRead = sizeof(uint32_t); + return ODR_OK; +} + +static ODR_t +OD_write_1014(OD_stream_t* stream, const void* buf, OD_size_t count, OD_size_t* countWritten) { + if ((stream == NULL) || (stream->subIndex != 0U) || (buf == NULL) || (count != sizeof(uint32_t)) + || (countWritten == NULL)) { + return ODR_DEV_INCOMPAT; + } + + CO_EM_t* em = (CO_EM_t*)stream->object; + + /* Verify written value. COB ID must not change, if emergency is enabled */ + uint32_t COB_IDEmergency32 = CO_getUint32(buf); + uint16_t newCanId = (uint16_t)(COB_IDEmergency32 & 0x7FFU); + uint16_t curCanId = (em->producerCanId == CO_CAN_ID_EMERGENCY) ? (CO_CAN_ID_EMERGENCY + em->nodeId) + : em->producerCanId; + bool_t newEnabled = ((COB_IDEmergency32 & 0x80000000U) == 0U) && (newCanId != 0U); + if (((COB_IDEmergency32 & 0x7FFFF800U) != 0U) || CO_IS_RESTRICTED_CAN_ID(newCanId) + || ((em->producerEnabled && newEnabled) && (newCanId != curCanId))) { + return ODR_INVALID_VALUE; + } + + /* store values. If default CAN-ID is used, then store only value of CO_CAN_ID_EMERGENCY without node id. */ + em->producerEnabled = newEnabled; + em->producerCanId = (newCanId == ((uint16_t)CO_CAN_ID_EMERGENCY + em->nodeId)) ? CO_CAN_ID_EMERGENCY : newCanId; + + /* configure emergency message CAN transmission */ + if (newEnabled) { + em->CANtxBuff = CO_CANtxBufferInit(em->CANdevTx, em->CANdevTxIdx, newCanId, false, 8U, false); + } + + /* write value to the original location in the Object Dictionary */ + return OD_writeOriginal(stream, buf, count, countWritten); +} +#else +/* + * Custom functions for read/write OD object "COB-ID EMCY" + * + * For more information see file CO_ODinterface.h, OD_IO_t. + */ +static ODR_t +OD_read_1014_default(OD_stream_t* stream, void* buf, OD_size_t count, OD_size_t* countRead) { + if ((stream == NULL) || (stream->subIndex != 0U) || (buf == NULL) || (count < sizeof(uint32_t)) + || (countRead == NULL)) { + return ODR_DEV_INCOMPAT; + } + + CO_EM_t* em = (CO_EM_t*)stream->object; + + uint32_t COB_IDEmergency32 = em->producerEnabled ? 0U : 0x80000000U; + COB_IDEmergency32 |= CO_CAN_ID_EMERGENCY + (uint32_t)em->nodeId; + (void)CO_setUint32(buf, COB_IDEmergency32); + + *countRead = sizeof(uint32_t); + return ODR_OK; +} +#endif /* (CO_CONFIG_EM) & CO_CONFIG_EM_PROD_CONFIGURABLE */ + +#if ((CO_CONFIG_EM)&CO_CONFIG_EM_PROD_INHIBIT) != 0 +/* + * Custom function for writing OD object "Inhibit time EMCY" + * + * For more information see file CO_ODinterface.h, OD_IO_t. + */ +static ODR_t +OD_write_1015(OD_stream_t* stream, const void* buf, OD_size_t count, OD_size_t* countWritten) { + if ((stream == NULL) || (stream->subIndex != 0U) || (buf == NULL) || (count != sizeof(uint16_t)) + || (countWritten == NULL)) { + return ODR_DEV_INCOMPAT; + } + + CO_EM_t* em = (CO_EM_t*)stream->object; + + /* update object */ + em->inhibitEmTime_us = (uint32_t)CO_getUint16(buf) * 100U; + em->inhibitEmTimer = 0; + + /* write value to the original location in the Object Dictionary */ + return OD_writeOriginal(stream, buf, count, countWritten); +} +#endif /* (CO_CONFIG_EM) & CO_CONFIG_EM_PROD_INHIBIT */ +#endif /* (CO_CONFIG_EM) & CO_CONFIG_EM_PRODUCER */ + +#if ((CO_CONFIG_EM)&CO_CONFIG_EM_HISTORY) != 0 +/* + * Custom functions for read/write OD object _OD_statusBits_, optional + * + * For more information see file CO_ODinterface.h, OD_IO_t. + */ +static ODR_t +OD_read_1003(OD_stream_t* stream, void* buf, OD_size_t count, OD_size_t* countRead) { + if ((stream == NULL) || (buf == NULL) || (countRead == NULL) || ((count < 4U) && (stream->subIndex > 0U)) + || (count < 1U)) { + return ODR_DEV_INCOMPAT; + } + + CO_EM_t* em = (CO_EM_t*)stream->object; + + if (em->fifoSize < 2U) { + return ODR_DEV_INCOMPAT; + } + if (stream->subIndex == 0U) { + (void)CO_setUint8(buf, em->fifoCount); + + *countRead = sizeof(uint8_t); + return ODR_OK; + } else if (stream->subIndex <= em->fifoCount) { + /* newest error is reported on subIndex 1 and is stored just behind + * fifoWrPtr. Get correct index in FIFO buffer. */ + int16_t index = (int16_t)em->fifoWrPtr - (int16_t)stream->subIndex; + if (index < 0) { + index += (int16_t)em->fifoSize; + } else if (index >= (int16_t)(em->fifoSize)) { + return ODR_DEV_INCOMPAT; + } else { /* MISRA C 2004 14.10 */ + } + (void)CO_setUint32(buf, em->fifo[index].msg); + + *countRead = sizeof(uint32_t); + return ODR_OK; + } else { + return ODR_NO_DATA; + } +} + +static ODR_t +OD_write_1003(OD_stream_t* stream, const void* buf, OD_size_t count, OD_size_t* countWritten) { + if ((stream == NULL) || (stream->subIndex != 0U) || (buf == NULL) || (count != 1U) || (countWritten == NULL)) { + return ODR_DEV_INCOMPAT; + } + + if (CO_getUint8(buf) != 0U) { + return ODR_INVALID_VALUE; + } + + CO_EM_t* em = (CO_EM_t*)stream->object; + + /* clear error history */ + em->fifoCount = 0; + + *countWritten = sizeof(uint8_t); + return ODR_OK; +} +#endif /* (CO_CONFIG_EM) & CO_CONFIG_EM_HISTORY */ + +#if ((CO_CONFIG_EM)&CO_CONFIG_EM_STATUS_BITS) != 0 +/* + * Custom functions for read/write OD object _OD_statusBits_, optional + * + * For more information see file CO_ODinterface.h, OD_IO_t. + */ +static ODR_t +OD_read_statusBits(OD_stream_t* stream, void* buf, OD_size_t count, OD_size_t* countRead) { + if ((stream == NULL) || (stream->subIndex != 0U) || (buf == NULL) || (countRead == NULL)) { + return ODR_DEV_INCOMPAT; + } + + CO_EM_t* em = (CO_EM_t*)stream->object; + + /* get MAX(errorStatusBitsSize, bufSize, ODsizeIndication) */ + OD_size_t countReadLocal = CO_CONFIG_EM_ERR_STATUS_BITS_COUNT / 8U; + if (countReadLocal > count) { + countReadLocal = count; + } + if ((stream->dataLength != 0U) && (countReadLocal > stream->dataLength)) { + countReadLocal = stream->dataLength; + } else { + stream->dataLength = countReadLocal; + } + + (void)memcpy((void*)(buf), (const void*)(&em->errorStatusBits[0]), countReadLocal); + + *countRead = countReadLocal; + return ODR_OK; +} + +static ODR_t +OD_write_statusBits(OD_stream_t* stream, const void* buf, OD_size_t count, OD_size_t* countWritten) { + if ((stream == NULL) || (stream->subIndex != 0U) || (buf == NULL) || (countWritten == NULL)) { + return ODR_DEV_INCOMPAT; + } + + CO_EM_t* em = (CO_EM_t*)stream->object; + + /* get MAX(errorStatusBitsSize, bufSize, ODsizeIndication) */ + OD_size_t countWrite = CO_CONFIG_EM_ERR_STATUS_BITS_COUNT / 8U; + if (countWrite > count) { + countWrite = count; + } + if ((stream->dataLength != 0U) && (countWrite > stream->dataLength)) { + countWrite = stream->dataLength; + } else { + stream->dataLength = countWrite; + } + + (void)memcpy((void*)(&em->errorStatusBits[0]), (const void*)(buf), countWrite); + + *countWritten = countWrite; + return ODR_OK; +} +#endif /* (CO_CONFIG_EM) & CO_CONFIG_EM_STATUS_BITS */ + +#if ((CO_CONFIG_EM)&CO_CONFIG_EM_CONSUMER) != 0 +/* + * Read received message from CAN module. + * + * Function will be called (by CAN receive interrupt) every time, when CAN message with correct identifier + * will be received. For more information and description of parameters see file CO_driver.h. + */ +static void +CO_EM_receive(void* object, void* msg) { + CO_EM_t* em = (CO_EM_t*)object; + + if ((em != NULL) && (em->pFunctSignalRx != NULL)) { + uint16_t ident = CO_CANrxMsg_readIdent(msg); + + /* ignore sync messages (necessary if sync object is not used) */ + if (ident != 0x80U) { + const uint8_t* data = CO_CANrxMsg_readData(msg); + uint16_t errorCode; + uint32_t infoCode; + + (void)memcpy((void*)(&errorCode), (const void*)(&data[0]), sizeof(errorCode)); + (void)memcpy((void*)(&infoCode), (const void*)(&data[4]), sizeof(infoCode)); + em->pFunctSignalRx(ident, CO_SWAP_16(errorCode), data[2], data[3], CO_SWAP_32(infoCode)); + } + } +} +#endif + +CO_ReturnError_t +CO_EM_init(CO_EM_t* em, CO_CANmodule_t* CANdevTx, const OD_entry_t* OD_1001_errReg, +#if (((CO_CONFIG_EM) & (CO_CONFIG_EM_PRODUCER | CO_CONFIG_EM_HISTORY)) != 0) || defined CO_DOXYGEN + CO_EM_fifo_t* fifo, uint8_t fifoSize, +#endif +#if (((CO_CONFIG_EM)&CO_CONFIG_EM_PRODUCER) != 0) || defined CO_DOXYGEN + OD_entry_t* OD_1014_cobIdEm, uint16_t CANdevTxIdx, +#if (((CO_CONFIG_EM)&CO_CONFIG_EM_PROD_INHIBIT) != 0) || defined CO_DOXYGEN + OD_entry_t* OD_1015_InhTime, +#endif +#endif +#if (((CO_CONFIG_EM)&CO_CONFIG_EM_HISTORY) != 0) || defined CO_DOXYGEN + OD_entry_t* OD_1003_preDefErr, +#endif +#if (((CO_CONFIG_EM)&CO_CONFIG_EM_STATUS_BITS) != 0) || defined CO_DOXYGEN + OD_entry_t* OD_statusBits, +#endif +#if (((CO_CONFIG_EM)&CO_CONFIG_EM_CONSUMER) != 0) || defined CO_DOXYGEN + CO_CANmodule_t* CANdevRx, uint16_t CANdevRxIdx, +#endif + const uint8_t nodeId, uint32_t* errInfo) { + (void)nodeId; /* may be unused */ + CO_ReturnError_t ret = CO_ERROR_NO; + + /* verify arguments */ + if ((em == NULL) || (OD_1001_errReg == NULL) +#if ((CO_CONFIG_EM) & (CO_CONFIG_EM_PRODUCER | CO_CONFIG_EM_HISTORY)) != 0 + || ((fifo == NULL) && (fifoSize >= 2U)) +#endif +#if ((CO_CONFIG_EM)&CO_CONFIG_EM_PRODUCER) != 0 + || (OD_1014_cobIdEm == NULL) || (CANdevTx == NULL) || (nodeId < 1U) || (nodeId > 127U) +#endif +#if ((CO_CONFIG_EM)&CO_CONFIG_EM_HISTORY) != 0 + || (OD_1003_preDefErr == NULL) +#endif +#if ((CO_CONFIG_EM)&CO_CONFIG_EM_CONSUMER) != 0 + || (CANdevRx == NULL) +#endif + ) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + /* clear the object */ + (void)memset(em, 0, sizeof(CO_EM_t)); + + /* set object variables */ + em->CANdevTx = CANdevTx; + + /* get and verify "Error register" from Object Dictionary */ + em->errorRegister = OD_getPtr(OD_1001_errReg, 0, sizeof(uint8_t), NULL); + if (em->errorRegister == NULL) { + if (errInfo != NULL) { + *errInfo = OD_getIndex(OD_1001_errReg); + } + return CO_ERROR_OD_PARAMETERS; + } + *em->errorRegister = 0; + +#if ((CO_CONFIG_EM) & (CO_CONFIG_EM_PRODUCER | CO_CONFIG_EM_HISTORY)) != 0 + em->fifo = fifo; + em->fifoSize = fifoSize; +#endif +#if ((CO_CONFIG_EM)&CO_CONFIG_EM_PRODUCER) != 0 + /* get initial and verify "COB-ID EMCY" from Object Dictionary */ + uint32_t COB_IDEmergency32; + ODR_t odRet; + odRet = OD_get_u32(OD_1014_cobIdEm, 0, &COB_IDEmergency32, true); + if ((odRet != ODR_OK) || ((COB_IDEmergency32 & 0x7FFFF800U) != 0U)) { + if (errInfo != NULL) { + *errInfo = OD_getIndex(OD_1014_cobIdEm); + } + /* don't break a program, if only value of a parameter is wrong */ + if (odRet != ODR_OK) { + return CO_ERROR_OD_PARAMETERS; + } + } + +#if ((CO_CONFIG_EM)&CO_CONFIG_EM_PROD_CONFIGURABLE) != 0 + uint16_t producerCanId = (uint16_t)(COB_IDEmergency32 & 0x7FFU); + em->producerEnabled = ((COB_IDEmergency32 & 0x80000000U) == 0U) && (producerCanId != 0U); + + em->OD_1014_extension.object = em; + em->OD_1014_extension.read = OD_read_1014; + em->OD_1014_extension.write = OD_write_1014; + odRet = OD_extension_init(OD_1014_cobIdEm, &em->OD_1014_extension); + if (odRet != ODR_OK) { + if (errInfo != NULL) { + *errInfo = OD_getIndex(OD_1014_cobIdEm); + } + return CO_ERROR_OD_PARAMETERS; + } + /* following two variables are used inside OD_read_1014 and OD_write_1014 */ + em->producerCanId = producerCanId; + em->CANdevTxIdx = CANdevTxIdx; + /* if default producerCanId is used, then value of CO_CAN_ID_EMERGENCY (0x80) is stored into non-volatile + * memory. In that case it is necessary to add nodeId of this node to the stored value. */ + if (producerCanId == CO_CAN_ID_EMERGENCY) { + producerCanId += nodeId; + } +#else + uint16_t producerCanId = CO_CAN_ID_EMERGENCY + (uint16_t)nodeId; + em->producerEnabled = (COB_IDEmergency32 & 0x80000000U) == 0U; + + em->OD_1014_extension.object = em; + em->OD_1014_extension.read = OD_read_1014_default; + em->OD_1014_extension.write = OD_writeOriginal; + odRet = OD_extension_init(OD_1014_cobIdEm, &em->OD_1014_extension); + if (odRet != ODR_OK) { + if (errInfo != NULL) { + *errInfo = OD_getIndex(OD_1014_cobIdEm); + } + return CO_ERROR_OD_PARAMETERS; + } +#endif + + /* configure parameters and emergency message CAN transmission */ + em->nodeId = nodeId; + + em->CANtxBuff = CO_CANtxBufferInit(CANdevTx, CANdevTxIdx, producerCanId, false, 8U, false); + + if (em->CANtxBuff == NULL) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + +#if ((CO_CONFIG_EM)&CO_CONFIG_EM_PROD_INHIBIT) != 0 + /* get and verify optional "Inhibit time EMCY" from Object Dictionary */ + em->inhibitEmTime_us = 0; + em->inhibitEmTimer = 0; + uint16_t inhibitTime_100us; + odRet = OD_get_u16(OD_1015_InhTime, 0, &inhibitTime_100us, true); + if (odRet == ODR_OK) { + em->inhibitEmTime_us = (uint32_t)inhibitTime_100us * 100U; + + em->OD_1015_extension.object = em; + em->OD_1015_extension.read = OD_readOriginal; + em->OD_1015_extension.write = OD_write_1015; + (void)OD_extension_init(OD_1015_InhTime, &em->OD_1015_extension); + } +#endif /* (CO_CONFIG_EM) & CO_CONFIG_EM_PROD_INHIBIT */ +#endif /* (CO_CONFIG_EM) & CO_CONFIG_EM_PRODUCER */ + +#if ((CO_CONFIG_EM)&CO_CONFIG_EM_HISTORY) != 0 + /* If OD entry available, make access to em->preDefErr */ + em->OD_1003_extension.object = em; + em->OD_1003_extension.read = OD_read_1003; + em->OD_1003_extension.write = OD_write_1003; + (void)OD_extension_init(OD_1003_preDefErr, &em->OD_1003_extension); +#endif /* (CO_CONFIG_EM) & CO_CONFIG_EM_HISTORY */ + +#if ((CO_CONFIG_EM)&CO_CONFIG_EM_STATUS_BITS) != 0 + /* If OD entry available, make access to em->errorStatusBits */ + em->OD_statusBits_extension.object = em; + em->OD_statusBits_extension.read = OD_read_statusBits; + em->OD_statusBits_extension.write = OD_write_statusBits; + (void)OD_extension_init(OD_statusBits, &em->OD_statusBits_extension); +#endif /* (CO_CONFIG_EM) & CO_CONFIG_EM_STATUS_BITS */ + +#if ((CO_CONFIG_EM)&CO_CONFIG_EM_CONSUMER) != 0 + em->pFunctSignalRx = NULL; + /* configure SDO server CAN reception */ + ret = CO_CANrxBufferInit(CANdevRx, CANdevRxIdx, CO_CAN_ID_EMERGENCY, 0x780, false, (void*)em, CO_EM_receive); +#endif /* (CO_CONFIG_EM) & CO_CONFIG_EM_CONSUMER */ + + return ret; +} + +#if ((CO_CONFIG_EM)&CO_CONFIG_EM_CONSUMER) != 0 +void +CO_EM_initCallbackRx(CO_EM_t* em, + void (*pFunctSignalRx)(const uint16_t ident, const uint16_t errorCode, const uint8_t errorRegister, + const uint8_t errorBit, const uint32_t infoCode)) { + if (em != NULL) { + em->pFunctSignalRx = pFunctSignalRx; + } +} +#endif + +#if ((CO_CONFIG_EM)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0 +void +CO_EM_initCallbackPre(CO_EM_t* em, void* object, void (*pFunctSignal)(void* object)) { + if (em != NULL) { + em->functSignalObjectPre = object; + em->pFunctSignalPre = pFunctSignal; + } +} +#endif + +void +CO_EM_process(CO_EM_t* em, bool_t NMTisPreOrOperational, uint32_t timeDifference_us, uint32_t* timerNext_us) { + (void)timerNext_us; /* may be unused */ + +#if ((CO_CONFIG_EM)&CO_CONFIG_EM_PROD_INHIBIT) == 0 + (void)timeDifference_us; /* may be unused */ +#endif + + /* verify errors from driver */ + uint16_t CANerrSt = em->CANdevTx->CANerrorStatus; + if (CANerrSt != em->CANerrorStatusOld) { + uint16_t CANerrStChanged = CANerrSt ^ em->CANerrorStatusOld; + em->CANerrorStatusOld = CANerrSt; + + if ((CANerrStChanged & (CO_CAN_ERRTX_WARNING | CO_CAN_ERRRX_WARNING)) != 0U) { + CO_error(em, (CANerrSt & (CO_CAN_ERRTX_WARNING | CO_CAN_ERRRX_WARNING)) != 0U, CO_EM_CAN_BUS_WARNING, + CO_EMC_NO_ERROR, 0); + } + if ((CANerrStChanged & CO_CAN_ERRTX_PASSIVE) != 0U) { + CO_error(em, (CANerrSt & CO_CAN_ERRTX_PASSIVE) != 0U, CO_EM_CAN_TX_BUS_PASSIVE, CO_EMC_CAN_PASSIVE, 0); + } + if ((CANerrStChanged & CO_CAN_ERRTX_BUS_OFF) != 0U) { + CO_error(em, (CANerrSt & CO_CAN_ERRTX_BUS_OFF) != 0U, CO_EM_CAN_TX_BUS_OFF, CO_EMC_BUS_OFF_RECOVERED, 0); + } + if ((CANerrStChanged & CO_CAN_ERRTX_OVERFLOW) != 0U) { + CO_error(em, (CANerrSt & CO_CAN_ERRTX_OVERFLOW) != 0U, CO_EM_CAN_TX_OVERFLOW, CO_EMC_CAN_OVERRUN, 0); + } + if ((CANerrStChanged & CO_CAN_ERRTX_PDO_LATE) != 0U) { + CO_error(em, (CANerrSt & CO_CAN_ERRTX_PDO_LATE) != 0U, CO_EM_TPDO_OUTSIDE_WINDOW, CO_EMC_COMMUNICATION, 0); + } + if ((CANerrStChanged & CO_CAN_ERRRX_PASSIVE) != 0U) { + CO_error(em, (CANerrSt & CO_CAN_ERRRX_PASSIVE) != 0U, CO_EM_CAN_RX_BUS_PASSIVE, CO_EMC_CAN_PASSIVE, 0); + } + if ((CANerrStChanged & CO_CAN_ERRRX_OVERFLOW) != 0U) { + CO_error(em, (CANerrSt & CO_CAN_ERRRX_OVERFLOW) != 0U, CO_EM_CAN_RXB_OVERFLOW, CO_EMC_CAN_OVERRUN, 0); + } + } + + /* calculate Error register */ + uint8_t errorRegister = 0U; + if (CO_CONFIG_ERR_CONDITION_GENERIC) { + errorRegister |= (uint8_t)CO_ERR_REG_GENERIC_ERR; + } +#ifdef CO_CONFIG_ERR_CONDITION_CURRENT + if (CO_CONFIG_ERR_CONDITION_CURRENT) { + errorRegister |= (uint8_t)CO_ERR_REG_CURRENT; + } +#endif +#ifdef CO_CONFIG_ERR_CONDITION_VOLTAGE + if (CO_CONFIG_ERR_CONDITION_VOLTAGE) { + errorRegister |= (uint8_t)CO_ERR_REG_VOLTAGE; + } +#endif +#ifdef CO_CONFIG_ERR_CONDITION_TEMPERATURE + if (CO_CONFIG_ERR_CONDITION_TEMPERATURE) { + errorRegister |= (uint8_t)CO_ERR_REG_TEMPERATURE; + } +#endif + if (CO_CONFIG_ERR_CONDITION_COMMUNICATION) { + errorRegister |= (uint8_t)CO_ERR_REG_COMMUNICATION; + } +#ifdef CO_CONFIG_ERR_CONDITION_DEV_PROFILE + if (CO_CONFIG_ERR_CONDITION_DEV_PROFILE) { + errorRegister |= (uint8_t)CO_ERR_REG_DEV_PROFILE; + } +#endif + if (CO_CONFIG_ERR_CONDITION_MANUFACTURER) { + errorRegister |= (uint8_t)CO_ERR_REG_MANUFACTURER; + } + *em->errorRegister = errorRegister; + + if (!NMTisPreOrOperational) { + return; + } + + /* post-process Emergency message in fifo buffer. */ +#if ((CO_CONFIG_EM)&CO_CONFIG_EM_PRODUCER) != 0 + if (em->fifoSize >= 2U) { + uint8_t fifoPpPtr = em->fifoPpPtr; + +#if ((CO_CONFIG_EM)&CO_CONFIG_EM_PROD_INHIBIT) != 0 + if (em->inhibitEmTimer < em->inhibitEmTime_us) { + em->inhibitEmTimer += timeDifference_us; + } + + if (!em->CANtxBuff->bufferFull && (fifoPpPtr != em->fifoWrPtr) + && (em->inhibitEmTimer >= em->inhibitEmTime_us)) { + em->inhibitEmTimer = 0; +#else + if ((!em->CANtxBuff->bufferFull) && (fifoPpPtr != em->fifoWrPtr)) { +#endif + /* add error register to emergency message */ + em->fifo[fifoPpPtr].msg |= (uint32_t)errorRegister << 16; + + /* send emergency message */ + (void)memcpy((void*)em->CANtxBuff->data, (void*)&em->fifo[fifoPpPtr].msg, sizeof(em->CANtxBuff->data)); + (void)CO_CANsend(em->CANdevTx, em->CANtxBuff); + +#if ((CO_CONFIG_EM)&CO_CONFIG_EM_CONSUMER) != 0 + /* report also own emergency messages */ + if (em->pFunctSignalRx != NULL) { + uint32_t errMsg = em->fifo[fifoPpPtr].msg; + em->pFunctSignalRx(0, CO_SWAP_16((uint16_t)errMsg), errorRegister, (uint8_t)(errMsg >> 24), + CO_SWAP_32(em->fifo[fifoPpPtr].info)); + } +#endif + + /* increment pointer */ + fifoPpPtr++; + em->fifoPpPtr = (fifoPpPtr < em->fifoSize) ? fifoPpPtr : 0U; + + /* verify message buffer overflow. Clear error condition if all messages from fifo buffer are processed */ + if (em->fifoOverflow == 1U) { + em->fifoOverflow = 2; + CO_errorReport(em, CO_EM_EMERGENCY_BUFFER_FULL, CO_EMC_GENERIC, 0); + } else if ((em->fifoOverflow == 2U) && (em->fifoPpPtr == em->fifoWrPtr)) { + em->fifoOverflow = 0; + CO_errorReset(em, CO_EM_EMERGENCY_BUFFER_FULL, 0); + } else { /* MISRA C 2004 14.10 */ + } + } +#if ((CO_CONFIG_EM)&CO_CONFIG_EM_PROD_INHIBIT) != 0 +#if ((CO_CONFIG_EM)&CO_CONFIG_FLAG_TIMERNEXT) != 0 + else if ((timerNext_us != NULL) && (em->inhibitEmTimer < em->inhibitEmTime_us)) { + /* check again after inhibit time elapsed */ + uint32_t diff = em->inhibitEmTime_us - em->inhibitEmTimer; + if (*timerNext_us > diff) { + *timerNext_us = diff; + } + } else { /* MISRA C 2004 14.10 */ + } +#endif +#endif + } +#elif ((CO_CONFIG_EM)&CO_CONFIG_EM_HISTORY) != 0 + if (em->fifoSize >= 2) { + uint8_t fifoPpPtr = em->fifoPpPtr; + while (fifoPpPtr != em->fifoWrPtr) { + /* add error register to emergency message and increment pointers */ + em->fifo[fifoPpPtr].msg |= (uint32_t)errorRegister << 16; + + if (++fifoPpPtr >= em->fifoSize) { + fifoPpPtr = 0; + } + } + em->fifoPpPtr = fifoPpPtr; + } +#endif /* (CO_CONFIG_EM) & CO_CONFIG_EM_PRODUCER, #elif CO_CONFIG_EM_HISTORY */ + + return; +} + +void +CO_error(CO_EM_t* em, bool_t setError, const uint8_t errorBit, uint16_t errorCode, uint32_t infoCode) { + if (em == NULL) { + return; + } + + uint8_t index = errorBit >> 3; + uint8_t bitmask = 1U << (errorBit & 0x7U); + + /* if unsupported errorBit, change to 'CO_EM_WRONG_ERROR_REPORT' */ + if (index >= (CO_CONFIG_EM_ERR_STATUS_BITS_COUNT / 8U)) { + index = CO_EM_WRONG_ERROR_REPORT >> 3; + bitmask = 1U << (CO_EM_WRONG_ERROR_REPORT & 0x7U); + errorCode = CO_EMC_SOFTWARE_INTERNAL; + infoCode = errorBit; + } + + uint8_t* errorStatusBits = &em->errorStatusBits[index]; + uint8_t errorStatusBitMasked = *errorStatusBits & bitmask; + + /* If error is already set (or unset), return without further actions, + * otherwise toggle bit and continue with error indication. */ + if (setError) { + if (errorStatusBitMasked != 0U) { + return; + } + } else { + if (errorStatusBitMasked == 0U) { + return; + } + errorCode = CO_EMC_NO_ERROR; + } + +#if ((CO_CONFIG_EM) & (CO_CONFIG_EM_PRODUCER | CO_CONFIG_EM_HISTORY)) != 0 + /* prepare emergency message. Error register will be added in post-process */ + uint32_t errMsg = ((uint32_t)errorBit << 24) | CO_SWAP_16(errorCode); +#if ((CO_CONFIG_EM)&CO_CONFIG_EM_PRODUCER) != 0 + uint32_t infoCodeSwapped = CO_SWAP_32(infoCode); +#endif +#endif + + /* safely write data, and increment pointers */ + CO_LOCK_EMCY(em->CANdevTx); + if (setError) { + *errorStatusBits |= bitmask; + } else { + *errorStatusBits &= ~bitmask; + } + +#if ((CO_CONFIG_EM) & (CO_CONFIG_EM_PRODUCER | CO_CONFIG_EM_HISTORY)) != 0 + if (em->fifoSize >= 2U) { + uint8_t fifoWrPtr = em->fifoWrPtr; + uint8_t fifoWrPtrNext = fifoWrPtr + 1U; + if (fifoWrPtrNext >= em->fifoSize) { + fifoWrPtrNext = 0; + } + + if (fifoWrPtrNext == em->fifoPpPtr) { + em->fifoOverflow = 1; + } else { + em->fifo[fifoWrPtr].msg = errMsg; +#if ((CO_CONFIG_EM)&CO_CONFIG_EM_PRODUCER) != 0 + em->fifo[fifoWrPtr].info = infoCodeSwapped; +#endif + em->fifoWrPtr = fifoWrPtrNext; + if (em->fifoCount < (em->fifoSize - 1U)) { + em->fifoCount++; + } + } + } +#endif /* (CO_CONFIG_EM) & (CO_CONFIG_EM_PRODUCER | CO_CONFIG_EM_HISTORY) */ + + CO_UNLOCK_EMCY(em->CANdevTx); + +#if ((CO_CONFIG_EM)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0 +#if ((CO_CONFIG_EM)&CO_CONFIG_EM_PRODUCER) != 0 + /* Optional signal to RTOS, which can resume task, which handles CO_EM_process */ + if ((em->pFunctSignalPre != NULL) && em->producerEnabled) { + em->pFunctSignalPre(em->functSignalObjectPre); + } +#endif +#endif +} diff --git a/301/CO_Emergency.h b/301/CO_Emergency.h new file mode 100644 index 00000000..c449c7c9 --- /dev/null +++ b/301/CO_Emergency.h @@ -0,0 +1,482 @@ +/** + * CANopen Emergency protocol. + * + * @file CO_Emergency.h + * @ingroup CO_Emergency + * @author Janez Paternoster + * @copyright 2020 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#ifndef CO_EMERGENCY_H +#define CO_EMERGENCY_H + +#include "301/CO_driver.h" +#include "301/CO_ODinterface.h" + +/* default configuration, see CO_config.h */ +#ifndef CO_CONFIG_EM +#define CO_CONFIG_EM \ + (CO_CONFIG_EM_PRODUCER | CO_CONFIG_EM_HISTORY | CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE \ + | CO_CONFIG_GLOBAL_FLAG_TIMERNEXT) +#endif +#ifndef CO_CONFIG_EM_ERR_STATUS_BITS_COUNT +#define CO_CONFIG_EM_ERR_STATUS_BITS_COUNT (10U * 8U) +#endif +#ifndef CO_CONFIG_ERR_CONDITION_GENERIC +#define CO_CONFIG_ERR_CONDITION_GENERIC (em->errorStatusBits[5] != 0U) +#endif +#ifndef CO_CONFIG_ERR_CONDITION_COMMUNICATION +#define CO_CONFIG_ERR_CONDITION_COMMUNICATION ((em->errorStatusBits[2] != 0U) || (em->errorStatusBits[3] != 0U)) +#endif +#ifndef CO_CONFIG_ERR_CONDITION_MANUFACTURER +#define CO_CONFIG_ERR_CONDITION_MANUFACTURER ((em->errorStatusBits[8] != 0U) || (em->errorStatusBits[9] != 0U)) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup CO_Emergency Emergency + * CANopen Emergency protocol. + * + * @ingroup CO_CANopen_301 + * @{ + * Error control and Emergency is used for control internal error state and for sending a CANopen Emergency message. + * + * In case of error condition stack or application calls CO_errorReport() function with indication of the error. + * Specific error condition is reported (with CANopen Emergency message) only the first time after it occurs. Internal + * state of specific error condition is indicated by internal bitfield variable, with space for maximum @ref + * CO_CONFIG_EM_ERR_STATUS_BITS_COUNT bits. Meaning for each bit is described by @ref CO_EM_errorStatusBits_t. Specific + * error condition can be reset by CO_errorReset() function. In that case Emergency message is sent with CO_EM_NO_ERROR + * indication. + * + * Some error conditions are informative and some are critical. Critical error conditions set the corresponding bit in + * @ref CO_errorRegister_t. Critical error conditions for generic error are specified by @ref + * CO_CONFIG_ERR_CONDITION_GENERIC macro. Similar macros are defined for other error bits in in @ref CO_errorRegister_t. + * + * ### Emergency producer + * If @ref CO_CONFIG_EM has CO_CONFIG_EM_PRODUCER enabled, then CANopen Emergency message will be sent on each change of + * any error condition. Emergency message contents are: + * + * Byte | Description + * -----|----------------------------------------------------------- + * 0..1 | @ref CO_EM_errorCode_t + * 2 | @ref CO_errorRegister_t + * 3 | Index of error condition (see @ref CO_EM_errorStatusBits_t). + * 4..7 | Additional informative argument to CO_errorReport() function. + * + * ### Error history + * If @ref CO_CONFIG_EM has CO_CONFIG_EM_HISTORY enabled, then latest errors can be read from _Pre Defined Error Field_ + * (object dictionary, index 0x1003). Contents corresponds to bytes 0..3 from the Emergency message. + * + * ### Emergency consumer + * If @ref CO_CONFIG_EM has CO_CONFIG_EM_CONSUMER enabled, then callback can be registered by @ref + * CO_EM_initCallbackRx() function. + */ + +/** + * @defgroup CO_errorRegister_t CANopen Error register + * @{ + * + * Mandatory for CANopen, resides in object dictionary, index 0x1001. + * + * Error register is calculated from internal bitfield variable, critical bits. See @ref CO_EM_errorStatusBits_t and + * @ref CO_STACK_CONFIG_EMERGENCY for error condition macros. + * + * Internal errors may prevent device to stay in NMT Operational state and changes may switch between the states. See + * @ref CO_NMT_control_t for details. + */ +#define CO_ERR_REG_GENERIC_ERR 0x01U /**< bit 0, generic error */ +#define CO_ERR_REG_CURRENT 0x02U /**< bit 1, current */ +#define CO_ERR_REG_VOLTAGE 0x04U /**< bit 2, voltage */ +#define CO_ERR_REG_TEMPERATURE 0x08U /**< bit 3, temperature */ +#define CO_ERR_REG_COMMUNICATION 0x10U /**< bit 4, communication error */ +#define CO_ERR_REG_DEV_PROFILE 0x20U /**< bit 5, device profile specific */ +#define CO_ERR_REG_RESERVED 0x40U /**< bit 6, reserved (always 0) */ +#define CO_ERR_REG_MANUFACTURER 0x80U /**< bit 7, manufacturer specific */ + +/** @} */ /* CO_errorRegister_t */ + +/** + * @defgroup CO_EM_errorCode_t CANopen Error code + * @{ + * + * Standard error codes according to CiA DS-301 and DS-401. + */ +#define CO_EMC_NO_ERROR 0x0000U /**< 0x00xx error Reset or No Error */ +#define CO_EMC_GENERIC 0x1000U /**< 0x10xx Generic Error */ +#define CO_EMC_CURRENT 0x2000U /**< 0x20xx Current */ +#define CO_EMC_CURRENT_INPUT 0x2100U /**< 0x21xx Current device input side */ +#define CO_EMC_CURRENT_INSIDE 0x2200U /**< 0x22xx Current inside the device */ +#define CO_EMC_CURRENT_OUTPUT 0x2300U /**< 0x23xx Current device output side */ +#define CO_EMC_VOLTAGE 0x3000U /**< 0x30xx Voltage */ +#define CO_EMC_VOLTAGE_MAINS 0x3100U /**< 0x31xx Mains Voltage */ +#define CO_EMC_VOLTAGE_INSIDE 0x3200U /**< 0x32xx Voltage inside the device */ +#define CO_EMC_VOLTAGE_OUTPUT 0x3300U /**< 0x33xx Output Voltage */ +#define CO_EMC_TEMPERATURE 0x4000U /**< 0x40xx Temperature */ +#define CO_EMC_TEMP_AMBIENT 0x4100U /**< 0x41xx Ambient Temperature */ +#define CO_EMC_TEMP_DEVICE 0x4200U /**< 0x42xx Device Temperature */ +#define CO_EMC_HARDWARE 0x5000U /**< 0x50xx Device Hardware */ +#define CO_EMC_SOFTWARE_DEVICE 0x6000U /**< 0x60xx Device Software */ +#define CO_EMC_SOFTWARE_INTERNAL 0x6100U /**< 0x61xx Internal Software */ +#define CO_EMC_SOFTWARE_USER 0x6200U /**< 0x62xx User Software */ +#define CO_EMC_DATA_SET 0x6300U /**< 0x63xx Data Set */ +#define CO_EMC_ADDITIONAL_MODUL 0x7000U /**< 0x70xx Additional Modules */ +#define CO_EMC_MONITORING 0x8000U /**< 0x80xx Monitoring */ +#define CO_EMC_COMMUNICATION 0x8100U /**< 0x81xx Communication */ +#define CO_EMC_CAN_OVERRUN 0x8110U /**< 0x8110 CAN Overrun (Objects lost) */ +#define CO_EMC_CAN_PASSIVE 0x8120U /**< 0x8120 CAN in Error Passive Mode */ +#define CO_EMC_HEARTBEAT 0x8130U /**< 0x8130 Life Guard Error or Heartbeat Error */ +#define CO_EMC_BUS_OFF_RECOVERED 0x8140U /**< 0x8140 recovered from bus off */ +#define CO_EMC_CAN_ID_COLLISION 0x8150U /**< 0x8150 CAN-ID collision */ +#define CO_EMC_PROTOCOL_ERROR 0x8200U /**< 0x82xx Protocol Error */ +#define CO_EMC_PDO_LENGTH 0x8210U /**< 0x8210 PDO not processed due to length error */ +#define CO_EMC_PDO_LENGTH_EXC 0x8220U /**< 0x8220 PDO length exceeded */ +#define CO_EMC_DAM_MPDO 0x8230U /**< 0x8230 DAM MPDO not processed destination object not available */ +#define CO_EMC_SYNC_DATA_LENGTH 0x8240U /**< 0x8240 Unexpected SYNC data length */ +#define CO_EMC_RPDO_TIMEOUT 0x8250U /**< 0x8250 RPDO timeout */ +#define CO_EMC_EXTERNAL_ERROR 0x9000U /**< 0x90xx External Error */ +#define CO_EMC_ADDITIONAL_FUNC 0xF000U /**< 0xF0xx Additional Functions */ +#define CO_EMC_DEVICE_SPECIFIC 0xFF00U /**< 0xFFxx Device specific */ + +#define CO_EMC401_OUT_CUR_HI 0x2310U /**< 0x2310 DS401 Current at outputs too high (overload) */ +#define CO_EMC401_OUT_SHORTED 0x2320U /**< 0x2320 DS401 Short circuit at outputs */ +#define CO_EMC401_OUT_LOAD_DUMP 0x2330U /**< 0x2330 DS401 Load dump at outputs */ +#define CO_EMC401_IN_VOLT_HI 0x3110U /**< 0x3110 DS401 Input voltage too high */ +#define CO_EMC401_IN_VOLT_LOW 0x3120U /**< 0x3120 DS401 Input voltage too low */ +#define CO_EMC401_INTERN_VOLT_HI 0x3210U /**< 0x3210 DS401 Internal voltage too high */ +#define CO_EMC401_INTERN_VOLT_LO 0x3220U /**< 0x3220 DS401 Internal voltage too low */ +#define CO_EMC401_OUT_VOLT_HIGH 0x3310U /**< 0x3310 DS401 Output voltage too high */ +#define CO_EMC401_OUT_VOLT_LOW 0x3320U /**< 0x3320 DS401 Output voltage too low */ + +/** @} */ /* CO_EM_errorCode_t */ + +/** + * @defgroup CO_EM_errorStatusBits_t Error status bits + * @{ + * + * Bits for internal indication of the error condition. Each error condition is specified by unique index from 0x00 up + * to 0xFF. + * + * If specific error occurs in the stack or in the application, CO_errorReport() sets specific bit in the + * _errorStatusBit_ variable from @ref CO_EM_t. If bit was already set, function returns without any action. Otherwise + * it prepares emergency message. + * + * Maximum size (in bits) of the _errorStatusBit_ variable is specified by @ref CO_CONFIG_EM_ERR_STATUS_BITS_COUNT (set + * to 10*8 bits by default). Stack uses first 6 bytes. Additional 4 bytes are pre-defined for manufacturer or device + * specific error indications, by default. + */ +#define CO_EM_NO_ERROR 0x00U /**< 0x00 Error Reset or No Error */ +#define CO_EM_CAN_BUS_WARNING 0x01U /**< 0x01 communication info CAN bus warning limit reached */ +#define CO_EM_RXMSG_WRONG_LENGTH 0x02U /**< 0x02 communication info Wrong data length of the received CAN message */ +#define CO_EM_RXMSG_OVERFLOW 0x03U /**< 0x03 communication info Previous received CAN message wasn't processed */ +#define CO_EM_RPDO_WRONG_LENGTH 0x04U /**< 0x04 communication info Wrong data length of received PDO */ +#define CO_EM_RPDO_OVERFLOW 0x05U /**< 0x05 communication info Previous received PDO wasn't processed yet */ +#define CO_EM_CAN_RX_BUS_PASSIVE 0x06U /**< 0x06 communication info CAN receive bus is passive */ +#define CO_EM_CAN_TX_BUS_PASSIVE 0x07U /**< 0x07 communication info CAN transmit bus is passive */ +#define CO_EM_NMT_WRONG_COMMAND 0x08U /**< 0x08 communication info Wrong NMT command received */ +#define CO_EM_TIME_TIMEOUT 0x09U /**< 0x09 communication info TIME message timeout */ +#define CO_EM_0A_unused 0x0AU /**< 0x0A communication info (unused) */ +#define CO_EM_0B_unused 0x0BU /**< 0x0B communication info (unused) */ +#define CO_EM_0C_unused 0x0CU /**< 0x0C communication info (unused) */ +#define CO_EM_0D_unused 0x0DU /**< 0x0D communication info (unused) */ +#define CO_EM_0E_unused 0x0EU /**< 0x0E communication info (unused) */ +#define CO_EM_0F_unused 0x0FU /**< 0x0F communication info (unused) */ + +#define CO_EM_10_unused 0x10U /**< 0x10 communication critical (unused) */ +#define CO_EM_11_unused 0x11U /**< 0x11 communication critical (unused) */ +#define CO_EM_CAN_TX_BUS_OFF 0x12U /**< 0x12 communication critical CAN transmit bus is off */ +#define CO_EM_CAN_RXB_OVERFLOW 0x13U /**< 0x13 communication critical CAN module receive buffer overflowed */ +#define CO_EM_CAN_TX_OVERFLOW 0x14U /**< 0x14 communication critical CAN transmit buffer overflowed */ +#define CO_EM_TPDO_OUTSIDE_WINDOW 0x15U /**< 0x15 communication critical TPDO is outside SYNC window */ +#define CO_EM_16_unused 0x16U /**< 0x16 communication critical (unused) */ +#define CO_EM_RPDO_TIME_OUT 0x17U /**< 0x17 communication critical RPDO message timeout */ +#define CO_EM_SYNC_TIME_OUT 0x18U /**< 0x18 communication critical SYNC message timeout */ +#define CO_EM_SYNC_LENGTH 0x19U /**< 0x19 communication critical Unexpected SYNC data length */ +#define CO_EM_PDO_WRONG_MAPPING 0x1AU /**< 0x1A communication critical Error with PDO mapping */ +#define CO_EM_HEARTBEAT_CONSUMER 0x1BU /**< 0x1B communication critical Heartbeat consumer timeout */ +#define CO_EM_HB_CONSUMER_REMOTE_RESET 0x1CU /**< 0x1C comm. critical Heartbeat consumer detected remote node reset */ +#define CO_EM_SRDO_CONFIGURATION 0x1DU /**< 0x1D communication critical Error in SRDO configuration parameters */ +#define CO_EM_1E_unused 0x1EU /**< 0x1E communication critical (unused) */ +#define CO_EM_1F_unused 0x1FU /**< 0x1F communication critical (unused) */ + +#define CO_EM_EMERGENCY_BUFFER_FULL 0x20U /**< 0x20 generic info Emergency buffer is full or message wasn't sent */ +#define CO_EM_21_unused 0x21U /**< 0x21 generic info (unused) */ +#define CO_EM_MICROCONTROLLER_RESET 0x22U /**< 0x22 generic info Microcontroller has just started */ +#define CO_EM_23_unused 0x23U /**< 0x23 generic info (unused) */ +#define CO_EM_24_unused 0x24U /**< 0x24 generic info (unused) */ +#define CO_EM_25_unused 0x25U /**< 0x25 generic info (unused) */ +#define CO_EM_26_unused 0x26U /**< 0x26 generic info (unused) */ +#define CO_EM_NON_VOLATILE_AUTO_SAVE 0x27U /**< 0x27 generic info Automatic store to non-volatile memory failed */ + +#define CO_EM_WRONG_ERROR_REPORT 0x28U /**< 0x28 generic critical Wrong parameters to CO_errorReport() */ +#define CO_EM_ISR_TIMER_OVERFLOW 0x29U /**< 0x29 generic critical Timer task has overflowed */ +#define CO_EM_MEMORY_ALLOCATION_ERROR 0x2AU /**< 0x2A generic critical Unable to allocate memory for objects */ +#define CO_EM_GENERIC_ERROR 0x2BU /**< 0x2B generic critical Generic error test usage */ +#define CO_EM_GENERIC_SOFTWARE_ERROR 0x2CU /**< 0x2C generic critical Software error */ +#define CO_EM_INCONSISTENT_OBJECT_DICT 0x2DU /**< 0x2D generic critical Object dict. does not match the software */ +#define CO_EM_CALCULATION_OF_PARAMETERS 0x2EU /**< 0x2E generic critical Error in calculation of device parameters */ +#define CO_EM_NON_VOLATILE_MEMORY 0x2FU /**< 0x2F generic critical Error with access to non volatile memory */ + +/** + * 0x30+ manufacturer info or critical Error status buts free to use by manufacturer. By default bits 0x30..0x3F are set + * as informational and bits 0x40..0x4F are set as critical. Manufacturer critical bits sets the error register as + * specified by @ref CO_CONFIG_ERR_CONDITION_MANUFACTURER + */ +#define CO_EM_MANUFACTURER_START 0x30U +/** (@ref CO_CONFIG_EM_ERR_STATUS_BITS_COUNT - 1) largest value of the Error status bit. */ +#define CO_EM_MANUFACTURER_END (CO_CONFIG_EM_ERR_STATUS_BITS_COUNT - 1U) + +/** @} */ /* CO_EM_errorStatusBits_t */ + +#if (((CO_CONFIG_EM) & (CO_CONFIG_EM_PRODUCER | CO_CONFIG_EM_HISTORY)) != 0) || defined CO_DOXYGEN +/** + * Fifo buffer for emergency producer and error history + */ +typedef struct { + uint32_t msg; +#if (((CO_CONFIG_EM)&CO_CONFIG_EM_PRODUCER) != 0) || defined CO_DOXYGEN + uint32_t info; +#endif +} CO_EM_fifo_t; +#endif + +/** + * Emergency object. + */ +typedef struct { + uint8_t errorStatusBits[CO_CONFIG_EM_ERR_STATUS_BITS_COUNT / 8U]; /**< Bitfield for the internal indication of + the error condition. */ + uint8_t* errorRegister; /**< Pointer to error register in object dictionary at 0x1001,00. */ + uint16_t CANerrorStatusOld; /**< Old CAN error status bitfield */ + CO_CANmodule_t* CANdevTx; /**< From CO_EM_init() */ + +#if (((CO_CONFIG_EM) & (CO_CONFIG_EM_PRODUCER | CO_CONFIG_EM_HISTORY)) != 0) || defined CO_DOXYGEN + CO_EM_fifo_t* + fifo; /**< Internal circular FIFO buffer for storing pre-processed emergency messages. Messages are added by + @ref CO_error() function. All messages are later post-processed by @ref CO_EM_process() function. In + case of overflow, error is indicated but emergency message is not sent. Fifo is also used for error + history, OD object 0x1003, "Pre-defined error field". Buffer is defined by @ref CO_EM_init(). */ + uint8_t fifoSize; /**< Size of the above buffer, specified by @ref CO_EM_init(). */ + uint8_t fifoWrPtr; /**< Pointer for the fifo buffer, where next emergency message will be written by @ref CO_error() + function. */ + uint8_t fifoPpPtr; /**< Pointer for the fifo, where next emergency message has to be post-processed by @ref + CO_EM_process() function. If equal to bufWrPtr, then all messages has been post-processed. */ + uint8_t fifoOverflow; /**< Indication of overflow - messages in buffer are not post-processed */ + uint8_t fifoCount; /**< Count of emergency messages in fifo, used for OD object 0x1003 */ +#endif /* (CO_CONFIG_EM) & (CO_CONFIG_EM_PRODUCER | CO_CONFIG_EM_HISTORY) */ + +#if (((CO_CONFIG_EM)&CO_CONFIG_EM_PRODUCER) != 0) || defined CO_DOXYGEN + bool_t producerEnabled; /**< True, if emergency producer is enabled, from Object dictionary */ + uint8_t nodeId; /**< Copy of CANopen node ID, from CO_EM_init() */ + CO_CANtx_t* CANtxBuff; /**< CAN transmit buffer */ + OD_extension_t OD_1014_extension; /**< Extension for OD object */ +#if (((CO_CONFIG_EM)&CO_CONFIG_EM_PROD_CONFIGURABLE) != 0) || defined CO_DOXYGEN + uint16_t producerCanId; /**< COB ID of emergency message, from Object dictionary */ + uint16_t CANdevTxIdx; /**< From CO_EM_init() */ +#endif +#if (((CO_CONFIG_EM)&CO_CONFIG_EM_PROD_INHIBIT) != 0) || defined CO_DOXYGEN + uint32_t inhibitEmTime_us; /**< Inhibit time for emergency message, from Object dictionary */ + uint32_t inhibitEmTimer; /**< Internal timer for inhibit time */ + OD_extension_t OD_1015_extension; /**< Extension for OD object */ +#endif +#endif /* (CO_CONFIG_EM) & CO_CONFIG_EM_PRODUCER */ + +#if (((CO_CONFIG_EM)&CO_CONFIG_EM_HISTORY) != 0) || defined CO_DOXYGEN + OD_extension_t OD_1003_extension; /**< Extension for OD object */ +#endif + +#if (((CO_CONFIG_EM)&CO_CONFIG_EM_STATUS_BITS) != 0) || defined CO_DOXYGEN + OD_extension_t OD_statusBits_extension; /**< Extension for OD object */ +#endif + +#if (((CO_CONFIG_EM)&CO_CONFIG_EM_CONSUMER) != 0) || defined CO_DOXYGEN + void (*pFunctSignalRx)(const uint16_t ident, const uint16_t errorCode, const uint8_t errorRegister, + const uint8_t errorBit, const uint32_t infoCode); /**< From CO_EM_initCallbackRx() or NULL */ +#endif + +#if (((CO_CONFIG_EM)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0) || defined CO_DOXYGEN + void (*pFunctSignalPre)(void* object); /**< From CO_EM_initCallbackPre() or NULL */ + void* functSignalObjectPre; /**< From CO_EM_initCallbackPre() or NULL */ +#endif +} CO_EM_t; + +/** + * Initialize Emergency object. + * + * Function must be called in the communication reset section. + * + * @param em This object will be initialized. + * @param fifo Fifo buffer for emergency producer and error history. It must be defined externally. Its size must be + * capacity+1. See also @ref CO_EM_t, fifo. + * @param fifoSize Size of the above fifo buffer. It is usually equal to the length of the OD array 0x1003 + 1. If + * fifoSize is smaller than 2, then emergency producer and error history will not work and 'fifo' may be NULL. + * @param CANdevTx CAN device for Emergency transmission. + * @param OD_1001_errReg OD entry for 0x1001 - "Error register", entry is required, without IO extension. + * @param OD_1014_cobIdEm OD entry for 0x1014 - "COB-ID EMCY", entry is required, IO extension is required. + * @param CANdevTxIdx Index of transmit buffer in the above CAN device. + * @param OD_1015_InhTime OD entry for 0x1015 - "Inhibit time EMCY", entry is optional (can be NULL), IO extension is + * optional for runtime configuration. + * @param OD_1003_preDefErr OD entry for 0x1003 - "Pre-defined error field". Emergency object has own memory buffer for + * this entry. Entry is optional, IO extension is required. + * @param OD_statusBits Custom OD entry for accessing errorStatusBits from + * @ref CO_EM_t. Entry must have variable of size (CO_CONFIG_EM_ERR_STATUS_BITS_COUNT/8) bytes available for read/write + * access on subindex 0. Emergency object has own memory buffer for this entry. Entry is optional, IO extension is + * required. + * @param CANdevRx CAN device for Emergency consumer reception. + * @param CANdevRxIdx Index of receive buffer in the above CAN device. + * @param nodeId CANopen node ID of this device (for default emergency producer) + * @param [out] errInfo Additional information in case of error, may be NULL. + * + * @return @ref CO_ReturnError_t CO_ERROR_NO in case of success. + */ +CO_ReturnError_t CO_EM_init(CO_EM_t* em, CO_CANmodule_t* CANdevTx, const OD_entry_t* OD_1001_errReg, +#if (((CO_CONFIG_EM) & (CO_CONFIG_EM_PRODUCER | CO_CONFIG_EM_HISTORY)) != 0) || defined CO_DOXYGEN + CO_EM_fifo_t* fifo, uint8_t fifoSize, +#endif +#if (((CO_CONFIG_EM)&CO_CONFIG_EM_PRODUCER) != 0) || defined CO_DOXYGEN + OD_entry_t* OD_1014_cobIdEm, uint16_t CANdevTxIdx, +#if (((CO_CONFIG_EM)&CO_CONFIG_EM_PROD_INHIBIT) != 0) || defined CO_DOXYGEN + OD_entry_t* OD_1015_InhTime, +#endif +#endif +#if (((CO_CONFIG_EM)&CO_CONFIG_EM_HISTORY) != 0) || defined CO_DOXYGEN + OD_entry_t* OD_1003_preDefErr, +#endif +#if (((CO_CONFIG_EM)&CO_CONFIG_EM_STATUS_BITS) != 0) || defined CO_DOXYGEN + OD_entry_t* OD_statusBits, +#endif +#if (((CO_CONFIG_EM)&CO_CONFIG_EM_CONSUMER) != 0) || defined CO_DOXYGEN + CO_CANmodule_t* CANdevRx, uint16_t CANdevRxIdx, +#endif + const uint8_t nodeId, uint32_t* errInfo); + +#if (((CO_CONFIG_EM)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0) || defined CO_DOXYGEN +/** + * Initialize Emergency callback function. + * + * Function initializes optional callback function, which should immediately start processing of CO_EM_process() + * function. Callback is called from CO_errorReport() or CO_errorReset() function. Those functions are fast and may be + * called from any thread. Callback should immediately start mainline thread, which calls CO_EM_process() function. + * + * @param em This object. + * @param object Pointer to object, which will be passed to pFunctSignal(). Can be NULL + * @param pFunctSignal Pointer to the callback function. Not called if NULL. + */ +void CO_EM_initCallbackPre(CO_EM_t* em, void* object, void (*pFunctSignal)(void* object)); +#endif + +#if (((CO_CONFIG_EM)&CO_CONFIG_EM_CONSUMER) != 0) || defined CO_DOXYGEN +/** + * Initialize Emergency received callback function. + * + * Function initializes optional callback function, which executes after error condition is received. + * + * _ident_ argument from callback contains CAN-ID of the emergency message. If _ident_ == 0, then emergency message was + * sent from this device. + * + * @remark Depending on the CAN driver implementation, this function is called inside an ISR or inside a mainline. Must + * be thread safe. + * + * @param em This object. + * @param pFunctSignalRx Pointer to the callback function. Not called if NULL. + */ +void CO_EM_initCallbackRx(CO_EM_t* em, void (*pFunctSignalRx)(const uint16_t ident, const uint16_t errorCode, + const uint8_t errorRegister, const uint8_t errorBit, + const uint32_t infoCode)); +#endif + +/** + * Process Error control and Emergency object. + * + * Function must be called cyclically. It verifies some communication errors, calculates OD object 0x1001 - "Error + * register" and sends emergency message if necessary. + * + * @param em This object. + * @param NMTisPreOrOperational True if this node is NMT_PRE_OPERATIONAL or NMT_OPERATIONAL state. + * @param timeDifference_us Time difference from previous function call in [microseconds]. + * @param [out] timerNext_us info to OS - see CO_process(). + */ +void CO_EM_process(CO_EM_t* em, bool_t NMTisPreOrOperational, uint32_t timeDifference_us, uint32_t* timerNext_us); + +/** + * Set or reset error condition. + * + * Function can be called on any error condition inside CANopen stack or application. Function first checks change of + * error condition (setError is true and error bit wasn't set or setError is false and error bit was set before). If + * changed, then Emergency message is prepared and record in history is added. Emergency message is later sent by + * CO_EM_process() function. + * + * Function is short and thread safe. + * + * @param em Emergency object. + * @param setError True if error occurred or false if error resolved. + * @param errorBit from @ref CO_EM_errorStatusBits_t. + * @param errorCode from @ref CO_EM_errorCode_t. + * @param infoCode 32 bit value is passed to bytes 4...7 of the Emergency message. It contains optional additional + * information. + */ +void CO_error(CO_EM_t* em, bool_t setError, const uint8_t errorBit, uint16_t errorCode, uint32_t infoCode); + +/** + * Report error condition, for description of parameters see @ref CO_error. + */ +#define CO_errorReport(em, errorBit, errorCode, infoCode) CO_error(em, true, errorBit, errorCode, infoCode) + +/** + * Reset error condition, for description of parameters see @ref CO_error. + */ +#define CO_errorReset(em, errorBit, infoCode) CO_error(em, false, errorBit, CO_EMC_NO_ERROR, infoCode) + +/** + * Check specific error condition. + * + * Function returns true, if specific internal error is present. + * + * @param em Emergency object. + * @param errorBit from @ref CO_EM_errorStatusBits_t. + * + * @return true if Error is present. + */ +static inline bool_t +CO_isError(CO_EM_t* em, const uint8_t errorBit) { + uint8_t index = errorBit >> 3; + uint8_t bitmask = 1 << (errorBit & 0x7); + + return (em == NULL || index >= (CO_CONFIG_EM_ERR_STATUS_BITS_COUNT / 8U) + || (em->errorStatusBits[index] & bitmask) != 0) + ? true + : false; +} + +/** + * Get error register + * + * @param em Emergency object. + * + * @return Error register or 0 if doesn't exist. + */ +static inline uint8_t +CO_getErrorRegister(CO_EM_t* em) { + return (em == NULL || em->errorRegister == NULL) ? 0 : *em->errorRegister; +} + +/** @} */ /* CO_Emergency */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CO_EMERGENCY_H */ diff --git a/301/CO_HBconsumer.c b/301/CO_HBconsumer.c new file mode 100644 index 00000000..2216f9c9 --- /dev/null +++ b/301/CO_HBconsumer.c @@ -0,0 +1,488 @@ +/* + * CANopen Heartbeat consumer object. + * + * @file CO_HBconsumer.c + * @ingroup CO_HBconsumer + * @author Janez Paternoster + * @copyright 2021 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#include "301/CO_HBconsumer.h" + +#if ((CO_CONFIG_HB_CONS)&CO_CONFIG_HB_CONS_ENABLE) != 0 + +/* Verify HB consumer configuration */ +#if (((CO_CONFIG_HB_CONS)&CO_CONFIG_HB_CONS_CALLBACK_CHANGE) != 0) \ + && (((CO_CONFIG_HB_CONS)&CO_CONFIG_HB_CONS_CALLBACK_MULTI) != 0) +#error CO_CONFIG_HB_CONS_CALLBACK_CHANGE and CO_CONFIG_HB_CONS_CALLBACK_MULTI cannot be set simultaneously! +#endif + +/* + * Read received message from CAN module. + * + * Function will be called (by CAN receive interrupt) every time, when CAN message with correct identifier + * will be received. For more information and description of parameters see file CO_driver.h. + */ +static void +CO_HBcons_receive(void* object, void* msg) { + CO_HBconsNode_t* HBconsNode = object; + uint8_t DLC = CO_CANrxMsg_readDLC(msg); + const uint8_t* data = CO_CANrxMsg_readData(msg); + + if (DLC == 1U) { + /* copy data and set 'new message' flag. */ + HBconsNode->NMTstate = (CO_NMT_internalState_t)data[0]; + CO_FLAG_SET(HBconsNode->CANrxNew); +#if ((CO_CONFIG_HB_CONS)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0 + /* Optional signal to RTOS, which can resume task, which handles HBcons. */ + if (HBconsNode->pFunctSignalPre != NULL) { + HBconsNode->pFunctSignalPre(HBconsNode->functSignalObjectPre); + } +#endif + } +} + +/* + * Initialize one Heartbeat consumer entry + * + * This function is called from the @ref CO_HBconsumer_init() or when writing to OD entry 1016. + * + * @param HBcons This object. + * @param idx index of the node in HBcons object + * @param nodeId see OD 0x1016 description + * @param consumerTime_ms in milliseconds. see OD 0x1016 description + * @return + */ +static CO_ReturnError_t CO_HBconsumer_initEntry(CO_HBconsumer_t* HBcons, uint8_t idx, uint8_t nodeId, + uint16_t consumerTime_ms); + +#if ((CO_CONFIG_HB_CONS)&CO_CONFIG_FLAG_OD_DYNAMIC) != 0 +/* + * Custom function for writing OD object "Consumer heartbeat time" + * + * For more information see file CO_ODinterface.h, OD_IO_t. + */ +static ODR_t +OD_write_1016(OD_stream_t* stream, const void* buf, OD_size_t count, OD_size_t* countWritten) { + CO_HBconsumer_t* HBcons; + + if (stream == NULL) { + return ODR_DEV_INCOMPAT; + } + HBcons = stream->object; + + if ((buf == NULL) || (stream->subIndex < 1U) + || (stream->subIndex > HBcons->numberOfMonitoredNodes) || (count != sizeof(uint32_t)) + || (countWritten == NULL)) { + return ODR_DEV_INCOMPAT; + } + + uint32_t val = CO_getUint32(buf); + uint8_t nodeId = (uint8_t)((val >> 16) & 0xFFU); + uint16_t consumer_time = (uint16_t)(val & 0xFFFFU); + CO_ReturnError_t ret = CO_HBconsumer_initEntry(HBcons, stream->subIndex - 1U, nodeId, consumer_time); + if (ret != CO_ERROR_NO) { + return ODR_PAR_INCOMPAT; + } + + /* write value to the original location in the Object Dictionary */ + return OD_writeOriginal(stream, buf, count, countWritten); +} +#endif + +CO_ReturnError_t +CO_HBconsumer_init(CO_HBconsumer_t* HBcons, CO_EM_t* em, CO_HBconsNode_t* monitoredNodes, uint8_t monitoredNodesCount, + OD_entry_t* OD_1016_HBcons, CO_CANmodule_t* CANdevRx, uint16_t CANdevRxIdxStart, uint32_t* errInfo) { + ODR_t odRet; + + /* verify arguments */ + if ((HBcons == NULL) || (em == NULL) || (monitoredNodes == NULL) || (OD_1016_HBcons == NULL) + || (CANdevRx == NULL)) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + /* Configure object variables */ + (void)memset(HBcons, 0, sizeof(CO_HBconsumer_t)); + HBcons->em = em; + HBcons->monitoredNodes = monitoredNodes; + HBcons->CANdevRx = CANdevRx; + HBcons->CANdevRxIdxStart = CANdevRxIdxStart; + + /* get actual number of monitored nodes */ + HBcons->numberOfMonitoredNodes = ((OD_1016_HBcons->subEntriesCount - 1U) < monitoredNodesCount) + ? (OD_1016_HBcons->subEntriesCount - 1U) + : monitoredNodesCount; + + for (uint8_t i = 0; i < HBcons->numberOfMonitoredNodes; i++) { + uint32_t val; + odRet = OD_get_u32(OD_1016_HBcons, i + 1U, &val, true); + if (odRet != ODR_OK) { + if (errInfo != NULL) { + *errInfo = OD_getIndex(OD_1016_HBcons); + } + return CO_ERROR_OD_PARAMETERS; + } + + uint8_t nodeId = (uint8_t)((val >> 16) & 0xFFU); + uint16_t consumer_time = (uint16_t)(val & 0xFFFFU); + CO_ReturnError_t ret = CO_HBconsumer_initEntry(HBcons, i, nodeId, consumer_time); + if (ret != CO_ERROR_NO) { + if (errInfo != NULL) { + *errInfo = OD_getIndex(OD_1016_HBcons); + } + /* don't break a program, if only value of a parameter is wrong */ + if (ret != CO_ERROR_OD_PARAMETERS) { + return ret; + } + } + } + + /* configure extension for OD */ +#if ((CO_CONFIG_HB_CONS)&CO_CONFIG_FLAG_OD_DYNAMIC) != 0 + HBcons->OD_1016_extension.object = HBcons; + HBcons->OD_1016_extension.read = OD_readOriginal; + HBcons->OD_1016_extension.write = OD_write_1016; + odRet = OD_extension_init(OD_1016_HBcons, &HBcons->OD_1016_extension); + if (odRet != ODR_OK) { + if (errInfo != NULL) { + *errInfo = OD_getIndex(OD_1016_HBcons); + } + return CO_ERROR_OD_PARAMETERS; + } +#endif + + return CO_ERROR_NO; +} + +static CO_ReturnError_t +CO_HBconsumer_initEntry(CO_HBconsumer_t* HBcons, uint8_t idx, uint8_t nodeId, uint16_t consumerTime_ms) { + CO_ReturnError_t ret = CO_ERROR_NO; + + /* verify arguments */ + if ((HBcons == NULL) || (idx >= HBcons->numberOfMonitoredNodes)) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + /* verify for duplicate entries */ + if ((consumerTime_ms != 0U) && (nodeId != 0U)) { + for (uint8_t i = 0; i < HBcons->numberOfMonitoredNodes; i++) { + CO_HBconsNode_t node = HBcons->monitoredNodes[i]; + if ((idx != i) && (node.time_us != 0U) && (node.nodeId == nodeId)) { + ret = CO_ERROR_OD_PARAMETERS; + } + } + } + + /* Configure one monitored node */ + if (ret == CO_ERROR_NO) { + uint16_t COB_ID; + + CO_HBconsNode_t* monitoredNode = &HBcons->monitoredNodes[idx]; + monitoredNode->nodeId = nodeId; + monitoredNode->time_us = (uint32_t)consumerTime_ms * 1000U; + monitoredNode->NMTstate = CO_NMT_UNKNOWN; +#if (((CO_CONFIG_HB_CONS)&CO_CONFIG_HB_CONS_CALLBACK_CHANGE) != 0) \ + || (((CO_CONFIG_HB_CONS)&CO_CONFIG_HB_CONS_CALLBACK_MULTI) != 0) + monitoredNode->NMTstatePrev = CO_NMT_UNKNOWN; +#endif + CO_FLAG_CLEAR(monitoredNode->CANrxNew); + + /* is channel used */ + if ((monitoredNode->nodeId != 0U) && (monitoredNode->time_us != 0U)) { + COB_ID = monitoredNode->nodeId + (uint16_t)CO_CAN_ID_HEARTBEAT; + monitoredNode->HBstate = CO_HBconsumer_UNKNOWN; + } else { + COB_ID = 0; + monitoredNode->time_us = 0; + monitoredNode->HBstate = CO_HBconsumer_UNCONFIGURED; + } + + /* configure Heartbeat consumer (or disable) CAN reception */ + ret = CO_CANrxBufferInit(HBcons->CANdevRx, HBcons->CANdevRxIdxStart + idx, COB_ID, 0x7FF, false, + (void*)&HBcons->monitoredNodes[idx], CO_HBcons_receive); + } + return ret; +} + +#if ((CO_CONFIG_HB_CONS)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0 +void +CO_HBconsumer_initCallbackPre(CO_HBconsumer_t* HBcons, void* object, void (*pFunctSignal)(void* object)) { + if (HBcons != NULL) { + uint8_t i; + for (i = 0; i < HBcons->numberOfMonitoredNodes; i++) { + HBcons->monitoredNodes[i].pFunctSignalPre = pFunctSignal; + HBcons->monitoredNodes[i].functSignalObjectPre = object; + } + } +} +#endif + +#if ((CO_CONFIG_HB_CONS)&CO_CONFIG_HB_CONS_CALLBACK_CHANGE) != 0 +void +CO_HBconsumer_initCallbackNmtChanged(CO_HBconsumer_t* HBcons, uint8_t idx, void* object, + void (*pFunctSignal)(uint8_t nodeId, uint8_t idx, CO_NMT_internalState_t NMTstate, + void* object)) { + (void)idx; + if (HBcons == NULL) { + return; + } + + HBcons->pFunctSignalNmtChanged = pFunctSignal; + HBcons->pFunctSignalObjectNmtChanged = object; +} +#endif + +#if ((CO_CONFIG_HB_CONS)&CO_CONFIG_HB_CONS_CALLBACK_MULTI) != 0 +void +CO_HBconsumer_initCallbackNmtChanged(CO_HBconsumer_t* HBcons, uint8_t idx, void* object, + void (*pFunctSignal)(uint8_t nodeId, uint8_t idx, CO_NMT_internalState_t NMTstate, + void* object)) { + if (HBcons == NULL || idx >= HBcons->numberOfMonitoredNodes) { + return; + } + + CO_HBconsNode_t* const monitoredNode = &HBcons->monitoredNodes[idx]; + monitoredNode->pFunctSignalNmtChanged = pFunctSignal; + monitoredNode->pFunctSignalObjectNmtChanged = object; +} + +void +CO_HBconsumer_initCallbackHeartbeatStarted(CO_HBconsumer_t* HBcons, uint8_t idx, void* object, + void (*pFunctSignal)(uint8_t nodeId, uint8_t idx, void* object)) { + CO_HBconsNode_t* monitoredNode; + + if (HBcons == NULL || idx >= HBcons->numberOfMonitoredNodes) { + return; + } + + monitoredNode = &HBcons->monitoredNodes[idx]; + monitoredNode->pFunctSignalHbStarted = pFunctSignal; + monitoredNode->functSignalObjectHbStarted = object; +} + +void +CO_HBconsumer_initCallbackTimeout(CO_HBconsumer_t* HBcons, uint8_t idx, void* object, + void (*pFunctSignal)(uint8_t nodeId, uint8_t idx, void* object)) { + CO_HBconsNode_t* monitoredNode; + + if (HBcons == NULL || idx >= HBcons->numberOfMonitoredNodes) { + return; + } + + monitoredNode = &HBcons->monitoredNodes[idx]; + monitoredNode->pFunctSignalTimeout = pFunctSignal; + monitoredNode->functSignalObjectTimeout = object; +} + +void +CO_HBconsumer_initCallbackRemoteReset(CO_HBconsumer_t* HBcons, uint8_t idx, void* object, + void (*pFunctSignal)(uint8_t nodeId, uint8_t idx, void* object)) { + CO_HBconsNode_t* monitoredNode; + + if (HBcons == NULL || idx >= HBcons->numberOfMonitoredNodes) { + return; + } + + monitoredNode = &HBcons->monitoredNodes[idx]; + monitoredNode->pFunctSignalRemoteReset = pFunctSignal; + monitoredNode->functSignalObjectRemoteReset = object; +} +#endif /* (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_MULTI */ + +void +CO_HBconsumer_process(CO_HBconsumer_t* HBcons, bool_t NMTisPreOrOperational, uint32_t timeDifference_us, + uint32_t* timerNext_us) { + (void)timerNext_us; /* may be unused */ + + bool_t allMonitoredActiveCurrent = true; + bool_t allMonitoredOperationalCurrent = true; + + if (NMTisPreOrOperational && HBcons->NMTisPreOrOperationalPrev) { + for (uint8_t i = 0; i < HBcons->numberOfMonitoredNodes; i++) { + uint32_t timeDifference_us_copy = timeDifference_us; + CO_HBconsNode_t* const monitoredNode = &HBcons->monitoredNodes[i]; + + if (monitoredNode->HBstate == CO_HBconsumer_UNCONFIGURED) { + /* continue, if node is not monitored */ + continue; + } + /* Verify if received message is heartbeat or bootup */ + if (CO_FLAG_READ(monitoredNode->CANrxNew)) { + if (monitoredNode->NMTstate == CO_NMT_INITIALIZING) { + /* bootup message */ +#if ((CO_CONFIG_HB_CONS)&CO_CONFIG_HB_CONS_CALLBACK_MULTI) != 0 + if (monitoredNode->pFunctSignalRemoteReset != NULL) { + monitoredNode->pFunctSignalRemoteReset(monitoredNode->nodeId, i, + monitoredNode->functSignalObjectRemoteReset); + } +#endif + if (monitoredNode->HBstate == CO_HBconsumer_ACTIVE) { + CO_errorReport(HBcons->em, CO_EM_HB_CONSUMER_REMOTE_RESET, CO_EMC_HEARTBEAT, i); + } + monitoredNode->HBstate = CO_HBconsumer_UNKNOWN; + + } else { + /* heartbeat message */ +#if ((CO_CONFIG_HB_CONS)&CO_CONFIG_HB_CONS_CALLBACK_MULTI) != 0 + if (monitoredNode->HBstate != CO_HBconsumer_ACTIVE + && monitoredNode->pFunctSignalHbStarted != NULL) { + monitoredNode->pFunctSignalHbStarted(monitoredNode->nodeId, i, + monitoredNode->functSignalObjectHbStarted); + } +#endif + monitoredNode->HBstate = CO_HBconsumer_ACTIVE; + /* reset timer */ + monitoredNode->timeoutTimer = 0; + timeDifference_us_copy = 0; + } + CO_FLAG_CLEAR(monitoredNode->CANrxNew); + } + + /* Verify timeout */ + if (monitoredNode->HBstate == CO_HBconsumer_ACTIVE) { + monitoredNode->timeoutTimer += timeDifference_us_copy; + + if (monitoredNode->timeoutTimer >= monitoredNode->time_us) { + /* timeout expired */ +#if ((CO_CONFIG_HB_CONS)&CO_CONFIG_HB_CONS_CALLBACK_MULTI) != 0 + if (monitoredNode->pFunctSignalTimeout != NULL) { + monitoredNode->pFunctSignalTimeout(monitoredNode->nodeId, i, + monitoredNode->functSignalObjectTimeout); + } +#endif + CO_errorReport(HBcons->em, CO_EM_HEARTBEAT_CONSUMER, CO_EMC_HEARTBEAT, i); + monitoredNode->NMTstate = CO_NMT_UNKNOWN; + monitoredNode->HBstate = CO_HBconsumer_TIMEOUT; + } + +#if ((CO_CONFIG_HB_CONS)&CO_CONFIG_FLAG_TIMERNEXT) != 0 + else if (timerNext_us != NULL) { + /* Calculate timerNext_us for next timeout checking. */ + uint32_t diff = monitoredNode->time_us - monitoredNode->timeoutTimer; + if (*timerNext_us > diff) { + *timerNext_us = diff; + } + } +#endif + } + + if (monitoredNode->HBstate != CO_HBconsumer_ACTIVE) { + allMonitoredActiveCurrent = false; + } + if (monitoredNode->NMTstate != CO_NMT_OPERATIONAL) { + allMonitoredOperationalCurrent = false; + } +#if (((CO_CONFIG_HB_CONS)&CO_CONFIG_HB_CONS_CALLBACK_CHANGE) != 0) \ + || (((CO_CONFIG_HB_CONS)&CO_CONFIG_HB_CONS_CALLBACK_MULTI) != 0) + /* Verify, if NMT state of monitored node changed */ + if (monitoredNode->NMTstate != monitoredNode->NMTstatePrev) { +#if ((CO_CONFIG_HB_CONS)&CO_CONFIG_HB_CONS_CALLBACK_CHANGE) != 0 + if (HBcons->pFunctSignalNmtChanged != NULL) { + HBcons->pFunctSignalNmtChanged(monitoredNode->nodeId, i, monitoredNode->NMTstate, + HBcons->pFunctSignalObjectNmtChanged); +#else + if (monitoredNode->pFunctSignalNmtChanged != NULL) { + monitoredNode->pFunctSignalNmtChanged(monitoredNode->nodeId, i, monitoredNode->NMTstate, + monitoredNode->pFunctSignalObjectNmtChanged); +#endif + } + monitoredNode->NMTstatePrev = monitoredNode->NMTstate; + } +#endif + } + } else if (NMTisPreOrOperational || HBcons->NMTisPreOrOperationalPrev) { + /* (pre)operational state changed, clear variables */ + for (uint8_t i = 0; i < HBcons->numberOfMonitoredNodes; i++) { + CO_HBconsNode_t* const monitoredNode = &HBcons->monitoredNodes[i]; + monitoredNode->NMTstate = CO_NMT_UNKNOWN; +#if (((CO_CONFIG_HB_CONS)&CO_CONFIG_HB_CONS_CALLBACK_CHANGE) != 0) \ + || (((CO_CONFIG_HB_CONS)&CO_CONFIG_HB_CONS_CALLBACK_MULTI) != 0) + monitoredNode->NMTstatePrev = CO_NMT_UNKNOWN; +#endif + CO_FLAG_CLEAR(monitoredNode->CANrxNew); + if (monitoredNode->HBstate != CO_HBconsumer_UNCONFIGURED) { + monitoredNode->HBstate = CO_HBconsumer_UNKNOWN; + } + } + allMonitoredActiveCurrent = false; + allMonitoredOperationalCurrent = false; + } else { /* MISRA C 2004 14.10 */ + } + + /* Clear emergencies when all monitored nodes becomes active. + * We only have one emergency index for all monitored nodes! */ + if (!HBcons->allMonitoredActive && allMonitoredActiveCurrent) { + CO_errorReset(HBcons->em, CO_EM_HEARTBEAT_CONSUMER, 0); + CO_errorReset(HBcons->em, CO_EM_HB_CONSUMER_REMOTE_RESET, 0); + } + + HBcons->allMonitoredActive = allMonitoredActiveCurrent; + HBcons->allMonitoredOperational = allMonitoredOperationalCurrent; + HBcons->NMTisPreOrOperationalPrev = NMTisPreOrOperational; +} + +#if ((CO_CONFIG_HB_CONS)&CO_CONFIG_HB_CONS_QUERY_FUNCT) != 0 +int8_t +CO_HBconsumer_getIdxByNodeId(CO_HBconsumer_t* HBcons, uint8_t nodeId) { + uint8_t i; + CO_HBconsNode_t* monitoredNode; + + if (HBcons == NULL) { + return -1; + } + + /* linear search for the node */ + monitoredNode = &HBcons->monitoredNodes[0]; + for (i = 0; i < HBcons->numberOfMonitoredNodes; i++) { + if (monitoredNode->nodeId == nodeId) { + return i; + } + monitoredNode++; + } + /* not found */ + return -1; +} + +CO_HBconsumer_state_t +CO_HBconsumer_getState(CO_HBconsumer_t* HBcons, uint8_t idx) { + CO_HBconsNode_t* monitoredNode; + + if (HBcons == NULL || idx >= HBcons->numberOfMonitoredNodes) { + return CO_HBconsumer_UNCONFIGURED; + } + + monitoredNode = &HBcons->monitoredNodes[idx]; + return monitoredNode->HBstate; +} + +int8_t +CO_HBconsumer_getNmtState(CO_HBconsumer_t* HBcons, uint8_t idx, CO_NMT_internalState_t* nmtState) { + CO_HBconsNode_t* monitoredNode; + + if (HBcons == NULL || nmtState == NULL || idx >= HBcons->numberOfMonitoredNodes) { + return -1; + } + *nmtState = CO_NMT_INITIALIZING; + + monitoredNode = &HBcons->monitoredNodes[idx]; + + if (monitoredNode->HBstate == CO_HBconsumer_ACTIVE) { + *nmtState = monitoredNode->NMTstate; + return 0; + } + return -1; +} +#endif /* (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_QUERY_FUNCT */ + +#endif /* (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_ENABLE */ diff --git a/301/CO_HBconsumer.h b/301/CO_HBconsumer.h new file mode 100644 index 00000000..904781e0 --- /dev/null +++ b/301/CO_HBconsumer.h @@ -0,0 +1,284 @@ +/** + * CANopen Heartbeat consumer protocol. + * + * @file CO_HBconsumer.h + * @ingroup CO_HBconsumer + * @author Janez Paternoster + * @copyright 2021 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#ifndef CO_HB_CONS_H +#define CO_HB_CONS_H + +#include "301/CO_driver.h" +#include "301/CO_ODinterface.h" +#include "301/CO_NMT_Heartbeat.h" +#include "301/CO_Emergency.h" + +/* default configuration, see CO_config.h */ +#ifndef CO_CONFIG_HB_CONS +#define CO_CONFIG_HB_CONS \ + (CO_CONFIG_HB_CONS_ENABLE | CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | CO_CONFIG_GLOBAL_FLAG_TIMERNEXT \ + | CO_CONFIG_GLOBAL_FLAG_OD_DYNAMIC) +#endif + +#if (((CO_CONFIG_HB_CONS)&CO_CONFIG_HB_CONS_ENABLE) != 0) || defined CO_DOXYGEN + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup CO_HBconsumer Heartbeat consumer + * CANopen Heartbeat consumer protocol. + * + * @ingroup CO_CANopen_301 + * @{ + * Heartbeat consumer monitors Heartbeat messages from remote nodes. If any monitored node don't send his Heartbeat in + * specified time, Heartbeat consumer sends emergency message. If all monitored nodes are operational, then variable + * _allMonitoredOperational_ inside CO_HBconsumer_t is set to true. Monitoring starts after the reception of the first + * HeartBeat (not bootup). + * + * Heartbeat set up is done by writing to the OD registers 0x1016. To setup heartbeat consumer by application, use + * @code ODR_t odRet = OD_set_u32(entry, subIndex, val, false); @endcode + * + * @see @ref CO_NMT_Heartbeat + */ + +/** + * Heartbeat state of a node + */ +typedef enum { + CO_HBconsumer_UNCONFIGURED = 0x00U, /**< Consumer entry inactive */ + CO_HBconsumer_UNKNOWN = 0x01U, /**< Consumer enabled, but no heartbeat received yet */ + CO_HBconsumer_ACTIVE = 0x02U, /**< Heartbeat received within set time */ + CO_HBconsumer_TIMEOUT = 0x03U, /**< No heatbeat received for set time */ +} CO_HBconsumer_state_t; + +/** + * One monitored node inside CO_HBconsumer_t. + */ +typedef struct { + uint8_t nodeId; /**< Node Id of the monitored node */ + CO_NMT_internalState_t NMTstate; /**< NMT state of the remote node (Heartbeat payload) */ + CO_HBconsumer_state_t HBstate; /**< Current heartbeat monitoring state of the remote node */ + uint32_t timeoutTimer; /**< Time since last heartbeat received */ + uint32_t time_us; /**< Consumer heartbeat time from OD */ + volatile void* CANrxNew; /**< Indication if new Heartbeat message received from the CAN bus */ +#if (((CO_CONFIG_HB_CONS)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0) || defined CO_DOXYGEN + void (*pFunctSignalPre)(void* object); /**< From CO_HBconsumer_initCallbackPre() or NULL */ + void* functSignalObjectPre; /**< From CO_HBconsumer_initCallbackPre() or NULL */ +#endif +#if (((CO_CONFIG_HB_CONS)&CO_CONFIG_HB_CONS_CALLBACK_CHANGE) != 0) \ + || (((CO_CONFIG_HB_CONS)&CO_CONFIG_HB_CONS_CALLBACK_MULTI) != 0) || defined CO_DOXYGEN + CO_NMT_internalState_t NMTstatePrev; /**< Previous value of the remote node (Heartbeat payload) */ +#endif +#if (((CO_CONFIG_HB_CONS)&CO_CONFIG_HB_CONS_CALLBACK_MULTI) != 0) || defined CO_DOXYGEN + /** Callback for remote NMT changed event. From CO_HBconsumer_initCallbackNmtChanged() or NULL. */ + void (*pFunctSignalNmtChanged)(uint8_t nodeId, uint8_t idx, CO_NMT_internalState_t NMTstate, void* object); + void* pFunctSignalObjectNmtChanged; /**< Pointer to object */ + /** Callback for heartbeat state change to active event. From CO_HBconsumer_initCallbackHeartbeatStarted() or NULL. + */ + void (*pFunctSignalHbStarted)(uint8_t nodeId, uint8_t idx, void* object); + void* functSignalObjectHbStarted; /**< Pointer to object */ + /** Callback for consumer timeout event. From CO_HBconsumer_initCallbackTimeout() or NULL. */ + void (*pFunctSignalTimeout)(uint8_t nodeId, uint8_t idx, void* object); + void* functSignalObjectTimeout; /**< Pointer to object */ + /** Callback for remote reset event. From CO_HBconsumer_initCallbackRemoteReset() or NULL. */ + void (*pFunctSignalRemoteReset)(uint8_t nodeId, uint8_t idx, void* object); + void* functSignalObjectRemoteReset; /**< Pointer to object */ +#endif +} CO_HBconsNode_t; + +/** + * Heartbeat consumer object. + * + * Object is initilaized by CO_HBconsumer_init(). It contains an array of CO_HBconsNode_t objects. + */ +typedef struct { + CO_EM_t* em; /**< From CO_HBconsumer_init() */ + CO_HBconsNode_t* monitoredNodes; /**< Array of monitored nodes, from CO_HBconsumer_init() */ + uint8_t numberOfMonitoredNodes; /**< Actual number of monitored nodes, size-of-the-above-array or + number-of-array-elements-in-OD-0x1016, whichever is smaller. */ + bool_t allMonitoredActive; /**< True, if all monitored nodes are active or no node is monitored. Can be read + by the application */ + bool_t allMonitoredOperational; /**< True, if all monitored nodes are NMT operational or no node is monitored. Can + be read by the application */ + bool_t NMTisPreOrOperationalPrev; /**< previous state of the variable */ + CO_CANmodule_t* CANdevRx; /**< From CO_HBconsumer_init() */ + uint16_t CANdevRxIdxStart; /**< From CO_HBconsumer_init() */ +#if (((CO_CONFIG_HB_CONS)&CO_CONFIG_FLAG_OD_DYNAMIC) != 0) || defined CO_DOXYGEN + OD_extension_t OD_1016_extension; /**< Extension for OD object */ +#endif +#if (((CO_CONFIG_HB_CONS)&CO_CONFIG_HB_CONS_CALLBACK_CHANGE) != 0) || defined CO_DOXYGEN + /** Callback for remote NMT changed event. From CO_HBconsumer_initCallbackNmtChanged() or NULL. */ + void (*pFunctSignalNmtChanged)(uint8_t nodeId, uint8_t idx, CO_NMT_internalState_t NMTstate, void* object); + void* pFunctSignalObjectNmtChanged; /**< Pointer to object */ +#endif +} CO_HBconsumer_t; + +/** + * Initialize Heartbeat consumer object. + * + * Function must be called in the communication reset section. + * + * @param HBcons This object will be initialized. + * @param em Emergency object. + * @param monitoredNodes Array of monitored nodes, must be defined externally. + * @param monitoredNodesCount Size of the above array, usually equal to number of array elements in OD 0x1016, valid + * values are 1 to 127 + * @param OD_1016_HBcons OD entry for 0x1016 - "Consumer heartbeat time", entry is required, IO extension will be + * applied. + * @param CANdevRx CAN device for Heartbeat reception. + * @param CANdevRxIdxStart Starting index of receive buffer in the above CAN device. Number of used indexes is equal to + * monitoredNodesCount. + * @param [out] errInfo Additional information in case of error, may be NULL. + * + * @return @ref CO_ReturnError_t CO_ERROR_NO in case of success. + */ +CO_ReturnError_t CO_HBconsumer_init(CO_HBconsumer_t* HBcons, CO_EM_t* em, CO_HBconsNode_t* monitoredNodes, + uint8_t monitoredNodesCount, OD_entry_t* OD_1016_HBcons, CO_CANmodule_t* CANdevRx, + uint16_t CANdevRxIdxStart, uint32_t* errInfo); + +#if (((CO_CONFIG_HB_CONS)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0) || defined CO_DOXYGEN +/** + * Initialize Heartbeat consumer callback function. + * + * Function initializes optional callback function, which should immediately start processing of CO_HBconsumer_process() + * function. Callback is called after HBconsumer message is received from the CAN bus. + * + * @param HBcons This object. + * @param object Pointer to object, which will be passed to pFunctSignal(). Can be NULL + * @param pFunctSignal Pointer to the callback function. Not called if NULL. + */ +void CO_HBconsumer_initCallbackPre(CO_HBconsumer_t* HBcons, void* object, void (*pFunctSignal)(void* object)); +#endif + +#if (((CO_CONFIG_HB_CONS)&CO_CONFIG_HB_CONS_CALLBACK_CHANGE) != 0) \ + || (((CO_CONFIG_HB_CONS)&CO_CONFIG_HB_CONS_CALLBACK_MULTI) != 0) || defined CO_DOXYGEN +/** + * Initialize Heartbeat consumer NMT changed callback function. + * + * Function initializes optional callback function, which is called when NMT state from the remote node changes. + * + * @param HBcons This object. + * @param idx index of the node in HBcons object (only when CO_CONFIG_HB_CONS_CALLBACK_MULTI is enabled) + * @param object Pointer to object, which will be passed to pFunctSignal(). Can be NULL. + * @param pFunctSignal Pointer to the callback function. Not called if NULL. + */ +void CO_HBconsumer_initCallbackNmtChanged(CO_HBconsumer_t* HBcons, uint8_t idx, void* object, + void (*pFunctSignal)(uint8_t nodeId, uint8_t idx, + CO_NMT_internalState_t NMTstate, void* object)); +#endif + +#if (((CO_CONFIG_HB_CONS)&CO_CONFIG_HB_CONS_CALLBACK_MULTI) != 0) || defined CO_DOXYGEN +/** + * Initialize Heartbeat consumer started callback function. + * + * Function initializes optional callback function, which is called for the first received heartbeat after activating hb + * consumer or timeout. Function may wake up external task, which handles this event. + * + * @param HBcons This object. + * @param idx index of the node in HBcons object + * @param object Pointer to object, which will be passed to pFunctSignal(). Can be NULL + * @param pFunctSignal Pointer to the callback function. Not called if NULL. + */ +void CO_HBconsumer_initCallbackHeartbeatStarted(CO_HBconsumer_t* HBcons, uint8_t idx, void* object, + void (*pFunctSignal)(uint8_t nodeId, uint8_t idx, void* object)); + +/** + * Initialize Heartbeat consumer timeout callback function. + * + * Function initializes optional callback function, which is called when the node state changes from active to timeout. + * Function may wake up external task, which handles this event. + * + * @param HBcons This object. + * @param idx index of the node in HBcons object + * @param object Pointer to object, which will be passed to pFunctSignal(). Can be NULL + * @param pFunctSignal Pointer to the callback function. Not called if NULL. + */ +void CO_HBconsumer_initCallbackTimeout(CO_HBconsumer_t* HBcons, uint8_t idx, void* object, + void (*pFunctSignal)(uint8_t nodeId, uint8_t idx, void* object)); + +/** + * Initialize Heartbeat consumer remote reset detected callback function. + * + * Function initializes optional callback function, which is called when a bootup message is received from the remote + * node. Function may wake up external task, which handles this event. + * + * @param HBcons This object. + * @param idx index of the node in HBcons object + * @param object Pointer to object, which will be passed to pFunctSignal(). Can be NULL + * @param pFunctSignal Pointer to the callback function. Not called if NULL. + */ +void CO_HBconsumer_initCallbackRemoteReset(CO_HBconsumer_t* HBcons, uint8_t idx, void* object, + void (*pFunctSignal)(uint8_t nodeId, uint8_t idx, void* object)); +#endif /* (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_MULTI */ + +/** + * Process Heartbeat consumer object. + * + * Function must be called cyclically. + * + * @param HBcons This object. + * @param NMTisPreOrOperational True if this node is NMT_PRE_OPERATIONAL or NMT_OPERATIONAL. + * @param timeDifference_us Time difference from previous function call in [microseconds]. + * @param [out] timerNext_us info to OS - see CO_process(). + */ +void CO_HBconsumer_process(CO_HBconsumer_t* HBcons, bool_t NMTisPreOrOperational, uint32_t timeDifference_us, + uint32_t* timerNext_us); + +#if (((CO_CONFIG_HB_CONS)&CO_CONFIG_HB_CONS_QUERY_FUNCT) != 0) || defined CO_DOXYGEN +/** + * Get the heartbeat producer object index by node ID + * + * @param HBcons This object. + * @param nodeId producer node ID + * @return index. -1 if not found + */ +int8_t CO_HBconsumer_getIdxByNodeId(CO_HBconsumer_t* HBcons, uint8_t nodeId); + +/** + * Get the current state of a heartbeat producer by the index in OD 0x1016 + * + * @param HBcons This object. + * @param idx object sub index + * @return #CO_HBconsumer_state_t + */ +CO_HBconsumer_state_t CO_HBconsumer_getState(CO_HBconsumer_t* HBcons, uint8_t idx); + +/** + * Get the current NMT state of a heartbeat producer by the index in OD 0x1016 + * + * NMT state is only available when heartbeat is enabled for this index! + * + * @param HBcons This object. + * @param idx object sub index + * @param [out] nmtState of this index + * @retval 0 NMT state has been received and is valid + * @retval -1 not valid + */ +int8_t CO_HBconsumer_getNmtState(CO_HBconsumer_t* HBcons, uint8_t idx, CO_NMT_internalState_t* nmtState); + +#endif /* (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_QUERY_FUNCT */ + +/** @} */ /* CO_HBconsumer */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_ENABLE */ + +#endif /* CO_HB_CONS_H */ diff --git a/301/CO_NMT_Heartbeat.c b/301/CO_NMT_Heartbeat.c new file mode 100644 index 00000000..e3a420c2 --- /dev/null +++ b/301/CO_NMT_Heartbeat.c @@ -0,0 +1,290 @@ +/* + * CANopen NMT and Heartbeat producer object. + * + * @file CO_NMT_Heartbeat.c + * @ingroup CO_NMT_Heartbeat + * @author Janez Paternoster + * @copyright 2004 - 2020 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#include "301/CO_NMT_Heartbeat.h" + +/* + * Read received message from CAN module. + * + * Function will be called (by CAN receive interrupt) every time, when CAN message with correct identifier + * will be received. For more information and description of parameters see file CO_driver.h. + */ +static void +CO_NMT_receive(void* object, void* msg) { + uint8_t DLC = CO_CANrxMsg_readDLC(msg); + const uint8_t* data = CO_CANrxMsg_readData(msg); + CO_NMT_command_t command = (CO_NMT_command_t)data[0]; + uint8_t nodeId = data[1]; + + CO_NMT_t* NMT = (CO_NMT_t*)object; + + if ((DLC == 2U) && ((nodeId == 0U) || (nodeId == NMT->nodeId))) { + NMT->internalCommand = command; + +#if ((CO_CONFIG_NMT)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0 + /* Optional signal to RTOS, which can resume task, which handles NMT. */ + if (NMT->pFunctSignalPre != NULL) { + NMT->pFunctSignalPre(NMT->functSignalObjectPre); + } +#endif + } +} + +/* + * Custom function for writing OD object "Producer heartbeat time" + * + * For more information see file CO_ODinterface.h, OD_IO_t. + */ +static ODR_t +OD_write_1017(OD_stream_t* stream, const void* buf, OD_size_t count, OD_size_t* countWritten) { + if ((stream == NULL) || (stream->subIndex != 0U) || (buf == NULL) || (count != sizeof(uint16_t)) + || (countWritten == NULL)) { + return ODR_DEV_INCOMPAT; + } + + CO_NMT_t* NMT = (CO_NMT_t*)stream->object; + + /* update object, send Heartbeat immediately */ + NMT->HBproducerTime_us = (uint32_t)CO_getUint16(buf) * 1000U; + NMT->HBproducerTimer = 0; + + /* write value to the original location in the Object Dictionary */ + return OD_writeOriginal(stream, buf, count, countWritten); +} + +CO_ReturnError_t +CO_NMT_init(CO_NMT_t* NMT, OD_entry_t* OD_1017_ProducerHbTime, CO_EM_t* em, uint8_t nodeId, uint16_t NMTcontrol, + uint16_t firstHBTime_ms, CO_CANmodule_t* NMT_CANdevRx, uint16_t NMT_rxIdx, uint16_t CANidRxNMT, +#if (((CO_CONFIG_NMT)&CO_CONFIG_NMT_MASTER) != 0) || defined CO_DOXYGEN + CO_CANmodule_t* NMT_CANdevTx, uint16_t NMT_txIdx, uint16_t CANidTxNMT, +#endif + CO_CANmodule_t* HB_CANdevTx, uint16_t HB_txIdx, uint16_t CANidTxHB, uint32_t* errInfo) { + CO_ReturnError_t ret = CO_ERROR_NO; + + /* verify arguments */ + if ((NMT == NULL) || (OD_1017_ProducerHbTime == NULL) || (em == NULL) || (NMT_CANdevRx == NULL) + || (HB_CANdevTx == NULL) +#if ((CO_CONFIG_NMT)&CO_CONFIG_NMT_MASTER) != 0 + || (NMT_CANdevTx == NULL) +#endif + ) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + /* clear the object */ + (void)memset(NMT, 0, sizeof(CO_NMT_t)); + + /* Configure object variables */ + NMT->operatingState = CO_NMT_INITIALIZING; + NMT->operatingStatePrev = CO_NMT_INITIALIZING; + NMT->nodeId = nodeId; + NMT->NMTcontrol = NMTcontrol; + NMT->em = em; + NMT->HBproducerTimer = (uint32_t)firstHBTime_ms * 1000U; + + /* get and verify required "Producer heartbeat time" from Object Dict. */ + uint16_t HBprodTime_ms; + ODR_t odRet = OD_get_u16(OD_1017_ProducerHbTime, 0, &HBprodTime_ms, true); + if (odRet != ODR_OK) { + if (errInfo != NULL) { + *errInfo = OD_getIndex(OD_1017_ProducerHbTime); + } + return CO_ERROR_OD_PARAMETERS; + } + NMT->HBproducerTime_us = (uint32_t)HBprodTime_ms * 1000U; + + NMT->OD_1017_extension.object = NMT; + NMT->OD_1017_extension.read = OD_readOriginal; + NMT->OD_1017_extension.write = OD_write_1017; + odRet = OD_extension_init(OD_1017_ProducerHbTime, &NMT->OD_1017_extension); + if (odRet != ODR_OK) { + if (errInfo != NULL) { + *errInfo = OD_getIndex(OD_1017_ProducerHbTime); + } + return CO_ERROR_OD_PARAMETERS; + } + + if (NMT->HBproducerTimer > NMT->HBproducerTime_us) { + NMT->HBproducerTimer = NMT->HBproducerTime_us; + } + + /* configure NMT CAN reception */ + ret = CO_CANrxBufferInit(NMT_CANdevRx, NMT_rxIdx, CANidRxNMT, 0x7FF, false, (void*)NMT, CO_NMT_receive); + if (ret != CO_ERROR_NO) { + return ret; + } + +#if ((CO_CONFIG_NMT)&CO_CONFIG_NMT_MASTER) != 0 + /* configure NMT CAN transmission */ + NMT->NMT_CANdevTx = NMT_CANdevTx; + NMT->NMT_TXbuff = CO_CANtxBufferInit(NMT_CANdevTx, NMT_txIdx, CANidTxNMT, false, 2, false); + if (NMT->NMT_TXbuff == NULL) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } +#endif + + /* configure HB CAN transmission */ + NMT->HB_CANdevTx = HB_CANdevTx; + NMT->HB_TXbuff = CO_CANtxBufferInit(HB_CANdevTx, HB_txIdx, CANidTxHB, false, 1, false); + if (NMT->HB_TXbuff == NULL) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + return ret; +} + +#if ((CO_CONFIG_NMT)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0 +void +CO_NMT_initCallbackPre(CO_NMT_t* NMT, void* object, void (*pFunctSignal)(void* object)) { + if (NMT != NULL) { + NMT->pFunctSignalPre = pFunctSignal; + NMT->functSignalObjectPre = object; + } +} +#endif + +#if ((CO_CONFIG_NMT)&CO_CONFIG_NMT_CALLBACK_CHANGE) != 0 +void +CO_NMT_initCallbackChanged(CO_NMT_t* NMT, void (*pFunctNMT)(CO_NMT_internalState_t state)) { + if (NMT != NULL) { + NMT->pFunctNMT = pFunctNMT; + if (NMT->pFunctNMT != NULL) { + NMT->pFunctNMT(NMT->operatingState); + } + } +} +#endif + +CO_NMT_reset_cmd_t +CO_NMT_process(CO_NMT_t* NMT, CO_NMT_internalState_t* NMTstate, uint32_t timeDifference_us, uint32_t* timerNext_us) { + (void)timerNext_us; /* may be unused */ + CO_NMT_internalState_t NMTstateCpy = NMT->operatingState; + CO_NMT_reset_cmd_t resetCommand = CO_RESET_NOT; + bool_t NNTinit = NMTstateCpy == CO_NMT_INITIALIZING; + + NMT->HBproducerTimer = (NMT->HBproducerTimer > timeDifference_us) ? (NMT->HBproducerTimer - timeDifference_us) : 0U; + + /* Send heartbeat producer message if: + * - First start, send bootup message or + * - HB producer enabled and: Timer expired or NMT->operatingState changed */ + if (NNTinit + || ((NMT->HBproducerTime_us != 0U) + && ((NMT->HBproducerTimer == 0U) || (NMTstateCpy != NMT->operatingStatePrev)))) { + NMT->HB_TXbuff->data[0] = (uint8_t)NMTstateCpy; + (void)CO_CANsend(NMT->HB_CANdevTx, NMT->HB_TXbuff); + + if (NMTstateCpy == CO_NMT_INITIALIZING) { + /* NMT slave self starting */ + NMTstateCpy = (((uint16_t)NMT->NMTcontrol & (uint16_t)CO_NMT_STARTUP_TO_OPERATIONAL) != 0U) + ? CO_NMT_OPERATIONAL + : CO_NMT_PRE_OPERATIONAL; + } else { + /* Start timer from the beginning. If OS is slow, time sliding may occur. However, + * heartbeat is not for synchronization, it is for health report. In case of + * initializing, timer is set in the CO_NMT_init() function with pre-defined value. */ + NMT->HBproducerTimer = NMT->HBproducerTime_us; + } + } + NMT->operatingStatePrev = NMTstateCpy; + + /* process internal NMT commands, received from CO_NMT_receive() or CO_NMT_sendCommand() */ + if (NMT->internalCommand != CO_NMT_NO_COMMAND) { + switch (NMT->internalCommand) { + case CO_NMT_ENTER_OPERATIONAL: NMTstateCpy = CO_NMT_OPERATIONAL; break; + case CO_NMT_ENTER_STOPPED: NMTstateCpy = CO_NMT_STOPPED; break; + case CO_NMT_ENTER_PRE_OPERATIONAL: NMTstateCpy = CO_NMT_PRE_OPERATIONAL; break; + case CO_NMT_RESET_NODE: resetCommand = CO_RESET_APP; break; + case CO_NMT_RESET_COMMUNICATION: resetCommand = CO_RESET_COMM; break; + case CO_NMT_NO_COMMAND: + default: + /* done */ + break; + } + NMT->internalCommand = CO_NMT_NO_COMMAND; + } + + /* verify NMT transitions based on error register */ + bool_t ErrOnBusOffHB = (((uint16_t)NMT->NMTcontrol & (uint16_t)CO_NMT_ERR_ON_BUSOFF_HB) != 0U); + bool_t ErrBusOff = CO_isError(NMT->em, CO_EM_CAN_TX_BUS_OFF); + bool_t ErrHbCons = CO_isError(NMT->em, CO_EM_HEARTBEAT_CONSUMER); + bool_t ErrHbConsRemote = CO_isError(NMT->em, CO_EM_HB_CONSUMER_REMOTE_RESET); + bool_t busOff_HB = ErrOnBusOffHB && (ErrBusOff || ErrHbCons || ErrHbConsRemote); + + bool_t ErrNMTErrReg = (((uint16_t)NMT->NMTcontrol & (uint16_t)CO_NMT_ERR_ON_ERR_REG) != 0U); + bool_t ErrNMTcontrol = ((CO_getErrorRegister(NMT->em) & (uint8_t)NMT->NMTcontrol) != 0U); + bool_t errRegMasked = ErrNMTErrReg && ErrNMTcontrol; + + if ((NMTstateCpy == CO_NMT_OPERATIONAL) && (busOff_HB || errRegMasked)) { + NMTstateCpy = (((uint16_t)NMT->NMTcontrol & (uint16_t)CO_NMT_ERR_TO_STOPPED) != 0U) ? CO_NMT_STOPPED + : CO_NMT_PRE_OPERATIONAL; + } else if ((((uint16_t)NMT->NMTcontrol & (uint16_t)CO_NMT_ERR_FREE_TO_OPERATIONAL) != 0U) + && (NMTstateCpy == CO_NMT_PRE_OPERATIONAL) && (!busOff_HB && !errRegMasked)) { + NMTstateCpy = CO_NMT_OPERATIONAL; + } else { /* MISRA C 2004 14.10 */ + } + +#if ((CO_CONFIG_NMT)&CO_CONFIG_NMT_CALLBACK_CHANGE) != 0 + /* Notify operating state change */ + if ((NMT->operatingStatePrev != NMTstateCpy) || NNTinit) { + if (NMT->pFunctNMT != NULL) { + NMT->pFunctNMT(NMTstateCpy); + } + } +#endif + +#if ((CO_CONFIG_NMT)&CO_CONFIG_FLAG_TIMERNEXT) != 0 + /* Calculate, when next Heartbeat needs to be send */ + if ((NMT->HBproducerTime_us != 0U) && (timerNext_us != NULL)) { + if (NMT->operatingStatePrev != NMTstateCpy) { + *timerNext_us = 0; + } else if (*timerNext_us > NMT->HBproducerTimer) { + *timerNext_us = NMT->HBproducerTimer; + } else { /* MISRA C 2004 14.10 */ + } + } +#endif + + NMT->operatingState = NMTstateCpy; + if (NMTstate != NULL) { + *NMTstate = NMTstateCpy; + } + + return resetCommand; +} + +#if ((CO_CONFIG_NMT)&CO_CONFIG_NMT_MASTER) != 0 +CO_ReturnError_t +CO_NMT_sendCommand(CO_NMT_t* NMT, CO_NMT_command_t command, uint8_t nodeID) { + /* verify arguments */ + if (NMT == NULL) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + /* Apply NMT command also to this node, if set so. */ + if ((nodeID == 0U) || (nodeID == NMT->nodeId)) { + NMT->internalCommand = command; + } + + /* Send NMT master message. */ + NMT->NMT_TXbuff->data[0] = (uint8_t)command; + NMT->NMT_TXbuff->data[1] = nodeID; + return CO_CANsend(NMT->NMT_CANdevTx, NMT->NMT_TXbuff); +} +#endif diff --git a/301/CO_NMT_Heartbeat.h b/301/CO_NMT_Heartbeat.h new file mode 100644 index 00000000..feff1865 --- /dev/null +++ b/301/CO_NMT_Heartbeat.h @@ -0,0 +1,293 @@ +/** + * CANopen Network management and Heartbeat producer protocol. + * + * @file CO_NMT_Heartbeat.h + * @ingroup CO_NMT_Heartbeat + * @author Janez Paternoster + * @copyright 2004 - 2020 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#ifndef CO_NMT_HEARTBEAT_H +#define CO_NMT_HEARTBEAT_H + +#include "301/CO_driver.h" +#include "301/CO_ODinterface.h" +#include "301/CO_Emergency.h" + +/* default configuration, see CO_config.h */ +#ifndef CO_CONFIG_NMT +#define CO_CONFIG_NMT (CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | CO_CONFIG_GLOBAL_FLAG_TIMERNEXT) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup CO_NMT_Heartbeat NMT and Heartbeat + * CANopen Network management and Heartbeat producer protocol. + * + * @ingroup CO_CANopen_301 + * @{ + * CANopen device can be in one of the @ref CO_NMT_internalState_t + * - Initializing. It is active before CANopen is initialized. + * - Pre-operational. All CANopen objects are active, except PDOs. + * - Operational. Process data objects (PDOs) are active too. + * - Stopped. Only Heartbeat producer and NMT consumer are active. + * + * NMT master can change the internal state of the devices by sending @ref CO_NMT_command_t. + * + * ### NMT message contents: + * + * Byte | Description + * -----|----------------------------------------------------------- + * 0 | @ref CO_NMT_command_t + * 1 | Node ID. If zero, command addresses all nodes. + * + * ### Heartbeat message contents: + * + * Byte | Description + * -----|----------------------------------------------------------- + * 0 | @ref CO_NMT_internalState_t + * + * See @ref CO_Default_CAN_ID_t for CAN identifiers. + */ + +/** + * Internal network state of the CANopen node + */ +typedef enum { + CO_NMT_UNKNOWN = -1, /**< -1, Device state is unknown (for heartbeat consumer) */ + CO_NMT_INITIALIZING = 0, /**< 0, Device is initializing */ + CO_NMT_PRE_OPERATIONAL = 127, /**< 127, Device is in pre-operational state */ + CO_NMT_OPERATIONAL = 5, /**< 5, Device is in operational state */ + CO_NMT_STOPPED = 4 /**< 4, Device is stopped */ +} CO_NMT_internalState_t; + +/** + * Commands from NMT master. + */ +typedef enum { + CO_NMT_NO_COMMAND = 0, /**< 0, No command */ + CO_NMT_ENTER_OPERATIONAL = 1, /**< 1, Start device */ + CO_NMT_ENTER_STOPPED = 2, /**< 2, Stop device */ + CO_NMT_ENTER_PRE_OPERATIONAL = 128, /**< 128, Put device into pre-operational */ + CO_NMT_RESET_NODE = 129, /**< 129, Reset device */ + CO_NMT_RESET_COMMUNICATION = 130 /**< 130, Reset CANopen communication on device */ +} CO_NMT_command_t; + +/** + * Return code from CO_NMT_process() that tells application code what to reset. + */ +typedef enum { + CO_RESET_NOT = 0, /**< 0, Normal return, no action */ + CO_RESET_COMM = 1, /**< 1, Application must provide communication reset. */ + CO_RESET_APP = 2, /**< 2, Application must provide complete device reset */ + CO_RESET_QUIT = 3 /**< 3, Application must quit, no reset of microcontroller (command is not requested by the + stack.) */ +} CO_NMT_reset_cmd_t; + +/** + * @defgroup CO_NMT_control_t NMT control bitfield for NMT internal state. + * @{ + * + * Variable of this type is passed to @ref CO_NMT_init() function. It controls behavior of the @ref + * CO_NMT_internalState_t of the device according to CANopen error register. + * + * Internal NMT state is controlled also with external NMT command, @ref CO_NMT_sendInternalCommand() or @ref + * CO_NMT_sendCommand() functions. + */ + +/** First 8 bits can be used to specify bitmask for the @ref CO_errorRegister_t to get relevant bits for the + * calculation. */ +#define CO_NMT_ERR_REG_MASK 0x00FFU +/** If bit is set then device enters NMT operational state after the initialization phase otherwise it enters NMT + * pre-operational state. */ +#define CO_NMT_STARTUP_TO_OPERATIONAL 0x0100U +/** If bit is set and device is operational it enters NMT pre-operational or stopped state if CAN bus is off or + * heartbeat consumer timeout is detected. */ +#define CO_NMT_ERR_ON_BUSOFF_HB 0x1000U +/** If bit is set and device is operational it enters NMT pre-operational or stopped state if masked CANopen error + * register is different than zero. */ +#define CO_NMT_ERR_ON_ERR_REG 0x2000U +/** If bit is set and CO_NMT_ERR_ON_xx condition is met then device will enter NMT stopped state otherwise it will enter + * NMT pre-op state. */ +#define CO_NMT_ERR_TO_STOPPED 0x4000U +/** If bit is set and device is pre-operational it enters NMT operational state automatically if conditions from + * CO_NMT_ERR_ON_xx are all false. */ +#define CO_NMT_ERR_FREE_TO_OPERATIONAL 0x8000U + +/** @} */ /* CO_NMT_control_t */ + +/** + * NMT consumer and Heartbeat producer object + */ +typedef struct { + CO_NMT_internalState_t operatingState; /**< Current NMT operating state. */ + CO_NMT_internalState_t operatingStatePrev; /**< Previous NMT operating state. */ + CO_NMT_command_t internalCommand; /**< NMT internal command from CO_NMT_receive() or CO_NMT_sendCommand(), processed + in CO_NMT_process(). */ + uint8_t nodeId; /**< From CO_NMT_init() */ + uint16_t NMTcontrol; /**< From CO_NMT_init() */ + uint32_t HBproducerTime_us; /**< Producer heartbeat time, calculated from OD 0x1017 */ + uint32_t HBproducerTimer; /**< Internal timer for HB producer */ + OD_extension_t OD_1017_extension; /**< Extension for OD object */ + CO_EM_t* em; /**< From CO_NMT_init() */ +#if (((CO_CONFIG_NMT)&CO_CONFIG_NMT_MASTER) != 0) || defined CO_DOXYGEN + CO_CANmodule_t* NMT_CANdevTx; /**< From CO_NMT_init() */ + CO_CANtx_t* NMT_TXbuff; /**< CAN transmit buffer for NMT master message */ +#endif + CO_CANmodule_t* HB_CANdevTx; /**< From CO_NMT_init() */ + CO_CANtx_t* HB_TXbuff; /**< CAN transmit buffer for heartbeat message */ +#if (((CO_CONFIG_NMT)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0) || defined CO_DOXYGEN + void (*pFunctSignalPre)(void* object); /**< From CO_NMT_initCallbackPre() or NULL */ + void* functSignalObjectPre; /**< From CO_NMT_initCallbackPre() or NULL */ +#endif +#if (((CO_CONFIG_NMT)&CO_CONFIG_NMT_CALLBACK_CHANGE) != 0) || defined CO_DOXYGEN + void (*pFunctNMT)(CO_NMT_internalState_t state); /**< From CO_NMT_initCallbackChanged() or NULL */ +#endif +} CO_NMT_t; + +/** + * Initialize NMT and Heartbeat producer object. + * + * Function must be called in the communication reset section. + * + * @param NMT This object will be initialized. + * @param OD_1017_ProducerHbTime OD entry for 0x1017 -"Producer heartbeat time", entry is required, IO extension is + * optional for runtime configuration. + * @param em Emergency object. + * @param nodeId CANopen Node ID of this device. + * @param NMTcontrol Control variable for calculation of NMT internal state, based on error register, startup and + * runtime behavior. + * @param firstHBTime_ms Time between bootup and first heartbeat message in milliseconds. If firstHBTime_ms is greater + * than "Producer Heartbeat time" (OD object 0x1017), latter is used instead. Entry is required, IO extension is + * optional. + * @param NMT_CANdevRx CAN device for NMT reception. + * @param NMT_rxIdx Index of receive buffer in above CAN device. + * @param CANidRxNMT CAN identifier for NMT receive message. + * @param NMT_CANdevTx CAN device for NMT master transmission. + * @param NMT_txIdx Index of transmit buffer in above CAN device. + * @param CANidTxNMT CAN identifier for NMT transmit message. + * @param HB_CANdevTx CAN device for HB transmission. + * @param HB_txIdx Index of transmit buffer in the above CAN device. + * @param CANidTxHB CAN identifier for HB message. + * @param [out] errInfo Additional information in case of error, may be NULL. + * + * @return #CO_ReturnError_t CO_ERROR_NO on success. + */ +CO_ReturnError_t CO_NMT_init(CO_NMT_t* NMT, OD_entry_t* OD_1017_ProducerHbTime, CO_EM_t* em, uint8_t nodeId, + uint16_t NMTcontrol, uint16_t firstHBTime_ms, CO_CANmodule_t* NMT_CANdevRx, + uint16_t NMT_rxIdx, uint16_t CANidRxNMT, +#if (((CO_CONFIG_NMT)&CO_CONFIG_NMT_MASTER) != 0) || defined CO_DOXYGEN + CO_CANmodule_t* NMT_CANdevTx, uint16_t NMT_txIdx, uint16_t CANidTxNMT, +#endif + CO_CANmodule_t* HB_CANdevTx, uint16_t HB_txIdx, uint16_t CANidTxHB, uint32_t* errInfo); + +#if (((CO_CONFIG_NMT)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0) || defined CO_DOXYGEN +/** + * Initialize NMT callback function after message preprocessed. + * + * Function initializes optional callback function, which should immediately start processing of CO_NMT_process() + * function. Callback is called after NMT message is received from the CAN bus. + * + * @param NMT This object. + * @param object Pointer to object, which will be passed to pFunctSignal(). Can be NULL + * @param pFunctSignal Pointer to the callback function. Not called if NULL. + */ +void CO_NMT_initCallbackPre(CO_NMT_t* NMT, void* object, void (*pFunctSignal)(void* object)); +#endif + +#if (((CO_CONFIG_NMT)&CO_CONFIG_NMT_CALLBACK_CHANGE) != 0) || defined CO_DOXYGEN +/** + * Initialize NMT callback function. + * + * Function initializes optional callback function, which is called after NMT State change has occurred. Function may + * wake up external task which handles NMT events. The first call is made immediately to give the consumer the current + * NMT state. + * + * @param NMT This object. + * @param pFunctNMT Pointer to the callback function. Not called if NULL. + */ +void CO_NMT_initCallbackChanged(CO_NMT_t* NMT, void (*pFunctNMT)(CO_NMT_internalState_t state)); +#endif + +/** + * Process received NMT and produce Heartbeat messages. + * + * Function must be called cyclically. + * + * @param NMT This object. + * @param [out] NMTstate If not NULL, CANopen NMT internal state is returned. + * @param timeDifference_us Time difference from previous function call in microseconds. + * @param [out] timerNext_us info to OS - see CO_process(). + * + * @return #CO_NMT_reset_cmd_t + */ +CO_NMT_reset_cmd_t CO_NMT_process(CO_NMT_t* NMT, CO_NMT_internalState_t* NMTstate, uint32_t timeDifference_us, + uint32_t* timerNext_us); + +/** + * Query current NMT state + * + * @param NMT This object. + * + * @return @ref CO_NMT_internalState_t + */ +static inline CO_NMT_internalState_t +CO_NMT_getInternalState(CO_NMT_t* NMT) { + return (NMT == NULL) ? CO_NMT_INITIALIZING : NMT->operatingState; +} + +/** + * Send NMT command to self, without sending NMT message + * + * Internal NMT state will be verified and switched inside @ref CO_NMT_process() + * + * @param NMT This object. + * @param command NMT command + */ +static inline void +CO_NMT_sendInternalCommand(CO_NMT_t* NMT, CO_NMT_command_t command) { + if (NMT != NULL) { + NMT->internalCommand = command; + } +} + +#if ((CO_CONFIG_NMT)&CO_CONFIG_NMT_MASTER) || defined CO_DOXYGEN +/** + * Send NMT master command. + * + * This functionality may only be used from NMT master, as specified by standard CiA302-2. Standard provides one + * exception, where application from slave node may send NMT master command: "If CANopen object 0x1F80 has value of + * **0x2**, then NMT slave shall execute the NMT service start remote node (CO_NMT_ENTER_OPERATIONAL) with nodeID set to + * 0." + * + * @param NMT This object. + * @param command NMT command from CO_NMT_command_t. + * @param nodeID Node ID of the remote node. 0 for all nodes including self. + * + * @return CO_ERROR_NO on success or CO_ReturnError_t from CO_CANsend(). + */ +CO_ReturnError_t CO_NMT_sendCommand(CO_NMT_t* NMT, CO_NMT_command_t command, uint8_t nodeID); + +#endif /* (CO_CONFIG_NMT) & CO_CONFIG_NMT_MASTER */ + +/** @} */ /* CO_NMT_Heartbeat */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CO_NMT_HEARTBEAT_H */ diff --git a/301/CO_Node_Guarding.c b/301/CO_Node_Guarding.c new file mode 100644 index 00000000..e7cda3d0 --- /dev/null +++ b/301/CO_Node_Guarding.c @@ -0,0 +1,390 @@ +/* + * CANopen Node Guarding slave and master objects. + * + * @file CO_Node_Guarding.c + * @ingroup CO_Node_Guarding + * @author Janez Paternoster + * @copyright 2023 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#include "301/CO_Node_Guarding.h" + +#if ((CO_CONFIG_NODE_GUARDING)&CO_CONFIG_NODE_GUARDING_SLAVE_ENABLE) != 0 + +/* + * Read received message from CAN module. + * + * Function will be called (by CAN receive interrupt) every time, when CAN message with correct identifier + * will be received. For more information and description of parameters see file CO_driver.h. + */ +static void +CO_ngs_receive(void* object, void* msg) { + (void)msg; + CO_nodeGuardingSlave_t* ngs = (CO_nodeGuardingSlave_t*)object; + + CO_FLAG_SET(ngs->CANrxNew); +} + +/* + * Custom function for writing OD object "Guard time" + * + * For more information see file CO_ODinterface.h, OD_IO_t. + */ +static ODR_t +OD_write_100C(OD_stream_t* stream, const void* buf, OD_size_t count, OD_size_t* countWritten) { + if ((stream == NULL) || (stream->subIndex != 0U) || (buf == NULL) || (count != sizeof(uint16_t)) + || (countWritten == NULL)) { + return ODR_DEV_INCOMPAT; + } + + CO_nodeGuardingSlave_t* ngs = (CO_nodeGuardingSlave_t*)stream->object; + + /* update objects */ + ngs->guardTime_us = (uint32_t)CO_getUint16(buf) * 1000U; + ngs->lifeTime_us = ngs->guardTime_us * ngs->lifeTimeFactor; + + /* reset running timer */ + if (ngs->lifeTimer > 0U) { + ngs->lifeTimer = ngs->lifeTime_us; + } + + /* write value to the original location in the Object Dictionary */ + return OD_writeOriginal(stream, buf, count, countWritten); +} + +/* + * Custom function for writing OD object "Life time factor" + * + * For more information see file CO_ODinterface.h, OD_IO_t. + */ +static ODR_t +OD_write_100D(OD_stream_t* stream, const void* buf, OD_size_t count, OD_size_t* countWritten) { + if ((stream == NULL) || (stream->subIndex != 0U) || (buf == NULL) || (count != sizeof(uint8_t)) + || (countWritten == NULL)) { + return ODR_DEV_INCOMPAT; + } + + CO_nodeGuardingSlave_t* ngs = (CO_nodeGuardingSlave_t*)stream->object; + + /* update objects */ + ngs->lifeTimeFactor = (uint8_t)CO_getUint8(buf); + ngs->lifeTime_us = ngs->guardTime_us * ngs->lifeTimeFactor; + + /* reset running timer */ + if (ngs->lifeTimer > 0U) { + ngs->lifeTimer = ngs->lifeTime_us; + } + + /* write value to the original location in the Object Dictionary */ + return OD_writeOriginal(stream, buf, count, countWritten); +} + +CO_ReturnError_t +CO_nodeGuardingSlave_init(CO_nodeGuardingSlave_t* ngs, OD_entry_t* OD_100C_GuardTime, + OD_entry_t* OD_100D_LifeTimeFactor, CO_EM_t* em, uint16_t CANidNodeGuarding, + CO_CANmodule_t* CANdevRx, uint16_t CANdevRxIdx, CO_CANmodule_t* CANdevTx, + uint16_t CANdevTxIdx, uint32_t* errInfo) { + CO_ReturnError_t ret = CO_ERROR_NO; + + /* verify arguments */ + if ((ngs == NULL) || (em == NULL) || (CANdevRx == NULL) || (CANdevTx == NULL) || (OD_100C_GuardTime == NULL) + || (OD_100D_LifeTimeFactor == NULL)) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + /* clear the object */ + (void)memset(ngs, 0, sizeof(CO_nodeGuardingSlave_t)); + + /* Configure object variables */ + ngs->em = em; + + /* get and verify required "Guard time" from the Object Dictionary */ + uint16_t guardTime_ms; + ODR_t odRet = OD_get_u16(OD_100C_GuardTime, 0, &guardTime_ms, true); + if (odRet != ODR_OK) { + if (errInfo != NULL) { + *errInfo = OD_getIndex(OD_100C_GuardTime); + } + return CO_ERROR_OD_PARAMETERS; + } + ngs->guardTime_us = (uint32_t)guardTime_ms * 1000U; + + ngs->OD_100C_extension.object = ngs; + ngs->OD_100C_extension.read = OD_readOriginal; + ngs->OD_100C_extension.write = OD_write_100C; + odRet = OD_extension_init(OD_100C_GuardTime, &ngs->OD_100C_extension); + if (odRet != ODR_OK) { + if (errInfo != NULL) { + *errInfo = OD_getIndex(OD_100C_GuardTime); + } + return CO_ERROR_OD_PARAMETERS; + } + + /* get and verify required "Life time factor" from the Object Dictionary */ + uint8_t lifeTimeFactor; + odRet = OD_get_u8(OD_100D_LifeTimeFactor, 0, &lifeTimeFactor, true); + if (odRet != ODR_OK) { + if (errInfo != NULL) { + *errInfo = OD_getIndex(OD_100D_LifeTimeFactor); + } + return CO_ERROR_OD_PARAMETERS; + } + ngs->lifeTimeFactor = lifeTimeFactor; + ngs->lifeTime_us = ngs->guardTime_us * ngs->lifeTimeFactor; + + ngs->OD_100D_extension.object = ngs; + ngs->OD_100D_extension.read = OD_readOriginal; + ngs->OD_100D_extension.write = OD_write_100D; + odRet = OD_extension_init(OD_100D_LifeTimeFactor, &ngs->OD_100D_extension); + if (odRet != ODR_OK) { + if (errInfo != NULL) { + *errInfo = OD_getIndex(OD_100D_LifeTimeFactor); + } + return CO_ERROR_OD_PARAMETERS; + } + + /* configure CAN reception */ + ret = CO_CANrxBufferInit(CANdevRx, CANdevRxIdx, CANidNodeGuarding, 0x7FF, true, (void*)ngs, CO_ngs_receive); + if (ret != CO_ERROR_NO) { + return ret; + } + + /* configure CAN transmission */ + ngs->CANdevTx = CANdevTx; + ngs->CANtxBuff = CO_CANtxBufferInit(CANdevTx, CANdevTxIdx, CANidNodeGuarding, false, 1, false); + if (ngs->CANtxBuff == NULL) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + return ret; +} + +void +CO_nodeGuardingSlave_process(CO_nodeGuardingSlave_t* ngs, CO_NMT_internalState_t NMTstate, bool_t slaveDisable, + uint32_t timeDifference_us, uint32_t* timerNext_us) { + (void)timerNext_us; /* may be unused */ + + if (slaveDisable) { + ngs->toggle = false; + ngs->lifeTimer = 0; + CO_FLAG_CLEAR(ngs->CANrxNew); + return; + } + + /* was RTR just received */ + if (CO_FLAG_READ(ngs->CANrxNew)) { + ngs->lifeTimer = ngs->lifeTime_us; + + /* send response */ + ngs->CANtxBuff->data[0] = (uint8_t)NMTstate; + if (ngs->toggle) { + ngs->CANtxBuff->data[0] |= 0x80U; + ngs->toggle = false; + } else { + ngs->toggle = true; + } + (void)CO_CANsend(ngs->CANdevTx, ngs->CANtxBuff); + + if (ngs->lifeTimeTimeout) { + /* error bit is shared with HB consumer */ + CO_errorReset(ngs->em, CO_EM_HEARTBEAT_CONSUMER, 0); + ngs->lifeTimeTimeout = false; + } + + CO_FLAG_CLEAR(ngs->CANrxNew); + } + + /* verify "Life time" timeout and update the timer */ + else if (ngs->lifeTimer > 0U) { + if (timeDifference_us < ngs->lifeTimer) { + ngs->lifeTimer -= timeDifference_us; +#if ((CO_CONFIG_NMT)&CO_CONFIG_FLAG_TIMERNEXT) != 0 + /* Calculate, when timeout expires */ + if (timerNext_us != NULL && *timerNext_us > ngs->lifeTimer) { + *timerNext_us = ngs->lifeTimer; + } +#endif + } else { + ngs->lifeTimer = 0; + ngs->lifeTimeTimeout = true; + + /* error bit is shared with HB consumer */ + CO_errorReport(ngs->em, CO_EM_HEARTBEAT_CONSUMER, CO_EMC_HEARTBEAT, 0); + } + } else { /* MISRA C 2004 14.10 */ + } + + return; +} + +#endif /* (CO_CONFIG_NODE_GUARDING) & CO_CONFIG_NODE_GUARDING_SLAVE_ENABLE */ + +#if ((CO_CONFIG_NODE_GUARDING)&CO_CONFIG_NODE_GUARDING_MASTER_ENABLE) != 0 +/* + * Read received message from CAN module. + * + * Function will be called (by CAN receive interrupt) every time, when CAN message with correct identifier + * will be received. For more information and description of parameters see file CO_driver.h. + * + * Function receives messages from CAN identifier from 0x700 to 0x7FF. It + * searches matching node->ident from nodes array. + */ +static void +CO_ngm_receive(void* object, void* msg) { + CO_nodeGuardingMaster_t* ngm = (CO_nodeGuardingMaster_t*)object; + + uint8_t DLC = CO_CANrxMsg_readDLC(msg); + const uint8_t* data = CO_CANrxMsg_readData(msg); + uint16_t ident = CO_CANrxMsg_readIdent(msg); + CO_nodeGuardingMasterNode_t* node = &ngm->nodes[0]; + + if (DLC == 1) { + for (uint8_t i = 0; i < CO_CONFIG_NODE_GUARDING_MASTER_COUNT; i++) { + if (ident == node->ident) { + uint8_t toggle = data[0] & 0x80; + if (toggle == node->toggle) { + node->responseRecived = true; + node->NMTstate = (CO_NMT_internalState_t)(data[0] & 0x7F); + node->toggle = (toggle != 0) ? 0x00 : 0x80; + } + break; + } + node++; + } + } +} + +CO_ReturnError_t +CO_nodeGuardingMaster_init(CO_nodeGuardingMaster_t* ngm, CO_EM_t* em, CO_CANmodule_t* CANdevRx, uint16_t CANdevRxIdx, + CO_CANmodule_t* CANdevTx, uint16_t CANdevTxIdx) { + CO_ReturnError_t ret = CO_ERROR_NO; + + /* verify arguments */ + if (ngm == NULL || em == NULL || CANdevRx == NULL || CANdevTx == NULL) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + /* clear the object */ + (void)memset(ngm, 0, sizeof(CO_nodeGuardingMaster_t)); + + /* Configure object variables */ + ngm->em = em; + + /* configure CAN reception. One buffer will receive all messages from CAN-id 0x700 to 0x7FF. */ + ret = CO_CANrxBufferInit(CANdevRx, CANdevRxIdx, CO_CAN_ID_HEARTBEAT, 0x780, false, (void*)ngm, CO_ngm_receive); + if (ret != CO_ERROR_NO) { + return ret; + } + + /* configure CAN transmission */ + ngm->CANdevTx = CANdevTx; + ngm->CANdevTxIdx = CANdevTxIdx; + ngm->CANtxBuff = CO_CANtxBufferInit(CANdevTx, CANdevTxIdx, CO_CAN_ID_HEARTBEAT, true, 1, 0); + if (ngm->CANtxBuff == NULL) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + return ret; +} + +CO_ReturnError_t +CO_nodeGuardingMaster_initNode(CO_nodeGuardingMaster_t* ngm, uint8_t index, uint8_t nodeId, uint16_t guardTime_ms) { + if (ngm == NULL || index >= CO_CONFIG_NODE_GUARDING_MASTER_COUNT || nodeId < 1 || nodeId > 0x7F) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + CO_nodeGuardingMasterNode_t* node = &ngm->nodes[index]; + + node->guardTime_us = (uint32_t)guardTime_ms * 1000; + node->guardTimer = 0; + node->ident = CO_CAN_ID_HEARTBEAT + nodeId; + node->NMTstate = CO_NMT_UNKNOWN; /* for the first time */ + node->toggle = false; + node->responseRecived = true; /* for the first time */ + node->CANtxWasBusy = false; + node->monitoringActive = false; + +#if CO_CONFIG_NODE_GUARDING_MASTER_COUNT == 1 + ngm->CANtxBuff = CO_CANtxBufferInit(ngm->CANdevTx, ngm->CANdevTxIdx, node->ident, true, 1, 0); +#endif + + return CO_ERROR_NO; +} + +void +CO_nodeGuardingMaster_process(CO_nodeGuardingMaster_t* ngm, uint32_t timeDifference_us, uint32_t* timerNext_us) { + (void)timerNext_us; /* may be unused */ + bool_t allMonitoredActiveCurrent = true; + bool_t allMonitoredOperationalCurrent = true; + CO_nodeGuardingMasterNode_t* node = &ngm->nodes[0]; + + for (uint8_t i = 0; i < CO_CONFIG_NODE_GUARDING_MASTER_COUNT; i++) { + if (node->guardTime_us > 0 && node->ident > CO_CAN_ID_HEARTBEAT) { + if (timeDifference_us < node->guardTimer) { + node->guardTimer -= timeDifference_us; +#if ((CO_CONFIG_NMT)&CO_CONFIG_FLAG_TIMERNEXT) != 0 + /* Calculate, when timeout expires */ + if (timerNext_us != NULL && *timerNext_us > node->guardTimer) { + *timerNext_us = node->guardTimer; + } +#endif + } else { + /* it is time to send new rtr, but first verify last response */ + if (!node->CANtxWasBusy) { + if (!node->responseRecived) { + node->monitoringActive = false; + /* error bit is shared with HB consumer */ + CO_errorReport(ngm->em, CO_EM_HEARTBEAT_CONSUMER, CO_EMC_HEARTBEAT, node->ident & 0x7F); + } else if (node->NMTstate != CO_NMT_UNKNOWN) { + node->monitoringActive = true; + CO_errorReset(ngm->em, CO_EM_HEARTBEAT_CONSUMER, node->ident & 0x7F); + } + } + + if (ngm->CANtxBuff->bufferFull) { + node->guardTimer = 0; + node->CANtxWasBusy = true; + } else { +#if CO_CONFIG_NODE_GUARDING_MASTER_COUNT > 1 + ngm->CANtxBuff = CO_CANtxBufferInit(ngm->CANdevTx, ngm->CANdevTxIdx, node->ident, true, 1, 0); +#endif + (void)CO_CANsend(ngm->CANdevTx, ngm->CANtxBuff); + node->CANtxWasBusy = false; + node->responseRecived = false; + node->guardTimer = node->guardTime_us; + } + } + + if (allMonitoredActiveCurrent) { + if (node->monitoringActive) { + if (node->NMTstate != CO_NMT_OPERATIONAL) { + allMonitoredOperationalCurrent = false; + } + } else { + allMonitoredActiveCurrent = false; + allMonitoredOperationalCurrent = false; + } + } + } /* if node enabled */ + + node++; + } /* for */ + + ngm->allMonitoredActive = allMonitoredActiveCurrent; + ngm->allMonitoredOperational = allMonitoredOperationalCurrent; + + return; +} + +#endif /* (CO_CONFIG_NODE_GUARDING) & CO_CONFIG_NODE_GUARDING_MASTER_ENABLE */ diff --git a/301/CO_Node_Guarding.h b/301/CO_Node_Guarding.h new file mode 100644 index 00000000..467527da --- /dev/null +++ b/301/CO_Node_Guarding.h @@ -0,0 +1,244 @@ +/** + * CANopen Node Guarding slave and master objects. + * + * @file CO_Node_Guarding.h + * @ingroup CO_Node_Guarding + * @author Janez Paternoster + * @copyright 2023 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#ifndef CO_NODE_GUARDING_H +#define CO_NODE_GUARDING_H + +#include "301/CO_driver.h" +#include "301/CO_ODinterface.h" +#include "301/CO_Emergency.h" +#include "301/CO_NMT_Heartbeat.h" + +/* default configuration, see CO_config.h */ +#ifndef CO_CONFIG_NODE_GUARDING +#define CO_CONFIG_NODE_GUARDING (0) +#endif +#ifndef CO_CONFIG_NODE_GUARDING_MASTER_COUNT +#define CO_CONFIG_NODE_GUARDING_MASTER_COUNT 0x7F +#endif + +#if (((CO_CONFIG_NODE_GUARDING)&CO_CONFIG_NODE_GUARDING_SLAVE_ENABLE) != 0) || defined CO_DOXYGEN + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup CO_Node_Guarding Node Guarding CANopen Node Guarding, an older alternative to the Heartbeat protocol. + * + * @ingroup CO_CANopen_301 + * @{ + * Node guarding master pools each node guarding slave at time intervals, called guard time. Master sends a CAN RTR + * message, and slave responds. Slave also monitors presence of RTR message from master and indicates error, if it + * wasn't received within life time. ('Life time' is 'Guard time' multiplied by 'Life time factor'). + * + * Adding Node guarding to the project: + * - Make sure, driver supports it. RTR bit should be part of CAN identifier. + * - Enable it with 'CO_CONFIG_NODE_GUARDING', see CO_config.h + * - For slave add 0x100C and 0x100D objects to the Object dictionary. + * - For master use CO_nodeGuardingMaster_initNode() to add monitored nodes. + * + * @warning Usage of Node guarding is not recommended, as it is outdated and uses RTR CAN functionality, which is also + * not recommended. Use Heartbeat and Heartbeat consumer, if possible. + * + * ### Node Guarding slave response message contents: + * + * Byte, bits | Description + * ---------------|----------------------------------------------------------- + * 0, bits 0..6 | @ref CO_NMT_internalState_t + * 0, bit 7 | toggle bit + * + * See @ref CO_Default_CAN_ID_t for CAN identifiers. + */ + +/** + * Node Guarding slave object + */ +typedef struct { + CO_EM_t* em; /**< From CO_nodeGuardingSlave_init() */ + volatile void* CANrxNew; /**< Indicates, if new rtr message received from CAN bus */ + uint32_t guardTime_us; /**< Guard time in microseconds, calculated from OD_0x100C */ + uint32_t lifeTime_us; /**< Life time in microseconds, calculated from guardTime_us * lifeTimeFactor */ + uint32_t lifeTimer; /**< Timer for monitoring Life time, counting down from lifeTime_us. */ + uint8_t lifeTimeFactor; /**< Life time factor, from OD_0x100D */ + bool_t toggle; /**< Toggle bit for response */ + bool_t lifeTimeTimeout; /**< True if rtr from master is missing */ + OD_extension_t OD_100C_extension; /**< Extension for OD object */ + OD_extension_t OD_100D_extension; /**< Extension for OD object */ + CO_CANmodule_t* CANdevTx; /**< From CO_nodeGuardingSlave_init() */ + CO_CANtx_t* CANtxBuff; /**< CAN transmit buffer for the message */ +} CO_nodeGuardingSlave_t; + +/** + * Initialize Node Guarding slave object. + * + * Function must be called in the communication reset section. + * + * @param ngs This object will be initialized. + * @param OD_100C_GuardTime OD entry for 0x100C -"Guard time", entry is required. + * @param OD_100D_LifeTimeFactor OD entry for 0x100D -"Life time factor", entry is required. + * @param em Emergency object. + * @param CANidNodeGuarding CAN identifier for Node Guarding rtr and response message (usually CO_CAN_ID_HEARTBEAT + + * nodeId). + * @param CANdevRx CAN device for Node Guarding rtr reception. + * @param CANdevRxIdx Index of the receive buffer in the above CAN device. + * @param CANdevTx CAN device for Node Guarding response transmission. + * @param CANdevTxIdx Index of the transmit buffer in the above CAN device. + * @param [out] errInfo Additional information in case of error, may be NULL. + * + * @return #CO_ReturnError_t CO_ERROR_NO on success. + */ +CO_ReturnError_t CO_nodeGuardingSlave_init(CO_nodeGuardingSlave_t* ngs, OD_entry_t* OD_100C_GuardTime, + OD_entry_t* OD_100D_LifeTimeFactor, CO_EM_t* em, uint16_t CANidNodeGuarding, + CO_CANmodule_t* CANdevRx, uint16_t CANdevRxIdx, CO_CANmodule_t* CANdevTx, + uint16_t CANdevTxIdx, uint32_t* errInfo); + +/** + * Process Node Guarding slave. + * + * Function must be called cyclically. + * + * @param ngs This object. + * @param NMTstate NMT operating state. + * @param slaveDisable If true, then Node guarding slave is disabled. + * @param timeDifference_us Time difference from previous function call in microseconds. + * @param [out] timerNext_us info to OS - see CO_process(). + */ +void CO_nodeGuardingSlave_process(CO_nodeGuardingSlave_t* ngs, CO_NMT_internalState_t NMTstate, bool_t slaveDisable, + uint32_t timeDifference_us, uint32_t* timerNext_us); + +/** + * Inquire, if Node guarding slave detected life time timeout + * + * Error is reset after pool request from master. + * + * @param ngs This object. + * + * @return true, if life time timeout was detected. + */ +static inline bool_t +CO_nodeGuardingSlave_isTimeout(CO_nodeGuardingSlave_t* ngs) { + return (ngs == NULL) || ngs->lifeTimeTimeout; +} + +/** @} */ /* CO_Node_Guarding */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* (CO_CONFIG_NODE_GUARDING) & CO_CONFIG_NODE_GUARDING_SLAVE_ENABLE */ + +#if (((CO_CONFIG_NODE_GUARDING)&CO_CONFIG_NODE_GUARDING_MASTER_ENABLE) != 0) || defined CO_DOXYGEN + +#if CO_CONFIG_NODE_GUARDING_MASTER_COUNT < 1 || CO_CONFIG_NODE_GUARDING_MASTER_COUNT > 127 +#error CO_CONFIG_NODE_GUARDING_MASTER_COUNT value is wrong! +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup CO_Node_Guarding + * @{ + */ + +/** + * Node Guarding master - monitored node + */ +typedef struct { + uint32_t guardTime_us; /**< Guard time in microseconds */ + uint32_t guardTimer; /**< Guard timer in microseconds, counting down */ + uint16_t ident; /**< CAN identifier (CO_CAN_ID_HEARTBEAT + Node Id) */ + CO_NMT_internalState_t NMTstate; /**< NMT operating state */ + uint8_t toggle; /**< toggle bit7, expected from the next received message */ + bool_t responseRecived; /**< True, if response was received since last rtr message */ + bool_t CANtxWasBusy; /**< True, if CANtxBuff was busy since last processing */ + bool_t monitoringActive; /**< True, if monitoring is active (response within time). */ +} CO_nodeGuardingMasterNode_t; + +/** + * Node Guarding master object + */ +typedef struct { + CO_EM_t* em; /**< From CO_nodeGuardingMaster_init() */ + CO_CANmodule_t* CANdevTx; /**< From CO_nodeGuardingMaster_init() */ + uint16_t CANdevTxIdx; /**< From CO_nodeGuardingMaster_init() */ + CO_CANtx_t* CANtxBuff; /**< CAN transmit buffer for the message */ + bool_t allMonitoredActive; /**< True, if all monitored nodes are active or no node is monitored. Can be read by the + application */ + bool_t allMonitoredOperational; /**< True, if all monitored nodes are NMT operational or no node is monitored. Can + be read by the application */ + CO_nodeGuardingMasterNode_t nodes[CO_CONFIG_NODE_GUARDING_MASTER_COUNT]; /**< Array of monitored nodes */ +} CO_nodeGuardingMaster_t; + +/** + * Initialize Node Guarding master object. + * + * Function must be called in the communication reset section. + * + * @param ngm This object will be initialized. + * @param em Emergency object. + * @param CANdevRx CAN device for Node Guarding reception. + * @param CANdevRxIdx Index of the receive buffer in the above CAN device. + * @param CANdevTx CAN device for Node Guarding rtr transmission. + * @param CANdevTxIdx Index of the transmit buffer in the above CAN device. + * + * @return #CO_ReturnError_t CO_ERROR_NO on success. + */ +CO_ReturnError_t CO_nodeGuardingMaster_init(CO_nodeGuardingMaster_t* ngm, CO_EM_t* em, CO_CANmodule_t* CANdevRx, + uint16_t CANdevRxIdx, CO_CANmodule_t* CANdevTx, uint16_t CANdevTxIdx); + +/** + * Initialize node inside Node Guarding master object. + * + * Function may be called any time after CO_nodeGuardingMaster_init(). It configures monitoring of the remote node. + * + * @param ngm Node Guarding master object. + * @param index Index of the slot, which will be configured. 0 <= index < CO_CONFIG_NODE_GUARDING_MASTER_COUNT. + * @param nodeId Node Id of the monitored node. + * @param guardTime_ms Guard time of the monitored node. + * + * @return #CO_ReturnError_t CO_ERROR_NO on success. + */ +CO_ReturnError_t CO_nodeGuardingMaster_initNode(CO_nodeGuardingMaster_t* ngm, uint8_t index, uint8_t nodeId, + uint16_t guardTime_ms); + +/** + * Process Node Guarding master. + * + * Function must be called cyclically. + * + * @param ngm This object. + * @param timeDifference_us Time difference from previous function call in microseconds. + * @param [out] timerNext_us info to OS - see CO_process(). + */ +void CO_nodeGuardingMaster_process(CO_nodeGuardingMaster_t* ngm, uint32_t timeDifference_us, uint32_t* timerNext_us); + +/** @} */ /* @addtogroup CO_Node_Guarding */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* (CO_CONFIG_NODE_GUARDING) & CO_CONFIG_NODE_GUARDING_MASTER_ENABLE */ + +#endif /* CO_NODE_GUARDING_H */ diff --git a/301/CO_ODinterface.c b/301/CO_ODinterface.c new file mode 100644 index 00000000..8b961152 --- /dev/null +++ b/301/CO_ODinterface.c @@ -0,0 +1,371 @@ +/* + * CANopen Object Dictionary interface + * + * @file CO_ODinterface.c + * @author Janez Paternoster + * @copyright 2020 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#include +#define OD_DEFINITION +#include "301/CO_ODinterface.h" + +ODR_t +OD_readOriginal(OD_stream_t* stream, void* buf, OD_size_t count, OD_size_t* countRead) { + if ((stream == NULL) || (buf == NULL) || (countRead == NULL)) { + return ODR_DEV_INCOMPAT; + } + + OD_size_t dataLenToCopy = stream->dataLength; /* length of OD variable */ + const uint8_t* dataOrig = stream->dataOrig; + + if (dataOrig == NULL) { + return ODR_SUB_NOT_EXIST; + } + + ODR_t returnCode = ODR_OK; + + /* If previous read was partial or OD variable length is larger than + * current buffer size, then data was (will be) read in several segments */ + if ((stream->dataOffset > 0U) || (dataLenToCopy > count)) { + if (stream->dataOffset >= dataLenToCopy) { + return ODR_DEV_INCOMPAT; + } + /* Reduce for already copied data */ + dataLenToCopy -= stream->dataOffset; + dataOrig += stream->dataOffset; + + if (dataLenToCopy > count) { + /* Not enough space in destination buffer */ + dataLenToCopy = count; + stream->dataOffset += dataLenToCopy; + returnCode = ODR_PARTIAL; + } else { + stream->dataOffset = 0; /* copy finished, reset offset */ + } + } + + (void)memcpy((void*)buf, (const void*)dataOrig, dataLenToCopy); + + *countRead = dataLenToCopy; + return returnCode; +} + +ODR_t +OD_writeOriginal(OD_stream_t* stream, const void* buf, OD_size_t count, OD_size_t* countWritten) { + if ((stream == NULL) || (buf == NULL) || (countWritten == NULL)) { + return ODR_DEV_INCOMPAT; + } + + OD_size_t dataLenToCopy = stream->dataLength; /* length of OD variable */ + OD_size_t dataLenRemain = dataLenToCopy; /* remaining length of dataOrig buffer */ + uint8_t* dataOrig = stream->dataOrig; + + if (dataOrig == NULL) { + return ODR_SUB_NOT_EXIST; + } + + ODR_t returnCode = ODR_OK; + + /* If previous write was partial or OD variable length is larger than current buffer size, + * then data was (will be) written in several segments */ + if ((stream->dataOffset > 0U) || (dataLenToCopy > count)) { + if (stream->dataOffset >= dataLenToCopy) { + return ODR_DEV_INCOMPAT; + } + /* reduce for already copied data */ + dataLenToCopy -= stream->dataOffset; + dataLenRemain = dataLenToCopy; + dataOrig += stream->dataOffset; + + if (dataLenToCopy > count) { + /* Remaining data space in OD variable is larger than current count + * of data, so only current count of data will be copied */ + dataLenToCopy = count; + stream->dataOffset += dataLenToCopy; + returnCode = ODR_PARTIAL; + } else { + stream->dataOffset = 0; /* copy finished, reset offset */ + } + } + + if (dataLenToCopy < count) { + /* OD variable is smaller than current amount of data */ + return ODR_DATA_LONG; + } + + /* additional check for Misra c compliance */ + if ((dataLenToCopy <= dataLenRemain) && (dataLenToCopy <= count)) { + (void)memcpy((void*)dataOrig, (const void*)buf, dataLenToCopy); + } else { + return ODR_DEV_INCOMPAT; + } + + *countWritten = dataLenToCopy; + return returnCode; +} + +/* Read value from variable from Object Dictionary disabled, see OD_IO_t */ +static ODR_t +OD_readDisabled(OD_stream_t* stream, void* buf, OD_size_t count, OD_size_t* countRead) { + (void)stream; + (void)buf; + (void)count; + (void)countRead; + return ODR_UNSUPP_ACCESS; +} + +/* Write value to variable from Object Dictionary disabled, see OD_IO_t */ +static ODR_t +OD_writeDisabled(OD_stream_t* stream, const void* buf, OD_size_t count, OD_size_t* countWritten) { + (void)stream; + (void)buf; + (void)count; + (void)countWritten; + return ODR_UNSUPP_ACCESS; +} + +OD_entry_t* +OD_find(OD_t* od, uint16_t index) { + if ((od == NULL) || (od->size == 0U)) { + return NULL; + } + + uint16_t min = 0; + uint16_t max = od->size - 1U; + + /* Fast search in ordered Object Dictionary. If indexes are mixed, this won't work. If Object + * Dictionary has up to N entries, then the max number of loop passes is log2(N) */ + while (min < max) { + /* get entry between min and max */ + uint16_t cur = (min + max) >> 1; + OD_entry_t* entry = &od->list[cur]; + + if (index == entry->index) { + return entry; + } + + if (index < entry->index) { + max = (cur > 0U) ? (cur - 1U) : cur; + } else { + min = cur + 1U; + } + } + + if (min == max) { + OD_entry_t* entry = &od->list[min]; + if (index == entry->index) { + return entry; + } + } + + return NULL; /* entry does not exist in OD */ +} + +ODR_t +OD_getSub(const OD_entry_t* entry, uint8_t subIndex, OD_IO_t* io, bool_t odOrig) { + if ((entry == NULL) || (entry->odObject == NULL)) { + return ODR_IDX_NOT_EXIST; + } + if (io == NULL) { + return ODR_DEV_INCOMPAT; + } + + ODR_t ret = ODR_OK; + OD_stream_t* stream = &io->stream; + + /* attribute, dataOrig and dataLength, depends on object type */ + switch (entry->odObjectType & (uint8_t)ODT_TYPE_MASK) { + case ODT_VAR: { + if (subIndex > 0U) { + ret = ODR_SUB_NOT_EXIST; + break; + } + CO_PROGMEM OD_obj_var_t* odo = entry->odObject; + + stream->attribute = odo->attribute; + stream->dataOrig = odo->dataOrig; + stream->dataLength = odo->dataLength; + break; + } + case ODT_ARR: { + if (subIndex >= entry->subEntriesCount) { + ret = ODR_SUB_NOT_EXIST; + break; + } + CO_PROGMEM OD_obj_array_t* odo = entry->odObject; + + if (subIndex == 0U) { + stream->attribute = odo->attribute0; + stream->dataOrig = odo->dataOrig0; + stream->dataLength = 1; + } else { + stream->attribute = odo->attribute; + uint8_t* ptr = odo->dataOrig; + stream->dataOrig = (ptr == NULL) ? ptr : (ptr + (odo->dataElementSizeof * (uint8_t)(subIndex - 1U))); + stream->dataLength = odo->dataElementLength; + } + break; + } + case ODT_REC: { + CO_PROGMEM OD_obj_record_t* odoArr = entry->odObject; + CO_PROGMEM OD_obj_record_t* odo = NULL; + for (uint8_t i = 0; i < entry->subEntriesCount; i++) { + if (odoArr[i].subIndex == subIndex) { + odo = &odoArr[i]; + break; + } + } + if (odo == NULL) { + ret = ODR_SUB_NOT_EXIST; + break; + } + + stream->attribute = odo->attribute; + stream->dataOrig = odo->dataOrig; + stream->dataLength = odo->dataLength; + break; + } + default: { + ret = ODR_DEV_INCOMPAT; + break; + } + } + + if (ret == ODR_OK) { + /* Access data from the original OD location */ + if ((entry->extension == NULL) || odOrig) { + io->read = OD_readOriginal; + io->write = OD_writeOriginal; + stream->object = NULL; + } + /* Access data from extension specified by application */ + else { + io->read = (entry->extension->read != NULL) ? entry->extension->read : OD_readDisabled; + io->write = (entry->extension->write != NULL) ? entry->extension->write : OD_writeDisabled; + stream->object = entry->extension->object; + } + + /* Reset stream data offset */ + stream->dataOffset = 0; + + /* Add informative data */ + stream->index = entry->index; + stream->subIndex = subIndex; + } + + return ret; +} + +uint32_t +OD_getSDOabCode(ODR_t returnCode) { + static const uint32_t abortCodes[(uint8_t)ODR_COUNT] = { + 0x00000000UL, /* No abort */ + 0x05040005UL, /* Out of memory */ + 0x06010000UL, /* Unsupported access to an object */ + 0x06010001UL, /* Attempt to read a write only object */ + 0x06010002UL, /* Attempt to write a read only object */ + 0x06020000UL, /* Object does not exist in the object dictionary */ + 0x06040041UL, /* Object cannot be mapped to the PDO */ + 0x06040042UL, /* Num and len of object to be mapped exceeds PDO len */ + 0x06040043UL, /* General parameter incompatibility reasons */ + 0x06040047UL, /* General internal incompatibility in device */ + 0x06060000UL, /* Access failed due to hardware error */ + 0x06070010UL, /* Data type does not match, length does not match */ + 0x06070012UL, /* Data type does not match, length too high */ + 0x06070013UL, /* Data type does not match, length too short */ + 0x06090011UL, /* Sub index does not exist */ + 0x06090030UL, /* Invalid value for parameter (download only). */ + 0x06090031UL, /* Value range of parameter written too high */ + 0x06090032UL, /* Value range of parameter written too low */ + 0x06090036UL, /* Maximum value is less than minimum value. */ + 0x060A0023UL, /* Resource not available: SDO connection */ + 0x08000000UL, /* General error */ + 0x08000020UL, /* Data cannot be transferred or stored to application */ + 0x08000021UL, /* Data cannot be transferred because of local control */ + 0x08000022UL, /* Data cannot be tran. because of present device state */ + 0x08000023UL, /* Object dict. not present or dynamic generation fails */ + 0x08000024UL /* No data available */ + }; + + return ((returnCode < ODR_OK) || (returnCode >= ODR_COUNT)) ? abortCodes[ODR_DEV_INCOMPAT] : abortCodes[returnCode]; +} + +ODR_t +OD_get_value(const OD_entry_t* entry, uint8_t subIndex, void* val, OD_size_t len, bool_t odOrig) { + if (val == NULL) { + return ODR_DEV_INCOMPAT; + } + + OD_IO_t io = {NULL}; + OD_stream_t* stream = &io.stream; + OD_size_t countRd = 0; + + ODR_t ret = OD_getSub(entry, subIndex, &io, odOrig); + + if (ret != ODR_OK) { + return ret; + } + if (stream->dataLength != len) { + return ODR_TYPE_MISMATCH; + } + + return io.read(stream, val, len, &countRd); +} + +ODR_t +OD_set_value(const OD_entry_t* entry, uint8_t subIndex, void* val, OD_size_t len, bool_t odOrig) { + if (val == NULL) { + return ODR_DEV_INCOMPAT; + } + + OD_IO_t io = {NULL}; + OD_stream_t* stream = &io.stream; + OD_size_t countWritten = 0; + + ODR_t ret = OD_getSub(entry, subIndex, &io, odOrig); + + if (ret != ODR_OK) { + return ret; + } + if (stream->dataLength != len) { + return ODR_TYPE_MISMATCH; + } + + return io.write(stream, val, len, &countWritten); +} + +void* +OD_getPtr(const OD_entry_t* entry, uint8_t subIndex, OD_size_t len, ODR_t* err) { + ODR_t errCopy; + OD_IO_t io; + OD_stream_t* stream = &io.stream; + + errCopy = OD_getSub(entry, subIndex, &io, true); + + if (errCopy == ODR_OK) { + if ((stream->dataOrig == NULL) || (stream->dataLength == 0U)) { + errCopy = ODR_DEV_INCOMPAT; + } else if ((len != 0U) && (len != stream->dataLength)) { + errCopy = ODR_TYPE_MISMATCH; + } else { /* MISRA C 2004 14.10 */ + } + } + + if (err != NULL) { + *err = errCopy; + } + + return (errCopy == ODR_OK) ? stream->dataOrig : NULL; +} diff --git a/301/CO_ODinterface.h b/301/CO_ODinterface.h new file mode 100644 index 00000000..6c6bd65e --- /dev/null +++ b/301/CO_ODinterface.h @@ -0,0 +1,711 @@ +/** + * CANopen Object Dictionary interface + * + * @file CO_ODinterface.h + * @ingroup CO_ODinterface + * @author Janez Paternoster + * @copyright 2020 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#ifndef CO_OD_INTERFACE_H +#define CO_OD_INTERFACE_H + +#include "301/CO_driver.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup CO_ODinterface OD interface + * CANopen Object Dictionary interface. + * + * @ingroup CO_CANopen_301 + * @{ + * See @ref md_doc_2objectDictionary + */ + +#ifndef CO_OD_OWN_TYPES +typedef uint32_t OD_size_t; /**< Variable of type OD_size_t contains data length in bytes of OD variable */ +typedef uint8_t OD_attr_t; /**< Type (and size) of Object Dictionary attribute */ +#endif + +#ifndef OD_FLAGS_PDO_SIZE +#define OD_FLAGS_PDO_SIZE 4U /**< Size of of flagsPDO variable inside @ref OD_extension_t, from 0 to 32. */ +#endif + +#ifndef CO_PROGMEM +/** Modifier for OD objects. This is large amount of data and is specified in Object Dictionary (OD.c file usually) */ +#define CO_PROGMEM const +#endif + +/** + * Common DS301 object dictionary entries. + */ +typedef enum { + OD_H1000_DEV_TYPE = 0x1000U, /**< Device type */ + OD_H1001_ERR_REG = 0x1001U, /**< Error register */ + OD_H1002_MANUF_STATUS_REG = 0x1002U, /**< Manufacturer status register */ + OD_H1003_PREDEF_ERR_FIELD = 0x1003U, /**< Predefined error field */ + OD_H1004_RSV = 0x1004U, /**< Reserved */ + OD_H1005_COBID_SYNC = 0x1005U, /**< Sync message cob-id */ + OD_H1006_COMM_CYCL_PERIOD = 0x1006U, /**< Communication cycle period */ + OD_H1007_SYNC_WINDOW_LEN = 0x1007U, /**< Sync windows length */ + OD_H1008_MANUF_DEV_NAME = 0x1008U, /**< Manufacturer device name */ + OD_H1009_MANUF_HW_VERSION = 0x1009U, /**< Manufacturer hardware version */ + OD_H100A_MANUF_SW_VERSION = 0x100AU, /**< Manufacturer software version */ + OD_H100B_RSV = 0x100BU, /**< Reserved */ + OD_H100C_GUARD_TIME = 0x100CU, /**< Guard time */ + OD_H100D_LIFETIME_FACTOR = 0x100DU, /**< Life time factor */ + OD_H100E_RSV = 0x100EU, /**< Reserved */ + OD_H100F_RSV = 0x100FU, /**< Reserved */ + OD_H1010_STORE_PARAMETERS = 0x1010U, /**< Store params in persistent mem. */ + OD_H1011_RESTORE_DEFAULT = 0x1011U, /**< Restore default parameters */ + OD_H1012_COBID_TIME = 0x1012U, /**< Timestamp message cob-id */ + OD_H1013_HIGH_RES_TIMESTAMP = 0x1013U, /**< High resolution timestamp */ + OD_H1014_COBID_EMERGENCY = 0x1014U, /**< Emergency message cob-id */ + OD_H1015_INHIBIT_TIME_EMCY = 0x1015U, /**< Inhibit time emergency message */ + OD_H1016_CONSUMER_HB_TIME = 0x1016U, /**< Consumer heartbeat time */ + OD_H1017_PRODUCER_HB_TIME = 0x1017U, /**< Producer heartbeat time */ + OD_H1018_IDENTITY_OBJECT = 0x1018U, /**< Identity object */ + OD_H1019_SYNC_CNT_OVERFLOW = 0x1019U, /**< Sync counter overflow value */ + OD_H1020_VERIFY_CONFIG = 0x1020U, /**< Verify configuration */ + OD_H1021_STORE_EDS = 0x1021U, /**< Store EDS */ + OD_H1022_STORE_FORMAT = 0x1022U, /**< Store format */ + OD_H1023_OS_CMD = 0x1023U, /**< OS command */ + OD_H1024_OS_CMD_MODE = 0x1024U, /**< OS command mode */ + OD_H1025_OS_DBG_INTERFACE = 0x1025U, /**< OS debug interface */ + OD_H1026_OS_PROMPT = 0x1026U, /**< OS prompt */ + OD_H1027_MODULE_LIST = 0x1027U, /**< Module list */ + OD_H1028_EMCY_CONSUMER = 0x1028U, /**< Emergency consumer object */ + OD_H1029_ERR_BEHAVIOR = 0x1029U, /**< Error behaviour */ + OD_H1200_SDO_SERVER_1_PARAM = 0x1200U, /**< SDO server parameter */ + OD_H1280_SDO_CLIENT_1_PARAM = 0x1280U, /**< SDO client parameter */ + OD_H1300_GFC_PARAM = 0x1300U, /**< Global fail-safe command param */ + OD_H1301_SRDO_1_PARAM = 0x1301U, /**< SRDO communication parameter */ + OD_H1381_SRDO_1_MAPPING = 0x1381U, /**< SRDO mapping parameter */ + OD_H13FE_SRDO_VALID = 0x13FEU, /**< SRDO Configuration valid */ + OD_H13FF_SRDO_CHECKSUM = 0x13FFU, /**< SRDO configuration checksum */ + OD_H1400_RXPDO_1_PARAM = 0x1400U, /**< RXPDO communication parameter */ + OD_H1600_RXPDO_1_MAPPING = 0x1600U, /**< RXPDO mapping parameters */ + OD_H1800_TXPDO_1_PARAM = 0x1800U, /**< TXPDO communication parameter */ + OD_H1A00_TXPDO_1_MAPPING = 0x1A00U, /**< TXPDO mapping parameters */ +} OD_ObjDicId_30x_t; + +/** + * Attributes (bit masks) for OD sub-object. + */ +typedef enum { + ODA_SDO_R = 0x01U, /**< SDO server may read from the variable */ + ODA_SDO_W = 0x02U, /**< SDO server may write to the variable */ + ODA_SDO_RW = 0x03U, /**< SDO server may read from or write to the variable */ + ODA_TPDO = 0x04U, /**< Variable is mappable into TPDO (can be read) */ + ODA_RPDO = 0x08U, /**< Variable is mappable into RPDO (can be written) */ + ODA_TRPDO = 0x0CU, /**< Variable is mappable into TPDO or RPDO */ + ODA_TSRDO = 0x10U, /**< Variable is mappable into transmitting SRDO */ + ODA_RSRDO = 0x20U, /**< Variable is mappable into receiving SRDO */ + ODA_TRSRDO = 0x30U, /**< Variable is mappable into tx or rx SRDO */ + ODA_MB = 0x40U, /**< Variable is multi-byte ((u)int16_t to (u)int64_t) */ + ODA_STR = 0x80U /**< Shorter value, than specified variable size, may be written to the variable. SDO write will + fill remaining memory with zeroes. Attribute is used for VISIBLE_STRING and UNICODE_STRING. */ +} OD_attributes_t; + +/** + * Return codes from OD access functions. + * + * @ref OD_getSDOabCode() can be used to retrieve corresponding SDO abort code. + */ +typedef enum { + /* !!!! WARNING !!!! If changing these values, change also OD_getSDOabCode() function! */ + ODR_PARTIAL = -1, /**< Read/write is only partial, make more calls */ + ODR_OK = 0, /**< SDO abort 0x00000000 - Read/write successfully finished */ + ODR_OUT_OF_MEM = 1, /**< SDO abort 0x05040005 - Out of memory */ + ODR_UNSUPP_ACCESS = 2, /**< SDO abort 0x06010000 - Unsupported access to an object */ + ODR_WRITEONLY = 3, /**< SDO abort 0x06010001 - Attempt to read a write only object */ + ODR_READONLY = 4, /**< SDO abort 0x06010002 - Attempt to write a read only object */ + ODR_IDX_NOT_EXIST = 5, /**< SDO abort 0x06020000 - Object does not exist in the object dict. */ + ODR_NO_MAP = 6, /**< SDO abort 0x06040041 - Object cannot be mapped to the PDO */ + ODR_MAP_LEN = 7, /**< SDO abort 0x06040042 - PDO length exceeded */ + ODR_PAR_INCOMPAT = 8, /**< SDO abort 0x06040043 - General parameter incompatibility reasons */ + ODR_DEV_INCOMPAT = 9, /**< SDO abort 0x06040047 - General internal incompatibility in device */ + ODR_HW = 10, /**< SDO abort 0x06060000 - Access failed due to hardware error */ + ODR_TYPE_MISMATCH = 11, /**< SDO abort 0x06070010 - Data type does not match */ + ODR_DATA_LONG = 12, /**< SDO abort 0x06070012 - Data type does not match, length too high */ + ODR_DATA_SHORT = 13, /**< SDO abort 0x06070013 - Data type does not match, length too short */ + ODR_SUB_NOT_EXIST = 14, /**< SDO abort 0x06090011 - Sub index does not exist */ + ODR_INVALID_VALUE = 15, /**< SDO abort 0x06090030 - Invalid value for parameter (download only) */ + ODR_VALUE_HIGH = 16, /**< SDO abort 0x06090031 - Value range of parameter written too high */ + ODR_VALUE_LOW = 17, /**< SDO abort 0x06090032 - Value range of parameter written too low */ + ODR_MAX_LESS_MIN = 18, /**< SDO abort 0x06090036 - Maximum value is less than minimum value */ + ODR_NO_RESOURCE = 19, /**< SDO abort 0x060A0023 - Resource not available: SDO connection */ + ODR_GENERAL = 20, /**< SDO abort 0x08000000 - General error */ + ODR_DATA_TRANSF = 21, /**< SDO abort 0x08000020 - Data cannot be transferred or stored to app */ + ODR_DATA_LOC_CTRL = 22, /**< SDO abort 0x08000021 - Data can't be transferred (local control) */ + ODR_DATA_DEV_STATE = 23, /**< SDO abort 0x08000022 - Data can't be transf. (present device state) */ + ODR_OD_MISSING = 24, /**< SDO abort 0x08000023 - Object dictionary not present */ + ODR_NO_DATA = 25, /**< SDO abort 0x08000024 - No data available */ + ODR_COUNT = 26 /**< Last element, number of responses */ +} ODR_t; + +/** + * IO stream structure, used for read/write access to OD variable, part of @ref OD_IO_t. + */ +typedef struct { + void* dataOrig; /**< Pointer to original data object, defined by Object Dictionary. Default read/write functions + * operate on it. If memory for data object is not specified by Object Dictionary, then dataOrig is + * NULL. */ + void* object; /**< Pointer to object, passed by @ref OD_extension_init(). Can be used inside read / write functions + * from IO extension. */ + OD_size_t dataLength; /**< Data length in bytes or 0, if length is not specified */ + OD_size_t dataOffset; /**< In case of large data, dataOffset indicates position of already transferred data */ + OD_attr_t attribute; /**< Attribute bit-field of the OD sub-object, see @ref OD_attributes_t */ + uint16_t index; /**< Index of the OD object, informative */ + uint8_t subIndex; /**< Sub index of the OD sub-object, informative */ +} OD_stream_t; + +/** + * Structure for input / output on the OD variable. It is initialized with @ref OD_getSub() function. Access principle + * to OD variable is via read/write functions operating on stream, similar as standard read/write. + */ +typedef struct { + /** Object Dictionary stream object, passed to read or write */ + OD_stream_t stream; + /** + * Function pointer for reading value from specified variable from Object Dictionary. If OD variable is larger than + * buf, then this function must be called several times. After completed successful read function returns 'ODR_OK'. + * If read is partial, it returns 'ODR_PARTIAL'. In case of errors function returns code similar to SDO abort code. + * + * Read can be restarted with @ref OD_rwRestart() function. + * + * At the moment, when Object Dictionary is initialized, every variable has assigned the same "read" function. This + * default function simply copies data from Object Dictionary variable. Application can bind its own "read" function + * for specific object. In that case application is able to calculate data for reading from own internal state at + * the moment of "read" function call. Own "read" function on OD object can be initialized with @ref + * OD_extension_init() function. + * + * "read" function must always copy all own data to buf, except if "buf" is not large enough. ("*returnCode" must + * not return 'ODR_PARTIAL', if there is still space in "buf".) + * + * @warning Do not use @ref CO_LOCK_OD() and @ref CO_UNLOCK_OD() macros inside the read() function. See also @ref + * CO_critical_sections. + * + * @param stream Object Dictionary stream object. + * @param buf Pointer to external buffer, where to data will be copied. + * @param count Size of the external buffer in bytes. + * @param [out] countRead If return value is "ODR_OK" or "ODR_PARTIAL", then number of bytes successfully read must + * be returned here. + * + * @return Value from @ref ODR_t, "ODR_OK" in case of success. + */ + ODR_t (*read)(OD_stream_t* stream, void* buf, OD_size_t count, OD_size_t* countRead); + /** + * Function pointer for writing value into specified variable inside Object Dictionary. If OD variable is larger + * than buf, then this function must be called several times. After completed successful write function returns + * 'ODR_OK'. If write is partial, it returns 'ODR_PARTIAL'. In case of errors function returns code similar to SDO + * abort code. + * + * Write can be restarted with @ref OD_rwRestart() function. + * + * At the moment, when Object Dictionary is initialized, every variable has assigned the same "write" function, + * which simply copies data to Object Dictionary variable. Application can bind its own "write" function, similar as + * it can bind "read" function. + * + * "write" function must always copy all available data from buf. If OD variable expect more data, then + * "*returnCode" must return 'ODR_PARTIAL'. + * + * @warning Do not use @ref CO_LOCK_OD() and @ref CO_UNLOCK_OD() macros inside the write() function. See also @ref + * CO_critical_sections. + * + * @param stream Object Dictionary stream object. + * @param buf Pointer to external buffer, from where data will be copied. + * @param count Size of the external buffer in bytes. + * @param [out] countWritten If return value is "ODR_OK" or "ODR_PARTIAL", then number of bytes successfully written + * must be returned here. + * + * @return Value from @ref ODR_t, "ODR_OK" in case of success. + */ + ODR_t (*write)(OD_stream_t* stream, const void* buf, OD_size_t count, OD_size_t* countWritten); +} OD_IO_t; + +/** + * Extension of OD object, which can optionally be specified by application in initialization phase with @ref + * OD_extension_init() function. + */ +typedef struct { + /** Object on which read and write will operate, part of @ref OD_stream_t */ + void* object; + /** Application specified read function pointer. If NULL, then read will be disabled. @ref OD_readOriginal can be + * used here to keep the original read function. For function description see @ref OD_IO_t. */ + ODR_t (*read)(OD_stream_t* stream, void* buf, OD_size_t count, OD_size_t* countRead); + /** Application specified write function pointer. If NULL, then write will be disabled. @ref OD_writeOriginal can be + * used here to keep the original write function. For function description see @ref OD_IO_t. */ + ODR_t (*write)(OD_stream_t* stream, const void* buf, OD_size_t count, OD_size_t* countWritten); +#if OD_FLAGS_PDO_SIZE > 0 + /** PDO flags bit-field provides one bit for each OD variable, which exist inside OD object at specific sub index. + * If application clears that bit, and OD variable is mapped to an event driven TPDO, then TPDO will be sent. + * + * @ref OD_FLAGS_PDO_SIZE can have a value from 0 to 32 bytes, which corresponds to 0 to 256 available bits. If, for + * example, @ref OD_FLAGS_PDO_SIZE has value 4, then OD variables with sub index up to 31 will have the TPDO + * requesting functionality. See also @ref OD_requestTPDO and @ref OD_TPDOtransmitted. */ + uint8_t flagsPDO[OD_FLAGS_PDO_SIZE]; +#endif +} OD_extension_t; + +/** + * Object Dictionary entry for one OD object. + * + * OD entries are collected inside OD_t as array (list). Each OD entry contains basic information about OD object (index + * and subEntriesCount), pointer to odObject with additional information about var, array or record entry and pointer to + * extension, configurable by application. + */ +typedef struct { + uint16_t index; /**< Object Dictionary index */ + uint8_t subEntriesCount; /**< Number of all sub-entries, including sub-entry at sub-index 0 */ + uint8_t odObjectType; /**< Type of the odObject, indicated by @ref OD_objectTypes_t enumerator. */ + CO_PROGMEM void* odObject; /**< OD object of type indicated by odObjectType, from which @ref OD_getSub() fetches the + information */ + OD_extension_t* extension; /**< Extension to OD, specified by application */ +} OD_entry_t; + +/** + * Object Dictionary + */ +typedef struct { + uint16_t size; /**< Number of elements in the list, without last element, which is blank */ + OD_entry_t* list; /**< List OD entries (table of contents), ordered by index */ +} OD_t; + +/** + * Read value from original OD location + * + * This function can be used inside read / write functions, specified by @ref OD_extension_init(). It reads data + * directly from memory location specified by Object dictionary. If no IO extension is used on OD entry, then io->read + * returned by @ref OD_getSub() equals to this function. See also @ref OD_IO_t. + */ +ODR_t OD_readOriginal(OD_stream_t* stream, void* buf, OD_size_t count, OD_size_t* countRead); + +/** + * Write value to original OD location + * + * This function can be used inside read / write functions, specified by @ref OD_extension_init(). It writes data + * directly to memory location specified by Object dictionary. If no IO extension is used on OD entry, then io->write + * returned by @ref OD_getSub() equals to this function. See also @ref OD_IO_t. + */ +ODR_t OD_writeOriginal(OD_stream_t* stream, const void* buf, OD_size_t count, OD_size_t* countWritten); + +/** + * Find OD entry in Object Dictionary + * + * @param od Object Dictionary + * @param index CANopen Object Dictionary index of object in Object Dictionary + * + * @return Pointer to OD entry or NULL if not found + */ +OD_entry_t* OD_find(OD_t* od, uint16_t index); + +/** + * Find sub-object with specified sub-index on OD entry returned by OD_find. Function populates io structure with + * sub-object data. + * + * @warning + * Read and write functions may be called from different threads, so critical sections in custom functions must be + * observed, see @ref CO_critical_sections. + * + * @param entry Object Dictionary entry. + * @param subIndex Sub-index of the variable from the OD object. + * @param [out] io Structure will be populated on success. + * @param odOrig If true, then potential IO extension on entry will be ignored and access to data entry in the original + * OD location will be returned + * + * @return Value from @ref ODR_t, "ODR_OK" in case of success. + */ +ODR_t OD_getSub(const OD_entry_t* entry, uint8_t subIndex, OD_IO_t* io, bool_t odOrig); + +/** + * Return index from OD entry + * + * @param entry Object Dictionary entry. + * + * @return OD index + */ +static inline uint16_t +OD_getIndex(const OD_entry_t* entry) { + return (entry != NULL) ? entry->index : 0U; +} + +/** + * Check, if OD variable is mappable to PDO or SRDO. + * + * If OD variable is mappable, then it may be necessary to protect read/write access from mainline function. See @ref + * CO_critical_sections. + * + * @param stream Object Dictionary stream object. + * + * @return true, if OD variable is mappable. + */ +static inline bool_t +OD_mappable(OD_stream_t* stream) { + return (stream != NULL) ? ((stream->attribute & ((OD_attr_t)ODA_TRPDO | (OD_attr_t)ODA_TRSRDO)) != 0U) : false; +} + +/** + * Restart read or write operation on OD variable + * + * It is not necessary to call this function, if stream was initialized by @ref OD_getSub(). It is also not necessary to + * call this function, if previous read or write was successfully finished. + * + * @param stream Object Dictionary stream object. + */ +static inline void +OD_rwRestart(OD_stream_t* stream) { + if (stream != NULL) { + stream->dataOffset = 0U; + } +} + +/** + * Request TPDO, to which OD variable is mapped + * + * Function clears the flagPDO bit, which corresponds to OD variable at specific OD index and subindex. For this + * functionality to work, @ref OD_extension_t must be enabled on OD variable. If OD variable is mapped to any TPDO with + * event driven transmission, then TPDO will be transmitted after this function call. If OD variable is mapped to more + * than one TPDO with event driven transmission, only the first matched TPDO will be transmitted. + * + * TPDO event driven transmission is enabled, if TPDO communication parameter, transmission type is set to 0, 254 + * or 255. For other transmission types (synchronous) flagPDO bit is ignored. + * + * @param entry Object Dictionary entry. + * @param subIndex subIndex of the OD variable. + */ +static inline void +OD_requestTPDO(OD_entry_t* entry, uint8_t subIndex) { +#if OD_FLAGS_PDO_SIZE > 0 + if ((entry != NULL) && (entry->extension != NULL) && (subIndex < (OD_FLAGS_PDO_SIZE * 8U))) { + /* clear subIndex-th bit */ + uint8_t mask = ~(1U << (subIndex & 0x07U)); + entry->extension->flagsPDO[subIndex >> 3] &= mask; + } +#endif +} + +/** + * Check if requested TPDO was transmitted + * + * @param entry Object Dictionary entry. + * @param subIndex subIndex of the OD variable. + * + * @return Return true if event driven TPDO with mapping to OD variable, indicated by entry and subIndex, was + * transmitted since last @ref OD_requestTPDO call. If there was no @ref OD_requestTPDO call yet and TPDO was + * transmitted by other event, function also returns true. + */ +static inline bool_t +OD_TPDOtransmitted(OD_entry_t* entry, uint8_t subIndex) { +#if OD_FLAGS_PDO_SIZE > 0 + if ((entry != NULL) && (entry->extension != NULL) && (subIndex < (OD_FLAGS_PDO_SIZE * 8U))) { + /* return true, if subIndex-th bit is set */ + uint8_t mask = 1U << (subIndex & 0x07U); + if ((entry->extension->flagsPDO[subIndex >> 3] & mask) != 0U) { + return true; + } + } +#endif + return false; +} + +/** + * Get SDO abort code from returnCode + * + * @param returnCode Returned from some OD access functions + * + * @return Corresponding @ref CO_SDO_abortCode_t + */ +uint32_t OD_getSDOabCode(ODR_t returnCode); + +/** + * Extend OD object with own read/write functions and/or flagsPDO + * + * This function gives application very powerful tool: definition of own IO access on OD object. Structure and + * attributes are the same as defined in original OD object, but data are read directly from (or written directly to) + * application specified object via custom function calls. + * + * Before this function specifies extension, OD variables are accessed from original OD location. After this function + * specifies extension OD variables are accessed from read/write functions specified by extension. (Except when "odOrig" + * argument to @ref OD_getSub() is set to true.) + * + * This function must also be used, when flagsPDO needs to be enabled for specific entry. + * + * @warning + * Object dictionary storage works only directly on OD variables. It does not access read function specified here. So, + * if extended OD objects needs to be preserved, then @ref OD_writeOriginal can be used inside custom write function. + * + * @warning + * Read and write functions may be called from different threads, so critical sections in custom functions must be + * observed, see @ref CO_critical_sections. + * + * @param entry Object Dictionary entry. + * @param extension Extension object, which must be initialized externally. Extension object must exist permanently. If + * NULL, extension will be removed. + * + * @return "ODR_OK" on success, "ODR_IDX_NOT_EXIST" if OD object doesn't exist. + */ +static inline ODR_t +OD_extension_init(OD_entry_t* entry, OD_extension_t* extension) { + if (entry == NULL) { + return ODR_IDX_NOT_EXIST; + } + entry->extension = extension; + return ODR_OK; +} + +/** + * @defgroup CO_ODgetSetters Getters and setters + * @{ + * + * Getter and setter helper functions for accessing different types of Object Dictionary variables. + */ +/** + * Get variable from Object Dictionary + * + * @param entry Object Dictionary entry. + * @param subIndex Sub-index of the variable from the OD object. + * @param [out] val Value will be written here. + * @param len Size of value to retrieve from OD. + * @param odOrig If true, then potential IO extension on entry will be ignored and data in the original OD location will + * be returned. + * + * @return Value from @ref ODR_t, "ODR_OK" in case of success. Error, if variable does not exist in object dictionary or + * it does not have the correct length or other reason. + */ +ODR_t OD_get_value(const OD_entry_t* entry, uint8_t subIndex, void* val, OD_size_t len, bool_t odOrig); + +/** Get int8_t variable from Object Dictionary, see @ref OD_get_value */ +static inline ODR_t +OD_get_i8(const OD_entry_t* entry, uint8_t subIndex, int8_t* val, bool_t odOrig) { + return OD_get_value(entry, subIndex, val, sizeof(*val), odOrig); +} + +/** Get int16_t variable from Object Dictionary, see @ref OD_get_value */ +static inline ODR_t +OD_get_i16(const OD_entry_t* entry, uint8_t subIndex, int16_t* val, bool_t odOrig) { + return OD_get_value(entry, subIndex, val, sizeof(*val), odOrig); +} + +/** Get int32_t variable from Object Dictionary, see @ref OD_get_value */ +static inline ODR_t +OD_get_i32(const OD_entry_t* entry, uint8_t subIndex, int32_t* val, bool_t odOrig) { + return OD_get_value(entry, subIndex, val, sizeof(*val), odOrig); +} + +/** Get int64_t variable from Object Dictionary, see @ref OD_get_value */ +static inline ODR_t +OD_get_i64(const OD_entry_t* entry, uint8_t subIndex, int64_t* val, bool_t odOrig) { + return OD_get_value(entry, subIndex, val, sizeof(*val), odOrig); +} + +/** Get uint8_t variable from Object Dictionary, see @ref OD_get_value */ +static inline ODR_t +OD_get_u8(const OD_entry_t* entry, uint8_t subIndex, uint8_t* val, bool_t odOrig) { + return OD_get_value(entry, subIndex, val, sizeof(*val), odOrig); +} + +/** Get uint16_t variable from Object Dictionary, see @ref OD_get_value */ +static inline ODR_t +OD_get_u16(const OD_entry_t* entry, uint8_t subIndex, uint16_t* val, bool_t odOrig) { + return OD_get_value(entry, subIndex, val, sizeof(*val), odOrig); +} + +/** Get uint32_t variable from Object Dictionary, see @ref OD_get_value */ +static inline ODR_t +OD_get_u32(const OD_entry_t* entry, uint8_t subIndex, uint32_t* val, bool_t odOrig) { + return OD_get_value(entry, subIndex, val, sizeof(*val), odOrig); +} + +/** Get uint64_t variable from Object Dictionary, see @ref OD_get_value */ +static inline ODR_t +OD_get_u64(const OD_entry_t* entry, uint8_t subIndex, uint64_t* val, bool_t odOrig) { + return OD_get_value(entry, subIndex, val, sizeof(*val), odOrig); +} + +/** Get float32_t variable from Object Dictionary, see @ref OD_get_value */ +static inline ODR_t +OD_get_f32(const OD_entry_t* entry, uint8_t subIndex, float32_t* val, bool_t odOrig) { + return OD_get_value(entry, subIndex, val, sizeof(*val), odOrig); +} + +/** Get float64_t variable from Object Dictionary, see @ref OD_get_value */ +static inline ODR_t +OD_get_f64(const OD_entry_t* entry, uint8_t subIndex, float64_t* val, bool_t odOrig) { + return OD_get_value(entry, subIndex, val, sizeof(*val), odOrig); +} + +/** + * Set variable in Object Dictionary + * + * @param entry Object Dictionary entry. + * @param subIndex Sub-index of the variable from the OD object. + * @param val Pointer to value to write. + * @param len Size of value to write. + * @param odOrig If true, then potential IO extension on entry will be ignored and data in the original OD location will + * be written. + * + * @return Value from @ref ODR_t, "ODR_OK" in case of success. Error, if variable does not exist in object dictionary or + * it does not have the correct length or other reason. + */ +ODR_t OD_set_value(const OD_entry_t* entry, uint8_t subIndex, void* val, OD_size_t len, bool_t odOrig); + +/** Set int8_t variable in Object Dictionary, see @ref OD_set_value */ +static inline ODR_t +OD_set_i8(const OD_entry_t* entry, uint8_t subIndex, int8_t val, bool_t odOrig) { + return OD_set_value(entry, subIndex, &val, sizeof(val), odOrig); +} + +/** Set int16_t variable in Object Dictionary, see @ref OD_set_value */ +static inline ODR_t +OD_set_i16(const OD_entry_t* entry, uint8_t subIndex, int16_t val, bool_t odOrig) { + return OD_set_value(entry, subIndex, &val, sizeof(val), odOrig); +} + +/** Set int32_t variable in Object Dictionary, see @ref OD_set_value */ +static inline ODR_t +OD_set_i32(const OD_entry_t* entry, uint8_t subIndex, int32_t val, bool_t odOrig) { + return OD_set_value(entry, subIndex, &val, sizeof(val), odOrig); +} + +/** Set int32_t variable in Object Dictionary, see @ref OD_set_value */ +static inline ODR_t +OD_set_i64(const OD_entry_t* entry, uint8_t subIndex, int64_t val, bool_t odOrig) { + return OD_set_value(entry, subIndex, &val, sizeof(val), odOrig); +} + +/** Set uint8_t variable in Object Dictionary, see @ref OD_set_value */ +static inline ODR_t +OD_set_u8(const OD_entry_t* entry, uint8_t subIndex, uint8_t val, bool_t odOrig) { + return OD_set_value(entry, subIndex, &val, sizeof(val), odOrig); +} + +/** Set uint16_t variable in Object Dictionary, see @ref OD_set_value */ +static inline ODR_t +OD_set_u16(const OD_entry_t* entry, uint8_t subIndex, uint16_t val, bool_t odOrig) { + return OD_set_value(entry, subIndex, &val, sizeof(val), odOrig); +} + +/** Set uint32_t variable in Object Dictionary, see @ref OD_set_value */ +static inline ODR_t +OD_set_u32(const OD_entry_t* entry, uint8_t subIndex, uint32_t val, bool_t odOrig) { + return OD_set_value(entry, subIndex, &val, sizeof(val), odOrig); +} + +/** Set uint64_t variable in Object Dictionary, see @ref OD_set_value */ +static inline ODR_t +OD_set_u64(const OD_entry_t* entry, uint8_t subIndex, uint64_t val, bool_t odOrig) { + return OD_set_value(entry, subIndex, &val, sizeof(val), odOrig); +} + +/** Set float32_t variable in Object Dictionary, see @ref OD_set_value */ +static inline ODR_t +OD_set_f32(const OD_entry_t* entry, uint8_t subIndex, float32_t val, bool_t odOrig) { + return OD_set_value(entry, subIndex, &val, sizeof(val), odOrig); +} + +/** Set float64_t variable in Object Dictionary, see @ref OD_set_value */ +static inline ODR_t +OD_set_f64(const OD_entry_t* entry, uint8_t subIndex, float64_t val, bool_t odOrig) { + return OD_set_value(entry, subIndex, &val, sizeof(val), odOrig); +} + +/** + * Get pointer to memory which holds data variable from Object Dictionary + * + * Function always returns "dataOrig" pointer, which points to data in the original OD location. Take care, if IO + * extension is enabled on OD entry. Take also care that "dataOrig" could be not aligned to data type. + * + * @param entry Object Dictionary entry. + * @param subIndex Sub-index of the variable from the OD object. + * @param len Required length of the variable. If len is different than zero, then actual length of the variable must + * match len or error is returned. + * @param [out] err Error reason is written here in case of error (allow NULL). + * + * @return Pointer to variable in Object Dictionary or NULL in case of error. + */ +void* OD_getPtr(const OD_entry_t* entry, uint8_t subIndex, OD_size_t len, ODR_t* err); +/** @} */ /* CO_ODgetSetters */ + +#if defined OD_DEFINITION || defined CO_DOXYGEN +/** + * @defgroup CO_ODdefinition OD definition objects + * @{ + * + * Types and functions used only for definition of Object Dictionary + */ +/** + * Types for OD object. + */ +typedef enum { + ODT_VAR = 0x01, /**< This type corresponds to CANopen Object Dictionary object with object code equal to VAR. OD + object is type of @ref OD_obj_var_t and represents single variable of any type (any length), + located on sub-index 0. Other sub-indexes are not used. */ + ODT_ARR = 0x02, /**< This type corresponds to CANopen Object Dictionary object with object code equal to ARRAY. OD + object is type of @ref OD_obj_array_t and represents array of variables with the same type, + located on sub-indexes above 0. Sub-index 0 is of type uint8_t and usually represents length of + the array. */ + ODT_REC = 0x03, /**< This type corresponds to CANopen Object Dictionary object with object code equal to RECORD. + This type of OD object represents structure of the variables. Each variable from the structure + can have own type and own attribute. OD object is an array of elements of type @ref OD_obj_var_t. + Variable at sub-index 0 is of type uint8_t and usually represents number of sub-elements in the + structure. */ + ODT_TYPE_MASK = 0x0F, /**< Mask for basic type */ +} OD_objectTypes_t; + +/** + * Object for single OD variable, used for "VAR" type OD objects + */ +typedef struct { + void* dataOrig; /**< Pointer to data */ + OD_attr_t attribute; /**< Attribute bitfield, see @ref OD_attributes_t */ + OD_size_t dataLength; /**< Data length in bytes */ +} OD_obj_var_t; + +/** + * Object for OD array of variables, used for "ARRAY" type OD objects + */ +typedef struct { + uint8_t* dataOrig0; /**< Pointer to data for sub-index 0 */ + void* dataOrig; /**< Pointer to array of data */ + OD_attr_t attribute0; /**< Attribute bitfield for sub-index 0, see @ref OD_attributes_t */ + OD_attr_t attribute; /**< Attribute bitfield for array elements */ + OD_size_t dataElementLength; /**< Data length of array elements in bytes */ + OD_size_t dataElementSizeof; /**< Sizeof one array element in bytes */ +} OD_obj_array_t; + +/** + * Object for OD sub-elements, used in "RECORD" type OD objects + */ +typedef struct { + void* dataOrig; /**< Pointer to data */ + uint8_t subIndex; /**< Sub index of element. */ + OD_attr_t attribute; /**< Attribute bitfield, see @ref OD_attributes_t */ + OD_size_t dataLength; /**< Data length in bytes */ +} OD_obj_record_t; + +/** @} */ /* CO_ODdefinition */ + +#endif /* defined OD_DEFINITION */ + +/** @} */ /* CO_ODinterface */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CO_OD_INTERFACE_H */ diff --git a/301/CO_PDO.c b/301/CO_PDO.c new file mode 100644 index 00000000..f264c73a --- /dev/null +++ b/301/CO_PDO.c @@ -0,0 +1,1358 @@ +/* + * CANopen Receive Process Data Object protocol. + * + * @file CO_PDO.c + * @ingroup CO_PDO + * @author Janez Paternoster + * @copyright 2021 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#include + +#include "301/CO_PDO.h" + +#if ((CO_CONFIG_PDO) & (CO_CONFIG_RPDO_ENABLE | CO_CONFIG_TPDO_ENABLE)) != 0 + +#if ((CO_CONFIG_PDO) & CO_CONFIG_FLAG_OD_DYNAMIC) != 0 +#if ((CO_CONFIG_PDO) & CO_CONFIG_PDO_OD_IO_ACCESS) == 0 +#error Dynamic PDO mapping is not possible without CO_CONFIG_PDO_OD_IO_ACCESS +#endif +#endif + +#if ((CO_CONFIG_PDO) & CO_CONFIG_PDO_OD_IO_ACCESS) != 0 +/* + * Custom function for write dummy OD object. Will be used only from RPDO. + * + * For more information see file CO_ODinterface.h, OD_IO_t. + */ +static ODR_t +OD_write_dummy(OD_stream_t* stream, const void* buf, OD_size_t count, OD_size_t* countWritten) { + (void)stream; + (void)buf; + if (countWritten != NULL) { + *countWritten = count; + } + return ODR_OK; +} + +/* + * Custom function for read dummy OD object. Will be used only from TPDO. + * + * For more information see file CO_ODinterface.h, OD_IO_t. + */ +static ODR_t +OD_read_dummy(OD_stream_t* stream, void* buf, OD_size_t count, OD_size_t* countRead) { + if ((buf == NULL) || (stream == NULL) || (countRead == NULL)) { + return ODR_DEV_INCOMPAT; + } + + if (count > stream->dataLength) { + count = stream->dataLength; + } + + (void)memset(buf, 0, count); + + *countRead = count; + return ODR_OK; +} + +/* + * Find mapped variable in Object Dictionary and configure entry in RPDO or TPDO + * + * @param PDO This object will be configured. If map is erroneous, then it will stay unchanged. + * @param map PDO mapping parameter. + * @param mapIndex from 0 to CO_PDO_MAX_MAPPED_ENTRIES + * @param isRPDO True for RPDO and false for TPDO. + * @param OD Object Dictionary. + * + * @return ODR_OK on success, otherwise error reason. + */ +static ODR_t +PDOconfigMap(CO_PDO_common_t* PDO, uint32_t map, uint8_t mapIndex, bool_t isRPDO, OD_t* OD) { + uint16_t index = (uint16_t)(map >> 16); + uint8_t subIndex = (uint8_t)(map >> 8); + uint8_t mappedLengthBits = (uint8_t)map; + uint8_t mappedLength = mappedLengthBits >> 3; + OD_IO_t* OD_IO = &PDO->OD_IO[mapIndex]; + + /* total PDO length can not be more than CO_PDO_MAX_SIZE bytes */ + if (mappedLength > CO_PDO_MAX_SIZE) { + return ODR_MAP_LEN; /* PDO length exceeded */ + } + + /* is there a reference to the dummy entry */ + if ((index < 0x20U) && (subIndex == 0U)) { + OD_stream_t* stream = &OD_IO->stream; + (void)memset(stream, 0, sizeof(OD_stream_t)); + stream->dataLength = mappedLength; + stream->dataOffset = mappedLength; + OD_IO->read = OD_read_dummy; + OD_IO->write = OD_write_dummy; + return ODR_OK; + } + + /* find entry in the Object Dictionary */ + OD_IO_t OD_IOcopy; + OD_entry_t* entry = OD_find(OD, index); + ODR_t odRet = OD_getSub(entry, subIndex, &OD_IOcopy, false); + if (odRet != ODR_OK) { + return odRet; + } + + /* verify access attributes, byte alignment and length */ + OD_attr_t testAttribute = isRPDO ? (OD_attr_t)(ODA_RPDO) : (OD_attr_t)(ODA_TPDO); + if (((OD_IOcopy.stream.attribute & testAttribute) == 0U) || ((mappedLengthBits & 0x07U) != 0U) + || (OD_IOcopy.stream.dataLength < mappedLength)) { + return ODR_NO_MAP; /* Object cannot be mapped to the PDO. */ + } + + /* Copy values and store mappedLength temporary. */ + *OD_IO = OD_IOcopy; + OD_IO->stream.dataOffset = mappedLength; + + /* get TPDO request flag byte from extension */ +#if OD_FLAGS_PDO_SIZE > 0 + if (!isRPDO) { + if ((subIndex < (OD_FLAGS_PDO_SIZE * 8U)) && (entry->extension != NULL)) { + PDO->flagPDObyte[mapIndex] = &entry->extension->flagsPDO[subIndex >> 3]; + PDO->flagPDObitmask[mapIndex] = 1U << (subIndex & 0x07U); + } else { + PDO->flagPDObyte[mapIndex] = NULL; + } + } +#endif + + return ODR_OK; +} + +/* + * Initialize PDO mapping parameters + * + * @param PDO This object. + * @param OD Object Dictionary. + * @param OD_PDOMapPar OD entry for "PDO mapping parameter". + * @param isRPDO True for RPDO and false for TPDO. + * @param [out] errInfo Additional information in case of error, may be NULL. + * @param [out] erroneousMap Additional information about erroneous map. + * + * @return #CO_ReturnError_t CO_ERROR_NO on success. + */ +static CO_ReturnError_t +PDO_initMapping(CO_PDO_common_t* PDO, OD_t* OD, OD_entry_t* OD_PDOMapPar, bool_t isRPDO, uint32_t* errInfo, + uint32_t* erroneousMap) { + ODR_t odRet; + size_t pdoDataLength = 0; + uint8_t mappedObjectsCount = 0; + + /* number of mapped application objects in PDO */ + odRet = OD_get_u8(OD_PDOMapPar, 0, &mappedObjectsCount, true); + if (odRet != ODR_OK) { + if (errInfo != NULL) { + *errInfo = ((uint32_t)OD_getIndex(OD_PDOMapPar)) << 8; + } + return CO_ERROR_OD_PARAMETERS; + } + + for (uint8_t i = 0; i < CO_PDO_MAX_MAPPED_ENTRIES; i++) { + OD_IO_t* OD_IO = &PDO->OD_IO[i]; + uint32_t map = 0; + + odRet = OD_get_u32(OD_PDOMapPar, i + 1U, &map, true); + if (odRet == ODR_SUB_NOT_EXIST) { + continue; + } + if (odRet != ODR_OK) { + if (errInfo != NULL) { + *errInfo = (((uint32_t)OD_getIndex(OD_PDOMapPar)) << 8) | i; + } + return CO_ERROR_OD_PARAMETERS; + } + + odRet = PDOconfigMap(PDO, map, i, isRPDO, OD); + if (odRet != ODR_OK) { + /* indicate erroneous mapping in initialization phase */ + OD_IO->stream.dataLength = 0; + OD_IO->stream.dataOffset = 0xFF; + if (*erroneousMap == 0U) { + *erroneousMap = map; + } + } + + if (i < mappedObjectsCount) { + pdoDataLength += OD_IO->stream.dataOffset; + } + } + if ((pdoDataLength > CO_PDO_MAX_SIZE) || ((pdoDataLength == 0U) && (mappedObjectsCount > 0U))) { + if (*erroneousMap == 0U) { + *erroneousMap = 1; + } + } + + if (*erroneousMap == 0U) { + PDO->dataLength = (CO_PDO_size_t)pdoDataLength; + PDO->mappedObjectsCount = mappedObjectsCount; + } + + return CO_ERROR_NO; +} + +#if ((CO_CONFIG_PDO) & CO_CONFIG_FLAG_OD_DYNAMIC) != 0 +/* + * Custom function for writing OD object "PDO mapping parameter" + * + * For more information see file CO_ODinterface.h, OD_IO_t. + */ +static ODR_t +OD_write_PDO_mapping(OD_stream_t* stream, const void* buf, OD_size_t count, OD_size_t* countWritten) { + /* "count" is already verified in *_init() function */ + if ((stream == NULL) || (buf == NULL) || (countWritten == NULL) || (stream->subIndex > CO_PDO_MAX_MAPPED_ENTRIES)) { + return ODR_DEV_INCOMPAT; + } + + /* Only common part of the CO_RPDO_t or CO_TPDO_t will be used */ + CO_PDO_common_t* PDO = stream->object; + + /* PDO must be disabled before mapping configuration */ + if ((PDO->valid) || ((PDO->mappedObjectsCount != 0U) && (stream->subIndex > 0U))) { + return ODR_UNSUPP_ACCESS; + } + + if (stream->subIndex == 0U) { + uint8_t mappedObjectsCount = CO_getUint8(buf); + size_t pdoDataLength = 0; + + if (mappedObjectsCount > CO_PDO_MAX_MAPPED_ENTRIES) { + return ODR_MAP_LEN; + } + + /* validate enabled mapping parameters */ + for (uint8_t i = 0; i < mappedObjectsCount; i++) { + OD_IO_t* OD_IO = &PDO->OD_IO[i]; + size_t dataLength = (size_t)OD_IO->stream.dataLength; + size_t mappedLength = (size_t)OD_IO->stream.dataOffset; + + if (mappedLength > dataLength) { + /* erroneous map since device initial values */ + return ODR_NO_MAP; + } + pdoDataLength += mappedLength; + } + + if (pdoDataLength > CO_PDO_MAX_SIZE) { + return ODR_MAP_LEN; + } + if ((pdoDataLength == 0U) && (mappedObjectsCount > 0U)) { + return ODR_INVALID_VALUE; + } + + /* success, update PDO */ + PDO->dataLength = (CO_PDO_size_t)pdoDataLength; + PDO->mappedObjectsCount = mappedObjectsCount; + } else { + uint32_t val = CO_getUint32(buf); + ODR_t odRet = PDOconfigMap(PDO, val, stream->subIndex - 1U, PDO->isRPDO, PDO->OD); + if (odRet != ODR_OK) { + return odRet; + } + } + + /* write value to the original location in the Object Dictionary */ + return OD_writeOriginal(stream, buf, count, countWritten); +} +#endif /* (CO_CONFIG_PDO) & CO_CONFIG_FLAG_OD_DYNAMIC */ +#endif /* (CO_CONFIG_PDO) & CO_CONFIG_PDO_OD_IO_ACCESS */ + +#if ((CO_CONFIG_PDO) & CO_CONFIG_PDO_OD_IO_ACCESS) == 0 +static CO_ReturnError_t +PDO_initMapping(CO_PDO_common_t* PDO, OD_t* OD, OD_entry_t* OD_PDOMapPar, bool_t isRPDO, uint32_t* errInfo, + uint32_t* erroneousMap) { + ODR_t odRet; + size_t pdoDataLength = 0; + + /* number of mapped application objects in PDO */ + uint8_t mappedObjectsCount = 0; + odRet = OD_get_u8(OD_PDOMapPar, 0, &mappedObjectsCount, true); + if (odRet != ODR_OK) { + if (errInfo != NULL) { + *errInfo = ((uint32_t)OD_getIndex(OD_PDOMapPar)) << 8; + } + return CO_ERROR_OD_PARAMETERS; + } + if (mappedObjectsCount > CO_PDO_MAX_SIZE) { + *erroneousMap = 1; + return CO_ERROR_NO; + } + + /* iterate mapped OD variables */ + for (uint8_t i = 0; i < mappedObjectsCount; i++) { + uint32_t map = 0; + + odRet = OD_get_u32(OD_PDOMapPar, i + 1, &map, true); + if (odRet != ODR_OK) { + if (errInfo != NULL) { + *errInfo = (((uint32_t)OD_getIndex(OD_PDOMapPar)) << 8) | i; + } + return CO_ERROR_OD_PARAMETERS; + } + uint16_t index = (uint16_t)(map >> 16); + uint8_t subIndex = (uint8_t)(map >> 8); + uint8_t mappedLengthBits = (uint8_t)map; + uint8_t mappedLength = mappedLengthBits >> 3; + uint8_t pdoDataStart = pdoDataLength; + pdoDataLength += mappedLength; + + if ((mappedLengthBits & 0x07) != 0 || pdoDataLength > CO_PDO_MAX_SIZE) { + *erroneousMap = map; + return CO_ERROR_NO; + } + + /* is there a reference to the dummy entry */ + if (index < 0x20 && subIndex == 0) { + for (uint8_t j = pdoDataStart; j < pdoDataLength; j++) { + static uint8_t dummyTX = 0; + static uint8_t dummyRX; + PDO->mapPointer[j] = isRPDO ? &dummyRX : &dummyTX; + } + continue; + } + + /* find entry in the Object Dictionary, original location */ + OD_IO_t OD_IO; + OD_entry_t* entry = OD_find(OD, index); + OD_attr_t testAttribute = isRPDO ? ODA_RPDO : ODA_TPDO; + + ODR_t odRet = OD_getSub(entry, subIndex, &OD_IO, true); + if (odRet != ODR_OK || (OD_IO.stream.attribute & testAttribute) == 0 || OD_IO.stream.dataLength < mappedLength + || OD_IO.stream.dataOrig == NULL) { + *erroneousMap = map; + return CO_ERROR_NO; + } + + /* write locations to OD variable data bytes into PDO map pointers */ +#ifdef CO_BIG_ENDIAN + if ((OD_IO.stream.attribute & ODA_MB) != 0) { + uint8_t* odDataPointer = OD_IO.stream.dataOrig + OD_IO.stream.dataLength - 1; + for (uint8_t j = pdoDataStart; j < pdoDataLength; j++) { + PDO->mapPointer[j] = odDataPointer--; + } + } else +#endif + { + uint8_t* odDataPointer = OD_IO.stream.dataOrig; + for (uint8_t j = pdoDataStart; j < pdoDataLength; j++) { + PDO->mapPointer[j] = odDataPointer++; + } + } + + /* get TPDO request flag byte from extension */ +#if OD_FLAGS_PDO_SIZE > 0 + if (!isRPDO && subIndex < (OD_FLAGS_PDO_SIZE * 8) && entry->extension != NULL) { + PDO->flagPDObyte[pdoDataStart] = &entry->extension->flagsPDO[subIndex >> 3]; + PDO->flagPDObitmask[pdoDataStart] = 1 << (subIndex & 0x07); + } +#endif + } + + PDO->dataLength = PDO->mappedObjectsCount = pdoDataLength; + return CO_ERROR_NO; +} + +#endif /* ((CO_CONFIG_PDO) & CO_CONFIG_PDO_OD_IO_ACCESS) == 0 */ + +#if ((CO_CONFIG_PDO) & CO_CONFIG_FLAG_OD_DYNAMIC) != 0 +/* + * Custom function for reading OD object "PDO communication parameter" + * + * For more information see file CO_ODinterface.h, OD_IO_t. + */ +static ODR_t +OD_read_PDO_commParam(OD_stream_t* stream, void* buf, OD_size_t count, OD_size_t* countRead) { + ODR_t returnCode = OD_readOriginal(stream, buf, count, countRead); + + /* When reading COB_ID, add Node-Id to the read value, if necessary */ + if ((returnCode == ODR_OK) && (stream->subIndex == 1U) && (*countRead == 4U)) { + /* Only common part of the CO_RPDO_t or CO_TPDO_t will be used */ + CO_PDO_common_t* PDO = stream->object; + uint32_t COB_ID = CO_getUint32(buf); + uint16_t CAN_ID = (uint16_t)(COB_ID & 0x7FFU); + + /* If default CAN-ID is stored in OD (without Node-ID), add Node-ID */ + if ((CAN_ID != 0U) && (CAN_ID == (PDO->preDefinedCanId & 0xFF80U))) { + COB_ID = (COB_ID & 0xFFFF0000U) | PDO->preDefinedCanId; + } + + /* If PDO is not valid, set bit 31 */ + if (!PDO->valid) { + COB_ID |= 0x80000000U; + } + + (void)CO_setUint32(buf, COB_ID); + } + + return returnCode; +} +#endif /* (CO_CONFIG_PDO) & CO_CONFIG_FLAG_OD_DYNAMIC */ + +/******************************************************************************* + * R P D O + ******************************************************************************/ +#if ((CO_CONFIG_PDO) & CO_CONFIG_RPDO_ENABLE) != 0 +/* + * @defgroup CO_PDO_receiveErrors_t States for RPDO->receiveError indicates received RPDOs with wrong length. + * @{ + * + */ +#define CO_RPDO_RX_ACK_NO_ERROR 0U /* No error */ +#define CO_RPDO_RX_ACK_ERROR 1U /* Error is acknowledged */ +#define CO_RPDO_RX_ACK 10U /* Auxiliary value */ +#define CO_RPDO_RX_OK 11U /* Correct RPDO received, not acknowledged */ +#define CO_RPDO_RX_SHORT 12U /* Too short RPDO received, not acknowledged */ +#define CO_RPDO_RX_LONG 13U /* Too long RPDO received, not acknowledged */ + +/* @} */ /* CO_PDO_receiveErrors_t */ + +/* + * Read received message from CAN module. + * + * Function will be called (by CAN receive interrupt) every time, when CAN message with correct identifier + * will be received. For more information and description of parameters see file CO_driver.h. + * If new message arrives and previous message wasn't processed yet, then + * previous message will be lost and overwritten by the new message. + */ +static void +CO_PDO_receive(void* object, void* msg) { + CO_RPDO_t* RPDO = object; + CO_PDO_common_t* PDO = &RPDO->PDO_common; + uint8_t DLC = CO_CANrxMsg_readDLC(msg); + const uint8_t* data = CO_CANrxMsg_readData(msg); + uint8_t err = RPDO->receiveError; + + if (PDO->valid) { + if (DLC >= PDO->dataLength) { + /* indicate errors in PDO length */ + if (DLC == PDO->dataLength) { + if (err == CO_RPDO_RX_ACK_ERROR) { + err = CO_RPDO_RX_OK; + } + } else { + if (err == CO_RPDO_RX_ACK_NO_ERROR) { + err = CO_RPDO_RX_LONG; + } + } + + /* Determine, to which of the two rx buffers copy the message. */ + uint8_t bufNo = 0; +#if ((CO_CONFIG_PDO) & CO_CONFIG_PDO_SYNC_ENABLE) != 0 + if (RPDO->synchronous && (RPDO->SYNC != NULL) && RPDO->SYNC->CANrxToggle) { + bufNo = 1; + } +#endif + + /* copy data into appropriate buffer and set 'new message' flag */ + if (data != NULL) { + (void)memcpy(RPDO->CANrxData[bufNo], data, CO_PDO_MAX_SIZE); + CO_FLAG_SET(RPDO->CANrxNew[bufNo]); + } + +#if ((CO_CONFIG_PDO) & CO_CONFIG_FLAG_CALLBACK_PRE) != 0 + /* Optional signal to RTOS, which can resume task, which handles the RPDO. */ + if (RPDO->pFunctSignalPre != NULL) { + RPDO->pFunctSignalPre(RPDO->functSignalObjectPre); + } +#endif + } else if (err == CO_RPDO_RX_ACK_NO_ERROR) { + err = CO_RPDO_RX_SHORT; + } else { /* MISRA C 2004 14.10 */ + } + } + + RPDO->receiveError = err; +} + +#if ((CO_CONFIG_PDO) & CO_CONFIG_FLAG_OD_DYNAMIC) != 0 +/* + * Custom function for writing OD object "RPDO communication parameter" + * + * For more information see file CO_ODinterface.h, OD_IO_t. + */ +static ODR_t +OD_write_14xx(OD_stream_t* stream, const void* buf, OD_size_t count, OD_size_t* countWritten) { + /* "count" is also verified in *_init() function */ + if ((stream == NULL) || (buf == NULL) || (countWritten == NULL) || (count > 4U)) { + return ODR_DEV_INCOMPAT; + } + + CO_RPDO_t* RPDO = stream->object; + CO_PDO_common_t* PDO = &RPDO->PDO_common; + uint8_t bufCopy[4]; + (void)memcpy((void*)bufCopy, (const void*)buf, count); + + switch (stream->subIndex) { + case 1: { /* COB-ID used by PDO */ + uint32_t COB_ID = CO_getUint32(buf); + uint16_t CAN_ID = (uint16_t)(COB_ID & 0x7FFU); + bool_t valid = (COB_ID & 0x80000000U) == 0U; + + /* bits 11...29 must be zero, PDO must be disabled on change, CAN_ID == 0 is + * not allowed, mapping must be configured before enabling the PDO */ + if (((COB_ID & 0x3FFFF800U) != 0U) || (valid && PDO->valid && (CAN_ID != PDO->configuredCanId)) + || (valid && CO_IS_RESTRICTED_CAN_ID(CAN_ID)) || (valid && (PDO->mappedObjectsCount == 0U))) { + return ODR_INVALID_VALUE; + } + + /* parameter changed? */ + if ((valid != PDO->valid) || (CAN_ID != PDO->configuredCanId)) { + /* if default CAN-ID is written, store to OD without Node-ID */ + if (CAN_ID == PDO->preDefinedCanId) { + (void)CO_setUint32(bufCopy, COB_ID & 0xFFFFFF80U); + } + if (!valid) { + CAN_ID = 0; + } + + CO_ReturnError_t ret = CO_CANrxBufferInit(PDO->CANdev, PDO->CANdevIdx, CAN_ID, 0x7FF, false, + (void*)RPDO, CO_PDO_receive); + + if (valid && (ret == CO_ERROR_NO)) { + PDO->valid = true; + PDO->configuredCanId = CAN_ID; + } else { + PDO->valid = false; + CO_FLAG_CLEAR(RPDO->CANrxNew[0]); +#if ((CO_CONFIG_PDO) & CO_CONFIG_PDO_SYNC_ENABLE) != 0 + CO_FLAG_CLEAR(RPDO->CANrxNew[1]); +#endif + if (ret != CO_ERROR_NO) { + return ODR_DEV_INCOMPAT; + } + } + } + break; + } + + case 2: { /* transmission type */ uint8_t transmissionType = CO_getUint8(buf); +#if ((CO_CONFIG_PDO) & CO_CONFIG_PDO_SYNC_ENABLE) != 0 + if ((transmissionType > (uint8_t)CO_PDO_TRANSM_TYPE_SYNC_240) + && (transmissionType < (uint8_t)CO_PDO_TRANSM_TYPE_SYNC_EVENT_LO)) { + return ODR_INVALID_VALUE; + } + + bool_t synchronous = transmissionType <= (uint8_t)CO_PDO_TRANSM_TYPE_SYNC_240; + /* Remove old message from the second buffer. */ + if (RPDO->synchronous != synchronous) { + CO_FLAG_CLEAR(RPDO->CANrxNew[1]); + } + + RPDO->synchronous = synchronous; +#else + if (transmissionType < CO_PDO_TRANSM_TYPE_SYNC_EVENT_LO) { + return ODR_INVALID_VALUE; + } +#endif + break; + } + +#if ((CO_CONFIG_PDO) & CO_CONFIG_RPDO_TIMERS_ENABLE) != 0 + case 5: { /* event-timer */ + uint32_t eventTime = CO_getUint16(buf); + RPDO->timeoutTime_us = eventTime * 1000U; + RPDO->timeoutTimer = 0; + break; + } +#endif + default: + /* MISRA C 2004 15.3 */ + break; + } + + /* write value to the original location in the Object Dictionary */ + return OD_writeOriginal(stream, bufCopy, count, countWritten); +} +#endif /* (CO_CONFIG_PDO) & CO_CONFIG_FLAG_OD_DYNAMIC */ + +CO_ReturnError_t +CO_RPDO_init(CO_RPDO_t* RPDO, OD_t* OD, CO_EM_t* em, +#if ((CO_CONFIG_PDO) & CO_CONFIG_PDO_SYNC_ENABLE) != 0 + CO_SYNC_t* SYNC, +#endif + uint16_t preDefinedCanId, OD_entry_t* OD_14xx_RPDOCommPar, OD_entry_t* OD_16xx_RPDOMapPar, + CO_CANmodule_t* CANdevRx, uint16_t CANdevRxIdx, uint32_t* errInfo) { + CO_PDO_common_t* PDO = &RPDO->PDO_common; + CO_ReturnError_t ret; + ODR_t odRet; + + /* verify arguments */ + if ((RPDO == NULL) || (OD == NULL) || (em == NULL) || (OD_14xx_RPDOCommPar == NULL) || (OD_16xx_RPDOMapPar == NULL) + || (CANdevRx == NULL)) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + /* clear object */ + (void)memset(RPDO, 0, sizeof(CO_RPDO_t)); + + /* Configure object variables */ + PDO->em = em; + PDO->CANdev = CANdevRx; + + /* Configure mapping parameters */ + uint32_t erroneousMap = 0; + ret = PDO_initMapping(PDO, OD, OD_16xx_RPDOMapPar, true, errInfo, &erroneousMap); + if (ret != CO_ERROR_NO) { + return ret; + } + + /* Configure communication parameter - COB-ID */ + uint32_t COB_ID = 0; + odRet = OD_get_u32(OD_14xx_RPDOCommPar, 1, &COB_ID, true); + if (odRet != ODR_OK) { + if (errInfo != NULL) { + *errInfo = (((uint32_t)OD_getIndex(OD_14xx_RPDOCommPar)) << 8) | 1U; + } + return CO_ERROR_OD_PARAMETERS; + } + + bool_t valid = (COB_ID & 0x80000000U) == 0U; + uint16_t CAN_ID = (uint16_t)(COB_ID & 0x7FFU); + if (valid && ((PDO->mappedObjectsCount == 0U) || (CAN_ID == 0U))) { + valid = false; + if (erroneousMap == 0U) { + erroneousMap = 1; + } + } + + if (erroneousMap != 0U) { + CO_errorReport(PDO->em, CO_EM_PDO_WRONG_MAPPING, CO_EMC_PROTOCOL_ERROR, + (erroneousMap != 1U) ? erroneousMap : COB_ID); + } + if (!valid) { + CAN_ID = 0; + } + + /* If default CAN-ID is stored in OD (without Node-ID), add Node-ID */ + if ((CAN_ID != 0U) && (CAN_ID == (preDefinedCanId & 0xFF80U))) { + CAN_ID = preDefinedCanId; + } + + ret = CO_CANrxBufferInit(CANdevRx, CANdevRxIdx, CAN_ID, 0x7FF, false, (void*)RPDO, CO_PDO_receive); + if (ret != CO_ERROR_NO) { + return ret; + } + + PDO->valid = valid; + + /* Configure communication parameter - transmission type */ +#if ((CO_CONFIG_PDO) & CO_CONFIG_PDO_SYNC_ENABLE) != 0 + uint8_t transmissionType = (uint8_t)(CO_PDO_TRANSM_TYPE_SYNC_EVENT_LO); + odRet = OD_get_u8(OD_14xx_RPDOCommPar, 2, &transmissionType, true); + if (odRet != ODR_OK) { + if (errInfo != NULL) { + *errInfo = (((uint32_t)OD_getIndex(OD_14xx_RPDOCommPar)) << 8) | 2U; + } + return CO_ERROR_OD_PARAMETERS; + } + + RPDO->SYNC = SYNC; + RPDO->synchronous = transmissionType <= (uint8_t)CO_PDO_TRANSM_TYPE_SYNC_240; +#endif + + /* Configure communication parameter - event-timer (optional) */ +#if ((CO_CONFIG_PDO) & CO_CONFIG_RPDO_TIMERS_ENABLE) != 0 + uint16_t eventTime = 0; + (void)OD_get_u16(OD_14xx_RPDOCommPar, 5, &eventTime, true); + RPDO->timeoutTime_us = (uint32_t)eventTime * 1000U; +#endif + + /* Configure OD extensions */ +#if ((CO_CONFIG_PDO) & CO_CONFIG_FLAG_OD_DYNAMIC) != 0 + PDO->isRPDO = true; + PDO->OD = OD; + PDO->CANdevIdx = CANdevRxIdx; + PDO->preDefinedCanId = preDefinedCanId; + PDO->configuredCanId = CAN_ID; + PDO->OD_communicationParam_ext.object = RPDO; + PDO->OD_communicationParam_ext.read = OD_read_PDO_commParam; + PDO->OD_communicationParam_ext.write = OD_write_14xx; + PDO->OD_mappingParam_extension.object = RPDO; + PDO->OD_mappingParam_extension.read = OD_readOriginal; + PDO->OD_mappingParam_extension.write = OD_write_PDO_mapping; + (void)OD_extension_init(OD_14xx_RPDOCommPar, &PDO->OD_communicationParam_ext); + (void)OD_extension_init(OD_16xx_RPDOMapPar, &PDO->OD_mappingParam_extension); +#endif + + return CO_ERROR_NO; +} + +#if ((CO_CONFIG_PDO) & CO_CONFIG_FLAG_CALLBACK_PRE) != 0 +void +CO_RPDO_initCallbackPre(CO_RPDO_t* RPDO, void* object, void (*pFunctSignalPre)(void* object)) { + if (RPDO != NULL) { + RPDO->functSignalObjectPre = object; + RPDO->pFunctSignalPre = pFunctSignalPre; + } +} +#endif + +void +CO_RPDO_process(CO_RPDO_t* RPDO, +#if ((CO_CONFIG_PDO) & CO_CONFIG_RPDO_TIMERS_ENABLE) != 0 + uint32_t timeDifference_us, uint32_t* timerNext_us, +#endif + bool_t NMTisOperational, bool_t syncWas) { + (void)syncWas; +#if ((CO_CONFIG_PDO) & CO_CONFIG_RPDO_TIMERS_ENABLE) != 0 + (void)timerNext_us; +#endif + + CO_PDO_common_t* PDO = &RPDO->PDO_common; + + if (PDO->valid && NMTisOperational +#if ((CO_CONFIG_PDO) & CO_CONFIG_PDO_SYNC_ENABLE) != 0 + && (syncWas || !RPDO->synchronous) +#endif + ) { + /* Verify errors in length of received RPDO CAN message */ + if (RPDO->receiveError > CO_RPDO_RX_ACK) { + bool_t setError = RPDO->receiveError != CO_RPDO_RX_OK; + uint16_t code = (RPDO->receiveError == CO_RPDO_RX_SHORT) ? CO_EMC_PDO_LENGTH : CO_EMC_PDO_LENGTH_EXC; + CO_error(PDO->em, setError, CO_EM_RPDO_WRONG_LENGTH, code, PDO->dataLength); + RPDO->receiveError = setError ? CO_RPDO_RX_ACK_ERROR : CO_RPDO_RX_ACK_NO_ERROR; + } + + /* Determine, which of the two rx buffers contains relevant message. */ + uint8_t bufNo = 0; +#if ((CO_CONFIG_PDO) & CO_CONFIG_PDO_SYNC_ENABLE) != 0 + if (RPDO->synchronous && (RPDO->SYNC != NULL) && !RPDO->SYNC->CANrxToggle) { + bufNo = 1; + } +#endif + + /* copy RPDO into OD variables according to mappings */ + bool_t rpdoReceived = false; + while (CO_FLAG_READ(RPDO->CANrxNew[bufNo])) { + rpdoReceived = true; + uint8_t* dataRPDO = RPDO->CANrxData[bufNo]; + OD_size_t verifyLength = 0U; + + /* Clear the flag. If between the copy operation CANrxNew is set + * by receive thread, then copy the latest data again. */ + CO_FLAG_CLEAR(RPDO->CANrxNew[bufNo]); + +#if ((CO_CONFIG_PDO) & CO_CONFIG_PDO_OD_IO_ACCESS) != 0 + for (uint8_t i = 0; i < PDO->mappedObjectsCount; i++) { + OD_IO_t* OD_IO = &PDO->OD_IO[i]; + + /* get mappedLength from temporary storage */ + OD_size_t* dataOffset = &OD_IO->stream.dataOffset; + uint8_t mappedLength = (uint8_t)(*dataOffset); + + /* additional safety check. */ + verifyLength += (OD_size_t)mappedLength; + if (verifyLength > CO_PDO_MAX_SIZE) { + break; + } + + /* length of OD variable may be larger than mappedLength */ + OD_size_t ODdataLength = OD_IO->stream.dataLength; + if (ODdataLength > CO_PDO_MAX_SIZE) { + ODdataLength = CO_PDO_MAX_SIZE; + } + /* Prepare data for writing into OD variable. If mappedLength + * is smaller than ODdataLength, then use auxiliary buffer */ + uint8_t buf[CO_PDO_MAX_SIZE]; + uint8_t* dataOD; + if (ODdataLength > mappedLength) { + (void)memset(buf, 0, sizeof(buf)); + (void)memcpy(buf, dataRPDO, mappedLength); + dataOD = buf; + } else { + dataOD = dataRPDO; + } + + /* swap multibyte data if big-endian */ +#ifdef CO_BIG_ENDIAN + if ((OD_IO->stream.attribute & ODA_MB) != 0) { + uint8_t* lo = dataOD; + uint8_t* hi = dataOD + ODdataLength - 1; + while (lo < hi) { + uint8_t swap = *lo; + *lo++ = *hi; + *hi-- = swap; + } + } +#endif + + /* Set stream.dataOffset to zero, perform OD_IO.write() + * and store mappedLength back to stream.dataOffset */ + *dataOffset = 0; + OD_size_t countWritten; + OD_IO->write(&OD_IO->stream, dataOD, ODdataLength, &countWritten); + *dataOffset = mappedLength; + + dataRPDO += mappedLength; + } + +#else + verifyLength = (OD_size_t)PDO->dataLength; + for (uint8_t i = 0; i < PDO->dataLength; i++) { + *PDO->mapPointer[i] = dataRPDO[i]; + } +#endif /* (CO_CONFIG_PDO) & CO_CONFIG_PDO_OD_IO_ACCESS */ + + if ((verifyLength > CO_PDO_MAX_SIZE) || (verifyLength != (OD_size_t)PDO->dataLength)) { + /* bug in software, should not happen */ + CO_errorReport(PDO->em, CO_EM_GENERIC_SOFTWARE_ERROR, CO_EMC_SOFTWARE_INTERNAL, + (0x100000U | verifyLength)); + } + } /* while (CO_FLAG_READ(RPDO->CANrxNew[bufNo])) */ + + /* verify RPDO timeout */ + (void)rpdoReceived; +#if ((CO_CONFIG_PDO) & CO_CONFIG_RPDO_TIMERS_ENABLE) != 0 + if (RPDO->timeoutTime_us > 0U) { + if (rpdoReceived) { + if (RPDO->timeoutTimer > RPDO->timeoutTime_us) { + CO_errorReset(PDO->em, CO_EM_RPDO_TIME_OUT, RPDO->timeoutTimer); + } + /* enable monitoring */ + RPDO->timeoutTimer = 1; + } else if ((RPDO->timeoutTimer > 0U) && (RPDO->timeoutTimer < RPDO->timeoutTime_us)) { + RPDO->timeoutTimer += timeDifference_us; + + if (RPDO->timeoutTimer > RPDO->timeoutTime_us) { + CO_errorReport(PDO->em, CO_EM_RPDO_TIME_OUT, CO_EMC_RPDO_TIMEOUT, RPDO->timeoutTimer); + } + } else { /* MISRA C 2004 14.10 */ + } +#if ((CO_CONFIG_PDO) & CO_CONFIG_FLAG_TIMERNEXT) != 0 + if ((timerNext_us != NULL) && (RPDO->timeoutTimer < RPDO->timeoutTime_us)) { + uint32_t diff = RPDO->timeoutTime_us - RPDO->timeoutTimer; + if (*timerNext_us > diff) { + *timerNext_us = diff; + } + } +#endif + } +#endif /* (CO_CONFIG_PDO) & CO_CONFIG_RPDO_TIMERS_ENABLE */ + } /* if (PDO->valid && NMTisOperational) */ + else { + /* not valid and operational, clear CAN receive flags and timeoutTimer */ +#if ((CO_CONFIG_PDO) & CO_CONFIG_PDO_SYNC_ENABLE) != 0 + if (!PDO->valid || !NMTisOperational) { + CO_FLAG_CLEAR(RPDO->CANrxNew[0]); + CO_FLAG_CLEAR(RPDO->CANrxNew[1]); +#if ((CO_CONFIG_PDO) & CO_CONFIG_RPDO_TIMERS_ENABLE) != 0 + RPDO->timeoutTimer = 0; +#endif + } +#else + CO_FLAG_CLEAR(RPDO->CANrxNew[0]); +#if ((CO_CONFIG_PDO) & CO_CONFIG_RPDO_TIMERS_ENABLE) != 0 + RPDO->timeoutTimer = 0; +#endif +#endif + } +} +#endif /* (CO_CONFIG_PDO) & CO_CONFIG_RPDO_ENABLE */ + +/******************************************************************************* + * T P D O + ******************************************************************************/ +#if ((CO_CONFIG_PDO) & CO_CONFIG_TPDO_ENABLE) != 0 +#if ((CO_CONFIG_PDO) & CO_CONFIG_FLAG_OD_DYNAMIC) != 0 +/* + * Custom function for writing OD object "TPDO communication parameter" + * + * For more information see file CO_ODinterface.h, OD_IO_t. + */ +static ODR_t +OD_write_18xx(OD_stream_t* stream, const void* buf, OD_size_t count, OD_size_t* countWritten) { + /* "count" is also verified in *_init() function */ + if ((stream == NULL) || (buf == NULL) || (countWritten == NULL) || (count > 4U)) { + return ODR_DEV_INCOMPAT; + } + + CO_TPDO_t* TPDO = stream->object; + CO_PDO_common_t* PDO = &TPDO->PDO_common; + uint8_t bufCopy[4]; + (void)memcpy((void*)bufCopy, (const void*)buf, count); + + switch (stream->subIndex) { + case 1: { /* COB-ID used by PDO */ + uint32_t COB_ID = CO_getUint32(buf); + uint16_t CAN_ID = (uint16_t)(COB_ID & 0x7FFU); + bool_t valid = (COB_ID & 0x80000000U) == 0U; + + /* bits 11...29 must be zero, PDO must be disabled on change, CAN_ID == 0 is + * not allowed, mapping must be configured before enabling the PDO */ + if (((COB_ID & 0x3FFFF800U) != 0U) || (valid && (PDO->valid && (CAN_ID != PDO->configuredCanId))) + || (valid && CO_IS_RESTRICTED_CAN_ID(CAN_ID)) || (valid && (PDO->mappedObjectsCount == 0U))) { + return ODR_INVALID_VALUE; + } + + /* parameter changed? */ + if ((valid != PDO->valid) || (CAN_ID != PDO->configuredCanId)) { + /* if default CAN-ID is written, store to OD without Node-ID */ + if (CAN_ID == PDO->preDefinedCanId) { + (void)CO_setUint32(bufCopy, COB_ID & 0xFFFFFF80U); + } + if (!valid) { + CAN_ID = 0; + } + + CO_CANtx_t* CANtxBuff = CO_CANtxBufferInit( + PDO->CANdev, PDO->CANdevIdx, CAN_ID, false, PDO->dataLength, + TPDO->transmissionType <= (uint8_t)CO_PDO_TRANSM_TYPE_SYNC_240); + + if (CANtxBuff == NULL) { + return ODR_DEV_INCOMPAT; + } + + TPDO->CANtxBuff = CANtxBuff; + PDO->valid = valid; + PDO->configuredCanId = CAN_ID; + } + break; + } + + case 2: { /* transmission type */ uint8_t transmissionType = CO_getUint8(buf); +#if ((CO_CONFIG_PDO) & CO_CONFIG_PDO_SYNC_ENABLE) != 0 + if ((transmissionType > (uint8_t)CO_PDO_TRANSM_TYPE_SYNC_240) + && (transmissionType < (uint8_t)CO_PDO_TRANSM_TYPE_SYNC_EVENT_LO)) { + return ODR_INVALID_VALUE; + } + TPDO->CANtxBuff->syncFlag = transmissionType <= (uint8_t)CO_PDO_TRANSM_TYPE_SYNC_240; + TPDO->syncCounter = 255; +#else + if (transmissionType < CO_PDO_TRANSM_TYPE_SYNC_EVENT_LO) { + return ODR_INVALID_VALUE; + } +#endif + TPDO->transmissionType = transmissionType; + TPDO->sendRequest = true; +#if ((CO_CONFIG_PDO) & CO_CONFIG_TPDO_TIMERS_ENABLE) != 0 + TPDO->inhibitTimer = 0; + TPDO->eventTimer = 0; +#endif + break; + } + +#if ((CO_CONFIG_PDO) & CO_CONFIG_TPDO_TIMERS_ENABLE) != 0 + case 3: { /* inhibit time */ + if (PDO->valid) { + return ODR_INVALID_VALUE; + } + uint32_t inhibitTime = CO_getUint16(buf); + TPDO->inhibitTime_us = inhibitTime * 100U; + TPDO->inhibitTimer = 0; + break; + } + + case 5: { /* event-timer */ + uint32_t eventTime = CO_getUint16(buf); + TPDO->eventTime_us = eventTime * 1000U; + TPDO->eventTimer = 0; + break; + } +#endif + +#if ((CO_CONFIG_PDO) & CO_CONFIG_PDO_SYNC_ENABLE) != 0 + case 6: { /* SYNC start value */ + uint8_t syncStartValue = CO_getUint8(buf); + + if (PDO->valid || (syncStartValue > 240U)) { + return ODR_INVALID_VALUE; + } + TPDO->syncStartValue = syncStartValue; + break; + } +#endif + default: + /* MISRA C 2004 15.3 */ + break; + } + + /* write value to the original location in the Object Dictionary */ + return OD_writeOriginal(stream, bufCopy, count, countWritten); +} +#endif /* (CO_CONFIG_PDO) & CO_CONFIG_FLAG_OD_DYNAMIC */ + +CO_ReturnError_t +CO_TPDO_init(CO_TPDO_t* TPDO, OD_t* OD, CO_EM_t* em, +#if ((CO_CONFIG_PDO) & CO_CONFIG_PDO_SYNC_ENABLE) != 0 + CO_SYNC_t* SYNC, +#endif + uint16_t preDefinedCanId, OD_entry_t* OD_18xx_TPDOCommPar, OD_entry_t* OD_1Axx_TPDOMapPar, + CO_CANmodule_t* CANdevTx, uint16_t CANdevTxIdx, uint32_t* errInfo) { + CO_PDO_common_t* PDO = &TPDO->PDO_common; + ODR_t odRet; + + /* verify arguments */ + if ((TPDO == NULL) || (OD == NULL) || (em == NULL) || (OD_18xx_TPDOCommPar == NULL) || (OD_1Axx_TPDOMapPar == NULL) + || (CANdevTx == NULL)) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + /* clear object */ + (void)memset(TPDO, 0, sizeof(CO_TPDO_t)); + + /* Configure object variables */ + PDO->em = em; + PDO->CANdev = CANdevTx; + + /* Configure mapping parameters */ + uint32_t erroneousMap = 0; + CO_ReturnError_t ret = PDO_initMapping(PDO, OD, OD_1Axx_TPDOMapPar, false, errInfo, &erroneousMap); + if (ret != CO_ERROR_NO) { + return ret; + } + + /* Configure communication parameter - transmission type */ + uint8_t transmissionType = (uint8_t)(CO_PDO_TRANSM_TYPE_SYNC_EVENT_LO); + odRet = OD_get_u8(OD_18xx_TPDOCommPar, 2, &transmissionType, true); + if (odRet != ODR_OK) { + if (errInfo != NULL) { + *errInfo = (((uint32_t)OD_getIndex(OD_18xx_TPDOCommPar)) << 8) | 2U; + } + return CO_ERROR_OD_PARAMETERS; + } + if ((transmissionType < (uint8_t)CO_PDO_TRANSM_TYPE_SYNC_EVENT_LO) +#if ((CO_CONFIG_PDO) & CO_CONFIG_PDO_SYNC_ENABLE) != 0 + && (transmissionType > (uint8_t)CO_PDO_TRANSM_TYPE_SYNC_240) +#endif + ) { + transmissionType = (uint8_t)(CO_PDO_TRANSM_TYPE_SYNC_EVENT_LO); + } + TPDO->transmissionType = transmissionType; + TPDO->sendRequest = true; + + /* Configure communication parameter - COB-ID */ + uint32_t COB_ID = 0; + odRet = OD_get_u32(OD_18xx_TPDOCommPar, 1, &COB_ID, true); + if (odRet != ODR_OK) { + if (errInfo != NULL) { + *errInfo = (((uint32_t)OD_getIndex(OD_18xx_TPDOCommPar)) << 8) | 1U; + } + return CO_ERROR_OD_PARAMETERS; + } + + bool_t valid = (COB_ID & 0x80000000U) == 0U; + uint16_t CAN_ID = (uint16_t)(COB_ID & 0x7FFU); + if (valid && ((PDO->mappedObjectsCount == 0U) || (CAN_ID == 0U))) { + valid = false; + if (erroneousMap == 0U) { + erroneousMap = 1; + } + } + + if (erroneousMap != 0U) { + CO_errorReport(PDO->em, CO_EM_PDO_WRONG_MAPPING, CO_EMC_PROTOCOL_ERROR, + (erroneousMap != 1U) ? erroneousMap : COB_ID); + } + if (!valid) { + CAN_ID = 0; + } + + /* If default CAN-ID is stored in OD (without Node-ID), add Node-ID */ + if ((CAN_ID != 0U) && (CAN_ID == (preDefinedCanId & 0xFF80U))) { + CAN_ID = preDefinedCanId; + } + + TPDO->CANtxBuff = CO_CANtxBufferInit(CANdevTx, CANdevTxIdx, CAN_ID, false, PDO->dataLength, + TPDO->transmissionType <= (uint8_t)CO_PDO_TRANSM_TYPE_SYNC_240); + + if (TPDO->CANtxBuff == NULL) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + PDO->valid = valid; + + /* Configure communication parameter - inhibit time and event-timer (opt) */ +#if ((CO_CONFIG_PDO) & CO_CONFIG_TPDO_TIMERS_ENABLE) != 0 + uint16_t inhibitTime = 0; + uint16_t eventTime = 0; + (void)OD_get_u16(OD_18xx_TPDOCommPar, 3, &inhibitTime, true); + (void)OD_get_u16(OD_18xx_TPDOCommPar, 5, &eventTime, true); + TPDO->inhibitTime_us = (uint32_t)inhibitTime * 100U; + TPDO->eventTime_us = (uint32_t)eventTime * 1000U; +#endif + + /* Configure communication parameter - SYNC start value (optional) */ +#if ((CO_CONFIG_PDO) & CO_CONFIG_PDO_SYNC_ENABLE) != 0 + TPDO->syncStartValue = 0; + (void)OD_get_u8(OD_18xx_TPDOCommPar, 6, &TPDO->syncStartValue, true); + TPDO->SYNC = SYNC; + TPDO->syncCounter = 255; +#endif + + /* Configure OD extensions */ +#if ((CO_CONFIG_PDO) & CO_CONFIG_FLAG_OD_DYNAMIC) != 0 + PDO->isRPDO = false; + PDO->OD = OD; + PDO->CANdevIdx = CANdevTxIdx; + PDO->preDefinedCanId = preDefinedCanId; + PDO->configuredCanId = CAN_ID; + PDO->OD_communicationParam_ext.object = TPDO; + PDO->OD_communicationParam_ext.read = OD_read_PDO_commParam; + PDO->OD_communicationParam_ext.write = OD_write_18xx; + PDO->OD_mappingParam_extension.object = TPDO; + PDO->OD_mappingParam_extension.read = OD_readOriginal; + PDO->OD_mappingParam_extension.write = OD_write_PDO_mapping; + (void)OD_extension_init(OD_18xx_TPDOCommPar, &PDO->OD_communicationParam_ext); + (void)OD_extension_init(OD_1Axx_TPDOMapPar, &PDO->OD_mappingParam_extension); +#endif + + return CO_ERROR_NO; +} + +/* + * Send TPDO message. + * + * Function prepares TPDO data from Object Dictionary variables. It is called + * from CO_TPDO_process() according to TPDO communication parameters. + * + * @param TPDO TPDO object. + * + * @return Same as CO_CANsend(). + */ +static CO_ReturnError_t +CO_TPDOsend(CO_TPDO_t* TPDO) { + CO_PDO_common_t* PDO = &TPDO->PDO_common; + uint8_t* dataTPDO = &TPDO->CANtxBuff->data[0]; + OD_size_t verifyLength = 0U; + +#if OD_FLAGS_PDO_SIZE > 0 + bool_t eventDriven = ((TPDO->transmissionType == (uint8_t)CO_PDO_TRANSM_TYPE_SYNC_ACYCLIC) + || (TPDO->transmissionType >= (uint8_t)CO_PDO_TRANSM_TYPE_SYNC_EVENT_LO)); +#endif + +#if ((CO_CONFIG_PDO) & CO_CONFIG_PDO_OD_IO_ACCESS) != 0 + for (uint8_t i = 0; i < PDO->mappedObjectsCount; i++) { + OD_IO_t* OD_IO = &PDO->OD_IO[i]; + OD_stream_t* stream = &OD_IO->stream; + + /* get mappedLength from temporary storage */ + uint8_t mappedLength = (uint8_t)stream->dataOffset; + + /* additional safety check */ + verifyLength += (OD_size_t)mappedLength; + if (verifyLength > CO_PDO_MAX_SIZE) { + break; + } + + /* length of OD variable may be larger than mappedLength */ + OD_size_t ODdataLength = stream->dataLength; + if (ODdataLength > CO_PDO_MAX_SIZE) { + ODdataLength = CO_PDO_MAX_SIZE; + } + /* If mappedLength is smaller than ODdataLength, use auxiliary buffer */ + uint8_t buf[CO_PDO_MAX_SIZE]; + uint8_t* dataTPDOCopy; + if (ODdataLength > mappedLength) { + (void)memset(buf, 0, sizeof(buf)); + dataTPDOCopy = buf; + } else { + dataTPDOCopy = dataTPDO; + } + + /* Set stream.dataOffset to zero, perform OD_IO.read() and store mappedLength back to stream.dataOffset */ + stream->dataOffset = 0; + OD_size_t countRd; + OD_IO->read(stream, dataTPDOCopy, ODdataLength, &countRd); + stream->dataOffset = mappedLength; + + /* swap multibyte data if big-endian */ +#ifdef CO_BIG_ENDIAN + if ((stream->attribute & ODA_MB) != 0) { + uint8_t* lo = dataTPDOCopy; + uint8_t* hi = dataTPDOCopy + ODdataLength - 1; + while (lo < hi) { + uint8_t swap = *lo; + *lo++ = *hi; + *hi-- = swap; + } + } +#endif + + /* If auxiliary buffer, copy it to the TPDO */ + if (ODdataLength > mappedLength) { + (void)memcpy(dataTPDO, buf, mappedLength); + } + + /* In event driven TPDO indicate transmission of OD variable */ +#if OD_FLAGS_PDO_SIZE > 0 + uint8_t* flagPDObyte = PDO->flagPDObyte[i]; + if ((flagPDObyte != NULL) && eventDriven) { + *flagPDObyte |= PDO->flagPDObitmask[i]; + } +#endif + + dataTPDO += mappedLength; + } +#else + verifyLength = (OD_size_t)PDO->dataLength; + for (uint8_t i = 0; i < PDO->dataLength; i++) { + dataTPDO[i] = *PDO->mapPointer[i]; + + /* In event driven TPDO indicate transmission of OD variable */ +#if OD_FLAGS_PDO_SIZE > 0 + uint8_t* flagPDObyte = PDO->flagPDObyte[i]; + if (flagPDObyte != NULL && eventDriven) { + *flagPDObyte |= PDO->flagPDObitmask[i]; + } +#endif + } +#endif /* (CO_CONFIG_PDO) & CO_CONFIG_PDO_OD_IO_ACCESS */ + + if ((verifyLength > CO_PDO_MAX_SIZE) || (verifyLength != (OD_size_t)PDO->dataLength)) { + /* bug in software, should not happen */ + CO_errorReport(PDO->em, CO_EM_GENERIC_SOFTWARE_ERROR, CO_EMC_SOFTWARE_INTERNAL, (0x200000U | verifyLength)); + return CO_ERROR_DATA_CORRUPT; + } + + TPDO->sendRequest = false; +#if ((CO_CONFIG_PDO) & CO_CONFIG_TPDO_TIMERS_ENABLE) != 0 + TPDO->eventTimer = TPDO->eventTime_us; + TPDO->inhibitTimer = TPDO->inhibitTime_us; +#endif + return CO_CANsend(PDO->CANdev, TPDO->CANtxBuff); +} + +void +CO_TPDO_process(CO_TPDO_t* TPDO, +#if (((CO_CONFIG_PDO) & CO_CONFIG_TPDO_TIMERS_ENABLE) != 0) || defined CO_DOXYGEN + uint32_t timeDifference_us, uint32_t* timerNext_us, +#endif + bool_t NMTisOperational, bool_t syncWas) { + CO_PDO_common_t* PDO = &TPDO->PDO_common; +#if (((CO_CONFIG_PDO) & CO_CONFIG_TPDO_TIMERS_ENABLE)) != 0 + (void)timerNext_us; +#endif + (void)syncWas; + + if (PDO->valid && NMTisOperational) { + + /* check for event timer or application event */ +#if (((CO_CONFIG_PDO) & CO_CONFIG_TPDO_TIMERS_ENABLE) != 0) || (OD_FLAGS_PDO_SIZE > 0) + if ((TPDO->transmissionType == (uint8_t)CO_PDO_TRANSM_TYPE_SYNC_ACYCLIC) + || (TPDO->transmissionType >= (uint8_t)CO_PDO_TRANSM_TYPE_SYNC_EVENT_LO)) { + /* event timer */ +#if ((CO_CONFIG_PDO) & CO_CONFIG_TPDO_TIMERS_ENABLE) != 0 + if (TPDO->eventTime_us != 0U) { + TPDO->eventTimer = (TPDO->eventTimer > timeDifference_us) ? (TPDO->eventTimer - timeDifference_us) : 0U; + if (TPDO->eventTimer == 0U) { + TPDO->sendRequest = true; + } +#if ((CO_CONFIG_PDO) & CO_CONFIG_FLAG_TIMERNEXT) != 0 + if ((timerNext_us != NULL) && (*timerNext_us > TPDO->eventTimer)) { + /* Schedule for next event time */ + *timerNext_us = TPDO->eventTimer; + } +#endif + } +#endif + /* check for any OD_requestTPDO() */ +#if OD_FLAGS_PDO_SIZE > 0 + if (!TPDO->sendRequest) { + for (uint8_t i = 0; i < PDO->mappedObjectsCount; i++) { + uint8_t* flagPDObyte = PDO->flagPDObyte[i]; + if (flagPDObyte != NULL) { + if ((*flagPDObyte & PDO->flagPDObitmask[i]) == 0U) { + TPDO->sendRequest = true; + break; + } + } + } + } +#endif + } +#endif /* ((CO_CONFIG_PDO)&CO_CONFIG_TPDO_TIMERS_ENABLE)||(OD_FLAGS_PDO_SIZE>0) */ + + /* Send PDO by application request or by Event timer */ + if (TPDO->transmissionType >= (uint8_t)CO_PDO_TRANSM_TYPE_SYNC_EVENT_LO) { +#if ((CO_CONFIG_PDO) & CO_CONFIG_TPDO_TIMERS_ENABLE) != 0 + TPDO->inhibitTimer = (TPDO->inhibitTimer > timeDifference_us) ? (TPDO->inhibitTimer - timeDifference_us) + : 0U; + + /* send TPDO */ + if (TPDO->sendRequest && (TPDO->inhibitTimer == 0U)) { + (void)CO_TPDOsend(TPDO); + } + +#if ((CO_CONFIG_PDO) & CO_CONFIG_FLAG_TIMERNEXT) != 0 + if (TPDO->sendRequest && (timerNext_us != NULL) && (*timerNext_us > TPDO->inhibitTimer)) { + /* Schedule for just beyond inhibit window */ + *timerNext_us = TPDO->inhibitTimer; + } +#endif +#else + if (TPDO->sendRequest) { + (void)CO_TPDOsend(TPDO); + } +#endif + } /* if (TPDO->transmissionType >= CO_PDO_TRANSM_TYPE_SYNC_EVENT_LO) */ + + /* Synchronous PDOs */ +#if ((CO_CONFIG_PDO) & CO_CONFIG_PDO_SYNC_ENABLE) != 0 + else if ((TPDO->SYNC != NULL) && syncWas) { + /* send synchronous acyclic TPDO */ + if (TPDO->transmissionType == (uint8_t)CO_PDO_TRANSM_TYPE_SYNC_ACYCLIC) { + if (TPDO->sendRequest) { + (void)CO_TPDOsend(TPDO); + } + } + /* send synchronous cyclic TPDO */ + else { + /* is the start of synchronous TPDO transmission */ + if (TPDO->syncCounter == 255U) { + if ((TPDO->SYNC->counterOverflowValue != 0U) && (TPDO->syncStartValue != 0U)) { + /* syncStartValue is in use */ + TPDO->syncCounter = 254; + } else { + /* Send first TPDO somewhere in the middle */ + TPDO->syncCounter = (TPDO->transmissionType / 2U) + 1U; + } + } + /* If the syncStartValue is in use, start first TPDO after SYNC with matched syncStartValue. */ + if (TPDO->syncCounter == 254U) { + if (TPDO->SYNC->counter == TPDO->syncStartValue) { + TPDO->syncCounter = TPDO->transmissionType; + (void)CO_TPDOsend(TPDO); + } + } + /* Send TPDO after every N-th Sync */ + else if (--TPDO->syncCounter == 0U) { + TPDO->syncCounter = TPDO->transmissionType; + (void)CO_TPDOsend(TPDO); + } else { /* MISRA C 2004 14.10 */ + } + } + } /* else if (TPDO->SYNC && syncWas) */ + else { /* MISRA C 2004 14.10 */ + } +#endif + + } else { + /* Not operational or valid, reset triggers */ + TPDO->sendRequest = true; +#if ((CO_CONFIG_PDO) & CO_CONFIG_TPDO_TIMERS_ENABLE) != 0 + TPDO->inhibitTimer = 0; + TPDO->eventTimer = 0; +#endif +#if ((CO_CONFIG_PDO) & CO_CONFIG_PDO_SYNC_ENABLE) != 0 + TPDO->syncCounter = 255; +#endif + } +} +#endif /* (CO_CONFIG_PDO) & CO_CONFIG_TPDO_ENABLE */ +#endif /* (CO_CONFIG_PDO) & (CO_CONFIG_RPDO_ENABLE | CO_CONFIG_TPDO_ENABLE) */ diff --git a/301/CO_PDO.h b/301/CO_PDO.h new file mode 100644 index 00000000..3ce6390e --- /dev/null +++ b/301/CO_PDO.h @@ -0,0 +1,382 @@ +/** + * CANopen Process Data Object protocol. + * + * @file CO_PDO.h + * @ingroup CO_PDO + * @author Janez Paternoster + * @copyright 2021 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#ifndef CO_PDO_H +#define CO_PDO_H + +#include "301/CO_ODinterface.h" +#include "301/CO_Emergency.h" +#include "301/CO_SYNC.h" + +/* default configuration, see CO_config.h */ +#ifndef CO_CONFIG_PDO +#define CO_CONFIG_PDO \ + (CO_CONFIG_RPDO_ENABLE | CO_CONFIG_TPDO_ENABLE | CO_CONFIG_RPDO_TIMERS_ENABLE | CO_CONFIG_TPDO_TIMERS_ENABLE \ + | CO_CONFIG_PDO_SYNC_ENABLE | CO_CONFIG_PDO_OD_IO_ACCESS | CO_CONFIG_GLOBAL_RT_FLAG_CALLBACK_PRE \ + | CO_CONFIG_GLOBAL_FLAG_TIMERNEXT | CO_CONFIG_GLOBAL_FLAG_OD_DYNAMIC) +#endif + +#if (((CO_CONFIG_PDO) & (CO_CONFIG_RPDO_ENABLE | CO_CONFIG_TPDO_ENABLE)) != 0) || defined CO_DOXYGEN + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup CO_PDO PDO + * CANopen Process Data Object protocol. + * + * @ingroup CO_CANopen_301 + * @{ + * Process data objects are used for real-time data transfer with no protocol overhead. + * + * TPDO with specific identifier is transmitted by one device and recieved by zero or more devices as RPDO. PDO + * communication parameters(COB-ID, transmission type, etc.) are in the Object Dictionary at index 0x1400+ and 0x1800+. + * PDO mapping parameters (size and contents of the PDO) are in the Object Dictionary at index 0x1600+ and 0x1A00+. + * + * Features of the PDO as implemented in CANopenNode: + * - Dynamic PDO mapping. + * - Map granularity of one byte. + * - Data from OD variables are accessed via @ref OD_IO_t read()/write() functions, which gives a great usefulness to + * the application. + * - For systems with very low memory and processing capabilities there is a simplified @ref CO_CONFIG_PDO option, + * where instead of read()/write() access, PDO data are copied directly to/from memory locations of OD variables. + * - After RPDO is received from CAN bus, its data are copied to internal buffer (inside fast CAN receive interrupt). + * Function CO_RPDO_process() (called by application) copies data to the mapped objects in the Object Dictionary. + * Synchronous RPDOs are processed AFTER reception of the next SYNC message. + * - Function CO_TPDO_process() (called by application) sends TPDO when necessary. There are different transmission + * types possible, controlled by: SYNC message, event timer, @ref CO_TPDOsendRequest() by application or @ref + * OD_requestTPDO(), where application can request TPDO for OD variable mapped to any of them. In later case + * application may, for example, monitor change of state of the OD variable and indicate TPDO request on it. + * + * @anchor CO_PDO_CAN_ID + * ### CAN identifiers for PDO + * + * Each PDO can be configured with any valid 11-bit CAN identifier. Lower numbers have higher priorities on CAN bus. As + * a general rule, each CAN message is identified with own CAN-ID, which must be unique and produced by single source. + * The same is with PDO objects: Any TPDO produced on the CANopen network must have unique CAN-ID and there can be zero + * to many RPDOs (from different devices) configured to match the CAN-ID of the TPDO of interest. + * + * CANopen standard provides pre-defined connection sets for four RPDOs and four TPDOs on each device with specific + * 7-bit Node-ID. These are default values and are usable in configuration, where CANopen network contains a master + * device, which directly communicates with many slaves. In de-centralized systems, where devices operate without a + * master, it makes sense to configure CAN-IDs of the RPDOs to the non-default values. + * + * Default CAN identifiers for first four TPDOs on device with specific CANopen Node-Id are: 0x180+NodeId, 0x280+NodeId, + * 0x380+NodeId and 0x480+NodeId. + * + * Default CAN identifiers for first four RPDOs on device with specific CANopen Node-Id are: 0x200+NodeId, 0x300+NodeId, + * 0x400+NodeId and 0x500+NodeId. + * + * CANopenNode handles default (pre-defined) CAN-IDs. If it is detected, that PDO is configured with default CAN-ID + * (when writing to OD variable PDO communication parameter, COB-ID), then COB-ID is stored without Node-Id to the + * Object Dictionary. If Node-ID is changed, then COB-ID will always contain correct default CAN-ID (default CAN-ID + + * Node-ID). If PDO is configured with non-default CAN-ID, then it will be stored to the Object Dictionary as is. + * + * If configuration CO_CONFIG_FLAG_OD_DYNAMIC is enabled in @ref CO_CONFIG_PDO, then PDOs can be configured dynamically, + * also in NMT operational state. Otherwise PDOs are configured only in reset communication section and also default + * CAN-IDs are always stored to OD as is, no default node-id is handled. + * + * Configure PDO by writing to the OD variables in the following procedure: + * - Disable the PDO by setting bit-31 to 1 in PDO communication parameter, COB-ID + * - Node-Id can be configured only when PDO is disabled. + * - Disable mapping by setting PDO mapping parameter, sub index 0 to 0 + * - Configure mapping + * - Enable mapping by setting PDO mapping param, sub 0 to number of mapped objects + * - Enable the PDO by setting bit-31 to 0 in PDO communication parameter, COB-ID + */ + +/** Maximum size of PDO message, 8 for standard CAN */ +#ifndef CO_PDO_MAX_SIZE +#define CO_PDO_MAX_SIZE 8U +#endif + +/** Maximum number of entries, which can be mapped to PDO, 8 for standard CAN, may be less to preserve RAM usage */ +#ifndef CO_PDO_MAX_MAPPED_ENTRIES +#define CO_PDO_MAX_MAPPED_ENTRIES 8U +#endif + +/** Number of CANopen RPDO objects, which uses default CAN indentifiers. By default first four RPDOs have pre-defined + * CAN identifiers, which depends on node-id. This constant may be set to 0 to disable functionality or set to any other + * value. For example, if there are several logical devices inside single CANopen device, then more than four RPDOs may + * have pre-defined CAN identifiers. In that case RPDO5 has CAN_ID=0x200+NodeId+1, RPDO6 has CAN_ID=0x300+NodeId+1, + * RPDO9 has CAN_ID=0x200+NodeId+2 and so on. */ +#ifndef CO_RPDO_DEFAULT_CANID_COUNT +#define CO_RPDO_DEFAULT_CANID_COUNT 4U +#endif + +/** Number of CANopen TPDO objects, which uses default CAN indentifiers. If value is more than four, then pre-defined + * pre-defined CAN identifiers are: TPDO5 has CAN_ID=0x180+NodeId+1, TPDO6 has CAN_ID=0x280+NodeId+1, TPDO9 has + * CAN_ID=0x180+NodeId+2 and so on. For description see @ref CO_RPDO_DEFAULT_CANID_COUNT. */ +#ifndef CO_TPDO_DEFAULT_CANID_COUNT +#define CO_TPDO_DEFAULT_CANID_COUNT 4U +#endif + +#ifndef CO_PDO_OWN_TYPES +/** Variable of type CO_PDO_size_t contains data length in bytes of PDO */ +typedef uint8_t CO_PDO_size_t; +#endif + +/** + * PDO transmission Types + */ +typedef enum { + CO_PDO_TRANSM_TYPE_SYNC_ACYCLIC = 0U, /**< synchronous (acyclic) */ + CO_PDO_TRANSM_TYPE_SYNC_1 = 1U, /**< synchronous (cyclic every sync) */ + CO_PDO_TRANSM_TYPE_SYNC_240 = 0xF0U, /**< synchronous (cyclic every 240-th sync) */ + CO_PDO_TRANSM_TYPE_SYNC_EVENT_LO = 0xFEU, /**< event-driven, lower value (manufacturer specific), */ + CO_PDO_TRANSM_TYPE_SYNC_EVENT_HI = 0xFFU /**< event-driven, higher value (device profile and application profile + specific) */ +} CO_PDO_transmissionTypes_t; + +/** + * PDO object, common properties + */ +typedef struct { + CO_EM_t* em; /**< From CO_xPDO_init() */ + CO_CANmodule_t* CANdev; /**< From CO_xPDO_init() */ + bool_t valid; /**< True, if PDO is enabled and valid */ + CO_PDO_size_t dataLength; /**< Data length of the received PDO message. Calculated from mapping */ + uint8_t mappedObjectsCount; /**< Number of mapped objects in PDO */ +#if (((CO_CONFIG_PDO)&CO_CONFIG_PDO_OD_IO_ACCESS) != 0) || defined CO_DOXYGEN + OD_IO_t OD_IO[CO_PDO_MAX_MAPPED_ENTRIES]; /**< Object dictionary interface for all mapped entries. OD_IO.dataOffset + has special usage with PDO. It stores information about mappedLength + of the variable. mappedLength can be less or equal to the + OD_IO.dataLength. mappedLength greater than OD_IO.dataLength indicates + erroneous mapping. OD_IO.dataOffset is set to 0 before read/write + function call and after the call OD_IO.dataOffset is set back to + mappedLength. */ +#if OD_FLAGS_PDO_SIZE > 0 + uint8_t* flagPDObyte[CO_PDO_MAX_MAPPED_ENTRIES]; /**< Pointer to byte, which contains PDO flag bit from @ref + OD_extension_t */ + uint8_t flagPDObitmask[CO_PDO_MAX_MAPPED_ENTRIES]; /**< Bitmask for the flagPDObyte */ +#endif +#else + /* Pointers to data objects inside OD, where PDO will be copied */ + uint8_t* mapPointer[CO_PDO_MAX_SIZE]; +#if OD_FLAGS_PDO_SIZE > 0 + uint8_t* flagPDObyte[CO_PDO_MAX_SIZE]; + uint8_t flagPDObitmask[CO_PDO_MAX_SIZE]; +#endif +#endif +#if (((CO_CONFIG_PDO)&CO_CONFIG_FLAG_OD_DYNAMIC) != 0) || defined CO_DOXYGEN + bool_t isRPDO; /**< True for RPDO, false for TPDO */ + OD_t* OD; /**< From CO_xPDO_init() */ + uint16_t CANdevIdx; /**< From CO_xPDO_init() */ + uint16_t preDefinedCanId; /**< From CO_xPDO_init() */ + uint16_t configuredCanId; /**< Currently configured CAN identifier */ + OD_extension_t OD_communicationParam_ext; /**< Extension for OD object */ + OD_extension_t OD_mappingParam_extension; /**< Extension for OD object */ +#endif +} CO_PDO_common_t; + +/******************************************************************************* + * R P D O + ******************************************************************************/ +#if (((CO_CONFIG_PDO)&CO_CONFIG_RPDO_ENABLE) != 0) || defined CO_DOXYGEN +/** + * Number of buffers for received CAN message for RPDO + */ +#if (((CO_CONFIG_PDO)&CO_CONFIG_PDO_SYNC_ENABLE) != 0) || defined CO_DOXYGEN +#define CO_RPDO_CAN_BUFFERS_COUNT 2 +#else +#define CO_RPDO_CAN_BUFFERS_COUNT 1 +#endif + +/** + * RPDO object. + */ +typedef struct { + CO_PDO_common_t PDO_common; /**< PDO common properties, must be first element in this object */ + volatile void* CANrxNew[CO_RPDO_CAN_BUFFERS_COUNT]; /**< Variable indicates, if new PDO message received from CAN */ + uint8_t CANrxData[CO_RPDO_CAN_BUFFERS_COUNT][CO_PDO_MAX_SIZE]; /**< CO_PDO_MAX_SIZE data bytes of the received + message. */ + uint8_t receiveError; /**< Indication of RPDO length errors, use with CO_PDO_receiveErrors_t */ +#if (((CO_CONFIG_PDO)&CO_CONFIG_PDO_SYNC_ENABLE) != 0) || defined CO_DOXYGEN + CO_SYNC_t* SYNC; /**< From CO_RPDO_init() */ + bool_t synchronous; /**< True if transmissionType <= 240 */ +#endif +#if (((CO_CONFIG_PDO)&CO_CONFIG_RPDO_TIMERS_ENABLE) != 0) || defined CO_DOXYGEN + uint32_t timeoutTime_us; /**< Maximum timeout time between received PDOs in microseconds. Configurable by OD + variable RPDO communication parameter, event-timer. */ + uint32_t timeoutTimer; /**< Timeout timer variable in microseconds */ +#endif +#if (((CO_CONFIG_PDO)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0) || defined CO_DOXYGEN + void (*pFunctSignalPre)(void* object); /**< From CO_RPDO_initCallbackPre() or NULL */ + void* functSignalObjectPre; /**< From CO_RPDO_initCallbackPre() or NULL */ +#endif +} CO_RPDO_t; + +/** + * Initialize RPDO object. + * + * Function must be called in the end of the communication reset section, after all application initialization. + * Otherwise mapping to application OD variables will not be correct. + * + * @param RPDO This object will be initialized. + * @param OD Object Dictionary. + * @param em Emergency object. + * @param SYNC SYNC object, may be NULL. + * @param preDefinedCanId CAN identifier from pre-defined connection set, including node-id for first four PDOs, or 0 + * otherwise, see @ref CO_PDO_CAN_ID + * @param OD_14xx_RPDOCommPar OD entry for 0x1400+ - "RPDO communication parameter", entry is required. + * @param OD_16xx_RPDOMapPar OD entry for 0x1600+ - "RPDO mapping parameter", entry is required. + * @param CANdevRx CAN device for PDO reception. + * @param CANdevRxIdx Index of receive buffer in the above CAN device. + * @param [out] errInfo Additional information in case of error, may be NULL. + * + * @return #CO_ReturnError_t CO_ERROR_NO on success. + */ +CO_ReturnError_t CO_RPDO_init(CO_RPDO_t* RPDO, OD_t* OD, CO_EM_t* em, +#if (((CO_CONFIG_PDO)&CO_CONFIG_PDO_SYNC_ENABLE) != 0) || defined CO_DOXYGEN + CO_SYNC_t* SYNC, +#endif + uint16_t preDefinedCanId, OD_entry_t* OD_14xx_RPDOCommPar, OD_entry_t* OD_16xx_RPDOMapPar, + CO_CANmodule_t* CANdevRx, uint16_t CANdevRxIdx, uint32_t* errInfo); + +#if (((CO_CONFIG_PDO)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0) || defined CO_DOXYGEN +/** + * Initialize RPDO callback function. + * + * Function initializes optional callback function, which should immediately start processing of CO_RPDO_process() + * function. Callback is called after RPDO message is received from the CAN bus. + * + * @param RPDO This object. + * @param object Pointer to object, which will be passed to pFunctSignalPre(). + * @param pFunctSignalPre Pointer to the callback function. Not called if NULL. + */ +void CO_RPDO_initCallbackPre(CO_RPDO_t* RPDO, void* object, void (*pFunctSignalPre)(void* object)); +#endif + +/** + * Process received PDO messages. + * + * Function must be called cyclically in any NMT state. It copies data from RPDO to Object Dictionary variables if: new + * PDO receives and PDO is valid and NMT operating state is operational. Synchronous RPDOs are processed after next SYNC + * message. + * + * @param RPDO This object. + * @param timeDifference_us Time difference from previous function call. + * @param [out] timerNext_us info to OS - see CO_process(). + * @param NMTisOperational True if this node is in NMT_OPERATIONAL state. + * @param syncWas True, if CANopen SYNC message was just received or transmitted. + */ +void CO_RPDO_process(CO_RPDO_t* RPDO, +#if (((CO_CONFIG_PDO)&CO_CONFIG_RPDO_TIMERS_ENABLE) != 0) || defined CO_DOXYGEN + uint32_t timeDifference_us, uint32_t* timerNext_us, +#endif + bool_t NMTisOperational, bool_t syncWas); +#endif /* (CO_CONFIG_PDO) & CO_CONFIG_RPDO_ENABLE */ + +/******************************************************************************* + * T P D O + ******************************************************************************/ +#if (((CO_CONFIG_PDO)&CO_CONFIG_TPDO_ENABLE) != 0) || defined CO_DOXYGEN +/** + * TPDO object. + */ +typedef struct { + CO_PDO_common_t PDO_common; /**< PDO common properties, must be first element in this object */ + CO_CANtx_t* CANtxBuff; /**< CAN transmit buffer inside CANdev */ + uint8_t transmissionType; /**< Copy of the variable from object dictionary */ + bool_t sendRequest; /**< If this flag is set and TPDO is event driven (transmission type is 0, 254 or 255), + then PDO will be sent by CO_TPDO_process(). */ +#if (((CO_CONFIG_PDO)&CO_CONFIG_PDO_SYNC_ENABLE) != 0) || defined CO_DOXYGEN + CO_SYNC_t* SYNC; /**< From CO_TPDO_init() */ + uint8_t syncStartValue; /**< Copy of the variable from object dictionary */ + uint8_t syncCounter; /**< SYNC counter used for PDO sending */ +#endif +#if (((CO_CONFIG_PDO)&CO_CONFIG_TPDO_TIMERS_ENABLE) != 0) || defined CO_DOXYGEN + uint32_t inhibitTime_us; /**< Inhibit time from object dictionary translated to microseconds */ + uint32_t eventTime_us; /**< Event time from object dictionary translated to microseconds */ + uint32_t inhibitTimer; /**< Inhibit timer variable in microseconds */ + uint32_t eventTimer; /**< Event timer variable in microseconds */ +#endif +} CO_TPDO_t; + +/** + * Initialize TPDO object. + * + * Function must be called in the end of the communication reset section, after all application initialization. + * Otherwise mapping to application OD variables will not be correct. + * + * @param TPDO This object will be initialized. + * @param OD Object Dictionary. + * @param em Emergency object. + * @param SYNC SYNC object, may be NULL. + * @param preDefinedCanId CAN identifier from pre-defined connection set, including node-id for first four PDOs, or 0 + * otherwise, see @ref CO_PDO_CAN_ID + * @param OD_18xx_TPDOCommPar OD entry for 0x1800+ - "TPDO communication parameter", entry is required. + * @param OD_1Axx_TPDOMapPar OD entry for 0x1A00+ - "TPDO mapping parameter", entry is required. + * @param CANdevTx CAN device used for PDO transmission. + * @param CANdevTxIdx Index of transmit buffer in the above CAN device. + * @param [out] errInfo Additional information in case of error, may be NULL. + * + * @return #CO_ReturnError_t CO_ERROR_NO on success. + */ +CO_ReturnError_t CO_TPDO_init(CO_TPDO_t* TPDO, OD_t* OD, CO_EM_t* em, +#if (((CO_CONFIG_PDO)&CO_CONFIG_PDO_SYNC_ENABLE) != 0) || defined CO_DOXYGEN + CO_SYNC_t* SYNC, +#endif + uint16_t preDefinedCanId, OD_entry_t* OD_18xx_TPDOCommPar, OD_entry_t* OD_1Axx_TPDOMapPar, + CO_CANmodule_t* CANdevTx, uint16_t CANdevTxIdx, uint32_t* errInfo); + +/** + * Request transmission of TPDO message. + * + * If TPDO transmission type is 0, 254 or 255, then TPDO will be sent by @ref CO_TPDO_process() after inhibit timer + * expires. See also @ref OD_requestTPDO() and @ref OD_TPDOtransmitted(). + * + * @param TPDO TPDO object. + */ +static inline void +CO_TPDOsendRequest(CO_TPDO_t* TPDO) { + if (TPDO != NULL) { + TPDO->sendRequest = true; + } +} + +/** + * Process transmitting PDO messages. + * + * Function must be called cyclically in any NMT state. It prepares and sends TPDO if necessary. + * + * @param TPDO This object. + * @param timeDifference_us Time difference from previous function call. + * @param [out] timerNext_us info to OS - see CO_process(). + * @param NMTisOperational True if this node is in NMT_OPERATIONAL state. + * @param syncWas True, if CANopen SYNC message was just received or transmitted. + */ +void CO_TPDO_process(CO_TPDO_t* TPDO, +#if (((CO_CONFIG_PDO)&CO_CONFIG_TPDO_TIMERS_ENABLE) != 0) || defined CO_DOXYGEN + uint32_t timeDifference_us, uint32_t* timerNext_us, +#endif + bool_t NMTisOperational, bool_t syncWas); +#endif /* (CO_CONFIG_PDO) & CO_CONFIG_TPDO_ENABLE */ + +/** @} */ /* CO_PDO */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* (CO_CONFIG_PDO) & (CO_CONFIG_RPDO_ENABLE | CO_CONFIG_TPDO_ENABLE) */ + +#endif /* CO_PDO_H */ diff --git a/301/CO_SDOclient.c b/301/CO_SDOclient.c new file mode 100644 index 00000000..8318e60c --- /dev/null +++ b/301/CO_SDOclient.c @@ -0,0 +1,1777 @@ +/* + * CANopen Service Data Object - client. + * + * @file CO_SDOclient.c + * @ingroup CO_SDOclient + * @author Janez Paternoster + * @author Matej Severkar + * @copyright 2020 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#include "301/CO_SDOclient.h" + +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_ENABLE) != 0 + +/* verify configuration */ +#if CO_CONFIG_SDO_CLI_BUFFER_SIZE < 7U +#error CO_CONFIG_SDO_CLI_BUFFER_SIZE must be set to 7 or more. +#endif +#if ((CO_CONFIG_FIFO)&CO_CONFIG_FIFO_ENABLE) == 0 +#error CO_CONFIG_FIFO_ENABLE must be enabled. +#endif +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) != 0 +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_SEGMENTED) == 0 +#error CO_CONFIG_SDO_CLI_SEGMENTED must be enabled. +#endif +#if ((CO_CONFIG_FIFO)&CO_CONFIG_FIFO_ALT_READ) == 0 +#error CO_CONFIG_FIFO_ALT_READ must be enabled. +#endif +#if ((CO_CONFIG_FIFO)&CO_CONFIG_FIFO_CRC16_CCITT) == 0 +#error CO_CONFIG_FIFO_CRC16_CCITT must be enabled. +#endif +#endif + +/* default 'protocol switch threshold' size for block transfer */ +#ifndef CO_CONFIG_SDO_CLI_PST +#define CO_CONFIG_SDO_CLI_PST 21U +#endif + +/* + * Read received message from CAN module. + * + * Function will be called (by CAN receive interrupt) every time, when CAN message with correct identifier + * will be received. For more information and description of parameters see file CO_driver.h. + */ +static void +CO_SDOclient_receive(void* object, void* msg) { + CO_SDOclient_t* SDO_C = (CO_SDOclient_t*)object; + uint8_t DLC = CO_CANrxMsg_readDLC(msg); + const uint8_t* data = CO_CANrxMsg_readData(msg); + + /* Ignore messages in idle state and messages with wrong length. Ignore + * message also if previous message was not processed yet and not abort */ + if ((SDO_C->state != CO_SDO_ST_IDLE) && (DLC == 8U) && (!CO_FLAG_READ(SDO_C->CANrxNew) || (data[0] == 0x80U))) { +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) != 0 + bool_t state_not_upload_blk_sublock_sreq = (SDO_C->state != CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ); + bool_t state_not_upload_blk_sublock_crsp = (SDO_C->state != CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_CRSP); + if ((data[0] == 0x80U) /* abort from server */ + || (state_not_upload_blk_sublock_sreq && state_not_upload_blk_sublock_crsp)) { +#endif + /* copy data and set 'new message' flag */ + (void)memcpy((void*)&SDO_C->CANrxData[0], (const void*)&data[0], 8); + CO_FLAG_SET(SDO_C->CANrxNew); +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0 + /* Optional signal to RTOS, which can resume task, which handles + * SDO client processing. */ + if (SDO_C->pFunctSignal != NULL) { + SDO_C->pFunctSignal(SDO_C->functSignalObject); + } +#endif + +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) != 0 + } else if (SDO_C->state == CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ) { + /* block upload, copy data directly */ + CO_SDO_state_t state = CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ; + uint8_t seqno = data[0] & 0x7FU; + SDO_C->timeoutTimer = 0; + SDO_C->block_timeoutTimer = 0; + + /* verify if sequence number is correct */ + if ((seqno <= SDO_C->block_blksize) && (seqno == (SDO_C->block_seqno + 1U))) { + SDO_C->block_seqno = seqno; + + /* is this the last segment? */ + if ((data[0] & 0x80U) != 0U) { + /* copy data to temporary buffer, because we don't know the number of bytes not containing data */ + (void)memcpy((void*)&SDO_C->block_dataUploadLast[0], (const void*)&data[1], 7); + SDO_C->finished = true; + state = CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_CRSP; + } else { + /* Copy data. There is always enough space in fifo buffer, + * because block_blksize was calculated before */ + (void)CO_fifo_write(&SDO_C->bufFifo, &data[1], 7, &SDO_C->block_crc); + SDO_C->sizeTran += 7U; + /* all segments in sub-block has been transferred */ + if (seqno == SDO_C->block_blksize) { + state = CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_CRSP; + } + } + } + /* If message is duplicate or sequence didn't start yet, ignore it. Otherwise seqno is wrong, + * so break sub-block. Data after last good seqno will be re-transmitted. */ + else if ((seqno != SDO_C->block_seqno) && (SDO_C->block_seqno != 0U)) { + state = CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_CRSP; +#ifdef CO_DEBUG_SDO_CLIENT + char msg[80]; + sprintf(msg, "sub-block, rx WRONG: sequno=%02X, previous=%02X", seqno, SDO_C->block_seqno); + CO_DEBUG_SDO_CLIENT(msg); +#endif + } else { + /* MISRA C 2004 14.10 */ +#ifdef CO_DEBUG_SDO_CLIENT + char msg[80]; + sprintf(msg, "sub-block, rx ignored: sequno=%02X, expected=%02X", seqno, SDO_C->block_seqno + 1); + CO_DEBUG_SDO_CLIENT(msg); +#endif + } + + /* Is exit from sub-block receive state? */ + if (state != CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ) { + /* Processing will continue in another thread, so make memory + * barrier here with CO_FLAG_CLEAR() call. */ + CO_FLAG_CLEAR(SDO_C->CANrxNew); + SDO_C->state = state; +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0 + /* Optional signal to RTOS, which can resume task, which handles SDO client processing. */ + if (SDO_C->pFunctSignal != NULL) { + SDO_C->pFunctSignal(SDO_C->functSignalObject); + } +#endif + } + } else { /* MISRA C 2004 14.10 */ + } + +#endif /* (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_BLOCK */ + } +} + +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_FLAG_OD_DYNAMIC) != 0 +/* + * Custom function for writing OD object _SDO client parameter_ + * + * For more information see file CO_ODinterface.h, OD_IO_t. + */ +static ODR_t +OD_write_1280(OD_stream_t* stream, const void* buf, OD_size_t count, OD_size_t* countWritten) { + /* "count" is already verified in *_init() function */ + if ((stream == NULL) || (buf == NULL) || (countWritten == NULL)) { + return ODR_DEV_INCOMPAT; + } + + CO_SDOclient_t* SDO_C = (CO_SDOclient_t*)stream->object; + + switch (stream->subIndex) { + case 0: /* Highest sub-index supported */ return ODR_READONLY; break; + + case 1: { /* COB-ID client -> server */ + uint32_t COB_ID = CO_getUint32(buf); + uint16_t CAN_ID = (uint16_t)(COB_ID & 0x7FFU); + uint16_t CAN_ID_cur = (uint16_t)(SDO_C->COB_IDClientToServer & 0x7FFU); + bool_t valid = (COB_ID & 0x80000000U) == 0U; + + /* SDO client must not be valid when changing COB_ID */ + if (((COB_ID & 0x3FFFF800U) != 0U) || (valid && SDO_C->valid && (CAN_ID != CAN_ID_cur)) + || (valid && CO_IS_RESTRICTED_CAN_ID(CAN_ID))) { + return ODR_INVALID_VALUE; + } + (void)CO_SDOclient_setup(SDO_C, COB_ID, SDO_C->COB_IDServerToClient, SDO_C->nodeIDOfTheSDOServer); + break; + } + + case 2: { /* COB-ID server -> client */ + uint32_t COB_ID = CO_getUint32(buf); + uint16_t CAN_ID = (uint16_t)(COB_ID & 0x7FFU); + uint16_t CAN_ID_cur = (uint16_t)(SDO_C->COB_IDServerToClient & 0x7FFU); + bool_t valid = (COB_ID & 0x80000000U) == 0U; + + /* SDO client must not be valid when changing COB_ID */ + if (((COB_ID & 0x3FFFF800U) != 0U) || (valid && SDO_C->valid && (CAN_ID != CAN_ID_cur)) + || (valid && CO_IS_RESTRICTED_CAN_ID(CAN_ID))) { + return ODR_INVALID_VALUE; + } + (void)CO_SDOclient_setup(SDO_C, SDO_C->COB_IDClientToServer, COB_ID, SDO_C->nodeIDOfTheSDOServer); + break; + } + + case 3: { /* Node-ID of the SDO server */ + uint8_t nodeId = CO_getUint8(buf); + if (nodeId > 127U) { + return ODR_INVALID_VALUE; + } + SDO_C->nodeIDOfTheSDOServer = nodeId; + break; + } + + default: return ODR_SUB_NOT_EXIST; break; + } + + /* write value to the original location in the Object Dictionary */ + return OD_writeOriginal(stream, buf, count, countWritten); +} +#endif /* (CO_CONFIG_SDO_CLI) & CO_CONFIG_FLAG_OD_DYNAMIC */ + +CO_ReturnError_t +CO_SDOclient_init(CO_SDOclient_t* SDO_C, OD_t* OD, OD_entry_t* OD_1280_SDOcliPar, uint8_t nodeId, + CO_CANmodule_t* CANdevRx, uint16_t CANdevRxIdx, CO_CANmodule_t* CANdevTx, uint16_t CANdevTxIdx, + uint32_t* errInfo) { + bool_t index_SDOcliPar_min = (OD_getIndex(OD_1280_SDOcliPar) < (uint16_t)(OD_H1280_SDO_CLIENT_1_PARAM)); + bool_t index_SDOcliPar_max = (OD_getIndex(OD_1280_SDOcliPar) > ((uint16_t)(OD_H1280_SDO_CLIENT_1_PARAM) + 0x7FU)); + + /* verify arguments */ + if ((SDO_C == NULL) || (OD_1280_SDOcliPar == NULL) || index_SDOcliPar_min || index_SDOcliPar_max + || (CANdevRx == NULL) || (CANdevTx == NULL)) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + /* Configure object variables */ +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_LOCAL) != 0 + SDO_C->OD = OD; + SDO_C->nodeId = nodeId; +#endif + SDO_C->CANdevRx = CANdevRx; + SDO_C->CANdevRxIdx = CANdevRxIdx; + SDO_C->CANdevTx = CANdevTx; + SDO_C->CANdevTxIdx = CANdevTxIdx; +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0 + SDO_C->pFunctSignal = NULL; + SDO_C->functSignalObject = NULL; +#endif + + /* prepare circular fifo buffer */ + CO_fifo_init(&SDO_C->bufFifo, SDO_C->buf, CO_CONFIG_SDO_CLI_BUFFER_SIZE + 1U); + + /* Get parameters from Object Dictionary (initial values) */ + uint8_t maxSubIndex, nodeIDOfTheSDOServer; + uint32_t COB_IDClientToServer, COB_IDServerToClient; + ODR_t odRet0 = OD_get_u8(OD_1280_SDOcliPar, 0, &maxSubIndex, true); + ODR_t odRet1 = OD_get_u32(OD_1280_SDOcliPar, 1, &COB_IDClientToServer, true); + ODR_t odRet2 = OD_get_u32(OD_1280_SDOcliPar, 2, &COB_IDServerToClient, true); + ODR_t odRet3 = OD_get_u8(OD_1280_SDOcliPar, 3, &nodeIDOfTheSDOServer, true); + + if ((odRet0 != ODR_OK) || (maxSubIndex != 3U) || (odRet1 != ODR_OK) || (odRet2 != ODR_OK) || (odRet3 != ODR_OK)) { + if (errInfo != NULL) { + *errInfo = OD_getIndex(OD_1280_SDOcliPar); + } + return CO_ERROR_OD_PARAMETERS; + } + +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_FLAG_OD_DYNAMIC) != 0 + SDO_C->OD_1280_extension.object = SDO_C; + SDO_C->OD_1280_extension.read = OD_readOriginal; + SDO_C->OD_1280_extension.write = OD_write_1280; + ODR_t odRetE = OD_extension_init(OD_1280_SDOcliPar, &SDO_C->OD_1280_extension); + if (odRetE != ODR_OK) { + if (errInfo != NULL) { + *errInfo = OD_getIndex(OD_1280_SDOcliPar); + } + return CO_ERROR_OD_PARAMETERS; + } + + /* set to zero to make sure CO_SDOclient_setup() will reconfigure CAN */ + SDO_C->COB_IDClientToServer = 0; + SDO_C->COB_IDServerToClient = 0; +#endif + + CO_SDO_return_t cliSetupRet = CO_SDOclient_setup(SDO_C, COB_IDClientToServer, COB_IDServerToClient, + nodeIDOfTheSDOServer); + + if (cliSetupRet != CO_SDO_RT_ok_communicationEnd) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + return CO_ERROR_NO; +} + +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0 +void +CO_SDOclient_initCallbackPre(CO_SDOclient_t* SDOclient, void* object, void (*pFunctSignal)(void* object)) { + if (SDOclient != NULL) { + SDOclient->functSignalObject = object; + SDOclient->pFunctSignal = pFunctSignal; + } +} +#endif + +#if (((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_LOCAL) != 0) && defined CO_BIG_ENDIAN +static inline void +reverseBytes(void* start, OD_size_t size) { + uint8_t* lo = (uint8_t*)start; + uint8_t* hi = (uint8_t*)start + size - 1; + uint8_t swap; + while (lo < hi) { + swap = *lo; + *lo++ = *hi; + *hi-- = swap; + } +} +#endif + +CO_SDO_return_t +CO_SDOclient_setup(CO_SDOclient_t* SDO_C, uint32_t COB_IDClientToServer, uint32_t COB_IDServerToClient, + uint8_t nodeIDOfTheSDOServer) { + /* verify parameters */ + if (SDO_C == NULL) { + return CO_SDO_RT_wrongArguments; + } + + /* Configure object variables */ + SDO_C->state = CO_SDO_ST_IDLE; + CO_FLAG_CLEAR(SDO_C->CANrxNew); + SDO_C->nodeIDOfTheSDOServer = nodeIDOfTheSDOServer; + +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_FLAG_OD_DYNAMIC) != 0 + /* proceed only, if parameters change */ + if ((COB_IDClientToServer == SDO_C->COB_IDClientToServer) + && (COB_IDServerToClient == SDO_C->COB_IDServerToClient)) { + return CO_SDO_RT_ok_communicationEnd; + } + /* store variables */ + SDO_C->COB_IDClientToServer = COB_IDClientToServer; + SDO_C->COB_IDServerToClient = COB_IDServerToClient; +#endif + + /* verify valid bit */ + uint16_t CanIdC2S = ((COB_IDClientToServer & 0x80000000UL) == 0U) ? (uint16_t)(COB_IDClientToServer & 0x7FFU) : 0U; + uint16_t CanIdS2C = ((COB_IDServerToClient & 0x80000000UL) == 0U) ? (uint16_t)(COB_IDServerToClient & 0x7FFU) : 0U; + if ((CanIdC2S != 0U) && (CanIdS2C != 0U)) { + SDO_C->valid = true; + } else { + CanIdC2S = 0; + CanIdS2C = 0; + SDO_C->valid = false; + } + + /* configure SDO client CAN reception */ + CO_ReturnError_t ret = CO_CANrxBufferInit(SDO_C->CANdevRx, SDO_C->CANdevRxIdx, CanIdS2C, 0x7FF, false, (void*)SDO_C, + CO_SDOclient_receive); + + /* configure SDO client CAN transmission */ + SDO_C->CANtxBuff = CO_CANtxBufferInit(SDO_C->CANdevTx, SDO_C->CANdevTxIdx, CanIdC2S, false, 8, false); + + if ((ret != CO_ERROR_NO) || (SDO_C->CANtxBuff == NULL)) { + SDO_C->valid = false; + return CO_SDO_RT_wrongArguments; + } + + return CO_SDO_RT_ok_communicationEnd; +} + +/****************************************************************************** + * DOWNLOAD * + ******************************************************************************/ +CO_SDO_return_t +CO_SDOclientDownloadInitiate(CO_SDOclient_t* SDO_C, uint16_t index, uint8_t subIndex, size_t sizeIndicated, + uint16_t SDOtimeoutTime_ms, bool_t blockEnable) { + /* verify parameters */ + if ((SDO_C == NULL) || !SDO_C->valid) { + return CO_SDO_RT_wrongArguments; + } + + /* save parameters */ + SDO_C->index = index; + SDO_C->subIndex = subIndex; + SDO_C->sizeInd = sizeIndicated; + SDO_C->sizeTran = 0; + SDO_C->finished = false; + SDO_C->SDOtimeoutTime_us = (uint32_t)SDOtimeoutTime_ms * 1000U; + SDO_C->timeoutTimer = 0; + CO_fifo_reset(&SDO_C->bufFifo); + +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_LOCAL) != 0 + /* if node-ID of the SDO server is the same as node-ID of this node, then transfer data within this node */ + if ((SDO_C->OD != NULL) && (SDO_C->nodeId != 0U) && (SDO_C->nodeIDOfTheSDOServer == SDO_C->nodeId)) { + SDO_C->OD_IO.write = NULL; + SDO_C->state = CO_SDO_ST_DOWNLOAD_LOCAL_TRANSFER; + } else +#endif +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) != 0 + if (blockEnable && ((sizeIndicated == 0U) || (sizeIndicated > (size_t)(CO_CONFIG_SDO_CLI_PST)))) { + SDO_C->state = CO_SDO_ST_DOWNLOAD_BLK_INITIATE_REQ; + } else +#endif + { + SDO_C->state = CO_SDO_ST_DOWNLOAD_INITIATE_REQ; + } + + CO_FLAG_CLEAR(SDO_C->CANrxNew); + + return CO_SDO_RT_ok_communicationEnd; +} + +void +CO_SDOclientDownloadInitSize(CO_SDOclient_t* SDO_C, size_t sizeIndicated) { + if (SDO_C != NULL) { + SDO_C->sizeInd = sizeIndicated; +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) != 0 + if ((SDO_C->state == CO_SDO_ST_DOWNLOAD_BLK_INITIATE_REQ) && (sizeIndicated > 0U) + && (sizeIndicated <= (size_t)(CO_CONFIG_SDO_CLI_PST))) { + SDO_C->state = CO_SDO_ST_DOWNLOAD_INITIATE_REQ; + } +#endif + } +} + +size_t +CO_SDOclientDownloadBufWrite(CO_SDOclient_t* SDO_C, const uint8_t* buf, size_t count) { + size_t ret = 0; + if ((SDO_C != NULL) && (buf != NULL)) { + ret = CO_fifo_write(&SDO_C->bufFifo, buf, count, NULL); + } + return ret; +} + +CO_SDO_return_t +CO_SDOclientDownload(CO_SDOclient_t* SDO_C, uint32_t timeDifference_us, bool_t send_abort, bool_t bufferPartial, + CO_SDO_abortCode_t* SDOabortCode, size_t* sizeTransferred, uint32_t* timerNext_us) { + (void)timerNext_us; + (void)bufferPartial; /* may be unused */ + + CO_SDO_return_t ret = CO_SDO_RT_waitingResponse; + CO_SDO_abortCode_t abortCode = CO_SDO_AB_NONE; + + if ((SDO_C == NULL) || !SDO_C->valid) { + abortCode = CO_SDO_AB_DEVICE_INCOMPAT; + ret = CO_SDO_RT_wrongArguments; + } else if (SDO_C->state == CO_SDO_ST_IDLE) { + ret = CO_SDO_RT_ok_communicationEnd; + } +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_LOCAL) != 0 + /* Transfer data locally */ + else if ((SDO_C->state == CO_SDO_ST_DOWNLOAD_LOCAL_TRANSFER) && !send_abort) { + /* search object dictionary in first pass */ + if (SDO_C->OD_IO.write == NULL) { + ODR_t odRet; + + odRet = OD_getSub(OD_find(SDO_C->OD, SDO_C->index), SDO_C->subIndex, &SDO_C->OD_IO, false); + + if (odRet != ODR_OK) { + abortCode = (CO_SDO_abortCode_t)OD_getSDOabCode(odRet); + ret = CO_SDO_RT_endedWithClientAbort; + } else if ((SDO_C->OD_IO.stream.attribute & (OD_attr_t)ODA_SDO_RW) == 0U) { + abortCode = CO_SDO_AB_UNSUPPORTED_ACCESS; + ret = CO_SDO_RT_endedWithClientAbort; + } else if ((SDO_C->OD_IO.stream.attribute & (OD_attr_t)ODA_SDO_W) == 0U) { + abortCode = CO_SDO_AB_READONLY; + ret = CO_SDO_RT_endedWithClientAbort; + } else if (SDO_C->OD_IO.write == NULL) { + abortCode = CO_SDO_AB_DEVICE_INCOMPAT; + ret = CO_SDO_RT_endedWithClientAbort; + } else { /* MISRA C 2004 14.10 */ + } + } + /* write data, in several passes if necessary */ + if (SDO_C->OD_IO.write != NULL) { + size_t count = CO_fifo_getOccupied(&SDO_C->bufFifo); + uint8_t buf[CO_CONFIG_SDO_CLI_BUFFER_SIZE + 2U]; + + (void)CO_fifo_read(&SDO_C->bufFifo, buf, count, NULL); + SDO_C->sizeTran += count; + + /* error: no data */ + if ((count == 0U) || (count > CO_CONFIG_SDO_CLI_BUFFER_SIZE)) { + abortCode = CO_SDO_AB_DEVICE_INCOMPAT; + ret = CO_SDO_RT_endedWithClientAbort; + } + /* verify if sizeTran is too large */ + else if ((SDO_C->sizeInd > 0U) && (SDO_C->sizeTran > SDO_C->sizeInd)) { + SDO_C->sizeTran -= count; + abortCode = CO_SDO_AB_DATA_LONG; + ret = CO_SDO_RT_endedWithClientAbort; + } + /* Verify sizeTran is too small in last segment of data */ + else if (!bufferPartial && (SDO_C->sizeInd > 0U) && (SDO_C->sizeTran < SDO_C->sizeInd)) { + abortCode = CO_SDO_AB_DATA_SHORT; + ret = CO_SDO_RT_endedWithClientAbort; + } + /* is the last segment of data? */ + else if (!bufferPartial) { +#ifdef CO_BIG_ENDIAN + /* swap int16_t .. uint64_t data if necessary */ + if ((SDO_C->OD_IO.stream.attribute & ODA_MB) != 0) { + reverseBytes(buf, count); + } +#endif + OD_size_t sizeInOd = SDO_C->OD_IO.stream.dataLength; + + /* If dataType is string, then size of data downloaded may be shorter than size of + * OD data buffer. If so, add two zero bytes to terminate (unicode) string. Shorten + * also OD data size, (temporary, send info about EOF into OD_IO.write) */ + if (((SDO_C->OD_IO.stream.attribute & (OD_attr_t)ODA_STR) != 0U) + && ((sizeInOd == 0U) || (SDO_C->sizeTran < sizeInOd))) { + buf[count] = 0; + count++; + SDO_C->sizeTran++; + if ((sizeInOd == 0U) || (sizeInOd > SDO_C->sizeTran)) { + buf[count] = 0; + count++; + SDO_C->sizeTran++; + } + SDO_C->OD_IO.stream.dataLength = (OD_size_t)SDO_C->sizeTran; + } + /* Indicate OD data size, if necessary. Used for EOF check. */ + else if (sizeInOd == 0U) { + SDO_C->OD_IO.stream.dataLength = (OD_size_t)SDO_C->sizeTran; + } + /* Verify if size of data downloaded matches data size in OD. */ + else if (SDO_C->sizeTran != sizeInOd) { + abortCode = (SDO_C->sizeTran > sizeInOd) ? CO_SDO_AB_DATA_LONG : CO_SDO_AB_DATA_SHORT; + ret = CO_SDO_RT_endedWithClientAbort; + } else { /* MISRA C 2004 14.10 */ + } + } else { /* MISRA C 2004 14.10 */ + } + + if (abortCode == CO_SDO_AB_NONE) { + OD_size_t countWritten = 0; + ODR_t odRet; + + /* write data to Object Dictionary */ + CO_LOCK_OD(SDO_C->CANdevTx); + odRet = SDO_C->OD_IO.write(&SDO_C->OD_IO.stream, buf, (OD_size_t)count, &countWritten); + CO_UNLOCK_OD(SDO_C->CANdevTx); + + /* verify for errors in write */ + if ((odRet != ODR_OK) && (odRet != ODR_PARTIAL)) { + abortCode = (CO_SDO_abortCode_t)OD_getSDOabCode(odRet); + ret = CO_SDO_RT_endedWithServerAbort; + } + /* error if OD variable was written completely, but SDO download still has data */ + else if (bufferPartial && (odRet == ODR_OK)) { + abortCode = CO_SDO_AB_DATA_LONG; + ret = CO_SDO_RT_endedWithClientAbort; + } + /* is end of transfer? */ + else if (!bufferPartial) { + /* error if OD variable was not written completely, but SDO download finished */ + if (odRet == ODR_PARTIAL) { + abortCode = CO_SDO_AB_DATA_SHORT; + ret = CO_SDO_RT_endedWithClientAbort; + } + /* data transfer finished successfully */ + else { + ret = CO_SDO_RT_ok_communicationEnd; + } + } else { + ret = CO_SDO_RT_waitingLocalTransfer; + } + } + } + + if (ret != CO_SDO_RT_waitingLocalTransfer) { + SDO_C->state = CO_SDO_ST_IDLE; + } +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_FLAG_TIMERNEXT) != 0 + /* Inform OS to call this function again without delay. */ + else if (timerNext_us != NULL) { + *timerNext_us = 0; + } else { /* MISRA C 2004 14.10 */ + } +#endif + } +#endif /* CO_CONFIG_SDO_CLI_LOCAL */ + /* CAN data received */ + else if (CO_FLAG_READ(SDO_C->CANrxNew)) { + /* is SDO abort */ + if (SDO_C->CANrxData[0] == 0x80U) { + uint32_t code; + (void)memcpy((void*)(&code), (const void*)(&SDO_C->CANrxData[4]), sizeof(code)); + abortCode = (CO_SDO_abortCode_t)CO_SWAP_32(code); + SDO_C->state = CO_SDO_ST_IDLE; + ret = CO_SDO_RT_endedWithServerAbort; + } else if (send_abort) { + abortCode = (SDOabortCode != NULL) ? *SDOabortCode : CO_SDO_AB_DEVICE_INCOMPAT; + SDO_C->state = CO_SDO_ST_ABORT; + } else { + switch (SDO_C->state) { + case CO_SDO_ST_DOWNLOAD_INITIATE_RSP: { + if (SDO_C->CANrxData[0] == 0x60U) { + /* verify index and subindex */ + uint16_t index; + uint8_t subindex; + index = ((uint16_t)SDO_C->CANrxData[2]) << 8; + index |= SDO_C->CANrxData[1]; + subindex = SDO_C->CANrxData[3]; + if ((index != SDO_C->index) || (subindex != SDO_C->subIndex)) { + abortCode = CO_SDO_AB_PRAM_INCOMPAT; + SDO_C->state = CO_SDO_ST_ABORT; + break; + } + +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_SEGMENTED) != 0 + if (SDO_C->finished) { + /* expedited transfer */ + SDO_C->state = CO_SDO_ST_IDLE; + ret = CO_SDO_RT_ok_communicationEnd; + } else { + /* segmented transfer - prepare the first segment */ + SDO_C->toggle = 0x00; + SDO_C->state = CO_SDO_ST_DOWNLOAD_SEGMENT_REQ; + } +#else + /* expedited transfer */ + SDO_C->state = CO_SDO_ST_IDLE; + ret = CO_SDO_RT_ok_communicationEnd; +#endif + } else { + abortCode = CO_SDO_AB_CMD; + SDO_C->state = CO_SDO_ST_ABORT; + } + break; + } + +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_SEGMENTED) != 0 + case CO_SDO_ST_DOWNLOAD_SEGMENT_RSP: { + if ((SDO_C->CANrxData[0] & 0xEFU) == 0x20U) { + /* verify and alternate toggle bit */ + uint8_t toggle = SDO_C->CANrxData[0] & 0x10U; + if (toggle != SDO_C->toggle) { + abortCode = CO_SDO_AB_TOGGLE_BIT; + SDO_C->state = CO_SDO_ST_ABORT; + break; + } + SDO_C->toggle = (toggle == 0x00U) ? 0x10U : 0x00U; + + /* is end of transfer? */ + if (SDO_C->finished) { + SDO_C->state = CO_SDO_ST_IDLE; + ret = CO_SDO_RT_ok_communicationEnd; + } else { + SDO_C->state = CO_SDO_ST_DOWNLOAD_SEGMENT_REQ; + } + } else { + abortCode = CO_SDO_AB_CMD; + SDO_C->state = CO_SDO_ST_ABORT; + } + break; + } +#endif /* (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_SEGMENTED */ + +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) != 0 + case CO_SDO_ST_DOWNLOAD_BLK_INITIATE_RSP: { + if ((SDO_C->CANrxData[0] & 0xFBU) == 0xA0U) { + /* verify index and subindex */ + uint16_t index; + uint8_t subindex; + index = ((uint16_t)SDO_C->CANrxData[2]) << 8; + index |= SDO_C->CANrxData[1]; + subindex = SDO_C->CANrxData[3]; + if ((index != SDO_C->index) || (subindex != SDO_C->subIndex)) { + abortCode = CO_SDO_AB_PRAM_INCOMPAT; + SDO_C->state = CO_SDO_ST_ABORT; + break; + } + + SDO_C->block_crc = 0; + SDO_C->block_blksize = SDO_C->CANrxData[4]; + if ((SDO_C->block_blksize < 1U) || (SDO_C->block_blksize > 127U)) { + SDO_C->block_blksize = 127; + } + SDO_C->block_seqno = 0; + (void)CO_fifo_altBegin(&SDO_C->bufFifo, 0); + SDO_C->state = CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_REQ; + } else { + abortCode = CO_SDO_AB_CMD; + SDO_C->state = CO_SDO_ST_ABORT; + } + break; + } + + case CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_REQ: + case CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_RSP: { + if (SDO_C->CANrxData[0] == 0xA2U) { + /* check number of segments */ + if (SDO_C->CANrxData[1] < SDO_C->block_seqno) { + /* NOT all segments transferred successfully. Re-transmit data after erroneous segment. */ + size_t cntFailed = (size_t)(SDO_C->block_seqno) - (size_t)(SDO_C->CANrxData[1]); + cntFailed = (cntFailed * 7U) - SDO_C->block_noData; + SDO_C->sizeTran -= cntFailed; + (void)CO_fifo_altBegin(&SDO_C->bufFifo, (size_t)SDO_C->CANrxData[1] * 7U); + SDO_C->finished = false; + } else if (SDO_C->CANrxData[1] > SDO_C->block_seqno) { + /* something strange from server, break transmission */ + abortCode = CO_SDO_AB_CMD; + SDO_C->state = CO_SDO_ST_ABORT; + break; + } else { /* MISRA C 2004 14.10 */ + } + + /* confirm successfully transmitted data */ + CO_fifo_altFinish(&SDO_C->bufFifo, &SDO_C->block_crc); + + if (SDO_C->finished) { + SDO_C->state = CO_SDO_ST_DOWNLOAD_BLK_END_REQ; + } else { + SDO_C->block_blksize = SDO_C->CANrxData[2]; + SDO_C->block_seqno = 0; + (void)CO_fifo_altBegin(&SDO_C->bufFifo, 0); + SDO_C->state = CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_REQ; + } + } else { + abortCode = CO_SDO_AB_CMD; + SDO_C->state = CO_SDO_ST_ABORT; + } + break; + } + + case CO_SDO_ST_DOWNLOAD_BLK_END_RSP: { + if (SDO_C->CANrxData[0] == 0xA1U) { + /* SDO block download successfully transferred */ + SDO_C->state = CO_SDO_ST_IDLE; + ret = CO_SDO_RT_ok_communicationEnd; + } else { + abortCode = CO_SDO_AB_CMD; + SDO_C->state = CO_SDO_ST_ABORT; + } + break; + } +#endif /* (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_BLOCK */ +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_SEGMENTED) == 0 + case CO_SDO_ST_DOWNLOAD_SEGMENT_RSP: +#endif +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) == 0 + case CO_SDO_ST_DOWNLOAD_BLK_INITIATE_RSP: + case CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_REQ: + case CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_RSP: + case CO_SDO_ST_DOWNLOAD_BLK_END_RSP: +#endif + case CO_SDO_ST_IDLE: + case CO_SDO_ST_ABORT: + case CO_SDO_ST_DOWNLOAD_LOCAL_TRANSFER: + case CO_SDO_ST_DOWNLOAD_INITIATE_REQ: + case CO_SDO_ST_DOWNLOAD_SEGMENT_REQ: + case CO_SDO_ST_UPLOAD_LOCAL_TRANSFER: + case CO_SDO_ST_UPLOAD_INITIATE_REQ: + case CO_SDO_ST_UPLOAD_INITIATE_RSP: + case CO_SDO_ST_UPLOAD_SEGMENT_REQ: + case CO_SDO_ST_UPLOAD_SEGMENT_RSP: + case CO_SDO_ST_DOWNLOAD_BLK_INITIATE_REQ: + case CO_SDO_ST_DOWNLOAD_BLK_END_REQ: + case CO_SDO_ST_UPLOAD_BLK_INITIATE_REQ: + case CO_SDO_ST_UPLOAD_BLK_INITIATE_RSP: + case CO_SDO_ST_UPLOAD_BLK_INITIATE_REQ2: + case CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ: + case CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_CRSP: + case CO_SDO_ST_UPLOAD_BLK_END_SREQ: + case CO_SDO_ST_UPLOAD_BLK_END_CRSP: + default: { + abortCode = CO_SDO_AB_CMD; + SDO_C->state = CO_SDO_ST_ABORT; + break; + } + } + } + SDO_C->timeoutTimer = 0; + timeDifference_us = 0; + CO_FLAG_CLEAR(SDO_C->CANrxNew); + } else if (send_abort) { + abortCode = (SDOabortCode != NULL) ? *SDOabortCode : CO_SDO_AB_DEVICE_INCOMPAT; + SDO_C->state = CO_SDO_ST_ABORT; + } else { /* MISRA C 2004 14.10 */ + } + + /* Timeout timers and transmit bufferFull flag */ + if (ret == CO_SDO_RT_waitingResponse) { + if (SDO_C->timeoutTimer < SDO_C->SDOtimeoutTime_us) { + SDO_C->timeoutTimer += timeDifference_us; + } + if (SDO_C->timeoutTimer >= SDO_C->SDOtimeoutTime_us) { + abortCode = CO_SDO_AB_TIMEOUT; + SDO_C->state = CO_SDO_ST_ABORT; + } +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_FLAG_TIMERNEXT) != 0 + else if (timerNext_us != NULL) { + /* check again after timeout time elapsed */ + uint32_t diff = SDO_C->SDOtimeoutTime_us - SDO_C->timeoutTimer; + if (*timerNext_us > diff) { + *timerNext_us = diff; + } + } else { /* MISRA C 2004 14.10 */ + } +#endif + if (SDO_C->CANtxBuff->bufferFull) { + ret = CO_SDO_RT_transmittBufferFull; + } + } + + /* Transmit CAN data */ + if (ret == CO_SDO_RT_waitingResponse) { + size_t count; + (void)memset((void*)&SDO_C->CANtxBuff->data[0], 0, 8); + + switch (SDO_C->state) { + case CO_SDO_ST_DOWNLOAD_INITIATE_REQ: { + SDO_C->CANtxBuff->data[0] = 0x20; + SDO_C->CANtxBuff->data[1] = (uint8_t)SDO_C->index; + SDO_C->CANtxBuff->data[2] = (uint8_t)(SDO_C->index >> 8); + SDO_C->CANtxBuff->data[3] = SDO_C->subIndex; + + /* get count of data bytes to transfer */ + count = CO_fifo_getOccupied(&SDO_C->bufFifo); + + /* is expedited transfer, <= 4bytes of data */ + if (((SDO_C->sizeInd == 0U) && (count <= 4U)) || ((SDO_C->sizeInd > 0U) && (SDO_C->sizeInd <= 4U))) { + SDO_C->CANtxBuff->data[0] |= 0x02U; + + /* verify length, indicate data size */ + if ((count == 0U) || ((SDO_C->sizeInd > 0U) && (SDO_C->sizeInd != count))) { + SDO_C->state = CO_SDO_ST_IDLE; + abortCode = CO_SDO_AB_TYPE_MISMATCH; + ret = CO_SDO_RT_endedWithClientAbort; + break; + } + if (SDO_C->sizeInd > 0U) { + SDO_C->CANtxBuff->data[0] |= (uint8_t)(0x01U | ((4U - count) << 2)); + } + + /* copy data */ + (void)CO_fifo_read(&SDO_C->bufFifo, &SDO_C->CANtxBuff->data[4], count, NULL); + SDO_C->sizeTran = count; + SDO_C->finished = true; + } else { +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_SEGMENTED) != 0 + /* segmented transfer, indicate data size */ + if (SDO_C->sizeInd > 0U) { + uint32_t size = CO_SWAP_32((uint32_t)SDO_C->sizeInd); + SDO_C->CANtxBuff->data[0] |= 0x01U; + (void)memcpy((void*)(&SDO_C->CANtxBuff->data[4]), (const void*)(&size), sizeof(size)); + } +#else + SDO_C->state = CO_SDO_ST_IDLE; + abortCode = CO_SDO_AB_UNSUPPORTED_ACCESS; + ret = CO_SDO_RT_endedWithClientAbort; + break; +#endif + } + + /* reset timeout timer and send message */ + SDO_C->timeoutTimer = 0; + (void)CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff); + SDO_C->state = CO_SDO_ST_DOWNLOAD_INITIATE_RSP; + break; + } + +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_SEGMENTED) != 0 + case CO_SDO_ST_DOWNLOAD_SEGMENT_REQ: { + /* fill data bytes */ + count = CO_fifo_read(&SDO_C->bufFifo, &SDO_C->CANtxBuff->data[1], 7, NULL); + + /* verify if sizeTran is too large */ + SDO_C->sizeTran += count; + if ((SDO_C->sizeInd > 0U) && (SDO_C->sizeTran > SDO_C->sizeInd)) { + SDO_C->sizeTran -= count; + abortCode = CO_SDO_AB_DATA_LONG; + SDO_C->state = CO_SDO_ST_ABORT; + break; + } + + /* SDO command specifier */ + SDO_C->CANtxBuff->data[0] = (uint8_t)(SDO_C->toggle | ((7U - count) << 1)); + + /* is end of transfer? Verify also sizeTran */ + if ((CO_fifo_getOccupied(&SDO_C->bufFifo) == 0U) && !bufferPartial) { + if ((SDO_C->sizeInd > 0U) && (SDO_C->sizeTran < SDO_C->sizeInd)) { + abortCode = CO_SDO_AB_DATA_SHORT; + SDO_C->state = CO_SDO_ST_ABORT; + break; + } + SDO_C->CANtxBuff->data[0] |= 0x01U; + SDO_C->finished = true; + } + + /* reset timeout timer and send message */ + SDO_C->timeoutTimer = 0; + (void)CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff); + SDO_C->state = CO_SDO_ST_DOWNLOAD_SEGMENT_RSP; + break; + } +#endif /* (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_SEGMENTED */ + +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) != 0 + case CO_SDO_ST_DOWNLOAD_BLK_INITIATE_REQ: { + SDO_C->CANtxBuff->data[0] = 0xC4; + SDO_C->CANtxBuff->data[1] = (uint8_t)SDO_C->index; + SDO_C->CANtxBuff->data[2] = (uint8_t)(SDO_C->index >> 8); + SDO_C->CANtxBuff->data[3] = SDO_C->subIndex; + + /* indicate data size */ + if (SDO_C->sizeInd > 0U) { + uint32_t size = CO_SWAP_32((uint32_t)SDO_C->sizeInd); + SDO_C->CANtxBuff->data[0] |= 0x02U; + (void)memcpy((void*)(&SDO_C->CANtxBuff->data[4]), (const void*)(&size), sizeof(size)); + } + + /* reset timeout timer and send message */ + SDO_C->timeoutTimer = 0; + (void)CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff); + SDO_C->state = CO_SDO_ST_DOWNLOAD_BLK_INITIATE_RSP; + break; + } + + case CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_REQ: { + if ((CO_fifo_altGetOccupied(&SDO_C->bufFifo) < 7U) && bufferPartial) { + /* wait until data are refilled */ + break; + } + SDO_C->block_seqno++; + SDO_C->CANtxBuff->data[0] = SDO_C->block_seqno; + + /* get up to 7 data bytes */ + count = CO_fifo_altRead(&SDO_C->bufFifo, &SDO_C->CANtxBuff->data[1], 7); + SDO_C->block_noData = (uint8_t)(7U - count); + + /* verify if sizeTran is too large */ + SDO_C->sizeTran += count; + if ((SDO_C->sizeInd > 0U) && (SDO_C->sizeTran > SDO_C->sizeInd)) { + SDO_C->sizeTran -= count; + abortCode = CO_SDO_AB_DATA_LONG; + SDO_C->state = CO_SDO_ST_ABORT; + break; + } + + /* is end of transfer? Verify also sizeTran */ + if ((CO_fifo_altGetOccupied(&SDO_C->bufFifo) == 0U) && !bufferPartial) { + if ((SDO_C->sizeInd > 0U) && (SDO_C->sizeTran < SDO_C->sizeInd)) { + abortCode = CO_SDO_AB_DATA_SHORT; + SDO_C->state = CO_SDO_ST_ABORT; + break; + } + SDO_C->CANtxBuff->data[0] |= 0x80U; + SDO_C->finished = true; + SDO_C->state = CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_RSP; + } + /* are all segments in current block transferred? */ + else if (SDO_C->block_seqno >= SDO_C->block_blksize) { + SDO_C->state = CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_RSP; + } +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_FLAG_TIMERNEXT) != 0 + else { + /* Inform OS to call this function again without delay. */ + if (timerNext_us != NULL) { + *timerNext_us = 0; + } + } +#endif + /* reset timeout timer and send message */ + SDO_C->timeoutTimer = 0; + (void)CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff); + break; + } + + case CO_SDO_ST_DOWNLOAD_BLK_END_REQ: { + SDO_C->CANtxBuff->data[0] = (uint8_t)(0xC1U | (SDO_C->block_noData << 2)); + SDO_C->CANtxBuff->data[1] = (uint8_t)SDO_C->block_crc; + SDO_C->CANtxBuff->data[2] = (uint8_t)(SDO_C->block_crc >> 8); + + /* reset timeout timer and send message */ + SDO_C->timeoutTimer = 0; + (void)CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff); + SDO_C->state = CO_SDO_ST_DOWNLOAD_BLK_END_RSP; + break; + } +#endif /* (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_BLOCK */ +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_SEGMENTED) == 0 + case CO_SDO_ST_DOWNLOAD_SEGMENT_REQ: +#endif +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) == 0 + case CO_SDO_ST_DOWNLOAD_BLK_INITIATE_REQ: + case CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_REQ: + case CO_SDO_ST_DOWNLOAD_BLK_END_REQ: +#endif + case CO_SDO_ST_IDLE: + case CO_SDO_ST_ABORT: + case CO_SDO_ST_DOWNLOAD_LOCAL_TRANSFER: + case CO_SDO_ST_DOWNLOAD_INITIATE_RSP: + case CO_SDO_ST_DOWNLOAD_SEGMENT_RSP: + case CO_SDO_ST_UPLOAD_LOCAL_TRANSFER: + case CO_SDO_ST_UPLOAD_INITIATE_REQ: + case CO_SDO_ST_UPLOAD_INITIATE_RSP: + case CO_SDO_ST_UPLOAD_SEGMENT_REQ: + case CO_SDO_ST_UPLOAD_SEGMENT_RSP: + case CO_SDO_ST_DOWNLOAD_BLK_INITIATE_RSP: + case CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_RSP: + case CO_SDO_ST_DOWNLOAD_BLK_END_RSP: + case CO_SDO_ST_UPLOAD_BLK_INITIATE_REQ: + case CO_SDO_ST_UPLOAD_BLK_INITIATE_RSP: + case CO_SDO_ST_UPLOAD_BLK_INITIATE_REQ2: + case CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ: + case CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_CRSP: + case CO_SDO_ST_UPLOAD_BLK_END_SREQ: + case CO_SDO_ST_UPLOAD_BLK_END_CRSP: + default: { + /* none */ + break; + } + } + } + + if (ret == CO_SDO_RT_waitingResponse) { + if (SDO_C->state == CO_SDO_ST_ABORT) { + uint32_t code = CO_SWAP_32((uint32_t)abortCode); + /* Send SDO abort message */ + SDO_C->CANtxBuff->data[0] = 0x80; + SDO_C->CANtxBuff->data[1] = (uint8_t)SDO_C->index; + SDO_C->CANtxBuff->data[2] = (uint8_t)(SDO_C->index >> 8); + SDO_C->CANtxBuff->data[3] = SDO_C->subIndex; + + (void)memcpy((void*)(&SDO_C->CANtxBuff->data[4]), (const void*)(&code), sizeof(code)); + (void)CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff); + SDO_C->state = CO_SDO_ST_IDLE; + ret = CO_SDO_RT_endedWithClientAbort; + } +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) != 0 + else if (SDO_C->state == CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_REQ) { + ret = CO_SDO_RT_blockDownldInProgress; + } else { /* MISRA C 2004 14.10 */ + } +#endif + } + + if (sizeTransferred != NULL) { + *sizeTransferred = SDO_C->sizeTran; + } + if (SDOabortCode != NULL) { + *SDOabortCode = abortCode; + } + + return ret; +} + +/****************************************************************************** + * UPLOAD * + ******************************************************************************/ +CO_SDO_return_t +CO_SDOclientUploadInitiate(CO_SDOclient_t* SDO_C, uint16_t index, uint8_t subIndex, uint16_t SDOtimeoutTime_ms, + bool_t blockEnable) { + /* verify parameters */ + if ((SDO_C == NULL) || !SDO_C->valid) { + return CO_SDO_RT_wrongArguments; + } + + /* save parameters */ + SDO_C->index = index; + SDO_C->subIndex = subIndex; + SDO_C->sizeInd = 0; + SDO_C->sizeTran = 0; + SDO_C->finished = false; + CO_fifo_reset(&SDO_C->bufFifo); + SDO_C->SDOtimeoutTime_us = (uint32_t)SDOtimeoutTime_ms * 1000U; + SDO_C->timeoutTimer = 0; +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) != 0 + SDO_C->block_SDOtimeoutTime_us = (uint32_t)SDOtimeoutTime_ms * 700U; +#endif + +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_LOCAL) != 0 + /* if node-ID of the SDO server is the same as node-ID of this node, then transfer data within this node */ + if (((SDO_C->OD != NULL) && (SDO_C->nodeId != 0U)) && (SDO_C->nodeIDOfTheSDOServer == SDO_C->nodeId)) { + SDO_C->OD_IO.read = NULL; + SDO_C->state = CO_SDO_ST_UPLOAD_LOCAL_TRANSFER; + } else +#endif +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) != 0 + if (blockEnable) { + SDO_C->state = CO_SDO_ST_UPLOAD_BLK_INITIATE_REQ; + } else +#endif + { + SDO_C->state = CO_SDO_ST_UPLOAD_INITIATE_REQ; + } + + CO_FLAG_CLEAR(SDO_C->CANrxNew); + + return CO_SDO_RT_ok_communicationEnd; +} + +CO_SDO_return_t +CO_SDOclientUpload(CO_SDOclient_t* SDO_C, uint32_t timeDifference_us, bool_t send_abort, + CO_SDO_abortCode_t* SDOabortCode, size_t* sizeIndicated, size_t* sizeTransferred, + uint32_t* timerNext_us) { + (void)timerNext_us; /* may be unused */ + + CO_SDO_return_t ret = CO_SDO_RT_waitingResponse; + CO_SDO_abortCode_t abortCode = CO_SDO_AB_NONE; + + if ((SDO_C == NULL) || !SDO_C->valid) { + abortCode = CO_SDO_AB_DEVICE_INCOMPAT; + ret = CO_SDO_RT_wrongArguments; + } else if (SDO_C->state == CO_SDO_ST_IDLE) { + ret = CO_SDO_RT_ok_communicationEnd; + } +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_LOCAL) != 0 + /* Transfer data locally */ + else if ((SDO_C->state == CO_SDO_ST_UPLOAD_LOCAL_TRANSFER) && !send_abort) { + /* search object dictionary in first pass */ + if (SDO_C->OD_IO.read == NULL) { + ODR_t odRet; + + odRet = OD_getSub(OD_find(SDO_C->OD, SDO_C->index), SDO_C->subIndex, &SDO_C->OD_IO, false); + + if (odRet != ODR_OK) { + abortCode = (CO_SDO_abortCode_t)OD_getSDOabCode(odRet); + ret = CO_SDO_RT_endedWithClientAbort; + } else if ((SDO_C->OD_IO.stream.attribute & (OD_attr_t)ODA_SDO_RW) == 0U) { + abortCode = CO_SDO_AB_UNSUPPORTED_ACCESS; + ret = CO_SDO_RT_endedWithClientAbort; + } else if ((SDO_C->OD_IO.stream.attribute & (OD_attr_t)ODA_SDO_R) == 0U) { + abortCode = CO_SDO_AB_WRITEONLY; + ret = CO_SDO_RT_endedWithClientAbort; + } else if (SDO_C->OD_IO.read == NULL) { + abortCode = CO_SDO_AB_DEVICE_INCOMPAT; + ret = CO_SDO_RT_endedWithClientAbort; + } else { /* MISRA C 2004 14.10 */ + } + } + + size_t countFifo = CO_fifo_getSpace(&SDO_C->bufFifo); + + /* skip copying if buffer full */ + if (countFifo == 0U) { + ret = CO_SDO_RT_uploadDataBufferFull; + } + /* read data, in several passes if necessary */ + else if (SDO_C->OD_IO.read != NULL) { + /* Get size of data in Object Dictionary. If size is not indicated + * use maximum SDO client buffer size. Prepare temp buffer. */ + OD_size_t countData = SDO_C->OD_IO.stream.dataLength; + OD_size_t countBuf = ((countData > 0U) && (countData <= countFifo)) ? countData : (OD_size_t)countFifo; + OD_size_t countRd = 0; + uint8_t buf[CO_CONFIG_SDO_CLI_BUFFER_SIZE + 1U]; + ODR_t odRet; + + /* load data from OD variable into the buffer */ + CO_LOCK_OD(SDO_C->CANdevTx); + odRet = SDO_C->OD_IO.read(&SDO_C->OD_IO.stream, buf, countBuf, &countRd); + CO_UNLOCK_OD(SDO_C->CANdevTx); + + if ((odRet != ODR_OK) && (odRet != ODR_PARTIAL)) { + abortCode = (CO_SDO_abortCode_t)OD_getSDOabCode(odRet); + ret = CO_SDO_RT_endedWithServerAbort; + } else { + /* if data is string, send only data up to null termination */ + if ((countRd > 0U) && (countRd <= CO_CONFIG_SDO_CLI_BUFFER_SIZE) + && ((SDO_C->OD_IO.stream.attribute & (OD_attr_t)ODA_STR) != 0U)) { + buf[countRd] = 0; /* (buf is one byte larger) */ + OD_size_t countStr = (OD_size_t)strlen((char*)buf); + if (countStr == 0U) { + countStr = 1; /* no zero length */ + } + if (countStr < countRd) { + /* string terminator found, finish read, shorten data */ + countRd = countStr; + odRet = ODR_OK; + SDO_C->OD_IO.stream.dataLength = (OD_size_t)SDO_C->sizeTran + countRd; + } + } + + (void)CO_fifo_write(&SDO_C->bufFifo, buf, countRd, NULL); + SDO_C->sizeTran += countRd; + + /* verify if size of data uploaded is too large */ + SDO_C->sizeInd = SDO_C->OD_IO.stream.dataLength; + if ((SDO_C->sizeInd > 0U) && (SDO_C->sizeTran > SDO_C->sizeInd)) { + abortCode = CO_SDO_AB_DATA_LONG; + ret = CO_SDO_RT_endedWithClientAbort; + } + /* If no more segments to be upload, finish */ + else if (odRet == ODR_OK) { + /* verify size of data uploaded */ + if ((SDO_C->sizeInd > 0U) && (SDO_C->sizeTran < SDO_C->sizeInd)) { + abortCode = CO_SDO_AB_DATA_SHORT; + ret = CO_SDO_RT_endedWithClientAbort; + } else { + ret = CO_SDO_RT_ok_communicationEnd; + } + } else { + ret = CO_SDO_RT_waitingLocalTransfer; + } + } + } else { /* MISRA C 2004 14.10 */ + } + + if ((ret != CO_SDO_RT_uploadDataBufferFull) && (ret != CO_SDO_RT_waitingLocalTransfer)) { + SDO_C->state = CO_SDO_ST_IDLE; + } +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_FLAG_TIMERNEXT) != 0 + /* Inform OS to call this function again without delay. */ + else if (timerNext_us != NULL) { + *timerNext_us = 0; + } else { /* MISRA C 2004 14.10 */ + } +#endif + } +#endif /* CO_CONFIG_SDO_CLI_LOCAL */ + /* CAN data received */ + else if (CO_FLAG_READ(SDO_C->CANrxNew)) { + /* is SDO abort */ + if (SDO_C->CANrxData[0] == 0x80U) { + uint32_t code; + (void)memcpy((void*)(&code), (const void*)(&SDO_C->CANrxData[4]), sizeof(code)); + abortCode = (CO_SDO_abortCode_t)CO_SWAP_32(code); + SDO_C->state = CO_SDO_ST_IDLE; + ret = CO_SDO_RT_endedWithServerAbort; + } else if (send_abort) { + abortCode = (SDOabortCode != NULL) ? *SDOabortCode : CO_SDO_AB_DEVICE_INCOMPAT; + SDO_C->state = CO_SDO_ST_ABORT; + } else { + switch (SDO_C->state) { + case CO_SDO_ST_UPLOAD_INITIATE_RSP: { + if ((SDO_C->CANrxData[0] & 0xF0U) == 0x40U) { + /* verify index and subindex */ + uint16_t index; + uint8_t subindex; + index = ((uint16_t)SDO_C->CANrxData[2]) << 8; + index |= SDO_C->CANrxData[1]; + subindex = SDO_C->CANrxData[3]; + if ((index != SDO_C->index) || (subindex != SDO_C->subIndex)) { + abortCode = CO_SDO_AB_PRAM_INCOMPAT; + SDO_C->state = CO_SDO_ST_ABORT; + break; + } + + if ((SDO_C->CANrxData[0] & 0x02U) != 0U) { + /* Expedited transfer */ + size_t count = 4; + /* is size indicated? */ + if ((SDO_C->CANrxData[0] & 0x01U) != 0U) { + count -= (((size_t)SDO_C->CANrxData[0]) >> 2) & 0x03U; + } + /* copy data, indicate size and finish */ + (void)CO_fifo_write(&SDO_C->bufFifo, &SDO_C->CANrxData[4], count, NULL); + SDO_C->sizeTran = count; + SDO_C->state = CO_SDO_ST_IDLE; + ret = CO_SDO_RT_ok_communicationEnd; + } else { +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_SEGMENTED) != 0 + /* segmented transfer, is size indicated? */ + if ((SDO_C->CANrxData[0] & 0x01U) != 0U) { + uint32_t size; + (void)memcpy((void*)(&size), (void*)(&SDO_C->CANrxData[4]), sizeof(size)); + SDO_C->sizeInd = CO_SWAP_32(size); + } + SDO_C->toggle = 0x00; + SDO_C->state = CO_SDO_ST_UPLOAD_SEGMENT_REQ; +#else + abortCode = CO_SDO_AB_UNSUPPORTED_ACCESS; + SDO_C->state = CO_SDO_ST_ABORT; +#endif + } + } else { + abortCode = CO_SDO_AB_CMD; + SDO_C->state = CO_SDO_ST_ABORT; + } + break; + } + +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_SEGMENTED) != 0 + case CO_SDO_ST_UPLOAD_SEGMENT_RSP: { + if ((SDO_C->CANrxData[0] & 0xE0U) == 0x00U) { + size_t count, countWr; + + /* verify and alternate toggle bit */ + uint8_t toggle = SDO_C->CANrxData[0] & 0x10U; + if (toggle != SDO_C->toggle) { + abortCode = CO_SDO_AB_TOGGLE_BIT; + SDO_C->state = CO_SDO_ST_ABORT; + break; + } + SDO_C->toggle = (toggle == 0x00U) ? 0x10U : 0x00U; + + /* get data size and write data to the buffer */ + count = (size_t)(7U) - (((size_t)(SDO_C->CANrxData[0]) >> 1) & 0x07U); + countWr = CO_fifo_write(&SDO_C->bufFifo, &SDO_C->CANrxData[1], count, NULL); + SDO_C->sizeTran += countWr; + + /* verify, if there was not enough space in fifo buffer */ + if (countWr != count) { + abortCode = CO_SDO_AB_OUT_OF_MEM; + SDO_C->state = CO_SDO_ST_ABORT; + break; + } + + /* verify if size of data uploaded is too large */ + if ((SDO_C->sizeInd > 0U) && (SDO_C->sizeTran > SDO_C->sizeInd)) { + abortCode = CO_SDO_AB_DATA_LONG; + SDO_C->state = CO_SDO_ST_ABORT; + break; + } + + /* If no more segments to be upload, finish */ + if ((SDO_C->CANrxData[0] & 0x01U) != 0U) { + /* verify size of data uploaded */ + if ((SDO_C->sizeInd > 0U) && (SDO_C->sizeTran < SDO_C->sizeInd)) { + abortCode = CO_SDO_AB_DATA_SHORT; + SDO_C->state = CO_SDO_ST_ABORT; + } else { + SDO_C->state = CO_SDO_ST_IDLE; + ret = CO_SDO_RT_ok_communicationEnd; + } + } else { + SDO_C->state = CO_SDO_ST_UPLOAD_SEGMENT_REQ; + } + } else { + abortCode = CO_SDO_AB_CMD; + SDO_C->state = CO_SDO_ST_ABORT; + } + break; + } +#endif /* (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_SEGMENTED */ + +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) != 0 + case CO_SDO_ST_UPLOAD_BLK_INITIATE_RSP: { + if ((SDO_C->CANrxData[0] & 0xF9U) == 0xC0U) { + uint16_t index; + uint8_t subindex; + + /* get server CRC support info and data size */ + if ((SDO_C->CANrxData[0] & 0x04U) != 0U) { + SDO_C->block_crcEnabled = true; + } else { + SDO_C->block_crcEnabled = false; + } + if ((SDO_C->CANrxData[0] & 0x02U) != 0U) { + uint32_t size; + (void)memcpy((void*)(&size), (const void*)(&SDO_C->CANrxData[4]), sizeof(size)); + SDO_C->sizeInd = CO_SWAP_32(size); + } + + /* verify index and subindex */ + index = ((uint16_t)SDO_C->CANrxData[2]) << 8; + index |= SDO_C->CANrxData[1]; + subindex = SDO_C->CANrxData[3]; + if ((index != SDO_C->index) || (subindex != SDO_C->subIndex)) { + abortCode = CO_SDO_AB_PRAM_INCOMPAT; + SDO_C->state = CO_SDO_ST_ABORT; + } else { + SDO_C->state = CO_SDO_ST_UPLOAD_BLK_INITIATE_REQ2; + } + } + /* switch to regular transfer, CO_SDO_ST_UPLOAD_INITIATE_RSP */ + else if ((SDO_C->CANrxData[0] & 0xF0U) == 0x40U) { + /* verify index and subindex */ + uint16_t index; + uint8_t subindex; + index = ((uint16_t)SDO_C->CANrxData[2]) << 8; + index |= SDO_C->CANrxData[1]; + subindex = SDO_C->CANrxData[3]; + if ((index != SDO_C->index) || (subindex != SDO_C->subIndex)) { + abortCode = CO_SDO_AB_PRAM_INCOMPAT; + SDO_C->state = CO_SDO_ST_ABORT; + break; + } + + if ((SDO_C->CANrxData[0] & 0x02U) != 0U) { + /* Expedited transfer */ + size_t count = 4; + /* is size indicated? */ + if ((SDO_C->CANrxData[0] & 0x01U) != 0U) { + count -= ((size_t)(SDO_C->CANrxData[0]) >> 2) & 0x03U; + } + /* copy data, indicate size and finish */ + (void)CO_fifo_write(&SDO_C->bufFifo, &SDO_C->CANrxData[4], count, NULL); + SDO_C->sizeTran = count; + SDO_C->state = CO_SDO_ST_IDLE; + ret = CO_SDO_RT_ok_communicationEnd; + } else { + /* segmented transfer, is size indicated? */ + if ((SDO_C->CANrxData[0] & 0x01U) != 0U) { + uint32_t size; + (void)memcpy((void*)(&size), (const void*)(&SDO_C->CANrxData[4]), sizeof(size)); + SDO_C->sizeInd = CO_SWAP_32(size); + } + SDO_C->toggle = 0x00; + SDO_C->state = CO_SDO_ST_UPLOAD_SEGMENT_REQ; + } + } else { + abortCode = CO_SDO_AB_CMD; + SDO_C->state = CO_SDO_ST_ABORT; + } + break; + } + + case CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ: { + /* data are copied directly in the receive function */ + break; + } + + case CO_SDO_ST_UPLOAD_BLK_END_SREQ: { + if ((SDO_C->CANrxData[0] & 0xE3U) == 0xC1U) { + /* Get number of data bytes in last segment, that do not + * contain data. Then copy remaining data into fifo */ + uint8_t noData = ((SDO_C->CANrxData[0] >> 2) & 0x07U); + (void)CO_fifo_write(&SDO_C->bufFifo, &SDO_C->block_dataUploadLast[0], (size_t)(7U) - noData, + &SDO_C->block_crc); + SDO_C->sizeTran += (size_t)(7U) - noData; + + /* verify length */ + if ((SDO_C->sizeInd > 0U) && (SDO_C->sizeTran != SDO_C->sizeInd)) { + abortCode = (SDO_C->sizeTran > SDO_C->sizeInd) ? CO_SDO_AB_DATA_LONG : CO_SDO_AB_DATA_SHORT; + SDO_C->state = CO_SDO_ST_ABORT; + break; + } + + /* verify CRC */ + if (SDO_C->block_crcEnabled) { + uint16_t crcServer; + crcServer = ((uint16_t)SDO_C->CANrxData[2]) << 8; + crcServer |= SDO_C->CANrxData[1]; + if (crcServer != SDO_C->block_crc) { + abortCode = CO_SDO_AB_CRC; + SDO_C->state = CO_SDO_ST_ABORT; + break; + } + } + SDO_C->state = CO_SDO_ST_UPLOAD_BLK_END_CRSP; + } else { + abortCode = CO_SDO_AB_CMD; + SDO_C->state = CO_SDO_ST_ABORT; + } + break; + } +#endif /* (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_BLOCK */ + +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_SEGMENTED) == 0 + case CO_SDO_ST_UPLOAD_SEGMENT_RSP: +#endif +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) == 0 + case CO_SDO_ST_UPLOAD_BLK_INITIATE_RSP: + case CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ: + case CO_SDO_ST_UPLOAD_BLK_END_SREQ: +#endif + case CO_SDO_ST_IDLE: + case CO_SDO_ST_ABORT: + case CO_SDO_ST_DOWNLOAD_LOCAL_TRANSFER: + case CO_SDO_ST_DOWNLOAD_INITIATE_REQ: + case CO_SDO_ST_DOWNLOAD_INITIATE_RSP: + case CO_SDO_ST_DOWNLOAD_SEGMENT_REQ: + case CO_SDO_ST_DOWNLOAD_SEGMENT_RSP: + case CO_SDO_ST_UPLOAD_LOCAL_TRANSFER: + case CO_SDO_ST_UPLOAD_INITIATE_REQ: + case CO_SDO_ST_UPLOAD_SEGMENT_REQ: + case CO_SDO_ST_DOWNLOAD_BLK_INITIATE_REQ: + case CO_SDO_ST_DOWNLOAD_BLK_INITIATE_RSP: + case CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_REQ: + case CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_RSP: + case CO_SDO_ST_DOWNLOAD_BLK_END_REQ: + case CO_SDO_ST_DOWNLOAD_BLK_END_RSP: + case CO_SDO_ST_UPLOAD_BLK_INITIATE_REQ: + case CO_SDO_ST_UPLOAD_BLK_INITIATE_REQ2: + case CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_CRSP: + case CO_SDO_ST_UPLOAD_BLK_END_CRSP: + default: { + abortCode = CO_SDO_AB_CMD; + SDO_C->state = CO_SDO_ST_ABORT; + break; + } + } + } + SDO_C->timeoutTimer = 0; + timeDifference_us = 0; + CO_FLAG_CLEAR(SDO_C->CANrxNew); + } else if (send_abort) { + abortCode = (SDOabortCode != NULL) ? *SDOabortCode : CO_SDO_AB_DEVICE_INCOMPAT; + SDO_C->state = CO_SDO_ST_ABORT; + } else { /* MISRA C 2004 14.10 */ + } + + /* Timeout timers and transmit bufferFull flag */ + if (ret == CO_SDO_RT_waitingResponse) { + if (SDO_C->timeoutTimer < SDO_C->SDOtimeoutTime_us) { + SDO_C->timeoutTimer += timeDifference_us; + } + if (SDO_C->timeoutTimer >= SDO_C->SDOtimeoutTime_us) { + bool_t state_upload_seg_req = (SDO_C->state == CO_SDO_ST_UPLOAD_SEGMENT_REQ); + bool_t state_upload_blk_sublock_crsp = (SDO_C->state == CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_CRSP); + if (state_upload_seg_req || state_upload_blk_sublock_crsp) { + /* application didn't empty buffer */ + abortCode = CO_SDO_AB_GENERAL; + } else { + abortCode = CO_SDO_AB_TIMEOUT; + } + SDO_C->state = CO_SDO_ST_ABORT; + } +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_FLAG_TIMERNEXT) != 0 + else if (timerNext_us != NULL) { + /* check again after timeout time elapsed */ + uint32_t diff = SDO_C->SDOtimeoutTime_us - SDO_C->timeoutTimer; + if (*timerNext_us > diff) { + *timerNext_us = diff; + } + } else { /* MISRA C 2004 14.10 */ + } +#endif + +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) != 0 + /* Timeout for sub-block reception */ + if (SDO_C->state == CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ) { + if (SDO_C->block_timeoutTimer < SDO_C->block_SDOtimeoutTime_us) { + SDO_C->block_timeoutTimer += timeDifference_us; + } + if (SDO_C->block_timeoutTimer >= SDO_C->block_SDOtimeoutTime_us) { + /* SDO_C->state will change, processing will continue in this + * thread. Make memory barrier here with CO_FLAG_CLEAR() call. */ + SDO_C->state = CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_CRSP; + CO_FLAG_CLEAR(SDO_C->CANrxNew); + } +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_FLAG_TIMERNEXT) != 0 + else if (timerNext_us != NULL) { + /* check again after timeout time elapsed */ + uint32_t diff = SDO_C->block_SDOtimeoutTime_us - SDO_C->block_timeoutTimer; + if (*timerNext_us > diff) { + *timerNext_us = diff; + } + } else { /* MISRA C 2004 14.10 */ + } +#endif + } +#endif /* (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_BLOCK */ + + if (SDO_C->CANtxBuff->bufferFull) { + ret = CO_SDO_RT_transmittBufferFull; + } + } + + /* Transmit CAN data */ + if (ret == CO_SDO_RT_waitingResponse) { +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) != 0 + size_t count; +#endif + (void)memset((void*)&SDO_C->CANtxBuff->data[0], 0, 8); + + switch (SDO_C->state) { + case CO_SDO_ST_UPLOAD_INITIATE_REQ: { + SDO_C->CANtxBuff->data[0] = 0x40; + SDO_C->CANtxBuff->data[1] = (uint8_t)SDO_C->index; + SDO_C->CANtxBuff->data[2] = (uint8_t)(SDO_C->index >> 8); + SDO_C->CANtxBuff->data[3] = SDO_C->subIndex; + + /* reset timeout timer and send message */ + SDO_C->timeoutTimer = 0; + (void)CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff); + SDO_C->state = CO_SDO_ST_UPLOAD_INITIATE_RSP; + break; + } + +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_SEGMENTED) != 0 + case CO_SDO_ST_UPLOAD_SEGMENT_REQ: { + /* verify, if there is enough space in data buffer */ + if (CO_fifo_getSpace(&SDO_C->bufFifo) < 7U) { + ret = CO_SDO_RT_uploadDataBufferFull; + break; + } + SDO_C->CANtxBuff->data[0] = 0x60U | SDO_C->toggle; + + /* reset timeout timer and send message */ + SDO_C->timeoutTimer = 0; + (void)CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff); + SDO_C->state = CO_SDO_ST_UPLOAD_SEGMENT_RSP; + break; + } +#endif /* (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_SEGMENTED */ + +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) != 0 + case CO_SDO_ST_UPLOAD_BLK_INITIATE_REQ: { + SDO_C->CANtxBuff->data[0] = 0xA4; + SDO_C->CANtxBuff->data[1] = (uint8_t)SDO_C->index; + SDO_C->CANtxBuff->data[2] = (uint8_t)(SDO_C->index >> 8); + SDO_C->CANtxBuff->data[3] = SDO_C->subIndex; + + /* calculate number of block segments from free buffer space */ + count = CO_fifo_getSpace(&SDO_C->bufFifo) / 7U; + if (count > 127U) { + count = 127; + } else if (count == 0U) { + abortCode = CO_SDO_AB_OUT_OF_MEM; + SDO_C->state = CO_SDO_ST_ABORT; + break; + } else { /* MISRA C 2004 14.10 */ + } + SDO_C->block_blksize = (uint8_t)count; + SDO_C->CANtxBuff->data[4] = SDO_C->block_blksize; + SDO_C->CANtxBuff->data[5] = CO_CONFIG_SDO_CLI_PST; + + /* reset timeout timer and send message */ + SDO_C->timeoutTimer = 0; + (void)CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff); + SDO_C->state = CO_SDO_ST_UPLOAD_BLK_INITIATE_RSP; + break; + } + + case CO_SDO_ST_UPLOAD_BLK_INITIATE_REQ2: { + SDO_C->CANtxBuff->data[0] = 0xA3; + + /* reset timeout timers, seqno and send message */ + SDO_C->timeoutTimer = 0; + SDO_C->block_timeoutTimer = 0; + SDO_C->block_seqno = 0; + SDO_C->block_crc = 0; + /* Block segments will be received in different thread. Make memory + * barrier here with CO_FLAG_CLEAR() call. */ + SDO_C->state = CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ; + CO_FLAG_CLEAR(SDO_C->CANrxNew); + (void)CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff); + break; + } + + case CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_CRSP: { + SDO_C->CANtxBuff->data[0] = 0xA2; + SDO_C->CANtxBuff->data[1] = SDO_C->block_seqno; +#ifdef CO_DEBUG_SDO_CLIENT + bool_t transferShort = SDO_C->block_seqno != SDO_C->block_blksize; + uint8_t seqnoStart = SDO_C->block_seqno; +#endif + + /* Is last segment? */ + if (SDO_C->finished) { + SDO_C->state = CO_SDO_ST_UPLOAD_BLK_END_SREQ; + } else { + /* verify if size of data uploaded is too large */ + if ((SDO_C->sizeInd > 0U) && (SDO_C->sizeTran > SDO_C->sizeInd)) { + abortCode = CO_SDO_AB_DATA_LONG; + SDO_C->state = CO_SDO_ST_ABORT; + break; + } + + /* calculate number of block segments from free buffer space */ + count = CO_fifo_getSpace(&SDO_C->bufFifo) / 7U; + if (count >= 127U) { + count = 127; + } else if (CO_fifo_getOccupied(&SDO_C->bufFifo) > 0U) { + /* application must empty data buffer first */ + ret = CO_SDO_RT_uploadDataBufferFull; +#ifdef CO_DEBUG_SDO_CLIENT + if (transferShort) { + char msg[80]; + sprintf(msg, "sub-block, uploadDataBufferFull: sequno=%02X", seqnoStart); + CO_DEBUG_SDO_CLIENT(msg); + } +#endif +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_FLAG_TIMERNEXT) != 0 + /* Inform OS to call this function again without delay. */ + if (timerNext_us != NULL) { + *timerNext_us = 0; + } +#endif + break; + } else { /* MISRA C 2004 14.10 */ + } + + SDO_C->block_blksize = (uint8_t)count; + SDO_C->block_seqno = 0; + /* Block segments will be received in different thread. Make + * memory barrier here with CO_FLAG_CLEAR() call. */ + SDO_C->state = CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ; + CO_FLAG_CLEAR(SDO_C->CANrxNew); + } + + SDO_C->CANtxBuff->data[2] = SDO_C->block_blksize; + + /* reset block_timeoutTimer, but not SDO_C->timeoutTimer */ + SDO_C->block_timeoutTimer = 0; + (void)CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff); +#ifdef CO_DEBUG_SDO_CLIENT + if (transferShort && !SDO_C->finished) { + char msg[80]; + sprintf(msg, "sub-block restarted: sequnoPrev=%02X, blksize=%02X", seqnoStart, + SDO_C->block_blksize); + CO_DEBUG_SDO_CLIENT(msg); + } +#endif + break; + } + + case CO_SDO_ST_UPLOAD_BLK_END_CRSP: { + SDO_C->CANtxBuff->data[0] = 0xA1; + + (void)CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff); + SDO_C->state = CO_SDO_ST_IDLE; + ret = CO_SDO_RT_ok_communicationEnd; + break; + } +#endif /* (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_BLOCK */ +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_SEGMENTED) == 0 + case CO_SDO_ST_UPLOAD_SEGMENT_REQ: +#endif +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) == 0 + case CO_SDO_ST_UPLOAD_BLK_INITIATE_REQ: + case CO_SDO_ST_UPLOAD_BLK_INITIATE_REQ2: + case CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_CRSP: + case CO_SDO_ST_UPLOAD_BLK_END_CRSP: +#endif + case CO_SDO_ST_IDLE: + case CO_SDO_ST_ABORT: + case CO_SDO_ST_DOWNLOAD_LOCAL_TRANSFER: + case CO_SDO_ST_DOWNLOAD_INITIATE_REQ: + case CO_SDO_ST_DOWNLOAD_INITIATE_RSP: + case CO_SDO_ST_DOWNLOAD_SEGMENT_REQ: + case CO_SDO_ST_DOWNLOAD_SEGMENT_RSP: + case CO_SDO_ST_UPLOAD_LOCAL_TRANSFER: + case CO_SDO_ST_UPLOAD_INITIATE_RSP: + case CO_SDO_ST_UPLOAD_SEGMENT_RSP: + case CO_SDO_ST_DOWNLOAD_BLK_INITIATE_REQ: + case CO_SDO_ST_DOWNLOAD_BLK_INITIATE_RSP: + case CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_REQ: + case CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_RSP: + case CO_SDO_ST_DOWNLOAD_BLK_END_REQ: + case CO_SDO_ST_DOWNLOAD_BLK_END_RSP: + case CO_SDO_ST_UPLOAD_BLK_INITIATE_RSP: + case CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ: + case CO_SDO_ST_UPLOAD_BLK_END_SREQ: + default: { + /* none */ + break; + } + } + } + + if (ret == CO_SDO_RT_waitingResponse) { + if (SDO_C->state == CO_SDO_ST_ABORT) { + uint32_t code = CO_SWAP_32((uint32_t)abortCode); + /* Send SDO abort message */ + SDO_C->CANtxBuff->data[0] = 0x80; + SDO_C->CANtxBuff->data[1] = (uint8_t)SDO_C->index; + SDO_C->CANtxBuff->data[2] = (uint8_t)(SDO_C->index >> 8); + SDO_C->CANtxBuff->data[3] = SDO_C->subIndex; + + (void)memcpy((void*)(&SDO_C->CANtxBuff->data[4]), (const void*)(&code), sizeof(code)); + (void)CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff); + SDO_C->state = CO_SDO_ST_IDLE; + ret = CO_SDO_RT_endedWithClientAbort; + } +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) != 0 + else if (SDO_C->state == CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ) { + ret = CO_SDO_RT_blockUploadInProgress; + } else { /* MISRA C 2004 14.10 */ + } +#endif + } + + if (sizeIndicated != NULL) { + *sizeIndicated = SDO_C->sizeInd; + } + if (sizeTransferred != NULL) { + *sizeTransferred = SDO_C->sizeTran; + } + if (SDOabortCode != NULL) { + *SDOabortCode = abortCode; + } + + return ret; +} + +size_t +CO_SDOclientUploadBufRead(CO_SDOclient_t* SDO_C, uint8_t* buf, size_t count) { + size_t ret = 0; + if ((SDO_C != NULL) && (buf != NULL)) { + ret = CO_fifo_read(&SDO_C->bufFifo, buf, count, NULL); + } + return ret; +} + +void +CO_SDOclientClose(CO_SDOclient_t* SDO_C) { + if (SDO_C != NULL) { + SDO_C->state = CO_SDO_ST_IDLE; + } +} + +#endif /* (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_ENABLE */ diff --git a/301/CO_SDOclient.h b/301/CO_SDOclient.h new file mode 100644 index 00000000..c12f6c24 --- /dev/null +++ b/301/CO_SDOclient.h @@ -0,0 +1,427 @@ +/** + * CANopen Service Data Object - client protocol. + * + * @file CO_SDOclient.h + * @ingroup CO_SDOclient + * @author Janez Paternoster + * @author Matej Severkar + * @copyright 2020 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#ifndef CO_SDO_CLIENT_H +#define CO_SDO_CLIENT_H + +#include "301/CO_driver.h" +#include "301/CO_ODinterface.h" +#include "301/CO_SDOserver.h" +#include "301/CO_fifo.h" + +/* default configuration, see CO_config.h */ +#ifndef CO_CONFIG_SDO_CLI +#define CO_CONFIG_SDO_CLI (0) +#endif + +#ifndef CO_DOXYGEN +#ifndef CO_CONFIG_SDO_CLI_BUFFER_SIZE +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) != 0 +#define CO_CONFIG_SDO_CLI_BUFFER_SIZE 1000U +#else +#define CO_CONFIG_SDO_CLI_BUFFER_SIZE 32U +#endif +#endif +#endif + +#if (((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_ENABLE) != 0) || defined CO_DOXYGEN + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup CO_SDOclient SDO client + * CANopen Service Data Object - client protocol. + * + * @ingroup CO_CANopen_301 + * @{ + * SDO client is able to access Object Dictionary variables from remote nodes. Usually there is one SDO client on + * CANopen network, which is able to configure other CANopen nodes. It is also possible to establish individual SDO + * client-server communication channels between devices. + * + * SDO client is used in CANopenNode from CO_gateway_ascii.c with default SDO CAN identifiers. There is quite advanced + * usage in non-blocking function. + * + * If enabled, SDO client is initialized in CANopen.c file with @ref CO_SDOclient_init() function. + * + * Basic usage: + * @code{.c} +CO_SDO_abortCode_t +read_SDO(CO_SDOclient_t* SDO_C, uint8_t nodeId, uint16_t index, uint8_t subIndex, uint8_t* buf, size_t bufSize, + size_t* readSize) { + CO_SDO_return_t SDO_ret; + + // setup client (this can be skipped, if remote device don't change) + SDO_ret = CO_SDOclient_setup(SDO_C, CO_CAN_ID_SDO_CLI + nodeId, CO_CAN_ID_SDO_SRV + nodeId, nodeId); + if (SDO_ret != CO_SDO_RT_ok_communicationEnd) { + return CO_SDO_AB_GENERAL; + } + + // initiate upload + SDO_ret = CO_SDOclientUploadInitiate(SDO_C, index, subIndex, 1000, false); + if (SDO_ret != CO_SDO_RT_ok_communicationEnd) { + return CO_SDO_AB_GENERAL; + } + + // upload data + do { + uint32_t timeDifference_us = 10000; + CO_SDO_abortCode_t abortCode = CO_SDO_AB_NONE; + + SDO_ret = CO_SDOclientUpload(SDO_C, timeDifference_us, false, &abortCode, NULL, NULL, NULL); + if (SDO_ret < 0) { + return abortCode; + } + + sleep_us(timeDifference_us); + } while (SDO_ret > 0); + + // copy data to the user buffer (for long data function must be called several times inside the loop) + *readSize = CO_SDOclientUploadBufRead(SDO_C, buf, bufSize); + + return CO_SDO_AB_NONE; +} + +CO_SDO_abortCode_t +write_SDO(CO_SDOclient_t* SDO_C, uint8_t nodeId, uint16_t index, uint8_t subIndex, uint8_t* data, size_t dataSize) { + CO_SDO_return_t SDO_ret; + bool_t bufferPartial = false; + + // setup client (this can be skipped, if remote device is the same) + SDO_ret = CO_SDOclient_setup(SDO_C, CO_CAN_ID_SDO_CLI + nodeId, CO_CAN_ID_SDO_SRV + nodeId, nodeId); + if (SDO_ret != CO_SDO_RT_ok_communicationEnd) { + return -1 + } + + // initiate download + SDO_ret = CO_SDOclientDownloadInitiate(SDO_C, index, subIndex, dataSize, 1000, false); + if (SDO_ret != CO_SDO_RT_ok_communicationEnd) { + return -1 + } + + // fill data + size_t nWritten = CO_SDOclientDownloadBufWrite(SDO_C, data, dataSize); + if (nWritten < dataSize) { + bufferPartial = true; + // If SDO Fifo buffer is too small, data can be refilled in the loop. + } + + // download data + do { + uint32_t timeDifference_us = 10000; + CO_SDO_abortCode_t abortCode = CO_SDO_AB_NONE; + + SDO_ret = CO_SDOclientDownload(SDO_C, timeDifference_us, false, bufferPartial, &abortCode, NULL, NULL); + if (SDO_ret < 0) { + return abortCode; + } + + sleep_us(timeDifference_us); + } while (SDO_ret > 0); + + return CO_SDO_AB_NONE; +} + * @endcode + * + * @see @ref CO_SDOserver + */ + +/** + * SDO client object + */ +typedef struct { +#if (((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_LOCAL) != 0) || defined CO_DOXYGEN + OD_t* OD; /**< From CO_SDOclient_init() */ + uint8_t nodeId; /**< From CO_SDOclient_init() */ + OD_IO_t OD_IO; /**< Object dictionary interface for locally transferred object */ +#endif + CO_CANmodule_t* CANdevRx; /**< From CO_SDOclient_init() */ + uint16_t CANdevRxIdx; /**< From CO_SDOclient_init() */ + CO_CANmodule_t* CANdevTx; /**< From CO_SDOclient_init() */ + uint16_t CANdevTxIdx; /**< From CO_SDOclient_init() */ + CO_CANtx_t* CANtxBuff; /**< CAN transmit buffer inside CANdevTx for CAN tx message */ +#if (((CO_CONFIG_SDO_CLI)&CO_CONFIG_FLAG_OD_DYNAMIC) != 0) || defined CO_DOXYGEN + + uint32_t COB_IDClientToServer; /**< Copy of CANopen COB_ID Client -> Server, meaning of the specific bits: + - Bit 0...10: 11-bit CAN identifier. + - Bit 11..30: reserved, must be 0. + - Bit 31: if 1, SDO client object is not used. */ + uint32_t COB_IDServerToClient; /**< Copy of CANopen COB_ID Server -> Client, similar as above */ + OD_extension_t OD_1280_extension; /**< Extension for OD object */ +#endif + uint8_t nodeIDOfTheSDOServer; /**< Node-ID of the SDO server */ + bool_t valid; /**< If true, SDO channel is valid */ + uint16_t index; /**< Index of current object in Object Dictionary */ + uint8_t subIndex; /**< Subindex of current object in Object Dictionary */ + bool_t finished; /**< If true, then data transfer is finished */ + size_t sizeInd; /**< Size of data, which will be transferred. It is optionally indicated by client + in case of download or by server in case of upload. */ + size_t sizeTran; /**< Size of data which is actually transferred. */ + volatile CO_SDO_state_t state; /**< Internal state of the SDO client */ + uint32_t SDOtimeoutTime_us; /**< Maximum timeout time between request and response in microseconds */ + uint32_t timeoutTimer; /**< Timeout timer for SDO communication */ + CO_fifo_t bufFifo; /**< CO_fifo_t object for data buffer (not pointer) */ + uint8_t buf[CO_CONFIG_SDO_CLI_BUFFER_SIZE + 1U]; /**< Data buffer of usable size @ref CO_CONFIG_SDO_CLI_BUFFER_SIZE, + used inside bufFifo. Must be one byte larger for fifo usage. */ + volatile void* CANrxNew; /**< Indicates, if new SDO message received from CAN bus. It is not cleared, until received + message is completely processed. */ + uint8_t CANrxData[8]; /**< 8 data bytes of the received message */ +#if (((CO_CONFIG_SDO_CLI)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0) || defined CO_DOXYGEN + void (*pFunctSignal)(void* object); /**< From CO_SDOclient_initCallbackPre() or NULL */ + void* functSignalObject; /**< From CO_SDOclient_initCallbackPre() or NULL */ +#endif +#if (((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_SEGMENTED) != 0) || defined CO_DOXYGEN + uint8_t toggle; /**< Toggle bit toggled in each segment in segmented transfer */ +#endif +#if (((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) != 0) || defined CO_DOXYGEN + uint32_t block_SDOtimeoutTime_us; /**< Timeout time for SDO sub-block upload, half of #SDOtimeoutTime_us */ + uint32_t block_timeoutTimer; /**< Timeout timer for SDO sub-block upload */ + uint8_t block_seqno; /**< Sequence number of segment in block, 1..127 */ + uint8_t block_blksize; /**< Number of segments per block, 1..127 */ + uint8_t block_noData; /**< Number of bytes in last segment that do not contain data */ + bool_t block_crcEnabled; /**< Server CRC support in block transfer */ + uint8_t block_dataUploadLast[7]; /**< Last 7 bytes of data at block upload */ + uint16_t block_crc; /**< Calculated CRC checksum */ +#endif +} CO_SDOclient_t; + +/** + * Initialize SDO client object. + * + * Function must be called in the communication reset section. + * + * @param SDO_C This object will be initialized. + * @param OD Object Dictionary. It is used in case, if client is accessing object dictionary from its own device. If + * NULL, it will be ignored. + * @param OD_1280_SDOcliPar OD entry for SDO client parameter (0x1280+). It may have IO extension enabled to allow + * dynamic configuration (see also + * @ref CO_CONFIG_FLAG_OD_DYNAMIC). Entry is required. + * @param nodeId CANopen Node ID of this device. It is used in case, if client is accessing object dictionary from its + * own device. If 0, it will be ignored. + * @param CANdevRx CAN device for SDO client reception. + * @param CANdevRxIdx Index of receive buffer in the above CAN device. + * @param CANdevTx CAN device for SDO client transmission. + * @param CANdevTxIdx Index of transmit buffer in the above CAN device. + * @param [out] errInfo Additional information in case of error, may be NULL. + * + * @return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT. + */ +CO_ReturnError_t CO_SDOclient_init(CO_SDOclient_t* SDO_C, OD_t* OD, OD_entry_t* OD_1280_SDOcliPar, uint8_t nodeId, + CO_CANmodule_t* CANdevRx, uint16_t CANdevRxIdx, CO_CANmodule_t* CANdevTx, + uint16_t CANdevTxIdx, uint32_t* errInfo); + +#if (((CO_CONFIG_SDO_CLI)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0) || defined CO_DOXYGEN +/** + * Initialize SDOclient callback function. + * + * Function initializes optional callback function, which should immediately start processing of CO_SDOclientDownload() + * or CO_SDOclientUpload() function. Callback is called after SDOclient message is received from the CAN bus or when new + * call without delay is necessary (exchange data with own SDO server or SDO block transfer is in progress). + * + * @param SDOclient This object. + * @param object Pointer to object, which will be passed to pFunctSignal(). Can be NULL. + * @param pFunctSignal Pointer to the callback function. Not called if NULL. + */ +void CO_SDOclient_initCallbackPre(CO_SDOclient_t* SDOclient, void* object, void (*pFunctSignal)(void* object)); +#endif + +/** + * Setup SDO client object. + * + * Function is called in from CO_SDOclient_init() and each time when "SDO client parameter" is written. Application can + * call this function before new SDO communication. If parameters to this function are the same as before, then CAN is + * not reconfigured. + * + * @param SDO_C This object. + * @param COB_IDClientToServer See @ref CO_SDOclient_t. + * @param COB_IDServerToClient See @ref CO_SDOclient_t. + * @param nodeIDOfTheSDOServer Node-ID of the SDO server. If it is the same as node-ID of this node, then data will be + * exchanged with this node (without CAN communication). + * + * @return #CO_SDO_return_t, CO_SDO_RT_ok_communicationEnd or CO_SDO_RT_wrongArguments + */ +CO_SDO_return_t CO_SDOclient_setup(CO_SDOclient_t* SDO_C, uint32_t COB_IDClientToServer, uint32_t COB_IDServerToClient, + uint8_t nodeIDOfTheSDOServer); + +/** + * Initiate SDO download communication. + * + * Function initiates SDO download communication with server specified in CO_SDOclient_init() function. Data will be + * written to remote node. Function is non-blocking. + * + * @param SDO_C This object. + * @param index Index of object in object dictionary in remote node. + * @param subIndex Subindex of object in object dictionary in remote node. + * @param sizeIndicated Optionally indicate size of data to be downloaded. Actual data are written with one or multiple + * CO_SDOclientDownloadBufWrite() calls. + * - If sizeIndicated is different than 0, then total number of data written by CO_SDOclientDownloadBufWrite() will be + * compared against sizeIndicated. Also sizeIndicated info will be passed to the server, which will compare actual + * data size downloaded. In case of mismatch, SDO abort message will be generated. + * - If sizeIndicated is 0, then actual data size will not be verified. + * @param SDOtimeoutTime_ms Timeout time for SDO communication in milliseconds. + * @param blockEnable Try to initiate block transfer. + * + * @return #CO_SDO_return_t + */ +CO_SDO_return_t CO_SDOclientDownloadInitiate(CO_SDOclient_t* SDO_C, uint16_t index, uint8_t subIndex, + size_t sizeIndicated, uint16_t SDOtimeoutTime_ms, bool_t blockEnable); + +/** + * Initiate SDO download communication - update size. + * + * This is optional function, which updates sizeIndicated, if it was not known in the CO_SDOclientDownloadInitiate() + * function call. This function can be used after CO_SDOclientDownloadBufWrite(), but must be used before + * CO_SDOclientDownload(). + * + * @param SDO_C This object. + * @param sizeIndicated Same as in CO_SDOclientDownloadInitiate(). + */ +void CO_SDOclientDownloadInitSize(CO_SDOclient_t* SDO_C, size_t sizeIndicated); + +/** + * Write data into SDO client buffer + * + * This function copies data from buf into internal SDO client fifo buffer. Function returns number of bytes + * successfully copied. If there is not enough space in destination, not all bytes will be copied. Additional data can + * be copied in next cycles. If there is enough space in destination and sizeIndicated is different than zero, then all + * data must be written at once. + * + * This function is basically a wrapper for CO_fifo_write() function. As alternative, other functions from CO_fifo can + * be used directly, for example CO_fifo_cpyTok2U8() or similar. + * + * @param SDO_C This object. + * @param buf Buffer which will be copied + * @param count Number of bytes in buf + * + * @return number of bytes actually written. + */ +size_t CO_SDOclientDownloadBufWrite(CO_SDOclient_t* SDO_C, const uint8_t* buf, size_t count); + +/** + * Process SDO download communication. + * + * Function must be called cyclically until it returns <=0. It Proceeds SDO download communication initiated with + * CO_SDOclientDownloadInitiate(). Function is non-blocking. + * + * If function returns #CO_SDO_RT_blockDownldInProgress and OS has buffer for CAN tx messages, then this function may be + * called multiple times within own loop. This can speed-up SDO block transfer. + * + * @param SDO_C This object. + * @param timeDifference_us Time difference from previous function call in [microseconds]. + * @param send_abort If true, SDO client will send abort message from SDOabortCode and transmission will be aborted. + * @param bufferPartial True indicates, not all data were copied to internal buffer yet. Buffer will be refilled later + * with #CO_SDOclientDownloadBufWrite. + * @param [out] SDOabortCode In case of error in communication, SDO abort code contains reason of error. Ignored if + * NULL. + * @param [out] sizeTransferred Actual size of data transferred. Ignored if NULL + * @param [out] timerNext_us info to OS - see CO_process(). Ignored if NULL. + * + * @return #CO_SDO_return_t. If less than 0, then error occurred, SDOabortCode contains reason and state becomes idle. + * If 0, communication ends successfully and state becomes idle. If greater than 0, then communication is in progress. + */ +CO_SDO_return_t CO_SDOclientDownload(CO_SDOclient_t* SDO_C, uint32_t timeDifference_us, bool_t send_abort, + bool_t bufferPartial, CO_SDO_abortCode_t* SDOabortCode, size_t* sizeTransferred, + uint32_t* timerNext_us); + +/** + * Initiate SDO upload communication. + * + * Function initiates SDO upload communication with server specified in CO_SDOclient_init() function. Data will be read + * from remote node. Function is non-blocking. + * + * @param SDO_C This object. + * @param index Index of object in object dictionary in remote node. + * @param subIndex Subindex of object in object dictionary in remote node. + * @param SDOtimeoutTime_ms Timeout time for SDO communication in milliseconds. + * @param blockEnable Try to initiate block transfer. + * + * @return #CO_SDO_return_t + */ +CO_SDO_return_t CO_SDOclientUploadInitiate(CO_SDOclient_t* SDO_C, uint16_t index, uint8_t subIndex, + uint16_t SDOtimeoutTime_ms, bool_t blockEnable); + +/** + * Process SDO upload communication. + * + * Function must be called cyclically until it returns <=0. It Proceeds SDO upload communication initiated with + * CO_SDOclientUploadInitiate(). Function is non-blocking. + * + * If this function returns #CO_SDO_RT_uploadDataBufferFull, then data must be read from fifo buffer to make it empty. + * This function can then be called once again immediately to speed-up block transfer. Note also, that remaining data + * must be read after function returns #CO_SDO_RT_ok_communicationEnd. Data must not be read, if function returns + * #CO_SDO_RT_blockUploadInProgress. + * + * @param SDO_C This object. + * @param timeDifference_us Time difference from previous function call in [microseconds]. + * @param send_abort If true, SDO client will send abort message from SDOabortCode and reception will be aborted. + * @param [out] SDOabortCode In case of error in communication, SDO abort code contains reason of error. Ignored if + * NULL. + * @param [out] sizeIndicated If larger than 0, then SDO server has indicated size of data transfer. Ignored if NULL. + * @param [out] sizeTransferred Actual size of data transferred. Ignored if NULL + * @param [out] timerNext_us info to OS - see CO_process(). Ignored if NULL. + * + * @return #CO_SDO_return_t. If less than 0, then error occurred, SDOabortCode contains reason and state becomes idle. + * If 0, communication ends successfully and state becomes idle. If greater than 0, then communication is in progress. + */ +CO_SDO_return_t CO_SDOclientUpload(CO_SDOclient_t* SDO_C, uint32_t timeDifference_us, bool_t send_abort, + CO_SDO_abortCode_t* SDOabortCode, size_t* sizeIndicated, size_t* sizeTransferred, + uint32_t* timerNext_us); + +/** + * Read data from SDO client buffer. + * + * This function copies data from internal fifo buffer of SDO client into buf. Function returns number of bytes + * successfully copied. It can be called in multiple cycles, if data length is large. + * + * This function is basically a wrapper for CO_fifo_read() function. As alternative, other functions from CO_fifo can be + * used directly, for example CO_fifo_readU82a() or similar. + * + * @warning This function (or similar) must NOT be called when CO_SDOclientUpload() returns + * #CO_SDO_RT_blockUploadInProgress! + * + * @param SDO_C This object. + * @param buf Buffer into which data will be copied + * @param count Copy up to count bytes into buffer + * + * @return number of bytes actually read. + */ +size_t CO_SDOclientUploadBufRead(CO_SDOclient_t* SDO_C, uint8_t* buf, size_t count); + +/** + * Close SDO communication temporary. + * + * Function must be called after finish of each SDO client communication cycle. It disables reception of SDO client CAN + * messages. It is necessary, because CO_SDOclient_receive function may otherwise write into undefined SDO buffer. + * + * @param SDO_C This object. + */ +void CO_SDOclientClose(CO_SDOclient_t* SDO_C); + +/** @} */ /* CO_SDOclient */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_ENABLE */ + +#endif /* CO_SDO_CLIENT_H */ diff --git a/301/CO_SDOserver.c b/301/CO_SDOserver.c new file mode 100644 index 00000000..570bdb2f --- /dev/null +++ b/301/CO_SDOserver.c @@ -0,0 +1,1520 @@ +/* + * CANopen Service Data Object - server. + * + * @file CO_SDOserver.c + * @ingroup CO_SDOserver + * @author Janez Paternoster + * @copyright 2020 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#include + +#include "301/CO_SDOserver.h" +#include "301/CO_crc16-ccitt.h" + +/* verify configuration */ +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_SDO_SRV_SEGMENTED) != 0 +#if CO_CONFIG_SDO_SRV_BUFFER_SIZE < 20 +#error CO_CONFIG_SDO_SRV_BUFFER_SIZE must be greater or equal than 20. +#endif +#endif +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_SDO_SRV_BLOCK) != 0 +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_SDO_SRV_SEGMENTED) == 0 +#error CO_CONFIG_SDO_SRV_SEGMENTED must be enabled. +#endif +#if ((CO_CONFIG_CRC16)&CO_CONFIG_CRC16_ENABLE) == 0 +#error CO_CONFIG_CRC16_ENABLE must be enabled. +#endif +#if CO_CONFIG_SDO_SRV_BUFFER_SIZE < 900 +#error CO_CONFIG_SDO_SRV_BUFFER_SIZE must be greater or equal than 900. +#endif +#endif + +/* + * Read received message from CAN module. + * + * Function will be called (by CAN receive interrupt) every time, when CAN message with correct identifier + * will be received. For more information and description of parameters see file CO_driver.h. + */ +static void +CO_SDO_receive(void* object, void* msg) { + CO_SDOserver_t* SDO = (CO_SDOserver_t*)object; + uint8_t DLC = CO_CANrxMsg_readDLC(msg); + const uint8_t* data = CO_CANrxMsg_readData(msg); + + /* ignore messages with wrong length */ + if (DLC == 8U) { + if (data[0] == 0x80U) { + /* abort from client, just make idle */ + SDO->state = CO_SDO_ST_IDLE; + } else if (CO_FLAG_READ(SDO->CANrxNew)) { + /* ignore message if previous message was not processed yet */ + } +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_SDO_SRV_BLOCK) != 0 + else if (SDO->state == CO_SDO_ST_UPLOAD_BLK_END_CRSP && data[0] == 0xA1) { + /* SDO block download successfully transferred, just make idle */ + SDO->state = CO_SDO_ST_IDLE; + } else if (SDO->state == CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_REQ) { + /* just in case, condition should always pass */ + if (SDO->bufOffsetWr <= (CO_CONFIG_SDO_SRV_BUFFER_SIZE - (7 + 2))) { + /* block download, copy data directly */ + CO_SDO_state_t state = CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_REQ; + uint8_t seqno = data[0] & 0x7F; + SDO->timeoutTimer = 0; + SDO->block_timeoutTimer = 0; + + /* verify if sequence number is correct */ + if (seqno <= SDO->block_blksize && seqno == (SDO->block_seqno + 1)) { + SDO->block_seqno = seqno; + + /* Copy data. There is always enough space in buffer, + * because block_blksize was calculated before */ + (void)memcpy(SDO->buf + SDO->bufOffsetWr, &data[1], 7); + SDO->bufOffsetWr += 7; + SDO->sizeTran += 7; + + /* is this the last segment? */ + if ((data[0] & 0x80) != 0) { + SDO->finished = true; + state = CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_RSP; + } else if (seqno == SDO->block_blksize) { + /* all segments in sub-block has been transferred */ + state = CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_RSP; + } + } + /* If message is duplicate or sequence didn't start yet, ignore it. Otherwise seqno is wrong, + * so break sub-block. Data after last good seqno will be re-transmitted. */ + else if (seqno != SDO->block_seqno && SDO->block_seqno != 0U) { + state = CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_RSP; +#ifdef CO_DEBUG_SDO_SERVER + char msg[80]; + sprintf(msg, "sub-block, rx WRONG: sequno=%02X, previous=%02X", seqno, SDO->block_seqno); + CO_DEBUG_SDO_SERVER(msg); +#endif + } +#ifdef CO_DEBUG_SDO_SERVER + else { + char msg[80]; + sprintf(msg, "sub-block, rx ignored: sequno=%02X, expected=%02X", seqno, SDO->block_seqno + 1); + CO_DEBUG_SDO_SERVER(msg); + } +#endif + + if (state != CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_REQ) { + /* SDO->state has changed, processing will continue in another thread. + * Make memory barrier here with CO_FLAG_CLEAR() call. */ + CO_FLAG_CLEAR(SDO->CANrxNew); + SDO->state = state; +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0 + /* Optional signal to RTOS, which can resume task, which handles SDO server processing. */ + if (SDO->pFunctSignalPre != NULL) { + SDO->pFunctSignalPre(SDO->functSignalObjectPre); + } +#endif + } + } + } else if (SDO->state == CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_RSP) { + /* ignore subsequent server messages, if response was requested */ + } +#endif /* (CO_CONFIG_SDO_SRV) & CO_CONFIG_SDO_SRV_BLOCK */ + else { + /* copy data and set 'new message' flag, data will be processed in CO_SDOserver_process() */ + (void)memcpy(SDO->CANrxData, data, DLC); + CO_FLAG_SET(SDO->CANrxNew); +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0 + /* Optional signal to RTOS, which can resume task, which handles SDO server processing. */ + if (SDO->pFunctSignalPre != NULL) { + SDO->pFunctSignalPre(SDO->functSignalObjectPre); + } +#endif + } + } +} + +/* helper for configuring CANrx and CANtx */ +static CO_ReturnError_t +CO_SDOserver_init_canRxTx(CO_SDOserver_t* SDO, CO_CANmodule_t* CANdevRx, uint16_t CANdevRxIdx, uint16_t CANdevTxIdx, + uint32_t COB_IDClientToServer, uint32_t COB_IDServerToClient) { +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_FLAG_OD_DYNAMIC) != 0 + /* proceed only, if parameters change */ + if ((COB_IDClientToServer == SDO->COB_IDClientToServer) && (COB_IDServerToClient == SDO->COB_IDServerToClient)) { + return CO_ERROR_NO; + } + /* store variables */ + SDO->COB_IDClientToServer = COB_IDClientToServer; + SDO->COB_IDServerToClient = COB_IDServerToClient; +#endif + + /* verify valid bit */ + uint16_t idC2S = ((COB_IDClientToServer & 0x80000000UL) == 0U) ? (uint16_t)COB_IDClientToServer : 0U; + uint16_t idS2C = ((COB_IDServerToClient & 0x80000000UL) == 0U) ? (uint16_t)COB_IDServerToClient : 0U; + if ((idC2S != 0U) && (idS2C != 0U)) { + SDO->valid = true; + } else { + idC2S = 0; + idS2C = 0; + SDO->valid = false; + } + + /* configure SDO server CAN reception */ + CO_ReturnError_t ret = CO_CANrxBufferInit(CANdevRx, CANdevRxIdx, idC2S, 0x7FF, false, (void*)SDO, CO_SDO_receive); + + /* configure SDO server CAN transmission */ + SDO->CANtxBuff = CO_CANtxBufferInit(SDO->CANdevTx, CANdevTxIdx, idS2C, false, 8, false); + + if (SDO->CANtxBuff == NULL) { + ret = CO_ERROR_ILLEGAL_ARGUMENT; + SDO->valid = false; + } + + return ret; +} + +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_FLAG_OD_DYNAMIC) != 0 +/* + * Custom function for writing OD object _SDO server parameter_, additional channels + * + * For more information see file CO_ODinterface.h, OD_IO_t. + */ +static ODR_t +OD_write_1201_additional(OD_stream_t* stream, const void* buf, OD_size_t count, OD_size_t* countWritten) { + /* "count" is already verified in *_init() function */ + if ((stream == NULL) || (buf == NULL) || (countWritten == NULL)) { + return ODR_DEV_INCOMPAT; + } + + CO_SDOserver_t* SDO = (CO_SDOserver_t*)stream->object; + + switch (stream->subIndex) { + case 0: /* Highest sub-index supported */ return ODR_READONLY; break; + + case 1: { /* COB-ID client -> server */ + uint32_t COB_ID = CO_getUint32(buf); + uint16_t CAN_ID = (uint16_t)(COB_ID & 0x7FFU); + uint16_t CAN_ID_cur = (uint16_t)(SDO->COB_IDClientToServer & 0x7FFU); + bool_t valid = (COB_ID & 0x80000000U) == 0U; + + /* SDO client must not be valid when changing COB_ID */ + if (((COB_ID & 0x3FFFF800U) != 0U) || ((valid && SDO->valid) && (CAN_ID != CAN_ID_cur)) + || (valid && CO_IS_RESTRICTED_CAN_ID(CAN_ID))) { + return ODR_INVALID_VALUE; + } + (void)CO_SDOserver_init_canRxTx(SDO, SDO->CANdevRx, SDO->CANdevRxIdx, SDO->CANdevTxIdx, COB_ID, + SDO->COB_IDServerToClient); + break; + } + + case 2: { /* COB-ID server -> client */ + uint32_t COB_ID = CO_getUint32(buf); + uint16_t CAN_ID = (uint16_t)(COB_ID & 0x7FFU); + uint16_t CAN_ID_cur = (uint16_t)(SDO->COB_IDServerToClient & 0x7FFU); + bool_t valid = (COB_ID & 0x80000000U) == 0U; + + /* SDO client must not be valid when changing COB_ID */ + if (((COB_ID & 0x3FFFF800U) != 0U) || (valid && (SDO->valid && (CAN_ID != CAN_ID_cur))) + || (valid && CO_IS_RESTRICTED_CAN_ID(CAN_ID))) { + return ODR_INVALID_VALUE; + } + (void)CO_SDOserver_init_canRxTx(SDO, SDO->CANdevRx, SDO->CANdevRxIdx, SDO->CANdevTxIdx, + SDO->COB_IDClientToServer, COB_ID); + break; + } + + case 3: { /* Node-ID of the SDO server */ + if (count != 1U) { + return ODR_TYPE_MISMATCH; + } + uint8_t nodeId = CO_getUint8(buf); + if ((nodeId < 1U) || (nodeId > 127U)) { + return ODR_INVALID_VALUE; + } + break; + } + + default: return ODR_SUB_NOT_EXIST; break; + } + + /* write value to the original location in the Object Dictionary */ + return OD_writeOriginal(stream, buf, count, countWritten); +} +#endif /* (CO_CONFIG_SDO_SRV) & CO_CONFIG_FLAG_OD_DYNAMIC */ + +CO_ReturnError_t +CO_SDOserver_init(CO_SDOserver_t* SDO, OD_t* OD, OD_entry_t* OD_1200_SDOsrvPar, uint8_t nodeId, + uint16_t SDOtimeoutTime_ms, CO_CANmodule_t* CANdevRx, uint16_t CANdevRxIdx, CO_CANmodule_t* CANdevTx, + uint16_t CANdevTxIdx, uint32_t* errInfo) { + /* verify arguments */ + if ((SDO == NULL) || (OD == NULL) || (CANdevRx == NULL) || (CANdevTx == NULL)) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + /* Configure object variables */ + SDO->OD = OD; + SDO->nodeId = nodeId; +#if (((CO_CONFIG_SDO_SRV)&CO_CONFIG_SDO_SRV_SEGMENTED)) != 0 + SDO->SDOtimeoutTime_us = (uint32_t)SDOtimeoutTime_ms * 1000U; +#endif +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_SDO_SRV_BLOCK) != 0 + SDO->block_SDOtimeoutTime_us = (uint32_t)SDOtimeoutTime_ms * 700; +#endif + SDO->state = CO_SDO_ST_IDLE; + +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0 + SDO->pFunctSignalPre = NULL; + SDO->functSignalObjectPre = NULL; +#endif + + /* configure CAN identifiers and SDO server parameters if available */ + uint16_t CanId_ClientToServer, CanId_ServerToClient; + + if (OD_1200_SDOsrvPar == NULL) { + /* configure default SDO channel */ + if ((nodeId < 1U) || (nodeId > 127U)) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + CanId_ClientToServer = CO_CAN_ID_SDO_CLI + nodeId; + CanId_ServerToClient = CO_CAN_ID_SDO_SRV + nodeId; + SDO->valid = true; + } else { + uint16_t OD_SDOsrvParIdx = OD_getIndex(OD_1200_SDOsrvPar); + + if (OD_SDOsrvParIdx == (uint16_t)OD_H1200_SDO_SERVER_1_PARAM) { + /* configure default SDO channel and SDO server parameters for it */ + if ((nodeId < 1U) || (nodeId > 127U)) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + CanId_ClientToServer = CO_CAN_ID_SDO_CLI + nodeId; + CanId_ServerToClient = CO_CAN_ID_SDO_SRV + nodeId; + SDO->valid = true; + + (void)OD_set_u32(OD_1200_SDOsrvPar, 1, CanId_ClientToServer, true); + (void)OD_set_u32(OD_1200_SDOsrvPar, 2, CanId_ServerToClient, true); + } else if ((OD_SDOsrvParIdx > (uint16_t)OD_H1200_SDO_SERVER_1_PARAM) + && (OD_SDOsrvParIdx <= ((uint16_t)OD_H1200_SDO_SERVER_1_PARAM + 0x7FU))) { + /* configure additional SDO channel and SDO server parameters for it */ + uint8_t maxSubIndex; + uint32_t COB_IDClientToServer32, COB_IDServerToClient32; + + /* get and verify parameters from Object Dictionary (initial values) */ + ODR_t odRet0 = OD_get_u8(OD_1200_SDOsrvPar, 0, &maxSubIndex, true); + ODR_t odRet1 = OD_get_u32(OD_1200_SDOsrvPar, 1, &COB_IDClientToServer32, true); + ODR_t odRet2 = OD_get_u32(OD_1200_SDOsrvPar, 2, &COB_IDServerToClient32, true); + + if ((odRet0 != ODR_OK) || ((maxSubIndex != 2U) && (maxSubIndex != 3U)) || (odRet1 != ODR_OK) + || (odRet2 != ODR_OK)) { + if (errInfo != NULL) { + *errInfo = OD_SDOsrvParIdx; + } + return CO_ERROR_OD_PARAMETERS; + } + + CanId_ClientToServer = ((COB_IDClientToServer32 & 0x80000000U) == 0U) + ? (uint16_t)(COB_IDClientToServer32 & 0x7FFU) + : 0U; + CanId_ServerToClient = ((COB_IDServerToClient32 & 0x80000000U) == 0U) + ? (uint16_t)(COB_IDServerToClient32 & 0x7FFU) + : 0U; + +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_FLAG_OD_DYNAMIC) != 0 + SDO->OD_1200_extension.object = SDO; + SDO->OD_1200_extension.read = OD_readOriginal; + SDO->OD_1200_extension.write = OD_write_1201_additional; + ODR_t odRetE = OD_extension_init(OD_1200_SDOsrvPar, &SDO->OD_1200_extension); + if (odRetE != ODR_OK) { + if (errInfo != NULL) { + *errInfo = OD_SDOsrvParIdx; + } + return CO_ERROR_OD_PARAMETERS; + } +#endif + } else { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + } + CO_FLAG_CLEAR(SDO->CANrxNew); + + /* store the parameters and configure CANrx and CANtx */ +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_FLAG_OD_DYNAMIC) != 0 + SDO->CANdevRx = CANdevRx; + SDO->CANdevRxIdx = CANdevRxIdx; + SDO->CANdevTxIdx = CANdevTxIdx; + /* set to zero to make sure CO_SDOserver_init_canRxTx() will reconfig CAN */ + SDO->COB_IDClientToServer = 0; + SDO->COB_IDServerToClient = 0; +#endif + SDO->CANdevTx = CANdevTx; + + return CO_SDOserver_init_canRxTx(SDO, CANdevRx, CANdevRxIdx, CANdevTxIdx, CanId_ClientToServer, + CanId_ServerToClient); +} + +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0 +void +CO_SDOserver_initCallbackPre(CO_SDOserver_t* SDO, void* object, void (*pFunctSignalPre)(void* object)) { + if (SDO != NULL) { + SDO->functSignalObjectPre = object; + SDO->pFunctSignalPre = pFunctSignalPre; + } +} +#endif + +#ifdef CO_BIG_ENDIAN +static inline void +reverseBytes(void* start, OD_size_t size) { + uint8_t* lo = (uint8_t*)start; + uint8_t* hi = (uint8_t*)start + size - 1; + while (lo < hi) { + uint8_t swap = *lo; + *lo++ = *hi; + *hi-- = swap; + } +} +#endif + +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_SDO_SRV_SEGMENTED) != 0 +/* Helper function for writing data to Object dictionary. Function swaps data if necessary, + * calcualtes (and verifies CRC) writes data to OD and verifies data lengths. + * + * @param SDO SDO server + * @param [out] abortCode SDO abort code in case of error + * @param crcOperation 0=none, 1=calculate, 2=calculate and compare + * @parma crcClient crc checksum to campare with + * + * Returns true on success, otherwise write also abortCode and sets state to CO_SDO_ST_ABORT */ +static bool_t +validateAndWriteToOD(CO_SDOserver_t* SDO, CO_SDO_abortCode_t* abortCode, uint8_t crcOperation, uint16_t crcClient) { + OD_size_t bufOffsetWrOrig = SDO->bufOffsetWr; + + if (SDO->finished) { + /* Verify if size of data downloaded matches size indicated. */ + if ((SDO->sizeInd > 0U) && (SDO->sizeTran != SDO->sizeInd)) { + *abortCode = (SDO->sizeTran > SDO->sizeInd) ? CO_SDO_AB_DATA_LONG : CO_SDO_AB_DATA_SHORT; + SDO->state = CO_SDO_ST_ABORT; + return false; + } + +#ifdef CO_BIG_ENDIAN + /* swap int16_t .. uint64_t data if necessary */ + if ((SDO->OD_IO.stream.attribute & ODA_MB) != 0) { + reverseBytes(SDO->buf, SDO->bufOffsetWr); + } +#endif + + OD_size_t sizeInOd = SDO->OD_IO.stream.dataLength; + + /* If dataType is string, then size of data downloaded may be shorter than size of the + * OD data buffer. If so, add two zero bytes to terminate (unicode) string. Shorten + * also OD data size, (temporary, send information about EOF into OD_IO.write) */ + if (((SDO->OD_IO.stream.attribute & (OD_attr_t)ODA_STR) != 0U) + && ((sizeInOd == 0U) || (SDO->sizeTran < sizeInOd)) + && ((SDO->bufOffsetWr + 2U) <= CO_CONFIG_SDO_SRV_BUFFER_SIZE)) { + SDO->buf[SDO->bufOffsetWr] = 0; + SDO->bufOffsetWr++; + SDO->sizeTran++; + if ((sizeInOd == 0U) || (SDO->sizeTran < sizeInOd)) { + SDO->buf[SDO->bufOffsetWr] = 0; + SDO->bufOffsetWr++; + SDO->sizeTran++; + } + SDO->OD_IO.stream.dataLength = SDO->sizeTran; + } + /* Indicate OD data size, if not indicated. Can be used for EOF check. */ + else if (sizeInOd == 0U) { + SDO->OD_IO.stream.dataLength = SDO->sizeTran; + } + /* Verify if size of data downloaded matches data size in OD. */ + else if (SDO->sizeTran != sizeInOd) { + *abortCode = (SDO->sizeTran > sizeInOd) ? CO_SDO_AB_DATA_LONG : CO_SDO_AB_DATA_SHORT; + SDO->state = CO_SDO_ST_ABORT; + return false; + } else { /* MISRA C 2004 14.10 */ + } + } else { + /* Verify if size of data downloaded is not too large. */ + if ((SDO->sizeInd > 0U) && (SDO->sizeTran > SDO->sizeInd)) { + *abortCode = CO_SDO_AB_DATA_LONG; + SDO->state = CO_SDO_ST_ABORT; + return false; + } + } + +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_SDO_SRV_BLOCK) != 0 + /* calculate crc on current data */ + if (SDO->block_crcEnabled && crcOperation > 0) { + SDO->block_crc = CO_crc16_ccitt(SDO->buf, bufOffsetWrOrig, SDO->block_crc); + if (crcOperation == 2 && crcClient != SDO->block_crc) { + *abortCode = CO_SDO_AB_CRC; + SDO->state = CO_SDO_ST_ABORT; + return false; + } + } +#endif + /* may be unused */ + (void)crcOperation; + (void)crcClient; + (void)bufOffsetWrOrig; + + /* write data */ + OD_size_t countWritten = 0; + ODR_t odRet; + + CO_LOCK_OD(SDO->CANdevTx); + odRet = SDO->OD_IO.write(&SDO->OD_IO.stream, SDO->buf, SDO->bufOffsetWr, &countWritten); + CO_UNLOCK_OD(SDO->CANdevTx); + + SDO->bufOffsetWr = 0; + + /* verify write error value */ + if ((odRet != ODR_OK) && (odRet != ODR_PARTIAL)) { + *abortCode = (CO_SDO_abortCode_t)OD_getSDOabCode(odRet); + SDO->state = CO_SDO_ST_ABORT; + return false; + } else if (SDO->finished && (odRet == ODR_PARTIAL)) { + /* OD variable was not written completely, but SDO download finished */ + *abortCode = CO_SDO_AB_DATA_SHORT; + SDO->state = CO_SDO_ST_ABORT; + return false; + } else if (!SDO->finished && (odRet == ODR_OK)) { + /* OD variable was written completely, but SDO download still has data */ + *abortCode = CO_SDO_AB_DATA_LONG; + SDO->state = CO_SDO_ST_ABORT; + return false; + } else { /* MISRA C 2004 14.10 */ + } + + return true; +} + +/* Helper function for reading data from Object dictionary. Function also swaps data if necessary and calcualtes CRC. + * + * @param SDO SDO server + * @param [out] abortCode SDO abort code in case of error + * @parma countMinimum if data size in buffer is less than countMinimum, then buffer is refilled from OD variable + * @param calculateCrc if true, crc is calculated + * + * Returns true on success, otherwise write also abortCode and sets state to CO_SDO_ST_ABORT */ +static bool_t +readFromOd(CO_SDOserver_t* SDO, CO_SDO_abortCode_t* abortCode, OD_size_t countMinimum, bool_t calculateCrc) { +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_SDO_SRV_BLOCK) == 0 + (void)calculateCrc; /* may be unused */ +#endif + OD_size_t countRemain = SDO->bufOffsetWr - SDO->bufOffsetRd; + + if (!SDO->finished && (countRemain < countMinimum)) { + /* first move remaining data to the start of the buffer */ + (void)memmove(SDO->buf, SDO->buf + SDO->bufOffsetRd, countRemain); + SDO->bufOffsetRd = 0; + SDO->bufOffsetWr = countRemain; + + /* Get size of free data buffer */ + OD_size_t countRdRequest = CO_CONFIG_SDO_SRV_BUFFER_SIZE - countRemain; + + /* load data from OD variable into the buffer */ + OD_size_t countRd = 0; + ODR_t odRet; + + CO_LOCK_OD(SDO->CANdevTx); + odRet = SDO->OD_IO.read(&SDO->OD_IO.stream, &SDO->buf[countRemain], countRdRequest, &countRd); + CO_UNLOCK_OD(SDO->CANdevTx); + + if ((odRet != ODR_OK) && (odRet != ODR_PARTIAL)) { + *abortCode = (CO_SDO_abortCode_t)OD_getSDOabCode(odRet); + SDO->state = CO_SDO_ST_ABORT; + return false; + } + + /* if data is string, send only data up to null termination */ + OD_size_t lastRd = countRd + countRemain; + if ((countRd > 0U) && ((SDO->OD_IO.stream.attribute & (OD_attr_t)ODA_STR) != 0U)) { + SDO->buf[lastRd] = 0; /* (SDO->buf is one byte larger) */ + OD_size_t countStr = (OD_size_t)strlen((char*)&SDO->buf[countRemain]); + if (countStr == 0U) { + countStr = 1; + } /* zero length is not allowed */ + if (countStr < countRd) { + /* string terminator found, read is finished, shorten data */ + countRd = countStr; + odRet = ODR_OK; + SDO->OD_IO.stream.dataLength = SDO->sizeTran + countRd; + } + } + + /* partial or finished read */ + SDO->bufOffsetWr = countRemain + countRd; + if ((SDO->bufOffsetWr == 0U) || (odRet == ODR_PARTIAL)) { + SDO->finished = false; + if (SDO->bufOffsetWr < countMinimum) { + *abortCode = CO_SDO_AB_DEVICE_INCOMPAT; + SDO->state = CO_SDO_ST_ABORT; + return false; + } + } else { + SDO->finished = true; + } + +#ifdef CO_BIG_ENDIAN + /* swap data if necessary */ + if ((SDO->OD_IO.stream.attribute & ODA_MB) != 0) { + if (SDO->finished) { + /* int16_t .. uint64_t */ + reverseBytes(&SDO->buf[countRemain], countRd); + } else { + *abortCode = CO_SDO_AB_PRAM_INCOMPAT; + SDO->state = CO_SDO_ST_ABORT; + return false; + } + } +#endif + +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_SDO_SRV_BLOCK) != 0 + /* update the crc */ + if (calculateCrc && SDO->block_crcEnabled) { + SDO->block_crc = CO_crc16_ccitt(&SDO->buf[countRemain], countRd, SDO->block_crc); + } +#endif + } + return true; +} +#endif + +CO_SDO_return_t +CO_SDOserver_process(CO_SDOserver_t* SDO, bool_t NMTisPreOrOperational, uint32_t timeDifference_us, + uint32_t* timerNext_us) { + if (SDO == NULL) { + return CO_SDO_RT_wrongArguments; + } + + (void)timerNext_us; /* may be unused */ + + CO_SDO_return_t ret = CO_SDO_RT_waitingResponse; + CO_SDO_abortCode_t abortCode = CO_SDO_AB_NONE; + bool_t isNew = CO_FLAG_READ(SDO->CANrxNew); + + if ((SDO->state == CO_SDO_ST_IDLE) && SDO->valid && !isNew) { + /* Idle and nothing new */ + ret = CO_SDO_RT_ok_communicationEnd; + } else if (!NMTisPreOrOperational || !SDO->valid) { + /* SDO is allowed only in operational or pre-operational NMT state and must be valid */ + SDO->state = CO_SDO_ST_IDLE; + CO_FLAG_CLEAR(SDO->CANrxNew); + ret = CO_SDO_RT_ok_communicationEnd; + } + /* CAN data received ******************************************************/ + else if (isNew) { + if (SDO->state == CO_SDO_ST_IDLE) { /* new SDO communication? */ + bool_t upload = false; + + if ((SDO->CANrxData[0] & 0xF0U) == 0x20U) { + SDO->state = CO_SDO_ST_DOWNLOAD_INITIATE_REQ; + } else if (SDO->CANrxData[0] == 0x40U) { + upload = true; + SDO->state = CO_SDO_ST_UPLOAD_INITIATE_REQ; + } +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_SDO_SRV_BLOCK) != 0 + else if ((SDO->CANrxData[0] & 0xF9) == 0xC0) { + SDO->state = CO_SDO_ST_DOWNLOAD_BLK_INITIATE_REQ; + } else if ((SDO->CANrxData[0] & 0xFB) == 0xA0) { + upload = true; + SDO->state = CO_SDO_ST_UPLOAD_BLK_INITIATE_REQ; + } +#endif + else { + abortCode = CO_SDO_AB_CMD; + SDO->state = CO_SDO_ST_ABORT; + } + + /* if no error search object dictionary for new SDO request */ + if (abortCode == CO_SDO_AB_NONE) { + ODR_t odRet; + SDO->index = (uint16_t)((((uint16_t)SDO->CANrxData[2]) << 8) | SDO->CANrxData[1]); + SDO->subIndex = SDO->CANrxData[3]; + odRet = OD_getSub(OD_find(SDO->OD, SDO->index), SDO->subIndex, &SDO->OD_IO, false); + if (odRet != ODR_OK) { + abortCode = (CO_SDO_abortCode_t)OD_getSDOabCode(odRet); + SDO->state = CO_SDO_ST_ABORT; + } else { + /* verify read/write attributes */ + if ((SDO->OD_IO.stream.attribute & (OD_attr_t)ODA_SDO_RW) == 0U) { + abortCode = CO_SDO_AB_UNSUPPORTED_ACCESS; + SDO->state = CO_SDO_ST_ABORT; + } else if (upload && ((SDO->OD_IO.stream.attribute & (OD_attr_t)ODA_SDO_R) == 0U)) { + abortCode = CO_SDO_AB_WRITEONLY; + SDO->state = CO_SDO_ST_ABORT; + } else if (!upload && ((SDO->OD_IO.stream.attribute & (OD_attr_t)ODA_SDO_W) == 0U)) { + abortCode = CO_SDO_AB_READONLY; + SDO->state = CO_SDO_ST_ABORT; + } else { /* MISRA C 2004 14.10 */ + } + } + } + +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_SDO_SRV_SEGMENTED) != 0 + /* load data from object dictionary, if upload and no error */ + if (upload && (abortCode == CO_SDO_AB_NONE)) { + SDO->bufOffsetRd = 0; + SDO->bufOffsetWr = 0; + SDO->sizeTran = 0; + SDO->finished = false; + + if (readFromOd(SDO, &abortCode, 7, false)) { + /* Size of variable in OD (may not be known yet) */ + if (SDO->finished) { + /* OD variable was completely read, its size is known */ + + SDO->sizeInd = SDO->OD_IO.stream.dataLength; + + if (SDO->sizeInd == 0U) { + SDO->sizeInd = SDO->bufOffsetWr; + } else if (SDO->sizeInd != SDO->bufOffsetWr) { + abortCode = CO_SDO_AB_DEVICE_INCOMPAT; + SDO->state = CO_SDO_ST_ABORT; + } else { /* MISRA C 2004 14.10 */ + } + } else { + /* If data type is string, size is not known */ + SDO->sizeInd = ((SDO->OD_IO.stream.attribute & (OD_attr_t)ODA_STR) == 0U) + ? SDO->OD_IO.stream.dataLength + : 0U; + } + } + } +#endif /* (CO_CONFIG_SDO_SRV) & CO_CONFIG_SDO_SRV_SEGMENTED */ + } /* (SDO->state == CO_SDO_ST_IDLE) */ + + bool isOKstate = (SDO->state != CO_SDO_ST_IDLE); + isOKstate = (SDO->state != CO_SDO_ST_ABORT) && isOKstate; + if (isOKstate) { + switch (SDO->state) { + case CO_SDO_ST_DOWNLOAD_INITIATE_REQ: { + if ((SDO->CANrxData[0] & 0x02U) != 0U) { + /* Expedited transfer, max 4 bytes of data */ + + /* Size of OD variable (>0 if indicated) */ + OD_size_t sizeInOd = SDO->OD_IO.stream.dataLength; + + /* Get SDO data size (indicated by SDO client or get from OD) */ + OD_size_t dataSizeToWrite = 4; + if ((SDO->CANrxData[0] & 0x01U) != 0U) { + dataSizeToWrite -= ((OD_size_t)(SDO->CANrxData[0]) >> 2) & 0x03U; + } else if ((sizeInOd > 0U) && (sizeInOd < 4U)) { + dataSizeToWrite = sizeInOd; + } else { /* MISRA C 2004 14.10 */ + } + + /* copy data to the temp buffer, swap data if necessary */ + uint8_t buf[6] = {0}; + (void)memcpy(buf, &SDO->CANrxData[4], dataSizeToWrite); +#ifdef CO_BIG_ENDIAN + if ((SDO->OD_IO.stream.attribute & ODA_MB) != 0) { + reverseBytes(buf, dataSizeToWrite); + } +#endif + + /* If dataType is string, then size of data downloaded may be shorter as size of + * the OD data buffer. If so, add two zero bytes to terminate (unicode) string. + * Shorten also OD data size, (temporary, send information about EOF into OD_IO.write) */ + if (((SDO->OD_IO.stream.attribute & (OD_attr_t)ODA_STR) != 0U) + && ((sizeInOd == 0U) || (dataSizeToWrite < sizeInOd))) { + OD_size_t delta = sizeInOd - dataSizeToWrite; + dataSizeToWrite += (delta == 1U) ? 1U : 2U; + SDO->OD_IO.stream.dataLength = dataSizeToWrite; + } else if (sizeInOd == 0U) { + SDO->OD_IO.stream.dataLength = dataSizeToWrite; + } + /* Verify if size of data downloaded matches data size in OD. */ + else if (dataSizeToWrite != sizeInOd) { + abortCode = (dataSizeToWrite > sizeInOd) ? CO_SDO_AB_DATA_LONG : CO_SDO_AB_DATA_SHORT; + SDO->state = CO_SDO_ST_ABORT; + break; + } else { /* MISRA C 2004 14.10 */ + } + + /* Copy data */ + OD_size_t countWritten = 0; + ODR_t odRet; + + CO_LOCK_OD(SDO->CANdevTx); + odRet = SDO->OD_IO.write(&SDO->OD_IO.stream, buf, dataSizeToWrite, &countWritten); + CO_UNLOCK_OD(SDO->CANdevTx); + + if (odRet != ODR_OK) { + abortCode = (CO_SDO_abortCode_t)OD_getSDOabCode(odRet); + SDO->state = CO_SDO_ST_ABORT; + break; + } else { + SDO->state = CO_SDO_ST_DOWNLOAD_INITIATE_RSP; +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_SDO_SRV_SEGMENTED) != 0 + SDO->finished = true; +#endif + } + } else { +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_SDO_SRV_SEGMENTED) != 0 + /* segmented transfer, is size indicated? */ + if ((SDO->CANrxData[0] & 0x01U) != 0U) { + uint32_t size; + OD_size_t sizeInOd = SDO->OD_IO.stream.dataLength; + + (void)memcpy((void*)(&size), (const void*)(&SDO->CANrxData[4]), sizeof(size)); + SDO->sizeInd = CO_SWAP_32(size); + + /* Indicated size of SDO matches sizeof OD variable? */ + if (sizeInOd > 0U) { + if (SDO->sizeInd > sizeInOd) { + abortCode = CO_SDO_AB_DATA_LONG; + SDO->state = CO_SDO_ST_ABORT; + break; + } + /* strings are allowed to be shorter */ + else if ((SDO->sizeInd < sizeInOd) + && ((SDO->OD_IO.stream.attribute & (OD_attr_t)ODA_STR) == 0U)) { + abortCode = CO_SDO_AB_DATA_SHORT; + SDO->state = CO_SDO_ST_ABORT; + break; + } else { /* MISRA C 2004 14.10 */ + } + } + } else { + SDO->sizeInd = 0; + } + SDO->state = CO_SDO_ST_DOWNLOAD_INITIATE_RSP; + SDO->finished = false; +#else + abortCode = CO_SDO_AB_UNSUPPORTED_ACCESS; + SDO->state = CO_SDO_ST_ABORT; +#endif + } + break; + } + +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_SDO_SRV_SEGMENTED) != 0 + case CO_SDO_ST_DOWNLOAD_SEGMENT_REQ: { + if ((SDO->CANrxData[0] & 0xE0U) == 0x00U) { + SDO->finished = (SDO->CANrxData[0] & 0x01U) != 0U; + + /* verify and alternate toggle bit */ + uint8_t toggle = SDO->CANrxData[0] & 0x10U; + if (toggle != SDO->toggle) { + abortCode = CO_SDO_AB_TOGGLE_BIT; + SDO->state = CO_SDO_ST_ABORT; + break; + } + + /* get data size and write data to the buffer */ + OD_size_t count = (OD_size_t)(7U - (((OD_size_t)(SDO->CANrxData[0]) >> 1) & 0x07U)); + (void)memcpy(SDO->buf + SDO->bufOffsetWr, &SDO->CANrxData[1], count); + SDO->bufOffsetWr += count; + SDO->sizeTran += count; + + /* if data size exceeds variable size, abort */ + if ((SDO->OD_IO.stream.dataLength > 0U) && (SDO->sizeTran > SDO->OD_IO.stream.dataLength)) { + abortCode = CO_SDO_AB_DATA_LONG; + SDO->state = CO_SDO_ST_ABORT; + break; + } + + /* if necessary, empty the buffer */ + if (SDO->finished || ((CO_CONFIG_SDO_SRV_BUFFER_SIZE - SDO->bufOffsetWr) < (7U + 2U))) { + if (!validateAndWriteToOD(SDO, &abortCode, 0, 0)) { + break; + } + } + + SDO->state = CO_SDO_ST_DOWNLOAD_SEGMENT_RSP; + } else { + abortCode = CO_SDO_AB_CMD; + SDO->state = CO_SDO_ST_ABORT; + } + break; + } +#endif /* (CO_CONFIG_SDO_SRV) & CO_CONFIG_SDO_SRV_SEGMENTED */ + + case CO_SDO_ST_UPLOAD_INITIATE_REQ: { + SDO->state = CO_SDO_ST_UPLOAD_INITIATE_RSP; + break; + } + +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_SDO_SRV_SEGMENTED) != 0 + case CO_SDO_ST_UPLOAD_SEGMENT_REQ: { + if ((SDO->CANrxData[0] & 0xEFU) == 0x60U) { + /* verify and alternate toggle bit */ + uint8_t toggle = SDO->CANrxData[0] & 0x10U; + if (toggle != SDO->toggle) { + abortCode = CO_SDO_AB_TOGGLE_BIT; + SDO->state = CO_SDO_ST_ABORT; + break; + } + SDO->state = CO_SDO_ST_UPLOAD_SEGMENT_RSP; + } else { + abortCode = CO_SDO_AB_CMD; + SDO->state = CO_SDO_ST_ABORT; + } + break; + } +#endif /* (CO_CONFIG_SDO_SRV) & CO_CONFIG_SDO_SRV_SEGMENTED */ + +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_SDO_SRV_BLOCK) != 0 + case CO_SDO_ST_DOWNLOAD_BLK_INITIATE_REQ: { + SDO->block_crcEnabled = (SDO->CANrxData[0] & 0x04) != 0; + + /* is size indicated? */ + if ((SDO->CANrxData[0] & 0x02) != 0) { + uint32_t size; + OD_size_t sizeInOd = SDO->OD_IO.stream.dataLength; + + (void)memcpy(&size, &SDO->CANrxData[4], sizeof(size)); + SDO->sizeInd = CO_SWAP_32(size); + + /* Indicated size of SDO matches sizeof OD variable? */ + if (sizeInOd > 0) { + if (SDO->sizeInd > sizeInOd) { + abortCode = CO_SDO_AB_DATA_LONG; + SDO->state = CO_SDO_ST_ABORT; + break; + } + /* strings are allowed to be shorter */ + else if (SDO->sizeInd < sizeInOd && (SDO->OD_IO.stream.attribute & ODA_STR) == 0) { + abortCode = CO_SDO_AB_DATA_SHORT; + SDO->state = CO_SDO_ST_ABORT; + break; + } + } + } else { + SDO->sizeInd = 0; + } + SDO->state = CO_SDO_ST_DOWNLOAD_BLK_INITIATE_RSP; + SDO->finished = false; + break; + } + + case CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_REQ: { + /* data are copied directly in the receive function */ + break; + } + + case CO_SDO_ST_DOWNLOAD_BLK_END_REQ: { + if ((SDO->CANrxData[0] & 0xE3) == 0xC1) { + /* Get number of data bytes in last segment, that do not contain data. Then reduce buffer. */ + uint8_t noData = ((SDO->CANrxData[0] >> 2) & 0x07); + if (SDO->bufOffsetWr <= noData) { + /* just in case, should never happen */ + abortCode = CO_SDO_AB_DEVICE_INCOMPAT; + SDO->state = CO_SDO_ST_ABORT; + break; + } + SDO->sizeTran -= noData; + SDO->bufOffsetWr -= noData; + + uint16_t crcClient = 0; + if (SDO->block_crcEnabled) { + crcClient = ((uint16_t)SDO->CANrxData[2]) << 8; + crcClient |= SDO->CANrxData[1]; + } + + if (!validateAndWriteToOD(SDO, &abortCode, 2, crcClient)) { + break; + } + + SDO->state = CO_SDO_ST_DOWNLOAD_BLK_END_RSP; + } else { + abortCode = CO_SDO_AB_CMD; + SDO->state = CO_SDO_ST_ABORT; + } + break; + } + + case CO_SDO_ST_UPLOAD_BLK_INITIATE_REQ: { + /* if pst (protocol switch threshold, byte5) is larger than data + * size of OD variable, then switch to segmented transfer */ + if (SDO->sizeInd > 0 && SDO->CANrxData[5] > 0 && SDO->CANrxData[5] >= SDO->sizeInd) { + SDO->state = CO_SDO_ST_UPLOAD_INITIATE_RSP; + } else { + /* data were already loaded from OD variable, verify crc */ + if ((SDO->CANrxData[0] & 0x04) != 0) { + SDO->block_crcEnabled = true; + SDO->block_crc = CO_crc16_ccitt(SDO->buf, SDO->bufOffsetWr, 0); + } else { + SDO->block_crcEnabled = false; + } + + /* get blksize and verify it */ + SDO->block_blksize = SDO->CANrxData[4]; + if (SDO->block_blksize < 1 || SDO->block_blksize > 127) { + abortCode = CO_SDO_AB_BLOCK_SIZE; + SDO->state = CO_SDO_ST_ABORT; + break; + } + + /* verify, if there is enough data */ + if (!SDO->finished && SDO->bufOffsetWr < SDO->block_blksize * 7U) { + abortCode = CO_SDO_AB_DEVICE_INCOMPAT; + SDO->state = CO_SDO_ST_ABORT; + break; + } + SDO->state = CO_SDO_ST_UPLOAD_BLK_INITIATE_RSP; + } + break; + } + + case CO_SDO_ST_UPLOAD_BLK_INITIATE_REQ2: { + if (SDO->CANrxData[0] == 0xA3) { + SDO->block_seqno = 0; + SDO->state = CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ; + } else { + abortCode = CO_SDO_AB_CMD; + SDO->state = CO_SDO_ST_ABORT; + } + break; + } + + case CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ: + case CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_CRSP: { + if (SDO->CANrxData[0] == 0xA2) { + SDO->block_blksize = SDO->CANrxData[2]; + if (SDO->block_blksize < 1 || SDO->block_blksize > 127) { + abortCode = CO_SDO_AB_BLOCK_SIZE; + SDO->state = CO_SDO_ST_ABORT; + break; + } + + /* check number of segments */ + if (SDO->CANrxData[1] < SDO->block_seqno) { + /* NOT all segments transferred successfully. Re-transmit data after erroneous segment. */ + OD_size_t cntFailed = SDO->block_seqno - SDO->CANrxData[1]; + cntFailed = cntFailed * 7 - SDO->block_noData; + SDO->bufOffsetRd -= cntFailed; + SDO->sizeTran -= cntFailed; + } else if (SDO->CANrxData[1] > SDO->block_seqno) { + /* something strange from server, break transmission */ + abortCode = CO_SDO_AB_CMD; + SDO->state = CO_SDO_ST_ABORT; + break; + } + + /* refill data buffer if necessary */ + if (!readFromOd(SDO, &abortCode, SDO->block_blksize * 7, true)) { + break; + } + + if (SDO->bufOffsetWr == SDO->bufOffsetRd) { + SDO->state = CO_SDO_ST_UPLOAD_BLK_END_SREQ; + } else { + SDO->block_seqno = 0; + SDO->state = CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ; + } + } else { + abortCode = CO_SDO_AB_CMD; + SDO->state = CO_SDO_ST_ABORT; + } + break; + } +#endif /* (CO_CONFIG_SDO_SRV) & CO_CONFIG_SDO_SRV_BLOCK */ + + case CO_SDO_ST_IDLE: + case CO_SDO_ST_ABORT: + case CO_SDO_ST_DOWNLOAD_LOCAL_TRANSFER: + case CO_SDO_ST_DOWNLOAD_INITIATE_RSP: + case CO_SDO_ST_DOWNLOAD_SEGMENT_RSP: + case CO_SDO_ST_UPLOAD_LOCAL_TRANSFER: + case CO_SDO_ST_UPLOAD_INITIATE_RSP: + case CO_SDO_ST_UPLOAD_SEGMENT_RSP: + case CO_SDO_ST_DOWNLOAD_BLK_INITIATE_RSP: + case CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_RSP: + case CO_SDO_ST_DOWNLOAD_BLK_END_RSP: + case CO_SDO_ST_UPLOAD_BLK_INITIATE_RSP: + case CO_SDO_ST_UPLOAD_BLK_END_SREQ: + case CO_SDO_ST_UPLOAD_BLK_END_CRSP: + +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_SDO_SRV_SEGMENTED) == 0 + case CO_SDO_ST_DOWNLOAD_SEGMENT_REQ: + case CO_SDO_ST_UPLOAD_SEGMENT_REQ: +#endif /* (CO_CONFIG_SDO_SRV) & CO_CONFIG_SDO_SRV_SEGMENTED */ +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_SDO_SRV_BLOCK) == 0 + case CO_SDO_ST_DOWNLOAD_BLK_INITIATE_REQ: + case CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_REQ: + case CO_SDO_ST_DOWNLOAD_BLK_END_REQ: + case CO_SDO_ST_UPLOAD_BLK_INITIATE_REQ: + case CO_SDO_ST_UPLOAD_BLK_INITIATE_REQ2: + case CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ: + case CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_CRSP: +#endif /* (CO_CONFIG_SDO_SRV) & CO_CONFIG_SDO_SRV_BLOCK */ + default: { + /* unknown message received */ + abortCode = CO_SDO_AB_CMD; + SDO->state = CO_SDO_ST_ABORT; + break; + } + } /* switch (SDO->state) */ + } /* if (SDO->state != CO_SDO_ST_IDLE && SDO->state != CO_SDO_ST_ABORT) */ +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_SDO_SRV_SEGMENTED) != 0 + SDO->timeoutTimer = 0; +#endif + timeDifference_us = 0; + CO_FLAG_CLEAR(SDO->CANrxNew); + } /* else if (isNew) */ + else { /* MISRA C 2004 14.10 */ + } + + /* Timeout timers and transmit bufferFull flag ****************************/ +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_SDO_SRV_SEGMENTED) != 0 + if (ret == CO_SDO_RT_waitingResponse) { + if (SDO->timeoutTimer < SDO->SDOtimeoutTime_us) { + SDO->timeoutTimer += timeDifference_us; + } + if (SDO->timeoutTimer >= SDO->SDOtimeoutTime_us) { + abortCode = CO_SDO_AB_TIMEOUT; + SDO->state = CO_SDO_ST_ABORT; + } +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_FLAG_TIMERNEXT) != 0 + else if (timerNext_us != NULL) { + /* check again after timeout time elapsed */ + uint32_t diff = SDO->SDOtimeoutTime_us - SDO->timeoutTimer; + if (*timerNext_us > diff) { + *timerNext_us = diff; + } + } +#endif + +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_SDO_SRV_BLOCK) != 0 + /* Timeout for sub-block transmission */ + if (SDO->state == CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_REQ) { + if (SDO->block_timeoutTimer < SDO->block_SDOtimeoutTime_us) { + SDO->block_timeoutTimer += timeDifference_us; + } + if (SDO->block_timeoutTimer >= SDO->block_SDOtimeoutTime_us) { + /* SDO->state will change, processing will continue in this + * thread. Make memory barrier here with CO_FLAG_CLEAR() call. */ + SDO->state = CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_RSP; + CO_FLAG_CLEAR(SDO->CANrxNew); + } +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_FLAG_TIMERNEXT) != 0 + else if (timerNext_us != NULL) { + /* check again after timeout time elapsed */ + uint32_t diff = SDO->block_SDOtimeoutTime_us - SDO->block_timeoutTimer; + if (*timerNext_us > diff) { + *timerNext_us = diff; + } + } +#endif + } +#endif /* (CO_CONFIG_SDO_SRV) & CO_CONFIG_SDO_SRV_BLOCK */ + + if (SDO->CANtxBuff->bufferFull) { + ret = CO_SDO_RT_transmittBufferFull; + } + } +#endif /* (CO_CONFIG_SDO_SRV) & CO_CONFIG_SDO_SRV_SEGMENTED */ + + /* Transmit CAN data ******************************************************/ + if (ret == CO_SDO_RT_waitingResponse) { + /* clear response buffer */ + (void)memset(SDO->CANtxBuff->data, 0, sizeof(SDO->CANtxBuff->data)); + + switch (SDO->state) { + case CO_SDO_ST_DOWNLOAD_INITIATE_RSP: { + SDO->CANtxBuff->data[0] = 0x60; + SDO->CANtxBuff->data[1] = (uint8_t)SDO->index; + SDO->CANtxBuff->data[2] = (uint8_t)(SDO->index >> 8); + SDO->CANtxBuff->data[3] = SDO->subIndex; + + /* reset timeout timer and send message */ +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_SDO_SRV_SEGMENTED) != 0 + SDO->timeoutTimer = 0; +#endif + (void)CO_CANsend(SDO->CANdevTx, SDO->CANtxBuff); +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_SDO_SRV_SEGMENTED) != 0 + if (SDO->finished) { + SDO->state = CO_SDO_ST_IDLE; + ret = CO_SDO_RT_ok_communicationEnd; + } else { + SDO->toggle = 0x00; + SDO->sizeTran = 0; + SDO->bufOffsetWr = 0; + SDO->bufOffsetRd = 0; + SDO->state = CO_SDO_ST_DOWNLOAD_SEGMENT_REQ; + } +#else + SDO->state = CO_SDO_ST_IDLE; + ret = CO_SDO_RT_ok_communicationEnd; +#endif + break; + } + +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_SDO_SRV_SEGMENTED) != 0 + case CO_SDO_ST_DOWNLOAD_SEGMENT_RSP: { + SDO->CANtxBuff->data[0] = 0x20U | SDO->toggle; + SDO->toggle = (SDO->toggle == 0x00U) ? 0x10U : 0x00U; + + /* reset timeout timer and send message */ + SDO->timeoutTimer = 0; + (void)CO_CANsend(SDO->CANdevTx, SDO->CANtxBuff); + if (SDO->finished) { + SDO->state = CO_SDO_ST_IDLE; + ret = CO_SDO_RT_ok_communicationEnd; + } else { + SDO->state = CO_SDO_ST_DOWNLOAD_SEGMENT_REQ; + } + break; + } +#endif + + case CO_SDO_ST_UPLOAD_INITIATE_RSP: { +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_SDO_SRV_SEGMENTED) != 0 + /* data were already loaded from OD variable */ + if ((SDO->sizeInd > 0U) && (SDO->sizeInd <= 4U)) { + /* expedited transfer */ + SDO->CANtxBuff->data[0] = (uint8_t)(0x43U | ((4U - SDO->sizeInd) << 2U)); + (void)memcpy((void*)(&SDO->CANtxBuff->data[4]), (const void*)(&SDO->buf[0]), SDO->sizeInd); + SDO->state = CO_SDO_ST_IDLE; + ret = CO_SDO_RT_ok_communicationEnd; + } else { + /* data will be transferred with segmented transfer */ + if (SDO->sizeInd > 0U) { + /* indicate data size, if known */ + uint32_t sizeInd = SDO->sizeInd; + uint32_t sizeIndSw = CO_SWAP_32(sizeInd); + SDO->CANtxBuff->data[0] = 0x41; + (void)memcpy((void*)(&SDO->CANtxBuff->data[4]), (const void*)(&sizeIndSw), sizeof(sizeIndSw)); + } else { + SDO->CANtxBuff->data[0] = 0x40; + } + SDO->toggle = 0x00; + SDO->timeoutTimer = 0; + SDO->state = CO_SDO_ST_UPLOAD_SEGMENT_REQ; + } +#else /* Expedited transfer only */ + /* load data from OD variable */ + OD_size_t count = 0; + ODR_t odRet; + + CO_LOCK_OD(SDO->CANdevTx); + odRet = SDO->OD_IO.read(&SDO->OD_IO.stream, &SDO->CANtxBuff->data[4], 4, &count); + CO_UNLOCK_OD(SDO->CANdevTx); + + /* strings are allowed to be shorter */ + if (odRet == ODR_PARTIAL && (SDO->OD_IO.stream.attribute & ODA_STR) != 0) { + odRet = ODR_OK; + } + + if (odRet != ODR_OK || count == 0) { + abortCode = (odRet == ODR_OK) ? CO_SDO_AB_DEVICE_INCOMPAT + : (CO_SDO_abortCode_t)OD_getSDOabCode(odRet); + SDO->state = CO_SDO_ST_ABORT; + break; + } + +#ifdef CO_BIG_ENDIAN + /* swap data if necessary */ + if ((SDO->OD_IO.stream.attribute & ODA_MB) != 0) { + reverseBytes(buf, dataSizeToWrite); + } +#endif + SDO->CANtxBuff->data[0] = 0x43 | ((4 - count) << 2); + SDO->state = CO_SDO_ST_IDLE; + ret = CO_SDO_RT_ok_communicationEnd; +#endif /* (CO_CONFIG_SDO_SRV) & CO_CONFIG_SDO_SRV_SEGMENTED */ + + /* send message */ + SDO->CANtxBuff->data[1] = (uint8_t)SDO->index; + SDO->CANtxBuff->data[2] = (uint8_t)(SDO->index >> 8); + SDO->CANtxBuff->data[3] = SDO->subIndex; + (void)CO_CANsend(SDO->CANdevTx, SDO->CANtxBuff); + break; + } + +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_SDO_SRV_SEGMENTED) != 0 + case CO_SDO_ST_UPLOAD_SEGMENT_RSP: { + /* refill the data buffer if necessary */ + if (!readFromOd(SDO, &abortCode, 7, false)) { + break; + } + + /* SDO command specifier with toggle bit */ + SDO->CANtxBuff->data[0] = SDO->toggle; + SDO->toggle = (SDO->toggle == 0x00U) ? 0x10U : 0x00U; + + OD_size_t count = SDO->bufOffsetWr - SDO->bufOffsetRd; + /* verify, if this is the last segment */ + if ((count < 7U) || (SDO->finished && (count == 7U))) { + /* indicate last segment and nnn */ + SDO->CANtxBuff->data[0] |= (uint8_t)(((7U - count) << 1U) | 0x01U); + SDO->state = CO_SDO_ST_IDLE; + ret = CO_SDO_RT_ok_communicationEnd; + } else { + SDO->timeoutTimer = 0; + SDO->state = CO_SDO_ST_UPLOAD_SEGMENT_REQ; + count = 7; + } + + /* copy data segment to CAN message */ + (void)memcpy(&SDO->CANtxBuff->data[1], SDO->buf + SDO->bufOffsetRd, count); + SDO->bufOffsetRd += count; + SDO->sizeTran += count; + + /* verify if sizeTran is too large or too short if last segment */ + if (SDO->sizeInd > 0U) { + if (SDO->sizeTran > SDO->sizeInd) { + abortCode = CO_SDO_AB_DATA_LONG; + SDO->state = CO_SDO_ST_ABORT; + break; + } else if ((ret == CO_SDO_RT_ok_communicationEnd) && (SDO->sizeTran < SDO->sizeInd)) { + abortCode = CO_SDO_AB_DATA_SHORT; + ret = CO_SDO_RT_waitingResponse; + SDO->state = CO_SDO_ST_ABORT; + break; + } else { /* MISRA C 2004 14.10 */ + } + } + + /* send message */ + (void)CO_CANsend(SDO->CANdevTx, SDO->CANtxBuff); + break; + } +#endif /* (CO_CONFIG_SDO_SRV) & CO_CONFIG_SDO_SRV_SEGMENTED */ + +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_SDO_SRV_BLOCK) != 0 + case CO_SDO_ST_DOWNLOAD_BLK_INITIATE_RSP: { + SDO->CANtxBuff->data[0] = 0xA4; + SDO->CANtxBuff->data[1] = (uint8_t)SDO->index; + SDO->CANtxBuff->data[2] = (uint8_t)(SDO->index >> 8); + SDO->CANtxBuff->data[3] = SDO->subIndex; + + /* calculate number of block segments from free buffer space */ + OD_size_t count = (CO_CONFIG_SDO_SRV_BUFFER_SIZE - 2) / 7; + if (count > 127) { + count = 127; + } + SDO->block_blksize = (uint8_t)count; + SDO->CANtxBuff->data[4] = SDO->block_blksize; + + /* reset variables */ + SDO->sizeTran = 0; + SDO->finished = false; + SDO->bufOffsetWr = 0; + SDO->bufOffsetRd = 0; + SDO->block_seqno = 0; + SDO->block_crc = 0; + SDO->timeoutTimer = 0; + SDO->block_timeoutTimer = 0; + + /* Block segments will be received in different thread. Make memory + * barrier here with CO_FLAG_CLEAR() call. */ + SDO->state = CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_REQ; + CO_FLAG_CLEAR(SDO->CANrxNew); + (void)CO_CANsend(SDO->CANdevTx, SDO->CANtxBuff); + break; + } + + case CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_RSP: { + SDO->CANtxBuff->data[0] = 0xA2; + SDO->CANtxBuff->data[1] = SDO->block_seqno; +#ifdef CO_DEBUG_SDO_SERVER + bool_t transferShort = SDO->block_seqno != SDO->block_blksize; + uint8_t seqnoStart = SDO->block_seqno; +#endif + + /* Is last segment? */ + if (SDO->finished) { + SDO->state = CO_SDO_ST_DOWNLOAD_BLK_END_REQ; + } else { + /* calculate number of block segments from free buffer space */ + OD_size_t count; + count = (CO_CONFIG_SDO_SRV_BUFFER_SIZE - 2 - SDO->bufOffsetWr) / 7; + if (count >= 127) { + count = 127; + } else if (SDO->bufOffsetWr > 0) { + /* it is necessary to empty the buffer */ + if (!validateAndWriteToOD(SDO, &abortCode, 1, 0)) { + break; + } + + count = (CO_CONFIG_SDO_SRV_BUFFER_SIZE - 2 - SDO->bufOffsetWr) / 7; + if (count >= 127) { + count = 127; + } + } + + SDO->block_blksize = (uint8_t)count; + SDO->block_seqno = 0; + /* Block segments will be received in different thread. Make + * memory barrier here with CO_FLAG_CLEAR() call. */ + SDO->state = CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_REQ; + CO_FLAG_CLEAR(SDO->CANrxNew); + } + + SDO->CANtxBuff->data[2] = SDO->block_blksize; + + /* reset block_timeoutTimer, but not SDO->timeoutTimer */ + SDO->block_timeoutTimer = 0; + (void)CO_CANsend(SDO->CANdevTx, SDO->CANtxBuff); +#ifdef CO_DEBUG_SDO_SERVER + if (transferShort && !SDO->finished) { + char msg[80]; + sprintf(msg, "sub-block restarted: sequnoPrev=%02X, blksize=%02X", seqnoStart, SDO->block_blksize); + CO_DEBUG_SDO_SERVER(msg); + } +#endif + break; + } + + case CO_SDO_ST_DOWNLOAD_BLK_END_RSP: { + SDO->CANtxBuff->data[0] = 0xA1; + + (void)CO_CANsend(SDO->CANdevTx, SDO->CANtxBuff); + SDO->state = CO_SDO_ST_IDLE; + ret = CO_SDO_RT_ok_communicationEnd; + break; + } + + case CO_SDO_ST_UPLOAD_BLK_INITIATE_RSP: { + SDO->CANtxBuff->data[0] = 0xC4; + SDO->CANtxBuff->data[1] = (uint8_t)SDO->index; + SDO->CANtxBuff->data[2] = (uint8_t)(SDO->index >> 8); + SDO->CANtxBuff->data[3] = SDO->subIndex; + + /* indicate data size */ + if (SDO->sizeInd > 0) { + uint32_t size = CO_SWAP_32(SDO->sizeInd); + SDO->CANtxBuff->data[0] |= 0x02; + (void)memcpy(&SDO->CANtxBuff->data[4], &size, sizeof(size)); + } + + /* reset timeout timer and send message */ + SDO->timeoutTimer = 0; + (void)CO_CANsend(SDO->CANdevTx, SDO->CANtxBuff); + SDO->state = CO_SDO_ST_UPLOAD_BLK_INITIATE_REQ2; + break; + } + + case CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ: { + /* write header and get current count */ + SDO->CANtxBuff->data[0] = ++SDO->block_seqno; + OD_size_t count = SDO->bufOffsetWr - SDO->bufOffsetRd; + /* verify, if this is the last segment */ + if (count < 7 || (SDO->finished && count == 7)) { + SDO->CANtxBuff->data[0] |= 0x80; + } else { + count = 7; + } + + /* copy data segment to CAN message */ + (void)memcpy(&SDO->CANtxBuff->data[1], SDO->buf + SDO->bufOffsetRd, count); + SDO->bufOffsetRd += count; + SDO->block_noData = (uint8_t)(7 - count); + SDO->sizeTran += count; + + /* verify if sizeTran is too large or too short if last segment */ + if (SDO->sizeInd > 0) { + if (SDO->sizeTran > SDO->sizeInd) { + abortCode = CO_SDO_AB_DATA_LONG; + SDO->state = CO_SDO_ST_ABORT; + break; + } else if (SDO->bufOffsetWr == SDO->bufOffsetRd && SDO->sizeTran < SDO->sizeInd) { + abortCode = CO_SDO_AB_DATA_SHORT; + SDO->state = CO_SDO_ST_ABORT; + break; + } + } + + /* is last segment or all segments in current block transferred? */ + if (SDO->bufOffsetWr == SDO->bufOffsetRd || SDO->block_seqno >= SDO->block_blksize) { + SDO->state = CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_CRSP; + } +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_FLAG_TIMERNEXT) != 0 + else { + /* Inform OS to call this function again without delay. */ + if (timerNext_us != NULL) { + *timerNext_us = 0; + } + } +#endif + /* reset timeout timer and send message */ + SDO->timeoutTimer = 0; + (void)CO_CANsend(SDO->CANdevTx, SDO->CANtxBuff); + break; + } + + case CO_SDO_ST_UPLOAD_BLK_END_SREQ: { + SDO->CANtxBuff->data[0] = 0xC1 | (SDO->block_noData << 2); + SDO->CANtxBuff->data[1] = (uint8_t)SDO->block_crc; + SDO->CANtxBuff->data[2] = (uint8_t)(SDO->block_crc >> 8); + + /* reset timeout timer and send message */ + SDO->timeoutTimer = 0; + (void)CO_CANsend(SDO->CANdevTx, SDO->CANtxBuff); + SDO->state = CO_SDO_ST_UPLOAD_BLK_END_CRSP; + break; + } +#endif /* (CO_CONFIG_SDO_SRV) & CO_CONFIG_SDO_SRV_BLOCK */ + +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_SDO_SRV_SEGMENTED) == 0 + case CO_SDO_ST_DOWNLOAD_SEGMENT_RSP: + case CO_SDO_ST_UPLOAD_SEGMENT_RSP: +#endif +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_SDO_SRV_BLOCK) == 0 + case CO_SDO_ST_DOWNLOAD_BLK_INITIATE_RSP: + case CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_RSP: + case CO_SDO_ST_DOWNLOAD_BLK_END_RSP: + case CO_SDO_ST_UPLOAD_BLK_INITIATE_RSP: + case CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ: + case CO_SDO_ST_UPLOAD_BLK_END_SREQ: +#endif + case CO_SDO_ST_IDLE: + case CO_SDO_ST_ABORT: + case CO_SDO_ST_DOWNLOAD_LOCAL_TRANSFER: + case CO_SDO_ST_DOWNLOAD_INITIATE_REQ: + case CO_SDO_ST_DOWNLOAD_SEGMENT_REQ: + case CO_SDO_ST_UPLOAD_LOCAL_TRANSFER: + case CO_SDO_ST_UPLOAD_INITIATE_REQ: + case CO_SDO_ST_UPLOAD_SEGMENT_REQ: + case CO_SDO_ST_DOWNLOAD_BLK_INITIATE_REQ: + case CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_REQ: + case CO_SDO_ST_DOWNLOAD_BLK_END_REQ: + case CO_SDO_ST_UPLOAD_BLK_INITIATE_REQ: + case CO_SDO_ST_UPLOAD_BLK_INITIATE_REQ2: + case CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_CRSP: + case CO_SDO_ST_UPLOAD_BLK_END_CRSP: + default: { + /* none */ + break; + } + } /* switch (SDO->state) */ + } + + if (ret == CO_SDO_RT_waitingResponse) { + if (SDO->state == CO_SDO_ST_ABORT) { + uint32_t code = CO_SWAP_32((uint32_t)abortCode); + /* Send SDO abort message */ + SDO->CANtxBuff->data[0] = 0x80; + SDO->CANtxBuff->data[1] = (uint8_t)SDO->index; + SDO->CANtxBuff->data[2] = (uint8_t)(SDO->index >> 8); + SDO->CANtxBuff->data[3] = SDO->subIndex; + + (void)memcpy((void*)(&SDO->CANtxBuff->data[4]), (const void*)(&code), sizeof(code)); + (void)CO_CANsend(SDO->CANdevTx, SDO->CANtxBuff); + SDO->state = CO_SDO_ST_IDLE; + ret = CO_SDO_RT_endedWithServerAbort; + } +#if ((CO_CONFIG_SDO_SRV)&CO_CONFIG_SDO_SRV_BLOCK) != 0 + else if (SDO->state == CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_REQ) { + ret = CO_SDO_RT_blockDownldInProgress; + } else if (SDO->state == CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ) { + ret = CO_SDO_RT_blockUploadInProgress; + } +#endif + } + + return ret; +} diff --git a/301/CO_SDOserver.h b/301/CO_SDOserver.h new file mode 100644 index 00000000..5193aac4 --- /dev/null +++ b/301/CO_SDOserver.h @@ -0,0 +1,473 @@ +/** + * CANopen Service Data Object - server protocol. + * + * @file CO_SDOserver.h + * @ingroup CO_SDOserver + * @author Janez Paternoster + * @copyright 2020 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#ifndef CO_SDO_SERVER_H +#define CO_SDO_SERVER_H + +#include "301/CO_driver.h" +#include "301/CO_ODinterface.h" + +/* default configuration, see CO_config.h */ +#ifndef CO_CONFIG_SDO_SRV +#define CO_CONFIG_SDO_SRV \ + (CO_CONFIG_SDO_SRV_SEGMENTED | CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | CO_CONFIG_GLOBAL_FLAG_TIMERNEXT \ + | CO_CONFIG_GLOBAL_FLAG_OD_DYNAMIC) +#endif +#ifndef CO_CONFIG_SDO_SRV_BUFFER_SIZE +#define CO_CONFIG_SDO_SRV_BUFFER_SIZE 32U +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup CO_SDOserver SDO server + * CANopen Service Data Object - server protocol. + * + * @ingroup CO_CANopen_301 + * @{ + * Service data objects (SDOs) allow the access to any entry of the CANopen Object dictionary. By SDO a peer-to-peer + * communication channel between two CANopen devices is established. In addition, the SDO protocol enables to transfer + * any amount of data in a segmented way. Therefore the SDO protocol is mainly used in order to communicate + * configuration data. + * + * All CANopen devices must have implemented SDO server and first SDO server channel. Servers serves data from Object + * dictionary. Object dictionary is a collection of variables, arrays or records (structures), which can be used by the + * stack or by the application. This file (CO_SDOserver.h) implements SDO server. + * + * SDO client can be (optionally) implemented on one (or multiple, if multiple SDO channels are used) device in CANopen + * network. Usually this is master device and provides also some kind of user interface, so configuration of the network + * is possible. Code for the SDO client is in file CO_SDOclient.h. + * + * SDO communication cycle is initiated by the client. Client can upload (read) data from device or can download (write) + * data to device. If data size is less or equal to 4 bytes, communication is finished by one server response (expedited + * transfer). If data size is longer, data are split into multiple segments of request/response pairs (normal or + * segmented transfer). For longer data there is also a block transfer protocol, which transfers larger block of data in + * secure way with little protocol overhead. If error occurs during SDO transfer #CO_SDO_abortCode_t is send by client + * or server and transfer is terminated. For more details see #CO_SDO_state_t. + * + * Access to Object dictionary is specified in @ref CO_ODinterface. + */ + +/** + * Internal state flags indicate type of transfer + * + * These flags correspond to the upper nibble of the SDO state machine states and can be used to determine the type of + * state an SDO object is in. + */ +#define CO_SDO_ST_FLAG_DOWNLOAD 0x10U +#define CO_SDO_ST_FLAG_UPLOAD 0x20U +#define CO_SDO_ST_FLAG_BLOCK 0x40U + +/** + * Internal states of the SDO state machine. + * + * Upper nibble of byte indicates type of state: + * 0x10: Download + * 0x20: Upload + * 0x40: Block Mode + * + * Note: CANopen has little endian byte order. + */ +typedef enum { + /** + * - SDO client may start new download to or upload from specified node, specified index and specified subindex. It + * can start normal or block communication. + * - SDO server is waiting for client request. */ + CO_SDO_ST_IDLE = 0x00U, + /** + * - SDO client or server may send SDO abort message in case of error: + * - byte 0: @b 10000000 binary. + * - byte 1..3: Object index and subIndex. + * - byte 4..7: #CO_SDO_abortCode_t. */ + CO_SDO_ST_ABORT = 0x01U, + + /** + * - SDO client: Node-ID of the SDO server is the same as node-ID of this node, SDO client is the same device as + * SDO server. Transfer data directly without communication on CAN. + * - SDO server does not use this state. */ + CO_SDO_ST_DOWNLOAD_LOCAL_TRANSFER = 0x10U, + /** + * - SDO client initiates SDO download: + * - byte 0: @b 0010nnes binary: (nn: if e=s=1, number of data bytes, that do @b not contain data; e=1 for + * expedited transfer; s=1 if data size is indicated.) + * - byte 1..3: Object index and subIndex. + * - byte 4..7: If e=1, expedited data are here. If e=0 s=1, size of data for segmented transfer is indicated here. + * - SDO server is in #CO_SDO_ST_IDLE state and waits for client request. */ + CO_SDO_ST_DOWNLOAD_INITIATE_REQ = 0x11U, + /** + * - SDO client waits for response. + * - SDO server responses: + * - byte 0: @b 01100000 binary. + * - byte 1..3: Object index and subIndex. + * - byte 4..7: Reserved. + * - In case of expedited transfer communication ends here. */ + CO_SDO_ST_DOWNLOAD_INITIATE_RSP = 0x12U, + /** + * - SDO client sends SDO segment: + * - byte 0: @b 000tnnnc binary: (t: toggle bit, set to 0 in first segment; nnn: number of data bytes, that do + * @b not contain data; c=1 if this is the last segment). + * - byte 1..7: Data segment. + * - SDO server waits for segment. */ + CO_SDO_ST_DOWNLOAD_SEGMENT_REQ = 0x13U, + /** + * - SDO client waits for response. + * - SDO server responses: + * - byte 0: @b 001t0000 binary: (t: toggle bit, set to 0 in first segment). + * - byte 1..7: Reserved. + * - If c was set to 1, then communication ends here. */ + CO_SDO_ST_DOWNLOAD_SEGMENT_RSP = 0x14U, + + /** + * - SDO client: Node-ID of the SDO server is the same as node-ID of this node, SDO client is the same device as + * SDO server. Transfer data directly without communication on CAN. + * - SDO server does not use this state. */ + CO_SDO_ST_UPLOAD_LOCAL_TRANSFER = 0x20U, + /** + * - SDO client initiates SDO upload: + * - byte 0: @b 01000000 binary. + * - byte 1..3: Object index and subIndex. + * - byte 4..7: Reserved. + * - SDO server is in #CO_SDO_ST_IDLE state and waits for client request. */ + CO_SDO_ST_UPLOAD_INITIATE_REQ = 0x21U, + /** + * - SDO client waits for response. + * - SDO server responses: + * - byte 0: @b 0100nnes binary: (nn: if e=s=1, number of data bytes, that do @b not contain data; e=1 for + * expedited transfer; s=1 if data size is indicated). + * - byte 1..3: Object index and subIndex. + * - byte 4..7: If e=1, expedited data are here. If e=0 s=1, size of data for segmented transfer is indicated here. + * - In case of expedited transfer communication ends here. */ + CO_SDO_ST_UPLOAD_INITIATE_RSP = 0x22U, + /** + * - SDO client requests SDO segment: + * - byte 0: @b 011t0000 binary: (t: toggle bit, set to 0 in first segment). + * - byte 1..7: Reserved. + * - SDO server waits for segment request. */ + CO_SDO_ST_UPLOAD_SEGMENT_REQ = 0x23U, + /** + * - SDO client waits for response. + * - SDO server responses with data: + * - byte 0: @b 000tnnnc binary: (t: toggle bit, set to 0 in first segment; nnn: number of data bytes, that do + * @b not contain data; c=1 if this is the last segment). + * - byte 1..7: Data segment. + * - If c is set to 1, then communication ends here. */ + CO_SDO_ST_UPLOAD_SEGMENT_RSP = 0x24U, + + /** + * - SDO client initiates SDO block download: + * - byte 0: @b 11000rs0 binary: (r=1 if client supports generating CRC on data; s=1 if data size is indicated.) + * - byte 1..3: Object index and subIndex. + * - byte 4..7: If s=1, then size of data for block download is indicated here. + * - SDO server is in #CO_SDO_ST_IDLE state and waits for client request. */ + CO_SDO_ST_DOWNLOAD_BLK_INITIATE_REQ = 0x51U, + /** + * - SDO client waits for response. + * - SDO server responses: + * - byte 0: @b 10100r00 binary: (r=1 if server supports generating CRC on data.) + * - byte 1..3: Object index and subIndex. + * - byte 4: blksize: Number of segments per block that shall be used by the client for the following block + * download with 0 < blksize < 128. + * - byte 5..7: Reserved. */ + CO_SDO_ST_DOWNLOAD_BLK_INITIATE_RSP = 0x52U, + /** + * - SDO client sends 'blksize' segments of data in sequence: + * - byte 0: @b cnnnnnnn binary: (c=1 if no more segments to be downloaded, enter SDO block download end phase; + * nnnnnnn is sequence number of segment, 1..127. + * - byte 1..7: At most 7 bytes of segment data to be downloaded. + * - SDO server reads sequence of 'blksize' blocks. */ + CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_REQ = 0x53U, + /** + * - SDO client waits for response. + * - SDO server responses: + * - byte 0: @b 10100010 binary. + * - byte 1: ackseq: sequence number of last segment that was received successfully during the last block + * download. If ackseq is set to 0 the server indicates the client that the segment with the sequence number 1 + * was not received correctly and all segments shall be retransmitted by the client. + * - byte 2: Number of segments per block that shall be used by the client for the following block download with + * 0 < blksize < 128. + * - byte 3..7: Reserved. + * - If c was set to 1, then communication enters SDO block download end phase. + */ + CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_RSP = 0x54U, + /** + * - SDO client sends SDO block download end: + * - byte 0: @b 110nnn01 binary: (nnn: number of data bytes, that do @b not contain data) + * - byte 1..2: 16 bit CRC for the data set, if enabled by client and server. + * - byte 3..7: Reserved. + * - SDO server waits for client request. */ + CO_SDO_ST_DOWNLOAD_BLK_END_REQ = 0x55U, + /** + * - SDO client waits for response. + * - SDO server responses: + * - byte 0: @b 10100001 binary. + * - byte 1..7: Reserved. + * - Block download successfully ends here. + */ + CO_SDO_ST_DOWNLOAD_BLK_END_RSP = 0x56U, + + /** + * - SDO client initiates SDO block upload: + * - byte 0: @b 10100r00 binary: (r=1 if client supports generating CRC on data.) + * - byte 1..3: Object index and subIndex. + * - byte 4: blksize: Number of segments per block with 0 < blksize < 128. + * - byte 5: pst - protocol switch threshold. If pst > 0 and size of the data in bytes is less or equal pst, + * then the server may switch to the SDO upload protocol #CO_SDO_ST_UPLOAD_INITIATE_RSP. + * - byte 6..7: Reserved. + * - SDO server is in #CO_SDO_ST_IDLE state and waits for client request. */ + CO_SDO_ST_UPLOAD_BLK_INITIATE_REQ = 0x61U, + /** + * - SDO client waits for response. + * - SDO server responses: + * - byte 0: @b 11000rs0 binary: (r=1 if server supports generating CRC on data; s=1 if data size is indicated.) + * - byte 1..3: Object index and subIndex. + * - byte 4..7: If s=1, then size of data for block upload is indicated here. + * - If enabled by pst, then server may alternatively response with #CO_SDO_ST_UPLOAD_INITIATE_RSP */ + CO_SDO_ST_UPLOAD_BLK_INITIATE_RSP = 0x62U, + /** + * - SDO client sends second initiate for SDO block upload: + * - byte 0: @b 10100011 binary. + * - byte 1..7: Reserved. + * - SDO server waits for client request. */ + CO_SDO_ST_UPLOAD_BLK_INITIATE_REQ2 = 0x63U, + /** + * - SDO client reads sequence of 'blksize' blocks. + * - SDO server sends 'blksize' segments of data in sequence: + * - byte 0: @b cnnnnnnn binary: (c=1 if no more segments to be uploaded, enter SDO block upload end phase; + * nnnnnnn is sequence number of segment, 1..127. + * - byte 1..7: At most 7 bytes of segment data to be uploaded. */ + CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ = 0x64U, + /** + * - SDO client responses: + * - byte 0: @b 10100010 binary. + * - byte 1: ackseq: sequence number of last segment that was received successfully during the last block + * upload. If ackseq is set to 0 the client indicates the server that the segment with the sequence number 1 was + * not received correctly and all segments shall be retransmitted by the server. + * - byte 2: Number of segments per block that shall be used by the server for the following block upload with + * 0 < blksize < 128. + * - byte 3..7: Reserved. + * - SDO server waits for response. + * - If c was set to 1 and all segments were successfull received, then communication enters SDO block upload end + * phase. */ + CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_CRSP = 0x65U, + /** + * - SDO client waits for server request. + * - SDO server sends SDO block upload end: + * - byte 0: @b 110nnn01 binary: (nnn: number of data bytes, that do @b not contain data) + * - byte 1..2: 16 bit CRC for the data set, if enabled by client and server. + * - byte 3..7: Reserved. */ + CO_SDO_ST_UPLOAD_BLK_END_SREQ = 0x66U, + /** + * - SDO client responses: + * - byte 0: @b 10100001 binary. + * - byte 1..7: Reserved. + * - SDO server waits for response. + * - Block download successfully ends here. Note that this communication ends with client response. Client may + * then start next SDO communication immediately. + */ + CO_SDO_ST_UPLOAD_BLK_END_CRSP = 0x67U, +} CO_SDO_state_t; + +/** + * SDO abort codes. + * + * Send with Abort SDO transfer message. + * + * The abort codes not listed here are reserved. + */ +typedef enum { + CO_SDO_AB_NONE = 0x00000000UL, /**< 0x00000000, No abort */ + CO_SDO_AB_TOGGLE_BIT = 0x05030000UL, /**< 0x05030000, Toggle bit not altered */ + CO_SDO_AB_TIMEOUT = 0x05040000UL, /**< 0x05040000, SDO protocol timed out */ + CO_SDO_AB_CMD = 0x05040001UL, /**< 0x05040001, Command specifier not valid or unknown */ + CO_SDO_AB_BLOCK_SIZE = 0x05040002UL, /**< 0x05040002, Invalid block size in block mode */ + CO_SDO_AB_SEQ_NUM = 0x05040003UL, /**< 0x05040003, Invalid sequence number in block mode */ + CO_SDO_AB_CRC = 0x05040004UL, /**< 0x05040004, CRC error (block mode only) */ + CO_SDO_AB_OUT_OF_MEM = 0x05040005UL, /**< 0x05040005, Out of memory */ + CO_SDO_AB_UNSUPPORTED_ACCESS = 0x06010000UL, /**< 0x06010000, Unsupported access to an object */ + CO_SDO_AB_WRITEONLY = 0x06010001UL, /**< 0x06010001, Attempt to read a write only object */ + CO_SDO_AB_READONLY = 0x06010002UL, /**< 0x06010002, Attempt to write a read only object */ + CO_SDO_AB_NOT_EXIST = 0x06020000UL, /**< 0x06020000, Object does not exist in the object dictionary */ + CO_SDO_AB_NO_MAP = 0x06040041UL, /**< 0x06040041, Object cannot be mapped to the PDO */ + CO_SDO_AB_MAP_LEN = 0x06040042UL, /**< 0x06040042, Number and length of object to be mapped exceeds PDO + length */ + CO_SDO_AB_PRAM_INCOMPAT = 0x06040043UL, /**< 0x06040043, General parameter incompatibility reasons */ + CO_SDO_AB_DEVICE_INCOMPAT = 0x06040047UL, /**< 0x06040047, General internal incompatibility in device */ + CO_SDO_AB_HW = 0x06060000UL, /**< 0x06060000, Access failed due to hardware error */ + CO_SDO_AB_TYPE_MISMATCH = 0x06070010UL, /**< 0x06070010, Data type does not match, length of service parameter + does not match */ + CO_SDO_AB_DATA_LONG = 0x06070012UL, /**< 0x06070012, Data type does not match, length of service parameter + too high */ + CO_SDO_AB_DATA_SHORT = 0x06070013UL, /**< 0x06070013, Data type does not match, length of service parameter + too short */ + CO_SDO_AB_SUB_UNKNOWN = 0x06090011UL, /**< 0x06090011, Sub index does not exist */ + CO_SDO_AB_INVALID_VALUE = 0x06090030UL, /**< 0x06090030, Invalid value for parameter (download only). */ + CO_SDO_AB_VALUE_HIGH = 0x06090031UL, /**< 0x06090031, Value range of parameter written too high */ + CO_SDO_AB_VALUE_LOW = 0x06090032UL, /**< 0x06090032, Value range of parameter written too low */ + CO_SDO_AB_MAX_LESS_MIN = 0x06090036UL, /**< 0x06090036, Maximum value is less than minimum value. */ + CO_SDO_AB_NO_RESOURCE = 0x060A0023UL, /**< 0x060A0023, Resource not available: SDO connection */ + CO_SDO_AB_GENERAL = 0x08000000UL, /**< 0x08000000, General error */ + CO_SDO_AB_DATA_TRANSF = 0x08000020UL, /**< 0x08000020, Data cannot be transferred or stored to application */ + CO_SDO_AB_DATA_LOC_CTRL = 0x08000021UL, /**< 0x08000021, Data cannot be transferred or stored to application + because of local control */ + CO_SDO_AB_DATA_DEV_STATE = 0x08000022UL, /**< 0x08000022, Data cannot be transferred or stored to application + because of present device state */ + CO_SDO_AB_DATA_OD = 0x08000023UL, /**< 0x08000023, Object dictionary not present or dynamic generation + fails */ + CO_SDO_AB_NO_DATA = 0x08000024UL /**< 0x08000024, No data available */ +} CO_SDO_abortCode_t; + +/** + * Return values from SDO server or client functions. + */ +typedef enum { + CO_SDO_RT_waitingLocalTransfer = 6, /**< Waiting in client local transfer. */ + CO_SDO_RT_uploadDataBufferFull = 5, /**< Data buffer is full. SDO client: data must be read before next upload + cycle begins. */ + CO_SDO_RT_transmittBufferFull = 4, /**< CAN transmit buffer is full. Waiting. */ + CO_SDO_RT_blockDownldInProgress = 3, /**< Block download is in progress. Sending train of messages. */ + CO_SDO_RT_blockUploadInProgress = 2, /**< Block upload is in progress. Receiving train of messages. SDO client: Data + must not be read in this state. */ + CO_SDO_RT_waitingResponse = 1, /**< Waiting server or client response. */ + CO_SDO_RT_ok_communicationEnd = 0, /**< Success, end of communication. SDO client: uploaded data must be read. */ + CO_SDO_RT_wrongArguments = -2, /**< Error in arguments */ + CO_SDO_RT_endedWithClientAbort = -9, /**< Communication ended with client abort */ + CO_SDO_RT_endedWithServerAbort = -10, /**< Communication ended with server abort */ +} CO_SDO_return_t; + +/** + * SDO server object. + */ +typedef struct { + CO_CANmodule_t* CANdevTx; /**< From CO_SDOserver_init() */ + CO_CANtx_t* CANtxBuff; /**< CAN transmit buffer inside CANdevTx for CAN tx message */ + OD_t* OD; /**< From CO_SDOserver_init() */ + uint8_t nodeId; /**< From CO_SDOserver_init() */ + bool_t valid; /**< If true, SDO channel is valid */ + volatile CO_SDO_state_t state; /**< Internal state of the SDO server */ + OD_IO_t OD_IO; /**< Object dictionary interface for current object. */ + uint16_t index; /**< Index of the current object in Object Dictionary */ + uint8_t subIndex; /**< Subindex of the current object in Object Dictionary */ + volatile void* CANrxNew; /**< Indicates, if new SDO message received from CAN bus. It is not cleared, + until received message is completely processed. */ + uint8_t CANrxData[8]; /**< 8 data bytes of the received message */ +#if (((CO_CONFIG_SDO_SRV)&CO_CONFIG_FLAG_OD_DYNAMIC) != 0) || defined CO_DOXYGEN + CO_CANmodule_t* CANdevRx; /**< From CO_SDOserver_init() */ + uint16_t CANdevRxIdx; /**< From CO_SDOserver_init() */ + uint16_t CANdevTxIdx; /**< From CO_SDOserver_init() */ + uint32_t COB_IDClientToServer; /**< Copy of CANopen COB_ID Client -> Server, meaning of the specific bits: + - Bit 0...10: 11-bit CAN identifier. + - Bit 11..30: reserved, must be 0. + - Bit 31: if 1, SDO client object is not used. */ + uint32_t COB_IDServerToClient; /**< Copy of CANopen COB_ID Server -> Client, similar as above */ + OD_extension_t OD_1200_extension; /**< Extension for OD object */ +#endif +#if (((CO_CONFIG_SDO_SRV)&CO_CONFIG_SDO_SRV_SEGMENTED) != 0) || defined CO_DOXYGEN + OD_size_t sizeInd; /**< Size of data, which will be transferred. It is optionally indicated by client + in case of download or by server in case of upload. */ + OD_size_t sizeTran; /**< Size of data which is actually transferred. */ + uint8_t toggle; /**< Toggle bit toggled in each segment in segmented transfer */ + bool_t finished; /**< If true, then: data transfer is finished (by download) or read from OD variable + is finished (by upload) */ + uint32_t SDOtimeoutTime_us; /**< Maximum timeout time between request and response in microseconds */ + uint32_t timeoutTimer; /**< Timeout timer for SDO communication */ + uint8_t buf[CO_CONFIG_SDO_SRV_BUFFER_SIZE + 1U]; /**< Interim data buffer for segmented or + block transfer + byte for '\0' */ + OD_size_t bufOffsetWr; /**< Offset of next free data byte available for write in the buffer. */ + OD_size_t bufOffsetRd; /**< Offset of first data available for read in the buffer */ +#endif +#if (((CO_CONFIG_SDO_SRV)&CO_CONFIG_SDO_SRV_BLOCK) != 0) || defined CO_DOXYGEN + uint32_t block_SDOtimeoutTime_us; /**< Timeout time for SDO sub-block download, half of #SDOtimeoutTime_us */ + uint32_t block_timeoutTimer; /**< Timeout timer for SDO sub-block download */ + uint8_t block_seqno; /**< Sequence number of segment in block, 1..127 */ + uint8_t block_blksize; /**< Number of segments per block, 1..127 */ + uint8_t block_noData; /**< Number of bytes in last segment that do not contain data */ + bool_t block_crcEnabled; /**< Client CRC support in block transfer */ + uint16_t block_crc; /**< Calculated CRC checksum */ +#endif +#if (((CO_CONFIG_SDO_SRV)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0) || defined CO_DOXYGEN + void (*pFunctSignalPre)(void* object); /**< From CO_SDOserver_initCallbackPre() or NULL */ + void* functSignalObjectPre; /**< From CO_SDOserver_initCallbackPre() or NULL */ +#endif +} CO_SDOserver_t; + +/** + * Initialize SDO object. + * + * Function must be called in the communication reset section. + * + * @param SDO This object will be initialized. + * @param OD Object Dictionary. + * @param OD_1200_SDOsrvPar OD entry for SDO server parameter (0x1200+), can be NULL for default single SDO server and + * must not be NULL for additional SDO servers. With additional SDO servers it may also have IO extension enabled, to + * allow dynamic configuration (see also @ref CO_CONFIG_FLAG_OD_DYNAMIC). + * @param nodeId If this is first SDO channel, then "nodeId" is CANopen Node ID of this device. In all additional + * channels "nodeId" is ignored. + * @param SDOtimeoutTime_ms Timeout time for SDO communication in milliseconds. + * @param CANdevRx CAN device for SDO server reception. + * @param CANdevRxIdx Index of receive buffer in the above CAN device. + * @param CANdevTx CAN device for SDO server transmission. + * @param CANdevTxIdx Index of transmit buffer in the above CAN device. + * @param [out] errInfo Additional information in case of error, may be NULL. + * + * @return @ref CO_ReturnError_t CO_ERROR_NO in case of success. + */ +CO_ReturnError_t CO_SDOserver_init(CO_SDOserver_t* SDO, OD_t* OD, OD_entry_t* OD_1200_SDOsrvPar, uint8_t nodeId, + uint16_t SDOtimeoutTime_ms, CO_CANmodule_t* CANdevRx, uint16_t CANdevRxIdx, + CO_CANmodule_t* CANdevTx, uint16_t CANdevTxIdx, uint32_t* errInfo); + +#if (((CO_CONFIG_SDO_SRV)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0) || defined CO_DOXYGEN +/** + * Initialize SDOrx callback function. + * + * Function initializes optional callback function, which should immediately start processing of CO_SDOserver_process() + * function. Callback is called after SDOserver message is received from the CAN bus or when new call without delay is + * necessary (SDO block transfer is in progress). + * + * @param SDO This object. + * @param object Pointer to object, which will be passed to pFunctSignalPre(). Can be NULL + * @param pFunctSignalPre Pointer to the callback function. Not called if NULL. + */ +void CO_SDOserver_initCallbackPre(CO_SDOserver_t* SDO, void* object, void (*pFunctSignalPre)(void* object)); +#endif + +/** + * Process SDO communication. + * + * Function must be called cyclically. + * + * @param SDO This object. + * @param NMTisPreOrOperational True if #CO_NMT_internalState_t is NMT_PRE_OPERATIONAL or NMT_OPERATIONAL. + * @param timeDifference_us Time difference from previous function call in [microseconds]. + * @param [out] timerNext_us info to OS - see CO_process(). + * + * @return #CO_SDO_return_t + */ +CO_SDO_return_t CO_SDOserver_process(CO_SDOserver_t* SDO, bool_t NMTisPreOrOperational, uint32_t timeDifference_us, + uint32_t* timerNext_us); + +/** @} */ /* CO_SDOserver */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CO_SDO_SERVER_H */ diff --git a/301/CO_SYNC.c b/301/CO_SYNC.c new file mode 100644 index 00000000..bc2f49d5 --- /dev/null +++ b/301/CO_SYNC.c @@ -0,0 +1,411 @@ +/* + * CANopen SYNC object. + * + * @file CO_SYNC.c + * @ingroup CO_SYNC + * @author Janez Paternoster + * @copyright 2021 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#include "301/CO_SYNC.h" + +#if ((CO_CONFIG_SYNC)&CO_CONFIG_SYNC_ENABLE) != 0 + +/* + * Read received message from CAN module. + * + * Function will be called (by CAN receive interrupt) every time, when CAN message with correct identifier + * will be received. For more information and description of parameters see file CO_driver.h. + */ +static void +CO_SYNC_receive(void* object, void* msg) { + CO_SYNC_t* SYNC = object; + uint8_t DLC = CO_CANrxMsg_readDLC(msg); + bool_t syncReceived = false; + + if (SYNC->counterOverflowValue == 0U) { + if (DLC == 0U) { + syncReceived = true; + } else { + SYNC->receiveError = DLC | 0x40U; + } + } else { + if (DLC == 1U) { + const uint8_t* data = CO_CANrxMsg_readData(msg); + SYNC->counter = data[0]; + syncReceived = true; + } else { + SYNC->receiveError = DLC | 0x80U; + } + } + + if (syncReceived) { + /* toggle PDO receive buffer */ + SYNC->CANrxToggle = SYNC->CANrxToggle ? false : true; + + CO_FLAG_SET(SYNC->CANrxNew); + +#if ((CO_CONFIG_SYNC)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0 + /* Optional signal to RTOS, which can resume task, which handles SYNC. */ + if (SYNC->pFunctSignalPre != NULL) { + SYNC->pFunctSignalPre(SYNC->functSignalObjectPre); + } +#endif + } +} + +#if ((CO_CONFIG_SYNC)&CO_CONFIG_FLAG_OD_DYNAMIC) != 0 +/* + * Custom function for writing OD object "COB-ID sync message" + * + * For more information see file CO_ODinterface.h, OD_IO_t. + */ +static ODR_t +OD_write_1005(OD_stream_t* stream, const void* buf, OD_size_t count, OD_size_t* countWritten) { + if ((stream == NULL) || (stream->subIndex != 0U) || (buf == NULL) || (count != sizeof(uint32_t)) + || (countWritten == NULL)) { + return ODR_DEV_INCOMPAT; + } + + CO_SYNC_t* SYNC = stream->object; + uint32_t cobIdSync = CO_getUint32(buf); + uint16_t CAN_ID = (uint16_t)(cobIdSync & 0x7FFU); + + /* verify written value */ +#if ((CO_CONFIG_SYNC)&CO_CONFIG_SYNC_PRODUCER) != 0 + bool_t isProducer = (cobIdSync & 0x40000000U) != 0U; + if (((cobIdSync & 0xBFFFF800U) != 0U) || CO_IS_RESTRICTED_CAN_ID(CAN_ID) + || (SYNC->isProducer && isProducer && (CAN_ID != SYNC->CAN_ID))) { + return ODR_INVALID_VALUE; + } +#else + if (((cobIdSync & 0xFFFFF800U) != 0U) || CO_IS_RESTRICTED_CAN_ID(CAN_ID)) { + return ODR_INVALID_VALUE; + } +#endif + + /* Configure CAN receive and transmit buffers */ + if (CAN_ID != SYNC->CAN_ID) { + CO_ReturnError_t CANret = CO_CANrxBufferInit(SYNC->CANdevRx, SYNC->CANdevRxIdx, CAN_ID, 0x7FF, false, + (void*)SYNC, CO_SYNC_receive); + + if (CANret != CO_ERROR_NO) { + return ODR_DEV_INCOMPAT; + } + +#if ((CO_CONFIG_SYNC)&CO_CONFIG_SYNC_PRODUCER) != 0 + SYNC->CANtxBuff = CO_CANtxBufferInit(SYNC->CANdevTx, SYNC->CANdevTxIdx, CAN_ID, false, + (SYNC->counterOverflowValue != 0U) ? 1U : 0U, false); + + if (SYNC->CANtxBuff == NULL) { + SYNC->isProducer = false; + return ODR_DEV_INCOMPAT; + } +#endif + + SYNC->CAN_ID = CAN_ID; + } + +#if ((CO_CONFIG_SYNC)&CO_CONFIG_SYNC_PRODUCER) != 0 + SYNC->isProducer = isProducer; + if (isProducer) { + SYNC->counter = 0; + SYNC->timer = 0; + } +#endif /* CO_CONFIG_SYNC) & CO_CONFIG_SYNC_PRODUCER */ + + /* write value to the original location in the Object Dictionary */ + return OD_writeOriginal(stream, buf, count, countWritten); +} + +#if ((CO_CONFIG_SYNC)&CO_CONFIG_SYNC_PRODUCER) != 0 +/* + * Custom function for writing OD object "Synchronous counter overflow value" + * + * For more information see file CO_ODinterface.h, OD_IO_t. + */ +static ODR_t +OD_write_1019(OD_stream_t* stream, const void* buf, OD_size_t count, OD_size_t* countWritten) { + if ((stream == NULL) || (stream->subIndex != 0U) || (buf == NULL) || (count != sizeof(uint8_t)) + || (countWritten == NULL)) { + return ODR_DEV_INCOMPAT; + } + + CO_SYNC_t* SYNC = stream->object; + uint8_t syncCounterOvf = CO_getUint8(buf); + + /* verify written value */ + if ((syncCounterOvf == 1U) || (syncCounterOvf > 240U)) { + return ODR_INVALID_VALUE; + } + if (*SYNC->OD_1006_period != 0U) { + return ODR_DATA_DEV_STATE; + } + + /* Configure CAN transmit buffer */ + SYNC->CANtxBuff = CO_CANtxBufferInit(SYNC->CANdevTx, SYNC->CANdevTxIdx, SYNC->CAN_ID, false, + (syncCounterOvf != 0U) ? 1U : 0U, false); + + if (SYNC->CANtxBuff == NULL) { + SYNC->isProducer = false; + return ODR_DEV_INCOMPAT; + } + + SYNC->counterOverflowValue = syncCounterOvf; + + /* write value to the original location in the Object Dictionary */ + return OD_writeOriginal(stream, buf, count, countWritten); +} +#endif /* (CO_CONFIG_SYNC) & CO_CONFIG_SYNC_PRODUCER */ +#endif /* (CO_CONFIG_SYNC) & CO_CONFIG_FLAG_OD_DYNAMIC */ + +CO_ReturnError_t +CO_SYNC_init(CO_SYNC_t* SYNC, CO_EM_t* em, OD_entry_t* OD_1005_cobIdSync, OD_entry_t* OD_1006_commCyclePeriod, + OD_entry_t* OD_1007_syncWindowLen, OD_entry_t* OD_1019_syncCounterOvf, CO_CANmodule_t* CANdevRx, + uint16_t CANdevRxIdx, +#if ((CO_CONFIG_SYNC)&CO_CONFIG_SYNC_PRODUCER) != 0 + CO_CANmodule_t* CANdevTx, uint16_t CANdevTxIdx, +#endif + uint32_t* errInfo) { + ODR_t odRet; + + /* verify arguments */ + if ((SYNC == NULL) || (em == NULL) || (OD_1005_cobIdSync == NULL) +#if ((CO_CONFIG_SYNC)&CO_CONFIG_SYNC_PRODUCER) != 0 + || (OD_1006_commCyclePeriod == NULL) || (CANdevTx == NULL) +#endif + || (CANdevRx == NULL)) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + /* clear object */ + (void)memset(SYNC, 0, sizeof(CO_SYNC_t)); + + /* get and verify "COB-ID SYNC message" from OD and configure extension */ + uint32_t cobIdSync = 0x00000080; + + odRet = OD_get_u32(OD_1005_cobIdSync, 0, &cobIdSync, true); + if (odRet != ODR_OK) { + if (errInfo != NULL) { + *errInfo = OD_getIndex(OD_1005_cobIdSync); + } + return CO_ERROR_OD_PARAMETERS; + } +#if ((CO_CONFIG_SYNC)&CO_CONFIG_FLAG_OD_DYNAMIC) != 0 + SYNC->OD_1005_extension.object = SYNC; + SYNC->OD_1005_extension.read = OD_readOriginal; + SYNC->OD_1005_extension.write = OD_write_1005; + (void)OD_extension_init(OD_1005_cobIdSync, &SYNC->OD_1005_extension); +#endif + + /* get and verify "Communication cycle period" from OD */ + SYNC->OD_1006_period = OD_getPtr(OD_1006_commCyclePeriod, 0, sizeof(uint32_t), NULL); +#if ((CO_CONFIG_SYNC)&CO_CONFIG_SYNC_PRODUCER) != 0 + if (SYNC->OD_1006_period == NULL) { + if (errInfo != NULL) { + *errInfo = OD_getIndex(OD_1006_commCyclePeriod); + } + return CO_ERROR_OD_PARAMETERS; + } +#else + if ((OD_1006_commCyclePeriod != NULL) && (SYNC->OD_1006_period == NULL)) { + if (errInfo != NULL) { + *errInfo = OD_getIndex(OD_1006_commCyclePeriod); + } + return CO_ERROR_OD_PARAMETERS; + } +#endif + + /* get "Synchronous window length" from OD (optional parameter) */ + SYNC->OD_1007_window = OD_getPtr(OD_1007_syncWindowLen, 0, sizeof(uint32_t), NULL); + if ((OD_1007_syncWindowLen != NULL) && (SYNC->OD_1007_window == NULL)) { + if (errInfo != NULL) { + *errInfo = OD_getIndex(OD_1007_syncWindowLen); + } + return CO_ERROR_OD_PARAMETERS; + } + + /* get and verify optional "Synchronous counter overflow value" from OD and configure extension */ + uint8_t syncCounterOvf = 0; + + if (OD_1019_syncCounterOvf != NULL) { + odRet = OD_get_u8(OD_1019_syncCounterOvf, 0, &syncCounterOvf, true); + if (odRet != ODR_OK) { + if (errInfo != NULL) { + *errInfo = OD_getIndex(OD_1019_syncCounterOvf); + } + return CO_ERROR_OD_PARAMETERS; + } + if (syncCounterOvf == 1U) { + syncCounterOvf = 2; + } else if (syncCounterOvf > 240U) { + syncCounterOvf = 240; + } else { /* MISRA C 2004 14.10 */ + } + +#if ((CO_CONFIG_SYNC)&CO_CONFIG_FLAG_OD_DYNAMIC) != 0 +#if ((CO_CONFIG_SYNC)&CO_CONFIG_SYNC_PRODUCER) != 0 + SYNC->OD_1019_extension.object = SYNC; + SYNC->OD_1019_extension.read = OD_readOriginal; + SYNC->OD_1019_extension.write = OD_write_1019; + (void)OD_extension_init(OD_1019_syncCounterOvf, &SYNC->OD_1019_extension); +#endif +#endif + } + SYNC->counterOverflowValue = syncCounterOvf; + + /* Configure object variables */ + SYNC->em = em; +#if ((CO_CONFIG_SYNC)&CO_CONFIG_SYNC_PRODUCER) != 0 + SYNC->isProducer = (cobIdSync & 0x40000000U) != 0U; + SYNC->CANdevTx = CANdevTx; +#endif +#if ((CO_CONFIG_SYNC)&CO_CONFIG_FLAG_OD_DYNAMIC) != 0 + SYNC->CAN_ID = (uint16_t)(cobIdSync & 0x7FFU); + SYNC->CANdevRx = CANdevRx; + SYNC->CANdevRxIdx = CANdevRxIdx; +#if ((CO_CONFIG_SYNC)&CO_CONFIG_SYNC_PRODUCER) != 0 + SYNC->CANdevTxIdx = CANdevTxIdx; +#endif +#endif + + /* configure SYNC CAN reception and transmission */ + CO_ReturnError_t ret = CO_CANrxBufferInit(CANdevRx, CANdevRxIdx, (uint16_t)(cobIdSync & 0x7FFU), 0x7FF, false, + (void*)SYNC, CO_SYNC_receive); + if (ret != CO_ERROR_NO) { + return ret; + } + +#if ((CO_CONFIG_SYNC)&CO_CONFIG_SYNC_PRODUCER) != 0 + SYNC->CANtxBuff = CO_CANtxBufferInit(CANdevTx, CANdevTxIdx, (uint16_t)(cobIdSync & 0x7FFU), false, + (syncCounterOvf != 0U) ? 1U : 0U, false); + + if (SYNC->CANtxBuff == NULL) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } +#endif + + return CO_ERROR_NO; +} + +#if ((CO_CONFIG_SYNC)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0 +void +CO_SYNC_initCallbackPre(CO_SYNC_t* SYNC, void* object, void (*pFunctSignalPre)(void* object)) { + if (SYNC != NULL) { + SYNC->functSignalObjectPre = object; + SYNC->pFunctSignalPre = pFunctSignalPre; + } +} +#endif + +CO_SYNC_status_t +CO_SYNC_process(CO_SYNC_t* SYNC, bool_t NMTisPreOrOperational, uint32_t timeDifference_us, uint32_t* timerNext_us) { + (void)timerNext_us; /* may be unused */ + + CO_SYNC_status_t syncStatus = CO_SYNC_NONE; + + if (NMTisPreOrOperational) { + /* update sync timer, no overflow */ + uint32_t timerNew = SYNC->timer + timeDifference_us; + if (timerNew > SYNC->timer) { + SYNC->timer = timerNew; + } + + /* was SYNC just received */ + if (CO_FLAG_READ(SYNC->CANrxNew)) { + SYNC->timer = 0; + syncStatus = CO_SYNC_RX_TX; + CO_FLAG_CLEAR(SYNC->CANrxNew); + } + + uint32_t OD_1006_period = (SYNC->OD_1006_period != NULL) ? *SYNC->OD_1006_period : 0U; + + if (OD_1006_period > 0U) { +#if ((CO_CONFIG_SYNC)&CO_CONFIG_SYNC_PRODUCER) != 0 + if (SYNC->isProducer) { + if (SYNC->timer >= OD_1006_period) { + syncStatus = CO_SYNC_RX_TX; + (void)CO_SYNCsend(SYNC); + } +#if ((CO_CONFIG_SYNC)&CO_CONFIG_FLAG_TIMERNEXT) != 0 + /* Calculate when next SYNC needs to be sent */ + if (timerNext_us != NULL) { + uint32_t diff = OD_1006_period - SYNC->timer; + if (*timerNext_us > diff) { + *timerNext_us = diff; + } + } +#endif + } else +#endif /* (CO_CONFIG_SYNC) & CO_CONFIG_SYNC_PRODUCER */ + + /* Verify timeout of SYNC */ + if (SYNC->timeoutError == 1U) { + /* periodTimeout is 1,5 * OD_1006_period, no overflow */ + uint32_t periodTimeout = OD_1006_period + (OD_1006_period >> 1); + if (periodTimeout < OD_1006_period) { + periodTimeout = 0xFFFFFFFFU; + } + + if (SYNC->timer > periodTimeout) { + CO_errorReport(SYNC->em, CO_EM_SYNC_TIME_OUT, CO_EMC_COMMUNICATION, SYNC->timer); + SYNC->timeoutError = 2; + } +#if ((CO_CONFIG_SYNC)&CO_CONFIG_FLAG_TIMERNEXT) != 0 + else if (timerNext_us != NULL) { + uint32_t diff = periodTimeout - SYNC->timer; + if (*timerNext_us > diff) { + *timerNext_us = diff; + } + } else { /* MISRA C 2004 14.10 */ + } +#endif + } else { /* MISRA C 2004 14.10 */ + } + } /* if (OD_1006_period > 0) */ + + /* Synchronous PDOs are allowed only inside time window */ + if ((SYNC->OD_1007_window != NULL) && (*SYNC->OD_1007_window > 0U) && (SYNC->timer > *SYNC->OD_1007_window)) { + if (!SYNC->syncIsOutsideWindow) { + syncStatus = CO_SYNC_PASSED_WINDOW; + } + SYNC->syncIsOutsideWindow = true; + } else { + SYNC->syncIsOutsideWindow = false; + } + + /* verify error from receive function */ + if (SYNC->receiveError != 0U) { + CO_errorReport(SYNC->em, CO_EM_SYNC_LENGTH, CO_EMC_SYNC_DATA_LENGTH, SYNC->receiveError); + SYNC->receiveError = 0; + } + } /* if (NMTisPreOrOperational) */ + else { + CO_FLAG_CLEAR(SYNC->CANrxNew); + SYNC->receiveError = 0; + SYNC->counter = 0; + SYNC->timer = 0; + } + + if (syncStatus == CO_SYNC_RX_TX) { + if (SYNC->timeoutError == 2U) { + CO_errorReset(SYNC->em, CO_EM_SYNC_TIME_OUT, 0); + } + SYNC->timeoutError = 1; + } + + return syncStatus; +} + +#endif /* (CO_CONFIG_SYNC) & CO_CONFIG_SYNC_ENABLE */ diff --git a/301/CO_SYNC.h b/301/CO_SYNC.h new file mode 100644 index 00000000..e281fe67 --- /dev/null +++ b/301/CO_SYNC.h @@ -0,0 +1,205 @@ +/** + * CANopen Synchronisation protocol. + * + * @file CO_SYNC.h + * @ingroup CO_SYNC + * @author Janez Paternoster + * @copyright 2004 - 2020 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#ifndef CO_SYNC_H +#define CO_SYNC_H + +#include "301/CO_driver.h" +#include "301/CO_ODinterface.h" +#include "301/CO_Emergency.h" + +/* default configuration, see CO_config.h */ +#ifndef CO_CONFIG_SYNC +#define CO_CONFIG_SYNC \ + (CO_CONFIG_SYNC_ENABLE | CO_CONFIG_SYNC_PRODUCER | CO_CONFIG_GLOBAL_RT_FLAG_CALLBACK_PRE \ + | CO_CONFIG_GLOBAL_FLAG_TIMERNEXT | CO_CONFIG_GLOBAL_FLAG_OD_DYNAMIC) +#endif + +#if (((CO_CONFIG_SYNC)&CO_CONFIG_SYNC_ENABLE) != 0) || defined CO_DOXYGEN + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup CO_SYNC SYNC + * CANopen Synchronisation protocol. + * + * @ingroup CO_CANopen_301 + * @{ + * For CAN identifier see @ref CO_Default_CAN_ID_t + * + * SYNC message is used for synchronization of the nodes on network. One node can be SYNC producer, others can be SYNC + * consumers. Synchronous TPDOs are transmitted after the CANopen SYNC message. Synchronous received PDOs are + * accepted(copied to OD) immediatelly after the reception of the next SYNC message. + * + * ####Contents of SYNC message + * By default SYNC message has no data. If _Synchronous counter overflow value_ from Object dictionary (index 0x1019) is + * different than 0, SYNC message has one data byte: _counter_ incremented by 1 with every SYNC transmission. + * + * ####SYNC in CANopenNode + * According to CANopen, synchronous RPDOs must be processed after reception of the next sync messsage. For that reason, + * there is a double receive buffer for each synchronous RPDO. At the moment, when SYNC is received or transmitted, + * internal variable CANrxToggle toggles. That variable is then used by synchronous RPDO to determine, which of the two + * buffers is used for RPDO reception and which for RPDO processing. + */ + +/** + * SYNC producer and consumer object. + */ +typedef struct { + CO_EM_t* em; /**< From CO_SYNC_init() */ + volatile void* CANrxNew; /**< Indicates, if new SYNC message received from CAN bus */ + uint8_t receiveError; /**< Set to nonzero value, if SYNC with wrong data length is received */ + bool_t CANrxToggle; /**< Variable toggles, if new SYNC message received from CAN bus */ + uint8_t timeoutError; /**< Sync timeout monitoring: 0 = not started; 1 = started; 2 = sync timeout error state */ + uint8_t counterOverflowValue; /**< Value from _Synchronous counter overflow value_ variable from Object dictionary + (index 0x1019) */ + uint8_t counter; /**< Counter of the SYNC message if counterOverflowValue is different than zero */ + bool_t syncIsOutsideWindow; /**< True, if current time is outside "synchronous window" (OD 1007) */ + uint32_t timer; /**< Timer for the SYNC message in [microseconds]. Set to zero after received or + transmitted SYNC message */ + uint32_t* OD_1006_period; /**< Pointer to variable in OD, "Communication cycle period" in microseconds */ + uint32_t* OD_1007_window; /**< Pointer to variable in OD, "Synchronous window length" in microseconds */ + +#if (((CO_CONFIG_SYNC)&CO_CONFIG_SYNC_PRODUCER) != 0) || defined CO_DOXYGEN + bool_t isProducer; /**< True, if device is SYNC producer. Calculated from _COB ID SYNC Message_ variable + from Object dictionary(index 0x1005).*/ + CO_CANmodule_t* CANdevTx; /**< From CO_SYNC_init() */ + CO_CANtx_t* CANtxBuff; /**< CAN transmit buffer inside CANdevTx */ +#endif + +#if ((CO_CONFIG_SYNC)&CO_CONFIG_FLAG_OD_DYNAMIC) || defined CO_DOXYGEN + CO_CANmodule_t* CANdevRx; /**< From CO_SYNC_init() */ + uint16_t CANdevRxIdx; /**< From CO_SYNC_init() */ + OD_extension_t OD_1005_extension; /**< Extension for OD object */ + uint16_t CAN_ID; /**< CAN ID of the SYNC message. Calculated from _COB ID SYNC Message_ variable + from Object dictionary (index 0x1005). */ +#if (((CO_CONFIG_SYNC)&CO_CONFIG_SYNC_PRODUCER) != 0) || defined CO_DOXYGEN + uint16_t CANdevTxIdx; /**< From CO_SYNC_init() */ + OD_extension_t OD_1019_extension; /**< Extension for OD object */ +#endif +#endif + +#if (((CO_CONFIG_SYNC)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0) || defined CO_DOXYGEN + void (*pFunctSignalPre)(void* object); /**< From CO_SYNC_initCallbackPre() or NULL */ + void* functSignalObjectPre; /**< From CO_SYNC_initCallbackPre() or NULL */ +#endif +} CO_SYNC_t; + +/** + * Return value for @ref CO_SYNC_process + */ +typedef enum { + CO_SYNC_NONE = 0, /**< No SYNC event in last cycle */ + CO_SYNC_RX_TX = 1, /**< SYNC message was received or transmitted in last cycle */ + CO_SYNC_PASSED_WINDOW = 2 /**< Time has just passed SYNC window (OD_1007) in last cycle */ +} CO_SYNC_status_t; + +/** + * Initialize SYNC object. + * + * Function must be called in the communication reset section. + * + * @param SYNC This object will be initialized. + * @param em Emergency object. + * @param OD_1005_cobIdSync OD entry for 0x1005 - "COB-ID SYNC message", entry is required. + * @param OD_1006_commCyclePeriod OD entry for 0x1006 - "Communication cycle period", entry is required if device is + * sync producer. + * @param OD_1007_syncWindowLen OD entry for 0x1007 - "Synchronous window length", entry is optional, may be NULL. + * @param OD_1019_syncCounterOvf OD entry for 0x1019 - "Synchronous counter overflow value", entry is optional, may be + * NULL. + * @param CANdevRx CAN device for SYNC reception. + * @param CANdevRxIdx Index of receive buffer in the above CAN device. + * @param CANdevTx CAN device for SYNC transmission. + * @param CANdevTxIdx Index of transmit buffer in the above CAN device. + * @param [out] errInfo Additional information in case of error, may be NULL. + * + * @return #CO_ReturnError_t CO_ERROR_NO on success. + */ +CO_ReturnError_t CO_SYNC_init(CO_SYNC_t* SYNC, CO_EM_t* em, OD_entry_t* OD_1005_cobIdSync, + OD_entry_t* OD_1006_commCyclePeriod, OD_entry_t* OD_1007_syncWindowLen, + OD_entry_t* OD_1019_syncCounterOvf, CO_CANmodule_t* CANdevRx, uint16_t CANdevRxIdx, +#if (((CO_CONFIG_SYNC)&CO_CONFIG_SYNC_PRODUCER) != 0) || defined CO_DOXYGEN + CO_CANmodule_t* CANdevTx, uint16_t CANdevTxIdx, +#endif + uint32_t* errInfo); + +#if (((CO_CONFIG_SYNC)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0) || defined CO_DOXYGEN +/** + * Initialize SYNC callback function. + * + * Function initializes optional callback function, which should immediately start processing of CO_SYNC_process() + * function. Callback is called after SYNC message is received from the CAN bus. + * + * @param SYNC This object. + * @param object Pointer to object, which will be passed to pFunctSignalPre(). + * @param pFunctSignalPre Pointer to the callback function. Not called if NULL. + */ +void CO_SYNC_initCallbackPre(CO_SYNC_t* SYNC, void* object, void (*pFunctSignalPre)(void* object)); +#endif + +#if (((CO_CONFIG_SYNC)&CO_CONFIG_SYNC_PRODUCER) != 0) || defined CO_DOXYGEN +/** + * Send SYNC message. + * + * This function prepares and sends a SYNC object. The application should only call this if direct control of SYNC + * transmission is needed, otherwise use CO_SYNC_process(). + * + * @param SYNC SYNC object. + * + * @return Same as CO_CANsend(). + */ +static inline CO_ReturnError_t +CO_SYNCsend(CO_SYNC_t* SYNC) { + if (++SYNC->counter > SYNC->counterOverflowValue) { + SYNC->counter = 1; + } + SYNC->timer = 0; + SYNC->CANrxToggle = SYNC->CANrxToggle ? false : true; + SYNC->CANtxBuff->data[0] = SYNC->counter; + return CO_CANsend(SYNC->CANdevTx, SYNC->CANtxBuff); +} +#endif + +/** + * Process SYNC communication. + * + * Function must be called cyclically. + * + * @param SYNC This object. + * @param NMTisPreOrOperational True if this node is NMT_PRE_OPERATIONAL or NMT_OPERATIONAL state. + * @param timeDifference_us Time difference from previous function call in [microseconds]. + * @param [out] timerNext_us info to OS - see CO_process(). + * + * @return @ref CO_SYNC_status_t + */ +CO_SYNC_status_t CO_SYNC_process(CO_SYNC_t* SYNC, bool_t NMTisPreOrOperational, uint32_t timeDifference_us, + uint32_t* timerNext_us); + +/** @} */ /* CO_SYNC */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* (CO_CONFIG_SYNC) & CO_CONFIG_SYNC_ENABLE */ + +#endif /* CO_SYNC_H */ diff --git a/301/CO_TIME.c b/301/CO_TIME.c new file mode 100644 index 00000000..499271cd --- /dev/null +++ b/301/CO_TIME.c @@ -0,0 +1,208 @@ +/* + * CANopen TIME object. + * + * @file CO_TIME.c + * @ingroup CO_TIME + * @author Julien PEYREGNE + * @copyright 2019 - 2020 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#include + +#include "301/CO_TIME.h" + +#if ((CO_CONFIG_TIME)&CO_CONFIG_TIME_ENABLE) != 0 + +/* + * Read received message from CAN module. + * + * Function will be called (by CAN receive interrupt) every time, when CAN message with correct identifier + * will be received. For more information and description of parameters see file CO_driver.h. + */ +static void +CO_TIME_receive(void* object, void* msg) { + CO_TIME_t* TIME = object; + uint8_t DLC = CO_CANrxMsg_readDLC(msg); + const uint8_t* data = CO_CANrxMsg_readData(msg); + + if (DLC == CO_TIME_MSG_LENGTH) { + (void)memcpy(TIME->timeStamp, data, sizeof(TIME->timeStamp)); + CO_FLAG_SET(TIME->CANrxNew); + +#if ((CO_CONFIG_TIME)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0 + /* Optional signal to RTOS, which can resume task, which handles TIME. */ + if (TIME->pFunctSignalPre != NULL) { + TIME->pFunctSignalPre(TIME->functSignalObjectPre); + } +#endif + } +} + +#if ((CO_CONFIG_TIME)&CO_CONFIG_FLAG_OD_DYNAMIC) != 0 +/* + * Custom function for writing OD object "COB-ID time stamp" + * + * For more information see file CO_ODinterface.h, OD_IO_t. + */ +static ODR_t +OD_write_1012(OD_stream_t* stream, const void* buf, OD_size_t count, OD_size_t* countWritten) { + if ((stream == NULL) || (stream->subIndex != 0U) || (buf == NULL) || (count != sizeof(uint32_t)) + || (countWritten == NULL)) { + return ODR_DEV_INCOMPAT; + } + + CO_TIME_t* TIME = stream->object; + + /* verify written value */ + uint32_t cobIdTimeStamp = CO_getUint32(buf); + uint16_t CAN_ID = (uint16_t)(cobIdTimeStamp & 0x7FFU); + if (((cobIdTimeStamp & 0x3FFFF800U) != 0U) || CO_IS_RESTRICTED_CAN_ID(CAN_ID)) { + return ODR_INVALID_VALUE; + } + + /* update object */ + TIME->isConsumer = (cobIdTimeStamp & 0x80000000UL) != 0U; + TIME->isProducer = (cobIdTimeStamp & 0x40000000UL) != 0U; + + /* write value to the original location in the Object Dictionary */ + return OD_writeOriginal(stream, buf, count, countWritten); +} +#endif + +CO_ReturnError_t +CO_TIME_init(CO_TIME_t* TIME, OD_entry_t* OD_1012_cobIdTimeStamp, CO_CANmodule_t* CANdevRx, uint16_t CANdevRxIdx, +#if ((CO_CONFIG_TIME)&CO_CONFIG_TIME_PRODUCER) != 0 + CO_CANmodule_t* CANdevTx, uint16_t CANdevTxIdx, +#endif + uint32_t* errInfo) { + /* verify arguments */ + if ((TIME == NULL) || (OD_1012_cobIdTimeStamp == NULL) || (CANdevRx == NULL) +#if ((CO_CONFIG_TIME)&CO_CONFIG_TIME_PRODUCER) != 0 + || CANdevTx == NULL +#endif + ) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + (void)memset(TIME, 0, sizeof(CO_TIME_t)); + + /* get parameters from object dictionary and configure extension */ + uint32_t cobIdTimeStamp; + ODR_t odRet = OD_get_u32(OD_1012_cobIdTimeStamp, 0, &cobIdTimeStamp, true); + if (odRet != ODR_OK) { + if (errInfo != NULL) { + *errInfo = OD_getIndex(OD_1012_cobIdTimeStamp); + } + return CO_ERROR_OD_PARAMETERS; + } +#if ((CO_CONFIG_TIME)&CO_CONFIG_FLAG_OD_DYNAMIC) != 0 + TIME->OD_1012_extension.object = TIME; + TIME->OD_1012_extension.read = OD_readOriginal; + TIME->OD_1012_extension.write = OD_write_1012; + (void)OD_extension_init(OD_1012_cobIdTimeStamp, &TIME->OD_1012_extension); +#endif + + /* Configure object variables */ + uint16_t cobId = (uint16_t)(cobIdTimeStamp & 0x7FFU); + TIME->isConsumer = (cobIdTimeStamp & 0x80000000UL) != 0U; + TIME->isProducer = (cobIdTimeStamp & 0x40000000UL) != 0U; + CO_FLAG_CLEAR(TIME->CANrxNew); + + /* configure TIME consumer message reception */ + if (TIME->isConsumer) { + CO_ReturnError_t ret = CO_CANrxBufferInit(CANdevRx, CANdevRxIdx, cobId, 0x7FF, false, (void*)TIME, + CO_TIME_receive); + if (ret != CO_ERROR_NO) { + return ret; + } + } + +#if ((CO_CONFIG_TIME)&CO_CONFIG_TIME_PRODUCER) != 0 + /* configure TIME producer message transmission */ + TIME->CANdevTx = CANdevTx; + TIME->CANtxBuff = CO_CANtxBufferInit(CANdevTx, CANdevTxIdx, cobId, false, CO_TIME_MSG_LENGTH, false); + + if (TIME->CANtxBuff == NULL) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } +#endif + + return CO_ERROR_NO; +} + +#if ((CO_CONFIG_TIME)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0 +void +CO_TIME_initCallbackPre(CO_TIME_t* TIME, void* object, void (*pFunctSignalPre)(void* object)) { + if (TIME != NULL) { + TIME->functSignalObjectPre = object; + TIME->pFunctSignalPre = pFunctSignalPre; + } +} +#endif + +bool_t +CO_TIME_process(CO_TIME_t* TIME, bool_t NMTisPreOrOperational, uint32_t timeDifference_us) { + bool_t timestampReceived = false; + + /* Was TIME stamp message just received */ + if (NMTisPreOrOperational && TIME->isConsumer) { + if (CO_FLAG_READ(TIME->CANrxNew)) { + uint32_t ms_swapped = CO_getUint32(&TIME->timeStamp[0]); + uint16_t days_swapped = CO_getUint16(&TIME->timeStamp[4]); + TIME->ms = CO_SWAP_32(ms_swapped) & 0x0FFFFFFFU; + TIME->days = CO_SWAP_16(days_swapped); + TIME->residual_us = 0; + timestampReceived = true; + + CO_FLAG_CLEAR(TIME->CANrxNew); + } + } else { + CO_FLAG_CLEAR(TIME->CANrxNew); + } + + /* Update time */ + uint32_t ms = 0; + if (!timestampReceived && (timeDifference_us > 0U)) { + uint32_t us = timeDifference_us + TIME->residual_us; + ms = us / 1000U; + TIME->residual_us = (uint16_t)(us % 1000U); + TIME->ms += ms; + if (TIME->ms >= ((uint32_t)1000U * 60U * 60U * 24U)) { + TIME->ms -= ((uint32_t)1000U * 60U * 60U * 24U); + TIME->days += 1U; + } + } + +#if ((CO_CONFIG_TIME)&CO_CONFIG_TIME_PRODUCER) != 0 + if (NMTisPreOrOperational && TIME->isProducer && TIME->producerInterval_ms > 0) { + if (TIME->producerTimer_ms >= TIME->producerInterval_ms) { + TIME->producerTimer_ms -= TIME->producerInterval_ms; + + uint32_t ms_swapped = CO_SWAP_32(TIME->ms); + uint16_t days_swapped = CO_SWAP_16(TIME->days); + (void)CO_setUint32(&TIME->CANtxBuff->data[0], ms_swapped); + (void)CO_setUint16(&TIME->CANtxBuff->data[4], days_swapped); + (void)CO_CANsend(TIME->CANdevTx, TIME->CANtxBuff); + } else { + TIME->producerTimer_ms += ms; + } + } else { + TIME->producerTimer_ms = TIME->producerInterval_ms; + } +#endif + + return timestampReceived; +} + +#endif /* (CO_CONFIG_TIME) & CO_CONFIG_TIME_ENABLE */ diff --git a/301/CO_TIME.h b/301/CO_TIME.h new file mode 100644 index 00000000..86a2b9e7 --- /dev/null +++ b/301/CO_TIME.h @@ -0,0 +1,172 @@ +/** + * CANopen Time-stamp protocol. + * + * @file CO_TIME.h + * @ingroup CO_TIME + * @author Julien PEYREGNE + * @copyright 2019 - 2020 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#ifndef CO_TIME_H +#define CO_TIME_H + +#include "301/CO_driver.h" +#include "301/CO_ODinterface.h" +#include "301/CO_NMT_Heartbeat.h" + +/* default configuration, see CO_config.h */ +#ifndef CO_CONFIG_TIME +#define CO_CONFIG_TIME (CO_CONFIG_TIME_ENABLE | CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | CO_CONFIG_GLOBAL_FLAG_OD_DYNAMIC) +#endif + +#if (((CO_CONFIG_TIME)&CO_CONFIG_TIME_ENABLE) != 0) || defined CO_DOXYGEN + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup CO_TIME TIME + * CANopen Time-stamp protocol. + * + * @ingroup CO_CANopen_301 + * @{ + * For CAN identifier see @ref CO_Default_CAN_ID_t + * + * TIME message is used for time synchronization of the nodes on the network. One node should be TIME producer, others + * can be TIME consumers. This is configured by COB_ID_TIME object 0x1012: + * + * - bit 31 should be set for a consumer + * - bit 30 should be set for a producer + * - bits 0..10 is CAN-ID, 0x100 by default + * + * Current time can be read from @p CO_TIME_t->ms (milliseconds after midnight) and @p CO_TIME_t->days (number of days + * since January 1, 1984). Those values are updated on each @ref CO_TIME_process() call, either from internal timer or + * from received time stamp message. + * + * Current time can be set with @ref CO_TIME_set() function, which is necessary at least once, if time producer. If + * configured, time stamp message is send from @ref CO_TIME_process() in intervals specified by @ref CO_TIME_set() + */ + +#define CO_TIME_MSG_LENGTH 6U /**< Length of the TIME message */ + +/** + * TIME producer and consumer object. + */ +typedef struct { + uint8_t timeStamp[CO_TIME_MSG_LENGTH]; /**< Received timestamp data */ + uint32_t ms; /**< Milliseconds after midnight */ + uint16_t days; /**< Number of days since January 1, 1984 */ + uint16_t residual_us; /**< Residual microseconds calculated inside CO_TIME_process() */ + bool_t isConsumer; /**< True, if device is TIME consumer. Calculated from _COB ID TIME Message_ + variable from Object dictionary (index 0x1012). */ + bool_t isProducer; /**< True, if device is TIME producer. Calculated from _COB ID TIME Message_ + variable from Object dictionary (index 0x1012). */ + volatile void* CANrxNew; /**< Variable indicates, if new TIME message received from CAN bus */ +#if (((CO_CONFIG_TIME)&CO_CONFIG_TIME_PRODUCER) != 0) || defined CO_DOXYGEN + uint32_t producerInterval_ms; /**< Interval for time producer in milli seconds */ + uint32_t producerTimer_ms; /**< Sync producer timer */ + CO_CANmodule_t* CANdevTx; /**< From CO_TIME_init() */ + CO_CANtx_t* CANtxBuff; /**< CAN transmit buffer */ +#endif +#if (((CO_CONFIG_TIME)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0) || defined CO_DOXYGEN + void (*pFunctSignalPre)(void* object); /**< From CO_TIME_initCallbackPre() or NULL */ + void* functSignalObjectPre; /**< From CO_TIME_initCallbackPre() or NULL */ +#endif +#if (((CO_CONFIG_TIME)&CO_CONFIG_FLAG_OD_DYNAMIC) != 0) || defined CO_DOXYGEN + OD_extension_t OD_1012_extension; /**< Extension for OD object */ +#endif +} CO_TIME_t; + +/** + * Initialize TIME object. + * + * Function must be called in the communication reset section. + * + * @param TIME This object will be initialized. + * @param OD_1012_cobIdTimeStamp OD entry for 0x1012 - "COB-ID time stamp", entry is required. + * @param CANdevRx CAN device for TIME reception. + * @param CANdevRxIdx Index of receive buffer in the above CAN device. + * @param CANdevTx CAN device for TIME transmission. + * @param CANdevTxIdx Index of transmit buffer in the above CAN device. + * @param [out] errInfo Additional information in case of error, may be NULL. + * + * @return #CO_ReturnError_t CO_ERROR_NO on success. + */ +CO_ReturnError_t CO_TIME_init(CO_TIME_t* TIME, OD_entry_t* OD_1012_cobIdTimeStamp, CO_CANmodule_t* CANdevRx, + uint16_t CANdevRxIdx, +#if (((CO_CONFIG_TIME)&CO_CONFIG_TIME_PRODUCER) != 0) || defined CO_DOXYGEN + CO_CANmodule_t* CANdevTx, uint16_t CANdevTxIdx, +#endif + uint32_t* errInfo); + +#if (((CO_CONFIG_TIME)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0) || defined CO_DOXYGEN +/** + * Initialize TIME callback function. + * + * Function initializes optional callback function, which should immediately start processing of CO_TIME_process() + * function. Callback is called after TIME message is received from the CAN bus. + * + * @param TIME This object. + * @param object Pointer to object, which will be passed to pFunctSignalPre(). + * @param pFunctSignalPre Pointer to the callback function. Not called if NULL. + */ +void CO_TIME_initCallbackPre(CO_TIME_t* TIME, void* object, void (*pFunctSignalPre)(void* object)); +#endif + +/** + * Set current time + * + * @param TIME This object. + * @param ms Milliseconds after midnight + * @param days Number of days since January 1, 1984 + * @param producerInterval_ms Interval time for time producer in milliseconds + */ +static inline void +CO_TIME_set(CO_TIME_t* TIME, uint32_t ms, uint16_t days, uint32_t producerInterval_ms) { + (void)producerInterval_ms; /* may be unused */ + + if (TIME != NULL) { + TIME->residual_us = 0; + TIME->ms = ms; + TIME->days = days; +#if ((CO_CONFIG_TIME)&CO_CONFIG_TIME_PRODUCER) != 0 + TIME->producerTimer_ms = TIME->producerInterval_ms = producerInterval_ms; +#endif + } +} + +/** + * Process TIME object. + * + * Function must be called cyclically. It updates internal time from received time stamp message or from + * timeDifference_us. It also sends produces timestamp message, if producer and producerInterval_ms is set. + * + * @param TIME This object. + * @param timeDifference_us Time difference from previous function call in [microseconds]. + * @param NMTisPreOrOperational True if this node is NMT_PRE_OPERATIONAL or NMT_OPERATIONAL state. + * + * @return True if new TIME stamp message recently received (consumer). + */ +bool_t CO_TIME_process(CO_TIME_t* TIME, bool_t NMTisPreOrOperational, uint32_t timeDifference_us); + +/** @} */ /* CO_TIME */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* (CO_CONFIG_TIME) & CO_CONFIG_TIME_ENABLE */ + +#endif /* CO_TIME_H */ diff --git a/301/CO_config.h b/301/CO_config.h new file mode 100644 index 00000000..0d22406c --- /dev/null +++ b/301/CO_config.h @@ -0,0 +1,831 @@ +/** + * Configuration macros for CANopenNode. + * + * @file CO_config.h + * @ingroup CO_STACK_CONFIG + * @author Janez Paternoster + * @copyright 2020 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#ifndef CO_CONFIG_FLAGS_H +#define CO_CONFIG_FLAGS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup CO_STACK_CONFIG Stack configuration + * Stack configuration and enabling macros. + * + * @ingroup CO_driver + * + * Default values for stack configuration macros are set in corresponding + * header files. The same default values are also provided in this file, but + * only for documentation generator. Default values can be overridden by + * CO_driver_target.h file. If specified so, they can further be overridden by + * CO_driver_custom.h file. + * + * Stack configuration macro is specified as bits, where each bit + * enables/disables some part of the configurable CANopenNode object. Flags are + * used for enabling or checking specific bit. Multiple flags can be ORed + * together. + * + * Some functionalities of CANopenNode objects, enabled by configuration macros, + * requires some objects from Object Dictionary to exist. Object Dictionary + * configuration must match @ref CO_STACK_CONFIG. + * @{ + */ + +/** + * @defgroup CO_STACK_CONFIG_COMMON Common definitions + * Constants for common definitions. + * @{ + */ +/** + * Enable custom callback after CAN receive + * + * Flag enables optional callback functions, which are part of some CANopenNode + * objects. Callbacks can optionally be registered by application, which + * configures threads in operating system. Callbacks are called after something + * has been preprocessed by higher priority thread and must be further + * processed by lower priority thread. For example when CAN message is received + * and preprocessed, callback should wake up mainline processing function. + * See also @ref CO_process() function. + * + * If callback functions are used, they must be initialized separately, after + * the object initialization. + * + * This flag is common to multiple configuration macros. + */ +#define CO_CONFIG_FLAG_CALLBACK_PRE 0x1000 + +/** + * Enable calculation of timerNext_us variable. + * + * Calculation of the timerNext_us variable is useful for smooth operation on + * operating system. See also @ref CO_process() function. + * + * This flag is common to multiple configuration macros. + */ +#define CO_CONFIG_FLAG_TIMERNEXT 0x2000 + +/** + * Enable dynamic behaviour of Object Dictionary variables + * + * Some CANopen objects uses Object Dictionary variables as arguments to + * initialization functions, which are processed in communication reset section. + * If this flag is set, then writing to OD variable will reconfigure + * corresponding CANopen object also during CANopen normal operation. + * + * This flag is common to multiple configuration macros. + */ +#define CO_CONFIG_FLAG_OD_DYNAMIC 0x4000 + +/** This flag may be set globally for mainline objects to + * @ref CO_CONFIG_FLAG_CALLBACK_PRE */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE (0) +#endif + +/** This flag may be set globally for Real-Time objects (SYNC, PDO) to + * @ref CO_CONFIG_FLAG_CALLBACK_PRE */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_GLOBAL_RT_FLAG_CALLBACK_PRE (0) +#endif + +/** This flag may be set globally to @ref CO_CONFIG_FLAG_TIMERNEXT */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_GLOBAL_FLAG_TIMERNEXT (0) +#endif + +/** This flag may be set globally to (0) */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_GLOBAL_FLAG_OD_DYNAMIC CO_CONFIG_FLAG_OD_DYNAMIC +#endif +/** @} */ /* CO_STACK_CONFIG_COMMON */ + +/** + * @defgroup CO_STACK_CONFIG_NMT_HB NMT master/slave and HB producer/consumer + * Specified in standard CiA 301 + * @{ + */ +/** + * Configuration of @ref CO_NMT_Heartbeat. + * + * Possible flags, can be ORed: + * - CO_CONFIG_NMT_CALLBACK_CHANGE - Enable custom callback after NMT + * state changes. Callback is configured by + * CO_NMT_initCallbackChanged(). + * - CO_CONFIG_NMT_MASTER - Enable simple NMT master + * - #CO_CONFIG_FLAG_CALLBACK_PRE - Enable custom callback after preprocessing + * received NMT CAN message. + * Callback is configured by CO_NMT_initCallbackPre(). + * - #CO_CONFIG_FLAG_TIMERNEXT - Enable calculation of timerNext_us variable + * inside CO_NMT_process(). + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_NMT (CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | CO_CONFIG_GLOBAL_FLAG_TIMERNEXT) +#endif +#define CO_CONFIG_NMT_CALLBACK_CHANGE 0x01 +#define CO_CONFIG_NMT_MASTER 0x02 + +/** + * Configuration of @ref CO_HBconsumer + * + * Possible flags, can be ORed: + * - CO_CONFIG_HB_CONS_ENABLE - Enable heartbeat consumer. + * - CO_CONFIG_HB_CONS_CALLBACK_CHANGE - Enable custom common callback after NMT + * state of the monitored node changes. Callback is configured by + * CO_HBconsumer_initCallbackNmtChanged(). + * - CO_CONFIG_HB_CONS_CALLBACK_MULTI - Enable multiple custom callbacks, which + * can be configured individually for each monitored node. Callbacks are + * configured by CO_HBconsumer_initCallbackNmtChanged(), + * CO_HBconsumer_initCallbackHeartbeatStarted(), + * CO_HBconsumer_initCallbackTimeout() and + * CO_HBconsumer_initCallbackRemoteReset() functions. + * - CO_CONFIG_HB_CONS_QUERY_FUNCT - Enable functions for query HB state or + * NMT state of the specific monitored node. + * - #CO_CONFIG_FLAG_CALLBACK_PRE - Enable custom callback after preprocessing + * received heartbeat CAN message. + * Callback is configured by CO_HBconsumer_initCallbackPre(). + * - #CO_CONFIG_FLAG_TIMERNEXT - Enable calculation of timerNext_us variable + * inside CO_HBconsumer_process(). + * - #CO_CONFIG_FLAG_OD_DYNAMIC - Enable dynamic configuration of monitored + * nodes (Writing to object 0x1016 re-configures the monitored nodes). + * + * @warning CO_CONFIG_HB_CONS_CALLBACK_CHANGE and + * CO_CONFIG_HB_CONS_CALLBACK_MULTI cannot be set simultaneously. + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_HB_CONS \ + (CO_CONFIG_HB_CONS_ENABLE | CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | CO_CONFIG_GLOBAL_FLAG_TIMERNEXT \ + | CO_CONFIG_GLOBAL_FLAG_OD_DYNAMIC) +#endif +#define CO_CONFIG_HB_CONS_ENABLE 0x01 +#define CO_CONFIG_HB_CONS_CALLBACK_CHANGE 0x02 +#define CO_CONFIG_HB_CONS_CALLBACK_MULTI 0x04 +#define CO_CONFIG_HB_CONS_QUERY_FUNCT 0x08 +/** @} */ /* CO_STACK_CONFIG_NMT_HB */ + +/** + * @defgroup CO_STACK_CONFIG_NODE_GUARDING CANopen Node Guarding slave and master objects. + * Specified in standard CiA 301 + * @{ + */ +/** + * Configuration of @ref CO_Node_Guarding + * + * Possible flags, can be ORed: + * - CO_CONFIG_NODE_GUARDING_SLAVE_ENABLE - Enable Node guarding slave. + * - CO_CONFIG_NODE_GUARDING_MASTER_ENABLE - Enable Node guarding master. + * - #CO_CONFIG_FLAG_TIMERNEXT - Enable calculation of timerNext_us variable + * inside CO_nodeGuardingSlave_process(). + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_NODE_GUARDING (0) +#endif +#define CO_CONFIG_NODE_GUARDING_SLAVE_ENABLE 0x01 +#define CO_CONFIG_NODE_GUARDING_MASTER_ENABLE 0x02 + +/** + * Maximum number of nodes monitored by master + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_NODE_GUARDING_MASTER_COUNT 0x7F +#endif +/** @} */ /* CO_STACK_CONFIG_NODE_GUARDING */ + +/** + * @defgroup CO_STACK_CONFIG_EMERGENCY Emergency producer/consumer + * Specified in standard CiA 301 + * @{ + */ +/** + * Configuration of @ref CO_Emergency + * + * Possible flags, can be ORed: + * - CO_CONFIG_EM_PRODUCER - Enable emergency producer. + * - CO_CONFIG_EM_PROD_CONFIGURABLE - Emergency producer COB-ID is configurable, + * OD object 0x1014. If not configurable, then 0x1014 is read-only, COB_ID + * is set to CO_CAN_ID_EMERGENCY + nodeId and write is not verified. + * - CO_CONFIG_EM_PROD_INHIBIT - Enable inhibit timer on emergency producer, + * OD object 0x1015. + * - CO_CONFIG_EM_HISTORY - Enable error history, OD object 0x1003, + * "Pre-defined error field" + * - CO_CONFIG_EM_CONSUMER - Enable simple emergency consumer with callback. + * - CO_CONFIG_EM_STATUS_BITS - Access @ref CO_EM_errorStatusBits_t from OD. + * - #CO_CONFIG_FLAG_CALLBACK_PRE - Enable custom callback after preprocessing + * emergency condition by CO_errorReport() or CO_errorReset() call. + * Callback is configured by CO_EM_initCallbackPre(). + * - #CO_CONFIG_FLAG_TIMERNEXT - Enable calculation of timerNext_us variable + * inside CO_EM_process(). + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_EM \ + (CO_CONFIG_EM_PRODUCER | CO_CONFIG_EM_HISTORY | CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE \ + | CO_CONFIG_GLOBAL_FLAG_TIMERNEXT) +#endif +#define CO_CONFIG_EM_PRODUCER 0x01 +#define CO_CONFIG_EM_PROD_CONFIGURABLE 0x02 +#define CO_CONFIG_EM_PROD_INHIBIT 0x04 +#define CO_CONFIG_EM_HISTORY 0x08 +#define CO_CONFIG_EM_STATUS_BITS 0x10 +#define CO_CONFIG_EM_CONSUMER 0x20 + +/** + * Maximum number of @ref CO_EM_errorStatusBits_t + * + * Stack uses 6*8 = 48 @ref CO_EM_errorStatusBits_t, others are free to use by + * manufacturer. Allowable value range is from 48 to 256 bits in steps of 8. + * Default is 80. + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_EM_ERR_STATUS_BITS_COUNT (10 * 8) +#endif + +/** + * Condition for calculating CANopen Error register, "generic" error bit. + * + * Condition must observe suitable @ref CO_EM_errorStatusBits_t and use + * corresponding member of errorStatusBits array from CO_EM_t to calculate the + * condition. See also @ref CO_errorRegister_t. + * + * @warning Size of @ref CO_CONFIG_EM_ERR_STATUS_BITS_COUNT must be large + * enough. (CO_CONFIG_EM_ERR_STATUS_BITS_COUNT/8) must be larger than index of + * array member in em->errorStatusBits[index]. + * + * em->errorStatusBits[5] should be included in the condition, because they are + * used by the stack. + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_ERR_CONDITION_GENERIC (em->errorStatusBits[5] != 0) +#endif + +/** + * Condition for calculating CANopen Error register, "current" error bit. + * See @ref CO_CONFIG_ERR_CONDITION_GENERIC for description. + * Macro is not defined by default, so no error is verified. + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_ERR_CONDITION_CURRENT +#endif + +/** + * Condition for calculating CANopen Error register, "voltage" error bit. + * See @ref CO_CONFIG_ERR_CONDITION_GENERIC for description. + * Macro is not defined by default, so no error is verified. + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_ERR_CONDITION_VOLTAGE +#endif + +/** + * Condition for calculating CANopen Error register, "temperature" error bit. + * See @ref CO_CONFIG_ERR_CONDITION_GENERIC for description. + * Macro is not defined by default, so no error is verified. + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_ERR_CONDITION_TEMPERATURE +#endif + +/** + * Condition for calculating CANopen Error register, "communication" error bit. + * See @ref CO_CONFIG_ERR_CONDITION_GENERIC for description. + * + * em->errorStatusBits[2] and em->errorStatusBits[3] must be included in the + * condition, because they are used by the stack. + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_ERR_CONDITION_COMMUNICATION (em->errorStatusBits[2] || em->errorStatusBits[3]) +#endif + +/** + * Condition for calculating CANopen Error register, "device profile" error bit. + * See @ref CO_CONFIG_ERR_CONDITION_GENERIC for description. + * Macro is not defined by default, so no error is verified. + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_ERR_CONDITION_DEV_PROFILE +#endif + +/** + * Condition for calculating CANopen Error register, "manufacturer" error bit. + * See @ref CO_CONFIG_ERR_CONDITION_GENERIC for description. + * + * em->errorStatusBits[8] and em->errorStatusBits[8] are pre-defined, but can + * be changed. + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_ERR_CONDITION_MANUFACTURER (em->errorStatusBits[8] || em->errorStatusBits[9]) +#endif +/** @} */ /* CO_STACK_CONFIG_EMERGENCY */ + +/** + * @defgroup CO_STACK_CONFIG_SDO SDO server/client + * Specified in standard CiA 301 + * @{ + */ +/** + * Configuration of @ref CO_SDOserver + * + * Possible flags, can be ORed: + * - CO_CONFIG_SDO_SRV_SEGMENTED - Enable SDO server segmented transfer. + * - CO_CONFIG_SDO_SRV_BLOCK - Enable SDO server block transfer. If set, then + * CO_CONFIG_SDO_SRV_SEGMENTED must also be set. + * - #CO_CONFIG_FLAG_CALLBACK_PRE - Enable custom callback after preprocessing + * received SDO CAN message. + * Callback is configured by CO_SDOserver_initCallbackPre(). + * - #CO_CONFIG_FLAG_TIMERNEXT - Enable calculation of timerNext_us variable + * inside CO_SDOserver_process(). + * - #CO_CONFIG_FLAG_OD_DYNAMIC - Enable dynamic configuration of additional SDO + * servers (Writing to object 0x1201+ re-configures the additional server). + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_SDO_SRV \ + (CO_CONFIG_SDO_SRV_SEGMENTED | CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | CO_CONFIG_GLOBAL_FLAG_TIMERNEXT \ + | CO_CONFIG_GLOBAL_FLAG_OD_DYNAMIC) +#endif +#define CO_CONFIG_SDO_SRV_SEGMENTED 0x02 +#define CO_CONFIG_SDO_SRV_BLOCK 0x04 + +/** + * Size of the internal data buffer for the SDO server. + * + * If size is less than size of some variables in Object Dictionary, then data + * will be transferred to internal buffer in several segments. Minimum size is + * 8 or 899 (127*7) for block transfer. + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_SDO_SRV_BUFFER_SIZE 32 +#endif + +/** + * Configuration of @ref CO_SDOclient + * + * Possible flags, can be ORed: + * - CO_CONFIG_SDO_CLI_ENABLE - Enable SDO client. + * - CO_CONFIG_SDO_CLI_SEGMENTED - Enable SDO client segmented transfer. + * - CO_CONFIG_SDO_CLI_BLOCK - Enable SDO client block transfer. If set, then + * CO_CONFIG_SDO_CLI_SEGMENTED, CO_CONFIG_FIFO_ALT_READ and + * CO_CONFIG_FIFO_CRC16_CCITT must also be set. + * - CO_CONFIG_SDO_CLI_LOCAL - Enable local transfer, if Node-ID of the SDO + * server is the same as node-ID of the SDO client. (SDO client is the same + * device as SDO server.) Transfer data directly without communication on CAN. + * - #CO_CONFIG_FLAG_CALLBACK_PRE - Enable custom callback after preprocessing + * received SDO CAN message. + * Callback is configured by CO_SDOclient_initCallbackPre(). + * - #CO_CONFIG_FLAG_TIMERNEXT - Enable calculation of timerNext_us variable + * inside CO_SDOclientDownloadInitiate(), CO_SDOclientDownload(), + * CO_SDOclientUploadInitiate(), CO_SDOclientUpload(). + * - #CO_CONFIG_FLAG_OD_DYNAMIC - Enable dynamic configuration of SDO clients + * (Writing to object 0x1280+ re-configures the client). + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_SDO_CLI (0) +#endif +#define CO_CONFIG_SDO_CLI_ENABLE 0x01 +#define CO_CONFIG_SDO_CLI_SEGMENTED 0x02 +#define CO_CONFIG_SDO_CLI_BLOCK 0x04 +#define CO_CONFIG_SDO_CLI_LOCAL 0x08 + +/** + * Size of the internal data buffer for the SDO client. + * + * Circular buffer is used for SDO communication. It can be read or written + * between successive SDO calls. So size of the buffer can be lower than size of + * the actual size of data transferred. If only segmented transfer is used, then + * buffer size can be as low as 7 bytes, if data are read/written each cycle. If + * block transfer is used, buffer size should be set to at least 1000 bytes, so + * maximum blksize can be used (blksize is calculated from free buffer space). + * Default value for block transfer is 1000, otherwise 32. + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_SDO_CLI_BUFFER_SIZE 32 +#endif +/** @} */ /* CO_STACK_CONFIG_SDO */ + +/** + * @defgroup CO_STACK_CONFIG_TIME Time producer/consumer + * Specified in standard CiA 301 + * @{ + */ +/** + * Configuration of @ref CO_TIME + * + * Possible flags, can be ORed: + * - CO_CONFIG_TIME_ENABLE - Enable TIME object and TIME consumer. + * - CO_CONFIG_TIME_PRODUCER - Enable TIME producer. + * - #CO_CONFIG_FLAG_CALLBACK_PRE - Enable custom callback after preprocessing + * received TIME CAN message. + * Callback is configured by CO_TIME_initCallbackPre(). + * - #CO_CONFIG_FLAG_OD_DYNAMIC - Enable dynamic configuration - writing to + * object 0x1012 enables / disables time producer or consumer. + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_TIME (CO_CONFIG_TIME_ENABLE | CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | CO_CONFIG_GLOBAL_FLAG_OD_DYNAMIC) +#endif +#define CO_CONFIG_TIME_ENABLE 0x01 +#define CO_CONFIG_TIME_PRODUCER 0x02 +/** @} */ /* CO_STACK_CONFIG_TIME */ + +/** + * @defgroup CO_STACK_CONFIG_SYNC_PDO SYNC and PDO producer/consumer + * Specified in standard CiA 301 + * @{ + */ +/** + * Configuration of @ref CO_SYNC + * + * Possible flags, can be ORed: + * - CO_CONFIG_SYNC_ENABLE - Enable SYNC object and SYNC consumer. + * - CO_CONFIG_SYNC_PRODUCER - Enable SYNC producer. + * - #CO_CONFIG_FLAG_CALLBACK_PRE - Enable custom callback after preprocessing + * received SYNC CAN message. + * Callback is configured by CO_SYNC_initCallbackPre(). + * - #CO_CONFIG_FLAG_TIMERNEXT - Enable calculation of timerNext_us variable + * inside CO_SYNC_process(). + * - #CO_CONFIG_FLAG_OD_DYNAMIC - Enable dynamic configuration of SYNC. + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_SYNC \ + (CO_CONFIG_SYNC_ENABLE | CO_CONFIG_SYNC_PRODUCER | CO_CONFIG_GLOBAL_RT_FLAG_CALLBACK_PRE \ + | CO_CONFIG_GLOBAL_FLAG_TIMERNEXT | CO_CONFIG_GLOBAL_FLAG_OD_DYNAMIC) +#endif +#define CO_CONFIG_SYNC_ENABLE 0x01 +#define CO_CONFIG_SYNC_PRODUCER 0x02 + +/** + * Configuration of @ref CO_PDO + * + * Possible flags, can be ORed: + * - CO_CONFIG_RPDO_ENABLE - Enable receive PDO objects. + * - CO_CONFIG_TPDO_ENABLE - Enable transmit PDO objects. + * - CO_CONFIG_RPDO_TIMERS_ENABLE - Enable RPDO timers: RPDO timeout monitoring + * with event timer. + * - CO_CONFIG_TPDO_TIMERS_ENABLE - Enable TPDO timers: TPDO inhibit and event + * timers. + * - CO_CONFIG_PDO_SYNC_ENABLE - Enable SYNC in PDO objects. + * - CO_CONFIG_PDO_OD_IO_ACCESS - For OD variables mapped to PDO use read/write + * function access with @ref OD_IO_t. This option enables much more + * flexibility for application program, but consumes some additional memory + * and processor resources. If this option is not enabled, then data from OD + * variables are fetched directly from memory allocated by Object dictionary. + * - #CO_CONFIG_FLAG_CALLBACK_PRE - Enable custom callback after preprocessing + * received RPDO CAN message. + * Callback is configured by CO_RPDO_initCallbackPre(). + * - #CO_CONFIG_FLAG_TIMERNEXT - Enable calculation of timerNext_us variable + * inside CO_TPDO_process(). + * - #CO_CONFIG_FLAG_OD_DYNAMIC - Enable dynamic configuration of PDO. + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_PDO \ + (CO_CONFIG_RPDO_ENABLE | CO_CONFIG_TPDO_ENABLE | CO_CONFIG_RPDO_TIMERS_ENABLE | CO_CONFIG_TPDO_TIMERS_ENABLE \ + | CO_CONFIG_PDO_SYNC_ENABLE | CO_CONFIG_PDO_OD_IO_ACCESS | CO_CONFIG_GLOBAL_RT_FLAG_CALLBACK_PRE \ + | CO_CONFIG_GLOBAL_FLAG_TIMERNEXT | CO_CONFIG_GLOBAL_FLAG_OD_DYNAMIC) +#endif +#define CO_CONFIG_RPDO_ENABLE 0x01 +#define CO_CONFIG_RPDO_TIMERS_ENABLE 0x04 +#define CO_CONFIG_TPDO_ENABLE 0x02 +#define CO_CONFIG_TPDO_TIMERS_ENABLE 0x08 +#define CO_CONFIG_PDO_SYNC_ENABLE 0x10 +#define CO_CONFIG_PDO_OD_IO_ACCESS 0x20 +/** @} */ /* CO_STACK_CONFIG_SYNC_PDO */ + +/** + * @defgroup CO_STACK_CONFIG_STORAGE Data storage + * Data storage with CANopen OD objects 1010 and 1011, CiA 301 + * @{ + */ +/** + * Configuration of @ref CO_storage + * + * Possible flags, can be ORed: + * - CO_CONFIG_STORAGE_ENABLE - Enable data storage + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_STORAGE (CO_CONFIG_STORAGE_ENABLE) +#endif +#define CO_CONFIG_STORAGE_ENABLE 0x01 +/** @} */ /* CO_STACK_CONFIG_STORAGE */ + +/** + * @defgroup CO_STACK_CONFIG_PROG_DOWNLOAD Program download (CiA 302-3) + * Specified in CiA 302-3: Configuration and Program download + * @{ + */ +/** + * Configuration of @ref CO_Prog_Download + * + * Possible flags, can be ORed: + * - CO_CONFIG_PROG_DOWNLOAD_ENABLE - Enable Program Download object (0x1F50, 0x1F51, 0x1F56, 0x1F57) + * - CO_CONFIG_PROG_DOWNLOAD_PERMANENT - Determines if program commit is permanent upgrade + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_PROG_DOWNLOAD (CO_CONFIG_PROG_DOWNLOAD_ENABLE | CO_CONFIG_PROG_DOWNLOAD_PERMANENT) +#endif +#define CO_CONFIG_PROG_DOWNLOAD_ENABLE 0x01 +#define CO_CONFIG_PROG_DOWNLOAD_PERMANENT 0x02 + +/** + * Maximum EDS file size in bytes. + * + * Used by CO_Prog_Download for OD entry 0x1F23 ("Store EDS NMT slave"). + * If the EDS data exceeds this limit, the download will be aborted + * with ODR_OUT_OF_MEM. + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_PROG_DOWNLOAD_EDS_MAX_SIZE 2048 +#endif + +/** @} */ /* CO_STACK_CONFIG_PROG_DOWNLOAD */ + +/** + * @defgroup CO_STACK_CONFIG_LEDS CANopen LED diodes + * Specified in standard CiA 303-3 + * @{ + */ +/** + * Configuration of @ref CO_LEDs + * + * Possible flags, can be ORed: + * - CO_CONFIG_LEDS_ENABLE - Enable calculation of the CANopen LED indicators. + * - CO_CONFIG_LEDS_CALLBACK - Enable application callback after LED state changes. + * Callback is configured by CO_LEDs_registerCallback(). It is invoked from + * CO_LEDs_process() after @ref CO_LEDs_t::LEDred and + * @ref CO_LEDs_t::LEDgreen are updated. This can be used to mirror the + * computed CANopen LED states to hardware (e.g. GPIO pins or OS-level LEDs). + * The callback receives a pointer to the @ref CO_LEDs_t object and a + * user-supplied argument. + * - #CO_CONFIG_FLAG_TIMERNEXT - Enable calculation of timerNext_us variable + * inside CO_NMT_process(). + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_LEDS (CO_CONFIG_LEDS_ENABLE | CO_CONFIG_LEDS_CALLBACK | CO_CONFIG_GLOBAL_FLAG_TIMERNEXT) +#endif +#define CO_CONFIG_LEDS_ENABLE 0x01 +#define CO_CONFIG_LEDS_CALLBACK 0x02 +/** @} */ /* CO_STACK_CONFIG_LEDS */ + +/** + * @defgroup CO_STACK_CONFIG_SRDO Safety Related Data Objects (SRDO) + * Specified in standard EN 50325-5 (CiA 304) + * @{ + */ +/** + * Configuration of @ref CO_GFC + * + * Possible flags, can be ORed: + * - CO_CONFIG_GFC_ENABLE - Enable the GFC object + * - CO_CONFIG_GFC_CONSUMER - Enable the GFC consumer + * - CO_CONFIG_GFC_PRODUCER - Enable the GFC producer + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_GFC (0) +#endif +#define CO_CONFIG_GFC_ENABLE 0x01 +#define CO_CONFIG_GFC_CONSUMER 0x02 +#define CO_CONFIG_GFC_PRODUCER 0x04 + +/** + * Configuration of @ref CO_SRDO + * + * Possible flags, can be ORed: + * - CO_CONFIG_SRDO_ENABLE - Enable the SRDO object. + * - CO_CONFIG_SRDO_CHECK_TX - Enable checking data before sending. + * - #CO_CONFIG_FLAG_CALLBACK_PRE - Enable custom callback after preprocessing + * received RSRDO CAN message. + * Callback is configured by CO_SRDO_initCallbackPre(). + * - #CO_CONFIG_FLAG_TIMERNEXT - Enable calculation of timerNext_us variable + * inside CO_SRDO_process() (Tx SRDO only). + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_SRDO (0) +#endif +#define CO_CONFIG_SRDO_ENABLE 0x01 +#define CO_CONFIG_SRDO_CHECK_TX 0x02 + +/** + * SRDO Tx time delay + * + * minimum time between the first and second SRDO (Tx) message + * in us + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_SRDO_MINIMUM_DELAY 0 +#endif +/** @} */ /* CO_STACK_CONFIG_SRDO */ + +/** + * @defgroup CO_STACK_CONFIG_LSS LSS master/slave + * Specified in standard CiA 305 + * @{ + */ +/** + * Configuration of @ref CO_LSS + * + * Possible flags, can be ORed: + * - CO_CONFIG_LSS_SLAVE - Enable LSS slave + * - CO_CONFIG_LSS_SLAVE_FASTSCAN_DIRECT_RESPOND - Send LSS fastscan respond + * directly from CO_LSSslave_receive() function. + * - CO_CONFIG_LSS_MASTER - Enable LSS master + * - #CO_CONFIG_FLAG_CALLBACK_PRE - Enable custom callback after preprocessing + * received CAN message. + * Callback is configured by CO_LSSmaster_initCallbackPre(). + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_LSS (CO_CONFIG_LSS_SLAVE | CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE) +#endif +#define CO_CONFIG_LSS_SLAVE 0x01 +#define CO_CONFIG_LSS_SLAVE_FASTSCAN_DIRECT_RESPOND 0x02 +#define CO_CONFIG_LSS_MASTER 0x10 +/** @} */ /* CO_STACK_CONFIG_LSS */ + +/** + * @defgroup CO_STACK_CONFIG_GATEWAY CANopen gateway + * Specified in standard CiA 309 + * @{ + */ +/** + * Configuration of @ref CO_CANopen_309_3 + * + * Gateway object is covered by standard CiA 309 - CANopen access from other + * networks. It enables usage of the NMT master, SDO client and LSS master as a + * gateway device. + * + * Possible flags, can be ORed: + * - CO_CONFIG_GTW_MULTI_NET - Enable multiple network interfaces in gateway + * device. This functionality is currently not implemented. + * - CO_CONFIG_GTW_ASCII - Enable gateway device with ASCII mapping (CiA 309-3) + * If set, then CO_CONFIG_FIFO_ASCII_COMMANDS must also be set. + * - CO_CONFIG_GTW_ASCII_SDO - Enable SDO client. If set, then + * CO_CONFIG_FIFO_ASCII_DATATYPES must also be set. + * - CO_CONFIG_GTW_ASCII_NMT - Enable NMT master + * - CO_CONFIG_GTW_ASCII_LSS - Enable LSS master + * - CO_CONFIG_GTW_ASCII_LOG - Enable non-standard message log read + * - CO_CONFIG_GTW_ASCII_ERROR_DESC - Print error description as additional + * comments in gateway-ascii device for SDO and gateway errors. + * - CO_CONFIG_GTW_ASCII_PRINT_HELP - use non-standard command "help" to print + * help usage. + * - CO_CONFIG_GTW_ASCII_PRINT_LEDS - Display "red" and "green" CANopen status + * LED diodes on terminal. + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_GTW (0) +#endif +#define CO_CONFIG_GTW_MULTI_NET 0x01 +#define CO_CONFIG_GTW_ASCII 0x02 +#define CO_CONFIG_GTW_ASCII_SDO 0x04 +#define CO_CONFIG_GTW_ASCII_NMT 0x08 +#define CO_CONFIG_GTW_ASCII_LSS 0x10 +#define CO_CONFIG_GTW_ASCII_LOG 0x20 +#define CO_CONFIG_GTW_ASCII_ERROR_DESC 0x40 +#define CO_CONFIG_GTW_ASCII_PRINT_HELP 0x80 +#define CO_CONFIG_GTW_ASCII_PRINT_LEDS 0x100 + +/** + * Number of loops of #CO_SDOclientDownload() in case of block download + * + * If SDO clint has block download in progress and OS has buffer for CAN tx + * messages, then #CO_SDOclientDownload() functionion can be called multiple + * times within own loop (up to 127). This can speed-up SDO block transfer. + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_GTW_BLOCK_DL_LOOP 1 +#endif + +/** + * Size of command buffer in ASCII gateway object. + * + * If large amount of data is transferred (block transfer), then this should be + * increased to 1000 or more. Buffer may be refilled between the block transfer. + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_GTWA_COMM_BUF_SIZE 200 +#endif + +/** + * Size of message log buffer in ASCII gateway object. + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_GTWA_LOG_BUF_SIZE 2000 +#endif +/** @} */ /* CO_STACK_CONFIG_GATEWAY */ + +/** + * @defgroup CO_STACK_CONFIG_CRC16 CRC 16 calculation + * Helper object for CRC-16 checksum + * @{ + */ +/** + * Configuration of @ref CO_crc16_ccitt calculation + * + * Possible flags, can be ORed: + * - CO_CONFIG_CRC16_ENABLE - Enable CRC16 calculation + * - CO_CONFIG_CRC16_EXTERNAL - CRC functions are defined externally + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_CRC16 (0) +#endif +#define CO_CONFIG_CRC16_ENABLE 0x01 +#define CO_CONFIG_CRC16_EXTERNAL 0x02 +/** @} */ /* CO_STACK_CONFIG_CRC16 */ + +/** + * @defgroup CO_STACK_CONFIG_FIFO FIFO buffer + * Helper object for FIFO buffer + * @{ + */ +/** + * Configuration of @ref CO_CANopen_301_fifo + * + * FIFO buffer is basically a simple first-in first-out circular data buffer. It + * is used by the SDO client and by the CANopen gateway. It has additional + * advanced functions for data passed to FIFO. + * + * + * Possible flags, can be ORed: + * - CO_CONFIG_FIFO_ENABLE - Enable FIFO buffer + * - CO_CONFIG_FIFO_ALT_READ - This must be enabled, when SDO client has + * CO_CONFIG_SDO_CLI_BLOCK enabled. See @ref CO_fifo_altRead(). + * - CO_CONFIG_FIFO_CRC16_CCITT - This must be enabled, when SDO client has + * CO_CONFIG_SDO_CLI_BLOCK enabled. It enables CRC calculation on data. + * - CO_CONFIG_FIFO_ASCII_COMMANDS - This must be enabled, when CANopen gateway + * has CO_CONFIG_GTW_ASCII enabled. It adds command handling functions. + * - CO_CONFIG_FIFO_ASCII_DATATYPES - This must be enabled, when CANopen gateway + * has CO_CONFIG_GTW_ASCII and CO_CONFIG_GTW_ASCII_SDO enabled. It adds + * datatype transform functions between binary and ascii, which are necessary + * for SDO client. + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_FIFO (0) +#endif +#define CO_CONFIG_FIFO_ENABLE 0x01 +#define CO_CONFIG_FIFO_ALT_READ 0x02 +#define CO_CONFIG_FIFO_CRC16_CCITT 0x04 +#define CO_CONFIG_FIFO_ASCII_COMMANDS 0x08 +#define CO_CONFIG_FIFO_ASCII_DATATYPES 0x10 +/** @} */ /* CO_STACK_CONFIG_FIFO */ + +/** + * @defgroup CO_STACK_CONFIG_TRACE Trace recorder + * Non standard object + * @{ + */ +/** + * Configuration of @ref CO_trace for recording variables over time. + * + * Possible flags, can be ORed: + * - CO_CONFIG_TRACE_ENABLE - Enable Trace recorder + * - CO_CONFIG_TRACE_OWN_INTTYPES - If set, then macros PRIu32("u" or "lu") + * and PRId32("d" or "ld") must be set. (File inttypes.h can not be included). + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_TRACE (0) +#endif +#define CO_CONFIG_TRACE_ENABLE 0x01 +#define CO_CONFIG_TRACE_OWN_INTTYPES 0x02 +/** @} */ /* CO_STACK_CONFIG_TRACE */ + +/** + * @defgroup CO_STACK_CONFIG_DEBUG Debug messages + * Messages from different parts of the stack. + * @{ + */ +/** + * Configuration of debug messages from different parts of the stack, which can + * be logged according to target specific function. + * + * Possible flags, can be ORed: + * - CO_CONFIG_DEBUG_COMMON - Define default CO_DEBUG_COMMON(msg) macro. This + * macro is target specific. This macro is then used as default macro in all + * other defined CO_DEBUG_XXX(msg) macros. + * - CO_CONFIG_DEBUG_SDO_CLIENT - Define default CO_DEBUG_SDO_CLIENT(msg) macro. + * - CO_CONFIG_DEBUG_SDO_SERVER - Define default CO_DEBUG_SDO_SERVER(msg) macro. + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_DEBUG (0) +#endif +#define CO_CONFIG_DEBUG_COMMON 0x01 +#define CO_CONFIG_DEBUG_SDO_CLIENT 0x02 +#define CO_CONFIG_DEBUG_SDO_SERVER 0x04 +/** @} */ /* CO_STACK_CONFIG_DEBUG */ + +/** @} */ /* CO_STACK_CONFIG */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CO_CONFIG_FLAGS_H */ diff --git a/301/CO_crc16-ccitt.c b/301/CO_crc16-ccitt.c new file mode 100644 index 00000000..2520479b --- /dev/null +++ b/301/CO_crc16-ccitt.c @@ -0,0 +1,83 @@ +/* + * Calculation of CRC 16 CCITT polynomial, x^16 + x^12 + x^5 + 1. + * + * @file crc16-ccitt.c + * @ingroup crc16-ccitt + * @author Janez Paternoster + * @copyright 2012 - 2020 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#include "301/CO_crc16-ccitt.h" + +#if ((CO_CONFIG_CRC16) & CO_CONFIG_CRC16_ENABLE) != 0 +#if ((CO_CONFIG_CRC16) & CO_CONFIG_CRC16_EXTERNAL) == 0 + +/* + * CRC table calculated by the following algorithm: + * + * void CO_crc16_ccitt_table_init(void){ + * uint16_t i, j; + * for(i=0; i<256; i++){ + * uint16_t crc = 0; + * uint16_t c = i << 8; + * for(j=0; j<8; j++){ + * if((crc ^ c) & 0x8000) crc = (crc << 1) ^ 0x1021; + * else crc = crc << 1; + * c = c << 1; + * } + * crc16_ccitt_table[i] = crc; + * } + * } + */ +static const uint16_t crc16_ccitt_table[256] = { + 0x0000U, 0x1021U, 0x2042U, 0x3063U, 0x4084U, 0x50A5U, 0x60C6U, 0x70E7U, 0x8108U, 0x9129U, 0xA14AU, 0xB16BU, 0xC18CU, + 0xD1ADU, 0xE1CEU, 0xF1EFU, 0x1231U, 0x0210U, 0x3273U, 0x2252U, 0x52B5U, 0x4294U, 0x72F7U, 0x62D6U, 0x9339U, 0x8318U, + 0xB37BU, 0xA35AU, 0xD3BDU, 0xC39CU, 0xF3FFU, 0xE3DEU, 0x2462U, 0x3443U, 0x0420U, 0x1401U, 0x64E6U, 0x74C7U, 0x44A4U, + 0x5485U, 0xA56AU, 0xB54BU, 0x8528U, 0x9509U, 0xE5EEU, 0xF5CFU, 0xC5ACU, 0xD58DU, 0x3653U, 0x2672U, 0x1611U, 0x0630U, + 0x76D7U, 0x66F6U, 0x5695U, 0x46B4U, 0xB75BU, 0xA77AU, 0x9719U, 0x8738U, 0xF7DFU, 0xE7FEU, 0xD79DU, 0xC7BCU, 0x48C4U, + 0x58E5U, 0x6886U, 0x78A7U, 0x0840U, 0x1861U, 0x2802U, 0x3823U, 0xC9CCU, 0xD9EDU, 0xE98EU, 0xF9AFU, 0x8948U, 0x9969U, + 0xA90AU, 0xB92BU, 0x5AF5U, 0x4AD4U, 0x7AB7U, 0x6A96U, 0x1A71U, 0x0A50U, 0x3A33U, 0x2A12U, 0xDBFDU, 0xCBDCU, 0xFBBFU, + 0xEB9EU, 0x9B79U, 0x8B58U, 0xBB3BU, 0xAB1AU, 0x6CA6U, 0x7C87U, 0x4CE4U, 0x5CC5U, 0x2C22U, 0x3C03U, 0x0C60U, 0x1C41U, + 0xEDAEU, 0xFD8FU, 0xCDECU, 0xDDCDU, 0xAD2AU, 0xBD0BU, 0x8D68U, 0x9D49U, 0x7E97U, 0x6EB6U, 0x5ED5U, 0x4EF4U, 0x3E13U, + 0x2E32U, 0x1E51U, 0x0E70U, 0xFF9FU, 0xEFBEU, 0xDFDDU, 0xCFFCU, 0xBF1BU, 0xAF3AU, 0x9F59U, 0x8F78U, 0x9188U, 0x81A9U, + 0xB1CAU, 0xA1EBU, 0xD10CU, 0xC12DU, 0xF14EU, 0xE16FU, 0x1080U, 0x00A1U, 0x30C2U, 0x20E3U, 0x5004U, 0x4025U, 0x7046U, + 0x6067U, 0x83B9U, 0x9398U, 0xA3FBU, 0xB3DAU, 0xC33DU, 0xD31CU, 0xE37FU, 0xF35EU, 0x02B1U, 0x1290U, 0x22F3U, 0x32D2U, + 0x4235U, 0x5214U, 0x6277U, 0x7256U, 0xB5EAU, 0xA5CBU, 0x95A8U, 0x8589U, 0xF56EU, 0xE54FU, 0xD52CU, 0xC50DU, 0x34E2U, + 0x24C3U, 0x14A0U, 0x0481U, 0x7466U, 0x6447U, 0x5424U, 0x4405U, 0xA7DBU, 0xB7FAU, 0x8799U, 0x97B8U, 0xE75FU, 0xF77EU, + 0xC71DU, 0xD73CU, 0x26D3U, 0x36F2U, 0x0691U, 0x16B0U, 0x6657U, 0x7676U, 0x4615U, 0x5634U, 0xD94CU, 0xC96DU, 0xF90EU, + 0xE92FU, 0x99C8U, 0x89E9U, 0xB98AU, 0xA9ABU, 0x5844U, 0x4865U, 0x7806U, 0x6827U, 0x18C0U, 0x08E1U, 0x3882U, 0x28A3U, + 0xCB7DU, 0xDB5CU, 0xEB3FU, 0xFB1EU, 0x8BF9U, 0x9BD8U, 0xABBBU, 0xBB9AU, 0x4A75U, 0x5A54U, 0x6A37U, 0x7A16U, 0x0AF1U, + 0x1AD0U, 0x2AB3U, 0x3A92U, 0xFD2EU, 0xED0FU, 0xDD6CU, 0xCD4DU, 0xBDAAU, 0xAD8BU, 0x9DE8U, 0x8DC9U, 0x7C26U, 0x6C07U, + 0x5C64U, 0x4C45U, 0x3CA2U, 0x2C83U, 0x1CE0U, 0x0CC1U, 0xEF1FU, 0xFF3EU, 0xCF5DU, 0xDF7CU, 0xAF9BU, 0xBFBAU, 0x8FD9U, + 0x9FF8U, 0x6E17U, 0x7E36U, 0x4E55U, 0x5E74U, 0x2E93U, 0x3EB2U, 0x0ED1U, 0x1EF0U}; + +void +CO_crc16_ccitt_single(uint16_t* crc, const uint8_t chr) { + uint8_t tmp = (uint8_t)(*crc >> 8U) ^ chr; + *crc = (uint16_t)((*crc << 8U) ^ crc16_ccitt_table[tmp]); +} + +uint16_t +CO_crc16_ccitt(const uint8_t block[], size_t blockLength, uint16_t crc) { + size_t i; + + for (i = 0U; i < blockLength; i++) { + uint8_t tmp = (uint8_t)((uint8_t)(crc >> 8U) ^ block[i]); + crc = (uint16_t)((crc << 8U) ^ crc16_ccitt_table[tmp]); + } + return crc; +} + +#endif /* !((CO_CONFIG_CRC16) & CO_CONFIG_CRC16_EXTERNAL) */ +#endif /* (CO_CONFIG_CRC16) & CO_CONFIG_CRC16_ENABLE */ diff --git a/301/CO_crc16-ccitt.h b/301/CO_crc16-ccitt.h new file mode 100644 index 00000000..12f1c6af --- /dev/null +++ b/301/CO_crc16-ccitt.h @@ -0,0 +1,80 @@ +/** + * Calculation of CRC 16 CCITT polynomial. + * + * @file crc16-ccitt.h + * @ingroup CO_crc16_ccitt + * @author Lammert Bies + * @author Janez Paternoster + * @copyright 2012 - 2020 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#ifndef CRC16_CCITT_H +#define CRC16_CCITT_H + +#include "301/CO_driver.h" + +/* default configuration, see CO_config.h */ +#ifndef CO_CONFIG_CRC16 +#define CO_CONFIG_CRC16 (0) +#endif + +#if (((CO_CONFIG_CRC16) & CO_CONFIG_CRC16_ENABLE) != 0) || defined CO_DOXYGEN + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup CO_crc16_ccitt CRC 16 CCITT + * Calculation of CRC 16 CCITT polynomial. + * + * @ingroup CO_CANopen_301 + * @{ + * Equation: + * + * `x^16 + x^12 + x^5 + 1` + */ + +/** + * Update crc16_ccitt variable with one data byte + * + * This function updates crc variable for one data byte using crc16 ccitt algorithm. + * + * @param [in,out] crc Externally defined variable for CRC checksum. Before start of new CRC calculation, variable must + * be initialized (zero for xmodem). + * @param chr One byte of data + */ +void CO_crc16_ccitt_single(uint16_t* crc, const uint8_t chr); + +/** + * Calculate CRC sum on block of data. + * + * @param block Pointer to block of data. + * @param blockLength Length of data in bytes; + * @param crc Initial value (zero for xmodem). If block is split into multiple segments, previous CRC is used as + * initial. + * + * @return Calculated CRC. + */ +uint16_t CO_crc16_ccitt(const uint8_t block[], size_t blockLength, uint16_t crc); + +/** @} */ /* CO_crc16_ccitt */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* (CO_CONFIG_CRC16) & CO_CONFIG_CRC16_ENABLE */ + +#endif /* CRC16_CCITT_H */ diff --git a/301/CO_driver.h b/301/CO_driver.h new file mode 100644 index 00000000..e48c98f0 --- /dev/null +++ b/301/CO_driver.h @@ -0,0 +1,657 @@ +/** + * Interface between CAN hardware and CANopenNode. + * + * @file CO_driver.h + * @ingroup CO_driver + * @author Janez Paternoster + * @copyright 2004 - 2020 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#ifndef CO_DRIVER_H +#define CO_DRIVER_H + +#include + +#include "CO_config.h" +#include "CO_driver_target.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Stack configuration default global values. For more information see file CO_config.h. */ +#ifndef CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE +#define CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE (0) +#endif +#ifndef CO_CONFIG_GLOBAL_RT_FLAG_CALLBACK_PRE +#define CO_CONFIG_GLOBAL_RT_FLAG_CALLBACK_PRE (0) +#endif +#ifndef CO_CONFIG_GLOBAL_FLAG_TIMERNEXT +#define CO_CONFIG_GLOBAL_FLAG_TIMERNEXT (0) +#endif +#ifndef CO_CONFIG_GLOBAL_FLAG_OD_DYNAMIC +#define CO_CONFIG_GLOBAL_FLAG_OD_DYNAMIC CO_CONFIG_FLAG_OD_DYNAMIC +#endif +#ifdef CO_DEBUG_COMMON +#if (CO_CONFIG_DEBUG) & CO_CONFIG_DEBUG_SDO_CLIENT +#define CO_DEBUG_SDO_CLIENT(msg) CO_DEBUG_COMMON(msg) +#endif +#if (CO_CONFIG_DEBUG) & CO_CONFIG_DEBUG_SDO_SERVER +#define CO_DEBUG_SDO_SERVER(msg) CO_DEBUG_COMMON(msg) +#endif +#endif + +/** + * @defgroup CO_driver Driver + * Interface between CAN hardware and CANopenNode. + * + * @ingroup CO_CANopen_301 + * @{ + * CANopenNode is designed for speed and portability. It runs efficiently on devices from simple 16-bit microcontrollers + * to PC computers. It can run in multiple threads. Reception of CAN messages is pre-processed with very fast functions. + * Time critical objects, such as PDO or SYNC are processed in real-time thread and other objects are processed in + * normal thread. See Flowchart in [README.md](index.html) for more information. + * + * @anchor CO_obj + * #### CANopenNode Object + * CANopenNode is implemented as a collection of different objects, for example SDO, SYNC, Emergency, PDO, NMT, + * Heartbeat, etc. Code is written in C language and tries to be object oriented. So each CANopenNode Object is + * implemented in a pair of .h/.c files. It basically contains a structure with all necessary variables and some + * functions which operates on it. CANopenNode Object is usually connected with one or more CAN receive or transmit + * Message Objects. (CAN message Object is a CAN message with specific 11-bit CAN identifier (usually one fixed or a + * range).) + * + * #### Hardware interface of CANopenNode + * It consists of minimum three files: + * - **CO_driver.h** file declares common functions. This file is part of the CANopenNode. It is included from each .c + * file from CANopenNode. + * - **CO_driver_target.h** file declares microcontroller specific type declarations and defines some macros, which are + * necessary for CANopenNode. This file is included from CO_driver.h. + * - **CO_driver.c** file defines functions declared in CO_driver.h. + * + * **CO_driver_target.h** and **CO_driver.c** files are specific for each different microcontroller and are not part of + * CANopenNode. There are separate projects for different microcontrollers, which usually include CANopenNode as a git + * submodule. CANopenNode only includes those two files in the `example` directory and they are basically empty. It + * should be possible to compile the `CANopenNode/example` on any system, however compiled program is not usable. + * CO_driver.h contains documentation for all necessary macros, types and functions. + * + * See [CANopenNode/Wiki](https://github.com/CANopenNode/CANopenNode/wiki) for a known list of available implementations + * of CANopenNode on different systems and microcontrollers. Everybody is welcome to extend the list with a link to his + * own implementation. + * + * Implementation of the hardware interface for specific microcontroller is not always an easy task. For reliable and + * efficient operation it is necessary to know some parts of the target microcontroller in detail (for example threads + * (or interrupts), CAN module, etc.). + */ + +/** Major version number of CANopenNode */ +#define CO_VERSION_MAJOR 4 +/** Minor version number of CANopenNode */ +#define CO_VERSION_MINOR 0 + +/* Macros and declarations in following part are only used for documentation. */ +#ifdef CO_DOXYGEN +/** + * @defgroup CO_dataTypes Basic definitions + * @{ + * + * Target specific basic definitions and data types. + * + * Must be defined in the **CO_driver_target.h** file. + * + * Depending on processor or compiler architecture, one of the two macros must be defined: CO_LITTLE_ENDIAN or + * CO_BIG_ENDIAN. CANopen itself is little endian. + * + * Basic data types may be specified differently on different architectures. Usually `true` and `false` are defined in + * ``, `NULL` is defined in ``, `int8_t` to `uint64_t` are defined in ``. + */ +#define CO_LITTLE_ENDIAN /**< CO_LITTLE_ENDIAN or CO_BIG_ENDIAN must be defined */ +#define CO_SWAP_16(x) x /**< Macro must swap bytes, if CO_BIG_ENDIAN is defined */ +#define CO_SWAP_32(x) x /**< Macro must swap bytes, if CO_BIG_ENDIAN is defined */ +#define CO_SWAP_64(x) x /**< Macro must swap bytes, if CO_BIG_ENDIAN is defined */ +#define NULL (0) /**< NULL, for general usage */ +#define true 1 /**< Logical true, for general use */ +#define false 0 /**< Logical false, for general use */ +typedef uint_fast8_t bool_t; /**< Boolean data type for general use */ +typedef signed char int8_t; /**< INTEGER8 in CANopen (0002h), 8-bit signed integer */ +typedef signed int int16_t; /**< INTEGER16 in CANopen (0003h), 16-bit signed integer */ +typedef signed long int int32_t; /**< INTEGER32 in CANopen (0004h), 32-bit signed integer */ +typedef signed long long int int64_t; /**< INTEGER64 in CANopen (0015h), 64-bit signed integer */ +typedef unsigned char uint8_t; /**< UNSIGNED8 in CANopen (0005h), 8-bit unsigned integer */ +typedef unsigned int uint16_t; /**< UNSIGNED16 in CANopen (0006h), 16-bit unsigned integer */ +typedef unsigned long int uint32_t; /**< UNSIGNED32 in CANopen (0007h), 32-bit unsigned integer */ +typedef unsigned long long int uint64_t; /**< UNSIGNED64 in CANopen (001Bh), 64-bit unsigned integer */ +typedef float float32_t; /**< REAL32 in CANopen (0008h), single precision floating point value, 32-bit */ +typedef double float64_t; /**< REAL64 in CANopen (0011h), double precision floating point value, 64-bit */ +/** @} */ + +/** + * @defgroup CO_CAN_Message_reception Reception of CAN messages + * @{ + * + * Target specific definitions and description of CAN message reception + * + * CAN messages in CANopenNode are usually received by its own thread or higher priority interrupt. Received CAN + * messages are first filtered by hardware or by software. Thread then examines its 11-bit CAN-id and mask and + * determines, to which \ref CO_obj "CANopenNode Object" it belongs to. After that it calls predefined CANrx_callback() + * function, which quickly pre-processes the message and fetches the relevant data. CANrx_callback() function is defined + * by each \ref CO_obj "CANopenNode Object" separately. Pre-processed fetched data are later processed in another + * thread. + * + * If \ref CO_obj "CANopenNode Object" reception of specific CAN message, it must first configure its own CO_CANrx_t + * object with the CO_CANrxBufferInit() function. + */ + +/** + * CAN receive callback function which pre-processes received CAN message + * + * It is called by fast CAN receive thread. Each \ref CO_obj "CANopenNode Object" defines its own and registers it with + * CO_CANrxBufferInit(), by passing function pointer. + * + * @param object pointer to specific \ref CO_obj "CANopenNode Object", registered with CO_CANrxBufferInit() + * @param rxMsg pointer to received CAN message + */ +void CANrx_callback(void* object, void* rxMsg); + +/** + * CANrx_callback() can read CAN identifier from received CAN message + * + * Must be defined in the **CO_driver_target.h** file. + * + * This is target specific function and is specific for specific microcontroller. It is best to implement it by using + * inline function or macro. `rxMsg` parameter should cast to a pointer to structure. For best efficiency structure may + * have the same alignment as CAN registers inside CAN module. + * + * @param rxMsg Pointer to received message + * @return 11-bit CAN standard identifier. + */ +static inline uint16_t +CO_CANrxMsg_readIdent(void* rxMsg) { + return 0; +} + +/** + * CANrx_callback() can read Data Length Code from received CAN message + * + * See also CO_CANrxMsg_readIdent(): + * + * @param rxMsg Pointer to received message + * @return data length in bytes (0 to 8) + */ +static inline uint8_t +CO_CANrxMsg_readDLC(void* rxMsg) { + return 0; +} + +/** + * CANrx_callback() can read pointer to data from received CAN message + * + * See also CO_CANrxMsg_readIdent(): + * + * @param rxMsg Pointer to received message + * @return pointer to data buffer + */ +static inline const uint8_t* +CO_CANrxMsg_readData(void* rxMsg) { + return NULL; +} + +/** + * Configuration object for CAN received message for specific \ref CO_obj "CANopenNode Object". + * + * Must be defined in the **CO_driver_target.h** file. + * + * Data fields of this structure are used exclusively by the driver. Usually it has the following data fields, but they + * may differ for different microcontrollers. Array of multiple CO_CANrx_t objects is included inside CO_CANmodule_t. + */ +typedef struct { + uint16_t ident; /**< Standard CAN Identifier (bits 0..10) + RTR (bit 11) */ + uint16_t mask; /**< Standard CAN Identifier mask with the same alignment as ident */ + void* object; /**< \ref CO_obj "CANopenNode Object" initialized in from CO_CANrxBufferInit() */ + void (*pCANrx_callback)(void* object, + void* message); /**< Pointer to CANrx_callback() initialized in CO_CANrxBufferInit() */ +} CO_CANrx_t; + +/** @} */ + +/** + * @defgroup CO_CAN_Message_transmission Transmission of CAN messages + * @{ + * + * Target specific definitions and description of CAN message transmission + * + * If \ref CO_obj "CANopenNode Object" needs transmitting CAN message, it must first configure its own CO_CANtx_t object + * with the CO_CANtxBufferInit() function. CAN message can then be sent with CO_CANsend() function. If at that moment + * CAN transmit buffer inside microcontroller's CAN module is free, message is copied directly to the CAN module. + * Otherwise CO_CANsend() function sets _bufferFull_ flag to true. Message will be then sent by CAN TX interrupt as soon + * as CAN module is freed. Until message is not copied to CAN module, its contents must not change. If there are + * multiple CO_CANtx_t objects with _bufferFull_ flag set to true, then CO_CANtx_t with lower index will be sent first. + */ + +/** + * Configuration object for CAN transmit message for specific \ref CO_obj "CANopenNode Object". + * + * Must be defined in the **CO_driver_target.h** file. + * + * Data fields of this structure are used exclusively by the driver. Usually it has the following data fields, but they + * may differ for different microcontrollers. Array of multiple CO_CANtx_t objects is included inside CO_CANmodule_t. + */ +typedef struct { + uint32_t ident; /**< CAN identifier as aligned in CAN module */ + uint8_t DLC; /**< Length of CAN message */ + uint8_t data[8]; /**< 8 data bytes */ + volatile bool_t bufferFull; /**< True if previous message is still in the buffer */ + volatile bool_t syncFlag; /**< Synchronous PDO messages has this flag set. It prevents them to be sent outside the + synchronous window */ +} CO_CANtx_t; + +/** @} */ + +/** + * Complete CAN module object. + * + * Must be defined in the **CO_driver_target.h** file. + * + * Usually it has the following data fields, but they may differ for different microcontrollers. + */ +typedef struct { + void* CANptr; /**< From CO_CANmodule_init() */ + CO_CANrx_t* rxArray; /**< From CO_CANmodule_init() */ + uint16_t rxSize; /**< From CO_CANmodule_init() */ + CO_CANtx_t* txArray; /**< From CO_CANmodule_init() */ + uint16_t txSize; /**< From CO_CANmodule_init() */ + uint16_t CANerrorStatus; /**< CAN error status bitfield, see @ref CO_CAN_ERR_status_t */ + volatile bool_t CANnormal; /**< CAN module is in normal mode */ + volatile bool_t useCANrxFilters; /**< Value different than zero indicates, that CAN module hardware filters are used + for CAN reception. If there is not enough hardware filters, they won't be used. + In this case will be *all* received CAN messages processed by software. */ + volatile bool_t bufferInhibitFlag; /**< If flag is true, then message in transmit buffer is synchronous PDO message, + which will be aborted, if CO_clearPendingSyncPDOs() function will be called by + application. This may be necessary if Synchronous window time was expired. */ + volatile bool_t + firstCANtxMessage; /**< Equal to 1, when the first transmitted message (bootup message) is in CAN TX buffers */ + volatile uint16_t + CANtxCount; /**< Number of messages in transmit buffer, which are waiting to be copied to the CAN module */ + uint32_t errOld; /**< Previous state of CAN errors */ +} CO_CANmodule_t; + +/** + * Data storage object for one entry. + * + * Must be defined in the **CO_driver_target.h** file. + * + * For more information on Data storage see @ref CO_storage or **CO_storage.h** file. Structure members documented here + * are always required or required with @ref CO_storage_eeprom. Target system may add own additional, hardware specific + * variables. + */ +typedef struct { + void* addr; /**< Address of data to store, always required. */ + size_t len; /**< Length of data to store, always required. */ + uint8_t subIndexOD; /**< Sub index in OD objects 1010 and 1011, from 2 to 127. Writing 0x65766173 to 1010,subIndexOD + will store data to non-volatile memory Writing 0x64616F6C to 1011,subIndexOD will restore + default data, always required. */ + uint8_t attr; /**< Attribute from @ref CO_storage_attributes_t, always required. */ + void* storageModule; /**< Pointer to storage module, target system specific usage, required with @ref + CO_storage_eeprom. */ + uint16_t crc; /**< CRC checksum of the data stored in eeprom, set on store, required with @ref CO_storage_eeprom. */ + size_t eepromAddrSignature; /**< Address of entry signature inside eeprom, set by init, required with @ref + CO_storage_eeprom. */ + size_t eepromAddr; /**< Address of data inside eeprom, set by init, required with @ref CO_storage_eeprom. */ + size_t offset; /**< Offset of next byte being updated by automatic storage, required with @ref CO_storage_eeprom. */ + void* additionalParameters; /**< Additional target specific parameters, optional. */ +} CO_storage_entry_t; + +/** + * @defgroup CO_critical_sections Critical sections + * @{ + * + * Protection of critical sections in multi-threaded operation. + * + * CANopenNode is designed to run in different threads, as described in [README.md](index.html). Threads are implemented + * differently in different systems. In microcontrollers threads are interrupts with different priorities, for example. + * It is necessary to protect sections, where different threads access to the same resource. In simple systems + * interrupts or scheduler may be temporary disabled between access to the shared resource. Otherwise mutexes or + * semaphores can be used. + * + * #### Reentrant functions + * Functions CO_CANsend() from C_driver.h, and CO_error() from CO_Emergency.h may be called from different threads. + * Critical sections must be protected. Either by disabling scheduler or interrupts or by mutexes or semaphores. + * Lock/unlock macro is called with pointer to CAN module, which may be used inside. + * + * #### Object Dictionary variables + * In general, there are two threads, which accesses OD variables: mainline (initialization, storage, SDO access) and + * timer (PDO access). CANopenNode uses locking mechanism, where SDO server (or other mainline code) prevents execution + * of the real-time thread at the moment it reads or writes OD variable. CO_LOCK_OD(CAN_MODULE) and + * CO_UNLOCK_OD(CAN_MODULE) macros are used to protect: + * - Whole real-time thread, + * - SDO server protects read/write access to OD variable. Locking of long OD variables, not accessible from real-time + * thread, may block RT thread. + * - Any mainline code, which accesses PDO-mappable OD variable, must protect read/write with locking macros. Use @ref + * OD_mappable() for check. + * - Other cases, where non-PDO-mappable OD variable is used inside real-time thread by some other part of the user + * application must be considered with special care. Also when there are multiple threads accessing the OD + * (e.g. when using a RTOS), you should always lock the OD. + * + * #### Synchronization functions for CAN receive + * After CAN message is received, it is pre-processed in CANrx_callback(), which copies some data into appropriate + * object and at the end sets **new_message** flag. This flag is then pooled in another thread, which further processes + * the message. The problem is, that compiler optimization may shuffle memory operations, so it is necessary to ensure, + * that **new_message** flag is surely set at the end. It is necessary to use [Memory + * barrier](https://en.wikipedia.org/wiki/Memory_barrier). + * + * If receive function runs inside IRQ, no further synchronization is needed. Otherwise, some kind of synchronization + * has to be included. The following example uses GCC builtin memory barrier `__sync_synchronize()`. More information + * can be found [here](https://stackoverflow.com/questions/982129/what-does-sync-synchronize-do#982179). + */ + +#define CO_LOCK_CAN_SEND(CAN_MODULE) /**< Lock critical section in CO_CANsend() */ +#define CO_UNLOCK_CAN_SEND(CAN_MODULE) /**< Unlock critical section in CO_CANsend() */ +#define CO_LOCK_EMCY(CAN_MODULE) /**< Lock critical section in CO_errorReport() or CO_errorReset() */ +#define CO_UNLOCK_EMCY(CAN_MODULE) /**< Unlock critical section in CO_errorReport() or CO_errorReset() */ +#define CO_LOCK_OD(CAN_MODULE) /**< Lock critical section when accessing Object Dictionary */ +#define CO_UNLOCK_OD(CAN_MODULE) /**< Unock critical section when accessing Object Dictionary */ + +/** Check if new message has arrived */ +#define CO_FLAG_READ(rxNew) ((rxNew) != NULL) +/** Set new message flag */ +#define CO_FLAG_SET(rxNew) \ + { \ + __sync_synchronize(); \ + rxNew = (void*)1L; \ + } +/** Clear new message flag */ +#define CO_FLAG_CLEAR(rxNew) \ + { \ + __sync_synchronize(); \ + rxNew = NULL; \ + } + +/** @} */ +#endif /* CO_DOXYGEN */ + +/** + * @defgroup CO_Default_CAN_ID_t Default CANopen identifiers + * @{ + * + * Default CANopen identifiers for CANopen communication objects. Same as 11-bit addresses of CAN messages. These are + * default identifiers and can be changed in CANopen. Especially PDO identifiers are configured in PDO linking phase of + * the CANopen network configuration. + */ +#define CO_CAN_ID_NMT_SERVICE 0x000U /**< 0x000 Network management */ +#define CO_CAN_ID_GFC 0x001U /**< 0x001 Global fail-safe command */ +#define CO_CAN_ID_SYNC 0x080U /**< 0x080 Synchronous message */ +#define CO_CAN_ID_EMERGENCY 0x080U /**< 0x080 Emergency messages (+nodeID) */ +#define CO_CAN_ID_TIME 0x100U /**< 0x100 Time message */ +#define CO_CAN_ID_SRDO_1 0x0FFU /**< 0x0FF Default SRDO1 (+2*nodeID) */ +#define CO_CAN_ID_TPDO_1 0x180U /**< 0x180 Default TPDO1 (+nodeID) */ +#define CO_CAN_ID_RPDO_1 0x200U /**< 0x200 Default RPDO1 (+nodeID) */ +#define CO_CAN_ID_TPDO_2 0x280U /**< 0x280 Default TPDO2 (+nodeID) */ +#define CO_CAN_ID_RPDO_2 0x300U /**< 0x300 Default RPDO2 (+nodeID) */ +#define CO_CAN_ID_TPDO_3 0x380U /**< 0x380 Default TPDO3 (+nodeID) */ +#define CO_CAN_ID_RPDO_3 0x400U /**< 0x400 Default RPDO3 (+nodeID) */ +#define CO_CAN_ID_TPDO_4 0x480U /**< 0x480 Default TPDO4 (+nodeID) */ +#define CO_CAN_ID_RPDO_4 0x500U /**< 0x500 Default RPDO5 (+nodeID) */ +#define CO_CAN_ID_SDO_SRV 0x580U /**< 0x580 SDO response from server (+nodeID) */ +#define CO_CAN_ID_SDO_CLI 0x600U /**< 0x600 SDO request from client (+nodeID) */ +#define CO_CAN_ID_HEARTBEAT 0x700U /**< 0x700 Heartbeat message */ +#define CO_CAN_ID_LSS_SLV 0x7E4U /**< 0x7E4 LSS response from slave */ +#define CO_CAN_ID_LSS_MST 0x7E5U /**< 0x7E5 LSS request from master */ + +/** @} */ /* CO_Default_CAN_ID_t */ + +/** + * Restricted CAN-IDs + * + * Macro for verifying 'Restricted CAN-IDs', as specified by standard CiA301. They shall not be used for SYNC, TIME, + * EMCY, PDO and SDO. + */ +#ifndef CO_IS_RESTRICTED_CAN_ID +#define CO_IS_RESTRICTED_CAN_ID(CAN_ID) \ + (((CAN_ID) <= 0x7FU) || (((CAN_ID) >= 0x101U) && ((CAN_ID) <= 0x180U)) \ + || (((CAN_ID) >= 0x581U) && ((CAN_ID) <= 0x5FFU)) || (((CAN_ID) >= 0x601U) && ((CAN_ID) <= 0x67FU)) \ + || (((CAN_ID) >= 0x6E0U) && ((CAN_ID) <= 0x6FFU)) || ((CAN_ID) >= 0x701U)) +#endif + +/** + * @defgroup CO_CAN_ERR_status_t CAN error status bitmasks + * @{ + * + * CAN warning level is reached, if CAN transmit or receive error counter is more or equal to 96. CAN passive level is + * reached, if counters are more or equal to 128. Transmitter goes in error state 'bus off' if transmit error counter is + * more or equal to 256. + */ +#define CO_CAN_ERRTX_WARNING 0x0001U /**< 0x0001 CAN transmitter warning */ +#define CO_CAN_ERRTX_PASSIVE 0x0002U /**< 0x0002 CAN transmitter passive */ +#define CO_CAN_ERRTX_BUS_OFF 0x0004U /**< 0x0004 CAN transmitter bus off */ +#define CO_CAN_ERRTX_OVERFLOW 0x0008U /**< 0x0008 CAN transmitter overflow */ +#define CO_CAN_ERRTX_PDO_LATE 0x0080U /**< 0x0080 TPDO is outside sync window */ +#define CO_CAN_ERRRX_WARNING 0x0100U /**< 0x0100 CAN receiver warning */ +#define CO_CAN_ERRRX_PASSIVE 0x0200U /**< 0x0200 CAN receiver passive */ +#define CO_CAN_ERRRX_OVERFLOW 0x0800U /**< 0x0800 CAN receiver overflow */ +#define CO_CAN_ERR_WARN_PASSIVE 0x0303U /**< 0x0303 combination */ + +/** @} */ /* CO_CAN_ERR_status_t */ + +/** + * Return values of some CANopen functions. If function was executed successfully it returns 0 otherwise it returns <0. + */ +typedef enum { + CO_ERROR_NO = 0, /**< Operation completed successfully */ + CO_ERROR_ILLEGAL_ARGUMENT = -1, /**< Error in function arguments */ + CO_ERROR_OUT_OF_MEMORY = -2, /**< Memory allocation failed */ + CO_ERROR_TIMEOUT = -3, /**< Function timeout */ + CO_ERROR_ILLEGAL_BAUDRATE = -4, /**< Illegal baudrate passed to function CO_CANmodule_init() */ + CO_ERROR_RX_OVERFLOW = -5, /**< Previous message was not processed yet */ + CO_ERROR_RX_PDO_OVERFLOW = -6, /**< previous PDO was not processed yet */ + CO_ERROR_RX_MSG_LENGTH = -7, /**< Wrong receive message length */ + CO_ERROR_RX_PDO_LENGTH = -8, /**< Wrong receive PDO length */ + CO_ERROR_TX_OVERFLOW = -9, /**< Previous message is still waiting, buffer full */ + CO_ERROR_TX_PDO_WINDOW = -10, /**< Synchronous TPDO is outside window */ + CO_ERROR_TX_UNCONFIGURED = -11, /**< Transmit buffer was not configured properly */ + CO_ERROR_OD_PARAMETERS = -12, /**< Error in Object Dictionary parameters */ + CO_ERROR_DATA_CORRUPT = -13, /**< Stored data are corrupt */ + CO_ERROR_CRC = -14, /**< CRC does not match */ + CO_ERROR_TX_BUSY = -15, /**< Sending rejected because driver is busy. Try again */ + CO_ERROR_WRONG_NMT_STATE = -16, /**< Command can't be processed in current state */ + CO_ERROR_SYSCALL = -17, /**< Syscall failed */ + CO_ERROR_INVALID_STATE = -18, /**< Driver not ready */ + CO_ERROR_NODE_ID_UNCONFIGURED_LSS = + -19 /**< Node-id is in LSS unconfigured state. If objects are handled properly, this may not be an error. */ +} CO_ReturnError_t; + +/** + * Request CAN configuration (stopped) mode and *wait* until it is set. + * + * @param CANptr Pointer to CAN device + */ +void CO_CANsetConfigurationMode(void* CANptr); + +/** + * Request CAN normal (operational) mode and *wait* until it is set. + * + * @param CANmodule CO_CANmodule_t object. + */ +void CO_CANsetNormalMode(CO_CANmodule_t* CANmodule); + +/** + * Initialize CAN module object. + * + * Function must be called in the communication reset section. CAN module must be in Configuration Mode before. + * + * @param CANmodule This object will be initialized. + * @param CANptr Pointer to CAN device. + * @param rxArray Array for handling received CAN messages + * @param rxSize Size of the above array. Must be equal to number of receiving CAN objects. + * @param txArray Array for handling transmitting CAN messages + * @param txSize Size of the above array. Must be equal to number of transmitting CAN objects. + * @param CANbitRate Valid values are (in kbps): 10, 20, 50, 125, 250, 500, 800, 1000. If value is illegal, bitrate + * defaults to 125. + * + * Return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT. + */ +CO_ReturnError_t CO_CANmodule_init(CO_CANmodule_t* CANmodule, void* CANptr, CO_CANrx_t rxArray[], uint16_t rxSize, + CO_CANtx_t txArray[], uint16_t txSize, uint16_t CANbitRate); + +/** + * Switch off CANmodule. Call at program exit. + * + * @param CANmodule CAN module object. + */ +void CO_CANmodule_disable(CO_CANmodule_t* CANmodule); + +/** + * Configure CAN message receive buffer. + * + * Function configures specific CAN receive buffer. It sets CAN identifier and connects buffer with specific object. + * Function must be called for each member in _rxArray_ from CO_CANmodule_t. + * + * @param CANmodule This object. + * @param index Index of the specific buffer in _rxArray_. + * @param ident 11-bit standard CAN Identifier. If two or more CANrx buffers have the same _ident_, then buffer with + * lowest _index_ has precedence and other CANrx buffers will be ignored. + * @param mask 11-bit mask for identifier. Most usually set to 0x7FF. Received message (rcvMsg) will be accepted if the + * following condition is true: (((rcvMsgId ^ ident) & mask) == 0). + * @param rtr If true, 'Remote Transmit Request' messages will be accepted. + * @param object CANopen object, to which buffer is connected. It will be used as an argument to CANrx_callback. Its + * type is (void), CANrx_callback will change its type back to the correct object type. + * @param CANrx_callback Pointer to function, which will be called, if received CAN message matches the identifier. It + * must be fast function. + * + * Return #CO_ReturnError_t: CO_ERROR_NO CO_ERROR_ILLEGAL_ARGUMENT or CO_ERROR_OUT_OF_MEMORY (not enough masks for + * configuration). + */ +CO_ReturnError_t CO_CANrxBufferInit(CO_CANmodule_t* CANmodule, uint16_t index, uint16_t ident, uint16_t mask, + bool_t rtr, void* object, void (*CANrx_callback)(void* object, void* message)); + +/** + * Configure CAN message transmit buffer. + * + * Function configures specific CAN transmit buffer. Function must be called for each member in _txArray_ from + * CO_CANmodule_t. + * + * @param CANmodule This object. + * @param index Index of the specific buffer in _txArray_. + * @param ident 11-bit standard CAN Identifier. + * @param rtr If true, 'Remote Transmit Request' messages will be transmitted. + * @param noOfBytes Length of CAN message in bytes (0 to 8 bytes). + * @param syncFlag This flag bit is used for synchronous TPDO messages. If it is set, message will not be sent, if + * current time is outside synchronous window. + * + * @return Pointer to CAN transmit message buffer. 8 bytes data array inside buffer should be written, before + * CO_CANsend() function is called. Zero is returned in case of wrong arguments. + */ +CO_CANtx_t* CO_CANtxBufferInit(CO_CANmodule_t* CANmodule, uint16_t index, uint16_t ident, bool_t rtr, uint8_t noOfBytes, + bool_t syncFlag); + +/** + * Send CAN message. + * + * @param CANmodule This object. + * @param buffer Pointer to transmit buffer, returned by CO_CANtxBufferInit(). Data bytes must be written in buffer + * before function call. + * + * @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_TX_OVERFLOW or CO_ERROR_TX_PDO_WINDOW (Synchronous TPDO is outside + * window). + */ +CO_ReturnError_t CO_CANsend(CO_CANmodule_t* CANmodule, CO_CANtx_t* buffer); + +/** + * Clear all synchronous TPDOs from CAN module transmit buffers. + * + * CANopen allows synchronous PDO communication only inside time between SYNC message and SYNC Window. If time is + * outside this window, new synchronous PDOs must not be sent and all pending sync TPDOs, which may be on CAN TX + * buffers, may optionally be cleared. + * + * This function checks (and aborts transmission if necessary) CAN TX buffers when it is called. Function should be + * called by the stack in the moment, when SYNC time was just passed out of synchronous window. + * + * @param CANmodule This object. + */ +void CO_CANclearPendingSyncPDOs(CO_CANmodule_t* CANmodule); + +/** + * Process can module - verify CAN errors + * + * Function must be called cyclically. It should calculate CANerrorStatus bitfield for CAN errors defined in @ref + * CO_CAN_ERR_status_t. + * + * @param CANmodule This object. + */ +void CO_CANmodule_process(CO_CANmodule_t* CANmodule); + +/** + * Get uint8_t value from memory buffer + * + * @param buf Memory buffer to get value from. + * + * @return Value + */ +static inline uint8_t +CO_getUint8(const void* buf) { + uint8_t value; + (void)memmove((void*)&value, buf, sizeof(value)); + return value; +} + +/** Get uint16_t value from memory buffer, see @ref CO_getUint8 */ +static inline uint16_t +CO_getUint16(const void* buf) { + uint16_t value; + (void)memmove((void*)&value, buf, sizeof(value)); + return value; +} + +/** Get uint32_t value from memory buffer, see @ref CO_getUint8 */ +static inline uint32_t +CO_getUint32(const void* buf) { + uint32_t value; + (void)memmove((void*)&value, buf, sizeof(value)); + return value; +} + +/** + * Write uint8_t value into memory buffer + * + * @param buf Memory buffer. + * @param value Value to be written into buf. + * + * @return number of bytes written. + */ +static inline uint8_t +CO_setUint8(void* buf, uint8_t value) { + (void)memmove(buf, (const void*)&value, sizeof(value)); + return (uint8_t)(sizeof(value)); +} + +/** Write uint16_t value into memory buffer, see @ref CO_setUint8 */ +static inline uint8_t +CO_setUint16(void* buf, uint16_t value) { + (void)memmove(buf, (const void*)&value, sizeof(value)); + return (uint8_t)(sizeof(value)); +} + +/** Write uint32_t value into memory buffer, see @ref CO_setUint8 */ +static inline uint8_t +CO_setUint32(void* buf, uint32_t value) { + (void)memmove(buf, (const void*)&value, sizeof(value)); + return (uint8_t)(sizeof(value)); +} + +/** @} */ /* CO_driver */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CO_DRIVER_H */ diff --git a/301/CO_fifo.c b/301/CO_fifo.c new file mode 100644 index 00000000..69e032b9 --- /dev/null +++ b/301/CO_fifo.c @@ -0,0 +1,1493 @@ +/* + * FIFO circular buffer + * + * @file CO_fifo.c + * @ingroup CO_CANopen_309_fifo + * @author Janez Paternoster + * @copyright 2020 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#include + +#include "301/CO_fifo.h" + +#if ((CO_CONFIG_FIFO)&CO_CONFIG_FIFO_ENABLE) != 0 + +#include +#include +#include "CO_crc16-ccitt.h" + +#if ((CO_CONFIG_FIFO)&CO_CONFIG_FIFO_ASCII_COMMANDS) != 0 +#include +#include + +/* Non-graphical character for command delimiter */ +#define DELIM_COMMAND ((uint8_t)'\n') +/* Graphical character for comment delimiter */ +#define DELIM_COMMENT ((uint8_t)'#') +/* Graphical character for double quotes */ +#define DELIM_DQUOTE ((uint8_t)'"') +#endif /* (CO_CONFIG_FIFO) & CO_CONFIG_FIFO_ASCII_COMMANDS */ + +/* verify configuration */ +#if ((CO_CONFIG_FIFO)&CO_CONFIG_FIFO_CRC16_CCITT) != 0 +#if ((CO_CONFIG_CRC16)&CO_CONFIG_CRC16_ENABLE) == 0 +#error CO_CONFIG_CRC16_ENABLE must be enabled. +#endif +#endif + +void +CO_fifo_init(CO_fifo_t* fifo, uint8_t* buf, size_t bufSize) { + + if ((fifo == NULL) || (buf == NULL) || (bufSize < 2U)) { + return; + } + + fifo->readPtr = 0; + fifo->writePtr = 0; + fifo->buf = buf; + fifo->bufSize = bufSize; + + return; +} + +/* Circular FIFO buffer example for fifo->bufSize = 7 (usable size = 6): ****** + * * + * 0 * * * * * + * 1 rp==wp readPtr writePtr * * + * 2 * * * * * + * 3 * * * writePtr * + * 4 * writePtr readPtr readPtr * + * 5 * * * * * + * 6 * * * * * + * * + * empty 3 bytes 4 bytes buffer * + * buffer in buff in buff full * + ******************************************************************************/ +size_t +CO_fifo_write(CO_fifo_t* fifo, const uint8_t* buf, size_t count, uint16_t* crc) { + size_t i; + uint8_t* bufDest; + + if ((fifo == NULL) || (fifo->buf == NULL) || (buf == NULL)) { + return 0; + } + + bufDest = &fifo->buf[fifo->writePtr]; + for (i = count; i > 0U; i--) { + size_t writePtrNext = fifo->writePtr + 1U; + + /* is circular buffer full */ + if ((writePtrNext == fifo->readPtr) || ((writePtrNext == fifo->bufSize) && (fifo->readPtr == 0U))) { + break; + } + + *bufDest = *buf; + +#if ((CO_CONFIG_FIFO)&CO_CONFIG_FIFO_CRC16_CCITT) != 0 + if (crc != NULL) { + CO_crc16_ccitt_single(crc, *buf); + } +#endif + + /* increment variables */ + if (writePtrNext == fifo->bufSize) { + fifo->writePtr = 0; + bufDest = &fifo->buf[0]; + } else { + fifo->writePtr++; + bufDest++; + } + buf++; + } + + return count - i; +} + +size_t +CO_fifo_read(CO_fifo_t* fifo, uint8_t* buf, size_t count, bool_t* eof) { + size_t i; + const uint8_t* bufSrc; + bool_t alive_cycle = true; + + if (eof != NULL) { + *eof = false; + } + if ((fifo == NULL) || (buf == NULL) || (fifo->readPtr == fifo->writePtr)) { + return 0; + } + + bufSrc = &fifo->buf[fifo->readPtr]; + for (i = count; (i > 0U) && alive_cycle;) { + const uint8_t c = *bufSrc; + + /* is circular buffer empty */ + if (fifo->readPtr == fifo->writePtr) { + alive_cycle = false; + } else { + *buf = c; + buf++; + + /* increment variables */ + if (++fifo->readPtr == fifo->bufSize) { + fifo->readPtr = 0; + bufSrc = &fifo->buf[0]; + } else { + bufSrc++; + } + i--; + +#if ((CO_CONFIG_FIFO)&CO_CONFIG_FIFO_ASCII_COMMANDS) != 0 + /* is delimiter? */ + if ((eof != NULL) && (c == DELIM_COMMAND)) { + *eof = true; + alive_cycle = false; + } +#endif + } + } + + return count - i; +} + +#if ((CO_CONFIG_FIFO)&CO_CONFIG_FIFO_ALT_READ) != 0 +size_t +CO_fifo_altBegin(CO_fifo_t* fifo, size_t offset) { + size_t i; + + if (fifo == NULL) { + return 0; + } + + fifo->altReadPtr = fifo->readPtr; + for (i = offset; i > 0U; i--) { + /* is circular buffer empty */ + if (fifo->altReadPtr == fifo->writePtr) { + break; + } + + /* increment variable */ + if (++fifo->altReadPtr == fifo->bufSize) { + fifo->altReadPtr = 0; + } + } + + return offset - i; +} + +void +CO_fifo_altFinish(CO_fifo_t* fifo, uint16_t* crc) { + + if (fifo == NULL) { + return; + } + + if (crc == NULL) { + fifo->readPtr = fifo->altReadPtr; + } else { + const uint8_t* bufSrc = &fifo->buf[fifo->readPtr]; + while (fifo->readPtr != fifo->altReadPtr) { +#if ((CO_CONFIG_FIFO)&CO_CONFIG_FIFO_CRC16_CCITT) != 0 + CO_crc16_ccitt_single(crc, *bufSrc); +#endif + /* increment variable */ + if (++fifo->readPtr == fifo->bufSize) { + fifo->readPtr = 0; + bufSrc = &fifo->buf[0]; + } else { + bufSrc++; + } + } + } +} + +size_t +CO_fifo_altRead(CO_fifo_t* fifo, uint8_t* buf, size_t count) { + size_t i; + const uint8_t* bufSrc; + + bufSrc = &fifo->buf[fifo->altReadPtr]; + for (i = count; i > 0U; i--) { + const uint8_t c = *bufSrc; + + /* is there no more data */ + if (fifo->altReadPtr == fifo->writePtr) { + break; + } + + *buf = c; + buf++; + + /* increment variables */ + if (++fifo->altReadPtr == fifo->bufSize) { + fifo->altReadPtr = 0; + bufSrc = &fifo->buf[0]; + } else { + bufSrc++; + } + } + + return count - i; +} +#endif /* (CO_CONFIG_FIFO) & CO_CONFIG_FIFO_ALT_READ */ + +#if ((CO_CONFIG_FIFO)&CO_CONFIG_FIFO_ASCII_COMMANDS) != 0 +bool_t +CO_fifo_CommSearch(CO_fifo_t* fifo, bool_t clear) { + bool_t newCommand = false; + size_t count; + uint8_t* commandEnd; + + if ((fifo == NULL) || (fifo->readPtr == fifo->writePtr)) { + return false; + } + + /* search delimiter until writePtr or until end of buffer */ + if (fifo->readPtr < fifo->writePtr) { + count = fifo->writePtr - fifo->readPtr; + } else { + count = fifo->bufSize - fifo->readPtr; + } + commandEnd = (uint8_t*)memchr((const void*)&fifo->buf[fifo->readPtr], (int32_t)DELIM_COMMAND, count); + if (commandEnd != NULL) { + newCommand = true; + } else if (fifo->readPtr > fifo->writePtr) { + /* not found, search in the beginning of the circular buffer */ + commandEnd = (uint8_t*)memchr((const void*)&fifo->buf[0], (int32_t)DELIM_COMMAND, fifo->writePtr); + if ((commandEnd != NULL) || (fifo->readPtr == (fifo->writePtr + 1U))) { + /* command delimiter found or buffer full */ + newCommand = true; + } + } else if ((fifo->readPtr == 0U) && (fifo->writePtr == (fifo->bufSize - 1U))) { + /* buffer full */ + newCommand = true; + } else { /* MISRA C 2004 14.10 */ + } + + /* Clear buffer if set so */ + if (clear) { + if (commandEnd != NULL) { + fifo->readPtr = ((size_t)commandEnd - (size_t)fifo->buf) + 1U; + if (fifo->readPtr == fifo->bufSize) { + fifo->readPtr = 0; + } + } else { + fifo->readPtr = fifo->writePtr; + } + } + + return newCommand; +} + +bool_t +CO_fifo_trimSpaces(CO_fifo_t* fifo, bool_t* insideComment) { + bool_t delimCommandFound = false; + bool_t alive_cycle = true; + + if ((fifo != NULL) && (insideComment != NULL)) { + while ((fifo->readPtr != fifo->writePtr) && alive_cycle) { + uint8_t c = fifo->buf[fifo->readPtr]; + + if (c == DELIM_COMMENT) { + *insideComment = true; + } else if ((isgraph((int)c) != 0) && !(*insideComment)) { + alive_cycle = false; + } else { /* MISRA C 2004 14.10 */ + } + + if (alive_cycle) { + if (++fifo->readPtr == fifo->bufSize) { + fifo->readPtr = 0; + } + if (c == DELIM_COMMAND) { + delimCommandFound = true; + *insideComment = false; + alive_cycle = false; + } + } + } + } + return delimCommandFound; +} + +size_t +CO_fifo_readToken(CO_fifo_t* fifo, char* buf, size_t count, uint8_t* closed, bool_t* err) { + bool_t delimCommandFound = false; + bool_t delimCommentFound = false; + size_t tokenSize = 0; + + if ((fifo != NULL) && (buf != NULL) && (count > 1U) && ((err == NULL) || (*err == false)) + && (fifo->readPtr != fifo->writePtr)) { + bool_t finished = false; + uint8_t step = 0; + size_t ptr = fifo->readPtr; /* current pointer (integer, 0 based) */ + uint8_t* c = &fifo->buf[ptr]; /* current character */ + do { + switch (step) { + case 0: /* skip leading empty characters, stop on delimiter */ + if (isgraph((int)*c) != 0) { + if (*c == DELIM_COMMENT) { + delimCommentFound = true; + } else { + buf[tokenSize] = (char)*c; + tokenSize++; + step++; + } + } else if (*c == DELIM_COMMAND) { + delimCommandFound = true; + } else { /* MISRA C 2004 14.10 */ + } + break; + case 1: /* search for end of the token */ + if (isgraph((int)*c) != 0) { + if (*c == DELIM_COMMENT) { + delimCommentFound = true; + } else if (tokenSize < count) { + buf[tokenSize] = (char)*c; + tokenSize++; + } else { /* MISRA C 2004 14.10 */ + } + } else { + if (*c == DELIM_COMMAND) { + delimCommandFound = true; + } + step++; + } + break; + case 2: /* skip trailing empty characters */ + if (isgraph((int)*c) != 0) { + if (*c == DELIM_COMMENT) { + delimCommentFound = true; + } else { + fifo->readPtr = ptr; + finished = true; + } + } else if (*c == DELIM_COMMAND) { + delimCommandFound = true; + } else { /* MISRA C 2004 14.10 */ + } + break; + default: + /* MISRA C 2004 15.3 */ + break; + } + if (delimCommentFound == true) { + /* Comment delimiter found, clear all till end of the line. */ + fifo->readPtr = ptr; + delimCommandFound = CO_fifo_CommSearch(fifo, true); + finished = true; + } else if (delimCommandFound) { + /* command delimiter found, set readPtr behind it. */ + if (++ptr == fifo->bufSize) { + ptr = 0; + } + fifo->readPtr = ptr; + finished = true; + } else if (!finished) { + /* find next character in the circular buffer */ + if (++ptr == fifo->bufSize) { + ptr = 0; + c = &fifo->buf[ptr]; + } else { + c++; + } + /* end, if buffer is now empty */ + if (ptr == fifo->writePtr) { + if (step == 2U) { + fifo->readPtr = ptr; + } else { + tokenSize = 0; + } + finished = true; + } + } else { /* MISRA C 2004 14.10 */ + } + } while (!finished); + } + + /* set 'err' return value */ + if ((err != NULL) && (*err == false)) { + if ((tokenSize == count) + || ((closed != NULL) + && (((*closed == 1U) && (!delimCommandFound || (tokenSize == 0U))) + || ((*closed == 0U) && (delimCommandFound || (tokenSize == 0U)))))) { + *err = true; + } + } + /* set 'closed' return value */ + if (closed != NULL) { + *closed = delimCommandFound ? 1U : 0U; + } + + /* token was larger then size of the buffer, all was cleaned, return empty */ + if (tokenSize == count) { + tokenSize = 0; + } + /* write string terminator character */ + if ((buf != NULL) && (count > tokenSize)) { + buf[tokenSize] = '\0'; + } + + return tokenSize; +} +#endif /* (CO_CONFIG_FIFO) & CO_CONFIG_FIFO_ASCII_COMMANDS */ + +#if ((CO_CONFIG_FIFO)&CO_CONFIG_FIFO_ASCII_DATATYPES) != 0 +/* Tables for mime-base64 encoding, as specified in RFC 2045, (without CR-LF, but one long string). + * Base64 is used for encoding binary data into easy transferable printable characters. In general, + * each three bytes of binary data are translated into four characters, where characters are + * selected from 64 characters long table. See https://en.wikipedia.org/wiki/Base64 */ +static const char base64EncTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +static const uint8_t base64DecTable[] = { + 255, 255, 255, 255, 255, 255, 255, 255, 255, 103, 101, 255, 255, 102, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 103, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, + 255, 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 100, 255, 255, 255, 0, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 255, 255, 255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255}; + +size_t +CO_fifo_readU82a(CO_fifo_t* fifo, char* buf, size_t count, bool_t end) { + uint8_t n = 0; + + if (fifo == NULL) { + return 0; + } + + if ((CO_fifo_getOccupied(fifo) == sizeof(n)) && (count >= 6U)) { + (void)CO_fifo_read(fifo, &n, sizeof(n), NULL); + return (size_t)sprintf(buf, "%" PRIu8, n); + } else { + return CO_fifo_readHex2a(fifo, buf, count, end); + } +} + +size_t +CO_fifo_readU162a(CO_fifo_t* fifo, char* buf, size_t count, bool_t end) { + uint16_t n = 0; + + if (fifo == NULL) { + return 0; + } + + if ((CO_fifo_getOccupied(fifo) == sizeof(n)) && (count >= 8U)) { + (void)CO_fifo_read(fifo, (uint8_t*)&n, sizeof(n), NULL); + return (size_t)sprintf(buf, "%" PRIu16, CO_SWAP_16(n)); + } else { + return CO_fifo_readHex2a(fifo, buf, count, end); + } +} + +size_t +CO_fifo_readU322a(CO_fifo_t* fifo, char* buf, size_t count, bool_t end) { + uint32_t n = 0; + + if (fifo == NULL) { + return 0; + } + + if ((CO_fifo_getOccupied(fifo) == sizeof(n)) && (count >= 12U)) { + (void)CO_fifo_read(fifo, (uint8_t*)&n, sizeof(n), NULL); + return (size_t)sprintf(buf, "%" PRIu32, CO_SWAP_32(n)); + } else { + return CO_fifo_readHex2a(fifo, buf, count, end); + } +} + +size_t +CO_fifo_readU642a(CO_fifo_t* fifo, char* buf, size_t count, bool_t end) { + uint64_t n = 0; + + if (fifo == NULL) { + return 0; + } + + if ((CO_fifo_getOccupied(fifo) == sizeof(n)) && (count >= 20U)) { + (void)CO_fifo_read(fifo, (uint8_t*)&n, sizeof(n), NULL); + return (size_t)sprintf(buf, "%" PRIu64, CO_SWAP_64(n)); + } else { + return CO_fifo_readHex2a(fifo, buf, count, end); + } +} + +size_t +CO_fifo_readX82a(CO_fifo_t* fifo, char* buf, size_t count, bool_t end) { + uint8_t n = 0; + + if (fifo == NULL) { + return 0; + } + + if ((CO_fifo_getOccupied(fifo) == sizeof(n)) && (count >= 6U)) { + (void)CO_fifo_read(fifo, (uint8_t*)&n, sizeof(n), NULL); + return (size_t)sprintf(buf, "0x%02" PRIX8, (uint32_t)n); + } else { + return CO_fifo_readHex2a(fifo, buf, count, end); + } +} + +size_t +CO_fifo_readX162a(CO_fifo_t* fifo, char* buf, size_t count, bool_t end) { + uint16_t n = 0; + + if (fifo == NULL) { + return 0; + } + + if ((CO_fifo_getOccupied(fifo) == sizeof(n)) && (count >= 8U)) { + (void)CO_fifo_read(fifo, (uint8_t*)&n, sizeof(n), NULL); + return (size_t)sprintf(buf, "0x%04" PRIX16, (uint32_t)CO_SWAP_16(n)); + } else { + return CO_fifo_readHex2a(fifo, buf, count, end); + } +} + +size_t +CO_fifo_readX322a(CO_fifo_t* fifo, char* buf, size_t count, bool_t end) { + uint32_t n = 0; + + if (fifo == NULL) { + return 0; + } + + if ((CO_fifo_getOccupied(fifo) == sizeof(n)) && (count >= 12U)) { + (void)CO_fifo_read(fifo, (uint8_t*)&n, sizeof(n), NULL); + return (size_t)sprintf(buf, "0x%08" PRIX32, CO_SWAP_32(n)); + } else { + return CO_fifo_readHex2a(fifo, buf, count, end); + } +} + +size_t +CO_fifo_readX642a(CO_fifo_t* fifo, char* buf, size_t count, bool_t end) { + uint64_t n = 0; + + if (fifo == NULL) { + return 0; + } + + if ((CO_fifo_getOccupied(fifo) == sizeof(n)) && (count >= 20U)) { + (void)CO_fifo_read(fifo, (uint8_t*)&n, sizeof(n), NULL); + return (size_t)sprintf(buf, "0x%016" PRIX64, CO_SWAP_64(n)); + } else { + return CO_fifo_readHex2a(fifo, buf, count, end); + } +} + +size_t +CO_fifo_readI82a(CO_fifo_t* fifo, char* buf, size_t count, bool_t end) { + int8_t n = 0; + + if (fifo == NULL) { + return 0; + } + + if ((CO_fifo_getOccupied(fifo) == sizeof(n)) && (count >= 6U)) { + (void)CO_fifo_read(fifo, (uint8_t*)&n, sizeof(n), NULL); + return (size_t)sprintf(buf, "%" PRId8, n); + } else { + return CO_fifo_readHex2a(fifo, buf, count, end); + } +} + +size_t +CO_fifo_readI162a(CO_fifo_t* fifo, char* buf, size_t count, bool_t end) { + int16_t n = 0; + + if (fifo == NULL) { + return 0; + } + + if ((CO_fifo_getOccupied(fifo) == sizeof(n)) && (count >= 8U)) { + (void)CO_fifo_read(fifo, (uint8_t*)&n, sizeof(n), NULL); + return (size_t)sprintf(buf, "%" PRId16, CO_SWAP_16(n)); + } else { + return CO_fifo_readHex2a(fifo, buf, count, end); + } +} + +size_t +CO_fifo_readI322a(CO_fifo_t* fifo, char* buf, size_t count, bool_t end) { + int32_t n = 0; + + if (fifo == NULL) { + return 0; + } + + if ((CO_fifo_getOccupied(fifo) == sizeof(n)) && (count >= 13U)) { + (void)CO_fifo_read(fifo, (uint8_t*)&n, sizeof(n), NULL); + return (size_t)sprintf(buf, "%" PRId32, CO_SWAP_32(n)); + } else { + return CO_fifo_readHex2a(fifo, buf, count, end); + } +} + +size_t +CO_fifo_readI642a(CO_fifo_t* fifo, char* buf, size_t count, bool_t end) { + int64_t n = 0; + + if (fifo == NULL) { + return 0; + } + + if ((CO_fifo_getOccupied(fifo) == sizeof(n)) && (count >= 23U)) { + (void)CO_fifo_read(fifo, (uint8_t*)&n, sizeof(n), NULL); + return (size_t)sprintf(buf, "%" PRId64, CO_SWAP_64(n)); + } else { + return CO_fifo_readHex2a(fifo, buf, count, end); + } +} + +size_t +CO_fifo_readR322a(CO_fifo_t* fifo, char* buf, size_t count, bool_t end) { + float32_t n = (float32_t)0; + + if (fifo == NULL) { + return 0; + } + + if ((CO_fifo_getOccupied(fifo) == sizeof(n)) && (count >= 20U)) { + (void)CO_fifo_read(fifo, (uint8_t*)&n, sizeof(n), NULL); + return (size_t)sprintf(buf, "%g", (float32_t)CO_SWAP_32(n)); + } else { + return CO_fifo_readHex2a(fifo, buf, count, end); + } +} + +size_t +CO_fifo_readR642a(CO_fifo_t* fifo, char* buf, size_t count, bool_t end) { + float64_t n = (float64_t)0; + + if (fifo == NULL) { + return 0; + } + + if ((CO_fifo_getOccupied(fifo) == sizeof(n)) && (count >= 30U)) { + (void)CO_fifo_read(fifo, (uint8_t*)&n, sizeof(n), NULL); + return (size_t)sprintf(buf, "%g", (float64_t)CO_SWAP_64(n)); + } else { + return CO_fifo_readHex2a(fifo, buf, count, end); + } +} + +size_t +CO_fifo_readHex2a(CO_fifo_t* fifo, char* buf, size_t count, bool_t end) { + (void)end; /* unused */ + + size_t len = 0; + + if ((fifo != NULL) && (count > 3U)) { + /* Very first write is without leading space */ + if (!fifo->started) { + uint8_t c; + if (CO_fifo_getc(fifo, &c)) { + len = (size_t)sprintf(&buf[0], "%02" PRIX8, (uint32_t)c); + fifo->started = true; + } + } + + while ((len + 3U) < count) { + uint8_t c; + if (!CO_fifo_getc(fifo, &c)) { + break; + } + len += (size_t)sprintf(&buf[len], " %02" PRIX8, (uint32_t)c); + } + } + + return len; +} + +size_t +CO_fifo_readVs2a(CO_fifo_t* fifo, char* buf, size_t count, bool_t end) { + size_t len = 0; + + if ((fifo != NULL) && (count > 3U)) { + /* Start with '"' */ + if (!fifo->started) { + buf[len] = '"'; + len++; + fifo->started = true; + } + + while ((len + 2U) < count) { + uint8_t c; + if (!CO_fifo_getc(fifo, &c)) { + if (end) { + buf[len] = '"'; + len++; + } + break; + } else if ((c != 0U) && (c != (uint8_t)'\r')) { + /* skip null and CR inside string */ + buf[len] = (char)c; + len++; + if (c == DELIM_DQUOTE) { + buf[len] = '"'; + len++; + } + } else { /* MISRA C 2004 14.10 */ + } + } + } + + return len; +} + +size_t +CO_fifo_readB642a(CO_fifo_t* fifo, char* buf, size_t count, bool_t end) { + /* mime-base64 encoding, see description above base64EncTable */ + + size_t len = 0; + + if ((fifo != NULL) && (count >= 4U)) { + uint8_t step; + uint16_t word; + + if (!fifo->started) { + fifo->started = true; + step = 0; + word = 0; + } else { + /* get memorized variables from previous function calls */ + step = (uint8_t)(fifo->aux >> 16); + word = (uint16_t)fifo->aux; + } + + while ((len + 3U) <= count) { + uint8_t c; + + if (!CO_fifo_getc(fifo, &c)) { + /* buffer is empty, is also SDO communication finished? */ + if (end) { + /* add padding if necessary */ + switch (step) { + case 1: + buf[len] = base64EncTable[(word >> 4) & 0x3FU]; + len++; + buf[len] = '='; + len++; + buf[len] = '='; + len++; + break; + case 2: + buf[len] = base64EncTable[(word >> 6) & 0x3FU]; + len++; + buf[len] = '='; + len++; + break; + default: + /* MISRA C 2004 15.3 */ + break; + } + } + break; + } + + word |= c; + + switch (step++) { + case 0: + buf[len] = base64EncTable[(word >> 2) & 0x3FU]; + len++; + break; + case 1: + buf[len] = base64EncTable[(word >> 4) & 0x3FU]; + len++; + break; + default: + buf[len] = base64EncTable[(word >> 6) & 0x3FU]; + len++; + buf[len] = base64EncTable[word & 0x3FU]; + len++; + step = 0; + break; + } + word <<= 8; + } + + /* memorize variables for next iteration */ + fifo->aux = ((uint32_t)step << 16) | word; + } + + return len; +} + +size_t +CO_fifo_cpyTok2U8(CO_fifo_t* dest, CO_fifo_t* src, uint8_t* status) { + char buf[15]; + uint8_t closed = 0xFFU; + bool_t err = false; + size_t nWr = 0; + size_t nRd = CO_fifo_readToken(src, buf, sizeof(buf), &closed, &err); + uint8_t st = closed; + if ((nRd == 0U) || err) { + st |= CO_fifo_st_errTok; + } else { + char* sRet; + uint32_t u32 = strtoul(buf, &sRet, 0); + if ((sRet != strchr(buf, (int32_t)('\0'))) || (u32 > (uint32_t)UINT8_MAX)) { + st |= CO_fifo_st_errVal; + } else { + uint8_t num = (uint8_t)u32; + nWr = CO_fifo_write(dest, &num, sizeof(num), NULL); + if (nWr != sizeof(num)) { + st |= CO_fifo_st_errBuf; + } + } + } + if (status != NULL) { + *status = st; + } + return nWr; +} + +size_t +CO_fifo_cpyTok2U16(CO_fifo_t* dest, CO_fifo_t* src, uint8_t* status) { + char buf[15]; + uint8_t closed = 0xFFU; + bool_t err = false; + size_t nWr = 0; + size_t nRd = CO_fifo_readToken(src, buf, sizeof(buf), &closed, &err); + uint8_t st = closed; + if ((nRd == 0U) || err) { + st |= CO_fifo_st_errTok; + } else { + char* sRet; + uint32_t u32 = strtoul(buf, &sRet, 0); + if ((sRet != strchr(buf, (int32_t)('\0'))) || (u32 > (uint32_t)UINT16_MAX)) { + st |= CO_fifo_st_errVal; + } else { + uint16_t num = CO_SWAP_16((uint16_t)u32); + nWr = CO_fifo_write(dest, (uint8_t*)&num, sizeof(num), NULL); + if (nWr != sizeof(num)) { + st |= CO_fifo_st_errBuf; + } + } + } + if (status != NULL) { + *status = st; + } + return nWr; +} + +size_t +CO_fifo_cpyTok2U32(CO_fifo_t* dest, CO_fifo_t* src, uint8_t* status) { + char buf[15]; + uint8_t closed = 0xFFU; + bool_t err = false; + size_t nWr = 0; + size_t nRd = CO_fifo_readToken(src, buf, sizeof(buf), &closed, &err); + uint8_t st = closed; + if ((nRd == 0U) || err) { + st |= CO_fifo_st_errTok; + } else { + char* sRet; + uint32_t u32 = strtoul(buf, &sRet, 0); + if (sRet != strchr(buf, (int32_t)('\0'))) { + st |= CO_fifo_st_errVal; + } else { + uint32_t num = CO_SWAP_32(u32); + nWr = CO_fifo_write(dest, (uint8_t*)&num, sizeof(num), NULL); + if (nWr != sizeof(num)) { + st |= CO_fifo_st_errBuf; + } + } + } + if (status != NULL) { + *status = st; + } + return nWr; +} + +size_t +CO_fifo_cpyTok2U64(CO_fifo_t* dest, CO_fifo_t* src, uint8_t* status) { + char buf[25]; + uint8_t closed = 0xFFU; + bool_t err = false; + size_t nWr = 0; + size_t nRd = CO_fifo_readToken(src, buf, sizeof(buf), &closed, &err); + uint8_t st = closed; + if ((nRd == 0U) || err) { + st |= CO_fifo_st_errTok; + } else { + char* sRet; + uint64_t u64 = strtoull(buf, &sRet, 0); + if (sRet != strchr(buf, (int32_t)('\0'))) { + st |= CO_fifo_st_errVal; + } else { + uint64_t num = CO_SWAP_64(u64); + nWr = CO_fifo_write(dest, (uint8_t*)&num, sizeof(num), NULL); + if (nWr != sizeof(num)) { + st |= CO_fifo_st_errBuf; + } + } + } + if (status != NULL) { + *status = (uint8_t)st; + } + return nWr; +} + +size_t +CO_fifo_cpyTok2I8(CO_fifo_t* dest, CO_fifo_t* src, uint8_t* status) { + char buf[15]; + uint8_t closed = 0xFFU; + bool_t err = false; + size_t nWr = 0; + size_t nRd = CO_fifo_readToken(src, buf, sizeof(buf), &closed, &err); + uint8_t st = closed; + if ((nRd == 0U) || err) { + st |= CO_fifo_st_errTok; + } else { + char* sRet; + int32_t i32 = strtol(buf, &sRet, 0); + if ((sRet != strchr(buf, (int32_t)('\0'))) || (i32 < INT8_MIN) || (i32 > INT8_MAX)) { + st |= CO_fifo_st_errVal; + } else { + int8_t num = (int8_t)i32; + nWr = CO_fifo_write(dest, (uint8_t*)&num, sizeof(num), NULL); + if (nWr != sizeof(num)) { + st |= CO_fifo_st_errBuf; + } + } + } + if (status != NULL) { + *status = st; + } + return nWr; +} + +size_t +CO_fifo_cpyTok2I16(CO_fifo_t* dest, CO_fifo_t* src, uint8_t* status) { + char buf[15]; + uint8_t closed = 0xFFU; + bool_t err = false; + size_t nWr = 0; + size_t nRd = CO_fifo_readToken(src, buf, sizeof(buf), &closed, &err); + uint8_t st = closed; + if ((nRd == 0U) || err) { + st |= CO_fifo_st_errTok; + } else { + char* sRet; + int32_t i32 = strtol(buf, &sRet, 0); + if ((sRet != strchr(buf, (int32_t)('\0'))) || (i32 < INT16_MIN) || (i32 > INT16_MAX)) { + st |= CO_fifo_st_errVal; + } else { + int16_t num = CO_SWAP_16((int16_t)i32); + nWr = CO_fifo_write(dest, (uint8_t*)&num, sizeof(num), NULL); + if (nWr != sizeof(num)) { + st |= CO_fifo_st_errBuf; + } + } + } + if (status != NULL) { + *status = st; + } + return nWr; +} + +size_t +CO_fifo_cpyTok2I32(CO_fifo_t* dest, CO_fifo_t* src, uint8_t* status) { + char buf[15]; + uint8_t closed = 0xFFU; + bool_t err = false; + size_t nWr = 0; + size_t nRd = CO_fifo_readToken(src, buf, sizeof(buf), &closed, &err); + uint8_t st = closed; + if ((nRd == 0U) || err) { + st |= CO_fifo_st_errTok; + } else { + char* sRet; + int32_t i32 = strtol(buf, &sRet, 0); + if (sRet != strchr(buf, (int32_t)('\0'))) { + st |= CO_fifo_st_errVal; + } else { + int32_t num = CO_SWAP_32(i32); + nWr = CO_fifo_write(dest, (uint8_t*)&num, sizeof(num), NULL); + if (nWr != sizeof(num)) { + st |= CO_fifo_st_errBuf; + } + } + } + if (status != NULL) { + *status = st; + } + return nWr; +} + +size_t +CO_fifo_cpyTok2I64(CO_fifo_t* dest, CO_fifo_t* src, uint8_t* status) { + char buf[25]; + uint8_t closed = 0xFFU; + bool_t err = false; + size_t nWr = 0; + size_t nRd = CO_fifo_readToken(src, buf, sizeof(buf), &closed, &err); + uint8_t st = closed; + if ((nRd == 0U) || err) { + st |= CO_fifo_st_errTok; + } else { + char* sRet; + int64_t i64 = strtoll(buf, &sRet, 0); + if (sRet != strchr(buf, (int32_t)('\0'))) { + st |= CO_fifo_st_errVal; + } else { + int64_t num = CO_SWAP_64(i64); + nWr = CO_fifo_write(dest, (uint8_t*)&num, sizeof(num), NULL); + if (nWr != sizeof(num)) { + st |= CO_fifo_st_errBuf; + } + } + } + if (status != NULL) { + *status = (uint8_t)st; + } + return nWr; +} + +size_t +CO_fifo_cpyTok2R32(CO_fifo_t* dest, CO_fifo_t* src, uint8_t* status) { + char buf[30]; + uint8_t closed = 0xFFU; + bool_t err = false; + size_t nWr = 0; + size_t nRd = CO_fifo_readToken(src, buf, sizeof(buf), &closed, &err); + uint8_t st = closed; + if ((nRd == 0U) || err) { + st |= CO_fifo_st_errTok; + } else { + char* sRet; + float32_t f32 = strtof(buf, &sRet); + if (sRet != strchr(buf, (int32_t)('\0'))) { + st |= CO_fifo_st_errVal; + } else { + float32_t num = CO_SWAP_32(f32); + nWr = CO_fifo_write(dest, (uint8_t*)&num, sizeof(num), NULL); + if (nWr != sizeof(num)) { + st |= CO_fifo_st_errBuf; + } + } + } + if (status != NULL) { + *status = st; + } + return nWr; +} + +size_t +CO_fifo_cpyTok2R64(CO_fifo_t* dest, CO_fifo_t* src, uint8_t* status) { + char buf[40]; + uint8_t closed = 0xFFU; + bool_t err = false; + size_t nWr = 0; + size_t nRd = CO_fifo_readToken(src, buf, sizeof(buf), &closed, &err); + uint8_t st = closed; + if ((nRd == 0U) || err) { + st |= CO_fifo_st_errTok; + } else { + char* sRet; + float64_t f64 = strtof(buf, &sRet); + if (sRet != strchr(buf, (int32_t)('\0'))) { + st |= CO_fifo_st_errVal; + } else { + float64_t num = CO_SWAP_64(f64); + nWr = CO_fifo_write(dest, (uint8_t*)&num, sizeof(num), NULL); + if (nWr != sizeof(num)) { + st |= CO_fifo_st_errBuf; + } + } + } + if (status != NULL) { + *status = st; + } + return nWr; +} + +size_t +CO_fifo_cpyTok2Hex(CO_fifo_t* dest, CO_fifo_t* src, uint8_t* status) { + size_t destSpace, destSpaceStart; + bool_t finished = false; + uint8_t step; + uint8_t firstChar; + uint8_t st = 0; + + if ((dest == NULL) || (src == NULL)) { + return 0; + } + + /* get free space of the dest fifo */ + destSpaceStart = CO_fifo_getSpace(dest); + destSpace = destSpaceStart; + + /* is this the first write into dest? */ + if (!dest->started) { + bool_t insideComment = false; + if (CO_fifo_trimSpaces(src, &insideComment) || insideComment) { + /* command delimiter found without string, this is an error */ + st |= CO_fifo_st_errTok; + } + dest->started = true; + step = 0; + firstChar = 0; + } else { + /* get memorized variables from previous function calls */ + step = (uint8_t)(dest->aux >> 8); + firstChar = (uint8_t)(dest->aux & 0xFFU); + } + + /* repeat until destination space available and no error and not finished + * and source characters available */ + while ((destSpace > 0U) && ((st & CO_fifo_st_errMask) == 0U) && !finished) { + uint8_t c; + if (!CO_fifo_getc(src, &c)) { + break; + } + + if (step == 6U) { + /* command is inside comment, waiting for command delimiter */ + bool_t insideComment = true; + if (CO_fifo_trimSpaces(src, &insideComment) || (c == DELIM_COMMAND)) { + st |= CO_fifo_st_closed; + finished = true; + } + continue; + } + + if ((int32_t)(isxdigit((int32_t)c)) != 0) { + /* first or second hex digit */ + if (step == 0U) { + firstChar = c; + step = 1; + } else { + /* write the byte */ + uint8_t s[3]; + int32_t num; + s[0] = firstChar; + s[1] = c; + s[2] = 0; + num = strtol((char*)&s[0], NULL, 16); + (void)CO_fifo_putc(dest, (uint8_t)num); + destSpace--; + step = 0; + } + } else if ((int32_t)(isgraph((int32_t)c)) != 0) { + /* printable character, not hex digit */ + if (c == DELIM_COMMENT) { /* comment start */ + step = 6; + } else { /* syntax error */ + st |= CO_fifo_st_errTok; + } + } else { + /* this is space or delimiter */ + if (step == 1U) { + /* write the byte */ + uint8_t s[2]; + int32_t num; + s[0] = firstChar; + s[1] = 0; + num = strtol((char*)&s[0], NULL, 16); + (void)CO_fifo_putc(dest, (uint8_t)num); + destSpace--; + step = 0; + } + bool_t insideComment = false; + if (CO_fifo_trimSpaces(src, &insideComment) || (c == DELIM_COMMAND)) { + /* newline found, finish */ + st |= CO_fifo_st_closed; + finished = true; + } else if (insideComment) { + step = 6; + } else { /* MISRA C 2004 14.10 */ + } + } + } /* while ... */ + + if (!finished) { + st |= CO_fifo_st_partial; + /* memorize variables for next iteration */ + dest->aux = ((uint32_t)step << 8) | firstChar; + } + + if (status != NULL) { + *status = st; + } + + return destSpaceStart - destSpace; +} + +size_t +CO_fifo_cpyTok2Vs(CO_fifo_t* dest, CO_fifo_t* src, uint8_t* status) { + size_t destSpace, destSpaceStart; + bool_t finished = false; + uint8_t step; + uint8_t st = 0; + + if ((dest == NULL) || (src == NULL)) { + return 0; + } + + /* get free space of the dest fifo */ + destSpaceStart = CO_fifo_getSpace(dest); + destSpace = destSpaceStart; + + /* is this the first write into dest? */ + if (!dest->started) { + bool_t insideComment = false; + if (CO_fifo_trimSpaces(src, &insideComment) || insideComment) { + /* command delimiter found without string, this is an error */ + st |= CO_fifo_st_errTok; + } + dest->started = true; + step = 0; + } else { + /* get memorized variables from previous function calls */ + step = (uint8_t)dest->aux; + } + + /* repeat until destination space available and no error and not finished and source characters available */ + while ((destSpace > 0U) && ((st & CO_fifo_st_errMask) == 0U) && !finished) { + uint8_t c; + if (!CO_fifo_getc(src, &c)) { + break; + } + + switch (step) { + case 0: /* beginning of the string, first write into dest */ + if (c == DELIM_DQUOTE) { + /* Indicated beginning of the string, skip this character. */ + step = 1; + } else { + /* this must be a single word string without '"' */ + /* copy the character */ + (void)CO_fifo_putc(dest, c); + destSpace--; + step = 2; + } + break; + + case 1: /* inside string, quoted string */ + case 2: /* inside string, single word, no quotes */ + if (c == DELIM_DQUOTE) { + /* double quote found, this may be end of the string or escaped + * double quote (with two double quotes) */ + step += 2U; + } else if ((isgraph((int)c) == 0) && (step == 2U)) { + /* end of single word string */ + bool_t insideComment = false; + if (CO_fifo_trimSpaces(src, &insideComment) || (c == DELIM_COMMAND)) { + st |= CO_fifo_st_closed; + finished = true; + } else { + step = insideComment ? 6U : 5U; + } + } else if (c == DELIM_COMMAND) { + /* no closing quote, error */ + st |= CO_fifo_st_errTok; + } else { + /* copy the character */ + (void)CO_fifo_putc(dest, c); + destSpace--; + } + break; + + case 3: /* previous was double quote, parsing quoted string */ + case 4: /* previous was double quote, parsing no quoted word */ + if (c == DELIM_DQUOTE) { + /* escaped double quote, copy the character and continue */ + (void)CO_fifo_putc(dest, c); + destSpace--; + step -= 2U; + } else { + /* previous character was closing double quote */ + if (step == 4U) { + /* no opening double quote, syntax error */ + st |= CO_fifo_st_errTok; + } else { + if (isgraph((int)c) == 0) { + /* end of quoted string */ + bool_t insideComment = false; + if (CO_fifo_trimSpaces(src, &insideComment) || (c == DELIM_COMMAND)) { + st |= CO_fifo_st_closed; + finished = true; + } else { + step = insideComment ? 6U : 5U; + } + } else { + /* space must follow closing double quote, error */ + st |= CO_fifo_st_errTok; + } + } + } + break; + + case 5: { /* String token is finished, waiting for command delimiter */ + bool_t insideComment = false; + if (CO_fifo_trimSpaces(src, &insideComment) || (c == DELIM_COMMAND)) { + st |= CO_fifo_st_closed; + finished = true; + } else if (insideComment) { + step = 6; + } else if (isgraph((int)c) != 0) { + if (c == DELIM_COMMENT) { /* comment start */ + step = 6; + } else { /* syntax error */ + st |= CO_fifo_st_errTok; + } + } else { /* MISRA C 2004 14.10 */ + } + break; + } + case 6: { /* String token is finished, waiting for command delimiter */ + bool_t insideComment = true; + if (CO_fifo_trimSpaces(src, &insideComment) || (c == DELIM_COMMAND)) { + st |= CO_fifo_st_closed; + finished = true; + } + break; + } + default: /* internal error */ st |= CO_fifo_st_errInt; break; + } + } + + if (!finished) { + st |= CO_fifo_st_partial; + /* memorize variables for next iteration */ + dest->aux = step; + } + + if (status != NULL) { + *status = st; + } + + return destSpaceStart - destSpace; +} + +size_t +CO_fifo_cpyTok2B64(CO_fifo_t* dest, CO_fifo_t* src, uint8_t* status) { + /* mime-base64 decoding, see description above base64EncTable */ + + size_t destSpace, destSpaceStart; + bool_t finished = false; + uint8_t step; + uint32_t dword; + uint8_t st = 0; + + if ((dest == NULL) || (src == NULL)) { + return 0; + } + + /* get free space of the dest fifo */ + destSpaceStart = CO_fifo_getSpace(dest); + destSpace = destSpaceStart; + + /* is this the first write into dest? */ + if (!dest->started) { + bool_t insideComment = false; + if (CO_fifo_trimSpaces(src, &insideComment) || insideComment) { + /* command delimiter found without string, this is an error */ + st |= CO_fifo_st_errTok; + } + dest->started = true; + step = 0; + dword = 0; + } else { + /* get memorized variables from previous function calls */ + step = (uint8_t)(dest->aux >> 24); + dword = dest->aux & 0xFFFFFFU; + } + + /* repeat until destination space available and no error and not finished and source characters available */ + while ((destSpace >= 3U) && ((st & CO_fifo_st_errMask) == 0U) && !finished) { + uint8_t c; + if (!CO_fifo_getc(src, &c)) { + break; + } + + if (step >= 5U) { + /* String token is finished, waiting for command delimiter */ + bool_t insideComment = step > 5U; + if (CO_fifo_trimSpaces(src, &insideComment) || (c == DELIM_COMMAND)) { + st |= CO_fifo_st_closed; + finished = true; + } else if (insideComment) { + step = 6; + } else if ((isgraph((int)c) != 0) && (c != (uint8_t)'=')) { + if (c == DELIM_COMMENT) { /* comment start */ + step = 6; + } else { /* syntax error */ + st |= CO_fifo_st_errTok; + } + } else { /* MISRA C 2004 14.10 */ + } + continue; + } + + uint8_t code = base64DecTable[c & 0x7FU]; + + if (((c & 0x80U) != 0U) || ((code & 0x80U) != 0U)) { + st |= CO_fifo_st_errTok; + } else if (code >= 64U /* '=' (pad) or DELIM_COMMAND or space */) { + /* base64 string finished, write remaining bytes */ + switch (step) { + case 2: + (void)CO_fifo_putc(dest, (uint8_t)(dword >> 4)); + destSpace--; + break; + case 3: + (void)CO_fifo_putc(dest, (uint8_t)(dword >> 10)); + (void)CO_fifo_putc(dest, (uint8_t)(dword >> 2)); + destSpace -= 2U; + break; + default: + /* MISRA C 2004 15.3 */ + break; + } + + bool_t insideComment = false; + if (CO_fifo_trimSpaces(src, &insideComment) || (c == DELIM_COMMAND)) { + st |= CO_fifo_st_closed; + finished = true; + } else { + step = insideComment ? 6U : 5U; + } + } else { + dword = (dword << 6) | code; + if (step++ == 3U) { + (void)CO_fifo_putc(dest, (uint8_t)((dword >> 16) & 0xFFU)); + (void)CO_fifo_putc(dest, (uint8_t)((dword >> 8) & 0xFFU)); + (void)CO_fifo_putc(dest, (uint8_t)(dword & 0xFFU)); + destSpace -= 3U; + dword = 0; + step = 0; + } + } + } /* while ... */ + + if (!finished) { + st |= CO_fifo_st_partial; + /* memorize variables for next iteration */ + dest->aux = ((uint32_t)step << 24) | (dword & 0xFFFFFFU); + } + + if (status != NULL) { + *status = st; + } + + return destSpaceStart - destSpace; +} + +#endif /* (CO_CONFIG_FIFO) & CO_CONFIG_FIFO_ASCII_DATATYPES */ + +#endif /* (CO_CONFIG_FIFO) & CO_CONFIG_FIFO_ENABLE */ diff --git a/301/CO_fifo.h b/301/CO_fifo.h new file mode 100644 index 00000000..0b88809e --- /dev/null +++ b/301/CO_fifo.h @@ -0,0 +1,484 @@ +/** + * FIFO circular buffer + * + * @file CO_fifo.h + * @ingroup CO_CANopen_301_fifo + * @author Janez Paternoster + * @copyright 2020 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#ifndef CO_FIFO_H +#define CO_FIFO_H + +#include "301/CO_driver.h" + +/* default configuration, see CO_config.h */ +#ifndef CO_CONFIG_FIFO +#define CO_CONFIG_FIFO (0) +#endif + +#if (((CO_CONFIG_FIFO)&CO_CONFIG_FIFO_ENABLE) != 0) || defined CO_DOXYGEN + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup CO_CANopen_301_fifo FIFO circular buffer FIFO circular buffer for continuous data flow. + * + * @ingroup CO_CANopen_301 + * @{ + * FIFO is organized as circular buffer with predefined capacity. It must be initialized by CO_fifo_init(). Functions + * are not not thread safe. + * + * It can be used as general purpose FIFO circular buffer for any data. Data can be written by CO_fifo_write() and read + * by CO_fifo_read() functions. + * + * Buffer has additional functions for usage with CiA309-3 standard. It acts as circular buffer for storing ascii + * commands and fetching tokens from them. + */ + +/** + * Fifo object + */ +typedef struct { + uint8_t* buf; /**< Buffer of size bufSize. Initialized by CO_fifo_init() */ + size_t bufSize; /**< Initialized by CO_fifo_init() */ + size_t writePtr; /**< Location in the buffer, which will be next written. */ + size_t readPtr; /**< Location in the buffer, which will be next read. */ +#if (((CO_CONFIG_FIFO)&CO_CONFIG_FIFO_ALT_READ) != 0) || defined CO_DOXYGEN + size_t altReadPtr; /**< Location in the buffer, which will be next read. */ +#endif +#if (((CO_CONFIG_FIFO)&CO_CONFIG_FIFO_ASCII_DATATYPES) != 0) || defined CO_DOXYGEN + bool_t started; /**< helper variable, set to false in CO_fifo_reset(), used in some functions. */ + uint32_t aux; /**< auxiliary variable, used in some functions. */ +#endif +} CO_fifo_t; + +/** + * Initialize fifo object + * + * @param fifo This object will be initialized + * @param buf Pointer to externally defined buffer + * @param bufSize Size of the above buffer. Usable size of the buffer will be one byte less than bufSize, it is used for + * operation of the circular buffer. + */ +void CO_fifo_init(CO_fifo_t* fifo, uint8_t* buf, size_t bufSize); + +/** + * Reset fifo object, make it empty + * + * @param fifo This object + */ +static inline void +CO_fifo_reset(CO_fifo_t* fifo) { + if (fifo != NULL) { + fifo->readPtr = 0; + fifo->writePtr = 0; +#if ((CO_CONFIG_FIFO)&CO_CONFIG_FIFO_ASCII_DATATYPES) != 0 + fifo->started = false; +#endif + } + + return; +} + +/** + * Purge all data in fifo object, keep other properties + * + * @param fifo This object + * + * @return true, if data were purged or false, if fifo were already empty + */ +static inline bool_t +CO_fifo_purge(CO_fifo_t* fifo) { + if (fifo != NULL && fifo->readPtr != fifo->writePtr) { + fifo->readPtr = 0; + fifo->writePtr = 0; + return true; + } + + return false; +} + +/** + * Get free buffer space in CO_fifo_t object + * + * @param fifo This object + * + * @return number of available bytes + */ +static inline size_t +CO_fifo_getSpace(CO_fifo_t* fifo) { + int sizeLeft = (int)fifo->readPtr - (int)fifo->writePtr - 1; + if (sizeLeft < 0) { + sizeLeft += (int)fifo->bufSize; + } + + return (size_t)sizeLeft; +} + +/** + * Get size of data inside CO_fifo_t buffer object + * + * @param fifo This object + * + * @return number of occupied bytes + */ +static inline size_t +CO_fifo_getOccupied(CO_fifo_t* fifo) { + int sizeOccupied = (int)fifo->writePtr - (int)fifo->readPtr; + if (sizeOccupied < 0) { + sizeOccupied += (int)fifo->bufSize; + } + + return (size_t)sizeOccupied; +} + +/** + * Put one character into CO_fifo_t buffer object + * + * @param fifo This object + * @param c Character to put + * + * @return true, if write was successful (enough space in fifo buffer) + */ +static inline bool_t +CO_fifo_putc(CO_fifo_t* fifo, const uint8_t c) { + if (fifo != NULL && fifo->buf != NULL) { + size_t writePtrNext = fifo->writePtr + 1; + if (writePtrNext != fifo->readPtr && !(writePtrNext == fifo->bufSize && fifo->readPtr == 0)) { + fifo->buf[fifo->writePtr] = c; + fifo->writePtr = (writePtrNext == fifo->bufSize) ? 0 : writePtrNext; + return true; + } + } + return false; +} + +/** + * Put one character into CO_fifo_t buffer object + * + * Overwrite old characters, if run out of space + * + * @param fifo This object + * @param c Character to put + */ +static inline void +CO_fifo_putc_ov(CO_fifo_t* fifo, const uint8_t c) { + if (fifo != NULL && fifo->buf != NULL) { + fifo->buf[fifo->writePtr] = c; + + if (++fifo->writePtr == fifo->bufSize) { + fifo->writePtr = 0; + } + if (fifo->readPtr == fifo->writePtr) { + if (++fifo->readPtr == fifo->bufSize) { + fifo->readPtr = 0; + } + } + } +} + +/** + * Get one character from CO_fifo_t buffer object + * + * @param fifo This object + * @param buf Buffer of length one byte, where character will be copied + * + * @return true, if read was successful (non-empty fifo buffer) + */ +static inline bool_t +CO_fifo_getc(CO_fifo_t* fifo, uint8_t* buf) { + if (fifo != NULL && buf != NULL && fifo->readPtr != fifo->writePtr) { + *buf = fifo->buf[fifo->readPtr]; + if (++fifo->readPtr == fifo->bufSize) { + fifo->readPtr = 0; + } + return true; + } + return false; +} + +/** + * Write data into CO_fifo_t object. + * + * This function copies data from buf into internal buffer of CO_fifo_t object. Function returns number of bytes + * successfully copied. If there is not enough space in destination, not all bytes will be copied. + * + * @param fifo This object + * @param buf Buffer which will be copied + * @param count Number of bytes in buf + * @param [in,out] crc Externally defined variable for CRC checksum, ignored if NULL + * + * @return number of bytes actually written. + */ +size_t CO_fifo_write(CO_fifo_t* fifo, const uint8_t* buf, size_t count, uint16_t* crc); + +/** + * Read data from CO_fifo_t object. + * + * This function copies data from internal buffer of CO_fifo_t object into buf. Function returns number of bytes + * successfully copied. Function also writes true into eof argument, if command delimiter character is reached. + * + * @param fifo This object + * @param buf Buffer into which data will be copied + * @param count Copy up to count bytes into buffer + * @param [out] eof If different than NULL, then search for delimiter character. If found, then read up to (including) + * that character and set *eof to true. Otherwise set *eof to false. + * + * @return number of bytes actually read. + */ +size_t CO_fifo_read(CO_fifo_t* fifo, uint8_t* buf, size_t count, bool_t* eof); + +#if (((CO_CONFIG_FIFO)&CO_CONFIG_FIFO_ALT_READ) != 0) || defined CO_DOXYGEN +/** + * Initializes alternate read with #CO_fifo_altRead + * + * @param fifo This object + * @param offset Offset in bytes from original read pointer + * + * @return same as offset or lower, if there is not enough data. + */ +size_t CO_fifo_altBegin(CO_fifo_t* fifo, size_t offset); + +/** + * Ends alternate read with #CO_fifo_altRead and calculate crc checksum + * + * @param fifo This object + * @param [in,out] crc Externally defined variable for CRC checksum, ignored if NULL + */ +void CO_fifo_altFinish(CO_fifo_t* fifo, uint16_t* crc); + +/** + * Get alternate size of remaining data, see #CO_fifo_altRead + * + * @param fifo This object + * + * @return number of occupied bytes. + */ +static inline size_t +CO_fifo_altGetOccupied(CO_fifo_t* fifo) { + int sizeOccupied = (int)fifo->writePtr - (int)fifo->altReadPtr; + if (sizeOccupied < 0) { + sizeOccupied += (int)fifo->bufSize; + } + + return (size_t)sizeOccupied; +} + +/** + * Alternate read data from CO_fifo_t object. + * + * This function is similar as CO_fifo_read(), but uses alternate read pointer inside circular buffer. It reads data + * from the buffer and data remains in it. This function uses alternate read pointer and keeps original read pointer + * unchanged. Before using this function, alternate read pointer must be initialized with CO_fifo_altBegin(). + * CO_fifo_altFinish() sets original read pointer to alternate read pointer and so empties the buffer. + * + * @param fifo This object + * @param buf Buffer into which data will be copied + * @param count Copy up to count bytes into buffer + * + * @return number of bytes actually read. + */ +size_t CO_fifo_altRead(CO_fifo_t* fifo, uint8_t* buf, size_t count); +#endif /* #if (CO_CONFIG_FIFO) & CO_CONFIG_FIFO_ALT_READ */ + +#if (((CO_CONFIG_FIFO)&CO_CONFIG_FIFO_ASCII_COMMANDS) != 0) || defined CO_DOXYGEN +/** + * Search command inside FIFO + * + * If there are some data inside the FIFO, then search for command delimiter. + * + * If command is long, then in the buffer may not be enough space for it. In that case buffer is full and no delimiter + * is present. Function then returns true and command should be processed for the starting tokens. Buffer can later be + * refilled multiple times, until command is closed by command delimiter. + * + * If this function returns different than 0, then buffer is usually read by multiple CO_fifo_readToken() calls. If + * reads was successful, then delimiter is reached and fifo->readPtr is set after the command. If any + * CO_fifo_readToken() returns nonzero *err, then there is an error and command should be cleared. All this procedure + * must be implemented inside single function call. + * + * @param fifo This object. + * @param clear If true, then command will be cleared from the buffer. If there is no delimiter, buffer will be cleared + * entirely. + * + * @return true if command with delimiter found or buffer full. + */ +bool_t CO_fifo_CommSearch(CO_fifo_t* fifo, bool_t clear); + +/** + * Trim spaces inside FIFO + * + * Function removes all non graphical characters and comments from fifo buffer. It stops on first graphical character or + * on command delimiter (later is also removed). + * + * @param fifo This object. + * @param [in, out] insideComment if set to true as input, it skips all characters and searches only for delimiter. As + * output it is set to true, if fifo is empty, is inside comment and command delimiter is not found. + * + * @return true if command delimiter was found. + */ +bool_t CO_fifo_trimSpaces(CO_fifo_t* fifo, bool_t* insideComment); + +/** + * Get token from FIFO buffer + * + * Function search FIFO buffer for token. Token is string of only graphical characters. Graphical character is any + * printable character except space with acsii code within limits: 0x20 < code < 0x7F (see isgraph() function). + * + * If token is found, then copy it to the buf, if count is large enough. On success also set readPtr to point to the + * next graphical character. + * + * Each token must have at least one empty space after it (space, command delimiter, '\0', etc.). Delimiter must not be + * graphical character. + * + * If comment delimiter (delimComment, see #CO_fifo_init) character is found, then all string till command delimiter + * (delimCommand, see #CO_fifo_init) will be erased from the buffer. + * + * See also #CO_fifo_CommSearch(). + * + * @param fifo This object. + * @param buf Buffer into which data will be copied. Will be terminated by '\0'. + * @param count Copy up to count bytes into buffer + * @param [in,out] closed This is input/output variable. Not used if NULL. + * - As output variable it is set to 1, if command delimiter (delimCommand, see #CO_fifo_init) is found after the token + * and set to 0 otherwise. + * - As input variable it is used for verifying error condition: + * - *closed = 0: Set *err to true if token is empty or command delimiter is found. + * - *closed = 1: Set *err to true if token is empty or command delimiter is NOT found. + * - *closed = any other value: No checking of token size or command delimiter. + * @param [out] err If not NULL, it is set to true if token is larger than buf or in matching combination in 'closed' + * argument. If it is already true, then function returns immediately. + * + * @return Number of bytes read. + */ +size_t CO_fifo_readToken(CO_fifo_t* fifo, char* buf, size_t count, uint8_t* closed, bool_t* err); +#endif /* (CO_CONFIG_FIFO) & CO_CONFIG_FIFO_ASCII_COMMANDS */ + +#if (((CO_CONFIG_FIFO)&CO_CONFIG_FIFO_ASCII_DATATYPES) != 0) || defined CO_DOXYGEN +/** + * Read uint8_t variable from fifo and output as ascii string. + * + * @param fifo This object. + * @param buf Buffer into which ascii string will be copied. + * @param count Available count of bytes inside the buf. + * @param end True indicates, that fifo contains last bytes of data. + * + * @return Number of ascii bytes written into buf. + */ +size_t CO_fifo_readU82a(CO_fifo_t* fifo, char* buf, size_t count, bool_t end); +/** Read uint16_t variable from fifo as ascii string, see CO_fifo_readU82a */ +size_t CO_fifo_readU162a(CO_fifo_t* fifo, char* buf, size_t count, bool_t end); +/** Read uint32_t variable from fifo as ascii string, see CO_fifo_readU82a */ +size_t CO_fifo_readU322a(CO_fifo_t* fifo, char* buf, size_t count, bool_t end); +/** Read uint64_t variable from fifo as ascii string, see CO_fifo_readU82a */ +size_t CO_fifo_readU642a(CO_fifo_t* fifo, char* buf, size_t count, bool_t end); +/** Read uint8_t variable from fifo as ascii string, see CO_fifo_readU82a */ +size_t CO_fifo_readX82a(CO_fifo_t* fifo, char* buf, size_t count, bool_t end); +/** Read uint16_t variable from fifo as ascii string, see CO_fifo_readU82a */ +size_t CO_fifo_readX162a(CO_fifo_t* fifo, char* buf, size_t count, bool_t end); +/** Read uint32_t variable from fifo as ascii string, see CO_fifo_readU82a */ +size_t CO_fifo_readX322a(CO_fifo_t* fifo, char* buf, size_t count, bool_t end); +/** Read uint64_t variable from fifo as ascii string, see CO_fifo_readU82a */ +size_t CO_fifo_readX642a(CO_fifo_t* fifo, char* buf, size_t count, bool_t end); +/** Read int8_t variable from fifo as ascii string, see CO_fifo_readU82a */ +size_t CO_fifo_readI82a(CO_fifo_t* fifo, char* buf, size_t count, bool_t end); +/** Read int16_t variable from fifo as ascii string, see CO_fifo_readU82a */ +size_t CO_fifo_readI162a(CO_fifo_t* fifo, char* buf, size_t count, bool_t end); +/** Read int32_t variable from fifo as ascii string, see CO_fifo_readU82a */ +size_t CO_fifo_readI322a(CO_fifo_t* fifo, char* buf, size_t count, bool_t end); +/** Read int64_t variable from fifo as ascii string, see CO_fifo_readU82a */ +size_t CO_fifo_readI642a(CO_fifo_t* fifo, char* buf, size_t count, bool_t end); +/** Read float32_t variable from fifo as ascii string, see CO_fifo_readU82a */ +size_t CO_fifo_readR322a(CO_fifo_t* fifo, char* buf, size_t count, bool_t end); +/** Read float64_t variable from fifo as ascii string, see CO_fifo_readU82a */ +size_t CO_fifo_readR642a(CO_fifo_t* fifo, char* buf, size_t count, bool_t end); +/** Read data from fifo and output as space separated two digit ascii string, + * see also CO_fifo_readU82a */ +size_t CO_fifo_readHex2a(CO_fifo_t* fifo, char* buf, size_t count, bool_t end); +/** Read data from fifo and output as visible string. A visible string is enclosed with double quotes. If a double quote + * is used within the string, the quotes are escaped by a second quotes, e.g. “Hello “”World””, CANopen is great”. UTF-8 + * characters and also line breaks works with this function. Function removes all NULL and CR characters from output + * string. See also CO_fifo_readU82a */ +size_t CO_fifo_readVs2a(CO_fifo_t* fifo, char* buf, size_t count, bool_t end); +/** Read data from fifo and output as mime-base64 encoded string. Encoding is as specified in RFC 2045, without CR-LF, + * but one long string. See also CO_fifo_readU82a */ +size_t CO_fifo_readB642a(CO_fifo_t* fifo, char* buf, size_t count, bool_t end); + +/** + * @defgroup uint8_t Bitfields for status argument from CO_fifo_cpyTok2U8 function and similar + * @{ + */ +#define CO_fifo_st_closed 0x01U /**< Bit is set, if command delimiter is reached in src */ +#define CO_fifo_st_partial \ + 0x02U /**< Bit is set, if copy was partial and more data are available. If unset and no error, then all data was \ + successfully copied. */ +#define CO_fifo_st_errTok 0x10U /**< Bit is set, if no valid token found */ +#define CO_fifo_st_errVal 0x20U /**< Bit is set, if value is not valid or out of limits */ +#define CO_fifo_st_errBuf 0x40U /**< Bit is set, if destination buffer is to small */ +#define CO_fifo_st_errInt 0x80U /**< Bit is set, if internal error */ +#define CO_fifo_st_errMask 0xF0U /**< Bitmask for error bits */ +/** @} */ /* uint8_t Bitfields */ + +/** + * Read ascii string from src fifo and copy as uint8_t variable to dest fifo. + * + * @param dest destination fifo buffer object. + * @param src source fifo buffer object. + * @param [out] status bitfield of the uint8_t type. + * + * @return Number of bytes written into dest. + */ +size_t CO_fifo_cpyTok2U8(CO_fifo_t* dest, CO_fifo_t* src, uint8_t* status); +/** Copy ascii string to uint16_t variable, see CO_fifo_cpyTok2U8 */ +size_t CO_fifo_cpyTok2U16(CO_fifo_t* dest, CO_fifo_t* src, uint8_t* status); +/** Copy ascii string to uint32_t variable, see CO_fifo_cpyTok2U8 */ +size_t CO_fifo_cpyTok2U32(CO_fifo_t* dest, CO_fifo_t* src, uint8_t* status); +/** Copy ascii string to uint64_t variable, see CO_fifo_cpyTok2U8 */ +size_t CO_fifo_cpyTok2U64(CO_fifo_t* dest, CO_fifo_t* src, uint8_t* status); +/** Copy ascii string to int8_t variable, see CO_fifo_cpyTok2U8 */ +size_t CO_fifo_cpyTok2I8(CO_fifo_t* dest, CO_fifo_t* src, uint8_t* status); +/** Copy ascii string to int16_t variable, see CO_fifo_cpyTok2U8 */ +size_t CO_fifo_cpyTok2I16(CO_fifo_t* dest, CO_fifo_t* src, uint8_t* status); +/** Copy ascii string to int32_t variable, see CO_fifo_cpyTok2U8 */ +size_t CO_fifo_cpyTok2I32(CO_fifo_t* dest, CO_fifo_t* src, uint8_t* status); +/** Copy ascii string to int64_t variable, see CO_fifo_cpyTok2U8 */ +size_t CO_fifo_cpyTok2I64(CO_fifo_t* dest, CO_fifo_t* src, uint8_t* status); +/** Copy ascii string to float32_t variable, see CO_fifo_cpyTok2U8 */ +size_t CO_fifo_cpyTok2R32(CO_fifo_t* dest, CO_fifo_t* src, uint8_t* status); +/** Copy ascii string to float64_t variable, see CO_fifo_cpyTok2U8 */ +size_t CO_fifo_cpyTok2R64(CO_fifo_t* dest, CO_fifo_t* src, uint8_t* status); +/** Copy bytes written as two hex digits into to data. Bytes may be space separated. See CO_fifo_cpyTok2U8 for + * parameters. */ +size_t CO_fifo_cpyTok2Hex(CO_fifo_t* dest, CO_fifo_t* src, uint8_t* status); +/** Copy visible string to data. A visible string must be enclosed with double quotes, if it contains space. If a double + * quote is used within the string, the quotes are escaped by a second quotes. Input string can not contain newline + * characters. See CO_fifo_cpyTok2U8 */ +size_t CO_fifo_cpyTok2Vs(CO_fifo_t* dest, CO_fifo_t* src, uint8_t* status); +/** Read ascii mime-base64 encoded string from src fifo and copy as binary data to dest fifo. Encoding is as specified + * in RFC 2045, without CR-LF, but one long string in single line. See also CO_fifo_readU82a */ +size_t CO_fifo_cpyTok2B64(CO_fifo_t* dest, CO_fifo_t* src, uint8_t* status); + +#endif /* (CO_CONFIG_FIFO) & CO_CONFIG_FIFO_ASCII_DATATYPES */ + +/** @} */ /* CO_CANopen_301_fifo */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* (CO_CONFIG_FIFO) & CO_CONFIG_FIFO_ENABLE */ + +#endif /* CO_FIFO_H */ diff --git a/302/CO_Prog_Download.c b/302/CO_Prog_Download.c new file mode 100644 index 00000000..1bb04ba4 --- /dev/null +++ b/302/CO_Prog_Download.c @@ -0,0 +1,365 @@ +/* + * CANopen Program Download (CiA 302-3 oriented, streaming + storage-friendly) + * + * @file CO_Prog_Download.c + * @ingroup CO_Prog_Download + * @author BitConcepts + * @copyright 2025 BitConcepts + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#include "302/CO_Prog_Download.h" + +#if ((CO_CONFIG_PROG_DOWNLOAD) & CO_CONFIG_PROG_DOWNLOAD_ENABLE) != 0 + +#ifndef CO_PROGDL_EDS_MAX +#ifdef CO_CONFIG_PROG_DOWNLOAD_EDS_MAX_SIZE +#define CO_PROGDL_EDS_MAX ((uint32_t)CO_CONFIG_PROG_DOWNLOAD_EDS_MAX_SIZE) +#else +#define CO_PROGDL_EDS_MAX (2048) +#endif +#endif + +/* If globals are requested, provide a static buffer for EDS data. */ +#ifdef CO_USE_GLOBALS +static uint8_t CO_ProgDL_EDS_StaticBuf[CO_PROGDL_EDS_MAX]; +#endif + +/* ---------- Internal helpers ---------- */ + +static inline void +pdl_setStatus(CO_ProgDL_t* pdl, uint32_t set, uint32_t clr) { + pdl->status &= ~clr; + pdl->status |= set; +} + +/* Optionally persist EDS via CO_storage after a completed transfer. */ +#if ((CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE) != 0 +static ODR_t +pdl_persistEDS(CO_ProgDL_t* pdl) { + if ((pdl != NULL) && pdl->storage && pdl->edsEntry && pdl->storage->store) { + return pdl->storage->store(pdl->edsEntry, pdl->storage->CANmodule); + } + return ODR_OK; +} +#endif /* (CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE */ + +/* ---------- OD extension read/write hooks ---------- */ + +/* 0x1F21 Store format (RO) */ +static ODR_t +OD_read_1F21(OD_stream_t* stream, void* buf, OD_size_t count, OD_size_t* countRead) { + (void)buf; + (void)count; + + if ((buf == NULL) || (stream == NULL) || (countRead == NULL)) { + return ODR_DEV_INCOMPAT; + } + + uint32_t fmt = CO_PROGDL_STORE_FMT_PROG; + return OD_readOriginal(stream, &fmt, sizeof(fmt), countRead); +} + +/* 0x1F24 Store format EDS NMT slave (RO) */ +static ODR_t +OD_read_1F24(OD_stream_t* stream, void* buf, OD_size_t count, OD_size_t* countRead) { + (void)buf; + (void)count; + + if ((buf == NULL) || (stream == NULL) || (countRead == NULL)) { + return ODR_DEV_INCOMPAT; + } + + uint32_t fmt = CO_PROGDL_STORE_FMT_EDS; + return OD_readOriginal(stream, &fmt, sizeof(fmt), countRead); +} + +/* 0x1F56 Program software identification (RW VISIBLE_STRING) */ +static ODR_t +OD_read_1F56(OD_stream_t* stream, void* buf, OD_size_t count, OD_size_t* countRead) { + if ((buf == NULL) || (stream == NULL) || (countRead == NULL)) { + return ODR_DEV_INCOMPAT; + } + + CO_ProgDL_t* pdl = (CO_ProgDL_t*)stream->object; + size_t len = strnlen(pdl->swId, sizeof(pdl->swId)); + return OD_readOriginal(stream, pdl->swId, (OD_size_t)len, countRead); +} + +static ODR_t +OD_write_1F56(OD_stream_t* stream, const void* buf, OD_size_t count, OD_size_t* countWritten) { + if ((stream == NULL) || (buf == NULL) || (countWritten == NULL)) { + return ODR_DEV_INCOMPAT; + } + + CO_ProgDL_t* pdl = (CO_ProgDL_t*)stream->object; + size_t n = (size_t)((count < sizeof(pdl->swId) - 1) ? count : (sizeof(pdl->swId) - 1)); + (void)memset(pdl->swId, 0, sizeof(pdl->swId)); + (void)memcpy(pdl->swId, buf, n); + if (countWritten) { + *countWritten = (OD_size_t)n; + } + return ODR_OK; +} + +/* 0x1F57 Flash status identification (RO U32) */ +static ODR_t +OD_read_1F57(OD_stream_t* stream, void* buf, OD_size_t count, OD_size_t* countRead) { + if ((buf == NULL) || (stream == NULL) || (countRead == NULL)) { + return ODR_DEV_INCOMPAT; + } + + CO_ProgDL_t* pdl = (CO_ProgDL_t*)stream->object; + return OD_readOriginal(stream, &pdl->status, sizeof(pdl->status), countRead); +} + +/* 0x1F50 Program data – forward chunks to streaming backend and update CRC16-CCITT */ +static ODR_t +OD_write_1F50(OD_stream_t* stream, const void* buf, OD_size_t count, OD_size_t* countWritten) { + if ((stream == NULL) || (buf == NULL) || (countWritten == NULL)) { + return ODR_DEV_INCOMPAT; + } + + CO_ProgDL_t* pdl = (CO_ProgDL_t*)stream->object; + if (!pdl->sessionOpen) { + return ODR_NO_RESOURCE; /* require BEGIN */ + } + if (!pdl->ops.write) { + return ODR_GENERAL; + } + + if (!pdl->ops.write((const uint8_t*)buf, (uint32_t)count)) { + pdl_setStatus(pdl, CO_PROGDL_STATUS_ERROR, 0); + return ODR_HW; + } + + pdl->bytesReceived += (uint32_t)count; + pdl->runningCRC16 = CO_crc16_ccitt((const uint8_t*)buf, (size_t)count, pdl->runningCRC16); + pdl_setStatus(pdl, CO_PROGDL_STATUS_RECEIVING | CO_PROGDL_STATUS_WRITING, 0); + + if (countWritten) { + *countWritten = count; + } + return ODR_OK; +} + +/* 0x1F51 Program control (U32) */ +static ODR_t +OD_write_1F51(OD_stream_t* stream, const void* buf, OD_size_t count, OD_size_t* countWritten) { + (void)count; + + if ((stream == NULL) || (buf == NULL) || (countWritten == NULL)) { + return ODR_DEV_INCOMPAT; + } + + CO_ProgDL_t* pdl = (CO_ProgDL_t*)stream->object; + uint32_t cmd = 0; + (void)memcpy(&cmd, buf, sizeof(uint32_t)); + + switch (cmd) { + case CO_PROGDL_CTRL_ABORT: + if (pdl->ops.abort) { + pdl->ops.abort(); + } + pdl->sessionOpen = false; + pdl->bytesReceived = 0; + pdl->runningCRC16 = 0; + pdl->imageSizeHint = 0; + pdl_setStatus(pdl, 0, + CO_PROGDL_STATUS_RECEIVING | CO_PROGDL_STATUS_WRITING | CO_PROGDL_STATUS_ERASING + | CO_PROGDL_STATUS_VERIFYING | CO_PROGDL_STATUS_DONE_OK | CO_PROGDL_STATUS_ERROR); + break; + + case CO_PROGDL_CTRL_BEGIN: + if (!pdl->ops.begin) { + return ODR_GENERAL; + } + pdl_setStatus(pdl, CO_PROGDL_STATUS_ERASING, CO_PROGDL_STATUS_DONE_OK | CO_PROGDL_STATUS_ERROR); + if (!pdl->ops.begin(pdl->imageSizeHint)) { + pdl_setStatus(pdl, CO_PROGDL_STATUS_ERROR, CO_PROGDL_STATUS_ERASING); + return ODR_HW; + } + pdl->sessionOpen = true; + pdl->bytesReceived = 0; + pdl->runningCRC16 = 0; + pdl_setStatus(pdl, CO_PROGDL_STATUS_RECEIVING, CO_PROGDL_STATUS_ERASING); + break; + + case CO_PROGDL_CTRL_COMMIT: + if (!pdl->sessionOpen) { + return ODR_INVALID_VALUE; + } + if (!pdl->ops.commit) { + return ODR_GENERAL; + } + pdl_setStatus(pdl, CO_PROGDL_STATUS_VERIFYING, CO_PROGDL_STATUS_RECEIVING | CO_PROGDL_STATUS_WRITING); + if (!pdl->ops.commit()) { + pdl_setStatus(pdl, CO_PROGDL_STATUS_ERROR, CO_PROGDL_STATUS_VERIFYING); + return ODR_HW; + } + /* Optionally persist via CO_storage (e.g. mark/program metadata) */ +#if ((CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE) != 0 + if (pdl->storage && pdl->fwEntry && pdl->storage->store) { + ODR_t r = pdl->storage->store(pdl->fwEntry, pdl->storage->CANmodule); + if (r != ODR_OK) { + pdl_setStatus(pdl, CO_PROGDL_STATUS_ERROR, CO_PROGDL_STATUS_DONE_OK); + return ODR_HW; + } + } +#endif + pdl->sessionOpen = false; + pdl_setStatus(pdl, CO_PROGDL_STATUS_DONE_OK, CO_PROGDL_STATUS_VERIFYING); + break; + + case CO_PROGDL_CTRL_JUMP_BOOT: + if (pdl->ops.jumpToBootloader) { + pdl->ops.jumpToBootloader(); + } + return ODR_GENERAL; /* Should not return if jump was successful */ + + default: return ODR_INVALID_VALUE; + } + + if (countWritten) { + *countWritten = sizeof(uint32_t); + } + return ODR_OK; +} + +/* 0x1F23 Store EDS NMT slave – buffered here; app can persist via CO_storage */ +static ODR_t +OD_write_1F23(OD_stream_t* stream, const void* buf, OD_size_t count, OD_size_t* countWritten) { + if ((stream == NULL) || (buf == NULL) || (countWritten == NULL)) { + return ODR_DEV_INCOMPAT; + } + + CO_ProgDL_t* pdl = (CO_ProgDL_t*)stream->object; + +#ifdef CO_USE_GLOBALS + /* Use statically defined buffer; initialize it lazily if needed */ + if (pdl->edsBuf == NULL) { + pdl->edsBuf = CO_ProgDL_EDS_StaticBuf; + pdl->edsLen = 0; + } +#else + /* Allocate from heap on first use */ + if (pdl->edsBuf == NULL) { + pdl->edsBuf = (uint8_t*)CO_alloc((size_t)CO_PROGDL_EDS_MAX); + if (pdl->edsBuf == NULL) { + return ODR_OUT_OF_MEM; + } + pdl->edsLen = 0; + } +#endif + + if (pdl->edsLen + count > CO_PROGDL_EDS_MAX) { + return ODR_DATA_LONG; + } + (void)memcpy(&pdl->edsBuf[pdl->edsLen], buf, count); + pdl->edsLen += (uint32_t)count; + if (countWritten) { + *countWritten = count; + } + return ODR_OK; +} + +/* ---------- Initialization / API ---------- */ + +static void +attach_ext(CO_ProgDL_t* pdl, OD_extension_t* ext, uint16_t idx, void* rd, void* wr) { + OD_entry_t* entry = OD_find(pdl->co->SDOserver->OD, idx); + if (entry) { + ext->read = rd; + ext->write = wr; + (void)OD_extension_init(entry, ext); + } +} + +CO_ReturnError_t +CO_Prog_Download_init(CO_ProgDL_t* pdl, CO_t* co) { + if ((pdl == NULL) || (co == NULL)) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + (void)memset(pdl, 0, sizeof(*pdl)); + pdl->co = co; + pdl->status = CO_PROGDL_STATUS_IDLE; + +#ifdef CO_USE_GLOBALS + /* Pre-bind the static buffer for EDS. */ + pdl->edsBuf = CO_ProgDL_EDS_StaticBuf; + pdl->edsLen = 0; +#endif + + attach_ext(pdl, &pdl->ext_1F21, 0x1F21, OD_read_1F21, NULL); + attach_ext(pdl, &pdl->ext_1F24, 0x1F24, OD_read_1F24, NULL); + attach_ext(pdl, &pdl->ext_1F56, 0x1F56, OD_read_1F56, OD_write_1F56); + attach_ext(pdl, &pdl->ext_1F57, 0x1F57, OD_read_1F57, NULL); + attach_ext(pdl, &pdl->ext_1F50, 0x1F50, NULL, OD_write_1F50); + attach_ext(pdl, &pdl->ext_1F51, 0x1F51, NULL, OD_write_1F51); + attach_ext(pdl, &pdl->ext_1F23, 0x1F23, NULL, OD_write_1F23); + + return CO_ERROR_NO; +} + +void +CO_Prog_Download_deinit(CO_ProgDL_t* pdl) { + if (pdl == NULL) { + return; + } +#ifndef CO_USE_GLOBALS + if (pdl->edsBuf) { + CO_free(pdl->edsBuf); + pdl->edsBuf = NULL; + } +#else + /* Using static storage: nothing to free, just clear length. */ + pdl->edsLen = 0; +#endif +} + +int +CO_Prog_Download_registerStreamOps(CO_ProgDL_t* pdl, const CO_ProgDL_StreamOps_t* ops) { + if ((pdl == NULL) || (ops == NULL)) { + return -1; + } + pdl->ops = *ops; + return 0; +} + +void +CO_Prog_Download_setImageSizeHint(CO_ProgDL_t* pdl, uint32_t size_hint) { + if (pdl == NULL) { + return; + } + pdl->imageSizeHint = size_hint; +} + +#if ((CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE) != 0 +void +CO_Prog_Download_bindStorage(CO_ProgDL_t* pdl, CO_storage_t* storage, CO_storage_entry_t* fwEntry, + CO_storage_entry_t* edsEntry) { + if (pdl == NULL) { + return; + } + pdl->storage = storage; + pdl->fwEntry = fwEntry; + pdl->edsEntry = edsEntry; +} + +ODR_t +CO_Prog_Download_persistEDS(CO_ProgDL_t* pdl) { + return pdl_persistEDS(pdl); +} +#endif /* (CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE */ +#endif /* (CO_CONFIG_PROG_DOWNLOAD) & CO_CONFIG_PROG_DOWNLOAD_ENABLE */ diff --git a/302/CO_Prog_Download.h b/302/CO_Prog_Download.h new file mode 100644 index 00000000..4fe663b1 --- /dev/null +++ b/302/CO_Prog_Download.h @@ -0,0 +1,215 @@ +/** + * CANopen Program Download (CiA 302-3 oriented, streaming + storage-friendly) + * + * @file CO_Prog_Download.h + * @ingroup CO_Prog_Download + * @author BitConcepts + * @copyright 2025 BitConcepts + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#ifndef CO_PROG_DOWNLOAD_H +#define CO_PROG_DOWNLOAD_H + +#include "CANopen.h" +#include "301/CO_crc16-ccitt.h" +#include "301/CO_driver.h" +#include "301/CO_ODinterface.h" + +#if ((CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE) != 0 +#include "storage/CO_storage.h" +#endif + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* default configuration, see CO_config.h */ +#ifndef CO_CONFIG_PROG_DOWNLOAD +#define CO_CONFIG_PROG_DOWNLOAD (CO_CONFIG_PROG_DOWNLOAD_ENABLE | CO_CONFIG_PROG_DOWNLOAD_PERMANENT) +#endif + +#if (((CO_CONFIG_PROG_DOWNLOAD) & CO_CONFIG_PROG_DOWNLOAD_ENABLE) != 0) || defined CO_DOXYGEN + +/** + * @defgroup CO_Prog_Download Program Download + * Program Download support per CiA 302-3 using CANopen SDO (DOMAIN) transfer. + * + * @ingroup CO_CANopen_302 + * @{ + * + * Bytes arrive over SDO into 0x1F50 and are forwarded to a streaming backend + * (e.g., flash writer, bootloader) via `begin`/`write`/`commit`. This module is + * storage-agnostic and *optionally* binds to CO_storage to persist metadata/EDS. + * + * ### OD entries used by this module + * - 0x1F21: Store format (U32, RO) + * - 0x1F23: Store EDS NMT slave (DOMAIN) + * - 0x1F24: Store format EDS NMT slave (U32, RO) + * - 0x1F50: Program data (DOMAIN) + * - 0x1F51: Program control (U32) + * - 0x1F56: Program software identification (VISIBLE_STRING) + * - 0x1F57: Flash status identification (U32, RO) + * + * ### Control values for 0x1F51 + * - 0x00000000: ABORT + * - 0x00000001: BEGIN + * - 0x00000002: COMMIT + * - 0x0000B007: JUMP (optional jump to bootloader) + */ + +/** Streaming backend operations supplied by the application/port. */ +typedef struct { + /** Called on BEGIN. May prepare erase or open a stream. */ + bool_t (*begin)(uint32_t image_size_hint); + /** Called for each chunk written to 0x1F50. Must be safe for repeated calls. */ + bool_t (*write)(const uint8_t* data, uint32_t len); + /** Called on COMMIT to finalize/verify and make image ready. */ + bool_t (*commit)(void); + /** Called on ABORT to cleanup (optional). */ + void (*abort)(void); + /** Optional: jump to bootloader after successful programming. */ + void (*jumpToBootloader)(void); +} CO_ProgDL_StreamOps_t; + +/** Status bits for 0x1F57. */ +typedef enum { + CO_PROGDL_STATUS_IDLE = 0u, + CO_PROGDL_STATUS_RECEIVING = 1u << 0, + CO_PROGDL_STATUS_ERASING = 1u << 1, + CO_PROGDL_STATUS_WRITING = 1u << 2, + CO_PROGDL_STATUS_VERIFYING = 1u << 3, + CO_PROGDL_STATUS_DONE_OK = 1u << 4, + CO_PROGDL_STATUS_ERROR = 1u << 5 +} CO_ProgDL_StatusBit_t; + +/** Control values for 0x1F51. */ +typedef enum { + CO_PROGDL_CTRL_ABORT = 0x00000000u, + CO_PROGDL_CTRL_BEGIN = 0x00000001u, + CO_PROGDL_CTRL_COMMIT = 0x00000002u, + CO_PROGDL_CTRL_JUMP_BOOT = 0x0000B007u +} CO_ProgDL_Control_t; + +/** Informational store format constants returned in 0x1F21/0x1F24. */ +typedef enum { CO_PROGDL_STORE_FMT_PROG = 0x00000001u, CO_PROGDL_STORE_FMT_EDS = 0x00000001u } CO_ProgDL_StoreFmt_t; + +/** Program Download object */ +typedef struct { + /* Core */ + CO_t* co; /**< From CO_Prog_Download_init() */ + +/* Optional: CANopenNode Storage binding (0x1010/0x1011) */ +#if ((CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE) != 0 + CO_storage_t* storage; /**< From CO_Prog_Download_bindStorage(), optional */ + CO_storage_entry_t* fwEntry; /**< Storage entry for firmware image metadata, optional */ + CO_storage_entry_t* edsEntry; /**< Storage entry for EDS, optional */ +#endif + + /* Streaming backend (port-supplied) */ + CO_ProgDL_StreamOps_t ops; + + /* State */ + uint32_t status; /**< Status bitfield for 0x1F57 */ + bool_t sessionOpen; /**< True after BEGIN until COMMIT/ABORT */ + uint32_t bytesReceived; /**< Bytes written to 0x1F50 in current session */ + uint32_t imageSizeHint; /**< Optional size hint (0 if unknown) */ + uint16_t runningCRC16; /**< Running CRC16 (CRC16-CCITT) */ + + /* 0x1F56: software identification */ + char swId[64]; + + /* EDS buffering (0x1F23); can be replaced by app-specific persistence */ + uint8_t* edsBuf; + uint32_t edsLen; + + /* OD extensions */ + OD_extension_t ext_1F21, ext_1F23, ext_1F24, ext_1F50, ext_1F51, ext_1F56, ext_1F57; +} CO_ProgDL_t; + +/** + * Initialize Program Download object. + * + * Function must be called in the communication reset section, after SDO/OD are ready. + * + * @param pdl This object will be initialized. + * @param co CANopen root object. + * + * @return #CO_ReturnError_t CO_ERROR_NO on success. + */ +CO_ReturnError_t CO_Prog_Download_init(CO_ProgDL_t* pdl, CO_t* co); + +/** + * Deinitialize Program Download object. + * + * @param pdl This object. + */ +void CO_Prog_Download_deinit(CO_ProgDL_t* pdl); + +/** + * Register streaming backend operations (required). + * + * @param pdl Program Download object. + * @param ops Streaming operations; copied by value. + * + * @return 0 on success, -1 on invalid args. + */ +int CO_Prog_Download_registerStreamOps(CO_ProgDL_t* pdl, const CO_ProgDL_StreamOps_t* ops); + +/** + * Optionally set an image size hint (forwarded to ops.begin()). + * + * @param pdl Program Download object. + * @param size_hint Expected total image size in bytes (0 if unknown). + */ +void CO_Prog_Download_setImageSizeHint(CO_ProgDL_t* pdl, uint32_t size_hint); + +#if ((CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE) != 0 +/** + * Optionally bind to CANopenNode Storage API. + * + * If bound, this module will call `storage->store(fwEntry, ...)` after a successful + * COMMIT. Application may also persist EDS after transfer using + * CO_Prog_Download_persistEDS(). + * + * @param pdl Program Download object. + * @param storage CO_storage instance. + * @param fwEntry Storage entry describing the firmware image metadata region. + * @param edsEntry Storage entry describing EDS region. + */ +#if ((CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE) != 0 +void CO_Prog_Download_bindStorage(CO_ProgDL_t* pdl, CO_storage_t* storage, CO_storage_entry_t* fwEntry, + CO_storage_entry_t* edsEntry); +#endif +/** + * Persist EDS (0x1F23) via CO_storage after transfer completes. + * + * This is optional and only effective if CO_storage is bound and `edsEntry` provided. + * + * @param pdl Program Download object. + * @return ODR_OK on success or an appropriate ODR_* error. + */ +ODR_t CO_Prog_Download_persistEDS(CO_ProgDL_t* pdl); +#endif /* (CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE */ + +/** @} */ /* CO_Prog_Download */ + +#endif /* (CO_CONFIG_PROG_DOWNLOAD) & CO_CONFIG_PROG_DOWNLOAD_ENABLE */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CO_PROG_DOWNLOAD_H */ diff --git a/303/CO_LEDs.c b/303/CO_LEDs.c new file mode 100644 index 00000000..96b2dcd4 --- /dev/null +++ b/303/CO_LEDs.c @@ -0,0 +1,200 @@ +/* + * CANopen Indicator specification (CiA 303-3 v1.4.0) + * + * @file CO_LEDs.h + * @ingroup CO_LEDs + * @author Janez Paternoster + * @copyright 2020 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#include "303/CO_LEDs.h" + +#if ((CO_CONFIG_LEDS) & CO_CONFIG_LEDS_ENABLE) != 0 + +CO_ReturnError_t +CO_LEDs_init(CO_LEDs_t* LEDs) { + CO_ReturnError_t ret = CO_ERROR_NO; + + /* verify arguments */ + if (LEDs == NULL) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + /* clear the object */ + (void)memset(LEDs, 0, sizeof(CO_LEDs_t)); + +#if (CO_CONFIG_LEDS_CALLBACK) != 0 + /* Explicitly ensure callback fields are cleared even if layout changes */ + LEDs->cb = NULL; + LEDs->cb_user = NULL; +#endif + + return ret; +} + +#if (CO_CONFIG_LEDS_CALLBACK) != 0 +void +CO_LEDs_registerCallback(CO_LEDs_t* LEDs, CO_LEDs_cb_t cb, void* user_arg) { + if (LEDs == NULL) { + return; + } + LEDs->cb = cb; + LEDs->cb_user = user_arg; +} +#endif /* CO_CONFIG_LEDS_CALLBACK */ + +void +CO_LEDs_process(CO_LEDs_t* LEDs, uint32_t timeDifference_us, CO_NMT_internalState_t NMTstate, bool_t LSSconfig, + bool_t ErrCANbusOff, bool_t ErrCANbusWarn, bool_t ErrRpdo, bool_t ErrSync, bool_t ErrHbCons, + bool_t ErrOther, bool_t firmwareDownload, uint32_t* timerNext_us) { + (void)timerNext_us; /* may be unused */ + + uint8_t rd = 0; + uint8_t gr = 0; + bool_t tick = false; + + LEDs->LEDtmr50ms += timeDifference_us; + while (LEDs->LEDtmr50ms >= 50000U) { + bool_t rdFlickerNext = (LEDs->LEDred & (uint8_t)CO_LED_flicker) == 0U; + + tick = true; + LEDs->LEDtmr50ms -= 50000U; + + if (++LEDs->LEDtmr200ms > 3U) { + /* calculate 2,5Hz blinking and flashing */ + LEDs->LEDtmr200ms = 0; + rd = 0; + gr = 0; + + if ((LEDs->LEDred & CO_LED_blink) == 0U) { + rd |= CO_LED_blink; + } else { + gr |= CO_LED_blink; + } + + switch (++LEDs->LEDtmrflash_1) { + case 1: rd |= CO_LED_flash_1; break; + case 2: gr |= CO_LED_flash_1; break; + case 6: LEDs->LEDtmrflash_1 = 0; break; + default: /* none */ break; + } + switch (++LEDs->LEDtmrflash_2) { + case 1: + case 3: rd |= CO_LED_flash_2; break; + case 2: + case 4: gr |= CO_LED_flash_2; break; + case 8: LEDs->LEDtmrflash_2 = 0; break; + default: /* none */ break; + } + switch (++LEDs->LEDtmrflash_3) { + case 1: + case 3: + case 5: rd |= CO_LED_flash_3; break; + case 2: + case 4: + case 6: gr |= CO_LED_flash_3; break; + case 10: LEDs->LEDtmrflash_3 = 0; break; + default: /* none */ break; + } + switch (++LEDs->LEDtmrflash_4) { + case 1: + case 3: + case 5: + case 7: rd |= CO_LED_flash_4; break; + case 2: + case 4: + case 6: + case 8: gr |= CO_LED_flash_4; break; + case 12: LEDs->LEDtmrflash_4 = 0; break; + default: /* none */ break; + } + } else { + /* clear flicker and CANopen bits, keep others */ + rd = LEDs->LEDred & (0xFFU ^ (CO_LED_flicker | CO_LED_CANopen)); + gr = LEDs->LEDgreen & (0xFFU ^ (CO_LED_flicker | CO_LED_CANopen)); + } + + /* calculate 10Hz flickering */ + if (rdFlickerNext) { + rd |= CO_LED_flicker; + } else { + gr |= CO_LED_flicker; + } + + } /* while (LEDs->LEDtmr50ms >= 50000) */ + + if (tick) { + uint8_t rd_co, gr_co; + + /* CANopen red ERROR LED */ + if (ErrCANbusOff) { + rd_co = 1; + } else if (NMTstate == CO_NMT_INITIALIZING) { + rd_co = rd & CO_LED_flicker; + } else if (ErrRpdo) { + rd_co = rd & CO_LED_flash_4; + } else if (ErrSync) { + rd_co = rd & CO_LED_flash_3; + } else if (ErrHbCons) { + rd_co = rd & CO_LED_flash_2; + } else if (ErrCANbusWarn) { + rd_co = rd & CO_LED_flash_1; + } else if (ErrOther) { + rd_co = rd & CO_LED_blink; + } else { + rd_co = 0; + } + +#if CO_CONFIG_LEDS_CALLBACK + /* Invoke application callback after state update */ + if (LEDs->cb != NULL) { + LEDs->cb(LEDs, LEDs->cb_user); + } +#endif + + /* CANopen green RUN LED */ + if (LSSconfig) { + gr_co = gr & CO_LED_flicker; + } else if (firmwareDownload) { + gr_co = gr & CO_LED_flash_3; + } else if (NMTstate == CO_NMT_STOPPED) { + gr_co = gr & CO_LED_flash_1; + } else if (NMTstate == CO_NMT_PRE_OPERATIONAL) { + gr_co = gr & CO_LED_blink; + } else if (NMTstate == CO_NMT_OPERATIONAL) { + gr_co = 1; + } else { + gr_co = 0; + } + + if (rd_co != 0U) { + rd |= CO_LED_CANopen; + } + if (gr_co != 0U) { + gr |= CO_LED_CANopen; + } + LEDs->LEDred = rd; + LEDs->LEDgreen = gr; + } /* if (tick) */ + +#if ((CO_CONFIG_LEDS) & CO_CONFIG_FLAG_TIMERNEXT) != 0 + if (timerNext_us != NULL) { + uint32_t diff = 50000 - LEDs->LEDtmr50ms; + if (*timerNext_us > diff) { + *timerNext_us = diff; + } + } +#endif } + +#endif /* (CO_CONFIG_LEDS) & CO_CONFIG_LEDS_ENABLE */ diff --git a/303/CO_LEDs.h b/303/CO_LEDs.h new file mode 100644 index 00000000..a2609ce6 --- /dev/null +++ b/303/CO_LEDs.h @@ -0,0 +1,197 @@ +/** + * CANopen Indicator specification (CiA 303-3 v1.4.0) + * + * @file CO_LEDs.h + * @ingroup CO_LEDs + * @author Janez Paternoster + * @copyright 2020 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#ifndef CO_LEDS_H +#define CO_LEDS_H + +#include "301/CO_driver.h" +#include "301/CO_NMT_Heartbeat.h" + +/* default configuration, see CO_config.h */ +#ifndef CO_CONFIG_LEDS +#define CO_CONFIG_LEDS (CO_CONFIG_LEDS_ENABLE | CO_CONFIG_GLOBAL_FLAG_TIMERNEXT) +#endif + +#if (((CO_CONFIG_LEDS) & CO_CONFIG_LEDS_ENABLE) != 0) || defined CO_DOXYGEN + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup CO_LEDs LED indicators + * Specified in standard CiA 303-3. + * + * @ingroup CO_CANopen_303 + * @{ + * CIA 303-3 standard specifies indicator LED diodes, which reflects state of the CANopen device. Green and red leds or + * bi-color led can be used. + * + * CANopen green led - run led: + * - flickering: LSS configuration state is active + * - blinking: device is in NMT pre-operational state + * - single flash: device is in NMT stopped state + * - triple flash: a software download is running in the device + * - on: device is in NMT operational state + * + * CANopen red led - error led: + * - off: no error + * - flickering: LSS node id is not configured, CANopen is not initialized + * - blinking: invalid configuration, general error + * - single flash: CAN warning limit reached + * - double flash: heartbeat consumer - error in remote monitored node + * - triple flash: sync message reception timeout + * - quadruple flash: PDO has not been received before the event timer elapsed + * - on: CAN bus off + * + * To apply on/off state to the led diode, use #CO_LED_RED or #CO_LED_GREEN macros with one of the @ref CO_LED_bitmasks. + * For CANopen leds use #CO_LED_CANopen bitmask. + */ + +/** + * @defgroup CO_LED_bitmasks CO_LED bitmasks + * @{ + * Bitmasks for the LED indicators + */ +#define CO_LED_flicker 0x01U /**< LED flickering 10Hz */ +#define CO_LED_blink 0x02U /**< LED blinking 2,5Hz */ +#define CO_LED_flash_1 0x04U /**< LED single flash */ +#define CO_LED_flash_2 0x08U /**< LED double flash */ +#define CO_LED_flash_3 0x10U /**< LED triple flash */ +#define CO_LED_flash_4 0x20U /**< LED quadruple flash */ +#define CO_LED_CANopen 0x80U /**< LED CANopen according to CiA 303-3 */ +/** @} */ + +/** Get on/off state for red led for one of the @ref CO_LED_bitmasks */ +#define CO_LED_RED(LEDs, BITMASK) ((((LEDs)->LEDred & BITMASK) != 0U) ? 1U : 0U) +/** Get on/off state for green led for one of the @ref CO_LED_bitmasks */ +#define CO_LED_GREEN(LEDs, BITMASK) ((((LEDs)->LEDgreen & BITMASK) != 0U) ? 1U : 0U) + +/* Forward declare the tagged struct and typedef it, so we can use the name + * inside member declarations and in callback typedefs without incomplete-type warnings. + */ +typedef struct CO_LEDs_t CO_LEDs_t; + +#if (CO_CONFIG_LEDS_CALLBACK) != 0 +/** + * Callback invoked after LED state is updated. + * + * The callback is called from within CO_LEDs_process() after the LED state + * machine computes and stores new values into @ref CO_LEDs_t::LEDred and + * @ref CO_LEDs_t::LEDgreen. The callback must be lightweight and non-blocking. + * + * @param leds Pointer to the @ref CO_LEDs_t instance that triggered the callback. + * @param user_arg Opaque user pointer supplied at registration time. + */ +typedef void (*CO_LEDs_cb_t)(CO_LEDs_t* leds, void* user_arg); +#endif /* CO_CONFIG_LEDS_CALLBACK */ + +/** + * LEDs object, initialized by CO_LEDs_init() + */ +struct CO_LEDs_t { + uint32_t LEDtmr50ms; /**< 50ms led timer */ + uint8_t LEDtmr200ms; /**< 200ms led timer */ + uint8_t LEDtmrflash_1; /**< single flash led timer */ + uint8_t LEDtmrflash_2; /**< double flash led timer */ + uint8_t LEDtmrflash_3; /**< triple flash led timer */ + uint8_t LEDtmrflash_4; /**< quadruple flash led timer */ + uint8_t LEDred; /**< red led bitfield, to be combined with @ref CO_LED_bitmasks */ + uint8_t LEDgreen; /**< green led bitfield, to be combined with @ref CO_LED_bitmasks */ +#if (CO_CONFIG_LEDS_CALLBACK) != 0 + CO_LEDs_cb_t cb; /**< Callback invoked after LED fields update */ + void* cb_user; /**< Opaque user pointer passed to @ref cb */ +#endif +}; + +/** + * Initialize LEDs object. + * + * Function must be called in the communication reset section. + * + * @param LEDs This object will be initialized. + * + * @return #CO_ReturnError_t CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT. + */ +CO_ReturnError_t CO_LEDs_init(CO_LEDs_t* LEDs); + +#if (CO_CONFIG_LEDS_CALLBACK) != 0 +/** + * Register (or replace) LED state callback. + * + * Registers a callback that is invoked after LED states are updated in + * CO_LEDs_process(). Function may be called any time after CO_LEDs_init(). + * Passing @p cb as NULL clears the callback (same effect as + * CO_LEDs_unregisterCallback()). + * + * Typical usage is to mirror the computed CANopen LED indication to hardware + * (e.g. GPIO pins or OS-level LED drivers). + * + * @param LEDs LEDs object to configure. + * @param cb Callback function pointer (NULL to clear). + * @param user_arg Opaque pointer passed back to @p cb on each invocation. + */ +void CO_LEDs_registerCallback(CO_LEDs_t* LEDs, CO_LEDs_cb_t cb, void* user_arg); + +/** + * Unregister LED state callback (header-only helper). + * + * @param LEDs LEDs object to modify. + */ +static inline void +CO_LEDs_unregisterCallback(CO_LEDs_t* LEDs) { + if (LEDs != NULL) { + LEDs->cb = NULL; + LEDs->cb_user = NULL; + } +} +#endif /* CO_CONFIG_LEDS_CALLBACK */ + +/** + * Process indicator states + * + * Function must be called cyclically. + * + * @param LEDs This object. + * @param timeDifference_us Time difference from previous function call in [microseconds]. + * @param NMTstate NMT operating state. + * @param LSSconfig Node is in LSS configuration state indication. + * @param ErrCANbusOff CAN bus off indication (highest priority). + * @param ErrCANbusWarn CAN error warning limit reached indication. + * @param ErrRpdo RPDO event timer timeout indication. + * @param ErrSync Sync receive timeout indication. + * @param ErrHbCons Heartbeat consumer error (remote node) indication. + * @param ErrOther Other error indication (lowest priority). + * @param firmwareDownload Firmware download is in progress indication. + * @param [out] timerNext_us info to OS - see CO_process(). + */ +void CO_LEDs_process(CO_LEDs_t* LEDs, uint32_t timeDifference_us, CO_NMT_internalState_t NMTstate, bool_t LSSconfig, + bool_t ErrCANbusOff, bool_t ErrCANbusWarn, bool_t ErrRpdo, bool_t ErrSync, bool_t ErrHbCons, + bool_t ErrOther, bool_t firmwareDownload, uint32_t* timerNext_us); + +/** @} */ /* CO_LEDs */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* (CO_CONFIG_LEDS) & CO_CONFIG_LEDS_ENABLE */ + +#endif /* CO_LEDS_H */ diff --git a/304/CO_GFC.c b/304/CO_GFC.c new file mode 100644 index 00000000..86743e30 --- /dev/null +++ b/304/CO_GFC.c @@ -0,0 +1,133 @@ +/* + * CANopen Global fail-safe command protocol. + * + * @file CO_GFC.c + * @ingroup CO_GFC + * @author Robert Grüning + * @copyright 2020 Robert Grüning + * @copyright 2024 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#include "304/CO_GFC.h" + +#if ((CO_CONFIG_GFC)&CO_CONFIG_GFC_ENABLE) != 0 + +/* + * Custom function for reading or writing OD object. + * + * For more information see file CO_ODinterface.h, OD_IO_t. + */ +static ODR_t +OD_write_1300(OD_stream_t* stream, const void* buf, OD_size_t count, OD_size_t* countWritten) { + if ((stream == NULL) || (stream->subIndex != 0U) || (buf == NULL) || (countWritten == NULL)) { + return ODR_DEV_INCOMPAT; + } + + CO_GFC_t* GFC = stream->object; + + uint8_t value = CO_getUint8(buf); + if (value > 1U) { + return ODR_INVALID_VALUE; + } + + GFC->valid = (value == 1U); + + /* write value to the original location in the Object Dictionary */ + return OD_writeOriginal(stream, buf, count, countWritten); +} + +#if ((CO_CONFIG_GFC)&CO_CONFIG_GFC_CONSUMER) != 0 +static void +CO_GFC_receive(void* object, void* msg) { + CO_GFC_t* GFC; + uint8_t DLC = CO_CANrxMsg_readDLC(msg); + + GFC = (CO_GFC_t*)object; /* this is the correct pointer type of the first argument */ + + if (GFC->valid && (DLC == 0U)) { + + /* Callback signals Global Failsafe Command */ + if (GFC->pFunctSignalSafe != NULL) { + GFC->pFunctSignalSafe(GFC->functSignalObjectSafe); + } + } +} + +void +CO_GFC_initCallbackEnterSafeState(CO_GFC_t* GFC, void* object, void (*pFunctSignalSafe)(void* object)) { + if (GFC != NULL) { + GFC->functSignalObjectSafe = object; + GFC->pFunctSignalSafe = pFunctSignalSafe; + } +} +#endif + +CO_ReturnError_t +CO_GFC_init(CO_GFC_t* GFC, OD_entry_t* OD_1300_gfcParameter, CO_CANmodule_t* GFC_CANdevRx, uint16_t GFC_rxIdx, + uint16_t CANidRxGFC, CO_CANmodule_t* GFC_CANdevTx, uint16_t GFC_txIdx, uint16_t CANidTxGFC) { + if ((GFC == NULL) || (OD_1300_gfcParameter == NULL) || (GFC_CANdevRx == NULL) || (GFC_CANdevTx == NULL)) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + uint8_t valid = 0; + if (OD_get_u8(OD_1300_gfcParameter, 0, &valid, true) != ODR_OK) { + return CO_ERROR_OD_PARAMETERS; + } + GFC->valid = (valid == 1U); + + /* Configure Object dictionary entry at index 0x1300+ */ + GFC->OD_gfcParam_ext.object = GFC; + GFC->OD_gfcParam_ext.read = OD_readOriginal; + GFC->OD_gfcParam_ext.write = OD_write_1300; + (void)OD_extension_init(OD_1300_gfcParameter, &GFC->OD_gfcParam_ext); + +#if ((CO_CONFIG_GFC)&CO_CONFIG_GFC_PRODUCER) != 0 + GFC->CANdevTx = GFC_CANdevTx; + GFC->CANtxBuff = CO_CANtxBufferInit(GFC->CANdevTx, GFC_txIdx, CANidTxGFC, false, 0, false); + + if (GFC->CANtxBuff == NULL) { + return CO_ERROR_TX_UNCONFIGURED; + } +#else + (void)GFC_txIdx; /* unused */ + (void)CANidTxGFC; /* unused */ +#endif + +#if ((CO_CONFIG_GFC)&CO_CONFIG_GFC_CONSUMER) != 0 + GFC->functSignalObjectSafe = NULL; + GFC->pFunctSignalSafe = NULL; + const CO_ReturnError_t r = CO_CANrxBufferInit(GFC_CANdevRx, GFC_rxIdx, CANidRxGFC, 0x7FF, false, (void*)GFC, + CO_GFC_receive); + if (r != CO_ERROR_NO) { + return r; + } +#else + (void)GFC_rxIdx; /* unused */ + (void)CANidRxGFC; /* unused */ +#endif + + return CO_ERROR_NO; +} + +#if ((CO_CONFIG_GFC)&CO_CONFIG_GFC_PRODUCER) != 0 +CO_ReturnError_t +CO_GFCsend(CO_GFC_t* GFC) { + if (GFC->valid) { + return CO_CANsend(GFC->CANdevTx, GFC->CANtxBuff); + } + return CO_ERROR_NO; +} +#endif + +#endif /* (CO_CONFIG_GFC) & CO_CONFIG_GFC_ENABLE */ diff --git a/304/CO_GFC.h b/304/CO_GFC.h new file mode 100644 index 00000000..a7a58168 --- /dev/null +++ b/304/CO_GFC.h @@ -0,0 +1,122 @@ +/** + * CANopen Global fail-safe command protocol. + * + * @file CO_GFC.h + * @ingroup CO_GFC + * @author Robert Grüning + * @copyright 2020 Robert Grüning + * @copyright 2024 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#ifndef CO_GFC_H +#define CO_GFC_H + +#include "301/CO_driver.h" +#include "301/CO_ODinterface.h" + +/* default configuration, see CO_config.h */ +#ifndef CO_CONFIG_GFC +#define CO_CONFIG_GFC (0) +#endif + +#if (((CO_CONFIG_GFC)&CO_CONFIG_GFC_ENABLE) != 0) || defined CO_DOXYGEN + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup CO_GFC GFC + * Global fail-safe command protocol. + * + * @ingroup CO_CANopen_304 + * @{ + * Very simple consumer/producer protocol. A net can have multiple GFC producer and multiple GFC consumer. On a + * safety-relevant the producer can send a GFC message (ID 1, DLC 0). The consumer can use this message to start the + * transition to a safe state. The GFC is optional for the security protocol and is not monitored (timed). + */ + +/** + * GFC object. + */ +typedef struct { + bool_t valid; /**< From OD parameter 1300 */ + OD_extension_t OD_gfcParam_ext; /**< Extension for OD object */ +#if (((CO_CONFIG_GFC)&CO_CONFIG_GFC_PRODUCER) != 0) || defined CO_DOXYGEN + CO_CANmodule_t* CANdevTx; /**< From CO_GFC_init() */ + CO_CANtx_t* CANtxBuff; /**< CAN transmit buffer inside CANdevTx */ +#endif +#if (((CO_CONFIG_GFC)&CO_CONFIG_GFC_CONSUMER) != 0) || defined CO_DOXYGEN + void (*pFunctSignalSafe)(void* object); /**< From CO_GFC_initCallbackEnterSafeState() or NULL */ + void* functSignalObjectSafe; /**< From CO_GFC_initCallbackEnterSafeState() or NULL */ +#endif +} CO_GFC_t; + +/** + * Initialize GFC object. + * + * Function must be called in the communication reset section. + * + * @param GFC This object will be initialized. + * @param OD_1300_gfcParameter Pointer to _Global fail-safe command parameter_ variable from Object dictionary (index + * 0x1300). + * @param GFC_CANdevRx CAN device used for SRDO reception. + * @param GFC_rxIdx Index of receive buffer in the above CAN device. + * @param CANidRxGFC GFC CAN ID for reception + * @param GFC_CANdevTx AN device used for SRDO transmission. + * @param GFC_txIdx Index of transmit buffer in the above CAN device. + * @param CANidTxGFC GFC CAN ID for transmission + * + * @return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT. + */ +CO_ReturnError_t CO_GFC_init(CO_GFC_t* GFC, OD_entry_t* OD_1300_gfcParameter, CO_CANmodule_t* GFC_CANdevRx, + uint16_t GFC_rxIdx, uint16_t CANidRxGFC, CO_CANmodule_t* GFC_CANdevTx, uint16_t GFC_txIdx, + uint16_t CANidTxGFC); + +#if (((CO_CONFIG_GFC)&CO_CONFIG_GFC_CONSUMER) != 0) || defined CO_DOXYGEN +/** + * Initialize GFC callback function. + * + * Function initializes optional callback function, that is called when GFC is received. Callback is called from receive + * function (interrupt). + * + * @param GFC This object. + * @param object Pointer to object, which will be passed to pFunctSignalSafe(). Can be NULL + * @param pFunctSignalSafe Pointer to the callback function. Not called if NULL. + */ +void CO_GFC_initCallbackEnterSafeState(CO_GFC_t* GFC, void* object, void (*pFunctSignalSafe)(void* object)); +#endif + +#if (((CO_CONFIG_GFC)&CO_CONFIG_GFC_PRODUCER) != 0) || defined CO_DOXYGEN +/** + * Send GFC message. + * + * It should be called by application, for example after a safety-relevant change. + * + * @param GFC GFC object. + * + * @return Same as CO_CANsend(). + */ +CO_ReturnError_t CO_GFCsend(CO_GFC_t* GFC); +#endif + +/** @} */ /* CO_GFC */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* (CO_CONFIG_GFC) & CO_CONFIG_GFC_ENABLE */ + +#endif /* CO_GFC_H */ diff --git a/304/CO_SRDO.c b/304/CO_SRDO.c new file mode 100644 index 00000000..d1cd0fa6 --- /dev/null +++ b/304/CO_SRDO.c @@ -0,0 +1,1010 @@ +/* + * CANopen Safety Related Data Object protocol. + * + * @file CO_SRDO.c + * @ingroup CO_SRDO + * @author Robert Grüning + * @copyright 2020 Robert Grüning + * @copyright 2024 temi54c1l8(at)github + * @copyright 2024 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#include "304/CO_SRDO.h" + +#if ((CO_CONFIG_SRDO)&CO_CONFIG_SRDO_ENABLE) != 0 + +#include "301/CO_crc16-ccitt.h" + +/* verify configuration */ +#if ((CO_CONFIG_CRC16)&CO_CONFIG_CRC16_ENABLE) == 0 +#error CO_CONFIG_CRC16_ENABLE must be enabled. +#endif + +#ifdef CO_CONFORMANCE_TEST_TOOL_ADAPTATION +#warning CO_CONFORMANCE_TEST_TOOL_ADAPTATION may be used only for conformance testing (because of CTT limitations) +#endif + +/* values for informationDirection and configurationValid */ +#define CO_SRDO_INVALID (0U) +#define CO_SRDO_TX (1U) +#define CO_SRDO_RX (2U) +#define CO_SRDO_VALID_MAGIC (0xA5U) + +/* macro for information about SRDO configuration error */ +#define ERR_INFO(index, subindex, info) (((uint32_t)(index) << 16) | ((uint32_t)(subindex) << 8) | ((uint32_t)(info))) + +static void +CO_SRDO_receive_normal(void* object, void* msg) { + CO_SRDO_t* SRDO = (CO_SRDO_t*)object; + uint8_t DLC = CO_CANrxMsg_readDLC(msg); + const uint8_t* data = CO_CANrxMsg_readData(msg); + + if ((SRDO->informationDirection == CO_SRDO_RX) && (DLC >= SRDO->dataLength) && !CO_FLAG_READ(SRDO->CANrxNew[1])) { + /* copy data into appropriate buffer and set 'new message' flag */ + (void)memcpy(SRDO->CANrxData[0], data, sizeof(SRDO->CANrxData[0])); + CO_FLAG_SET(SRDO->CANrxNew[0]); + +#if ((CO_CONFIG_SRDO)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0 + /* Optional signal to RTOS, which can resume task, which handles SRDO. */ + if (SRDO->pFunctSignalPre != NULL) { + SRDO->pFunctSignalPre(SRDO->functSignalObjectPre); + } +#endif + } else if (DLC < SRDO->dataLength) { + SRDO->rxSrdoShort = true; + } else { /* MISRA C 2004 14.10 */ + } +} + +static void +CO_SRDO_receive_inverted(void* object, void* msg) { + CO_SRDO_t* SRDO = (CO_SRDO_t*)object; + uint8_t DLC = CO_CANrxMsg_readDLC(msg); + const uint8_t* data = CO_CANrxMsg_readData(msg); + + if ((SRDO->informationDirection == CO_SRDO_RX) && (DLC >= SRDO->dataLength) && CO_FLAG_READ(SRDO->CANrxNew[0])) { + /* copy data into appropriate buffer and set 'new message' flag */ + (void)memcpy(SRDO->CANrxData[1], data, sizeof(SRDO->CANrxData[1])); + CO_FLAG_SET(SRDO->CANrxNew[1]); + +#if ((CO_CONFIG_SRDO)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0 + /* Optional signal to RTOS, which can resume task, which handles SRDO. */ + if (SRDO->pFunctSignalPre != NULL) { + SRDO->pFunctSignalPre(SRDO->functSignalObjectPre); + } +#endif + } else if (DLC < SRDO->dataLength) { + SRDO->rxSrdoShort = true; + } else { /* MISRA C 2004 14.10 */ + } +} + +/* Set OD object 13FE:00 to CO_SRDO_INVALID and clear configurationValid flag. */ +static void +configurationValidUnset(CO_SRDOGuard_t* SRDOGuard) { + if (SRDOGuard != NULL) { + OD_IO_t* OD_IO = &SRDOGuard->OD_IO_configurationValid; + uint8_t val = CO_SRDO_INVALID; + OD_size_t dummy; + + SRDOGuard->configurationValid = false; + + OD_IO->write(&OD_IO->stream, &val, sizeof(val), &dummy); + } +} + +/* + * Custom functions for reading or writing OD object. + * + * For more information see file CO_ODinterface.h, OD_IO_t. + */ +static ODR_t +OD_write_dummy(OD_stream_t* stream, const void* buf, OD_size_t count, OD_size_t* countWritten) { + (void)stream; + (void)buf; + if (countWritten != NULL) { + *countWritten = count; + } + return ODR_OK; +} + +static ODR_t +OD_read_dummy(OD_stream_t* stream, void* buf, OD_size_t count, OD_size_t* countRead) { + if ((buf == NULL) || (stream == NULL) || (countRead == NULL)) { + return ODR_DEV_INCOMPAT; + } + + if (count > stream->dataLength) { + count = stream->dataLength; + } + + (void)memset(buf, 0, count); + + *countRead = count; + return ODR_OK; +} + +#ifdef CO_CONFORMANCE_TEST_TOOL_ADAPTATION +static bool_t +OD_not_write_same_value(OD_stream_t* stream, const void* buf, OD_size_t count) { + /* The conformance test tool does not recognize CANopen Safety and on all object + * dictionaty tries to read and write the same value */ + OD_size_t countRead = 0; + uint8_t bufRead[6] = {0}; + if (count > 6U) { + return false; + } + ODR_t returnCode = OD_readOriginal(stream, bufRead, count, &countRead); + if (returnCode != ODR_OK) { + return false; + } + if (memcmp((const void*)(buf), (const void*)(bufRead), count) == 0) { + return true; + } + return false; +} +#endif + +static ODR_t +OD_read_SRDO_communicationParam(OD_stream_t* stream, void* buf, OD_size_t count, OD_size_t* countRead) { + ODR_t returnCode = OD_readOriginal(stream, buf, count, countRead); + + /* When reading COB_ID, add Node-Id to the read value, if necessary */ + if ((returnCode == ODR_OK) && ((stream->subIndex == 5U) || (stream->subIndex == 6U)) && (*countRead == 4U)) { + CO_SRDO_t* SRDO = stream->object; + + uint32_t value = CO_getUint32(buf); + uint16_t defaultCOB_ID = SRDO->defaultCOB_ID + ((uint16_t)(stream->subIndex) - 5U); + + /* If default COB ID is used, then OD entry does not contain $NodeId. Add it here. */ + if ((value == defaultCOB_ID) && (SRDO->nodeId <= 64U)) { + value += (uint32_t)SRDO->nodeId * 2U; + } + + (void)CO_setUint32(buf, value); + } + + return returnCode; +} + +static ODR_t +OD_write_SRDO_communicationParam(OD_stream_t* stream, const void* buf, OD_size_t count, OD_size_t* countWritten) { + if ((stream == NULL) || (buf == NULL) || (countWritten == NULL) || (count > 4U)) { + return ODR_DEV_INCOMPAT; + } + +#ifdef CO_CONFORMANCE_TEST_TOOL_ADAPTATION + if (OD_not_write_same_value(stream, buf, count)) { + return ODR_OK; + } +#endif + + CO_SRDO_t* SRDO = stream->object; + CO_SRDOGuard_t* SRDOGuard = SRDO->SRDOGuard; + uint8_t bufCopy[4]; + (void)memcpy((void*)(bufCopy), (const void*)(buf), count); + + /* Writing Object Dictionary variable */ + if (SRDOGuard->NMTisOperational) { + /* Data cannot be transferred or stored to the application because of the present device state. */ + return ODR_DATA_DEV_STATE; + } + + if (stream->subIndex == 1U) { /* Information direction */ + uint8_t value = CO_getUint8(buf); + if (value > 2U) { + return ODR_INVALID_VALUE; + } + SRDO->informationDirection = value; + } else if (stream->subIndex == 2U) { /* SCT */ + uint16_t value = CO_getUint16(buf); + if (value < ((CO_CONFIG_SRDO_MINIMUM_DELAY / 1000U) + 1U)) { + return ODR_INVALID_VALUE; + } + } else if (stream->subIndex == 3U) { /* SRVT */ + uint8_t value = CO_getUint8(buf); + if (value == 0U) { + return ODR_INVALID_VALUE; + } + } else if (stream->subIndex == 4U) { /* Transmission_type */ + uint8_t value = CO_getUint8(buf); + if (value != 254U) { + return ODR_INVALID_VALUE; + } + } else if ((stream->subIndex == 5U) || (stream->subIndex == 6U)) { /* COB_ID */ + uint32_t value = CO_getUint32(buf); + uint16_t index = (uint16_t)(stream->subIndex) - 5U; + uint16_t defaultCOB_ID = SRDO->defaultCOB_ID + index; + + /* check value range, the spec does not specify if COB-ID flags are allowed */ + if ((value < 0x101U) || (value > 0x180U) || ((value & 1U) == index)) { + return ODR_INVALID_VALUE; /* Invalid value for parameter (download only). */ + } + + /* if default COB-ID is being written, write defaultCOB_ID without nodeId */ + if ((SRDO->nodeId <= 64U) && (value == (defaultCOB_ID + ((uint32_t)SRDO->nodeId * 2U)))) { + value = defaultCOB_ID; + (void)CO_setUint32(bufCopy, value); + } + } else { /* MISRA C 2004 14.10 */ + } + + /* set OD object 13FE:00 to CO_SRDO_INVALID */ + configurationValidUnset(SRDOGuard); + + /* write value to the original location in the Object Dictionary */ + return OD_writeOriginal(stream, bufCopy, count, countWritten); +} + +static ODR_t +OD_write_SRDO_mappingParam(OD_stream_t* stream, const void* buf, OD_size_t count, OD_size_t* countWritten) { + if ((stream == NULL) || (buf == NULL) || (countWritten == NULL) + || (stream->subIndex > CO_SRDO_MAX_MAPPED_ENTRIES)) { + return ODR_DEV_INCOMPAT; + } + +#ifdef CO_CONFORMANCE_TEST_TOOL_ADAPTATION + if (OD_not_write_same_value(stream, buf, count)) { + return ODR_OK; + } +#endif + + CO_SRDO_t* SRDO = stream->object; + CO_SRDOGuard_t* SRDOGuard = SRDO->SRDOGuard; + + /* Writing Object Dictionary variable */ + if (SRDOGuard->NMTisOperational) { + /* Data cannot be transferred or stored to the application because of the present device state. */ + return ODR_DATA_DEV_STATE; + } + + /* SRDO must be disabled */ + if (SRDO->informationDirection != 0U) { + return ODR_UNSUPP_ACCESS; /* Unsupported access to an object. */ + } + + /* numberOfMappedObjects */ + if (stream->subIndex == 0U) { + uint8_t value = CO_getUint8(buf); + /* only odd numbers are allowed */ + if ((value > CO_SRDO_MAX_MAPPED_ENTRIES) || ((value & 1U) != 0U)) { + return ODR_MAP_LEN; /* Number and length of object to be mapped exceeds SRDO length. */ + } + SRDO->mappedObjectsCount = value; + } + /* mapping objects */ + else { + if (SRDO->mappedObjectsCount != 0U) { + return ODR_UNSUPP_ACCESS; + } + /* No other checking is implemented here. Values are validated in the configuration function. */ + } + + /* set OD object 13FE:00 to CO_SRDO_INVALID */ + configurationValidUnset(SRDOGuard); + + /* write value to the original location in the Object Dictionary */ + return OD_writeOriginal(stream, buf, count, countWritten); +} + +static ODR_t +OD_write_13FE(OD_stream_t* stream, const void* buf, OD_size_t count, OD_size_t* countWritten) { + if ((stream == NULL) || (stream->subIndex != 0U) || (buf == NULL) || (countWritten == NULL)) { + return ODR_DEV_INCOMPAT; + } + + CO_SRDOGuard_t* SRDOGuard = stream->object; + + if (SRDOGuard->NMTisOperational) { + /* Data cannot be transferred or stored to the application because of the present device state. */ + return ODR_DATA_DEV_STATE; + } + +#ifdef CO_CONFORMANCE_TEST_TOOL_ADAPTATION + if (OD_not_write_same_value(stream, buf, count)) { + return ODR_OK; + } +#endif + + uint8_t configurationValid = CO_getUint8(buf); + if (configurationValid == CO_SRDO_VALID_MAGIC) { + SRDOGuard->configurationValid = true; + } else { + SRDOGuard->configurationValid = false; + } + + /* write value to the original location in the Object Dictionary */ + return OD_writeOriginal(stream, buf, count, countWritten); +} + +static ODR_t +OD_write_13FF(OD_stream_t* stream, const void* buf, OD_size_t count, OD_size_t* countWritten) { + if ((stream == NULL) || (stream->subIndex == 0U) || (buf == NULL) || (countWritten == NULL)) { + return ODR_DEV_INCOMPAT; + } + + CO_SRDOGuard_t* SRDOGuard = stream->object; + + if (SRDOGuard->NMTisOperational) { + /* Data cannot be transferred or stored to the application because of the present device state. */ + return ODR_DATA_DEV_STATE; + } + +#ifdef CO_CONFORMANCE_TEST_TOOL_ADAPTATION + if (OD_not_write_same_value(stream, buf, count)) { + return ODR_OK; + } +#endif + + /* set OD object 13FE:00 to CO_SRDO_INVALID */ + configurationValidUnset(SRDOGuard); + + /* write value to the original location in the Object Dictionary */ + return OD_writeOriginal(stream, buf, count, countWritten); +} + +#if ((CO_CONFIG_SRDO)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0 +void +CO_SRDO_initCallbackPre(CO_SRDO_t* SRDO, void* object, void (*pFunctSignalPre)(void* object)) { + if (SRDO != NULL) { + SRDO->functSignalObjectPre = object; + SRDO->pFunctSignalPre = pFunctSignalPre; + } +} +#endif + +CO_ReturnError_t +CO_SRDOGuard_init(CO_SRDOGuard_t* SRDOGuard, OD_entry_t* OD_13FE_configurationValid, + OD_entry_t* OD_13FF_safetyConfigurationSignature, uint32_t* errInfo) { + ODR_t odRet; + uint8_t configurationValid; + + /* verify arguments */ + if ((SRDOGuard == NULL) || (OD_13FE_configurationValid == NULL) || (OD_13FF_safetyConfigurationSignature == NULL)) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + /* clear object */ + (void)memset(SRDOGuard, 0, sizeof(CO_SRDOGuard_t)); + + SRDOGuard->OD_13FE_entry = OD_13FE_configurationValid; + SRDOGuard->OD_13FF_entry = OD_13FF_safetyConfigurationSignature; + + /* Configure Object dictionary extensions */ + SRDOGuard->OD_13FE_extension.object = SRDOGuard; + SRDOGuard->OD_13FE_extension.read = OD_readOriginal; + SRDOGuard->OD_13FE_extension.write = OD_write_13FE; + (void)OD_extension_init(OD_13FE_configurationValid, &SRDOGuard->OD_13FE_extension); + + SRDOGuard->OD_13FF_extension.object = SRDOGuard; + SRDOGuard->OD_13FF_extension.read = OD_readOriginal; + SRDOGuard->OD_13FF_extension.write = OD_write_13FF; + (void)OD_extension_init(OD_13FF_safetyConfigurationSignature, &SRDOGuard->OD_13FF_extension); + + /* Configure SRDOGuard->OD_IO_configurationValid variable. It will be used for writing 0 to OD variable 13FE,00 */ + odRet = OD_getSub(OD_13FE_configurationValid, 0, &SRDOGuard->OD_IO_configurationValid, false); + if ((odRet != ODR_OK) || (SRDOGuard->OD_IO_configurationValid.stream.dataLength != 1U)) { + if (errInfo != NULL) { + *errInfo = (((uint32_t)OD_getIndex(OD_13FE_configurationValid)) << 8) | 1U; + } + return CO_ERROR_OD_PARAMETERS; + } + + if (OD_get_u8(OD_13FE_configurationValid, 0, &configurationValid, true) != ODR_OK) { + *errInfo = (((uint32_t)OD_getIndex(OD_13FE_configurationValid)) << 8) | 1U; + return CO_ERROR_OD_PARAMETERS; + } + if (configurationValid == CO_SRDO_VALID_MAGIC) { + SRDOGuard->configurationValid = true; + } else { + SRDOGuard->configurationValid = false; + } + + return CO_ERROR_NO; +} + +CO_ReturnError_t +CO_SRDO_config(CO_SRDO_t* SRDO, uint8_t SRDO_Index, CO_SRDOGuard_t* SRDOGuard, uint32_t* errInfo) { + CO_ReturnError_t ret = CO_ERROR_NO; + uint32_t err = 0; + bool_t configurationInProgress = false; + + /* variables will be retrieved from Object Dictionary */ + uint8_t cp_highestSubindexSupported = 0; + uint8_t informationDirection = 0; + uint16_t safetyCycleTime = 0; + uint8_t safetyRelatedValidationTime = 0; + uint8_t transmissionType = 0; + uint32_t COB_ID1_normal = 0; + uint32_t COB_ID2_inverted = 0; + uint8_t configurationValid = 0; + uint16_t crcSignatureFromOD = 0; + uint8_t mappedObjectsCount = 0; + uint32_t mapping[CO_SRDO_MAX_MAPPED_ENTRIES]; + + /* Get variables from object Dictionary and verify it's structure. */ + if (err == 0U) { + if (OD_get_u8(SRDOGuard->OD_13FE_entry, 0, &configurationValid, true) != ODR_OK) { + err = ERR_INFO(0x13FEUL, 0, 1); + } else if (OD_get_u16(SRDOGuard->OD_13FF_entry, SRDO_Index + 1U, &crcSignatureFromOD, true) != ODR_OK) { + err = ERR_INFO(0x13FFUL, SRDO_Index + 1UL, 1); + } else if (OD_get_u8(SRDO->OD_communicationParam_entry, 0, &cp_highestSubindexSupported, true) != ODR_OK) { + err = ERR_INFO(0x1301UL + SRDO_Index, 0, 1); + } else if (OD_get_u8(SRDO->OD_communicationParam_entry, 1, &informationDirection, true) != ODR_OK) { + err = ERR_INFO(0x1301UL + SRDO_Index, 1, 1); + } else if (OD_get_u16(SRDO->OD_communicationParam_entry, 2, &safetyCycleTime, true) != ODR_OK) { + err = ERR_INFO(0x1301UL + SRDO_Index, 2, 1); + } else if (OD_get_u8(SRDO->OD_communicationParam_entry, 3, &safetyRelatedValidationTime, true) != ODR_OK) { + err = ERR_INFO(0x1301UL + SRDO_Index, 3, 1); + } else if (OD_get_u8(SRDO->OD_communicationParam_entry, 4, &transmissionType, true) != ODR_OK) { + err = ERR_INFO(0x1301UL + SRDO_Index, 4, 1); + } else if (OD_get_u32(SRDO->OD_communicationParam_entry, 5, &COB_ID1_normal, true) != ODR_OK) { + err = ERR_INFO(0x1301UL + SRDO_Index, 5, 1); + } else if (OD_get_u32(SRDO->OD_communicationParam_entry, 6, &COB_ID2_inverted, true) != ODR_OK) { + err = ERR_INFO(0x1301UL + SRDO_Index, 6, 1); + } else if (OD_get_u8(SRDO->OD_mappingParam_entry, 0, &mappedObjectsCount, true) != ODR_OK) { + err = ERR_INFO(0x1381UL + SRDO_Index, 0, 1); + } else { + for (uint8_t i = 0; i < mappedObjectsCount; i++) { + if (OD_get_u32(SRDO->OD_mappingParam_entry, i + 1U, &mapping[i], true) != ODR_OK) { + err = ERR_INFO(0x1381UL + SRDO_Index, i + 1UL, 1); + break; + } + } + } + + /* if OD contains default COB_IDs, add node-id */ + if ((COB_ID1_normal == SRDO->defaultCOB_ID) && (COB_ID2_inverted == ((uint32_t)SRDO->defaultCOB_ID + 1UL)) + && (SRDO->nodeId <= 64U)) { + uint32_t add = (uint32_t)SRDO->nodeId * 2U; + COB_ID1_normal += add; + COB_ID2_inverted += add; + } + + /* If this fails, something is wrong with the Object Dictionary. Device have to be reprogrammed. */ + if (err != 0U) { + ret = CO_ERROR_OD_PARAMETERS; + } + } + + /* If configurationValid is set and SRDO is valid, continue with further configuration */ + if ((err == 0U) && (configurationValid == CO_SRDO_VALID_MAGIC) && (informationDirection != CO_SRDO_INVALID)) { + configurationInProgress = true; + } + + /* Verify parameters from OD */ + if ((err == 0U) && configurationInProgress) { + if (cp_highestSubindexSupported != 6U) { + err = ERR_INFO(0x1301UL + SRDO_Index, 0, 2); + } else if (informationDirection > 3U) { + err = ERR_INFO(0x1301UL + SRDO_Index, 1, 2); + } else if (safetyCycleTime < ((CO_CONFIG_SRDO_MINIMUM_DELAY / 1000U) + 1U)) { + err = ERR_INFO(0x1301UL + SRDO_Index, 2, 2); + } else if (safetyRelatedValidationTime < 1U) { + err = ERR_INFO(0x1301UL + SRDO_Index, 3, 2); + } else if (transmissionType != 254U) { + err = ERR_INFO(0x1301UL + SRDO_Index, 4, 2); + } else if ((COB_ID1_normal < 0x101U) || ((COB_ID1_normal & 1U) == 0U)) { + err = ERR_INFO(0x1301UL + SRDO_Index, 5, 2); + } else if (((COB_ID1_normal + 1U) != COB_ID2_inverted) || (COB_ID2_inverted > 0x180U)) { + err = ERR_INFO(0x1301UL + SRDO_Index, 6, 2); + } else if ((mappedObjectsCount > CO_SRDO_MAX_MAPPED_ENTRIES) || ((mappedObjectsCount & 1U) != 0U)) { + err = ERR_INFO(0x1381UL + SRDO_Index, 0, 2); + } else { + /* MISRA C 2004 14.10 */ + } + } + + /* Verify CRC */ + if ((err == 0U) && configurationInProgress) { + uint16_t crcResult = 0x0000; + uint16_t tmp_u16; + uint32_t tmp_u32; + + crcResult = CO_crc16_ccitt(&informationDirection, 1, crcResult); + tmp_u16 = CO_SWAP_16(safetyCycleTime); + crcResult = CO_crc16_ccitt((uint8_t*)&tmp_u16, 2, crcResult); + crcResult = CO_crc16_ccitt(&safetyRelatedValidationTime, 1, crcResult); + tmp_u32 = CO_SWAP_32(COB_ID1_normal); + crcResult = CO_crc16_ccitt((uint8_t*)&tmp_u32, 4, crcResult); + tmp_u32 = CO_SWAP_32(COB_ID2_inverted); + crcResult = CO_crc16_ccitt((uint8_t*)&tmp_u32, 4, crcResult); + crcResult = CO_crc16_ccitt(&mappedObjectsCount, 1, crcResult); + for (uint8_t i = 0; i < mappedObjectsCount; i++) { + uint8_t crcsubindex = i + 1U; + crcResult = CO_crc16_ccitt(&crcsubindex, 1, crcResult); + tmp_u32 = CO_SWAP_32(mapping[i]); + crcResult = CO_crc16_ccitt((uint8_t*)&tmp_u32, 4, crcResult); + } + + if (crcResult != crcSignatureFromOD) { + err = ERR_INFO(0x13FFUL, SRDO_Index + 1UL, 3); + } + } + + /* Configure mappings */ + if ((err == 0U) && configurationInProgress) { + CO_SRDO_size_t srdoDataLength[2] = {0, 0}; + + for (uint8_t i = 0; i < mappedObjectsCount; i++) { + uint8_t plain_inverted = i % 2U; + uint32_t map = mapping[i]; + uint16_t index = (uint16_t)(map >> 16); + uint8_t subIndex = (uint8_t)(map >> 8); + uint8_t mappedLengthBits = (uint8_t)map; + uint8_t mappedLength = mappedLengthBits >> 3; + OD_IO_t* OD_IO = &SRDO->OD_IO[i]; + + /* total SRDO length can not be more than CO_SRDO_MAX_SIZE bytes */ + if (mappedLength > CO_SRDO_MAX_SIZE) { + err = ERR_INFO(0x1381UL + SRDO_Index, i + 1UL, 4); + } + /* is there a reference to the dummy entry */ + else if ((index < 0x20U) && (subIndex == 0U)) { + OD_stream_t* stream = &OD_IO->stream; + (void)memset(stream, 0, sizeof(OD_stream_t)); + stream->dataLength = mappedLength; + stream->dataOffset = mappedLength; + OD_IO->read = OD_read_dummy; + OD_IO->write = OD_write_dummy; + } + /* find entry in the Object Dictionary */ + else { + OD_IO_t OD_IOcopy; + OD_entry_t* entry = OD_find(SRDO->OD, index); + ODR_t odRet = OD_getSub(entry, subIndex, &OD_IOcopy, false); + if (odRet != ODR_OK) { + err = ERR_INFO(0x1381UL + SRDO_Index, i + 1UL, 5); + } else { + /* verify access attributes, byte alignment and length */ + OD_attr_t testAttribute = (informationDirection == CO_SRDO_RX) ? (OD_attr_t)(ODA_RSRDO) + : (OD_attr_t)(ODA_TSRDO); + if (((OD_IOcopy.stream.attribute & testAttribute) == 0U) || ((mappedLengthBits & 0x07U) != 0U) + || (OD_IOcopy.stream.dataLength < mappedLength)) { + err = ERR_INFO(0x1381UL + SRDO_Index, i + 1UL, 6); + } + + /* Copy values and store mappedLength temporary. */ + *OD_IO = OD_IOcopy; + OD_IO->stream.dataOffset = mappedLength; + srdoDataLength[plain_inverted] += mappedLength; + } + } + if (err != 0U) { + break; + } + } /* for (uint8_t i = 0; i < mappedObjectsCount; i++) */ + + if (err == 0U) { + if (srdoDataLength[0] != srdoDataLength[1]) { + err = ERR_INFO(0x1381UL + SRDO_Index, 0, 7); + } else if ((srdoDataLength[0] == 0U) || (srdoDataLength[0] > CO_SRDO_MAX_SIZE)) { + err = ERR_INFO(0x1381UL + SRDO_Index, 0, 8); + } else { + SRDO->dataLength = srdoDataLength[0]; + SRDO->mappedObjectsCount = mappedObjectsCount; + } + } + } + + /* Configure CAN tx buffers */ + if ((err == 0U) && configurationInProgress && (informationDirection == CO_SRDO_TX)) { + /* Normal Configuration */ + SRDO->CANtxBuff[0] = CO_CANtxBufferInit(SRDO->CANdevTx[0], SRDO->CANdevTxIdx[0], (uint16_t)COB_ID1_normal, + false, SRDO->dataLength, false); + + if (SRDO->CANtxBuff[0] == NULL) { + err = ERR_INFO(0x1301UL + SRDO_Index, 5, 10); + } + + /* Inverted Configuration */ + SRDO->CANtxBuff[1] = CO_CANtxBufferInit(SRDO->CANdevTx[1], SRDO->CANdevTxIdx[1], (uint16_t)COB_ID2_inverted, + false, SRDO->dataLength, false); + + if (SRDO->CANtxBuff[1] == NULL) { + err = ERR_INFO(0x1301UL + SRDO_Index, 6, 10); + } + } + + /* Configure CAN rx buffers */ + if ((err == 0U) && configurationInProgress && (informationDirection == CO_SRDO_RX)) { + /* Normal Configuration */ + ret = CO_CANrxBufferInit(SRDO->CANdevRx[0], SRDO->CANdevRxIdx[0], (uint16_t)COB_ID1_normal, 0x7FF, false, + (void*)SRDO, CO_SRDO_receive_normal); + + if (ret != CO_ERROR_NO) { + err = ERR_INFO(0x1301UL + SRDO_Index, 5, 11); + } + + /* Inverted Configuration */ + ret = CO_CANrxBufferInit(SRDO->CANdevRx[1], SRDO->CANdevRxIdx[1], (uint16_t)COB_ID2_inverted, 0x7FF, false, + (void*)SRDO, CO_SRDO_receive_inverted); + + if (ret != CO_ERROR_NO) { + err = ERR_INFO(0x1301UL + SRDO_Index, 6, 11); + } + } + + /* Configure remaining variables */ + if (err == 0U) { + SRDO->informationDirection = informationDirection; + SRDO->cycleTime_us = (uint32_t)safetyCycleTime * 1000U; + SRDO->validationTime_us = (uint32_t)safetyRelatedValidationTime * 1000U; + } else { + if (ret == CO_ERROR_NO) { + CO_errorReport(SRDO->em, CO_EM_SRDO_CONFIGURATION, CO_EMC_DATA_SET, err); + configurationValidUnset(SRDO->SRDOGuard); + } + } + + if (errInfo != NULL) { + *errInfo = err; + } + + return ret; +} + +CO_ReturnError_t +CO_SRDO_init(CO_SRDO_t* SRDO, uint8_t SRDO_Index, CO_SRDOGuard_t* SRDOGuard, OD_t* OD, CO_EM_t* em, uint8_t nodeId, + uint16_t defaultCOB_ID, OD_entry_t* OD_130x_SRDOCommPar, OD_entry_t* OD_138x_SRDOMapPar, + CO_CANmodule_t* CANdevRxNormal, CO_CANmodule_t* CANdevRxInverted, uint16_t CANdevRxIdxNormal, + uint16_t CANdevRxIdxInverted, CO_CANmodule_t* CANdevTxNormal, CO_CANmodule_t* CANdevTxInverted, + uint16_t CANdevTxIdxNormal, uint16_t CANdevTxIdxInverted, uint32_t* errInfo) { + + CO_ReturnError_t ret = CO_ERROR_NO; + + /* verify arguments */ + if ((SRDO == NULL) || (SRDOGuard == NULL) || (OD == NULL) || (em == NULL) || (OD_130x_SRDOCommPar == NULL) + || (OD_138x_SRDOMapPar == NULL) || (CANdevRxNormal == NULL) || (CANdevRxInverted == NULL) + || (CANdevTxNormal == NULL) || (CANdevTxInverted == NULL)) { + ret = CO_ERROR_ILLEGAL_ARGUMENT; + } + /* clear object and configure some object variables */ + else { + (void)memset(SRDO, 0, sizeof(CO_SRDO_t)); + + SRDO->SRDOGuard = SRDOGuard; + SRDO->OD = OD; + SRDO->em = em; + SRDO->defaultCOB_ID = defaultCOB_ID; + SRDO->nodeId = nodeId; + SRDO->CANdevTx[0] = CANdevTxNormal; + SRDO->CANdevTx[1] = CANdevTxInverted; + SRDO->CANdevRx[0] = CANdevRxNormal; + SRDO->CANdevRx[1] = CANdevRxInverted; + + SRDO->CANdevTxIdx[0] = CANdevTxIdxNormal; + SRDO->CANdevTxIdx[1] = CANdevTxIdxInverted; + SRDO->CANdevRxIdx[0] = CANdevRxIdxNormal; + SRDO->CANdevRxIdx[1] = CANdevRxIdxInverted; + + SRDO->OD_communicationParam_entry = OD_130x_SRDOCommPar; + SRDO->OD_mappingParam_entry = OD_138x_SRDOMapPar; + + /* Configure Object dictionary entry at index 0x1301+ */ + SRDO->OD_communicationParam_ext.object = SRDO; + SRDO->OD_communicationParam_ext.read = OD_read_SRDO_communicationParam; + SRDO->OD_communicationParam_ext.write = OD_write_SRDO_communicationParam; + (void)OD_extension_init(OD_130x_SRDOCommPar, &SRDO->OD_communicationParam_ext); + + /* Configure Object dictionary entry at index 0x1381+ */ + SRDO->OD_mappingParam_extension.object = SRDO; + SRDO->OD_mappingParam_extension.read = OD_readOriginal; + SRDO->OD_mappingParam_extension.write = OD_write_SRDO_mappingParam; + (void)OD_extension_init(OD_138x_SRDOMapPar, &SRDO->OD_mappingParam_extension); + + ret = CO_SRDO_config(SRDO, SRDO_Index, SRDOGuard, errInfo); + } + + return ret; +} + +CO_ReturnError_t +CO_SRDO_requestSend(CO_SRDO_t* SRDO) { + CO_ReturnError_t ret; + + if (SRDO->SRDOGuard->NMTisOperational == false) { + ret = CO_ERROR_WRONG_NMT_STATE; + } else if (SRDO->SRDOGuard->configurationValid == false) { + ret = CO_ERROR_OD_PARAMETERS; + } else if (SRDO->informationDirection != CO_SRDO_TX) { + ret = CO_ERROR_TX_UNCONFIGURED; + } else if (SRDO->nextIsNormal == false) { + ret = CO_ERROR_TX_BUSY; + } else { + SRDO->cycleTimer = 0; + ret = CO_ERROR_NO; + } + + return ret; +} + +CO_SRDO_state_t +CO_SRDO_process(CO_SRDO_t* SRDO, uint32_t timeDifference_us, uint32_t* timerNext_us, bool_t NMTisOperational) { + (void)timerNext_us; /* may be unused */ + + if (NMTisOperational && (SRDO->informationDirection != CO_SRDO_INVALID) && SRDO->SRDOGuard->configurationValid + && (SRDO->internalState >= CO_SRDO_state_unknown)) { + SRDO->cycleTimer = (SRDO->cycleTimer > timeDifference_us) ? (SRDO->cycleTimer - timeDifference_us) : 0U; + SRDO->invertedDelay = (SRDO->invertedDelay > timeDifference_us) ? (SRDO->invertedDelay - timeDifference_us) + : 0U; + SRDO->validationTimer = (SRDO->validationTimer > timeDifference_us) + ? (SRDO->validationTimer - timeDifference_us) + : 0U; + + /* Detect transition to NMT operational */ + if (!SRDO->NMTisOperationalPrevious) { + SRDO->cycleTimer = (SRDO->informationDirection == CO_SRDO_TX) + ? ((uint32_t)SRDO->nodeId * 500U) /* 0.5ms * node-ID delay */ + : SRDO->cycleTime_us; + SRDO->validationTimer = SRDO->cycleTime_us; + SRDO->internalState = CO_SRDO_state_initializing; + SRDO->nextIsNormal = true; + } + + if (SRDO->internalState <= CO_SRDO_state_unknown) { + SRDO->internalState = CO_SRDO_state_error_internal; /* should not happen */ + } else if (SRDO->informationDirection == CO_SRDO_TX) { + if (SRDO->nextIsNormal) { + if (SRDO->cycleTimer == 0U) { + uint8_t* dataSRDO[2] = {&SRDO->CANtxBuff[0]->data[0], &SRDO->CANtxBuff[1]->data[0]}; + size_t verifyLength[2] = {0, 0}; + + /* copy mapped data from Object Dictionary into CAN buffers */ + for (uint8_t i = 0; i < SRDO->mappedObjectsCount; i++) { + uint8_t plain_inverted = i % 2U; + OD_IO_t* OD_IO = &SRDO->OD_IO[i]; + OD_stream_t* stream = &OD_IO->stream; + + /* get mappedLength from temporary storage */ + uint8_t mappedLength = (uint8_t)stream->dataOffset; + + /* additional safety check */ + verifyLength[plain_inverted] += mappedLength; + if (verifyLength[plain_inverted] > CO_SRDO_MAX_SIZE) { + break; + } + + /* length of OD variable may be larger than mappedLength */ + OD_size_t ODdataLength = stream->dataLength; + if (ODdataLength > CO_SRDO_MAX_SIZE) { + ODdataLength = CO_SRDO_MAX_SIZE; + } + /* If mappedLength is smaller than ODdataLength, use auxiliary buffer */ + uint8_t buf[CO_SRDO_MAX_SIZE]; + uint8_t* dataSRDOCopy; + if (ODdataLength > mappedLength) { + (void)memset(buf, 0, sizeof(buf)); + dataSRDOCopy = buf; + } else { + dataSRDOCopy = dataSRDO[plain_inverted]; + } + + /* Set stream.dataOffset to zero, perform OD_IO.read() + * and store mappedLength back to stream.dataOffset */ + stream->dataOffset = 0; + OD_size_t countRd; + OD_IO->read(stream, dataSRDOCopy, ODdataLength, &countRd); + stream->dataOffset = mappedLength; + + /* swap multibyte data if big-endian */ +#ifdef CO_BIG_ENDIAN + if ((stream->attribute & ODA_MB) != 0) { + uint8_t* lo = dataSRDOCopy; + uint8_t* hi = dataSRDOCopy + ODdataLength - 1; + while (lo < hi) { + uint8_t swap = *lo; + *lo++ = *hi; + *hi-- = swap; + } + } +#endif + + /* If auxiliary buffer, copy it to the SRDO */ + if (ODdataLength > mappedLength) { + (void)memcpy(dataSRDO[plain_inverted], buf, mappedLength); + } + + dataSRDO[plain_inverted] += mappedLength; + } + + if ((verifyLength[0] != verifyLength[1]) || (verifyLength[0] > CO_SRDO_MAX_SIZE) + || (verifyLength[0] != SRDO->dataLength)) { + SRDO->internalState = CO_SRDO_state_error_internal; /* should not happen */ + } else { + bool_t data_ok = true; +#if ((CO_CONFIG_SRDO)&CO_CONFIG_SRDO_CHECK_TX) != 0 + /* check data before sending (optional) */ + for (uint8_t i = 0; i < SRDO->dataLength; i++) { + if ((uint8_t)(~SRDO->CANtxBuff[0]->data[i]) != SRDO->CANtxBuff[1]->data[i]) { + SRDO->internalState = CO_SRDO_state_error_txNotInverted; + data_ok = false; + break; + } + } +#endif + if (data_ok) { + if (CO_CANsend(SRDO->CANdevTx[0], SRDO->CANtxBuff[0]) == CO_ERROR_NO) { + SRDO->cycleTimer = + SRDO->cycleTime_us; /* cycleTime_us is verified, result can't be <0 */ + SRDO->invertedDelay = CO_CONFIG_SRDO_MINIMUM_DELAY; + SRDO->nextIsNormal = false; + SRDO->internalState = CO_SRDO_state_communicationEstablished; + } else { + SRDO->internalState = CO_SRDO_state_error_txFail; + } + } + } + } + } else { + if (SRDO->invertedDelay == 0U) { + if (CO_CANsend(SRDO->CANdevTx[1], SRDO->CANtxBuff[1]) == CO_ERROR_NO) { + SRDO->nextIsNormal = true; + } else { + SRDO->internalState = CO_SRDO_state_error_txFail; + } + } + } + +#if ((CO_CONFIG_SRDO)&CO_CONFIG_FLAG_TIMERNEXT) != 0 + if (timerNext_us != NULL) { + if (*timerNext_us > SRDO->cycleTimer) { + *timerNext_us = SRDO->cycleTimer; /* Schedule for the next message timer */ + } + } +#endif + } else { /* CO_SRDO_RX */ + /* verify error from receive function */ + if (SRDO->rxSrdoShort) { + CO_errorReport(SRDO->em, CO_EM_RPDO_WRONG_LENGTH, CO_EMC_PDO_LENGTH, 0); + SRDO->internalState = CO_SRDO_state_error_rxShort; + } else if (CO_FLAG_READ(SRDO->CANrxNew[SRDO->nextIsNormal ? 0 : 1])) { + /* normal message received ? */ + if (SRDO->nextIsNormal) { + SRDO->validationTimer = SRDO->validationTime_us; + SRDO->nextIsNormal = false; + } + /* inverted message received */ + else { + SRDO->cycleTimer = SRDO->cycleTime_us; + SRDO->validationTimer = SRDO->cycleTime_us; + SRDO->nextIsNormal = true; + + uint8_t* dataSRDO[2] = {&SRDO->CANrxData[0][0], &SRDO->CANrxData[1][0]}; + bool_t data_ok = true; + + /* Verify, if normal and inverted data matches properly */ + for (uint8_t i = 0; i < SRDO->dataLength; i++) { + if ((uint8_t)(~dataSRDO[0][i]) != dataSRDO[1][i]) { + data_ok = false; + SRDO->internalState = CO_SRDO_state_error_rxNotInverted; + break; + } + } + + /* copy data from CAN messages into mapped data from Object Dictionary */ + if (data_ok) { + size_t verifyLength[2] = {0, 0}; + for (uint8_t i = 0; i < SRDO->mappedObjectsCount; i++) { + uint8_t plain_inverted = i % 2U; + OD_IO_t* OD_IO = &SRDO->OD_IO[i]; + OD_stream_t* stream = &OD_IO->stream; + + /* get mappedLength from temporary storage */ + OD_size_t* dataOffset = &stream->dataOffset; + uint8_t mappedLength = (uint8_t)(*dataOffset); + + /* additional safety check */ + verifyLength[plain_inverted] += mappedLength; + if (verifyLength[plain_inverted] > CO_SRDO_MAX_SIZE) { + break; + } + + /* length of OD variable may be larger than mappedLength */ + OD_size_t ODdataLength = stream->dataLength; + if (ODdataLength > CO_SRDO_MAX_SIZE) { + ODdataLength = CO_SRDO_MAX_SIZE; + } + /* Prepare data for writing into OD variable. If mappedLength + * is smaller than ODdataLength, then use auxiliary buffer */ + uint8_t buf[CO_SRDO_MAX_SIZE]; + uint8_t* dataOD; + if (ODdataLength > mappedLength) { + (void)memset(buf, 0, sizeof(buf)); + (void)memcpy(buf, dataSRDO[plain_inverted], mappedLength); + dataOD = buf; + } else { + dataOD = dataSRDO[plain_inverted]; + } + + /* swap multibyte data if big-endian */ +#ifdef CO_BIG_ENDIAN + if ((stream->attribute & ODA_MB) != 0) { + uint8_t* lo = dataOD; + uint8_t* hi = dataOD + ODdataLength - 1; + while (lo < hi) { + uint8_t swap = *lo; + *lo++ = *hi; + *hi-- = swap; + } + } +#endif + + /* Set stream.dataOffset to zero, perform OD_IO.write() + * and store mappedLength back to stream.dataOffset */ + *dataOffset = 0; + OD_size_t countWritten; + OD_IO->write(&OD_IO->stream, dataOD, ODdataLength, &countWritten); + *dataOffset = mappedLength; + + dataSRDO[plain_inverted] += mappedLength; + } /* for (uint8_t i = 0; i < SRDO->mappedObjectsCount; i++) */ + + /* safety check, this should not happen */ + if ((verifyLength[0] != verifyLength[1]) || (verifyLength[0] > CO_SRDO_MAX_SIZE) + || (verifyLength[0] != SRDO->dataLength)) { + SRDO->internalState = CO_SRDO_state_error_internal; + } else { + SRDO->internalState = CO_SRDO_state_communicationEstablished; + } + } /* if (data_ok) { */ + + CO_FLAG_CLEAR(SRDO->CANrxNew[0]); + CO_FLAG_CLEAR(SRDO->CANrxNew[1]); + } /* inverted message received */ + } else { /* MISRA C 2004 14.10 */ + } + + /* verify timeouts */ + if (SRDO->cycleTimer == 0U) { + SRDO->internalState = CO_SRDO_state_error_rxTimeoutSCT; + } else if (SRDO->validationTimer == 0U) { + SRDO->internalState = CO_SRDO_state_error_rxTimeoutSRVT; + } else { + /* MISRA C 2004 14.10 */ + } +#if ((CO_CONFIG_SRDO)&CO_CONFIG_FLAG_TIMERNEXT) != 0 + if (timerNext_us != NULL) { + if (*timerNext_us > SRDO->cycleTimer) { + *timerNext_us = SRDO->cycleTimer; /* Schedule for the next message timer */ + } + if (*timerNext_us > SRDO->validationTimer) { + *timerNext_us = SRDO->validationTimer; /* Schedule for the next message timer */ + } + } +#endif + } /* CO_SRDO_RX */ + } /* if (NMTisOperational && ... */ + else { + CO_FLAG_CLEAR(SRDO->CANrxNew[0]); + CO_FLAG_CLEAR(SRDO->CANrxNew[1]); + if (!SRDO->SRDOGuard->configurationValid) { + SRDO->internalState = CO_SRDO_state_error_configuration; + } else if (!NMTisOperational) { + SRDO->internalState = CO_SRDO_state_nmtNotOperational; + } else if (SRDO->informationDirection == CO_SRDO_INVALID) { + SRDO->internalState = CO_SRDO_state_deleted; + } else { + /* keep internalState unchanged */ + /* MISRA C 2004 14.10 */ + } + } + + SRDO->NMTisOperationalPrevious = NMTisOperational; + SRDO->SRDOGuard->NMTisOperational = NMTisOperational; + + return SRDO->internalState; +} + +#endif /* (CO_CONFIG_SRDO) & CO_CONFIG_SRDO_ENABLE */ diff --git a/304/CO_SRDO.h b/304/CO_SRDO.h new file mode 100644 index 00000000..e25e7861 --- /dev/null +++ b/304/CO_SRDO.h @@ -0,0 +1,285 @@ +/** + * CANopen Safety Related Data Object protocol. + * + * @file CO_SRDO.h + * @ingroup CO_SRDO + * @author Robert Grüning + * @copyright 2020 Robert Grüning + * @copyright 2024 temi54c1l8(at)github + * @copyright 2024 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#ifndef CO_SRDO_H +#define CO_SRDO_H + +#include "301/CO_Emergency.h" +#include "301/CO_ODinterface.h" + +/* default configuration, see CO_config.h */ +#ifndef CO_CONFIG_SRDO +#define CO_CONFIG_SRDO (0) +#endif +#ifndef CO_CONFIG_SRDO_MINIMUM_DELAY +#define CO_CONFIG_SRDO_MINIMUM_DELAY 0U +#endif + +#if (((CO_CONFIG_SRDO)&CO_CONFIG_SRDO_ENABLE) != 0) || defined CO_DOXYGEN + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup CO_SRDO SRDO + * CANopen Safety Related Data Object protocol + * + * @ingroup CO_CANopen_304 + * @{ + * Safety Related Data Object protocol is specified by standard EN 50325-5:2010 (formerly CiA304). Its functionality is + * very similar to that of the PDOs. The main differences is every message is send and received twice. The second + * message must be bitwise inverted. The delay between the two messages and between each message pair is monitored. The + * distinction between sending and receiving SRDO is made at runtime (for PDO it is compile time). If the security + * protocol is used, at least one SRDO is mandatory. + * + * If there is erroneous structure of OD entries for SRDO parameters, then CO_SRDO_init() function returns error and + * CANopen device doesn't work. It is necessary to repair Object Dictionary and reprogram the device. + * + * If there are erroneous values inside SRDO parameters, then Emergency message @ref CO_EM_SRDO_CONFIGURATION is sent. + * Info code (32bit) contains OD index, subindex and additional byte, which helps to determine erroneous OD object. + * + * SRDO is first configured in CANopen in CANopen initialization section after all other CANopen objects are + * initialized. It consists of one CO_SRDOGuard_init() and CO_SRDO_init() for each SRDO. On transition to NMT + * operational CO_SRDO_config() must be called for each SRDO. + * + * CO_SRDO_process() must be executed cyclically, similar as PDO processing. Function is fast, no time consuming tasks. + * Function returns @ref CO_SRDO_state_t value, which may be used to determine working-state or safe-state of safety + * related device. If return values from all SRDO objects are >= @ref CO_SRDO_state_communicationEstablished, then + * working state is allowed. Otherwise SR device must be in safe state. + * + * Requirement for mapped objects: + * - @ref OD_attributes_t must have set bit ODA_RSRDO or ODA_RSRDO or ODA_TRSRDO (by CANopenEditor). + */ + +/** Maximum size of SRDO message, 8 for standard CAN */ +#ifndef CO_SRDO_MAX_SIZE +#define CO_SRDO_MAX_SIZE 8U +#endif + +/** Maximum number of entries, which can be mapped to SRDO, 2*8 for standard CAN, may be less to preserve RAM usage. + * Must be multiple of 2. */ +#ifndef CO_SRDO_MAX_MAPPED_ENTRIES +#define CO_SRDO_MAX_MAPPED_ENTRIES 16U +#endif + +#ifndef CO_SRDO_OWN_TYPES +/** Variable of type @ref CO_SRDO_size_t contains data length in bytes of SRDO */ +typedef uint8_t CO_SRDO_size_t; +#endif + +/** + * SRDO internal state + */ +typedef enum { + CO_SRDO_state_error_internal = -10, /**< internal software error */ + CO_SRDO_state_error_configuration = -9, /**< error in parameters, emergency message was sent */ + CO_SRDO_state_error_txNotInverted = -6, /**< Transmitting SRDO messages was not inverted */ + CO_SRDO_state_error_txFail = -5, /**< SRDO CAN message transmission failed */ + CO_SRDO_state_error_rxTimeoutSRVT = -4, /**< SRDO message didn't receive inside SRVT time */ + CO_SRDO_state_error_rxTimeoutSCT = -3, /**< SRDO inverted message didn't receive inside SCT time */ + CO_SRDO_state_error_rxNotInverted = -2, /**< Received SRDO messages was not inverted */ + CO_SRDO_state_error_rxShort = -1, /**< Received SRDO message is too short */ + CO_SRDO_state_unknown = 0, /**< unknown state, set by CO_SRDO_init() */ + CO_SRDO_state_nmtNotOperational = 1, /**< Internal NMT operating state is not NMT operational */ + CO_SRDO_state_initializing = 2, /**< Just entered NMT operational state, SRDO message not yet received or + transmitted */ + CO_SRDO_state_communicationEstablished = 3, /**< SRDO communication established, fully functional */ + CO_SRDO_state_deleted = 10 /**< informationDirection for this SRDO is set to 0 */ +} CO_SRDO_state_t; + +/** + * Guard Object for SRDO. + * + * Guard object monitors all SRDO objects for: + * - access to CRC objects + * - access configuration valid flag + * - change in operation state + */ +typedef struct { + bool_t NMTisOperational; /**< True if NMT operating state is operational */ + bool_t configurationValid; /**< True if all SRDO objects are properly configured. Set after successful finish of all + CO_SRDO_init() functions. Cleared on configuration change. */ + OD_IO_t OD_IO_configurationValid; /**< Object for input / output on the OD variable 13FE:00. Configuration of any of + the the SRDO parameters will write 0 to that variable. */ + OD_entry_t* OD_13FE_entry; /**< From CO_SRDOGuard_init() */ + OD_entry_t* OD_13FF_entry; /**< From CO_SRDOGuard_init() */ + OD_extension_t OD_13FE_extension; /**< Extension for OD object */ + OD_extension_t OD_13FF_extension; /**< Extension for OD object */ +} CO_SRDOGuard_t; + +/** + * SRDO object. + */ +typedef struct { + CO_SRDOGuard_t* SRDOGuard; /**< From CO_SRDO_init() */ + OD_t* OD; /**< From CO_SRDO_init() */ + CO_EM_t* em; /**< From CO_SRDO_init() */ + uint16_t defaultCOB_ID; /**< From CO_SRDO_init() */ + uint8_t nodeId; /**< From CO_SRDO_init() */ + CO_CANmodule_t* CANdevTx[2]; /**< From CO_SRDO_init() */ + uint16_t CANdevTxIdx[2]; /**< From CO_SRDO_init() */ + CO_CANmodule_t* CANdevRx[2]; /**< From CO_SRDO_init() */ + uint16_t CANdevRxIdx[2]; /**< From CO_SRDO_init() */ + CO_SRDO_state_t internalState; /**< Internal state of this SRDO. */ + bool_t NMTisOperationalPrevious; /**< Copy of variable, internal usage. */ + uint8_t informationDirection; /**< 0 - SRDO is disabled; 1 - SRDO is producer (tx); 2 - SRDO is consumer (rx) */ + uint32_t cycleTime_us; /**< Safety Cycle Time from object dictionary translated to microseconds */ + uint32_t validationTime_us; /**< Safety related validation time from object dictionary translated to microseconds */ + uint32_t cycleTimer; /**< cycle timer variable in microseconds */ + uint32_t invertedDelay; /**< inverted delay timer variable in microseconds */ + uint32_t validationTimer; /**< validation timer variable in microseconds */ + CO_SRDO_size_t dataLength; /**< Data length of the received SRDO message. Calculated from mapping */ + uint8_t mappedObjectsCount; /**< Number of mapped objects in SRDO */ + OD_IO_t OD_IO[CO_SRDO_MAX_MAPPED_ENTRIES]; /**< Object dictionary interface for all mapped entries. OD_IO.dataOffset + has special usage with SRDO. It stores information about mappedLength + of the variable. mappedLength can be less or equal to the + OD_IO.dataLength. mappedLength greater than OD_IO.dataLength indicates + erroneous mapping. OD_IO.dataOffset is set to 0 before read/write + function call and after the call OD_IO.dataOffset is set back to + mappedLength. */ + CO_CANtx_t* CANtxBuff[2]; /**< CAN transmit buffers inside CANdevTx */ + volatile void* CANrxNew[2]; /**< Variable indicates, if new SRDO message received from CAN bus. */ + bool_t rxSrdoShort; /**< true, if received SRDO is too short */ + uint8_t CANrxData[2][CO_SRDO_MAX_SIZE]; /**< two buffers of data bytes for the received message. */ + bool_t nextIsNormal; /**< If true, next processed SRDO message is normal (not inverted) */ + OD_entry_t* OD_communicationParam_entry; /**< From CO_SRDO_init() */ + OD_entry_t* OD_mappingParam_entry; /**< From CO_SRDO_init() */ + OD_extension_t OD_communicationParam_ext; /**< Extension for OD object */ + OD_extension_t OD_mappingParam_extension; /**< Extension for OD object */ +#if (((CO_CONFIG_SRDO)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0) || defined CO_DOXYGEN + void (*pFunctSignalPre)(void* object); /**< From CO_SRDO_initCallbackPre() or NULL */ + void* functSignalObjectPre; /**< From CO_SRDO_initCallbackPre() or NULL */ +#endif +} CO_SRDO_t; + +/** + * Initialize SRDOGuard object. + * + * Function must be called in the communication reset section before CO_SRDO_init() functions. + * + * @param SRDOGuard This object will be initialized. + * @param OD_13FE_configurationValid Pointer to _Configuration valid_ variable from Object dictionary (index 0x13FE). + * @param OD_13FF_safetyConfigurationSignature Pointer to _Safety configuration signature_ variable from Object + * dictionary (index 0x13FF). + * @param [out] errInfo Additional information in case of error, may be NULL. + * + * @return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT. + */ +CO_ReturnError_t CO_SRDOGuard_init(CO_SRDOGuard_t* SRDOGuard, OD_entry_t* OD_13FE_configurationValid, + OD_entry_t* OD_13FF_safetyConfigurationSignature, uint32_t* errInfo); + +/** + * Initialize SRDO object. + * + * Function must be called in the communication reset section. + * + * @param SRDO This object will be initialized. + * @param SRDO_Index OD index of this SRDO, 0 for the first. + * @param SRDOGuard SRDOGuard object. + * @param OD CANopen Object Dictionary + * @param em Emergency object. + * @param nodeId CANopen Node ID of this device. If default COB_ID is used, value will be added. + * @param defaultCOB_ID Default COB ID for this SRDO for plain data (without NodeId). + * @param OD_130x_SRDOCommPar Pointer to _SRDO communication parameter_ record from Object dictionary (index 0x1301+). + * @param OD_138x_SRDOMapPar Pointer to _SRDO mapping parameter_ record from Object dictionary (index 0x1381+). + * @param CANdevRxNormal CAN device used for SRDO reception for normal object. + * @param CANdevRxInverted CAN device used for SRDO reception for inverted object. + * @param CANdevRxIdxNormal Index of receive buffer in the above CAN device (normal). + * @param CANdevRxIdxInverted Index of receive buffer in the above CAN device (inverted). + * @param CANdevTxNormal CAN device used for SRDO transmission for normal object. + * @param CANdevTxInverted CAN device used for SRDO transmission for inverted object. + * @param CANdevTxIdxNormal Index of transmit buffer in the above CAN device (normal). + * @param CANdevTxIdxInverted Index of transmit buffer in the above CAN device (inverted). + * @param [out] errInfo Additional information in case of error, may be NULL. + * + * @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_ILLEGAL_ARGUMENT or CO_ERROR_OD_PARAMETERS. + */ +CO_ReturnError_t CO_SRDO_init(CO_SRDO_t* SRDO, uint8_t SRDO_Index, CO_SRDOGuard_t* SRDOGuard, OD_t* OD, CO_EM_t* em, + uint8_t nodeId, uint16_t defaultCOB_ID, OD_entry_t* OD_130x_SRDOCommPar, + OD_entry_t* OD_138x_SRDOMapPar, CO_CANmodule_t* CANdevRxNormal, + CO_CANmodule_t* CANdevRxInverted, uint16_t CANdevRxIdxNormal, + uint16_t CANdevRxIdxInverted, CO_CANmodule_t* CANdevTxNormal, + CO_CANmodule_t* CANdevTxInverted, uint16_t CANdevTxIdxNormal, + uint16_t CANdevTxIdxInverted, uint32_t* errInfo); + +#if (((CO_CONFIG_SRDO)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0) || defined CO_DOXYGEN +/** + * Initialize SRDO callback function. + * + * Function initializes optional callback function, which should immediately start processing of CO_SRDO_process() + * function. Callback is called after SRDO message is received from the CAN bus. + * + * @param SRDO This object. + * @param object Pointer to object, which will be passed to pFunctSignalPre(). Can be NULL + * @param pFunctSignalPre Pointer to the callback function. Not called if NULL. + */ +void CO_SRDO_initCallbackPre(CO_SRDO_t* SRDO, void* object, void (*pFunctSignalPre)(void* object)); +#endif + +/** + * Configure SRDO object. + * + * Function must be called in on transition to NMT operational. Function is also called from CO_SRDO_init() function. + * + * @param SRDO This object will be configured. + * @param SRDO_Index OD index of this SRDO, 0 for the first. + * @param SRDOGuard SRDOGuard object. + * @param [out] errInfo Additional information in case of error, may be NULL. + * + * @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_ILLEGAL_ARGUMENT or CO_ERROR_OD_PARAMETERS. + */ +CO_ReturnError_t CO_SRDO_config(CO_SRDO_t* SRDO, uint8_t SRDO_Index, CO_SRDOGuard_t* SRDOGuard, uint32_t* errInfo); + +/** + * Send SRDO on event + * + * Sends SRDO before the next refresh timer tiggers. The message itself is send in CO_SRDO_process(). Note that RTOS + * have to trigger its processing quickly. After the transmission the timer is reset to the full refresh time. + * + * @param SRDO This object. + * @return #CO_ReturnError_t: CO_ERROR_NO if request is granted + */ +CO_ReturnError_t CO_SRDO_requestSend(CO_SRDO_t* SRDO); + +/** + * Process transmitting/receiving individual SRDO message. + * + * @param SRDO This object. + * @param timeDifference_us Time difference from previous function call in [microseconds]. + * @param [out] timerNext_us info to OS, may be null. + * @param NMTisOperational True if this node is in NMT_OPERATIONAL state. + * + * @return CO_SRDO_state_t internal state + */ +CO_SRDO_state_t CO_SRDO_process(CO_SRDO_t* SRDO, uint32_t timeDifference_us, uint32_t* timerNext_us, + bool_t NMTisOperational); + +/** @} */ /* CO_SRDO */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* (CO_CONFIG_SRDO) & CO_CONFIG_SRDO_ENABLE */ + +#endif /* CO_SRDO_H */ diff --git a/305/CO_LSS.h b/305/CO_LSS.h new file mode 100644 index 00000000..4430de37 --- /dev/null +++ b/305/CO_LSS.h @@ -0,0 +1,223 @@ +/** + * CANopen Layer Setting Services protocol (common). + * + * @file CO_LSS.h + * @ingroup CO_LSS + * @author Martin Wagner + * @copyright 2017 - 2020 Neuberger Gebaeudeautomation GmbH + * + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#ifndef CO_LSS_H +#define CO_LSS_H + +#include "301/CO_driver.h" + +/* default configuration, see CO_config.h */ +#ifndef CO_CONFIG_LSS +#define CO_CONFIG_LSS (CO_CONFIG_LSS_SLAVE | CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE) +#endif + +#if (((CO_CONFIG_LSS) & (CO_CONFIG_LSS_SLAVE | CO_CONFIG_LSS_MASTER)) != 0) || defined CO_DOXYGEN + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup CO_LSS LSS + * CANopen Layer Setting Services protocol (common). + * + * @ingroup CO_CANopen_305 + * @{ + * LSS protocol is according to CiA DSP 305 V3.0.0. + * + * LSS services and protocols are used to inquire or to change the settings of three parameters of the physical layer, + * data link layer, and application layer on a CANopen device with LSS slave capability by a CANopen device with LSS + * master capability via the CAN network. + * + * The following parameters may be inquired or changed: + * - Node-ID of the CANopen device + * - Bit timing parameters of the physical layer (bit rate) + * - LSS address compliant to the identity object (1018h) + * + * The connection is established in one of two ways: + * - addressing a node by it's 128 bit LSS address. This requires that the master already knows the node's LSS address. + * - scanning the network for unknown nodes (Fastscan). Using this method, unknown devices can be found and configured + * one by one. + * + * Be aware that changing the bit rate is a critical step for the network. A failure will render the network unusable! + * + * Using this implementation, only master or slave can be included in one node at a time. + * + * For CAN identifiers see @ref CO_Default_CAN_ID_t + */ + +/** + * @defgroup CO_LSS_command_specifiers CO_LSS command specifiers + * @{ + * + * The LSS protocols are executed between the LSS master device and the LSS slave device(s) to implement the LSS + * services. Some LSS protocols require a sequence of CAN messages. + * + * As identifying method only "LSS fastscan" is supported. + */ +#define CO_LSS_SWITCH_STATE_GLOBAL 0x04U /**< Switch state global protocol */ +#define CO_LSS_SWITCH_STATE_SEL_VENDOR 0x40U /**< Switch state selective protocol - Vendor ID */ +#define CO_LSS_SWITCH_STATE_SEL_PRODUCT 0x41U /**< Switch state selective protocol - Product code */ +#define CO_LSS_SWITCH_STATE_SEL_REV 0x42U /**< Switch state selective protocol - Revision number */ +#define CO_LSS_SWITCH_STATE_SEL_SERIAL 0x43U /**< Switch state selective protocol - Serial number */ +#define CO_LSS_SWITCH_STATE_SEL 0x44U /**< Switch state selective protocol - Slave response */ +#define CO_LSS_CFG_NODE_ID 0x11U /**< Configure node ID protocol */ +#define CO_LSS_CFG_BIT_TIMING 0x13U /**< Configure bit timing parameter protocol */ +#define CO_LSS_CFG_ACTIVATE_BIT_TIMING 0x15U /**< Activate bit timing parameter protocol */ +#define CO_LSS_CFG_STORE 0x17U /**< Store configuration protocol */ +#define CO_LSS_IDENT_SLAVE 0x4FU /**< LSS Fastscan response */ +#define CO_LSS_IDENT_FASTSCAN 0x51U /**< LSS Fastscan protocol */ +#define CO_LSS_INQUIRE_VENDOR 0x5AU /**< Inquire identity vendor-ID protocol */ +#define CO_LSS_INQUIRE_PRODUCT 0x5BU /**< Inquire identity product-code protocol */ +#define CO_LSS_INQUIRE_REV 0x5CU /**< Inquire identity revision-number protocol */ +#define CO_LSS_INQUIRE_SERIAL 0x5DU /**< Inquire identity serial-number protocol */ +#define CO_LSS_INQUIRE_NODE_ID 0x5EU /**< Inquire node-ID protocol */ +/** @} */ + +/** + * @defgroup CO_LSS_CFG_NODE_ID_status CO_LSS_CFG_NODE_ID status + * @{ + * Error codes for Configure node ID protocol + */ +#define CO_LSS_CFG_NODE_ID_OK 0x00U /**< Protocol successfully completed */ +#define CO_LSS_CFG_NODE_ID_OUT_OF_RANGE 0x01U /**< NID out of range */ +#define CO_LSS_CFG_NODE_ID_MANUFACTURER 0xFFU /**< Manufacturer specific error. No further support */ +/** @} */ + +/** + * @defgroup CO_LSS_CFG_BIT_TIMING_status CO_LSS_CFG_BIT_TIMING status + * @{ + * Error codes for Configure bit timing parameters protocol + */ +#define CO_LSS_CFG_BIT_TIMING_OK 0x00U /**< Protocol successfully completed */ +#define CO_LSS_CFG_BIT_TIMING_OUT_OF_RANGE 0x01U /**< Bit timing / Bit rate not supported */ +#define CO_LSS_CFG_BIT_TIMING_MANUFACTURER 0xFFU /**< Manufacturer specific error. No further support */ +/** @} */ + +/** + * @defgroup CO_LSS_CFG_STORE_status CO_LSS_CFG_STORE status + * @{ + * Error codes for Store configuration protocol + */ +#define CO_LSS_CFG_STORE_OK 0x00U /**< Protocol successfully completed */ +#define CO_LSS_CFG_STORE_NOT_SUPPORTED 0x01U /**< Store configuration not supported */ +#define CO_LSS_CFG_STORE_FAILED 0x02U /**< Storage media access error */ +#define CO_LSS_CFG_STORE_MANUFACTURER 0xFFU /**< Manufacturer specific error. No further support */ +/** @} */ + +/** + * @defgroup CO_LSS_FASTSCAN_bitcheck CO_LSS_FASTSCAN bitcheck + * @{ + * Fastscan BitCheck. BIT0 means all bits are checked for equality by slave + */ +#define CO_LSS_FASTSCAN_BIT0 0x00U /**< Least significant bit of IDnumbners bit area to be checked */ +/* ... */ +#define CO_LSS_FASTSCAN_BIT31 0x1FU /**< dito */ +#define CO_LSS_FASTSCAN_CONFIRM 0x80U /**< All LSS slaves waiting for scan respond and previous scan is reset */ +/** @} */ + +/** + * @defgroup CO_LSS_FASTSCAN_lssSub_lssNext CO_LSS_FASTSCAN lssSub lssNext + * @{ + */ +#define CO_LSS_FASTSCAN_VENDOR_ID 0x00U /**< Vendor ID */ +#define CO_LSS_FASTSCAN_PRODUCT 0x01U /**< Product code */ +#define CO_LSS_FASTSCAN_REV 0x02U /**< Revision number */ +#define CO_LSS_FASTSCAN_SERIAL 0x03U /**< Serial number */ +/** @} */ + +/** + * The LSS address is a 128 bit number, uniquely identifying each node. It consists of the values in object 0x1018. + */ +typedef union { + uint32_t addr[4]; + + struct { + uint32_t vendorID; + uint32_t productCode; + uint32_t revisionNumber; + uint32_t serialNumber; + } identity; +} CO_LSS_address_t; + +/** + * @defgroup CO_LSS_STATE_state CO_LSS_STATE state + * @{ + * + * The LSS FSA shall provide the following states: + * - Initial: Pseudo state, indicating the activation of the FSA. + * - LSS waiting: In this state, the LSS slave device waits for requests. + * - LSS configuration: In this state variables may be configured in the LSS slave. + * - Final: Pseudo state, indicating the deactivation of the FSA. + */ +#define CO_LSS_STATE_WAITING 0x00U /**< LSS FSA waiting for requests */ +#define CO_LSS_STATE_CONFIGURATION 0x01U /**< LSS FSA waiting for configuration */ +/** @} */ + +/** + * @defgroup CO_LSS_BIT_TIMING_table CO_LSS_BIT_TIMING table + * @{ + * Definition of table_index for /CiA301/ bit timing table + */ +#define CO_LSS_BIT_TIMING_1000 0U /**< 1000kbit/s */ +#define CO_LSS_BIT_TIMING_800 1U /**< 800kbit/s */ +#define CO_LSS_BIT_TIMING_500 2U /**< 500kbit/s */ +#define CO_LSS_BIT_TIMING_250 3U /**< 250kbit/s */ +#define CO_LSS_BIT_TIMING_125 4U /**< 125kbit/s */ + /* 5U - reserved */ +#define CO_LSS_BIT_TIMING_50 6U /**< 50kbit/s */ +#define CO_LSS_BIT_TIMING_20 7U /**< 20kbit/s */ +#define CO_LSS_BIT_TIMING_10 8U /**< 10kbit/s */ +#define CO_LSS_BIT_TIMING_AUTO 9U /**< Automatic bit rate detection */ +/** @} */ + +/** + * Lookup table for conversion between bit timing table and numerical bit rate + */ +static const uint16_t CO_LSS_bitTimingTableLookup[] = {1000, 800, 500, 250, 125, 0, 50, 20, 10, 0}; + +/** + * Invalid node ID triggers node ID assignment + */ +#define CO_LSS_NODE_ID_ASSIGNMENT 0xFFU + +/** + * Macro to check if node id is valid + */ +#define CO_LSS_NODE_ID_VALID(nid) (((nid >= 1U) && (nid <= 0x7FU)) || (nid == CO_LSS_NODE_ID_ASSIGNMENT)) + +/** + * Macro to check if two LSS addresses are equal + */ +#define CO_LSS_ADDRESS_EQUAL(/* CO_LSS_address_t */ a1, /* CO_LSS_address_t */ a2) \ + ((a1.identity.productCode == a2.identity.productCode) \ + && (a1.identity.revisionNumber == a2.identity.revisionNumber) \ + && (a1.identity.serialNumber == a2.identity.serialNumber) && (a1.identity.vendorID == a2.identity.vendorID)) + +/** @} */ /* @defgroup CO_LSS */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* (CO_CONFIG_LSS) & (CO_CONFIG_LSS_SLAVE | CO_CONFIG_LSS_MASTER) */ + +#endif /* CO_LSS_H */ diff --git a/305/CO_LSSmaster.c b/305/CO_LSSmaster.c new file mode 100644 index 00000000..1fb87426 --- /dev/null +++ b/305/CO_LSSmaster.c @@ -0,0 +1,965 @@ +/* + * CANopen LSS Master protocol. + * + * @file CO_LSSmaster.c + * @ingroup CO_LSS + * @author Martin Wagner + * @copyright 2017 - 2020 Neuberger Gebaeudeautomation GmbH + * + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#include + +#include "305/CO_LSSmaster.h" + +#if ((CO_CONFIG_LSS)&CO_CONFIG_LSS_MASTER) != 0 + +/* + * @defgroup CO_LSSmaster_state_t + * @{ + * LSS master slave select state machine. Compared to @ref CO_LSS_STATE_state this has information if we + * currently have selected one or all slaves. This allows for some basic error checking. + */ +#define CO_LSSmaster_STATE_WAITING 0x00U +#define CO_LSSmaster_STATE_CFG_SLECTIVE 0x01U +#define CO_LSSmaster_STATE_CFG_GLOBAL 0x02U +/* @} */ /* CO_LSSmaster_state_t */ + +/* + * @defgroup CO_LSSmaster_command_t LSS master slave command state machine + * @{ + */ +#define CO_LSSmaster_COMMAND_WAITING 0x00U +#define CO_LSSmaster_COMMAND_SWITCH_STATE 0x01U +#define CO_LSSmaster_COMMAND_CFG_BIT_TIMING 0x02U +#define CO_LSSmaster_COMMAND_CFG_NODE_ID 0x03U +#define CO_LSSmaster_COMMAND_CFG_STORE 0x04U +#define CO_LSSmaster_COMMAND_INQUIRE_VENDOR 0x05U +#define CO_LSSmaster_COMMAND_INQUIRE_PRODUCT 0x06U +#define CO_LSSmaster_COMMAND_INQUIRE_REV 0x07U +#define CO_LSSmaster_COMMAND_INQUIRE_SERIAL 0x08U +#define CO_LSSmaster_COMMAND_INQUIRE 0x09U +#define CO_LSSmaster_COMMAND_IDENTIFY_FASTSCAN 0x0AU +/* @} */ /* CO_LSSmaster_command_t */ + +/* + * @defgroup CO_LSSmaster_fs_t LSS master fastscan state machine + * @{ + */ +#define CO_LSSmaster_FS_STATE_CHECK 0x00U +#define CO_LSSmaster_FS_STATE_SCAN 0x01U +#define CO_LSSmaster_FS_STATE_VERIFY 0x02U + +/* @} */ /* CO_LSSmaster_fs_t */ + +/* + * Read received message from CAN module. + * + * Function will be called (by CAN receive interrupt) every time, when CAN message with correct identifier + * will be received. For more information and description of parameters see file CO_driver.h. + */ +static void +CO_LSSmaster_receive(void* object, void* msg) { + CO_LSSmaster_t* LSSmaster; + uint8_t DLC = CO_CANrxMsg_readDLC(msg); + const uint8_t* data = CO_CANrxMsg_readData(msg); + + LSSmaster = (CO_LSSmaster_t*)object; /* this is the correct pointer type of the first argument */ + + /* verify message length and message overflow (previous message was not processed yet). */ + if ((DLC == 8U) && !CO_FLAG_READ(LSSmaster->CANrxNew) && (LSSmaster->command != CO_LSSmaster_COMMAND_WAITING)) { + + /* copy data and set 'new message' flag */ + (void)memcpy(LSSmaster->CANrxData, data, sizeof(LSSmaster->CANrxData)); + + CO_FLAG_SET(LSSmaster->CANrxNew); + +#if ((CO_CONFIG_LSS)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0 + /* Optional signal to RTOS, which can resume task, which handles further processing. */ + if (LSSmaster->pFunctSignal != NULL) { + LSSmaster->pFunctSignal(LSSmaster->functSignalObject); + } +#endif + } +} + +/* + * Check LSS timeout. + * + * Generally, we do not really care if the message has been received before or after the timeout + * expired. Only if no message has been received we have to check for timeouts. + */ +static inline CO_LSSmaster_return_t +CO_LSSmaster_check_timeout(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us) { + CO_LSSmaster_return_t ret = CO_LSSmaster_WAIT_SLAVE; + + LSSmaster->timeoutTimer += timeDifference_us; + if (LSSmaster->timeoutTimer >= LSSmaster->timeout_us) { + LSSmaster->timeoutTimer = 0; + ret = CO_LSSmaster_TIMEOUT; + } + + return ret; +} + +CO_ReturnError_t +CO_LSSmaster_init(CO_LSSmaster_t* LSSmaster, uint16_t timeout_ms, CO_CANmodule_t* CANdevRx, uint16_t CANdevRxIdx, + uint16_t CANidLssSlave, CO_CANmodule_t* CANdevTx, uint16_t CANdevTxIdx, uint16_t CANidLssMaster) { + CO_ReturnError_t ret = CO_ERROR_NO; + + /* verify arguments */ + if ((LSSmaster == NULL) || (CANdevRx == NULL) || (CANdevTx == NULL)) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + LSSmaster->timeout_us = (uint32_t)timeout_ms * 1000U; + LSSmaster->state = CO_LSSmaster_STATE_WAITING; + LSSmaster->command = CO_LSSmaster_COMMAND_WAITING; + LSSmaster->timeoutTimer = 0; + CO_FLAG_CLEAR(LSSmaster->CANrxNew); + (void)memset(LSSmaster->CANrxData, 0, sizeof(LSSmaster->CANrxData)); +#if ((CO_CONFIG_LSS)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0 + LSSmaster->pFunctSignal = NULL; + LSSmaster->functSignalObject = NULL; +#endif + + /* configure LSS CAN Slave response message reception */ + ret = CO_CANrxBufferInit(CANdevRx, CANdevRxIdx, CANidLssSlave, 0x7FF, false, (void*)LSSmaster, + CO_LSSmaster_receive); + + /* configure LSS CAN Master message transmission */ + LSSmaster->CANdevTx = CANdevTx; + LSSmaster->TXbuff = CO_CANtxBufferInit(CANdevTx, CANdevTxIdx, CANidLssMaster, false, 8, false); + + if (LSSmaster->TXbuff == NULL) { + ret = CO_ERROR_ILLEGAL_ARGUMENT; + } + + return ret; +} + +void +CO_LSSmaster_changeTimeout(CO_LSSmaster_t* LSSmaster, uint16_t timeout_ms) { + if (LSSmaster != NULL) { + LSSmaster->timeout_us = (uint32_t)timeout_ms * 1000U; + } +} + +#if ((CO_CONFIG_LSS)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0 +void +CO_LSSmaster_initCallbackPre(CO_LSSmaster_t* LSSmaster, void* object, void (*pFunctSignal)(void* object)) { + if (LSSmaster != NULL) { + LSSmaster->functSignalObject = object; + LSSmaster->pFunctSignal = pFunctSignal; + } +} +#endif + +/* + * Helper function - initiate switch state + */ +static CO_LSSmaster_return_t +CO_LSSmaster_switchStateSelectInitiate(CO_LSSmaster_t* LSSmaster, CO_LSS_address_t* lssAddress) { + CO_LSSmaster_return_t ret; + + if (lssAddress != NULL) { + /* switch state select specific using LSS address */ + LSSmaster->state = CO_LSSmaster_STATE_CFG_SLECTIVE; + LSSmaster->command = CO_LSSmaster_COMMAND_SWITCH_STATE; + LSSmaster->timeoutTimer = 0; + + CO_FLAG_CLEAR(LSSmaster->CANrxNew); + (void)memset(&LSSmaster->TXbuff->data[6], 0, sizeof(LSSmaster->TXbuff->data) - 6U); + LSSmaster->TXbuff->data[0] = CO_LSS_SWITCH_STATE_SEL_VENDOR; + (void)CO_setUint32(&LSSmaster->TXbuff->data[1], CO_SWAP_32(lssAddress->identity.vendorID)); + (void)CO_CANsend(LSSmaster->CANdevTx, LSSmaster->TXbuff); + LSSmaster->TXbuff->data[0] = CO_LSS_SWITCH_STATE_SEL_PRODUCT; + (void)CO_setUint32(&LSSmaster->TXbuff->data[1], CO_SWAP_32(lssAddress->identity.productCode)); + (void)CO_CANsend(LSSmaster->CANdevTx, LSSmaster->TXbuff); + LSSmaster->TXbuff->data[0] = CO_LSS_SWITCH_STATE_SEL_REV; + (void)CO_setUint32(&LSSmaster->TXbuff->data[1], CO_SWAP_32(lssAddress->identity.revisionNumber)); + (void)CO_CANsend(LSSmaster->CANdevTx, LSSmaster->TXbuff); + LSSmaster->TXbuff->data[0] = CO_LSS_SWITCH_STATE_SEL_SERIAL; + (void)CO_setUint32(&LSSmaster->TXbuff->data[1], CO_SWAP_32(lssAddress->identity.serialNumber)); + (void)CO_CANsend(LSSmaster->CANdevTx, LSSmaster->TXbuff); + + ret = CO_LSSmaster_WAIT_SLAVE; + } else { + /* switch state global */ + LSSmaster->state = CO_LSSmaster_STATE_CFG_GLOBAL; + + CO_FLAG_CLEAR(LSSmaster->CANrxNew); + LSSmaster->TXbuff->data[0] = CO_LSS_SWITCH_STATE_GLOBAL; + LSSmaster->TXbuff->data[1] = CO_LSS_STATE_CONFIGURATION; + (void)memset(&LSSmaster->TXbuff->data[2], 0, sizeof(LSSmaster->TXbuff->data) - 2U); + (void)CO_CANsend(LSSmaster->CANdevTx, LSSmaster->TXbuff); + + /* This is non-confirmed service! */ + ret = CO_LSSmaster_OK; + } + return ret; +} + +/* + * Helper function - wait for confirmation + */ +static CO_LSSmaster_return_t +CO_LSSmaster_switchStateSelectWait(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us) { + CO_LSSmaster_return_t ret; + + if (CO_FLAG_READ(LSSmaster->CANrxNew)) { + uint8_t cs = LSSmaster->CANrxData[0]; + CO_FLAG_CLEAR(LSSmaster->CANrxNew); + + if (cs == CO_LSS_SWITCH_STATE_SEL) { + /* confirmation received */ + ret = CO_LSSmaster_OK; + } else { + ret = CO_LSSmaster_check_timeout(LSSmaster, timeDifference_us); + } + } else { + ret = CO_LSSmaster_check_timeout(LSSmaster, timeDifference_us); + } + + return ret; +} + +CO_LSSmaster_return_t +CO_LSSmaster_swStateSelect(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us, CO_LSS_address_t* lssAddress) { + CO_LSSmaster_return_t ret = CO_LSSmaster_INVALID_STATE; + + if (LSSmaster == NULL) { + return CO_LSSmaster_ILLEGAL_ARGUMENT; + } + + /* Initiate select */ + if ((LSSmaster->state == CO_LSSmaster_STATE_WAITING) && (LSSmaster->command == CO_LSSmaster_COMMAND_WAITING)) { + + ret = CO_LSSmaster_switchStateSelectInitiate(LSSmaster, lssAddress); + } + /* Wait for confirmation */ + else if (LSSmaster->command == CO_LSSmaster_COMMAND_SWITCH_STATE) { + ret = CO_LSSmaster_switchStateSelectWait(LSSmaster, timeDifference_us); + } else { /* MISRA C 2004 14.10 */ + } + + if ((ret != CO_LSSmaster_INVALID_STATE) && (ret != CO_LSSmaster_WAIT_SLAVE)) { + /* finished */ + LSSmaster->command = CO_LSSmaster_COMMAND_WAITING; + } + if (ret < CO_LSSmaster_OK) { + /* switching failed, go back to waiting */ + LSSmaster->state = CO_LSSmaster_STATE_WAITING; + LSSmaster->command = CO_LSSmaster_COMMAND_WAITING; + } + return ret; +} + +CO_LSSmaster_return_t +CO_LSSmaster_swStateDeselect(CO_LSSmaster_t* LSSmaster) { + CO_LSSmaster_return_t ret = CO_LSSmaster_INVALID_STATE; + + if (LSSmaster == NULL) { + return CO_LSSmaster_ILLEGAL_ARGUMENT; + } + + /* We can always send this command to get into a clean state on the network. + * If no slave is selected, this command is ignored. */ + LSSmaster->state = CO_LSSmaster_STATE_WAITING; + LSSmaster->command = CO_LSSmaster_COMMAND_WAITING; + LSSmaster->timeoutTimer = 0; + + /* switch state global */ + CO_FLAG_CLEAR(LSSmaster->CANrxNew); + LSSmaster->TXbuff->data[0] = CO_LSS_SWITCH_STATE_GLOBAL; + LSSmaster->TXbuff->data[1] = CO_LSS_STATE_WAITING; + (void)memset(&LSSmaster->TXbuff->data[2], 0, sizeof(LSSmaster->TXbuff->data) - 2U); + (void)CO_CANsend(LSSmaster->CANdevTx, LSSmaster->TXbuff); + + /* This is non-confirmed service! */ + ret = CO_LSSmaster_OK; + + return ret; +} + +/* + * Helper function - wait for confirmation, check for returned error code + * + * This uses the nature of the configure confirmation message design: + * - byte 0 -> cs + * - byte 1 -> Error Code, where + * - 0 = OK + * - 1 .. FE = Values defined by CiA. All currently defined values are slave rejects. + * No further distinction on why the slave did reject the request. + * - FF = Manufacturer Error Code in byte 2 + * - byte 2 -> Manufacturer Error, currently not used + * + * enums for the errorCode are + * - CO_LSS_CFG_NODE_ID_status + * - CO_LSS_CFG_BIT_TIMING + * - CO_LSS_CFG_STORE_status + */ +static CO_LSSmaster_return_t +CO_LSSmaster_configureCheckWait(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us, uint8_t csWait) { + CO_LSSmaster_return_t ret; + + if (CO_FLAG_READ(LSSmaster->CANrxNew)) { + uint8_t cs = LSSmaster->CANrxData[0]; + uint8_t errorCode = LSSmaster->CANrxData[1]; + CO_FLAG_CLEAR(LSSmaster->CANrxNew); + + if (cs == csWait) { + if (errorCode == 0U) { + ret = CO_LSSmaster_OK; + } else if (errorCode == 0xFFU) { + ret = CO_LSSmaster_OK_MANUFACTURER; + } else { + ret = CO_LSSmaster_OK_ILLEGAL_ARGUMENT; + } + } else { + ret = CO_LSSmaster_check_timeout(LSSmaster, timeDifference_us); + } + } else { + ret = CO_LSSmaster_check_timeout(LSSmaster, timeDifference_us); + } + + if ((ret != CO_LSSmaster_INVALID_STATE) && (ret != CO_LSSmaster_WAIT_SLAVE)) { + /* finished */ + LSSmaster->command = CO_LSSmaster_COMMAND_WAITING; + } + return ret; +} + +CO_LSSmaster_return_t +CO_LSSmaster_configureBitTiming(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us, uint16_t bit) { + CO_LSSmaster_return_t ret = CO_LSSmaster_INVALID_STATE; + uint8_t bitTiming; + + if (LSSmaster == NULL) { + return CO_LSSmaster_ILLEGAL_ARGUMENT; + } + + switch (bit) { + case 1000: bitTiming = CO_LSS_BIT_TIMING_1000; break; + case 800: bitTiming = CO_LSS_BIT_TIMING_800; break; + case 500: bitTiming = CO_LSS_BIT_TIMING_500; break; + case 250: bitTiming = CO_LSS_BIT_TIMING_250; break; + case 125: bitTiming = CO_LSS_BIT_TIMING_125; break; + case 50: bitTiming = CO_LSS_BIT_TIMING_50; break; + case 20: bitTiming = CO_LSS_BIT_TIMING_20; break; + case 10: bitTiming = CO_LSS_BIT_TIMING_10; break; + case 0: bitTiming = CO_LSS_BIT_TIMING_AUTO; break; + default: return CO_LSSmaster_ILLEGAL_ARGUMENT; break; + } + + /* Initiate config bit */ + if ((LSSmaster->state == CO_LSSmaster_STATE_CFG_SLECTIVE) && (LSSmaster->command == CO_LSSmaster_COMMAND_WAITING)) { + + LSSmaster->command = CO_LSSmaster_COMMAND_CFG_BIT_TIMING; + LSSmaster->timeoutTimer = 0; + + CO_FLAG_CLEAR(LSSmaster->CANrxNew); + LSSmaster->TXbuff->data[0] = CO_LSS_CFG_BIT_TIMING; + LSSmaster->TXbuff->data[1] = 0; + LSSmaster->TXbuff->data[2] = bitTiming; + (void)memset(&LSSmaster->TXbuff->data[3], 0, sizeof(LSSmaster->TXbuff->data) - 3U); + (void)CO_CANsend(LSSmaster->CANdevTx, LSSmaster->TXbuff); + + ret = CO_LSSmaster_WAIT_SLAVE; + } + /* Wait for confirmation */ + else if (LSSmaster->command == CO_LSSmaster_COMMAND_CFG_BIT_TIMING) { + + ret = CO_LSSmaster_configureCheckWait(LSSmaster, timeDifference_us, CO_LSS_CFG_BIT_TIMING); + } else { /* MISRA C 2004 14.10 */ + } + + if ((ret != CO_LSSmaster_INVALID_STATE) && (ret != CO_LSSmaster_WAIT_SLAVE)) { + /* finished */ + LSSmaster->command = CO_LSSmaster_COMMAND_WAITING; + } + return ret; +} + +CO_LSSmaster_return_t +CO_LSSmaster_configureNodeId(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us, uint8_t nodeId) { + CO_LSSmaster_return_t ret = CO_LSSmaster_INVALID_STATE; + + if ((LSSmaster == NULL) || !CO_LSS_NODE_ID_VALID(nodeId)) { + return CO_LSSmaster_ILLEGAL_ARGUMENT; + } + + /* Initiate config node ID */ + if (((LSSmaster->state == CO_LSSmaster_STATE_CFG_SLECTIVE) || + /* Let un-config node ID also be run in global mode for unconfiguring all nodes */ + ((LSSmaster->state == CO_LSSmaster_STATE_CFG_GLOBAL) && (nodeId == CO_LSS_NODE_ID_ASSIGNMENT))) + && (LSSmaster->command == CO_LSSmaster_COMMAND_WAITING)) { + + LSSmaster->command = CO_LSSmaster_COMMAND_CFG_NODE_ID; + LSSmaster->timeoutTimer = 0; + + CO_FLAG_CLEAR(LSSmaster->CANrxNew); + LSSmaster->TXbuff->data[0] = CO_LSS_CFG_NODE_ID; + LSSmaster->TXbuff->data[1] = nodeId; + (void)memset(&LSSmaster->TXbuff->data[2], 0, sizeof(LSSmaster->TXbuff->data) - 2U); + (void)CO_CANsend(LSSmaster->CANdevTx, LSSmaster->TXbuff); + + ret = CO_LSSmaster_WAIT_SLAVE; + } + /* Wait for confirmation */ + else if (LSSmaster->command == CO_LSSmaster_COMMAND_CFG_NODE_ID) { + + ret = CO_LSSmaster_configureCheckWait(LSSmaster, timeDifference_us, CO_LSS_CFG_NODE_ID); + } else { /* MISRA C 2004 14.10 */ + } + + if ((ret != CO_LSSmaster_INVALID_STATE) && (ret != CO_LSSmaster_WAIT_SLAVE)) { + /* finished */ + LSSmaster->command = CO_LSSmaster_COMMAND_WAITING; + } + return ret; +} + +CO_LSSmaster_return_t +CO_LSSmaster_configureStore(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us) { + CO_LSSmaster_return_t ret = CO_LSSmaster_INVALID_STATE; + + if (LSSmaster == NULL) { + return CO_LSSmaster_ILLEGAL_ARGUMENT; + } + + /* Initiate config store */ + if ((LSSmaster->state == CO_LSSmaster_STATE_CFG_SLECTIVE) && (LSSmaster->command == CO_LSSmaster_COMMAND_WAITING)) { + + LSSmaster->command = CO_LSSmaster_COMMAND_CFG_STORE; + LSSmaster->timeoutTimer = 0; + + CO_FLAG_CLEAR(LSSmaster->CANrxNew); + LSSmaster->TXbuff->data[0] = CO_LSS_CFG_STORE; + (void)memset(&LSSmaster->TXbuff->data[1], 0, sizeof(LSSmaster->TXbuff->data) - 1U); + (void)CO_CANsend(LSSmaster->CANdevTx, LSSmaster->TXbuff); + + ret = CO_LSSmaster_WAIT_SLAVE; + } + /* Wait for confirmation */ + else if (LSSmaster->command == CO_LSSmaster_COMMAND_CFG_STORE) { + + ret = CO_LSSmaster_configureCheckWait(LSSmaster, timeDifference_us, CO_LSS_CFG_STORE); + } else { /* MISRA C 2004 14.10 */ + } + + if ((ret != CO_LSSmaster_INVALID_STATE) && (ret != CO_LSSmaster_WAIT_SLAVE)) { + /* finished */ + LSSmaster->command = CO_LSSmaster_COMMAND_WAITING; + } + return ret; +} + +CO_LSSmaster_return_t +CO_LSSmaster_ActivateBit(CO_LSSmaster_t* LSSmaster, uint16_t switchDelay_ms) { + CO_LSSmaster_return_t ret = CO_LSSmaster_INVALID_STATE; + + if (LSSmaster == NULL) { + return CO_LSSmaster_ILLEGAL_ARGUMENT; + } + + /* for activating bit timing, we need to have all slaves set to config + * state. This check makes it a bit harder to shoot ourselves in the foot */ + if ((LSSmaster->state == CO_LSSmaster_STATE_CFG_GLOBAL) && (LSSmaster->command == CO_LSSmaster_COMMAND_WAITING)) { + + CO_FLAG_CLEAR(LSSmaster->CANrxNew); + LSSmaster->TXbuff->data[0] = CO_LSS_CFG_ACTIVATE_BIT_TIMING; + (void)CO_setUint16(&LSSmaster->TXbuff->data[1], CO_SWAP_16(switchDelay_ms)); + (void)memset(&LSSmaster->TXbuff->data[3], 0, sizeof(LSSmaster->TXbuff->data) - 3U); + (void)CO_CANsend(LSSmaster->CANdevTx, LSSmaster->TXbuff); + + /* This is non-confirmed service! */ + ret = CO_LSSmaster_OK; + } + + return ret; +} + +/* + * Helper function - send request + */ +static CO_LSSmaster_return_t +CO_LSSmaster_inquireInitiate(CO_LSSmaster_t* LSSmaster, uint8_t cs) { + CO_FLAG_CLEAR(LSSmaster->CANrxNew); + LSSmaster->TXbuff->data[0] = cs; + (void)memset(&LSSmaster->TXbuff->data[1], 0, sizeof(LSSmaster->TXbuff->data) - 1U); + (void)CO_CANsend(LSSmaster->CANdevTx, LSSmaster->TXbuff); + + return CO_LSSmaster_WAIT_SLAVE; +} + +/* + * Helper function - wait for confirmation + */ +static CO_LSSmaster_return_t +CO_LSSmaster_inquireCheckWait(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us, uint8_t csWait, uint32_t* value) { + CO_LSSmaster_return_t ret; + + if (CO_FLAG_READ(LSSmaster->CANrxNew)) { + uint8_t cs = LSSmaster->CANrxData[0]; + *value = CO_getUint32(&LSSmaster->CANrxData[1]); + *value = CO_SWAP_32(*value); + CO_FLAG_CLEAR(LSSmaster->CANrxNew); + + if (cs == csWait) { + ret = CO_LSSmaster_OK; + } else { + ret = CO_LSSmaster_check_timeout(LSSmaster, timeDifference_us); + } + } else { + ret = CO_LSSmaster_check_timeout(LSSmaster, timeDifference_us); + } + + return ret; +} + +CO_LSSmaster_return_t +CO_LSSmaster_InquireLssAddress(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us, CO_LSS_address_t* lssAddress) { + CO_LSSmaster_return_t ret = CO_LSSmaster_INVALID_STATE; + uint8_t next = CO_LSSmaster_COMMAND_WAITING; + + if ((LSSmaster == NULL) || (lssAddress == NULL)) { + return CO_LSSmaster_ILLEGAL_ARGUMENT; + } + + /* Check for reply */ + if (LSSmaster->command == CO_LSSmaster_COMMAND_INQUIRE_VENDOR) { + + ret = CO_LSSmaster_inquireCheckWait(LSSmaster, timeDifference_us, CO_LSS_INQUIRE_VENDOR, + &lssAddress->identity.vendorID); + if (ret == CO_LSSmaster_OK) { + /* Start next request */ + next = CO_LSSmaster_COMMAND_INQUIRE_PRODUCT; + ret = CO_LSSmaster_WAIT_SLAVE; + } + } else if (LSSmaster->command == CO_LSSmaster_COMMAND_INQUIRE_PRODUCT) { + + ret = CO_LSSmaster_inquireCheckWait(LSSmaster, timeDifference_us, CO_LSS_INQUIRE_PRODUCT, + &lssAddress->identity.productCode); + if (ret == CO_LSSmaster_OK) { + /* Start next request */ + next = CO_LSSmaster_COMMAND_INQUIRE_REV; + ret = CO_LSSmaster_WAIT_SLAVE; + } + } else if (LSSmaster->command == CO_LSSmaster_COMMAND_INQUIRE_REV) { + + ret = CO_LSSmaster_inquireCheckWait(LSSmaster, timeDifference_us, CO_LSS_INQUIRE_REV, + &lssAddress->identity.revisionNumber); + if (ret == CO_LSSmaster_OK) { + /* Start next request */ + next = CO_LSSmaster_COMMAND_INQUIRE_SERIAL; + ret = CO_LSSmaster_WAIT_SLAVE; + } + } else if (LSSmaster->command == CO_LSSmaster_COMMAND_INQUIRE_SERIAL) { + + ret = CO_LSSmaster_inquireCheckWait(LSSmaster, timeDifference_us, CO_LSS_INQUIRE_SERIAL, + &lssAddress->identity.serialNumber); + } else { /* MISRA C 2004 14.10 */ + } + + /* Check for next request */ + if ((LSSmaster->state == CO_LSSmaster_STATE_CFG_SLECTIVE) || (LSSmaster->state == CO_LSSmaster_STATE_CFG_GLOBAL)) { + if (LSSmaster->command == CO_LSSmaster_COMMAND_WAITING) { + + LSSmaster->command = CO_LSSmaster_COMMAND_INQUIRE_VENDOR; + LSSmaster->timeoutTimer = 0; + + ret = CO_LSSmaster_inquireInitiate(LSSmaster, CO_LSS_INQUIRE_VENDOR); + } else if (next == CO_LSSmaster_COMMAND_INQUIRE_PRODUCT) { + LSSmaster->command = CO_LSSmaster_COMMAND_INQUIRE_PRODUCT; + LSSmaster->timeoutTimer = 0; + + ret = CO_LSSmaster_inquireInitiate(LSSmaster, CO_LSS_INQUIRE_PRODUCT); + } else if (next == CO_LSSmaster_COMMAND_INQUIRE_REV) { + LSSmaster->command = CO_LSSmaster_COMMAND_INQUIRE_REV; + LSSmaster->timeoutTimer = 0; + + ret = CO_LSSmaster_inquireInitiate(LSSmaster, CO_LSS_INQUIRE_REV); + } else if (next == CO_LSSmaster_COMMAND_INQUIRE_SERIAL) { + LSSmaster->command = CO_LSSmaster_COMMAND_INQUIRE_SERIAL; + LSSmaster->timeoutTimer = 0; + + ret = CO_LSSmaster_inquireInitiate(LSSmaster, CO_LSS_INQUIRE_SERIAL); + } else { /* MISRA C 2004 14.10 */ + } + } + + if ((ret != CO_LSSmaster_INVALID_STATE) && (ret != CO_LSSmaster_WAIT_SLAVE)) { + /* finished */ + LSSmaster->command = CO_LSSmaster_COMMAND_WAITING; + } + return ret; +} + +CO_LSSmaster_return_t +CO_LSSmaster_Inquire(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us, uint8_t lssInquireCs, uint32_t* value) { + CO_LSSmaster_return_t ret = CO_LSSmaster_INVALID_STATE; + + if ((LSSmaster == NULL) || (value == NULL)) { + return CO_LSSmaster_ILLEGAL_ARGUMENT; + } + + /* send request */ + if (((LSSmaster->state == CO_LSSmaster_STATE_CFG_SLECTIVE) || (LSSmaster->state == CO_LSSmaster_STATE_CFG_GLOBAL)) + && (LSSmaster->command == CO_LSSmaster_COMMAND_WAITING)) { + + LSSmaster->command = CO_LSSmaster_COMMAND_INQUIRE; + LSSmaster->timeoutTimer = 0; + + ret = CO_LSSmaster_inquireInitiate(LSSmaster, lssInquireCs); + } + /* Check for reply */ + else if (LSSmaster->command == CO_LSSmaster_COMMAND_INQUIRE) { + ret = CO_LSSmaster_inquireCheckWait(LSSmaster, timeDifference_us, lssInquireCs, value); + } else { /* MISRA C 2004 14.10 */ + } + + if (ret != CO_LSSmaster_WAIT_SLAVE) { + LSSmaster->command = CO_LSSmaster_COMMAND_WAITING; + } + return ret; +} + +/* + * Helper function - send request + */ +static void +CO_LSSmaster_FsSendMsg(CO_LSSmaster_t* LSSmaster, uint32_t idNumber, uint8_t bitCheck, uint8_t lssSub, + uint8_t lssNext) { + LSSmaster->timeoutTimer = 0; + + CO_FLAG_CLEAR(LSSmaster->CANrxNew); + LSSmaster->TXbuff->data[0] = CO_LSS_IDENT_FASTSCAN; + (void)CO_setUint32(&LSSmaster->TXbuff->data[1], CO_SWAP_32(idNumber)); + LSSmaster->TXbuff->data[5] = bitCheck; + LSSmaster->TXbuff->data[6] = lssSub; + LSSmaster->TXbuff->data[7] = lssNext; + + (void)CO_CANsend(LSSmaster->CANdevTx, LSSmaster->TXbuff); +} + +/* + * Helper function - wait for confirmation + */ +static CO_LSSmaster_return_t +CO_LSSmaster_FsCheckWait(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us) { + CO_LSSmaster_return_t ret; + + ret = CO_LSSmaster_check_timeout(LSSmaster, timeDifference_us); + if (ret == CO_LSSmaster_TIMEOUT) { + ret = CO_LSSmaster_SCAN_NOACK; + + if (CO_FLAG_READ(LSSmaster->CANrxNew)) { + uint8_t cs = LSSmaster->CANrxData[0]; + CO_FLAG_CLEAR(LSSmaster->CANrxNew); + + if (cs == CO_LSS_IDENT_SLAVE) { + /* At least one node is waiting for fastscan */ + ret = CO_LSSmaster_SCAN_FINISHED; + } + } + } + + return ret; +} + +/* + * Helper function - initiate scan for 32 bit part of LSS address + */ +static CO_LSSmaster_return_t +CO_LSSmaster_FsScanInitiate(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us, CO_LSSmaster_scantype_t scan, + uint8_t lssSub) { + (void)timeDifference_us; /* unused */ + + LSSmaster->fsLssSub = lssSub; + LSSmaster->fsIdNumber = 0; + + switch (scan) { + case CO_LSSmaster_FS_SCAN: break; + case CO_LSSmaster_FS_MATCH: + /* No scanning requested */ + return CO_LSSmaster_SCAN_FINISHED; + break; + case CO_LSSmaster_FS_SKIP: + default: return CO_LSSmaster_SCAN_FAILED; break; + } + + LSSmaster->fsBitChecked = CO_LSS_FASTSCAN_BIT31; + + /* trigger scan procedure by sending first message */ + CO_LSSmaster_FsSendMsg(LSSmaster, LSSmaster->fsIdNumber, LSSmaster->fsBitChecked, LSSmaster->fsLssSub, + LSSmaster->fsLssSub); + + return CO_LSSmaster_WAIT_SLAVE; +} + +/* + * Helper function - scan for 32 bits of LSS address, one by one + */ +static CO_LSSmaster_return_t +CO_LSSmaster_FsScanWait(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us, CO_LSSmaster_scantype_t scan) { + CO_LSSmaster_return_t ret; + + switch (scan) { + case CO_LSSmaster_FS_SCAN: break; + case CO_LSSmaster_FS_MATCH: + /* No scanning requested */ + return CO_LSSmaster_SCAN_FINISHED; + break; + case CO_LSSmaster_FS_SKIP: + default: return CO_LSSmaster_SCAN_FAILED; break; + } + + ret = CO_LSSmaster_check_timeout(LSSmaster, timeDifference_us); + if (ret == CO_LSSmaster_TIMEOUT) { + + ret = CO_LSSmaster_WAIT_SLAVE; + + if (CO_FLAG_READ(LSSmaster->CANrxNew)) { + uint8_t cs = LSSmaster->CANrxData[0]; + CO_FLAG_CLEAR(LSSmaster->CANrxNew); + + if (cs != CO_LSS_IDENT_SLAVE) { + /* wrong response received. Can not continue */ + return CO_LSSmaster_SCAN_FAILED; + } + } else { + /* no response received, assumption is wrong */ + LSSmaster->fsIdNumber |= 1UL << LSSmaster->fsBitChecked; + } + + if (LSSmaster->fsBitChecked == CO_LSS_FASTSCAN_BIT0) { + /* Scanning cycle is finished, we now have 32 bit address data */ + ret = CO_LSSmaster_SCAN_FINISHED; + } else { + LSSmaster->fsBitChecked--; + + CO_LSSmaster_FsSendMsg(LSSmaster, LSSmaster->fsIdNumber, LSSmaster->fsBitChecked, LSSmaster->fsLssSub, + LSSmaster->fsLssSub); + } + } + + return ret; +} + +/* + * Helper function - initiate check for 32 bit part of LSS address + */ +static CO_LSSmaster_return_t +CO_LSSmaster_FsVerifyInitiate(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us, CO_LSSmaster_scantype_t scan, + uint32_t idNumberCheck, uint8_t lssNext) { + (void)timeDifference_us; /* unused */ + + switch (scan) { + case CO_LSSmaster_FS_SCAN: + /* ID obtained by scan */ + break; + case CO_LSSmaster_FS_MATCH: + /* ID given by user */ + LSSmaster->fsIdNumber = idNumberCheck; + break; + case CO_LSSmaster_FS_SKIP: + default: return CO_LSSmaster_SCAN_FAILED; break; + } + + LSSmaster->fsBitChecked = CO_LSS_FASTSCAN_BIT0; + + /* send request */ + CO_LSSmaster_FsSendMsg(LSSmaster, LSSmaster->fsIdNumber, LSSmaster->fsBitChecked, LSSmaster->fsLssSub, lssNext); + + return CO_LSSmaster_WAIT_SLAVE; +} + +/* + * Helper function - verify 32 bit LSS address, request node(s) to switch their state machine to the next state + */ +static CO_LSSmaster_return_t +CO_LSSmaster_FsVerifyWait(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us, CO_LSSmaster_scantype_t scan, + uint32_t* idNumberRet) { + CO_LSSmaster_return_t ret; + + if (scan == CO_LSSmaster_FS_SKIP) { + return CO_LSSmaster_SCAN_FAILED; + } + + ret = CO_LSSmaster_check_timeout(LSSmaster, timeDifference_us); + if (ret == CO_LSSmaster_TIMEOUT) { + + *idNumberRet = 0; + ret = CO_LSSmaster_SCAN_NOACK; + + if (CO_FLAG_READ(LSSmaster->CANrxNew)) { + uint8_t cs = LSSmaster->CANrxData[0]; + CO_FLAG_CLEAR(LSSmaster->CANrxNew); + + if (cs == CO_LSS_IDENT_SLAVE) { + *idNumberRet = LSSmaster->fsIdNumber; + ret = CO_LSSmaster_SCAN_FINISHED; + } else { + ret = CO_LSSmaster_SCAN_FAILED; + } + } + } + + return ret; +} + +/* + * Helper function - check which 32 bit to scan for next, if any + */ +static uint8_t +CO_LSSmaster_FsSearchNext(CO_LSSmaster_t* LSSmaster, const CO_LSSmaster_fastscan_t* fastscan) { + uint8_t i; + + /* we search for the next LSS address part to scan for, beginning with the + * one after the current one. If there is none remaining, scanning is finished */ + for (i = LSSmaster->fsLssSub + 1U; i <= CO_LSS_FASTSCAN_SERIAL; i++) { + if (fastscan->scan[i] != CO_LSSmaster_FS_SKIP) { + return i; + } + } + /* node selection is triggered by switching node state machine back to initial state */ + return CO_LSS_FASTSCAN_VENDOR_ID; +} + +CO_LSSmaster_return_t +CO_LSSmaster_IdentifyFastscan(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us, + CO_LSSmaster_fastscan_t* fastscan) { + uint8_t i; + uint8_t count; + CO_LSSmaster_return_t ret = CO_LSSmaster_INVALID_STATE; + uint8_t next; + + /* parameter validation */ + if ((LSSmaster == NULL) || (fastscan == NULL)) { + return CO_LSSmaster_ILLEGAL_ARGUMENT; + } + if (fastscan->scan[0] == CO_LSSmaster_FS_SKIP) { + /* vendor ID scan cannot be skipped */ + return CO_LSSmaster_ILLEGAL_ARGUMENT; + } + count = 0; + for (i = 0; i < (sizeof(fastscan->scan) / sizeof(fastscan->scan[0])); i++) { + if (fastscan->scan[i] == CO_LSSmaster_FS_SKIP) { + count++; + } + if (count > 2U) { + /* Node selection needs the Vendor ID and at least one other value */ + return CO_LSSmaster_ILLEGAL_ARGUMENT; + } + } + + /* state machine validation */ + if ((LSSmaster->state != CO_LSSmaster_STATE_WAITING) + || ((LSSmaster->command != CO_LSSmaster_COMMAND_WAITING) + && (LSSmaster->command != CO_LSSmaster_COMMAND_IDENTIFY_FASTSCAN))) { + /* state machine not ready, other command is already processed */ + return CO_LSSmaster_INVALID_STATE; + } + + /* evaluate LSS state machine */ + if (LSSmaster->command == CO_LSSmaster_COMMAND_WAITING) { + /* start fastscan */ + LSSmaster->command = CO_LSSmaster_COMMAND_IDENTIFY_FASTSCAN; + + /* check if any nodes are waiting, if yes fastscan is reset */ + LSSmaster->fsState = CO_LSSmaster_FS_STATE_CHECK; + CO_LSSmaster_FsSendMsg(LSSmaster, 0, CO_LSS_FASTSCAN_CONFIRM, 0, 0); + + return CO_LSSmaster_WAIT_SLAVE; + } else { + /* continue with evaluating fastscan state machine */ + } + + /* + * evaluate fastscan state machine. The state machine is evaluated as following + * - check for non-configured nodes + * - scan for vendor ID + * - verify vendor ID, switch node state + * - scan for product code + * - verify product code, switch node state + * - scan for revision number + * - verify revision number, switch node state + * - scan for serial number + * - verify serial number, switch node to LSS configuration mode + * Certain steps can be skipped as mentioned in the function description. If one step is + * not ack'ed by a node, the scanning process is terminated and the correspondign error is returned. + */ + switch (LSSmaster->fsState) { + case CO_LSSmaster_FS_STATE_CHECK: + ret = CO_LSSmaster_FsCheckWait(LSSmaster, timeDifference_us); + if (ret == CO_LSSmaster_SCAN_FINISHED) { + (void)memset(&fastscan->found, 0, sizeof(fastscan->found)); + + /* start scanning procedure by triggering vendor ID scan */ + (void)CO_LSSmaster_FsScanInitiate(LSSmaster, timeDifference_us, + fastscan->scan[CO_LSS_FASTSCAN_VENDOR_ID], CO_LSS_FASTSCAN_VENDOR_ID); + ret = CO_LSSmaster_WAIT_SLAVE; + + LSSmaster->fsState = CO_LSSmaster_FS_STATE_SCAN; + } + break; + case CO_LSSmaster_FS_STATE_SCAN: + ret = CO_LSSmaster_FsScanWait(LSSmaster, timeDifference_us, fastscan->scan[LSSmaster->fsLssSub]); + if (ret == CO_LSSmaster_SCAN_FINISHED) { + /* scanning finished, initiate verifcation. The verification message also contains + * the node state machine "switch to next state" request */ + next = CO_LSSmaster_FsSearchNext(LSSmaster, fastscan); + ret = CO_LSSmaster_FsVerifyInitiate(LSSmaster, timeDifference_us, fastscan->scan[LSSmaster->fsLssSub], + fastscan->match.addr[LSSmaster->fsLssSub], next); + + LSSmaster->fsState = CO_LSSmaster_FS_STATE_VERIFY; + } + break; + case CO_LSSmaster_FS_STATE_VERIFY: + ret = CO_LSSmaster_FsVerifyWait(LSSmaster, timeDifference_us, fastscan->scan[LSSmaster->fsLssSub], + &fastscan->found.addr[LSSmaster->fsLssSub]); + if (ret == CO_LSSmaster_SCAN_FINISHED) { + /* verification successful: + * - assumed node id is correct + * - node state machine has switched to the requested state, mirror that in the local copy */ + next = CO_LSSmaster_FsSearchNext(LSSmaster, fastscan); + if (next == CO_LSS_FASTSCAN_VENDOR_ID) { + /* fastscan finished, one node is now in LSS configuration mode */ + LSSmaster->state = CO_LSSmaster_STATE_CFG_SLECTIVE; + } else { + /* initiate scan for next part of LSS address */ + ret = CO_LSSmaster_FsScanInitiate(LSSmaster, timeDifference_us, fastscan->scan[next], next); + if (ret == CO_LSSmaster_SCAN_FINISHED) { + /* Scanning is not requested. Initiate verification step in next function call */ + ret = CO_LSSmaster_WAIT_SLAVE; + } + + LSSmaster->fsState = CO_LSSmaster_FS_STATE_SCAN; + } + } + break; + default: + /* none */ + break; + } + + if (ret != CO_LSSmaster_WAIT_SLAVE) { + /* finished */ + LSSmaster->command = CO_LSSmaster_COMMAND_WAITING; + } + return ret; +} + +#endif /* (CO_CONFIG_LSS) & CO_CONFIG_LSS_MASTER */ diff --git a/305/CO_LSSmaster.h b/305/CO_LSSmaster.h new file mode 100644 index 00000000..05cd2bf8 --- /dev/null +++ b/305/CO_LSSmaster.h @@ -0,0 +1,389 @@ +/** + * CANopen Layer Setting Service - master protocol. + * + * @file CO_LSSmaster.h + * @ingroup CO_LSS + * @author Martin Wagner + * @copyright 2017 - 2020 Neuberger Gebaeudeautomation GmbH + * + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#ifndef CO_LSSmaster_H +#define CO_LSSmaster_H + +#include "305/CO_LSS.h" + +#if (((CO_CONFIG_LSS)&CO_CONFIG_LSS_MASTER) != 0) || defined CO_DOXYGEN + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup CO_LSSmaster LSS Master + * CANopen Layer Setting Service - master protocol. + * + * @ingroup CO_CANopen_305 + * @{ + * The client/master can use the following services + * - node selection via LSS address + * - node selection via LSS fastscan + * - Inquire LSS address of currently selected node + * - Inquire node ID + * - Configure bit timing + * - Configure node ID + * - Activate bit timing parameters + * - Store configuration + * + * The LSS master is initalized during the CANopenNode initialization process. Except for enabling the LSS master in the + * configurator, no further run-time configuration is needed for basic operation. The LSS master does basic checking of + * commands and command sequence. + * + * ###Usage + * Usage of the CANopen LSS master is demonstrated in file 309/CO_gateway_ascii.c + * + * It essentially is always as following: + * - select node(s) + * - call master command(s) + * - evaluate return value + * - deselect nodes + * + * All commands need to be run cyclically, e.g. like this + * \code{.c} +interval = 0; +do { + ret = CO_LSSmaster_InquireNodeId(LSSmaster, interval, &outval); + + interval = 1; + ms sleep(interval); +} while (ret == CO_LSSmaster_WAIT_SLAVE); + * \endcode + * + * A more advanced implementation can make use of the callback function to shorten waiting times. + */ + +/** + * Return values of LSS master functions. + */ +typedef enum { + CO_LSSmaster_SCAN_FINISHED = 2, /**< Scanning finished successful */ + CO_LSSmaster_WAIT_SLAVE = 1, /**< No response arrived from slave yet */ + CO_LSSmaster_OK = 0, /**< Success, end of communication */ + CO_LSSmaster_TIMEOUT = -1, /**< No reply received */ + CO_LSSmaster_ILLEGAL_ARGUMENT = -2, /**< Invalid argument */ + CO_LSSmaster_INVALID_STATE = -3, /**< State machine not ready or already processing a request */ + CO_LSSmaster_SCAN_NOACK = -4, /**< No node found that matches scan request */ + CO_LSSmaster_SCAN_FAILED = -5, /**< An error occurred while scanning. Try again */ + CO_LSSmaster_OK_ILLEGAL_ARGUMENT = -101, /**< LSS success, node rejected argument because of non-supported value */ + CO_LSSmaster_OK_MANUFACTURER = -102, /**< LSS success, node rejected argument with manufacturer error code */ +} CO_LSSmaster_return_t; + +/** + * LSS master object. + */ +typedef struct { + uint32_t timeout_us; /**< LSS response timeout in us */ + uint8_t state; /**< Node is currently selected */ + uint8_t command; /**< Active command */ + uint32_t timeoutTimer; /**< Timeout timer for LSS communication */ + uint8_t fsState; /**< Current state of fastscan master state machine */ + uint8_t fsLssSub; /**< Current state of node state machine */ + uint8_t fsBitChecked; /**< Current scan bit position */ + uint32_t fsIdNumber; /**< Current scan result */ + volatile void* CANrxNew; /**< Indication if new LSS message is received from CAN bus. It needs to be cleared when + received message is completely processed. */ + uint8_t CANrxData[8]; /**< 8 data bytes of the received message */ +#if (((CO_CONFIG_LSS)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0) || defined CO_DOXYGEN + void (*pFunctSignal)(void* object); /**< From CO_LSSmaster_initCallbackPre() or NULL */ + void* functSignalObject; /**< Pointer to object */ +#endif + CO_CANmodule_t* CANdevTx; /**< From CO_LSSmaster_init() */ + CO_CANtx_t* TXbuff; /**< CAN transmit buffer */ +} CO_LSSmaster_t; + +/** + * Default timeout for LSS slave in ms. This is the same as for SDO. For more info about LSS timeout see + * #CO_LSSmaster_changeTimeout() + */ +#ifndef CO_LSSmaster_DEFAULT_TIMEOUT +#define CO_LSSmaster_DEFAULT_TIMEOUT 1000U /* ms */ +#endif + +/** + * Initialize LSS object. + * + * Function must be called in the communication reset section. + * + * @param LSSmaster This object will be initialized. + * @param timeout_ms slave response timeout in ms, for more detail see #CO_LSSmaster_changeTimeout() + * @param CANdevRx CAN device for LSS master reception. + * @param CANdevRxIdx Index of receive buffer in the above CAN device. + * @param CANidLssSlave COB ID for reception. + * @param CANdevTx CAN device for LSS master transmission. + * @param CANdevTxIdx Index of transmit buffer in the above CAN device. + * @param CANidLssMaster COB ID for transmission. + * @return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT. + */ +CO_ReturnError_t CO_LSSmaster_init(CO_LSSmaster_t* LSSmaster, uint16_t timeout_ms, CO_CANmodule_t* CANdevRx, + uint16_t CANdevRxIdx, uint16_t CANidLssSlave, CO_CANmodule_t* CANdevTx, + uint16_t CANdevTxIdx, uint16_t CANidLssMaster); + +/** + * Change LSS master timeout + * + * On LSS, a "negative ack" is signaled by the slave not answering. Because of that, a low timeout value can + * significantly increase protocol speed in some cases (e.g. fastscan). However, as soon as there is activity on the + * bus, LSS messages can be delayed because of their low CAN network priority (see @ref CO_Default_CAN_ID_t). + * + * @remark Be aware that a "late response" will seriously mess up LSS, so this value must be selected "as high as + * necessary and as low as possible". CiA does neither specify nor recommend a value. + * + * @remark This timeout is per-transfer. If a command internally needs multiple transfers to complete, this timeout is + * applied on each transfer. + * + * @param LSSmaster This object. + * @param timeout_ms timeout value in ms + */ +void CO_LSSmaster_changeTimeout(CO_LSSmaster_t* LSSmaster, uint16_t timeout_ms); + +#if (((CO_CONFIG_LSS)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0) || defined CO_DOXYGEN +/** + * Initialize LSSmasterRx callback function. + * + * Function initializes optional callback function, which should immediately start further LSS processing. Callback is + * called after LSS message is received from the CAN bus. It should signal the RTOS to resume corresponding task. + * + * @param LSSmaster This object. + * @param object Pointer to object, which will be passed to pFunctSignal(). Can be NULL + * @param pFunctSignal Pointer to the callback function. Not called if NULL. + */ +void CO_LSSmaster_initCallbackPre(CO_LSSmaster_t* LSSmaster, void* object, void (*pFunctSignal)(void* object)); +#endif + +/** + * Request LSS switch state select + * + * This function can select one specific or all nodes. + * + * Function must be called cyclically until it returns != #CO_LSSmaster_WAIT_SLAVE Function is non-blocking. + * + * @remark Only one selection can be active at any time. + * + * @param LSSmaster This object. + * @param timeDifference_us Time difference from previous function call in [microseconds]. Zero when request is started. + * @param lssAddress LSS target address. If NULL, all nodes are selected + * @return #CO_LSSmaster_ILLEGAL_ARGUMENT, #CO_LSSmaster_INVALID_STATE, #CO_LSSmaster_WAIT_SLAVE, #CO_LSSmaster_OK, + * #CO_LSSmaster_TIMEOUT + */ +CO_LSSmaster_return_t CO_LSSmaster_swStateSelect(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us, + CO_LSS_address_t* lssAddress); + +/** + * Request LSS switch state deselect + * + * This function deselects all nodes, so it doesn't matter if a specific node is selected. + * + * This function also resets the LSS master state machine to a clean state + * + * @param LSSmaster This object. + * @return #CO_LSSmaster_ILLEGAL_ARGUMENT, #CO_LSSmaster_INVALID_STATE, #CO_LSSmaster_OK + */ +CO_LSSmaster_return_t CO_LSSmaster_swStateDeselect(CO_LSSmaster_t* LSSmaster); + +/** + * Request LSS configure Bit Timing + * + * The new bit rate is set as new pending value. + * + * This function needs one specific node to be selected. + * + * Function must be called cyclically until it returns != #CO_LSSmaster_WAIT_SLAVE. Function is non-blocking. + * + * @param LSSmaster This object. + * @param timeDifference_us Time difference from previous function call in [microseconds]. Zero when request is started. + * @param bit new bit rate + * @return #CO_LSSmaster_ILLEGAL_ARGUMENT, #CO_LSSmaster_INVALID_STATE, #CO_LSSmaster_WAIT_SLAVE, #CO_LSSmaster_OK, + * #CO_LSSmaster_TIMEOUT, #CO_LSSmaster_OK_MANUFACTURER, #CO_LSSmaster_OK_ILLEGAL_ARGUMENT + */ +CO_LSSmaster_return_t CO_LSSmaster_configureBitTiming(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us, + uint16_t bit); + +/** + * Request LSS configure node ID + * + * The new node id is set as new pending node ID. + * + * This function needs one specific node to be selected. + * + * Function must be called cyclically until it returns != #CO_LSSmaster_WAIT_SLAVE. Function is non-blocking. + * + * @param LSSmaster This object. + * @param timeDifference_us Time difference from previous function call in [microseconds]. Zero when request is started. + * @param nodeId new node ID. Special value #CO_LSS_NODE_ID_ASSIGNMENT can be used to invalidate node ID. + * @return #CO_LSSmaster_ILLEGAL_ARGUMENT, #CO_LSSmaster_INVALID_STATE, #CO_LSSmaster_WAIT_SLAVE, #CO_LSSmaster_OK, + * #CO_LSSmaster_TIMEOUT, #CO_LSSmaster_OK_MANUFACTURER, #CO_LSSmaster_OK_ILLEGAL_ARGUMENT + */ +CO_LSSmaster_return_t CO_LSSmaster_configureNodeId(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us, + uint8_t nodeId); + +/** + * Request LSS store configuration + * + * The current "pending" values for bit rate and node ID in LSS slave are stored as "permanent" values. + * + * This function needs one specific node to be selected. + * + * Function must be called cyclically until it returns != #CO_LSSmaster_WAIT_SLAVE. Function is non-blocking. + * + * @param LSSmaster This object. + * @param timeDifference_us Time difference from previous function call in [microseconds]. Zero when request is started. + * @return #CO_LSSmaster_ILLEGAL_ARGUMENT, #CO_LSSmaster_INVALID_STATE, #CO_LSSmaster_WAIT_SLAVE, #CO_LSSmaster_OK, + * #CO_LSSmaster_TIMEOUT, #CO_LSSmaster_OK_MANUFACTURER, #CO_LSSmaster_OK_ILLEGAL_ARGUMENT + */ +CO_LSSmaster_return_t CO_LSSmaster_configureStore(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us); + +/** + * Request LSS activate bit timing + * + * The current "pending" bit rate in LSS slave is applied. + * + * Be aware that changing the bit rate is a critical step for the network. A failure will render the network unusable! + * Therefore, this function only should be called if the following conditions are met: + * - all nodes support changing bit timing + * - new bit timing is successfully set as "pending" in all nodes + * - all nodes have to activate the new bit timing roughly at the same time. Therefore this function needs all nodes + * to be selected. + * + * @param LSSmaster This object. + * @param switchDelay_ms delay that is applied by the slave once before and once after switching in ms. + * @return #CO_LSSmaster_ILLEGAL_ARGUMENT, #CO_LSSmaster_INVALID_STATE, #CO_LSSmaster_OK + */ +CO_LSSmaster_return_t CO_LSSmaster_ActivateBit(CO_LSSmaster_t* LSSmaster, uint16_t switchDelay_ms); + +/** + * Request LSS inquire LSS address + * + * The LSS address value is read from the node. This is useful when the node was selected by fastscan. + * + * This function needs one specific node to be selected. + * + * Function must be called cyclically until it returns != #CO_LSSmaster_WAIT_SLAVE. Function is non-blocking. + * + * @param LSSmaster This object. + * @param timeDifference_us Time difference from previous function call in [microseconds]. Zero when request is started. + * @param [out] lssAddress read result when function returns successfully + * @return #CO_LSSmaster_ILLEGAL_ARGUMENT, #CO_LSSmaster_INVALID_STATE, #CO_LSSmaster_WAIT_SLAVE, #CO_LSSmaster_OK, + * #CO_LSSmaster_TIMEOUT + */ +CO_LSSmaster_return_t CO_LSSmaster_InquireLssAddress(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us, + CO_LSS_address_t* lssAddress); + +/** + * Request LSS inquire node ID or part of LSS address + * + * The node-ID, identity vendor-ID, product-code, revision-number or serial-number value is read from the node. + * + * This function needs one specific node to be selected. + * + * Function must be called cyclically until it returns != #CO_LSSmaster_WAIT_SLAVE. Function is non-blocking. + * + * @param LSSmaster This object. + * @param timeDifference_us Time difference from previous function call in [microseconds]. Zero when request is started. + * @param lssInquireCs One of CO_LSS_INQUIRE_xx commands from @ref CO_LSS_command_specifiers. + * @param [out] value read result when function returns successfully + * @return #CO_LSSmaster_ILLEGAL_ARGUMENT, #CO_LSSmaster_INVALID_STATE, #CO_LSSmaster_WAIT_SLAVE, #CO_LSSmaster_OK, + * #CO_LSSmaster_TIMEOUT + */ +CO_LSSmaster_return_t CO_LSSmaster_Inquire(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us, uint8_t lssInquireCs, + uint32_t* value); + +/** + * Scan type for #CO_LSSmaster_fastscan_t scan + */ +typedef enum { + CO_LSSmaster_FS_SCAN = 0, /**< Do full 32 bit scan */ + CO_LSSmaster_FS_SKIP = 1, /**< Skip this value */ + CO_LSSmaster_FS_MATCH = 2, /**< Full 32 bit value is given as argument, just verify */ +} CO_LSSmaster_scantype_t; + +/** + * Parameters for LSS fastscan #CO_LSSmaster_IdentifyFastscan + */ +typedef struct { + CO_LSSmaster_scantype_t scan[4]; /**< Scan type for each part of the LSS address */ + CO_LSS_address_t match; /**< Value to match in case of #CO_LSSmaster_FS_MATCH */ + CO_LSS_address_t found; /**< Scan result */ +} CO_LSSmaster_fastscan_t; + +/** + * Select a node by LSS identify fastscan + * + * This initiates searching for a unconfigured node by the means of LSS fastscan mechanism. When this function is + * finished + * - a (more or less) arbitrary node is selected and ready for node ID assingment + * - no node is selected because the given criteria do not match a node + * - no node is selected because all nodes are already configured + * + * There are multiple ways to scan for a node. Depending on those, the scan will take different amounts of time: + * - full scan + * - partial scan + * - verification + * + * Most of the time, those are used in combination. Consider the following example: + * - Vendor ID and product code are known + * - Software version doesn't matter + * - Serial number is unknown + * + * In this case, the fastscan structure should be set up as following: + * \code{.c} +CO_LSSmaster_fastscan_t fastscan; +fastscan.scan[CO_LSS_FASTSCAN_VENDOR_ID] = CO_LSSmaster_FS_MATCH; +fastscan.match.vendorID = YOUR_VENDOR_ID; +fastscan.scan[CO_LSS_FASTSCAN_PRODUCT] = CO_LSSmaster_FS_MATCH; +fastscan.match.productCode = YOUR_PRODUCT_CODE; +fastscan.scan[CO_LSS_FASTSCAN_REV] = CO_LSSmaster_FS_SKIP; +fastscan.scan[CO_LSS_FASTSCAN_SERIAL] = CO_LSSmaster_FS_SCAN; + * \endcode + * + * This example will take 2 scan cyles for verifying vendor ID and product code and 33 scan cycles to find the serial + * number. + * + * For scanning, the following limitations apply: + * - No more than two values can be skipped + * - Vendor ID cannot be skipped + * + * @remark When doing partial scans, it is in the responsibility of the user that the LSS address is unique. + * + * This function needs that no node is selected when starting the scan process. + * + * Function must be called cyclically until it returns != #CO_LSSmaster_WAIT_SLAVE. Function is non-blocking. + * + * @param LSSmaster This object. + * @param timeDifference_us Time difference from previous function call in [microseconds]. Zero when request is started. + * @param fastscan struct according to #CO_LSSmaster_fastscan_t. + * @return #CO_LSSmaster_ILLEGAL_ARGUMENT, #CO_LSSmaster_INVALID_STATE, #CO_LSSmaster_WAIT_SLAVE, + * #CO_LSSmaster_SCAN_FINISHED, #CO_LSSmaster_SCAN_NOACK, #CO_LSSmaster_SCAN_FAILED + */ +CO_LSSmaster_return_t CO_LSSmaster_IdentifyFastscan(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us, + CO_LSSmaster_fastscan_t* fastscan); + +/** @} */ /* @defgroup CO_LSSmaster */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* (CO_CONFIG_LSS) & CO_CONFIG_LSS_MASTER */ + +#endif /* CO_LSSmaster_H */ diff --git a/305/CO_LSSslave.c b/305/CO_LSSslave.c new file mode 100644 index 00000000..fa74caa7 --- /dev/null +++ b/305/CO_LSSslave.c @@ -0,0 +1,429 @@ +/* + * CANopen LSS Slave protocol. + * + * @file CO_LSSslave.c + * @ingroup CO_LSS + * @author Martin Wagner + * @author Janez Paternoster + * @copyright 2017 - 2020 Neuberger Gebaeudeautomation GmbH + * + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#include + +#include "305/CO_LSSslave.h" + +#if ((CO_CONFIG_LSS)&CO_CONFIG_LSS_SLAVE) != 0 + +/* 'bit' must be unsigned or additional range check must be added: bit>=CO_LSS_FASTSCAN_BIT0 */ +#define CO_LSS_FASTSCAN_BITCHECK_VALID(bit) ((bit <= CO_LSS_FASTSCAN_BIT31) || (bit == CO_LSS_FASTSCAN_CONFIRM)) +/* 'index' must be unsigned or additional range check must be added: index>=CO_LSS_FASTSCAN_VENDOR_ID */ +#define CO_LSS_FASTSCAN_LSS_SUB_NEXT_VALID(index) (index <= CO_LSS_FASTSCAN_SERIAL) +/* 'index' must be unsigned or additional range check must be added: index>=CO_LSS_BIT_TIMING_1000 */ +#define CO_LSS_BIT_TIMING_VALID(index) ((index != 5U) && (index <= CO_LSS_BIT_TIMING_AUTO)) + +/* + * Read received message from CAN module. + * + * Function will be called (by CAN receive interrupt) every time, when CAN message with correct identifier + * will be received. For more information and description of parameters see file CO_driver.h. + */ +static void +CO_LSSslave_receive(void* object, void* msg) { + CO_LSSslave_t* LSSslave = (CO_LSSslave_t*)object; + uint8_t DLC = CO_CANrxMsg_readDLC(msg); + + if ((DLC == 8U) && !CO_FLAG_READ(LSSslave->sendResponse)) { + bool_t request_LSSslave_process = false; + const uint8_t* data = CO_CANrxMsg_readData(msg); + uint8_t cs = data[0]; + + if (cs == CO_LSS_SWITCH_STATE_GLOBAL) { + uint8_t mode = data[1]; + + switch (mode) { + case CO_LSS_STATE_WAITING: + if ((LSSslave->lssState == CO_LSS_STATE_CONFIGURATION) + && (LSSslave->activeNodeID == CO_LSS_NODE_ID_ASSIGNMENT) + && (*LSSslave->pendingNodeID != CO_LSS_NODE_ID_ASSIGNMENT)) { + /* Slave process function will request NMT Reset comm. */ + LSSslave->service = cs; + request_LSSslave_process = true; + } + LSSslave->lssState = CO_LSS_STATE_WAITING; + (void)memset(&LSSslave->lssSelect, 0, sizeof(LSSslave->lssSelect)); + break; + case CO_LSS_STATE_CONFIGURATION: LSSslave->lssState = CO_LSS_STATE_CONFIGURATION; break; + default: + /* none */ + break; + } + } else if (LSSslave->lssState == CO_LSS_STATE_WAITING) { + switch (cs) { + case CO_LSS_SWITCH_STATE_SEL_VENDOR: { + uint32_t valSw; + (void)memcpy((void*)(&valSw), (const void*)(&data[1]), sizeof(valSw)); + LSSslave->lssSelect.identity.vendorID = CO_SWAP_32(valSw); + break; + } + case CO_LSS_SWITCH_STATE_SEL_PRODUCT: { + uint32_t valSw; + (void)memcpy((void*)(&valSw), (const void*)(&data[1]), sizeof(valSw)); + LSSslave->lssSelect.identity.productCode = CO_SWAP_32(valSw); + break; + } + case CO_LSS_SWITCH_STATE_SEL_REV: { + uint32_t valSw; + (void)memcpy((void*)(&valSw), (const void*)(&data[1]), sizeof(valSw)); + LSSslave->lssSelect.identity.revisionNumber = CO_SWAP_32(valSw); + break; + } + case CO_LSS_SWITCH_STATE_SEL_SERIAL: { + uint32_t valSw; + (void)memcpy((void*)(&valSw), (const void*)(&data[1]), sizeof(valSw)); + LSSslave->lssSelect.identity.serialNumber = CO_SWAP_32(valSw); + + if (CO_LSS_ADDRESS_EQUAL(LSSslave->lssAddress, LSSslave->lssSelect)) { + LSSslave->lssState = CO_LSS_STATE_CONFIGURATION; + LSSslave->service = cs; + request_LSSslave_process = true; + } + break; + } + case CO_LSS_IDENT_FASTSCAN: { + /* fastscan is only active on unconfigured nodes */ + if ((*LSSslave->pendingNodeID == CO_LSS_NODE_ID_ASSIGNMENT) + && (LSSslave->activeNodeID == CO_LSS_NODE_ID_ASSIGNMENT)) { + uint8_t bitCheck = data[5]; + uint8_t lssSub = data[6]; + uint8_t lssNext = data[7]; + uint32_t valSw; + uint32_t idNumber; + bool_t ack; + + if (!CO_LSS_FASTSCAN_BITCHECK_VALID(bitCheck) || !CO_LSS_FASTSCAN_LSS_SUB_NEXT_VALID(lssSub) + || !CO_LSS_FASTSCAN_LSS_SUB_NEXT_VALID(lssNext)) { + /* Invalid request */ + break; + } + + (void)memcpy((void*)(&valSw), (const void*)(&data[1]), sizeof(valSw)); + idNumber = CO_SWAP_32(valSw); + ack = false; + + if (bitCheck == CO_LSS_FASTSCAN_CONFIRM) { + /* Confirm, Reset */ + ack = true; + LSSslave->fastscanPos = CO_LSS_FASTSCAN_VENDOR_ID; + (void)memset(&LSSslave->lssFastscan, 0, sizeof(LSSslave->lssFastscan)); + } else if (LSSslave->fastscanPos == lssSub) { + uint32_t mask = 0xFFFFFFFFU << bitCheck; + + if ((LSSslave->lssAddress.addr[lssSub] & mask) == (idNumber & mask)) { + /* all requested bits match */ + ack = true; + LSSslave->fastscanPos = lssNext; + + if ((bitCheck == 0U) && (lssNext < lssSub)) { + /* complete match, enter configuration state */ + LSSslave->lssState = CO_LSS_STATE_CONFIGURATION; + } + } + } else { /* MISRA C 2004 14.10 */ + } + if (ack) { +#if ((CO_CONFIG_LSS)&CO_CONFIG_LSS_SLAVE_FASTSCAN_DIRECT_RESPOND) != 0 + LSSslave->TXbuff->data[0] = CO_LSS_IDENT_SLAVE; + (void)memset(&LSSslave->TXbuff->data[1], 0, sizeof(LSSslave->TXbuff->data) - 1U); + (void)CO_CANsend(LSSslave->CANdevTx, LSSslave->TXbuff); +#else + LSSslave->service = cs; + request_LSSslave_process = true; +#endif + } + } + break; + } + default: { + /* none */ + break; + } + } + } else { /* LSSslave->lssState == CO_LSS_STATE_CONFIGURATION */ + (void)memcpy((void*)(&LSSslave->CANdata[0]), (const void*)(&data[0]), sizeof(LSSslave->CANdata)); + LSSslave->service = cs; + request_LSSslave_process = true; + } + + if (request_LSSslave_process) { + CO_FLAG_SET(LSSslave->sendResponse); +#if ((CO_CONFIG_LSS)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0 + /* Optional signal to RTOS, which can resume task, which handles further processing. */ + if (LSSslave->pFunctSignalPre != NULL) { + LSSslave->pFunctSignalPre(LSSslave->functSignalObjectPre); + } +#endif + } + } +} + +CO_ReturnError_t +CO_LSSslave_init(CO_LSSslave_t* LSSslave, CO_LSS_address_t* lssAddress, uint16_t* pendingBitRate, + uint8_t* pendingNodeID, CO_CANmodule_t* CANdevRx, uint16_t CANdevRxIdx, uint16_t CANidLssMaster, + CO_CANmodule_t* CANdevTx, uint16_t CANdevTxIdx, uint16_t CANidLssSlave) { + CO_ReturnError_t ret = CO_ERROR_NO; + + /* verify arguments */ + if ((LSSslave == NULL) || (pendingBitRate == NULL) || (pendingNodeID == NULL) || (CANdevRx == NULL) + || (CANdevTx == NULL) || !CO_LSS_NODE_ID_VALID(*pendingNodeID)) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + /* Application must make sure that lssAddress is filled with data. */ + + /* clear the object */ + (void)memset(LSSslave, 0, sizeof(CO_LSSslave_t)); + + /* Configure object variables */ + (void)memcpy(&LSSslave->lssAddress, lssAddress, sizeof(LSSslave->lssAddress)); + LSSslave->lssState = CO_LSS_STATE_WAITING; + LSSslave->fastscanPos = CO_LSS_FASTSCAN_VENDOR_ID; + + LSSslave->pendingBitRate = pendingBitRate; + LSSslave->pendingNodeID = pendingNodeID; + LSSslave->activeNodeID = *pendingNodeID; + CO_FLAG_CLEAR(LSSslave->sendResponse); + + /* configure LSS CAN Master message reception */ + ret = CO_CANrxBufferInit(CANdevRx, CANdevRxIdx, CANidLssMaster, 0x7FF, false, (void*)LSSslave, CO_LSSslave_receive); + + /* configure LSS CAN Slave response message transmission */ + LSSslave->CANdevTx = CANdevTx; + LSSslave->TXbuff = CO_CANtxBufferInit(CANdevTx, CANdevTxIdx, CANidLssSlave, false, 8, false); + + if (LSSslave->TXbuff == NULL) { + ret = CO_ERROR_ILLEGAL_ARGUMENT; + } + + return ret; +} + +#if ((CO_CONFIG_LSS)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0 +void +CO_LSSslave_initCallbackPre(CO_LSSslave_t* LSSslave, void* object, void (*pFunctSignalPre)(void* object)) { + if (LSSslave != NULL) { + LSSslave->functSignalObjectPre = object; + LSSslave->pFunctSignalPre = pFunctSignalPre; + } +} +#endif + +void +CO_LSSslave_initCkBitRateCall(CO_LSSslave_t* LSSslave, void* object, + bool_t (*pFunctLSScheckBitRate)(void* object, uint16_t bitRate)) { + if (LSSslave != NULL) { + LSSslave->functLSScheckBitRateObject = object; + LSSslave->pFunctLSScheckBitRate = pFunctLSScheckBitRate; + } +} + +void +CO_LSSslave_initActBitRateCall(CO_LSSslave_t* LSSslave, void* object, + void (*pFunctLSSactivateBitRate)(void* object, uint16_t delay)) { + if (LSSslave != NULL) { + LSSslave->functLSSactivateBitRateObject = object; + LSSslave->pFunctLSSactivateBitRate = pFunctLSSactivateBitRate; + } +} + +void +CO_LSSslave_initCfgStoreCall(CO_LSSslave_t* LSSslave, void* object, + bool_t (*pFunctLSScfgStore)(void* object, uint8_t id, uint16_t bitRate)) { + if (LSSslave != NULL) { + LSSslave->functLSScfgStoreObject = object; + LSSslave->pFunctLSScfgStore = pFunctLSScfgStore; + } +} + +bool_t +CO_LSSslave_process(CO_LSSslave_t* LSSslave) { + bool_t resetCommunication = false; + + if (CO_FLAG_READ(LSSslave->sendResponse)) { + uint8_t nid; + uint8_t errorCode; + uint8_t errorCodeManuf; + uint8_t tableSelector; + uint8_t tableIndex; + bool_t CANsend = false; + uint32_t valSw; + + (void)memset(&LSSslave->TXbuff->data[0], 0, sizeof(LSSslave->TXbuff->data)); + + switch (LSSslave->service) { + case CO_LSS_SWITCH_STATE_GLOBAL: { + /* Node-Id was unconfigured before, now it is configured, + * enter the NMT Reset communication autonomously. */ + resetCommunication = true; + break; + } + case CO_LSS_SWITCH_STATE_SEL_SERIAL: { + LSSslave->TXbuff->data[0] = CO_LSS_SWITCH_STATE_SEL; + CANsend = true; + break; + } + case CO_LSS_CFG_NODE_ID: { + nid = LSSslave->CANdata[1]; + errorCode = CO_LSS_CFG_NODE_ID_OK; + + if (CO_LSS_NODE_ID_VALID(nid)) { + *LSSslave->pendingNodeID = nid; + } else { + errorCode = CO_LSS_CFG_NODE_ID_OUT_OF_RANGE; + } + + /* send confirmation */ + LSSslave->TXbuff->data[0] = LSSslave->service; + LSSslave->TXbuff->data[1] = errorCode; + /* we do not use spec-error, always 0 */ + CANsend = true; + break; + } + case CO_LSS_CFG_BIT_TIMING: { + if (LSSslave->pFunctLSScheckBitRate == NULL) { + /* setting bit timing is not supported. Drop request */ + break; + } + + tableSelector = LSSslave->CANdata[1]; + tableIndex = LSSslave->CANdata[2]; + errorCode = CO_LSS_CFG_BIT_TIMING_OK; + errorCodeManuf = CO_LSS_CFG_BIT_TIMING_OK; + + if ((tableSelector == 0U) && CO_LSS_BIT_TIMING_VALID(tableIndex)) { + uint16_t bit = CO_LSS_bitTimingTableLookup[tableIndex]; + bool_t bit_rate_supported = LSSslave->pFunctLSScheckBitRate(LSSslave->functLSScheckBitRateObject, + bit); + + if (bit_rate_supported) { + *LSSslave->pendingBitRate = bit; + } else { + errorCode = CO_LSS_CFG_BIT_TIMING_MANUFACTURER; + errorCodeManuf = CO_LSS_CFG_BIT_TIMING_OUT_OF_RANGE; + } + } else { + /* we currently only support CiA301 bit timing table */ + errorCode = CO_LSS_CFG_BIT_TIMING_OUT_OF_RANGE; + } + + /* send confirmation */ + LSSslave->TXbuff->data[0] = LSSslave->service; + LSSslave->TXbuff->data[1] = errorCode; + LSSslave->TXbuff->data[2] = errorCodeManuf; + CANsend = true; + break; + } + case CO_LSS_CFG_ACTIVATE_BIT_TIMING: { + if (LSSslave->pFunctLSScheckBitRate == NULL) { + /* setting bit timing is not supported. Drop request */ + break; + } + + /* notify application */ + if (LSSslave->pFunctLSSactivateBitRate != NULL) { + uint16_t delay = ((uint16_t)LSSslave->CANdata[2]) << 8; + delay |= LSSslave->CANdata[1]; + LSSslave->pFunctLSSactivateBitRate(LSSslave->functLSSactivateBitRateObject, delay); + } + break; + } + case CO_LSS_CFG_STORE: { + errorCode = CO_LSS_CFG_STORE_OK; + + if (LSSslave->pFunctLSScfgStore == NULL) { + /* storing is not supported. Reply error */ + errorCode = CO_LSS_CFG_STORE_NOT_SUPPORTED; + } else { + bool_t result; + /* Store "pending" to "persistent" */ + result = LSSslave->pFunctLSScfgStore(LSSslave->functLSScfgStoreObject, *LSSslave->pendingNodeID, + *LSSslave->pendingBitRate); + if (!result) { + errorCode = CO_LSS_CFG_STORE_FAILED; + } + } + + /* send confirmation */ + LSSslave->TXbuff->data[0] = LSSslave->service; + LSSslave->TXbuff->data[1] = errorCode; + /* we do not use spec-error, always 0 */ + CANsend = true; + break; + } + case CO_LSS_INQUIRE_VENDOR: { + LSSslave->TXbuff->data[0] = LSSslave->service; + valSw = CO_SWAP_32(LSSslave->lssAddress.identity.vendorID); + (void)memcpy((void*)(&LSSslave->TXbuff->data[1]), (const void*)(&valSw), sizeof(valSw)); + CANsend = true; + break; + } + case CO_LSS_INQUIRE_PRODUCT: { + LSSslave->TXbuff->data[0] = LSSslave->service; + valSw = CO_SWAP_32(LSSslave->lssAddress.identity.productCode); + (void)memcpy((void*)(&LSSslave->TXbuff->data[1]), (const void*)(&valSw), sizeof(valSw)); + CANsend = true; + break; + } + case CO_LSS_INQUIRE_REV: { + LSSslave->TXbuff->data[0] = LSSslave->service; + valSw = CO_SWAP_32(LSSslave->lssAddress.identity.revisionNumber); + (void)memcpy((void*)(&LSSslave->TXbuff->data[1]), (const void*)(&valSw), sizeof(valSw)); + CANsend = true; + break; + } + case CO_LSS_INQUIRE_SERIAL: { + LSSslave->TXbuff->data[0] = LSSslave->service; + valSw = CO_SWAP_32(LSSslave->lssAddress.identity.serialNumber); + (void)memcpy((void*)(&LSSslave->TXbuff->data[1]), (const void*)(&valSw), sizeof(valSw)); + CANsend = true; + break; + } + case CO_LSS_INQUIRE_NODE_ID: { + LSSslave->TXbuff->data[0] = LSSslave->service; + LSSslave->TXbuff->data[1] = LSSslave->activeNodeID; + CANsend = true; + break; + } + case CO_LSS_IDENT_FASTSCAN: { + LSSslave->TXbuff->data[0] = CO_LSS_IDENT_SLAVE; + CANsend = true; + break; + } + default: { + /* none */ + break; + } + } + + if (CANsend) { + (void)CO_CANsend(LSSslave->CANdevTx, LSSslave->TXbuff); + } + + CO_FLAG_CLEAR(LSSslave->sendResponse); + } + + return resetCommunication; +} + +#endif /* (CO_CONFIG_LSS) & CO_CONFIG_LSS_SLAVE */ diff --git a/305/CO_LSSslave.h b/305/CO_LSSslave.h new file mode 100644 index 00000000..6727bfd8 --- /dev/null +++ b/305/CO_LSSslave.h @@ -0,0 +1,238 @@ +/** + * CANopen Layer Setting Service - slave protocol. + * + * @file CO_LSSslave.h + * @ingroup CO_LSS + * @author Martin Wagner + * @author Janez Paternoster + * @copyright 2017 - 2020 Neuberger Gebaeudeautomation GmbH + * + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#ifndef CO_LSSslave_H +#define CO_LSSslave_H + +#include "305/CO_LSS.h" + +#if (((CO_CONFIG_LSS)&CO_CONFIG_LSS_SLAVE) != 0) || defined CO_DOXYGEN + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup CO_LSSslave LSS Slave + * CANopen Layer Setting Service - slave protocol. + * + * @ingroup CO_CANopen_305 + * @{ + * The slave provides the following services + * - node selection via LSS address + * - node selection via LSS fastscan + * - Inquire LSS address of currently selected node + * - Inquire node ID + * - Configure bit timing + * - Configure node ID + * - Activate bit timing parameters + * - Store configuration (bit rate and node ID) + * + * After CAN module start, the LSS slave and NMT slave are started and then coexist alongside each other. To achieve + * this behaviour, the CANopen node startup process has to be controlled more detailed. Therefore, CO_LSSinit() must be + * invoked between CO_CANinit() and CO_CANopenInit() in the communication reset section. + * + * Moreover, the LSS slave needs to pause the NMT slave initialization in case no valid node ID is available at start + * up. In that case CO_CANopenInit() skips initialization of other CANopen modules and CO_process() skips processing of + * other modules than LSS slave automatically. + * + * Variables for CAN-bitrate and CANopen node-id must be initialized by application from non-volatile memory or dip + * switches. Pointers to them are passed to CO_LSSinit() function. Those variables represents pending values. If node-id + * is valid in the moment it enters CO_LSSinit(), it also becomes active node-id and the stack initialises normally. + * Otherwise, node-id must be configured by lss and after successful configuration stack passes reset communication + * autonomously. + * + * Device with all threads can be normally initialized and running despite that node-id is not valid. Application must + * take care, because CANopen is not initialized. In that case CO_CANopenInit() returns error condition + * CO_ERROR_NODE_ID_UNCONFIGURED_LSS which must be handled properly. Status can also be checked with + * CO->nodeIdUnconfigured variable. + * + * Some callback functions may be initialized by application with CO_LSSslave_initCkBitRateCall(), + * CO_LSSslave_initActBitRateCall() and CO_LSSslave_initCfgStoreCall(). + */ + +/** + * LSS slave object. + */ +typedef struct { + CO_LSS_address_t lssAddress; /**< From #CO_LSSslave_init */ + uint8_t lssState; /**< @ref CO_LSS_STATE_state */ + CO_LSS_address_t lssSelect; /**< Received LSS Address by select */ + CO_LSS_address_t lssFastscan; /**< Received LSS Address by fastscan */ + uint8_t fastscanPos; /**< Current state of fastscan */ + uint16_t* pendingBitRate; /**< Bit rate value that is temporarily configured */ + uint8_t* pendingNodeID; /**< Node ID that is temporarily configured */ + uint8_t activeNodeID; /**< Node ID used at the CAN interface */ + volatile void* + sendResponse; /**< Variable indicates, if LSS response has to be sent by mainline processing function */ + uint8_t service; /**< Service, which will have to be processed by mainline processing function */ + uint8_t CANdata[8]; /**< Received CAN data, which will be processed by mainline processing function */ + +#if (((CO_CONFIG_LSS)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0) || defined CO_DOXYGEN + void (*pFunctSignalPre)(void* object); /**< From CO_LSSslave_initCallbackPre() or NULL */ + void* functSignalObjectPre; /**< Pointer to object */ +#endif + bool_t (*pFunctLSScheckBitRate)(void* object, + uint16_t bitRate); /**< From CO_LSSslave_initCkBitRateCall() or NULL */ + void* functLSScheckBitRateObject; /** Pointer to object */ + void (*pFunctLSSactivateBitRate)( + void* object, uint16_t delay); /**< From CO_LSSslave_initActBitRateCall() or NULL. Delay is in ms */ + void* functLSSactivateBitRateObject; /** Pointer to object */ + bool_t (*pFunctLSScfgStore)(void* object, uint8_t id, + uint16_t bitRate); /**< From CO_LSSslave_initCfgStoreCall() or NULL */ + void* functLSScfgStoreObject; /** Pointer to object */ + CO_CANmodule_t* CANdevTx; /**< From #CO_LSSslave_init() */ + CO_CANtx_t* TXbuff; /**< CAN transmit buffer */ +} CO_LSSslave_t; + +/** + * Initialize LSS object. + * + * Function must be called in the communication reset section. + * + * pendingBitRate and pendingNodeID must be pointers to external variables. Both variables must be initialized on + * program startup (after #CO_NMT_RESET_NODE) from non-volatile memory, dip switches or similar. They must not change + * during #CO_NMT_RESET_COMMUNICATION. Both variables can be changed by CO_LSSslave_process(), depending on commands + * from the LSS master. + * + * If pendingNodeID is valid (1 <= pendingNodeID <= 0x7F), then this becomes valid active nodeId just after exit of this + * function. In that case all other CANopen objects may be initialized and processed in run time. + * + * If pendingNodeID is not valid (pendingNodeID == 0xFF), then only LSS slave is initialized and processed in run time. + * In that state pendingNodeID can be configured and after successful configuration reset communication with all CANopen + * object is activated automatically. + * + * @remark The LSS address needs to be unique on the network. For this, the 128 bit wide identity object (1018h) is + * used. Therefore, this object has to be fully initialized before passing it to this function (vendorID, product code, + * revisionNo, serialNo are set to 0 by default). Otherwise, if non-configured devices are present on CANopen network, + * LSS configuration may behave unpredictable. + * + * @param LSSslave This object will be initialized. + * @param lssAddress LSS address + * @param [in,out] pendingBitRate Pending bit rate of the CAN interface + * @param [in,out] pendingNodeID Pending node ID or 0xFF - invalid + * @param CANdevRx CAN device for LSS slave reception. + * @param CANdevRxIdx Index of receive buffer in the above CAN device. + * @param CANidLssMaster COB ID for reception. + * @param CANdevTx CAN device for LSS slave transmission. + * @param CANdevTxIdx Index of transmit buffer in the above CAN device. + * @param CANidLssSlave COB ID for transmission. + * @return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT. + */ +CO_ReturnError_t CO_LSSslave_init(CO_LSSslave_t* LSSslave, CO_LSS_address_t* lssAddress, uint16_t* pendingBitRate, + uint8_t* pendingNodeID, CO_CANmodule_t* CANdevRx, uint16_t CANdevRxIdx, + uint16_t CANidLssMaster, CO_CANmodule_t* CANdevTx, uint16_t CANdevTxIdx, + uint16_t CANidLssSlave); + +/** + * Process LSS communication + * + * Object is partially pre-processed after LSS message received. Further processing is inside this function. + * + * In case that Node-Id is unconfigured, then this function may request CANopen communication reset. This happens, when + * valid node-id is configured by LSS master. + * + * @param LSSslave This object. + * @return True, if #CO_NMT_RESET_COMMUNICATION is requested + */ +bool_t CO_LSSslave_process(CO_LSSslave_t* LSSslave); + +/** + * Get current LSS state + * + * @param LSSslave This object. + * @return @ref CO_LSS_STATE_state + */ +static inline uint8_t +CO_LSSslave_getState(CO_LSSslave_t* LSSslave) { + return (LSSslave == NULL) ? CO_LSS_STATE_WAITING : LSSslave->lssState; +} + +#if (((CO_CONFIG_LSS)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0) || defined CO_DOXYGEN +/** + * Initialize LSSslaveRx callback function. + * + * Function initializes optional callback function, which should immediately start further LSS processing. Callback is + * called after LSS message is received from the CAN bus. It should signal the RTOS to resume corresponding task. + * + * @param LSSslave This object. + * @param object Pointer to object, which will be passed to pFunctSignal(). Can be NULL + * @param pFunctSignalPre Pointer to the callback function. Not called if NULL. + */ +void CO_LSSslave_initCallbackPre(CO_LSSslave_t* LSSslave, void* object, void (*pFunctSignalPre)(void* object)); +#endif + +/** + * Initialize verify bit rate callback + * + * Function initializes callback function, which is called when "config bit timing parameters" is used. The callback + * function needs to check if the new bit rate is supported by the CANopen device. Callback returns "true" if supported. + * When no callback is set the LSS slave will no-ack the request, indicating to the master that bit rate change is not + * supported. + * + * @param LSSslave This object. + * @param object Pointer to object, which will be passed to pFunctLSScheckBitRate(). Can be NULL + * @param pFunctLSScheckBitRate Pointer to the callback function. Not called if NULL. + */ +void CO_LSSslave_initCkBitRateCall(CO_LSSslave_t* LSSslave, void* object, + bool_t (*pFunctLSScheckBitRate)(void* object, uint16_t bitRate)); + +/** + * Initialize activate bit rate callback + * + * Function initializes callback function, which is called when "activate bit timing parameters" is used. The callback + * function gives the user an event to allow setting a timer or do calculations based on the exact time the request + * arrived. According to DSP 305 6.4.4, the delay has to be applied once before and once after switching bit rates. + * During this time, a device mustn't send any messages. + * + * @param LSSslave This object. + * @param object Pointer to object, which will be passed to pFunctLSSactivateBitRate(). Can be NULL + * @param pFunctLSSactivateBitRate Pointer to the callback function. Not called if NULL. + */ +void CO_LSSslave_initActBitRateCall(CO_LSSslave_t* LSSslave, void* object, + void (*pFunctLSSactivateBitRate)(void* object, uint16_t delay)); + +/** + * Store configuration callback + * + * Function initializes callback function, which is called when "store configuration" is used. The callback function + * gives the user an event to store the corresponding node id and bit rate to NVM. Those values have to be supplied to + * the init function as "persistent values" after reset. If callback returns "true", success is send to the LSS master. + * When no callback is set the LSS slave will no-ack the request, indicating to the master that storing is not + * supported. + * + * @param LSSslave This object. + * @param object Pointer to object, which will be passed to pFunctLSScfgStore(). Can be NULL + * @param pFunctLSScfgStore Pointer to the callback function. Not called if NULL. + */ +void CO_LSSslave_initCfgStoreCall(CO_LSSslave_t* LSSslave, void* object, + bool_t (*pFunctLSScfgStore)(void* object, uint8_t id, uint16_t bitRate)); + +/** @} */ /* @defgroup CO_LSSslave */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* (CO_CONFIG_LSS) & CO_CONFIG_LSS_SLAVE */ + +#endif /* CO_LSSslave_H */ diff --git a/309/CO_gateway_ascii.c b/309/CO_gateway_ascii.c new file mode 100644 index 00000000..4e3213a2 --- /dev/null +++ b/309/CO_gateway_ascii.c @@ -0,0 +1,2009 @@ +/* + * CANopen access from other networks - ASCII mapping (CiA 309-3 DS v3.0.0) + * + * @file CO_gateway_ascii.c + * @ingroup CO_CANopen_309_3 + * @author Janez Paternoster + * @author Martin Wagner + * @copyright 2020 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#include + +#include "309/CO_gateway_ascii.h" + +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII) != 0 + +#include +#include +#include +#include + +/* verify configuration */ +#if ((CO_CONFIG_FIFO)&CO_CONFIG_FIFO_ENABLE) == 0 +#error CO_CONFIG_FIFO_ENABLE must be enabled. +#endif +#if ((CO_CONFIG_FIFO)&CO_CONFIG_FIFO_ASCII_COMMANDS) == 0 +#error CO_CONFIG_FIFO_ASCII_COMMANDS must be enabled. +#endif +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_SDO) != 0 +#if ((CO_CONFIG_FIFO)&CO_CONFIG_FIFO_ASCII_DATATYPES) == 0 +#error CO_CONFIG_FIFO_ASCII_DATATYPES must be enabled. +#endif +#endif + +CO_ReturnError_t +CO_GTWA_init(CO_GTWA_t* gtwa, +#if (((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_SDO) != 0) || defined CO_DOXYGEN + CO_SDOclient_t* SDO_C, uint16_t SDOclientTimeoutTime_ms, bool_t SDOclientBlockTransfer, +#endif +#if (((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_NMT) != 0) || defined CO_DOXYGEN + CO_NMT_t* NMT, +#endif +#if (((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_LSS) != 0) || defined CO_DOXYGEN + CO_LSSmaster_t* LSSmaster, +#endif +#if (((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_PRINT_LEDS) != 0) || defined CO_DOXYGEN + CO_LEDs_t* LEDs, +#endif + uint8_t dummy) { + (void)dummy; + /* verify arguments */ + if ((gtwa == NULL) +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_SDO) != 0 + || (SDO_C == NULL) || (SDOclientTimeoutTime_ms == 0U) +#endif +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_NMT) != 0 + || (NMT == NULL) +#endif +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_LSS) != 0 + || (LSSmaster == NULL) +#endif +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_PRINT_LEDS) != 0 + || (LEDs == NULL) +#endif + ) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + /* clear the object */ + (void)memset(gtwa, 0, sizeof(CO_GTWA_t)); + + /* initialize variables */ +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_SDO) != 0 + gtwa->SDO_C = SDO_C; + gtwa->SDOtimeoutTime = SDOclientTimeoutTime_ms; + gtwa->SDOblockTransferEnable = SDOclientBlockTransfer; +#endif +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_NMT) != 0 + gtwa->NMT = NMT; +#endif +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_LSS) != 0 + gtwa->LSSmaster = LSSmaster; +#endif +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_PRINT_LEDS) != 0 + gtwa->LEDs = LEDs; +#endif + gtwa->net_default = -1; + gtwa->node_default = -1; + gtwa->state = CO_GTWA_ST_IDLE; + gtwa->respHold = false; + + CO_fifo_init(>wa->commFifo, >wa->commBuf[0], CO_CONFIG_GTWA_COMM_BUF_SIZE + 1); + +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_LOG) != 0 + CO_fifo_init(>wa->logFifo, >wa->logBuf[0], CO_CONFIG_GTWA_LOG_BUF_SIZE + 1); +#endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_LOG) */ + + return CO_ERROR_NO; +} + +void +CO_GTWA_initRead(CO_GTWA_t* gtwa, + size_t (*readCallback)(void* object, const char* buf, size_t count, uint8_t* connectionOK), + void* readCallbackObject) { + if (gtwa != NULL) { + gtwa->readCallback = readCallback; + gtwa->readCallbackObject = readCallbackObject; + } +} + +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_LOG) != 0 +void +CO_GTWA_log_print(CO_GTWA_t* gtwa, const char* message) { + if ((gtwa != NULL) && (message != NULL)) { + const char* c; + + /* add newline between messages */ + if (CO_fifo_getOccupied(>wa->logFifo) > 0U) { + CO_fifo_putc_ov(>wa->logFifo, '\n'); + } + for (c = &message[0]; *c != '\0'; c++) { + CO_fifo_putc_ov(>wa->logFifo, (const uint8_t)*c); + } + } +} +#endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_LOG */ + +/******************************************************************************* + * HELPER FUNCTIONS + ******************************************************************************/ +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_PRINT_HELP) != 0 +/* help strings ("\n" is used between string, "\r\n" closes the response.) */ +static const char CO_GTWA_helpString[] = + "\nCommand strings start with '\"[\"\"]\"' followed by:\n" + "[[] ] r[ead] [] # SDO upload.\n" + "[[] ] w[rite] # SDO download.\n" + "\n" + "[[] ] start # NMT Start node.\n" + "[[] ] stop # NMT Stop node.\n" + "[[] ] preop[erational] # NMT Set node to pre-operational.\n" + "[[] ] reset node # NMT Reset node.\n" + "[[] ] reset comm[unication] # NMT Reset communication.\n" + "\n" + "[] set network # Set default net.\n" + "[] set node # Set default node.\n" + "[] set sdo_timeout # Configure SDO client time-out in ms.\n" + "[] set sdo_block <0|1> # Enable/disable SDO block transfer.\n" + "\n" + "help [datatype|lss] # Print this or datatype or lss help.\n" + "led # Print status LEDs of this device.\n" + "log # Print message log.\n" + "\n" + "Response:\n" + "\"[\"\"]\" OK | |\n" + " ERROR: | ERROR:\n" + "\n" + "* Every command must be terminated with ('\\r\\n'). characters. Same\n" + " is response. String is not null terminated, is optional in command.\n" + "* Comments started with '#' are ignored. They may be on the beginning of the\n" + " line or after the command string.\n" + "* 'sdo_timeout' is in milliseconds, 500 by default. Block transfer is\n" + " disabled by default.\n" + "* If '' or '' is not specified within commands, then value defined\n" + " by 'set network' or 'set node' command is used.\r\n"; + +static const char CO_GTWA_helpStringDatatypes[] = + "\nDatatypes:\n" + "b # Boolean.\n" + "i8, i16, i32, i64 # Signed integers.\n" + "u8, u16, u32, u64 # Unsigned integers.\n" + "x8, x16, x32, x64 # Unsigned integers, displayed as hexadecimal, non-standard.\n" + "r32, r64 # Real numbers.\n" + "vs # Visible string (between double quotes if multi-word).\n" + "os, us # Octet, unicode string, (mime-base64 (RFC2045) based, line).\n" + "d # domain (mime-base64 (RFC2045) based, one line).\n" + "hex # Hexagonal data, optionally space separated, non-standard.\r\n"; + +static const char CO_GTWA_helpStringLss[] = + "\nLSS commands:\n" + "lss_switch_glob <0|1> # Switch state global command.\n" + "lss_switch_sel \\\n" + " #Switch state selective.\n" + "lss_set_node # Configure node-ID.\n" + "lss_conf_bitrate \\\n" + " # Configure bit-rate.\n" + "lss_activate_bitrate # Activate new bit-rate.\n" + "lss_store # LSS store configuration.\n" + "lss_inquire_addr [] # Inquire LSS address.\n" + "lss_get_node # Inquire node-ID.\n" + "_lss_fastscan [] # Identify fastscan, non-standard.\n" + "lss_allnodes [ [ \\\n" + " [ \\\n" + " ]]]\n" + " # Node-ID configuration of all nodes.\n" + "\n" + "* All LSS commands start with '\"[\"\"]\" []'.\n" + "* : 0=1000 kbit/s, 1=800 kbit/s, 2=500 kbit/s, 3=250 kbit/s,\n" + " 4=125 kbit/s, 6=50 kbit/s, 7=20 kbit/s, 8=10 kbit/s, 9=auto\n" + "* : 0=fastscan, 1=ignore, 2=match value in next parameter\r\n"; +#endif + +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_PRINT_LEDS) != 0 +#define CO_GTWA_LED_PRINTOUTS_SIZE 5U +static const char* CO_GTWA_LED_PRINTOUTS[CO_GTWA_LED_PRINTOUTS_SIZE] = { + " CANopen status LEDs: R G \r", " CANopen status LEDs: R G* \r", + " CANopen status LEDs: R* G \r", " CANopen status LEDs: R* G* \r", + " \r"}; +#endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_PRINT_LEDS */ + +/* Get uint32 number from token, verify limits and set *err if necessary */ +static inline uint32_t +getU32(char* token, uint32_t min, uint32_t max, bool_t* err) { + char* sRet; + uint32_t num = strtoul(token, &sRet, 0); + + if ((sRet != strchr(token, (int32_t)'\0')) || (num < min) || (num > max)) { + *err = true; + } + + return num; +} + +/* Verify net and node, return true on error */ +static bool_t +checkNetNode(CO_GTWA_t* gtwa, int32_t net, int16_t node, uint8_t NodeMin, CO_GTWA_respErrorCode_t* errCode) { + bool_t e = false; + CO_GTWA_respErrorCode_t eCode; + + if (node == -1) { + eCode = CO_GTWA_respErrorNoDefaultNodeSet; + e = true; + } else if ((node < (int16_t)NodeMin) || (node > (int16_t)127)) { + eCode = CO_GTWA_respErrorUnsupportedNode; + e = true; + } +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_MULTI_NET) != 0 + else if (net == -1) { + eCode = CO_GTWA_respErrorNoDefaultNetSet; + e = true; + } + /* not implemented */ + else if ((net < CO_CONFIG_GTW_NET_MIN) || (net > CO_CONFIG_GTW_NET_MAX)) { + eCode = CO_GTWA_respErrorUnsupportedNet; + e = true; + } +#endif + else { + gtwa->net = (uint16_t)net; + gtwa->node = (uint8_t)node; + } + if (e) { + *errCode = eCode; + } + return e; +} + +/* Verify net, return true on error */ +static bool_t +checkNet(CO_GTWA_t* gtwa, int32_t net, CO_GTWA_respErrorCode_t* errCode) { +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_MULTI_NET) != 0 + bool_t e = false; + CO_GTWA_respErrorCode_t eCode; + + if (net == -1) { + eCode = CO_GTWA_respErrorNoDefaultNetSet; + e = true; + } + /* not implemented */ + else if ((net < CO_CONFIG_GTW_NET_MIN) || (net > CO_CONFIG_GTW_NET_MAX)) { + eCode = CO_GTWA_respErrorUnsupportedNet; + e = true; + } else { + gtwa->net = (uint16_t)net; + } + if (e) { + *errCode = eCode; + } + return e; +#else + (void)errCode; /* unused */ +#define CO_CONFIG_GTW_NET_MIN 0 +#define CO_CONFIG_GTW_NET_MAX 0xFFFF + gtwa->net = (uint16_t)net; + return false; +#endif +} + +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_SDO) != 0 +/* data types for SDO read or write */ +static const CO_GTWA_dataType_t dataTypes[] = { + {(char*)"hex", 0, CO_fifo_readHex2a, CO_fifo_cpyTok2Hex}, /* hex, non-standard */ + {(char*)"b", 1, CO_fifo_readU82a, CO_fifo_cpyTok2U8}, /* BOOLEAN */ + {(char*)"i8", 1, CO_fifo_readI82a, CO_fifo_cpyTok2I8}, /* INTEGER8 */ + {(char*)"i16", 2, CO_fifo_readI162a, CO_fifo_cpyTok2I16}, /* INTEGER16 */ + {(char*)"i32", 4, CO_fifo_readI322a, CO_fifo_cpyTok2I32}, /* INTEGER32 */ + {(char*)"i64", 8, CO_fifo_readI642a, CO_fifo_cpyTok2I64}, /* INTEGER64 */ + {(char*)"u8", 1, CO_fifo_readU82a, CO_fifo_cpyTok2U8}, /* UNSIGNED8 */ + {(char*)"u16", 2, CO_fifo_readU162a, CO_fifo_cpyTok2U16}, /* UNSIGNED16 */ + {(char*)"u32", 4, CO_fifo_readU322a, CO_fifo_cpyTok2U32}, /* UNSIGNED32 */ + {(char*)"u64", 8, CO_fifo_readU642a, CO_fifo_cpyTok2U64}, /* UNSIGNED64 */ + {(char*)"x8", 1, CO_fifo_readX82a, CO_fifo_cpyTok2U8}, /* UNSIGNED8 */ + {(char*)"x16", 2, CO_fifo_readX162a, CO_fifo_cpyTok2U16}, /* UNSIGNED16 */ + {(char*)"x32", 4, CO_fifo_readX322a, CO_fifo_cpyTok2U32}, /* UNSIGNED32 */ + {(char*)"x64", 8, CO_fifo_readX642a, CO_fifo_cpyTok2U64}, /* UNSIGNED64 */ + {(char*)"r32", 4, CO_fifo_readR322a, CO_fifo_cpyTok2R32}, /* REAL32 */ + {(char*)"r64", 8, CO_fifo_readR642a, CO_fifo_cpyTok2R64}, /* REAL64 */ + {(char*)"vs", 0, CO_fifo_readVs2a, CO_fifo_cpyTok2Vs}, /* VISIBLE_STRING */ + {(char*)"os", 0, CO_fifo_readB642a, CO_fifo_cpyTok2B64}, /* OCTET_STRING base64 */ + {(char*)"us", 0, CO_fifo_readB642a, CO_fifo_cpyTok2B64}, /* UNICODE_STRING base64 */ + {(char*)"d", 0, CO_fifo_readB642a, CO_fifo_cpyTok2B64} /* DOMAIN - base64 */ +}; + +/* get data type from token */ +static const CO_GTWA_dataType_t* +CO_GTWA_getDataType(char* token, bool_t* err) { + if ((token != NULL) && (*err == false)) { + uint32_t i; + uint32_t len = sizeof(dataTypes) / sizeof(CO_GTWA_dataType_t); + + for (i = 0; i < len; i++) { + const CO_GTWA_dataType_t* dt = &dataTypes[i]; + if (strcmp(token, dt->syntax) == 0) { + return dt; + } + } + } + + *err = true; + return NULL; +} +#endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_SDO */ + +/* transfer response buffer and verify if all bytes was read. Return true on + * success, or false, if communication is broken. */ +static bool_t +respBufTransfer(CO_GTWA_t* gtwa) { + uint8_t connectionOK = 1; + + if (gtwa->readCallback == NULL) { + /* no callback registered, just purge the response */ + gtwa->respBufOffset = 0; + gtwa->respBufCount = 0; + gtwa->respHold = false; + } else { + /* transfer response to the application */ + size_t countRead = gtwa->readCallback(gtwa->readCallbackObject, + (const char*)>wa->respBuf[gtwa->respBufOffset], gtwa->respBufCount, + &connectionOK); + + if (countRead < gtwa->respBufCount) { + gtwa->respBufOffset += countRead; + gtwa->respBufCount -= countRead; + gtwa->respHold = true; + } else { + gtwa->respBufOffset = 0; + gtwa->respBufCount = 0; + gtwa->respHold = false; + } + } + return connectionOK != 0U; +} + +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_ERROR_DESC) != 0 +#ifndef CO_CONFIG_GTW_ASCII_ERROR_DESC_STRINGS +#define CO_CONFIG_GTW_ASCII_ERROR_DESC_STRINGS + +typedef struct { + const uint32_t code; + const char* desc; +} errorDescs_t; + +static const errorDescs_t errorDescs[] = {{100, "Request not supported."}, + {101, "Syntax error."}, + {102, "Request not processed due to internal state."}, + {103, "Time-out."}, + {104, "No default net set."}, + {105, "No default node set."}, + {106, "Unsupported net."}, + {107, "Unsupported node."}, + {200, "Lost guarding message."}, + {201, "Lost connection."}, + {202, "Heartbeat started."}, + {203, "Heartbeat lost."}, + {204, "Wrong NMT state."}, + {205, "Boot-up."}, + {300, "Error passive."}, + {301, "Bus off."}, + {303, "CAN buffer overflow."}, + {304, "CAN init."}, + {305, "CAN active (at init or start-up)."}, + {400, "PDO already used."}, + {401, "PDO length exceeded."}, + {501, "LSS implementation- / manufacturer-specific error."}, + {502, "LSS node-ID not supported."}, + {503, "LSS bit-rate not supported."}, + {504, "LSS parameter storing failed."}, + {505, "LSS command failed because of media error."}, + {600, "Running out of memory."}}; +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_SDO) != 0 +static const errorDescs_t errorDescsSDO[] = { + {0x00000000, "No abort."}, + {0x05030000, "Toggle bit not altered."}, + {0x05040000, "SDO protocol timed out."}, + {0x05040001, "Command specifier not valid or unknown."}, + {0x05040002, "Invalid block size in block mode."}, + {0x05040003, "Invalid sequence number in block mode."}, + {0x05040004, "CRC error (block mode only)."}, + {0x05040005, "Out of memory."}, + {0x06010000, "Unsupported access to an object."}, + {0x06010001, "Attempt to read a write only object."}, + {0x06010002, "Attempt to write a read only object."}, + {0x06020000, "Object does not exist."}, + {0x06040041, "Object cannot be mapped to the PDO."}, + {0x06040042, "Number and length of object to be mapped exceeds PDO length."}, + {0x06040043, "General parameter incompatibility reasons."}, + {0x06040047, "General internal incompatibility in device."}, + {0x06060000, "Access failed due to hardware error."}, + {0x06070010, "Data type does not match, length of service parameter does not match."}, + {0x06070012, "Data type does not match, length of service parameter too high."}, + {0x06070013, "Data type does not match, length of service parameter too short."}, + {0x06090011, "Sub index does not exist."}, + {0x06090030, "Invalid value for parameter (download only)."}, + {0x06090031, "Value range of parameter written too high."}, + {0x06090032, "Value range of parameter written too low."}, + {0x06090036, "Maximum value is less than minimum value."}, + {0x060A0023, "Resource not available: SDO connection."}, + {0x08000000, "General error."}, + {0x08000020, "Data cannot be transferred or stored to application."}, + {0x08000021, "Data cannot be transferred or stored to application because of local control."}, + {0x08000022, "Data cannot be transferred or stored to application because of present device state."}, + {0x08000023, "Object dictionary not present or dynamic generation fails."}, + {0x08000024, "No data available."}}; +#endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_SDO */ +#endif /* CO_CONFIG_GTW_ASCII_ERROR_DESC_STRINGS */ + +static void +responseWithError(CO_GTWA_t* gtwa, CO_GTWA_respErrorCode_t respErrorCode) { + uint32_t i; + uint32_t len = sizeof(errorDescs) / sizeof(errorDescs_t); + const char* desc = "-"; + + for (i = 0; i < len; i++) { + const errorDescs_t* ed = &errorDescs[i]; + if ((CO_GTWA_respErrorCode_t)ed->code == respErrorCode) { + desc = ed->desc; + } + } + + gtwa->respBufCount = (size_t)snprintf(gtwa->respBuf, CO_GTWA_RESP_BUF_SIZE, "[%" PRId32 "] ERROR:%d #%s\r\n", + (int32_t)gtwa->sequence, (int32_t)respErrorCode, desc); + (void)respBufTransfer(gtwa); +} + +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_SDO) != 0 +static void +responseWithErrorSDO(CO_GTWA_t* gtwa, CO_SDO_abortCode_t abortCode, bool_t postponed) { + uint32_t i; + uint32_t len = sizeof(errorDescsSDO) / sizeof(errorDescs_t); + const char* desc = "-"; + + for (i = 0; i < len; i++) { + const errorDescs_t* ed = &errorDescsSDO[i]; + if ((CO_SDO_abortCode_t)ed->code == abortCode) { + desc = ed->desc; + } + } + + if (!postponed) { + gtwa->respBufCount = (size_t)snprintf(gtwa->respBuf, CO_GTWA_RESP_BUF_SIZE, + "[%" PRId32 "] ERROR:0x%08X #%s\r\n", (int32_t)gtwa->sequence, + (uint32_t)abortCode, desc); + } else { + gtwa->respBufCount = (size_t)snprintf(gtwa->respBuf, CO_GTWA_RESP_BUF_SIZE, "\n...ERROR:0x%08X #%s\r\n", + (uint32_t)abortCode, desc); + } + + (void)respBufTransfer(gtwa); +} +#endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_SDO */ + +#else /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_ERROR_DESC */ +static inline void +responseWithError(CO_GTWA_t* gtwa, CO_GTWA_respErrorCode_t respErrorCode) { + gtwa->respBufCount = (size_t)snprintf(gtwa->respBuf, CO_GTWA_RESP_BUF_SIZE, "[%" PRId32 "] ERROR:%d\r\n", + gtwa->sequence, respErrorCode); + (void)respBufTransfer(gtwa); +} + +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_SDO) != 0 +static inline void +responseWithErrorSDO(CO_GTWA_t* gtwa, CO_SDO_abortCode_t abortCode, bool_t postponed) { + if (!postponed) { + gtwa->respBufCount = (size_t)snprintf(gtwa->respBuf, CO_GTWA_RESP_BUF_SIZE, "[%" PRId32 "] ERROR:0x%08X\r\n", + gtwa->sequence, abortCode); + } else { + gtwa->respBufCount = (size_t)snprintf(gtwa->respBuf, CO_GTWA_RESP_BUF_SIZE, "\n...ERROR:0x%08X\r\n", abortCode); + } + + (void)respBufTransfer(gtwa); +} +#endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_SDO */ +#endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_ERROR_DESC */ + +static inline void +responseWithOK(CO_GTWA_t* gtwa) { + gtwa->respBufCount = (size_t)snprintf(gtwa->respBuf, CO_GTWA_RESP_BUF_SIZE, "[%" PRId32 "] OK\r\n", + (int32_t)gtwa->sequence); + (void)respBufTransfer(gtwa); +} + +static inline void +responseWithEmpty(CO_GTWA_t* gtwa) { + gtwa->respBufCount = (size_t)snprintf(gtwa->respBuf, CO_GTWA_RESP_BUF_SIZE, "\r\n"); + (void)respBufTransfer(gtwa); +} + +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_LSS) != 0 +static void +responseLSS(CO_GTWA_t* gtwa, CO_LSSmaster_return_t lss_ret) { + if (lss_ret == CO_LSSmaster_OK) { + responseWithOK(gtwa); + } else { + CO_GTWA_respErrorCode_t respErrorCode; + + if ((lss_ret == CO_LSSmaster_TIMEOUT) || (lss_ret == CO_LSSmaster_SCAN_NOACK)) { + respErrorCode = CO_GTWA_respErrorTimeOut; + } else if (lss_ret == CO_LSSmaster_OK_MANUFACTURER) { + respErrorCode = CO_GTWA_respErrorLSSmanufacturer; + } else { + respErrorCode = CO_GTWA_respErrorInternalState; + } + responseWithError(gtwa, respErrorCode); + } +} +#endif + +static inline void +convertToLower(char* token, size_t maxCount) { + size_t i; + char* c = &token[0]; + + for (i = 0; i < maxCount; i++) { + if (*c == '\0') { + break; + } else { + *c = (char)tolower((int32_t)*c); + } + c++; + } +} + +/******************************************************************************* + * PROCESS FUNCTION + ******************************************************************************/ +void +CO_GTWA_process(CO_GTWA_t* gtwa, bool_t enable, uint32_t timeDifference_us, uint32_t* timerNext_us) { + (void)timerNext_us; /* may be unused */ + + bool_t err = false; /* syntax or other error, true or false, I/O variable */ + uint8_t closed; /* indication of command delimiter, I/O variable */ + CO_GTWA_respErrorCode_t respErrorCode = CO_GTWA_respErrorNone; + + if (gtwa == NULL) { + return; + } + + if (!enable) { + gtwa->state = CO_GTWA_ST_IDLE; + CO_fifo_reset(>wa->commFifo); + return; + } + + /* If there is some more output data for application, read them first. Hold on this state, if necessary. */ + if (gtwa->respHold) { + timeDifference_us += gtwa->timeDifference_us_cumulative; + + (void)respBufTransfer(gtwa); + if (gtwa->respHold) { + gtwa->timeDifference_us_cumulative = timeDifference_us; + return; + } else { + gtwa->timeDifference_us_cumulative = 0; + } + } + + /*************************************************************************** + * COMMAND PARSER + ***************************************************************************/ + /* if idle, search for new command, skip comments or empty lines */ + while (CO_fifo_CommSearch(>wa->commFifo, false) && (gtwa->state == CO_GTWA_ST_IDLE)) { + char tok[20]; + size_t n; + uint32_t ui[3]; + int32_t i; + int32_t net = gtwa->net_default; + int16_t node = gtwa->node_default; + + /* parse mandatory token '"[""]"' */ + closed = 0xFFU; + n = CO_fifo_readToken(>wa->commFifo, tok, sizeof(tok), &closed, &err); + /* Break if error in token or token was found, but closed with command delimiter. */ + if (err || ((n > 0U) && (closed != 0U))) { + err = true; + break; + } + /* If empty line or just comment, continue with next command */ + else if ((n == 0U) && (closed != 0U)) { + responseWithEmpty(gtwa); + continue; + } else { /* MISRA C 2004 14.10 */ + } + + if (tok[0] != '[') { + err = true; + break; + } + if (tok[strlen(tok) - 1U] != ']') { + err = true; + break; + } + tok[strlen(tok) - 1U] = '\0'; + gtwa->sequence = getU32(tok + 1, 0, 0xFFFFFFFFU, &err); + if (err) { + break; + } + + /* parse optional tokens '[[] ]', both numerical. Then + * follows mandatory token , which is not numerical. */ + for (i = 0; i < 3; i++) { + closed = 0xFFU; + n = CO_fifo_readToken(>wa->commFifo, tok, sizeof(tok), &closed, &err); + if (err || (n == 0U)) { + /* empty token, break on error */ + err = true; + break; + } else if ((int32_t)isdigit((int)tok[0]) == 0) { + /* found */ + break; + } else if (closed != 0U) { + /* numerical value must not be closed */ + err = true; + break; + } else { /* MISRA C 2004 14.10 */ + } + + ui[i] = getU32(tok, 0, 0xFFFFFFFFU, &err); + if (err) { + break; + } + } + if (err) { + break; + } + + switch (i) { + case 0: /* only (pointed by token) */ break; + case 1: /* and tokens */ + if (ui[0] > 127U) { + err = true; + respErrorCode = CO_GTWA_respErrorUnsupportedNode; + } else { + node = (int16_t)ui[0]; + } + break; + case 2: /* , and tokens */ + if (ui[0] > 0xFFFFU) { + err = true; + respErrorCode = CO_GTWA_respErrorUnsupportedNet; + } else if (ui[1] > 127U) { + err = true; + respErrorCode = CO_GTWA_respErrorUnsupportedNode; + } else { + net = (int32_t)ui[0]; + node = (int16_t)ui[1]; + } + break; + case 3: /* token contains digit */ err = true; break; + default: + /* MISRA C 2004 15.3 */ + break; + } + if (err) { + break; + } + + /* command is case insensitive */ + convertToLower(tok, sizeof(tok)); + + bool_t tok_is_set = strcmp(tok, "set") == 0; +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_SDO) != 0 + bool_t tok_is_read = strcmp(tok, "r") == 0; + tok_is_read = (strcmp(tok, "read") == 0) || tok_is_read; + bool_t tok_is_write = strcmp(tok, "w") == 0; + tok_is_write = (strcmp(tok, "write") == 0) || tok_is_write; +#endif +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_NMT) != 0 + bool_t tok_is_start = strcmp(tok, "start") == 0; + bool_t tok_is_stop = strcmp(tok, "stop") == 0; + bool_t tok_is_preop = strcmp(tok, "preop") == 0; + tok_is_preop = (strcmp(tok, "preoperational") == 0) || tok_is_preop; + bool_t tok_is_reset = strcmp(tok, "reset") == 0; +#endif +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_LSS) != 0 + bool_t tok_is_lss_switch_glob = strcmp(tok, "lss_switch_glob") == 0; + bool_t tok_is_lss_switch_sel = strcmp(tok, "lss_switch_sel") == 0; + bool_t tok_is_lss_set_node = strcmp(tok, "lss_set_node") == 0; + bool_t tok_is_lss_conf_bitrate = strcmp(tok, "lss_conf_bitrate") == 0; + bool_t tok_is_lss_activate_bitrate = strcmp(tok, "lss_activate_bitrate") == 0; + bool_t tok_is_lss_store = strcmp(tok, "lss_store") == 0; + bool_t tok_is_lss_inquire_addr = strcmp(tok, "lss_inquire_addr") == 0; + bool_t tok_is_lss_get_node = strcmp(tok, "lss_get_node") == 0; + bool_t tok_is__lss_fastscan = strcmp(tok, "_lss_fastscan") == 0; + bool_t tok_is_lss_allnodes = strcmp(tok, "lss_allnodes") == 0; +#endif +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_LOG) != 0 + bool_t tok_is_log = strcmp(tok, "log") == 0; +#endif +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_PRINT_HELP) != 0 + bool_t tok_is_help = strcmp(tok, "help") == 0; +#endif +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_PRINT_LEDS) != 0 + bool_t tok_is_led = strcmp(tok, "led") == 0; +#endif + /* set command - multiple sub commands */ + if (tok_is_set) { + if (closed != 0U) { + err = true; + break; + } + + /* command 2 */ + closed = 0xFFU; + (void)CO_fifo_readToken(>wa->commFifo, tok, sizeof(tok), &closed, &err); + if (err) { + break; + } + + convertToLower(tok, sizeof(tok)); + /* 'set network ' */ + if (strcmp(tok, "network") == 0) { + uint16_t value; + + if (closed != 0U) { + err = true; + break; + } + + /* value */ + closed = 1U; + (void)CO_fifo_readToken(>wa->commFifo, tok, sizeof(tok), &closed, &err); + value = (uint16_t)getU32(tok, CO_CONFIG_GTW_NET_MIN, CO_CONFIG_GTW_NET_MAX, &err); + if (err) { + break; + } + + gtwa->net_default = (int32_t)value; + responseWithOK(gtwa); + } + /* 'set node ' */ + else if (strcmp(tok, "node") == 0) { + bool_t NodeErr = checkNet(gtwa, net, &respErrorCode); + uint8_t value; + + if ((closed != 0U) || NodeErr) { + err = true; + break; + } + + /* value */ + closed = 1U; + (void)CO_fifo_readToken(>wa->commFifo, tok, sizeof(tok), &closed, &err); + value = (uint8_t)getU32(tok, 1, 127, &err); + if (err) { + break; + } + + gtwa->node_default = (int16_t)value; + responseWithOK(gtwa); + } +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_SDO) != 0 + /* 'set sdo_timeout ' */ + else if (strcmp(tok, "sdo_timeout") == 0) { + bool_t NodeErr = checkNet(gtwa, net, &respErrorCode); + uint16_t value; + + if ((closed != 0U) || NodeErr) { + err = true; + break; + } + + /* value */ + closed = 1U; + (void)CO_fifo_readToken(>wa->commFifo, tok, sizeof(tok), &closed, &err); + value = (uint16_t)getU32(tok, 1, 0xFFFF, &err); + if (err) { + break; + } + + gtwa->SDOtimeoutTime = value; + responseWithOK(gtwa); + } + /* 'set sdo_timeout <0|1>' */ + else if (strcmp(tok, "sdo_block") == 0) { + bool_t NodeErr = checkNet(gtwa, net, &respErrorCode); + uint16_t value; + + if ((closed != 0U) || NodeErr) { + err = true; + break; + } + + /* value */ + closed = 1U; + (void)CO_fifo_readToken(>wa->commFifo, tok, sizeof(tok), &closed, &err); + value = (uint16_t)getU32(tok, 0, 1, &err); + if (err) { + break; + } + + gtwa->SDOblockTransferEnable = (value == 1U) ? true : false; + responseWithOK(gtwa); + } +#endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_SDO */ + else { + respErrorCode = CO_GTWA_respErrorReqNotSupported; + err = true; + break; + } + } + +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_SDO) != 0 + /* Upload SDO command - 'r[ead] ' */ + else if (tok_is_read) { + uint16_t idx; + uint8_t subidx; + CO_SDO_return_t SDO_ret; + bool_t NodeErr = checkNetNode(gtwa, net, node, 1, &respErrorCode); + + if ((closed != 0U) || NodeErr) { + err = true; + break; + } + + /* index */ + closed = 0U; + (void)CO_fifo_readToken(>wa->commFifo, tok, sizeof(tok), &closed, &err); + idx = (uint16_t)getU32(tok, 0, 0xFFFF, &err); + if (err) { + break; + } + + /* subindex */ + closed = 0xFFU; + n = CO_fifo_readToken(>wa->commFifo, tok, sizeof(tok), &closed, &err); + subidx = (uint8_t)getU32(tok, 0, 0xFF, &err); + if (err || (n == 0U)) { + err = true; + break; + } + + /* optional data type */ + if (closed == 0U) { + closed = 1U; + (void)CO_fifo_readToken(>wa->commFifo, tok, sizeof(tok), &closed, &err); + convertToLower(tok, sizeof(tok)); + gtwa->SDOdataType = CO_GTWA_getDataType(tok, &err); + if (err) { + break; + } + } else { + gtwa->SDOdataType = &dataTypes[0]; /* use generic data type */ + } + + /* setup client */ + SDO_ret = CO_SDOclient_setup(gtwa->SDO_C, (uint32_t)CO_CAN_ID_SDO_CLI + gtwa->node, + (uint32_t)CO_CAN_ID_SDO_SRV + gtwa->node, gtwa->node); + if (SDO_ret != CO_SDO_RT_ok_communicationEnd) { + respErrorCode = CO_GTWA_respErrorInternalState; + err = true; + break; + } + + /* initiate upload */ + SDO_ret = CO_SDOclientUploadInitiate(gtwa->SDO_C, idx, subidx, gtwa->SDOtimeoutTime, + gtwa->SDOblockTransferEnable); + if (SDO_ret != CO_SDO_RT_ok_communicationEnd) { + respErrorCode = CO_GTWA_respErrorInternalState; + err = true; + break; + } + + /* indicate that gateway response didn't start yet */ + gtwa->SDOdataCopyStatus = false; + /* continue with state machine */ + timeDifference_us = 0; + gtwa->state = CO_GTWA_ST_READ; + } + + /* Download SDO comm. - w[rite] */ + else if (tok_is_write) { + uint16_t idx; + uint8_t subidx; + uint8_t status; + CO_SDO_return_t SDO_ret; + size_t size; + bool_t NodeErr = checkNetNode(gtwa, net, node, 1, &respErrorCode); + + if ((closed != 0U) || NodeErr) { + err = true; + break; + } + + /* index */ + closed = 0U; + (void)CO_fifo_readToken(>wa->commFifo, tok, sizeof(tok), &closed, &err); + idx = (uint16_t)getU32(tok, 0, 0xFFFF, &err); + if (err) { + break; + } + + /* subindex */ + closed = 0U; + n = CO_fifo_readToken(>wa->commFifo, tok, sizeof(tok), &closed, &err); + subidx = (uint8_t)getU32(tok, 0, 0xFF, &err); + if (err) { + break; + } + + /* data type */ + closed = 0U; + (void)CO_fifo_readToken(>wa->commFifo, tok, sizeof(tok), &closed, &err); + convertToLower(tok, sizeof(tok)); + gtwa->SDOdataType = CO_GTWA_getDataType(tok, &err); + if (err) { + break; + } + + /* setup client */ + SDO_ret = CO_SDOclient_setup(gtwa->SDO_C, (uint32_t)CO_CAN_ID_SDO_CLI + gtwa->node, + (uint32_t)CO_CAN_ID_SDO_SRV + gtwa->node, gtwa->node); + if (SDO_ret != CO_SDO_RT_ok_communicationEnd) { + respErrorCode = CO_GTWA_respErrorInternalState; + err = true; + break; + } + + /* initiate download */ + SDO_ret = CO_SDOclientDownloadInitiate(gtwa->SDO_C, idx, subidx, gtwa->SDOdataType->length, + gtwa->SDOtimeoutTime, gtwa->SDOblockTransferEnable); + if (SDO_ret != CO_SDO_RT_ok_communicationEnd) { + respErrorCode = CO_GTWA_respErrorInternalState; + err = true; + break; + } + + /* copy data from comm to the SDO buffer, according to data type */ + size = gtwa->SDOdataType->dataTypeScan(>wa->SDO_C->bufFifo, >wa->commFifo, &status); + /* set to true, if command delimiter was found */ + closed = ((status & CO_fifo_st_closed) == 0U) ? 0U : 1U; + /* set to true, if data are copied only partially */ + gtwa->SDOdataCopyStatus = (status & CO_fifo_st_partial) != 0U; + + /* is syntax error in command or size is zero or not the last token + * in command */ + if (((status & CO_fifo_st_errMask) != 0U) || (size == 0U) + || ((gtwa->SDOdataCopyStatus == false) && (closed != 1U))) { + err = true; + break; + } + + /* if data size was not known before and is known now, update SDO */ + if ((gtwa->SDOdataType->length == 0U) && !gtwa->SDOdataCopyStatus) { + CO_SDOclientDownloadInitSize(gtwa->SDO_C, size); + } + + /* continue with state machine */ + gtwa->stateTimeoutTmr = 0; + timeDifference_us = 0; + gtwa->state = CO_GTWA_ST_WRITE; + } +#endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_SDO */ + +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_NMT) != 0 + /* NMT start node - 'start' */ + else if (tok_is_start) { + CO_ReturnError_t ret; + bool_t NodeErr = checkNetNode(gtwa, net, node, 0, &respErrorCode); + CO_NMT_command_t command2 = CO_NMT_ENTER_OPERATIONAL; + + if ((closed != 1U) || NodeErr) { + err = true; + break; + } + ret = CO_NMT_sendCommand(gtwa->NMT, command2, gtwa->node); + + if (ret == CO_ERROR_NO) { + responseWithOK(gtwa); + } else { + respErrorCode = CO_GTWA_respErrorInternalState; + err = true; + break; + } + } + + /* NMT stop node - 'stop' */ + else if (tok_is_stop) { + CO_ReturnError_t ret; + bool_t NodeErr = checkNetNode(gtwa, net, node, 0, &respErrorCode); + CO_NMT_command_t command2 = CO_NMT_ENTER_STOPPED; + + if ((closed != 1U) || NodeErr) { + err = true; + break; + } + ret = CO_NMT_sendCommand(gtwa->NMT, command2, gtwa->node); + + if (ret == CO_ERROR_NO) { + responseWithOK(gtwa); + } else { + respErrorCode = CO_GTWA_respErrorInternalState; + err = true; + break; + } + } + + /* NMT Set node to pre-operational - 'preop[erational]' */ + else if (tok_is_preop) { + CO_ReturnError_t ret; + bool_t NodeErr = checkNetNode(gtwa, net, node, 0, &respErrorCode); + CO_NMT_command_t command2 = CO_NMT_ENTER_PRE_OPERATIONAL; + + if ((closed != 1U) || NodeErr) { + err = true; + break; + } + ret = CO_NMT_sendCommand(gtwa->NMT, command2, gtwa->node); + + if (ret == CO_ERROR_NO) { + responseWithOK(gtwa); + } else { + respErrorCode = CO_GTWA_respErrorInternalState; + err = true; + break; + } + } + + /* NMT reset (node or communication) - 'reset ' */ + else if (tok_is_reset) { + CO_ReturnError_t ret; + bool_t NodeErr = checkNetNode(gtwa, net, node, 0, &respErrorCode); + CO_NMT_command_t command2; + + if ((closed != 0U) || NodeErr) { + err = true; + break; + } + + /* command 2 */ + closed = 1U; + (void)CO_fifo_readToken(>wa->commFifo, tok, sizeof(tok), &closed, &err); + if (err) { + break; + } + + convertToLower(tok, sizeof(tok)); + if (strcmp(tok, "node") == 0) { + command2 = CO_NMT_RESET_NODE; + } else if (strcmp(tok, "comm") == 0) { + command2 = CO_NMT_RESET_COMMUNICATION; + } else if (strcmp(tok, "communication") == 0) { + command2 = CO_NMT_RESET_COMMUNICATION; + } else { + err = true; + break; + } + + ret = CO_NMT_sendCommand(gtwa->NMT, command2, gtwa->node); + + if (ret == CO_ERROR_NO) { + responseWithOK(gtwa); + } else { + respErrorCode = CO_GTWA_respErrorInternalState; + err = true; + break; + } + } +#endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_NMT */ + +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_LSS) != 0 + /* Switch state global command - 'lss_switch_glob <0|1>' */ + else if (tok_is_lss_switch_glob) { + bool_t NodeErr = checkNet(gtwa, net, &respErrorCode); + uint8_t select; + + if ((closed != 0U) || NodeErr) { + err = true; + break; + } + + /* get value */ + closed = 1U; + (void)CO_fifo_readToken(>wa->commFifo, tok, sizeof(tok), &closed, &err); + select = (uint8_t)getU32(tok, 0, 1, &err); + if (err) { + break; + } + + if (select == 0U) { + /* send non-confirmed message */ + CO_LSSmaster_return_t ret; + ret = CO_LSSmaster_swStateDeselect(gtwa->LSSmaster); + if (ret == CO_LSSmaster_OK) { + responseWithOK(gtwa); + } else { + respErrorCode = CO_GTWA_respErrorInternalState; + err = true; + break; + } + } else { + /* continue with state machine */ + gtwa->state = CO_GTWA_ST_LSS_SWITCH_GLOB; + } + } + /* Switch state selective command - + * 'lss_switch_sel ' */ + else if (tok_is_lss_switch_sel) { + bool_t NodeErr = checkNet(gtwa, net, &respErrorCode); + CO_LSS_address_t* addr = >wa->lssAddress; + + if ((closed != 0U) || NodeErr) { + err = true; + break; + } + + /* get values */ + closed = 0U; + (void)CO_fifo_readToken(>wa->commFifo, tok, sizeof(tok), &closed, &err); + addr->identity.vendorID = getU32(tok, 0, 0xFFFFFFFFU, &err); + if (err) { + break; + } + + (void)CO_fifo_readToken(>wa->commFifo, tok, sizeof(tok), &closed, &err); + addr->identity.productCode = getU32(tok, 0, 0xFFFFFFFFU, &err); + if (err) { + break; + } + + (void)CO_fifo_readToken(>wa->commFifo, tok, sizeof(tok), &closed, &err); + addr->identity.revisionNumber = getU32(tok, 0, 0xFFFFFFFFU, &err); + if (err) { + break; + } + + closed = 1U; + (void)CO_fifo_readToken(>wa->commFifo, tok, sizeof(tok), &closed, &err); + addr->identity.serialNumber = getU32(tok, 0, 0xFFFFFFFFU, &err); + if (err) { + break; + } + + /* continue with state machine */ + gtwa->state = CO_GTWA_ST_LSS_SWITCH_SEL; + } + /* LSS configure node-ID command - 'lss_set_node ' */ + else if (tok_is_lss_set_node) { + bool_t NodeErr = checkNet(gtwa, net, &respErrorCode); + + if ((closed != 0U) || NodeErr) { + err = true; + break; + } + + /* get value */ + closed = 1U; + (void)CO_fifo_readToken(>wa->commFifo, tok, sizeof(tok), &closed, &err); + gtwa->lssNID = (uint8_t)getU32(tok, 0, 0xFF, &err); + if ((gtwa->lssNID > 0x7FU) && (gtwa->lssNID < 0xFFU)) { + err = true; + } + if (err) { + break; + } + + /* continue with state machine */ + gtwa->state = CO_GTWA_ST_LSS_SET_NODE; + } + /* LSS configure bit-rate command - + * 'lss_conf_bitrate ' + * table_index: 0=1000 kbit/s, 1=800 kbit/s, 2=500 kbit/s, 3=250 kbit/s, + * 4=125 kbit/s, 6=50 kbit/s, 7=20 kbit/s, 8=10 kbit/s, 9=auto */ + else if (tok_is_lss_conf_bitrate) { + bool_t NodeErr = checkNet(gtwa, net, &respErrorCode); + uint8_t tableIndex; + uint32_t maxIndex = (sizeof(CO_LSS_bitTimingTableLookup) / sizeof(CO_LSS_bitTimingTableLookup[0])) - 1U; + + if ((closed != 0U) || NodeErr) { + err = true; + break; + } + + /* First parameter is table selector. We only support the CiA bit timing table from CiA301 ("0") */ + closed = 0U; + (void)CO_fifo_readToken(>wa->commFifo, tok, sizeof(tok), &closed, &err); + (void)getU32(tok, 0, 0, &err); + + /* get value */ + closed = 1U; + (void)CO_fifo_readToken(>wa->commFifo, tok, sizeof(tok), &closed, &err); + tableIndex = (uint8_t)getU32(tok, 0, maxIndex, &err); + if (tableIndex == 5U) { + err = true; + } + if (err) { + break; + } + gtwa->lssBitrate = CO_LSS_bitTimingTableLookup[tableIndex]; + + /* continue with state machine */ + gtwa->state = CO_GTWA_ST_LSS_CONF_BITRATE; + } + /* LSS activate new bit-rate command - + * 'lss_activate_bitrate ' */ + else if (tok_is_lss_activate_bitrate) { + bool_t NodeErr = checkNet(gtwa, net, &respErrorCode); + uint16_t switchDelay; + CO_LSSmaster_return_t ret; + + if ((closed != 0U) || NodeErr) { + err = true; + break; + } + + /* get value */ + closed = 1U; + (void)CO_fifo_readToken(>wa->commFifo, tok, sizeof(tok), &closed, &err); + switchDelay = (uint16_t)getU32(tok, 0, 0xFFFF, &err); + if (err) { + break; + } + + /* send non-confirmed message */ + ret = CO_LSSmaster_ActivateBit(gtwa->LSSmaster, switchDelay); + if (ret == CO_LSSmaster_OK) { + responseWithOK(gtwa); + } else { + respErrorCode = CO_GTWA_respErrorInternalState; + err = true; + break; + } + } + /* LSS store configuration command - 'lss_store' */ + else if (tok_is_lss_store) { + bool_t NodeErr = checkNet(gtwa, net, &respErrorCode); + + if ((closed != 1U) || NodeErr) { + err = true; + break; + } + + /* continue with state machine */ + gtwa->state = CO_GTWA_ST_LSS_STORE; + } + /* Inquire LSS address command - 'lss_inquire_addr []' */ + else if (tok_is_lss_inquire_addr) { + bool_t NodeErr = checkNet(gtwa, net, &respErrorCode); + + if (NodeErr) { + err = true; + break; + } + + if (closed == 0U) { + uint8_t lsssub; + /* get value */ + closed = 1U; + (void)CO_fifo_readToken(>wa->commFifo, tok, sizeof(tok), &closed, &err); + lsssub = (uint8_t)getU32(tok, 0, 3, &err); + if (err) { + break; + } + switch (lsssub) { + case 0: gtwa->lssInquireCs = CO_LSS_INQUIRE_VENDOR; break; + case 1: gtwa->lssInquireCs = CO_LSS_INQUIRE_PRODUCT; break; + case 2: gtwa->lssInquireCs = CO_LSS_INQUIRE_REV; break; + default: gtwa->lssInquireCs = CO_LSS_INQUIRE_SERIAL; break; + } + + /* continue with state machine */ + gtwa->state = CO_GTWA_ST_LSS_INQUIRE; + } else { + /* continue with state machine */ + gtwa->state = CO_GTWA_ST_LSS_INQUIRE_ADDR_ALL; + } + } + /* LSS inquire node-ID command - 'lss_get_node' */ + else if (tok_is_lss_get_node) { + bool_t NodeErr = checkNet(gtwa, net, &respErrorCode); + + if ((closed != 1U) || NodeErr) { + err = true; + break; + } + + /* continue with state machine */ + gtwa->lssInquireCs = CO_LSS_INQUIRE_NODE_ID; + gtwa->state = CO_GTWA_ST_LSS_INQUIRE; + } + /* LSS identify fastscan. This is a manufacturer specific command as + * the one in DSP309 is quite useless - '_lss_fastscan []' */ + else if (tok_is__lss_fastscan) { + bool_t NodeErr = checkNet(gtwa, net, &respErrorCode); + uint16_t timeout_ms = 0; + + if (NodeErr) { + err = true; + break; + } + + if (closed == 0U) { + /* get value */ + closed = 1U; + (void)CO_fifo_readToken(>wa->commFifo, tok, sizeof(tok), &closed, &err); + timeout_ms = (uint16_t)getU32(tok, 0, 0xFFFF, &err); + if (err) { + break; + } + } + + /* If timeout not specified, use 100ms. Should work in most cases */ + if (timeout_ms == 0U) { + timeout_ms = 100; + } + CO_LSSmaster_changeTimeout(gtwa->LSSmaster, timeout_ms); + + /* prepare lssFastscan, all zero */ + (void)memset(>wa->lssFastscan, 0, sizeof(gtwa->lssFastscan)); + + /* continue with state machine */ + gtwa->state = CO_GTWA_ST__LSS_FASTSCAN; + } + /* LSS complete node-ID configuration command - 'lss_allnodes + * [ [ + * + * ]]' */ + else if (tok_is_lss_allnodes) { + /* Request node enumeration by LSS identify fastscan. This initiates node enumeration + * by the means of LSS fastscan mechanism. When this function is finished: + * - All nodes that match the given criteria are assigned a node ID beginning with nodeId. + * If 127 is reached, the process is stopped, no matter if there are nodes remaining or not. + * - No IDs are assigned because: + * - the given criteria do not match any node, + * - all nodes are already configured. + * This function needs that no node is selected when starting the scan process. */ + bool_t NodeErr = checkNet(gtwa, net, &respErrorCode); + uint16_t timeout_ms = 0; + + if (NodeErr) { + err = true; + break; + } + + if (closed == 0U) { + /* get optional token timeout (non standard) */ + closed = 0xFFU; + (void)CO_fifo_readToken(>wa->commFifo, tok, sizeof(tok), &closed, &err); + timeout_ms = (uint16_t)getU32(tok, 0, 0xFFFF, &err); + if (err) { + break; + } + } + /* If timeout not specified, use 100ms. Should work in most cases */ + gtwa->lssTimeout_ms = (timeout_ms == 0U) ? 100U : timeout_ms; + CO_LSSmaster_changeTimeout(gtwa->LSSmaster, gtwa->lssTimeout_ms); + gtwa->lssNodeCount = 0; + gtwa->lssSubState = 0; + + if (closed == 1U) { + /* No other arguments, as by CiA specification for this command. Do full scan. */ + /* use start node ID 2. Should work in most cases */ + gtwa->lssNID = 2; + /* store node ID in node's NVM */ + gtwa->lssStore = true; + /* prepare lssFastscan, all zero */ + (void)memset(>wa->lssFastscan, 0, sizeof(gtwa->lssFastscan)); + } + if (closed == 0U) { + /* more arguments follow */ + (void)CO_fifo_readToken(>wa->commFifo, tok, sizeof(tok), &closed, &err); + gtwa->lssNID = (uint8_t)getU32(tok, 1, 127, &err); + if (err) { + break; + } + + closed = 0xFFU; + (void)CO_fifo_readToken(>wa->commFifo, tok, sizeof(tok), &closed, &err); + gtwa->lssStore = (bool_t)getU32(tok, 0, 1, &err); + if (err) { + break; + } + + if (closed == 1U) { + /* No other arguments, prepare lssFastscan, all zero */ + (void)memset(>wa->lssFastscan, 0, sizeof(gtwa->lssFastscan)); + } + } + if (closed == 0U) { + /* more arguments follow */ + CO_LSSmaster_fastscan_t* fs = >wa->lssFastscan; + + (void)CO_fifo_readToken(>wa->commFifo, tok, sizeof(tok), &closed, &err); + fs->scan[CO_LSS_FASTSCAN_VENDOR_ID] = (CO_LSSmaster_scantype_t)getU32(tok, 0, 2, &err); + if (err) { + break; + } + + (void)CO_fifo_readToken(>wa->commFifo, tok, sizeof(tok), &closed, &err); + fs->match.identity.vendorID = getU32(tok, 0, 0xFFFFFFFFU, &err); + if (err) { + break; + } + + (void)CO_fifo_readToken(>wa->commFifo, tok, sizeof(tok), &closed, &err); + fs->scan[CO_LSS_FASTSCAN_PRODUCT] = (CO_LSSmaster_scantype_t)getU32(tok, 0, 2, &err); + if (err) { + break; + } + + (void)CO_fifo_readToken(>wa->commFifo, tok, sizeof(tok), &closed, &err); + fs->match.identity.productCode = getU32(tok, 0, 0xFFFFFFFFU, &err); + if (err) { + break; + } + + (void)CO_fifo_readToken(>wa->commFifo, tok, sizeof(tok), &closed, &err); + fs->scan[CO_LSS_FASTSCAN_REV] = (CO_LSSmaster_scantype_t)getU32(tok, 0, 2, &err); + if (err) { + break; + } + + (void)CO_fifo_readToken(>wa->commFifo, tok, sizeof(tok), &closed, &err); + fs->match.identity.revisionNumber = getU32(tok, 0, 0xFFFFFFFFU, &err); + if (err) { + break; + } + + (void)CO_fifo_readToken(>wa->commFifo, tok, sizeof(tok), &closed, &err); + fs->scan[CO_LSS_FASTSCAN_SERIAL] = (CO_LSSmaster_scantype_t)getU32(tok, 0, 2, &err); + if (err) { + break; + } + + closed = 1U; + (void)CO_fifo_readToken(>wa->commFifo, tok, sizeof(tok), &closed, &err); + fs->match.identity.serialNumber = getU32(tok, 0, 0xFFFFFFFFU, &err); + if (err) { + break; + } + } + + /* continue with state machine */ + gtwa->state = CO_GTWA_ST_LSS_ALLNODES; + } +#endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_LSS */ + +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_LOG) != 0 + /* Print message log */ + else if (tok_is_log) { + if (closed == 0U) { + err = true; + break; + } + gtwa->state = CO_GTWA_ST_LOG; + } +#endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_LOG */ + +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_PRINT_HELP) != 0 + /* Print help */ + else if (tok_is_help) { + if (closed == 1U) { + gtwa->helpString = CO_GTWA_helpString; + } else { + /* get second token */ + closed = 1U; + (void)CO_fifo_readToken(>wa->commFifo, tok, sizeof(tok), &closed, &err); + if (err) { + break; + } + + convertToLower(tok, sizeof(tok)); + if (strcmp(tok, "datatype") == 0) { + gtwa->helpString = CO_GTWA_helpStringDatatypes; + } else if (strcmp(tok, "lss") == 0) { + gtwa->helpString = CO_GTWA_helpStringLss; + } else { + err = true; + break; + } + } + /* continue with state machine */ + gtwa->helpStringOffset = 0; + gtwa->state = CO_GTWA_ST_HELP; + } +#endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_PRINT_HELP */ + +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_PRINT_LEDS) != 0 + /* Print status led diodes */ + else if (tok_is_led) { + if (closed == 0U) { + err = true; + break; + } + gtwa->ledStringPreviousIndex = 0xFF; + gtwa->state = CO_GTWA_ST_LED; + } +#endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_PRINT_LEDS */ + + /* Unrecognized command */ + else { + respErrorCode = CO_GTWA_respErrorReqNotSupported; + err = true; + break; + } + } /* while CO_GTWA_ST_IDLE && CO_fifo_CommSearch */ + + /*************************************************************************** + * STATE MACHINE + ***************************************************************************/ + /* If error, generate error response */ + if (err) { + if (respErrorCode == CO_GTWA_respErrorNone) { + respErrorCode = CO_GTWA_respErrorSyntax; + } + responseWithError(gtwa, respErrorCode); + + /* delete command, if it was only partially read */ + if (closed == 0U) { + (void)CO_fifo_CommSearch(>wa->commFifo, true); + } + gtwa->state = CO_GTWA_ST_IDLE; + } + + else { + switch (gtwa->state) { + case CO_GTWA_ST_IDLE: { + return; /* skip timerNext_us calculation */ + break; + } + +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_SDO) != 0 + /* SDO upload state */ + case CO_GTWA_ST_READ: { + CO_SDO_abortCode_t abortCode; + size_t sizeTransferred; + CO_SDO_return_t ret; + + ret = CO_SDOclientUpload(gtwa->SDO_C, timeDifference_us, false, &abortCode, NULL, &sizeTransferred, + timerNext_us); + + if (ret < CO_SDO_RT_ok_communicationEnd) { + responseWithErrorSDO(gtwa, abortCode, gtwa->SDOdataCopyStatus); + gtwa->state = CO_GTWA_ST_IDLE; + } + /* Response data must be read, partially or whole */ + else if ((ret == CO_SDO_RT_uploadDataBufferFull) || (ret == CO_SDO_RT_ok_communicationEnd)) { + size_t fifoRemain; + + /* write response head first */ + if (!gtwa->SDOdataCopyStatus) { + gtwa->respBufCount = (size_t)snprintf(gtwa->respBuf, CO_GTWA_RESP_BUF_SIZE - 2U, + "[%" PRId32 "] ", (int32_t)gtwa->sequence); + gtwa->SDOdataCopyStatus = true; + } + + /* Empty SDO fifo buffer in multiple cycles. Repeat until + * application runs out of space (respHold) or fifo empty. */ + do { + /* read SDO fifo (partially) and print specific data type as + * ascii into intermediate respBuf */ + gtwa->respBufCount += gtwa->SDOdataType->dataTypePrint( + >wa->SDO_C->bufFifo, >wa->respBuf[gtwa->respBufCount], + (CO_GTWA_RESP_BUF_SIZE - 2U) - gtwa->respBufCount, ret == CO_SDO_RT_ok_communicationEnd); + fifoRemain = CO_fifo_getOccupied(>wa->SDO_C->bufFifo); + + /* end of communication, print newline and enter idle state */ + if ((ret == CO_SDO_RT_ok_communicationEnd) && (fifoRemain == 0U)) { + gtwa->respBufCount += (size_t)sprintf(>wa->respBuf[gtwa->respBufCount], "\r\n"); + gtwa->state = CO_GTWA_ST_IDLE; + } + + /* transfer response to the application */ + if (respBufTransfer(gtwa) == false) { + /* broken communication, send SDO abort and force finish. */ + abortCode = CO_SDO_AB_DATA_TRANSF; + (void)CO_SDOclientUpload(gtwa->SDO_C, 0, true, &abortCode, NULL, NULL, NULL); + gtwa->state = CO_GTWA_ST_IDLE; + break; + } + } while ((gtwa->respHold == false) && (fifoRemain > 0U)); + } else { /* MISRA C 2004 14.10 */ + } + break; + } + + /* SDO download state */ + case CO_GTWA_ST_WRITE: + case CO_GTWA_ST_WRITE_ABORTED: { + CO_SDO_abortCode_t abortCode; + size_t sizeTransferred; + bool_t abort_comm = false; + bool_t hold = false; + CO_SDO_return_t ret; + + /* copy data to the SDO buffer if previous dataTypeScan was partial */ + if (gtwa->SDOdataCopyStatus) { + uint8_t status; + gtwa->SDOdataType->dataTypeScan(>wa->SDO_C->bufFifo, >wa->commFifo, &status); + /* set to true, if command delimiter was found */ + closed = ((status & CO_fifo_st_closed) == 0U) ? 0U : 1U; + /* set to true, if data are copied only partially */ + gtwa->SDOdataCopyStatus = (status & CO_fifo_st_partial) != 0U; + + /* is syntax error in command or not the last token in command */ + if (((status & CO_fifo_st_errMask) != 0U) + || ((gtwa->SDOdataCopyStatus == false) && (closed != 1U))) { + abortCode = CO_SDO_AB_DEVICE_INCOMPAT; + abort_comm = true; /* abort SDO communication */ + /* clear the rest of the command, if necessary */ + if (closed != 1U) { + (void)CO_fifo_CommSearch(>wa->commFifo, true); + } + } + if (gtwa->state == CO_GTWA_ST_WRITE_ABORTED) { + /* Stay in this state, until all data transferred via commFifo will be purged. */ + if (!CO_fifo_purge(>wa->SDO_C->bufFifo) || (closed == 1U)) { + gtwa->state = CO_GTWA_ST_IDLE; + } + break; + } + } + /* If not all data were transferred, make sure, there is enough data in + * SDO buffer, to continue communication. Otherwise wait and check for timeout */ + if (gtwa->SDOdataCopyStatus) { + if (CO_fifo_getOccupied(>wa->SDO_C->bufFifo) < (CO_CONFIG_GTW_BLOCK_DL_LOOP * 7U)) { + if (gtwa->stateTimeoutTmr > CO_GTWA_STATE_TIMEOUT_TIME_US) { + abortCode = CO_SDO_AB_DEVICE_INCOMPAT; + abort_comm = true; + } else { + gtwa->stateTimeoutTmr += timeDifference_us; + hold = true; + } + } + } + if (!hold || abort_comm) { + /* if OS has CANtx queue, speedup block transfer */ + uint32_t loop = 0; + do { + ret = CO_SDOclientDownload(gtwa->SDO_C, timeDifference_us, abort_comm, gtwa->SDOdataCopyStatus, + &abortCode, &sizeTransferred, timerNext_us); + if (++loop >= CO_CONFIG_GTW_BLOCK_DL_LOOP) { + break; + } + } while (ret == CO_SDO_RT_blockDownldInProgress); + + /* send response in case of error or finish */ + if (ret < CO_SDO_RT_ok_communicationEnd) { + responseWithErrorSDO(gtwa, abortCode, false); + /* purge remaining data if necessary */ + gtwa->state = gtwa->SDOdataCopyStatus ? CO_GTWA_ST_WRITE_ABORTED : CO_GTWA_ST_IDLE; + } else if (ret == CO_SDO_RT_ok_communicationEnd) { + responseWithOK(gtwa); + gtwa->state = CO_GTWA_ST_IDLE; + } else { /* MISRA C 2004 14.10 */ + } + } + break; + } +#endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_SDO */ + +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_LSS) != 0 + case CO_GTWA_ST_LSS_SWITCH_GLOB: { + CO_LSSmaster_return_t ret; + ret = CO_LSSmaster_swStateSelect(gtwa->LSSmaster, timeDifference_us, NULL); + if (ret != CO_LSSmaster_WAIT_SLAVE) { + responseLSS(gtwa, ret); + gtwa->state = CO_GTWA_ST_IDLE; + } + break; + } + case CO_GTWA_ST_LSS_SWITCH_SEL: { + CO_LSSmaster_return_t ret; + ret = CO_LSSmaster_swStateSelect(gtwa->LSSmaster, timeDifference_us, >wa->lssAddress); + if (ret != CO_LSSmaster_WAIT_SLAVE) { + responseLSS(gtwa, ret); + gtwa->state = CO_GTWA_ST_IDLE; + } + break; + } + case CO_GTWA_ST_LSS_SET_NODE: { + CO_LSSmaster_return_t ret; + ret = CO_LSSmaster_configureNodeId(gtwa->LSSmaster, timeDifference_us, gtwa->lssNID); + if (ret != CO_LSSmaster_WAIT_SLAVE) { + if (ret == CO_LSSmaster_OK_ILLEGAL_ARGUMENT) { + respErrorCode = CO_GTWA_respErrorLSSnodeIdNotSupported; + responseWithError(gtwa, respErrorCode); + } else { + responseLSS(gtwa, ret); + } + gtwa->state = CO_GTWA_ST_IDLE; + } + break; + } + case CO_GTWA_ST_LSS_CONF_BITRATE: { + CO_LSSmaster_return_t ret; + ret = CO_LSSmaster_configureBitTiming(gtwa->LSSmaster, timeDifference_us, gtwa->lssBitrate); + if (ret != CO_LSSmaster_WAIT_SLAVE) { + if (ret == CO_LSSmaster_OK_ILLEGAL_ARGUMENT) { + respErrorCode = CO_GTWA_respErrorLSSbitRateNotSupported; + responseWithError(gtwa, respErrorCode); + } else { + responseLSS(gtwa, ret); + } + gtwa->state = CO_GTWA_ST_IDLE; + } + break; + } + case CO_GTWA_ST_LSS_STORE: { + CO_LSSmaster_return_t ret; + + ret = CO_LSSmaster_configureStore(gtwa->LSSmaster, timeDifference_us); + if (ret != CO_LSSmaster_WAIT_SLAVE) { + if (ret == CO_LSSmaster_OK_ILLEGAL_ARGUMENT) { + respErrorCode = CO_GTWA_respErrorLSSparameterStoringFailed; + responseWithError(gtwa, respErrorCode); + } else { + responseLSS(gtwa, ret); + } + gtwa->state = CO_GTWA_ST_IDLE; + } + break; + } + case CO_GTWA_ST_LSS_INQUIRE: { + CO_LSSmaster_return_t ret; + uint32_t value; + + ret = CO_LSSmaster_Inquire(gtwa->LSSmaster, timeDifference_us, gtwa->lssInquireCs, &value); + if (ret != CO_LSSmaster_WAIT_SLAVE) { + if (ret == CO_LSSmaster_OK) { + if (gtwa->lssInquireCs == CO_LSS_INQUIRE_NODE_ID) { + gtwa->respBufCount = (size_t)snprintf(gtwa->respBuf, CO_GTWA_RESP_BUF_SIZE, + "[%" PRId32 "] 0x%02" PRIX32 "\r\n", + (int32_t)gtwa->sequence, value & 0xFFU); + } else { + gtwa->respBufCount = (size_t)snprintf(gtwa->respBuf, CO_GTWA_RESP_BUF_SIZE, + "[%" PRId32 "] 0x%08" PRIX32 "\r\n", + (int32_t)gtwa->sequence, value); + } + (void)respBufTransfer(gtwa); + } else { + responseLSS(gtwa, ret); + } + gtwa->state = CO_GTWA_ST_IDLE; + } + break; + } + case CO_GTWA_ST_LSS_INQUIRE_ADDR_ALL: { + CO_LSSmaster_return_t ret; + + ret = CO_LSSmaster_InquireLssAddress(gtwa->LSSmaster, timeDifference_us, >wa->lssAddress); + if (ret != CO_LSSmaster_WAIT_SLAVE) { + if (ret == CO_LSSmaster_OK) { + gtwa->respBufCount = (size_t)snprintf( + gtwa->respBuf, CO_GTWA_RESP_BUF_SIZE, + "[%" PRId32 "] 0x%08" PRIX32 " 0x%08" PRIX32 " 0x%08" PRIX32 " 0x%08" PRIX32 "\r\n", + (int32_t)gtwa->sequence, gtwa->lssAddress.identity.vendorID, + gtwa->lssAddress.identity.productCode, gtwa->lssAddress.identity.revisionNumber, + gtwa->lssAddress.identity.serialNumber); + (void)respBufTransfer(gtwa); + } else { + responseLSS(gtwa, ret); + } + gtwa->state = CO_GTWA_ST_IDLE; + } + break; + } + case CO_GTWA_ST__LSS_FASTSCAN: { + CO_LSSmaster_return_t ret; + + ret = CO_LSSmaster_IdentifyFastscan(gtwa->LSSmaster, timeDifference_us, >wa->lssFastscan); + if (ret != CO_LSSmaster_WAIT_SLAVE) { + if ((ret == CO_LSSmaster_OK) || (ret == CO_LSSmaster_SCAN_FINISHED)) { + gtwa->respBufCount = (size_t)snprintf( + gtwa->respBuf, CO_GTWA_RESP_BUF_SIZE, + "[%" PRId32 "] 0x%08" PRIX32 " 0x%08" PRIX32 " 0x%08" PRIX32 " 0x%08" PRIX32 "\r\n", + (int32_t)gtwa->sequence, gtwa->lssFastscan.found.identity.vendorID, + gtwa->lssFastscan.found.identity.productCode, + gtwa->lssFastscan.found.identity.revisionNumber, + gtwa->lssFastscan.found.identity.serialNumber); + (void)respBufTransfer(gtwa); + } else { + responseLSS(gtwa, ret); + } + CO_LSSmaster_changeTimeout(gtwa->LSSmaster, CO_LSSmaster_DEFAULT_TIMEOUT); + gtwa->state = CO_GTWA_ST_IDLE; + } + break; + } + case CO_GTWA_ST_LSS_ALLNODES: { + CO_LSSmaster_return_t ret; + if (gtwa->lssSubState == 0U) { /* _lss_fastscan */ + ret = CO_LSSmaster_IdentifyFastscan(gtwa->LSSmaster, timeDifference_us, >wa->lssFastscan); + if (ret != CO_LSSmaster_WAIT_SLAVE) { + CO_LSSmaster_changeTimeout(gtwa->LSSmaster, CO_LSSmaster_DEFAULT_TIMEOUT); + + if ((ret == CO_LSSmaster_OK) || (ret == CO_LSSmaster_SCAN_NOACK)) { + /* no (more) nodes found, send report sum and finish */ + gtwa->respBufCount = (size_t)snprintf(gtwa->respBuf, CO_GTWA_RESP_BUF_SIZE, + "# Found %d nodes, search finished.\n" + "[%" PRId32 "] OK\r\n", + gtwa->lssNodeCount, (int32_t)gtwa->sequence); + (void)respBufTransfer(gtwa); + gtwa->state = CO_GTWA_ST_IDLE; + } else if (ret == CO_LSSmaster_SCAN_FINISHED) { + /* next sub-step */ + gtwa->lssSubState++; + } else { + /* error occurred */ + responseLSS(gtwa, ret); + gtwa->state = CO_GTWA_ST_IDLE; + } + } + } + if (gtwa->lssSubState == 1U) { /* lss_set_node */ + ret = CO_LSSmaster_configureNodeId(gtwa->LSSmaster, timeDifference_us, gtwa->lssNID); + if (ret != CO_LSSmaster_WAIT_SLAVE) { + if (ret == CO_LSSmaster_OK) { + /* next sub-step */ + gtwa->lssSubState += gtwa->lssStore ? 1U : 2U; + } else { + /* error occurred */ + if (ret == CO_LSSmaster_OK_ILLEGAL_ARGUMENT) { + respErrorCode = CO_GTWA_respErrorLSSnodeIdNotSupported; + responseWithError(gtwa, respErrorCode); + } else { + responseLSS(gtwa, ret); + } + gtwa->state = CO_GTWA_ST_IDLE; + } + } + } + if (gtwa->lssSubState == 2U) { /* lss_store */ + ret = CO_LSSmaster_configureStore(gtwa->LSSmaster, timeDifference_us); + if (ret != CO_LSSmaster_WAIT_SLAVE) { + if (ret == CO_LSSmaster_OK) { + /* next sub-step */ + gtwa->lssSubState++; + } else { + /* error occurred */ + if (ret == CO_LSSmaster_OK_ILLEGAL_ARGUMENT) { + respErrorCode = CO_GTWA_respErrorLSSparameterStoringFailed; + responseWithError(gtwa, respErrorCode); + } else { + responseLSS(gtwa, ret); + } + gtwa->state = CO_GTWA_ST_IDLE; + } + } + } + if (gtwa->lssSubState >= 3U) { /* lss_switch_glob 0 */ + /* send non-confirmed message */ + ret = CO_LSSmaster_swStateDeselect(gtwa->LSSmaster); + if (ret != CO_LSSmaster_OK) { + /* error occurred */ + responseLSS(gtwa, ret); + gtwa->state = CO_GTWA_ST_IDLE; + } else { + /* cycle finished successfully, send report */ + uint8_t lssNidAssigned = gtwa->lssNID; + const char msg2Fmt[] = "# Not all nodes scanned!\n" + "[%" PRId32 "] OK\r\n"; + char msg2[sizeof(msg2Fmt) + 10U] = {0}; + + /* increment variables, check end-of-nodeId */ + gtwa->lssNodeCount++; + if (gtwa->lssNID < 127U) { + /* repeat cycle with next node-id */ + gtwa->lssNID++; + CO_LSSmaster_changeTimeout(gtwa->LSSmaster, gtwa->lssTimeout_ms); + gtwa->lssSubState = 0; + } else { + /* If we can't assign more node IDs, quit scanning */ + sprintf(msg2, msg2Fmt, (int32_t)gtwa->sequence); + gtwa->state = CO_GTWA_ST_IDLE; + } + + /* send report */ + gtwa->respBufCount = (size_t)snprintf(gtwa->respBuf, CO_GTWA_RESP_BUF_SIZE, + "# Node-ID %d assigned to: 0x%08" PRIX32 " 0x%08" PRIX32 + " 0x%08" PRIX32 " 0x%08" PRIX32 "\n%s", + lssNidAssigned, gtwa->lssFastscan.found.identity.vendorID, + gtwa->lssFastscan.found.identity.productCode, + gtwa->lssFastscan.found.identity.revisionNumber, + gtwa->lssFastscan.found.identity.serialNumber, msg2); + (void)respBufTransfer(gtwa); + } + } + break; + } /* CO_GTWA_ST_LSS_ALLNODES */ +#endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_LSS */ + +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_LOG) != 0 + /* print message log */ + case CO_GTWA_ST_LOG: { + CO_fifo_putc_ov(>wa->logFifo, '\r'); + CO_fifo_putc_ov(>wa->logFifo, '\n'); + do { + gtwa->respBufCount = CO_fifo_read(>wa->logFifo, (uint8_t*)gtwa->respBuf, CO_GTWA_RESP_BUF_SIZE, + NULL); + (void)respBufTransfer(gtwa); + + if (CO_fifo_getOccupied(>wa->logFifo) == 0U) { + gtwa->state = CO_GTWA_ST_IDLE; + break; + } + } while (gtwa->respHold == false); + break; + } +#endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_LOG */ + +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_PRINT_HELP) != 0 + /* Print help string (in multiple segments if necessary) */ + case CO_GTWA_ST_HELP: { + size_t lenBuf = CO_GTWA_RESP_BUF_SIZE; + size_t lenHelp = strlen(gtwa->helpString); + + do { + size_t lenHelpRemain = lenHelp - gtwa->helpStringOffset; + size_t lenCopied = (lenBuf < lenHelpRemain) ? lenBuf : lenHelpRemain; + + (void)memcpy(gtwa->respBuf, >wa->helpString[gtwa->helpStringOffset], lenCopied); + + gtwa->respBufCount = lenCopied; + gtwa->helpStringOffset += lenCopied; + (void)respBufTransfer(gtwa); + + if (gtwa->helpStringOffset == lenHelp) { + gtwa->state = CO_GTWA_ST_IDLE; + break; + } + } while (gtwa->respHold == false); + break; + } +#endif + +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_PRINT_LEDS) != 0 + /* print CANopen status LED diodes */ + case CO_GTWA_ST_LED: { + uint8_t i; + + if (CO_fifo_CommSearch(>wa->commFifo, false)) { + gtwa->state = CO_GTWA_ST_IDLE; + i = 4; + } else { + i = (CO_LED_RED(gtwa->LEDs, CO_LED_CANopen) * 2U) + CO_LED_GREEN(gtwa->LEDs, CO_LED_CANopen); + } + if (i > (CO_GTWA_LED_PRINTOUTS_SIZE - 1U)) { + i = CO_GTWA_LED_PRINTOUTS_SIZE - 1U; + } + + if (i != gtwa->ledStringPreviousIndex) { + gtwa->respBufCount = (size_t)snprintf(gtwa->respBuf, CO_GTWA_RESP_BUF_SIZE, "%s", + CO_GTWA_LED_PRINTOUTS[i]); + (void)respBufTransfer(gtwa); + gtwa->ledStringPreviousIndex = i; + } + break; + } +#endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_PRINT_LEDS */ + + /* illegal state */ + default: { + respErrorCode = CO_GTWA_respErrorInternalState; + responseWithError(gtwa, respErrorCode); + gtwa->state = CO_GTWA_ST_IDLE; + break; + } + } /* switch (gtwa->state) */ + } + + /* execute next CANopen processing immediately, if idle and more commands available */ + if ((timerNext_us != NULL) && (gtwa->state == CO_GTWA_ST_IDLE)) { + if (CO_fifo_CommSearch(>wa->commFifo, false)) { + *timerNext_us = 0; + } + } +} + +#endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII */ diff --git a/309/CO_gateway_ascii.h b/309/CO_gateway_ascii.h new file mode 100644 index 00000000..1c1da6d5 --- /dev/null +++ b/309/CO_gateway_ascii.h @@ -0,0 +1,410 @@ +/** + * CANopen access from other networks - ASCII mapping (CiA 309-3 DS v3.0.0) + * + * @file CO_gateway_ascii.h + * @ingroup CO_CANopen_309_3 + * @author Janez Paternoster + * @author Martin Wagner + * @copyright 2020 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#ifndef CO_GATEWAY_ASCII_H +#define CO_GATEWAY_ASCII_H + +#include "301/CO_driver.h" +#include "301/CO_fifo.h" +#include "301/CO_SDOclient.h" +#include "301/CO_NMT_Heartbeat.h" +#include "305/CO_LSSmaster.h" +#include "303/CO_LEDs.h" + +/* default configuration, see CO_config.h */ +#ifndef CO_CONFIG_GTW +#define CO_CONFIG_GTW (0) +#endif + +#if (((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII) != 0) || defined CO_DOXYGEN + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup CO_CANopen_309_3 Gateway ASCII mapping + * CANopen access from other networks - ASCII mapping (CiA 309-3 DSP v3.0.0) + * + * @ingroup CO_CANopen_309 + * @{ + * This module enables ascii command interface (CAN gateway), which can be used for master interaction with CANopen + * network. Some sort of string input/output stream can be used, for example serial port + terminal on microcontroller + * or stdio in OS or sockets, etc. + * + * For example, one wants to read 'Heartbeat producer time' parameter (0x1017,0) on remote node (with id=4). Parameter + * is 16-bit integer. He can can enter command string: `[1] 4 read 0x1017 0 i16`. CANopenNode will use SDO client, send + * request to remote node via CAN, wait for response via CAN and prints `[1] OK` to output stream on success. + * + * This module is usually initialized and processed in CANopen.c file. Application should register own callback function + * for reading the output stream. Application writes new commands with CO_GTWA_write(). + */ + +/** + * @defgroup CO_CANopen_309_3_Syntax Command syntax + * ASCII command syntax. + * + * @{ + * + * @code{.unparsed} +Command strings start with '"[""]"' followed by: +[[] ] r[ead] [] # SDO upload. +[[] ] w[rite] # SDO download. + +[[] ] start # NMT Start node. +[[] ] stop # NMT Stop node. +[[] ] preop[erational] # NMT Set node to pre-operational. +[[] ] reset node # NMT Reset node. +[[] ] reset comm[unication] # NMT Reset communication. + +[] set network # Set default net. +[] set node # Set default node. +[] set sdo_timeout # Configure SDO time-out. +[] set sdo_block # Enable/disable SDO block transfer. + +help [datatype|lss] # Print this or datatype or lss help. +led # Print status LED diodes. +log # Print message log. + +Response: +"[""]" OK | | + ERROR: | ERROR: + +* Every command must be terminated with ('\\r\\n'). characters. Same + is response. String is not null terminated, is optional in command. +* Comments started with '#' are ignored. They may be on the beginning of the + line or after the command string. +* 'sdo_timeout' is in milliseconds, 500 by default. Block transfer is + disabled by default. +* If '' or '' is not specified within commands, then value defined + by 'set network' or 'set node' command is used. + +Datatypes: +b # Boolean. +i8, i16, i32, i64 # Signed integers. +u8, u16, u32, u64 # Unsigned integers. +x8, x16, x32, x64 # Unsigned integers, displayed as hexadecimal, non-standard. +r32, r64 # Real numbers. +t, td # Time of day, time difference. +vs # Visible string (between double quotes if multi-word). +os, us # Octet, unicode string, (mime-base64 (RFC2045) based, line). +d # domain (mime-base64 (RFC2045) based, one line). +hex # Hexagonal data, optionally space separated, non-standard. + +LSS commands: +lss_switch_glob <0|1> # Switch state global command. +lss_switch_sel \\ + #Switch state selective. +lss_set_node # Configure node-ID. +lss_conf_bitrate \\ + # Configure bit-rate. +lss_activate_bitrate # Activate new bit-rate. +lss_store # LSS store configuration. +lss_inquire_addr [] # Inquire LSS address. +lss_get_node # Inquire node-ID. +_lss_fastscan [] # Identify fastscan, non-standard. +lss_allnodes [ [ \\ + [ \\ + ]]] + # Node-ID configuration of all nodes. + +* All LSS commands start with '\"[\"\"]\" []'. +* : 0=1000 kbit/s, 1=800 kbit/s, 2=500 kbit/s, 3=250 kbit/s, + 4=125 kbit/s, 6=50 kbit/s, 7=20 kbit/s, 8=10 kbit/s, 9=auto +* : 0=fastscan, 1=ignore, 2=match value in next parameter + * @endcode + * + * This help text is the same as variable contents in CO_GTWA_helpString. + * @} + */ + +/** Size of response string buffer. This is intermediate buffer. If there is larger amount of data to transfer, then + * multiple transfers will occur. */ +#ifndef CO_GTWA_RESP_BUF_SIZE +#define CO_GTWA_RESP_BUF_SIZE 200U +#endif + +/** Timeout time in microseconds for some internal states. */ +#ifndef CO_GTWA_STATE_TIMEOUT_TIME_US +#define CO_GTWA_STATE_TIMEOUT_TIME_US 1200000U +#endif + +/** + * Response error codes as specified by CiA 309-3. Values less or equal to 0 are used for control for some functions and + * are not part of the standard. + */ +typedef enum { + CO_GTWA_respErrorNone = 0, /**< 0 - No error or idle */ + CO_GTWA_respErrorReqNotSupported = 100, /**< 100 - Request not supported */ + CO_GTWA_respErrorSyntax = 101, /**< 101 - Syntax error */ + CO_GTWA_respErrorInternalState = 102, /**< 102 - Request not processed due to internal state */ + CO_GTWA_respErrorTimeOut = 103, /**< 103 - Time-out (where applicable) */ + CO_GTWA_respErrorNoDefaultNetSet = 104, /**< 104 - No default net set */ + CO_GTWA_respErrorNoDefaultNodeSet = 105, /**< 105 - No default node set */ + CO_GTWA_respErrorUnsupportedNet = 106, /**< 106 - Unsupported net */ + CO_GTWA_respErrorUnsupportedNode = 107, /**< 107 - Unsupported node */ + CO_GTWA_respErrorLostGuardingMessage = 200, /**< 200 - Lost guarding message */ + CO_GTWA_respErrorLostConnection = 201, /**< 201 - Lost connection */ + CO_GTWA_respErrorHeartbeatStarted = 202, /**< 202 - Heartbeat started */ + CO_GTWA_respErrorHeartbeatLost = 203, /**< 203 - Heartbeat lost */ + CO_GTWA_respErrorWrongNMTstate = 204, /**< 204 - Wrong NMT state */ + CO_GTWA_respErrorBootUp = 205, /**< 205 - Boot-up */ + CO_GTWA_respErrorErrorPassive = 300, /**< 300 - Error passive */ + CO_GTWA_respErrorBusOff = 301, /**< 301 - Bus off */ + CO_GTWA_respErrorCANbufferOverflow = 303, /**< 303 - CAN buffer overflow */ + CO_GTWA_respErrorCANinit = 304, /**< 304 - CAN init */ + CO_GTWA_respErrorCANactive = 305, /**< 305 - CAN active (at init or start-up) */ + CO_GTWA_respErrorPDOalreadyUsed = 400, /**< 400 - PDO already used */ + CO_GTWA_respErrorPDOlengthExceeded = 401, /**< 401 - PDO length exceeded */ + CO_GTWA_respErrorLSSmanufacturer = 501, /**< 501 - LSS implementation- / manufacturer-specific error */ + CO_GTWA_respErrorLSSnodeIdNotSupported = 502, /**< 502 - LSS node-ID not supported */ + CO_GTWA_respErrorLSSbitRateNotSupported = 503, /**< 503 - LSS bit-rate not supported */ + CO_GTWA_respErrorLSSparameterStoringFailed = 504, /**< 504 - LSS parameter storing failed */ + CO_GTWA_respErrorLSSmediaError = 505, /**< 505 - LSS command failed because of media error */ + CO_GTWA_respErrorRunningOutOfMemory = 600 /**< 600 - Running out of memory */ +} CO_GTWA_respErrorCode_t; + +/** + * Internal states of the Gateway-ascii state machine. + */ +typedef enum { + CO_GTWA_ST_IDLE = 0x00U, /**< Gateway is idle, no command is processing. This state is starting point for new + commands, which are parsed here. */ + CO_GTWA_ST_READ = 0x10U, /**< SDO 'read' (upload) */ + CO_GTWA_ST_WRITE = 0x11U, /**< SDO 'write' (download) */ + CO_GTWA_ST_WRITE_ABORTED = 0x12U, /**< SDO 'write' (download) - aborted, purging remaining data */ + CO_GTWA_ST_LSS_SWITCH_GLOB = 0x20U, /**< LSS 'lss_switch_glob' */ + CO_GTWA_ST_LSS_SWITCH_SEL = 0x21U, /**< LSS 'lss_switch_sel' */ + CO_GTWA_ST_LSS_SET_NODE = 0x22U, /**< LSS 'lss_set_node' */ + CO_GTWA_ST_LSS_CONF_BITRATE = 0x23U, /**< LSS 'lss_conf_bitrate' */ + CO_GTWA_ST_LSS_STORE = 0x24U, /**< LSS 'lss_store' */ + CO_GTWA_ST_LSS_INQUIRE = 0x25U, /**< LSS 'lss_inquire_addr' or 'lss_get_node' */ + CO_GTWA_ST_LSS_INQUIRE_ADDR_ALL = 0x26U, /**< LSS 'lss_inquire_addr', all parameters */ + CO_GTWA_ST__LSS_FASTSCAN = 0x30U, /**< LSS '_lss_fastscan' */ + CO_GTWA_ST_LSS_ALLNODES = 0x31U, /**< LSS 'lss_allnodes' */ + CO_GTWA_ST_LOG = 0x80U, /**< print message 'log' */ + CO_GTWA_ST_HELP = 0x81U, /**< print 'help' text */ + CO_GTWA_ST_LED = 0x82U /**< print 'status' of the node */ +} CO_GTWA_state_t; + +#if (((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_SDO) != 0) || defined CO_DOXYGEN +/* + * CANopen Gateway-ascii data types structure + */ +typedef struct { + char* syntax; /**< Data type syntax, as defined in CiA309-3 */ + size_t length; /**< Data type length in bytes, 0 if size is not known */ + /** Function, which reads data of specific data type from fifo buffer and writes them as corresponding ascii string. + * It is a pointer to #CO_fifo_readU82a function or similar and is used with SDO upload. For description of + * parameters see #CO_fifo_readU82a */ + size_t (*dataTypePrint)(CO_fifo_t* fifo, char* buf, size_t count, bool_t end); + /** Function, which reads ascii-data of specific data type from fifo buffer and copies them to another fifo buffer + * as binary data. It is a pointer to #CO_fifo_cpyTok2U8 function or similar and is used with SDO download. For + * description of parameters see #CO_fifo_cpyTok2U8 */ + size_t (*dataTypeScan)(CO_fifo_t* dest, CO_fifo_t* src, uint8_t* status); +} CO_GTWA_dataType_t; +#endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_SDO */ + +/** + * CANopen Gateway-ascii object + */ +typedef struct { + /** Pointer to external function for reading response from Gateway-ascii object. Pointer is initialized in + * CO_GTWA_initRead(). + * + * @param object Void pointer to custom object + * @param buf Buffer from which data can be read + * @param count Count of bytes available inside buffer + * @param [out] connectionOK different than 0 indicates connection is OK. + * + * @return Count of bytes actually transferred. + */ + size_t (*readCallback)(void* object, const char* buf, size_t count, uint8_t* connectionOK); + void* readCallbackObject; /**< Pointer to object, which will be used inside readCallback, from CO_GTWA_init() */ + uint32_t sequence; /**< Sequence number of the command */ + int32_t net_default; /**< Default CANopen Net number is undefined (-1) at startup */ + int16_t node_default; /**< Default CANopen Node ID number is undefined (-1) at startup */ + uint16_t net; /**< Current CANopen Net number */ + uint8_t node; /**< Current CANopen Node ID */ + CO_fifo_t commFifo; /**< CO_fifo_t object for command (not pointer) */ + uint8_t commBuf[CO_CONFIG_GTWA_COMM_BUF_SIZE + 1]; /**< Command buffer of usable size + @ref CO_CONFIG_GTWA_COMM_BUF_SIZE */ + char respBuf[CO_GTWA_RESP_BUF_SIZE]; /**< Response buffer of usable size @ref CO_GTWA_RESP_BUF_SIZE */ + size_t respBufCount; /**< Actual size of data in respBuf */ + size_t respBufOffset; /**< If only part of data has been successfully written into external application (with + readCallback()), then Gateway-ascii object will stay in current state. This situation is + indicated with respHold variable and respBufOffset indicates offset to untransferred data + inside respBuf. */ + bool_t respHold; /**< See respBufOffset above */ + uint32_t timeDifference_us_cumulative; /**< Sum of time difference from CO_GTWA_process() in case of respHold */ + CO_GTWA_state_t state; /**< Current state of the gateway object */ + uint32_t stateTimeoutTmr; /**< Timeout timer for the current state */ +#if (((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_SDO) != 0) || defined CO_DOXYGEN + CO_SDOclient_t* SDO_C; /**< SDO client object from CO_GTWA_init() */ + uint16_t SDOtimeoutTime; /**< Timeout time for SDO transfer in milliseconds, if no response */ + bool_t SDOblockTransferEnable; /**< SDO block transfer enabled? */ + bool_t SDOdataCopyStatus; /**< Indicate status of data copy from / to SDO buffer. If reading, true indicates, that + response has started. If writing, true indicates, that SDO buffer contains only part of + data and more data will follow. */ + const CO_GTWA_dataType_t* SDOdataType; /**< Data type of variable in current SDO communication */ +#endif +#if (((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_NMT) != 0) || defined CO_DOXYGEN + CO_NMT_t* NMT; /**< NMT object from CO_GTWA_init() */ +#endif +#if (((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_LSS) != 0) || defined CO_DOXYGEN + CO_LSSmaster_t* LSSmaster; /**< LSSmaster object from CO_GTWA_init() */ + CO_LSS_address_t lssAddress; /**< 128 bit number, uniquely identifying each node */ + uint8_t lssNID; /**< LSS Node-ID parameter */ + uint16_t lssBitrate; /**< LSS bitrate parameter */ + uint8_t lssInquireCs; /**< LSS inquire parameter */ + CO_LSSmaster_fastscan_t lssFastscan; /**< LSS fastscan parameter */ + uint8_t lssSubState; /**< LSS allnodes sub state parameter */ + uint8_t lssNodeCount; /**< LSS allnodes node count parameter */ + bool_t lssStore; /**< LSS allnodes store parameter */ + uint16_t lssTimeout_ms; /**< LSS allnodes timeout parameter */ +#endif +#if (((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_LOG) != 0) || defined CO_DOXYGEN + uint8_t logBuf[CO_CONFIG_GTWA_LOG_BUF_SIZE + 1]; /**< Message log buffer of usable size + @ref CO_CONFIG_GTWA_LOG_BUF_SIZE */ + CO_fifo_t logFifo; /**< CO_fifo_t object for message log (not pointer) */ +#endif +#if (((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_PRINT_HELP) != 0) || defined CO_DOXYGEN + const char* helpString; /**< Offset, when printing help text */ + size_t helpStringOffset; +#endif +#if (((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_PRINT_LEDS) != 0) || defined CO_DOXYGEN + CO_LEDs_t* LEDs; /**< CO_LEDs_t object for CANopen status LEDs imitation from CO_GTWA_init() */ + uint8_t ledStringPreviousIndex; +#endif +} CO_GTWA_t; + +/** + * Initialize Gateway-ascii object + * + * @param gtwa This object will be initialized + * @param SDO_C SDO client object + * @param SDOclientTimeoutTime_ms Default timeout in milliseconds, 500 typically + * @param SDOclientBlockTransfer If true, block transfer will be set by default + * @param NMT NMT object + * @param LSSmaster LSS master object + * @param LEDs LEDs object + * @param dummy dummy argument, set to 0 + * + * @return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT + */ +CO_ReturnError_t CO_GTWA_init(CO_GTWA_t* gtwa, +#if (((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_SDO) != 0) || defined CO_DOXYGEN + CO_SDOclient_t* SDO_C, uint16_t SDOclientTimeoutTime_ms, bool_t SDOclientBlockTransfer, +#endif +#if (((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_NMT) != 0) || defined CO_DOXYGEN + CO_NMT_t* NMT, +#endif +#if (((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_LSS) != 0) || defined CO_DOXYGEN + CO_LSSmaster_t* LSSmaster, +#endif +#if (((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_PRINT_LEDS) != 0) || defined CO_DOXYGEN + CO_LEDs_t* LEDs, +#endif + uint8_t dummy); + +/** + * Initialize read callback in Gateway-ascii object + * + * Callback will be used for transfer data to output stream of the application. It will be called from CO_GTWA_process() + * zero or multiple times, depending on the data available. If readCallback is uninitialized or NULL, then output data + * will be purged. + * + * @param gtwa This object will be initialized + * @param readCallback Pointer to external function for reading response from Gateway-ascii object. See #CO_GTWA_t for + * parameters. + * @param readCallbackObject Pointer to object, which will be used inside readCallback + */ +void CO_GTWA_initRead(CO_GTWA_t* gtwa, + size_t (*readCallback)(void* object, const char* buf, size_t count, uint8_t* connectionOK), + void* readCallbackObject); + +/** + * Get free write buffer space + * + * @param gtwa This object + * + * @return number of available bytes + */ +static inline size_t +CO_GTWA_write_getSpace(CO_GTWA_t* gtwa) { + return CO_fifo_getSpace(>wa->commFifo); +} + +/** + * Write command into CO_GTWA_t object. + * + * This function copies ascii command from buf into internal fifo buffer. Command must be closed with '\n' character. + * Function returns number of bytes successfully copied. If there is not enough space in destination, not all bytes will + * be copied and data can be refilled later (in case of large SDO download). + * + * @param gtwa This object + * @param buf Buffer which will be copied + * @param count Number of bytes in buf + * + * @return number of bytes actually written. + */ +static inline size_t +CO_GTWA_write(CO_GTWA_t* gtwa, const char* buf, size_t count) { + return CO_fifo_write(>wa->commFifo, (const uint8_t*)buf, count, NULL); +} + +#if (((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_LOG) != 0) || defined CO_DOXYGEN +/** + * Print message log string into fifo buffer + * + * This function enables recording of system log messages including CANopen events. Function can be called by + * application for recording any message. Message is copied to internal fifo buffer. In case fifo is full, old messages + * will be owerwritten. Message log fifo can be read with non-standard command "log". After log is read, it is emptied. + * Message must not contain "\r\n" inside. Newline character '\n' will be added between the messages automatically. + * + * @param gtwa This object + * @param message Null terminated string + */ +void CO_GTWA_log_print(CO_GTWA_t* gtwa, const char* message); +#endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_LOG */ + +/** + * Process Gateway-ascii object + * + * This is non-blocking function and must be called cyclically + * + * @param gtwa This object will be initialized. + * @param enable If true, gateway operates normally. If false, gateway is completely disabled and no command interaction + * is possible. Can be connected to hardware switch, for example. + * @param timeDifference_us Time difference from previous function call in [microseconds]. + * @param [out] timerNext_us info to OS - see CO_process(). + */ +void CO_GTWA_process(CO_GTWA_t* gtwa, bool_t enable, uint32_t timeDifference_us, uint32_t* timerNext_us); + +/** @} */ /* CO_CANopen_309_3 */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII */ + +#endif /* CO_GATEWAY_ASCII_H */ diff --git a/CANopen.c b/CANopen.c index 141748c7..67c6d5cc 100644 --- a/CANopen.c +++ b/CANopen.c @@ -1,953 +1,1511 @@ /* - * Main CANopen stack file. It combines Object dictionary (CO_OD) and all other - * CANopen source files. Configuration information are read from CO_OD.h file. + * Main CANopenNode file. * * @file CANopen.c * @ingroup CO_CANopen * @author Janez Paternoster - * @copyright 2010 - 2020 Janez Paternoster + * @copyright 2010 - 2023 Janez Paternoster * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . + * This file is part of , a CANopen Stack. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. */ - #include "CANopen.h" +/* Get values from CO_config_t or from single default OD.h ********************/ +#ifdef CO_MULTIPLE_OD +#define CO_GET_CO(obj) co->obj +#define CO_GET_CNT(obj) co->config->CNT_##obj +#define OD_GET(entry, index) co->config->ENTRY_##entry + +#else +#include "OD.h" +#define CO_GET_CO(obj) ((uint16_t)(CO_##obj)) +#define CO_GET_CNT(obj) (uint8_t)(OD_CNT_##obj) +#define OD_GET(entry, index) OD_ENTRY_##entry + +/* Verify parameters from "OD.h" and calculate necessary values for each object: + * - verify OD_CNT_xx or set default + * - calculate number of CANrx and CYNtx messages: CO_RX_CNT_xx and CO_TX_CNT_xx + * - set optional undefined OD_ENTRY_Hxxxx to NULL. + * - calculate indexes: CO_RX_IDX_xx and CO_TX_IDX_xx + * - calculate total count of CAN message buffers: CO_CNT_ALL_RX_MSGS and CO_CNT_ALL_TX_MSGS. */ +#if OD_CNT_NMT != 1 +#error OD_CNT_NMT from OD.h not correct! +#endif +#define CO_RX_CNT_NMT_SLV OD_CNT_NMT +#if ((CO_CONFIG_NMT)&CO_CONFIG_NMT_MASTER) != 0 +#define CO_TX_CNT_NMT_MST 1 +#else +#define CO_TX_CNT_NMT_MST 0 +#endif + +#if OD_CNT_HB_PROD != 1 +#error OD_CNT_HB_PROD from OD.h not correct! +#endif +#define CO_TX_CNT_HB_PROD OD_CNT_HB_PROD +#if !defined OD_CNT_HB_CONS +#define OD_CNT_HB_CONS 0 +#elif OD_CNT_HB_CONS < 0 || OD_CNT_HB_CONS > 1 +#error OD_CNT_HB_CONS from OD.h not correct! +#endif +#if (((CO_CONFIG_HB_CONS)&CO_CONFIG_HB_CONS_ENABLE) != 0) && OD_CNT_HB_CONS == 1 +#if OD_CNT_ARR_1016 < 1 || OD_CNT_ARR_1016 > 127 +#error OD_CNT_ARR_1016 is not defined in Object Dictionary or value is wrong! +#endif +#define CO_RX_CNT_HB_CONS OD_CNT_ARR_1016 +#else +#define CO_RX_CNT_HB_CONS 0 +#endif + +#if ((CO_CONFIG_NODE_GUARDING)&CO_CONFIG_NODE_GUARDING_SLAVE_ENABLE) != 0 +#define CO_RX_CNT_NG_SLV 1 +#define CO_TX_CNT_NG_SLV 1 +#else +#define CO_RX_CNT_NG_SLV 0 +#define CO_TX_CNT_NG_SLV 0 +#endif +#if ((CO_CONFIG_NODE_GUARDING)&CO_CONFIG_NODE_GUARDING_MASTER_ENABLE) != 0 +#define CO_RX_CNT_NG_MST 1 +#define CO_TX_CNT_NG_MST 1 +#else +#define CO_RX_CNT_NG_MST 0 +#define CO_TX_CNT_NG_MST 0 +#endif + +#if OD_CNT_EM != 1 +#error OD_CNT_EM from OD.h not correct! +#endif +#ifndef OD_ENTRY_H1003 +#define OD_ENTRY_H1003 NULL +#endif +#ifndef OD_CNT_ARR_1003 +#define OD_CNT_ARR_1003 8 +#endif +#if ((CO_CONFIG_EM)&CO_CONFIG_EM_PRODUCER) != 0 +#if OD_CNT_EM_PROD == 1 +#define CO_TX_CNT_EM_PROD OD_CNT_EM_PROD +#else +#error wrong OD_CNT_EM_PROD +#endif +#ifndef OD_ENTRY_H1015 +#define OD_ENTRY_H1015 NULL +#endif +#else +#define CO_TX_CNT_EM_PROD 0 +#endif +#if ((CO_CONFIG_EM)&CO_CONFIG_EM_CONSUMER) != 0 +#define CO_RX_CNT_EM_CONS 1 +#else +#define CO_RX_CNT_EM_CONS 0 +#endif + +#if !defined OD_CNT_SDO_SRV +#define OD_CNT_SDO_SRV 1 +#define OD_ENTRY_H1200 NULL +#elif OD_CNT_SDO_SRV < 1 || OD_CNT_SDO_SRV > 128 +#error OD_CNT_SDO_SRV from OD.h not correct! +#endif +#define CO_RX_CNT_SDO_SRV OD_CNT_SDO_SRV +#define CO_TX_CNT_SDO_SRV OD_CNT_SDO_SRV + +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_ENABLE) != 0 +#if !defined OD_CNT_SDO_CLI +#define OD_CNT_SDO_CLI 0 +#define OD_ENTRY_H1280 NULL +#elif OD_CNT_SDO_CLI < 0 || OD_CNT_SDO_CLI > 128 +#error OD_CNT_SDO_CLI from OD.h not correct! +#endif +#define CO_RX_CNT_SDO_CLI OD_CNT_SDO_CLI +#define CO_TX_CNT_SDO_CLI OD_CNT_SDO_CLI +#else +#define CO_RX_CNT_SDO_CLI 0 +#define CO_TX_CNT_SDO_CLI 0 +#endif + +#if ((CO_CONFIG_TIME)&CO_CONFIG_TIME_ENABLE) != 0 +#if !defined OD_CNT_TIME +#define OD_CNT_TIME 0 +// #define OD_ENTRY_H1012 NULL +#elif OD_CNT_TIME < 0 || OD_CNT_TIME > 1 +#error OD_CNT_TIME from OD.h not correct! +#endif +#define CO_RX_CNT_TIME OD_CNT_TIME +#if ((CO_CONFIG_TIME)&CO_CONFIG_TIME_PRODUCER) != 0 +#define CO_TX_CNT_TIME OD_CNT_TIME +#else +#define CO_TX_CNT_TIME 0 +#endif +#else +#define CO_RX_CNT_TIME 0 +#define CO_TX_CNT_TIME 0 +#endif + +#if ((CO_CONFIG_SYNC)&CO_CONFIG_SYNC_ENABLE) != 0 +#if !defined OD_CNT_SYNC +#define OD_CNT_SYNC 0 +#define OD_ENTRY_H1005 NULL +#define OD_ENTRY_H1006 NULL +#elif OD_CNT_SYNC < 0 || OD_CNT_SYNC > 1 +#error OD_CNT_SYNC from OD.h not correct! +#endif +#define CO_RX_CNT_SYNC OD_CNT_SYNC +#if ((CO_CONFIG_SYNC)&CO_CONFIG_SYNC_PRODUCER) != 0 +#define CO_TX_CNT_SYNC OD_CNT_SYNC +#else +#define CO_TX_CNT_SYNC 0 +#endif +#ifndef OD_ENTRY_H1007 +#define OD_ENTRY_H1007 NULL +#endif +#ifndef OD_ENTRY_H1019 +#define OD_ENTRY_H1019 NULL +#endif +#else +#define CO_RX_CNT_SYNC 0 +#define CO_TX_CNT_SYNC 0 +#endif + +#if ((CO_CONFIG_PDO)&CO_CONFIG_RPDO_ENABLE) != 0 +#if !defined OD_CNT_RPDO +#define OD_CNT_RPDO 0 +#define OD_ENTRY_H1400 NULL +#define OD_ENTRY_H1600 NULL +#elif OD_CNT_RPDO < 0 || OD_CNT_RPDO > 0x200 +#error OD_CNT_RPDO from OD.h not correct! +#endif +#define CO_RX_CNT_RPDO OD_CNT_RPDO +#else +#define CO_RX_CNT_RPDO 0 +#endif + +#if ((CO_CONFIG_PDO)&CO_CONFIG_TPDO_ENABLE) != 0 +#if !defined OD_CNT_TPDO +#define OD_CNT_TPDO 0 +#define OD_ENTRY_H1800 NULL +#define OD_ENTRY_H1A00 NULL +#elif OD_CNT_TPDO < 0 || OD_CNT_TPDO > 0x200 +#error OD_CNT_TPDO from OD.h not correct! +#endif +#define CO_TX_CNT_TPDO OD_CNT_TPDO +#else +#define CO_TX_CNT_TPDO 0 +#endif + +#if ((CO_CONFIG_LEDS)&CO_CONFIG_LEDS_ENABLE) != 0 +#define OD_CNT_LEDS 1 +#endif + +#if ((CO_CONFIG_GFC)&CO_CONFIG_GFC_ENABLE) != 0 +#if !defined OD_CNT_GFC +#define OD_CNT_GFC 0 +#define OD_ENTRY_H1300 NULL +#elif OD_CNT_GFC < 0 || OD_CNT_GFC > 1 +#error OD_CNT_GFC from OD.h not correct! +#endif +#define CO_RX_CNT_GFC OD_CNT_GFC +#define CO_TX_CNT_GFC OD_CNT_GFC +#else +#define CO_RX_CNT_GFC 0 +#define CO_TX_CNT_GFC 0 +#endif + +#if ((CO_CONFIG_SRDO)&CO_CONFIG_SRDO_ENABLE) != 0 +#if !defined OD_CNT_SRDO +#define OD_CNT_SRDO 0 +#define OD_ENTRY_H1301 NULL +#define OD_ENTRY_H1381 NULL +#define OD_ENTRY_H13FE NULL +#define OD_ENTRY_H13FF NULL +#elif OD_CNT_SRDO < 0 || OD_CNT_SRDO > 64 +#error OD_CNT_SRDO from OD.h not correct! +#endif +#define CO_RX_CNT_SRDO OD_CNT_SRDO +#define CO_TX_CNT_SRDO OD_CNT_SRDO +#else +#define CO_RX_CNT_SRDO 0 +#define CO_TX_CNT_SRDO 0 +#endif -/* If defined, global variables will be used, otherwise CANopen objects will - be generated with calloc(). */ -/* #define CO_USE_GLOBALS */ +#if ((CO_CONFIG_LSS)&CO_CONFIG_LSS_SLAVE) != 0 +#define OD_CNT_LSS_SLV 1 +#else +#define OD_CNT_LSS_SLV 0 +#endif +#define CO_RX_CNT_LSS_SLV OD_CNT_LSS_SLV +#define CO_TX_CNT_LSS_SLV OD_CNT_LSS_SLV -/* If defined, the user provides an own implemetation for calculating the - * CRC16 CCITT checksum. */ -/* #define CO_USE_OWN_CRC16 */ +#if ((CO_CONFIG_LSS)&CO_CONFIG_LSS_MASTER) != 0 +#define OD_CNT_LSS_MST 1 +#else +#define OD_CNT_LSS_MST 0 +#endif +#define CO_RX_CNT_LSS_MST OD_CNT_LSS_MST +#define CO_TX_CNT_LSS_MST OD_CNT_LSS_MST +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII) != 0 +#define OD_CNT_GTWA 1 +#endif + +#if (CO_CONFIG_TRACE) & CO_CONFIG_TRACE_ENABLE +#if !defined OD_CNT_TRACE +#define OD_CNT_TRACE 0 +#elif OD_CNT_TRACE < 0 +#error OD_CNT_TRACE from OD.h not correct! +#endif +#endif + +/* Indexes of CO_CANrx_t and CO_CANtx_t objects in CO_CANmodule_t and total number of them. Indexes + * are sorted in a way, that objects with highest priority of the CAN identifier are listed first. */ +#define CO_RX_IDX_NMT_SLV 0U +#define CO_RX_IDX_GFC (CO_RX_IDX_NMT_SLV + (uint16_t)CO_RX_CNT_NMT_SLV) +#define CO_RX_IDX_SYNC (CO_RX_IDX_GFC + (uint16_t)CO_RX_CNT_GFC) +#define CO_RX_IDX_EM_CONS (CO_RX_IDX_SYNC + (uint16_t)CO_RX_CNT_SYNC) +#define CO_RX_IDX_TIME (CO_RX_IDX_EM_CONS + (uint16_t)CO_RX_CNT_EM_CONS) +#define CO_RX_IDX_SRDO (CO_RX_IDX_TIME + (uint16_t)CO_RX_CNT_TIME) +#define CO_RX_IDX_RPDO (CO_RX_IDX_SRDO + ((uint16_t)CO_RX_CNT_SRDO * 2U)) +#define CO_RX_IDX_SDO_SRV (CO_RX_IDX_RPDO + (uint16_t)CO_RX_CNT_RPDO) +#define CO_RX_IDX_SDO_CLI (CO_RX_IDX_SDO_SRV + (uint16_t)CO_RX_CNT_SDO_SRV) +#define CO_RX_IDX_HB_CONS (CO_RX_IDX_SDO_CLI + (uint16_t)CO_RX_CNT_SDO_CLI) +#define CO_RX_IDX_NG_SLV (CO_RX_IDX_HB_CONS + (uint16_t)CO_RX_CNT_HB_CONS) +#define CO_RX_IDX_NG_MST (CO_RX_IDX_NG_SLV + (uint16_t)CO_RX_CNT_NG_SLV) +#define CO_RX_IDX_LSS_SLV (CO_RX_IDX_NG_MST + (uint16_t)CO_RX_CNT_NG_MST) +#define CO_RX_IDX_LSS_MST (CO_RX_IDX_LSS_SLV + (uint16_t)CO_RX_CNT_LSS_SLV) +#define CO_CNT_ALL_RX_MSGS (CO_RX_IDX_LSS_MST + (uint16_t)CO_RX_CNT_LSS_MST) + +#define CO_TX_IDX_NMT_MST 0U +#define CO_TX_IDX_GFC (CO_TX_IDX_NMT_MST + (uint16_t)CO_TX_CNT_NMT_MST) +#define CO_TX_IDX_SYNC (CO_TX_IDX_GFC + (uint16_t)CO_TX_CNT_GFC) +#define CO_TX_IDX_EM_PROD (CO_TX_IDX_SYNC + (uint16_t)CO_TX_CNT_SYNC) +#define CO_TX_IDX_TIME (CO_TX_IDX_EM_PROD + (uint16_t)CO_TX_CNT_EM_PROD) +#define CO_TX_IDX_SRDO (CO_TX_IDX_TIME + (uint16_t)CO_TX_CNT_TIME) +#define CO_TX_IDX_TPDO (CO_TX_IDX_SRDO + ((uint16_t)CO_TX_CNT_SRDO * 2U)) +#define CO_TX_IDX_SDO_SRV (CO_TX_IDX_TPDO + (uint16_t)CO_TX_CNT_TPDO) +#define CO_TX_IDX_SDO_CLI (CO_TX_IDX_SDO_SRV + (uint16_t)CO_TX_CNT_SDO_SRV) +#define CO_TX_IDX_HB_PROD (CO_TX_IDX_SDO_CLI + (uint16_t)CO_TX_CNT_SDO_CLI) +#define CO_TX_IDX_NG_SLV (CO_TX_IDX_HB_PROD + (uint16_t)CO_TX_CNT_HB_PROD) +#define CO_TX_IDX_NG_MST (CO_TX_IDX_NG_SLV + (uint16_t)CO_TX_CNT_NG_SLV) +#define CO_TX_IDX_LSS_SLV (CO_TX_IDX_NG_MST + (uint16_t)CO_TX_CNT_NG_MST) +#define CO_TX_IDX_LSS_MST (CO_TX_IDX_LSS_SLV + (uint16_t)CO_TX_CNT_LSS_SLV) +#define CO_CNT_ALL_TX_MSGS (CO_TX_IDX_LSS_MST + (uint16_t)CO_TX_CNT_LSS_MST) +#endif /* #ifdef #else CO_MULTIPLE_OD */ + +/* Objects from heap **********************************************************/ #ifndef CO_USE_GLOBALS - #include /* for malloc, free */ - static uint32_t CO_memoryUsed = 0; /* informative */ -#endif - - -/* Global variables ***********************************************************/ - extern const CO_OD_entry_t CO_OD[CO_OD_NoOfElements]; /* Object Dictionary array */ - static CO_t COO; - CO_t *CO = NULL; - - static CO_CANrx_t *CO_CANmodule_rxArray0; - static CO_CANtx_t *CO_CANmodule_txArray0; - static CO_OD_extension_t *CO_SDO_ODExtensions; - static CO_HBconsNode_t *CO_HBcons_monitoredNodes; -#if CO_NO_TRACE > 0 - static uint32_t *CO_traceTimeBuffers[CO_NO_TRACE]; - static int32_t *CO_traceValueBuffers[CO_NO_TRACE]; - #ifdef CO_USE_GLOBALS - #ifndef CO_TRACE_BUFFER_SIZE_FIXED - #define CO_TRACE_BUFFER_SIZE_FIXED 100 - #endif - #endif -#endif - - -/* Verify features from CO_OD *************************************************/ - /* generate error, if features are not correctly configured for this project */ - #if CO_NO_NMT_MASTER > 1 \ - || CO_NO_SYNC > 1 \ - || CO_NO_EMERGENCY != 1 \ - || CO_NO_SDO_SERVER == 0 \ - || CO_NO_TIME > 1 \ - || CO_NO_SDO_CLIENT > 128 \ - || (CO_NO_RPDO < 1 || CO_NO_RPDO > 0x200) \ - || (CO_NO_TPDO < 1 || CO_NO_TPDO > 0x200) \ - || ODL_consumerHeartbeatTime_arrayLength == 0 \ - || ODL_errorStatusBits_stringLength < 10 \ - || CO_NO_LSS_SERVER > 1 \ - || CO_NO_LSS_CLIENT > 1 \ - || (CO_NO_LSS_SERVER > 0 && CO_NO_LSS_CLIENT > 0) - #error Features from CO_OD.h file are not corectly configured for this project! - #endif - - -/* Indexes for CANopenNode message objects ************************************/ - #ifdef ODL_consumerHeartbeatTime_arrayLength - #define CO_NO_HB_CONS ODL_consumerHeartbeatTime_arrayLength - #else - #define CO_NO_HB_CONS 0 - #endif - #define CO_NO_HB_PROD 1 /* Producer Heartbeat Cont */ - - #define CO_RXCAN_NMT 0 /* index for NMT message */ - #define CO_RXCAN_SYNC 1 /* index for SYNC message */ - #define CO_RXCAN_EMERG (CO_RXCAN_SYNC+CO_NO_SYNC) /* index for Emergency message */ - #define CO_RXCAN_TIME (CO_RXCAN_EMERG+CO_NO_EMERGENCY) /* index for TIME message */ - #define CO_RXCAN_RPDO (CO_RXCAN_TIME+CO_NO_TIME) /* start index for RPDO messages */ - #define CO_RXCAN_SDO_SRV (CO_RXCAN_RPDO+CO_NO_RPDO) /* start index for SDO server message (request) */ - #define CO_RXCAN_SDO_CLI (CO_RXCAN_SDO_SRV+CO_NO_SDO_SERVER) /* start index for SDO client message (response) */ - #define CO_RXCAN_CONS_HB (CO_RXCAN_SDO_CLI+CO_NO_SDO_CLIENT) /* start index for Heartbeat Consumer messages */ - #define CO_RXCAN_LSS (CO_RXCAN_CONS_HB+CO_NO_HB_CONS) /* index for LSS rx message */ - /* total number of received CAN messages */ - #define CO_RXCAN_NO_MSGS (\ - 1 + \ - CO_NO_SYNC + \ - CO_NO_EMERGENCY + \ - CO_NO_TIME + \ - CO_NO_RPDO + \ - CO_NO_SDO_SERVER + \ - CO_NO_SDO_CLIENT + \ - CO_NO_HB_CONS + \ - CO_NO_LSS_SERVER + \ - CO_NO_LSS_CLIENT + \ - 0 \ - ) - - #define CO_TXCAN_NMT 0 /* index for NMT master message */ - #define CO_TXCAN_SYNC CO_TXCAN_NMT+CO_NO_NMT_MASTER /* index for SYNC message */ - #define CO_TXCAN_EMERG (CO_TXCAN_SYNC+CO_NO_SYNC) /* index for Emergency message */ - #define CO_TXCAN_TIME (CO_TXCAN_EMERG+CO_NO_EMERGENCY) /* index for TIME message */ - #define CO_TXCAN_TPDO (CO_TXCAN_TIME+CO_NO_TIME) /* start index for TPDO messages */ - #define CO_TXCAN_SDO_SRV (CO_TXCAN_TPDO+CO_NO_TPDO) /* start index for SDO server message (response) */ - #define CO_TXCAN_SDO_CLI (CO_TXCAN_SDO_SRV+CO_NO_SDO_SERVER) /* start index for SDO client message (request) */ - #define CO_TXCAN_HB (CO_TXCAN_SDO_CLI+CO_NO_SDO_CLIENT) /* index for Heartbeat message */ - #define CO_TXCAN_LSS (CO_TXCAN_HB+CO_NO_HB_PROD) /* index for LSS tx message */ - /* total number of transmitted CAN messages */ - #define CO_TXCAN_NO_MSGS ( \ - CO_NO_NMT_MASTER + \ - CO_NO_SYNC + \ - CO_NO_EMERGENCY + \ - CO_NO_TIME + \ - CO_NO_TPDO + \ - CO_NO_SDO_SERVER + \ - CO_NO_SDO_CLIENT + \ - CO_NO_HB_PROD + \ - CO_NO_LSS_SERVER + \ - CO_NO_LSS_CLIENT + \ - 0\ - ) +#include + +/* Default allocation strategy ************************************************/ +#if !defined(CO_alloc) || !defined(CO_free) +#if defined(CO_alloc) +#warning CO_alloc is defined but CO_free is not. using default values instead +#undef CO_alloc +#endif +#if defined(CO_free) +#warning CO_free is defined but CO_alloc is not. using default values instead +#undef CO_free +#endif +/* Allocate memory for number of elements, each of specific size Allocated memory must be reset to all zeros */ +#define CO_alloc(num, size) calloc((num), (size)) +#define CO_free(ptr) free((ptr)) -#ifdef CO_USE_GLOBALS - static CO_CANmodule_t COO_CANmodule; - static CO_CANrx_t COO_CANmodule_rxArray0[CO_RXCAN_NO_MSGS]; - static CO_CANtx_t COO_CANmodule_txArray0[CO_TXCAN_NO_MSGS]; - static CO_SDO_t COO_SDO[CO_NO_SDO_SERVER]; - static CO_OD_extension_t COO_SDO_ODExtensions[CO_OD_NoOfElements]; - static CO_EM_t COO_EM; - static CO_EMpr_t COO_EMpr; - static CO_NMT_t COO_NMT; -#if CO_NO_SYNC == 1 - static CO_SYNC_t COO_SYNC; -#endif -#if CO_NO_TIME == 1 - static CO_TIME_t COO_TIME; -#endif - static CO_RPDO_t COO_RPDO[CO_NO_RPDO]; - static CO_TPDO_t COO_TPDO[CO_NO_TPDO]; - static CO_HBconsumer_t COO_HBcons; - static CO_HBconsNode_t COO_HBcons_monitoredNodes[CO_NO_HB_CONS]; -#if CO_NO_LSS_SERVER == 1 - static CO_LSSslave_t CO0_LSSslave; -#endif -#if CO_NO_LSS_CLIENT == 1 - static CO_LSSmaster_t CO0_LSSmaster; -#endif -#if CO_NO_SDO_CLIENT != 0 - static CO_SDOclient_t COO_SDOclient[CO_NO_SDO_CLIENT]; -#endif -#if CO_NO_TRACE > 0 - static CO_trace_t COO_trace[CO_NO_TRACE]; - static uint32_t COO_traceTimeBuffers[CO_NO_TRACE][CO_TRACE_BUFFER_SIZE_FIXED]; - static int32_t COO_traceValueBuffers[CO_NO_TRACE][CO_TRACE_BUFFER_SIZE_FIXED]; -#endif -#endif - -/* These declarations here are needed in the case the switches for the project - change the visibility in the headers in a way that the compiler doesn't see an declaration anymore */ - -#if CO_NO_LSS_SERVER == 0 /* LSS Server means LSS slave */ - -CO_ReturnError_t CO_new(void); - -CO_ReturnError_t CO_CANinit( - void *CANdriverState, - uint16_t bitRate); - -CO_ReturnError_t CO_LSSinit( - uint8_t nodeId, - uint16_t bitRate); - -CO_ReturnError_t CO_CANopenInit( - uint8_t nodeId); - -#else /* CO_NO_LSS_SERVER == 0 */ - -CO_ReturnError_t CO_init( - void *CANdriverState, - uint8_t nodeId, - uint16_t bitRate); - -#endif /* CO_NO_LSS_SERVER == 0 */ - - -/* Helper function for NMT master *********************************************/ -#if CO_NO_NMT_MASTER == 1 - CO_CANtx_t *NMTM_txBuff = 0; - - CO_ReturnError_t CO_sendNMTcommand(CO_t *co, uint8_t command, uint8_t nodeID){ - if(NMTM_txBuff == 0){ - /* error, CO_CANtxBufferInit() was not called for this buffer. */ - return CO_ERROR_TX_UNCONFIGURED; /* -11 */ - } - NMTM_txBuff->data[0] = command; - NMTM_txBuff->data[1] = nodeID; - - CO_ReturnError_t error = CO_ERROR_NO; - - /* Apply NMT command also to this node, if set so. */ - if(nodeID == 0 || nodeID == co->NMT->nodeId){ - switch(command){ - case CO_NMT_ENTER_OPERATIONAL: - if((*co->NMT->emPr->errorRegister) == 0) { - co->NMT->operatingState = CO_NMT_OPERATIONAL; - } - break; - case CO_NMT_ENTER_STOPPED: - co->NMT->operatingState = CO_NMT_STOPPED; - break; - case CO_NMT_ENTER_PRE_OPERATIONAL: - co->NMT->operatingState = CO_NMT_PRE_OPERATIONAL; - break; - case CO_NMT_RESET_NODE: - co->NMT->resetCommand = CO_RESET_APP; - break; - case CO_NMT_RESET_COMMUNICATION: - co->NMT->resetCommand = CO_RESET_COMM; - break; - default: - error = CO_ERROR_ILLEGAL_ARGUMENT; - break; +#endif + +/* Define macros for allocation */ +#define CO_alloc_break_on_fail(var, num, size) \ + { \ + var = CO_alloc((num), (size)); \ + if ((var) != NULL) { \ + mem += (size) * (num); \ + } else { \ + break; \ + } \ + } + +#ifdef CO_MULTIPLE_OD +#define ON_MULTI_OD(sentence) sentence +#else +#define ON_MULTI_OD(sentence) +#endif + +CO_t* +CO_new(CO_config_t* config, uint32_t* heapMemoryUsed) { + CO_t* co = NULL; + /* return values */ + CO_t* coFinal = NULL; + uint32_t mem = 0; + + /* For each object: + * - allocate memory, verify allocation and calculate size of heap used + * - if CO_MULTIPLE_OD is defined: + * - use config structure + * - calculate number of CANrx and CYNtx messages: RX_CNT_xx and TX_CNT_xx + * - calculate indexes: RX_IDX_xx and TX_IDX_xx + * - calculate total count of CAN message buffers: CNT_ALL_RX_MSGS and CNT_ALL_TX_MSGS. */ + do { +#ifdef CO_MULTIPLE_OD + /* verify arguments */ + if (config == NULL || config->CNT_NMT > 1 || config->CNT_HB_CONS > 1 || config->CNT_EM > 1 + || config->CNT_SDO_SRV > 128 || config->CNT_SDO_CLI > 128 || config->CNT_SYNC > 1 || config->CNT_RPDO > 512 + || config->CNT_TPDO > 512 || config->CNT_TIME > 1 || config->CNT_LEDS > 1 || config->CNT_GFC > 1 + || config->CNT_SRDO > 64 || config->CNT_LSS_SLV > 1 || config->CNT_LSS_MST > 1 || config->CNT_GTWA > 1) { + break; + } +#else + (void)config; +#endif + + /* CANopen object */ + CO_alloc_break_on_fail(co, 1U, sizeof(*co)); + +#ifdef CO_MULTIPLE_OD + co->config = config; +#endif + + /* NMT_Heartbeat */ + ON_MULTI_OD(uint8_t RX_CNT_NMT_SLV = 0); + ON_MULTI_OD(uint8_t TX_CNT_NMT_MST = 0); + ON_MULTI_OD(uint8_t TX_CNT_HB_PROD = 0); + if (CO_GET_CNT(NMT) == 1U) { + CO_alloc_break_on_fail(co->NMT, CO_GET_CNT(NMT), sizeof(*co->NMT)); + ON_MULTI_OD(RX_CNT_NMT_SLV = 1); +#if ((CO_CONFIG_NMT)&CO_CONFIG_NMT_MASTER) != 0 + ON_MULTI_OD(TX_CNT_NMT_MST = 1); +#endif + ON_MULTI_OD(TX_CNT_HB_PROD = 1); + } + +#if ((CO_CONFIG_HB_CONS)&CO_CONFIG_HB_CONS_ENABLE) != 0 + ON_MULTI_OD(uint8_t RX_CNT_HB_CONS = 0); + if (CO_GET_CNT(HB_CONS) == 1U) { + uint8_t countOfMonitoredNodes = CO_GET_CNT(ARR_1016); + CO_alloc_break_on_fail(co->HBcons, CO_GET_CNT(HB_CONS), sizeof(*co->HBcons)); + CO_alloc_break_on_fail(co->HBconsMonitoredNodes, countOfMonitoredNodes, sizeof(*co->HBconsMonitoredNodes)); + ON_MULTI_OD(RX_CNT_HB_CONS = countOfMonitoredNodes); + } +#endif + /* Node guarding */ +#if ((CO_CONFIG_NODE_GUARDING)&CO_CONFIG_NODE_GUARDING_SLAVE_ENABLE) != 0 + CO_alloc_break_on_fail(co->NGslave, 1, sizeof(*co->NGslave)); +#endif +#if ((CO_CONFIG_NODE_GUARDING)&CO_CONFIG_NODE_GUARDING_MASTER_ENABLE) != 0 + CO_alloc_break_on_fail(co->NGmaster, 1, sizeof(*co->NGmaster)); +#endif + + /* Emergency */ + ON_MULTI_OD(uint8_t RX_CNT_EM_CONS = 0); + ON_MULTI_OD(uint8_t TX_CNT_EM_PROD = 0); + if (CO_GET_CNT(EM) == 1U) { + CO_alloc_break_on_fail(co->em, CO_GET_CNT(EM), sizeof(*co->em)); +#if ((CO_CONFIG_EM)&CO_CONFIG_EM_CONSUMER) != 0 + ON_MULTI_OD(RX_CNT_EM_CONS = 1); +#endif +#if ((CO_CONFIG_EM)&CO_CONFIG_EM_PRODUCER) != 0 + ON_MULTI_OD(TX_CNT_EM_PROD = 1); +#endif +#if ((CO_CONFIG_EM) & (CO_CONFIG_EM_PRODUCER | CO_CONFIG_EM_HISTORY)) != 0 + uint8_t fifoSize = CO_GET_CNT(ARR_1003) + 1U; + if (fifoSize >= 2U) { + CO_alloc_break_on_fail(co->em_fifo, fifoSize, sizeof(*co->em_fifo)); } +#endif } - if(error == CO_ERROR_NO) - return CO_CANsend(co->CANmodule[0], NMTM_txBuff); /* 0 = success */ - else - { - return error; + /* SDOserver */ + ON_MULTI_OD(uint8_t RX_CNT_SDO_SRV = 0); + ON_MULTI_OD(uint8_t TX_CNT_SDO_SRV = 0); + if (CO_GET_CNT(SDO_SRV) > 0U) { + CO_alloc_break_on_fail(co->SDOserver, CO_GET_CNT(SDO_SRV), sizeof(*co->SDOserver)); + ON_MULTI_OD(RX_CNT_SDO_SRV = config->CNT_SDO_SRV); + ON_MULTI_OD(TX_CNT_SDO_SRV = config->CNT_SDO_SRV); } - } +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_ENABLE) != 0 + ON_MULTI_OD(uint8_t RX_CNT_SDO_CLI = 0); + ON_MULTI_OD(uint8_t TX_CNT_SDO_CLI = 0); + if (CO_GET_CNT(SDO_CLI) > 0U) { + CO_alloc_break_on_fail(co->SDOclient, CO_GET_CNT(SDO_CLI), sizeof(*co->SDOclient)); + ON_MULTI_OD(RX_CNT_SDO_CLI = config->CNT_SDO_CLI); + ON_MULTI_OD(TX_CNT_SDO_CLI = config->CNT_SDO_CLI); + } #endif +#if ((CO_CONFIG_TIME)&CO_CONFIG_TIME_ENABLE) != 0 + ON_MULTI_OD(uint8_t RX_CNT_TIME = 0); + ON_MULTI_OD(uint8_t TX_CNT_TIME = 0); + if (CO_GET_CNT(TIME) == 1U) { + CO_alloc_break_on_fail(co->TIME, CO_GET_CNT(TIME), sizeof(*co->TIME)); + ON_MULTI_OD(RX_CNT_TIME = 1); +#if ((CO_CONFIG_TIME)&CO_CONFIG_TIME_PRODUCER) != 0 + ON_MULTI_OD(TX_CNT_TIME = 1); +#endif + } +#endif -#if CO_NO_TRACE > 0 -static uint32_t CO_traceBufferSize[CO_NO_TRACE]; +#if ((CO_CONFIG_SYNC)&CO_CONFIG_SYNC_ENABLE) != 0 + ON_MULTI_OD(uint8_t RX_CNT_SYNC = 0); + ON_MULTI_OD(uint8_t TX_CNT_SYNC = 0); + if (CO_GET_CNT(SYNC) == 1U) { + CO_alloc_break_on_fail(co->SYNC, CO_GET_CNT(SYNC), sizeof(*co->SYNC)); + ON_MULTI_OD(RX_CNT_SYNC = 1); +#if ((CO_CONFIG_SYNC)&CO_CONFIG_SYNC_PRODUCER) != 0 + ON_MULTI_OD(TX_CNT_SYNC = 1); +#endif + } #endif -/******************************************************************************/ -CO_ReturnError_t CO_new(void) -{ - int16_t i; -#ifndef CO_USE_GLOBALS - uint16_t errCnt; +#if ((CO_CONFIG_PDO)&CO_CONFIG_RPDO_ENABLE) != 0 + ON_MULTI_OD(uint16_t RX_CNT_RPDO = 0); + if (CO_GET_CNT(RPDO) > 0U) { + CO_alloc_break_on_fail(co->RPDO, CO_GET_CNT(RPDO), sizeof(*co->RPDO)); + ON_MULTI_OD(RX_CNT_RPDO = config->CNT_RPDO); + } +#endif + +#if ((CO_CONFIG_PDO)&CO_CONFIG_TPDO_ENABLE) != 0 + ON_MULTI_OD(uint16_t TX_CNT_TPDO = 0); + if (CO_GET_CNT(TPDO) > 0U) { + CO_alloc_break_on_fail(co->TPDO, CO_GET_CNT(TPDO), sizeof(*co->TPDO)); + ON_MULTI_OD(TX_CNT_TPDO = config->CNT_TPDO); + } +#endif + +#if ((CO_CONFIG_LEDS)&CO_CONFIG_LEDS_ENABLE) != 0 + if (CO_GET_CNT(LEDS) == 1U) { + CO_alloc_break_on_fail(co->LEDs, CO_GET_CNT(LEDS), sizeof(*co->LEDs)); + } +#endif + +#if ((CO_CONFIG_GFC)&CO_CONFIG_GFC_ENABLE) != 0 + ON_MULTI_OD(uint8_t RX_CNT_GFC = 0); + ON_MULTI_OD(uint8_t TX_CNT_GFC = 0); + if (CO_GET_CNT(GFC) == 1) { + CO_alloc_break_on_fail(co->GFC, CO_GET_CNT(GFC), sizeof(*co->GFC)); + ON_MULTI_OD(RX_CNT_GFC = 1); + ON_MULTI_OD(TX_CNT_GFC = 1); + } +#endif + +#if ((CO_CONFIG_SRDO)&CO_CONFIG_SRDO_ENABLE) != 0 + ON_MULTI_OD(uint8_t RX_CNT_SRDO = 0); + ON_MULTI_OD(uint8_t TX_CNT_SRDO = 0); + if (CO_GET_CNT(SRDO) > 0U) { + CO_alloc_break_on_fail(co->SRDOGuard, 1U, sizeof(*co->SRDOGuard)); + CO_alloc_break_on_fail(co->SRDO, CO_GET_CNT(SRDO), sizeof(*co->SRDO)); + ON_MULTI_OD(RX_CNT_SRDO = config->CNT_SRDO * 2); + ON_MULTI_OD(TX_CNT_SRDO = config->CNT_SRDO * 2); + } +#endif + +#if ((CO_CONFIG_LSS)&CO_CONFIG_LSS_SLAVE) != 0 + ON_MULTI_OD(uint8_t RX_CNT_LSS_SLV = 0); + ON_MULTI_OD(uint8_t TX_CNT_LSS_SLV = 0); + if (CO_GET_CNT(LSS_SLV) == 1U) { + CO_alloc_break_on_fail(co->LSSslave, CO_GET_CNT(LSS_SLV), sizeof(*co->LSSslave)); + ON_MULTI_OD(RX_CNT_LSS_SLV = 1); + ON_MULTI_OD(TX_CNT_LSS_SLV = 1); + } +#endif + +#if ((CO_CONFIG_LSS)&CO_CONFIG_LSS_MASTER) != 0 + ON_MULTI_OD(uint8_t RX_CNT_LSS_MST = 0); + ON_MULTI_OD(uint8_t TX_CNT_LSS_MST = 0); + if (CO_GET_CNT(LSS_MST) == 1U) { + CO_alloc_break_on_fail(co->LSSmaster, CO_GET_CNT(LSS_MST), sizeof(*co->LSSmaster)); + ON_MULTI_OD(RX_CNT_LSS_MST = 1); + ON_MULTI_OD(TX_CNT_LSS_MST = 1); + } +#endif + +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII) != 0 + if (CO_GET_CNT(GTWA) == 1U) { + CO_alloc_break_on_fail(co->gtwa, CO_GET_CNT(GTWA), sizeof(*co->gtwa)); + } +#endif + +#if (CO_CONFIG_TRACE) & CO_CONFIG_TRACE_ENABLE + if (CO_GET_CNT(TRACE) > 0) { + CO_alloc_break_on_fail(co->trace, CO_GET_CNT(TRACE), sizeof(*co->trace)); + } +#endif + +#ifdef CO_MULTIPLE_OD + /* Indexes of CO_CANrx_t and CO_CANtx_t objects in CO_CANmodule_t and total number of them. Indexes + * are sorted in a way, that objects with highest priority of the CAN identifier are listed first. */ + int16_t idxRx = 0; + co->RX_IDX_NMT_SLV = idxRx; + idxRx += RX_CNT_NMT_SLV; +#if ((CO_CONFIG_GFC)&CO_CONFIG_GFC_ENABLE) != 0 + co->RX_IDX_GFC = idxRx; + idxRx += RX_CNT_GFC; +#endif +#if ((CO_CONFIG_SYNC)&CO_CONFIG_SYNC_ENABLE) != 0 + co->RX_IDX_SYNC = idxRx; + idxRx += RX_CNT_SYNC; +#endif + co->RX_IDX_EM_CONS = idxRx; + idxRx += RX_CNT_EM_CONS; +#if ((CO_CONFIG_TIME)&CO_CONFIG_TIME_ENABLE) != 0 + co->RX_IDX_TIME = idxRx; + idxRx += RX_CNT_TIME; +#endif +#if ((CO_CONFIG_SRDO)&CO_CONFIG_SRDO_ENABLE) != 0 + co->RX_IDX_SRDO = idxRx; + idxRx += RX_CNT_SRDO * 2; +#endif +#if ((CO_CONFIG_PDO)&CO_CONFIG_RPDO_ENABLE) != 0 + co->RX_IDX_RPDO = idxRx; + idxRx += RX_CNT_RPDO; +#endif + co->RX_IDX_SDO_SRV = idxRx; + idxRx += RX_CNT_SDO_SRV; +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_ENABLE) != 0 + co->RX_IDX_SDO_CLI = idxRx; + idxRx += RX_CNT_SDO_CLI; +#endif +#if ((CO_CONFIG_HB_CONS)&CO_CONFIG_HB_CONS_ENABLE) != 0 + co->RX_IDX_HB_CONS = idxRx; + idxRx += RX_CNT_HB_CONS; +#endif +#if ((CO_CONFIG_NODE_GUARDING)&CO_CONFIG_NODE_GUARDING_SLAVE_ENABLE) != 0 + co->RX_IDX_NG_SLV = idxRx; + idxRx += 1; +#endif +#if ((CO_CONFIG_NODE_GUARDING)&CO_CONFIG_NODE_GUARDING_MASTER_ENABLE) != 0 + co->RX_IDX_NG_MST = idxRx; + idxRx += 1; +#endif +#if ((CO_CONFIG_LSS)&CO_CONFIG_LSS_SLAVE) != 0 + co->RX_IDX_LSS_SLV = idxRx; + idxRx += RX_CNT_LSS_SLV; +#endif +#if ((CO_CONFIG_LSS)&CO_CONFIG_LSS_MASTER) != 0 + co->RX_IDX_LSS_MST = idxRx; + idxRx += RX_CNT_LSS_MST; +#endif + co->CNT_ALL_RX_MSGS = idxRx; + + int16_t idxTx = 0; + co->TX_IDX_NMT_MST = idxTx; + idxTx += TX_CNT_NMT_MST; +#if ((CO_CONFIG_GFC)&CO_CONFIG_GFC_ENABLE) != 0 + co->TX_IDX_GFC = idxTx; + idxTx += TX_CNT_GFC; +#endif +#if ((CO_CONFIG_SYNC)&CO_CONFIG_SYNC_ENABLE) != 0 + co->TX_IDX_SYNC = idxTx; + idxTx += TX_CNT_SYNC; +#endif + co->TX_IDX_EM_PROD = idxTx; + idxTx += TX_CNT_EM_PROD; +#if ((CO_CONFIG_TIME)&CO_CONFIG_TIME_ENABLE) != 0 + co->TX_IDX_TIME = idxTx; + idxTx += TX_CNT_TIME; +#endif +#if ((CO_CONFIG_SRDO)&CO_CONFIG_SRDO_ENABLE) != 0 + co->TX_IDX_SRDO = idxTx; + idxTx += TX_CNT_SRDO * 2; +#endif +#if ((CO_CONFIG_PDO)&CO_CONFIG_TPDO_ENABLE) != 0 + co->TX_IDX_TPDO = idxTx; + idxTx += TX_CNT_TPDO; +#endif + co->TX_IDX_SDO_SRV = idxTx; + idxTx += TX_CNT_SDO_SRV; +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_ENABLE) != 0 + co->TX_IDX_SDO_CLI = idxTx; + idxTx += TX_CNT_SDO_CLI; +#endif + co->TX_IDX_HB_PROD = idxTx; + idxTx += TX_CNT_HB_PROD; +#if ((CO_CONFIG_NODE_GUARDING)&CO_CONFIG_NODE_GUARDING_SLAVE_ENABLE) != 0 + co->TX_IDX_NG_SLV = idxTx; + idxTx += 1; #endif +#if ((CO_CONFIG_NODE_GUARDING)&CO_CONFIG_NODE_GUARDING_MASTER_ENABLE) != 0 + co->TX_IDX_NG_MST = idxTx; + idxTx += 1; +#endif +#if ((CO_CONFIG_LSS)&CO_CONFIG_LSS_SLAVE) != 0 + co->TX_IDX_LSS_SLV = idxTx; + idxTx += TX_CNT_LSS_SLV; +#endif +#if ((CO_CONFIG_LSS)&CO_CONFIG_LSS_MASTER) != 0 + co->TX_IDX_LSS_MST = idxTx; + idxTx += TX_CNT_LSS_MST; +#endif + co->CNT_ALL_TX_MSGS = idxTx; +#endif /* #ifdef CO_MULTIPLE_OD */ + + /* CANmodule */ + CO_alloc_break_on_fail(co->CANmodule, 1U, sizeof(*co->CANmodule)); + + /* CAN RX blocks */ + CO_alloc_break_on_fail(co->CANrx, CO_GET_CO(CNT_ALL_RX_MSGS), sizeof(*co->CANrx)); + + /* CAN TX blocks */ + CO_alloc_break_on_fail(co->CANtx, CO_GET_CO(CNT_ALL_TX_MSGS), sizeof(*co->CANtx)); + + /* finish successfully, set other parameters */ + co->nodeIdUnconfigured = true; + coFinal = co; + } while (false); - /* Verify parameters from CO_OD */ - if( sizeof(OD_TPDOCommunicationParameter_t) != sizeof(CO_TPDOCommPar_t) - || sizeof(OD_TPDOMappingParameter_t) != sizeof(CO_TPDOMapPar_t) - || sizeof(OD_RPDOCommunicationParameter_t) != sizeof(CO_RPDOCommPar_t) - || sizeof(OD_RPDOMappingParameter_t) != sizeof(CO_RPDOMapPar_t)) - { - return CO_ERROR_PARAMETERS; + if (coFinal == NULL) { + CO_delete(co); } + if (heapMemoryUsed != NULL) { + *heapMemoryUsed = mem; + } + return coFinal; +} - #if CO_NO_SDO_CLIENT != 0 - if(sizeof(OD_SDOClientParameter_t) != sizeof(CO_SDOclientPar_t)){ - return CO_ERROR_PARAMETERS; +void +CO_delete(CO_t* co) { + if (co == NULL) { + return; } - #endif + CO_CANmodule_disable(co->CANmodule); + + /* CANmodule */ + CO_free(co->CANtx); + CO_free(co->CANrx); + CO_free(co->CANmodule); + +#if (CO_CONFIG_TRACE) & CO_CONFIG_TRACE_ENABLE + CO_free(co->trace); +#endif + +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII) != 0 + CO_free(co->gtwa); +#endif + +#if ((CO_CONFIG_LSS)&CO_CONFIG_LSS_MASTER) != 0 + CO_free(co->LSSmaster); +#endif + +#if ((CO_CONFIG_LSS)&CO_CONFIG_LSS_SLAVE) != 0 + CO_free(co->LSSslave); +#endif + +#if ((CO_CONFIG_SRDO)&CO_CONFIG_SRDO_ENABLE) != 0 + CO_free(co->SRDO); + CO_free(co->SRDOGuard); +#endif + +#if ((CO_CONFIG_GFC)&CO_CONFIG_GFC_ENABLE) != 0 + CO_free(co->GFC); +#endif + +#if ((CO_CONFIG_LEDS)&CO_CONFIG_LEDS_ENABLE) != 0 + CO_free(co->LEDs); +#endif + +#if ((CO_CONFIG_PDO)&CO_CONFIG_TPDO_ENABLE) != 0 + CO_free(co->TPDO); +#endif + +#if ((CO_CONFIG_PDO)&CO_CONFIG_RPDO_ENABLE) != 0 + CO_free(co->RPDO); +#endif + +#if ((CO_CONFIG_SYNC)&CO_CONFIG_SYNC_ENABLE) != 0 + CO_free(co->SYNC); +#endif + +#if ((CO_CONFIG_TIME)&CO_CONFIG_TIME_ENABLE) != 0 + CO_free(co->TIME); +#endif + +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_ENABLE) != 0 + CO_free(co->SDOclient); +#endif + + /* SDOserver */ + CO_free(co->SDOserver); + + /* Emergency */ + CO_free(co->em); +#if ((CO_CONFIG_EM) & (CO_CONFIG_EM_PRODUCER | CO_CONFIG_EM_HISTORY)) != 0 + CO_free(co->em_fifo); +#endif + +#if ((CO_CONFIG_NODE_GUARDING)&CO_CONFIG_NODE_GUARDING_SLAVE_ENABLE) != 0 + CO_free(co->NGslave); +#endif +#if ((CO_CONFIG_NODE_GUARDING)&CO_CONFIG_NODE_GUARDING_MASTER_ENABLE) != 0 + CO_free(co->NGmaster); +#endif + +#if ((CO_CONFIG_HB_CONS)&CO_CONFIG_HB_CONS_ENABLE) != 0 + CO_free(co->HBconsMonitoredNodes); + CO_free(co->HBcons); +#endif + + /* NMT_Heartbeat */ + CO_free(co->NMT); - /* Initialize CANopen object */ + /* CANopen object */ + CO_free(co); +} +#endif /* #ifndef CO_USE_GLOBALS */ + +/* Objects as globals *********************************************************/ #ifdef CO_USE_GLOBALS - CO = &COO; - - CO_memset((uint8_t*)CO, 0, sizeof(CO_t)); - CO->CANmodule[0] = &COO_CANmodule; - CO_CANmodule_rxArray0 = &COO_CANmodule_rxArray0[0]; - CO_CANmodule_txArray0 = &COO_CANmodule_txArray0[0]; - for(i=0; iSDO[i] = &COO_SDO[i]; - CO_SDO_ODExtensions = &COO_SDO_ODExtensions[0]; - CO->em = &COO_EM; - CO->emPr = &COO_EMpr; - CO->NMT = &COO_NMT; - #if CO_NO_SYNC == 1 - CO->SYNC = &COO_SYNC; - #endif - #if CO_NO_TIME == 1 - CO->TIME = &COO_TIME; - #endif - for(i=0; iRPDO[i] = &COO_RPDO[i]; - for(i=0; iTPDO[i] = &COO_TPDO[i]; - CO->HBcons = &COO_HBcons; - CO_HBcons_monitoredNodes = &COO_HBcons_monitoredNodes[0]; - #if CO_NO_LSS_SERVER == 1 - CO->LSSslave = &CO0_LSSslave; - #endif - #if CO_NO_LSS_CLIENT == 1 - CO->LSSmaster = &CO0_LSSmaster; - #endif - #if CO_NO_SDO_CLIENT != 0 - for(i=0; iSDOclient[i] = &COO_SDOclient[i]; - } - #endif - #if CO_NO_TRACE > 0 - for(i=0; itrace[i] = &COO_trace[i]; - CO_traceTimeBuffers[i] = &COO_traceTimeBuffers[i][0]; - CO_traceValueBuffers[i] = &COO_traceValueBuffers[i][0]; - CO_traceBufferSize[i] = CO_TRACE_BUFFER_SIZE_FIXED; - } - #endif -#else - if(CO == NULL){ /* Use malloc only once */ - CO = &COO; - CO->CANmodule[0] = (CO_CANmodule_t *) calloc(1, sizeof(CO_CANmodule_t)); - CO_CANmodule_rxArray0 = (CO_CANrx_t *) calloc(CO_RXCAN_NO_MSGS, sizeof(CO_CANrx_t)); - CO_CANmodule_txArray0 = (CO_CANtx_t *) calloc(CO_TXCAN_NO_MSGS, sizeof(CO_CANtx_t)); - for(i=0; iSDO[i] = (CO_SDO_t *) calloc(1, sizeof(CO_SDO_t)); - } - CO_SDO_ODExtensions = (CO_OD_extension_t*) calloc(CO_OD_NoOfElements, sizeof(CO_OD_extension_t)); - CO->em = (CO_EM_t *) calloc(1, sizeof(CO_EM_t)); - CO->emPr = (CO_EMpr_t *) calloc(1, sizeof(CO_EMpr_t)); - CO->NMT = (CO_NMT_t *) calloc(1, sizeof(CO_NMT_t)); - #if CO_NO_SYNC == 1 - CO->SYNC = (CO_SYNC_t *) calloc(1, sizeof(CO_SYNC_t)); - #endif - #if CO_NO_TIME == 1 - CO->TIME = (CO_TIME_t *) calloc(1, sizeof(CO_TIME_t)); - #endif - for(i=0; iRPDO[i] = (CO_RPDO_t *) calloc(1, sizeof(CO_RPDO_t)); - } - for(i=0; iTPDO[i] = (CO_TPDO_t *) calloc(1, sizeof(CO_TPDO_t)); - } - CO->HBcons = (CO_HBconsumer_t *) calloc(1, sizeof(CO_HBconsumer_t)); - CO_HBcons_monitoredNodes = (CO_HBconsNode_t *) calloc(CO_NO_HB_CONS, sizeof(CO_HBconsNode_t)); - #if CO_NO_LSS_SERVER == 1 - CO->LSSslave = (CO_LSSslave_t *) calloc(1, sizeof(CO_LSSslave_t)); - #endif - #if CO_NO_LSS_CLIENT == 1 - CO->LSSmaster = (CO_LSSmaster_t *) calloc(1, sizeof(CO_LSSmaster_t)); - #endif - #if CO_NO_SDO_CLIENT != 0 - for(i=0; iSDOclient[i] = (CO_SDOclient_t *) calloc(1, sizeof(CO_SDOclient_t)); - } - #endif - #if CO_NO_TRACE > 0 - for(i=0; itrace[i] = (CO_trace_t *) calloc(1, sizeof(CO_trace_t)); - CO_traceTimeBuffers[i] = (uint32_t *) calloc(OD_traceConfig[i].size, sizeof(uint32_t)); - CO_traceValueBuffers[i] = (int32_t *) calloc(OD_traceConfig[i].size, sizeof(int32_t)); - if(CO_traceTimeBuffers[i] != NULL && CO_traceValueBuffers[i] != NULL) { - CO_traceBufferSize[i] = OD_traceConfig[i].size; - } else { - CO_traceBufferSize[i] = 0; - } - } - #endif - } - - CO_memoryUsed = sizeof(CO_CANmodule_t) - + sizeof(CO_CANrx_t) * CO_RXCAN_NO_MSGS - + sizeof(CO_CANtx_t) * CO_TXCAN_NO_MSGS - + sizeof(CO_SDO_t) * CO_NO_SDO_SERVER - + sizeof(CO_OD_extension_t) * CO_OD_NoOfElements - + sizeof(CO_EM_t) - + sizeof(CO_EMpr_t) - + sizeof(CO_NMT_t) - #if CO_NO_SYNC == 1 - + sizeof(CO_SYNC_t) - #endif - #if CO_NO_TIME == 1 - + sizeof(CO_TIME_t) - #endif - + sizeof(CO_RPDO_t) * CO_NO_RPDO - + sizeof(CO_TPDO_t) * CO_NO_TPDO - + sizeof(CO_HBconsumer_t) - + sizeof(CO_HBconsNode_t) * CO_NO_HB_CONS - #if CO_NO_LSS_SERVER == 1 - + sizeof(CO_LSSslave_t) - #endif - #if CO_NO_LSS_CLIENT == 1 - + sizeof(CO_LSSmaster_t) - #endif - #if CO_NO_SDO_CLIENT != 0 - + sizeof(CO_SDOclient_t) * CO_NO_SDO_CLIENT - #endif - + 0; - #if CO_NO_TRACE > 0 - CO_memoryUsed += sizeof(CO_trace_t) * CO_NO_TRACE; - for(i=0; iCANmodule[0] == NULL) errCnt++; - if(CO_CANmodule_rxArray0 == NULL) errCnt++; - if(CO_CANmodule_txArray0 == NULL) errCnt++; - for(i=0; iSDO[i] == NULL) errCnt++; - } - if(CO_SDO_ODExtensions == NULL) errCnt++; - if(CO->em == NULL) errCnt++; - if(CO->emPr == NULL) errCnt++; - if(CO->NMT == NULL) errCnt++; - #if CO_NO_SYNC == 1 - if(CO->SYNC == NULL) errCnt++; - #endif - #if CO_NO_TIME == 1 - if(CO->TIME == NULL) errCnt++; - #endif - for(i=0; iRPDO[i] == NULL) errCnt++; - } - for(i=0; iTPDO[i] == NULL) errCnt++; - } - if(CO->HBcons == NULL) errCnt++; - if(CO_HBcons_monitoredNodes == NULL) errCnt++; - #if CO_NO_LSS_SERVER == 1 - if(CO->LSSslave == NULL) errCnt++; - #endif - #if CO_NO_LSS_CLIENT == 1 - if(CO->LSSmaster == NULL) errCnt++; - #endif - #if CO_NO_SDO_CLIENT != 0 - for(i=0; iSDOclient[i] == NULL) errCnt++; - } - #endif - #if CO_NO_TRACE > 0 - for(i=0; itrace[i] == NULL) errCnt++; - } - #endif - - if(errCnt != 0) return CO_ERROR_OUT_OF_MEMORY; +#ifdef CO_MULTIPLE_OD +#error CO_MULTIPLE_OD can not be used with CO_USE_GLOBALS #endif - return CO_ERROR_NO; +static CO_t COO; +static CO_CANmodule_t COO_CANmodule; +static CO_CANrx_t COO_CANmodule_rxArray[CO_CNT_ALL_RX_MSGS]; +static CO_CANtx_t COO_CANmodule_txArray[CO_CNT_ALL_TX_MSGS]; +static CO_NMT_t COO_NMT; +#if ((CO_CONFIG_HB_CONS)&CO_CONFIG_HB_CONS_ENABLE) != 0 +static CO_HBconsumer_t COO_HBcons; +static CO_HBconsNode_t COO_HBconsMonitoredNodes[OD_CNT_ARR_1016]; +#endif +#if ((CO_CONFIG_NODE_GUARDING)&CO_CONFIG_NODE_GUARDING_SLAVE_ENABLE) != 0 +static CO_nodeGuardingSlave_t COO_NGslave; +#endif +#if ((CO_CONFIG_NODE_GUARDING)&CO_CONFIG_NODE_GUARDING_MASTER_ENABLE) != 0 +static CO_nodeGuardingMaster_t COO_NGmaster; +#endif +static CO_EM_t COO_EM; +#if ((CO_CONFIG_EM) & (CO_CONFIG_EM_PRODUCER | CO_CONFIG_EM_HISTORY)) != 0 +static CO_EM_fifo_t COO_EM_FIFO[CO_GET_CNT(ARR_1003) + 1U]; +#endif +static CO_SDOserver_t COO_SDOserver[OD_CNT_SDO_SRV]; +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_ENABLE) != 0 +static CO_SDOclient_t COO_SDOclient[OD_CNT_SDO_CLI]; +#endif +#if ((CO_CONFIG_TIME)&CO_CONFIG_TIME_ENABLE) != 0 +static CO_TIME_t COO_TIME; +#endif +#if ((CO_CONFIG_SYNC)&CO_CONFIG_SYNC_ENABLE) != 0 +static CO_SYNC_t COO_SYNC; +#endif +#if ((CO_CONFIG_PDO)&CO_CONFIG_RPDO_ENABLE) != 0 +static CO_RPDO_t COO_RPDO[OD_CNT_RPDO]; +#endif +#if ((CO_CONFIG_PDO)&CO_CONFIG_TPDO_ENABLE) != 0 +static CO_TPDO_t COO_TPDO[OD_CNT_TPDO]; +#endif +#if ((CO_CONFIG_LEDS)&CO_CONFIG_LEDS_ENABLE) != 0 +static CO_LEDs_t COO_LEDs; +#endif +#if ((CO_CONFIG_GFC)&CO_CONFIG_GFC_ENABLE) != 0 +static CO_GFC_t COO_GFC; +#endif +#if ((CO_CONFIG_SRDO)&CO_CONFIG_SRDO_ENABLE) != 0 +static CO_SRDOGuard_t COO_SRDOGuard; +static CO_SRDO_t COO_SRDO[OD_CNT_SRDO]; +#endif +#if ((CO_CONFIG_LSS)&CO_CONFIG_LSS_SLAVE) != 0 +static CO_LSSslave_t COO_LSSslave; +#endif +#if ((CO_CONFIG_LSS)&CO_CONFIG_LSS_MASTER) != 0 +static CO_LSSmaster_t COO_LSSmaster; +#endif +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII) != 0 +static CO_GTWA_t COO_gtwa; +#endif +#if ((CO_CONFIG_TRACE)&CO_CONFIG_TRACE_ENABLE) != 0 +#ifndef CO_TRACE_BUFFER_SIZE_FIXED +#define CO_TRACE_BUFFER_SIZE_FIXED 100 +#endif +static CO_trace_t COO_trace[OD_CNT_TRACE]; +static uint32_t COO_traceTimeBuffers[OD_CNT_TRACE][CO_TRACE_BUFFER_SIZE_FIXED]; +static int32_t COO_traceValueBuffers[OD_CNT_TRACE][CO_TRACE_BUFFER_SIZE_FIXED]; +#endif + +CO_t* +CO_new(CO_config_t* config, uint32_t* heapMemoryUsed) { + (void)config; + (void)heapMemoryUsed; + + CO_t* co = &COO; + + co->CANmodule = &COO_CANmodule; + co->CANrx = &COO_CANmodule_rxArray[0]; + co->CANtx = &COO_CANmodule_txArray[0]; + + co->NMT = &COO_NMT; +#if ((CO_CONFIG_HB_CONS)&CO_CONFIG_HB_CONS_ENABLE) != 0 + co->HBcons = &COO_HBcons; + co->HBconsMonitoredNodes = &COO_HBconsMonitoredNodes[0]; +#endif +#if ((CO_CONFIG_NODE_GUARDING)&CO_CONFIG_NODE_GUARDING_SLAVE_ENABLE) != 0 + co->NGslave = &COO_NGslave; +#endif +#if ((CO_CONFIG_NODE_GUARDING)&CO_CONFIG_NODE_GUARDING_MASTER_ENABLE) != 0 + co->NGmaster = &COO_NGmaster; +#endif + co->em = &COO_EM; +#if ((CO_CONFIG_EM) & (CO_CONFIG_EM_PRODUCER | CO_CONFIG_EM_HISTORY)) != 0 + co->em_fifo = &COO_EM_FIFO[0]; +#endif + co->SDOserver = &COO_SDOserver[0]; +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_ENABLE) != 0 + co->SDOclient = &COO_SDOclient[0]; +#endif +#if ((CO_CONFIG_TIME)&CO_CONFIG_TIME_ENABLE) != 0 + co->TIME = &COO_TIME; +#endif +#if ((CO_CONFIG_SYNC)&CO_CONFIG_SYNC_ENABLE) != 0 + co->SYNC = &COO_SYNC; +#endif +#if ((CO_CONFIG_PDO)&CO_CONFIG_RPDO_ENABLE) != 0 + co->RPDO = &COO_RPDO[0]; +#endif +#if ((CO_CONFIG_PDO)&CO_CONFIG_TPDO_ENABLE) != 0 + co->TPDO = &COO_TPDO[0]; +#endif +#if ((CO_CONFIG_LEDS)&CO_CONFIG_LEDS_ENABLE) != 0 + co->LEDs = &COO_LEDs; +#endif +#if ((CO_CONFIG_GFC)&CO_CONFIG_GFC_ENABLE) != 0 + co->GFC = &COO_GFC; +#endif +#if ((CO_CONFIG_SRDO)&CO_CONFIG_SRDO_ENABLE) != 0 + co->SRDOGuard = &COO_SRDOGuard; + co->SRDO = &COO_SRDO[0]; +#endif +#if ((CO_CONFIG_LSS)&CO_CONFIG_LSS_SLAVE) != 0 + co->LSSslave = &COO_LSSslave; +#endif +#if ((CO_CONFIG_LSS)&CO_CONFIG_LSS_MASTER) != 0 + co->LSSmaster = &COO_LSSmaster; +#endif +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII) != 0 + co->gtwa = &COO_gtwa; +#endif +#if ((CO_CONFIG_TRACE)&CO_CONFIG_TRACE_ENABLE) != 0 + co->trace = &COO_trace[0]; + co->traceTimeBuffers = &COO_traceTimeBuffers[0][0]; + co->traceValueBuffers = &COO_traceValueBuffers[0][0]; + co->traceBufferSize = CO_TRACE_BUFFER_SIZE_FIXED; +#endif + + return co; } +void +CO_delete(CO_t* co) { + if (co == NULL) { + return; + } + + CO_CANmodule_disable(co->CANmodule); +} +#endif /* #ifdef CO_USE_GLOBALS */ + +/* Helper functions ***********************************************************/ +bool_t +CO_isLSSslaveEnabled(CO_t* co) { + (void)co; /* may be unused */ + bool_t en = false; +#if ((CO_CONFIG_LSS)&CO_CONFIG_LSS_SLAVE) != 0 + if (CO_GET_CNT(LSS_SLV) == 1U) { + en = true; + } +#endif + return en; +} -/******************************************************************************/ -CO_ReturnError_t CO_CANinit( - void *CANdriverState, - uint16_t bitRate) -{ +CO_ReturnError_t +CO_CANinit(CO_t* co, void* CANptr, uint16_t bitRate) { CO_ReturnError_t err; - CO->CANmodule[0]->CANnormal = false; - CO_CANsetConfigurationMode(CANdriverState); + if (co == NULL) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + co->CANmodule->CANnormal = false; + CO_CANsetConfigurationMode(CANptr); - err = CO_CANmodule_init( - CO->CANmodule[0], - CANdriverState, - CO_CANmodule_rxArray0, - CO_RXCAN_NO_MSGS, - CO_CANmodule_txArray0, - CO_TXCAN_NO_MSGS, - bitRate); + /* CANmodule */ + err = CO_CANmodule_init(co->CANmodule, CANptr, co->CANrx, CO_GET_CO(CNT_ALL_RX_MSGS), co->CANtx, + CO_GET_CO(CNT_ALL_TX_MSGS), bitRate); return err; } - -/******************************************************************************/ -#if CO_NO_LSS_SERVER == 1 -CO_ReturnError_t CO_LSSinit( - uint8_t nodeId, - uint16_t bitRate) -{ - CO_LSS_address_t lssAddress; +#if ((CO_CONFIG_LSS)&CO_CONFIG_LSS_SLAVE) != 0 +CO_ReturnError_t +CO_LSSinit(CO_t* co, CO_LSS_address_t* lssAddress, uint8_t* pendingNodeID, uint16_t* pendingBitRate) { CO_ReturnError_t err; - lssAddress.identity.productCode = OD_identity.productCode; - lssAddress.identity.revisionNumber = OD_identity.revisionNumber; - lssAddress.identity.serialNumber = OD_identity.serialNumber; - lssAddress.identity.vendorID = OD_identity.vendorID; - err = CO_LSSslave_init( - CO->LSSslave, - lssAddress, - bitRate, - nodeId, - CO->CANmodule[0], - CO_RXCAN_LSS, - CO_CAN_ID_LSS_SRV, - CO->CANmodule[0], - CO_TXCAN_LSS, - CO_CAN_ID_LSS_CLI); + if ((co == NULL) || (CO_GET_CNT(LSS_SLV) != 1U)) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + /* LSSslave */ + err = CO_LSSslave_init(co->LSSslave, lssAddress, pendingBitRate, pendingNodeID, co->CANmodule, + CO_GET_CO(RX_IDX_LSS_SLV), CO_CAN_ID_LSS_MST, co->CANmodule, CO_GET_CO(TX_IDX_LSS_SLV), + CO_CAN_ID_LSS_SLV); return err; } -#endif /* CO_NO_LSS_SERVER == 1 */ +#endif /* (CO_CONFIG_LSS) & CO_CONFIG_LSS_SLAVE */ + +CO_ReturnError_t +CO_CANopenInit(CO_t* co, CO_NMT_t* NMT, CO_EM_t* em, OD_t* od, OD_entry_t* OD_statusBits, uint16_t NMTcontrol, + uint16_t firstHBTime_ms, uint16_t SDOserverTimeoutTime_ms, uint16_t SDOclientTimeoutTime_ms, + bool_t SDOclientBlockTransfer, uint8_t nodeId, uint32_t* errInfo) { + (void)SDOclientTimeoutTime_ms; + (void)SDOclientBlockTransfer; + CO_ReturnError_t err; +#if ((CO_CONFIG_EM)&CO_CONFIG_EM_STATUS_BITS) == 0 + (void)OD_statusBits; /* may be unused */ +#endif -/******************************************************************************/ -CO_ReturnError_t CO_CANopenInit( - uint8_t nodeId) -{ - int16_t i; - CO_ReturnError_t err; + if ((co == NULL) || ((CO_GET_CNT(NMT) == 0U) && (NMT == NULL)) || ((CO_GET_CNT(EM) == 0U) && (em == NULL))) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + /* alternatives */ + if (CO_GET_CNT(NMT) == 0U) { + co->NMT = NMT; + } + if (em == NULL) { + em = co->em; + } /* Verify CANopen Node-ID */ - if(nodeId<1 || nodeId>127) { - return CO_ERROR_PARAMETERS; - } - - for (i=0; iSDO[i], - COB_IDClientToServer, - COB_IDServerToClient, - OD_H1200_SDO_SERVER_PARAM+i, - i==0 ? 0 : CO->SDO[0], - &CO_OD[0], - CO_OD_NoOfElements, - CO_SDO_ODExtensions, - nodeId, - CO->CANmodule[0], - CO_RXCAN_SDO_SRV+i, - CO->CANmodule[0], - CO_TXCAN_SDO_SRV+i); - } - - if(err){return err;} - - - err = CO_EM_init( - CO->em, - CO->emPr, - CO->SDO[0], - &OD_errorStatusBits[0], - ODL_errorStatusBits_stringLength, - &OD_errorRegister, - &OD_preDefinedErrorField[0], - ODL_preDefinedErrorField_arrayLength, - CO->CANmodule[0], - CO_RXCAN_EMERG, - CO->CANmodule[0], - CO_TXCAN_EMERG, - (uint16_t)CO_CAN_ID_EMERGENCY + nodeId); - - if(err){return err;} - - - err = CO_NMT_init( - CO->NMT, - CO->emPr, - nodeId, - 500, - CO->CANmodule[0], - CO_RXCAN_NMT, - CO_CAN_ID_NMT_SERVICE, - CO->CANmodule[0], - CO_TXCAN_HB, - CO_CAN_ID_HEARTBEAT + nodeId); - - if(err){return err;} - - -#if CO_NO_NMT_MASTER == 1 - NMTM_txBuff = CO_CANtxBufferInit(/* return pointer to 8-byte CAN data buffer, which should be populated */ - CO->CANmodule[0], /* pointer to CAN module used for sending this message */ - CO_TXCAN_NMT, /* index of specific buffer inside CAN module */ - 0x0000, /* CAN identifier */ - 0, /* rtr */ - 2, /* number of data bytes */ - 0); /* synchronous message flag bit */ -#endif -#if CO_NO_LSS_CLIENT == 1 - err = CO_LSSmaster_init( - CO->LSSmaster, - CO_LSSmaster_DEFAULT_TIMEOUT, - CO->CANmodule[0], - CO_RXCAN_LSS, - CO_CAN_ID_LSS_CLI, - CO->CANmodule[0], - CO_TXCAN_LSS, - CO_CAN_ID_LSS_SRV); - - if(err){return err;} - -#endif - -#if CO_NO_SYNC == 1 - err = CO_SYNC_init( - CO->SYNC, - CO->em, - CO->SDO[0], - &CO->NMT->operatingState, - OD_COB_ID_SYNCMessage, - OD_communicationCyclePeriod, - OD_synchronousCounterOverflowValue, - CO->CANmodule[0], - CO_RXCAN_SYNC, - CO->CANmodule[0], - CO_TXCAN_SYNC); - - if(err){return err;} -#endif - -#if CO_NO_TIME == 1 - err = CO_TIME_init( - CO->TIME, - CO->em, - CO->SDO[0], - &CO->NMT->operatingState, - OD_COB_ID_TIME, - 0, - CO->CANmodule[0], - CO_RXCAN_TIME, - CO->CANmodule[0], - CO_TXCAN_TIME); - - if(err){return err;} -#endif - - for(i=0; iCANmodule[0]; - uint16_t CANdevRxIdx = CO_RXCAN_RPDO + i; - - err = CO_RPDO_init( - CO->RPDO[i], - CO->em, - CO->SDO[0], - CO->SYNC, - &CO->NMT->operatingState, - nodeId, - ((i<4) ? (CO_CAN_ID_RPDO_1+i*0x100) : 0), - 0, - (CO_RPDOCommPar_t*) &OD_RPDOCommunicationParameter[i], - (CO_RPDOMapPar_t*) &OD_RPDOMappingParameter[i], - OD_H1400_RXPDO_1_PARAM+i, - OD_H1600_RXPDO_1_MAPPING+i, - CANdevRx, - CANdevRxIdx); - - if(err){return err;} - } - - for(i=0; iTPDO[i], - CO->em, - CO->SDO[0], - CO->SYNC, - &CO->NMT->operatingState, - nodeId, - ((i<4) ? (CO_CAN_ID_TPDO_1+i*0x100) : 0), - 0, - (CO_TPDOCommPar_t*) &OD_TPDOCommunicationParameter[i], - (CO_TPDOMapPar_t*) &OD_TPDOMappingParameter[i], - OD_H1800_TXPDO_1_PARAM+i, - OD_H1A00_TXPDO_1_MAPPING+i, - CO->CANmodule[0], - CO_TXCAN_TPDO+i); - - if(err){return err;} - } - - - err = CO_HBconsumer_init( - CO->HBcons, - CO->em, - CO->SDO[0], - &OD_consumerHeartbeatTime[0], - CO_HBcons_monitoredNodes, - CO_NO_HB_CONS, - CO->CANmodule[0], - CO_RXCAN_CONS_HB); - - if(err){return err;} - - -#if CO_NO_SDO_CLIENT != 0 - - for(i=0; iSDOclient[i], - CO->SDO[0], - (CO_SDOclientPar_t*) &OD_SDOClientParameter[i], - CO->CANmodule[0], - CO_RXCAN_SDO_CLI+i, - CO->CANmodule[0], - CO_TXCAN_SDO_CLI+i); - - if(err){return err;} - - } -#endif - - -#if CO_NO_TRACE > 0 - for(i=0; itrace[i], - CO->SDO[0], - OD_traceConfig[i].axisNo, - CO_traceTimeBuffers[i], - CO_traceValueBuffers[i], - CO_traceBufferSize[i], - &OD_traceConfig[i].map, - &OD_traceConfig[i].format, - &OD_traceConfig[i].trigger, - &OD_traceConfig[i].threshold, - &OD_trace[i].value, - &OD_trace[i].min, - &OD_trace[i].max, - &OD_trace[i].triggerTime, - OD_INDEX_TRACE_CONFIG + i, - OD_INDEX_TRACE + i); + co->nodeIdUnconfigured = false; +#if ((CO_CONFIG_LSS)&CO_CONFIG_LSS_SLAVE) != 0 + if ((CO_GET_CNT(LSS_SLV) == 1U) && (nodeId == CO_LSS_NODE_ID_ASSIGNMENT)) { + co->nodeIdUnconfigured = true; + } else +#endif + if ((nodeId < 1U) || (nodeId > 127U)) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } else { /* MISRA C 2004 14.10 */ + } + +#if ((CO_CONFIG_LEDS)&CO_CONFIG_LEDS_ENABLE) != 0 + if (CO_GET_CNT(LEDS) == 1U) { + err = CO_LEDs_init(co->LEDs); + if (err != CO_ERROR_NO) { + return err; + } } #endif - return CO_ERROR_NO; -} + /* CANopen Node ID is unconfigured, stop initialization here */ + if (co->nodeIdUnconfigured) { + return CO_ERROR_NODE_ID_UNCONFIGURED_LSS; + } + /* Emergency */ + if (CO_GET_CNT(EM) == 1U) { + err = CO_EM_init(co->em, co->CANmodule, OD_GET(H1001, OD_H1001_ERR_REG), +#if ((CO_CONFIG_EM) & (CO_CONFIG_EM_PRODUCER | CO_CONFIG_EM_HISTORY)) != 0 + co->em_fifo, (CO_GET_CNT(ARR_1003) + 1U), +#endif +#if ((CO_CONFIG_EM)&CO_CONFIG_EM_PRODUCER) != 0 + OD_GET(H1014, OD_H1014_COBID_EMERGENCY), CO_GET_CO(TX_IDX_EM_PROD), +#if ((CO_CONFIG_EM)&CO_CONFIG_EM_PROD_INHIBIT) != 0 + OD_GET(H1015, OD_H1015_INHIBIT_TIME_EMCY), +#endif +#endif +#if ((CO_CONFIG_EM)&CO_CONFIG_EM_HISTORY) != 0 + OD_GET(H1003, OD_H1003_PREDEF_ERR_FIELD), +#endif +#if ((CO_CONFIG_EM)&CO_CONFIG_EM_STATUS_BITS) != 0 + OD_statusBits, +#endif +#if ((CO_CONFIG_EM)&CO_CONFIG_EM_CONSUMER) != 0 + co->CANmodule, CO_GET_CO(RX_IDX_EM_CONS), +#endif + nodeId, errInfo); + if (err != CO_ERROR_NO) { + return err; + } + } -/******************************************************************************/ -CO_ReturnError_t CO_init( - void *CANdriverState, - uint8_t nodeId, - uint16_t bitRate) -{ - CO_ReturnError_t err; + /* NMT_Heartbeat */ + if (CO_GET_CNT(NMT) == 1U) { + err = CO_NMT_init(co->NMT, OD_GET(H1017, OD_H1017_PRODUCER_HB_TIME), em, nodeId, NMTcontrol, firstHBTime_ms, + co->CANmodule, CO_GET_CO(RX_IDX_NMT_SLV), CO_CAN_ID_NMT_SERVICE, +#if ((CO_CONFIG_NMT)&CO_CONFIG_NMT_MASTER) != 0 + co->CANmodule, CO_GET_CO(TX_IDX_NMT_MST), CO_CAN_ID_NMT_SERVICE, +#endif + co->CANmodule, CO_GET_CO(TX_IDX_HB_PROD), CO_CAN_ID_HEARTBEAT + nodeId, errInfo); + if (err != CO_ERROR_NO) { + return err; + } + } - err = CO_new(); - if (err) { - return err; +#if ((CO_CONFIG_HB_CONS)&CO_CONFIG_HB_CONS_ENABLE) != 0 + if (CO_GET_CNT(HB_CONS) == 1U) { + err = CO_HBconsumer_init(co->HBcons, em, co->HBconsMonitoredNodes, CO_GET_CNT(ARR_1016), + OD_GET(H1016, OD_H1016_CONSUMER_HB_TIME), co->CANmodule, CO_GET_CO(RX_IDX_HB_CONS), + errInfo); + if (err != CO_ERROR_NO) { + return err; + } } +#endif - err = CO_CANinit(CANdriverState, bitRate); - if (err) { - CO_delete(CANdriverState); +#if ((CO_CONFIG_NODE_GUARDING)&CO_CONFIG_NODE_GUARDING_SLAVE_ENABLE) != 0 + err = CO_nodeGuardingSlave_init(co->NGslave, OD_GET(H100C, OD_H100C_GUARD_TIME), + OD_GET(H100D, OD_H100D_LIFETIME_FACTOR), em, CO_CAN_ID_HEARTBEAT + nodeId, + co->CANmodule, CO_GET_CO(RX_IDX_NG_SLV), co->CANmodule, CO_GET_CO(TX_IDX_NG_SLV), + errInfo); + if (err != CO_ERROR_NO) { return err; } - - err = CO_CANopenInit(nodeId); +#endif +#if ((CO_CONFIG_NODE_GUARDING)&CO_CONFIG_NODE_GUARDING_MASTER_ENABLE) != 0 + err = CO_nodeGuardingMaster_init(co->NGmaster, em, co->CANmodule, CO_GET_CO(RX_IDX_NG_MST), co->CANmodule, + CO_GET_CO(TX_IDX_NG_MST)); if (err) { - CO_delete(CANdriverState); return err; } +#endif + + /* SDOserver */ + if (CO_GET_CNT(SDO_SRV) > 0U) { + OD_entry_t* SDOsrvPar = OD_GET(H1200, OD_H1200_SDO_SERVER_1_PARAM); + for (uint16_t i = 0; i < CO_GET_CNT(SDO_SRV); i++) { + err = CO_SDOserver_init(&co->SDOserver[i], od, SDOsrvPar, nodeId, SDOserverTimeoutTime_ms, co->CANmodule, + CO_GET_CO(RX_IDX_SDO_SRV) + i, co->CANmodule, CO_GET_CO(TX_IDX_SDO_SRV) + i, + errInfo); + if (err != CO_ERROR_NO) { + return err; + } + SDOsrvPar++; + } + } + +#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_ENABLE) != 0 + if (CO_GET_CNT(SDO_CLI) > 0U) { + OD_entry_t* SDOcliPar = OD_GET(H1280, OD_H1280_SDO_CLIENT_1_PARAM); + for (uint16_t i = 0; i < CO_GET_CNT(SDO_CLI); i++) { + err = CO_SDOclient_init(&co->SDOclient[i], od, SDOcliPar, nodeId, co->CANmodule, + CO_GET_CO(RX_IDX_SDO_CLI) + i, co->CANmodule, CO_GET_CO(TX_IDX_SDO_CLI) + i, + errInfo); + SDOcliPar++; + if (err != CO_ERROR_NO) { + return err; + } + } + } +#endif + +#if ((CO_CONFIG_TIME)&CO_CONFIG_TIME_ENABLE) != 0 + if (CO_GET_CNT(TIME) == 1U) { + err = CO_TIME_init(co->TIME, OD_GET(H1012, OD_H1012_COBID_TIME), co->CANmodule, CO_GET_CO(RX_IDX_TIME), +#if ((CO_CONFIG_TIME)&CO_CONFIG_TIME_PRODUCER) != 0 + co->CANmodule, CO_GET_CO(TX_IDX_TIME), +#endif + errInfo); + if (err != CO_ERROR_NO) { + return err; + } + } +#endif + +#if ((CO_CONFIG_SYNC)&CO_CONFIG_SYNC_ENABLE) != 0 + if (CO_GET_CNT(SYNC) == 1U) { + err = CO_SYNC_init(co->SYNC, em, OD_GET(H1005, OD_H1005_COBID_SYNC), OD_GET(H1006, OD_H1006_COMM_CYCL_PERIOD), + OD_GET(H1007, OD_H1007_SYNC_WINDOW_LEN), OD_GET(H1019, OD_H1019_SYNC_CNT_OVERFLOW), + co->CANmodule, CO_GET_CO(RX_IDX_SYNC), +#if ((CO_CONFIG_SYNC)&CO_CONFIG_SYNC_PRODUCER) != 0 + co->CANmodule, CO_GET_CO(TX_IDX_SYNC), +#endif + errInfo); + if (err != CO_ERROR_NO) { + return err; + } + } +#endif + +#if ((CO_CONFIG_LSS)&CO_CONFIG_LSS_MASTER) != 0 + if (CO_GET_CNT(LSS_MST) == 1U) { + err = CO_LSSmaster_init(co->LSSmaster, CO_LSSmaster_DEFAULT_TIMEOUT, co->CANmodule, CO_GET_CO(RX_IDX_LSS_MST), + CO_CAN_ID_LSS_SLV, co->CANmodule, CO_GET_CO(TX_IDX_LSS_MST), CO_CAN_ID_LSS_MST); + if (err != CO_ERROR_NO) { + return err; + } + } +#endif + +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII) != 0 + if (CO_GET_CNT(GTWA) == 1U) { + err = CO_GTWA_init(co->gtwa, +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_SDO) != 0 + &co->SDOclient[0], SDOclientTimeoutTime_ms, SDOclientBlockTransfer, +#endif +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_NMT) != 0 + co->NMT, +#endif +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_LSS) != 0 + co->LSSmaster, +#endif +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII_PRINT_LEDS) != 0 + co->LEDs, +#endif + 0); + if (err != CO_ERROR_NO) { + return err; + } + } +#endif + +#if (CO_CONFIG_TRACE) & CO_CONFIG_TRACE_ENABLE + if (CO_GET_CNT(TRACE) > 0) { + for (uint16_t i = 0; i < CO_GET_CNT(TRACE); i++) { + err = CO_trace_init(co->trace[i], co->SDO[0], OD_traceConfig[i].axisNo, CO_traceTimeBuffers[i], + CO_traceValueBuffers[i], CO_traceBufferSize[i], &OD_traceConfig[i].map, + &OD_traceConfig[i].format, &OD_traceConfig[i].trigger, &OD_traceConfig[i].threshold, + &OD_trace[i].value, &OD_trace[i].min, &OD_trace[i].max, &OD_trace[i].triggerTime, + OD_INDEX_TRACE_CONFIG + i, OD_INDEX_TRACE + i); + if (err) { + return err; + } + } + } +#endif return CO_ERROR_NO; } +CO_ReturnError_t +CO_CANopenInitPDO(CO_t* co, CO_EM_t* em, OD_t* od, uint8_t nodeId, uint32_t* errInfo) { + if (co == NULL) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + if ((nodeId < 1U) || (nodeId > 127U) || co->nodeIdUnconfigured) { + return (co->nodeIdUnconfigured) ? CO_ERROR_NODE_ID_UNCONFIGURED_LSS : CO_ERROR_ILLEGAL_ARGUMENT; + } -/******************************************************************************/ -void CO_delete(void *CANdriverState){ -#ifndef CO_USE_GLOBALS - int16_t i; +#if ((CO_CONFIG_PDO)&CO_CONFIG_RPDO_ENABLE) != 0 + if (CO_GET_CNT(RPDO) > 0U) { + OD_entry_t* RPDOcomm = OD_GET(H1400, OD_H1400_RXPDO_1_PARAM); + OD_entry_t* RPDOmap = OD_GET(H1600, OD_H1600_RXPDO_1_MAPPING); + for (uint16_t i = 0; i < CO_GET_CNT(RPDO); i++) { + CO_ReturnError_t err; + uint16_t preDefinedCanId = 0; + if (i < CO_RPDO_DEFAULT_CANID_COUNT) { +#if CO_RPDO_DEFAULT_CANID_COUNT <= 4 + preDefinedCanId = (uint16_t)((CO_CAN_ID_RPDO_1 + (i * 0x100U)) + nodeId); +#else + uint16_t pdoOffset = i % 4; + uint16_t nodeIdOffset = i / 4; + preDefinedCanId = (CO_CAN_ID_RPDO_1 + pdoOffset * 0x100) + nodeId + nodeIdOffset; +#endif + } + err = CO_RPDO_init(&co->RPDO[i], od, em, +#if ((CO_CONFIG_PDO)&CO_CONFIG_PDO_SYNC_ENABLE) != 0 + co->SYNC, +#endif + preDefinedCanId, RPDOcomm, RPDOmap, co->CANmodule, CO_GET_CO(RX_IDX_RPDO) + i, errInfo); + if (err != CO_ERROR_NO) { + return err; + } + RPDOcomm++; + RPDOmap++; + } + } #endif - CO_CANsetConfigurationMode(CANdriverState); - CO_CANmodule_disable(CO->CANmodule[0]); - -#ifndef CO_USE_GLOBALS - #if CO_NO_TRACE > 0 - for(i=0; itrace[i]); - free(CO_traceTimeBuffers[i]); - free(CO_traceValueBuffers[i]); - } - #endif - #if CO_NO_SDO_CLIENT != 0 - for(i=0; iSDOclient[i]); - } - #endif - #if CO_NO_LSS_SERVER == 1 - free(CO->LSSslave); - #endif - #if CO_NO_LSS_CLIENT == 1 - free(CO->LSSmaster); - #endif - free(CO_HBcons_monitoredNodes); - free(CO->HBcons); - for(i=0; iRPDO[i]); - } - for(i=0; iTPDO[i]); - } - #if CO_NO_SYNC == 1 - free(CO->SYNC); - #endif - #if CO_NO_TIME == 1 - free(CO->TIME); - #endif - free(CO->NMT); - free(CO->emPr); - free(CO->em); - free(CO_SDO_ODExtensions); - for(i=0; iSDO[i]); - } - free(CO_CANmodule_txArray0); - free(CO_CANmodule_rxArray0); - free(CO->CANmodule[0]); - CO = NULL; +#if ((CO_CONFIG_PDO)&CO_CONFIG_TPDO_ENABLE) != 0 + if (CO_GET_CNT(TPDO) > 0U) { + OD_entry_t* TPDOcomm = OD_GET(H1800, OD_H1800_TXPDO_1_PARAM); + OD_entry_t* TPDOmap = OD_GET(H1A00, OD_H1A00_TXPDO_1_MAPPING); + for (uint16_t i = 0; i < CO_GET_CNT(TPDO); i++) { + CO_ReturnError_t err; + uint16_t preDefinedCanId = 0; + if (i < CO_TPDO_DEFAULT_CANID_COUNT) { +#if CO_TPDO_DEFAULT_CANID_COUNT <= 4 + preDefinedCanId = (uint16_t)((CO_CAN_ID_TPDO_1 + (i * 0x100U)) + nodeId); +#else + uint16_t pdoOffset = i % 4; + uint16_t nodeIdOffset = i / 4; + preDefinedCanId = (CO_CAN_ID_TPDO_1 + pdoOffset * 0x100) + nodeId + nodeIdOffset; +#endif + } + err = CO_TPDO_init(&co->TPDO[i], od, em, +#if ((CO_CONFIG_PDO)&CO_CONFIG_PDO_SYNC_ENABLE) != 0 + co->SYNC, #endif + preDefinedCanId, TPDOcomm, TPDOmap, co->CANmodule, CO_GET_CO(TX_IDX_TPDO) + i, errInfo); + if (err != CO_ERROR_NO) { + return err; + } + TPDOcomm++; + TPDOmap++; + } + } +#endif + + return CO_ERROR_NO; } +#if (((CO_CONFIG_GFC)&CO_CONFIG_GFC_ENABLE) != 0) || (((CO_CONFIG_SRDO)&CO_CONFIG_SRDO_ENABLE) != 0) +CO_ReturnError_t +CO_CANopenInitSRDO(CO_t* co, CO_EM_t* em, OD_t* od, uint8_t nodeId, uint32_t* errInfo) { + if (co == NULL) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + if ((nodeId < 1U) || (nodeId > 127U) || co->nodeIdUnconfigured) { + return (co->nodeIdUnconfigured) ? CO_ERROR_NODE_ID_UNCONFIGURED_LSS : CO_ERROR_ILLEGAL_ARGUMENT; + } -/******************************************************************************/ -CO_NMT_reset_cmd_t CO_process( - CO_t *co, - uint16_t timeDifference_ms, - uint16_t *timerNext_ms) -{ - uint8_t i; - bool_t NMTisPreOrOperational = false; +#if ((CO_CONFIG_GFC)&CO_CONFIG_GFC_ENABLE) != 0 + if (CO_GET_CNT(GFC) == 1) { + CO_ReturnError_t err; + err = CO_GFC_init(co->GFC, OD_GET(H1300, OD_H1300_GFC_PARAM), co->CANmodule, CO_GET_CO(RX_IDX_GFC), + CO_CAN_ID_GFC, co->CANmodule, CO_GET_CO(TX_IDX_GFC), CO_CAN_ID_GFC); + if (err) { + return err; + } + } +#endif + +#if ((CO_CONFIG_SRDO)&CO_CONFIG_SRDO_ENABLE) != 0 + if (CO_GET_CNT(SRDO) > 0U) { + CO_ReturnError_t err; + err = CO_SRDOGuard_init(co->SRDOGuard, OD_GET(H13FE, OD_H13FE_SRDO_VALID), + OD_GET(H13FF, OD_H13FF_SRDO_CHECKSUM), errInfo); + if (err != CO_ERROR_NO) { + return err; + } + + OD_entry_t* SRDOcomm = OD_GET(H1301, OD_H1301_SRDO_1_PARAM); + OD_entry_t* SRDOmap = OD_GET(H1381, OD_H1381_SRDO_1_MAPPING); + for (uint8_t i = 0; i < CO_GET_CNT(SRDO); i++) { + uint16_t CANdevRxIdx = (uint16_t)(CO_GET_CO(RX_IDX_SRDO) + ((uint16_t)(i)*2U)); + uint16_t CANdevTxIdx = (uint16_t)(CO_GET_CO(TX_IDX_SRDO) + ((uint16_t)(i)*2U)); + + err = CO_SRDO_init(&co->SRDO[i], i, co->SRDOGuard, od, em, nodeId, ((i == 0U) ? CO_CAN_ID_SRDO_1 : 0U), + SRDOcomm, SRDOmap, co->CANmodule, co->CANmodule, CANdevRxIdx, CANdevRxIdx + 1U, + co->CANmodule, co->CANmodule, CANdevTxIdx, CANdevTxIdx + 1U, errInfo); + if (err != CO_ERROR_NO) { + return err; + } + SRDOcomm++; + SRDOmap++; + } + } +#endif + + return CO_ERROR_NO; +} +#endif + +CO_NMT_reset_cmd_t +CO_process(CO_t* co, bool_t enableGateway, uint32_t timeDifference_us, uint32_t* timerNext_us) { + (void)enableGateway; /* may be unused */ CO_NMT_reset_cmd_t reset = CO_RESET_NOT; -#ifdef CO_USE_LEDS - static uint16_t ms50 = 0; -#endif /* CO_USE_LEDS */ + CO_NMT_internalState_t NMTstate = CO_NMT_getInternalState(co->NMT); + bool_t NMTisPreOrOperational = ((NMTstate == CO_NMT_PRE_OPERATIONAL) || (NMTstate == CO_NMT_OPERATIONAL)); - if(co->NMT->operatingState == CO_NMT_PRE_OPERATIONAL || co->NMT->operatingState == CO_NMT_OPERATIONAL) - NMTisPreOrOperational = true; + /* CAN module */ + CO_CANmodule_process(co->CANmodule); -#ifdef CO_USE_LEDS - ms50 += timeDifference_ms; - if(ms50 >= 50){ - ms50 -= 50; - CO_NMT_blinkingProcess50ms(co->NMT); +#if ((CO_CONFIG_LSS)&CO_CONFIG_LSS_SLAVE) + if (CO_GET_CNT(LSS_SLV) == 1U) { + if (CO_LSSslave_process(co->LSSslave)) { + reset = CO_RESET_COMM; + } } -#endif /* CO_USE_LEDS */ +#endif - for(i=0; iSDO[i], - NMTisPreOrOperational, - timeDifference_ms, - 1000, - timerNext_ms); +#if ((CO_CONFIG_LEDS)&CO_CONFIG_LEDS_ENABLE) != 0 + bool_t unc = co->nodeIdUnconfigured; + uint16_t CANerrorStatus = co->CANmodule->CANerrorStatus; + bool_t LSSslave_configuration = false; +#if ((CO_CONFIG_LSS)&CO_CONFIG_LSS_SLAVE) != 0 + if (CO_GET_CNT(LSS_SLV) == 1U) { + if (CO_LSSslave_getState(co->LSSslave) == CO_LSS_STATE_CONFIGURATION) { + LSSslave_configuration = true; + } } +#endif +/* default macro, can be defined externally */ +#ifndef CO_STATUS_FIRMWARE_DOWNLOAD_IN_PROGRESS +#define CO_STATUS_FIRMWARE_DOWNLOAD_IN_PROGRESS false +#endif - CO_EM_process( - co->emPr, - NMTisPreOrOperational, - timeDifference_ms * 10, - OD_inhibitTimeEMCY, - timerNext_ms); + if (CO_GET_CNT(LEDS) == 1U) { + bool_t ErrSync = CO_isError(co->em, CO_EM_SYNC_TIME_OUT); + bool_t ErrHbCons = CO_isError(co->em, CO_EM_HEARTBEAT_CONSUMER); + bool_t ErrHbConsRemote = CO_isError(co->em, CO_EM_HB_CONSUMER_REMOTE_RESET); + CO_LEDs_process(co->LEDs, timeDifference_us, unc ? CO_NMT_INITIALIZING : NMTstate, LSSslave_configuration, + (CANerrorStatus & CO_CAN_ERRTX_BUS_OFF) != 0U, (CANerrorStatus & CO_CAN_ERR_WARN_PASSIVE) != 0U, + false, /* RPDO event timer timeout */ + unc ? false : ErrSync, unc ? false : (ErrHbCons || ErrHbConsRemote), + CO_getErrorRegister(co->em) != 0U, CO_STATUS_FIRMWARE_DOWNLOAD_IN_PROGRESS, timerNext_us); + } +#endif + /* CANopen Node ID is unconfigured (LSS slave), stop processing here */ + if (co->nodeIdUnconfigured) { + return reset; + } + + /* Emergency */ + if (CO_GET_CNT(EM) == 1U) { + CO_EM_process(co->em, NMTisPreOrOperational, timeDifference_us, timerNext_us); + } + + /* NMT_Heartbeat */ + if (CO_GET_CNT(NMT) == 1U) { + reset = CO_NMT_process(co->NMT, &NMTstate, timeDifference_us, timerNext_us); + } + NMTisPreOrOperational = ((NMTstate == CO_NMT_PRE_OPERATIONAL) || (NMTstate == CO_NMT_OPERATIONAL)); + + /* SDOserver */ + for (uint8_t i = 0; i < CO_GET_CNT(SDO_SRV); i++) { + (void)CO_SDOserver_process(&co->SDOserver[i], NMTisPreOrOperational, timeDifference_us, timerNext_us); + } - reset = CO_NMT_process( - co->NMT, - timeDifference_ms, - OD_producerHeartbeatTime, - OD_NMTStartup, - OD_errorRegister, - OD_errorBehavior, - timerNext_ms); +#if ((CO_CONFIG_HB_CONS)&CO_CONFIG_HB_CONS_ENABLE) != 0 + if (CO_GET_CNT(HB_CONS) == 1U) { + CO_HBconsumer_process(co->HBcons, NMTisPreOrOperational, timeDifference_us, timerNext_us); + } +#endif +#if ((CO_CONFIG_NODE_GUARDING)&CO_CONFIG_NODE_GUARDING_SLAVE_ENABLE) != 0 + CO_nodeGuardingSlave_process(co->NGslave, NMTstate, (co->NMT->HBproducerTime_us > 0U), timeDifference_us, + timerNext_us); +#endif +#if ((CO_CONFIG_NODE_GUARDING)&CO_CONFIG_NODE_GUARDING_MASTER_ENABLE) != 0 + CO_nodeGuardingMaster_process(co->NGmaster, timeDifference_us, timerNext_us); +#endif - CO_HBconsumer_process( - co->HBcons, - NMTisPreOrOperational, - timeDifference_ms); +#if ((CO_CONFIG_TIME)&CO_CONFIG_TIME_ENABLE) != 0 + if (CO_GET_CNT(TIME) == 1U) { + (void)CO_TIME_process(co->TIME, NMTisPreOrOperational, timeDifference_us); + } +#endif -#if CO_NO_TIME == 1 - CO_TIME_process( - co->TIME, - timeDifference_ms); +#if ((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII) != 0 + if (CO_GET_CNT(GTWA) == 1U) { + CO_GTWA_process(co->gtwa, enableGateway, timeDifference_us, timerNext_us); + } #endif return reset; } - -/******************************************************************************/ -#if CO_NO_SYNC == 1 -bool_t CO_process_SYNC( - CO_t *co, - uint32_t timeDifference_us) -{ +#if ((CO_CONFIG_SYNC)&CO_CONFIG_SYNC_ENABLE) != 0 +bool_t +CO_process_SYNC(CO_t* co, uint32_t timeDifference_us, uint32_t* timerNext_us) { bool_t syncWas = false; - switch(CO_SYNC_process(co->SYNC, timeDifference_us, OD_synchronousWindowLength)){ - case 1: //immediately after the SYNC message - syncWas = true; - break; - case 2: //outside SYNC window - CO_CANclearPendingSyncPDOs(co->CANmodule[0]); - break; + if ((!co->nodeIdUnconfigured) && (CO_GET_CNT(SYNC) == 1U)) { + CO_NMT_internalState_t NMTstate = CO_NMT_getInternalState(co->NMT); + bool_t NMTisPreOrOperational = ((NMTstate == CO_NMT_PRE_OPERATIONAL) || (NMTstate == CO_NMT_OPERATIONAL)); + + CO_SYNC_status_t sync_process = CO_SYNC_process(co->SYNC, NMTisPreOrOperational, timeDifference_us, + timerNext_us); + + switch (sync_process) { + case CO_SYNC_NONE: break; + case CO_SYNC_RX_TX: syncWas = true; break; + case CO_SYNC_PASSED_WINDOW: CO_CANclearPendingSyncPDOs(co->CANmodule); break; + default: + /* MISRA C 2004 15.3 */ + break; + } } return syncWas; } #endif -/******************************************************************************/ -void CO_process_RPDO( - CO_t *co, - bool_t syncWas) -{ - int16_t i; +#if ((CO_CONFIG_PDO)&CO_CONFIG_RPDO_ENABLE) != 0 +void +CO_process_RPDO(CO_t* co, bool_t syncWas, uint32_t timeDifference_us, uint32_t* timerNext_us) { + (void)timeDifference_us; + (void)timerNext_us; + if (co->nodeIdUnconfigured) { + return; + } + + bool_t NMTisOperational = CO_NMT_getInternalState(co->NMT) == CO_NMT_OPERATIONAL; - for(i=0; iRPDO[i], syncWas); + for (uint16_t i = 0; i < CO_GET_CNT(RPDO); i++) { + CO_RPDO_process(&co->RPDO[i], +#if ((CO_CONFIG_PDO)&CO_CONFIG_RPDO_TIMERS_ENABLE) != 0 + timeDifference_us, timerNext_us, +#endif + NMTisOperational, syncWas); } } +#endif +#if ((CO_CONFIG_PDO)&CO_CONFIG_TPDO_ENABLE) != 0 +void +CO_process_TPDO(CO_t* co, bool_t syncWas, uint32_t timeDifference_us, uint32_t* timerNext_us) { + (void)timeDifference_us; + (void)timerNext_us; + if (co->nodeIdUnconfigured) { + return; + } -/******************************************************************************/ -void CO_process_TPDO( - CO_t *co, - bool_t syncWas, - uint32_t timeDifference_us) -{ - int16_t i; + bool_t NMTisOperational = CO_NMT_getInternalState(co->NMT) == CO_NMT_OPERATIONAL; - /* Verify PDO Change Of State and process PDOs */ - for(i=0; iTPDO[i]->sendRequest) - co->TPDO[i]->sendRequest = CO_TPDOisCOS(co->TPDO[i]); - CO_TPDO_process(co->TPDO[i], syncWas, timeDifference_us); + for (uint16_t i = 0; i < CO_GET_CNT(TPDO); i++) { + CO_TPDO_process(&co->TPDO[i], +#if ((CO_CONFIG_PDO)&CO_CONFIG_TPDO_TIMERS_ENABLE) != 0 + timeDifference_us, timerNext_us, +#endif + NMTisOperational, syncWas); } } +#endif + +#if ((CO_CONFIG_SRDO)&CO_CONFIG_SRDO_ENABLE) != 0 +CO_SRDO_state_t +CO_process_SRDO(CO_t* co, uint32_t timeDifference_us, uint32_t* timerNext_us) { + static bool_t NMTisOperationalPrevius = false; + uint8_t i; + CO_ReturnError_t err; + + if (co->nodeIdUnconfigured) { + return CO_SRDO_state_unknown; + } + + bool_t NMTisOperational = CO_NMT_getInternalState(co->NMT) == CO_NMT_OPERATIONAL; + + if (NMTisOperationalPrevius != NMTisOperational) { + NMTisOperationalPrevius = NMTisOperational; + if (NMTisOperational) { + for (i = 0; i < CO_GET_CNT(SRDO); i++) { + err = CO_SRDO_config(&co->SRDO[i], i, co->SRDOGuard, NULL); + + if (err != CO_ERROR_NO) { + return CO_SRDO_state_error_internal; + } + } + } + } + + CO_SRDO_state_t lowestState = CO_SRDO_state_deleted; + + for (i = 0; i < CO_GET_CNT(SRDO); i++) { + CO_SRDO_state_t state = CO_SRDO_process(&co->SRDO[i], timeDifference_us, timerNext_us, NMTisOperational); + if (state < lowestState) { + lowestState = state; + } + } + + return lowestState; +} +#endif diff --git a/CANopen.h b/CANopen.h index a9e558a6..53627179 100644 --- a/CANopen.h +++ b/CANopen.h @@ -1,324 +1,588 @@ /** - * Main CANopen stack file. - * - * It combines Object dictionary (CO_OD) and all other CANopen source files. - * Configuration information are read from CO_OD.h file. This file uses one - * CAN module. If multiple CAN modules are to be used, then this file may be - * customized for different CANopen configuration. (One or multiple CANopen - * device on one or multiple CAN modules.) + * Main CANopenNode file. * * @file CANopen.h * @ingroup CO_CANopen * @author Janez Paternoster * @author Uwe Kindler - * @copyright 2010 - 2020 Janez Paternoster + * @copyright 2010 - 2023 Janez Paternoster * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . + * This file is part of , a CANopen Stack. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. */ - #ifndef CANopen_H #define CANopen_H +#include "301/CO_driver.h" +#include "301/CO_ODinterface.h" +#include "301/CO_NMT_Heartbeat.h" +#include "301/CO_HBconsumer.h" +#include "301/CO_Node_Guarding.h" +#include "301/CO_Emergency.h" +#include "301/CO_SDOserver.h" +#include "301/CO_SDOclient.h" +#include "301/CO_SYNC.h" +#include "301/CO_PDO.h" +#include "301/CO_TIME.h" +#include "303/CO_LEDs.h" +#include "304/CO_GFC.h" +#include "304/CO_SRDO.h" +#include "305/CO_LSSslave.h" +#include "305/CO_LSSmaster.h" +#include "309/CO_gateway_ascii.h" +#include "extra/CO_trace.h" + #ifdef __cplusplus extern "C" { #endif /** - * @defgroup CO_CANopen CANopen stack + * @defgroup CO_CANopen CANopen * @{ * - * CANopenNode is free and open source CANopen Stack. + * CANopenNode is free and open source CANopen communication protocol stack. * - * CANopen is the internationally standardized (EN 50325-4) (CiA DS-301) - * CAN-based higher-layer protocol for embedded control system. For more - * information on CANopen see http://www.can-cia.org/ + * CANopen is the internationally standardized (EN 50325-4) (CiA DS-301) CAN-based higher-layer protocol for embedded + * control system. For more information on CANopen see http://www.can-cia.org/ * * CANopenNode homepage is https://github.com/CANopenNode/CANopenNode * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * CANopen.h file combines all CANopenNode source files. @ref CO_STACK_CONFIG is first defined in "CO_config.h" file. + * Number of different CANopenNode objects used is configured with @ref CO_config_t structure or is read directly from + * "OD.h" file, if single object dictionary definition is used. "OD.h" and "OD.c" files defines CANopen Object + * Dictionary and are generated by external tool. * - * http://www.apache.org/licenses/LICENSE-2.0 + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * @} */ +/** + * @defgroup CO_CANopen_301 CANopen_301 + * @{ + * + * CANopen application layer and communication profile (CiA 301 v4.2.0) + * + * Definitions of data types, encoding rules, object dictionary objects and CANopen communication services and + * protocols. + * @} + */ - #include "CO_driver.h" - #include "CO_OD.h" - #include "CO_SDO.h" - #include "CO_Emergency.h" - #include "CO_NMT_Heartbeat.h" - #include "CO_SYNC.h" - #include "CO_TIME.h" - #include "CO_PDO.h" - #include "CO_HBconsumer.h" -#if CO_NO_SDO_CLIENT != 0 - #include "CO_SDOmaster.h" -#endif -#if CO_NO_TRACE > 0 - #include "CO_trace.h" -#endif -#if CO_NO_LSS_SERVER == 1 - #include "CO_LSSslave.h" +/** + * @defgroup CO_CANopen_303 CANopen_303 + * @{ + * + * CANopen recommendation for indicator specification (CiA 303-3 v1.4.0) + * + * Description of communication related indicators - green and red LED diodes. + * @} + */ + +/** + * @defgroup CO_CANopen_304 CANopen_304 + * @{ + * + * CANopen Safety (EN 50325­-5:2010 (formerly CiA 304)) + * + * Standard defines the usage of Safety Related Data Objects (SRDO) and the GFC. This is an additional protocol (to SDO, + * PDO) to exchange data. The meaning of "security" here refers not to security (crypto) but to data consistency. + * @} + */ + +/** + * @defgroup CO_CANopen_305 CANopen_305 + * @{ + * + * CANopen layer setting services (LSS) and protocols (CiA 305 DSP v3.0.0) + * + * Inquire or change three parameters on a CANopen device with LSS slave capability by a CANopen device with LSS master + * capability via the CAN network: the settings of Node-ID of the CANopen device, bit timing parameters of the physical + * layer (bit rate) or LSS address compliant to the identity object (1018h). + * @} + */ + +/** + * @defgroup CO_CANopen_309 CANopen_309 + * @{ + * + * CANopen access from other networks (CiA 309) + * + * Standard defines the services and protocols to interface CANopen networks to other networks. Standard is organized as + * follows: + * - Part 1: General principles and services + * - Part 2: Modbus/TCP mapping + * - Part 3: ASCII mapping + * - Part 4: Amendment 7 to Fieldbus Integration into PROFINET IO + * @} + */ + +/** + * @defgroup CO_CANopen_storage CANopen_storage + * @{ + * + * CANopen Object Dictionary and other data storage. + * @} + */ + +/** + * @defgroup CO_CANopen_extra CANopen_extra + * @{ + * + * Additional non-standard objects related to CANopenNode. + * @} + */ + +/** + * @addtogroup CO_CANopen + * @{ + */ + +/** + * If macro is defined externally, then configuration with multiple object dictionaries will be possible. If macro is + * not defined, default "OD.h" file with necessary definitions, such as OD_CNT_xxx, will be used, and also memory + * consumption and startup time will be lower. + */ +#ifdef CO_DOXYGEN +#define CO_MULTIPLE_OD #endif -#if CO_NO_LSS_CLIENT == 1 - #include "CO_LSSmaster.h" + +/** + * If macro is defined externally, then global variables for CANopen objects + * will be used instead of heap. This is possible only if CO_MULTIPLE_OD is not + * defined. + */ +#ifdef CO_DOXYGEN +#define CO_USE_GLOBALS #endif +#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN /** - * Default CANopen identifiers. + * CANopen configuration, used with @ref CO_new() * - * Default CANopen identifiers for CANopen communication objects. Same as - * 11-bit addresses of CAN messages. These are default identifiers and - * can be changed in CANopen. Especially PDO identifiers are confgured - * in PDO linking phase of the CANopen network configuration. + * This structure is used only, if @ref CO_MULTIPLE_OD is enabled. Otherwise parameters are retrieved from default + * "OD.h" file. */ -typedef enum{ - CO_CAN_ID_NMT_SERVICE = 0x000, /**< 0x000, Network management */ - CO_CAN_ID_SYNC = 0x080, /**< 0x080, Synchronous message */ - CO_CAN_ID_EMERGENCY = 0x080, /**< 0x080, Emergency messages (+nodeID) */ - CO_CAN_ID_TIME = 0x100, /**< 0x100, Time message */ - CO_CAN_ID_TPDO_1 = 0x180, /**< 0x180, Default TPDO1 (+nodeID) */ - CO_CAN_ID_RPDO_1 = 0x200, /**< 0x200, Default RPDO1 (+nodeID) */ - CO_CAN_ID_TPDO_2 = 0x280, /**< 0x280, Default TPDO2 (+nodeID) */ - CO_CAN_ID_RPDO_2 = 0x300, /**< 0x300, Default RPDO2 (+nodeID) */ - CO_CAN_ID_TPDO_3 = 0x380, /**< 0x380, Default TPDO3 (+nodeID) */ - CO_CAN_ID_RPDO_3 = 0x400, /**< 0x400, Default RPDO3 (+nodeID) */ - CO_CAN_ID_TPDO_4 = 0x480, /**< 0x480, Default TPDO4 (+nodeID) */ - CO_CAN_ID_RPDO_4 = 0x500, /**< 0x500, Default RPDO5 (+nodeID) */ - CO_CAN_ID_TSDO = 0x580, /**< 0x580, SDO response from server (+nodeID) */ - CO_CAN_ID_RSDO = 0x600, /**< 0x600, SDO request from client (+nodeID) */ - CO_CAN_ID_HEARTBEAT = 0x700, /**< 0x700, Heartbeat message */ - CO_CAN_ID_LSS_CLI = 0x7E4, /**< 0x7E4, LSS response from server to client */ - CO_CAN_ID_LSS_SRV = 0x7E5 /**< 0x7E5, LSS request from client to server */ -}CO_Default_CAN_ID_t; - +typedef struct { + uint8_t CNT_NMT; /**< Number of NMT objects, 0 or 1: NMT slave (CANrx) + Heartbeat producer (CANtx) + + optional NMT master (CANtx), configurable by @ref CO_CONFIG_NMT. Start indexes inside + CANrx and CANtx are always 0. There must be one NMT object in the device. */ + OD_entry_t* ENTRY_H1017; /**< OD entry for @ref CO_NMT_init() */ + uint8_t CNT_HB_CONS; /**< Number of Heartbeat consumer objects, 0 or 1 */ + uint8_t CNT_ARR_1016; /**< Number of internal consumers (CANrx), used inside Heartbeat consumer object, 1 to 127. */ + OD_entry_t* ENTRY_H1016; /**< OD entry for @ref CO_HBconsumer_init() */ + OD_entry_t* ENTRY_H100C; /**< OD entry for @ref CO_nodeGuardingSlave_init() */ + OD_entry_t* ENTRY_H100D; /**< OD entry for @ref CO_nodeGuardingSlave_init() */ + uint8_t CNT_EM; /**< Number of Emergency objects, 0 or 1: optional producer (CANtx) + optional consumer (CANrx), + configurable by @ref CO_CONFIG_EM. There must be one Emergency object in the device. */ + const OD_entry_t* ENTRY_H1001; /**< OD entry for @ref CO_EM_init() */ + OD_entry_t* ENTRY_H1014; /**< OD entry for @ref CO_EM_init() */ + OD_entry_t* ENTRY_H1015; /**< OD entry for @ref CO_EM_init() */ + uint8_t CNT_ARR_1003; /**< Size of the fifo buffer, which is used for intermediate storage of emergency messages. + Fifo is used by emergency producer and by error history (OD object 0x1003). Size is usually + equal to size of array in OD object 0x1003. If later is not used, CNT_ARR_1003 must also be + set to value greater than 0, or emergency producer will not work. */ + OD_entry_t* ENTRY_H1003; /**< OD entry for @ref CO_EM_init() */ + uint8_t CNT_SDO_SRV; /**< Number of SDO server objects, from 0 to 128 (CANrx + CANtx). There must be at least + one SDO server object in the device. */ + OD_entry_t* ENTRY_H1200; /**< OD entry for @ref CO_SDOserver_init() */ + uint8_t CNT_SDO_CLI; /**< Number of SDO client objects, from 0 to 128 (CANrx + CANtx). */ + OD_entry_t* ENTRY_H1280; /**< OD entry for @ref CO_SDOclient_init() */ + uint8_t CNT_TIME; /**< Number of TIME objects, 0 or 1: consumer (CANrx) + optional producer (CANtx), + configurable by @ref CO_CONFIG_TIME. */ + OD_entry_t* ENTRY_H1012; /**< OD entry for @ref CO_TIME_init() */ + uint8_t CNT_SYNC; /**< Number of SYNC objects, 0 or 1: consumer (CANrx) + optional producer (CANtx), + configurable by @ref CO_CONFIG_SYNC. */ + OD_entry_t* ENTRY_H1005; /**< OD entry for @ref CO_SYNC_init() */ + OD_entry_t* ENTRY_H1006; /**< OD entry for @ref CO_SYNC_init() */ + OD_entry_t* ENTRY_H1007; /**< OD entry for @ref CO_SYNC_init() */ + OD_entry_t* ENTRY_H1019; /**< OD entry for @ref CO_SYNC_init() */ + uint16_t CNT_RPDO; /**< Number of RPDO objects, from 0 to 512 consumers (CANrx) */ + OD_entry_t* ENTRY_H1400; /**< OD entry for @ref CO_RPDO_init() */ + OD_entry_t* ENTRY_H1600; /**< OD entry for @ref CO_RPDO_init() */ + uint16_t CNT_TPDO; /**< Number of TPDO objects, from 0 to 512 producers (CANtx) */ + OD_entry_t* ENTRY_H1800; /**< OD entry for @ref CO_TPDO_init() */ + OD_entry_t* ENTRY_H1A00; /**< OD entry for @ref CO_TPDO_init() */ + uint8_t CNT_LEDS; /**< Number of LEDs objects, 0 or 1. */ + uint8_t CNT_GFC; /**< Number of GFC objects, 0 or 1 (CANrx + CANtx). */ + OD_entry_t* ENTRY_H1300; /**< OD entry for @ref CO_GFC_init() */ + uint8_t CNT_SRDO; /**< Number of SRDO objects, from 0 to 64 (2*CANrx + 2*CANtx). */ + OD_entry_t* ENTRY_H1301; /**< OD entry for @ref CO_SRDO_init() */ + OD_entry_t* ENTRY_H1381; /**< OD entry for @ref CO_SRDO_init() */ + OD_entry_t* ENTRY_H13FE; /**< OD entry for @ref CO_SRDO_init() */ + OD_entry_t* ENTRY_H13FF; /**< OD entry for @ref CO_SRDO_init() */ + uint8_t CNT_LSS_SLV; /**< Number of LSSslave objects, 0 or 1 (CANrx + CANtx). */ + uint8_t CNT_LSS_MST; /**< Number of LSSmaster objects, 0 or 1 (CANrx + CANtx). */ + uint8_t CNT_GTWA; /**< Number of gateway ascii objects, 0 or 1. */ + uint16_t CNT_TRACE; /**< Number of trace objects, 0 or more. */ +} CO_config_t; +#else +typedef void CO_config_t; +#endif /* CO_MULTIPLE_OD */ /** - * CANopen stack object combines pointers to all CANopen objects. + * CANopen object - collection of all CANopenNode objects */ -typedef struct{ - CO_CANmodule_t *CANmodule[1]; /**< CAN module objects */ - CO_SDO_t *SDO[CO_NO_SDO_SERVER]; /**< SDO object */ - CO_EM_t *em; /**< Emergency report object */ - CO_EMpr_t *emPr; /**< Emergency process object */ - CO_NMT_t *NMT; /**< NMT object */ - CO_SYNC_t *SYNC; /**< SYNC object */ - CO_TIME_t *TIME; /**< TIME object */ - CO_RPDO_t *RPDO[CO_NO_RPDO];/**< RPDO objects */ - CO_TPDO_t *TPDO[CO_NO_TPDO];/**< TPDO objects */ - CO_HBconsumer_t *HBcons; /**< Heartbeat consumer object*/ -#if CO_NO_LSS_SERVER == 1 - CO_LSSslave_t *LSSslave; /**< LSS server/slave object */ +typedef struct { + bool_t nodeIdUnconfigured; /**< True in un-configured LSS slave */ +#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN + CO_config_t* config; /**< Remember the configuration parameters */ #endif -#if CO_NO_LSS_CLIENT == 1 - CO_LSSmaster_t *LSSmaster; /**< LSS master/client object */ + CO_CANmodule_t* CANmodule; /**< One CAN module object, initialised by @ref CO_CANmodule_init() */ + CO_CANrx_t* CANrx; /**< CAN receive message objects */ + CO_CANtx_t* CANtx; /**< CAN transmit message objects */ +#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN + uint16_t CNT_ALL_RX_MSGS; /**< Number of all CAN receive message objects. */ + uint16_t CNT_ALL_TX_MSGS; /**< Number of all CAN transmit message objects. */ #endif -#if CO_NO_SDO_CLIENT != 0 - CO_SDOclient_t *SDOclient[CO_NO_SDO_CLIENT]; /**< SDO client object */ + CO_NMT_t* NMT; /**< NMT and heartbeat object, initialised by @ref CO_NMT_init() */ +#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN + uint16_t RX_IDX_NMT_SLV; /**< Start index in CANrx. */ + uint16_t TX_IDX_NMT_MST; /**< Start index in CANtx. */ + uint16_t TX_IDX_HB_PROD; /**< Start index in CANtx. */ #endif -#if CO_NO_TRACE > 0 - CO_trace_t *trace[CO_NO_TRACE]; /**< Trace object for monitoring variables */ +#if (((CO_CONFIG_HB_CONS)&CO_CONFIG_HB_CONS_ENABLE) != 0) || defined CO_DOXYGEN + CO_HBconsumer_t* HBcons; /**< Heartbeat consumer object, initialised by @ref CO_HBconsumer_init() */ + CO_HBconsNode_t* HBconsMonitoredNodes; /**< Object for monitored nodes, initialised by @ref CO_HBconsumer_init() */ +#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN + uint16_t RX_IDX_HB_CONS; /**< Start index in CANrx. */ #endif -}CO_t; - - -/** CANopen object */ - extern CO_t *CO; - +#endif +#if (((CO_CONFIG_NODE_GUARDING)&CO_CONFIG_NODE_GUARDING_SLAVE_ENABLE) != 0) || defined CO_DOXYGEN + CO_nodeGuardingSlave_t* NGslave; /**< Node guarding slave object, initialised by @ref CO_nodeGuardingSlave_init() */ +#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN + uint16_t RX_IDX_NG_SLV; /**< Start index in CANrx. */ + uint16_t TX_IDX_NG_SLV; /**< Start index in CANtx. */ +#endif +#endif +#if (((CO_CONFIG_NODE_GUARDING)&CO_CONFIG_NODE_GUARDING_MASTER_ENABLE) != 0) || defined CO_DOXYGEN + CO_nodeGuardingMaster_t* + NGmaster; /**< Node guarding master object, initialised by @ref CO_nodeGuardingMaster_init() */ +#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN + uint16_t RX_IDX_NG_MST; /**< Start index in CANrx. */ + uint16_t TX_IDX_NG_MST; /**< Start index in CANtx. */ +#endif +#endif + CO_EM_t* em; /**< Emergency object, initialised by @ref CO_EM_init() */ +#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN + uint16_t RX_IDX_EM_CONS; /**< Start index in CANrx. */ + uint16_t TX_IDX_EM_PROD; /**< Start index in CANtx. */ +#endif +#if (((CO_CONFIG_EM) & (CO_CONFIG_EM_PRODUCER | CO_CONFIG_EM_HISTORY)) != 0) || defined CO_DOXYGEN + CO_EM_fifo_t* em_fifo; /**< FIFO for emergency object, initialised by @ref CO_EM_init() */ +#endif + CO_SDOserver_t* SDOserver; /**< SDO server objects, initialised by @ref CO_SDOserver_init() */ +#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN + uint16_t RX_IDX_SDO_SRV; /**< Start index in CANrx. */ + uint16_t TX_IDX_SDO_SRV; /**< Start index in CANtx. */ +#endif +#if (((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_ENABLE) != 0) || defined CO_DOXYGEN + CO_SDOclient_t* SDOclient; /**< SDO client objects, initialised by @ref CO_SDOclient_init() */ +#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN + uint16_t RX_IDX_SDO_CLI; /**< Start index in CANrx. */ + uint16_t TX_IDX_SDO_CLI; /**< Start index in CANtx. */ +#endif +#endif +#if (((CO_CONFIG_TIME)&CO_CONFIG_TIME_ENABLE) != 0) || defined CO_DOXYGEN + CO_TIME_t* TIME; /**< TIME object, initialised by @ref CO_TIME_init() */ +#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN + uint16_t RX_IDX_TIME; /**< Start index in CANrx. */ + uint16_t TX_IDX_TIME; /**< Start index in CANtx. */ +#endif +#endif +#if (((CO_CONFIG_SYNC)&CO_CONFIG_SYNC_ENABLE) != 0) || defined CO_DOXYGEN + CO_SYNC_t* SYNC; /**< SYNC object, initialised by @ref CO_SYNC_init() */ +#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN + uint16_t RX_IDX_SYNC; /**< Start index in CANrx. */ + uint16_t TX_IDX_SYNC; /**< Start index in CANtx. */ +#endif +#endif +#if (((CO_CONFIG_PDO)&CO_CONFIG_RPDO_ENABLE) != 0) || defined CO_DOXYGEN + CO_RPDO_t* RPDO; /**< RPDO objects, initialised by @ref CO_RPDO_init() */ +#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN + uint16_t RX_IDX_RPDO; /**< Start index in CANrx. */ +#endif +#endif +#if (((CO_CONFIG_PDO)&CO_CONFIG_TPDO_ENABLE) != 0) || defined CO_DOXYGEN + CO_TPDO_t* TPDO; /**< TPDO objects, initialised by @ref CO_TPDO_init() */ +#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN + uint16_t TX_IDX_TPDO; /**< Start index in CANtx. */ +#endif +#endif +#if (((CO_CONFIG_LEDS)&CO_CONFIG_LEDS_ENABLE) != 0) || defined CO_DOXYGEN + CO_LEDs_t* LEDs; /**< LEDs object, initialised by @ref CO_LEDs_init() */ +#endif +#if (((CO_CONFIG_GFC)&CO_CONFIG_GFC_ENABLE) != 0) || defined CO_DOXYGEN + CO_GFC_t* GFC; /**< GFC object, initialised by @ref CO_GFC_init() */ +#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN + uint16_t RX_IDX_GFC; /**< Start index in CANrx. */ + uint16_t TX_IDX_GFC; /**< Start index in CANtx. */ +#endif +#endif +#if (((CO_CONFIG_SRDO)&CO_CONFIG_SRDO_ENABLE) != 0) || defined CO_DOXYGEN + CO_SRDOGuard_t* SRDOGuard; /**< SRDO guard object, initialised by CO_SRDOGuard_init(), single SRDOGuard object is + included inside all SRDO objects */ + CO_SRDO_t* SRDO; /**< SRDO objects, initialised by @ref CO_SRDO_init() */ +#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN + uint16_t RX_IDX_SRDO; /**< Start index in CANrx. */ + uint16_t TX_IDX_SRDO; /**< Start index in CANtx. */ +#endif +#endif +#if (((CO_CONFIG_LSS)&CO_CONFIG_LSS_SLAVE) != 0) || defined CO_DOXYGEN + CO_LSSslave_t* LSSslave; /**< LSS slave object, initialised by @ref CO_LSSslave_init(). */ +#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN + uint16_t RX_IDX_LSS_SLV; /**< Start index in CANrx. */ + uint16_t TX_IDX_LSS_SLV; /**< Start index in CANtx. */ +#endif +#endif +#if (((CO_CONFIG_LSS)&CO_CONFIG_LSS_MASTER) != 0) || defined CO_DOXYGEN + CO_LSSmaster_t* LSSmaster; /**< LSS master object, initialised by @ref CO_LSSmaster_init(). */ +#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN + uint16_t RX_IDX_LSS_MST; /**< Start index in CANrx. */ + uint16_t TX_IDX_LSS_MST; /**< Start index in CANtx. */ +#endif +#endif +#if (((CO_CONFIG_GTW)&CO_CONFIG_GTW_ASCII) != 0) || defined CO_DOXYGEN + CO_GTWA_t* gtwa; /**< Gateway-ascii object, initialised by @ref CO_GTWA_init(). */ +#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN +#endif +#endif +#if ((CO_CONFIG_TRACE)&CO_CONFIG_TRACE_ENABLE) || defined CO_DOXYGEN + CO_trace_t* trace; /**< Trace object, initialised by @ref CO_trace_init(). */ +#endif +} CO_t; /** - * Function CO_sendNMTcommand() is simple function, which sends CANopen message. - * This part of code is an example of custom definition of simple CANopen - * object. Follow the code in CANopen.c file. If macro CO_NO_NMT_MASTER is 1, - * function CO_sendNMTcommand can be used to send NMT master message. + * Create new CANopen object * - * @param co CANopen object. - * @param command NMT command. - * @param nodeID Node ID. + * If CO_USE_GLOBALS is defined, then function uses global static variables for all the CANopenNode objects. Otherwise + * it allocates all objects from heap. * - * @return 0: Operation completed successfully. - * @return other: same as CO_CANsend(). + * @remark + * With some microcontrollers it is necessary to specify Heap size within linker configuration, if heap is used. + * + * @param config Configuration structure, used if @ref CO_MULTIPLE_OD is defined. It must stay in memory permanently. If + * CO_MULTIPLE_OD is not defined, config should be NULL and parameters are retrieved from default "OD.h" file. + * @param [out] heapMemoryUsed Information about heap memory used. Ignored if NULL. + * + * @return Successfully allocated and configured CO_t object or NULL. */ -#if CO_NO_NMT_MASTER == 1 - CO_ReturnError_t CO_sendNMTcommand(CO_t *co, uint8_t command, uint8_t nodeID); -#endif +CO_t* CO_new(CO_config_t* config, uint32_t* heapMemoryUsed); +/** + * Delete CANopen object and free memory. Must be called at program exit. + * + * @param co CANopen object. + */ +void CO_delete(CO_t* co); -#if CO_NO_LSS_SERVER == 1 /** - * Allocate and initialize memory for CANopen object + * Test if LSS slave is enabled * - * Function must be called in the communication reset section. + * @param co CANopen object. * - * @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_ILLEGAL_ARGUMENT, - * CO_ERROR_OUT_OF_MEMORY + * @return True if enabled */ -CO_ReturnError_t CO_new(void); - +bool_t CO_isLSSslaveEnabled(CO_t* co); /** * Initialize CAN driver * * Function must be called in the communication reset section. * - * @param CANdriverState Pointer to the CAN module, passed to CO_CANmodule_init(). + * @param co CANopen object. + * @param CANptr Pointer to the user-defined CAN base structure, passed to CO_CANmodule_init(). * @param bitRate CAN bit rate. - * @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_ILLEGAL_ARGUMENT, - * CO_ERROR_ILLEGAL_BAUDRATE, CO_ERROR_OUT_OF_MEMORY + * @return CO_ERROR_NO in case of success. */ -CO_ReturnError_t CO_CANinit( - void *CANdriverState, - uint16_t bitRate); - +CO_ReturnError_t CO_CANinit(CO_t* co, void* CANptr, uint16_t bitRate); +#if (((CO_CONFIG_LSS)&CO_CONFIG_LSS_SLAVE) != 0) || defined CO_DOXYGEN /** * Initialize CANopen LSS slave * - * Function must be called in the communication reset section. + * Function must be called before CO_CANopenInit. * - * @param nodeId Node ID of the CANopen device (1 ... 127) or CO_LSS_NODE_ID_ASSIGNMENT - * @param bitRate CAN bit rate. - * @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_ILLEGAL_ARGUMENT + * See @ref CO_LSSslave_init() for description of parameters. + * + * @param co CANopen object. + * @param lssAddress LSS slave address, from OD object 0x1018 + * @param [in,out] pendingNodeID Pending node ID or 0xFF (unconfigured) + * @param [in,out] pendingBitRate Pending bit rate of the CAN interface + * + * @return CO_ERROR_NO in case of success. */ -CO_ReturnError_t CO_LSSinit( - uint8_t nodeId, - uint16_t bitRate); - +CO_ReturnError_t CO_LSSinit(CO_t* co, CO_LSS_address_t* lssAddress, uint8_t* pendingNodeID, uint16_t* pendingBitRate); +#endif /** - * Initialize CANopen stack. + * Initialize CANopenNode except PDO objects. * * Function must be called in the communication reset section. * - * @param nodeId Node ID of the CANopen device (1 ... 127). - * @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_ILLEGAL_ARGUMENT + * @param co CANopen object. + * @param em Emergency object, which is used inside different CANopen objects, usually for error reporting. If NULL, + * then 'co->em' will be used. if NULL and 'co->CNT_EM' is 0, then function returns with error. + * @param NMT If 'co->CNT_NMT' is 0, this object must be specified, If 'co->CNT_NMT' is 1,then it is ignored and can be + * NULL. NMT object is used for retrieving NMT internal state inside CO_process(). + * @param od CANopen Object dictionary + * @param OD_statusBits Argument passed to @ref CO_EM_init(). May be NULL. + * @param NMTcontrol Argument passed to @ref CO_NMT_init(). + * @param firstHBTime_ms Argument passed to @ref CO_NMT_init(). + * @param SDOserverTimeoutTime_ms Argument passed to @ref CO_SDOserver_init(). + * @param SDOclientTimeoutTime_ms Default timeout in milliseconds for SDO client, 500 typically. SDO client is + * configured from CO_GTWA_init(). + * @param SDOclientBlockTransfer If true, block transfer will be set in SDO client by default. SDO client is configured + * from by CO_GTWA_init(). + * @param nodeId CANopen Node ID (1 ... 127) or 0xFF(unconfigured). In the CANopen initialization it is the same as + * pendingBitRate from CO_LSSinit(). If it is unconfigured, then some CANopen objects will not be initialized nor + * processed. + * @param [out] errInfo Additional information in case of error, may be NULL. errInfo can also be set in noncritical + * errors, where function returns CO_ERROR_NO. For example, if OD parameter contains wrong value. + * + * @return CO_ERROR_NO in case of success. */ -CO_ReturnError_t CO_CANopenInit( - uint8_t nodeId); +CO_ReturnError_t CO_CANopenInit(CO_t* co, CO_NMT_t* NMT, CO_EM_t* em, OD_t* od, OD_entry_t* OD_statusBits, + uint16_t NMTcontrol, uint16_t firstHBTime_ms, uint16_t SDOserverTimeoutTime_ms, + uint16_t SDOclientTimeoutTime_ms, bool_t SDOclientBlockTransfer, uint8_t nodeId, + uint32_t* errInfo); - -#else /* CO_NO_LSS_SERVER == 1 */ /** - * Initialize CANopen stack. + * Initialize CANopenNode PDO objects. * - * Function must be called in the communication reset section. + * Function must be called in the end of communication reset section after all CANopen and application initialization, + * otherwise some OD variables wont be mapped into PDO correctly. * - * @param CANdriverState Pointer to the user-defined CAN base structure, passed to CO_CANmodule_init(). - * @param nodeId Node ID of the CANopen device (1 ... 127). - * @param bitRate CAN bit rate. + * @param co CANopen object. + * @param em Emergency object, which is used inside PDO objects for error reporting. + * @param od CANopen Object dictionary + * @param nodeId CANopen Node ID (1 ... 127) or 0xFF(unconfigured). If unconfigured, then PDO will not be initialized + * nor processed. + * @param [out] errInfo Additional information in case of error, may be NULL. * - * @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_ILLEGAL_ARGUMENT, - * CO_ERROR_OUT_OF_MEMORY, CO_ERROR_ILLEGAL_BAUDRATE + * @return CO_ERROR_NO in case of success. */ -CO_ReturnError_t CO_init( - void *CANdriverState, - uint8_t nodeId, - uint16_t bitRate); - -#endif /* CO_NO_LSS_SERVER == 1 */ - +CO_ReturnError_t CO_CANopenInitPDO(CO_t* co, CO_EM_t* em, OD_t* od, uint8_t nodeId, uint32_t* errInfo); /** - * Delete CANopen object and free memory. Must be called at program exit. + * Initialize Safety related Data Objects. + * + * Function must be called in the end of communication reset section after all CANopen and application initialization, + * otherwise some OD variables wont be mapped into SRDO correctly. * - * @param CANdriverState Pointer to the user-defined CAN base structure, passed to CO_CANmodule_init(). + * @param co CANopen object. + * @param em Emergency object, which is used inside PDO objects for error reporting. + * @param od CANopen Object dictionary + * @param nodeId CANopen Node ID (1 ... 127) or 0xFF(unconfigured). If unconfigured, then PDO will not be initialized + * nor processed. + * @param [out] errInfo Additional information in case of error, may be NULL. + * + * @return #CO_ERROR_NO in case of success. */ -void CO_delete(void *CANdriverState); - +#if (((CO_CONFIG_GFC)&CO_CONFIG_GFC_ENABLE) != 0) || (((CO_CONFIG_SRDO)&CO_CONFIG_SRDO_ENABLE) != 0) \ + || defined CO_DOXYGEN +CO_ReturnError_t CO_CANopenInitSRDO(CO_t* co, CO_EM_t* em, OD_t* od, uint8_t nodeId, uint32_t* errInfo); +#endif /** * Process CANopen objects. * - * Function must be called cyclically. It processes all "asynchronous" CANopen - * objects. + * Function must be called cyclically. It processes all "asynchronous" CANopen objects. * * @param co CANopen object. - * @param timeDifference_ms Time difference from previous function call in [milliseconds]. - * @param timerNext_ms Return value - info to OS - maximum delay after function - * should be called next time in [milliseconds]. Value can be used for OS - * sleep time. Initial value must be set to something, 50ms typically. - * Output will be equal or lower to initial value. If there is new object - * to process, delay should be suspended and this function should be - * called immediately. Parameter is ignored if NULL. - * - * @return #CO_NMT_reset_cmd_t from CO_NMT_process(). + * @param enableGateway If true, gateway to external world will be enabled. + * @param timeDifference_us Time difference from previous function call in microseconds. + * @param [out] timerNext_us info to OS - maximum delay time after this function should be called next time in + * [microseconds]. Value can be used for OS sleep time. Initial value must be set to maximum interval time. Output will + * be equal or lower to initial value. Calculation is based on various timers which expire in known time. Parameter + * should be used in combination with callbacks configured with CO_***_initCallbackPre() functions. Those callbacks + * should also trigger calling of CO_process() function. Parameter is ignored if NULL. See also @ref + * CO_CONFIG_FLAG_CALLBACK_PRE configuration macro. + * + * @return Node or communication reset request, from @ref CO_NMT_process(). */ -CO_NMT_reset_cmd_t CO_process( - CO_t *co, - uint16_t timeDifference_ms, - uint16_t *timerNext_ms); - +CO_NMT_reset_cmd_t CO_process(CO_t* co, bool_t enableGateway, uint32_t timeDifference_us, uint32_t* timerNext_us); -#if CO_NO_SYNC == 1 +#if (((CO_CONFIG_SYNC)&CO_CONFIG_SYNC_ENABLE) != 0) || defined CO_DOXYGEN /** * Process CANopen SYNC objects. * - * Function must be called cyclically from real time thread with constant - * interval (1ms typically). It processes SYNC CANopen objects. + * Function must be called cyclically. For time critical applications it may be called from real time thread with + * constant interval (1ms typically). It processes SYNC CANopen objects. * * @param co CANopen object. - * @param timeDifference_us Time difference from previous function call in [microseconds]. + * @param timeDifference_us Time difference from previous function call in microseconds. + * @param [out] timerNext_us info to OS - see CO_process(). * * @return True, if CANopen SYNC message was just received or transmitted. */ -bool_t CO_process_SYNC( - CO_t *co, - uint32_t timeDifference_us); +bool_t CO_process_SYNC(CO_t* co, uint32_t timeDifference_us, uint32_t* timerNext_us); #endif +#if (((CO_CONFIG_PDO)&CO_CONFIG_RPDO_ENABLE) != 0) || defined CO_DOXYGEN /** * Process CANopen RPDO objects. * - * Function must be called cyclically from real time thread with constant. - * interval (1ms typically). It processes receive PDO CANopen objects. + * Function must be called cyclically. For time critical applications it may be called from real time thread with + * constant interval (1ms typically). It processes receive PDO CANopen objects. * * @param co CANopen object. * @param syncWas True, if CANopen SYNC message was just received or transmitted. + * @param timeDifference_us Time difference from previous function call in microseconds. + * @param [out] timerNext_us info to OS - see CO_process(). */ -void CO_process_RPDO( - CO_t *co, - bool_t syncWas); +void CO_process_RPDO(CO_t* co, bool_t syncWas, uint32_t timeDifference_us, uint32_t* timerNext_us); +#endif +#if (((CO_CONFIG_PDO)&CO_CONFIG_TPDO_ENABLE) != 0) || defined CO_DOXYGEN /** * Process CANopen TPDO objects. * - * Function must be called cyclically from real time thread with constant. - * interval (1ms typically). It processes transmit PDO CANopen objects. + * Function must be called cyclically. For time critical applications it may be called from real time thread with + * constant interval (1ms typically). It processes transmit PDO CANopen objects. * * @param co CANopen object. * @param syncWas True, if CANopen SYNC message was just received or transmitted. - * @param timeDifference_us Time difference from previous function call in [microseconds]. + * @param timeDifference_us Time difference from previous function call in microseconds. + * @param [out] timerNext_us info to OS - see CO_process(). */ -void CO_process_TPDO( - CO_t *co, - bool_t syncWas, - uint32_t timeDifference_us); +void CO_process_TPDO(CO_t* co, bool_t syncWas, uint32_t timeDifference_us, uint32_t* timerNext_us); +#endif + +#if (((CO_CONFIG_SRDO)&CO_CONFIG_SRDO_ENABLE) != 0) || defined CO_DOXYGEN +/** + * Process CANopen SRDO objects. + * + * Function must be called cyclically. For time critical applications it may be called from real time thread with + * constant interval (1ms typically). It processes SRDO CANopen objects. + * + * @param co CANopen object. + * @param timeDifference_us Time difference from previous function call in microseconds. + * @param [out] timerNext_us info to OS - see CO_process(). + * + * @return #CO_SRDO_state_t: lowest state of the SRDO objects. + */ +CO_SRDO_state_t CO_process_SRDO(CO_t* co, uint32_t timeDifference_us, uint32_t* timerNext_us); +#endif + +/** @} */ /* CO_CANopen */ #ifdef __cplusplus } -#endif /*__cplusplus*/ +#endif /* __cplusplus */ -/** @} */ -#endif +#endif /* CANopen_H */ diff --git a/CO_driver.h b/CO_driver.h deleted file mode 100644 index 90c88f75..00000000 --- a/CO_driver.h +++ /dev/null @@ -1,281 +0,0 @@ -/** - * CAN driver functions. - * - * Defines functions that must be implemented for each target. - * - * @file CO_driver.h - * @ingroup CO_driver - * @author Janez Paternoster - * @copyright 2004 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef CO_DRIVER_H -#define CO_DRIVER_H - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#include "CO_driver_target.h" -#include "CO_types.h" - -/* Include processor header file */ -#include /* for 'int8_t' to 'uint64_t' */ - -/** - * @defgroup CO_driver Driver - * @ingroup CO_CANopen - * @{ - * - * Microcontroller specific code for CANopenNode. - * - * This file contains type definitions, functions and macros for: - * - Basic data types. - * - Receive and transmit buffers for CANopen messages. - * - Interaction with CAN module on the microcontroller. - * - CAN receive and transmit interrupts. - * - * This file is not only a CAN driver. There are no classic CAN queues for CAN - * messages. This file provides direct connection with other CANopen - * objects. It tries to provide fast responses and tries to avoid unnecessary - * calculations and memory consumptions. - * - * CO_CANmodule_t contains an array of _Received message objects_ (of type - * CO_CANrx_t) and an array of _Transmit message objects_ (of type CO_CANtx_t). - * Each CANopen communication object owns one member in one of the arrays. - * For example Heartbeat producer generates one CANopen transmitting object, - * so it has reserved one member in CO_CANtx_t array. - * SYNC module may produce sync or consume sync, so it has reserved one member - * in CO_CANtx_t and one member in CO_CANrx_t array. - * - * ###Reception of CAN messages. - * Before CAN messages can be received, each member in CO_CANrx_t must be - * initialized. CO_CANrxBufferInit() is called by CANopen module, which - * uses specific member. For example @ref CO_HBconsumer uses multiple members - * in CO_CANrx_t array. (It monitors multiple heartbeat messages from remote - * nodes.) It must call CO_CANrxBufferInit() multiple times. - * - * Main arguments to the CO_CANrxBufferInit() function are CAN identifier - * and a pointer to callback function. Those two arguments (and some others) - * are copied to the member of the CO_CANrx_t array. - * - * Callback function is a function, specified by specific CANopen module - * (for example by @ref CO_HBconsumer). Each CANopen module defines own - * callback function. Callback function will process the received CAN message. - * It will copy the necessary data from CAN message to proper place. It may - * also trigger additional task, which will further process the received message. - * Callback function must be fast and must only make the necessary calculations - * and copying. - * - * Received CAN messages are processed by CAN receive interrupt function. - * After CAN message is received, function first tries to find matching CAN - * identifier from CO_CANrx_t array. If found, then a corresponding callback - * function is called. - * - * Callback function accepts two parameters: - * - object is pointer to object registered by CO_CANrxBufferInit(). - * - msg is pointer to CAN message of type CO_CANrxMsg_t. - * - * Callback function must return #CO_ReturnError_t: CO_ERROR_NO, - * CO_ERROR_RX_OVERFLOW, CO_ERROR_RX_PDO_OVERFLOW, CO_ERROR_RX_MSG_LENGTH or - * CO_ERROR_RX_PDO_LENGTH. - * - * - * ###Transmission of CAN messages. - * Before CAN messages can be transmitted, each member in CO_CANtx_t must be - * initialized. CO_CANtxBufferInit() is called by CANopen module, which - * uses specific member. For example Heartbeat producer must initialize it's - * member in CO_CANtx_t array. - * - * CO_CANtxBufferInit() returns a pointer of type CO_CANtx_t, which contains buffer - * where CAN message data can be written. CAN message is send with calling - * CO_CANsend() function. If at that moment CAN transmit buffer inside - * microcontroller's CAN module is free, message is copied directly to CAN module. - * Otherwise CO_CANsend() function sets _bufferFull_ flag to true. Message will be - * then sent by CAN TX interrupt as soon as CAN module is freed. Until message is - * not copied to CAN module, its contents must not change. There may be multiple - * _bufferFull_ flags in CO_CANtx_t array set to true. In that case messages with - * lower index inside array will be sent first. - */ - -/** - * Request CAN configuration (stopped) mode and *wait* untill it is set. - * - * @param CANdriverState User-provided CAN module structure. - */ -void CO_CANsetConfigurationMode(void *CANdriverState); - - -/** - * Request CAN normal (opearational) mode and *wait* untill it is set. - * - * @param CANmodule This object. - */ -void CO_CANsetNormalMode(CO_CANmodule_t *CANmodule); - - -/** - * Initialize CAN module object. - * - * Function must be called in the communication reset section. CAN module must - * be in Configuration Mode before. - * - * @param CANmodule This object will be initialized. - * @param CANdriverState User-provided CAN module structure.. - * @param rxArray Array for handling received CAN messages - * @param rxSize Size of the above array. Must be equal to number of receiving CAN objects. - * @param txArray Array for handling transmitting CAN messages - * @param txSize Size of the above array. Must be equal to number of transmitting CAN objects. - * @param CANbitRate Valid values are (in kbps): 10, 20, 50, 125, 250, 500, 800, 1000. - * If value is illegal, bitrate defaults to 125. - * - * Return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT. - */ -CO_ReturnError_t CO_CANmodule_init( - CO_CANmodule_t *CANmodule, - void *CANdriverState, - CO_CANrx_t rxArray[], - uint16_t rxSize, - CO_CANtx_t txArray[], - uint16_t txSize, - uint16_t CANbitRate); - - -/** - * Switch off CANmodule. Call at program exit. - * - * @param CANmodule CAN module object. - */ -void CO_CANmodule_disable(CO_CANmodule_t *CANmodule); - - -/** - * Read CAN identifier from received message - * - * @param rxMsg Pointer to received message - * @return 11-bit CAN standard identifier. - */ -uint16_t CO_CANrxMsg_readIdent(const CO_CANrxMsg_t *rxMsg); - - -/** - * Configure CAN message receive buffer. - * - * Function configures specific CAN receive buffer. It sets CAN identifier - * and connects buffer with specific object. Function must be called for each - * member in _rxArray_ from CO_CANmodule_t. - * - * @param CANmodule This object. - * @param index Index of the specific buffer in _rxArray_. - * @param ident 11-bit standard CAN Identifier. - * @param mask 11-bit mask for identifier. Most usually set to 0x7FF. - * Received message (rcvMsg) will be accepted if the following - * condition is true: (((rcvMsgId ^ ident) & mask) == 0). - * @param rtr If true, 'Remote Transmit Request' messages will be accepted. - * @param object CANopen object, to which buffer is connected. It will be used as - * an argument to pFunct. Its type is (void), pFunct will change its - * type back to the correct object type. - * @param pFunct Pointer to function, which will be called, if received CAN - * message matches the identifier. It must be fast function. - * - * Return #CO_ReturnError_t: CO_ERROR_NO CO_ERROR_ILLEGAL_ARGUMENT or - * CO_ERROR_OUT_OF_MEMORY (not enough masks for configuration). - */ -CO_ReturnError_t CO_CANrxBufferInit( - CO_CANmodule_t *CANmodule, - uint16_t index, - uint16_t ident, - uint16_t mask, - bool_t rtr, - void *object, - void (*pFunct)(void *object, const CO_CANrxMsg_t *message)); - - -/** - * Configure CAN message transmit buffer. - * - * Function configures specific CAN transmit buffer. Function must be called for - * each member in _txArray_ from CO_CANmodule_t. - * - * @param CANmodule This object. - * @param index Index of the specific buffer in _txArray_. - * @param ident 11-bit standard CAN Identifier. - * @param rtr If true, 'Remote Transmit Request' messages will be transmitted. - * @param noOfBytes Length of CAN message in bytes (0 to 8 bytes). - * @param syncFlag This flag bit is used for synchronous TPDO messages. If it is set, - * message will not be sent, if curent time is outside synchronous window. - * - * @return Pointer to CAN transmit message buffer. 8 bytes data array inside - * buffer should be written, before CO_CANsend() function is called. - * Zero is returned in case of wrong arguments. - */ -CO_CANtx_t *CO_CANtxBufferInit( - CO_CANmodule_t *CANmodule, - uint16_t index, - uint16_t ident, - bool_t rtr, - uint8_t noOfBytes, - bool_t syncFlag); - - -/** - * Send CAN message. - * - * @param CANmodule This object. - * @param buffer Pointer to transmit buffer, returned by CO_CANtxBufferInit(). - * Data bytes must be written in buffer before function call. - * - * @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_TX_OVERFLOW or - * CO_ERROR_TX_PDO_WINDOW (Synchronous TPDO is outside window). - */ -CO_ReturnError_t CO_CANsend(CO_CANmodule_t *CANmodule, CO_CANtx_t *buffer); - - -/** - * Clear all synchronous TPDOs from CAN module transmit buffers. - * - * CANopen allows synchronous PDO communication only inside time between SYNC - * message and SYNC Window. If time is outside this window, new synchronous PDOs - * must not be sent and all pending sync TPDOs, which may be on CAN TX buffers, - * must be cleared. - * - * This function checks (and aborts transmission if necessary) CAN TX buffers - * when it is called. Function should be called by the stack in the moment, - * when SYNC time was just passed out of synchronous window. - * - * @param CANmodule This object. - */ -void CO_CANclearPendingSyncPDOs(CO_CANmodule_t *CANmodule); - - -/** - * Verify all errors of CAN module. - * - * Function is called directly from CO_EM_process() function. - * - * @param CANmodule This object. - */ -void CO_CANverifyErrors(CO_CANmodule_t *CANmodule); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -/** @} */ -#endif /* CO_DRIVER_H */ diff --git a/CO_types.h b/CO_types.h deleted file mode 100644 index 67a371cb..00000000 --- a/CO_types.h +++ /dev/null @@ -1,86 +0,0 @@ -/** - * CANopenNode types. - * - * Defines common types for CANopenNode. - * - * @file CO_types.h - * @ingroup CO_CANopen - * @author Olivier Desenfans - * @copyright 2004 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef CO_TYPES_H -#define CO_TYPES_H - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/** - * Return values of some CANopen functions. - * - * A function should return 0 on success and a negative value on error. - */ -typedef enum { - /** Operation completed successfully */ - CO_ERROR_NO = 0, - /** Error in function arguments */ - CO_ERROR_ILLEGAL_ARGUMENT = -1, - /** Memory allocation failed */ - CO_ERROR_OUT_OF_MEMORY = -2, - /** Function timeout */ - CO_ERROR_TIMEOUT = -3, - /** Illegal baudrate passed to function CO_CANmodule_init() */ - CO_ERROR_ILLEGAL_BAUDRATE = -4, - /** Previous message was not processed yet */ - CO_ERROR_RX_OVERFLOW = -5, - /** previous PDO was not processed yet */ - CO_ERROR_RX_PDO_OVERFLOW = -6, - /** Wrong receive message length */ - CO_ERROR_RX_MSG_LENGTH = -7, - /** Wrong receive PDO length */ - CO_ERROR_RX_PDO_LENGTH = -8, - /** Previous message is still waiting, buffer full */ - CO_ERROR_TX_OVERFLOW = -9, - /** Synchronous TPDO is outside window */ - CO_ERROR_TX_PDO_WINDOW = -10, - /** Transmit buffer was not confugured properly */ - CO_ERROR_TX_UNCONFIGURED = -11, - /** Error in function function parameters */ - CO_ERROR_PARAMETERS = -12, - /** Stored data are corrupt */ - CO_ERROR_DATA_CORRUPT = -13, - /** CRC does not match */ - CO_ERROR_CRC = -14, - /** Sending rejected because driver is busy. Try again */ - CO_ERROR_TX_BUSY = -15, - /** Command can't be processed in current state */ - CO_ERROR_WRONG_NMT_STATE = -16, - /** Syscall failed */ - CO_ERROR_SYSCALL = -17, - /** Driver not ready */ - CO_ERROR_INVALID_STATE = -18, -} CO_ReturnError_t; - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -/** @} */ -#endif /* CO_TYPES_H */ diff --git a/Doxyfile b/Doxyfile index 996c1b23..07f810f6 100644 --- a/Doxyfile +++ b/Doxyfile @@ -1,4 +1,4 @@ -# Doxyfile 1.8.8 +# Doxyfile 1.11.0 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -12,19 +12,29 @@ # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). +# +# Note: +# +# Use doxygen to compare the used configuration file with the template +# configuration file: +# doxygen -x [configFile] +# Use doxygen to compare the used configuration file with the template +# configuration file without replacing the environment variables or CMake type +# replacement variables: +# doxygen -x_noenv [configFile] #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all text -# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv -# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv -# for the list of possible encodings. +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. # The default value is: UTF-8. -DOXYFILE_ENCODING = UTF-8 +DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the @@ -32,43 +42,61 @@ DOXYFILE_ENCODING = UTF-8 # title of most generated pages and in a few other places. # The default value is: My Project. -PROJECT_NAME = CANopenNode +PROJECT_NAME = CANopenNode # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = +PROJECT_NUMBER = # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. -PROJECT_BRIEF = +PROJECT_BRIEF = "CANopen protocol stack" + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = doc/CANopenNode.png -# With the PROJECT_LOGO tag one can specify an logo or icon that is included in -# the documentation. The maximum height of the logo should not exceed 55 pixels -# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo -# to the output directory. +# With the PROJECT_ICON tag one can specify an icon that is included in the tabs +# when the HTML document is shown. Doxygen will copy the logo to the output +# directory. -PROJECT_LOGO = +PROJECT_ICON = doc/CANopenNode.png # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. -OUTPUT_DIRECTORY = doc +OUTPUT_DIRECTORY = doc -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- -# directories (in 2 levels) under the output directory of each output format and -# will distribute the generated files over these directories. Enabling this +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096 +# sub-directories (in 2 levels) under the output directory of each output format +# and will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes -# performance problems for the file system. +# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to +# control the number of sub-directories. # The default value is: NO. -CREATE_SUBDIRS = NO +CREATE_SUBDIRS = NO + +# Controls the number of sub-directories that will be created when +# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every +# level increment doubles the number of directories, resulting in 4096 +# directories at level 8 which is the default and also the maximum value. The +# sub-directories are organized in 2 levels, the first level always has a fixed +# number of 16 directories. +# Minimum value: 0, maximum value: 8, default value: 8. +# This tag requires that the tag CREATE_SUBDIRS is set to YES. + +CREATE_SUBDIRS_LEVEL = 8 # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII @@ -76,38 +104,38 @@ CREATE_SUBDIRS = NO # U+3044. # The default value is: NO. -ALLOW_UNICODE_NAMES = NO +ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. -# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, -# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), -# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, -# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), -# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, -# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, -# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, -# Ukrainian and Vietnamese. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian, +# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English +# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek, +# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with +# English messages), Korean, Korean-en (Korean with English messages), Latvian, +# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, +# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, +# Swedish, Turkish, Ukrainian and Vietnamese. # The default value is: English. -OUTPUT_LANGUAGE = English +OUTPUT_LANGUAGE = English -# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. -BRIEF_MEMBER_DESC = YES +BRIEF_MEMBER_DESC = YES -# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. # The default value is: YES. -REPEAT_BRIEF = YES +REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator that is # used to form the text in various listings. Each string in this list, if found @@ -118,14 +146,24 @@ REPEAT_BRIEF = YES # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. -ABBREVIATE_BRIEF = +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. -ALWAYS_DETAILED_SEC = NO +ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those @@ -133,14 +171,14 @@ ALWAYS_DETAILED_SEC = NO # operators of the base classes will not be shown. # The default value is: NO. -INLINE_INHERITED_MEMB = NO +INLINE_INHERITED_MEMB = NO -# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. -FULL_PATH_NAMES = YES +FULL_PATH_NAMES = YES # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand @@ -152,7 +190,7 @@ FULL_PATH_NAMES = YES # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. -STRIP_FROM_PATH = +STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which @@ -161,14 +199,14 @@ STRIP_FROM_PATH = # specify the list of include paths that are normally passed to the compiler # using the -I flag. -STRIP_FROM_INC_PATH = +STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. -SHORT_NAMES = NO +SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the # first line (until the first dot) of a Javadoc-style comment as the brief @@ -177,7 +215,17 @@ SHORT_NAMES = NO # description.) # The default value is: NO. -JAVADOC_AUTOBRIEF = YES +JAVADOC_AUTOBRIEF = YES + +# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by doxygen. +# The default value is: NO. + +JAVADOC_BANNER = NO # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If @@ -185,7 +233,7 @@ JAVADOC_AUTOBRIEF = YES # requiring an explicit \brief command for a brief description.) # The default value is: NO. -QT_AUTOBRIEF = NO +QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as @@ -199,42 +247,49 @@ QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO +# By default Python docstrings are displayed as preformatted text and doxygen's +# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the +# doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as doxygen documentation. +# The default value is: YES. + +PYTHON_DOCSTRING = YES + # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. -INHERIT_DOCS = YES +INHERIT_DOCS = YES -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a -# new page for each member. If set to NO, the documentation of a member will be -# part of the file/class/namespace that contains it. +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. # The default value is: NO. -SEPARATE_MEMBER_PAGES = NO +SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen # uses this value to replace tabs by spaces in code fragments. # Minimum value: 1, maximum value: 16, default value: 4. -TAB_SIZE = 4 +TAB_SIZE = 4 # This tag can be used to specify a number of aliases that act as commands in # the documentation. An alias has the form: # name=value # For example adding -# "sideeffect=@par Side Effects:\n" +# "sideeffect=@par Side Effects:^^" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading -# "Side Effects:". You can put \n's in the value part of an alias to insert -# newlines. - -ALIASES = - -# This tag can be used to specify a number of word-keyword mappings (TCL only). -# A mapping has the form "name=value". For example adding "class=itcl::class" -# will allow you to use the command class in the itcl::class meaning. +# "Side Effects:". Note that you cannot put \n's in the value part of an alias +# to insert newlines (in the resulting output). You can put ^^ in the value part +# of an alias to insert a newline as if a physical newline was in the original +# file. When you need a literal { or } or , in the value part of an alias you +# have to escape them by means of a backslash (\), this can lead to conflicts +# with the commands \{ and \} for these it is advised to use the version @{ and +# @} or use a double escape (\\{ and \\}) -TCL_SUBST = +ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For @@ -242,7 +297,7 @@ TCL_SUBST = # members will be omitted, etc. # The default value is: NO. -OPTIMIZE_OUTPUT_FOR_C = YES +OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored @@ -250,80 +305,112 @@ OPTIMIZE_OUTPUT_FOR_C = YES # qualified scopes will look different, etc. # The default value is: NO. -OPTIMIZE_OUTPUT_JAVA = NO +OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources. Doxygen will then generate output that is tailored for Fortran. # The default value is: NO. -OPTIMIZE_FOR_FORTRAN = NO +OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for VHDL. # The default value is: NO. -OPTIMIZE_OUTPUT_VHDL = NO +OPTIMIZE_OUTPUT_VHDL = NO + +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and -# language is one of the parsers supported by doxygen: IDL, Java, Javascript, -# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: -# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: -# Fortran. In the later case the parser tries to guess whether the code is fixed -# or free formatted code, this is the default for Fortran type files), VHDL. For -# instance to make doxygen treat .inc files as Fortran files (default is PHP), -# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, +# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files). For instance to make doxygen treat .inc files +# as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. # -# Note For files without extension you can use no_extension as a placeholder. +# Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise -# the files are not read by doxygen. +# the files are not read by doxygen. When specifying no_extension you should add +# * to the FILE_PATTERNS. +# +# Note see also the list of default file extension mappings. -EXTENSION_MAPPING = +EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable -# documentation. See http://daringfireball.net/projects/markdown/ for details. +# documentation. See https://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. -MARKDOWN_SUPPORT = YES +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 6. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 6 + +# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to +# generate identifiers for the Markdown headings. Note: Every identifier is +# unique. +# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a +# sequence number starting at 0 and GITHUB use the lower case version of title +# with any whitespace replaced by '-' and punctuation characters removed. +# The default value is: DOXYGEN. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +MARKDOWN_ID_STYLE = DOXYGEN # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can -# be prevented in individual cases by by putting a % sign in front of the word -# or globally by setting AUTOLINK_SUPPORT to NO. +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. -AUTOLINK_SUPPORT = YES +AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); -# versus func(std::string) {}). This also make the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. +# versus func(std::string) {}). This also makes the inheritance and +# collaboration diagrams that involve STL classes more complete and accurate. # The default value is: NO. -BUILTIN_STL_SUPPORT = NO +BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. # The default value is: NO. -CPP_CLI_SUPPORT = NO +CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: -# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen -# will parse them like normal C++ but will assume all classes use public instead -# of private inheritance when no explicit protection keyword is present. +# https://www.riverbankcomputing.com/software) sources only. Doxygen will parse +# them like normal C++ but will assume all classes use public instead of private +# inheritance when no explicit protection keyword is present. # The default value is: NO. -SIP_SUPPORT = NO +SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make @@ -333,15 +420,22 @@ SIP_SUPPORT = NO # should set this option to NO. # The default value is: YES. -IDL_PROPERTY_SUPPORT = YES +IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first +# tag is set to YES then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. -DISTRIBUTE_GROUP_DOC = YES +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that @@ -350,7 +444,7 @@ DISTRIBUTE_GROUP_DOC = YES # \nosubgrouping command. # The default value is: YES. -SUBGROUPING = YES +SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions # are shown inside the group in which they are included (e.g. using \ingroup) @@ -371,7 +465,7 @@ INLINE_GROUPED_CLASSES = NO # Man pages) or section (for LaTeX and RTF). # The default value is: NO. -INLINE_SIMPLE_STRUCTS = NO +INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or # enum is documented as struct, union, or enum with the name of the typedef. So @@ -382,7 +476,7 @@ INLINE_SIMPLE_STRUCTS = NO # types are typedef'ed and only the typedef is referenced, never the tag name. # The default value is: NO. -TYPEDEF_HIDES_STRUCT = NO +TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be @@ -395,13 +489,34 @@ TYPEDEF_HIDES_STRUCT = NO # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. -LOOKUP_CACHE_SIZE = 0 +LOOKUP_CACHE_SIZE = 0 + +# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use +# during processing. When set to 0 doxygen will based this on the number of +# cores available in the system. You can set it explicitly to a value larger +# than 0 to get more control over the balance between CPU load and processing +# speed. At this moment only the input processing can be done using multiple +# threads. Since this is still an experimental feature the default is set to 1, +# which effectively disables parallel processing. Please report any issues you +# encounter. Generating dot graphs in parallel is controlled by the +# DOT_NUM_THREADS setting. +# Minimum value: 0, maximum value: 32, default value: 1. + +NUM_PROC_THREADS = 1 + +# If the TIMESTAMP tag is set different from NO then each generated page will +# contain the date or date and time when the page was generated. Setting this to +# NO can help when comparing the output of multiple runs. +# Possible values are: YES, NO, DATETIME and DATE. +# The default value is: NO. + +TIMESTAMP = NO #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. @@ -409,41 +524,47 @@ LOOKUP_CACHE_SIZE = 0 # normally produced when WARNINGS is set to YES. # The default value is: NO. -EXTRACT_ALL = NO +EXTRACT_ALL = NO -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. -EXTRACT_PRIVATE = NO +EXTRACT_PRIVATE = NO -# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. -EXTRACT_PACKAGE = NO +EXTRACT_PACKAGE = NO -# If the EXTRACT_STATIC tag is set to YES all static members of a file will be +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be # included in the documentation. # The default value is: NO. -EXTRACT_STATIC = NO +EXTRACT_STATIC = YES -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined -# locally in source files will be included in the documentation. If set to NO +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. -EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_CLASSES = YES -# This flag is only useful for Objective-C code. When set to YES local methods, +# This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are -# included in the documentation. If set to NO only methods in the interface are +# included in the documentation. If set to NO, only methods in the interface are # included. # The default value is: NO. -EXTRACT_LOCAL_METHODS = NO +EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called @@ -452,7 +573,14 @@ EXTRACT_LOCAL_METHODS = NO # are hidden. # The default value is: NO. -EXTRACT_ANON_NSPACES = NO +EXTRACT_ANON_NSPACES = NO + +# If this flag is set to YES, the name of an unnamed parameter in a declaration +# will be determined by the corresponding definition. By default unnamed +# parameters remain unnamed in the output. +# The default value is: YES. + +RESOLVE_UNNAMED_PARAMS = YES # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these @@ -460,92 +588,114 @@ EXTRACT_ANON_NSPACES = NO # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. -HIDE_UNDOC_MEMBERS = YES +HIDE_UNDOC_MEMBERS = YES # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set -# to NO these classes will be included in the various overviews. This option has -# no effect if EXTRACT_ALL is enabled. +# to NO, these classes will be included in the various overviews. This option +# will also hide undocumented C++ concepts if enabled. This option has no effect +# if EXTRACT_ALL is enabled. # The default value is: NO. -HIDE_UNDOC_CLASSES = YES +HIDE_UNDOC_CLASSES = YES # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend -# (class|struct|union) declarations. If set to NO these declarations will be -# included in the documentation. +# declarations. If set to NO, these declarations will be included in the +# documentation. # The default value is: NO. -HIDE_FRIEND_COMPOUNDS = NO +HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any -# documentation blocks found inside the body of a function. If set to NO these +# documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. -HIDE_IN_BODY_DOCS = NO +HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation that is typed after a # \internal command is included. If the tag is set to NO then the documentation # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. -INTERNAL_DOCS = NO +INTERNAL_DOCS = NO -# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file -# names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. -# The default value is: system dependent. +# With the correct setting of option CASE_SENSE_NAMES doxygen will better be +# able to match the capabilities of the underlying filesystem. In case the +# filesystem is case sensitive (i.e. it supports files in the same directory +# whose names only differ in casing), the option must be set to YES to properly +# deal with such files in case they appear in the input. For filesystems that +# are not case sensitive the option should be set to NO to properly deal with +# output files written for symbols that only differ in casing, such as for two +# classes, one named CLASS and the other named Class, and to also support +# references to files without having to specify the exact matching casing. On +# Windows (including Cygwin) and MacOS, users should typically set this option +# to NO, whereas on Linux or other Unix flavors it should typically be set to +# YES. +# Possible values are: SYSTEM, NO and YES. +# The default value is: SYSTEM. -CASE_SENSE_NAMES = YES +CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with -# their full class and namespace scopes in the documentation. If set to YES the +# their full class and namespace scopes in the documentation. If set to YES, the # scope will be hidden. # The default value is: NO. -HIDE_SCOPE_NAMES = NO +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class +# will show which file needs to be included to use the class. +# The default value is: YES. + +SHOW_HEADERFILE = YES # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. -SHOW_INCLUDE_FILES = YES +SHOW_INCLUDE_FILES = YES # If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each # grouped member an include statement to the documentation, telling the reader # which file to include in order to use the member. # The default value is: NO. -SHOW_GROUPED_MEMB_INC = NO +SHOW_GROUPED_MEMB_INC = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. -FORCE_LOCAL_INCLUDES = NO +FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the # documentation for inline members. # The default value is: YES. -INLINE_INFO = YES +INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member -# name. If set to NO the members will appear in declaration order. +# name. If set to NO, the members will appear in declaration order. # The default value is: YES. -SORT_MEMBER_DOCS = YES +SORT_MEMBER_DOCS = NO # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member -# name. If set to NO the members will appear in declaration order. Note that +# name. If set to NO, the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. -SORT_BRIEF_DOCS = NO +SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the # (brief and detailed) documentation of class members so that constructors and @@ -564,7 +714,7 @@ SORT_MEMBERS_CTORS_1ST = NO # appear in their defined order. # The default value is: NO. -SORT_GROUP_NAMES = NO +SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will @@ -574,7 +724,7 @@ SORT_GROUP_NAMES = NO # list. # The default value is: NO. -SORT_BY_SCOPE_NAME = NO +SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between @@ -584,40 +734,38 @@ SORT_BY_SCOPE_NAME = NO # accept a match between prototype and implementation in such cases. # The default value is: NO. -STRICT_PROTO_MATCHING = NO +STRICT_PROTO_MATCHING = NO -# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the -# todo list. This list is created by putting \todo commands in the -# documentation. +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. # The default value is: YES. -GENERATE_TODOLIST = YES +GENERATE_TODOLIST = YES -# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the -# test list. This list is created by putting \test commands in the -# documentation. +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. # The default value is: YES. -GENERATE_TESTLIST = YES +GENERATE_TESTLIST = YES -# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. -GENERATE_BUGLIST = YES +GENERATE_BUGLIST = YES -# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. -GENERATE_DEPRECATEDLIST = YES +GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional documentation # sections, marked by \if ... \endif and \cond # ... \endcond blocks. -ENABLED_SECTIONS = +ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the @@ -628,28 +776,28 @@ ENABLED_SECTIONS = # documentation regardless of this setting. # Minimum value: 0, maximum value: 10000, default value: 30. -MAX_INITIALIZER_LINES = 30 +MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at -# the bottom of the documentation of classes and structs. If set to YES the list -# will mention the files that were used to generate the documentation. +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. # The default value is: YES. -SHOW_USED_FILES = YES +SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. This # will remove the Files entry from the Quick Index and from the Folder Tree View # (if specified). # The default value is: YES. -SHOW_FILES = YES +SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces # page. This will remove the Namespaces entry from the Quick Index and from the # Folder Tree View (if specified). # The default value is: YES. -SHOW_NAMESPACES = YES +SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from @@ -659,30 +807,31 @@ SHOW_NAMESPACES = YES # by doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. -FILE_VERSION_FILTER = +FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml -# will be used as the name of the layout file. +# will be used as the name of the layout file. See also section "Changing the +# layout of pages" for information. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. -LAYOUT_FILE = +LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool -# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. -CITE_BIB_FILES = +CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages @@ -693,39 +842,72 @@ CITE_BIB_FILES = # messages are off. # The default value is: NO. -QUIET = NO +QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are -# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. # The default value is: YES. -WARNINGS = YES +WARNINGS = YES -# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. -WARN_IF_UNDOCUMENTED = YES +WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some parameters -# in a documented function, or documenting parameters that don't exist or using -# markup commands wrongly. +# potential errors in the documentation, such as documenting some parameters in +# a documented function twice, or documenting parameters that don't exist or +# using markup commands wrongly. # The default value is: YES. -WARN_IF_DOC_ERROR = YES +WARN_IF_DOC_ERROR = YES + +# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete +# function parameter documentation. If set to NO, doxygen will accept that some +# parameters have no documentation without warning. +# The default value is: YES. + +WARN_IF_INCOMPLETE_DOC = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return -# value. If set to NO doxygen will only warn about wrong or incomplete parameter -# documentation, but not about the absence of documentation. +# value. If set to NO, doxygen will only warn about wrong parameter +# documentation, but not about the absence of documentation. If EXTRACT_ALL is +# set to YES then this flag will automatically be disabled. See also +# WARN_IF_INCOMPLETE_DOC +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about +# undocumented enumeration values. If set to NO, doxygen will accept +# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: NO. + +WARN_IF_UNDOC_ENUM_VAL = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the doxygen process doxygen will return with a non-zero status. +# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves +# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not +# write the warning messages in between other messages but write them at the end +# of a run, in case a WARN_LOGFILE is defined the warning messages will be +# besides being in the defined file also be shown at the end of a run, unless +# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case +# the behavior will remain as with the setting FAIL_ON_WARNINGS. +# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT. # The default value is: NO. -WARN_NO_PARAMDOC = NO +WARN_AS_ERROR = NO # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which @@ -733,15 +915,29 @@ WARN_NO_PARAMDOC = NO # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) +# See also: WARN_LINE_FORMAT # The default value is: $file:$line: $text. -WARN_FORMAT = "$file:$line: $text" +WARN_FORMAT = "$file:$line: $text" + +# In the $text part of the WARN_FORMAT command it is possible that a reference +# to a more specific place is given. To make it easier to jump to this place +# (outside of doxygen) the user can define a custom "cut" / "paste" string. +# Example: +# WARN_LINE_FORMAT = "'vi $file +$line'" +# See also: WARN_FORMAT +# The default value is: at line $line of file $file. + +WARN_LINE_FORMAT = "at line $line of file $file" # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard -# error (stderr). +# error (stderr). In case the file specified cannot be opened for writing the +# warning and error messages are written to standard error. When as file - is +# specified the warning and error messages are written to standard output +# (stdout). -WARN_LOGFILE = +WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files @@ -750,36 +946,119 @@ WARN_LOGFILE = # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with -# spaces. +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = . stack stack/drvTemplate +INPUT = README.md \ + MISRA.md \ + doc \ + example/DS301_profile.md \ + CANopen.h \ + 301 \ + 303 \ + 304 \ + 305 \ + 309 \ + storage \ + extra \ + zephyr \ + zephyr/include # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: http://www.gnu.org/software/libiconv) for the list of -# possible encodings. +# documentation (see: +# https://www.gnu.org/software/libiconv/) for the list of possible encodings. +# See also: INPUT_FILE_ENCODING # The default value is: UTF-8. -INPUT_ENCODING = UTF-8 +INPUT_ENCODING = UTF-8 + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify +# character encoding on a per file pattern basis. Doxygen will compare the file +# name with each pattern and apply the encoding instead of the default +# INPUT_ENCODING) if there is a match. The character encodings are a list of the +# form: pattern=encoding (like *.php=ISO-8859-1). +# See also: INPUT_ENCODING for further information on supported encodings. + +INPUT_FILE_ENCODING = # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and -# *.h) to filter out the source-files in the directories. If left blank the -# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, -# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, -# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, -# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, -# *.qsf, *.as and *.js. - -FILE_PATTERNS = +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# Note the list of default checked file patterns might differ from the list of +# default file extension mappings. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm, +# *.cpp, *.cppm, *.ccm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, +# *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d, +# *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to +# be provided as doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cxxm \ + *.cpp \ + *.cppm \ + *.ccm \ + *.c++ \ + *.c++m \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.idl \ + *.ddl \ + *.odl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.ixx \ + *.l \ + *.cs \ + *.d \ + *.php \ + *.php4 \ + *.php5 \ + *.phtml \ + *.inc \ + *.m \ + *.markdown \ + *.md \ + *.mm \ + *.dox \ + *.py \ + *.pyw \ + *.f90 \ + *.f95 \ + *.f03 \ + *.f08 \ + *.f18 \ + *.f \ + *.for \ + *.vhd \ + *.vhdl \ + *.ucf \ + *.qsf \ + *.ice # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. -RECURSIVE = NO +RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a @@ -788,14 +1067,14 @@ RECURSIVE = NO # Note that relative paths are relative to the directory from which doxygen is # run. -EXCLUDE = +EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. # The default value is: NO. -EXCLUDE_SYMLINKS = NO +EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude @@ -804,44 +1083,41 @@ EXCLUDE_SYMLINKS = NO # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* -EXCLUDE_PATTERNS = +EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories use the pattern */test/* +# ANamespace::AClass, ANamespace::*Test -EXCLUDE_SYMBOLS = +EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). -EXAMPLE_PATH = +EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. -EXAMPLE_PATTERNS = +EXAMPLE_PATTERNS = * # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands # irrespective of the value of the RECURSIVE tag. # The default value is: NO. -EXAMPLE_RECURSIVE = NO +EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be included in the documentation (see the # \image command). -IMAGE_PATH = +IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program @@ -857,8 +1133,17 @@ IMAGE_PATH = # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. +# +# Note that doxygen will use the data processed and written to standard output +# for further processing, therefore nothing else, like debug statements or used +# commands (so in case of a Windows batch file always use @echo OFF), should be +# written to standard output. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. -INPUT_FILTER = +INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the @@ -866,15 +1151,19 @@ INPUT_FILTER = # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. -FILTER_PATTERNS = +FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER ) will also be used to filter the input files that are used for +# INPUT_FILTER) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. -FILTER_SOURCE_FILES = NO +FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and @@ -882,14 +1171,23 @@ FILTER_SOURCE_FILES = NO # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. -FILTER_SOURCE_PATTERNS = +FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. -USE_MDFILE_AS_MAINPAGE = +USE_MDFILE_AS_MAINPAGE = ./README.md + +# The Fortran standard specifies that for fixed formatted Fortran code all +# characters from position 72 are to be considered as comment. A common +# extension is to allow longer lines before the automatic comment starts. The +# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can +# be processed before the automatic comment starts. +# Minimum value: 7, maximum value: 10000, default value: 72. + +FORTRAN_COMMENT_AFTER = 72 #--------------------------------------------------------------------------- # Configuration options related to source browsing @@ -902,23 +1200,24 @@ USE_MDFILE_AS_MAINPAGE = # also VERBATIM_HEADERS is set to NO. # The default value is: NO. -SOURCE_BROWSER = NO +SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body of functions, -# classes and enums directly into the documentation. +# multi-line macros, enums or list initialized variables directly into the +# documentation. # The default value is: NO. -INLINE_SOURCES = NO +INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. -STRIP_CODE_COMMENTS = YES +STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented -# function all documented functions referencing it will be listed. +# entity all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO @@ -927,10 +1226,10 @@ REFERENCED_BY_RELATION = NO # all documented entities called/used by that function will be listed. # The default value is: NO. -REFERENCES_RELATION = NO +REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set -# to YES, then the hyperlinks from functions in REFERENCES_RELATION and +# to YES then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. @@ -945,17 +1244,17 @@ REFERENCES_LINK_SOURCE = YES # The default value is: YES. # This tag requires that the tag SOURCE_BROWSER is set to YES. -SOURCE_TOOLTIPS = YES +SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system -# (see http://www.gnu.org/software/global/global.html). You will need version +# (see https://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global -# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # @@ -967,7 +1266,7 @@ SOURCE_TOOLTIPS = YES # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. -USE_HTAGS = NO +USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a # verbatim copy of the header file for each class for which an include is @@ -975,26 +1274,47 @@ USE_HTAGS = NO # See also: Section \class. # The default value is: YES. -VERBATIM_HEADERS = YES +VERBATIM_HEADERS = YES -# If the CLANG_ASSISTED_PARSING tag is set to YES, then doxygen will use the -# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the -# cost of reduced performance. This can be particularly helpful with template -# rich C++ code for which doxygen's built-in parser lacks the necessary type -# information. +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: +# http://clang.llvm.org/) for more accurate parsing at the cost of reduced +# performance. This can be particularly helpful with template rich C++ code for +# which doxygen's built-in parser lacks the necessary type information. # Note: The availability of this option depends on whether or not doxygen was -# compiled with the --with-libclang option. +# generated with the -Duse_libclang=ON option for CMake. # The default value is: NO. CLANG_ASSISTED_PARSING = NO +# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS +# tag is set to YES then doxygen will add the directory of each input to the +# include path. +# The default value is: YES. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_ADD_INC_PATHS = YES + # If clang assisted parsing is enabled you can provide the compiler with command # line options that you would normally use when invoking the compiler. Note that # the include paths will already be set by doxygen for the files and directories # specified with INPUT and INCLUDE_PATH. # This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. -CLANG_OPTIONS = +CLANG_OPTIONS = + +# If clang assisted parsing is enabled you can provide the clang parser with the +# path to the directory containing a file called compile_commands.json. This +# file is the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the +# options used when the source files were built. This is equivalent to +# specifying the -p option to a clang tool, such as clang-check. These options +# will then be passed to the parser. Any options specified with CLANG_OPTIONS +# will be added as well. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. + +CLANG_DATABASE_PATH = #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index @@ -1005,31 +1325,25 @@ CLANG_OPTIONS = # classes, structs, unions or interfaces. # The default value is: YES. -ALPHABETICAL_INDEX = YES - -# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in -# which the alphabetical index list will be split. -# Minimum value: 1, maximum value: 20, default value: 5. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -COLS_IN_ALPHA_INDEX = 5 +ALPHABETICAL_INDEX = YES -# In case all classes in a project start with a common prefix, all classes will -# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag -# can be used to specify a prefix (or a list of prefixes) that should be ignored -# while generating the index headers. +# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes) +# that should be ignored while generating the index headers. The IGNORE_PREFIX +# tag works for classes, function and member names. The entity will be placed in +# the alphabetical list under the first letter of the entity name that remains +# after removing the prefix. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. -IGNORE_PREFIX = +IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- -# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output # The default value is: YES. -GENERATE_HTML = YES +GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of @@ -1037,14 +1351,14 @@ GENERATE_HTML = YES # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_OUTPUT = html +HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). # The default value is: .html. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_FILE_EXTENSION = .html +HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for # each generated HTML page. If the tag is left blank doxygen will generate a @@ -1064,7 +1378,7 @@ HTML_FILE_EXTENSION = .html # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_HEADER = +HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard @@ -1074,7 +1388,7 @@ HTML_HEADER = # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_FOOTER = +HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of @@ -1086,20 +1400,25 @@ HTML_FOOTER = # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_STYLESHEET = +HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the -# standard style sheet and is therefor more robust against future updates. +# standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet files to the output directory. -# Note: The order of the extra stylesheet files is of importance (e.g. the last -# stylesheet in the list overrules the setting of the previous ones in the -# list). For an example see the documentation. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). +# Note: Since the styling of scrollbars can currently not be overruled in +# Webkit/Chromium, the styling will be left out of the default doxygen.css if +# one or more extra stylesheets have been specified. So if scrollbar +# customization is desired it has to be added explicitly. For an example see the +# documentation. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_EXTRA_STYLESHEET = +HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note @@ -1109,26 +1428,39 @@ HTML_EXTRA_STYLESHEET = # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_EXTRA_FILES = +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output +# should be rendered with a dark or light theme. +# Possible values are: LIGHT always generates light mode output, DARK always +# generates dark mode output, AUTO_LIGHT automatically sets the mode according +# to the user preference, uses light mode if no preference is set (the default), +# AUTO_DARK automatically sets the mode according to the user preference, uses +# dark mode if no preference is set and TOGGLE allows a user to switch between +# light and dark mode via a button. +# The default value is: AUTO_LIGHT. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE = AUTO_LIGHT # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen -# will adjust the colors in the stylesheet and background images according to -# this color. Hue is specified as an angle on a colorwheel, see -# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a color-wheel, see +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_COLORSTYLE_HUE = 220 +HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors -# in the HTML output. For a value of 0 the output will use grayscales only. A +# in the HTML output. For a value of 0 the output will use gray-scales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_COLORSTYLE_SAT = 100 +HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 @@ -1139,15 +1471,18 @@ HTML_COLORSTYLE_SAT = 100 # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_COLORSTYLE_GAMMA = 80 +HTML_COLORSTYLE_GAMMA = 80 -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting this -# to NO can help when comparing the output of multiple runs. +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via JavaScript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have JavaScript, +# like the Qt help browser. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_TIMESTAMP = YES +HTML_DYNAMIC_MENUS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the @@ -1155,7 +1490,34 @@ HTML_TIMESTAMP = YES # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_DYNAMIC_SECTIONS = NO +HTML_DYNAMIC_SECTIONS = YES + +# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be +# dynamically folded and expanded in the generated HTML source code. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_CODE_FOLDING = YES + +# If the HTML_COPY_CLIPBOARD tag is set to YES then doxygen will show an icon in +# the top right corner of code and text fragments that allows the user to copy +# its content to the clipboard. Note this only works if supported by the browser +# and the web page is served via a secure context (see: +# https://www.w3.org/TR/secure-contexts/), i.e. using the https: or file: +# protocol. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COPY_CLIPBOARD = YES + +# Doxygen stores a couple of settings persistently in the browser (via e.g. +# cookies). By default these settings apply to all HTML pages generated by +# doxygen across all projects. The HTML_PROJECT_COOKIE tag can be used to store +# the settings under a project specific key, such that the user preferences will +# be stored separately. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_PROJECT_COOKIE = # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand @@ -1168,21 +1530,22 @@ HTML_DYNAMIC_SECTIONS = NO # Minimum value: 0, maximum value: 9999, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_INDEX_NUM_ENTRIES = 100 +HTML_INDEX_NUM_ENTRIES = 35 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: http://developer.apple.com/tools/xcode/), introduced with -# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a -# Makefile in the HTML output directory. Running make will produce the docset in -# that directory and running make install will install the docset in +# environment (see: +# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To +# create a documentation set, doxygen will generate a Makefile in the HTML +# output directory. Running make will produce the docset in that directory and +# running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at -# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html -# for more information. +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. -GENERATE_DOCSET = NO +GENERATE_DOCSET = NO # This tag determines the name of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider @@ -1190,7 +1553,14 @@ GENERATE_DOCSET = NO # The default value is: Doxygen generated docs. # This tag requires that the tag GENERATE_DOCSET is set to YES. -DOCSET_FEEDNAME = "Doxygen generated docs" +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag determines the URL of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDURL = # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. @@ -1198,7 +1568,7 @@ DOCSET_FEEDNAME = "Doxygen generated docs" # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_DOCSET is set to YES. -DOCSET_BUNDLE_ID = org.doxygen.Project +DOCSET_BUNDLE_ID = org.doxygen.Project # The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style @@ -1206,19 +1576,23 @@ DOCSET_BUNDLE_ID = org.doxygen.Project # The default value is: org.doxygen.Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. -DOCSET_PUBLISHER_ID = org.doxygen.Publisher +DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. # The default value is: Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. -DOCSET_PUBLISHER_NAME = Publisher +DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on -# Windows. +# on Windows. In the beginning of 2021 Microsoft took the original page, with +# a.o. the download links, offline the HTML help workshop was already many years +# in maintenance mode). You can download the HTML help workshop from the web +# archives at Installation executable (see: +# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo +# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML @@ -1230,50 +1604,60 @@ DOCSET_PUBLISHER_NAME = Publisher # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. -GENERATE_HTMLHELP = NO +GENERATE_HTMLHELP = NO # The CHM_FILE tag can be used to specify the file name of the resulting .chm # file. You can add a path in front of the file if the result should not be # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. -CHM_FILE = +CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path -# including file name) of the HTML help compiler ( hhc.exe). If non-empty +# including file name) of the HTML help compiler (hhc.exe). If non-empty, # doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. -HHC_LOCATION = +HHC_LOCATION = -# The GENERATE_CHI flag controls if a separate .chi index file is generated ( -# YES) or that it should be included in the master .chm file ( NO). +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the main .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. -GENERATE_CHI = NO +GENERATE_CHI = NO -# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. -CHM_INDEX_ENCODING = +CHM_INDEX_ENCODING = -# The BINARY_TOC flag controls whether a binary table of contents is generated ( -# YES) or a normal table of contents ( NO) in the .chm file. Furthermore it +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it # enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. -BINARY_TOC = NO +BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members to # the table of contents of the HTML help documentation and to the tree view. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. -TOC_EXPAND = NO +TOC_EXPAND = NO + +# The SITEMAP_URL tag is used to specify the full URL of the place where the +# generated documentation will be placed on the server by the user during the +# deployment of the documentation. The generated sitemap is called sitemap.xml +# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL +# is specified no sitemap is generated. For information about the sitemap +# protocol see https://www.sitemaps.org +# This tag requires that the tag GENERATE_HTML is set to YES. + +SITEMAP_URL = # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that @@ -1282,61 +1666,62 @@ TOC_EXPAND = NO # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. -GENERATE_QHP = NO +GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify # the file name of the resulting .qch file. The path specified is relative to # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. -QCH_FILE = +QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace -# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. -QHP_NAMESPACE = org.doxygen.Project +QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- -# folders). +# Folders (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. -QHP_VIRTUAL_FOLDER = doc +QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- -# filters). +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. -QHP_CUST_FILTER_NAME = +QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- -# filters). +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. -QHP_CUST_FILTER_ATTRS = +QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: -# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. -QHP_SECT_FILTER_ATTRS = +QHP_SECT_FILTER_ATTRS = -# The QHG_LOCATION tag can be used to specify the location of Qt's -# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the -# generated .qhp file. +# The QHG_LOCATION tag can be used to specify the location (absolute path +# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to +# run qhelpgenerator on the generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. -QHG_LOCATION = +QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To @@ -1348,7 +1733,7 @@ QHG_LOCATION = # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. -GENERATE_ECLIPSEHELP = NO +GENERATE_ECLIPSEHELP = NO # A unique identifier for the Eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have this @@ -1356,7 +1741,7 @@ GENERATE_ECLIPSEHELP = NO # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. -ECLIPSE_DOC_ID = org.doxygen.Project +ECLIPSE_DOC_ID = org.doxygen.Project # If you want full control over the layout of the generated HTML pages it might # be necessary to disable the index and replace it with your own. The @@ -1367,7 +1752,7 @@ ECLIPSE_DOC_ID = org.doxygen.Project # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. -DISABLE_INDEX = NO +DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. If the tag @@ -1375,16 +1760,28 @@ DISABLE_INDEX = NO # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the -# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can -# further fine-tune the look of the index. As an example, the default style -# sheet generated by doxygen has an example that shows how to put an image at -# the root of the tree instead of the PROJECT_NAME. Since the tree basically has -# the same information as the tab index, you could consider setting -# DISABLE_INDEX to YES when enabling this option. +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine tune the look of the index (see "Fine-tuning the output"). As an +# example, the default style sheet generated by doxygen has an example that +# shows how to put an image at the root of the tree instead of the PROJECT_NAME. +# Since the tree basically has the same information as the tab index, you could +# consider setting DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. -GENERATE_TREEVIEW = YES +GENERATE_TREEVIEW = NO + +# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the +# FULL_SIDEBAR option determines if the side bar is limited to only the treeview +# area (value NO) or if it should extend to the full height of the window (value +# YES). Setting this to YES gives a layout similar to +# https://docs.readthedocs.io with more room for contents, but less room for the +# project logo, title, and description. If either GENERATE_TREEVIEW or +# DISABLE_INDEX is set to NO, this option has no effect. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FULL_SIDEBAR = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. @@ -1394,21 +1791,39 @@ GENERATE_TREEVIEW = YES # Minimum value: 0, maximum value: 20, default value: 4. # This tag requires that the tag GENERATE_HTML is set to YES. -ENUM_VALUES_PER_LINE = 4 +ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. # This tag requires that the tag GENERATE_HTML is set to YES. -TREEVIEW_WIDTH = 250 +TREEVIEW_WIDTH = 250 -# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. -EXT_LINKS_IN_WINDOW = NO +EXT_LINKS_IN_WINDOW = NO + +# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email +# addresses. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +OBFUSCATE_EMAILS = YES + +# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg +# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see +# https://inkscape.org) to generate formulas as SVG images instead of PNGs for +# the HTML output. These images will generally look nicer at scaled resolutions. +# Possible values are: png (the default) and svg (looks nicer but requires the +# pdf2svg or inkscape tool). +# The default value is: png. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FORMULA_FORMAT = png # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful @@ -1417,39 +1832,52 @@ EXT_LINKS_IN_WINDOW = NO # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. -FORMULA_FONTSIZE = 10 +FORMULA_FONTSIZE = 10 -# Use the FORMULA_TRANPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are not -# supported properly for IE 6.0, but are supported on all modern browsers. -# -# Note that when changing this option you need to delete any form_*.png files in -# the HTML output directory before the changes have effect. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. -FORMULA_TRANSPARENT = YES +FORMULA_MACROFILE = # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see -# http://www.mathjax.org) which uses client side Javascript for the rendering -# instead of using prerendered bitmaps. Use this if you do not have LaTeX +# https://www.mathjax.org) which uses client side JavaScript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. -USE_MATHJAX = NO +USE_MATHJAX = NO + +# With MATHJAX_VERSION it is possible to specify the MathJax version to be used. +# Note that the different versions of MathJax have different requirements with +# regards to the different settings, so it is possible that also other MathJax +# settings have to be changed when switching between the different MathJax +# versions. +# Possible values are: MathJax_2 and MathJax_3. +# The default value is: MathJax_2. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_VERSION = MathJax_2 # When MathJax is enabled you can set the default output format to be used for -# the MathJax output. See the MathJax site (see: -# http://docs.mathjax.org/en/latest/output.html) for more details. +# the MathJax output. For more details about the output format see MathJax +# version 2 (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 +# (see: +# http://docs.mathjax.org/en/latest/web/components/output.html). # Possible values are: HTML-CSS (which is slower, but has the best -# compatibility), NativeMML (i.e. MathML) and SVG. +# compatibility. This is the name for Mathjax version 2, for MathJax version 3 +# this will be translated into chtml), NativeMML (i.e. MathML. Only supported +# for MathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This +# is the name for Mathjax version 3, for MathJax version 2 this will be +# translated into HTML-CSS) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. -MATHJAX_FORMAT = HTML-CSS +MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the HTML # output directory using the MATHJAX_RELPATH option. The destination directory @@ -1458,26 +1886,33 @@ MATHJAX_FORMAT = HTML-CSS # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of -# MathJax from http://www.mathjax.org before deployment. -# The default value is: http://cdn.mathjax.org/mathjax/latest. +# MathJax from https://www.mathjax.org before deployment. The default value is: +# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 +# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 # This tag requires that the tag USE_MATHJAX is set to YES. -MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest +MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@2 # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example +# for MathJax version 2 (see +# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# For example for MathJax version 3 (see +# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): +# MATHJAX_EXTENSIONS = ams # This tag requires that the tag USE_MATHJAX is set to YES. -MATHJAX_EXTENSIONS = +MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site -# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. -MATHJAX_CODEFILE = +MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box for # the HTML output. The underlying search engine uses javascript and DHTML and @@ -1498,10 +1933,10 @@ MATHJAX_CODEFILE = # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. -SEARCHENGINE = YES +SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be -# implemented using a web server instead of a web client using Javascript. There +# implemented using a web server instead of a web client using JavaScript. There # are two flavors of web server based searching depending on the EXTERNAL_SEARCH # setting. When disabled, doxygen will generate a PHP script for searching and # an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing @@ -1510,7 +1945,7 @@ SEARCHENGINE = YES # The default value is: NO. # This tag requires that the tag SEARCHENGINE is set to YES. -SERVER_BASED_SEARCH = NO +SERVER_BASED_SEARCH = NO # When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP # script for searching. Instead the search results are written to an XML file @@ -1518,26 +1953,28 @@ SERVER_BASED_SEARCH = NO # external search engine pointed to by the SEARCHENGINE_URL option to obtain the # search results. # -# Doxygen ships with an example indexer ( doxyindexer) and search engine +# Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: http://xapian.org/). +# Xapian (see: +# https://xapian.org/). # # See the section "External Indexing and Searching" for details. # The default value is: NO. # This tag requires that the tag SEARCHENGINE is set to YES. -EXTERNAL_SEARCH = NO +EXTERNAL_SEARCH = NO # The SEARCHENGINE_URL should point to a search engine hosted by a web server # which will return the search results when EXTERNAL_SEARCH is enabled. # -# Doxygen ships with an example indexer ( doxyindexer) and search engine +# Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: http://xapian.org/). See the section "External Indexing and -# Searching" for details. +# Xapian (see: +# https://xapian.org/). See the section "External Indexing and Searching" for +# details. # This tag requires that the tag SEARCHENGINE is set to YES. -SEARCHENGINE_URL = +SEARCHENGINE_URL = # When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed # search data is written to a file for indexing by an external tool. With the @@ -1545,7 +1982,7 @@ SEARCHENGINE_URL = # The default file is: searchdata.xml. # This tag requires that the tag SEARCHENGINE is set to YES. -SEARCHDATA_FILE = searchdata.xml +SEARCHDATA_FILE = searchdata.xml # When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the # EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is @@ -1553,7 +1990,7 @@ SEARCHDATA_FILE = searchdata.xml # projects and redirect the results back to the right project. # This tag requires that the tag SEARCHENGINE is set to YES. -EXTERNAL_SEARCH_ID = +EXTERNAL_SEARCH_ID = # The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen # projects other than the one defined by this configuration file, but that are @@ -1563,16 +2000,16 @@ EXTERNAL_SEARCH_ID = # EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ... # This tag requires that the tag SEARCHENGINE is set to YES. -EXTRA_SEARCH_MAPPINGS = +EXTRA_SEARCH_MAPPINGS = #--------------------------------------------------------------------------- # Configuration options related to the LaTeX output #--------------------------------------------------------------------------- -# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output. +# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. # The default value is: YES. -GENERATE_LATEX = NO +GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of @@ -1580,33 +2017,47 @@ GENERATE_LATEX = NO # The default directory is: latex. # This tag requires that the tag GENERATE_LATEX is set to YES. -LATEX_OUTPUT = latex +LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. # -# Note that when enabling USE_PDFLATEX this option is only used for generating -# bitmaps for formulas in the HTML output, but not in the Makefile that is -# written to the output directory. -# The default file is: latex. +# Note that when not enabling USE_PDFLATEX the default is latex when enabling +# USE_PDFLATEX the default is pdflatex and when in the later case latex is +# chosen this is overwritten by pdflatex. For specific output languages the +# default can have been set differently, this depends on the implementation of +# the output language. # This tag requires that the tag GENERATE_LATEX is set to YES. -LATEX_CMD_NAME = latex +LATEX_CMD_NAME = # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate # index for LaTeX. +# Note: This tag is used in the Makefile / make.bat. +# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file +# (.tex). # The default file is: makeindex. # This tag requires that the tag GENERATE_LATEX is set to YES. -MAKEINDEX_CMD_NAME = makeindex +MAKEINDEX_CMD_NAME = makeindex + +# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to +# generate index for LaTeX. In case there is no backslash (\) as first character +# it will be automatically added in the LaTeX code. +# Note: This tag is used in the generated output file (.tex). +# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat. +# The default value is: makeindex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_MAKEINDEX_CMD = makeindex -# If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX +# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX # documents. This may be useful for small projects and may help to save some # trees in general. # The default value is: NO. # This tag requires that the tag GENERATE_LATEX is set to YES. -COMPACT_LATEX = NO +COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used by the # printer. @@ -1615,43 +2066,59 @@ COMPACT_LATEX = NO # The default value is: a4. # This tag requires that the tag GENERATE_LATEX is set to YES. -PAPER_TYPE = a4 +PAPER_TYPE = a4 # The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names -# that should be included in the LaTeX output. To get the times font for -# instance you can specify -# EXTRA_PACKAGES=times +# that should be included in the LaTeX output. The package can be specified just +# by its name or with the correct syntax as to be used with the LaTeX +# \usepackage command. To get the times font for instance you can specify : +# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times} +# To use the option intlimits with the amsmath package you can specify: +# EXTRA_PACKAGES=[intlimits]{amsmath} # If left blank no extra packages will be included. # This tag requires that the tag GENERATE_LATEX is set to YES. -EXTRA_PACKAGES = +EXTRA_PACKAGES = -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the -# generated LaTeX document. The header should contain everything until the first -# chapter. If it is left blank doxygen will generate a standard header. See -# section "Doxygen usage" for information on how to let doxygen write the -# default header to a separate file. +# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for +# the generated LaTeX document. The header should contain everything until the +# first chapter. If it is left blank doxygen will generate a standard header. It +# is highly recommended to start with a default header using +# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty +# and then modify the file new_header.tex. See also section "Doxygen usage" for +# information on how to generate the default header that doxygen normally uses. # -# Note: Only use a user-defined header if you know what you are doing! The -# following commands have a special meaning inside the header: $title, -# $datetime, $date, $doxygenversion, $projectname, $projectnumber, -# $projectbrief, $projectlogo. Doxygen will replace $title with the empy string, -# for the replacement values of the other commands the user is refered to -# HTML_HEADER. +# Note: Only use a user-defined header if you know what you are doing! +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. The following +# commands have a special meaning inside the header (and footer): For a +# description of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_LATEX is set to YES. -LATEX_HEADER = +LATEX_HEADER = -# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the -# generated LaTeX document. The footer should contain everything after the last -# chapter. If it is left blank doxygen will generate a standard footer. See +# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for +# the generated LaTeX document. The footer should contain everything after the +# last chapter. If it is left blank doxygen will generate a standard footer. See # LATEX_HEADER for more information on how to generate a default footer and what -# special commands can be used inside the footer. -# -# Note: Only use a user-defined footer if you know what you are doing! +# special commands can be used inside the footer. See also section "Doxygen +# usage" for information on how to generate the default footer that doxygen +# normally uses. Note: Only use a user-defined footer if you know what you are +# doing! # This tag requires that the tag GENERATE_LATEX is set to YES. -LATEX_FOOTER = +LATEX_FOOTER = + +# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# LaTeX style sheets that are included after the standard style sheets created +# by doxygen. Using this option one can overrule certain style aspects. Doxygen +# will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EXTRA_STYLESHEET = # The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the LATEX_OUTPUT output @@ -1659,7 +2126,7 @@ LATEX_FOOTER = # markers available. # This tag requires that the tag GENERATE_LATEX is set to YES. -LATEX_EXTRA_FILES = +LATEX_EXTRA_FILES = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is # prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will @@ -1668,60 +2135,66 @@ LATEX_EXTRA_FILES = # The default value is: YES. # This tag requires that the tag GENERATE_LATEX is set to YES. -PDF_HYPERLINKS = YES +PDF_HYPERLINKS = YES -# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate -# the PDF file directly from the LaTeX files. Set this option to YES to get a -# higher quality PDF documentation. +# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as +# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX +# files. Set this option to YES, to get a higher quality PDF documentation. +# +# See also section LATEX_CMD_NAME for selecting the engine. # The default value is: YES. # This tag requires that the tag GENERATE_LATEX is set to YES. -USE_PDFLATEX = YES +USE_PDFLATEX = YES -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode -# command to the generated LaTeX files. This will instruct LaTeX to keep running -# if errors occur, instead of asking the user for help. This option is also used -# when generating formulas in HTML. +# The LATEX_BATCHMODE tag signals the behavior of LaTeX in case of an error. +# Possible values are: NO same as ERROR_STOP, YES same as BATCH, BATCH In batch +# mode nothing is printed on the terminal, errors are scrolled as if is +# hit at every error; missing files that TeX tries to input or request from +# keyboard input (\read on a not open input stream) cause the job to abort, +# NON_STOP In nonstop mode the diagnostic message will appear on the terminal, +# but there is no possibility of user interaction just like in batch mode, +# SCROLL In scroll mode, TeX will stop only for missing files to input or if +# keyboard input is necessary and ERROR_STOP In errorstop mode, TeX will stop at +# each error, asking for user intervention. # The default value is: NO. # This tag requires that the tag GENERATE_LATEX is set to YES. -LATEX_BATCHMODE = NO +LATEX_BATCHMODE = NO # If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the # index chapters (such as File Index, Compound Index, etc.) in the output. # The default value is: NO. # This tag requires that the tag GENERATE_LATEX is set to YES. -LATEX_HIDE_INDICES = NO - -# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source -# code with syntax highlighting in the LaTeX output. -# -# Note that which sources are shown also depends on other settings such as -# SOURCE_BROWSER. -# The default value is: NO. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_SOURCE_CODE = NO +LATEX_HIDE_INDICES = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. See -# http://en.wikipedia.org/wiki/BibTeX and \cite for more info. +# https://en.wikipedia.org/wiki/BibTeX and \cite for more info. # The default value is: plain. # This tag requires that the tag GENERATE_LATEX is set to YES. -LATEX_BIB_STYLE = plain +LATEX_BIB_STYLE = plain + +# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute) +# path from which the emoji images will be read. If a relative path is entered, +# it will be relative to the LATEX_OUTPUT directory. If left blank the +# LATEX_OUTPUT directory will be used. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EMOJI_DIRECTORY = #--------------------------------------------------------------------------- # Configuration options related to the RTF output #--------------------------------------------------------------------------- -# If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The +# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The # RTF output is optimized for Word 97 and may not look too pretty with other RTF # readers/editors. # The default value is: NO. -GENERATE_RTF = NO +GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of @@ -1729,15 +2202,15 @@ GENERATE_RTF = NO # The default directory is: rtf. # This tag requires that the tag GENERATE_RTF is set to YES. -RTF_OUTPUT = rtf +RTF_OUTPUT = rtf -# If the COMPACT_RTF tag is set to YES doxygen generates more compact RTF +# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF # documents. This may be useful for small projects and may help to save some # trees in general. # The default value is: NO. # This tag requires that the tag GENERATE_RTF is set to YES. -COMPACT_RTF = NO +COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will # contain hyperlink fields. The RTF file will contain links (just like the HTML @@ -1749,34 +2222,42 @@ COMPACT_RTF = NO # The default value is: NO. # This tag requires that the tag GENERATE_RTF is set to YES. -RTF_HYPERLINKS = NO +RTF_HYPERLINKS = NO -# Load stylesheet definitions from file. Syntax is similar to doxygen's config -# file, i.e. a series of assignments. You only have to provide replacements, -# missing definitions are set to their default value. +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# configuration file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. # # See also section "Doxygen usage" for information on how to generate the # default style sheet that doxygen normally uses. # This tag requires that the tag GENERATE_RTF is set to YES. -RTF_STYLESHEET_FILE = +RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an RTF document. Syntax is -# similar to doxygen's config file. A template extensions file can be generated -# using doxygen -e rtf extensionFile. +# similar to doxygen's configuration file. A template extensions file can be +# generated using doxygen -e rtf extensionFile. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_EXTENSIONS_FILE = + +# The RTF_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the RTF_OUTPUT output directory. +# Note that the files will be copied as-is; there are no commands or markers +# available. # This tag requires that the tag GENERATE_RTF is set to YES. -RTF_EXTENSIONS_FILE = +RTF_EXTRA_FILES = #--------------------------------------------------------------------------- # Configuration options related to the man page output #--------------------------------------------------------------------------- -# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for +# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for # classes and files. # The default value is: NO. -GENERATE_MAN = NO +GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of @@ -1785,7 +2266,7 @@ GENERATE_MAN = NO # The default directory is: man. # This tag requires that the tag GENERATE_MAN is set to YES. -MAN_OUTPUT = man +MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to the generated # man pages. In case the manual section does not start with a number, the number @@ -1794,14 +2275,14 @@ MAN_OUTPUT = man # The default value is: .3. # This tag requires that the tag GENERATE_MAN is set to YES. -MAN_EXTENSION = .3 +MAN_EXTENSION = .3 # The MAN_SUBDIR tag determines the name of the directory created within # MAN_OUTPUT in which the man pages are placed. If defaults to man followed by # MAN_EXTENSION with the initial . removed. # This tag requires that the tag GENERATE_MAN is set to YES. -MAN_SUBDIR = +MAN_SUBDIR = # If the MAN_LINKS tag is set to YES and doxygen generates man output, then it # will generate one additional man file for each entity documented in the real @@ -1810,17 +2291,17 @@ MAN_SUBDIR = # The default value is: NO. # This tag requires that the tag GENERATE_MAN is set to YES. -MAN_LINKS = NO +MAN_LINKS = NO #--------------------------------------------------------------------------- # Configuration options related to the XML output #--------------------------------------------------------------------------- -# If the GENERATE_XML tag is set to YES doxygen will generate an XML file that +# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that # captures the structure of the code including all documentation. # The default value is: NO. -GENERATE_XML = NO +GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of @@ -1828,26 +2309,33 @@ GENERATE_XML = NO # The default directory is: xml. # This tag requires that the tag GENERATE_XML is set to YES. -XML_OUTPUT = xml +XML_OUTPUT = xml -# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program +# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program # listings (including syntax highlighting and cross-referencing information) to # the XML output. Note that enabling this will significantly increase the size # of the XML output. # The default value is: YES. # This tag requires that the tag GENERATE_XML is set to YES. -XML_PROGRAMLISTING = YES +XML_PROGRAMLISTING = YES + +# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include +# namespace members in file scope as well, matching the HTML output. +# The default value is: NO. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_NS_MEMB_FILE_SCOPE = NO #--------------------------------------------------------------------------- # Configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- -# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files +# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files # that can be used to generate PDF. # The default value is: NO. -GENERATE_DOCBOOK = NO +GENERATE_DOCBOOK = NO # The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be put in @@ -1855,58 +2343,75 @@ GENERATE_DOCBOOK = NO # The default directory is: docbook. # This tag requires that the tag GENERATE_DOCBOOK is set to YES. -DOCBOOK_OUTPUT = docbook +DOCBOOK_OUTPUT = docbook -# If the DOCBOOK_PROGRAMLISTING tag is set to YES doxygen will include the -# program listings (including syntax highlighting and cross-referencing -# information) to the DOCBOOK output. Note that enabling this will significantly -# increase the size of the DOCBOOK output. +#--------------------------------------------------------------------------- +# Configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an +# AutoGen Definitions (see https://autogen.sourceforge.net/) file that captures +# the structure of the code including all documentation. Note that this feature +# is still experimental and incomplete at the moment. # The default value is: NO. -# This tag requires that the tag GENERATE_DOCBOOK is set to YES. -DOCBOOK_PROGRAMLISTING = NO +GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- -# Configuration options for the AutoGen Definitions output +# Configuration options related to Sqlite3 output #--------------------------------------------------------------------------- -# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen -# Definitions (see http://autogen.sf.net) file that captures the structure of -# the code including all documentation. Note that this feature is still -# experimental and incomplete at the moment. +# If the GENERATE_SQLITE3 tag is set to YES doxygen will generate a Sqlite3 +# database with symbols found by doxygen stored in tables. # The default value is: NO. -GENERATE_AUTOGEN_DEF = NO +GENERATE_SQLITE3 = NO + +# The SQLITE3_OUTPUT tag is used to specify where the Sqlite3 database will be +# put. If a relative path is entered the value of OUTPUT_DIRECTORY will be put +# in front of it. +# The default directory is: sqlite3. +# This tag requires that the tag GENERATE_SQLITE3 is set to YES. + +SQLITE3_OUTPUT = sqlite3 + +# The SQLITE3_RECREATE_DB tag is set to YES, the existing doxygen_sqlite3.db +# database file will be recreated with each doxygen run. If set to NO, doxygen +# will warn if a database file is already found and not modify it. +# The default value is: YES. +# This tag requires that the tag GENERATE_SQLITE3 is set to YES. + +SQLITE3_RECREATE_DB = YES #--------------------------------------------------------------------------- # Configuration options related to the Perl module output #--------------------------------------------------------------------------- -# If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module +# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module # file that captures the structure of the code including all documentation. # # Note that this feature is still experimental and incomplete at the moment. # The default value is: NO. -GENERATE_PERLMOD = NO +GENERATE_PERLMOD = NO -# If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary +# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary # Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI # output from the Perl module output. # The default value is: NO. # This tag requires that the tag GENERATE_PERLMOD is set to YES. -PERLMOD_LATEX = NO +PERLMOD_LATEX = NO -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely +# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely # formatted so it can be parsed by a human reader. This is useful if you want to -# understand what is going on. On the other hand, if this tag is set to NO the +# understand what is going on. On the other hand, if this tag is set to NO, the # size of the Perl module output will be much smaller and Perl will parse it # just the same. # The default value is: YES. # This tag requires that the tag GENERATE_PERLMOD is set to YES. -PERLMOD_PRETTY = YES +PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file are # prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful @@ -1914,26 +2419,26 @@ PERLMOD_PRETTY = YES # overwrite each other's variables. # This tag requires that the tag GENERATE_PERLMOD is set to YES. -PERLMOD_MAKEVAR_PREFIX = +PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- -# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all +# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all # C-preprocessor directives found in the sources and include files. # The default value is: YES. -ENABLE_PREPROCESSING = YES +ENABLE_PREPROCESSING = YES -# If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names -# in the source code. If set to NO only conditional compilation will be +# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names +# in the source code. If set to NO, only conditional compilation will be # performed. Macro expansion can be done in a controlled way by setting # EXPAND_ONLY_PREDEF to YES. # The default value is: NO. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -MACRO_EXPANSION = NO +MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then # the macro expansion is limited to the macros specified with the PREDEFINED and @@ -1941,21 +2446,22 @@ MACRO_EXPANSION = NO # The default value is: NO. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -EXPAND_ONLY_PREDEF = NO +EXPAND_ONLY_PREDEF = NO -# If the SEARCH_INCLUDES tag is set to YES the includes files in the +# If the SEARCH_INCLUDES tag is set to YES, the include files in the # INCLUDE_PATH will be searched if a #include is found. # The default value is: YES. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -SEARCH_INCLUDES = YES +SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by the -# preprocessor. +# preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of +# RECURSIVE has no effect here. # This tag requires that the tag SEARCH_INCLUDES is set to YES. -INCLUDE_PATH = +INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the @@ -1963,7 +2469,7 @@ INCLUDE_PATH = # used. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -INCLUDE_FILE_PATTERNS = +INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that are # defined before the preprocessor is started (similar to the -D option of e.g. @@ -1973,7 +2479,7 @@ INCLUDE_FILE_PATTERNS = # recursively expanded use the := operator instead of the = operator. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -PREDEFINED = CO_DOXYGEN CO_NO_SDO_CLIENT=1 CO_NO_NMT_MASTER=1 +PREDEFINED = CO_DOXYGEN=1 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this # tag can be used to specify a list of macro names that should be expanded. The @@ -1982,7 +2488,7 @@ PREDEFINED = CO_DOXYGEN CO_NO_SDO_CLIENT=1 CO_NO_NMT_MASTER=1 # definition found in the source code. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -EXPAND_AS_DEFINED = +EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will # remove all references to function-like macros that are alone on a line, have @@ -1992,7 +2498,7 @@ EXPAND_AS_DEFINED = # The default value is: YES. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -SKIP_FUNCTION_MACROS = YES +SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration options related to external references @@ -2011,83 +2517,53 @@ SKIP_FUNCTION_MACROS = YES # the path). If a tag file is not located in the directory in which doxygen is # run, you must also specify the path to the tagfile here. -TAGFILES = +TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create a # tag file that is based on the input files it reads. See section "Linking to # external documentation" for more information about the usage of tag files. -GENERATE_TAGFILE = +GENERATE_TAGFILE = doc/html/CANopenNode.tag -# If the ALLEXTERNALS tag is set to YES all external class will be listed in the -# class index. If set to NO only the inherited external classes will be listed. +# If the ALLEXTERNALS tag is set to YES, all external classes and namespaces +# will be listed in the class and namespace index. If set to NO, only the +# inherited external classes will be listed. # The default value is: NO. -ALLEXTERNALS = NO +ALLEXTERNALS = NO -# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed in -# the modules index. If set to NO, only the current project's groups will be +# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed +# in the topic index. If set to NO, only the current project's groups will be # listed. # The default value is: YES. -EXTERNAL_GROUPS = YES +EXTERNAL_GROUPS = YES -# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in +# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in # the related pages index. If set to NO, only the current project's pages will # be listed. # The default value is: YES. -EXTERNAL_PAGES = YES - -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of 'which perl'). -# The default file (with absolute path) is: /usr/bin/perl. - -PERL_PATH = /usr/bin/perl +EXTERNAL_PAGES = YES #--------------------------------------------------------------------------- -# Configuration options related to the dot tool +# Configuration options related to diagram generator tools #--------------------------------------------------------------------------- -# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram -# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to -# NO turns the diagrams off. Note that this option also works with HAVE_DOT -# disabled, but it is recommended to install and use dot, since it yields more -# powerful graphs. -# The default value is: YES. - -CLASS_DIAGRAMS = NO - -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see: -# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the -# documentation. The MSCGEN_PATH tag allows you to specify the directory where -# the mscgen tool resides. If left empty the tool is assumed to be found in the -# default search path. - -MSCGEN_PATH = - -# You can include diagrams made with dia in doxygen documentation. Doxygen will -# then run dia to produce the diagram and insert it in the documentation. The -# DIA_PATH tag allows you to specify the directory where the dia binary resides. -# If left empty dia is assumed to be found in the default search path. - -DIA_PATH = - -# If set to YES, the inheritance and collaboration graphs will hide inheritance +# If set to YES the inheritance and collaboration graphs will hide inheritance # and usage relations if the target is undocumented or is not a class. # The default value is: YES. -HIDE_UNDOC_RELATIONS = YES +HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz (see: -# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent +# https://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent # Bell Labs. The other options in this section have no effect if this option is # set to NO -# The default value is: YES. +# The default value is: NO. -HAVE_DOT = NO +HAVE_DOT = YES # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed # to run in parallel. When set to 0 doxygen will base this on the number of @@ -2097,63 +2573,91 @@ HAVE_DOT = NO # Minimum value: 0, maximum value: 32, default value: 0. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_NUM_THREADS = 0 - -# When you want a differently looking font in the dot files that doxygen -# generates you can specify the font name using DOT_FONTNAME. You need to make -# sure dot is able to find the font, which can be done by putting it in a -# standard location or by setting the DOTFONTPATH environment variable or by -# setting DOT_FONTPATH to the directory containing the font. -# The default value is: Helvetica. +DOT_NUM_THREADS = 0 + +# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of +# subgraphs. When you want a differently looking font in the dot files that +# doxygen generates you can specify fontname, fontcolor and fontsize attributes. +# For details please see Node, +# Edge and Graph Attributes specification You need to make sure dot is able +# to find the font, which can be done by putting it in a standard location or by +# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. Default graphviz fontsize is 14. +# The default value is: fontname=Helvetica,fontsize=10. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_FONTNAME = Helvetica +DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10" -# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of -# dot graphs. -# Minimum value: 4, maximum value: 24, default value: 10. +# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can +# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. Complete documentation about +# arrows shapes. +# The default value is: labelfontname=Helvetica,labelfontsize=10. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_FONTSIZE = 10 +DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10" -# By default doxygen will tell dot to use the default font as specified with -# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set -# the path where dot can find it using this tag. +# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes +# around nodes set 'shape=plain' or 'shape=plaintext' Shapes specification +# The default value is: shape=box,height=0.2,width=0.4. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_FONTPATH = +DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4" -# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for -# each documented class showing the direct and indirect inheritance relations. -# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO. -# The default value is: YES. +# You can set the path where dot can find font specified with fontname in +# DOT_COMMON_ATTR and others dot attributes. # This tag requires that the tag HAVE_DOT is set to YES. -CLASS_GRAPH = YES +DOT_FONTPATH = + +# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then doxygen will +# generate a graph for each documented class showing the direct and indirect +# inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and +# HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case +# the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the +# CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used. +# If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance +# relations will be shown as texts / links. Explicit enabling an inheritance +# graph or choosing a different representation for an inheritance graph of a +# specific class, can be accomplished by means of the command \inheritancegraph. +# Disabling an inheritance graph can be accomplished by means of the command +# \hideinheritancegraph. +# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN. +# The default value is: YES. + +CLASS_GRAPH = YES # If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a # graph for each documented class showing the direct and indirect implementation # dependencies (inheritance, containment, and class references variables) of the -# class with other documented classes. +# class with other documented classes. Explicit enabling a collaboration graph, +# when COLLABORATION_GRAPH is set to NO, can be accomplished by means of the +# command \collaborationgraph. Disabling a collaboration graph can be +# accomplished by means of the command \hidecollaborationgraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. -COLLABORATION_GRAPH = YES +COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for -# groups, showing the direct groups dependencies. +# groups, showing the direct groups dependencies. Explicit enabling a group +# dependency graph, when GROUP_GRAPHS is set to NO, can be accomplished by means +# of the command \groupgraph. Disabling a directory graph can be accomplished by +# means of the command \hidegroupgraph. See also the chapter Grouping in the +# manual. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. -GROUP_GRAPHS = YES +GROUP_GRAPHS = YES -# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. -UML_LOOK = NO +UML_LOOK = NO # If the UML_LOOK tag is enabled, the fields and methods are shown inside the # class node. If there are many fields or methods and many nodes the graph may @@ -2164,9 +2668,31 @@ UML_LOOK = NO # but if the number exceeds 15, the total amount of fields shown is limited to # 10. # Minimum value: 0, maximum value: 100, default value: 10. +# This tag requires that the tag UML_LOOK is set to YES. + +UML_LIMIT_NUM_FIELDS = 10 + +# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and +# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS +# tag is set to YES, doxygen will add type and arguments for attributes and +# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen +# will not generate fields with class member information in the UML graphs. The +# class diagrams will look similar to the default class diagrams but using UML +# notation for the relationships. +# Possible values are: NO, YES and NONE. +# The default value is: NO. +# This tag requires that the tag UML_LOOK is set to YES. + +DOT_UML_DETAILS = NO + +# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters +# to display on a single line. If the actual line length exceeds this threshold +# significantly it will be wrapped across multiple lines. Some heuristics are +# applied to avoid ugly line breaks. +# Minimum value: 0, maximum value: 1000, default value: 17. # This tag requires that the tag HAVE_DOT is set to YES. -UML_LIMIT_NUM_FIELDS = 10 +DOT_WRAP_THRESHOLD = 17 # If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and # collaboration graphs will show the relations between templates and their @@ -2174,76 +2700,95 @@ UML_LIMIT_NUM_FIELDS = 10 # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. -TEMPLATE_RELATIONS = NO +TEMPLATE_RELATIONS = NO # If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to # YES then doxygen will generate a graph for each documented file showing the # direct and indirect include dependencies of the file with other documented -# files. +# files. Explicit enabling an include graph, when INCLUDE_GRAPH is is set to NO, +# can be accomplished by means of the command \includegraph. Disabling an +# include graph can be accomplished by means of the command \hideincludegraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. -INCLUDE_GRAPH = YES +INCLUDE_GRAPH = YES # If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are # set to YES then doxygen will generate a graph for each documented file showing # the direct and indirect include dependencies of the file with other documented -# files. +# files. Explicit enabling an included by graph, when INCLUDED_BY_GRAPH is set +# to NO, can be accomplished by means of the command \includedbygraph. Disabling +# an included by graph can be accomplished by means of the command +# \hideincludedbygraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. -INCLUDED_BY_GRAPH = YES +INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH tag is set to YES then doxygen will generate a call # dependency graph for every global function or class method. # # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable call graphs for selected -# functions only using the \callgraph command. +# functions only using the \callgraph command. Disabling a call graph can be +# accomplished by means of the command \hidecallgraph. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. -CALL_GRAPH = NO +CALL_GRAPH = NO # If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller # dependency graph for every global function or class method. # # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable caller graphs for selected -# functions only using the \callergraph command. +# functions only using the \callergraph command. Disabling a caller graph can be +# accomplished by means of the command \hidecallergraph. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. -CALLER_GRAPH = NO +CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical # hierarchy of all classes instead of a textual one. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. -GRAPHICAL_HIERARCHY = YES +GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the # dependencies a directory has on other directories in a graphical way. The # dependency relations are determined by the #include relations between the -# files in the directories. +# files in the directories. Explicit enabling a directory graph, when +# DIRECTORY_GRAPH is set to NO, can be accomplished by means of the command +# \directorygraph. Disabling a directory graph can be accomplished by means of +# the command \hidedirectorygraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. -DIRECTORY_GRAPH = YES +DIRECTORY_GRAPH = YES + +# The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels +# of child directories generated in directory dependency graphs by dot. +# Minimum value: 1, maximum value: 25, default value: 1. +# This tag requires that the tag DIRECTORY_GRAPH is set to YES. + +DIR_GRAPH_MAX_DEPTH = 1 # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. +# generated by dot. For an explanation of the image formats see the section +# output formats in the documentation of the dot tool (Graphviz (see: +# https://www.graphviz.org/)). # Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order # to make the SVG files visible in IE 9+ (other browsers do not have this # requirement). -# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd, -# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo, -# gif:cairo:gd, gif:gd, gif:gd:gd and svg. +# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo, +# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and +# png:gdiplus:gdiplus. # The default value is: png. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_IMAGE_FORMAT = png +DOT_IMAGE_FORMAT = svg # If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to # enable generation of interactive SVG images that allow zooming and panning. @@ -2255,53 +2800,63 @@ DOT_IMAGE_FORMAT = png # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. -INTERACTIVE_SVG = NO +INTERACTIVE_SVG = YES # The DOT_PATH tag can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_PATH = +DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the \dotfile # command). # This tag requires that the tag HAVE_DOT is set to YES. -DOTFILE_DIRS = +DOTFILE_DIRS = -# The MSCFILE_DIRS tag can be used to specify one or more directories that -# contain msc files that are included in the documentation (see the \mscfile -# command). +# You can include diagrams made with dia in doxygen documentation. Doxygen will +# then run dia to produce the diagram and insert it in the documentation. The +# DIA_PATH tag allows you to specify the directory where the dia binary resides. +# If left empty dia is assumed to be found in the default search path. -MSCFILE_DIRS = +DIA_PATH = # The DIAFILE_DIRS tag can be used to specify one or more directories that # contain dia files that are included in the documentation (see the \diafile # command). -DIAFILE_DIRS = +DIAFILE_DIRS = # When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the -# path where java can find the plantuml.jar file. If left blank, it is assumed -# PlantUML is not used or called during a preprocessing step. Doxygen will -# generate a warning when it encounters a \startuml command in this case and -# will not generate output for the diagram. -# This tag requires that the tag HAVE_DOT is set to YES. +# path where java can find the plantuml.jar file or to the filename of jar file +# to be used. If left blank, it is assumed PlantUML is not used or called during +# a preprocessing step. Doxygen will generate a warning when it encounters a +# \startuml command in this case and will not generate output for the diagram. + +PLANTUML_JAR_PATH = -PLANTUML_JAR_PATH = +# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a +# configuration file for plantuml. + +PLANTUML_CFG_FILE = + +# When using plantuml, the specified paths are searched for files specified by +# the !include statement in a plantuml block. + +PLANTUML_INCLUDE_PATH = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes # that will be shown in the graph. If the number of nodes in a graph becomes # larger than this value, doxygen will truncate the graph, which is visualized -# by representing a node as a red box. Note that doxygen if the number of direct +# by representing a node as a red box. Note that if the number of direct # children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that # the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. # Minimum value: 0, maximum value: 10000, default value: 50. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_GRAPH_MAX_NODES = 50 +DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs # generated by dot. A depth value of 3 means that only nodes reachable from the @@ -2313,40 +2868,48 @@ DOT_GRAPH_MAX_NODES = 50 # Minimum value: 0, maximum value: 1000, default value: 0. # This tag requires that the tag HAVE_DOT is set to YES. -MAX_DOT_GRAPH_DEPTH = 0 - -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, because dot on Windows does not seem -# to support this out of the box. -# -# Warning: Depending on the platform used, enabling this option may lead to -# badly anti-aliased labels on the edges of a graph (i.e. they become hard to -# read). -# The default value is: NO. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_TRANSPARENT = NO +MAX_DOT_GRAPH_DEPTH = 0 -# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) support # this, this feature is disabled by default. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_MULTI_TARGETS = NO +DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page # explaining the meaning of the various boxes and arrows in the dot generated # graphs. +# Note: This tag requires that UML_LOOK isn't set, i.e. the doxygen internal +# graphical representation for inheritance and collaboration diagrams is used. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. -GENERATE_LEGEND = YES +GENERATE_LEGEND = YES -# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot +# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate # files that are used to generate the various graphs. +# +# Note: This setting is not only used for dot files but also for msc temporary +# files. # The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. -DOT_CLEANUP = YES +DOT_CLEANUP = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. If the MSCGEN_TOOL tag is left empty (the default), then doxygen will +# use a built-in version of mscgen tool to produce the charts. Alternatively, +# the MSCGEN_TOOL tag can also specify the name an external tool. For instance, +# specifying prog as the value, doxygen will call the tool as prog -T +# -o . The external tool should support +# output file formats "png", "eps", "svg", and "ismap". + +MSCGEN_TOOL = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the \mscfile +# command). + +MSCFILE_DIRS = diff --git a/LICENSE.old b/LICENSE.old deleted file mode 100644 index 1f963da0..00000000 --- a/LICENSE.old +++ /dev/null @@ -1,340 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. - diff --git a/MISRA.md b/MISRA.md new file mode 100644 index 00000000..45af92f6 --- /dev/null +++ b/MISRA.md @@ -0,0 +1,140 @@ +# MISRA Compliance + +The CANopenNode files conform to the [MISRA C:2012](https://www.misra.org.uk) +guidelines, with some noted exceptions. Compliance is checked with [PC Lint Plus](https://pclintplus.com/). + +A recommendation for MISRA: memory allocation and deallocation functions should not be used. +You must define the macro CO_USE_GLOBALS in your driver configuration. + + +## Configuration Inhibits Control + +### Inhibits: Excluded the OD.c and OD.h files from the check because there are configuration parameters (not source code execution) +``` +-efile( *, CANopenNode\OD.c ) +-efile( *, CANopenNode\OD.h ) +``` + +### Inhibits: Excluded the CO_gateway_ascii and CO_fifo (currently static analysis not completed) +``` +-efile( *, CANopenNode\309\CO_gateway_ascii.c ) +-efile( *, CANopenNode\309\CO_gateway_ascii.h ) +-efile( *, CANopenNode\301\CO_fifo.c ) +-efile( *, CANopenNode\301\CO_fifo.h ) +``` + +### Inhibits: C comment contains '://' sequence +ref.: MISRA C 2012 Rule 3.1 +``` +-efile( 9259, CANopenNode* ) +``` + +### Inhibits: unknown preprocessor directive 'string' in conditionally excluded region +ref.: MISRA C 2012 Rule 20.13 +``` +-efile( 9160, CANopenNode* ) +``` + +### Inhibits: conversion from pointer to void to other pointer type (type) +ref.: MISRA C 2012 Rule 11.5 +``` +-efile( 9079, CANopenNode* ) +``` + +### Inhibits: C comment contains C++ comment +ref.: MISRA C 2012 Rule 3.1 +``` +-efile( 9059, CANopenNode* ) +``` + +### Inhibits: complete definition of symbol is unnecessary in this translation unit +ref.: MISRA C 2012 Dir 4.8 +``` +-efile( 9045, CANopenNode* ) +``` + +### Inhibits: function parameter symbol modified +ref.: MISRA C 2012 Rule 17.8 +``` +-efile( 9044, CANopenNode* ) +``` + +### Inhibits: cannot cast essential-type value to essential-type type +ref.: MISRA C 2012 Rule 10.5 +``` +-efile( 9030, CANopenNode* ) +``` + +### Inhibits: function-like macro, 'macro', defined +ref.: MISRA C 2012 Dir 4.9 +``` +-efile( 9026, CANopenNode* ) +``` + +### Inhibits: pasting/stringize operator used in definition of object-like/function-like macro 'string' +ref.: MISRA C 2012 Rule 20.10 +``` +-efile( 9024, CANopenNode* ) +``` + +### Inhibits: performing pointer arithmetic via addition/subtraction +ref.: MISRA C 2012 Rule 18.4 +``` +-efile( 9016, CANopenNode* ) +``` + +### Inhibits: local variable symbol could be pointer to const +ref.: MISRA C 2012 Rule 8.13 +``` +-efile( 954, CANopenNode* ) +``` + +### Inhibits: return statement before end of function symbol +ref.: MISRA C 2012 Rule 15.5 +``` +-efile( 904, CANopenNode* ) +``` + +### Inhibits: the left/right operand to operator always evaluates to 0 +ref.: MISRA C 2004 Rule 13.7 +``` +-efile( 845, CANopenNode* ) +``` + +### Inhibits: previous value assigned to symbol not used +``` +-efile( 838, CANopenNode* ) +``` + +### Inhibits: zero given as string argument to operator context +``` +-efile( 835, CANopenNode* ) +``` + +### Inhibits: parameter symbol of function symbol could be pointer to const +ref.: MISRA C 2012 Rule 8.13 +``` +-efile( 818, CANopenNode* ) +``` + +### Inhibits: constant expression evaluates to 0 in 'unary/binary' operation 'operator' +``` +-efile( 778, CANopenNode* ) +``` + +### Inhibits: boolean condition for 'detail' always evaluates to 'detail' +ref.: MISRA C 2012 Rule 2.2 and Rule 14.3 +``` +-efile( 774, CANopenNode* ) +``` + +### Inhibits: local macro 'string' not referenced +ref.: MISRA C 2012 Rule 2.5 +``` +-efile( 750, CANopenNode* ) +``` + +### Inhibits: constant value used in Boolean context (string) +``` +-efile( 506, CANopenNode* ) +``` diff --git a/Makefile b/Makefile deleted file mode 100644 index 58b06c86..00000000 --- a/Makefile +++ /dev/null @@ -1,55 +0,0 @@ -# Makefile for CANopenNode, basic compile with no CAN device. - - -STACKDRV_SRC = stack/drvTemplate -STACK_SRC = stack -CANOPEN_SRC = . -APPL_SRC = example - - -LINK_TARGET = canopennode - - -INCLUDE_DIRS = -I$(STACKDRV_SRC) \ - -I$(STACK_SRC) \ - -I$(CANOPEN_SRC) \ - -I$(APPL_SRC) - - -SOURCES = $(STACKDRV_SRC)/CO_driver.c \ - $(STACKDRV_SRC)/eeprom.c \ - $(STACK_SRC)/crc16-ccitt.c \ - $(STACK_SRC)/CO_SDO.c \ - $(STACK_SRC)/CO_Emergency.c \ - $(STACK_SRC)/CO_NMT_Heartbeat.c \ - $(STACK_SRC)/CO_SYNC.c \ - $(STACK_SRC)/CO_TIME.c \ - $(STACK_SRC)/CO_PDO.c \ - $(STACK_SRC)/CO_HBconsumer.c \ - $(STACK_SRC)/CO_SDOmaster.c \ - $(STACK_SRC)/CO_LSSmaster.c \ - $(STACK_SRC)/CO_LSSslave.c \ - $(STACK_SRC)/CO_trace.c \ - $(CANOPEN_SRC)/CANopen.c \ - $(APPL_SRC)/CO_OD.c \ - $(APPL_SRC)/main.c - - -OBJS = $(SOURCES:%.c=%.o) -CC = gcc -CFLAGS = -Wall $(INCLUDE_DIRS) -LDFLAGS = - - -.PHONY: all clean - -all: clean $(LINK_TARGET) - -clean: - rm -f $(OBJS) $(LINK_TARGET) - -%.o: %.c - $(CC) $(CFLAGS) -c $< -o $@ - -$(LINK_TARGET): $(OBJS) - $(CC) $(LDFLAGS) $^ -o $@ diff --git a/README.md b/README.md index 7fd420c5..a41dfe21 100644 --- a/README.md +++ b/README.md @@ -1,91 +1,181 @@ -CANopenNode -=========== +# CANopenNode -CANopenNode is free and open source CANopen Stack. +CANopenNode is free and open source CANopen protocol stack. -CANopen is the internationally standardized (EN 50325-4) -([CiA301](http://can-cia.org/standardization/technical-documents)) -CAN-based higher-layer protocol for embedded control system. For more -information on CANopen see http://www.can-cia.org/ +CANopen is the internationally standardized (EN 50325-4) ([CiA301](https://www.can-cia.org/cia-groups/technical-documents)) higher-layer protocol for embedded control system built on top of CAN. For more information on CANopen see [http://www.can-cia.org/](http://www.can-cia.org/) -CANopenNode is written in ANSI C in object-oriented way. It runs on -different microcontrollers, as standalone application or with RTOS. -Stack includes master functionalities. For Linux implementation with -CANopen master functionalities see -https://github.com/CANopenNode/CANopenSocket. +CANopenNode is written in ANSI C in object-oriented way. It runs on different microcontrollers, as standalone application or with RTOS. -Variables (communication, device, custom) are ordered in CANopen Object -Dictionary and are accessible from both: C code and from CAN network. +Variables (communication, device, custom) are collected in CANopen Object Dictionary and are accessible from both: C code and from CANopen network. -CANopenNode homepage is https://github.com/CANopenNode/CANopenNode +CANopenNode homepage is [https://github.com/CANopenNode/CANopenNode](https://github.com/CANopenNode/CANopenNode) +This is version 4 of CANopenNode with new Object Dictionary implementation. For older versions `git checkout` branches `v1.3-master` or `v2.0-master`. -CANopen Features ----------------- - - NMT slave to start, stop, reset device. Simple NMT master. - - Heartbeat producer/consumer error control. - - PDO linking and dynamic mapping for fast exchange of process variables. - - SDO expedited, segmented and block transfer for service access to all parameters. - - SDO master. - - Emergency message. - - Sync producer/consumer. - - Time protocol (producer/consumer). - - Non-volatile storage. - - LSS master and slave, LSS fastscan +## Characteristics + +### CANopen -### RTR -RTR (remote transmission request) is a feature of CAN bus. Usage of RTR -is not recommended for CANopen and it is not implemented in CANopenNode. - -### Self start -Object **0x1F80** from Object Dictionary enables the NMT slaves to start -automatically or allows it to start the whole network. It is specified in -DSP302-2 standard. Standard allows two values for slaves for object 0x1F80: -- Object 0x1F80, value = **0x8** - "NMT slave shall enter the NMT state - Operational after the NMT state Initialization autonomously (self starting)" -- Object 0x1F80, value = **0x2** - "NMT slave shall execute the NMT service - start remote node with node-ID set to 0" - -Note: When node is stated (in NMT operational state), it is allowed to send or -receive Process Data Objects (PDO). If Error Register (object 0x1001) is set, -then NMT operational state is not allowed. - - -Usage of the CANopenNode ------------------------- -CANopenNode itself doesn't have complete working code for any microcontroller. -It is only the library with the stack It has example, which should compile -on any system with template driver (drvTemplate), which actually doesn't -access CAN hardware. CANopenNode should be used as a git submodule included -in a project with specific hardware and specific application. - - -Documentation, support and contributions ----------------------------------------- -Code is documented in header files. Running [doxygen](http://www.doxygen.nl/) -or `make doc` in project base folder will produce complete html documentation. -Just open CANopenNode/doc/html/index.html in browser. - -Report issues on https://github.com/CANopenNode/CANopenNode/issues - -Older and still active discussion group is on Sourceforge -http://sourceforge.net/p/canopennode/discussion/387151/ - -For some implementations of CANopenNode on real hardware see -[Device support](#device-support) section. -[CANopenSocket](https://github.com/CANopenNode/CANopenSocket) is nice -implementation for Linux devices. It includes command line interface for -master access of the CANopen network. There is also some Getting started. - -Contributions are welcome. Best way to contribute your code is to fork -a project, modify it and then send a pull request. Some basic formatting -rules should be followed: Linux style with indentation of 4 spaces. There -is also a configuration file for `clang-format` tool. - - -Flowchart of a typical CANopenNode implementation -------------------------------------------------- -~~~ +* [Object Dictionary](https://www.can-cia.org/can-knowledge/canopen-internal-device-architecture/) offers clear and flexible organisation of any variables. Variables can be accessed directly or via read/write functions. +* [NMT](https://www.can-cia.org/can-knowledge/network-management/) slave to start, stop, reset device. Simple NMT master. +* [Heartbeat](https://www.can-cia.org/can-knowledge/error-control-protocols) producer/consumer error control for monitoring of CANopen devices. An older alternative, 'node guarding', is also available. +* [PDO](https://www.can-cia.org/can-knowledge/pdo-protocol/) for broadcasting process data with high priority and no protocol overhead. Variables from Object Dictionary can be dynamically mapped to the TPDO, which is then transmitted according to communication rules and received as RPDO by another device. +* [SDO](https://www.can-cia.org/can-knowledge/sdo-protocol/) server enables expedited, segmented and block transfer access to all Object Dictionary variables inside CANopen device. +* [SDO](https://www.can-cia.org/can-knowledge/sdo-protocol/) client can access any Object Dictionary variable on any CANopen device inside the network. +* [Emergency](https://www.can-cia.org/can-knowledge/special-function-protocols/) message producer/consumer. +* [Sync](https://www.can-cia.org/can-knowledge/special-function-protocols/) producer/consumer enables network synchronized transmission of the PDO objects, etc. +* [Time-stamp](https://www.can-cia.org/can-knowledge/special-function-protocols/) producer/consumer enables date and time synchronization in millisecond resolution. +* [LSS](https://www.can-cia.org/can-knowledge/cia-305-layer-setting-services-lss/) CANopen node-id and bitrate setup, master and slave, LSS fastscan. +* [CANopen gateway](https://www.can-cia.org/can-knowledge/cia-309-series-accessing-canopen-via-tcp/), CiA309-3 Ascii command interface for NMT master, LSS master and SDO client. +* [CANopen Safety](https://standards.globalspec.com/std/1284438/en-50325-5), EN 50325-5, CiA304, "PDO like" communication in safety-relevant networks +* [CANopen Conformance Test Tool](https://www.can-cia.org/services/canopen-conformance-test-tool/) passed. + +### Other + +* [Suitable for 16-bit microcontrollers and above](#device-support) +* [Multithreaded, real-time](#canopennode-flowchart) +* [Object Dictionary editor](#object-dictionary-editor) +* Non-volatile storage for Object Dictionary or other variables. Automatic or controlled by standard CANopen commands, configurable. +* [Power saving possible](#power-saving) +* [Bootloader possible](https://github.com/CANopenNode/CANopenNode/issues/111) (for firmware update) + +## Zephyr RTOS integration (module) + +CANopenNode ships an in-tree Zephyr module for first-class Zephyr support. + +**Location:** `modules/canopennode/zephyr/` +**What you get:** + +* **Native CAN driver backend** (`CO_zephyr_driver.c`) that adapts CANopenNode to Zephyr’s `drivers/can` API (bitrate/mode, filters, TX callbacks, error status). +* **Runtime control API** (`CO_zephyr_integration.c/.h`): start/stop the stack from your app (`co_canopen_start()/co_canopen_stop()`), pick CAN device, Node-ID, and bitrate at runtime. +* **Optional RT thread** to run SYNC/RPDO/TPDO with low jitter (configurable with Kconfig). +* **LED bridge** (`CO_zephyr_leds.c/.h`) that mirrors CANopen RUN/ERR LEDs to GPIOs via DT aliases `co-led-run` and `co-led-err`. +* **Storage** (`CO_zephyr_storage.c/.h`) that plugs CANopenNode’s storage object into Zephyr subsystems (e.g., `settings`) or a RAM-only mode. + +### Quick start (West-based app) + +1. **Add the module to your west manifest:** + + ```yaml + manifest: + projects: + - name: CANopenNode + url: https://github.com/CANopenNode/CANopenNode.git + path: modules/canopennode + revision: + ``` + +2. **Enable features in `prj.conf`:** + + ```ini + # Core + CONFIG_CAN=y + + # CANopenNode (Zephyr module) + CONFIG_CANOPENNODE=y + CONFIG_CANOPENNODE_INIT_NODE_ID=1 # default if not passed at runtime + + # Optional LED bridge + CONFIG_GPIO=y + CONFIG_CANOPENNODE_LEDS_ENABLE=y + + # Optional storage via Zephyr settings + CONFIG_SETTINGS=y + CONFIG_SETTINGS_RUNTIME=y + CONFIG_CANOPENNODE_STORAGE_ENABLE=y + CONFIG_CANOPENNODE_STORAGE_BACKEND_SETTINGS=y + ``` + +3. **Devicetree setup (CAN + LED aliases):** + + ```dts + / { + aliases { + zephyr,canbus = &can0; /* or set in your board .dts */ + co-led-run = &led0; + co-led-err = &led1; + }; + }; + /* Ensure &can0 has bitrate etc. or rely on runtime start() parameters */ + ``` + +4. **Start from your application (optional, if not auto-starting):** + + ```c + #include + #include "CO_zephyr_integration.h" + + void main(void) + { + /* Use chosen CAN (zephyr,canbus), Node-ID 1, 500 kbit/s. + * Pass NULL to use DT_CHOSEN(zephyr_canbus). + */ + (void)co_canopen_start(NULL, 1, 500); + /* ... your app ... */ + } + ``` + + If `CONFIG_CANOPENNODE_RT_THREAD_AUTO_START=y` is enabled (default), the stack can also auto-start after kernel init using defaults from Kconfig/DT. + +5. **LEDs** + + * Provide `co-led-run` and `co-led-err` DT aliases as shown above. + * If you integrate CANopen directly (without the runtime auto-wiring), call: + + ```c + co_zephyr_leds_init_dt_aliases(); + co_zephyr_leds_connect_callback(CO->LEDs); + ``` + + (Requires access to your `CO_t` instance.) + +6. **Storage** + + * With `CONFIG_CANOPENNODE_STORAGE_ENABLE=y` and `CONFIG_CANOPEN_STORAGE_BACKEND_SETTINGS=y`, + standard store/restore commands (OD 0x1010/0x1011) persist parameters via Zephyr `settings`. + +> Tip: You can also pass an explicit `const struct device *can_dev` to `co_canopen_start()` if you don’t want to rely on `zephyr,canbus`. + +## Related projects + +* [CANopenNode](https://github.com/CANopenNode/CANopenNode) (this project): CANopen protocol stack, base for CANopen device. It contains no device specific code (drivers), which must be added separately for each target system. An example shows the basic principles, compiles on any system, but does not connect to any CAN hardware. +* [CANopenDemo](https://github.com/CANopenNode/CANopenDemo): Demo device with CANopenNode and different target systems, tutorial and testing tools. +* [CANopenNode.github.io](https://github.com/CANopenNode/CANopenNode.github.io): Html documentation, compiled by doxygen, for CANopenDemo, CANopenNode and other devices, available also online: [https://canopennode.github.io](https://canopennode.github.io) +* [CANopenEditor](https://github.com/CANopenNode/CANopenEditor): Object Dictionary editor, external GUI tool for editing CANopen Object Dictionary for custom device. It generates C source code, electronic data sheet and documentation for the device. It is a fork from [libedssharp](https://github.com/robincornelius/libedssharp). +* [CANopenLinux](https://github.com/CANopenNode/CANopenLinux): CANopenNode on Linux devices. It can be a basic CANopen device or more advanced with commander functionalities. +* [CANopenSTM32](https://github.com/CANopenNode/CanOpenSTM32): CANopenNode on STM32 microcontrollers. +* [Analog Devices Inc.](https://github.com/Analog-Devices-MSDK/CANopenADI): CANopenNode on Analog Devices Inc. MAX32xx microcontrollers. +* [CANopenPIC](https://github.com/CANopenNode/CANopenPIC): CANopenNode on PIC microcontrollers from Microchip. Works with 16-bit and 32 bit devices. Includes example for Arduino style [Max32](https://reference.digilentinc.com/reference/microprocessor/max32/start) board. +* [doc/deviceSupport.md](doc/deviceSupport.md): List of other implementations of CANopenNode on different devices. + +## Documentation, support and contributions + +All code is documented in the source header files. Some additional documents are in `doc` directory. + +To generate complete html documentation, run [doxygen](https://www.doxygen.nl/index.html) in the project base directory: + +```bash +sudo apt install doxygen graphviz pdf2svg +doxygen > /dev/null +``` + +Complete generated documentation is also available online: [https://canopennode.github.io](https://canopennode.github.io) + +> **Note:** If you want Zephyr-port APIs (driver/integration/LEDs/storage) included in Doxygen, ensure the `zephyr/` directory is listed in `Doxyfile` `INPUT` (or keep the provided defaults if already present). + +Tutorial, demo device and tests are available in [CANopenDemo](https://github.com/CANopenNode/CANopenDemo) repository. + +Report issues on [https://github.com/CANopenNode/CANopenNode/issues](https://github.com/CANopenNode/CANopenNode/issues) + +Contributions are welcome. Best way to contribute your code is to fork a project, modify it and then send a pull request. Please follow the [Recommended C style and coding rules](https://github.com/MaJerle/c-code-style), use .clang-format file for automatic code formatting. + +The CANopenNode files conform to the [MISRA C:2012](https://www.misra.org.uk) guidelines, with some noted exceptions, as indicated in [MISRA.md](MISRA.md). + +## CANopenNode flowchart + +Flowchart of a typical CANopenNode implementation: + +``` ----------------------- | Program start | ----------------------- @@ -100,153 +190,177 @@ Flowchart of a typical CANopenNode implementation | | | -------------------- | -------------------- | | | - ----------------------- ----------------------- ----------------------- -| CAN receive thread | | Timer interval thread | | Mainline thread | -| | | | | | -| - Fast response. | | - Realtime thread with| | - Processing of time | -| - Detect CAN ID. | | constant interval, | | consuming tasks | -| - Partially process | | typically 1ms. | | in CANopen objects: | -| messages and copy | | - Network synchronized| | - SDO server, | -| data to target | | - Copy inputs (RPDOs, | | - Emergency, | -| CANopen objects. | | HW) to Object Dict. | | - Network state, | -| | | - May call application| | - Heartbeat. | -| | | for some processing.| | - May cyclically call | -| | | - Copy variables from | | application code. | -| | | Object Dictionary to| | | -| | | outputs (TPDOs, HW).| | | - ----------------------- ----------------------- ----------------------- - - ----------------------- - | SDO client (optional) | - | | - | - Can be called by | - | external application| - | - Can read or write | - | any variable from | - | Object Dictionary | - | from any node in the| - | CANopen network. | - ----------------------- - - ----------------------- - | LSS client (optional) | - | | - | - Can be called by | - | external application| - | - Can do LSS requests | - | - Can request node | - | enumeration | - ----------------------- - - -~~~ - - -File structure --------------- - - **CANopen.h/.c** - Initialization and processing of CANopen objects. Most - usual implementation of CANopen device. - - **stack** - Directory with all CANopen objects in separate files. - - **CO_Emergency.h/.c** - CANopen Emergency object. - - **CO_NMT_Heartbeat.h/.c** - CANopen Network slave and Heartbeat producer object. - - **CO_HBconsumer.h/.c** - CANopen Heartbeat consumer object. - - **CO_LSS.h** - CANopen LSS common. This is common to LSS master and slave. - - **CO_LSSmaster.h/.c** - CANopen LSS master functionality. - - **CO_LSSslave.h/.c** - CANopen LSS slave functionality. - - **CO_SYNC.h/.c** - CANopen SYNC producer and consumer object. - - **CO_TIME.h/.c** - CANopen TIME protocol object. - - **CO_SDO.h/.c** - CANopen SDO server object. It serves data from Object dictionary. - - **CO_PDO.h/.c** - CANopen PDO object. It configures, receives and transmits CANopen process data. - - **CO_SDOmaster.h/.c** - CANopen SDO client object (master functionality). - - **CO_trace.h/.c** - Trace object with timestamp for monitoring variables from Object Dictionary (optional). - - **crc16-ccitt.h/.c** - CRC calculation object. - - **drvTemplate** - Directory with microcontroller specific files. In this - case it is template for new implementations. It is also documented, other - directories are not. - - **CO_driver.h/.c** - Microcontroller specific objects for CAN module. - - **eeprom.h/.c** - Functions for storage of Object dictionary, optional. - - **helpers.h/.c** - Some optional files with specific helper functions. - - **socketCAN** - Directory for Linux socketCAN interface. - - **PIC32** - Directory for PIC32 devices from Microchip. - - **PIC24_dsPIC33** - Directory for PIC24 and dsPIC33 devices from Microchip. - - **dsPIC30F** - Directory for dsPIC30F devices from Microchip. - - **eCos** - Directory for all devices supported by eCos RTOS. - - **SAM3X** - Directory for SAM3X ARM Cortex M3 devices with ASF library from Atmel. - - **STM32** - Directory for STM32 ARM devices from ST. - - **LPC177x_8x** - Directory for LPC177x (Cortex M3) devices with FreeRTOS from NXP. - - **MCF5282** - Directory for MCF5282 (ColdFire V2) device from Freescale. - - **codingStyle** - Description of the coding style. - - **Doxyfile** - Configuration file for the documentation generator *doxygen*. - - **Makefile** - Basic makefile. - - **LICENSE** - License. - - **README.md** - This file. - - **example** - Directory with basic example. - - **main.c** - Mainline and other threads - example template. - - **application.h/.c** - Separate file with some functions, which are - called from main.c. May be used for application specific code. - - **CO_OD.h/.c** - CANopen Object dictionary. Automatically generated files. - - **IO.eds** - Standard CANopen EDS file, which may be used from CANopen - configuration tool. Automatically generated file. - - _ **project.xml** - XML file contains all data for CANopen Object dictionary. - It is used by *Object dictionary editor* application, which generates other - files. - - _ **project.html** - *Object dictionary editor* launcher. - - -### Object dictionary editor -Object Dictionary is one of the most important parts of CANopen. Its -implementation in CANopenNode is quite outdated and there are efforts to -rewrite it. Anyway, currently it is fully operational and works well. - -To customize the Object Dictionary it is necessary to use the -external application. There are two: - - [libedssharp](https://github.com/robincornelius/libedssharp) - - recommended, can be used with mono. - - [Object_Dictionary_Editor](http://sourceforge.net/p/canopennode/code_complete/) - - originally part of CANopenNode. It is still operational, but requiers - very old version of Firefox to run. - - -Device support --------------- -CANopenNode can be implemented on many different devices. It is -necessary to implement interface to specific hardware, so called 'driver'. -Currently driver files are part of CANopenNode, but they will be split from -it in the future. - -Most up to date information on device support can be found on -[CANopenNode/wiki](https://github.com/CANopenNode/CANopenNode/wiki). - - -### Note for contributors -For the driver developers, who wish to share and cooperate, I recommend the following approach: -1. Make own git repo for the Device specific demo project on the Github or somewhere. -2. Add https://github.com/CANopenNode/CANopenNode into your project (or at side of your project). - For example, include it in your project as a git submodule: - `git submodule add https://github.com/CANopenNode/CANopenNode` -3. Add specific driver and other files. -4. **Add a note** about your specific implementation here on - [CANopenNode/wiki](https://github.com/CANopenNode/CANopenNode/wiki) with some - basic description and status. Write a note, even it has an Alpha status. -5. Make a demo folder, which contains project files, etc., necessary to run the demo. -6. Write a good README.md file, where you describe your project, specify demo board, tools used, etc. - - -History of the project ----------------------- -Project was initially hosted on http://sourceforge.net/projects/canopennode/ -It started in 2004 with PIC18F microcontrollers from Microchip. -Fresh, cleaned repository of CANopenNode stack started on 25.7.2015. -For older history see http://sourceforge.net/p/canopennode/code_complete/ - - -License -------- + ---------------------- ------------------------ ----------------------- +| CAN receive thread | | Timer interval thread | | Mainline thread | +| | | | | | +| - Fast response. | | - Realtime thread with | | - Processing of time | +| - Detect CAN ID. | | constant interval, | | consuming tasks | +| - Partially process | | typically 1ms. | | in CANopen objects: | +| messages and copy | | - Network synchronized | | - SDO server, | +| data to target | | - Copy inputs (RPDOs, | | - Emergency, | +| CANopen objects. | | HW) to Object Dict. | | - Network state, | +| | | - May call application | | - Heartbeat. | +| | | for some processing. | | - LSS slave | +| | | - Copy variables from | | - Gateway (optional): | +| | | Object Dictionary to | | - NMT master | +| | | outputs (TPDOs, HW). | | - SDO client | +| | | | | - LSS master | +| | | | | - May cyclically call | +| | | | | application code. | + ---------------------- ------------------------ ----------------------- +``` + +All code of the CANopenNode is non-blocking. Code in source files is collected into objects. Parts of the code can be enabled/disabled, so only files and parts of code can be used, which are required for the project. See stack configuration in 301/CO\_config.h file. + +For most efficiency code can run in different thread as seen in above flowchart. This is suitable for microcontrollers. It is also possible to run everything from single thread, as available on Linux devices. Code includes mechanisms, which triggers processing of OD objects when necessary. + +In CANopen initialization section all CANopen objects are initialized. In run time CANopen objects are processed cyclically. + +Files CANopen.h and CANopen.c is a joint of all CANopen objects. It may seems complex, but offers some flexibility and is suitable for most common configurations of the CANopen objects. CANopen objects can be defined in global space or can be dynamically allocated. Object dictionary can be used default (OD.h/.c files), but configuration with multiple object dictionaries is also possible by using the #CO\_config\_t structure. CANopen.h and CANopen.c files can also be only a reference for more customized implementation of CANopenNode based device. + +Object Dictionary is a collection of all network accessible variables and offers most flexible usage. OD variables can be initialized by object dictionary or application can specify own read/write access functions for specific OD variables. Groups of OD variables are also able to be stored to non-volatile memory, either on command or automatically. + +## Object dictionary editor + +Object Dictionary is one of the most essential parts of CANopen. + +To customize the Object Dictionary it is necessary to use external application: [CANopenEditor](https://github.com/CANopenNode/CANopenEditor). Binaries are also available there. In Linux it runs with mono, which is available by default on Ubuntu. + +In program, in preferences, set exporter to "CANopenNode\_V4". Then start new project or open the existing project file. + +Many project file types are supported, EDS, XDD v1.0, XDD v1.1, old custom XML format. Generated project file can then be saved in XDD v1.1 file format (xmlns="[http://www.canopen.org/xml/1.1](http://www.canopen.org/xml/1.1)"). Project file can also be exported to other formats, it can be used to generate documentation and CANopenNode source files for Object Dictionary. + +If new project was started, then `DS301_profile.xpd` may be inserted. If existing (old) project is edited, then existing `Communication Specific Parameters` may be deleted and then new `DS301_profile.xpd` may be inserted. Alternative is editing existing communication parameters with observation to Object Dictionary Requirements By CANopenNode in [objectDictionary.md](doc/objectDictionary.md). + +To clone, add or delete, select object(s) and use right click. Some knowledge of CANopen is required to correctly set-up the custom Object Dictionary. Separate objects can also be inserted from another project. + +CANopenNode includes some custom properties inside standard project file. See [objectDictionary.md](doc/objectDictionary.md) for more information. + +### EDS → C generator (eds-utils) + +For teams that keep the Object Dictionary as an **EDS** file, this repo includes a small **CLI wrapper** around the Python package **`eds-utils`** so you can generate `OD.h`/`OD.c` without any GUI dependencies (handy on Windows). Usage example: + +```bash +python eds2c_wrapper.py generate path/to/device.eds -o build/od +``` + +The wrapper simply forwards all arguments to `eds_utils.eds2c`’s CLI entry point and avoids GTK/GUI modules. + +#### Build integration (CMake) + +A helper `CMakeLists.txt` is provided that will: + +* Ensure `eds-utils` is available (auto-installs it with `pip` if missing), +* Run `eds2c_wrapper.py` to generate `OD.h` and `OD.c` from your EDS, +* Place outputs in the build directory and export `CO_OD_H`, `CO_OD_C`, and `CO_OD_DIR` cache variables you can add to your build, +* Expose a `generate_od` target that depends on the generated files. + +This lets you regenerate the Object Dictionary whenever the EDS changes, fully integrated into your normal build. + +> Tip: If you prefer to drive generation manually, you can call `eds2c_wrapper.py` directly as shown above; it accepts the same arguments as the upstream `eds2c` tool. + +## File structure + +* **301/** – CANopen application layer and communication profile. + + * **CO\_config.h** – Configuration macros for CANopenNode. + * **CO\_driver.h** – Interface between CAN hardware and CANopenNode. + * **CO\_ODinterface.h/.c** – CANopen Object Dictionary interface. + * **CO\_Emergency.h/.c** – CANopen Emergency protocol. + * **CO\_HBconsumer.h/.c** – CANopen Heartbeat consumer protocol. + * **CO\_NMT\_Heartbeat.h/.c** – CANopen Network management and Heartbeat producer protocol. + * **CO\_PDO.h/.c** – CANopen Process Data Object protocol. + * **CO\_SDOclient.h/.c** – CANopen Service Data Object – client protocol (master functionality). + * **CO\_SDOserver.h/.c** – CANopen Service Data Object – server protocol. + * **CO\_SYNC.h/.c** – CANopen Synchronisation protocol (producer and consumer). + * **CO\_TIME.h/.c** – CANopen Time-stamp protocol. + * **CO\_fifo.h/.c** – Fifo buffer for SDO and gateway data transfer. + * **crc16-ccitt.h/.c** – Calculation of CRC 16 CCITT polynomial. +* **303/** – CANopen Recommendation + + * **CO\_LEDs.h/.c** – CANopen LED Indicators +* **304/** – CANopen Safety Related Data Object, as specified by EN 50325-5:2010 + + * **CO\_SRDO.h/.c** – CANopen Safety-relevant Data Object protocol. + * **CO\_GFC.h/.c** – CANopen Global Failsafe Command (producer and consumer). +* **305/** – CANopen layer setting services (LSS) and protocols. + + * **CO\_LSS.h** – CANopen Layer Setting Services protocol (common). + * **CO\_LSSmaster.h/.c** – CANopen Layer Setting Service – master protocol. + * **CO\_LSSslave.h/.c** – CANopen Layer Setting Service – slave protocol. +* **309/** – CANopen access from other networks. + + * **CO\_gateway\_ascii.h/.c** – Ascii mapping: NMT master, LSS master, SDO client. +* **storage/** + + * **CO\_storage.h/.c** – CANopen data storage base object. + * **CO\_storageEeprom.h/.c** – CANopen data storage object for storing data into block device (eeprom). + * **CO\_eeprom.h** – Eeprom interface for use with CO\_storageEeprom, functions are target system specific. +* **extra/** + + * **CO\_trace.h/.c** – CANopen trace object for recording variables over time. +* **example/** – Directory with basic example, should compile on any system. + + * **CO\_driver\_target.h** – Example hardware definitions for CANopenNode. + * **CO\_driver\_blank.c** – Example blank interface for CANopenNode. + * **main\_blank.c** – Mainline and other threads – example template. + * **CO\_storageBlank.h/.c** – Example blank demonstration for data storage to non-volatile memory. + * **Makefile** – Makefile for example. + * **DS301\_profile.xpd** – CANopen device description file for DS301. It includes also CANopenNode specific properties. This file is also available in Profiles in Object dictionary editor. + * **DS301\_profile.eds**, **DS301\_profile.md** – Standard CANopen EDS file and markdown documentation file, automatically generated from DS301\_profile.xpd. + * **OD.h/.c** – CANopen Object dictionary source files, automatically generated from DS301\_profile.xpd. +* **doc/** – Directory with documentation + + * **CHANGELOG.md** – Change Log file. + * **deviceSupport.md** – Information about supported devices. + * **objectDictionary.md** – Description of CANopen object dictionary interface. + * **CANopenNode.png** – Little icon. + * **html** – Directory with documentation – must be generated by Doxygen. +* **modules/canopennode/zephyr/** – Zephyr module integration + + * **module.yml** – Zephyr module metadata. + * **Kconfig** – Kconfig options for the Zephyr port. + * **src/** – Zephyr backend sources: `CO_zephyr_driver.c`, `CO_zephyr_integration.c`, `CO_zephyr_leds.c`, `CO_zephyr_storage.c`, etc. + * **include/** – Public headers for the Zephyr integration (e.g. `CO_zephyr_integration.h`). +* **eds2c\_wrapper.py** – CLI wrapper that calls `eds_utils.eds2c` to convert an EDS into `OD.h/OD.c` (Windows-friendly, no GUI deps). +* **CMakeLists.txt** – Helper target `generate_od` that installs `eds-utils` if needed and generates `OD.h/OD.c` from your EDS; exports `CO_OD_H/CO_OD_C/CO_OD_DIR`. +* **CANopen.h/.c** – Initialization and processing of CANopen objects, suitable for common configurations. +* **Doxyfile** – Configuration file for the documentation generator *doxygen*. +* **LICENSE** – License. +* **MISRA.md** – MISRA C:2012 conformance information. +* **README.md** – This file. + +## Device support + +CANopenNode can run on many different devices. Each device (or microcontroller) must have own interface to CANopenNode. CANopenNode can run with or without operating system. + +It is not practical to have all device interfaces in a single project. Interfaces to other microcontrollers are in separate projects. See [deviceSupport.md](doc/deviceSupport.md) for list of known device interfaces. + +## Some details + +### RTR + +RTR (remote transmission request) is a feature of CAN bus. Usage of RTR is not recommended for CANopen. RTR PDO is not implemented in CANopenNode. + +### Error control + +When node is started (in NMT operational state), it is allowed to send or receive Process Data Objects (PDO). If Error Register (object 0x1001) is set, then NMT operational state may not be allowed. + +### Power saving + +All CANopen objects calculates next timer info for OS. Calculation is based on various timers which expire in known time. Can be used to put microcontroller into sleep and wake at the calculated time. + +## Change Log + +See [CHANGELOG.md](doc/CHANGELOG.md) + +## License + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at -http://www.apache.org/licenses/LICENSE-2.0 +[http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, diff --git a/codingStyle b/codingStyle deleted file mode 100644 index 6452ba89..00000000 --- a/codingStyle +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Description of the coding style for the source files. - * - * @file codingStyle - * @ingroup codingStyle - * @author name - * @copyright 2020 name - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef XYZ_H -#define XYZ_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @defgroup codingStyle Description of coding style - * @ingroup parentGroup - * @{ - * - * Contents of this file should be the base for .h source file, except function - * body at the end. - * - * ###Style - * - Style is based on Linux style - * - Indent size is 4. - * - Spaces are used, not tabs. - * - More datails are defined in .clang-format file - * - Some (old) code is still not corectly formatted. (To not break git history.) - * - * ###Doxygen - * Documentation is generated by doxygen. - * Doxygen comment starts with /**. /**< is used after member. - * Documentation is usually in header. - * Doxygen settings: - * - JAVADOC_AUTOBRIEF = YES. - * - See doxyfile for other settings. - * - * Doxygen specifics: If description of the structure member is one sentence - * only, don't use period after the sentence. - * - * ###Misra C - * Code shall follow MISRA-C:2012 standard. - */ - - -/** - * Brief description of the object ends at this dot. Details follow - * here. - */ -typedef struct { - int8_t member1; /**< Short description of the member 1 */ - uint16_t member2; /**< Note the '/**<' sequence after the member 2 */ - /** Long description of the variable stringMember. More description. */ - char_t stringMember[5]; -} object1_t; - - -/** - * Function example 1. - * - * This is global function. Local functions (and variables) used inside one file - * are declared as static and not documented by Doxygen. - * - * @param obj Pointer to object. Function operates on this object (not on global - * variables). - * @param argument_2 Description of the argument. - * @param argument_2 Description of the argument. - * @param argument_4 Description of the argument. - * - * @return Some value. - */ -int32_t foo1(object1_t *obj, - int32_t argument_2, - uint16_t argument_3, - float32_t argument_4) -{ - /* Comment */ - - /* Multiline - * comment. - */ - - if (xy == yz) { /* Comment. '//' comments are not allowed */ - a = b; - } else { - a = c; - } - - switch (zx) { - case 1: - a = b; - break; - } -} - -/** @} */ - -#ifdef __cplusplus -} -#endif /*__cplusplus */ - -#endif /* XYZ_H */ diff --git a/doc/CANopenNode.png b/doc/CANopenNode.png new file mode 100644 index 00000000..4d885559 Binary files /dev/null and b/doc/CANopenNode.png differ diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md new file mode 100644 index 00000000..fee8b0c0 --- /dev/null +++ b/doc/CHANGELOG.md @@ -0,0 +1,129 @@ +Change Log +========== + +v4.0 - current +-------------- +- [Source Code](https://github.com/CANopenNode/CANopenNode/tree/master) +- [Full ChangeLog](https://github.com/CANopenNode/CANopenNode/compare/v2.0-master...master) +### Latest changes +- 2024-07-08: Code reforamtted according to .clang-format +- 2024-07-05: Static analysis for Misra C:2012 +- 2024-05-31: SRDO updated to current CANopenNode +- 2023-09-07: Node guarding added +### Removed +- Driver for Linux (socketCAN directory) moved to own repository https://github.com/CANopenNode/CANopenLinux. +### Changed +- New Object dictionary interface. It has similar principles as before. Main access to OD variables is via fast read/write functions, but direct access to OD variables is also possible. OD entries are passed with pointers to CANopen objects. All parts of CANopenNode objects, which works with OD entries, are rewritten. +- [CANopenEditor](https://github.com/CANopenNode/CANopenEditor) (formerly [libedssharp](https://github.com/robincornelius/libedssharp)) has new OD exporter, new project file format (standard CANopen XDD v1.1), new documentation generator, and many other improvements. +- New OD.h and OD.c files, replaces CO_OD files. +- CANopen.c and CANopen.h files redesigned. `#include OD.h` is optional. Configuration of multiple object dictionaries is possible with one CANopen device. Interface is the same, with some changes to function arguments. +- New CO_storage.h/c files enables easier integration to target system for storing OD variables. +- Rewritten SDO server. Object dictionary part is moved to CO_ODinterface.h/c files. +- Rewritten PDO. PDO mapped variables are accessed via fast read/write functions. New RPDO event timer (timeout). +- CO_Emergency is mostly rewritten. Now is much easier customisation. All other objects has been adjusted to newOD, inspected and some parts were redesigned. + + +v2.0 - 2020-02-25 +----------------- +- [Source Code](https://github.com/CANopenNode/CANopenNode/tree/v2.0-master) +- [Full ChangeLog](https://github.com/CANopenNode/CANopenNode/compare/v1.3-master...v2.0-master) +### Removed +- All drivers removed from this project, except Neuberger-socketCAN for Linux. +### Changed +- Directory structure rearranged. Before was all CANopen object files in `stack` directory, now they are in separate directories according to standard (`301`, `305`, `extra`, `socketCAN` for Linux driver). Include directives for that files now contain directory path. `CO_SDO` renamed to `CO_SDOserver` and `CO_SDOmaster` renamed to `CO_SDOclient`. Change of the project files will be necessary. +- Driver interface clarified. Before was pair of CO_driver.h/.c files for each microcontroller, now there is common CO_driver.h file. Drivers for other microcontrollers will be separate projects. Each driver must have own CO_driver_target.h file and function definitions from C_driver.h file. See documentation in CO_driver.h, example/CO_driver_target.h and example/CO_driver.c. There was no other major changes in driver interface. +- Time base is now microsecond in all functions. +- CANopen.h/.c files simplified and changed. `CO_USE_GLOBALS` and `CO_init()` removed. Interface to those functions changed. +- `CO_NMT_sendCommand()` master function renamed and moved from CANopen.c into CO_NMT_Heartbeat.c. +- Heartbeat consumer `CO_HBconsumer_process()` optimized. +- Rename in CO_driver_target.h: `IS_CANrxNew` -> `CO_FLAG_READ`, `SET_CANrxNew` -> `CO_FLAG_SET`, `CLEAR_CANrxNew` -> `CO_FLAG_CLEAR` +- CO_driver.h file, function `CO_CANrxBufferInit()`, last argument (callback) changed from `(*pFunct)(void *object, const CO_CANrxMsg_t *message)` to `void (*CANrx_callback)(void *object, void *message)`. New functions are defined in `CO_driver_target.h` file: `CO_CANrxMsg_readIdent()`, `CO_CANrxMsg_readDLC()` and `CO_CANrxMsg_readData()`. +- It is necessary to manually update CO_OD.c file - it must include: `301/CO_driver.h`, `CO_OD.h` and `301/CO_SDOserver.h`. +- Added `void *object` argument to CO_*_initCallback() functions. API clarified. +- Add emergency receive callback also for own emergency messages. +- Heartbeat is send immediately after NMT state changes. +- SDO client is rewritten. Now includes read/write fifo interface to transfer data. +- LED indicator indication (CiA303-3) moved from NMT into own files. Now fully comply to standard. +- LSS slave is integrated into CANopenNode more directly. +- CO_driver interface: remove Emergency object dependency for reporting CAN errors, use CANerrorStatus own variable instead. Emergency object updated. +### Changed SocketCAN +- ./stack/socketCAN removed from the project, ./stack/Neuberger-socketCAN moved to ./socketCAN +- driver API updated +- CO_Linux_threads.h, function `void CANrx_threadTmr_init(uint16_t interval_in_milliseconds (changed to) uint32_t interval_in_microseconds)` +- CO_CANrxBufferInit(): remove check COB ID already used. +- change macros CO_DRIVER_MULTI_INTERFACE and CO_DRIVER_ERROR_REPORTING. To enable(disable), set to 1(0). +- Rename CO_Linux_threads.h/.c to CO_epoll_interface.h/.c and reorganize them. Move epoll, timerfd and eventfd system calls from CO_driver.c to here. +- Can run in single thread, including gateway. +### Fixed +- Bugfix in `CO_HBconsumer_process()`: argument `timeDifference_us` was set to 0 inside for loop, fixed now. +- BUG in CO_HBconsumer.c #168 +### Added +- Documentation added to `doc` directory: CHANGELOG.md, deviceSupport.md, gettingStarted.md, LSSusage.md and traceUsage.md. +- All CANopen objects calculates next timer info for OS. Useful for energy saving. +- Added file CO_config.h for stack configuration. Can be overridden by target specific or by custom definitions. It enables/disables whole CanOpenNode objects or parts of them. It also specifies some constants. +- CO_fifo.h/c for fifo data buffer, used with rewritten SDO client, etc. +- CANopen gateway-ascii command interface according to CiA309-3 as a microcontroller independent module. It includes NMT master, LSS master and SDO client interface. Interface is non-blocking, it is added to mainline. Example for Linux stdio and socket is included. + + +v1.3 - 2020-04-27 +----------------- +- [Source Code](https://github.com/CANopenNode/CANopenNode/tree/v1.3-master) +- [Full ChangeLog](https://github.com/CANopenNode/CANopenNode/compare/v1.2...v1.3-master) +### Changed +- License changed to Apache 2.0. +- NMT self start functionality (OD object 1F80) implemented to strictly follow standard. Default value for object 1F80 have to be updated in OD editor. See README.md. +### Fixed +- Various fixes. +- neuberger-socketCAN fixed. +### Added +- CANopen TIME protocol added. + + +v1.2 - 2019-10-08 +----------------- +- [Source Code](https://github.com/CANopenNode/CANopenNode/tree/v1.2) +- [Full ChangeLog](https://github.com/CANopenNode/CANopenNode/compare/v1.1...v1.2) +### Fixed +- Memory barrier implemented for setting/clearing flags for CAN received message. +- CO_Emergency and CO_HBconsumer files revised. +### Added +- CANopen LSS master/slave protocol added for configuration of bitrate and node ID. +- Neuberger-socketCAN driver added. +- Emergency consumer added. +- Callbacks added to Emergency and Heartbeat consumer. + + +v1.1 - 2019-10-08 +----------------- +- [Source Code](https://github.com/CANopenNode/CANopenNode/tree/v1.1) +- [Full ChangeLog](https://github.com/CANopenNode/CANopenNode/compare/v1.0...v1.1) +- Bugfixes. Some non-critical warnings in stack, some formatting warnings in tracing stuff. + + +v1.0 - 2017-08-01 +----------------- +- [Source Code](https://github.com/CANopenNode/CANopenNode/tree/v1.0) +- [Full ChangeLog](https://github.com/CANopenNode/CANopenNode/compare/v0.5...v1.0) +- Stable. + + +v0.5 - 2015-10-20 +----------------- +- [Source Code](https://github.com/CANopenNode/CANopenNode/tree/v0.5) +- Git repository started on GitHub. + + +v0.4 - 2012-02-26 +----------------- +- [Source Code](https://sourceforge.net/p/canopennode/code_complete/ci/master/tree/) +- Git repository started on Sourceforge git. + + +v0.1 - 2004-06-29 +----------------- +- [Source Code](https://sourceforge.net/projects/canopennode/files/canopennode/CANopenNode-0.80/) +- First edition of CANopenNode on SourceForge, files section. (V0.80 on SourceForge). + +------ + +Changelog written according to recommendations from https://keepachangelog.com/ diff --git a/doc/deviceSupport.md b/doc/deviceSupport.md new file mode 100644 index 00000000..c9950735 --- /dev/null +++ b/doc/deviceSupport.md @@ -0,0 +1,134 @@ +Device Support +============== + +CANopenNode can run on many different devices. There are possible many different implementations on many different hardware, with many different development tools, by many different developers. It is not possible for single project maintainer to keep all the hardware interfaces updated. For that reason all hardware specific files are not part of the CANopenNode project. + +It is necessary to implement interface to specific hardware. Interface to Linux socketCAN is part of this projects. Interfaces to other controllers are separate projects. There are interfaces to: Zephyr RTOS, PIC, [Analog Devices Inc. (ADI)](https://www.analog.com), Mbed-os RTOS + STM32, NXP, etc. + + +Note for device driver contributors +----------------------------------- +Most up-to-date implementations of CANopenNode are: [CANopenLinux](https://github.com/CANopenNode/CANopenLinux) and [CANopenPIC](https://github.com/CANopenNode/CANopenPIC) for PIC32 microcontroller (bare-metal). Those can be used for reference. There is also an example directory, which doesn't include specific device interface. It should compile on any system and can be used as a template. Device interface is documented in common CO_driver.h file. + +There are many advantages of sharing the base code such as this. For the driver developers, who wish to share and cooperate, I recommend the following approach: +1. Make own git repo for the Device specific demo project on the Github or somewhere. +2. Add https://github.com/CANopenNode/CANopenNode into your project (or at side of your project). For example, include it in your project as a git submodule: `git submodule add https://github.com/CANopenNode/CANopenNode` +3. Add specific driver and other files. +4. Write a good README.md file, where you describe your project, specify demo board, tools used, etc. +5. Optionally prepare a demoDevice in [CANopenDemo](https://github.com/CANopenNode/CANopenDemo) repository and run the tests. +6. Share your work: add description of new device into this file (deviceSupport.md) and make a pull request to CANopenNode. Alternatively create an issue for new device on https://github.com/CANopenNode/CANopenNode/issues. +7. Offer your work for inclusion under the CANopenNode project and become its developer. It will increase code quality and functionality. + + +Linux +----- +* CANopenNode integration with Linux socketCAN with master command interface. SocketCAN is part of the Linux kernel. +* https://github.com/CANopenNode/CANopenLinux. +* CANopenNode version: (v4.0) +* Features: OD storage, error counters, master (SDO client, LSS master, NMT master) +* Systems: Linux PC, Raspberry PI, etc. + + +STM32 +----- +* CANopenNode integration with STM32 microcontrollers. +* https://github.com/CANopenNode/CanOpenSTM32 +* CANopenNode version: (v4.0) + + +PIC32, dsPIC30, dsPIC33 +----------------------- +* CANopenNode integration with 16 and 32 bit PIC microcontrollers from Microchip. +* https://github.com/CANopenNode/CANopenPIC +* CANopenNode version: (v4.0) +* Features: OD storage for PIC32, SDO client demo for PIC32, error counters +* Development tools: MPLAB X +* Demo hardware: Explorer 16 from Microchip, [Max32 board](https://reference.digilentinc.com/reference/microprocessor/max32/start) +* Example with smallest resuorces (less than 2kb RAM), 4TPDO+4RPDO: 16-bit dsPIC30F4011 + + +[Analog Devices Inc](https://www.analog.com): MAX32662, MAX32690 +------------------ +* CANopenNode integration with Analog Devices MAX32662 and MAX32690 microcontrollers. +* https://github.com/Analog-Devices-MSDK/CANopenADI +* CANopenNode version: (v4.0) +* Status: seems to be stable +* Features: LED indicators, error counters +* Development tools: Maxim Micros SDK +* Demo hardware: MAX32662-EVKIT and MAX32690-EVKIT +* Information updated 2023-02-17 + + +Zephyr RTOS +----------- +* CANopenNode integration with Zephyr RTOS +* https://github.com/zephyrproject-rtos/zephyr/tree/master/subsys/canbus/canopen +* Example integration: https://docs.zephyrproject.org/latest/samples/subsys/canbus/canopen/README.html +* CANopenNode version: v1.3 +* Status: stable +* Features: OD storage, LED indicators, Program Download, SDO server demo for Zephyr RTOS +* Development tools: Zephyr SDK +* Demo hardware: Any development board with CAN interface and Zephyr support (see https://docs.zephyrproject.org/latest/boards/index.html) +* Information updated 2020-09-28 + + +Mbed-os RTOS + STM32 (F091RC, L496ZG) +------------------------------------- +* CANopenNode integration with Mbed-os RTOS +* https://github.com/Alphatronics/mbed-os-example-canopen +* CANopenNode version: +* Status: Stable +* Features: OD storage, LED indicators, GPIO +* Development tools: Mbed CLI (gcc 7) +* Demo hardware: STM32 Nucleo F091RC (or similar) development board with CAN interface (see https://os.mbed.com/platforms/ST-Nucleo-F091RC) +* Information updated 2020-01-21 + + +Kinetis K20 (NXP Arm) +--------------------- +* CANopenNode driver for Teensy3 (Kinetis K20 Arm) +* Driver source: https://github.com/c0d3runn3r/CANopenNode/tree/add-k20-driver/stack/ARM_Kinetis_K20_teensy +* Discussion: https://github.com/CANopenNode/CANopenNode/pull/28 +* CANopenNode version: v1.0, 2017-08-01 +* Status: seems to be stable +* Features: error counters, ? +* Development tools: (see [readme](https://github.com/c0d3runn3r/CANopenNode/tree/add-k20-driver/stack/ARM_Kinetis_K20_teensy)) +* Demo hardware: Teensyduino, [Teensy3](https://www.pjrc.com/store/teensy32.html) +* Information updated 2020-03-02 + + +S32DS (NXP S32 Design studio for Arm or Powerpc) +------------------------------------------------ +* CANopenNode driver for S32DS and supported devices +* Driver source: https://github.com/bubi-soft/CANopenNode/tree/S32_SDK_support/stack/S32_SDK +* Documentation: included in driver, example available +* CANopenNode version: v1.0, 2017-08-01 +* Status: seems to be stable +* Features: error counters, ? +* Development tools: [NXP S32 Design studio for Arm or Powerpc](https://www.nxp.com/design/software/development-software/s32-design-studio-ide:S32-DESIGN-STUDIO-IDE?&fsrch=1&sr=1&pageNum=1) +* Demo hardware: [S32K144EVB-Q100](https://www.nxp.com/design/development-boards/automotive-development-platforms/s32k-mcu-platforms/s32k144-evaluation-board:S32K144EVB), [DEVKIT-MPC5748G](https://www.nxp.com/design/development-boards/automotive-development-platforms/mpc57xx-mcu-platforms/mpc5748g-development-board-for-secure-gateway:DEVKIT-MPC5748G) +* Information updated 2020-03-02 + + +Other +----- +* ESP32: + * 2023-03-11: https://github.com/CANopenNode/CANopenNode/issues/429 + * 2020-07-14: https://github.com/CANopenNode/CANopenNode/issues/198#issuecomment-658429391 + * [CANopenNode_ESP32](https://github.com/sicrisembay/CANopenNode_ESP32) is a CANopenNode component for ESP-IDF framework. For ease of maintenance, this uses unmodified CANopenNode stack. For project examples, refer to [CANopenNode_ESP32_Test](https://github.com/sicrisembay/CANopenNode_ESP32_Test). +* [FreeRTOS](https://github.com/martinwag/CANopenNode/tree/neuberger-freertos/stack/neuberger-FreeRTOS) by Neuberger, 2020-06-23, based on v1.3-master, see also [issue 198](https://github.com/CANopenNode/CANopenNode/issues/198). +* [STM32CubeMX HAL](https://github.com/w1ne/CANOpenNode-CubeMX-HAL), 2019-05-03, demo project for Atollic studio, tested on Nucleo STM32L452xx board. +* K64F_FreeRTOS, Kinetis SDK, 2018-02-13, [zip file](https://github.com/CANopenNode/CANopenNode/pull/28#issuecomment-365392867) +* LPC1768 (MBED) (released in 2016) - [CANopenNode v1.0](https://github.com/CANopenNode/CANopenNode/tree/v1.0), [known example from 2016](https://github.com/exmachina-dev/CANopenMbe: d) + +Other old versions +------------------ +* RTOS eCos - [CANopenNode v1.0](https://github.com/CANopenNode/CANopenNode/tree/v1.0), ([released in 2013, old repo](http://sourceforge.net/p/canopennode/code_complete/)) +* Atmel SAM3X - [CANopenNode v1.0](https://github.com/CANopenNode/CANopenNode/tree/v1.0), ([released in old repo](http://sourceforge.net/p/canopennode/code_complete/)) +* ST STM32 - [CANopenNode v1.0](https://github.com/CANopenNode/CANopenNode/tree/v1.0), ([released in old repo](http://sourceforge.net/p/canopennode/code_complete/)) +* NXP LPC177x_8x - [CANopenNode v1.0](https://github.com/CANopenNode/CANopenNode/tree/v1.0), ([released in old repo](http://sourceforge.net/p/canopennode/code_complete/)) +* Freescale MCF5282 - [CANopenNode v1.0](https://github.com/CANopenNode/CANopenNode/tree/v1.0), ([released in old repo](http://sourceforge.net/p/canopennode/code_complete/)) +* BECK IPC Embedded Web-Controller SC243 ([last release 2015, old repo](http://sourceforge.net/p/canopennode/code_complete/)) +* Old Object_Dictionary_Editor, originally part of CANopenNode. It requires very old version of Firefox to run. Available on [Sourceforge](http://sourceforge.net/p/canopennode/code_complete/). +* AD ADSP-CM408 mixed signal controller Contributed by Analog devices, Inc. ([released in 2014](http://sourceforge.net/projects/canopennode-for-adsp-cm408f/)) +* Microchip PIC18F ([last release 2006](https://sourceforge.net/projects/canopennode/files/canopennode/)) diff --git a/doc/objectDictionary.md b/doc/objectDictionary.md new file mode 100644 index 00000000..77e02048 --- /dev/null +++ b/doc/objectDictionary.md @@ -0,0 +1,508 @@ +Object Dictionary +================= + +Definitions from CiA 301 +------------------------ +The **Object Dictionary** is a collection of all the data items which have an influence on the behavior of the application objects, the communication objects and the state machine used on this device. It serves as an interface between the communication and the application. +The object dictionary is essentially a grouping of objects accessible via the network in an ordered pre-defined fashion. Each object within the object dictionary is addressed using a 16-bit index and a 8-bit sub-index. + +A **SDO** (Service Data Object) is providing direct access to object entries of a CANopen device's object dictionary. As these object entries may contain data of arbitrary size and data type. SDOs may be used to transfer multiple data sets (each containing an arbitrary large block of data) from a client to a server and vice versa. The client shall control via a multiplexer (index and sub-index of the object dictionary) which data set shall be transferred. The content of the data set is defined within the object dictionary. + +A **PDO** (Process Data Object) is providing real-time data transfer of object entries of a CANopen device's object dictionary. The transfer of PDO is performed with no protocol overhead. The PDO correspond to objects in the object dictionary and provide the interface to the application objects. Data type and mapping of application objects into a PDO is determined by a corresponding PDO mapping structure within the object dictionary. + + +Operation +--------- +### Terms +The term **OD object** means object from object dictionary located at specific 16-bit index. There are different types of OD objects in CANopen: variables, arrays and records (structures). Each OD object contains pointer to actual data, data length(s) and attribute(s). See @ref OD_objectTypes_t. + +The term **OD variable** is basic variable of specified type. For example: int8_t, uint32_t, float64_t, ... or just sequence of binary data with known or unknown data length. Each OD variable resides in Object dictionary at specified 16-bit index and 8-bit sub-index. + +The term **OD entry** means structure element, which contains some basic properties of the OD object, indication of type of OD object and pointer to all necessary data for the OD object. An array of OD entries together with information about total number of OD entries represents object dictionary as defined inside CANopenNode. See @ref OD_entry_t and @ref OD_t. + +### Access +Application and the stack have access to OD objects via universal @ref OD_t object and @ref OD_find() function. No direct access to custom structures, which define object dictionary, is required. Properties for specific OD variable is fetched with @ref OD_getSub() function. Access to actual variable is via **read** and **write** functions. Pointer to those two functions is fetched by @ref OD_getSub(). See @ref OD_stream_t. See also shortcuts: @ref CO_ODgetSetters, for access to data of different type. + +Note that OD variables can be accessed from different threads. CANopenNode basically runs in two threads: fast real-time (PDO processing, etc.) and time non-critical mainline (SDO etc.). Both threads have access to OD variables, so care must be taken into account. CANopenNode uses locking mechanism, where SDO server prevents execution of the real-time thread at the moment it reads or writes OD variable. The same protection of the OD variables is necessary in @ref CO_storage. For more information see @ref CO_critical_sections in CO_driver.h. + +### Example usage +```c +extern OD_t *ODxyz; + +void myFunc(OD_t *od) { + ODR_t odRet; + OD_entry_t *entry; + OD_IO_t io1008; + char buf[50]; + OD_size_t bytesRd; + int error = 0; + + /* Init IO for "Manufacturer device name" at index 0x1008, sub-index 0x00 */ + entry = OD_find(od, 0x1008); + odRet = OD_getSub(entry, 0x00, &io1008, false); + /* Read with io1008, subindex = 0x00 */ + if (odRet == ODR_OK) { + /* Locking is necessary from mainline thread, but must not be used from + * timer interval (real-time) thread. Locking is not necessary in the + * CANoopen initialization section. Locking is also not necessary, if + * OD variable is not mappable to PDO and not accessed from RT thread. */ + CO_LOCK_OD(CANmodule); + odRet = io1008.read(&io1008.stream, &buf[0], sizeof(buf), &bytesRd); + CO_UNLOCK_OD(CANmodule); + } + if (odRet != ODR_OK) error++; + + /* Use helper and set "Producer heartbeat time" at index 0x1017, sub 0x00 */ + CO_LOCK_OD(CANmodule); /* may not be necessary, see comment above */ + odRet = OD_set_u16(OD_find(od, 0x1017), 0x00, 500, false); + CO_UNLOCK_OD(CANmodule); + if (odRet != ODR_OK) error++; +} +``` +There is no need to include ODxyt.h file, it is only necessary to know, we have ODxyz defined somewhere. + +Second example is simpler and use helper function to access OD variable. However it is not very efficient, because it goes through all search procedures. + +If access to the same variable is very frequent, it is better to use first example. After initialization, application has to remember only "io1008" object. Frequent reading of the variable is then very efficient. + +### Simple access to OD via globals +Some simple user applications can also access some OD variables directly via globals. + +@warning +If OD object has OD extension enabled, then direct access to its OD variables must not be used. Only valid access is via read or write or helper functions. + +```c +#include ODxyz.h + +void myFuncGlob(void) { + /* Direct address instead of OD_find() */ + OD_entry_t *entry_errReg = ODxyz_1001_errorRegister; + + /* Direct access to OD variable */ + uint32_t devType = ODxyz_0.x1000_deviceType; + ODxyz_0.x1018_identity.serialNumber = 0x12345678; +} +``` + + +Object Dictionary Example +------------------------- +Actual Object dictionary for one CANopen device is defined by pair of OD_xyz.h and ODxyz.c files. + +Suffix "xyz" is unique name of the object dictionary. If single default object dictionary is used, suffix is omitted. Such way configuration with multiple object dictionaries is possible. + +Data for OD definition are arranged inside multiple structures. Structures are different for different configuration of OD. Data objects, created with those structures, are constant or are variable. + +Actual OD variables are arranged inside multiple structures, so called storage groups. Selected groups can optionally be stored to non-volatile memory. + +@warning +Manual editing of ODxyz.h/.c files is very error-prone. + +Pair of ODxyz.h/.c files can be generated by OD editor tool. The tool can edit standard CANopen device description file in xml format. Xml file may include also some non-standard elements, specific to CANopenNode. Xml file is then used for automatic generation of ODxyz.h/.c files. + +### Example ODxyz.h file +```c +/* OD data declaration of all groups ******************************************/ +typedef struct { + uint32_t x1000_deviceType; + struct { + uint8_t maxSubIndex; + uint32_t vendorID; + uint32_t productCode; + uint32_t revisionNumber; + uint32_t serialNumber; + } x1018_identity; +} ODxyz_PERSIST_COMM_t; + +typedef struct { + uint8_t x1001_errorRegister; + uint8_t x1003_preDefinedErrorField_sub0; + uint32_t x1003_preDefinedErrorField[8]; +} ODxyz_RAM_t; + +extern ODxyz_PERSIST_COMM_t ODxyz_PERSIST_COMM; +extern ODxyz_RAM_t ODxyz_RAM; +extern OD_t *ODxyz; + +/* Object dictionary entries - shortcuts **************************************/ +#define ODxyz_ENTRY_H1000 &ODxyz->list[0] +#define ODxyz_ENTRY_H1001 &ODxyz->list[1] +#define ODxyz_ENTRY_H1003 &ODxyz->list[2] +#define ODxyz_ENTRY_H1018 &ODxyz->list[3] + +#define ODxyz_ENTRY_H1000_deviceType &ODxyz->list[0] +#define ODxyz_ENTRY_H1001_errorRegister &ODxyz->list[1] +#define ODxyz_ENTRY_H1003_preDefinedErrorField &ODxyz->list[2] +#define ODxyz_ENTRY_H1018_identity &ODxyz->list[3] +``` + +### Example ODxyz.c file +```c +#define OD_DEFINITION +#include "301/CO_ODinterface.h" +#include "ODxyz.h" + +/* OD data initialization of all groups ***************************************/ +ODxyz_PERSIST_COMM_t ODxyz_PERSIST_COMM = { + .x1000_deviceType = 0, + .x1018_identity = { + .maxSubIndex = 4, + .vendorID = 0, + .productCode = 0, + .revisionNumber = 0, + .serialNumber = 0 + } +}; + +ODxyz_RAM_t ODxyz_RAM = { + .x1001_errorRegister = 0, + .x1003_preDefinedErrorField_sub0 = 0, + .x1003_preDefinedErrorField = {0, 0, 0, 0, 0, 0, 0, 0} +}; + +/* All OD objects (constant) **************************************************/ +typedef struct { + OD_obj_var_t o_1000_deviceType; + OD_obj_var_t o_1001_errorRegister; + OD_obj_array_t o_1003_preDefinedErrorField; + OD_obj_record_t o_1018_identity[5]; +} ODxyzObjs_t; + +static CO_PROGMEM ODxyzObjs_t ODxyzObjs = { + .o_1000_deviceType = { + .dataOrig = &ODxyz_PERSIST_COMM.x1000_deviceType, + .attribute = ODA_SDO_R | ODA_MB, + .dataLength = 4 + }, + .o_1001_errorRegister = { + .dataOrig = &ODxyz_RAM.x1001_errorRegister, + .attribute = ODA_SDO_R, + .dataLength = 1 + }, + .o_1003_preDefinedErrorField = { + .dataOrig0 = &ODxyz_RAM.x1003_preDefinedErrorField_sub0, + .dataOrig = &ODxyz_RAM.x1003_preDefinedErrorField[0], + .attribute0 = ODA_SDO_RW, + .attribute = ODA_SDO_R | ODA_MB, + .dataElementLength = 4, + .dataElementSizeof = sizeof(uint32_t) + }, + .o_1018_identity = { + { + .data = &ODxyz_PERSIST_COMM.x1018_identity.maxSubIndex, + .subIndex = 0, + .attribute = ODA_SDO_R, + .dataLength = 1 + }, + { + .data = &ODxyz_PERSIST_COMM.x1018_identity.vendorID, + .subIndex = 1, + .attribute = ODA_SDO_R | ODA_MB, + .dataLength = 4 + }, + { + .data = &ODxyz_PERSIST_COMM.x1018_identity.productCode, + .subIndex = 2, + .attribute = ODA_SDO_R | ODA_MB, + .dataLength = 4 + }, + { + .data = &ODxyz_PERSIST_COMM.x1018_identity.revisionNumber, + .subIndex = 3, + .attribute = ODA_SDO_R | ODA_MB, + .dataLength = 4 + }, + { + .data = &ODxyz_PERSIST_COMM.x1018_identity.serialNumber, + .subIndex = 4, + .attribute = ODA_SDO_R | ODA_MB, + .dataLength = 4 + } + } +}; + +/* Object dictionary **********************************************************/ +static OD_entry_t ODxyzList[] = { + {0x1000, 0x01, ODT_VAR, &ODxyzObjs.o_1000_deviceType, NULL}, + {0x1001, 0x01, ODT_VAR, &ODxyzObjs.o_1001_errorRegister, NULL}, + {0x1003, 0x09, ODT_VAR, &ODxyzObjs.o_1003_preDefinedErrorField, NULL}, + {0x1018, 0x05, ODT_REC, &ODxyzObjs.o_1018_identity, NULL}, + {0x0000, 0x00, 0, NULL, NULL} +}; + +OD_t _ODxyz = { + (sizeof(ODxyzList) / sizeof(ODxyzList[0])) - 1, + &ODxyzList[0] +}; + +OD_t *ODxyz = &_ODxyz; +``` + + +XML Device Description +---------------------- +CANopen device description - XML schema definition - is specified by CiA 311 standard. + +CiA 311 complies with standard ISO 15745-1:2005/Amd1 (Industrial automation systems and integration - Open systems application integration framework). + +CANopen device description is basically a XML file with all the information about CANopen device. The larges part of the file is a list of all object dictionary variables with all necessary properties and documentation. This file can be edited with OD editor application and can be used as data source, from which Object dictionary for CANopenNode is generated. Furthermore, this file can be used with CANopen configuration tool, which interacts with CANopen devices on running CANopen network. + +XML schema definitions are available at: http://www.canopen.org/xml/1.1 One of the tools for viewing XML schemas and validating XDD project files is "xsddiagram" (https://github.com/dgis/xsddiagram). Command line alternative to XDD file validation against schema is: `xmlstarlet val --err --xsd 311/CANopen.xsd project_file.xdd` + +CANopen specifies also another type of files for CANopen device description. These are EDS files, which are in INI format. It is possible to convert between those two formats. But CANopenNode uses XML format. + +The device description file has "XDD" file extension. The name of this file shall contain the vendor-ID of the CANopen device in the form of 8 hexadecimal digits in any position of the name and separated with underscores. For example "name1_12345678_name2.XDD". + +CANopenNode includes multiple profile definition files, one for each CANopen object. Those files have "XPD" extension. They are in the same XML format as XDD files. The XML editor tool can use XPD files to insert prepared data into device description file (XDD), which is being edited. + +There are also device configuration files with "XDC" extension. They are describing a configured CANopen device and include additional elements, such as default value, denominator and device commissioning elements. Similar as "dcf" files in INI format. + +### XDD, XPD file example +```xml + + + + ... + + ... + ... + + ... + + + + ... + ... + + + + + + ... + ... + + + + + + + ... + ... + + + + + ... + ... + + + + + + ... + ... + + + + + + ... + ... + + + + + + ... + ... + + + + + + ... + ... + + + + + + + + + ... + + + + + + + + + + + + + + + ... + + + +``` + +### Parameter description +Above XML file example shows necessary data for OD interface used by CANopenNode and other parameters required by the standard. Standard specifies many other parameters, which are not used by CANopenNode for simplicity. + +XML file is divided into two parts: + 1. "ProfileBody_Device_CANopen" - more standardized information + 2. "ProfileBody_CommunicationNetwork_CANopen" - CANopen specific information + +Largest part of the XML file is definition of each OD object. All OD objects are listed in "CANopenObjectList", which resides in the second part of the XML file. Each "CANopenObject" and "CANopenSubObject" of the list contains a link to parameter ("uniqueIDRef"), which resides in the first part of the XML file. So data for each OD object is split between two parts of the XML file. + +#### <CANopenObject> + * "index" (required) - Object dictionary index + * "name" (required) - Name of the parameter + * "objectType" (required) - "7"=VAR, "8"=ARRAY, "9"=RECORD + * "subNumber" (required if "objectType" is "8" or "9") + * "PDOmapping" (optional if "objectType" is "7"): + * "no" - mapping not allowed + * "default" - not used, same as "optional" + * "optional" - mapping allowed to TPDO or RPDO + * "TPDO" - mapping allowed to TPDO + * "RPDO" mapping allowed to RPDO + * "uniqueIDRef" (required or see below) - Reference to <parameter> + +#### <CANopenSubObject> + * "subIndex" (required) - Object dictionary sub-index + * "name" (required) - Name of the parameter + * "objectType" (required, always "7") + * "PDOmapping" (optional, same as above) + * "uniqueIDRef" (required or see below) - Reference to <parameter> + +#### uniqueIDRef +This is required attribute from "CANopenObject" and "CANopenSubObject". It contains reference to <parameter> in "ProfileBody_Device_CANopen" section of the XML file. + +If "uniqueIDRef" attribute is not specified and "objectType" is 7(VAR), then "CANopenObject" or "CANopenSubObject" must contain additional attributes: + * "dataType" (required for VAR) - CANopen basic data type, see below + * "accessType" (required for VAR) - "ro", "wo", "rw" or "const" + * "defaultValue" (optional) - Default value for the variable. + +#### <parameter> + * "**uniqueID**" (required) + * "**access**" (required for VAR) - can be one of: + * "const" - same as "read" + * "read" - only read access with SDO or PDO + * "write" - only write access with SDO or PDO + * "readWrite" - read or write access with SDO or PDO + * "readWriteInput" - same as "readWrite" + * "readWriteOutput" - same as "readWrite" + * "noAccess" - object will be in object dictionary, but no access. + * <**label** lang="en">, <**description** lang="en"> (required) - several elements in multiple languages possible + * <**INT and similar**/> (required) - Basic or complex data type. Basic data type (for VAR) is specified in IEC 61131-3 (see below). If data type is complex (ARRAY or RECORD), then **<dataTypeIDRef>** must be specified and entry must be added in the **<dataTypeList>**. Such definition of complex data types is required by the standard, but it is not required by CANopenNode. + * <**defaultValue**> (optional for VAR) - Default value for the variable. If it is empty string, then data is not stored inside object dictionary. Application should provide own data via OD extension. + * <**property** name="..." value="..."> (optional) - Multiple elements may contain additional custom properties. CANopenNode uses custom properties with following names: + * "**CO_disabled**" (used for base OD entry) - Valid value is "false" (default) or "true". if OD entry is disabled, then it will not be present in generated Object Dictionary .h and .c files. + * "**CO_countLabel**" (used for base OD entry) - Valid value is any string without spaces. OD exporter will generate a macro for each different string. For example, if four OD objects have "CO_countLabel" set to "TPDO", then macro "#define ODxyz_CNT_TPDO 4" will be generated by OD exporter. "CO_countLabel" is not required for configuration with multiple object dictionaries (CO_MULTIPLE_OD), although may be useful. Default is "". + * "**CO_storageGroup**" (used for base OD entry) - group name (string) into which the C variable will belong. Variables from specific storage group may then be stored into non-volatile memory, automatically or by SDO command. Default is "RAM". Please note: if <defaultValue> is empty string at specific subindex, then no storage group will be used for that variable. + * "**CO_accessSRDO**" (used for each "VAR") - Valid values are: "tx", "rx", "trx", "no"(default). + * "**CO_stringLengthMin**" (used for each "VAR") - Minimum length of the string. Used with "VISIBLE_STRING" and "UNICODE_STRING". If CO_stringLengthMin is smaller than length of the string in <defaultValue>, then it is ignored. Byte length of unicode string is 2 * CO_stringLengthMin. If <defaultValue> is empty and CO_stringLengthMin is 0, then data is not stored inside object dictionary. Default is 0. + +#### Other elements +Other elements listed in the above XML example are required by the standard. There are also many other elements, not listed above. All those does not influence the CANopenNode object dictionary exporter. + + +### CANopen basic data types +| CANopenNode | IEC 61131-3 | CANopen | dataType | +| -------------- | -------------- | --------------- | -------- | +| bool_t | BOOL | BOOLEAN | 0x01 | +| int8_t | SINT, (CHAR) | INTEGER8 | 0x02 | +| int16_t | INT | INTEGER16 | 0x03 | +| int32_t | DINT | INTEGER32 | 0x04 | +| int64_t | LINT | INTEGER64 | 0x15 | +| uint8_t | USINT, (BYTE) | UNSIGNED8 | 0x05 | +| uint16_t | UINT, (WORD) | UNSIGNED16 | 0x06 | +| uint32_t | UDINT, (DWORD) | UNSIGNED32 | 0x07 | +| uint64_t | ULINT, (LWORD) | UNSIGNED64 | 0x1B | +| float32_t | REAL | REAL32 | 0x08 | +| float64_t | LREAL | REAL64 | 0x11 | +| - | (1) | INTEGER24 | 0x10 | +| - | (1) | INTEGER40 | 0x12 | +| - | (1) | INTEGER48 | 0x13 | +| - | (1) | INTEGER56 | 0x14 | +| - | (1) | UNSIGNED24 | 0x16 | +| - | (1) | UNSIGNED40 | 0x18 | +| - | (1) | UNSIGNED48 | 0x19 | +| - | (1) | UNSIGNED56 | 0x1A | +| char [] | STRING (2) | VISIBLE_STRING | 0x09 | +| uint8_t [] | BITSTRING (3) | OCTET_STRING | 0x0A | +| uint16_t [] | WSTRING (2) | UNICODE_STRING | 0x0B | +| - | (1) | TIME_OF_DAY | 0x0C | +| - | (1) | TIME_DIFFERENCE | 0x0D | +| app. specific | BITSTRING (4) | DOMAIN | 0x0F | + +(1) Data is translated into OCTET_STRING. + +(2) Additional property "CO_stringLengthMin" indicates the minimum length of the string in CANopenNode. Property is ignored, if strlen(defaultValue) is larger. Otherwise remaining data locations are filled with zeroes. Strings always have additional element on the end of the array with value=0, which is never written (string is always null terminated). All strings have additional attribute "ODA_STR" in object dictionary. Attribute enables SDO to transfer data of length, which corresponds to actual string length. (Actual size of data read or written by SDO may be smaller than data size of the OD variable.) VISIBLE_STRING may contain control characters and UTF-8 characters, which should work on most target systems (like printf("%s", ...)). So VISIBLE_STRING may be more usable for printing complete set of unicode characters than UNICODE_STRING. + +(3) Default value for BITSTRING (OCTET_STRING) is written as space separated, two hex digit bytes in little-endian format. + +(4) Default value for DOMAIN is stored as empty string. + + +Object Dictionary Requirements By CANopenNode +--------------------------------------------- +* **Used by** column indicates CANopenNode object or its part, which uses the OD object. It also indicates, if OD object is required or optional for actual configuration. For the configuration of the CANopenNode objects see [Stack configuration](301/CO_config.h). If CANopenNode object or its part is disabled in stack configuration, then OD object is not used. Note that OD objects: 1000, 1001 and 1017 and 1018 are mandatory for CANopen. +* **CO_countLabel** column indicates, which value must have property "CO_countLabel" inside OD object. + +| index | Description | Used by | CO_countLabel | +| ----- | ----------------------------- | ---------------------| ------------- | +| 1000 | Device type | CANopen, req | NMT | +| 1001 | Error register | CANopen, EM, req | EM | +| 1002 | Manufacturer status register | | | +| 1003 | Pre-defined error field | EM_HISTORY, opt | | +| 1005 | COB-ID SYNC message | SYNC, req | SYNC | +| 1006 | Communication cycle period | SYNC_PRODUCER, req | SYNC_PROD | +| 1007 | Synchronous window length | SYNC, opt | | +| 1008 | Manufacturer device name | | | +| 1009 | Manufacturer hardware version | | | +| 100A | Manufacturer software version | | | +| 100C | Guard time | Node guarding slave | | +| 100D | Life time factor | Node guarding slave | | +| 1010 | Store parameters | | STORAGE | +| 1011 | Restore default parameters | | | +| 1012 | COB-ID time stamp object | TIME, req | TIME | +| 1013 | High resolution time stamp | | | +| 1014 | COB-ID EMCY | EM_PRODUCER, req | EM_PROD | +| 1015 | Inhibit time EMCY | EM_PROD_INHIBIT, opt | | +| 1016 | Consumer heartbeat time | HB_CONS, req | HB_CONS | +| 1017 | Producer heartbeat time | CANopen, NMT, req | HB_PROD | +| 1018 | Identity object | CANopen, LSS_SL, req | | +| 1019 | Synch. counter overflow value | SYNC, opt | | +| 1020 | Verify configuration | | | +| 1021 | Store EDS | | | +| 1022 | Store format | | | +| 1023 | OS command | | | +| 1024 | OS command mode | | | +| 1025 | OS debugger interface | | | +| 1026 | OS prompt | | | +| 1027 | Module list | | | +| 1028 | Emergency consumer object | | | +| 1029 | Error behavior object | | | +| 1200 | SDO server parameter (first) | SDO optional | SDO_SRV | +| 1201+ | SDO server parameter | SDO+, req | SDO_SRV | +| 1280+ | SDO client parameter | SDO_CLI, req | SDO_CLI | +| 1300 | Global fail-safe command par | GFC, req | GFC | +| 1301+ | SRDO communication parameter | SRDO, req | SRDO | +| 1381+ | SRDO mapping parameter | SRDO, req | | +| 13FE | Configuration valid | SRDO, req | | +| 13FF | Safety configuration checksum | SRDO, req | | +| 1400+ | RPDO communication parameter | RPDO, req | RPDO | +| 1600+ | RPDO mapping parameter | RPDO, req | | +| 1800+ | TPDO communication parameter | TPDO, req | TPDO | +| 1A00+ | TPDO mapping parameter | TPDO, req | | +| 1FA0+ | Object scanner list | | | +| 1FD0+ | Object dispatching list | | | +| | Custom objects | | | +| any | Error status bits | EM_STATUS_BITS, opt | | +| any+ | Trace | TRACE, req | TRACE | diff --git a/doc/traceUsage.md b/doc/traceUsage.md new file mode 100644 index 00000000..ab3ecb63 --- /dev/null +++ b/doc/traceUsage.md @@ -0,0 +1,44 @@ +Trace usage +=========== + +**TRACE DOES NOT WORK IN THE LAST VERSION** + +CANopenNode includes optional trace functionality (non-standard). It monitors +choosen variables from Object Dictionary. On change of state of variable it +makes a record with timestamp into circular buffer. String with points can later +be read via SDO. + +Trace is disabled by default. It can be enabled using Object Dictionary editor. +Include also *CO_trace.h/.c* into project, compile and run. + +Here is en example of monitoring variable, connected with buttons +(OD_readInput8Bit, index 0x6000, subindex 0x01). It was tested on PIC32: + +``` +# Enable trace first: +./canopencomm 0x30 w 0x2400 0 u8 1 + +# Press and hold the button on Explorer16 and execute SDO read command: +./canopencomm 0x30 r 0x6000 1 u8 +[1] 0x08 +# It displays same value, as was transmitted via PDO and visible on candump. + +# Now get the complete history for that buttons with timestamp for each change +# and store it as a text to the file: +./canopencomm 0x30 r 0x2401 5 vs > plot1.csv +cat plot1.csv +``` +If large data blocks are transmitted via CAN bus, then more efficient SDO block +transfer can be enabled with command `./canopencomm set sdo_block 1` + +For more info on using trace functionality see CANopenNode/example/IO.html +file. There is also a description of all Object Dictionary variables. + +Trace functionality can also be configured on CANopenSocket directly. In that +case CANopenSocket must first receive PDO data from remote node(s) and store it +to the local Object Dictionary variable. CANopenSocket's trace then monitors +that variable. Text buffer is then read with the similar command as above. But +local SDO data access from CANopenSocket itself doesn't occupy CAN bus, so large +data is transfered realy fast. Besides that, Linux machine has much more RAM to +store the monitored data. Except timestamp is less accurate. + diff --git a/example/CO_OD.c b/example/CO_OD.c deleted file mode 100644 index 4422ae9f..00000000 --- a/example/CO_OD.c +++ /dev/null @@ -1,341 +0,0 @@ -/* - * CANopen Object Dictionary. - * - * This file was automatically generated with CANopenNode Object - * Dictionary Editor. DON'T EDIT THIS FILE MANUALLY !!!! - * Object Dictionary Editor is currently an older, but functional web - * application. For more info see See 'Object_Dictionary_Editor/about.html' in - * - * For more information on CANopen Object Dictionary see . - * - * @file CO_OD.c - * @author Janez Paternoster - * @copyright 2010 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include "CO_driver.h" -#include "CO_OD.h" -#include "CO_SDO.h" - - -/******************************************************************************* - DEFINITION AND INITIALIZATION OF OBJECT DICTIONARY VARIABLES -*******************************************************************************/ - -/***** Definition for RAM variables *******************************************/ -struct sCO_OD_RAM CO_OD_RAM = { - CO_OD_FIRST_LAST_WORD, - -/*1001*/ 0x0, -/*1002*/ 0x0L, -/*1003*/ {0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L}, -/*1010*/ {0x3L}, -/*1011*/ {0x1L}, -/*2100*/ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, -/*2103*/ 0x0, -/*2104*/ 0x0, -/*2107*/ {0x3E8, 0x0, 0x0, 0x0, 0x0}, -/*2108*/ {0}, -/*2109*/ {0}, -/*2110*/ {0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L}, -/*2120*/ {0x5, 0x1234567890ABCDEFLL, 0x234567890ABCDEF1LL, 12.345, 456.789, 0}, -/*2130*/ {0x3, {'-', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, 0, 0x0L}, -/*6000*/ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, -/*6200*/ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, -/*6401*/ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, -/*6411*/ {0, 0, 0, 0, 0, 0, 0, 0}, - - CO_OD_FIRST_LAST_WORD, -}; - - -/***** Definition for EEPROM variables ****************************************/ -struct sCO_OD_EEPROM CO_OD_EEPROM = { - CO_OD_FIRST_LAST_WORD, - -/*2106*/ 0x0L, -/*2112*/ {1L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L}, - - CO_OD_FIRST_LAST_WORD, -}; - - -/***** Definition for ROM variables *******************************************/ - struct sCO_OD_ROM CO_OD_ROM = { //constant variables, stored in flash - CO_OD_FIRST_LAST_WORD, - -/*1000*/ 0x0L, -/*1005*/ 0x80L, -/*1006*/ 0x0L, -/*1007*/ 0x0L, -/*1008*/ {'C', 'A', 'N', 'o', 'p', 'e', 'n', 'N', 'o', 'd', 'e'}, -/*1009*/ {'3', '.', '0', '0'}, -/*100A*/ {'3', '.', '0', '0'}, -/*1012*/ 0x80000100L, -/*1014*/ 0x80L, -/*1015*/ 0x64, -/*1016*/ {0x0L, 0x0L, 0x0L, 0x0L}, -/*1017*/ 0x3E8, -/*1018*/ {0x4, 0x0L, 0x0L, 0x0L, 0x0L}, -/*1019*/ 0x0, -/*1029*/ {0x0, 0x0, 0x1, 0x0, 0x0, 0x0}, -/*1200*/{{0x2, 0x600L, 0x580L}}, -/*1400*/{{0x2, 0x200L, 0xFF}, -/*1401*/ {0x2, 0x300L, 0xFE}, -/*1402*/ {0x2, 0x400L, 0xFE}, -/*1403*/ {0x2, 0x500L, 0xFE}}, -/*1600*/{{0x2, 0x62000108L, 0x62000208L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L}, -/*1601*/ {0x0, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L}, -/*1602*/ {0x0, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L}, -/*1603*/ {0x0, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L}}, -/*1800*/{{0x6, 0x180L, 0xFF, 0x64, 0x0, 0x0, 0x0}, -/*1801*/ {0x6, 0x280L, 0xFE, 0x0, 0x0, 0x0, 0x0}, -/*1802*/ {0x6, 0x380L, 0xFE, 0x0, 0x0, 0x0, 0x0}, -/*1803*/ {0x6, 0x480L, 0xFE, 0x0, 0x0, 0x0, 0x0}}, -/*1A00*/{{0x2, 0x60000108L, 0x60000208L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L}, -/*1A01*/ {0x0, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L}, -/*1A02*/ {0x0, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L}, -/*1A03*/ {0x0, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L}}, -/*1F80*/ 0x00000008L, -/*2101*/ 0x30, -/*2102*/ 0xFA, -/*2111*/ {1L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L}, - - CO_OD_FIRST_LAST_WORD -}; - - -/******************************************************************************* - STRUCTURES FOR RECORD TYPE OBJECTS -*******************************************************************************/ -/*0x1018*/ const CO_OD_entryRecord_t OD_record1018[5] = { - {(void*)&CO_OD_ROM.identity.maxSubIndex, 0x05, 1}, - {(void*)&CO_OD_ROM.identity.vendorID, 0x85, 4}, - {(void*)&CO_OD_ROM.identity.productCode, 0x85, 4}, - {(void*)&CO_OD_ROM.identity.revisionNumber, 0x85, 4}, - {(void*)&CO_OD_ROM.identity.serialNumber, 0x85, 4}}; -/*0x1200*/ const CO_OD_entryRecord_t OD_record1200[3] = { - {(void*)&CO_OD_ROM.SDOServerParameter[0].maxSubIndex, 0x05, 1}, - {(void*)&CO_OD_ROM.SDOServerParameter[0].COB_IDClientToServer, 0x85, 4}, - {(void*)&CO_OD_ROM.SDOServerParameter[0].COB_IDServerToClient, 0x85, 4}}; -/*0x1400*/ const CO_OD_entryRecord_t OD_record1400[3] = { - {(void*)&CO_OD_ROM.RPDOCommunicationParameter[0].maxSubIndex, 0x05, 1}, - {(void*)&CO_OD_ROM.RPDOCommunicationParameter[0].COB_IDUsedByRPDO, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOCommunicationParameter[0].transmissionType, 0x0D, 1}}; -/*0x1401*/ const CO_OD_entryRecord_t OD_record1401[3] = { - {(void*)&CO_OD_ROM.RPDOCommunicationParameter[1].maxSubIndex, 0x05, 1}, - {(void*)&CO_OD_ROM.RPDOCommunicationParameter[1].COB_IDUsedByRPDO, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOCommunicationParameter[1].transmissionType, 0x0D, 1}}; -/*0x1402*/ const CO_OD_entryRecord_t OD_record1402[3] = { - {(void*)&CO_OD_ROM.RPDOCommunicationParameter[2].maxSubIndex, 0x05, 1}, - {(void*)&CO_OD_ROM.RPDOCommunicationParameter[2].COB_IDUsedByRPDO, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOCommunicationParameter[2].transmissionType, 0x0D, 1}}; -/*0x1403*/ const CO_OD_entryRecord_t OD_record1403[3] = { - {(void*)&CO_OD_ROM.RPDOCommunicationParameter[3].maxSubIndex, 0x05, 1}, - {(void*)&CO_OD_ROM.RPDOCommunicationParameter[3].COB_IDUsedByRPDO, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOCommunicationParameter[3].transmissionType, 0x0D, 1}}; -/*0x1600*/ const CO_OD_entryRecord_t OD_record1600[9] = { - {(void*)&CO_OD_ROM.RPDOMappingParameter[0].numberOfMappedObjects, 0x0D, 1}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[0].mappedObject1, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[0].mappedObject2, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[0].mappedObject3, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[0].mappedObject4, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[0].mappedObject5, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[0].mappedObject6, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[0].mappedObject7, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[0].mappedObject8, 0x8D, 4}}; -/*0x1601*/ const CO_OD_entryRecord_t OD_record1601[9] = { - {(void*)&CO_OD_ROM.RPDOMappingParameter[1].numberOfMappedObjects, 0x0D, 1}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[1].mappedObject1, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[1].mappedObject2, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[1].mappedObject3, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[1].mappedObject4, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[1].mappedObject5, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[1].mappedObject6, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[1].mappedObject7, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[1].mappedObject8, 0x8D, 4}}; -/*0x1602*/ const CO_OD_entryRecord_t OD_record1602[9] = { - {(void*)&CO_OD_ROM.RPDOMappingParameter[2].numberOfMappedObjects, 0x0D, 1}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[2].mappedObject1, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[2].mappedObject2, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[2].mappedObject3, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[2].mappedObject4, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[2].mappedObject5, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[2].mappedObject6, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[2].mappedObject7, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[2].mappedObject8, 0x8D, 4}}; -/*0x1603*/ const CO_OD_entryRecord_t OD_record1603[9] = { - {(void*)&CO_OD_ROM.RPDOMappingParameter[3].numberOfMappedObjects, 0x0D, 1}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[3].mappedObject1, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[3].mappedObject2, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[3].mappedObject3, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[3].mappedObject4, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[3].mappedObject5, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[3].mappedObject6, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[3].mappedObject7, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[3].mappedObject8, 0x8D, 4}}; -/*0x1800*/ const CO_OD_entryRecord_t OD_record1800[7] = { - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[0].maxSubIndex, 0x05, 1}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[0].COB_IDUsedByTPDO, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[0].transmissionType, 0x0D, 1}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[0].inhibitTime, 0x8D, 2}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[0].compatibilityEntry, 0x0D, 1}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[0].eventTimer, 0x8D, 2}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[0].SYNCStartValue, 0x0D, 1}}; -/*0x1801*/ const CO_OD_entryRecord_t OD_record1801[7] = { - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[1].maxSubIndex, 0x05, 1}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[1].COB_IDUsedByTPDO, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[1].transmissionType, 0x0D, 1}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[1].inhibitTime, 0x8D, 2}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[1].compatibilityEntry, 0x0D, 1}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[1].eventTimer, 0x8D, 2}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[1].SYNCStartValue, 0x0D, 1}}; -/*0x1802*/ const CO_OD_entryRecord_t OD_record1802[7] = { - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[2].maxSubIndex, 0x05, 1}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[2].COB_IDUsedByTPDO, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[2].transmissionType, 0x0D, 1}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[2].inhibitTime, 0x8D, 2}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[2].compatibilityEntry, 0x0D, 1}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[2].eventTimer, 0x8D, 2}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[2].SYNCStartValue, 0x0D, 1}}; -/*0x1803*/ const CO_OD_entryRecord_t OD_record1803[7] = { - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[3].maxSubIndex, 0x05, 1}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[3].COB_IDUsedByTPDO, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[3].transmissionType, 0x0D, 1}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[3].inhibitTime, 0x8D, 2}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[3].compatibilityEntry, 0x0D, 1}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[3].eventTimer, 0x8D, 2}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[3].SYNCStartValue, 0x0D, 1}}; -/*0x1A00*/ const CO_OD_entryRecord_t OD_record1A00[9] = { - {(void*)&CO_OD_ROM.TPDOMappingParameter[0].numberOfMappedObjects, 0x0D, 1}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[0].mappedObject1, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[0].mappedObject2, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[0].mappedObject3, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[0].mappedObject4, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[0].mappedObject5, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[0].mappedObject6, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[0].mappedObject7, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[0].mappedObject8, 0x8D, 4}}; -/*0x1A01*/ const CO_OD_entryRecord_t OD_record1A01[9] = { - {(void*)&CO_OD_ROM.TPDOMappingParameter[1].numberOfMappedObjects, 0x0D, 1}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[1].mappedObject1, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[1].mappedObject2, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[1].mappedObject3, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[1].mappedObject4, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[1].mappedObject5, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[1].mappedObject6, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[1].mappedObject7, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[1].mappedObject8, 0x8D, 4}}; -/*0x1A02*/ const CO_OD_entryRecord_t OD_record1A02[9] = { - {(void*)&CO_OD_ROM.TPDOMappingParameter[2].numberOfMappedObjects, 0x0D, 1}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[2].mappedObject1, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[2].mappedObject2, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[2].mappedObject3, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[2].mappedObject4, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[2].mappedObject5, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[2].mappedObject6, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[2].mappedObject7, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[2].mappedObject8, 0x8D, 4}}; -/*0x1A03*/ const CO_OD_entryRecord_t OD_record1A03[9] = { - {(void*)&CO_OD_ROM.TPDOMappingParameter[3].numberOfMappedObjects, 0x0D, 1}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[3].mappedObject1, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[3].mappedObject2, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[3].mappedObject3, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[3].mappedObject4, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[3].mappedObject5, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[3].mappedObject6, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[3].mappedObject7, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[3].mappedObject8, 0x8D, 4}}; -/*0x2120*/ const CO_OD_entryRecord_t OD_record2120[6] = { - {(void*)&CO_OD_RAM.testVar.maxSubIndex, 0x06, 1}, - {(void*)&CO_OD_RAM.testVar.I64, 0xBE, 8}, - {(void*)&CO_OD_RAM.testVar.U64, 0xBE, 8}, - {(void*)&CO_OD_RAM.testVar.R32, 0xBE, 4}, - {(void*)&CO_OD_RAM.testVar.R64, 0xBE, 8}, - {0, 0x0E, 0}}; -/*0x2130*/ const CO_OD_entryRecord_t OD_record2130[4] = { - {(void*)&CO_OD_RAM.time.maxSubIndex, 0x06, 1}, - {(void*)&CO_OD_RAM.time.string[0], 0x06, 30}, - {(void*)&CO_OD_RAM.time.epochTimeBaseMs, 0x8E, 8}, - {(void*)&CO_OD_RAM.time.epochTimeOffsetMs, 0xBE, 4}}; - - -/******************************************************************************* - OBJECT DICTIONARY -*******************************************************************************/ -const CO_OD_entry_t CO_OD[CO_OD_NoOfElements] = { -{0x1000, 0x00, 0x85, 4, (void*)&CO_OD_ROM.deviceType}, -{0x1001, 0x00, 0x36, 1, (void*)&CO_OD_RAM.errorRegister}, -{0x1002, 0x00, 0xB6, 4, (void*)&CO_OD_RAM.manufacturerStatusRegister}, -{0x1003, 0x08, 0x8E, 4, (void*)&CO_OD_RAM.preDefinedErrorField[0]}, -{0x1005, 0x00, 0x8D, 4, (void*)&CO_OD_ROM.COB_ID_SYNCMessage}, -{0x1006, 0x00, 0x8D, 4, (void*)&CO_OD_ROM.communicationCyclePeriod}, -{0x1007, 0x00, 0x8D, 4, (void*)&CO_OD_ROM.synchronousWindowLength}, -{0x1008, 0x00, 0x05, 11, (void*)&CO_OD_ROM.manufacturerDeviceName[0]}, -{0x1009, 0x00, 0x05, 4, (void*)&CO_OD_ROM.manufacturerHardwareVersion[0]}, -{0x100A, 0x00, 0x05, 4, (void*)&CO_OD_ROM.manufacturerSoftwareVersion[0]}, -{0x1010, 0x01, 0x8E, 4, (void*)&CO_OD_RAM.storeParameters[0]}, -{0x1011, 0x01, 0x8E, 4, (void*)&CO_OD_RAM.restoreDefaultParameters[0]}, -{0x1012, 0x00, 0x85, 4, (void*)&CO_OD_ROM.COB_ID_TIME}, -{0x1014, 0x00, 0x85, 4, (void*)&CO_OD_ROM.COB_ID_EMCY}, -{0x1015, 0x00, 0x8D, 2, (void*)&CO_OD_ROM.inhibitTimeEMCY}, -{0x1016, 0x04, 0x8D, 4, (void*)&CO_OD_ROM.consumerHeartbeatTime[0]}, -{0x1017, 0x00, 0x8D, 2, (void*)&CO_OD_ROM.producerHeartbeatTime}, -{0x1018, 0x04, 0x00, 0, (void*)&OD_record1018}, -{0x1019, 0x00, 0x0D, 1, (void*)&CO_OD_ROM.synchronousCounterOverflowValue}, -{0x1029, 0x06, 0x0D, 1, (void*)&CO_OD_ROM.errorBehavior[0]}, -{0x1200, 0x02, 0x00, 0, (void*)&OD_record1200}, -{0x1400, 0x02, 0x00, 0, (void*)&OD_record1400}, -{0x1401, 0x02, 0x00, 0, (void*)&OD_record1401}, -{0x1402, 0x02, 0x00, 0, (void*)&OD_record1402}, -{0x1403, 0x02, 0x00, 0, (void*)&OD_record1403}, -{0x1600, 0x08, 0x00, 0, (void*)&OD_record1600}, -{0x1601, 0x08, 0x00, 0, (void*)&OD_record1601}, -{0x1602, 0x08, 0x00, 0, (void*)&OD_record1602}, -{0x1603, 0x08, 0x00, 0, (void*)&OD_record1603}, -{0x1800, 0x06, 0x00, 0, (void*)&OD_record1800}, -{0x1801, 0x06, 0x00, 0, (void*)&OD_record1801}, -{0x1802, 0x06, 0x00, 0, (void*)&OD_record1802}, -{0x1803, 0x06, 0x00, 0, (void*)&OD_record1803}, -{0x1A00, 0x08, 0x00, 0, (void*)&OD_record1A00}, -{0x1A01, 0x08, 0x00, 0, (void*)&OD_record1A01}, -{0x1A02, 0x08, 0x00, 0, (void*)&OD_record1A02}, -{0x1A03, 0x08, 0x00, 0, (void*)&OD_record1A03}, -{0x1F80, 0x00, 0x8D, 4, (void*)&CO_OD_ROM.NMTStartup}, -{0x2100, 0x00, 0x36, 10, (void*)&CO_OD_RAM.errorStatusBits[0]}, -{0x2101, 0x00, 0x0D, 1, (void*)&CO_OD_ROM.CANNodeID}, -{0x2102, 0x00, 0x8D, 2, (void*)&CO_OD_ROM.CANBitRate}, -{0x2103, 0x00, 0x8E, 2, (void*)&CO_OD_RAM.SYNCCounter}, -{0x2104, 0x00, 0x86, 2, (void*)&CO_OD_RAM.SYNCTime}, -{0x2106, 0x00, 0x87, 4, (void*)&CO_OD_EEPROM.powerOnCounter}, -{0x2107, 0x05, 0xBE, 2, (void*)&CO_OD_RAM.performance[0]}, -{0x2108, 0x01, 0xB6, 2, (void*)&CO_OD_RAM.temperature[0]}, -{0x2109, 0x01, 0xB6, 2, (void*)&CO_OD_RAM.voltage[0]}, -{0x2110, 0x10, 0xFE, 4, (void*)&CO_OD_RAM.variableInt32[0]}, -{0x2111, 0x10, 0xFD, 4, (void*)&CO_OD_ROM.variableROMInt32[0]}, -{0x2112, 0x10, 0xFF, 4, (void*)&CO_OD_EEPROM.variableNVInt32[0]}, -{0x2120, 0x05, 0x00, 0, (void*)&OD_record2120}, -{0x2130, 0x03, 0x00, 0, (void*)&OD_record2130}, -{0x6000, 0x08, 0x76, 1, (void*)&CO_OD_RAM.readInput8Bit[0]}, -{0x6200, 0x08, 0x3E, 1, (void*)&CO_OD_RAM.writeOutput8Bit[0]}, -{0x6401, 0x0C, 0xB6, 2, (void*)&CO_OD_RAM.readAnalogueInput16Bit[0]}, -{0x6411, 0x08, 0xBE, 2, (void*)&CO_OD_RAM.writeAnalogueOutput16Bit[0]}, -}; - diff --git a/example/CO_OD.h b/example/CO_OD.h deleted file mode 100644 index c22264bb..00000000 --- a/example/CO_OD.h +++ /dev/null @@ -1,438 +0,0 @@ -/* - * CANopen Object Dictionary. - * - * This file was automatically generated with CANopenNode Object - * Dictionary Editor. DON'T EDIT THIS FILE MANUALLY !!!! - * Object Dictionary Editor is currently an older, but functional web - * application. For more info see See 'Object_Dictionary_Editor/about.html' in - * - * For more information on CANopen Object Dictionary see . - * - * @file CO_OD.h - * @author Janez Paternoster - * @copyright 2010 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#ifndef CO_OD_H -#define CO_OD_H - - -/******************************************************************************* - CANopen DATA DYPES -*******************************************************************************/ - typedef uint8_t UNSIGNED8; - typedef uint16_t UNSIGNED16; - typedef uint32_t UNSIGNED32; - typedef uint64_t UNSIGNED64; - typedef int8_t INTEGER8; - typedef int16_t INTEGER16; - typedef int32_t INTEGER32; - typedef int64_t INTEGER64; - typedef float32_t REAL32; - typedef float64_t REAL64; - typedef char_t VISIBLE_STRING; - typedef oChar_t OCTET_STRING; - typedef domain_t DOMAIN; - - #ifndef timeOfDay_t - typedef union { - unsigned long long ullValue; - struct { - unsigned long ms:28; - unsigned reserved:4; - unsigned days:16; - unsigned reserved2:16; - }; - }timeOfDay_t; -#endif - - typedef timeOfDay_t TIME_OF_DAY; - typedef timeOfDay_t TIME_DIFFERENCE; - - -/******************************************************************************* - FILE INFO: - FileName: IO Example - FileVersion: - - CreationTime: 18:04:29 - CreationDate: 2016-03-25 - CreatedBy: JP -*******************************************************************************/ - - -/******************************************************************************* - DEVICE INFO: - VendorName: CANopenNode - VendorNumber: 0 - ProductName: CANopenNode - ProductNumber: 0 -*******************************************************************************/ - - -/******************************************************************************* - FEATURES -*******************************************************************************/ - #define CO_NO_SYNC 1 //Associated objects: 1005, 1006, 1007, 2103, 2104 - #define CO_NO_TIME 1 //Associated objects: 1012-1013 - #define CO_NO_EMERGENCY 1 //Associated objects: 1014, 1015 - #define CO_NO_SDO_SERVER 1 //Associated objects: 1200 - #define CO_NO_SDO_CLIENT 0 - #define CO_NO_RPDO 4 //Associated objects: 1400, 1401, 1402, 1403, 1600, 1601, 1602, 1603 - #define CO_NO_TPDO 4 //Associated objects: 1800, 1801, 1802, 1803, 1A00, 1A01, 1A02, 1A03 - #define CO_NO_NMT_MASTER 0 - #define CO_NO_TRACE 0 - #define CO_NO_LSS_SERVER 0 - #define CO_NO_LSS_CLIENT 0 - -/******************************************************************************* - OBJECT DICTIONARY -*******************************************************************************/ - #define CO_OD_NoOfElements 56 - - -/******************************************************************************* - TYPE DEFINITIONS FOR RECORDS -*******************************************************************************/ -/*1018 */ typedef struct{ - UNSIGNED8 maxSubIndex; - UNSIGNED32 vendorID; - UNSIGNED32 productCode; - UNSIGNED32 revisionNumber; - UNSIGNED32 serialNumber; - } OD_identity_t; - -/*1200[1] */ typedef struct{ - UNSIGNED8 maxSubIndex; - UNSIGNED32 COB_IDClientToServer; - UNSIGNED32 COB_IDServerToClient; - } OD_SDOServerParameter_t; - -/*1400[4] */ typedef struct{ - UNSIGNED8 maxSubIndex; - UNSIGNED32 COB_IDUsedByRPDO; - UNSIGNED8 transmissionType; - } OD_RPDOCommunicationParameter_t; - -/*1600[4] */ typedef struct{ - UNSIGNED8 numberOfMappedObjects; - UNSIGNED32 mappedObject1; - UNSIGNED32 mappedObject2; - UNSIGNED32 mappedObject3; - UNSIGNED32 mappedObject4; - UNSIGNED32 mappedObject5; - UNSIGNED32 mappedObject6; - UNSIGNED32 mappedObject7; - UNSIGNED32 mappedObject8; - } OD_RPDOMappingParameter_t; - -/*1800[4] */ typedef struct{ - UNSIGNED8 maxSubIndex; - UNSIGNED32 COB_IDUsedByTPDO; - UNSIGNED8 transmissionType; - UNSIGNED16 inhibitTime; - UNSIGNED8 compatibilityEntry; - UNSIGNED16 eventTimer; - UNSIGNED8 SYNCStartValue; - } OD_TPDOCommunicationParameter_t; - -/*1A00[4] */ typedef struct{ - UNSIGNED8 numberOfMappedObjects; - UNSIGNED32 mappedObject1; - UNSIGNED32 mappedObject2; - UNSIGNED32 mappedObject3; - UNSIGNED32 mappedObject4; - UNSIGNED32 mappedObject5; - UNSIGNED32 mappedObject6; - UNSIGNED32 mappedObject7; - UNSIGNED32 mappedObject8; - } OD_TPDOMappingParameter_t; - -/*2120 */ typedef struct{ - UNSIGNED8 maxSubIndex; - INTEGER64 I64; - UNSIGNED64 U64; - REAL32 R32; - REAL64 R64; - DOMAIN domain; - } OD_testVar_t; - -/*2130 */ typedef struct{ - UNSIGNED8 maxSubIndex; - VISIBLE_STRING string[30]; - UNSIGNED64 epochTimeBaseMs; - UNSIGNED32 epochTimeOffsetMs; - } OD_time_t; - - -/******************************************************************************* - STRUCTURES FOR VARIABLES IN DIFFERENT MEMORY LOCATIONS -*******************************************************************************/ -#define CO_OD_FIRST_LAST_WORD 0x55 //Any value from 0x01 to 0xFE. If changed, EEPROM will be reinitialized. - -/***** Structure for RAM variables ********************************************/ -struct sCO_OD_RAM{ - UNSIGNED32 FirstWord; - -/*1001 */ UNSIGNED8 errorRegister; -/*1002 */ UNSIGNED32 manufacturerStatusRegister; -/*1003 */ UNSIGNED32 preDefinedErrorField[8]; -/*1010 */ UNSIGNED32 storeParameters[1]; -/*1011 */ UNSIGNED32 restoreDefaultParameters[1]; -/*2100 */ OCTET_STRING errorStatusBits[10]; -/*2103 */ UNSIGNED16 SYNCCounter; -/*2104 */ UNSIGNED16 SYNCTime; -/*2107 */ UNSIGNED16 performance[5]; -/*2108 */ INTEGER16 temperature[1]; -/*2109 */ INTEGER16 voltage[1]; -/*2110 */ INTEGER32 variableInt32[16]; -/*2120 */ OD_testVar_t testVar; -/*2130 */ OD_time_t time; -/*6000 */ UNSIGNED8 readInput8Bit[8]; -/*6200 */ UNSIGNED8 writeOutput8Bit[8]; -/*6401 */ INTEGER16 readAnalogueInput16Bit[12]; -/*6411 */ INTEGER16 writeAnalogueOutput16Bit[8]; - - UNSIGNED32 LastWord; -}; - -/***** Structure for EEPROM variables *****************************************/ -struct sCO_OD_EEPROM{ - UNSIGNED32 FirstWord; - -/*2106 */ UNSIGNED32 powerOnCounter; -/*2112 */ INTEGER32 variableNVInt32[16]; - - UNSIGNED32 LastWord; -}; - - -/***** Structure for ROM variables ********************************************/ -struct sCO_OD_ROM{ - UNSIGNED32 FirstWord; - -/*1000 */ UNSIGNED32 deviceType; -/*1005 */ UNSIGNED32 COB_ID_SYNCMessage; -/*1006 */ UNSIGNED32 communicationCyclePeriod; -/*1007 */ UNSIGNED32 synchronousWindowLength; -/*1008 */ VISIBLE_STRING manufacturerDeviceName[11]; -/*1009 */ VISIBLE_STRING manufacturerHardwareVersion[4]; -/*100A */ VISIBLE_STRING manufacturerSoftwareVersion[4]; -/*1012 */ UNSIGNED32 COB_ID_TIME; -/*1014 */ UNSIGNED32 COB_ID_EMCY; -/*1015 */ UNSIGNED16 inhibitTimeEMCY; -/*1016 */ UNSIGNED32 consumerHeartbeatTime[4]; -/*1017 */ UNSIGNED16 producerHeartbeatTime; -/*1018 */ OD_identity_t identity; -/*1019 */ UNSIGNED8 synchronousCounterOverflowValue; -/*1029 */ UNSIGNED8 errorBehavior[6]; -/*1200[1] */ OD_SDOServerParameter_t SDOServerParameter[1]; -/*1400[4] */ OD_RPDOCommunicationParameter_t RPDOCommunicationParameter[4]; -/*1600[4] */ OD_RPDOMappingParameter_t RPDOMappingParameter[4]; -/*1800[4] */ OD_TPDOCommunicationParameter_t TPDOCommunicationParameter[4]; -/*1A00[4] */ OD_TPDOMappingParameter_t TPDOMappingParameter[4]; -/*1F80 */ UNSIGNED32 NMTStartup; -/*2101 */ UNSIGNED8 CANNodeID; -/*2102 */ UNSIGNED16 CANBitRate; -/*2111 */ INTEGER32 variableROMInt32[16]; - - UNSIGNED32 LastWord; -}; - - -/***** Declaration of Object Dictionary variables *****************************/ -extern struct sCO_OD_RAM CO_OD_RAM; - -extern struct sCO_OD_EEPROM CO_OD_EEPROM; - -extern struct sCO_OD_ROM CO_OD_ROM; - - -/******************************************************************************* - ALIASES FOR OBJECT DICTIONARY VARIABLES -*******************************************************************************/ -/*1000, Data Type: UNSIGNED32 */ - #define OD_deviceType CO_OD_ROM.deviceType - -/*1001, Data Type: UNSIGNED8 */ - #define OD_errorRegister CO_OD_RAM.errorRegister - -/*1002, Data Type: UNSIGNED32 */ - #define OD_manufacturerStatusRegister CO_OD_RAM.manufacturerStatusRegister - -/*1003, Data Type: UNSIGNED32, Array[8] */ - #define OD_preDefinedErrorField CO_OD_RAM.preDefinedErrorField - #define ODL_preDefinedErrorField_arrayLength 8 - -/*1005, Data Type: UNSIGNED32 */ - #define OD_COB_ID_SYNCMessage CO_OD_ROM.COB_ID_SYNCMessage - -/*1006, Data Type: UNSIGNED32 */ - #define OD_communicationCyclePeriod CO_OD_ROM.communicationCyclePeriod - -/*1007, Data Type: UNSIGNED32 */ - #define OD_synchronousWindowLength CO_OD_ROM.synchronousWindowLength - -/*1008, Data Type: VISIBLE_STRING, Array[11] */ - #define OD_manufacturerDeviceName CO_OD_ROM.manufacturerDeviceName - #define ODL_manufacturerDeviceName_stringLength 11 - -/*1009, Data Type: VISIBLE_STRING, Array[4] */ - #define OD_manufacturerHardwareVersion CO_OD_ROM.manufacturerHardwareVersion - #define ODL_manufacturerHardwareVersion_stringLength 4 - -/*100A, Data Type: VISIBLE_STRING, Array[4] */ - #define OD_manufacturerSoftwareVersion CO_OD_ROM.manufacturerSoftwareVersion - #define ODL_manufacturerSoftwareVersion_stringLength 4 - -/*1010, Data Type: UNSIGNED32, Array[1] */ - #define OD_storeParameters CO_OD_RAM.storeParameters - #define ODL_storeParameters_arrayLength 1 - #define ODA_storeParameters_saveAllParameters 0 - -/*1011, Data Type: UNSIGNED32, Array[1] */ - #define OD_restoreDefaultParameters CO_OD_RAM.restoreDefaultParameters - #define ODL_restoreDefaultParameters_arrayLength 1 - #define ODA_restoreDefaultParameters_restoreAllDefaultParameters 0 - -/*1012, Data Type: UNSIGNED32 */ - #define OD_COB_ID_TIME CO_OD_ROM.COB_ID_TIME - -/*1014, Data Type: UNSIGNED32 */ - #define OD_COB_ID_EMCY CO_OD_ROM.COB_ID_EMCY - -/*1015, Data Type: UNSIGNED16 */ - #define OD_inhibitTimeEMCY CO_OD_ROM.inhibitTimeEMCY - -/*1016, Data Type: UNSIGNED32, Array[4] */ - #define OD_consumerHeartbeatTime CO_OD_ROM.consumerHeartbeatTime - #define ODL_consumerHeartbeatTime_arrayLength 4 - -/*1017, Data Type: UNSIGNED16 */ - #define OD_producerHeartbeatTime CO_OD_ROM.producerHeartbeatTime - -/*1018, Data Type: OD_identity_t */ - #define OD_identity CO_OD_ROM.identity - -/*1019, Data Type: UNSIGNED8 */ - #define OD_synchronousCounterOverflowValue CO_OD_ROM.synchronousCounterOverflowValue - -/*1029, Data Type: UNSIGNED8, Array[6] */ - #define OD_errorBehavior CO_OD_ROM.errorBehavior - #define ODL_errorBehavior_arrayLength 6 - #define ODA_errorBehavior_communication 0 - #define ODA_errorBehavior_communicationOther 1 - #define ODA_errorBehavior_communicationPassive 2 - #define ODA_errorBehavior_generic 3 - #define ODA_errorBehavior_deviceProfile 4 - #define ODA_errorBehavior_manufacturerSpecific 5 - -/*1200[1], Data Type: OD_SDOServerParameter_t, Array[1] */ - #define OD_SDOServerParameter CO_OD_ROM.SDOServerParameter - -/*1400[4], Data Type: OD_RPDOCommunicationParameter_t, Array[4] */ - #define OD_RPDOCommunicationParameter CO_OD_ROM.RPDOCommunicationParameter - -/*1600[4], Data Type: OD_RPDOMappingParameter_t, Array[4] */ - #define OD_RPDOMappingParameter CO_OD_ROM.RPDOMappingParameter - -/*1800[4], Data Type: OD_TPDOCommunicationParameter_t, Array[4] */ - #define OD_TPDOCommunicationParameter CO_OD_ROM.TPDOCommunicationParameter - -/*1A00[4], Data Type: OD_TPDOMappingParameter_t, Array[4] */ - #define OD_TPDOMappingParameter CO_OD_ROM.TPDOMappingParameter - -/*1F80, Data Type: UNSIGNED32 */ - #define OD_NMTStartup CO_OD_ROM.NMTStartup - -/*2100, Data Type: OCTET_STRING, Array[10] */ - #define OD_errorStatusBits CO_OD_RAM.errorStatusBits - #define ODL_errorStatusBits_stringLength 10 - -/*2101, Data Type: UNSIGNED8 */ - #define OD_CANNodeID CO_OD_ROM.CANNodeID - -/*2102, Data Type: UNSIGNED16 */ - #define OD_CANBitRate CO_OD_ROM.CANBitRate - -/*2103, Data Type: UNSIGNED16 */ - #define OD_SYNCCounter CO_OD_RAM.SYNCCounter - -/*2104, Data Type: UNSIGNED16 */ - #define OD_SYNCTime CO_OD_RAM.SYNCTime - -/*2106, Data Type: UNSIGNED32 */ - #define OD_powerOnCounter CO_OD_EEPROM.powerOnCounter - -/*2107, Data Type: UNSIGNED16, Array[5] */ - #define OD_performance CO_OD_RAM.performance - #define ODL_performance_arrayLength 5 - #define ODA_performance_cyclesPerSecond 0 - #define ODA_performance_timerCycleTime 1 - #define ODA_performance_timerCycleMaxTime 2 - #define ODA_performance_mainCycleTime 3 - #define ODA_performance_mainCycleMaxTime 4 - -/*2108, Data Type: INTEGER16, Array[1] */ - #define OD_temperature CO_OD_RAM.temperature - #define ODL_temperature_arrayLength 1 - #define ODA_temperature_mainPCB 0 - -/*2109, Data Type: INTEGER16, Array[1] */ - #define OD_voltage CO_OD_RAM.voltage - #define ODL_voltage_arrayLength 1 - #define ODA_voltage_mainPCBSupply 0 - -/*2110, Data Type: INTEGER32, Array[16] */ - #define OD_variableInt32 CO_OD_RAM.variableInt32 - #define ODL_variableInt32_arrayLength 16 - -/*2111, Data Type: INTEGER32, Array[16] */ - #define OD_variableROMInt32 CO_OD_ROM.variableROMInt32 - #define ODL_variableROMInt32_arrayLength 16 - -/*2112, Data Type: INTEGER32, Array[16] */ - #define OD_variableNVInt32 CO_OD_EEPROM.variableNVInt32 - #define ODL_variableNVInt32_arrayLength 16 - -/*2120, Data Type: OD_testVar_t */ - #define OD_testVar CO_OD_RAM.testVar - -/*2130, Data Type: OD_time_t */ - #define OD_time CO_OD_RAM.time - -/*6000, Data Type: UNSIGNED8, Array[8] */ - #define OD_readInput8Bit CO_OD_RAM.readInput8Bit - #define ODL_readInput8Bit_arrayLength 8 - -/*6200, Data Type: UNSIGNED8, Array[8] */ - #define OD_writeOutput8Bit CO_OD_RAM.writeOutput8Bit - #define ODL_writeOutput8Bit_arrayLength 8 - -/*6401, Data Type: INTEGER16, Array[12] */ - #define OD_readAnalogueInput16Bit CO_OD_RAM.readAnalogueInput16Bit - #define ODL_readAnalogueInput16Bit_arrayLength 12 - -/*6411, Data Type: INTEGER16, Array[8] */ - #define OD_writeAnalogueOutput16Bit CO_OD_RAM.writeAnalogueOutput16Bit - #define ODL_writeAnalogueOutput16Bit_arrayLength 8 - - -#endif - diff --git a/example/CO_OD_with_trace/CO_OD.c b/example/CO_OD_with_trace/CO_OD.c deleted file mode 100644 index 2ea6b490..00000000 --- a/example/CO_OD_with_trace/CO_OD.c +++ /dev/null @@ -1,385 +0,0 @@ -/* - * CANopen Object Dictionary. - * - * This file was automatically generated with CANopenNode Object - * Dictionary Editor. DON'T EDIT THIS FILE MANUALLY !!!! - * Object Dictionary Editor is currently an older, but functional web - * application. For more info see See 'Object_Dictionary_Editor/about.html' in - * - * For more information on CANopen Object Dictionary see . - * - * @file CO_OD.c - * @author Janez Paternoster - * @copyright 2010 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include "CO_driver.h" -#include "CO_OD.h" -#include "CO_SDO.h" - - -/******************************************************************************* - DEFINITION AND INITIALIZATION OF OBJECT DICTIONARY VARIABLES -*******************************************************************************/ - -/***** Definition for RAM variables *******************************************/ -struct sCO_OD_RAM CO_OD_RAM = { - CO_OD_FIRST_LAST_WORD, - -/*1001*/ 0x0, -/*1002*/ 0x0L, -/*1003*/ {0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L}, -/*1010*/ {0x3L}, -/*1011*/ {0x1L}, -/*2100*/ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, -/*2103*/ 0x0, -/*2104*/ 0x0, -/*2107*/ {0x3E8, 0x0, 0x0, 0x0, 0x0}, -/*2108*/ {0}, -/*2109*/ {0}, -/*2110*/ {0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L}, -/*2120*/ {0x5, 0x1234567890ABCDEFLL, 0x234567890ABCDEF1LL, 12.345, 456.789, 0}, -/*2130*/ {0x3, {'-', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, 0, 0x0L}, -/*2400*/ 0x0, -/*2401*/{{0x6, 0x0L, 0L, 0L, 0L, 0, 0x0L}, -/*2402*/ {0x6, 0x0L, 0L, 0L, 0L, 0, 0x0L}}, -/*6000*/ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, -/*6200*/ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, -/*6401*/ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, -/*6411*/ {0, 0, 0, 0, 0, 0, 0, 0}, - - CO_OD_FIRST_LAST_WORD, -}; - - -/***** Definition for EEPROM variables ****************************************/ -struct sCO_OD_EEPROM CO_OD_EEPROM = { - CO_OD_FIRST_LAST_WORD, - -/*2106*/ 0x0L, -/*2112*/ {1L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L}, - - CO_OD_FIRST_LAST_WORD, -}; - - -/***** Definition for ROM variables *******************************************/ - struct sCO_OD_ROM CO_OD_ROM = { //constant variables, stored in flash - CO_OD_FIRST_LAST_WORD, - -/*1000*/ 0x0L, -/*1005*/ 0x80L, -/*1006*/ 0x0L, -/*1007*/ 0x0L, -/*1008*/ {'C', 'A', 'N', 'o', 'p', 'e', 'n', 'N', 'o', 'd', 'e'}, -/*1009*/ {'3', '.', '0', '0'}, -/*100A*/ {'3', '.', '0', '0'}, -/*1014*/ 0x80L, -/*1015*/ 0x64, -/*1016*/ {0x0L, 0x0L, 0x0L, 0x0L}, -/*1017*/ 0x3E8, -/*1018*/ {0x4, 0x0L, 0x0L, 0x0L, 0x0L}, -/*1019*/ 0x0, -/*1029*/ {0x0, 0x0, 0x1, 0x0, 0x0, 0x0}, -/*1200*/{{0x2, 0x600L, 0x580L}}, -/*1400*/{{0x2, 0x200L, 0xFF}, -/*1401*/ {0x2, 0x300L, 0xFE}, -/*1402*/ {0x2, 0x400L, 0xFE}, -/*1403*/ {0x2, 0x500L, 0xFE}}, -/*1600*/{{0x2, 0x62000108L, 0x62000208L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L}, -/*1601*/ {0x0, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L}, -/*1602*/ {0x0, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L}, -/*1603*/ {0x0, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L}}, -/*1800*/{{0x6, 0x180L, 0xFF, 0x64, 0x0, 0x0, 0x0}, -/*1801*/ {0x6, 0x280L, 0xFE, 0x0, 0x0, 0x0, 0x0}, -/*1802*/ {0x6, 0x380L, 0xFE, 0x0, 0x0, 0x0, 0x0}, -/*1803*/ {0x6, 0x480L, 0xFE, 0x0, 0x0, 0x0, 0x0}}, -/*1A00*/{{0x2, 0x60000108L, 0x60000208L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L}, -/*1A01*/ {0x0, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L}, -/*1A02*/ {0x0, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L}, -/*1A03*/ {0x0, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L}}, -/*1F80*/ 0x0L, -/*2101*/ 0x30, -/*2102*/ 0xFA, -/*2111*/ {1L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L}, -/*2301*/{{0x8, 0x64L, 0x1, {'T', 'r', 'a', 'c', 'e', '1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, {'r', 'e', 'd', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, 0x60000108L, 0x1, 0x0, 0L}, -/*2302*/ {0x8, 0x0L, 0x0, {'T', 'r', 'a', 'c', 'e', '2', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, {'g', 'r', 'e', 'e', 'n', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, 0x0L, 0x0, 0x0, 0L}}, - - CO_OD_FIRST_LAST_WORD -}; - - -/******************************************************************************* - STRUCTURES FOR RECORD TYPE OBJECTS -*******************************************************************************/ -/*0x1018*/ const CO_OD_entryRecord_t OD_record1018[5] = { - {(void*)&CO_OD_ROM.identity.maxSubIndex, 0x05, 1}, - {(void*)&CO_OD_ROM.identity.vendorID, 0x85, 4}, - {(void*)&CO_OD_ROM.identity.productCode, 0x85, 4}, - {(void*)&CO_OD_ROM.identity.revisionNumber, 0x85, 4}, - {(void*)&CO_OD_ROM.identity.serialNumber, 0x85, 4}}; -/*0x1200*/ const CO_OD_entryRecord_t OD_record1200[3] = { - {(void*)&CO_OD_ROM.SDOServerParameter[0].maxSubIndex, 0x05, 1}, - {(void*)&CO_OD_ROM.SDOServerParameter[0].COB_IDClientToServer, 0x85, 4}, - {(void*)&CO_OD_ROM.SDOServerParameter[0].COB_IDServerToClient, 0x85, 4}}; -/*0x1400*/ const CO_OD_entryRecord_t OD_record1400[3] = { - {(void*)&CO_OD_ROM.RPDOCommunicationParameter[0].maxSubIndex, 0x05, 1}, - {(void*)&CO_OD_ROM.RPDOCommunicationParameter[0].COB_IDUsedByRPDO, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOCommunicationParameter[0].transmissionType, 0x0D, 1}}; -/*0x1401*/ const CO_OD_entryRecord_t OD_record1401[3] = { - {(void*)&CO_OD_ROM.RPDOCommunicationParameter[1].maxSubIndex, 0x05, 1}, - {(void*)&CO_OD_ROM.RPDOCommunicationParameter[1].COB_IDUsedByRPDO, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOCommunicationParameter[1].transmissionType, 0x0D, 1}}; -/*0x1402*/ const CO_OD_entryRecord_t OD_record1402[3] = { - {(void*)&CO_OD_ROM.RPDOCommunicationParameter[2].maxSubIndex, 0x05, 1}, - {(void*)&CO_OD_ROM.RPDOCommunicationParameter[2].COB_IDUsedByRPDO, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOCommunicationParameter[2].transmissionType, 0x0D, 1}}; -/*0x1403*/ const CO_OD_entryRecord_t OD_record1403[3] = { - {(void*)&CO_OD_ROM.RPDOCommunicationParameter[3].maxSubIndex, 0x05, 1}, - {(void*)&CO_OD_ROM.RPDOCommunicationParameter[3].COB_IDUsedByRPDO, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOCommunicationParameter[3].transmissionType, 0x0D, 1}}; -/*0x1600*/ const CO_OD_entryRecord_t OD_record1600[9] = { - {(void*)&CO_OD_ROM.RPDOMappingParameter[0].numberOfMappedObjects, 0x0D, 1}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[0].mappedObject1, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[0].mappedObject2, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[0].mappedObject3, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[0].mappedObject4, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[0].mappedObject5, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[0].mappedObject6, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[0].mappedObject7, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[0].mappedObject8, 0x8D, 4}}; -/*0x1601*/ const CO_OD_entryRecord_t OD_record1601[9] = { - {(void*)&CO_OD_ROM.RPDOMappingParameter[1].numberOfMappedObjects, 0x0D, 1}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[1].mappedObject1, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[1].mappedObject2, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[1].mappedObject3, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[1].mappedObject4, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[1].mappedObject5, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[1].mappedObject6, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[1].mappedObject7, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[1].mappedObject8, 0x8D, 4}}; -/*0x1602*/ const CO_OD_entryRecord_t OD_record1602[9] = { - {(void*)&CO_OD_ROM.RPDOMappingParameter[2].numberOfMappedObjects, 0x0D, 1}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[2].mappedObject1, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[2].mappedObject2, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[2].mappedObject3, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[2].mappedObject4, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[2].mappedObject5, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[2].mappedObject6, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[2].mappedObject7, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[2].mappedObject8, 0x8D, 4}}; -/*0x1603*/ const CO_OD_entryRecord_t OD_record1603[9] = { - {(void*)&CO_OD_ROM.RPDOMappingParameter[3].numberOfMappedObjects, 0x0D, 1}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[3].mappedObject1, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[3].mappedObject2, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[3].mappedObject3, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[3].mappedObject4, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[3].mappedObject5, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[3].mappedObject6, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[3].mappedObject7, 0x8D, 4}, - {(void*)&CO_OD_ROM.RPDOMappingParameter[3].mappedObject8, 0x8D, 4}}; -/*0x1800*/ const CO_OD_entryRecord_t OD_record1800[7] = { - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[0].maxSubIndex, 0x05, 1}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[0].COB_IDUsedByTPDO, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[0].transmissionType, 0x0D, 1}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[0].inhibitTime, 0x8D, 2}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[0].compatibilityEntry, 0x0D, 1}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[0].eventTimer, 0x8D, 2}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[0].SYNCStartValue, 0x0D, 1}}; -/*0x1801*/ const CO_OD_entryRecord_t OD_record1801[7] = { - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[1].maxSubIndex, 0x05, 1}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[1].COB_IDUsedByTPDO, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[1].transmissionType, 0x0D, 1}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[1].inhibitTime, 0x8D, 2}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[1].compatibilityEntry, 0x0D, 1}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[1].eventTimer, 0x8D, 2}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[1].SYNCStartValue, 0x0D, 1}}; -/*0x1802*/ const CO_OD_entryRecord_t OD_record1802[7] = { - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[2].maxSubIndex, 0x05, 1}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[2].COB_IDUsedByTPDO, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[2].transmissionType, 0x0D, 1}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[2].inhibitTime, 0x8D, 2}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[2].compatibilityEntry, 0x0D, 1}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[2].eventTimer, 0x8D, 2}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[2].SYNCStartValue, 0x0D, 1}}; -/*0x1803*/ const CO_OD_entryRecord_t OD_record1803[7] = { - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[3].maxSubIndex, 0x05, 1}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[3].COB_IDUsedByTPDO, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[3].transmissionType, 0x0D, 1}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[3].inhibitTime, 0x8D, 2}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[3].compatibilityEntry, 0x0D, 1}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[3].eventTimer, 0x8D, 2}, - {(void*)&CO_OD_ROM.TPDOCommunicationParameter[3].SYNCStartValue, 0x0D, 1}}; -/*0x1A00*/ const CO_OD_entryRecord_t OD_record1A00[9] = { - {(void*)&CO_OD_ROM.TPDOMappingParameter[0].numberOfMappedObjects, 0x0D, 1}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[0].mappedObject1, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[0].mappedObject2, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[0].mappedObject3, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[0].mappedObject4, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[0].mappedObject5, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[0].mappedObject6, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[0].mappedObject7, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[0].mappedObject8, 0x8D, 4}}; -/*0x1A01*/ const CO_OD_entryRecord_t OD_record1A01[9] = { - {(void*)&CO_OD_ROM.TPDOMappingParameter[1].numberOfMappedObjects, 0x0D, 1}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[1].mappedObject1, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[1].mappedObject2, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[1].mappedObject3, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[1].mappedObject4, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[1].mappedObject5, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[1].mappedObject6, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[1].mappedObject7, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[1].mappedObject8, 0x8D, 4}}; -/*0x1A02*/ const CO_OD_entryRecord_t OD_record1A02[9] = { - {(void*)&CO_OD_ROM.TPDOMappingParameter[2].numberOfMappedObjects, 0x0D, 1}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[2].mappedObject1, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[2].mappedObject2, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[2].mappedObject3, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[2].mappedObject4, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[2].mappedObject5, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[2].mappedObject6, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[2].mappedObject7, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[2].mappedObject8, 0x8D, 4}}; -/*0x1A03*/ const CO_OD_entryRecord_t OD_record1A03[9] = { - {(void*)&CO_OD_ROM.TPDOMappingParameter[3].numberOfMappedObjects, 0x0D, 1}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[3].mappedObject1, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[3].mappedObject2, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[3].mappedObject3, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[3].mappedObject4, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[3].mappedObject5, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[3].mappedObject6, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[3].mappedObject7, 0x8D, 4}, - {(void*)&CO_OD_ROM.TPDOMappingParameter[3].mappedObject8, 0x8D, 4}}; -/*0x2120*/ const CO_OD_entryRecord_t OD_record2120[6] = { - {(void*)&CO_OD_RAM.testVar.maxSubIndex, 0x06, 1}, - {(void*)&CO_OD_RAM.testVar.I64, 0xBE, 8}, - {(void*)&CO_OD_RAM.testVar.U64, 0xBE, 8}, - {(void*)&CO_OD_RAM.testVar.R32, 0xBE, 4}, - {(void*)&CO_OD_RAM.testVar.R64, 0xBE, 8}, - {0, 0x0E, 0}}; -/*0x2130*/ const CO_OD_entryRecord_t OD_record2130[4] = { - {(void*)&CO_OD_RAM.time.maxSubIndex, 0x06, 1}, - {(void*)&CO_OD_RAM.time.string[0], 0x06, 30}, - {(void*)&CO_OD_RAM.time.epochTimeBaseMs, 0x8E, 8}, - {(void*)&CO_OD_RAM.time.epochTimeOffsetMs, 0xBE, 4}}; -/*0x2301*/ const CO_OD_entryRecord_t OD_record2301[9] = { - {(void*)&CO_OD_ROM.traceConfig[0].maxSubIndex, 0x05, 1}, - {(void*)&CO_OD_ROM.traceConfig[0].size, 0x8D, 4}, - {(void*)&CO_OD_ROM.traceConfig[0].axisNo, 0x0D, 1}, - {(void*)&CO_OD_ROM.traceConfig[0].name[0], 0x0D, 30}, - {(void*)&CO_OD_ROM.traceConfig[0].color[0], 0x0D, 20}, - {(void*)&CO_OD_ROM.traceConfig[0].map, 0x8D, 4}, - {(void*)&CO_OD_ROM.traceConfig[0].format, 0x0D, 1}, - {(void*)&CO_OD_ROM.traceConfig[0].trigger, 0x0D, 1}, - {(void*)&CO_OD_ROM.traceConfig[0].threshold, 0x8D, 4}}; -/*0x2302*/ const CO_OD_entryRecord_t OD_record2302[9] = { - {(void*)&CO_OD_ROM.traceConfig[1].maxSubIndex, 0x05, 1}, - {(void*)&CO_OD_ROM.traceConfig[1].size, 0x8D, 4}, - {(void*)&CO_OD_ROM.traceConfig[1].axisNo, 0x0D, 1}, - {(void*)&CO_OD_ROM.traceConfig[1].name[0], 0x0D, 30}, - {(void*)&CO_OD_ROM.traceConfig[1].color[0], 0x0D, 20}, - {(void*)&CO_OD_ROM.traceConfig[1].map, 0x8D, 4}, - {(void*)&CO_OD_ROM.traceConfig[1].format, 0x0D, 1}, - {(void*)&CO_OD_ROM.traceConfig[1].trigger, 0x0D, 1}, - {(void*)&CO_OD_ROM.traceConfig[1].threshold, 0x8D, 4}}; -/*0x2401*/ const CO_OD_entryRecord_t OD_record2401[7] = { - {(void*)&CO_OD_RAM.trace[0].maxSubIndex, 0x06, 1}, - {(void*)&CO_OD_RAM.trace[0].size, 0xBE, 4}, - {(void*)&CO_OD_RAM.trace[0].value, 0xA6, 4}, - {(void*)&CO_OD_RAM.trace[0].min, 0xBE, 4}, - {(void*)&CO_OD_RAM.trace[0].max, 0xBE, 4}, - {0, 0x06, 0}, - {(void*)&CO_OD_RAM.trace[0].triggerTime, 0xBE, 4}}; -/*0x2402*/ const CO_OD_entryRecord_t OD_record2402[7] = { - {(void*)&CO_OD_RAM.trace[1].maxSubIndex, 0x06, 1}, - {(void*)&CO_OD_RAM.trace[1].size, 0xBE, 4}, - {(void*)&CO_OD_RAM.trace[1].value, 0xA6, 4}, - {(void*)&CO_OD_RAM.trace[1].min, 0xBE, 4}, - {(void*)&CO_OD_RAM.trace[1].max, 0xBE, 4}, - {0, 0x06, 0}, - {(void*)&CO_OD_RAM.trace[1].triggerTime, 0xBE, 4}}; - - -/******************************************************************************* - OBJECT DICTIONARY -*******************************************************************************/ -const CO_OD_entry_t CO_OD[CO_OD_NoOfElements] = { -{0x1000, 0x00, 0x85, 4, (void*)&CO_OD_ROM.deviceType}, -{0x1001, 0x00, 0x36, 1, (void*)&CO_OD_RAM.errorRegister}, -{0x1002, 0x00, 0xB6, 4, (void*)&CO_OD_RAM.manufacturerStatusRegister}, -{0x1003, 0x08, 0x8E, 4, (void*)&CO_OD_RAM.preDefinedErrorField[0]}, -{0x1005, 0x00, 0x8D, 4, (void*)&CO_OD_ROM.COB_ID_SYNCMessage}, -{0x1006, 0x00, 0x8D, 4, (void*)&CO_OD_ROM.communicationCyclePeriod}, -{0x1007, 0x00, 0x8D, 4, (void*)&CO_OD_ROM.synchronousWindowLength}, -{0x1008, 0x00, 0x05, 11, (void*)&CO_OD_ROM.manufacturerDeviceName[0]}, -{0x1009, 0x00, 0x05, 4, (void*)&CO_OD_ROM.manufacturerHardwareVersion[0]}, -{0x100A, 0x00, 0x05, 4, (void*)&CO_OD_ROM.manufacturerSoftwareVersion[0]}, -{0x1010, 0x01, 0x8E, 4, (void*)&CO_OD_RAM.storeParameters[0]}, -{0x1011, 0x01, 0x8E, 4, (void*)&CO_OD_RAM.restoreDefaultParameters[0]}, -{0x1014, 0x00, 0x85, 4, (void*)&CO_OD_ROM.COB_ID_EMCY}, -{0x1015, 0x00, 0x8D, 2, (void*)&CO_OD_ROM.inhibitTimeEMCY}, -{0x1016, 0x04, 0x8D, 4, (void*)&CO_OD_ROM.consumerHeartbeatTime[0]}, -{0x1017, 0x00, 0x8D, 2, (void*)&CO_OD_ROM.producerHeartbeatTime}, -{0x1018, 0x04, 0x00, 0, (void*)&OD_record1018}, -{0x1019, 0x00, 0x0D, 1, (void*)&CO_OD_ROM.synchronousCounterOverflowValue}, -{0x1029, 0x06, 0x0D, 1, (void*)&CO_OD_ROM.errorBehavior[0]}, -{0x1200, 0x02, 0x00, 0, (void*)&OD_record1200}, -{0x1400, 0x02, 0x00, 0, (void*)&OD_record1400}, -{0x1401, 0x02, 0x00, 0, (void*)&OD_record1401}, -{0x1402, 0x02, 0x00, 0, (void*)&OD_record1402}, -{0x1403, 0x02, 0x00, 0, (void*)&OD_record1403}, -{0x1600, 0x08, 0x00, 0, (void*)&OD_record1600}, -{0x1601, 0x08, 0x00, 0, (void*)&OD_record1601}, -{0x1602, 0x08, 0x00, 0, (void*)&OD_record1602}, -{0x1603, 0x08, 0x00, 0, (void*)&OD_record1603}, -{0x1800, 0x06, 0x00, 0, (void*)&OD_record1800}, -{0x1801, 0x06, 0x00, 0, (void*)&OD_record1801}, -{0x1802, 0x06, 0x00, 0, (void*)&OD_record1802}, -{0x1803, 0x06, 0x00, 0, (void*)&OD_record1803}, -{0x1A00, 0x08, 0x00, 0, (void*)&OD_record1A00}, -{0x1A01, 0x08, 0x00, 0, (void*)&OD_record1A01}, -{0x1A02, 0x08, 0x00, 0, (void*)&OD_record1A02}, -{0x1A03, 0x08, 0x00, 0, (void*)&OD_record1A03}, -{0x1F80, 0x00, 0x8D, 4, (void*)&CO_OD_ROM.NMTStartup}, -{0x2100, 0x00, 0x36, 10, (void*)&CO_OD_RAM.errorStatusBits[0]}, -{0x2101, 0x00, 0x0D, 1, (void*)&CO_OD_ROM.CANNodeID}, -{0x2102, 0x00, 0x8D, 2, (void*)&CO_OD_ROM.CANBitRate}, -{0x2103, 0x00, 0x8E, 2, (void*)&CO_OD_RAM.SYNCCounter}, -{0x2104, 0x00, 0x86, 2, (void*)&CO_OD_RAM.SYNCTime}, -{0x2106, 0x00, 0x87, 4, (void*)&CO_OD_EEPROM.powerOnCounter}, -{0x2107, 0x05, 0xBE, 2, (void*)&CO_OD_RAM.performance[0]}, -{0x2108, 0x01, 0xB6, 2, (void*)&CO_OD_RAM.temperature[0]}, -{0x2109, 0x01, 0xB6, 2, (void*)&CO_OD_RAM.voltage[0]}, -{0x2110, 0x10, 0xFE, 4, (void*)&CO_OD_RAM.variableInt32[0]}, -{0x2111, 0x10, 0xFD, 4, (void*)&CO_OD_ROM.variableROMInt32[0]}, -{0x2112, 0x10, 0xFF, 4, (void*)&CO_OD_EEPROM.variableNVInt32[0]}, -{0x2120, 0x05, 0x00, 0, (void*)&OD_record2120}, -{0x2130, 0x03, 0x00, 0, (void*)&OD_record2130}, -{0x2301, 0x08, 0x00, 0, (void*)&OD_record2301}, -{0x2302, 0x08, 0x00, 0, (void*)&OD_record2302}, -{0x2400, 0x00, 0x3E, 1, (void*)&CO_OD_RAM.traceEnable}, -{0x2401, 0x06, 0x00, 0, (void*)&OD_record2401}, -{0x2402, 0x06, 0x00, 0, (void*)&OD_record2402}, -{0x6000, 0x08, 0x76, 1, (void*)&CO_OD_RAM.readInput8Bit[0]}, -{0x6200, 0x08, 0x3E, 1, (void*)&CO_OD_RAM.writeOutput8Bit[0]}, -{0x6401, 0x0C, 0xB6, 2, (void*)&CO_OD_RAM.readAnalogueInput16Bit[0]}, -{0x6411, 0x08, 0xBE, 2, (void*)&CO_OD_RAM.writeAnalogueOutput16Bit[0]}, -}; - diff --git a/example/CO_OD_with_trace/CO_OD.h b/example/CO_OD_with_trace/CO_OD.h deleted file mode 100644 index f9710441..00000000 --- a/example/CO_OD_with_trace/CO_OD.h +++ /dev/null @@ -1,451 +0,0 @@ -/* - * CANopen Object Dictionary. - * - * This file was automatically generated with CANopenNode Object - * Dictionary Editor. DON'T EDIT THIS FILE MANUALLY !!!! - * Object Dictionary Editor is currently an older, but functional web - * application. For more info see See 'Object_Dictionary_Editor/about.html' in - * - * For more information on CANopen Object Dictionary see . - * - * @file CO_OD.h - * @author Janez Paternoster - * @copyright 2010 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#ifndef CO_OD_H -#define CO_OD_H - - -/******************************************************************************* - CANopen DATA DYPES -*******************************************************************************/ - typedef uint8_t UNSIGNED8; - typedef uint16_t UNSIGNED16; - typedef uint32_t UNSIGNED32; - typedef uint64_t UNSIGNED64; - typedef int8_t INTEGER8; - typedef int16_t INTEGER16; - typedef int32_t INTEGER32; - typedef int64_t INTEGER64; - typedef float32_t REAL32; - typedef float64_t REAL64; - typedef char_t VISIBLE_STRING; - typedef oChar_t OCTET_STRING; - typedef domain_t DOMAIN; - - -/******************************************************************************* - FILE INFO: - FileName: IO Example - FileVersion: - - CreationTime: 17:24:43 - CreationDate: 2016-03-25 - CreatedBy: JP -*******************************************************************************/ - - -/******************************************************************************* - DEVICE INFO: - VendorName: CANopenNode - VendorNumber: 0 - ProductName: CANopenNode - ProductNumber: 0 -*******************************************************************************/ - - -/******************************************************************************* - FEATURES -*******************************************************************************/ - #define CO_NO_SYNC 1 //Associated objects: 1005, 1006, 1007, 2103, 2104 - #define CO_NO_EMERGENCY 1 //Associated objects: 1014, 1015 - #define CO_NO_SDO_SERVER 1 //Associated objects: 1200 - #define CO_NO_SDO_CLIENT 0 - #define CO_NO_RPDO 4 //Associated objects: 1400, 1401, 1402, 1403, 1600, 1601, 1602, 1603 - #define CO_NO_TPDO 4 //Associated objects: 1800, 1801, 1802, 1803, 1A00, 1A01, 1A02, 1A03 - #define CO_NO_NMT_MASTER 0 - #define CO_NO_TRACE 2 //Associated objects: 2301, 2302, 2400, 2401, 2402 - - -/******************************************************************************* - OBJECT DICTIONARY -*******************************************************************************/ - #define CO_OD_NoOfElements 60 - - -/******************************************************************************* - TYPE DEFINITIONS FOR RECORDS -*******************************************************************************/ -/*1018 */ typedef struct{ - UNSIGNED8 maxSubIndex; - UNSIGNED32 vendorID; - UNSIGNED32 productCode; - UNSIGNED32 revisionNumber; - UNSIGNED32 serialNumber; - } OD_identity_t; - -/*1200[1] */ typedef struct{ - UNSIGNED8 maxSubIndex; - UNSIGNED32 COB_IDClientToServer; - UNSIGNED32 COB_IDServerToClient; - } OD_SDOServerParameter_t; - -/*1400[4] */ typedef struct{ - UNSIGNED8 maxSubIndex; - UNSIGNED32 COB_IDUsedByRPDO; - UNSIGNED8 transmissionType; - } OD_RPDOCommunicationParameter_t; - -/*1600[4] */ typedef struct{ - UNSIGNED8 numberOfMappedObjects; - UNSIGNED32 mappedObject1; - UNSIGNED32 mappedObject2; - UNSIGNED32 mappedObject3; - UNSIGNED32 mappedObject4; - UNSIGNED32 mappedObject5; - UNSIGNED32 mappedObject6; - UNSIGNED32 mappedObject7; - UNSIGNED32 mappedObject8; - } OD_RPDOMappingParameter_t; - -/*1800[4] */ typedef struct{ - UNSIGNED8 maxSubIndex; - UNSIGNED32 COB_IDUsedByTPDO; - UNSIGNED8 transmissionType; - UNSIGNED16 inhibitTime; - UNSIGNED8 compatibilityEntry; - UNSIGNED16 eventTimer; - UNSIGNED8 SYNCStartValue; - } OD_TPDOCommunicationParameter_t; - -/*1A00[4] */ typedef struct{ - UNSIGNED8 numberOfMappedObjects; - UNSIGNED32 mappedObject1; - UNSIGNED32 mappedObject2; - UNSIGNED32 mappedObject3; - UNSIGNED32 mappedObject4; - UNSIGNED32 mappedObject5; - UNSIGNED32 mappedObject6; - UNSIGNED32 mappedObject7; - UNSIGNED32 mappedObject8; - } OD_TPDOMappingParameter_t; - -/*2120 */ typedef struct{ - UNSIGNED8 maxSubIndex; - INTEGER64 I64; - UNSIGNED64 U64; - REAL32 R32; - REAL64 R64; - DOMAIN domain; - } OD_testVar_t; - -/*2130 */ typedef struct{ - UNSIGNED8 maxSubIndex; - VISIBLE_STRING string[30]; - UNSIGNED64 epochTimeBaseMs; - UNSIGNED32 epochTimeOffsetMs; - } OD_time_t; - -/*2301[2] */ typedef struct{ - UNSIGNED8 maxSubIndex; - UNSIGNED32 size; - UNSIGNED8 axisNo; - VISIBLE_STRING name[30]; - VISIBLE_STRING color[20]; - UNSIGNED32 map; - UNSIGNED8 format; - UNSIGNED8 trigger; - INTEGER32 threshold; - } OD_traceConfig_t; - -/*2401[2] */ typedef struct{ - UNSIGNED8 maxSubIndex; - UNSIGNED32 size; - INTEGER32 value; - INTEGER32 min; - INTEGER32 max; - DOMAIN plot; - UNSIGNED32 triggerTime; - } OD_trace_t; - - -/******************************************************************************* - STRUCTURES FOR VARIABLES IN DIFFERENT MEMORY LOCATIONS -*******************************************************************************/ -#define CO_OD_FIRST_LAST_WORD 0x55 //Any value from 0x01 to 0xFE. If changed, EEPROM will be reinitialized. - -/***** Structure for RAM variables ********************************************/ -struct sCO_OD_RAM{ - UNSIGNED32 FirstWord; - -/*1001 */ UNSIGNED8 errorRegister; -/*1002 */ UNSIGNED32 manufacturerStatusRegister; -/*1003 */ UNSIGNED32 preDefinedErrorField[8]; -/*1010 */ UNSIGNED32 storeParameters[1]; -/*1011 */ UNSIGNED32 restoreDefaultParameters[1]; -/*2100 */ OCTET_STRING errorStatusBits[10]; -/*2103 */ UNSIGNED16 SYNCCounter; -/*2104 */ UNSIGNED16 SYNCTime; -/*2107 */ UNSIGNED16 performance[5]; -/*2108 */ INTEGER16 temperature[1]; -/*2109 */ INTEGER16 voltage[1]; -/*2110 */ INTEGER32 variableInt32[16]; -/*2120 */ OD_testVar_t testVar; -/*2130 */ OD_time_t time; -/*2400 */ UNSIGNED8 traceEnable; -/*2401[2] */ OD_trace_t trace[2]; -/*6000 */ UNSIGNED8 readInput8Bit[8]; -/*6200 */ UNSIGNED8 writeOutput8Bit[8]; -/*6401 */ INTEGER16 readAnalogueInput16Bit[12]; -/*6411 */ INTEGER16 writeAnalogueOutput16Bit[8]; - - UNSIGNED32 LastWord; -}; - -/***** Structure for EEPROM variables *****************************************/ -struct sCO_OD_EEPROM{ - UNSIGNED32 FirstWord; - -/*2106 */ UNSIGNED32 powerOnCounter; -/*2112 */ INTEGER32 variableNVInt32[16]; - - UNSIGNED32 LastWord; -}; - - -/***** Structure for ROM variables ********************************************/ -struct sCO_OD_ROM{ - UNSIGNED32 FirstWord; - -/*1000 */ UNSIGNED32 deviceType; -/*1005 */ UNSIGNED32 COB_ID_SYNCMessage; -/*1006 */ UNSIGNED32 communicationCyclePeriod; -/*1007 */ UNSIGNED32 synchronousWindowLength; -/*1008 */ VISIBLE_STRING manufacturerDeviceName[11]; -/*1009 */ VISIBLE_STRING manufacturerHardwareVersion[4]; -/*100A */ VISIBLE_STRING manufacturerSoftwareVersion[4]; -/*1014 */ UNSIGNED32 COB_ID_EMCY; -/*1015 */ UNSIGNED16 inhibitTimeEMCY; -/*1016 */ UNSIGNED32 consumerHeartbeatTime[4]; -/*1017 */ UNSIGNED16 producerHeartbeatTime; -/*1018 */ OD_identity_t identity; -/*1019 */ UNSIGNED8 synchronousCounterOverflowValue; -/*1029 */ UNSIGNED8 errorBehavior[6]; -/*1200[1] */ OD_SDOServerParameter_t SDOServerParameter[1]; -/*1400[4] */ OD_RPDOCommunicationParameter_t RPDOCommunicationParameter[4]; -/*1600[4] */ OD_RPDOMappingParameter_t RPDOMappingParameter[4]; -/*1800[4] */ OD_TPDOCommunicationParameter_t TPDOCommunicationParameter[4]; -/*1A00[4] */ OD_TPDOMappingParameter_t TPDOMappingParameter[4]; -/*1F80 */ UNSIGNED32 NMTStartup; -/*2101 */ UNSIGNED8 CANNodeID; -/*2102 */ UNSIGNED16 CANBitRate; -/*2111 */ INTEGER32 variableROMInt32[16]; -/*2301[2] */ OD_traceConfig_t traceConfig[2]; - - UNSIGNED32 LastWord; -}; - - -/***** Declaration of Object Dictionary variables *****************************/ -extern struct sCO_OD_RAM CO_OD_RAM; - -extern struct sCO_OD_EEPROM CO_OD_EEPROM; - -extern struct sCO_OD_ROM CO_OD_ROM; - - -/******************************************************************************* - ALIASES FOR OBJECT DICTIONARY VARIABLES -*******************************************************************************/ -/*1000, Data Type: UNSIGNED32 */ - #define OD_deviceType CO_OD_ROM.deviceType - -/*1001, Data Type: UNSIGNED8 */ - #define OD_errorRegister CO_OD_RAM.errorRegister - -/*1002, Data Type: UNSIGNED32 */ - #define OD_manufacturerStatusRegister CO_OD_RAM.manufacturerStatusRegister - -/*1003, Data Type: UNSIGNED32, Array[8] */ - #define OD_preDefinedErrorField CO_OD_RAM.preDefinedErrorField - #define ODL_preDefinedErrorField_arrayLength 8 - -/*1005, Data Type: UNSIGNED32 */ - #define OD_COB_ID_SYNCMessage CO_OD_ROM.COB_ID_SYNCMessage - -/*1006, Data Type: UNSIGNED32 */ - #define OD_communicationCyclePeriod CO_OD_ROM.communicationCyclePeriod - -/*1007, Data Type: UNSIGNED32 */ - #define OD_synchronousWindowLength CO_OD_ROM.synchronousWindowLength - -/*1008, Data Type: VISIBLE_STRING, Array[11] */ - #define OD_manufacturerDeviceName CO_OD_ROM.manufacturerDeviceName - #define ODL_manufacturerDeviceName_stringLength 11 - -/*1009, Data Type: VISIBLE_STRING, Array[4] */ - #define OD_manufacturerHardwareVersion CO_OD_ROM.manufacturerHardwareVersion - #define ODL_manufacturerHardwareVersion_stringLength 4 - -/*100A, Data Type: VISIBLE_STRING, Array[4] */ - #define OD_manufacturerSoftwareVersion CO_OD_ROM.manufacturerSoftwareVersion - #define ODL_manufacturerSoftwareVersion_stringLength 4 - -/*1010, Data Type: UNSIGNED32, Array[1] */ - #define OD_storeParameters CO_OD_RAM.storeParameters - #define ODL_storeParameters_arrayLength 1 - #define ODA_storeParameters_saveAllParameters 0 - -/*1011, Data Type: UNSIGNED32, Array[1] */ - #define OD_restoreDefaultParameters CO_OD_RAM.restoreDefaultParameters - #define ODL_restoreDefaultParameters_arrayLength 1 - #define ODA_restoreDefaultParameters_restoreAllDefaultParameters 0 - -/*1014, Data Type: UNSIGNED32 */ - #define OD_COB_ID_EMCY CO_OD_ROM.COB_ID_EMCY - -/*1015, Data Type: UNSIGNED16 */ - #define OD_inhibitTimeEMCY CO_OD_ROM.inhibitTimeEMCY - -/*1016, Data Type: UNSIGNED32, Array[4] */ - #define OD_consumerHeartbeatTime CO_OD_ROM.consumerHeartbeatTime - #define ODL_consumerHeartbeatTime_arrayLength 4 - -/*1017, Data Type: UNSIGNED16 */ - #define OD_producerHeartbeatTime CO_OD_ROM.producerHeartbeatTime - -/*1018, Data Type: OD_identity_t */ - #define OD_identity CO_OD_ROM.identity - -/*1019, Data Type: UNSIGNED8 */ - #define OD_synchronousCounterOverflowValue CO_OD_ROM.synchronousCounterOverflowValue - -/*1029, Data Type: UNSIGNED8, Array[6] */ - #define OD_errorBehavior CO_OD_ROM.errorBehavior - #define ODL_errorBehavior_arrayLength 6 - #define ODA_errorBehavior_communication 0 - #define ODA_errorBehavior_communicationOther 1 - #define ODA_errorBehavior_communicationPassive 2 - #define ODA_errorBehavior_generic 3 - #define ODA_errorBehavior_deviceProfile 4 - #define ODA_errorBehavior_manufacturerSpecific 5 - -/*1200[1], Data Type: OD_SDOServerParameter_t, Array[1] */ - #define OD_SDOServerParameter CO_OD_ROM.SDOServerParameter - -/*1400[4], Data Type: OD_RPDOCommunicationParameter_t, Array[4] */ - #define OD_RPDOCommunicationParameter CO_OD_ROM.RPDOCommunicationParameter - -/*1600[4], Data Type: OD_RPDOMappingParameter_t, Array[4] */ - #define OD_RPDOMappingParameter CO_OD_ROM.RPDOMappingParameter - -/*1800[4], Data Type: OD_TPDOCommunicationParameter_t, Array[4] */ - #define OD_TPDOCommunicationParameter CO_OD_ROM.TPDOCommunicationParameter - -/*1A00[4], Data Type: OD_TPDOMappingParameter_t, Array[4] */ - #define OD_TPDOMappingParameter CO_OD_ROM.TPDOMappingParameter - -/*1F80, Data Type: UNSIGNED32 */ - #define OD_NMTStartup CO_OD_ROM.NMTStartup - -/*2100, Data Type: OCTET_STRING, Array[10] */ - #define OD_errorStatusBits CO_OD_RAM.errorStatusBits - #define ODL_errorStatusBits_stringLength 10 - -/*2101, Data Type: UNSIGNED8 */ - #define OD_CANNodeID CO_OD_ROM.CANNodeID - -/*2102, Data Type: UNSIGNED16 */ - #define OD_CANBitRate CO_OD_ROM.CANBitRate - -/*2103, Data Type: UNSIGNED16 */ - #define OD_SYNCCounter CO_OD_RAM.SYNCCounter - -/*2104, Data Type: UNSIGNED16 */ - #define OD_SYNCTime CO_OD_RAM.SYNCTime - -/*2106, Data Type: UNSIGNED32 */ - #define OD_powerOnCounter CO_OD_EEPROM.powerOnCounter - -/*2107, Data Type: UNSIGNED16, Array[5] */ - #define OD_performance CO_OD_RAM.performance - #define ODL_performance_arrayLength 5 - #define ODA_performance_cyclesPerSecond 0 - #define ODA_performance_timerCycleTime 1 - #define ODA_performance_timerCycleMaxTime 2 - #define ODA_performance_mainCycleTime 3 - #define ODA_performance_mainCycleMaxTime 4 - -/*2108, Data Type: INTEGER16, Array[1] */ - #define OD_temperature CO_OD_RAM.temperature - #define ODL_temperature_arrayLength 1 - #define ODA_temperature_mainPCB 0 - -/*2109, Data Type: INTEGER16, Array[1] */ - #define OD_voltage CO_OD_RAM.voltage - #define ODL_voltage_arrayLength 1 - #define ODA_voltage_mainPCBSupply 0 - -/*2110, Data Type: INTEGER32, Array[16] */ - #define OD_variableInt32 CO_OD_RAM.variableInt32 - #define ODL_variableInt32_arrayLength 16 - -/*2111, Data Type: INTEGER32, Array[16] */ - #define OD_variableROMInt32 CO_OD_ROM.variableROMInt32 - #define ODL_variableROMInt32_arrayLength 16 - -/*2112, Data Type: INTEGER32, Array[16] */ - #define OD_variableNVInt32 CO_OD_EEPROM.variableNVInt32 - #define ODL_variableNVInt32_arrayLength 16 - -/*2120, Data Type: OD_testVar_t */ - #define OD_testVar CO_OD_RAM.testVar - -/*2130, Data Type: OD_time_t */ - #define OD_time CO_OD_RAM.time - -/*2301[2], Data Type: OD_traceConfig_t, Array[2] */ - #define OD_traceConfig CO_OD_ROM.traceConfig - -/*2400, Data Type: UNSIGNED8 */ - #define OD_traceEnable CO_OD_RAM.traceEnable - -/*2401[2], Data Type: OD_trace_t, Array[2] */ - #define OD_trace CO_OD_RAM.trace - -/*6000, Data Type: UNSIGNED8, Array[8] */ - #define OD_readInput8Bit CO_OD_RAM.readInput8Bit - #define ODL_readInput8Bit_arrayLength 8 - -/*6200, Data Type: UNSIGNED8, Array[8] */ - #define OD_writeOutput8Bit CO_OD_RAM.writeOutput8Bit - #define ODL_writeOutput8Bit_arrayLength 8 - -/*6401, Data Type: INTEGER16, Array[12] */ - #define OD_readAnalogueInput16Bit CO_OD_RAM.readAnalogueInput16Bit - #define ODL_readAnalogueInput16Bit_arrayLength 12 - -/*6411, Data Type: INTEGER16, Array[8] */ - #define OD_writeAnalogueOutput16Bit CO_OD_RAM.writeAnalogueOutput16Bit - #define ODL_writeAnalogueOutput16Bit_arrayLength 8 - - -#endif - diff --git a/example/CO_driver_blank.c b/example/CO_driver_blank.c new file mode 100644 index 00000000..7eeeddc2 --- /dev/null +++ b/example/CO_driver_blank.c @@ -0,0 +1,350 @@ +/* + * CAN module object for generic microcontroller. + * + * This file is a template for other microcontrollers. + * + * @file CO_driver.c + * @ingroup CO_driver + * @author Janez Paternoster + * @copyright 2004 - 2020 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#include "301/CO_driver.h" + +void +CO_CANsetConfigurationMode(void* CANptr) { + /* Put CAN module in configuration mode */ +} + +void +CO_CANsetNormalMode(CO_CANmodule_t* CANmodule) { + /* Put CAN module in normal mode */ + + CANmodule->CANnormal = true; +} + +CO_ReturnError_t +CO_CANmodule_init(CO_CANmodule_t* CANmodule, void* CANptr, CO_CANrx_t rxArray[], uint16_t rxSize, CO_CANtx_t txArray[], + uint16_t txSize, uint16_t CANbitRate) { + uint16_t i; + + /* verify arguments */ + if (CANmodule == NULL || rxArray == NULL || txArray == NULL) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + /* Configure object variables */ + CANmodule->CANptr = CANptr; + CANmodule->rxArray = rxArray; + CANmodule->rxSize = rxSize; + CANmodule->txArray = txArray; + CANmodule->txSize = txSize; + CANmodule->CANerrorStatus = 0; + CANmodule->CANnormal = false; + CANmodule->useCANrxFilters = (rxSize <= 32U) ? true : false; /* microcontroller dependent */ + CANmodule->bufferInhibitFlag = false; + CANmodule->firstCANtxMessage = true; + CANmodule->CANtxCount = 0U; + CANmodule->errOld = 0U; + + for (i = 0U; i < rxSize; i++) { + rxArray[i].ident = 0U; + rxArray[i].mask = 0xFFFFU; + rxArray[i].object = NULL; + rxArray[i].CANrx_callback = NULL; + } + for (i = 0U; i < txSize; i++) { + txArray[i].bufferFull = false; + } + + /* Configure CAN module registers */ + + /* Configure CAN timing */ + + /* Configure CAN module hardware filters */ + if (CANmodule->useCANrxFilters) { + /* CAN module filters are used, they will be configured with */ + /* CO_CANrxBufferInit() functions, called by separate CANopen */ + /* init functions. */ + /* Configure all masks so, that received message must match filter */ + } else { + /* CAN module filters are not used, all messages with standard 11-bit */ + /* identifier will be received */ + /* Configure mask 0 so, that all messages with standard identifier are accepted */ + } + + /* configure CAN interrupt registers */ + + return CO_ERROR_NO; +} + +void +CO_CANmodule_disable(CO_CANmodule_t* CANmodule) { + if (CANmodule != NULL) { + /* turn off the module */ + } +} + +CO_ReturnError_t +CO_CANrxBufferInit(CO_CANmodule_t* CANmodule, uint16_t index, uint16_t ident, uint16_t mask, bool_t rtr, void* object, + void (*CANrx_callback)(void* object, void* message)) { + CO_ReturnError_t ret = CO_ERROR_NO; + + if ((CANmodule != NULL) && (object != NULL) && (CANrx_callback != NULL) && (index < CANmodule->rxSize)) { + /* buffer, which will be configured */ + CO_CANrx_t* buffer = &CANmodule->rxArray[index]; + + /* Configure object variables */ + buffer->object = object; + buffer->CANrx_callback = CANrx_callback; + + /* CAN identifier and CAN mask, bit aligned with CAN module. Different on different microcontrollers. */ + buffer->ident = ident & 0x07FFU; + if (rtr) { + buffer->ident |= 0x0800U; + } + buffer->mask = (mask & 0x07FFU) | 0x0800U; + + /* Set CAN hardware module filter and mask. */ + if (CANmodule->useCANrxFilters) {} + } else { + ret = CO_ERROR_ILLEGAL_ARGUMENT; + } + + return ret; +} + +CO_CANtx_t* +CO_CANtxBufferInit(CO_CANmodule_t* CANmodule, uint16_t index, uint16_t ident, bool_t rtr, uint8_t noOfBytes, + bool_t syncFlag) { + CO_CANtx_t* buffer = NULL; + + if ((CANmodule != NULL) && (index < CANmodule->txSize)) { + /* get specific buffer */ + buffer = &CANmodule->txArray[index]; + + /* CAN identifier, DLC and rtr, bit aligned with CAN module transmit buffer, microcontroller specific. */ + buffer->ident = ((uint32_t)ident & 0x07FFU) | ((uint32_t)(((uint32_t)noOfBytes & 0xFU) << 11U)) + | ((uint32_t)(rtr ? 0x8000U : 0U)); + + buffer->bufferFull = false; + buffer->syncFlag = syncFlag; + } + + return buffer; +} + +CO_ReturnError_t +CO_CANsend(CO_CANmodule_t* CANmodule, CO_CANtx_t* buffer) { + CO_ReturnError_t err = CO_ERROR_NO; + + /* Verify overflow */ + if (buffer->bufferFull) { + if (!CANmodule->firstCANtxMessage) { + /* don't set error, if bootup message is still on buffers */ + CANmodule->CANerrorStatus |= CO_CAN_ERRTX_OVERFLOW; + } + err = CO_ERROR_TX_OVERFLOW; + } + + CO_LOCK_CAN_SEND(CANmodule); + /* if CAN TX buffer is free, copy message to it */ + if (1 && CANmodule->CANtxCount == 0) { + CANmodule->bufferInhibitFlag = buffer->syncFlag; + /* copy message and txRequest */ + } + /* if no buffer is free, message will be sent by interrupt */ + else { + buffer->bufferFull = true; + CANmodule->CANtxCount++; + } + CO_UNLOCK_CAN_SEND(CANmodule); + + return err; +} + +void +CO_CANclearPendingSyncPDOs(CO_CANmodule_t* CANmodule) { + uint32_t tpdoDeleted = 0U; + + CO_LOCK_CAN_SEND(CANmodule); + /* Abort message from CAN module, if there is synchronous TPDO. + * Take special care with this functionality. */ + if (/* messageIsOnCanBuffer && */ CANmodule->bufferInhibitFlag) { + /* clear TXREQ */ + CANmodule->bufferInhibitFlag = false; + tpdoDeleted = 1U; + } + /* delete also pending synchronous TPDOs in TX buffers */ + if (CANmodule->CANtxCount != 0U) { + uint16_t i; + CO_CANtx_t* buffer = &CANmodule->txArray[0]; + for (i = CANmodule->txSize; i > 0U; i--) { + if (buffer->bufferFull) { + if (buffer->syncFlag) { + buffer->bufferFull = false; + CANmodule->CANtxCount--; + tpdoDeleted = 2U; + } + } + buffer++; + } + } + CO_UNLOCK_CAN_SEND(CANmodule); + + if (tpdoDeleted != 0U) { + CANmodule->CANerrorStatus |= CO_CAN_ERRTX_PDO_LATE; + } +} + +/* Get error counters from the module. If necessary, function may use different way to determine errors. */ +static uint16_t rxErrors = 0, txErrors = 0, overflow = 0; + +void +CO_CANmodule_process(CO_CANmodule_t* CANmodule) { + uint32_t err; + + err = ((uint32_t)txErrors << 16) | ((uint32_t)rxErrors << 8) | overflow; + + if (CANmodule->errOld != err) { + uint16_t status = CANmodule->CANerrorStatus; + + CANmodule->errOld = err; + + if (txErrors >= 256U) { + /* bus off */ + status |= CO_CAN_ERRTX_BUS_OFF; + } else { + /* recalculate CANerrorStatus, first clear some flags */ + status &= 0xFFFF + ^ (CO_CAN_ERRTX_BUS_OFF | CO_CAN_ERRRX_WARNING | CO_CAN_ERRRX_PASSIVE | CO_CAN_ERRTX_WARNING + | CO_CAN_ERRTX_PASSIVE); + + /* rx bus warning or passive */ + if (rxErrors >= 128) { + status |= CO_CAN_ERRRX_WARNING | CO_CAN_ERRRX_PASSIVE; + } else if (rxErrors >= 96) { + status |= CO_CAN_ERRRX_WARNING; + } + + /* tx bus warning or passive */ + if (txErrors >= 128) { + status |= CO_CAN_ERRTX_WARNING | CO_CAN_ERRTX_PASSIVE; + } else if (txErrors >= 96) { + status |= CO_CAN_ERRTX_WARNING; + } + + /* if not tx passive clear also overflow */ + if ((status & CO_CAN_ERRTX_PASSIVE) == 0) { + status &= 0xFFFF ^ CO_CAN_ERRTX_OVERFLOW; + } + } + + if (overflow != 0) { + /* CAN RX bus overflow */ + status |= CO_CAN_ERRRX_OVERFLOW; + } + + CANmodule->CANerrorStatus = status; + } +} + +typedef struct { + uint32_t ident; + uint8_t DLC; + uint8_t data[8]; +} CO_CANrxMsg_t; + +void +CO_CANinterrupt(CO_CANmodule_t* CANmodule) { + + /* receive interrupt */ + if (1) { + CO_CANrxMsg_t* rcvMsg; /* pointer to received message in CAN module */ + uint16_t index; /* index of received message */ + uint32_t rcvMsgIdent; /* identifier of the received message */ + CO_CANrx_t* buffer = NULL; /* receive message buffer from CO_CANmodule_t object. */ + bool_t msgMatched = false; + + rcvMsg = 0; /* get message from module here */ + rcvMsgIdent = rcvMsg->ident; + if (CANmodule->useCANrxFilters) { + /* CAN module filters are used. Message with known 11-bit identifier has been received */ + index = 0; /* get index of the received message here. Or something similar */ + if (index < CANmodule->rxSize) { + buffer = &CANmodule->rxArray[index]; + /* verify also RTR */ + if (((rcvMsgIdent ^ buffer->ident) & buffer->mask) == 0U) { + msgMatched = true; + } + } + } else { + /* CAN module filters are not used, message with any standard 11-bit identifier */ + /* has been received. Search rxArray form CANmodule for the same CAN-ID. */ + buffer = &CANmodule->rxArray[0]; + for (index = CANmodule->rxSize; index > 0U; index--) { + if (((rcvMsgIdent ^ buffer->ident) & buffer->mask) == 0U) { + msgMatched = true; + break; + } + buffer++; + } + } + + /* Call specific function, which will process the message */ + if (msgMatched && (buffer != NULL) && (buffer->CANrx_callback != NULL)) { + buffer->CANrx_callback(buffer->object, (void*)rcvMsg); + } + + /* Clear interrupt flag */ + } + + /* transmit interrupt */ + else if (0) { + /* Clear interrupt flag */ + + /* First CAN message (bootup) was sent successfully */ + CANmodule->firstCANtxMessage = false; + /* clear flag from previous message */ + CANmodule->bufferInhibitFlag = false; + /* Are there any new messages waiting to be send */ + if (CANmodule->CANtxCount > 0U) { + uint16_t i; /* index of transmitting message */ + + /* first buffer */ + CO_CANtx_t* buffer = &CANmodule->txArray[0]; + /* search through whole array of pointers to transmit message buffers. */ + for (i = CANmodule->txSize; i > 0U; i--) { + /* if message buffer is full, send it. */ + if (buffer->bufferFull) { + buffer->bufferFull = false; + CANmodule->CANtxCount--; + + /* Copy message to CAN buffer */ + CANmodule->bufferInhibitFlag = buffer->syncFlag; + /* canSend... */ + break; /* exit for loop */ + } + buffer++; + } /* end of for loop */ + + /* Clear counter if no more messages */ + if (i == 0U) { + CANmodule->CANtxCount = 0U; + } + } + } else { + /* some other interrupt reason */ + } +} diff --git a/example/CO_driver_target.h b/example/CO_driver_target.h new file mode 100644 index 00000000..a4023579 --- /dev/null +++ b/example/CO_driver_target.h @@ -0,0 +1,130 @@ +/* + * Device and application specific definitions for CANopenNode. + * + * @file CO_driver_target.h + * @author Janez Paternoster + * @copyright 2021 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#ifndef CO_DRIVER_TARGET_H +#define CO_DRIVER_TARGET_H + +/* This file contains device and application specific definitions. It is included from CO_driver.h, which contains + * documentation for common definitions below. */ + +#include +#include +#include + +#ifdef CO_DRIVER_CUSTOM +#include "CO_driver_custom.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Stack configuration override default values. For more information see file CO_config.h. */ + +/* Basic definitions. If big endian, CO_SWAP_xx macros must swap bytes. */ +#define CO_LITTLE_ENDIAN +#define CO_SWAP_16(x) x +#define CO_SWAP_32(x) x +#define CO_SWAP_64(x) x +/* NULL is defined in stddef.h */ +/* true and false are defined in stdbool.h */ +/* int8_t to uint64_t are defined in stdint.h */ +typedef uint_fast8_t bool_t; +typedef float float32_t; +typedef double float64_t; + +/* Access to received CAN message */ +#define CO_CANrxMsg_readIdent(msg) ((uint16_t)0) +#define CO_CANrxMsg_readDLC(msg) ((uint8_t)0) +#define CO_CANrxMsg_readData(msg) ((const uint8_t*)NULL) + +/* Received message object */ +typedef struct { + uint16_t ident; + uint16_t mask; + void* object; + void (*CANrx_callback)(void* object, void* message); +} CO_CANrx_t; + +/* Transmit message object */ +typedef struct { + uint32_t ident; + uint8_t DLC; + uint8_t data[8]; + volatile bool_t bufferFull; + volatile bool_t syncFlag; +} CO_CANtx_t; + +/* CAN module object */ +typedef struct { + void* CANptr; + CO_CANrx_t* rxArray; + uint16_t rxSize; + CO_CANtx_t* txArray; + uint16_t txSize; + uint16_t CANerrorStatus; + volatile bool_t CANnormal; + volatile bool_t useCANrxFilters; + volatile bool_t bufferInhibitFlag; + volatile bool_t firstCANtxMessage; + volatile uint16_t CANtxCount; + uint32_t errOld; +} CO_CANmodule_t; + +/* Data storage object for one entry */ +typedef struct { + void* addr; + size_t len; + uint8_t subIndexOD; + uint8_t attr; + /* Additional variables (target specific) */ + void* addrNV; +} CO_storage_entry_t; + +/* (un)lock critical section in CO_CANsend() */ +#define CO_LOCK_CAN_SEND(CAN_MODULE) +#define CO_UNLOCK_CAN_SEND(CAN_MODULE) + +/* (un)lock critical section in CO_errorReport() or CO_errorReset() */ +#define CO_LOCK_EMCY(CAN_MODULE) +#define CO_UNLOCK_EMCY(CAN_MODULE) + +/* (un)lock critical section when accessing Object Dictionary */ +#define CO_LOCK_OD(CAN_MODULE) +#define CO_UNLOCK_OD(CAN_MODULE) + +/* Synchronization between CAN receive and message processing threads. */ +#define CO_MemoryBarrier() +#define CO_FLAG_READ(rxNew) ((rxNew) != NULL) +#define CO_FLAG_SET(rxNew) \ + { \ + CO_MemoryBarrier(); \ + rxNew = (void*)1L; \ + } +#define CO_FLAG_CLEAR(rxNew) \ + { \ + CO_MemoryBarrier(); \ + rxNew = NULL; \ + } + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CO_DRIVER_TARGET_H */ diff --git a/example/CO_storageBlank.c b/example/CO_storageBlank.c new file mode 100644 index 00000000..a2306a5c --- /dev/null +++ b/example/CO_storageBlank.c @@ -0,0 +1,89 @@ +/* + * CANopen Object Dictionary storage object (blank example). + * + * @file CO_storageBlank.c + * @author Janez Paternoster + * @copyright 2021 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#include "CO_storageBlank.h" + +#if (CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE + +/* + * Function for writing data on "Store parameters" command - OD object 1010 + * + * For more information see file CO_storage.h, CO_storage_entry_t. + */ +static ODR_t +storeBlank(CO_storage_entry_t* entry, CO_CANmodule_t* CANmodule) { + + /* Open a file and write data to it */ + /* file = open(entry->pathToFileOrPointerToMemory); */ + /* write(entry->addr, entry->len, file); */ + + return ODR_OK; +} + +/* + * Function for restoring data on "Restore default parameters" command - OD 1011 + * + * For more information see file CO_storage.h, CO_storage_entry_t. + */ +static ODR_t +restoreBlank(CO_storage_entry_t* entry, CO_CANmodule_t* CANmodule) { + + /* disable (delete) the file, so default values will stay after startup */ + + return ODR_OK; +} + +CO_ReturnError_t +CO_storageBlank_init(CO_storage_t* storage, CO_CANmodule_t* CANmodule, OD_entry_t* OD_1010_StoreParameters, + OD_entry_t* OD_1011_RestoreDefaultParam, CO_storage_entry_t* entries, uint8_t entriesCount, + uint32_t* storageInitError) { + CO_ReturnError_t ret; + + /* verify arguments */ + if (storage == NULL || entries == NULL || entriesCount == 0 || storageInitError == NULL) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + /* initialize storage and OD extensions */ + ret = CO_storage_init(storage, CANmodule, OD_1010_StoreParameters, OD_1011_RestoreDefaultParam, storeBlank, + restoreBlank, entries, entriesCount); + if (ret != CO_ERROR_NO) { + return ret; + } + + /* initialize entries */ + *storageInitError = 0; + for (uint8_t i = 0; i < entriesCount; i++) { + CO_storage_entry_t* entry = &entries[i]; + + /* verify arguments */ + if (entry->addr == NULL || entry->len == 0 || entry->subIndexOD < 2) { + *storageInitError = i; + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + /* Open a file and read data from file to entry->addr */ + /* file = open(entry->pathToFileOrPointerToMemory); */ + /* read(entry->addr, entry->len, file); */ + } + + return ret; +} + +#endif /* (CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE */ diff --git a/example/CO_storageBlank.h b/example/CO_storageBlank.h new file mode 100644 index 00000000..48813ea9 --- /dev/null +++ b/example/CO_storageBlank.h @@ -0,0 +1,51 @@ +/* + * CANopen data storage object (blank example) + * + * @file CO_storageBlank.h + * @author Janez Paternoster + * @copyright 2021 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#ifndef CO_STORAGE_BLANK_H +#define CO_STORAGE_BLANK_H + +#include "storage/CO_storage.h" + +#if ((CO_CONFIG_STORAGE)&CO_CONFIG_STORAGE_ENABLE) || defined CO_DOXYGEN + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * This is very basic example of implementing (object dictionary) data storage. Data storage is target specific. + * CO_storageBlank.h and .c files only shows the basic principle, but does nothing. For complete example of storage see: + * - CANopenPIC/PIC32 uses eeprom with CANopenNode/storage/CO_storage.h/.c, CANopenNode/storage/CO_storageEeprom.h/.c, + * CANopenNode/storage/CO_eeprom.h and CANopenPIC/PIC32/CO_eepromPIC32.c files. + * - CANopenLinux uses file system with CANopenNode/storage/CO_storage.h/.c and CANopenLinux/CO_storageLinux.h files. + */ + +CO_ReturnError_t CO_storageBlank_init(CO_storage_t* storage, CO_CANmodule_t* CANmodule, + OD_entry_t* OD_1010_StoreParameters, OD_entry_t* OD_1011_RestoreDefaultParam, + CO_storage_entry_t* entries, uint8_t entriesCount, uint32_t* storageInitError); + +uint32_t CO_storageBlank_auto_process(CO_storage_t* storage, bool_t closeFiles); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* (CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE */ + +#endif /* CO_STORAGE_BLANK_H */ diff --git a/example/DS301_profile.eds b/example/DS301_profile.eds new file mode 100644 index 00000000..f203a8c3 --- /dev/null +++ b/example/DS301_profile.eds @@ -0,0 +1,1760 @@ +[FileInfo] +FileName=DS301_profile.eds +FileVersion=1 +FileRevision=1 +LastEDS= +EDSVersion=4.0 +Description= +CreationTime=12:00PM +CreationDate=11-23-2020 +CreatedBy= +ModificationTime=2:07AM +ModificationDate=03-17-2023 +ModifiedBy= + +[DeviceInfo] +VendorName= +VendorNumber= +ProductName=New Product +ProductNumber= +RevisionNumber=0 +BaudRate_10=1 +BaudRate_20=1 +BaudRate_50=1 +BaudRate_125=1 +BaudRate_250=1 +BaudRate_500=1 +BaudRate_800=1 +BaudRate_1000=1 +SimpleBootUpMaster=0 +SimpleBootUpSlave=0 +Granularity=8 +DynamicChannelsSupported=0 +CompactPDO=0 +GroupMessaging=0 +NrOfRXPDO=4 +NrOfTXPDO=4 +LSS_Supported=1 + +[DummyUsage] +Dummy0001=0 +Dummy0002=1 +Dummy0003=1 +Dummy0004=1 +Dummy0005=1 +Dummy0006=1 +Dummy0007=1 + +[Comments] +Lines=0 + +[MandatoryObjects] +SupportedObjects=3 +1=0x1000 +2=0x1001 +3=0x1018 + +[1000] +ParameterName=Device type +ObjectType=0x7 +;StorageLocation=PERSIST_COMM +DataType=0x0007 +AccessType=ro +DefaultValue=0x00000000 +PDOMapping=0 + +[1001] +ParameterName=Error register +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0005 +AccessType=ro +DefaultValue=0x00 +PDOMapping=1 + +[1018] +ParameterName=Identity +ObjectType=0x9 +;StorageLocation=PERSIST_COMM +SubNumber=0x5 + +[1018sub0] +ParameterName=Highest sub-index supported +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0005 +AccessType=ro +DefaultValue=0x04 +PDOMapping=0 + +[1018sub1] +ParameterName=Vendor-ID +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=ro +DefaultValue=0x00000000 +PDOMapping=0 + +[1018sub2] +ParameterName=Product code +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=ro +DefaultValue=0x00000000 +PDOMapping=0 + +[1018sub3] +ParameterName=Revision number +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=ro +DefaultValue=0x00000000 +PDOMapping=0 + +[1018sub4] +ParameterName=Serial number +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=ro +DefaultValue=0x00000000 +PDOMapping=0 + +[OptionalObjects] +SupportedObjects=30 +1=0x1003 +2=0x1005 +3=0x1006 +4=0x1007 +5=0x1010 +6=0x1011 +7=0x1012 +8=0x1014 +9=0x1015 +10=0x1016 +11=0x1017 +12=0x1019 +13=0x1200 +14=0x1280 +15=0x1400 +16=0x1401 +17=0x1402 +18=0x1403 +19=0x1600 +20=0x1601 +21=0x1602 +22=0x1603 +23=0x1800 +24=0x1801 +25=0x1802 +26=0x1803 +27=0x1A00 +28=0x1A01 +29=0x1A02 +30=0x1A03 + +[1003] +ParameterName=Pre-defined error field +ObjectType=0x8 +;StorageLocation=RAM +SubNumber=0x11 + +[1003sub0] +ParameterName=Number of errors +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0005 +AccessType=rw +DefaultValue= +PDOMapping=0 + +[1003sub1] +ParameterName=Standard error field +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=ro +DefaultValue= +PDOMapping=0 + +[1003sub2] +ParameterName=Standard error field +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=ro +DefaultValue= +PDOMapping=0 + +[1003sub3] +ParameterName=Standard error field +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=ro +DefaultValue= +PDOMapping=0 + +[1003sub4] +ParameterName=Standard error field +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=ro +DefaultValue= +PDOMapping=0 + +[1003sub5] +ParameterName=Standard error field +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=ro +DefaultValue= +PDOMapping=0 + +[1003sub6] +ParameterName=Standard error field +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=ro +DefaultValue= +PDOMapping=0 + +[1003sub7] +ParameterName=Standard error field +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=ro +DefaultValue= +PDOMapping=0 + +[1003sub8] +ParameterName=Standard error field +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=ro +DefaultValue= +PDOMapping=0 + +[1003sub9] +ParameterName=Standard error field +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=ro +DefaultValue= +PDOMapping=0 + +[1003subA] +ParameterName=Standard error field +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=ro +DefaultValue= +PDOMapping=0 + +[1003subB] +ParameterName=Standard error field +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=ro +DefaultValue= +PDOMapping=0 + +[1003subC] +ParameterName=Standard error field +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=ro +DefaultValue= +PDOMapping=0 + +[1003subD] +ParameterName=Standard error field +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=ro +DefaultValue= +PDOMapping=0 + +[1003subE] +ParameterName=Standard error field +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=ro +DefaultValue= +PDOMapping=0 + +[1003subF] +ParameterName=Standard error field +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=ro +DefaultValue= +PDOMapping=0 + +[1003sub10] +ParameterName=Standard error field +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=ro +DefaultValue= +PDOMapping=0 + +[1005] +ParameterName=COB-ID SYNC message +ObjectType=0x7 +;StorageLocation=PERSIST_COMM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000080 +PDOMapping=0 + +[1006] +ParameterName=Communication cycle period +ObjectType=0x7 +;StorageLocation=PERSIST_COMM +DataType=0x0007 +AccessType=rw +DefaultValue=0 +PDOMapping=0 + +[1007] +ParameterName=Synchronous window length +ObjectType=0x7 +;StorageLocation=PERSIST_COMM +DataType=0x0007 +AccessType=rw +DefaultValue=0 +PDOMapping=0 + +[1010] +ParameterName=Store parameters +ObjectType=0x8 +;StorageLocation=RAM +SubNumber=0x5 + +[1010sub0] +ParameterName=Highest sub-index supported +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0005 +AccessType=ro +DefaultValue=0x04 +PDOMapping=0 + +[1010sub1] +ParameterName=Save all parameters +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000001 +PDOMapping=0 + +[1010sub2] +ParameterName=Save communication parameters +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000001 +PDOMapping=0 + +[1010sub3] +ParameterName=Save application parameters +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000001 +PDOMapping=0 + +[1010sub4] +ParameterName=Save manufacturer defined parameters +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000001 +PDOMapping=0 + +[1011] +ParameterName=Restore default parameters +ObjectType=0x8 +;StorageLocation=RAM +SubNumber=0x5 + +[1011sub0] +ParameterName=Highest sub-index supported +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0005 +AccessType=ro +DefaultValue=0x04 +PDOMapping=0 + +[1011sub1] +ParameterName=Restore all default parameters +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000001 +PDOMapping=0 + +[1011sub2] +ParameterName=Restore communication default parameters +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000001 +PDOMapping=0 + +[1011sub3] +ParameterName=Restore application default parameters +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000001 +PDOMapping=0 + +[1011sub4] +ParameterName=Restore manufacturer defined default parameters +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000001 +PDOMapping=0 + +[1012] +ParameterName=COB-ID time stamp object +ObjectType=0x7 +;StorageLocation=PERSIST_COMM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000100 +PDOMapping=0 + +[1014] +ParameterName=COB-ID EMCY +ObjectType=0x7 +;StorageLocation=PERSIST_COMM +DataType=0x0007 +AccessType=rw +DefaultValue=$NODEID+0x80 +PDOMapping=0 + +[1015] +ParameterName=Inhibit time EMCY +ObjectType=0x7 +;StorageLocation=PERSIST_COMM +DataType=0x0006 +AccessType=rw +DefaultValue=0 +PDOMapping=0 + +[1016] +ParameterName=Consumer heartbeat time +ObjectType=0x8 +;StorageLocation=PERSIST_COMM +SubNumber=0x9 + +[1016sub0] +ParameterName=Highest sub-index supported +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0005 +AccessType=ro +DefaultValue=0x08 +PDOMapping=0 + +[1016sub1] +ParameterName=Consumer heartbeat time +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1016sub2] +ParameterName=Consumer heartbeat time +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1016sub3] +ParameterName=Consumer heartbeat time +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1016sub4] +ParameterName=Consumer heartbeat time +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1016sub5] +ParameterName=Consumer heartbeat time +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1016sub6] +ParameterName=Consumer heartbeat time +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1016sub7] +ParameterName=Consumer heartbeat time +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1016sub8] +ParameterName=Consumer heartbeat time +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1017] +ParameterName=Producer heartbeat time +ObjectType=0x7 +;StorageLocation=PERSIST_COMM +DataType=0x0006 +AccessType=rw +DefaultValue=0 +PDOMapping=0 + +[1019] +ParameterName=Synchronous counter overflow value +ObjectType=0x7 +;StorageLocation=PERSIST_COMM +DataType=0x0005 +AccessType=rw +DefaultValue=0 +PDOMapping=0 + +[1200] +ParameterName=SDO server parameter +ObjectType=0x9 +;StorageLocation=RAM +SubNumber=0x3 + +[1200sub0] +ParameterName=Highest sub-index supported +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0005 +AccessType=ro +DefaultValue=2 +PDOMapping=0 + +[1200sub1] +ParameterName=COB-ID client to server (rx) +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=ro +DefaultValue=$NODEID+0x600 +PDOMapping=1 + +[1200sub2] +ParameterName=COB-ID server to client (tx) +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=ro +DefaultValue=$NODEID+0x580 +PDOMapping=1 + +[1280] +ParameterName=SDO client parameter +ObjectType=0x9 +;StorageLocation=PERSIST_COMM +SubNumber=0x4 + +[1280sub0] +ParameterName=Highest sub-index supported +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0005 +AccessType=ro +DefaultValue=0x03 +PDOMapping=0 + +[1280sub1] +ParameterName=COB-ID client to server (tx) +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x80000000 +PDOMapping=1 + +[1280sub2] +ParameterName=COB-ID server to client (rx) +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x80000000 +PDOMapping=1 + +[1280sub3] +ParameterName=Node-ID of the SDO server +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0005 +AccessType=rw +DefaultValue=0x01 +PDOMapping=0 + +[1400] +ParameterName=RPDO communication parameter +ObjectType=0x9 +;StorageLocation=PERSIST_COMM +SubNumber=0x4 + +[1400sub0] +ParameterName=Highest sub-index supported +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0005 +AccessType=ro +DefaultValue=0x05 +PDOMapping=0 + +[1400sub1] +ParameterName=COB-ID used by RPDO +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=$NODEID+0x80000200 +PDOMapping=0 + +[1400sub2] +ParameterName=Transmission type +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0005 +AccessType=rw +DefaultValue=254 +PDOMapping=0 + +[1400sub5] +ParameterName=Event timer +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0006 +AccessType=rw +DefaultValue=0 +PDOMapping=0 + +[1401] +ParameterName=RPDO communication parameter +ObjectType=0x9 +;StorageLocation=PERSIST_COMM +SubNumber=0x4 + +[1401sub0] +ParameterName=Highest sub-index supported +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0005 +AccessType=ro +DefaultValue=0x05 +PDOMapping=0 + +[1401sub1] +ParameterName=COB-ID used by RPDO +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=$NODEID+0x80000300 +PDOMapping=0 + +[1401sub2] +ParameterName=Transmission type +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0005 +AccessType=rw +DefaultValue=254 +PDOMapping=0 + +[1401sub5] +ParameterName=Event timer +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0006 +AccessType=rw +DefaultValue=0 +PDOMapping=0 + +[1402] +ParameterName=RPDO communication parameter +ObjectType=0x9 +;StorageLocation=PERSIST_COMM +SubNumber=0x4 + +[1402sub0] +ParameterName=Highest sub-index supported +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0005 +AccessType=ro +DefaultValue=0x05 +PDOMapping=0 + +[1402sub1] +ParameterName=COB-ID used by RPDO +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=$NODEID+0x80000400 +PDOMapping=0 + +[1402sub2] +ParameterName=Transmission type +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0005 +AccessType=rw +DefaultValue=254 +PDOMapping=0 + +[1402sub5] +ParameterName=Event timer +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0006 +AccessType=rw +DefaultValue=0 +PDOMapping=0 + +[1403] +ParameterName=RPDO communication parameter +ObjectType=0x9 +;StorageLocation=PERSIST_COMM +SubNumber=0x4 + +[1403sub0] +ParameterName=Highest sub-index supported +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0005 +AccessType=ro +DefaultValue=0x05 +PDOMapping=0 + +[1403sub1] +ParameterName=COB-ID used by RPDO +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=$NODEID+0x80000500 +PDOMapping=0 + +[1403sub2] +ParameterName=Transmission type +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0005 +AccessType=rw +DefaultValue=254 +PDOMapping=0 + +[1403sub5] +ParameterName=Event timer +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0006 +AccessType=rw +DefaultValue=0 +PDOMapping=0 + +[1600] +ParameterName=RPDO mapping parameter +ObjectType=0x9 +;StorageLocation=PERSIST_COMM +SubNumber=0x9 + +[1600sub0] +ParameterName=Number of mapped application objects in PDO +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0005 +AccessType=rw +DefaultValue=0 +PDOMapping=0 + +[1600sub1] +ParameterName=Application object 1 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1600sub2] +ParameterName=Application object 2 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1600sub3] +ParameterName=Application object 3 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1600sub4] +ParameterName=Application object 4 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1600sub5] +ParameterName=Application object 5 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1600sub6] +ParameterName=Application object 6 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1600sub7] +ParameterName=Application object 7 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1600sub8] +ParameterName=Application object 8 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1601] +ParameterName=RPDO mapping parameter +ObjectType=0x9 +;StorageLocation=PERSIST_COMM +SubNumber=0x9 + +[1601sub0] +ParameterName=Number of mapped application objects in PDO +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0005 +AccessType=rw +DefaultValue=0 +PDOMapping=0 + +[1601sub1] +ParameterName=Application object 1 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1601sub2] +ParameterName=Application object 2 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1601sub3] +ParameterName=Application object 3 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1601sub4] +ParameterName=Application object 4 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1601sub5] +ParameterName=Application object 5 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1601sub6] +ParameterName=Application object 6 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1601sub7] +ParameterName=Application object 7 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1601sub8] +ParameterName=Application object 8 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1602] +ParameterName=RPDO mapping parameter +ObjectType=0x9 +;StorageLocation=PERSIST_COMM +SubNumber=0x9 + +[1602sub0] +ParameterName=Number of mapped application objects in PDO +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0005 +AccessType=rw +DefaultValue=0 +PDOMapping=0 + +[1602sub1] +ParameterName=Application object 1 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1602sub2] +ParameterName=Application object 2 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1602sub3] +ParameterName=Application object 3 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1602sub4] +ParameterName=Application object 4 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1602sub5] +ParameterName=Application object 5 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1602sub6] +ParameterName=Application object 6 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1602sub7] +ParameterName=Application object 7 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1602sub8] +ParameterName=Application object 8 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1603] +ParameterName=RPDO mapping parameter +ObjectType=0x9 +;StorageLocation=PERSIST_COMM +SubNumber=0x9 + +[1603sub0] +ParameterName=Number of mapped application objects in PDO +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0005 +AccessType=rw +DefaultValue=0 +PDOMapping=0 + +[1603sub1] +ParameterName=Application object 1 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1603sub2] +ParameterName=Application object 2 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1603sub3] +ParameterName=Application object 3 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1603sub4] +ParameterName=Application object 4 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1603sub5] +ParameterName=Application object 5 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1603sub6] +ParameterName=Application object 6 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1603sub7] +ParameterName=Application object 7 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1603sub8] +ParameterName=Application object 8 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1800] +ParameterName=TPDO communication parameter +ObjectType=0x9 +;StorageLocation=PERSIST_COMM +SubNumber=0x6 + +[1800sub0] +ParameterName=Highest sub-index supported +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0005 +AccessType=ro +DefaultValue=0x06 +PDOMapping=0 + +[1800sub1] +ParameterName=COB-ID used by TPDO +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=$NODEID+0xC0000180 +PDOMapping=0 + +[1800sub2] +ParameterName=Transmission type +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0005 +AccessType=rw +DefaultValue=254 +PDOMapping=0 + +[1800sub3] +ParameterName=Inhibit time +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0006 +AccessType=rw +DefaultValue=0 +PDOMapping=0 + +[1800sub5] +ParameterName=Event timer +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0006 +AccessType=rw +DefaultValue=0 +PDOMapping=0 + +[1800sub6] +ParameterName=SYNC start value +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0005 +AccessType=rw +DefaultValue=0 +PDOMapping=0 + +[1801] +ParameterName=TPDO communication parameter +ObjectType=0x9 +;StorageLocation=PERSIST_COMM +SubNumber=0x6 + +[1801sub0] +ParameterName=Highest sub-index supported +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0005 +AccessType=ro +DefaultValue=0x06 +PDOMapping=0 + +[1801sub1] +ParameterName=COB-ID used by TPDO +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=$NODEID+0xC0000280 +PDOMapping=0 + +[1801sub2] +ParameterName=Transmission type +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0005 +AccessType=rw +DefaultValue=254 +PDOMapping=0 + +[1801sub3] +ParameterName=Inhibit time +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0006 +AccessType=rw +DefaultValue=0 +PDOMapping=0 + +[1801sub5] +ParameterName=Event timer +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0006 +AccessType=rw +DefaultValue=0 +PDOMapping=0 + +[1801sub6] +ParameterName=SYNC start value +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0005 +AccessType=rw +DefaultValue=0 +PDOMapping=0 + +[1802] +ParameterName=TPDO communication parameter +ObjectType=0x9 +;StorageLocation=PERSIST_COMM +SubNumber=0x6 + +[1802sub0] +ParameterName=Highest sub-index supported +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0005 +AccessType=ro +DefaultValue=0x06 +PDOMapping=0 + +[1802sub1] +ParameterName=COB-ID used by TPDO +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=$NODEID+0xC0000380 +PDOMapping=0 + +[1802sub2] +ParameterName=Transmission type +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0005 +AccessType=rw +DefaultValue=254 +PDOMapping=0 + +[1802sub3] +ParameterName=Inhibit time +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0006 +AccessType=rw +DefaultValue=0 +PDOMapping=0 + +[1802sub5] +ParameterName=Event timer +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0006 +AccessType=rw +DefaultValue=0 +PDOMapping=0 + +[1802sub6] +ParameterName=SYNC start value +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0005 +AccessType=rw +DefaultValue=0 +PDOMapping=0 + +[1803] +ParameterName=TPDO communication parameter +ObjectType=0x9 +;StorageLocation=PERSIST_COMM +SubNumber=0x6 + +[1803sub0] +ParameterName=Highest sub-index supported +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0005 +AccessType=ro +DefaultValue=0x06 +PDOMapping=0 + +[1803sub1] +ParameterName=COB-ID used by TPDO +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=$NODEID+0xC0000480 +PDOMapping=0 + +[1803sub2] +ParameterName=Transmission type +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0005 +AccessType=rw +DefaultValue=254 +PDOMapping=0 + +[1803sub3] +ParameterName=Inhibit time +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0006 +AccessType=rw +DefaultValue=0 +PDOMapping=0 + +[1803sub5] +ParameterName=Event timer +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0006 +AccessType=rw +DefaultValue=0 +PDOMapping=0 + +[1803sub6] +ParameterName=SYNC start value +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0005 +AccessType=rw +DefaultValue=0 +PDOMapping=0 + +[1A00] +ParameterName=TPDO mapping parameter +ObjectType=0x9 +;StorageLocation=PERSIST_COMM +SubNumber=0x9 + +[1A00sub0] +ParameterName=Number of mapped application objects in PDO +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0005 +AccessType=rw +DefaultValue=0 +PDOMapping=0 + +[1A00sub1] +ParameterName=Application object 1 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1A00sub2] +ParameterName=Application object 2 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1A00sub3] +ParameterName=Application object 3 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1A00sub4] +ParameterName=Application object 4 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1A00sub5] +ParameterName=Application object 5 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1A00sub6] +ParameterName=Application object 6 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1A00sub7] +ParameterName=Application object 7 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1A00sub8] +ParameterName=Application object 8 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1A01] +ParameterName=TPDO mapping parameter +ObjectType=0x9 +;StorageLocation=PERSIST_COMM +SubNumber=0x9 + +[1A01sub0] +ParameterName=Number of mapped application objects in PDO +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0005 +AccessType=rw +DefaultValue=0 +PDOMapping=0 + +[1A01sub1] +ParameterName=Application object 1 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1A01sub2] +ParameterName=Application object 2 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1A01sub3] +ParameterName=Application object 3 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1A01sub4] +ParameterName=Application object 4 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1A01sub5] +ParameterName=Application object 5 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1A01sub6] +ParameterName=Application object 6 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1A01sub7] +ParameterName=Application object 7 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1A01sub8] +ParameterName=Application object 8 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1A02] +ParameterName=TPDO mapping parameter +ObjectType=0x9 +;StorageLocation=PERSIST_COMM +SubNumber=0x9 + +[1A02sub0] +ParameterName=Number of mapped application objects in PDO +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0005 +AccessType=rw +DefaultValue=0 +PDOMapping=0 + +[1A02sub1] +ParameterName=Application object 1 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1A02sub2] +ParameterName=Application object 2 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1A02sub3] +ParameterName=Application object 3 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1A02sub4] +ParameterName=Application object 4 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1A02sub5] +ParameterName=Application object 5 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1A02sub6] +ParameterName=Application object 6 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1A02sub7] +ParameterName=Application object 7 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1A02sub8] +ParameterName=Application object 8 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1A03] +ParameterName=TPDO mapping parameter +ObjectType=0x9 +;StorageLocation=PERSIST_COMM +SubNumber=0x9 + +[1A03sub0] +ParameterName=Number of mapped application objects in PDO +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0005 +AccessType=rw +DefaultValue=0 +PDOMapping=0 + +[1A03sub1] +ParameterName=Application object 1 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1A03sub2] +ParameterName=Application object 2 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1A03sub3] +ParameterName=Application object 3 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1A03sub4] +ParameterName=Application object 4 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1A03sub5] +ParameterName=Application object 5 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1A03sub6] +ParameterName=Application object 6 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1A03sub7] +ParameterName=Application object 7 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[1A03sub8] +ParameterName=Application object 8 +ObjectType=0x7 +;StorageLocation=RAM +DataType=0x0007 +AccessType=rw +DefaultValue=0x00000000 +PDOMapping=0 + +[ManufacturerObjects] +SupportedObjects=0 + diff --git a/example/DS301_profile.md b/example/DS301_profile.md new file mode 100644 index 00000000..7d1b504b --- /dev/null +++ b/example/DS301_profile.md @@ -0,0 +1,747 @@ +CANopen device documentation +============================ +**New Product** + + + +| | | +| ------------ | ------------------------------ | +| Project File | DS301_profile.xpd | +| File Version | 1 | +| Created | 23. 11. 2020 12:00:00 | +| Created By | | +| Modified | 17. 03. 2023 02:07:28 | +| Modified By | | + +This file was automatically generated by [CANopenEditor](https://github.com/CANopenNode/CANopenEditor) v4.1-1-ga49a51a + +[TOC] + + +Device Information +------------------ +| | | +| ------------ | ------------------------------ | +| Vendor Name | | +| Vendor ID | | +| Product Name | New Product | +| Product ID | | +| Granularity | 8 | +| RPDO count | 4 | +| TPDO count | 4 | +| LSS Slave | True | +| LSS Master | False | + +#### Supported Baud rates +* [x] 10 kBit/s +* [x] 20 kBit/s +* [x] 50 kBit/s +* [x] 125 kBit/s +* [x] 250 kBit/s +* [x] 500 kBit/s +* [x] 800 kBit/s +* [x] 1000 kBit/s +* [ ] auto + + +PDO Mapping +----------- + +Communication Specific Parameters +--------------------------------- + +### 0x1000 - Device type +| Object Type | Count Label | Storage Group | +| ----------- | -------------- | -------------- | +| VAR | NMT | PERSIST_COMM | + +| Data Type | SDO | PDO | SRDO | Default Value | +| ----------------------- | --- | --- | ---- | ------------------------------- | +| UNSIGNED32 | ro | no | no | 0x00000000 | + +* bit 16-31: Additional information +* bit 0-15: Device profile number + +### 0x1001 - Error register +| Object Type | Count Label | Storage Group | +| ----------- | -------------- | -------------- | +| VAR | EM | RAM | + +| Data Type | SDO | PDO | SRDO | Default Value | +| ----------------------- | --- | --- | ---- | ------------------------------- | +| UNSIGNED8 | ro | t | no | 0x00 | + +* bit 7: manufacturer specific +* bit 6: Reserved (always 0) +* bit 5: device profile specific +* bit 4: communication error (overrun, error state) +* bit 3: temperature +* bit 2: voltage +* bit 1: current +* bit 0: generic error + +### 0x1003 - Pre-defined error field +| Object Type | Count Label | Storage Group | +| ----------- | -------------- | -------------- | +| ARRAY | | RAM | + +| Sub | Name | Data Type | SDO | PDO | SRDO | Default Value | +| ---- | --------------------- | ---------- | --- | --- | ---- | ------------- | +| 0x00 | Number of errors | UNSIGNED8 | rw | no | no | | +| 0x01 | Standard error field | UNSIGNED32 | ro | no | no | | +| 0x02 | Standard error field | UNSIGNED32 | ro | no | no | | +| 0x03 | Standard error field | UNSIGNED32 | ro | no | no | | +| 0x04 | Standard error field | UNSIGNED32 | ro | no | no | | +| 0x05 | Standard error field | UNSIGNED32 | ro | no | no | | +| 0x06 | Standard error field | UNSIGNED32 | ro | no | no | | +| 0x07 | Standard error field | UNSIGNED32 | ro | no | no | | +| 0x08 | Standard error field | UNSIGNED32 | ro | no | no | | +| 0x09 | Standard error field | UNSIGNED32 | ro | no | no | | +| 0x0A | Standard error field | UNSIGNED32 | ro | no | no | | +| 0x0B | Standard error field | UNSIGNED32 | ro | no | no | | +| 0x0C | Standard error field | UNSIGNED32 | ro | no | no | | +| 0x0D | Standard error field | UNSIGNED32 | ro | no | no | | +| 0x0E | Standard error field | UNSIGNED32 | ro | no | no | | +| 0x0F | Standard error field | UNSIGNED32 | ro | no | no | | +| 0x10 | Standard error field | UNSIGNED32 | ro | no | no | | + +* Sub Index 0: Contains number of actual errors. 0 can be written to clear error history. +* sub-index 1 and above: + * bit 16-31: Manufacturer specific additional information + * bit 0-15: Error code as transmited in the Emergency object + +### 0x1005 - COB-ID SYNC message +| Object Type | Count Label | Storage Group | +| ----------- | -------------- | -------------- | +| VAR | SYNC | PERSIST_COMM | + +| Data Type | SDO | PDO | SRDO | Default Value | +| ----------------------- | --- | --- | ---- | ------------------------------- | +| UNSIGNED32 | rw | no | no | 0x00000080 | + +* bit 31: set to 0 +* bit 30: If set, CANopen device generates SYNC object +* bit 11-29: set to 0 +* bit 0-10: 11-bit CAN-ID + +### 0x1006 - Communication cycle period +| Object Type | Count Label | Storage Group | +| ----------- | -------------- | -------------- | +| VAR | SYNC_PROD | PERSIST_COMM | + +| Data Type | SDO | PDO | SRDO | Default Value | +| ----------------------- | --- | --- | ---- | ------------------------------- | +| UNSIGNED32 | rw | no | no | 0 | + +Period of SYNC transmission in µs (0 = transmission disabled). + +### 0x1007 - Synchronous window length +| Object Type | Count Label | Storage Group | +| ----------- | -------------- | -------------- | +| VAR | | PERSIST_COMM | + +| Data Type | SDO | PDO | SRDO | Default Value | +| ----------------------- | --- | --- | ---- | ------------------------------- | +| UNSIGNED32 | rw | no | no | 0 | + +Synchronous window leghth in µs (0 = not used). All synchronous PDOs must be transmitted within this time window. + +### 0x1010 - Store parameters +| Object Type | Count Label | Storage Group | +| ----------- | -------------- | -------------- | +| ARRAY | STORAGE | RAM | + +| Sub | Name | Data Type | SDO | PDO | SRDO | Default Value | +| ---- | --------------------- | ---------- | --- | --- | ---- | ------------- | +| 0x00 | Highest sub-index supported| UNSIGNED8 | ro | no | no | 0x04 | +| 0x01 | Save all parameters | UNSIGNED32 | rw | no | no | 0x00000001 | +| 0x02 | Save communication parameters| UNSIGNED32 | rw | no | no | 0x00000001 | +| 0x03 | Save application parameters| UNSIGNED32 | rw | no | no | 0x00000001 | +| 0x04 | Save manufacturer defined parameters| UNSIGNED32 | rw | no | no | 0x00000001 | + +Sub-indexes 1 and above: +* Reading provides information about its storage functionality: + * bit 1: If set, CANopen device saves parameters autonomously + * bit 0: If set, CANopen device saves parameters on command +* Writing value 0x65766173 ('s','a','v','e' from LSB to MSB) stores corresponding data. + +### 0x1011 - Restore default parameters +| Object Type | Count Label | Storage Group | +| ----------- | -------------- | -------------- | +| ARRAY | | RAM | + +| Sub | Name | Data Type | SDO | PDO | SRDO | Default Value | +| ---- | --------------------- | ---------- | --- | --- | ---- | ------------- | +| 0x00 | Highest sub-index supported| UNSIGNED8 | ro | no | no | 0x04 | +| 0x01 | Restore all default parameters| UNSIGNED32 | rw | no | no | 0x00000001 | +| 0x02 | Restore communication default parameters| UNSIGNED32 | rw | no | no | 0x00000001 | +| 0x03 | Restore application default parameters| UNSIGNED32 | rw | no | no | 0x00000001 | +| 0x04 | Restore manufacturer defined default parameters| UNSIGNED32 | rw | no | no | 0x00000001 | + +Sub-indexes 1 and above: +* Reading provides information about its restoring capability: + * bit 0: If set, CANopen device restores parameters +* Writing value 0x64616F6C ('l','o','a','d' from LSB to MSB) restores corresponding data. + +### 0x1012 - COB-ID time stamp object +| Object Type | Count Label | Storage Group | +| ----------- | -------------- | -------------- | +| VAR | TIME | PERSIST_COMM | + +| Data Type | SDO | PDO | SRDO | Default Value | +| ----------------------- | --- | --- | ---- | ------------------------------- | +| UNSIGNED32 | rw | no | no | 0x00000100 | + +* bit 31: If set, CANopen device consumes TIME message +* bit 30: If set, CANopen device produces TIME message +* bit 11-29: set to 0 +* bit 0-10: 11-bit CAN-ID + +### 0x1014 - COB-ID EMCY +| Object Type | Count Label | Storage Group | +| ----------- | -------------- | -------------- | +| VAR | EM_PROD | PERSIST_COMM | + +| Data Type | SDO | PDO | SRDO | Default Value | +| ----------------------- | --- | --- | ---- | ------------------------------- | +| UNSIGNED32 | rw | no | no | $NODEID+0x80 | + +* bit 31: If set, EMCY does NOT exist / is NOT valid +* bit 11-30: set to 0 +* bit 0-10: 11-bit CAN-ID + +### 0x1015 - Inhibit time EMCY +| Object Type | Count Label | Storage Group | +| ----------- | -------------- | -------------- | +| VAR | | PERSIST_COMM | + +| Data Type | SDO | PDO | SRDO | Default Value | +| ----------------------- | --- | --- | ---- | ------------------------------- | +| UNSIGNED16 | rw | no | no | 0 | + +Inhibit time of emergency message in multiples of 100µs. The value 0 disables the inhibit time. + +### 0x1016 - Consumer heartbeat time +| Object Type | Count Label | Storage Group | +| ----------- | -------------- | -------------- | +| ARRAY | HB_CONS | PERSIST_COMM | + +| Sub | Name | Data Type | SDO | PDO | SRDO | Default Value | +| ---- | --------------------- | ---------- | --- | --- | ---- | ------------- | +| 0x00 | Highest sub-index supported| UNSIGNED8 | ro | no | no | 0x08 | +| 0x01 | Consumer heartbeat time| UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x02 | Consumer heartbeat time| UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x03 | Consumer heartbeat time| UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x04 | Consumer heartbeat time| UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x05 | Consumer heartbeat time| UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x06 | Consumer heartbeat time| UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x07 | Consumer heartbeat time| UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x08 | Consumer heartbeat time| UNSIGNED32 | rw | no | no | 0x00000000 | + +Consumer Heartbeat Time: + * bit 24-31: set to 0 + * bit 16-23: Node ID of the monitored node. If 0 or greater than 127, sub-entry is not used. + * bit 0-15: Heartbeat time in ms (if 0, sub-intry is not used). Value should be higher than the corresponding producer heartbeat time. + +### 0x1017 - Producer heartbeat time +| Object Type | Count Label | Storage Group | +| ----------- | -------------- | -------------- | +| VAR | HB_PROD | PERSIST_COMM | + +| Data Type | SDO | PDO | SRDO | Default Value | +| ----------------------- | --- | --- | ---- | ------------------------------- | +| UNSIGNED16 | rw | no | no | 0 | + +Heartbeat producer time in ms (0 = disable transmission). + +### 0x1018 - Identity +| Object Type | Count Label | Storage Group | +| ----------- | -------------- | -------------- | +| RECORD | | PERSIST_COMM | + +| Sub | Name | Data Type | SDO | PDO | SRDO | Default Value | +| ---- | --------------------- | ---------- | --- | --- | ---- | ------------- | +| 0x00 | Highest sub-index supported| UNSIGNED8 | ro | no | no | 0x04 | +| 0x01 | Vendor-ID | UNSIGNED32 | ro | no | no | 0x00000000 | +| 0x02 | Product code | UNSIGNED32 | ro | no | no | 0x00000000 | +| 0x03 | Revision number | UNSIGNED32 | ro | no | no | 0x00000000 | +| 0x04 | Serial number | UNSIGNED32 | ro | no | no | 0x00000000 | + +* Vendor-ID, assigned by CiA +* Product code, manufacturer specific +* Revision number: + * bit 16-31: Major revision number (CANopen behavior has changed) + * bit 0-15: Minor revision num. (CANopen behavior has not changed) +* Serial number, manufacturer specific + +### 0x1019 - Synchronous counter overflow value +| Object Type | Count Label | Storage Group | +| ----------- | -------------- | -------------- | +| VAR | | PERSIST_COMM | + +| Data Type | SDO | PDO | SRDO | Default Value | +| ----------------------- | --- | --- | ---- | ------------------------------- | +| UNSIGNED8 | rw | no | no | 0 | + +* Value 0: SYNC message is transmitted with data length 0. +* Value 1: reserved. +* Value 2-240: SYNC message has one data byte, which contains the counter. +* Value 241-255: reserved. + +### 0x1200 - SDO server parameter +| Object Type | Count Label | Storage Group | +| ----------- | -------------- | -------------- | +| RECORD | SDO_SRV | RAM | + +| Sub | Name | Data Type | SDO | PDO | SRDO | Default Value | +| ---- | --------------------- | ---------- | --- | --- | ---- | ------------- | +| 0x00 | Highest sub-index supported| UNSIGNED8 | ro | no | no | 2 | +| 0x01 | COB-ID client to server (rx)| UNSIGNED32 | ro | t | no | $NODEID+0x600 | +| 0x02 | COB-ID server to client (tx)| UNSIGNED32 | ro | t | no | $NODEID+0x580 | + +Sub-indexes 1 and 2: +* bit 11-31: set to 0 +* bit 0-10: 11-bit CAN-ID + +### 0x1280 - SDO client parameter +| Object Type | Count Label | Storage Group | +| ----------- | -------------- | -------------- | +| RECORD | SDO_CLI | PERSIST_COMM | + +| Sub | Name | Data Type | SDO | PDO | SRDO | Default Value | +| ---- | --------------------- | ---------- | --- | --- | ---- | ------------- | +| 0x00 | Highest sub-index supported| UNSIGNED8 | ro | no | no | 0x03 | +| 0x01 | COB-ID client to server (tx)| UNSIGNED32 | rw | tr | no | 0x80000000 | +| 0x02 | COB-ID server to client (rx)| UNSIGNED32 | rw | tr | no | 0x80000000 | +| 0x03 | Node-ID of the SDO server| UNSIGNED8 | rw | no | no | 0x01 | + +* Sub-indexes 1 and 2: + * bit 31: If set, SDO does NOT exist / is NOT valid + * bit 30: If set, value is assigned dynamically + * bit 11-29: set to 0 + * bit 0-10: 11-bit CAN-ID +* Node-ID of the SDO server, 0x01 to 0x7F + +### 0x1400 - RPDO communication parameter +| Object Type | Count Label | Storage Group | +| ----------- | -------------- | -------------- | +| RECORD | RPDO | PERSIST_COMM | + +| Sub | Name | Data Type | SDO | PDO | SRDO | Default Value | +| ---- | --------------------- | ---------- | --- | --- | ---- | ------------- | +| 0x00 | Highest sub-index supported| UNSIGNED8 | ro | no | no | 0x05 | +| 0x01 | COB-ID used by RPDO | UNSIGNED32 | rw | no | no | $NODEID+0x80000200| +| 0x02 | Transmission type | UNSIGNED8 | rw | no | no | 254 | +| 0x05 | Event timer | UNSIGNED16 | rw | no | no | 0 | + +* COB-ID used by RPDO: + * bit 31: If set, PDO does not exist / is not valid + * bit 11-30: set to 0 + * bit 0-10: 11-bit CAN-ID +* Transmission type: + * Value 0-240: synchronous, processed after next reception of SYNC object + * Value 241-253: not used + * Value 254: event-driven (manufacturer-specific) + * Value 255: event-driven (device profile and application profile specific) +* Event timer in ms (0 = disabled) for deadline monitoring. + +### 0x1401 - RPDO communication parameter +| Object Type | Count Label | Storage Group | +| ----------- | -------------- | -------------- | +| RECORD | RPDO | PERSIST_COMM | + +| Sub | Name | Data Type | SDO | PDO | SRDO | Default Value | +| ---- | --------------------- | ---------- | --- | --- | ---- | ------------- | +| 0x00 | Highest sub-index supported| UNSIGNED8 | ro | no | no | 0x05 | +| 0x01 | COB-ID used by RPDO | UNSIGNED32 | rw | no | no | $NODEID+0x80000300| +| 0x02 | Transmission type | UNSIGNED8 | rw | no | no | 254 | +| 0x05 | Event timer | UNSIGNED16 | rw | no | no | 0 | + +* COB-ID used by RPDO: + * bit 31: If set, PDO does not exist / is not valid + * bit 11-30: set to 0 + * bit 0-10: 11-bit CAN-ID +* Transmission type: + * Value 0-240: synchronous, processed after next reception of SYNC object + * Value 241-253: not used + * Value 254: event-driven (manufacturer-specific) + * Value 255: event-driven (device profile and application profile specific) +* Event timer in ms (0 = disabled) for deadline monitoring. + +### 0x1402 - RPDO communication parameter +| Object Type | Count Label | Storage Group | +| ----------- | -------------- | -------------- | +| RECORD | RPDO | PERSIST_COMM | + +| Sub | Name | Data Type | SDO | PDO | SRDO | Default Value | +| ---- | --------------------- | ---------- | --- | --- | ---- | ------------- | +| 0x00 | Highest sub-index supported| UNSIGNED8 | ro | no | no | 0x05 | +| 0x01 | COB-ID used by RPDO | UNSIGNED32 | rw | no | no | $NODEID+0x80000400| +| 0x02 | Transmission type | UNSIGNED8 | rw | no | no | 254 | +| 0x05 | Event timer | UNSIGNED16 | rw | no | no | 0 | + +* COB-ID used by RPDO: + * bit 31: If set, PDO does not exist / is not valid + * bit 11-30: set to 0 + * bit 0-10: 11-bit CAN-ID +* Transmission type: + * Value 0-240: synchronous, processed after next reception of SYNC object + * Value 241-253: not used + * Value 254: event-driven (manufacturer-specific) + * Value 255: event-driven (device profile and application profile specific) +* Event timer in ms (0 = disabled) for deadline monitoring. + +### 0x1403 - RPDO communication parameter +| Object Type | Count Label | Storage Group | +| ----------- | -------------- | -------------- | +| RECORD | RPDO | PERSIST_COMM | + +| Sub | Name | Data Type | SDO | PDO | SRDO | Default Value | +| ---- | --------------------- | ---------- | --- | --- | ---- | ------------- | +| 0x00 | Highest sub-index supported| UNSIGNED8 | ro | no | no | 0x05 | +| 0x01 | COB-ID used by RPDO | UNSIGNED32 | rw | no | no | $NODEID+0x80000500| +| 0x02 | Transmission type | UNSIGNED8 | rw | no | no | 254 | +| 0x05 | Event timer | UNSIGNED16 | rw | no | no | 0 | + +* COB-ID used by RPDO: + * bit 31: If set, PDO does not exist / is not valid + * bit 11-30: set to 0 + * bit 0-10: 11-bit CAN-ID +* Transmission type: + * Value 0-240: synchronous, processed after next reception of SYNC object + * Value 241-253: not used + * Value 254: event-driven (manufacturer-specific) + * Value 255: event-driven (device profile and application profile specific) +* Event timer in ms (0 = disabled) for deadline monitoring. + +### 0x1600 - RPDO mapping parameter +| Object Type | Count Label | Storage Group | +| ----------- | -------------- | -------------- | +| RECORD | | PERSIST_COMM | + +| Sub | Name | Data Type | SDO | PDO | SRDO | Default Value | +| ---- | --------------------- | ---------- | --- | --- | ---- | ------------- | +| 0x00 | Number of mapped application objects in PDO| UNSIGNED8 | rw | no | no | 0 | +| 0x01 | Application object 1 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x02 | Application object 2 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x03 | Application object 3 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x04 | Application object 4 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x05 | Application object 5 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x06 | Application object 6 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x07 | Application object 7 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x08 | Application object 8 | UNSIGNED32 | rw | no | no | 0x00000000 | + +* Number of mapped application objects in PDO: + * Value 0: mapping is disabled. + * Value 1: sub-index 0x01 is valid. + * Value 2-8: sub-indexes 0x01 to (0x02 to 0x08) are valid. +* Application object 1-8: + * bit 16-31: index + * bit 8-15: sub-index + * bit 0-7: data length in bits + +### 0x1601 - RPDO mapping parameter +| Object Type | Count Label | Storage Group | +| ----------- | -------------- | -------------- | +| RECORD | | PERSIST_COMM | + +| Sub | Name | Data Type | SDO | PDO | SRDO | Default Value | +| ---- | --------------------- | ---------- | --- | --- | ---- | ------------- | +| 0x00 | Number of mapped application objects in PDO| UNSIGNED8 | rw | no | no | 0 | +| 0x01 | Application object 1 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x02 | Application object 2 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x03 | Application object 3 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x04 | Application object 4 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x05 | Application object 5 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x06 | Application object 6 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x07 | Application object 7 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x08 | Application object 8 | UNSIGNED32 | rw | no | no | 0x00000000 | + +* Number of mapped application objects in PDO: + * Value 0: mapping is disabled. + * Value 1: sub-index 0x01 is valid. + * Value 2-8: sub-indexes 0x01 to (0x02 to 0x08) are valid. +* Application object 1-8: + * bit 16-31: index + * bit 8-15: sub-index + * bit 0-7: data length in bits + +### 0x1602 - RPDO mapping parameter +| Object Type | Count Label | Storage Group | +| ----------- | -------------- | -------------- | +| RECORD | | PERSIST_COMM | + +| Sub | Name | Data Type | SDO | PDO | SRDO | Default Value | +| ---- | --------------------- | ---------- | --- | --- | ---- | ------------- | +| 0x00 | Number of mapped application objects in PDO| UNSIGNED8 | rw | no | no | 0 | +| 0x01 | Application object 1 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x02 | Application object 2 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x03 | Application object 3 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x04 | Application object 4 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x05 | Application object 5 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x06 | Application object 6 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x07 | Application object 7 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x08 | Application object 8 | UNSIGNED32 | rw | no | no | 0x00000000 | + +* Number of mapped application objects in PDO: + * Value 0: mapping is disabled. + * Value 1: sub-index 0x01 is valid. + * Value 2-8: sub-indexes 0x01 to (0x02 to 0x08) are valid. +* Application object 1-8: + * bit 16-31: index + * bit 8-15: sub-index + * bit 0-7: data length in bits + +### 0x1603 - RPDO mapping parameter +| Object Type | Count Label | Storage Group | +| ----------- | -------------- | -------------- | +| RECORD | | PERSIST_COMM | + +| Sub | Name | Data Type | SDO | PDO | SRDO | Default Value | +| ---- | --------------------- | ---------- | --- | --- | ---- | ------------- | +| 0x00 | Number of mapped application objects in PDO| UNSIGNED8 | rw | no | no | 0 | +| 0x01 | Application object 1 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x02 | Application object 2 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x03 | Application object 3 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x04 | Application object 4 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x05 | Application object 5 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x06 | Application object 6 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x07 | Application object 7 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x08 | Application object 8 | UNSIGNED32 | rw | no | no | 0x00000000 | + +* Number of mapped application objects in PDO: + * Value 0: mapping is disabled. + * Value 1: sub-index 0x01 is valid. + * Value 2-8: sub-indexes 0x01 to (0x02 to 0x08) are valid. +* Application object 1-8: + * bit 16-31: index + * bit 8-15: sub-index + * bit 0-7: data length in bits + +### 0x1800 - TPDO communication parameter +| Object Type | Count Label | Storage Group | +| ----------- | -------------- | -------------- | +| RECORD | TPDO | PERSIST_COMM | + +| Sub | Name | Data Type | SDO | PDO | SRDO | Default Value | +| ---- | --------------------- | ---------- | --- | --- | ---- | ------------- | +| 0x00 | Highest sub-index supported| UNSIGNED8 | ro | no | no | 0x06 | +| 0x01 | COB-ID used by TPDO | UNSIGNED32 | rw | no | no | $NODEID+0xC0000180| +| 0x02 | Transmission type | UNSIGNED8 | rw | no | no | 254 | +| 0x03 | Inhibit time | UNSIGNED16 | rw | no | no | 0 | +| 0x05 | Event timer | UNSIGNED16 | rw | no | no | 0 | +| 0x06 | SYNC start value | UNSIGNED8 | rw | no | no | 0 | + +* COB-ID used by RPDO: + * bit 31: If set, PDO does not exist / is not valid + * bit 30: If set, NO RTR is allowed on this PDO + * bit 11-29: set to 0 + * bit 0-10: 11-bit CAN-ID +* Transmission type: + * Value 0: synchronous (acyclic) + * Value 1-240: synchronous (cyclic every (1-240)-th sync) + * Value 241-253: not used + * Value 254: event-driven (manufacturer-specific) + * Value 255: event-driven (device profile and application profile specific) +* Inhibit time in multiple of 100µs, if the transmission type is set to 254 or 255 (0 = disabled). +* Event timer interval in ms, if the transmission type is set to 254 or 255 (0 = disabled). +* SYNC start value + * Value 0: Counter of the SYNC message shall not be processed. + * Value 1-240: The SYNC message with the counter value equal to this value shall be regarded as the first received SYNC message. + +### 0x1801 - TPDO communication parameter +| Object Type | Count Label | Storage Group | +| ----------- | -------------- | -------------- | +| RECORD | TPDO | PERSIST_COMM | + +| Sub | Name | Data Type | SDO | PDO | SRDO | Default Value | +| ---- | --------------------- | ---------- | --- | --- | ---- | ------------- | +| 0x00 | Highest sub-index supported| UNSIGNED8 | ro | no | no | 0x06 | +| 0x01 | COB-ID used by TPDO | UNSIGNED32 | rw | no | no | $NODEID+0xC0000280| +| 0x02 | Transmission type | UNSIGNED8 | rw | no | no | 254 | +| 0x03 | Inhibit time | UNSIGNED16 | rw | no | no | 0 | +| 0x05 | Event timer | UNSIGNED16 | rw | no | no | 0 | +| 0x06 | SYNC start value | UNSIGNED8 | rw | no | no | 0 | + +* COB-ID used by RPDO: + * bit 31: If set, PDO does not exist / is not valid + * bit 30: If set, NO RTR is allowed on this PDO + * bit 11-29: set to 0 + * bit 0-10: 11-bit CAN-ID +* Transmission type: + * Value 0: synchronous (acyclic) + * Value 1-240: synchronous (cyclic every (1-240)-th sync) + * Value 241-253: not used + * Value 254: event-driven (manufacturer-specific) + * Value 255: event-driven (device profile and application profile specific) +* Inhibit time in multiple of 100µs, if the transmission type is set to 254 or 255 (0 = disabled). +* Event timer interval in ms, if the transmission type is set to 254 or 255 (0 = disabled). +* SYNC start value + * Value 0: Counter of the SYNC message shall not be processed. + * Value 1-240: The SYNC message with the counter value equal to this value shall be regarded as the first received SYNC message. + +### 0x1802 - TPDO communication parameter +| Object Type | Count Label | Storage Group | +| ----------- | -------------- | -------------- | +| RECORD | TPDO | PERSIST_COMM | + +| Sub | Name | Data Type | SDO | PDO | SRDO | Default Value | +| ---- | --------------------- | ---------- | --- | --- | ---- | ------------- | +| 0x00 | Highest sub-index supported| UNSIGNED8 | ro | no | no | 0x06 | +| 0x01 | COB-ID used by TPDO | UNSIGNED32 | rw | no | no | $NODEID+0xC0000380| +| 0x02 | Transmission type | UNSIGNED8 | rw | no | no | 254 | +| 0x03 | Inhibit time | UNSIGNED16 | rw | no | no | 0 | +| 0x05 | Event timer | UNSIGNED16 | rw | no | no | 0 | +| 0x06 | SYNC start value | UNSIGNED8 | rw | no | no | 0 | + +* COB-ID used by RPDO: + * bit 31: If set, PDO does not exist / is not valid + * bit 30: If set, NO RTR is allowed on this PDO + * bit 11-29: set to 0 + * bit 0-10: 11-bit CAN-ID +* Transmission type: + * Value 0: synchronous (acyclic) + * Value 1-240: synchronous (cyclic every (1-240)-th sync) + * Value 241-253: not used + * Value 254: event-driven (manufacturer-specific) + * Value 255: event-driven (device profile and application profile specific) +* Inhibit time in multiple of 100µs, if the transmission type is set to 254 or 255 (0 = disabled). +* Event timer interval in ms, if the transmission type is set to 254 or 255 (0 = disabled). +* SYNC start value + * Value 0: Counter of the SYNC message shall not be processed. + * Value 1-240: The SYNC message with the counter value equal to this value shall be regarded as the first received SYNC message. + +### 0x1803 - TPDO communication parameter +| Object Type | Count Label | Storage Group | +| ----------- | -------------- | -------------- | +| RECORD | TPDO | PERSIST_COMM | + +| Sub | Name | Data Type | SDO | PDO | SRDO | Default Value | +| ---- | --------------------- | ---------- | --- | --- | ---- | ------------- | +| 0x00 | Highest sub-index supported| UNSIGNED8 | ro | no | no | 0x06 | +| 0x01 | COB-ID used by TPDO | UNSIGNED32 | rw | no | no | $NODEID+0xC0000480| +| 0x02 | Transmission type | UNSIGNED8 | rw | no | no | 254 | +| 0x03 | Inhibit time | UNSIGNED16 | rw | no | no | 0 | +| 0x05 | Event timer | UNSIGNED16 | rw | no | no | 0 | +| 0x06 | SYNC start value | UNSIGNED8 | rw | no | no | 0 | + +* COB-ID used by RPDO: + * bit 31: If set, PDO does not exist / is not valid + * bit 30: If set, NO RTR is allowed on this PDO + * bit 11-29: set to 0 + * bit 0-10: 11-bit CAN-ID +* Transmission type: + * Value 0: synchronous (acyclic) + * Value 1-240: synchronous (cyclic every (1-240)-th sync) + * Value 241-253: not used + * Value 254: event-driven (manufacturer-specific) + * Value 255: event-driven (device profile and application profile specific) +* Inhibit time in multiple of 100µs, if the transmission type is set to 254 or 255 (0 = disabled). +* Event timer interval in ms, if the transmission type is set to 254 or 255 (0 = disabled). +* SYNC start value + * Value 0: Counter of the SYNC message shall not be processed. + * Value 1-240: The SYNC message with the counter value equal to this value shall be regarded as the first received SYNC message. + +### 0x1A00 - TPDO mapping parameter +| Object Type | Count Label | Storage Group | +| ----------- | -------------- | -------------- | +| RECORD | | PERSIST_COMM | + +| Sub | Name | Data Type | SDO | PDO | SRDO | Default Value | +| ---- | --------------------- | ---------- | --- | --- | ---- | ------------- | +| 0x00 | Number of mapped application objects in PDO| UNSIGNED8 | rw | no | no | 0 | +| 0x01 | Application object 1 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x02 | Application object 2 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x03 | Application object 3 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x04 | Application object 4 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x05 | Application object 5 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x06 | Application object 6 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x07 | Application object 7 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x08 | Application object 8 | UNSIGNED32 | rw | no | no | 0x00000000 | + +* Number of mapped application objects in PDO: + * Value 0: mapping is disabled. + * Value 1: sub-index 0x01 is valid. + * Value 2-8: sub-indexes 0x01 to (0x02 to 0x08) are valid. +* Application object 1-8: + * bit 16-31: index + * bit 8-15: sub-index + * bit 0-7: data length in bits + +### 0x1A01 - TPDO mapping parameter +| Object Type | Count Label | Storage Group | +| ----------- | -------------- | -------------- | +| RECORD | | PERSIST_COMM | + +| Sub | Name | Data Type | SDO | PDO | SRDO | Default Value | +| ---- | --------------------- | ---------- | --- | --- | ---- | ------------- | +| 0x00 | Number of mapped application objects in PDO| UNSIGNED8 | rw | no | no | 0 | +| 0x01 | Application object 1 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x02 | Application object 2 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x03 | Application object 3 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x04 | Application object 4 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x05 | Application object 5 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x06 | Application object 6 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x07 | Application object 7 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x08 | Application object 8 | UNSIGNED32 | rw | no | no | 0x00000000 | + +* Number of mapped application objects in PDO: + * Value 0: mapping is disabled. + * Value 1: sub-index 0x01 is valid. + * Value 2-8: sub-indexes 0x01 to (0x02 to 0x08) are valid. +* Application object 1-8: + * bit 16-31: index + * bit 8-15: sub-index + * bit 0-7: data length in bits + +### 0x1A02 - TPDO mapping parameter +| Object Type | Count Label | Storage Group | +| ----------- | -------------- | -------------- | +| RECORD | | PERSIST_COMM | + +| Sub | Name | Data Type | SDO | PDO | SRDO | Default Value | +| ---- | --------------------- | ---------- | --- | --- | ---- | ------------- | +| 0x00 | Number of mapped application objects in PDO| UNSIGNED8 | rw | no | no | 0 | +| 0x01 | Application object 1 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x02 | Application object 2 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x03 | Application object 3 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x04 | Application object 4 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x05 | Application object 5 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x06 | Application object 6 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x07 | Application object 7 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x08 | Application object 8 | UNSIGNED32 | rw | no | no | 0x00000000 | + +* Number of mapped application objects in PDO: + * Value 0: mapping is disabled. + * Value 1: sub-index 0x01 is valid. + * Value 2-8: sub-indexes 0x01 to (0x02 to 0x08) are valid. +* Application object 1-8: + * bit 16-31: index + * bit 8-15: sub-index + * bit 0-7: data length in bits + +### 0x1A03 - TPDO mapping parameter +| Object Type | Count Label | Storage Group | +| ----------- | -------------- | -------------- | +| RECORD | | PERSIST_COMM | + +| Sub | Name | Data Type | SDO | PDO | SRDO | Default Value | +| ---- | --------------------- | ---------- | --- | --- | ---- | ------------- | +| 0x00 | Number of mapped application objects in PDO| UNSIGNED8 | rw | no | no | 0 | +| 0x01 | Application object 1 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x02 | Application object 2 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x03 | Application object 3 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x04 | Application object 4 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x05 | Application object 5 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x06 | Application object 6 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x07 | Application object 7 | UNSIGNED32 | rw | no | no | 0x00000000 | +| 0x08 | Application object 8 | UNSIGNED32 | rw | no | no | 0x00000000 | + +* Number of mapped application objects in PDO: + * Value 0: mapping is disabled. + * Value 1: sub-index 0x01 is valid. + * Value 2-8: sub-indexes 0x01 to (0x02 to 0x08) are valid. +* Application object 1-8: + * bit 16-31: index + * bit 8-15: sub-index + * bit 0-7: data length in bits diff --git a/example/DS301_profile.xpd b/example/DS301_profile.xpd new file mode 100644 index 00000000..40de57b1 --- /dev/null +++ b/example/DS301_profile.xpd @@ -0,0 +1,2639 @@ + + + + + + + CANopen device profile + 1.1 + + + Device + + 1 + 1 + CANopen + + + + + + + New Product + + 0 + 0 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * bit 16-31: Additional information +* bit 0-15: Device profile number + + + + + + + * bit 7: manufacturer specific +* bit 6: Reserved (always 0) +* bit 5: device profile specific +* bit 4: communication error (overrun, error state) +* bit 3: temperature +* bit 2: voltage +* bit 1: current +* bit 0: generic error + + + + + + + + + + + + * Sub Index 0: Contains number of actual errors. 0 can be written to clear error history. +* sub-index 1 and above: + * bit 16-31: Manufacturer specific additional information + * bit 0-15: Error code as transmited in the Emergency object + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * bit 31: set to 0 +* bit 30: If set, CANopen device generates SYNC object +* bit 11-29: set to 0 +* bit 0-10: 11-bit CAN-ID + + + + + + + Period of SYNC transmission in µs (0 = transmission disabled). + + + + + + + Synchronous window leghth in µs (0 = not used). All synchronous PDOs must be transmitted within this time window. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sub-indexes 1 and above: +* Reading provides information about its storage functionality: + * bit 1: If set, CANopen device saves parameters autonomously + * bit 0: If set, CANopen device saves parameters on command +* Writing value 0x65766173 ('s','a','v','e' from LSB to MSB) stores corresponding data. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sub-indexes 1 and above: +* Reading provides information about its restoring capability: + * bit 0: If set, CANopen device restores parameters +* Writing value 0x64616F6C ('l','o','a','d' from LSB to MSB) restores corresponding data. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * bit 31: If set, CANopen device consumes TIME message +* bit 30: If set, CANopen device produces TIME message +* bit 11-29: set to 0 +* bit 0-10: 11-bit CAN-ID + + + + + + + + + + + + + * bit 31: If set, EMCY does NOT exist / is NOT valid +* bit 11-30: set to 0 +* bit 0-10: 11-bit CAN-ID + + + + + + + Inhibit time of emergency message in multiples of 100µs. The value 0 disables the inhibit time. + + + + + + Consumer Heartbeat Time: + * bit 24-31: set to 0 + * bit 16-23: Node ID of the monitored node. If 0 or greater than 127, sub-entry is not used. + * bit 0-15: Heartbeat time in ms (if 0, sub-intry is not used). Value should be higher than the corresponding producer heartbeat time. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Heartbeat producer time in ms (0 = disable transmission). + + + + + + + * Vendor-ID, assigned by CiA +* Product code, manufacturer specific +* Revision number: + * bit 16-31: Major revision number (CANopen behavior has changed) + * bit 0-15: Minor revision num. (CANopen behavior has not changed) +* Serial number, manufacturer specific + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * Value 0: SYNC message is transmitted with data length 0. +* Value 1: reserved. +* Value 2-240: SYNC message has one data byte, which contains the counter. +* Value 241-255: reserved. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Emergency consumer 1 to 127: +* bit 31: If set, EMCY consumer does NOT exist / is NOT valid +* bit 11-30: set to 0 +* bit 0-10: 11-bit CAN-ID + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sub-indexes 1 and above: +* Value 0x00: on error change to NMT state Pre-operational (only if currently in NMT state Operational) +* Value 0x01: on error do nothing +* Value 0x02: on error change to NMT state Stopped + + + + + + + + + + + + + + + + + + + + + Sub-indexes 1 and 2: +* bit 11-31: set to 0 +* bit 0-10: 11-bit CAN-ID + + + + + + + + + + + + + + + + + + + + * Sub-indexes 1 and 2: + * bit 31: If set, SDO does NOT exist / is NOT valid + * bit 30: If set, value is assigned dynamically + * bit 11-29: set to 0 + * bit 0-10: 11-bit CAN-ID +* Node-ID of the SDO client, 0x01 to 0x7F + + + + + + + + + + + + + + + + + + + + + + + + + + + * Sub-indexes 1 and 2: + * bit 31: If set, SDO does NOT exist / is NOT valid + * bit 30: If set, value is assigned dynamically + * bit 11-29: set to 0 + * bit 0-10: 11-bit CAN-ID +* Node-ID of the SDO server, 0x01 to 0x7F + + + + + + + + + + + + + + + + + + + + + + + + + + * COB-ID used by RPDO: + * bit 31: If set, PDO does not exist / is not valid + * bit 11-30: set to 0 + * bit 0-10: 11-bit CAN-ID +* Transmission type: + * Value 0-240: synchronous, processed after next reception of SYNC object + * Value 241-253: not used + * Value 254: event-driven (manufacturer-specific) + * Value 255: event-driven (device profile and application profile specific) +* Event timer in ms (0 = disabled) for deadline monitoring. + + + + + + + + + + + + + + + + + + + + + + + + + + * COB-ID used by RPDO: + * bit 31: If set, PDO does not exist / is not valid + * bit 11-30: set to 0 + * bit 0-10: 11-bit CAN-ID +* Transmission type: + * Value 0-240: synchronous, processed after next reception of SYNC object + * Value 241-253: not used + * Value 254: event-driven (manufacturer-specific) + * Value 255: event-driven (device profile and application profile specific) +* Event timer in ms (0 = disabled) for deadline monitoring. + + + + + + + + + + + + + + + + + + + + + + + + + + * COB-ID used by RPDO: + * bit 31: If set, PDO does not exist / is not valid + * bit 11-30: set to 0 + * bit 0-10: 11-bit CAN-ID +* Transmission type: + * Value 0-240: synchronous, processed after next reception of SYNC object + * Value 241-253: not used + * Value 254: event-driven (manufacturer-specific) + * Value 255: event-driven (device profile and application profile specific) +* Event timer in ms (0 = disabled) for deadline monitoring. + + + + + + + + + + + + + + + + + + + + + + + + + + * COB-ID used by RPDO: + * bit 31: If set, PDO does not exist / is not valid + * bit 11-30: set to 0 + * bit 0-10: 11-bit CAN-ID +* Transmission type: + * Value 0-240: synchronous, processed after next reception of SYNC object + * Value 241-253: not used + * Value 254: event-driven (manufacturer-specific) + * Value 255: event-driven (device profile and application profile specific) +* Event timer in ms (0 = disabled) for deadline monitoring. + + + + + + + + + + + + + + + + + + + + + + + + + + * COB-ID used by RPDO: + * bit 31: If set, PDO does not exist / is not valid + * bit 11-30: set to 0 + * bit 0-10: 11-bit CAN-ID +* Transmission type: + * Value 0-240: synchronous, processed after next reception of SYNC object + * Value 241-253: not used + * Value 254: event-driven (manufacturer-specific) + * Value 255: event-driven (device profile and application profile specific) +* Event timer in ms (0 = disabled) for deadline monitoring. + + + + + + + + + + + + + + + + + + + + + + + + + + + * Number of mapped application objects in PDO: + * Value 0: mapping is disabled. + * Value 1: sub-index 0x01 is valid. + * Value 2-8: sub-indexes 0x01 to (0x02 to 0x08) are valid. +* Application object 1-8: + * bit 16-31: index + * bit 8-15: sub-index + * bit 0-7: data length in bits + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * Number of mapped application objects in PDO: + * Value 0: mapping is disabled. + * Value 1: sub-index 0x01 is valid. + * Value 2-8: sub-indexes 0x01 to (0x02 to 0x08) are valid. +* Application object 1-8: + * bit 16-31: index + * bit 8-15: sub-index + * bit 0-7: data length in bits + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * Number of mapped application objects in PDO: + * Value 0: mapping is disabled. + * Value 1: sub-index 0x01 is valid. + * Value 2-8: sub-indexes 0x01 to (0x02 to 0x08) are valid. +* Application object 1-8: + * bit 16-31: index + * bit 8-15: sub-index + * bit 0-7: data length in bits + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * Number of mapped application objects in PDO: + * Value 0: mapping is disabled. + * Value 1: sub-index 0x01 is valid. + * Value 2-8: sub-indexes 0x01 to (0x02 to 0x08) are valid. +* Application object 1-8: + * bit 16-31: index + * bit 8-15: sub-index + * bit 0-7: data length in bits + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * Number of mapped application objects in PDO: + * Value 0: mapping is disabled. + * Value 1: sub-index 0x01 is valid. + * Value 2-8: sub-indexes 0x01 to (0x02 to 0x08) are valid. +* Application object 1-8: + * bit 16-31: index + * bit 8-15: sub-index + * bit 0-7: data length in bits + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * COB-ID used by RPDO: + * bit 31: If set, PDO does not exist / is not valid + * bit 30: If set, NO RTR is allowed on this PDO + * bit 11-29: set to 0 + * bit 0-10: 11-bit CAN-ID +* Transmission type: + * Value 0: synchronous (acyclic) + * Value 1-240: synchronous (cyclic every (1-240)-th sync) + * Value 241-253: not used + * Value 254: event-driven (manufacturer-specific) + * Value 255: event-driven (device profile and application profile specific) +* Inhibit time in multiple of 100µs, if the transmission type is set to 254 or 255 (0 = disabled). +* Event timer interval in ms, if the transmission type is set to 254 or 255 (0 = disabled). +* SYNC start value + * Value 0: Counter of the SYNC message shall not be processed. + * Value 1-240: The SYNC message with the counter value equal to this value shall be regarded as the first received SYNC message. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * COB-ID used by RPDO: + * bit 31: If set, PDO does not exist / is not valid + * bit 30: If set, NO RTR is allowed on this PDO + * bit 11-29: set to 0 + * bit 0-10: 11-bit CAN-ID +* Transmission type: + * Value 0: synchronous (acyclic) + * Value 1-240: synchronous (cyclic every (1-240)-th sync) + * Value 241-253: not used + * Value 254: event-driven (manufacturer-specific) + * Value 255: event-driven (device profile and application profile specific) +* Inhibit time in multiple of 100µs, if the transmission type is set to 254 or 255 (0 = disabled). +* Event timer interval in ms, if the transmission type is set to 254 or 255 (0 = disabled). +* SYNC start value + * Value 0: Counter of the SYNC message shall not be processed. + * Value 1-240: The SYNC message with the counter value equal to this value shall be regarded as the first received SYNC message. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * COB-ID used by RPDO: + * bit 31: If set, PDO does not exist / is not valid + * bit 30: If set, NO RTR is allowed on this PDO + * bit 11-29: set to 0 + * bit 0-10: 11-bit CAN-ID +* Transmission type: + * Value 0: synchronous (acyclic) + * Value 1-240: synchronous (cyclic every (1-240)-th sync) + * Value 241-253: not used + * Value 254: event-driven (manufacturer-specific) + * Value 255: event-driven (device profile and application profile specific) +* Inhibit time in multiple of 100µs, if the transmission type is set to 254 or 255 (0 = disabled). +* Event timer interval in ms, if the transmission type is set to 254 or 255 (0 = disabled). +* SYNC start value + * Value 0: Counter of the SYNC message shall not be processed. + * Value 1-240: The SYNC message with the counter value equal to this value shall be regarded as the first received SYNC message. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * COB-ID used by RPDO: + * bit 31: If set, PDO does not exist / is not valid + * bit 30: If set, NO RTR is allowed on this PDO + * bit 11-29: set to 0 + * bit 0-10: 11-bit CAN-ID +* Transmission type: + * Value 0: synchronous (acyclic) + * Value 1-240: synchronous (cyclic every (1-240)-th sync) + * Value 241-253: not used + * Value 254: event-driven (manufacturer-specific) + * Value 255: event-driven (device profile and application profile specific) +* Inhibit time in multiple of 100µs, if the transmission type is set to 254 or 255 (0 = disabled). +* Event timer interval in ms, if the transmission type is set to 254 or 255 (0 = disabled). +* SYNC start value + * Value 0: Counter of the SYNC message shall not be processed. + * Value 1-240: The SYNC message with the counter value equal to this value shall be regarded as the first received SYNC message. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * COB-ID used by RPDO: + * bit 31: If set, PDO does not exist / is not valid + * bit 30: If set, NO RTR is allowed on this PDO + * bit 11-29: set to 0 + * bit 0-10: 11-bit CAN-ID +* Transmission type: + * Value 0: synchronous (acyclic) + * Value 1-240: synchronous (cyclic every (1-240)-th sync) + * Value 241-253: not used + * Value 254: event-driven (manufacturer-specific) + * Value 255: event-driven (device profile and application profile specific) +* Inhibit time in multiple of 100µs, if the transmission type is set to 254 or 255 (0 = disabled). +* Event timer interval in ms, if the transmission type is set to 254 or 255 (0 = disabled). +* SYNC start value + * Value 0: Counter of the SYNC message shall not be processed. + * Value 1-240: The SYNC message with the counter value equal to this value shall be regarded as the first received SYNC message. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * Number of mapped application objects in PDO: + * Value 0: mapping is disabled. + * Value 1: sub-index 0x01 is valid. + * Value 2-8: sub-indexes 0x01 to (0x02 to 0x08) are valid. +* Application object 1-8: + * bit 16-31: index + * bit 8-15: sub-index + * bit 0-7: data length in bits + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * Number of mapped application objects in PDO: + * Value 0: mapping is disabled. + * Value 1: sub-index 0x01 is valid. + * Value 2-8: sub-indexes 0x01 to (0x02 to 0x08) are valid. +* Application object 1-8: + * bit 16-31: index + * bit 8-15: sub-index + * bit 0-7: data length in bits + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * Number of mapped application objects in PDO: + * Value 0: mapping is disabled. + * Value 1: sub-index 0x01 is valid. + * Value 2-8: sub-indexes 0x01 to (0x02 to 0x08) are valid. +* Application object 1-8: + * bit 16-31: index + * bit 8-15: sub-index + * bit 0-7: data length in bits + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * Number of mapped application objects in PDO: + * Value 0: mapping is disabled. + * Value 1: sub-index 0x01 is valid. + * Value 2-8: sub-indexes 0x01 to (0x02 to 0x08) are valid. +* Application object 1-8: + * bit 16-31: index + * bit 8-15: sub-index + * bit 0-7: data length in bits + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * Number of mapped application objects in PDO: + * Value 0: mapping is disabled. + * Value 1: sub-index 0x01 is valid. + * Value 2-8: sub-indexes 0x01 to (0x02 to 0x08) are valid. +* Application object 1-8: + * bit 16-31: index + * bit 8-15: sub-index + * bit 0-7: data length in bits + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CANopen communication network profile + 1.1 + + + CommunicationNetwork + + 1 + 1 + CANopen + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/example/IO.eds b/example/IO.eds deleted file mode 100644 index fd82270a..00000000 --- a/example/IO.eds +++ /dev/null @@ -1,2800 +0,0 @@ - -; CANopen Electronic Data Sheet -; File was automatically generated by CANopenNode Object Dictionary Editor -; See http://canopennode.sourceforge.net/ - - -[FileInfo] -FileName=IO Example -FileVersion=- -FileRevision=0 -EDSVersion=4.0 -Description=Open Source CANopen implementation -CreationTime=17:24:43 -CreationDate=2016-03-25 -CreatedBy=JP - - -[DeviceInfo] -VendorName=CANopenNode -VendorNumber=0 -ProductName=CANopenNode -ProductNumber=0 -RevisionNumber=0 -OrderCode=0 -BaudRate_10=1 -BaudRate_20=1 -BaudRate_50=1 -BaudRate_125=1 -BaudRate_250=1 -BaudRate_500=1 -BaudRate_800=1 -BaudRate_1000=1 -SimpleBootUpMaster=0 -SimpleBootUpSlave=1 -Granularity=8 -DynamicChannelsSupported=0 -GroupMessaging=0 -NrOfRXPDO=4 -NrOfTXPDO=4 -LSS_Supported=0 - - -[Comments] -Lines=5 -Line1=EDS File for CANopen device -Line2=Open Source CANopen implementation -Line3=Stack Version: V3.00 -Line4=Generated by CANopenNode Object Dictionary Editor -line5=http://canopennode.sourceforge.net/ - - -[DummyUsage] -Dummy0001=0 -Dummy0002=1 -Dummy0003=1 -Dummy0004=1 -Dummy0005=1 -Dummy0006=1 -Dummy0007=1 - - -[MandatoryObjects] -SupportedObjects=3 -1=0x1000 -2=0x1001 -3=0x1018 - - -[OptionalObjects] -SupportedObjects=38 -1=0x1002 -2=0x1003 -3=0x1005 -4=0x1006 -5=0x1007 -6=0x1008 -7=0x1009 -8=0x100A -9=0x1010 -10=0x1011 -11=0x1014 -12=0x1015 -13=0x1016 -14=0x1017 -15=0x1019 -16=0x1029 -17=0x1200 -18=0x1400 -19=0x1401 -20=0x1402 -21=0x1403 -22=0x1600 -23=0x1601 -24=0x1602 -25=0x1603 -26=0x1800 -27=0x1801 -28=0x1802 -29=0x1803 -30=0x1A00 -31=0x1A01 -32=0x1A02 -33=0x1A03 -34=0x1F80 -35=0x6000 -36=0x6200 -37=0x6401 -38=0x6411 - - -[ManufacturerObjects] -SupportedObjects=19 -1=0x2100 -2=0x2101 -3=0x2102 -4=0x2103 -5=0x2104 -6=0x2106 -7=0x2107 -8=0x2108 -9=0x2109 -10=0x2110 -11=0x2111 -12=0x2112 -13=0x2120 -14=0x2130 -15=0x2301 -16=0x2302 -17=0x2400 -18=0x2401 -19=0x2402 - -[1000] -ParameterName=Device type -ObjectType=7 -DataType=0x0007 -AccessType=ro -PDOMapping=0 -DefaultValue=0x00000000 - -[1001] -ParameterName=Error register -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=1 -DefaultValue=0 - -[1002] -ParameterName=Manufacturer status register -ObjectType=7 -DataType=0x0007 -AccessType=ro -PDOMapping=1 -DefaultValue=0 - -[1003] -ParameterName=Pre-defined error field -ObjectType=8 -SubNumber=9 - -[1003sub0] -ParameterName=Number of errors -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=0 - -[1003sub1] -ParameterName=Standard error field -ObjectType=7 -DataType=0x0007 -AccessType=ro -PDOMapping=0 -DefaultValue=0 - -[1003sub2] -ParameterName=Standard error field -ObjectType=7 -DataType=0x0007 -AccessType=ro -PDOMapping=0 -DefaultValue=0 - -[1003sub3] -ParameterName=Standard error field -ObjectType=7 -DataType=0x0007 -AccessType=ro -PDOMapping=0 -DefaultValue=0 - -[1003sub4] -ParameterName=Standard error field -ObjectType=7 -DataType=0x0007 -AccessType=ro -PDOMapping=0 -DefaultValue=0 - -[1003sub5] -ParameterName=Standard error field -ObjectType=7 -DataType=0x0007 -AccessType=ro -PDOMapping=0 -DefaultValue=0 - -[1003sub6] -ParameterName=Standard error field -ObjectType=7 -DataType=0x0007 -AccessType=ro -PDOMapping=0 -DefaultValue=0 - -[1003sub7] -ParameterName=Standard error field -ObjectType=7 -DataType=0x0007 -AccessType=ro -PDOMapping=0 -DefaultValue=0 - -[1003sub8] -ParameterName=Standard error field -ObjectType=7 -DataType=0x0007 -AccessType=ro -PDOMapping=0 -DefaultValue=0 - -[1005] -ParameterName=COB-ID SYNC message -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000080 - -[1006] -ParameterName=Communication cycle period -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0 - -[1007] -ParameterName=Synchronous window length -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0 - -[1008] -ParameterName=Manufacturer device name -ObjectType=7 -DataType=0x0009 -AccessType=const -PDOMapping=0 -DefaultValue=CANopenNode - -[1009] -ParameterName=Manufacturer hardware version -ObjectType=7 -DataType=0x0009 -AccessType=const -PDOMapping=0 -DefaultValue=3.00 - -[100A] -ParameterName=Manufacturer software version -ObjectType=7 -DataType=0x0009 -AccessType=const -PDOMapping=0 -DefaultValue=3.00 - -[1010] -ParameterName=Store parameters -ObjectType=8 -SubNumber=2 - -[1010sub0] -ParameterName=max sub-index -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=0 -DefaultValue=1 - -[1010sub1] -ParameterName=save all parameters -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000003 - -[1011] -ParameterName=Restore default parameters -ObjectType=8 -SubNumber=2 - -[1011sub0] -ParameterName=max sub-index -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=0 -DefaultValue=1 - -[1011sub1] -ParameterName=restore all default parameters -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000001 - -[1014] -ParameterName=COB-ID EMCY -ObjectType=7 -DataType=0x0007 -AccessType=ro -PDOMapping=0 -DefaultValue=$NODEID+0x80 - -[1015] -ParameterName=inhibit time EMCY -ObjectType=7 -DataType=0x0006 -AccessType=rw -PDOMapping=0 -DefaultValue=100 - -[1016] -ParameterName=Consumer heartbeat time -ObjectType=8 -SubNumber=5 - -[1016sub0] -ParameterName=max sub-index -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=0 -DefaultValue=4 - -[1016sub1] -ParameterName=Consumer heartbeat time -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1016sub2] -ParameterName=Consumer heartbeat time -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1016sub3] -ParameterName=Consumer heartbeat time -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1016sub4] -ParameterName=Consumer heartbeat time -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1017] -ParameterName=Producer heartbeat time -ObjectType=7 -DataType=0x0006 -AccessType=rw -PDOMapping=0 -DefaultValue=1000 - -[1018] -ParameterName=Identity -ObjectType=9 -SubNumber=5 - -[1018sub0] -ParameterName=max sub-index -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=0 -DefaultValue=4 - -[1018sub1] -ParameterName=Vendor-ID -ObjectType=7 -DataType=0x0007 -AccessType=ro -PDOMapping=0 -DefaultValue=0x00000000 - -[1018sub2] -ParameterName=Product code -ObjectType=7 -DataType=0x0007 -AccessType=ro -PDOMapping=0 -DefaultValue=0x00000000 - -[1018sub3] -ParameterName=Revision number -ObjectType=7 -DataType=0x0007 -AccessType=ro -PDOMapping=0 -DefaultValue=0x00000000 - -[1018sub4] -ParameterName=Serial number -ObjectType=7 -DataType=0x0007 -AccessType=ro -PDOMapping=0 -DefaultValue=0x00000000 - -[1019] -ParameterName=Synchronous counter overflow value -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=0 - -[1029] -ParameterName=Error behavior -ObjectType=8 -SubNumber=7 - -[1029sub0] -ParameterName=max sub-index -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=0 -DefaultValue=6 - -[1029sub1] -ParameterName=Communication -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00 - -[1029sub2] -ParameterName=Communication other -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00 - -[1029sub3] -ParameterName=Communication passive -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=0x01 - -[1029sub4] -ParameterName=Generic -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00 - -[1029sub5] -ParameterName=Device profile -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00 - -[1029sub6] -ParameterName=Manufacturer specific -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00 - -[1200] -ParameterName=SDO server parameter -ObjectType=9 -SubNumber=3 - -[1200sub0] -ParameterName=max sub-index -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=0 -DefaultValue=2 - -[1200sub1] -ParameterName=COB-ID client to server -ObjectType=7 -DataType=0x0007 -AccessType=ro -PDOMapping=0 -DefaultValue=$NODEID+0x600 - -[1200sub2] -ParameterName=COB-ID server to client -ObjectType=7 -DataType=0x0007 -AccessType=ro -PDOMapping=0 -DefaultValue=$NODEID+0x580 - -[1400] -ParameterName=RPDO communication parameter -ObjectType=9 -SubNumber=3 - -[1400sub0] -ParameterName=max sub-index -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=0 -DefaultValue=2 - -[1400sub1] -ParameterName=COB-ID used by RPDO -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=$NODEID+0x200 - -[1400sub2] -ParameterName=transmission type -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=255 - -[1401] -ParameterName=RPDO communication parameter -ObjectType=9 -SubNumber=3 - -[1401sub0] -ParameterName=max sub-index -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=0 -DefaultValue=2 - -[1401sub1] -ParameterName=COB-ID used by RPDO -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=$NODEID+0x300 - -[1401sub2] -ParameterName=transmission type -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=254 - -[1402] -ParameterName=RPDO communication parameter -ObjectType=9 -SubNumber=3 - -[1402sub0] -ParameterName=max sub-index -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=0 -DefaultValue=2 - -[1402sub1] -ParameterName=COB-ID used by RPDO -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=$NODEID+0x400 - -[1402sub2] -ParameterName=transmission type -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=254 - -[1403] -ParameterName=RPDO communication parameter -ObjectType=9 -SubNumber=3 - -[1403sub0] -ParameterName=max sub-index -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=0 -DefaultValue=2 - -[1403sub1] -ParameterName=COB-ID used by RPDO -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=$NODEID+0x500 - -[1403sub2] -ParameterName=transmission type -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=254 - -[1600] -ParameterName=RPDO mapping parameter -ObjectType=9 -SubNumber=9 - -[1600sub0] -ParameterName=Number of mapped objects -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=2 - -[1600sub1] -ParameterName=mapped object 1 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x62000108 - -[1600sub2] -ParameterName=mapped object 2 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x62000208 - -[1600sub3] -ParameterName=mapped object 3 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1600sub4] -ParameterName=mapped object 4 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1600sub5] -ParameterName=mapped object 5 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1600sub6] -ParameterName=mapped object 6 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1600sub7] -ParameterName=mapped object 7 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1600sub8] -ParameterName=mapped object 8 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1601] -ParameterName=RPDO mapping parameter -ObjectType=9 -SubNumber=9 - -[1601sub0] -ParameterName=Number of mapped objects -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=0 - -[1601sub1] -ParameterName=mapped object 1 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1601sub2] -ParameterName=mapped object 2 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1601sub3] -ParameterName=mapped object 3 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1601sub4] -ParameterName=mapped object 4 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1601sub5] -ParameterName=mapped object 5 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1601sub6] -ParameterName=mapped object 6 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1601sub7] -ParameterName=mapped object 7 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1601sub8] -ParameterName=mapped object 8 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1602] -ParameterName=RPDO mapping parameter -ObjectType=9 -SubNumber=9 - -[1602sub0] -ParameterName=Number of mapped objects -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=0 - -[1602sub1] -ParameterName=mapped object 1 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1602sub2] -ParameterName=mapped object 2 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1602sub3] -ParameterName=mapped object 3 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1602sub4] -ParameterName=mapped object 4 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1602sub5] -ParameterName=mapped object 5 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1602sub6] -ParameterName=mapped object 6 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1602sub7] -ParameterName=mapped object 7 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1602sub8] -ParameterName=mapped object 8 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1603] -ParameterName=RPDO mapping parameter -ObjectType=9 -SubNumber=9 - -[1603sub0] -ParameterName=Number of mapped objects -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=0 - -[1603sub1] -ParameterName=mapped object 1 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1603sub2] -ParameterName=mapped object 2 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1603sub3] -ParameterName=mapped object 3 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1603sub4] -ParameterName=mapped object 4 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1603sub5] -ParameterName=mapped object 5 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1603sub6] -ParameterName=mapped object 6 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1603sub7] -ParameterName=mapped object 7 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1603sub8] -ParameterName=mapped object 8 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1800] -ParameterName=TPDO communication parameter -ObjectType=9 -SubNumber=7 - -[1800sub0] -ParameterName=max sub-index -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=0 -DefaultValue=6 - -[1800sub1] -ParameterName=COB-ID used by TPDO -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=$NODEID+0x180 - -[1800sub2] -ParameterName=transmission type -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=255 - -[1800sub3] -ParameterName=inhibit time -ObjectType=7 -DataType=0x0006 -AccessType=rw -PDOMapping=0 -DefaultValue=100 - -[1800sub4] -ParameterName=compatibility entry -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=0 - -[1800sub5] -ParameterName=event timer -ObjectType=7 -DataType=0x0006 -AccessType=rw -PDOMapping=0 -DefaultValue=0 - -[1800sub6] -ParameterName=SYNC start value -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=0 - -[1801] -ParameterName=TPDO communication parameter -ObjectType=9 -SubNumber=7 - -[1801sub0] -ParameterName=max sub-index -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=0 -DefaultValue=6 - -[1801sub1] -ParameterName=COB-ID used by TPDO -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=$NODEID+0x280 - -[1801sub2] -ParameterName=transmission type -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=254 - -[1801sub3] -ParameterName=inhibit time -ObjectType=7 -DataType=0x0006 -AccessType=rw -PDOMapping=0 -DefaultValue=0 - -[1801sub4] -ParameterName=compatibility entry -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=0 - -[1801sub5] -ParameterName=event timer -ObjectType=7 -DataType=0x0006 -AccessType=rw -PDOMapping=0 -DefaultValue=0 - -[1801sub6] -ParameterName=SYNC start value -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=0 - -[1802] -ParameterName=TPDO communication parameter -ObjectType=9 -SubNumber=7 - -[1802sub0] -ParameterName=max sub-index -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=0 -DefaultValue=6 - -[1802sub1] -ParameterName=COB-ID used by TPDO -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=$NODEID+0x380 - -[1802sub2] -ParameterName=transmission type -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=254 - -[1802sub3] -ParameterName=inhibit time -ObjectType=7 -DataType=0x0006 -AccessType=rw -PDOMapping=0 -DefaultValue=0 - -[1802sub4] -ParameterName=compatibility entry -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=0 - -[1802sub5] -ParameterName=event timer -ObjectType=7 -DataType=0x0006 -AccessType=rw -PDOMapping=0 -DefaultValue=0 - -[1802sub6] -ParameterName=SYNC start value -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=0 - -[1803] -ParameterName=TPDO communication parameter -ObjectType=9 -SubNumber=7 - -[1803sub0] -ParameterName=max sub-index -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=0 -DefaultValue=6 - -[1803sub1] -ParameterName=COB-ID used by TPDO -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=$NODEID+0x480 - -[1803sub2] -ParameterName=transmission type -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=254 - -[1803sub3] -ParameterName=inhibit time -ObjectType=7 -DataType=0x0006 -AccessType=rw -PDOMapping=0 -DefaultValue=0 - -[1803sub4] -ParameterName=compatibility entry -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=0 - -[1803sub5] -ParameterName=event timer -ObjectType=7 -DataType=0x0006 -AccessType=rw -PDOMapping=0 -DefaultValue=0 - -[1803sub6] -ParameterName=SYNC start value -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=0 - -[1A00] -ParameterName=TPDO mapping parameter -ObjectType=9 -SubNumber=9 - -[1A00sub0] -ParameterName=Number of mapped objects -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=2 - -[1A00sub1] -ParameterName=mapped object 1 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x60000108 - -[1A00sub2] -ParameterName=mapped object 2 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x60000208 - -[1A00sub3] -ParameterName=mapped object 3 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1A00sub4] -ParameterName=mapped object 4 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1A00sub5] -ParameterName=mapped object 5 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1A00sub6] -ParameterName=mapped object 6 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1A00sub7] -ParameterName=mapped object 7 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1A00sub8] -ParameterName=mapped object 8 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1A01] -ParameterName=TPDO mapping parameter -ObjectType=9 -SubNumber=9 - -[1A01sub0] -ParameterName=Number of mapped objects -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=0 - -[1A01sub1] -ParameterName=mapped object 1 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1A01sub2] -ParameterName=mapped object 2 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1A01sub3] -ParameterName=mapped object 3 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1A01sub4] -ParameterName=mapped object 4 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1A01sub5] -ParameterName=mapped object 5 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1A01sub6] -ParameterName=mapped object 6 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1A01sub7] -ParameterName=mapped object 7 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1A01sub8] -ParameterName=mapped object 8 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1A02] -ParameterName=TPDO mapping parameter -ObjectType=9 -SubNumber=9 - -[1A02sub0] -ParameterName=Number of mapped objects -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=0 - -[1A02sub1] -ParameterName=mapped object 1 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1A02sub2] -ParameterName=mapped object 2 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1A02sub3] -ParameterName=mapped object 3 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1A02sub4] -ParameterName=mapped object 4 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1A02sub5] -ParameterName=mapped object 5 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1A02sub6] -ParameterName=mapped object 6 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1A02sub7] -ParameterName=mapped object 7 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1A02sub8] -ParameterName=mapped object 8 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1A03] -ParameterName=TPDO mapping parameter -ObjectType=9 -SubNumber=9 - -[1A03sub0] -ParameterName=Number of mapped objects -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=0 - -[1A03sub1] -ParameterName=mapped object 1 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1A03sub2] -ParameterName=mapped object 2 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1A03sub3] -ParameterName=mapped object 3 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1A03sub4] -ParameterName=mapped object 4 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1A03sub5] -ParameterName=mapped object 5 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1A03sub6] -ParameterName=mapped object 6 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1A03sub7] -ParameterName=mapped object 7 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1A03sub8] -ParameterName=mapped object 8 -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[1F80] -ParameterName=NMT startup -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[2100] -ParameterName=Error status bits -ObjectType=7 -DataType=0x000A -AccessType=ro -PDOMapping=1 -DefaultValue=00000000000000000000 - -[2101] -ParameterName=CAN node ID -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=0x30 - -[2102] -ParameterName=CAN bit rate -ObjectType=7 -DataType=0x0006 -AccessType=rw -PDOMapping=0 -DefaultValue=250 - -[2103] -ParameterName=SYNC counter -ObjectType=7 -DataType=0x0006 -AccessType=rw -PDOMapping=0 -DefaultValue=0 - -[2104] -ParameterName=SYNC time -ObjectType=7 -DataType=0x0006 -AccessType=ro -PDOMapping=0 -DefaultValue=0 - -[2106] -ParameterName=Power-on counter -ObjectType=7 -DataType=0x0007 -AccessType=ro -PDOMapping=0 -DefaultValue=0 - -[2107] -ParameterName=Performance -ObjectType=8 -SubNumber=6 - -[2107sub0] -ParameterName=max sub-index -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=0 -DefaultValue=5 - -[2107sub1] -ParameterName=cycles per second -ObjectType=7 -DataType=0x0006 -AccessType=rww -PDOMapping=1 -DefaultValue=1000 - -[2107sub2] -ParameterName=timer cycle time -ObjectType=7 -DataType=0x0006 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2107sub3] -ParameterName=timer cycle max time -ObjectType=7 -DataType=0x0006 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2107sub4] -ParameterName=main cycle time -ObjectType=7 -DataType=0x0006 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2107sub5] -ParameterName=main cycle max time -ObjectType=7 -DataType=0x0006 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2108] -ParameterName=Temperature -ObjectType=8 -SubNumber=2 - -[2108sub0] -ParameterName=max sub-index -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=0 -DefaultValue=1 - -[2108sub1] -ParameterName=main PCB -ObjectType=7 -DataType=0x0003 -AccessType=ro -PDOMapping=1 -DefaultValue=0 - -[2109] -ParameterName=Voltage -ObjectType=8 -SubNumber=2 - -[2109sub0] -ParameterName=max sub-index -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=0 -DefaultValue=1 - -[2109sub1] -ParameterName=main PCB supply -ObjectType=7 -DataType=0x0003 -AccessType=ro -PDOMapping=1 -DefaultValue=0 - -[2110] -ParameterName=Variable Int32 -ObjectType=8 -SubNumber=17 - -[2110sub0] -ParameterName=max sub-index -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=0 -DefaultValue=16 - -[2110sub1] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2110sub2] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2110sub3] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2110sub4] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2110sub5] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2110sub6] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2110sub7] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2110sub8] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2110sub9] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2110subA] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2110subB] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2110subC] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2110subD] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2110subE] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2110subF] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2110sub10] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2111] -ParameterName=Variable ROM Int32 -ObjectType=8 -SubNumber=17 - -[2111sub0] -ParameterName=max sub-index -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=0 -DefaultValue=16 - -[2111sub1] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=1 - -[2111sub2] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2111sub3] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2111sub4] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2111sub5] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2111sub6] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2111sub7] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2111sub8] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2111sub9] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2111subA] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2111subB] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2111subC] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2111subD] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2111subE] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2111subF] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2111sub10] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2112] -ParameterName=Variable NV Int32 -ObjectType=8 -SubNumber=17 - -[2112sub0] -ParameterName=max sub-index -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=0 -DefaultValue=16 - -[2112sub1] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=1 - -[2112sub2] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2112sub3] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2112sub4] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2112sub5] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2112sub6] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2112sub7] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2112sub8] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2112sub9] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2112subA] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2112subB] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2112subC] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2112subD] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2112subE] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2112subF] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2112sub10] -ParameterName=int32 -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2120] -ParameterName=test var -ObjectType=9 -SubNumber=6 - -[2120sub0] -ParameterName=max sub-index -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=0 -DefaultValue=5 - -[2120sub1] -ParameterName=I64 -ObjectType=7 -DataType=0x0015 -AccessType=rww -PDOMapping=1 -DefaultValue=0x1234567890ABCDEFLL - -[2120sub2] -ParameterName=U64 -ObjectType=7 -DataType=0x001B -AccessType=rww -PDOMapping=1 -DefaultValue=0x234567890ABCDEF1LL - -[2120sub3] -ParameterName=R32 -ObjectType=7 -DataType=0x0008 -AccessType=rww -PDOMapping=1 -DefaultValue=12.345 - -[2120sub4] -ParameterName=R64 -ObjectType=7 -DataType=0x0011 -AccessType=rww -PDOMapping=1 -DefaultValue=456.789 - -[2120sub5] -ParameterName=domain -ObjectType=7 -DataType=0x000F -AccessType=rw -PDOMapping=0 -DefaultValue=0 - -[2130] -ParameterName=Time -ObjectType=9 -SubNumber=4 - -[2130sub0] -ParameterName=max sub-index -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=0 -DefaultValue=3 - -[2130sub1] -ParameterName=String -ObjectType=7 -DataType=0x0009 -AccessType=ro -PDOMapping=0 -DefaultValue=- - -[2130sub2] -ParameterName=Epoch time base ms -ObjectType=7 -DataType=0x001B -AccessType=rw -PDOMapping=0 -DefaultValue=0 - -[2130sub3] -ParameterName=Epoch time offset ms -ObjectType=7 -DataType=0x0007 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2301] -ParameterName=Trace config -ObjectType=9 -SubNumber=9 - -[2301sub0] -ParameterName=max sub-index -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=0 -DefaultValue=8 - -[2301sub1] -ParameterName=Size -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=100 - -[2301sub2] -ParameterName=Axis no -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=1 - -[2301sub3] -ParameterName=Name -ObjectType=7 -DataType=0x0009 -AccessType=rw -PDOMapping=0 -DefaultValue=Trace1 - -[2301sub4] -ParameterName=Color -ObjectType=7 -DataType=0x0009 -AccessType=rw -PDOMapping=0 -DefaultValue=red - -[2301sub5] -ParameterName=Map -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x60000108 - -[2301sub6] -ParameterName=Format -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=1 - -[2301sub7] -ParameterName=Trigger -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=0 - -[2301sub8] -ParameterName=Threshold -ObjectType=7 -DataType=0x0004 -AccessType=rw -PDOMapping=0 -DefaultValue=0 - -[2302] -ParameterName=Trace config -ObjectType=9 -SubNumber=9 - -[2302sub0] -ParameterName=max sub-index -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=0 -DefaultValue=8 - -[2302sub1] -ParameterName=Size -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0 - -[2302sub2] -ParameterName=Axis no -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=0 - -[2302sub3] -ParameterName=Name -ObjectType=7 -DataType=0x0009 -AccessType=rw -PDOMapping=0 -DefaultValue=Trace2 - -[2302sub4] -ParameterName=Color -ObjectType=7 -DataType=0x0009 -AccessType=rw -PDOMapping=0 -DefaultValue=green - -[2302sub5] -ParameterName=Map -ObjectType=7 -DataType=0x0007 -AccessType=rw -PDOMapping=0 -DefaultValue=0x00000000 - -[2302sub6] -ParameterName=Format -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=0 - -[2302sub7] -ParameterName=Trigger -ObjectType=7 -DataType=0x0005 -AccessType=rw -PDOMapping=0 -DefaultValue=0 - -[2302sub8] -ParameterName=Threshold -ObjectType=7 -DataType=0x0004 -AccessType=rw -PDOMapping=0 -DefaultValue=0 - -[2400] -ParameterName=Trace enable -ObjectType=7 -DataType=0x0005 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2401] -ParameterName=Trace -ObjectType=9 -SubNumber=7 - -[2401sub0] -ParameterName=max sub-index -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=0 -DefaultValue=6 - -[2401sub1] -ParameterName=Size -ObjectType=7 -DataType=0x0007 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2401sub2] -ParameterName=Value -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2401sub3] -ParameterName=Min -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2401sub4] -ParameterName=Max -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2401sub5] -ParameterName=Plot -ObjectType=7 -DataType=0x000F -AccessType=ro -PDOMapping=0 -DefaultValue=0 - -[2401sub6] -ParameterName=Trigger time -ObjectType=7 -DataType=0x0007 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2402] -ParameterName=Trace -ObjectType=9 -SubNumber=7 - -[2402sub0] -ParameterName=max sub-index -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=0 -DefaultValue=6 - -[2402sub1] -ParameterName=Size -ObjectType=7 -DataType=0x0007 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2402sub2] -ParameterName=Value -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2402sub3] -ParameterName=Min -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2402sub4] -ParameterName=Max -ObjectType=7 -DataType=0x0004 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[2402sub5] -ParameterName=Plot -ObjectType=7 -DataType=0x000F -AccessType=ro -PDOMapping=0 -DefaultValue=0 - -[2402sub6] -ParameterName=Trigger time -ObjectType=7 -DataType=0x0007 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[6000] -ParameterName=Read input 8 bit -ObjectType=8 -SubNumber=9 - -[6000sub0] -ParameterName=max sub-index -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=0 -DefaultValue=8 - -[6000sub1] -ParameterName=Input -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=1 -DefaultValue=0x00 - -[6000sub2] -ParameterName=Input -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=1 -DefaultValue=0x00 - -[6000sub3] -ParameterName=Input -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=1 -DefaultValue=0x00 - -[6000sub4] -ParameterName=Input -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=1 -DefaultValue=0x00 - -[6000sub5] -ParameterName=Input -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=1 -DefaultValue=0x00 - -[6000sub6] -ParameterName=Input -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=1 -DefaultValue=0x00 - -[6000sub7] -ParameterName=Input -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=1 -DefaultValue=0x00 - -[6000sub8] -ParameterName=Input -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=1 -DefaultValue=0x00 - -[6200] -ParameterName=Write output 8 bit -ObjectType=8 -SubNumber=9 - -[6200sub0] -ParameterName=max sub-index -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=0 -DefaultValue=8 - -[6200sub1] -ParameterName=Output -ObjectType=7 -DataType=0x0005 -AccessType=rww -PDOMapping=1 -DefaultValue=0x00 - -[6200sub2] -ParameterName=Output -ObjectType=7 -DataType=0x0005 -AccessType=rww -PDOMapping=1 -DefaultValue=0x00 - -[6200sub3] -ParameterName=Output -ObjectType=7 -DataType=0x0005 -AccessType=rww -PDOMapping=1 -DefaultValue=0x00 - -[6200sub4] -ParameterName=Output -ObjectType=7 -DataType=0x0005 -AccessType=rww -PDOMapping=1 -DefaultValue=0x00 - -[6200sub5] -ParameterName=Output -ObjectType=7 -DataType=0x0005 -AccessType=rww -PDOMapping=1 -DefaultValue=0x00 - -[6200sub6] -ParameterName=Output -ObjectType=7 -DataType=0x0005 -AccessType=rww -PDOMapping=1 -DefaultValue=0x00 - -[6200sub7] -ParameterName=Output -ObjectType=7 -DataType=0x0005 -AccessType=rww -PDOMapping=1 -DefaultValue=0x00 - -[6200sub8] -ParameterName=Output -ObjectType=7 -DataType=0x0005 -AccessType=rww -PDOMapping=1 -DefaultValue=0x00 - -[6401] -ParameterName=Read analogue input 16 bit -ObjectType=8 -SubNumber=13 - -[6401sub0] -ParameterName=max sub-index -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=0 -DefaultValue=12 - -[6401sub1] -ParameterName=Input -ObjectType=7 -DataType=0x0003 -AccessType=ro -PDOMapping=1 -DefaultValue=0 - -[6401sub2] -ParameterName=Input -ObjectType=7 -DataType=0x0003 -AccessType=ro -PDOMapping=1 -DefaultValue=0 - -[6401sub3] -ParameterName=Input -ObjectType=7 -DataType=0x0003 -AccessType=ro -PDOMapping=1 -DefaultValue=0 - -[6401sub4] -ParameterName=Input -ObjectType=7 -DataType=0x0003 -AccessType=ro -PDOMapping=1 -DefaultValue=0 - -[6401sub5] -ParameterName=Input -ObjectType=7 -DataType=0x0003 -AccessType=ro -PDOMapping=1 -DefaultValue=0 - -[6401sub6] -ParameterName=Input -ObjectType=7 -DataType=0x0003 -AccessType=ro -PDOMapping=1 -DefaultValue=0 - -[6401sub7] -ParameterName=Input -ObjectType=7 -DataType=0x0003 -AccessType=ro -PDOMapping=1 -DefaultValue=0 - -[6401sub8] -ParameterName=Input -ObjectType=7 -DataType=0x0003 -AccessType=ro -PDOMapping=1 -DefaultValue=0 - -[6401sub9] -ParameterName=Input -ObjectType=7 -DataType=0x0003 -AccessType=ro -PDOMapping=1 -DefaultValue=0 - -[6401subA] -ParameterName=Input -ObjectType=7 -DataType=0x0003 -AccessType=ro -PDOMapping=1 -DefaultValue=0 - -[6401subB] -ParameterName=Input -ObjectType=7 -DataType=0x0003 -AccessType=ro -PDOMapping=1 -DefaultValue=0 - -[6401subC] -ParameterName=Input -ObjectType=7 -DataType=0x0003 -AccessType=ro -PDOMapping=1 -DefaultValue=0 - -[6411] -ParameterName=Write analogue output 16 bit -ObjectType=8 -SubNumber=9 - -[6411sub0] -ParameterName=max sub-index -ObjectType=7 -DataType=0x0005 -AccessType=ro -PDOMapping=0 -DefaultValue=8 - -[6411sub1] -ParameterName=Output -ObjectType=7 -DataType=0x0003 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[6411sub2] -ParameterName=Output -ObjectType=7 -DataType=0x0003 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[6411sub3] -ParameterName=Output -ObjectType=7 -DataType=0x0003 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[6411sub4] -ParameterName=Output -ObjectType=7 -DataType=0x0003 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[6411sub5] -ParameterName=Output -ObjectType=7 -DataType=0x0003 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[6411sub6] -ParameterName=Output -ObjectType=7 -DataType=0x0003 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[6411sub7] -ParameterName=Output -ObjectType=7 -DataType=0x0003 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - -[6411sub8] -ParameterName=Output -ObjectType=7 -DataType=0x0003 -AccessType=rww -PDOMapping=1 -DefaultValue=0 - - diff --git a/example/IO.html b/example/IO.html deleted file mode 100644 index ec22b45b..00000000 --- a/example/IO.html +++ /dev/null @@ -1,6627 +0,0 @@ - - - - IO Example - CANopenNode - - - - - - - -

CANopen object dictionary for IO Example

-
    - - - - -
-
    -
  • -

    1000 - Device type

    - - - - - - - - - - - - - - - - - - - - -
    Object TypeMemory TypeData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    VarROMUNSIGNED32ronono0x00000000 -
    -
    -

    en: Device type

    -
    - bit 0-15: Device profile number -
    - bit 16-31: Additional information -
    -
    -
  • -
  • -

    1001 - Error register

    - - - - - - - - - - - - - - - - - - - - -
    Object TypeMemory TypeData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    VarRAMUNSIGNED8rooptionalno0 -
    -
    -

    en: Error register

    -
    - bit 0: generic error -
    - bit 1: current -
    - bit 2: voltage -
    - bit 3: temperature -
    - bit 4: communication error (overrun, error state) -
    - bit 5: device profile specific -
    - bit 6: Reserved (always 0) -
    - bit 7: manufacturer specific -
    -
    -
  • -
  • -

    1002 - Manufacturer status register

    - - - - - - - - - - - - - - - - - - - - -
    Object TypeMemory TypeData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    VarRAMUNSIGNED32rooptionalno0 -
    -
    -

    en: Manufacturer status register

    -
    bit 0-31: Not used by stack (available for user)
    -
    -
  • -
  • -

    1003 - Pre-defined error field

    - - - - - - - - - - - - - - - - - - - -
    Object TypeSub NumberMemory TypeData TypeAccess TypePDO MappingTPDO detects COS
    Array9RAMUNSIGNED32ronono
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SubindexNameDefault valueActual value
    00Number of errors0 -
    01Standard error field0 -
    02Standard error field0 -
    03Standard error field0 -
    04Standard error field0 -
    05Standard error field0 -
    06Standard error field0 -
    07Standard error field0 -
    08Standard error field0 -
    -
    -

    en: Pre-defined error field

    -
    - Number of Errors -
    - bit 0-7: Zero can be written to erase error history -
    -
    - Standard Error Field -
    - bit 0-15: Error code as transmited in the Emergency object -
    - bit 16-31: Manufacturer specific additional information -
    -
    -
  • -
  • -

    1005 - COB-ID SYNC message

    - - - - - - - - - - - - - - - - - - - - -
    Object TypeMemory TypeData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    VarROMUNSIGNED32rwnono0x00000080 -
    -
    -

    en: COB-ID SYNC message

    -
    - bit 0-10: COB-ID for SYNC object -
    - bit 11-29: set to 0 -
    - bit 30: 1(0) - node generates (does NOT generate) SYNC object -
    - bit 31: set to 0 -
    -
    -
  • -
  • -

    1006 - Communication cycle period

    - - - - - - - - - - - - - - - - - - - - -
    Object TypeMemory TypeData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    VarROMUNSIGNED32rwnono0 -
    -
    -

    en: Communication cycle period

    -
    bit 0-31: period of SYNC transmission in µs (0 = no transmission, no checking)
    -
    -
  • -
  • -

    1007 - Synchronous window length

    - - - - - - - - - - - - - - - - - - - - -
    Object TypeMemory TypeData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    VarROMUNSIGNED32rwnono0 -
    -
    -

    en: Synchronous window length

    -
    bit 0-31: window leghth after SYNC when PDOS must be transmited in µs, (0 = not used)
    -
    -
  • -
  • -

    1008 - Manufacturer device name

    - - - - - - - - - - - - - - - - - - - - -
    Object TypeMemory TypeData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    VarROMVISIBLE_STRINGconstnonoCANopenNode -
    -
    -

    en: Manufacturer device name

    -
    Name of the manufacturer as string
    -
    -
  • -
  • -

    1009 - Manufacturer hardware version

    - - - - - - - - - - - - - - - - - - - - -
    Object TypeMemory TypeData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    VarROMVISIBLE_STRINGconstnono3.00 -
    -
    -

    en: Manufacturer hardware version

    -
    Name of the hardware version as string
    -
    -
  • -
  • -

    100A - Manufacturer software version

    - - - - - - - - - - - - - - - - - - - - -
    Object TypeMemory TypeData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    VarROMVISIBLE_STRINGconstnono3.00 -
    -
    -

    en: Manufacturer software version

    -
    Name of the software version as string.
    -
    -
  • -
  • -

    1010 - Store parameters

    - - - - - - - - - - - - - - - - - - - -
    Object TypeSub NumberMemory TypeData TypeAccess TypePDO MappingTPDO detects COS
    Array2RAMUNSIGNED32rwnono
    -
    - - - - - - - - - - - - - - - - - -
    SubindexNameDefault valueActual value
    00max sub-index1 -
    01save all parameters0x00000003 -
    -
    -

    en: Store parameters

    -
    Writing value 0x65766173 ('s','a','v','e' from LSB to MSB) into this location stores all ROM variables into EEPROM.
    -
    -
  • -
  • -

    1011 - Restore default parameters

    - - - - - - - - - - - - - - - - - - - -
    Object TypeSub NumberMemory TypeData TypeAccess TypePDO MappingTPDO detects COS
    Array2RAMUNSIGNED32rwnono
    -
    - - - - - - - - - - - - - - - - - -
    SubindexNameDefault valueActual value
    00max sub-index1 -
    01restore all default parameters0x00000001 -
    -
    -

    en: Restore default parameters

    -
    Writing value 0x64616F6C ('l','o','a','d' from LSB to MSB) into this location restores all ROM and EEPROM variables after reset. (After reset read form EEPROM is not performed, so default values are used.)
    -
    -
  • -
  • -

    1014 - COB-ID EMCY

    - - - - - - - - - - - - - - - - - - - - -
    Object TypeMemory TypeData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    VarROMUNSIGNED32ronono$NODEID+0x80 -
    -
    -

    en: COB-ID emergency message

    -
    - bit 0-10: COB-ID -
    - bit 11-30: set to 0 for 11 bit COB-ID -
    - bit 31: 0(1) - node uses (does NOT use) Emergency object -
    -
    -
  • -
  • -

    1015 - inhibit time EMCY

    - - - - - - - - - - - - - - - - - - - - -
    Object TypeMemory TypeData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    VarROMUNSIGNED16rwnono100 -
    -
    -

    en: inhibit time emergency message

    -
    bit 0-15: Inhibit time of emergency message in 100µs
    -
    -
  • -
  • -

    1016 - Consumer heartbeat time

    - - - - - - - - - - - - - - - - - - - -
    Object TypeSub NumberMemory TypeData TypeAccess TypePDO MappingTPDO detects COS
    Array5ROMUNSIGNED32rwnono
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SubindexNameDefault valueActual value
    00max sub-index4 -
    01Consumer heartbeat time0x00000000 -
    02Consumer heartbeat time0x00000000 -
    03Consumer heartbeat time0x00000000 -
    04Consumer heartbeat time0x00000000 -
    -
    -

    en: Consumer heartbeat time

    -
    - max sub-index -
    -
    - Consumer Heartbeat Time -
    - bit 0-15: Heartbeat consumer time in ms (0 = node is not monitored) -
    - bit 16-23: Node ID -
    - bit 24-31: set to 0 -
    -
    -
  • -
  • -

    1017 - Producer heartbeat time

    - - - - - - - - - - - - - - - - - - - - -
    Object TypeMemory TypeData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    VarROMUNSIGNED16rwnono1000 -
    -
    -

    en: Producer heartbeat time

    -
    bit 0-15: Heartbeat producer time in ms (0 = disable transmission)
    -
    -
  • -
  • -

    1018 - Identity

    - - - - - - - - - - - -
    Object TypeSub NumberMemory Type
    Record5ROM
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SubindexNameData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    00max sub-indexUNSIGNED8ronono4 -
    01Vendor-IDUNSIGNED32ronono0x00000000 -
    02Product codeUNSIGNED32ronono0x00000000 -
    03Revision numberUNSIGNED32ronono0x00000000 -
    04Serial numberUNSIGNED32ronono0x00000000 -
    -
    -

    en: Identity

    -
    - max sub-index -
    -
    - Vendor-ID -
    - bit 0-31: Assigned by CiA -
    -
    - Product code -
    - bit 0-31: Manufacturer specific -
    -
    - Revision number -
    - bit 0-15: Minor revision num. (CANopen behavior has not changed) -
    - bit 16-31: Major revision number (CANopen behavior has changed) -
    -
    - Serial number -
    - bit 0-31: Manufacturer specific -
    -
    -
  • -
  • -

    1019 - Synchronous counter overflow value

    - - - - - - - - - - - - - - - - - - - - -
    Object TypeMemory TypeData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    VarROMUNSIGNED8rwnono0 -
    -
    -

    en: Synchronous counter overflow value

    -
    - If value is zero, then SYNC message is transmitted with data length 0. -
    -
    - If Value is from 2 to 240, then SYNC message has one data byte, which contains the counter. -
    -
    - Other values are reserved. -
    -
    -
  • -
  • -

    1029 - Error behavior

    - - - - - - - - - - - - - - - - - - - -
    Object TypeSub NumberMemory TypeData TypeAccess TypePDO MappingTPDO detects COS
    Array7ROMUNSIGNED8rwnono
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SubindexNameDefault valueActual value
    00max sub-index6 -
    01Communication0x00 -
    02Communication other0x00 -
    03Communication passive0x01 -
    04Generic0x00 -
    05Device profile0x00 -
    06Manufacturer specific0x00 -
    -
    -

    en: Error behavior

    -
    - If error is detected and operating NMT state is NMT operational, this object defines behavior of the device. -
    -
    - Value definition for all subindexes: -
    - 0x00 - if operational, switch to NMT pre-operational -
    - 0x01 - do nothing -
    - 0x02 - switch to NMT stopped -
    -
    - 01 - Communication error - bus off or Heartbeat consumer error. -
    - 02 - Communication other error (critical errors - see 'Error status bits') except CAN bus passive but including bus off or Heartbeat consumer. -
    - 03 - Communication passive - any communication error including CAN bus passive. -
    - 04 - Generic error (critical errors - see 'Error status bits'). -
    - 05 - Device profile error - bit 5 in error register is set. -
    - 06 - Manufacturer specific error - bit 7 in error register is set. -
    -
    -
  • -
  • -

    1200 - SDO server parameter

    - - - - - - - - - - - -
    Object TypeSub NumberMemory Type
    Record3ROM
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SubindexNameData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    00max sub-indexUNSIGNED8ronono2 -
    01COB-ID client to serverUNSIGNED32ronono$NODEID+0x600 -
    02COB-ID server to clientUNSIGNED32ronono$NODEID+0x580 -
    -
    -

    en: SDO server parameter

    -
    - 0x1200 SDO server parameter -
    - max sub-index -
    -
    - COB-ID client to server (Receive SDO) -
    - bit 0-31: 0x00000600 + Node ID -
    -
    - COB-ID server to client (Transmit SDO) -
    - bit 0-31: 0x00000580 + Node ID -
    -
    -
    -
    - 0x1201 - 0x127F SDO server parameter -
    - max sub-index -
    -
    - COB-ID client to server (Receive SDO) -
    - bit 0-10: COB_ID -
    - bit 11-30: Set to 0 -
    - bit 31*: 0(1) - node uses (does NOT use) SDO -
    -
    - COB-ID server to client (Transmit SDO) -
    - bit 0-31: same as previous -
    -
    - Node-ID of the SDO client -
    - bit 0-7: Node ID (optional) -
    -
    -
  • -
  • -

    1400 - RPDO communication parameter

    - - - - - - - - - - - -
    Object TypeSub NumberMemory Type
    Record3ROM
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SubindexNameData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    00max sub-indexUNSIGNED8ronono2 -
    01COB-ID used by RPDOUNSIGNED32rwnono$NODEID+0x200 -
    02transmission typeUNSIGNED8rwnono255 -
    -
    -

    en: RPDO communication parameter

    -
    - 0x1400 - 0x15FF RPDO communication parameter -
    - max sub-index -
    -
    - COB-ID -
    - bit 0-10: COB-ID for PDO, to change it bit 31 must be set -
    - bit 11-29: set to 0 for 11 bit COB-ID -
    - bit 30: 0(1) - rtr are allowed (are NOT allowed) for PDO -
    - bit 31: 0(1) - node uses (does NOT use) PDO -
    -
    - Transmission type -
    - value = 0-240: reciving is synchronous, process after next reception of SYNC object -
    - value = 241-253: not used -
    - value = 254: manufacturer specific -
    - value = 255: asynchronous -
    -
    -
  • -
  • -

    1401 - RPDO communication parameter

    - - - - - - - - - - - -
    Object TypeSub NumberMemory Type
    Record3ROM
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SubindexNameData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    00max sub-indexUNSIGNED8ronono2 -
    01COB-ID used by RPDOUNSIGNED32rwnono$NODEID+0x300 -
    02transmission typeUNSIGNED8rwnono254 -
    -
    -

    en: RPDO communication parameter

    -
    0x1400 - 0x15FF RPDO communication parameter (see description for 0x1400)
    -
    -
  • -
  • -

    1402 - RPDO communication parameter

    - - - - - - - - - - - -
    Object TypeSub NumberMemory Type
    Record3ROM
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SubindexNameData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    00max sub-indexUNSIGNED8ronono2 -
    01COB-ID used by RPDOUNSIGNED32rwnono$NODEID+0x400 -
    02transmission typeUNSIGNED8rwnono254 -
    -
    -

    en: RPDO communication parameter

    -
    0x1400 - 0x15FF RPDO communication parameter (see description for 0x1400)
    -
    -
  • -
  • -

    1403 - RPDO communication parameter

    - - - - - - - - - - - -
    Object TypeSub NumberMemory Type
    Record3ROM
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SubindexNameData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    00max sub-indexUNSIGNED8ronono2 -
    01COB-ID used by RPDOUNSIGNED32rwnono$NODEID+0x500 -
    02transmission typeUNSIGNED8rwnono254 -
    -
    -

    en: RPDO communication parameter

    -
    0x1400 - 0x15FF RPDO communication parameter (see description for 0x1400)
    -
    -
  • -
  • -

    1600 - RPDO mapping parameter

    - - - - - - - - - - - -
    Object TypeSub NumberMemory Type
    Record9ROM
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SubindexNameData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    00Number of mapped objectsUNSIGNED8rwnono2 -
    01mapped object 1UNSIGNED32rwnono0x62000108 -
    02mapped object 2UNSIGNED32rwnono0x62000208 -
    03mapped object 3UNSIGNED32rwnono0x00000000 -
    04mapped object 4UNSIGNED32rwnono0x00000000 -
    05mapped object 5UNSIGNED32rwnono0x00000000 -
    06mapped object 6UNSIGNED32rwnono0x00000000 -
    07mapped object 7UNSIGNED32rwnono0x00000000 -
    08mapped object 8UNSIGNED32rwnono0x00000000 -
    -
    -

    en: RPDO mapping parameter

    -
    - 0x1600 - 0x17FF RPDO mapping parameter (To change mapping, 'Number of mapped objects' must be set to 0) -
    - Number of mapped objects -
    -
    - mapped object (subindex 1...8) -
    - bit 0-7: data length in bits -
    - bit 8-15: subindex from OD -
    - bit 16-31: index from OD -
    -
    -
  • -
  • -

    1601 - RPDO mapping parameter

    - - - - - - - - - - - -
    Object TypeSub NumberMemory Type
    Record9ROM
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SubindexNameData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    00Number of mapped objectsUNSIGNED8rwnono0 -
    01mapped object 1UNSIGNED32rwnono0x00000000 -
    02mapped object 2UNSIGNED32rwnono0x00000000 -
    03mapped object 3UNSIGNED32rwnono0x00000000 -
    04mapped object 4UNSIGNED32rwnono0x00000000 -
    05mapped object 5UNSIGNED32rwnono0x00000000 -
    06mapped object 6UNSIGNED32rwnono0x00000000 -
    07mapped object 7UNSIGNED32rwnono0x00000000 -
    08mapped object 8UNSIGNED32rwnono0x00000000 -
    -
    -

    en: RPDO mapping parameter

    -
    0x1600 - 0x17FF RPDO mapping parameter (see description for 0x1600)
    -
    -
  • -
  • -

    1602 - RPDO mapping parameter

    - - - - - - - - - - - -
    Object TypeSub NumberMemory Type
    Record9ROM
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SubindexNameData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    00Number of mapped objectsUNSIGNED8rwnono0 -
    01mapped object 1UNSIGNED32rwnono0x00000000 -
    02mapped object 2UNSIGNED32rwnono0x00000000 -
    03mapped object 3UNSIGNED32rwnono0x00000000 -
    04mapped object 4UNSIGNED32rwnono0x00000000 -
    05mapped object 5UNSIGNED32rwnono0x00000000 -
    06mapped object 6UNSIGNED32rwnono0x00000000 -
    07mapped object 7UNSIGNED32rwnono0x00000000 -
    08mapped object 8UNSIGNED32rwnono0x00000000 -
    -
    -

    en: RPDO mapping parameter

    -
    0x1600 - 0x17FF RPDO mapping parameter (see description for 0x1600)
    -
    -
  • -
  • -

    1603 - RPDO mapping parameter

    - - - - - - - - - - - -
    Object TypeSub NumberMemory Type
    Record9ROM
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SubindexNameData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    00Number of mapped objectsUNSIGNED8rwnono0 -
    01mapped object 1UNSIGNED32rwnono0x00000000 -
    02mapped object 2UNSIGNED32rwnono0x00000000 -
    03mapped object 3UNSIGNED32rwnono0x00000000 -
    04mapped object 4UNSIGNED32rwnono0x00000000 -
    05mapped object 5UNSIGNED32rwnono0x00000000 -
    06mapped object 6UNSIGNED32rwnono0x00000000 -
    07mapped object 7UNSIGNED32rwnono0x00000000 -
    08mapped object 8UNSIGNED32rwnono0x00000000 -
    -
    -

    en: RPDO mapping parameter

    -
    0x1600 - 0x17FF RPDO mapping parameter (see description for 0x1600)
    -
    -
  • -
  • -

    1800 - TPDO communication parameter

    - - - - - - - - - - - -
    Object TypeSub NumberMemory Type
    Record7ROM
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SubindexNameData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    00max sub-indexUNSIGNED8ronono6 -
    01COB-ID used by TPDOUNSIGNED32rwnono$NODEID+0x180 -
    02transmission typeUNSIGNED8rwnono255 -
    03inhibit timeUNSIGNED16rwnono100 -
    04compatibility entryUNSIGNED8rwnono0 -
    05event timerUNSIGNED16rwnono0 -
    06SYNC start valueUNSIGNED8rwnono0 -
    -
    -

    en: TPDO communication parameter

    -
    - 0x1800 - 0x19FF TPDO communication parameter -
    - max sub-index -
    -
    - COB-ID -
    - bit 0-10: COB-ID for PDO, to change it bit 31 must be set -
    - bit 11-29: set to 0 for 11 bit COB-ID -
    - bit 30: 0(1) - rtr are allowed (are NOT allowed) for PDO -
    - bit 31: 0(1) - node uses (does NOT use) PDO -
    -
    - Transmission type -
    - value = 0: transmiting is synchronous, specification in device profile -
    - value = 1-240: transmiting is synchronous after every N-th SYNC object -
    - value = 241-251: not used -
    - value = 252-253: Transmited only on reception of Remote Transmission Request -
    - value = 254: manufacturer specific -
    - value = 255: asinchronous, specification in device profile -
    -
    - inhibit time -
    - bit 0-15: Minimum time between transmissions of the PDO in 100µs. Zero disables functionality. -
    -
    - compatibility entry -
    - bit 0-7: Not used. -
    -
    - event timer -
    - bit 0-15: Time between periodic transmissions of the PDO in ms. Zero disables functionality. -
    -
    - SYNC start value -
    - value = 0: Counter of the SYNC message shall not be processed. -
    - value = 1-240: The SYNC message with the counter value equal to this value shall be regarded as the first received SYNC message. -
    -
    -
  • -
  • -

    1801 - TPDO communication parameter

    - - - - - - - - - - - -
    Object TypeSub NumberMemory Type
    Record7ROM
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SubindexNameData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    00max sub-indexUNSIGNED8ronono6 -
    01COB-ID used by TPDOUNSIGNED32rwnono$NODEID+0x280 -
    02transmission typeUNSIGNED8rwnono254 -
    03inhibit timeUNSIGNED16rwnono0 -
    04compatibility entryUNSIGNED8rwnono0 -
    05event timerUNSIGNED16rwnono0 -
    06SYNC start valueUNSIGNED8rwnono0 -
    -
    -

    en: TPDO communication parameter

    -
    0x1800 - 0x19FF TPDO communication parameter (see description for 0x1800)
    -
    -
  • -
  • -

    1802 - TPDO communication parameter

    - - - - - - - - - - - -
    Object TypeSub NumberMemory Type
    Record7ROM
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SubindexNameData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    00max sub-indexUNSIGNED8ronono6 -
    01COB-ID used by TPDOUNSIGNED32rwnono$NODEID+0x380 -
    02transmission typeUNSIGNED8rwnono254 -
    03inhibit timeUNSIGNED16rwnono0 -
    04compatibility entryUNSIGNED8rwnono0 -
    05event timerUNSIGNED16rwnono0 -
    06SYNC start valueUNSIGNED8rwnono0 -
    -
    -

    en: TPDO communication parameter

    -
    0x1800 - 0x19FF TPDO communication parameter (see description for 0x1800)
    -
    -
  • -
  • -

    1803 - TPDO communication parameter

    - - - - - - - - - - - -
    Object TypeSub NumberMemory Type
    Record7ROM
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SubindexNameData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    00max sub-indexUNSIGNED8ronono6 -
    01COB-ID used by TPDOUNSIGNED32rwnono$NODEID+0x480 -
    02transmission typeUNSIGNED8rwnono254 -
    03inhibit timeUNSIGNED16rwnono0 -
    04compatibility entryUNSIGNED8rwnono0 -
    05event timerUNSIGNED16rwnono0 -
    06SYNC start valueUNSIGNED8rwnono0 -
    -
    -

    en: TPDO communication parameter

    -
    0x1800 - 0x19FF TPDO communication parameter (see description for 0x1800)
    -
    -
  • -
  • -

    1A00 - TPDO mapping parameter

    - - - - - - - - - - - -
    Object TypeSub NumberMemory Type
    Record9ROM
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SubindexNameData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    00Number of mapped objectsUNSIGNED8rwnono2 -
    01mapped object 1UNSIGNED32rwnono0x60000108 -
    02mapped object 2UNSIGNED32rwnono0x60000208 -
    03mapped object 3UNSIGNED32rwnono0x00000000 -
    04mapped object 4UNSIGNED32rwnono0x00000000 -
    05mapped object 5UNSIGNED32rwnono0x00000000 -
    06mapped object 6UNSIGNED32rwnono0x00000000 -
    07mapped object 7UNSIGNED32rwnono0x00000000 -
    08mapped object 8UNSIGNED32rwnono0x00000000 -
    -
    -

    en: TPDO mapping parameter

    -
    - 0x1A00 - 0x1BFF TPDO mapping parameter. (To change mapping, 'Number of mapped objects' must be set to 0). -
    - Number of mapped objects -
    -
    - mapped object (subindex 1...8) -
    - bit 0-7: data length in bits -
    - bit 8-15: subindex from OD -
    - bit 16-31: index from OD -
    -
    -
  • -
  • -

    1A01 - TPDO mapping parameter

    - - - - - - - - - - - -
    Object TypeSub NumberMemory Type
    Record9ROM
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SubindexNameData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    00Number of mapped objectsUNSIGNED8rwnono0 -
    01mapped object 1UNSIGNED32rwnono0x00000000 -
    02mapped object 2UNSIGNED32rwnono0x00000000 -
    03mapped object 3UNSIGNED32rwnono0x00000000 -
    04mapped object 4UNSIGNED32rwnono0x00000000 -
    05mapped object 5UNSIGNED32rwnono0x00000000 -
    06mapped object 6UNSIGNED32rwnono0x00000000 -
    07mapped object 7UNSIGNED32rwnono0x00000000 -
    08mapped object 8UNSIGNED32rwnono0x00000000 -
    -
    -

    en: TPDO mapping parameter

    -
    0x1A00 - 0x1BFF TPDO mapping parameter (see description for 0x1A00)
    -
    -
  • -
  • -

    1A02 - TPDO mapping parameter

    - - - - - - - - - - - -
    Object TypeSub NumberMemory Type
    Record9ROM
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SubindexNameData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    00Number of mapped objectsUNSIGNED8rwnono0 -
    01mapped object 1UNSIGNED32rwnono0x00000000 -
    02mapped object 2UNSIGNED32rwnono0x00000000 -
    03mapped object 3UNSIGNED32rwnono0x00000000 -
    04mapped object 4UNSIGNED32rwnono0x00000000 -
    05mapped object 5UNSIGNED32rwnono0x00000000 -
    06mapped object 6UNSIGNED32rwnono0x00000000 -
    07mapped object 7UNSIGNED32rwnono0x00000000 -
    08mapped object 8UNSIGNED32rwnono0x00000000 -
    -
    -

    en: TPDO mapping parameter

    -
    0x1A00 - 0x1BFF TPDO mapping parameter (see description for 0x1A00)
    -
    -
  • -
  • -

    1A03 - TPDO mapping parameter

    - - - - - - - - - - - -
    Object TypeSub NumberMemory Type
    Record9ROM
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SubindexNameData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    00Number of mapped objectsUNSIGNED8rwnono0 -
    01mapped object 1UNSIGNED32rwnono0x00000000 -
    02mapped object 2UNSIGNED32rwnono0x00000000 -
    03mapped object 3UNSIGNED32rwnono0x00000000 -
    04mapped object 4UNSIGNED32rwnono0x00000000 -
    05mapped object 5UNSIGNED32rwnono0x00000000 -
    06mapped object 6UNSIGNED32rwnono0x00000000 -
    07mapped object 7UNSIGNED32rwnono0x00000000 -
    08mapped object 8UNSIGNED32rwnono0x00000000 -
    -
    -

    en: TPDO mapping parameter

    -
    0x1A00 - 0x1BFF TPDO mapping parameter (see description for 0x1A00)
    -
    -
  • -
  • -

    1F80 - NMT startup

    - - - - - - - - - - - - - - - - - - - - -
    Object TypeMemory TypeData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    VarROMUNSIGNED32rwnono0x00000000 -
    -
    -

    en: NMT Startup

    -
    - Only bit 2 is implemented. -
    -
    - bit 0: 0(1) - device is not (is) NMT master -
    - bit 1: 0(1) - if bit3=0, start explicitly assigned (all) nodes -
    - bit 2: 0(1) - automaticaly enter (DO NOT automaticaly enter) the operational state on bootup -
    - bit 3: 0(1) - NMT master may (may not) start nodes automatically -
    - bit 4: 0(1) - if monitored node fails heartbeat handle that (all) node(s) -
    - bit 5: 0(1) - flying master process not (yes) supported -
    - bit 6: 0(1) - use bit 4 (ignore bit 4, stop all nodes) -
    - bit 7-31: reserved, set to 0 -
    -
    -
  • -
  • -

    2100 - Error status bits

    - - - - - - - - - - - - - - - - - - - - -
    Object TypeMemory TypeData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    VarRAMOCTET_STRINGrooptionalno00 00 00 00 00 00 00 00 00 00 -
    -
    -

    en: Error Status Bits

    -
    - Error Status Bits indicates error conditions inside stack or inside application. Specific bit is set by CO_errorReport() function, when error occurs in program. It can be reset by CO_errorReset() function, if error is solved. Emergency message is sent on each change of any Error Status Bit. If critical bits are set, node will not be able to stay in operational state. For more information see file CO_Emergency.h. -
    -
    - Default error status bits are: -
    -
    - Communication or protocol errors from driver (informative): -
    - 00 - ERROR_NO_ERROR - Error Reset or No Error. -
    - 01 - ERROR_CAN_BUS_WARNING - CAN bus warning. -
    - 02 - ERROR_RXMSG_WRONG_LENGTH - Wrong data length of received CAN message. -
    - 03 - ERROR_RXMSG_OVERFLOW - Previous received CAN message wasn't processed yet. -
    - 04 - ERROR_RPDO_WRONG_LENGTH - Wrong data length of received PDO. -
    - 05 - ERROR_RPDO_OVERFLOW - Previous received PDO wasn't processed yet. -
    - 06 - ERROR_CAN_RX_BUS_PASSIVE - CAN receive bus is passive. -
    - 07 - ERROR_CAN_TX_BUS_PASSIVE - CAN transmit bus is passive. -
    -
    - Communication or protocol errors from driver (critical): -
    - 08 - ERROR_08_reserved - (reserved) -
    - 09 - ERROR_09_reserved - (reserved) -
    - 0A - ERROR_CAN_TX_BUS_OFF - CAN transmit bus is off. -
    - 0B - ERROR_CAN_RXB_OVERFLOW - CAN module receive buffer has overflowed. -
    - 0C - ERROR_CAN_TX_OVERFLOW - CAN transmit buffer has overflowed. -
    - 0D - ERROR_TPDO_OUTSIDE_WINDOW - TPDO is outside SYNC window. -
    - 0E - ERROR_CAN_CONFIGURATION_FAILED - Configuration of CAN module CAN failed (Rx or Tx). -
    - 0F - ERROR_0F_reserved - (reserved) -
    -
    - Communication or protocol errors (informative): -
    - 10 - ERROR_NMT_WRONG_COMMAND - Wrong NMT command received. -
    - 11 - ERROR_SYNC_EARLY - SYNC message was too early. -
    - 12 - ERROR_12_reserved - (reserved) -
    - 13 - ERROR_13_reserved - (reserved) -
    - 14 - ERROR_14_reserved - (reserved) -
    - 15 - ERROR_15_reserved - (reserved) -
    - 16 - ERROR_16_reserved - (reserved) -
    - 17 - ERROR_17_reserved - (reserved) -
    -
    - Communication or protocol errors (critical): -
    - 18 - ERROR_SYNC_TIME_OUT - SYNC message timeout. -
    - 19 - ERROR_SYNC_LENGTH - Unexpected SYNC data length -
    - 1A - ERROR_PDO_WRONG_MAPPING - Error with PDO mapping. -
    - 1B - ERROR_HEARTBEAT_CONSUMER - Heartbeat consumer timeout. -
    - 1C - ERROR_HEARTBEAT_CONSUMER_REMOTE_RESET - Heartbeat consumer detected remote node reset. -
    - 1D - ERROR_1D_reserved - (reserved) -
    - 1E - ERROR_1E_reserved - (reserved) -
    - 1F - ERROR_1F_reserved - (reserved) -
    -
    - Generic errors (informative): -
    - 20 - ERROR_20_reserved - (reserved) -
    - 21 - ERROR_21_reserved - (reserved) -
    - 22 - ERROR_22_reserved - (reserved) -
    - 23 - ERROR_23_reserved - (reserved) -
    - 24 - ERROR_24_reserved - (reserved) -
    - 25 - ERROR_25_reserved - (reserved) -
    - 26 - ERROR_26_reserved - (reserved) -
    - 27 - ERROR_27_reserved - (reserved) -
    -
    - Generic errors (critical): -
    - 28 - ERROR_WRONG_ERROR_REPORT - Wrong parameters to <CO_errorReport()> function. -
    - 29 - ERROR_ISR_TIMER_OVERFLOW - Timer task has overflowed. -
    - 2A - ERROR_MEMORY_ALLOCATION_ERROR - Unable to allocate memory for objects. -
    - 2B - ERROR_GENERIC_ERROR - Generic error, test usage. -
    - 2C - ERROR_MAIN_TIMER_OVERFLOW - Mainline function exceeded maximum execution time. -
    - 2D - ERROR_INTERNAL_STATE_APPL - Error in application software internal state. -
    - 2E - ERROR_2E_reserved - (reserved) -
    - 2F - ERROR_2F_reserved - (reserved) -
    -
    - Manufacturer specific errors: -
    - Manufacturer may define its own constants up to index 0xFF. Of course, he must then define large enough buffer for error status bits (up to 32 bytes). -
    -
    -
  • -
  • -

    2101 - CAN node ID

    - - - - - - - - - - - - - - - - - - - - -
    Object TypeMemory TypeData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    VarROMUNSIGNED8rwnono0x30 -
    -
    -

    en: CAN Node ID

    -
    - CAN Node ID is CANopenNode specific variable. It sets node-ID for device on CANopen network. Node-ID can be set on other ways too, for example with DIP switches. More advanced solution is use of LSS. -
    -
    - Valid values are from 1 to 127: -
    -
    -
  • -
  • -

    2102 - CAN bit rate

    - - - - - - - - - - - - - - - - - - - - -
    Object TypeMemory TypeData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    VarROMUNSIGNED16rwnono250 -
    -
    -

    en: CAN Bit-Rate

    -
    - CAN Bit-Rate is CANopenNode specific variable. It sets CAN Bit-Rate for device on CANopen network. -
    -
    - Valid values are in [kbps]: -
    - 10, 20, 50, 125, 250, 500, 800, 1000 -
    -
    -
  • -
  • -

    2103 - SYNC counter

    - - - - - - - - - - - - - - - - - - - - -
    Object TypeMemory TypeData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    VarRAMUNSIGNED16rwnono0 -
    -
    -

    en: SYNC Counter

    -
    SYNC Counter is incremented each time, SYNC message is received or transmitted.
    -
    -
  • -
  • -

    2104 - SYNC time

    - - - - - - - - - - - - - - - - - - - - -
    Object TypeMemory TypeData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    VarRAMUNSIGNED16ronono0 -
    -
    -

    en: SYNC Time

    -
    SYNC Time is incremented each timer period and reset to zero, each time SYNC is received or transmitted.
    -
    -
  • -
  • -

    2106 - Power-on counter

    - - - - - - - - - - - - - - - - - - - - -
    Object TypeMemory TypeData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    VarEEPROMUNSIGNED32ronono0 -
    -
    -

    en: Power on Counter

    -
    Power on Counter counts total microcontroller resets in it's lifetime. Variable is an example of EEPROM usage.
    -
    -
  • -
  • -

    2107 - Performance

    - - - - - - - - - - - - - - - - - - - -
    Object TypeSub NumberMemory TypeData TypeAccess TypePDO MappingTPDO detects COS
    Array6RAMUNSIGNED16rwoptionalno
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SubindexNameDefault valueActual value
    00max sub-index5 -
    01cycles per second1000 -
    02timer cycle time0 -
    03timer cycle max time0 -
    04main cycle time0 -
    05main cycle max time0 -
    -
    -

    en: Performance

    -
    - Internal performance of the microcontroller. -
    -
    - cycles per second - cycle frequency of internal timer. -
    - timer cycle time - current timer cycle time in percent of timer period. -
    - timer cycle max time - maximum timer cycle time in percent of timer period. -
    - main cycle time - current mainline function cycle time in percent of timer period. -
    - main cycle max time - maximum mainline function cycle time in percent of timer period. -
    -
    -
  • -
  • -

    2108 - Temperature

    - - - - - - - - - - - - - - - - - - - -
    Object TypeSub NumberMemory TypeData TypeAccess TypePDO MappingTPDO detects COS
    Array2RAMINTEGER16rooptionalno
    -
    - - - - - - - - - - - - - - - - - -
    SubindexNameDefault valueActual value
    00max sub-index1 -
    01main PCB0 -
    -
    -

    en: Temperature

    -
    Current temperature on device in 0,1°C.
    -
    -
  • -
  • -

    2109 - Voltage

    - - - - - - - - - - - - - - - - - - - -
    Object TypeSub NumberMemory TypeData TypeAccess TypePDO MappingTPDO detects COS
    Array2RAMINTEGER16rooptionalno
    -
    - - - - - - - - - - - - - - - - - -
    SubindexNameDefault valueActual value
    00max sub-index1 -
    01main PCB supply0 -
    -
    -

    en: Voltage

    -
    Current voltage on device in 0,1V.
    -
    -
  • -
  • -

    2110 - Variable Int32

    - - - - - - - - - - - - - - - - - - - -
    Object TypeSub NumberMemory TypeData TypeAccess TypePDO MappingTPDO detects COS
    Array17RAMINTEGER32rwoptionalyes
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SubindexNameDefault valueActual value
    00max sub-index16 -
    01int320 -
    02int320 -
    03int320 -
    04int320 -
    05int320 -
    06int320 -
    07int320 -
    08int320 -
    09int320 -
    0Aint320 -
    0Bint320 -
    0Cint320 -
    0Dint320 -
    0Eint320 -
    0Fint320 -
    10int320 -
    -
    -

    en: Variable Integer 32bit

    -
    Vartiable is free to use by application.
    -
    -
  • -
  • -

    2111 - Variable ROM Int32

    - - - - - - - - - - - - - - - - - - - -
    Object TypeSub NumberMemory TypeData TypeAccess TypePDO MappingTPDO detects COS
    Array17ROMINTEGER32rwoptionalyes
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SubindexNameDefault valueActual value
    00max sub-index16 -
    01int321 -
    02int320 -
    03int320 -
    04int320 -
    05int320 -
    06int320 -
    07int320 -
    08int320 -
    09int320 -
    0Aint320 -
    0Bint320 -
    0Cint320 -
    0Dint320 -
    0Eint320 -
    0Fint320 -
    10int320 -
    -
    -

    en: Variable ROM Integer 32bit

    -
    Vartiable is free to use by application.
    -
    -
  • -
  • -

    2112 - Variable NV Int32

    - - - - - - - - - - - - - - - - - - - -
    Object TypeSub NumberMemory TypeData TypeAccess TypePDO MappingTPDO detects COS
    Array17EEPROMINTEGER32rwoptionalyes
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SubindexNameDefault valueActual value
    00max sub-index16 -
    01int321 -
    02int320 -
    03int320 -
    04int320 -
    05int320 -
    06int320 -
    07int320 -
    08int320 -
    09int320 -
    0Aint320 -
    0Bint320 -
    0Cint320 -
    0Dint320 -
    0Eint320 -
    0Fint320 -
    10int320 -
    -
    -

    en: Variable nonvolatile Integer 32bit

    -
    Vartiable is free to use by application. Variable is stored to internal baterry powered SRAM.
    -
    -
  • -
  • -

    2120 - test var

    - - - - - - - - - - - -
    Object TypeSub NumberMemory Type
    Record6RAM
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SubindexNameData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    00max sub-indexUNSIGNED8ronono5 -
    01I64INTEGER64rwoptionalno0x1234567890ABCDEFLL -
    02U64UNSIGNED64rwoptionalno0x234567890ABCDEF1LL -
    03R32REAL32rwoptionalno12.345 -
    04R64REAL64rwoptionalno456.789 -
    05domainDOMAINrwnono0 -
    -
  • -
  • -

    2130 - Time

    - - - - - - - - - - - -
    Object TypeSub NumberMemory Type
    Record4RAM
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SubindexNameData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    00max sub-indexUNSIGNED8ronono3 -
    01StringVISIBLE_STRINGronono- -
    02Epoch time base msUNSIGNED64rwnono0 -
    03Epoch time offset msUNSIGNED32rwoptionalno0 -
    -
    -

    en: Time

    -
    - Variable displays current time. -
    -
    - String - returns current time as string. -
    -
    - Epoch time ms - Milliseconds since unix epoch (1.1.1970, 00:00:00, UTC). It is calculated as base + offset. Base is calculated on startup of program and offset increments continuously since then. 'Epoch time offset ms' overflows each 49,7 days. 'Epoch time base ms' is recalculated in case of overflow or in case of write to 'Epoch time offset ms'. It is rounded to one minute. -
    -
    -
  • -
  • -

    2301 - Trace config

    - - - - - - - - - - - -
    Object TypeSub NumberMemory Type
    Record9ROM
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SubindexNameData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    00max sub-indexUNSIGNED8ronono8 -
    01SizeUNSIGNED32rwnono100 -
    02Axis noUNSIGNED8rwnono1 -
    03NameVISIBLE_STRINGrwnonoTrace1 -
    04ColorVISIBLE_STRINGrwnonored -
    05MapUNSIGNED32rwnono0x60000108 -
    06FormatUNSIGNED8rwnono1 -
    07TriggerUNSIGNED8rwnono0 -
    08ThresholdINTEGER32rwnono0 -
    -
    -

    en: Trace config

    -
    - Trace is used for recording variables. -
    -
    - See also Trace. -
    -
    - Size - Maximum number of trace records in circular buffer. By reading it returns actual buffer size (If zero, malloc may had been failed at startup). By writing it sets the size, which will be valid after next reboot. (It is necessary to store parameters before (0x1010).) -
    -
    - Axis no - If value is different than zero, trace is enabled. Value is informative and describes group of traces (Multiple traces can belong to single axis.). By reading it returns zero if trace is disabled or if it is not configured properly. When trace is enabled, internal buffer is cleared. -
    -
    - Name - name of the trace as a string (informative). -
    -
    - Color - color of the trace as a string (informative). -
    -
    - Map - Map to variable in object dictionary, similar as PDO map(two bytes of index, one byte of subindex and 1 byte of bitlength. It's value will be copied to trace.value in case index and subindex are correct. If map index and subindex is zero, RPDO may be mapped to trace.value, for example. Valid values of map.bitlength is 08, 10 or 20 or 00, which indicates 8-bit, 16bit, 32-bit or default-size variable. Trace must be disabled, if mapping is written (Axis no set to 0). -
    -
    - Format - If first bit is zero, then value is used as signed integer otherwise as unsigned integer. If format is 0 or 1, text points are generated for time and value: "123;321\n140;345\n..." If format is 2 or 3, binary data is generated: 4-byte timestamp and 4-byte value. If format is 4 or 5, SVG path is generated: "M123,321H140V345...". -
    -
    - Trigger - 0=disabled, 1=rising edge, 2=falling edge, 3=both edges. -
    -
    - Threshold - If integer value passes threshold (based on trigger setting), time is recorded into 'trigger time'. -
    -
    -
  • -
  • -

    2302 - Trace config

    - - - - - - - - - - - -
    Object TypeSub NumberMemory Type
    Record9ROM
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SubindexNameData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    00max sub-indexUNSIGNED8ronono8 -
    01SizeUNSIGNED32rwnono0 -
    02Axis noUNSIGNED8rwnono0 -
    03NameVISIBLE_STRINGrwnonoTrace2 -
    04ColorVISIBLE_STRINGrwnonogreen -
    05MapUNSIGNED32rwnono0x00000000 -
    06FormatUNSIGNED8rwnono0 -
    07TriggerUNSIGNED8rwnono0 -
    08ThresholdINTEGER32rwnono0 -
    -
  • -
  • -

    2400 - Trace enable

    - - - - - - - - - - - - - - - - - - - - -
    Object TypeMemory TypeData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    VarRAMUNSIGNED8rwoptionalno0 -
    -
    -

    en: Trace enable

    -
    This object controls, how many trace objects (beginning from index 0x2401) are enabled. If zero, all traces are disabled.
    -
    -
  • -
  • -

    2401 - Trace

    - - - - - - - - - - - -
    Object TypeSub NumberMemory Type
    Record7RAM
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SubindexNameData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    00max sub-indexUNSIGNED8ronono6 -
    01SizeUNSIGNED32rwoptionalno0 -
    02ValueINTEGER32rwoptionalno0 -
    03MinINTEGER32rwoptionalno0 -
    04MaxINTEGER32rwoptionalno0 -
    05PlotDOMAINronono0 -
    06Trigger timeUNSIGNED32rwoptionalno0 -
    -
    -

    en: Trace

    -
    - Trace is used for recording variables. -
    -
    - See also Trace config. -
    -
    - Size - Number of current records in buffer. -
    -
    - Value - Latest value. If traceConfig.map (index and subindex) is valid, then mapped variable will be copied here. -
    -
    - Min, Max - Minimum and maximum value. -
    -
    - Plot - SVG path generated from the values from circular buffer. For time axis is used Time->'Epoch time offset ms'. When Plot is read, internal buffer is emptied. -
    -
    - Trigger time - If integer value passes 'TraceConfig->threshold', then time is recorded into this variable. -
    -
    -
  • -
  • -

    2402 - Trace

    - - - - - - - - - - - -
    Object TypeSub NumberMemory Type
    Record7RAM
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SubindexNameData TypeAccess TypePDO MappingTPDO detects COSDefault valueActual value
    00max sub-indexUNSIGNED8ronono6 -
    01SizeUNSIGNED32rwoptionalno0 -
    02ValueINTEGER32rwoptionalno0 -
    03MinINTEGER32rwoptionalno0 -
    04MaxINTEGER32rwoptionalno0 -
    05PlotDOMAINronono0 -
    06Trigger timeUNSIGNED32rwoptionalno0 -
    -
  • -
  • -

    6000 - Read input 8 bit

    - - - - - - - - - - - - - - - - - - - -
    Object TypeSub NumberMemory TypeData TypeAccess TypePDO MappingTPDO detects COS
    Array9RAMUNSIGNED8rooptionalyes
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SubindexNameDefault valueActual value
    00max sub-index8 -
    01Input0x00 -
    02Input0x00 -
    03Input0x00 -
    04Input0x00 -
    05Input0x00 -
    06Input0x00 -
    07Input0x00 -
    08Input0x00 -
    -
    -

    en: Read input 8 bit

    -
    Digital inputs from hardware.
    -
    -
  • -
  • -

    6200 - Write output 8 bit

    - - - - - - - - - - - - - - - - - - - -
    Object TypeSub NumberMemory TypeData TypeAccess TypePDO MappingTPDO detects COS
    Array9RAMUNSIGNED8rwoptionalno
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SubindexNameDefault valueActual value
    00max sub-index8 -
    01Output0x00 -
    02Output0x00 -
    03Output0x00 -
    04Output0x00 -
    05Output0x00 -
    06Output0x00 -
    07Output0x00 -
    08Output0x00 -
    -
    -

    en: Write output 8 bit

    -
    Digital outputs on hardware.
    -
    -
  • -
  • -

    6401 - Read analogue input 16 bit

    - - - - - - - - - - - - - - - - - - - -
    Object TypeSub NumberMemory TypeData TypeAccess TypePDO MappingTPDO detects COS
    Array13RAMINTEGER16rooptionalno
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SubindexNameDefault valueActual value
    00max sub-index12 -
    01Input0 -
    02Input0 -
    03Input0 -
    04Input0 -
    05Input0 -
    06Input0 -
    07Input0 -
    08Input0 -
    09Input0 -
    0AInput0 -
    0BInput0 -
    0CInput0 -
    -
    -

    en: Read analogue input 16 bit

    -
    Analogue inputs from hardware. The integer value is left adjusted.
    -
    -
  • -
  • -

    6411 - Write analogue output 16 bit

    - - - - - - - - - - - - - - - - - - - -
    Object TypeSub NumberMemory TypeData TypeAccess TypePDO MappingTPDO detects COS
    Array9RAMINTEGER16rwoptionalno
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SubindexNameDefault valueActual value
    00max sub-index8 -
    01Output0 -
    02Output0 -
    03Output0 -
    04Output0 -
    05Output0 -
    06Output0 -
    07Output0 -
    08Output0 -
    -
    -

    en: Write analogue output 16 bit

    -
    Analogue outputs on hardware. The integer value is left adjusted.
    -
    -
  • -
- -
- - diff --git a/example/Makefile b/example/Makefile new file mode 100644 index 00000000..531d4bfd --- /dev/null +++ b/example/Makefile @@ -0,0 +1,58 @@ +# Makefile for CANopenNode, basic compile with blank CAN device + + +DRV_SRC = . +CANOPEN_SRC = .. +APPL_SRC = . + + +LINK_TARGET = canopennode_blank + + +INCLUDE_DIRS = \ + -I$(DRV_SRC) \ + -I$(CANOPEN_SRC) \ + -I$(APPL_SRC) + + +SOURCES = \ + $(DRV_SRC)/CO_driver_blank.c \ + $(DRV_SRC)/CO_storageBlank.c \ + $(CANOPEN_SRC)/301/CO_ODinterface.c \ + $(CANOPEN_SRC)/301/CO_NMT_Heartbeat.c \ + $(CANOPEN_SRC)/301/CO_HBconsumer.c \ + $(CANOPEN_SRC)/301/CO_Emergency.c \ + $(CANOPEN_SRC)/301/CO_SDOserver.c \ + $(CANOPEN_SRC)/301/CO_TIME.c \ + $(CANOPEN_SRC)/301/CO_SYNC.c \ + $(CANOPEN_SRC)/301/CO_PDO.c \ + $(CANOPEN_SRC)/303/CO_LEDs.c \ + $(CANOPEN_SRC)/305/CO_LSSslave.c \ + $(CANOPEN_SRC)/storage/CO_storage.c \ + $(CANOPEN_SRC)/CANopen.c \ + $(APPL_SRC)/OD.c \ + $(DRV_SRC)/main_blank.c + + +OBJS = $(SOURCES:%.c=%.o) +CC ?= gcc +OPT = +OPT += -g +#OPT += -DCO_USE_GLOBALS +#OPT += -DCO_MULTIPLE_OD +CFLAGS = -Wall $(OPT) $(INCLUDE_DIRS) +LDFLAGS = + + +.PHONY: all clean + +all: clean $(LINK_TARGET) + +clean: + rm -f $(OBJS) $(LINK_TARGET) + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +$(LINK_TARGET): $(OBJS) + $(CC) $(LDFLAGS) $^ -o $@ diff --git a/example/OD.c b/example/OD.c new file mode 100644 index 00000000..7db61075 --- /dev/null +++ b/example/OD.c @@ -0,0 +1,1161 @@ +/******************************************************************************* + CANopen Object Dictionary definition for CANopenNode V4 + + This file was automatically generated by CANopenEditor v4.1-1-ga49a51a + + https://github.com/CANopenNode/CANopenNode + https://github.com/CANopenNode/CANopenEditor + + DON'T EDIT THIS FILE MANUALLY, UNLESS YOU KNOW WHAT YOU ARE DOING !!!! +*******************************************************************************/ + +#define OD_DEFINITION +#include "301/CO_ODinterface.h" +#include "OD.h" + +#if CO_VERSION_MAJOR < 4 +#error This Object dictionary is compatible with CANopenNode V4.0 and above! +#endif + +/******************************************************************************* + OD data initialization of all groups +*******************************************************************************/ +OD_ATTR_PERSIST_COMM OD_PERSIST_COMM_t OD_PERSIST_COMM = { + .x1000_deviceType = 0x00000000, + .x1005_COB_ID_SYNCMessage = 0x00000080, + .x1006_communicationCyclePeriod = 0x00000000, + .x1007_synchronousWindowLength = 0x00000000, + .x1012_COB_IDTimeStampObject = 0x00000100, + .x1014_COB_ID_EMCY = 0x00000080, + .x1015_inhibitTimeEMCY = 0x0000, + .x1016_consumerHeartbeatTime_sub0 = 0x08, + .x1016_consumerHeartbeatTime = {0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + .x1017_producerHeartbeatTime = 0x0000, + .x1018_identity = { + .highestSub_indexSupported = 0x04, + .vendor_ID = 0x00000000, + .productCode = 0x00000000, + .revisionNumber = 0x00000000, + .serialNumber = 0x00000000 + }, + .x1019_synchronousCounterOverflowValue = 0x00, + .x1280_SDOClientParameter = { + .highestSub_indexSupported = 0x03, + .COB_IDClientToServerTx = 0x80000000, + .COB_IDServerToClientRx = 0x80000000, + .node_IDOfTheSDOServer = 0x01 + }, + .x1400_RPDOCommunicationParameter = { + .highestSub_indexSupported = 0x05, + .COB_IDUsedByRPDO = 0x80000200, + .transmissionType = 0xFE, + .eventTimer = 0x0000 + }, + .x1401_RPDOCommunicationParameter = { + .highestSub_indexSupported = 0x05, + .COB_IDUsedByRPDO = 0x80000300, + .transmissionType = 0xFE, + .eventTimer = 0x0000 + }, + .x1402_RPDOCommunicationParameter = { + .highestSub_indexSupported = 0x05, + .COB_IDUsedByRPDO = 0x80000400, + .transmissionType = 0xFE, + .eventTimer = 0x0000 + }, + .x1403_RPDOCommunicationParameter = { + .highestSub_indexSupported = 0x05, + .COB_IDUsedByRPDO = 0x80000500, + .transmissionType = 0xFE, + .eventTimer = 0x0000 + }, + .x1600_RPDOMappingParameter = { + .numberOfMappedApplicationObjectsInPDO = 0x00, + .applicationObject1 = 0x00000000, + .applicationObject2 = 0x00000000, + .applicationObject3 = 0x00000000, + .applicationObject4 = 0x00000000, + .applicationObject5 = 0x00000000, + .applicationObject6 = 0x00000000, + .applicationObject7 = 0x00000000, + .applicationObject8 = 0x00000000 + }, + .x1601_RPDOMappingParameter = { + .numberOfMappedApplicationObjectsInPDO = 0x00, + .applicationObject1 = 0x00000000, + .applicationObject2 = 0x00000000, + .applicationObject3 = 0x00000000, + .applicationObject4 = 0x00000000, + .applicationObject5 = 0x00000000, + .applicationObject6 = 0x00000000, + .applicationObject7 = 0x00000000, + .applicationObject8 = 0x00000000 + }, + .x1602_RPDOMappingParameter = { + .numberOfMappedApplicationObjectsInPDO = 0x00, + .applicationObject1 = 0x00000000, + .applicationObject2 = 0x00000000, + .applicationObject3 = 0x00000000, + .applicationObject4 = 0x00000000, + .applicationObject5 = 0x00000000, + .applicationObject6 = 0x00000000, + .applicationObject7 = 0x00000000, + .applicationObject8 = 0x00000000 + }, + .x1603_RPDOMappingParameter = { + .numberOfMappedApplicationObjectsInPDO = 0x00, + .applicationObject1 = 0x00000000, + .applicationObject2 = 0x00000000, + .applicationObject3 = 0x00000000, + .applicationObject4 = 0x00000000, + .applicationObject5 = 0x00000000, + .applicationObject6 = 0x00000000, + .applicationObject7 = 0x00000000, + .applicationObject8 = 0x00000000 + }, + .x1800_TPDOCommunicationParameter = { + .highestSub_indexSupported = 0x06, + .COB_IDUsedByTPDO = 0xC0000180, + .transmissionType = 0xFE, + .inhibitTime = 0x0000, + .eventTimer = 0x0000, + .SYNCStartValue = 0x00 + }, + .x1801_TPDOCommunicationParameter = { + .highestSub_indexSupported = 0x06, + .COB_IDUsedByTPDO = 0xC0000280, + .transmissionType = 0xFE, + .inhibitTime = 0x0000, + .eventTimer = 0x0000, + .SYNCStartValue = 0x00 + }, + .x1802_TPDOCommunicationParameter = { + .highestSub_indexSupported = 0x06, + .COB_IDUsedByTPDO = 0xC0000380, + .transmissionType = 0xFE, + .inhibitTime = 0x0000, + .eventTimer = 0x0000, + .SYNCStartValue = 0x00 + }, + .x1803_TPDOCommunicationParameter = { + .highestSub_indexSupported = 0x06, + .COB_IDUsedByTPDO = 0xC0000480, + .transmissionType = 0xFE, + .inhibitTime = 0x0000, + .eventTimer = 0x0000, + .SYNCStartValue = 0x00 + }, + .x1A00_TPDOMappingParameter = { + .numberOfMappedApplicationObjectsInPDO = 0x00, + .applicationObject1 = 0x00000000, + .applicationObject2 = 0x00000000, + .applicationObject3 = 0x00000000, + .applicationObject4 = 0x00000000, + .applicationObject5 = 0x00000000, + .applicationObject6 = 0x00000000, + .applicationObject7 = 0x00000000, + .applicationObject8 = 0x00000000 + }, + .x1A01_TPDOMappingParameter = { + .numberOfMappedApplicationObjectsInPDO = 0x00, + .applicationObject1 = 0x00000000, + .applicationObject2 = 0x00000000, + .applicationObject3 = 0x00000000, + .applicationObject4 = 0x00000000, + .applicationObject5 = 0x00000000, + .applicationObject6 = 0x00000000, + .applicationObject7 = 0x00000000, + .applicationObject8 = 0x00000000 + }, + .x1A02_TPDOMappingParameter = { + .numberOfMappedApplicationObjectsInPDO = 0x00, + .applicationObject1 = 0x00000000, + .applicationObject2 = 0x00000000, + .applicationObject3 = 0x00000000, + .applicationObject4 = 0x00000000, + .applicationObject5 = 0x00000000, + .applicationObject6 = 0x00000000, + .applicationObject7 = 0x00000000, + .applicationObject8 = 0x00000000 + }, + .x1A03_TPDOMappingParameter = { + .numberOfMappedApplicationObjectsInPDO = 0x00, + .applicationObject1 = 0x00000000, + .applicationObject2 = 0x00000000, + .applicationObject3 = 0x00000000, + .applicationObject4 = 0x00000000, + .applicationObject5 = 0x00000000, + .applicationObject6 = 0x00000000, + .applicationObject7 = 0x00000000, + .applicationObject8 = 0x00000000 + } +}; + +OD_ATTR_RAM OD_RAM_t OD_RAM = { + .x1001_errorRegister = 0x00, + .x1010_storeParameters_sub0 = 0x04, + .x1010_storeParameters = {0x00000001, 0x00000001, 0x00000001, 0x00000001}, + .x1011_restoreDefaultParameters_sub0 = 0x04, + .x1011_restoreDefaultParameters = {0x00000001, 0x00000001, 0x00000001, 0x00000001}, + .x1200_SDOServerParameter = { + .highestSub_indexSupported = 0x02, + .COB_IDClientToServerRx = 0x00000600, + .COB_IDServerToClientTx = 0x00000580 + } +}; + + + +/******************************************************************************* + All OD objects (constant definitions) +*******************************************************************************/ +typedef struct { + OD_obj_var_t o_1000_deviceType; + OD_obj_var_t o_1001_errorRegister; + OD_obj_array_t o_1003_pre_definedErrorField; + OD_obj_var_t o_1005_COB_ID_SYNCMessage; + OD_obj_var_t o_1006_communicationCyclePeriod; + OD_obj_var_t o_1007_synchronousWindowLength; + OD_obj_array_t o_1010_storeParameters; + OD_obj_array_t o_1011_restoreDefaultParameters; + OD_obj_var_t o_1012_COB_IDTimeStampObject; + OD_obj_var_t o_1014_COB_ID_EMCY; + OD_obj_var_t o_1015_inhibitTimeEMCY; + OD_obj_array_t o_1016_consumerHeartbeatTime; + OD_obj_var_t o_1017_producerHeartbeatTime; + OD_obj_record_t o_1018_identity[5]; + OD_obj_var_t o_1019_synchronousCounterOverflowValue; + OD_obj_record_t o_1200_SDOServerParameter[3]; + OD_obj_record_t o_1280_SDOClientParameter[4]; + OD_obj_record_t o_1400_RPDOCommunicationParameter[4]; + OD_obj_record_t o_1401_RPDOCommunicationParameter[4]; + OD_obj_record_t o_1402_RPDOCommunicationParameter[4]; + OD_obj_record_t o_1403_RPDOCommunicationParameter[4]; + OD_obj_record_t o_1600_RPDOMappingParameter[9]; + OD_obj_record_t o_1601_RPDOMappingParameter[9]; + OD_obj_record_t o_1602_RPDOMappingParameter[9]; + OD_obj_record_t o_1603_RPDOMappingParameter[9]; + OD_obj_record_t o_1800_TPDOCommunicationParameter[6]; + OD_obj_record_t o_1801_TPDOCommunicationParameter[6]; + OD_obj_record_t o_1802_TPDOCommunicationParameter[6]; + OD_obj_record_t o_1803_TPDOCommunicationParameter[6]; + OD_obj_record_t o_1A00_TPDOMappingParameter[9]; + OD_obj_record_t o_1A01_TPDOMappingParameter[9]; + OD_obj_record_t o_1A02_TPDOMappingParameter[9]; + OD_obj_record_t o_1A03_TPDOMappingParameter[9]; +} ODObjs_t; + +static CO_PROGMEM ODObjs_t ODObjs = { + .o_1000_deviceType = { + .dataOrig = &OD_PERSIST_COMM.x1000_deviceType, + .attribute = ODA_SDO_R | ODA_MB, + .dataLength = 4 + }, + .o_1001_errorRegister = { + .dataOrig = &OD_RAM.x1001_errorRegister, + .attribute = ODA_SDO_R | ODA_TPDO, + .dataLength = 1 + }, + .o_1003_pre_definedErrorField = { + .dataOrig0 = NULL, + .dataOrig = NULL, + .attribute0 = ODA_SDO_RW, + .attribute = ODA_SDO_R | ODA_MB, + .dataElementLength = 4, + .dataElementSizeof = sizeof(uint32_t) + }, + .o_1005_COB_ID_SYNCMessage = { + .dataOrig = &OD_PERSIST_COMM.x1005_COB_ID_SYNCMessage, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + .o_1006_communicationCyclePeriod = { + .dataOrig = &OD_PERSIST_COMM.x1006_communicationCyclePeriod, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + .o_1007_synchronousWindowLength = { + .dataOrig = &OD_PERSIST_COMM.x1007_synchronousWindowLength, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + .o_1010_storeParameters = { + .dataOrig0 = &OD_RAM.x1010_storeParameters_sub0, + .dataOrig = &OD_RAM.x1010_storeParameters[0], + .attribute0 = ODA_SDO_R, + .attribute = ODA_SDO_RW | ODA_MB, + .dataElementLength = 4, + .dataElementSizeof = sizeof(uint32_t) + }, + .o_1011_restoreDefaultParameters = { + .dataOrig0 = &OD_RAM.x1011_restoreDefaultParameters_sub0, + .dataOrig = &OD_RAM.x1011_restoreDefaultParameters[0], + .attribute0 = ODA_SDO_R, + .attribute = ODA_SDO_RW | ODA_MB, + .dataElementLength = 4, + .dataElementSizeof = sizeof(uint32_t) + }, + .o_1012_COB_IDTimeStampObject = { + .dataOrig = &OD_PERSIST_COMM.x1012_COB_IDTimeStampObject, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + .o_1014_COB_ID_EMCY = { + .dataOrig = &OD_PERSIST_COMM.x1014_COB_ID_EMCY, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + .o_1015_inhibitTimeEMCY = { + .dataOrig = &OD_PERSIST_COMM.x1015_inhibitTimeEMCY, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 2 + }, + .o_1016_consumerHeartbeatTime = { + .dataOrig0 = &OD_PERSIST_COMM.x1016_consumerHeartbeatTime_sub0, + .dataOrig = &OD_PERSIST_COMM.x1016_consumerHeartbeatTime[0], + .attribute0 = ODA_SDO_R, + .attribute = ODA_SDO_RW | ODA_MB, + .dataElementLength = 4, + .dataElementSizeof = sizeof(uint32_t) + }, + .o_1017_producerHeartbeatTime = { + .dataOrig = &OD_PERSIST_COMM.x1017_producerHeartbeatTime, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 2 + }, + .o_1018_identity = { + { + .dataOrig = &OD_PERSIST_COMM.x1018_identity.highestSub_indexSupported, + .subIndex = 0, + .attribute = ODA_SDO_R, + .dataLength = 1 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1018_identity.vendor_ID, + .subIndex = 1, + .attribute = ODA_SDO_R | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1018_identity.productCode, + .subIndex = 2, + .attribute = ODA_SDO_R | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1018_identity.revisionNumber, + .subIndex = 3, + .attribute = ODA_SDO_R | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1018_identity.serialNumber, + .subIndex = 4, + .attribute = ODA_SDO_R | ODA_MB, + .dataLength = 4 + } + }, + .o_1019_synchronousCounterOverflowValue = { + .dataOrig = &OD_PERSIST_COMM.x1019_synchronousCounterOverflowValue, + .attribute = ODA_SDO_RW, + .dataLength = 1 + }, + .o_1200_SDOServerParameter = { + { + .dataOrig = &OD_RAM.x1200_SDOServerParameter.highestSub_indexSupported, + .subIndex = 0, + .attribute = ODA_SDO_R, + .dataLength = 1 + }, + { + .dataOrig = &OD_RAM.x1200_SDOServerParameter.COB_IDClientToServerRx, + .subIndex = 1, + .attribute = ODA_SDO_R | ODA_TPDO | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_RAM.x1200_SDOServerParameter.COB_IDServerToClientTx, + .subIndex = 2, + .attribute = ODA_SDO_R | ODA_TPDO | ODA_MB, + .dataLength = 4 + } + }, + .o_1280_SDOClientParameter = { + { + .dataOrig = &OD_PERSIST_COMM.x1280_SDOClientParameter.highestSub_indexSupported, + .subIndex = 0, + .attribute = ODA_SDO_R, + .dataLength = 1 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1280_SDOClientParameter.COB_IDClientToServerTx, + .subIndex = 1, + .attribute = ODA_SDO_RW | ODA_TRPDO | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1280_SDOClientParameter.COB_IDServerToClientRx, + .subIndex = 2, + .attribute = ODA_SDO_RW | ODA_TRPDO | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1280_SDOClientParameter.node_IDOfTheSDOServer, + .subIndex = 3, + .attribute = ODA_SDO_RW, + .dataLength = 1 + } + }, + .o_1400_RPDOCommunicationParameter = { + { + .dataOrig = &OD_PERSIST_COMM.x1400_RPDOCommunicationParameter.highestSub_indexSupported, + .subIndex = 0, + .attribute = ODA_SDO_R, + .dataLength = 1 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1400_RPDOCommunicationParameter.COB_IDUsedByRPDO, + .subIndex = 1, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1400_RPDOCommunicationParameter.transmissionType, + .subIndex = 2, + .attribute = ODA_SDO_RW, + .dataLength = 1 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1400_RPDOCommunicationParameter.eventTimer, + .subIndex = 5, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 2 + } + }, + .o_1401_RPDOCommunicationParameter = { + { + .dataOrig = &OD_PERSIST_COMM.x1401_RPDOCommunicationParameter.highestSub_indexSupported, + .subIndex = 0, + .attribute = ODA_SDO_R, + .dataLength = 1 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1401_RPDOCommunicationParameter.COB_IDUsedByRPDO, + .subIndex = 1, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1401_RPDOCommunicationParameter.transmissionType, + .subIndex = 2, + .attribute = ODA_SDO_RW, + .dataLength = 1 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1401_RPDOCommunicationParameter.eventTimer, + .subIndex = 5, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 2 + } + }, + .o_1402_RPDOCommunicationParameter = { + { + .dataOrig = &OD_PERSIST_COMM.x1402_RPDOCommunicationParameter.highestSub_indexSupported, + .subIndex = 0, + .attribute = ODA_SDO_R, + .dataLength = 1 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1402_RPDOCommunicationParameter.COB_IDUsedByRPDO, + .subIndex = 1, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1402_RPDOCommunicationParameter.transmissionType, + .subIndex = 2, + .attribute = ODA_SDO_RW, + .dataLength = 1 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1402_RPDOCommunicationParameter.eventTimer, + .subIndex = 5, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 2 + } + }, + .o_1403_RPDOCommunicationParameter = { + { + .dataOrig = &OD_PERSIST_COMM.x1403_RPDOCommunicationParameter.highestSub_indexSupported, + .subIndex = 0, + .attribute = ODA_SDO_R, + .dataLength = 1 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1403_RPDOCommunicationParameter.COB_IDUsedByRPDO, + .subIndex = 1, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1403_RPDOCommunicationParameter.transmissionType, + .subIndex = 2, + .attribute = ODA_SDO_RW, + .dataLength = 1 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1403_RPDOCommunicationParameter.eventTimer, + .subIndex = 5, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 2 + } + }, + .o_1600_RPDOMappingParameter = { + { + .dataOrig = &OD_PERSIST_COMM.x1600_RPDOMappingParameter.numberOfMappedApplicationObjectsInPDO, + .subIndex = 0, + .attribute = ODA_SDO_RW, + .dataLength = 1 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1600_RPDOMappingParameter.applicationObject1, + .subIndex = 1, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1600_RPDOMappingParameter.applicationObject2, + .subIndex = 2, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1600_RPDOMappingParameter.applicationObject3, + .subIndex = 3, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1600_RPDOMappingParameter.applicationObject4, + .subIndex = 4, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1600_RPDOMappingParameter.applicationObject5, + .subIndex = 5, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1600_RPDOMappingParameter.applicationObject6, + .subIndex = 6, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1600_RPDOMappingParameter.applicationObject7, + .subIndex = 7, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1600_RPDOMappingParameter.applicationObject8, + .subIndex = 8, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + } + }, + .o_1601_RPDOMappingParameter = { + { + .dataOrig = &OD_PERSIST_COMM.x1601_RPDOMappingParameter.numberOfMappedApplicationObjectsInPDO, + .subIndex = 0, + .attribute = ODA_SDO_RW, + .dataLength = 1 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1601_RPDOMappingParameter.applicationObject1, + .subIndex = 1, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1601_RPDOMappingParameter.applicationObject2, + .subIndex = 2, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1601_RPDOMappingParameter.applicationObject3, + .subIndex = 3, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1601_RPDOMappingParameter.applicationObject4, + .subIndex = 4, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1601_RPDOMappingParameter.applicationObject5, + .subIndex = 5, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1601_RPDOMappingParameter.applicationObject6, + .subIndex = 6, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1601_RPDOMappingParameter.applicationObject7, + .subIndex = 7, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1601_RPDOMappingParameter.applicationObject8, + .subIndex = 8, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + } + }, + .o_1602_RPDOMappingParameter = { + { + .dataOrig = &OD_PERSIST_COMM.x1602_RPDOMappingParameter.numberOfMappedApplicationObjectsInPDO, + .subIndex = 0, + .attribute = ODA_SDO_RW, + .dataLength = 1 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1602_RPDOMappingParameter.applicationObject1, + .subIndex = 1, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1602_RPDOMappingParameter.applicationObject2, + .subIndex = 2, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1602_RPDOMappingParameter.applicationObject3, + .subIndex = 3, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1602_RPDOMappingParameter.applicationObject4, + .subIndex = 4, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1602_RPDOMappingParameter.applicationObject5, + .subIndex = 5, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1602_RPDOMappingParameter.applicationObject6, + .subIndex = 6, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1602_RPDOMappingParameter.applicationObject7, + .subIndex = 7, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1602_RPDOMappingParameter.applicationObject8, + .subIndex = 8, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + } + }, + .o_1603_RPDOMappingParameter = { + { + .dataOrig = &OD_PERSIST_COMM.x1603_RPDOMappingParameter.numberOfMappedApplicationObjectsInPDO, + .subIndex = 0, + .attribute = ODA_SDO_RW, + .dataLength = 1 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1603_RPDOMappingParameter.applicationObject1, + .subIndex = 1, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1603_RPDOMappingParameter.applicationObject2, + .subIndex = 2, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1603_RPDOMappingParameter.applicationObject3, + .subIndex = 3, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1603_RPDOMappingParameter.applicationObject4, + .subIndex = 4, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1603_RPDOMappingParameter.applicationObject5, + .subIndex = 5, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1603_RPDOMappingParameter.applicationObject6, + .subIndex = 6, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1603_RPDOMappingParameter.applicationObject7, + .subIndex = 7, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1603_RPDOMappingParameter.applicationObject8, + .subIndex = 8, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + } + }, + .o_1800_TPDOCommunicationParameter = { + { + .dataOrig = &OD_PERSIST_COMM.x1800_TPDOCommunicationParameter.highestSub_indexSupported, + .subIndex = 0, + .attribute = ODA_SDO_R, + .dataLength = 1 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1800_TPDOCommunicationParameter.COB_IDUsedByTPDO, + .subIndex = 1, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1800_TPDOCommunicationParameter.transmissionType, + .subIndex = 2, + .attribute = ODA_SDO_RW, + .dataLength = 1 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1800_TPDOCommunicationParameter.inhibitTime, + .subIndex = 3, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 2 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1800_TPDOCommunicationParameter.eventTimer, + .subIndex = 5, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 2 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1800_TPDOCommunicationParameter.SYNCStartValue, + .subIndex = 6, + .attribute = ODA_SDO_RW, + .dataLength = 1 + } + }, + .o_1801_TPDOCommunicationParameter = { + { + .dataOrig = &OD_PERSIST_COMM.x1801_TPDOCommunicationParameter.highestSub_indexSupported, + .subIndex = 0, + .attribute = ODA_SDO_R, + .dataLength = 1 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1801_TPDOCommunicationParameter.COB_IDUsedByTPDO, + .subIndex = 1, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1801_TPDOCommunicationParameter.transmissionType, + .subIndex = 2, + .attribute = ODA_SDO_RW, + .dataLength = 1 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1801_TPDOCommunicationParameter.inhibitTime, + .subIndex = 3, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 2 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1801_TPDOCommunicationParameter.eventTimer, + .subIndex = 5, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 2 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1801_TPDOCommunicationParameter.SYNCStartValue, + .subIndex = 6, + .attribute = ODA_SDO_RW, + .dataLength = 1 + } + }, + .o_1802_TPDOCommunicationParameter = { + { + .dataOrig = &OD_PERSIST_COMM.x1802_TPDOCommunicationParameter.highestSub_indexSupported, + .subIndex = 0, + .attribute = ODA_SDO_R, + .dataLength = 1 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1802_TPDOCommunicationParameter.COB_IDUsedByTPDO, + .subIndex = 1, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1802_TPDOCommunicationParameter.transmissionType, + .subIndex = 2, + .attribute = ODA_SDO_RW, + .dataLength = 1 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1802_TPDOCommunicationParameter.inhibitTime, + .subIndex = 3, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 2 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1802_TPDOCommunicationParameter.eventTimer, + .subIndex = 5, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 2 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1802_TPDOCommunicationParameter.SYNCStartValue, + .subIndex = 6, + .attribute = ODA_SDO_RW, + .dataLength = 1 + } + }, + .o_1803_TPDOCommunicationParameter = { + { + .dataOrig = &OD_PERSIST_COMM.x1803_TPDOCommunicationParameter.highestSub_indexSupported, + .subIndex = 0, + .attribute = ODA_SDO_R, + .dataLength = 1 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1803_TPDOCommunicationParameter.COB_IDUsedByTPDO, + .subIndex = 1, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1803_TPDOCommunicationParameter.transmissionType, + .subIndex = 2, + .attribute = ODA_SDO_RW, + .dataLength = 1 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1803_TPDOCommunicationParameter.inhibitTime, + .subIndex = 3, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 2 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1803_TPDOCommunicationParameter.eventTimer, + .subIndex = 5, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 2 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1803_TPDOCommunicationParameter.SYNCStartValue, + .subIndex = 6, + .attribute = ODA_SDO_RW, + .dataLength = 1 + } + }, + .o_1A00_TPDOMappingParameter = { + { + .dataOrig = &OD_PERSIST_COMM.x1A00_TPDOMappingParameter.numberOfMappedApplicationObjectsInPDO, + .subIndex = 0, + .attribute = ODA_SDO_RW, + .dataLength = 1 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1A00_TPDOMappingParameter.applicationObject1, + .subIndex = 1, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1A00_TPDOMappingParameter.applicationObject2, + .subIndex = 2, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1A00_TPDOMappingParameter.applicationObject3, + .subIndex = 3, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1A00_TPDOMappingParameter.applicationObject4, + .subIndex = 4, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1A00_TPDOMappingParameter.applicationObject5, + .subIndex = 5, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1A00_TPDOMappingParameter.applicationObject6, + .subIndex = 6, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1A00_TPDOMappingParameter.applicationObject7, + .subIndex = 7, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1A00_TPDOMappingParameter.applicationObject8, + .subIndex = 8, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + } + }, + .o_1A01_TPDOMappingParameter = { + { + .dataOrig = &OD_PERSIST_COMM.x1A01_TPDOMappingParameter.numberOfMappedApplicationObjectsInPDO, + .subIndex = 0, + .attribute = ODA_SDO_RW, + .dataLength = 1 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1A01_TPDOMappingParameter.applicationObject1, + .subIndex = 1, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1A01_TPDOMappingParameter.applicationObject2, + .subIndex = 2, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1A01_TPDOMappingParameter.applicationObject3, + .subIndex = 3, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1A01_TPDOMappingParameter.applicationObject4, + .subIndex = 4, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1A01_TPDOMappingParameter.applicationObject5, + .subIndex = 5, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1A01_TPDOMappingParameter.applicationObject6, + .subIndex = 6, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1A01_TPDOMappingParameter.applicationObject7, + .subIndex = 7, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1A01_TPDOMappingParameter.applicationObject8, + .subIndex = 8, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + } + }, + .o_1A02_TPDOMappingParameter = { + { + .dataOrig = &OD_PERSIST_COMM.x1A02_TPDOMappingParameter.numberOfMappedApplicationObjectsInPDO, + .subIndex = 0, + .attribute = ODA_SDO_RW, + .dataLength = 1 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1A02_TPDOMappingParameter.applicationObject1, + .subIndex = 1, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1A02_TPDOMappingParameter.applicationObject2, + .subIndex = 2, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1A02_TPDOMappingParameter.applicationObject3, + .subIndex = 3, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1A02_TPDOMappingParameter.applicationObject4, + .subIndex = 4, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1A02_TPDOMappingParameter.applicationObject5, + .subIndex = 5, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1A02_TPDOMappingParameter.applicationObject6, + .subIndex = 6, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1A02_TPDOMappingParameter.applicationObject7, + .subIndex = 7, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1A02_TPDOMappingParameter.applicationObject8, + .subIndex = 8, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + } + }, + .o_1A03_TPDOMappingParameter = { + { + .dataOrig = &OD_PERSIST_COMM.x1A03_TPDOMappingParameter.numberOfMappedApplicationObjectsInPDO, + .subIndex = 0, + .attribute = ODA_SDO_RW, + .dataLength = 1 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1A03_TPDOMappingParameter.applicationObject1, + .subIndex = 1, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1A03_TPDOMappingParameter.applicationObject2, + .subIndex = 2, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1A03_TPDOMappingParameter.applicationObject3, + .subIndex = 3, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1A03_TPDOMappingParameter.applicationObject4, + .subIndex = 4, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1A03_TPDOMappingParameter.applicationObject5, + .subIndex = 5, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1A03_TPDOMappingParameter.applicationObject6, + .subIndex = 6, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1A03_TPDOMappingParameter.applicationObject7, + .subIndex = 7, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + }, + { + .dataOrig = &OD_PERSIST_COMM.x1A03_TPDOMappingParameter.applicationObject8, + .subIndex = 8, + .attribute = ODA_SDO_RW | ODA_MB, + .dataLength = 4 + } + } +}; + + +/******************************************************************************* + Object dictionary +*******************************************************************************/ +static OD_ATTR_OD OD_entry_t ODList[] = { + {0x1000, 0x01, ODT_VAR, &ODObjs.o_1000_deviceType, NULL}, + {0x1001, 0x01, ODT_VAR, &ODObjs.o_1001_errorRegister, NULL}, + {0x1003, 0x11, ODT_ARR, &ODObjs.o_1003_pre_definedErrorField, NULL}, + {0x1005, 0x01, ODT_VAR, &ODObjs.o_1005_COB_ID_SYNCMessage, NULL}, + {0x1006, 0x01, ODT_VAR, &ODObjs.o_1006_communicationCyclePeriod, NULL}, + {0x1007, 0x01, ODT_VAR, &ODObjs.o_1007_synchronousWindowLength, NULL}, + {0x1010, 0x05, ODT_ARR, &ODObjs.o_1010_storeParameters, NULL}, + {0x1011, 0x05, ODT_ARR, &ODObjs.o_1011_restoreDefaultParameters, NULL}, + {0x1012, 0x01, ODT_VAR, &ODObjs.o_1012_COB_IDTimeStampObject, NULL}, + {0x1014, 0x01, ODT_VAR, &ODObjs.o_1014_COB_ID_EMCY, NULL}, + {0x1015, 0x01, ODT_VAR, &ODObjs.o_1015_inhibitTimeEMCY, NULL}, + {0x1016, 0x09, ODT_ARR, &ODObjs.o_1016_consumerHeartbeatTime, NULL}, + {0x1017, 0x01, ODT_VAR, &ODObjs.o_1017_producerHeartbeatTime, NULL}, + {0x1018, 0x05, ODT_REC, &ODObjs.o_1018_identity, NULL}, + {0x1019, 0x01, ODT_VAR, &ODObjs.o_1019_synchronousCounterOverflowValue, NULL}, + {0x1200, 0x03, ODT_REC, &ODObjs.o_1200_SDOServerParameter, NULL}, + {0x1280, 0x04, ODT_REC, &ODObjs.o_1280_SDOClientParameter, NULL}, + {0x1400, 0x04, ODT_REC, &ODObjs.o_1400_RPDOCommunicationParameter, NULL}, + {0x1401, 0x04, ODT_REC, &ODObjs.o_1401_RPDOCommunicationParameter, NULL}, + {0x1402, 0x04, ODT_REC, &ODObjs.o_1402_RPDOCommunicationParameter, NULL}, + {0x1403, 0x04, ODT_REC, &ODObjs.o_1403_RPDOCommunicationParameter, NULL}, + {0x1600, 0x09, ODT_REC, &ODObjs.o_1600_RPDOMappingParameter, NULL}, + {0x1601, 0x09, ODT_REC, &ODObjs.o_1601_RPDOMappingParameter, NULL}, + {0x1602, 0x09, ODT_REC, &ODObjs.o_1602_RPDOMappingParameter, NULL}, + {0x1603, 0x09, ODT_REC, &ODObjs.o_1603_RPDOMappingParameter, NULL}, + {0x1800, 0x06, ODT_REC, &ODObjs.o_1800_TPDOCommunicationParameter, NULL}, + {0x1801, 0x06, ODT_REC, &ODObjs.o_1801_TPDOCommunicationParameter, NULL}, + {0x1802, 0x06, ODT_REC, &ODObjs.o_1802_TPDOCommunicationParameter, NULL}, + {0x1803, 0x06, ODT_REC, &ODObjs.o_1803_TPDOCommunicationParameter, NULL}, + {0x1A00, 0x09, ODT_REC, &ODObjs.o_1A00_TPDOMappingParameter, NULL}, + {0x1A01, 0x09, ODT_REC, &ODObjs.o_1A01_TPDOMappingParameter, NULL}, + {0x1A02, 0x09, ODT_REC, &ODObjs.o_1A02_TPDOMappingParameter, NULL}, + {0x1A03, 0x09, ODT_REC, &ODObjs.o_1A03_TPDOMappingParameter, NULL}, + {0x0000, 0x00, 0, NULL, NULL} +}; + +static OD_t _OD = { + (sizeof(ODList) / sizeof(ODList[0])) - 1, + &ODList[0] +}; + +OD_t *OD = &_OD; diff --git a/example/OD.h b/example/OD.h new file mode 100644 index 00000000..aac9fab2 --- /dev/null +++ b/example/OD.h @@ -0,0 +1,387 @@ +/******************************************************************************* + CANopen Object Dictionary definition for CANopenNode V4 + + This file was automatically generated by CANopenEditor v4.1-1-ga49a51a + + https://github.com/CANopenNode/CANopenNode + https://github.com/CANopenNode/CANopenEditor + + DON'T EDIT THIS FILE MANUALLY !!!! +******************************************************************************** + + File info: + File Names: OD.h; OD.c + Project File: DS301_profile.xpd + File Version: 1 + + Created: 23. 11. 2020 12:00:00 + Created By: + Modified: 17. 03. 2023 02:07:28 + Modified By: + + Device Info: + Vendor Name: + Vendor ID: + Product Name: New Product + Product ID: + + Description: +*******************************************************************************/ + +#ifndef OD_H +#define OD_H +/******************************************************************************* + Counters of OD objects +*******************************************************************************/ +#define OD_CNT_NMT 1 +#define OD_CNT_EM 1 +#define OD_CNT_SYNC 1 +#define OD_CNT_SYNC_PROD 1 +#define OD_CNT_STORAGE 1 +#define OD_CNT_TIME 1 +#define OD_CNT_EM_PROD 1 +#define OD_CNT_HB_CONS 1 +#define OD_CNT_HB_PROD 1 +#define OD_CNT_SDO_SRV 1 +#define OD_CNT_SDO_CLI 1 +#define OD_CNT_RPDO 4 +#define OD_CNT_TPDO 4 + + +/******************************************************************************* + Sizes of OD arrays +*******************************************************************************/ +#define OD_CNT_ARR_1003 16 +#define OD_CNT_ARR_1010 4 +#define OD_CNT_ARR_1011 4 +#define OD_CNT_ARR_1016 8 + + +/******************************************************************************* + OD data declaration of all groups +*******************************************************************************/ +typedef struct { + uint32_t x1000_deviceType; + uint32_t x1005_COB_ID_SYNCMessage; + uint32_t x1006_communicationCyclePeriod; + uint32_t x1007_synchronousWindowLength; + uint32_t x1012_COB_IDTimeStampObject; + uint32_t x1014_COB_ID_EMCY; + uint16_t x1015_inhibitTimeEMCY; + uint8_t x1016_consumerHeartbeatTime_sub0; + uint32_t x1016_consumerHeartbeatTime[OD_CNT_ARR_1016]; + uint16_t x1017_producerHeartbeatTime; + struct { + uint8_t highestSub_indexSupported; + uint32_t vendor_ID; + uint32_t productCode; + uint32_t revisionNumber; + uint32_t serialNumber; + } x1018_identity; + uint8_t x1019_synchronousCounterOverflowValue; + struct { + uint8_t highestSub_indexSupported; + uint32_t COB_IDClientToServerTx; + uint32_t COB_IDServerToClientRx; + uint8_t node_IDOfTheSDOServer; + } x1280_SDOClientParameter; + struct { + uint8_t highestSub_indexSupported; + uint32_t COB_IDUsedByRPDO; + uint8_t transmissionType; + uint16_t eventTimer; + } x1400_RPDOCommunicationParameter; + struct { + uint8_t highestSub_indexSupported; + uint32_t COB_IDUsedByRPDO; + uint8_t transmissionType; + uint16_t eventTimer; + } x1401_RPDOCommunicationParameter; + struct { + uint8_t highestSub_indexSupported; + uint32_t COB_IDUsedByRPDO; + uint8_t transmissionType; + uint16_t eventTimer; + } x1402_RPDOCommunicationParameter; + struct { + uint8_t highestSub_indexSupported; + uint32_t COB_IDUsedByRPDO; + uint8_t transmissionType; + uint16_t eventTimer; + } x1403_RPDOCommunicationParameter; + struct { + uint8_t numberOfMappedApplicationObjectsInPDO; + uint32_t applicationObject1; + uint32_t applicationObject2; + uint32_t applicationObject3; + uint32_t applicationObject4; + uint32_t applicationObject5; + uint32_t applicationObject6; + uint32_t applicationObject7; + uint32_t applicationObject8; + } x1600_RPDOMappingParameter; + struct { + uint8_t numberOfMappedApplicationObjectsInPDO; + uint32_t applicationObject1; + uint32_t applicationObject2; + uint32_t applicationObject3; + uint32_t applicationObject4; + uint32_t applicationObject5; + uint32_t applicationObject6; + uint32_t applicationObject7; + uint32_t applicationObject8; + } x1601_RPDOMappingParameter; + struct { + uint8_t numberOfMappedApplicationObjectsInPDO; + uint32_t applicationObject1; + uint32_t applicationObject2; + uint32_t applicationObject3; + uint32_t applicationObject4; + uint32_t applicationObject5; + uint32_t applicationObject6; + uint32_t applicationObject7; + uint32_t applicationObject8; + } x1602_RPDOMappingParameter; + struct { + uint8_t numberOfMappedApplicationObjectsInPDO; + uint32_t applicationObject1; + uint32_t applicationObject2; + uint32_t applicationObject3; + uint32_t applicationObject4; + uint32_t applicationObject5; + uint32_t applicationObject6; + uint32_t applicationObject7; + uint32_t applicationObject8; + } x1603_RPDOMappingParameter; + struct { + uint8_t highestSub_indexSupported; + uint32_t COB_IDUsedByTPDO; + uint8_t transmissionType; + uint16_t inhibitTime; + uint16_t eventTimer; + uint8_t SYNCStartValue; + } x1800_TPDOCommunicationParameter; + struct { + uint8_t highestSub_indexSupported; + uint32_t COB_IDUsedByTPDO; + uint8_t transmissionType; + uint16_t inhibitTime; + uint16_t eventTimer; + uint8_t SYNCStartValue; + } x1801_TPDOCommunicationParameter; + struct { + uint8_t highestSub_indexSupported; + uint32_t COB_IDUsedByTPDO; + uint8_t transmissionType; + uint16_t inhibitTime; + uint16_t eventTimer; + uint8_t SYNCStartValue; + } x1802_TPDOCommunicationParameter; + struct { + uint8_t highestSub_indexSupported; + uint32_t COB_IDUsedByTPDO; + uint8_t transmissionType; + uint16_t inhibitTime; + uint16_t eventTimer; + uint8_t SYNCStartValue; + } x1803_TPDOCommunicationParameter; + struct { + uint8_t numberOfMappedApplicationObjectsInPDO; + uint32_t applicationObject1; + uint32_t applicationObject2; + uint32_t applicationObject3; + uint32_t applicationObject4; + uint32_t applicationObject5; + uint32_t applicationObject6; + uint32_t applicationObject7; + uint32_t applicationObject8; + } x1A00_TPDOMappingParameter; + struct { + uint8_t numberOfMappedApplicationObjectsInPDO; + uint32_t applicationObject1; + uint32_t applicationObject2; + uint32_t applicationObject3; + uint32_t applicationObject4; + uint32_t applicationObject5; + uint32_t applicationObject6; + uint32_t applicationObject7; + uint32_t applicationObject8; + } x1A01_TPDOMappingParameter; + struct { + uint8_t numberOfMappedApplicationObjectsInPDO; + uint32_t applicationObject1; + uint32_t applicationObject2; + uint32_t applicationObject3; + uint32_t applicationObject4; + uint32_t applicationObject5; + uint32_t applicationObject6; + uint32_t applicationObject7; + uint32_t applicationObject8; + } x1A02_TPDOMappingParameter; + struct { + uint8_t numberOfMappedApplicationObjectsInPDO; + uint32_t applicationObject1; + uint32_t applicationObject2; + uint32_t applicationObject3; + uint32_t applicationObject4; + uint32_t applicationObject5; + uint32_t applicationObject6; + uint32_t applicationObject7; + uint32_t applicationObject8; + } x1A03_TPDOMappingParameter; +} OD_PERSIST_COMM_t; + +typedef struct { + uint8_t x1001_errorRegister; + uint8_t x1010_storeParameters_sub0; + uint32_t x1010_storeParameters[OD_CNT_ARR_1010]; + uint8_t x1011_restoreDefaultParameters_sub0; + uint32_t x1011_restoreDefaultParameters[OD_CNT_ARR_1011]; + struct { + uint8_t highestSub_indexSupported; + uint32_t COB_IDClientToServerRx; + uint32_t COB_IDServerToClientTx; + } x1200_SDOServerParameter; +} OD_RAM_t; + +#ifndef OD_ATTR_PERSIST_COMM +#define OD_ATTR_PERSIST_COMM +#endif +extern OD_ATTR_PERSIST_COMM OD_PERSIST_COMM_t OD_PERSIST_COMM; + +#ifndef OD_ATTR_RAM +#define OD_ATTR_RAM +#endif +extern OD_ATTR_RAM OD_RAM_t OD_RAM; + +#ifndef OD_ATTR_OD +#define OD_ATTR_OD +#endif +extern OD_ATTR_OD OD_t *OD; + + +/******************************************************************************* + Object dictionary entries - shortcuts +*******************************************************************************/ +#define OD_ENTRY_H1000 &OD->list[0] +#define OD_ENTRY_H1001 &OD->list[1] +#define OD_ENTRY_H1003 &OD->list[2] +#define OD_ENTRY_H1005 &OD->list[3] +#define OD_ENTRY_H1006 &OD->list[4] +#define OD_ENTRY_H1007 &OD->list[5] +#define OD_ENTRY_H1010 &OD->list[6] +#define OD_ENTRY_H1011 &OD->list[7] +#define OD_ENTRY_H1012 &OD->list[8] +#define OD_ENTRY_H1014 &OD->list[9] +#define OD_ENTRY_H1015 &OD->list[10] +#define OD_ENTRY_H1016 &OD->list[11] +#define OD_ENTRY_H1017 &OD->list[12] +#define OD_ENTRY_H1018 &OD->list[13] +#define OD_ENTRY_H1019 &OD->list[14] +#define OD_ENTRY_H1200 &OD->list[15] +#define OD_ENTRY_H1280 &OD->list[16] +#define OD_ENTRY_H1400 &OD->list[17] +#define OD_ENTRY_H1401 &OD->list[18] +#define OD_ENTRY_H1402 &OD->list[19] +#define OD_ENTRY_H1403 &OD->list[20] +#define OD_ENTRY_H1600 &OD->list[21] +#define OD_ENTRY_H1601 &OD->list[22] +#define OD_ENTRY_H1602 &OD->list[23] +#define OD_ENTRY_H1603 &OD->list[24] +#define OD_ENTRY_H1800 &OD->list[25] +#define OD_ENTRY_H1801 &OD->list[26] +#define OD_ENTRY_H1802 &OD->list[27] +#define OD_ENTRY_H1803 &OD->list[28] +#define OD_ENTRY_H1A00 &OD->list[29] +#define OD_ENTRY_H1A01 &OD->list[30] +#define OD_ENTRY_H1A02 &OD->list[31] +#define OD_ENTRY_H1A03 &OD->list[32] + + +/******************************************************************************* + Object dictionary entries - shortcuts with names +*******************************************************************************/ +#define OD_ENTRY_H1000_deviceType &OD->list[0] +#define OD_ENTRY_H1001_errorRegister &OD->list[1] +#define OD_ENTRY_H1003_pre_definedErrorField &OD->list[2] +#define OD_ENTRY_H1005_COB_ID_SYNCMessage &OD->list[3] +#define OD_ENTRY_H1006_communicationCyclePeriod &OD->list[4] +#define OD_ENTRY_H1007_synchronousWindowLength &OD->list[5] +#define OD_ENTRY_H1010_storeParameters &OD->list[6] +#define OD_ENTRY_H1011_restoreDefaultParameters &OD->list[7] +#define OD_ENTRY_H1012_COB_IDTimeStampObject &OD->list[8] +#define OD_ENTRY_H1014_COB_ID_EMCY &OD->list[9] +#define OD_ENTRY_H1015_inhibitTimeEMCY &OD->list[10] +#define OD_ENTRY_H1016_consumerHeartbeatTime &OD->list[11] +#define OD_ENTRY_H1017_producerHeartbeatTime &OD->list[12] +#define OD_ENTRY_H1018_identity &OD->list[13] +#define OD_ENTRY_H1019_synchronousCounterOverflowValue &OD->list[14] +#define OD_ENTRY_H1200_SDOServerParameter &OD->list[15] +#define OD_ENTRY_H1280_SDOClientParameter &OD->list[16] +#define OD_ENTRY_H1400_RPDOCommunicationParameter &OD->list[17] +#define OD_ENTRY_H1401_RPDOCommunicationParameter &OD->list[18] +#define OD_ENTRY_H1402_RPDOCommunicationParameter &OD->list[19] +#define OD_ENTRY_H1403_RPDOCommunicationParameter &OD->list[20] +#define OD_ENTRY_H1600_RPDOMappingParameter &OD->list[21] +#define OD_ENTRY_H1601_RPDOMappingParameter &OD->list[22] +#define OD_ENTRY_H1602_RPDOMappingParameter &OD->list[23] +#define OD_ENTRY_H1603_RPDOMappingParameter &OD->list[24] +#define OD_ENTRY_H1800_TPDOCommunicationParameter &OD->list[25] +#define OD_ENTRY_H1801_TPDOCommunicationParameter &OD->list[26] +#define OD_ENTRY_H1802_TPDOCommunicationParameter &OD->list[27] +#define OD_ENTRY_H1803_TPDOCommunicationParameter &OD->list[28] +#define OD_ENTRY_H1A00_TPDOMappingParameter &OD->list[29] +#define OD_ENTRY_H1A01_TPDOMappingParameter &OD->list[30] +#define OD_ENTRY_H1A02_TPDOMappingParameter &OD->list[31] +#define OD_ENTRY_H1A03_TPDOMappingParameter &OD->list[32] + + +/******************************************************************************* + OD config structure +*******************************************************************************/ +#ifdef CO_MULTIPLE_OD +#define OD_INIT_CONFIG(config) {\ + (config).CNT_NMT = OD_CNT_NMT;\ + (config).ENTRY_H1017 = OD_ENTRY_H1017;\ + (config).CNT_HB_CONS = OD_CNT_HB_CONS;\ + (config).CNT_ARR_1016 = OD_CNT_ARR_1016;\ + (config).ENTRY_H1016 = OD_ENTRY_H1016;\ + (config).CNT_EM = OD_CNT_EM;\ + (config).ENTRY_H1001 = OD_ENTRY_H1001;\ + (config).ENTRY_H1014 = OD_ENTRY_H1014;\ + (config).ENTRY_H1015 = OD_ENTRY_H1015;\ + (config).CNT_ARR_1003 = OD_CNT_ARR_1003;\ + (config).ENTRY_H1003 = OD_ENTRY_H1003;\ + (config).CNT_SDO_SRV = OD_CNT_SDO_SRV;\ + (config).ENTRY_H1200 = OD_ENTRY_H1200;\ + (config).CNT_SDO_CLI = OD_CNT_SDO_CLI;\ + (config).ENTRY_H1280 = OD_ENTRY_H1280;\ + (config).CNT_TIME = OD_CNT_TIME;\ + (config).ENTRY_H1012 = OD_ENTRY_H1012;\ + (config).CNT_SYNC = OD_CNT_SYNC;\ + (config).ENTRY_H1005 = OD_ENTRY_H1005;\ + (config).ENTRY_H1006 = OD_ENTRY_H1006;\ + (config).ENTRY_H1007 = OD_ENTRY_H1007;\ + (config).ENTRY_H1019 = OD_ENTRY_H1019;\ + (config).CNT_RPDO = OD_CNT_RPDO;\ + (config).ENTRY_H1400 = OD_ENTRY_H1400;\ + (config).ENTRY_H1600 = OD_ENTRY_H1600;\ + (config).CNT_TPDO = OD_CNT_TPDO;\ + (config).ENTRY_H1800 = OD_ENTRY_H1800;\ + (config).ENTRY_H1A00 = OD_ENTRY_H1A00;\ + (config).CNT_LEDS = 0;\ + (config).CNT_GFC = 0;\ + (config).ENTRY_H1300 = NULL;\ + (config).CNT_SRDO = 0;\ + (config).ENTRY_H1301 = NULL;\ + (config).ENTRY_H1381 = NULL;\ + (config).ENTRY_H13FE = NULL;\ + (config).ENTRY_H13FF = NULL;\ + (config).CNT_LSS_SLV = 0;\ + (config).CNT_LSS_MST = 0;\ + (config).CNT_GTWA = 0;\ + (config).CNT_TRACE = 0;\ +} +#endif + +#endif /* OD_H */ diff --git a/example/_project.html b/example/_project.html deleted file mode 100644 index d000a248..00000000 --- a/example/_project.html +++ /dev/null @@ -1,311 +0,0 @@ - - - - Project - CANopenNode - - - -
-
- --- - --- - - at location: - - -
- - - - - - - - - - - - -
-
- CANopenNode project file: - -
-
-
-
- Documentation HTML file: - - -
- -
-
- Source code CO_OD.h file: - -
- -
-
- Source code CO_OD.c file: - -
- -
-
- EDS specification file: - -
- -
-
- Errors and warnings: - -
- -
-CANopenNode Editor - project file. -
- - \ No newline at end of file diff --git a/example/_project.xml b/example/_project.xml deleted file mode 100644 index 160fd9d3..00000000 --- a/example/_project.xml +++ /dev/null @@ -1,960 +0,0 @@ - - - - - - The Sync Object is broadcast periodically by the Sync Producer. - -Permissible value for SYNC is 0 or 1. - - - - - - - - - The Emergency message is triggered by the occurrence of a device internal error situation. - -Permissible value for Emergency is 0 or 1. - - - - - - A Service Data Object (SDO) reads from entries or writes to entries of the Object Dictionary. -SDO server is implemented on all CANopen devices. - -Permissible value for SDO sever is 0 or 1. - - - - - A Service Data Object (SDO) reads from entries or writes to entries of the Object Dictionary. -SDO client is usually a master device in a CANopen network. - -Permissible value for SDO client is 0 or 1. - - - - - Process Data Objects (PDOs) are mapped to a single CAN frame using up to 8 bytes of the data field to transmit application objects. Each PDO has a unique identifier and is transmitted by only one node, but it can be received by more than one (producer/consumer communication). - -Permissible value for RPDO is 0 to 16. For larger value Max Index must be changed. - - - - - - Process Data Objects (PDOs) are mapped to a single CAN frame using up to 8 bytes of the data field to transmit application objects. Each PDO has a unique identifier and is transmitted by only one node, but it can be received by more than one (producer/consumer communication). - -Permissible value for TPDO is 0 to 16. For larger value Max Index must be changed. - - - - - - NMT master can send CANopen NMT command, which can start, stop or reset nodes. - - - - Trace is used for recording variables. - - - - - - - - - bit 0-15: Device profile number -bit 16-31: Additional information - - - - bit 0: generic error -bit 1: current -bit 2: voltage -bit 3: temperature -bit 4: communication error (overrun, error state) -bit 5: device profile specific -bit 6: Reserved (always 0) -bit 7: manufacturer specific - - - - bit 0-31: Not used by stack (available for user) - - - - Number of Errors -bit 0-7: Zero can be written to erase error history - -Standard Error Field -bit 0-15: Error code as transmited in the Emergency object -bit 16-31: Manufacturer specific additional information - - - - - - - - - - - - - bit 0-10: COB-ID for SYNC object -bit 11-29: set to 0 -bit 30: 1(0) - node generates (does NOT generate) SYNC object -bit 31: set to 0 - - - - bit 0-31: period of SYNC transmission in µs (0 = no transmission, no checking) - - - - bit 0-31: window leghth after SYNC when PDOS must be transmited in µs, (0 = not used) - - - - Name of the manufacturer as string - - - - Name of the hardware version as string - - - - Name of the software version as string. - - - - Writing value 0x65766173 ('s','a','v','e' from LSB to MSB) into this location stores all ROM variables into EEPROM. - - - - - - Writing value 0x64616F6C ('l','o','a','d' from LSB to MSB) into this location restores all ROM and EEPROM variables after reset. (After reset read form EEPROM is not performed, so default values are used.) - - - - - - bit 0-10: COB-ID -bit 11-30: set to 0 for 11 bit COB-ID -bit 31: 0(1) - node uses (does NOT use) Emergency object - - - - bit 0-15: Inhibit time of emergency message in 100µs - - - - max sub-index - -Consumer Heartbeat Time -bit 0-15: Heartbeat consumer time in ms (0 = node is not monitored) -bit 16-23: Node ID -bit 24-31: set to 0 - - - - - - - - - bit 0-15: Heartbeat producer time in ms (0 = disable transmission) - - - - max sub-index - -Vendor-ID -bit 0-31: Assigned by CiA - -Product code -bit 0-31: Manufacturer specific - -Revision number -bit 0-15: Minor revision num. (CANopen behavior has not changed) -bit 16-31: Major revision number (CANopen behavior has changed) - -Serial number -bit 0-31: Manufacturer specific - - - - - - - - - If value is zero, then SYNC message is transmitted with data length 0. - -If Value is from 2 to 240, then SYNC message has one data byte, which contains the counter. - -Other values are reserved. - - - - If error is detected and operating NMT state is NMT operational, this object defines behavior of the device. - -Value definition for all subindexes: - 0x00 - if operational, switch to NMT pre-operational - 0x01 - do nothing - 0x02 - switch to NMT stopped - -01 - Communication error - bus off or Heartbeat consumer error. -02 - Communication other error (critical errors - see 'Error status bits') except CAN bus passive but including bus off or Heartbeat consumer. -03 - Communication passive - any communication error including CAN bus passive. -04 - Generic error (critical errors - see 'Error status bits'). -05 - Device profile error - bit 5 in error register is set. -06 - Manufacturer specific error - bit 7 in error register is set. - - - - - - - - - - - 0x1200 SDO server parameter -max sub-index - -COB-ID client to server (Receive SDO) -bit 0-31: 0x00000600 + Node ID - -COB-ID server to client (Transmit SDO) -bit 0-31: 0x00000580 + Node ID - - - -0x1201 - 0x127F SDO server parameter -max sub-index - -COB-ID client to server (Receive SDO) -bit 0-10: COB_ID -bit 11-30: Set to 0 -bit 31*: 0(1) - node uses (does NOT use) SDO - -COB-ID server to client (Transmit SDO) -bit 0-31: same as previous - -Node-ID of the SDO client -bit 0-7: Node ID (optional) - - - - - - - 0x1280 - 0x12FF SDO client parameter -max sub-index - -COB-ID client to server (Transmit SDO) -bit 0-10: COB_ID -bit 11-30: Set to 0 -bit 31: 0(1) - node uses (does NOT use) SDO - -COB-ID server to client (Receive SDO) -bit 0-31: same as previous - -Node-ID of the SDO server -0-7: Node ID - - - - - - - - - - - 0x1400 - 0x15FF RPDO communication parameter -max sub-index - -COB-ID -bit 0-10: COB-ID for PDO, to change it bit 31 must be set -bit 11-29: set to 0 for 11 bit COB-ID -bit 30: 0(1) - rtr are allowed (are NOT allowed) for PDO -bit 31: 0(1) - node uses (does NOT use) PDO - -Transmission type -value = 0-240: reciving is synchronous, process after next reception of SYNC object -value = 241-253: not used -value = 254: manufacturer specific -value = 255: asynchronous - - - - - - - 0x1400 - 0x15FF RPDO communication parameter (see description for 0x1400) - - - - - - - 0x1400 - 0x15FF RPDO communication parameter (see description for 0x1400) - - - - - - - 0x1400 - 0x15FF RPDO communication parameter (see description for 0x1400) - - - - - - - 0x1400 - 0x15FF RPDO communication parameter (see description for 0x1400) - - - - 0x1600 - 0x17FF RPDO mapping parameter (To change mapping, 'Number of mapped objects' must be set to 0) -Number of mapped objects - -mapped object (subindex 1...8) -bit 0-7: data length in bits -bit 8-15: subindex from OD -bit 16-31: index from OD - - - - - - - - - - - - - 0x1600 - 0x17FF RPDO mapping parameter (see description for 0x1600) - - - - - - - - - - - - - 0x1600 - 0x17FF RPDO mapping parameter (see description for 0x1600) - - - - - - - - - - - - - 0x1600 - 0x17FF RPDO mapping parameter (see description for 0x1600) - - - - - - - - - - - - - 0x1600 - 0x17FF RPDO mapping parameter (see description for 0x1600) - - - - - - - - - - - - - - - - - - - - 0x1800 - 0x19FF TPDO communication parameter -max sub-index - -COB-ID -bit 0-10: COB-ID for PDO, to change it bit 31 must be set -bit 11-29: set to 0 for 11 bit COB-ID -bit 30: 0(1) - rtr are allowed (are NOT allowed) for PDO -bit 31: 0(1) - node uses (does NOT use) PDO - -Transmission type -value = 0: transmiting is synchronous, specification in device profile -value = 1-240: transmiting is synchronous after every N-th SYNC object -value = 241-251: not used -value = 252-253: Transmited only on reception of Remote Transmission Request -value = 254: manufacturer specific -value = 255: asinchronous, specification in device profile - -inhibit time -bit 0-15: Minimum time between transmissions of the PDO in 100µs. Zero disables functionality. - -compatibility entry -bit 0-7: Not used. - -event timer -bit 0-15: Time between periodic transmissions of the PDO in ms. Zero disables functionality. - -SYNC start value -value = 0: Counter of the SYNC message shall not be processed. -value = 1-240: The SYNC message with the counter value equal to this value shall be regarded as the first received SYNC message. - - - - - - - - - - - 0x1800 - 0x19FF TPDO communication parameter (see description for 0x1800) - - - - - - - - - - - 0x1800 - 0x19FF TPDO communication parameter (see description for 0x1800) - - - - - - - - - - - 0x1800 - 0x19FF TPDO communication parameter (see description for 0x1800) - - - - - - - - - - - 0x1800 - 0x19FF TPDO communication parameter (see description for 0x1800) - - - - 0x1A00 - 0x1BFF TPDO mapping parameter. (To change mapping, 'Number of mapped objects' must be set to 0). -Number of mapped objects - -mapped object (subindex 1...8) -bit 0-7: data length in bits -bit 8-15: subindex from OD -bit 16-31: index from OD - - - - - - - - - - - - - 0x1A00 - 0x1BFF TPDO mapping parameter (see description for 0x1A00) - - - - - - - - - - - - - 0x1A00 - 0x1BFF TPDO mapping parameter (see description for 0x1A00) - - - - - - - - - - - - - 0x1A00 - 0x1BFF TPDO mapping parameter (see description for 0x1A00) - - - - - - - - - - - - - 0x1A00 - 0x1BFF TPDO mapping parameter (see description for 0x1A00) - - - - - - - - - - - - - Only bit 2 is implemented. - -bit 0: 0(1) - device is not (is) NMT master -bit 1: 0(1) - if bit3=0, start explicitly assigned (all) nodes -bit 2: 0(1) - automaticaly enter (DO NOT automaticaly enter) the operational state on bootup -bit 3: 0(1) - NMT master may (may not) start nodes automatically -bit 4: 0(1) - if monitored node fails heartbeat handle that (all) node(s) -bit 5: 0(1) - flying master process not (yes) supported -bit 6: 0(1) - use bit 4 (ignore bit 4, stop all nodes) -bit 7-31: reserved, set to 0 - - - - Error Status Bits indicates error conditions inside stack or inside application. Specific bit is set by CO_errorReport() function, when error occurs in program. It can be reset by CO_errorReset() function, if error is solved. Emergency message is sent on each change of any Error Status Bit. If critical bits are set, node will not be able to stay in operational state. For more information see file CO_Emergency.h. - -Default error status bits are: - -Communication or protocol errors from driver (informative): -00 - ERROR_NO_ERROR - Error Reset or No Error. -01 - ERROR_CAN_BUS_WARNING - CAN bus warning. -02 - ERROR_RXMSG_WRONG_LENGTH - Wrong data length of received CAN message. -03 - ERROR_RXMSG_OVERFLOW - Previous received CAN message wasn't processed yet. -04 - ERROR_RPDO_WRONG_LENGTH - Wrong data length of received PDO. -05 - ERROR_RPDO_OVERFLOW - Previous received PDO wasn't processed yet. -06 - ERROR_CAN_RX_BUS_PASSIVE - CAN receive bus is passive. -07 - ERROR_CAN_TX_BUS_PASSIVE - CAN transmit bus is passive. - -Communication or protocol errors from driver (critical): -08 - ERROR_08_reserved - (reserved) -09 - ERROR_09_reserved - (reserved) -0A - ERROR_CAN_TX_BUS_OFF - CAN transmit bus is off. -0B - ERROR_CAN_RXB_OVERFLOW - CAN module receive buffer has overflowed. -0C - ERROR_CAN_TX_OVERFLOW - CAN transmit buffer has overflowed. -0D - ERROR_TPDO_OUTSIDE_WINDOW - TPDO is outside SYNC window. -0E - ERROR_CAN_CONFIGURATION_FAILED - Configuration of CAN module CAN failed (Rx or Tx). -0F - ERROR_0F_reserved - (reserved) - -Communication or protocol errors (informative): -10 - ERROR_NMT_WRONG_COMMAND - Wrong NMT command received. -11 - ERROR_SYNC_EARLY - SYNC message was too early. -12 - ERROR_12_reserved - (reserved) -13 - ERROR_13_reserved - (reserved) -14 - ERROR_14_reserved - (reserved) -15 - ERROR_15_reserved - (reserved) -16 - ERROR_16_reserved - (reserved) -17 - ERROR_17_reserved - (reserved) - -Communication or protocol errors (critical): -18 - ERROR_SYNC_TIME_OUT - SYNC message timeout. -19 - ERROR_SYNC_LENGTH - Unexpected SYNC data length -1A - ERROR_PDO_WRONG_MAPPING - Error with PDO mapping. -1B - ERROR_HEARTBEAT_CONSUMER - Heartbeat consumer timeout. -1C - ERROR_HEARTBEAT_CONSUMER_REMOTE_RESET - Heartbeat consumer detected remote node reset. -1D - ERROR_1D_reserved - (reserved) -1E - ERROR_1E_reserved - (reserved) -1F - ERROR_1F_reserved - (reserved) - -Generic errors (informative): -20 - ERROR_20_reserved - (reserved) -21 - ERROR_21_reserved - (reserved) -22 - ERROR_22_reserved - (reserved) -23 - ERROR_23_reserved - (reserved) -24 - ERROR_24_reserved - (reserved) -25 - ERROR_25_reserved - (reserved) -26 - ERROR_26_reserved - (reserved) -27 - ERROR_27_reserved - (reserved) - -Generic errors (critical): -28 - ERROR_WRONG_ERROR_REPORT - Wrong parameters to <CO_errorReport()> function. -29 - ERROR_ISR_TIMER_OVERFLOW - Timer task has overflowed. -2A - ERROR_MEMORY_ALLOCATION_ERROR - Unable to allocate memory for objects. -2B - ERROR_GENERIC_ERROR - Generic error, test usage. -2C - ERROR_MAIN_TIMER_OVERFLOW - Mainline function exceeded maximum execution time. -2D - ERROR_INTERNAL_STATE_APPL - Error in application software internal state. -2E - ERROR_2E_reserved - (reserved) -2F - ERROR_2F_reserved - (reserved) - -Manufacturer specific errors: -Manufacturer may define its own constants up to index 0xFF. Of course, he must then define large enough buffer for error status bits (up to 32 bytes). - - - if(WRITING){ - UNSIGNED8 var = *((UNSIGNED8*)dataBuff); - if(var < 1) return 0x06090032L; //Value of parameter written too low. - if(var > 127) return 0x06090031L; //Value of parameter written too high. -} - - CAN Node ID is CANopenNode specific variable. It sets node-ID for device on CANopen network. Node-ID can be set on other ways too, for example with DIP switches. More advanced solution is use of LSS. - -Valid values are from 1 to 127: - - - if(WRITING){ - UNSIGNED16 var; - memcpySwap2((UNSIGNED8*)&var, (UNSIGNED8*)dataBuff); - if(!(var == 10 || var == 20 || var == 50 || var == 125 || var == 250 || var == 500 || var == 800 || var == 1000)) return 0x06090030L; //Invalid value for the parameter -} - - CAN Bit-Rate is CANopenNode specific variable. It sets CAN Bit-Rate for device on CANopen network. - -Valid values are in [kbps]: -10, 20, 50, 125, 250, 500, 800, 1000 - - - - SYNC Counter is incremented each time, SYNC message is received or transmitted. - - - - SYNC Time is incremented each timer period and reset to zero, each time SYNC is received or transmitted. - - - - Power on Counter counts total microcontroller resets in it's lifetime. Variable is an example of EEPROM usage. - - - if(WRITING){ - UNSIGNED16 var; - memcpySwap2((UNSIGNED8*)&var, (UNSIGNED8*)dataBuff); - if(!((subIndex == 3 || subIndex == 5) && var == 0)) - return 0x06090030; //Invalid value for parameter -} - - Internal performance of the microcontroller. - -cycles per second - cycle frequency of internal timer. -timer cycle time - current timer cycle time in percent of timer period. -timer cycle max time - maximum timer cycle time in percent of timer period. -main cycle time - current mainline function cycle time in percent of timer period. -main cycle max time - maximum mainline function cycle time in percent of timer period. - - - - - - - - - - Current temperature on device in 0,1°C. - - - - - - Current voltage on device in 0,1V. - - - - - - Vartiable is free to use by application. - - - - - - - - - - - - - - - - - - - - - Vartiable is free to use by application. - - - - - - - - - - - - - - - - - - - - - Vartiable is free to use by application. Variable is stored to internal baterry powered SRAM. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Variable displays current time. - -String - returns current time as string. - -Epoch time ms - Milliseconds since unix epoch (1.1.1970, 00:00:00, UTC). It is calculated as base + offset. Base is calculated on startup of program and offset increments continuously since then. 'Epoch time offset ms' overflows each 49,7 days. 'Epoch time base ms' is recalculated in case of overflow or in case of write to 'Epoch time offset ms'. It is rounded to one minute. - - - - - - - - Trace is used for recording variables. - -See also Trace. - -Size - Maximum number of trace records in circular buffer. By reading it returns actual buffer size (If zero, malloc may had been failed at startup). By writing it sets the size, which will be valid after next reboot. (It is necessary to store parameters before (0x1010).) - -Axis no - If value is different than zero, trace is enabled. Value is informative and describes group of traces (Multiple traces can belong to single axis.). By reading it returns zero if trace is disabled or if it is not configured properly. When trace is enabled, internal buffer is cleared. - -Name - name of the trace as a string (informative). - -Color - color of the trace as a string (informative). - -Map - Map to variable in object dictionary, similar as PDO map(two bytes of index, one byte of subindex and 1 byte of bitlength. It's value will be copied to trace.value in case index and subindex are correct. If map index and subindex is zero, RPDO may be mapped to trace.value, for example. Valid values of map.bitlength is 08, 10 or 20 or 00, which indicates 8-bit, 16bit, 32-bit or default-size variable. Trace must be disabled, if mapping is written (Axis no set to 0). - -Format - If first bit is zero, then value is used as signed integer otherwise as unsigned integer. If format is 0 or 1, text points are generated for time and value: "123;321\n140;345\n..." If format is 2 or 3, binary data is generated: 4-byte timestamp and 4-byte value. If format is 4 or 5, SVG path is generated: "M123,321H140V345...". - -Trigger - 0=disabled, 1=rising edge, 2=falling edge, 3=both edges. - -Threshold - If integer value passes threshold (based on trigger setting), time is recorded into 'trigger time'. - - - - - - - - - - - - - - - - - - - - - - - - This object controls, how many trace objects (beginning from index 0x2401) are enabled. If zero, all traces are disabled. - - - - Trace is used for recording variables. - -See also Trace config. - -Size - Number of current records in buffer. - -Value - Latest value. If traceConfig.map (index and subindex) is valid, then mapped variable will be copied here. - -Min, Max - Minimum and maximum value. - -Plot - SVG path generated from the values from circular buffer. For time axis is used Time->'Epoch time offset ms'. When Plot is read, internal buffer is emptied. - -Trigger time - If integer value passes 'TraceConfig->threshold', then time is recorded into this variable. - - - - - - - - - - - - - - - - - - - - Digital inputs from hardware. - - - - - - - - - - - - - Digital outputs on hardware. - - - - - - - - - - - - - Analogue inputs from hardware. The integer value is left adjusted. - - - - - - - - - - - - - - - - - Analogue outputs on hardware. The integer value is left adjusted. - - - - - - - - - - - - - - - CANopenNode - CANopenNode - - - CANopenNode is an open source software stack used for serial communication of multiple devices over the CAN/CANopen Network. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/example/application.c b/example/application.c deleted file mode 100644 index fbfa58c1..00000000 --- a/example/application.c +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Application interface for CANopenNode stack. - * - * @file application.c - * @ingroup application - * @author Janez Paternoster - * @copyright 2012 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include "CANopen.h" - - -/*******************************************************************************/ -void programStart(void){ - -} - - -/*******************************************************************************/ -void communicationReset(void){ - -} - - -/*******************************************************************************/ -void programEnd(void){ - -} - - -/*******************************************************************************/ -void programAsync(uint16_t timer1msDiff){ - -} - - -/*******************************************************************************/ -void program1ms(void){ - -} diff --git a/example/application.h b/example/application.h deleted file mode 100644 index cd42f0be..00000000 --- a/example/application.h +++ /dev/null @@ -1,153 +0,0 @@ -/** - * Application interface for CANopenNode stack. - * - * @file application.h - * @ingroup CO_application - * @author Janez Paternoster - * @copyright 2012 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#ifndef CO_APPLICATION_H -#define CO_APPLICATION_H - - -/** - * @defgroup CO_application Application interface - * @ingroup CO_CANopen - * @{ - * - * Application interface for CANopenNode stack. Function is called - * from file main_xxx.c (if implemented). - * - * ###Main program flow chart - * - * @code - (Program Start) - | - V - +------------------------------------+ - | programStart() | - +------------------------------------+ - | - |<-------------------------+ - | | - V | - (Initialze CANopen) | - | | - V | - +------------------------------------+ | - | communicationReset() | | - +------------------------------------+ | - | | - V | - (Enable CAN and interrupts) | - | | - |<----------------------+ | - | | | - V | | - +------------------------------------+ | | - | programAsync() | | | - +------------------------------------+ | | - | | | - V | | - (Process CANopen asynchronous) | | - | | | - +- infinite loop -------+ | - | | - +- reset communication ----+ - | - V - +------------------------------------+ - | programEnd() | - +------------------------------------+ - | - V - (delete CANopen) - | - V - (Program end) - @endcode - * - * - * ###Timer program flow chart - * - * @code - (Timer interrupt 1 millisecond) - | - V - (CANopen read RPDOs) - | - V - +------------------------------------+ - | program1ms() | - +------------------------------------+ - | - V - (CANopen write TPDOs) - @endcode - * - * - * ###Receive and transmit high priority interrupt flow chart - * - * @code - (CAN receive event or) - (CAN transmit buffer empty event) - | - V - (Process received CAN message or) - (copy next message to CAN transmit buffer) - @endcode - */ - - -/** - * Called after microcontroller reset. - */ -void programStart(void); - - -/** - * Called after communication reset. - */ -void communicationReset(void); - - -/** - * Called before program end. - */ -void programEnd(void); - - -/** - * Called cyclically from main. - * - * @param timer1msDiff Time difference since last call - */ -void programAsync(uint16_t timer1msDiff); - - -/** - * Called cyclically from 1ms timer task. - */ -void program1ms(void); - - -/** @} */ -#endif diff --git a/example/main.c b/example/main.c deleted file mode 100644 index 816ea4e3..00000000 --- a/example/main.c +++ /dev/null @@ -1,159 +0,0 @@ -/* - * CANopen main program file. - * - * This file is a template for other microcontrollers. - * - * @file main_generic.c - * @author Janez Paternoster - * @copyright 2004 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include "CANopen.h" - - -#define TMR_TASK_INTERVAL (1000) /* Interval of tmrTask thread in microseconds */ -#define INCREMENT_1MS(var) (var++) /* Increment 1ms variable in tmrTask */ - -/** - * User-defined CAN base structure, passed as argument to CO_init. - */ -struct CANbase { - uintptr_t baseAddress; /**< Base address of the CAN module */ -}; - -/* Global variables and objects */ - volatile uint16_t CO_timer1ms = 0U; /* variable increments each millisecond */ - - -/* main ***********************************************************************/ -int main (void){ - CO_NMT_reset_cmd_t reset = CO_RESET_NOT; - - /* Configure microcontroller. */ - - - /* initialize EEPROM */ - - - /* increase variable each startup. Variable is stored in EEPROM. */ - OD_powerOnCounter++; - - - while(reset != CO_RESET_APP){ -/* CANopen communication reset - initialize CANopen objects *******************/ - CO_ReturnError_t err; - uint16_t timer1msPrevious; - - /* disable CAN and CAN interrupts */ - struct CANbase canBase = { - .baseAddress = 0u, /* CAN module address */ - }; - - /* initialize CANopen */ - err = CO_init(&canBase, 10/* NodeID */, 125 /* bit rate */); - if(err != CO_ERROR_NO){ - while(1); - /* CO_errorReport(CO->em, CO_EM_MEMORY_ALLOCATION_ERROR, CO_EMC_SOFTWARE_INTERNAL, err); */ - } - - - /* Configure Timer interrupt function for execution every 1 millisecond */ - - - /* Configure CAN transmit and receive interrupt */ - - - /* start CAN */ - CO_CANsetNormalMode(CO->CANmodule[0]); - - reset = CO_RESET_NOT; - timer1msPrevious = CO_timer1ms; - - while(reset == CO_RESET_NOT){ -/* loop for normal program execution ******************************************/ - uint16_t timer1msCopy, timer1msDiff; - - timer1msCopy = CO_timer1ms; - timer1msDiff = timer1msCopy - timer1msPrevious; - timer1msPrevious = timer1msCopy; - - - /* CANopen process */ - reset = CO_process(CO, timer1msDiff, NULL); - - /* Nonblocking application code may go here. */ - - /* Process EEPROM */ - } - } - - -/* program exit ***************************************************************/ - /* stop threads */ - - - /* delete objects from memory */ - CO_delete((void*) 0/* CAN module address */); - - - /* reset */ - return 0; -} - - -/* timer thread executes in constant intervals ********************************/ -static void tmrTask_thread(void){ - - for(;;) { - - /* sleep for interval */ - - INCREMENT_1MS(CO_timer1ms); - - if(CO->CANmodule[0]->CANnormal) { - bool_t syncWas; - - /* Process Sync */ - syncWas = CO_process_SYNC(CO, TMR_TASK_INTERVAL); - - /* Read inputs */ - CO_process_RPDO(CO, syncWas); - - /* Further I/O or nonblocking application code may go here. */ - - /* Write outputs */ - CO_process_TPDO(CO, syncWas, TMR_TASK_INTERVAL); - - /* verify timer overflow */ - if(0) { - CO_errorReport(CO->em, CO_EM_ISR_TIMER_OVERFLOW, CO_EMC_SOFTWARE_INTERNAL, 0U); - } - } - } -} - - -/* CAN interrupt function *****************************************************/ -void /* interrupt */ CO_CAN1InterruptHandler(void){ - CO_CANinterrupt(CO->CANmodule[0]); - - - /* clear interrupt flag */ -} diff --git a/example/main_blank.c b/example/main_blank.c new file mode 100644 index 00000000..90dd2936 --- /dev/null +++ b/example/main_blank.c @@ -0,0 +1,245 @@ +/* + * CANopen main program file. + * + * This file is a template for other microcontrollers. + * + * @file main_generic.c + * @author Janez Paternoster + * @copyright 2021 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#include + +#include "CANopen.h" +#include "OD.h" +#include "CO_storageBlank.h" + +#define log_printf(macropar_message, ...) printf(macropar_message, ##__VA_ARGS__) + +/* default values for CO_CANopenInit() */ +#define NMT_CONTROL \ + CO_NMT_STARTUP_TO_OPERATIONAL \ + | CO_NMT_ERR_ON_ERR_REG | CO_ERR_REG_GENERIC_ERR | CO_ERR_REG_COMMUNICATION +#define FIRST_HB_TIME 500 +#define SDO_SRV_TIMEOUT_TIME 1000 +#define SDO_CLI_TIMEOUT_TIME 500 +#define SDO_CLI_BLOCK false +#define OD_STATUS_BITS NULL + +/* Global variables and objects */ +CO_t* CO = NULL; /* CANopen object */ +uint8_t LED_red, LED_green; + +/* main ***********************************************************************/ +int +main(void) { + CO_ReturnError_t err; + CO_NMT_reset_cmd_t reset = CO_RESET_NOT; + uint32_t heapMemoryUsed; + void* CANptr = NULL; /* CAN module address */ + uint8_t pendingNodeId = 10; /* read from dip switches or nonvolatile memory, configurable by LSS slave */ + uint8_t activeNodeId = 10; /* Copied from CO_pendingNodeId in the communication reset section */ + uint16_t pendingBitRate = 125; /* read from dip switches or nonvolatile memory, configurable by LSS slave */ + +#if (CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE + CO_storage_t storage; + CO_storage_entry_t storageEntries[] = {{.addr = &OD_PERSIST_COMM, + .len = sizeof(OD_PERSIST_COMM), + .subIndexOD = 2, + .attr = CO_storage_cmd | CO_storage_restore, + .addrNV = NULL}}; + uint8_t storageEntriesCount = sizeof(storageEntries) / sizeof(storageEntries[0]); + uint32_t storageInitError = 0; +#endif + + /* Configure microcontroller. */ + + /* Allocate memory */ + CO_config_t* config_ptr = NULL; +#ifdef CO_MULTIPLE_OD + /* example usage of CO_MULTIPLE_OD (but still single OD here) */ + CO_config_t co_config = {0}; + OD_INIT_CONFIG(co_config); /* helper macro from OD.h */ + co_config.CNT_LEDS = 1; + co_config.CNT_LSS_SLV = 1; + config_ptr = &co_config; +#endif /* CO_MULTIPLE_OD */ + CO = CO_new(config_ptr, &heapMemoryUsed); + if (CO == NULL) { + log_printf("Error: Can't allocate memory\n"); + return 0; + } else { + log_printf("Allocated %u bytes for CANopen objects\n", heapMemoryUsed); + } + +#if (CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE + err = CO_storageBlank_init(&storage, CO->CANmodule, OD_ENTRY_H1010_storeParameters, + OD_ENTRY_H1011_restoreDefaultParameters, storageEntries, storageEntriesCount, + &storageInitError); + + if (err != CO_ERROR_NO && err != CO_ERROR_DATA_CORRUPT) { + log_printf("Error: Storage %d\n", storageInitError); + return 0; + } +#endif + + while (reset != CO_RESET_APP) { + /* CANopen communication reset - initialize CANopen objects *******************/ + log_printf("CANopenNode - Reset communication...\n"); + + /* Wait rt_thread. */ + CO->CANmodule->CANnormal = false; + + /* Enter CAN configuration. */ + CO_CANsetConfigurationMode((void*)&CANptr); + CO_CANmodule_disable(CO->CANmodule); + + /* initialize CANopen */ + err = CO_CANinit(CO, CANptr, pendingBitRate); + if (err != CO_ERROR_NO) { + log_printf("Error: CAN initialization failed: %d\n", err); + return 0; + } + + CO_LSS_address_t lssAddress = {.identity = {.vendorID = OD_PERSIST_COMM.x1018_identity.vendor_ID, + .productCode = OD_PERSIST_COMM.x1018_identity.productCode, + .revisionNumber = OD_PERSIST_COMM.x1018_identity.revisionNumber, + .serialNumber = OD_PERSIST_COMM.x1018_identity.serialNumber}}; + err = CO_LSSinit(CO, &lssAddress, &pendingNodeId, &pendingBitRate); + if (err != CO_ERROR_NO) { + log_printf("Error: LSS slave initialization failed: %d\n", err); + return 0; + } + + activeNodeId = pendingNodeId; + uint32_t errInfo = 0; + + err = CO_CANopenInit(CO, /* CANopen object */ + NULL, /* alternate NMT */ + NULL, /* alternate em */ + OD, /* Object dictionary */ + OD_STATUS_BITS, /* Optional OD_statusBits */ + NMT_CONTROL, /* CO_NMT_control_t */ + FIRST_HB_TIME, /* firstHBTime_ms */ + SDO_SRV_TIMEOUT_TIME, /* SDOserverTimeoutTime_ms */ + SDO_CLI_TIMEOUT_TIME, /* SDOclientTimeoutTime_ms */ + SDO_CLI_BLOCK, /* SDOclientBlockTransfer */ + activeNodeId, &errInfo); + if (err != CO_ERROR_NO && err != CO_ERROR_NODE_ID_UNCONFIGURED_LSS) { + if (err == CO_ERROR_OD_PARAMETERS) { + log_printf("Error: Object Dictionary entry 0x%X\n", errInfo); + } else { + log_printf("Error: CANopen initialization failed: %d\n", err); + } + return 0; + } + + err = CO_CANopenInitPDO(CO, CO->em, OD, activeNodeId, &errInfo); + if (err != CO_ERROR_NO && err != CO_ERROR_NODE_ID_UNCONFIGURED_LSS) { + if (err == CO_ERROR_OD_PARAMETERS) { + log_printf("Error: Object Dictionary entry 0x%X\n", errInfo); + } else { + log_printf("Error: PDO initialization failed: %d\n", err); + } + return 0; + } + + /* Configure Timer interrupt function for execution every 1 millisecond */ + + /* Configure CAN transmit and receive interrupt */ + + /* Configure CANopen callbacks, etc */ + if (!CO->nodeIdUnconfigured) { + +#if (CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE + if (storageInitError != 0) { + CO_errorReport(CO->em, CO_EM_NON_VOLATILE_MEMORY, CO_EMC_HARDWARE, storageInitError); + } +#endif + } else { + log_printf("CANopenNode - Node-id not initialized\n"); + } + + /* start CAN */ + CO_CANsetNormalMode(CO->CANmodule); + + reset = CO_RESET_NOT; + + log_printf("CANopenNode - Running...\n"); + fflush(stdout); + + while (reset == CO_RESET_NOT) { + /* loop for normal program execution ******************************************/ + /* get time difference since last function call */ + uint32_t timeDifference_us = 500; + + /* CANopen process */ + reset = CO_process(CO, false, timeDifference_us, NULL); + LED_red = CO_LED_RED(CO->LEDs, CO_LED_CANopen); + LED_green = CO_LED_GREEN(CO->LEDs, CO_LED_CANopen); + + /* Nonblocking application code may go here. */ + + /* Process automatic storage */ + + /* optional sleep for short time */ + } + } + + /* program exit ***************************************************************/ + /* stop threads */ + + /* delete objects from memory */ + CO_CANsetConfigurationMode((void*)&CANptr); + CO_delete(CO); + + log_printf("CANopenNode finished\n"); + + /* reset */ + return 0; +} + +/* timer thread executes in constant intervals ********************************/ +void +tmrTask_thread(void) { + + for (;;) { + CO_LOCK_OD(CO->CANmodule); + if (!CO->nodeIdUnconfigured && CO->CANmodule->CANnormal) { + bool_t syncWas = false; + /* get time difference since last function call */ + uint32_t timeDifference_us = 1000; + +#if (CO_CONFIG_SYNC) & CO_CONFIG_SYNC_ENABLE + syncWas = CO_process_SYNC(CO, timeDifference_us, NULL); +#endif +#if (CO_CONFIG_PDO) & CO_CONFIG_RPDO_ENABLE + CO_process_RPDO(CO, syncWas, timeDifference_us, NULL); +#endif +#if (CO_CONFIG_PDO) & CO_CONFIG_TPDO_ENABLE + CO_process_TPDO(CO, syncWas, timeDifference_us, NULL); +#endif + + /* Further I/O or nonblocking application code may go here. */ + } + CO_UNLOCK_OD(CO->CANmodule); + } +} + +/* CAN interrupt function executes on received CAN message ********************/ +void /* interrupt */ +CO_CAN1InterruptHandler(void) { + + /* clear interrupt flag */ +} diff --git a/stack/CO_trace.c b/extra/CO_trace.c similarity index 93% rename from stack/CO_trace.c rename to extra/CO_trace.c index cf63f75d..e7a6a243 100644 --- a/stack/CO_trace.c +++ b/extra/CO_trace.c @@ -5,30 +5,27 @@ * @author Janez Paternoster * @copyright 2016 - 2020 Janez Paternoster * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . + * This file is part of , a CANopen Stack. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. */ +#include "extra/CO_trace.h" -#include "CO_trace.h" - -#if CO_NO_TRACE > 0 +#if (CO_CONFIG_TRACE) & CO_CONFIG_TRACE_ENABLE #include -#include +#include +#if !((CO_CONFIG_TRACE) & CO_CONFIG_TRACE_OWN_INTTYPES) +#include /* for PRIu32("u" or "lu") and PRId32("d" or "ld") */ +#endif /* Different functions for processing value for different data types. */ static int32_t getValueI8 (void *OD_variable) { return (int32_t) *((int8_t*) OD_variable);} @@ -48,8 +45,10 @@ static uint32_t printPointCsvUnsigned(char *s, uint32_t size, uint32_t timeStamp } static uint32_t printPointBinary(char *s, uint32_t size, uint32_t timeStamp, int32_t value) { if(size < 8) return 0; - CO_memcpySwap4(s, &timeStamp); - CO_memcpySwap4(s+4, &value); + uint32_t timeStampSw = CO_SWAP_32(timeStamp); + int32_t valueSw = CO_SWAP_32(value); + memcpy(s, &timeStampSw, sizeof(timeStampSw)); + memcpy(s+4, &valueSw, sizeof(valueSw)); return 8; } static uint32_t printPointSvgStart(char *s, uint32_t size, uint32_t timeStamp, int32_t value) { @@ -171,7 +170,7 @@ static void findVariable(CO_trace_t *trace) { /* OD function for accessing _OD_traceConfig_ (index 0x2300+) from SDO server. - * For more information see file CO_SDO.h. */ + * For more information see file CO_SDOserver.h. */ static CO_SDO_abortCode_t CO_ODF_traceConfig(CO_ODF_arg_t *ODF_arg) { CO_trace_t *trace; CO_SDO_abortCode_t ret = CO_SDO_AB_NONE; @@ -181,8 +180,7 @@ static CO_SDO_abortCode_t CO_ODF_traceConfig(CO_ODF_arg_t *ODF_arg) { switch(ODF_arg->subIndex) { case 1: /* size */ if(ODF_arg->reading) { - uint32_t *value = (uint32_t*) ODF_arg->data; - *value = trace->bufferSize; + CO_setUint32(ODF_arg->data, trace->bufferSize); } break; @@ -233,6 +231,9 @@ static CO_SDO_abortCode_t CO_ODF_traceConfig(CO_ODF_arg_t *ODF_arg) { } } break; + default: + /* MISRA C 2004 15.3 */ + break; } return ret; @@ -240,7 +241,7 @@ static CO_SDO_abortCode_t CO_ODF_traceConfig(CO_ODF_arg_t *ODF_arg) { /* OD function for accessing _OD_trace_ (index 0x2400+) from SDO server. - * For more information see file CO_SDO.h. */ + * For more information see file CO_SDOserver.h. */ static CO_SDO_abortCode_t CO_ODF_trace(CO_ODF_arg_t *ODF_arg) { CO_trace_t *trace; CO_SDO_abortCode_t ret = CO_SDO_AB_NONE; @@ -250,22 +251,19 @@ static CO_SDO_abortCode_t CO_ODF_trace(CO_ODF_arg_t *ODF_arg) { switch(ODF_arg->subIndex) { case 1: /* size */ if(ODF_arg->reading) { - uint32_t *value = (uint32_t*) ODF_arg->data; uint32_t size = trace->bufferSize; uint32_t wp = trace->writePtr; uint32_t rp = trace->readPtr; if(wp >= rp) { - *value = wp - rp; + CO_setUint32(ODF_arg->data, wp - rp); } else { - *value = size - rp + wp; + CO_setUint32(ODF_arg->data, size - rp + wp); } } else { - uint32_t *value = (uint32_t*) ODF_arg->data; - - if(*value == 0) { + if(CO_getUint32(ODF_arg->data) == 0) { /* clear buffer, handle race conditions */ while(trace->readPtr != 0 || trace->writePtr != 0) { trace->readPtr = 0; @@ -387,13 +385,15 @@ static CO_SDO_abortCode_t CO_ODF_trace(CO_ODF_arg_t *ODF_arg) { } } break; + default: + /* MISRA C 2004 15.3 */ + break; } return ret; } -/******************************************************************************/ void CO_trace_init( CO_trace_t *trace, CO_SDO_t *SDO, @@ -450,7 +450,6 @@ void CO_trace_init( } -/******************************************************************************/ void CO_trace_process(CO_trace_t *trace, uint32_t timestamp) { if(trace->enabled) { @@ -504,4 +503,4 @@ void CO_trace_process(CO_trace_t *trace, uint32_t timestamp) { } } -#endif /* CO_NO_TRACE */ +#endif /* (CO_CONFIG_TRACE) & CO_CONFIG_TRACE_ENABLE */ diff --git a/stack/CO_trace.h b/extra/CO_trace.h similarity index 86% rename from stack/CO_trace.h rename to extra/CO_trace.h index eb7f0d4e..096784f3 100644 --- a/stack/CO_trace.h +++ b/extra/CO_trace.h @@ -1,48 +1,46 @@ /** - * CANopen trace interface. + * CANopen trace object for recording variables over time. * * @file CO_trace.h * @ingroup CO_trace * @author Janez Paternoster * @copyright 2016 - 2020 Janez Paternoster * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . + * This file is part of , a CANopen Stack. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. */ - #ifndef CO_TRACE_H #define CO_TRACE_H -#ifdef __cplusplus -extern "C" { -#endif +#include "301/CO_driver.h" +#include "301/CO_SDOserver.h" -#include "CANopen.h" +/* default configuration, see CO_config.h */ +#ifndef CO_CONFIG_TRACE +#define CO_CONFIG_TRACE (0) +#endif -#if CO_NO_TRACE > 0 +#if ((CO_CONFIG_TRACE) & CO_CONFIG_TRACE_ENABLE) || defined CO_DOXYGEN +#ifdef __cplusplus +extern "C" { +#endif /** * @defgroup CO_trace Trace - * @ingroup CO_CANopen - * @{ - * - * CANopen trace for recording variables over time. + * CANopen trace object for recording variables over time. * + * @ingroup CO_CANopen_extra + * @{ * In embedded systems there is often a need to monitor some variables over time. * Results are then displayed on graph, similar as in oscilloscope. * @@ -121,7 +119,7 @@ typedef struct { * @param valueBuffer Memory block for storing value records. * @param bufferSize Size of the above buffers. * @param map Map to variable in Object Dictionary, which will be monitored. Same structure as in PDO. - * @param Format Format of the plot. If first bit is 1, above variable is unsigned. For more info see Object Dictionary. + * @param format Format of the plot. If first bit is 1, above variable is unsigned. For more info see Object Dictionary. * @param trigger If different than zero, trigger time is recorded, when variable goes through threshold. * @param threshold Used with trigger. * @param value Pointer to variable, which will show last value of the variable. @@ -130,8 +128,6 @@ typedef struct { * @param triggerTime Pointer to variable, which will show last trigger time of the variable. * @param idx_OD_traceConfig Index in Object Dictionary. * @param idx_OD_trace Index in Object Dictionary. - * - * @return 0 on success, -1 on error. */ void CO_trace_init( CO_trace_t *trace, @@ -159,16 +155,15 @@ void CO_trace_init( * * @param trace This object. * @param timestamp Timestamp (usually in millisecond resolution). - * - * @return 0 on success, -1 on error. */ void CO_trace_process(CO_trace_t *trace, uint32_t timestamp); -#endif /* CO_NO_TRACE */ +/** @} */ /* CO_trace */ #ifdef __cplusplus } -#endif /*__cplusplus*/ +#endif /* __cplusplus */ -/** @} */ -#endif +#endif /* (CO_CONFIG_TRACE) & CO_CONFIG_TRACE_ENABLE */ + +#endif /* CO_TRACE_H */ diff --git a/stack/CO_Emergency.c b/stack/CO_Emergency.c deleted file mode 100644 index 396ebf6e..00000000 --- a/stack/CO_Emergency.c +++ /dev/null @@ -1,469 +0,0 @@ -/* - * CANopen Emergency object. - * - * @file CO_Emergency.c - * @ingroup CO_Emergency - * @author Janez Paternoster - * @copyright 2004 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include "CO_driver.h" -#include "CO_SDO.h" -#include "CO_Emergency.h" -#include "CANopen.h" - - -/* - * Read received message from CAN module. - * - * Function will be called (by CAN receive interrupt) every time, when CAN - * message with correct identifier will be received. For more information and - * description of parameters see file CO_driver.h. - */ -static void CO_EM_receive(void *object, const CO_CANrxMsg_t *msg){ - CO_EM_t *em; - uint16_t errorCode; - uint32_t infoCode; - - em = (CO_EM_t*)object; - - if(em!=NULL && em->pFunctSignalRx!=NULL){ - CO_memcpySwap2(&errorCode, &msg->data[0]); - CO_memcpySwap4(&infoCode, &msg->data[4]); - em->pFunctSignalRx(CO_CANrxMsg_readIdent(msg), - errorCode, - msg->data[2], - msg->data[3], - infoCode); - } -} - -/* - * Function for accessing _Pre-Defined Error Field_ (index 0x1003) from SDO server. - * - * For more information see file CO_SDO.h. - */ -static CO_SDO_abortCode_t CO_ODF_1003(CO_ODF_arg_t *ODF_arg); -static CO_SDO_abortCode_t CO_ODF_1003(CO_ODF_arg_t *ODF_arg){ - CO_EMpr_t *emPr; - uint8_t value; - CO_SDO_abortCode_t ret = CO_SDO_AB_NONE; - - emPr = (CO_EMpr_t*) ODF_arg->object; - value = ODF_arg->data[0]; - - if(ODF_arg->reading){ - uint8_t noOfErrors; - noOfErrors = emPr->preDefErrNoOfErrors; - - if(ODF_arg->subIndex == 0U){ - ODF_arg->data[0] = noOfErrors; - } - else if(ODF_arg->subIndex > noOfErrors){ - ret = CO_SDO_AB_NO_DATA; - } - else{ - ret = CO_SDO_AB_NONE; - } - } - else{ - /* only '0' may be written to subIndex 0 */ - if(ODF_arg->subIndex == 0U){ - if(value == 0U){ - emPr->preDefErrNoOfErrors = 0U; - } - else{ - ret = CO_SDO_AB_INVALID_VALUE; - } - } - else{ - ret = CO_SDO_AB_READONLY; - } - } - - return ret; -} - - -/* - * Function for accessing _COB ID EMCY_ (index 0x1014) from SDO server. - * - * For more information see file CO_SDO.h. - */ -static CO_SDO_abortCode_t CO_ODF_1014(CO_ODF_arg_t *ODF_arg); -static CO_SDO_abortCode_t CO_ODF_1014(CO_ODF_arg_t *ODF_arg){ - uint8_t *nodeId; - uint32_t value; - CO_SDO_abortCode_t ret = CO_SDO_AB_NONE; - - nodeId = (uint8_t*) ODF_arg->object; - value = CO_getUint32(ODF_arg->data); - - /* add nodeId to the value */ - if(ODF_arg->reading){ - CO_setUint32(ODF_arg->data, value + *nodeId); - } - - return ret; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_EM_init( - CO_EM_t *em, - CO_EMpr_t *emPr, - CO_SDO_t *SDO, - uint8_t *errorStatusBits, - uint8_t errorStatusBitsSize, - uint8_t *errorRegister, - uint32_t *preDefErr, - uint8_t preDefErrSize, - CO_CANmodule_t *CANdevRx, - uint16_t CANdevRxIdx, - CO_CANmodule_t *CANdevTx, - uint16_t CANdevTxIdx, - uint16_t CANidTxEM) -{ - uint8_t i; - - /* verify arguments */ - if(em==NULL || emPr==NULL || SDO==NULL || errorStatusBits==NULL || errorStatusBitsSize<6U || - errorRegister==NULL || preDefErr==NULL || CANdevTx==NULL || CANdevRx==NULL){ - return CO_ERROR_ILLEGAL_ARGUMENT; - } - - /* Configure object variables */ - em->errorStatusBits = errorStatusBits; - em->errorStatusBitsSize = errorStatusBitsSize; - em->bufEnd = em->buf + (CO_EM_INTERNAL_BUFFER_SIZE * 8); - em->bufWritePtr = em->buf; - em->bufReadPtr = em->buf; - em->bufFull = 0U; - em->wrongErrorReport = 0U; - em->pFunctSignal = NULL; - em->pFunctSignalRx = NULL; - emPr->em = em; - emPr->errorRegister = errorRegister; - emPr->preDefErr = preDefErr; - emPr->preDefErrSize = preDefErrSize; - emPr->preDefErrNoOfErrors = 0U; - emPr->inhibitEmTimer = 0U; - - /* clear error status bits */ - for(i=0U; ierrorStatusBits[i] = 0U; - } - - /* Configure Object dictionary entry at index 0x1003 and 0x1014 */ - CO_OD_configure(SDO, OD_H1003_PREDEF_ERR_FIELD, CO_ODF_1003, (void*)emPr, 0, 0U); - CO_OD_configure(SDO, OD_H1014_COBID_EMERGENCY, CO_ODF_1014, (void*)&SDO->nodeId, 0, 0U); - - /* configure SDO server CAN reception */ - CO_CANrxBufferInit( - CANdevRx, /* CAN device */ - CANdevRxIdx, /* rx buffer index */ - CO_CAN_ID_EMERGENCY, /* CAN identifier */ - 0x780, /* mask */ - 0, /* rtr */ - (void*)em, /* object passed to receive function */ - CO_EM_receive); /* this function will process received message */ - - /* configure emergency message CAN transmission */ - emPr->CANdev = CANdevTx; - emPr->CANdev->em = (void*)em; /* update pointer inside CAN device. */ - emPr->CANtxBuff = CO_CANtxBufferInit( - CANdevTx, /* CAN device */ - CANdevTxIdx, /* index of specific buffer inside CAN module */ - CANidTxEM, /* CAN identifier */ - 0, /* rtr */ - 8U, /* number of data bytes */ - 0); /* synchronous message flag bit */ - - return CO_ERROR_NO; -} - - -/******************************************************************************/ -void CO_EM_initCallback( - CO_EM_t *em, - void (*pFunctSignal)(void)) -{ - if(em != NULL){ - em->pFunctSignal = pFunctSignal; - } -} - - -/******************************************************************************/ -void CO_EM_initCallbackRx( - CO_EM_t *em, - void (*pFunctSignalRx)(const uint16_t ident, - const uint16_t errorCode, - const uint8_t errorRegister, - const uint8_t errorBit, - const uint32_t infoCode)) -{ - if(em != NULL){ - em->pFunctSignalRx = pFunctSignalRx; - } -} - - -/******************************************************************************/ -void CO_EM_process( - CO_EMpr_t *emPr, - bool_t NMTisPreOrOperational, - uint16_t timeDifference_100us, - uint16_t emInhTime, - uint16_t *timerNext_ms) -{ - - CO_EM_t *em = emPr->em; - uint8_t errorRegister; - uint8_t errorMask; - uint8_t i; - - /* verify errors from driver and other */ - CO_CANverifyErrors(emPr->CANdev); - if(em->wrongErrorReport != 0U){ - CO_errorReport(em, CO_EM_WRONG_ERROR_REPORT, CO_EMC_SOFTWARE_INTERNAL, (uint32_t)em->wrongErrorReport); - em->wrongErrorReport = 0U; - } - - - /* calculate Error register */ - errorRegister = 0U; - errorMask = (uint8_t)~(CO_ERR_REG_GENERIC_ERR | CO_ERR_REG_COMM_ERR | CO_ERR_REG_MANUFACTURER); - /* generic error */ - if(em->errorStatusBits[5]){ - errorRegister |= CO_ERR_REG_GENERIC_ERR; - } - /* communication error (overrun, error state) */ - if(em->errorStatusBits[2] || em->errorStatusBits[3]){ - errorRegister |= CO_ERR_REG_COMM_ERR; - } - /* Manufacturer */ - for(i=6; ierrorStatusBitsSize; i++) { - if (em->errorStatusBits[i]) { - errorRegister |= CO_ERR_REG_MANUFACTURER; - } - } - *emPr->errorRegister = (*emPr->errorRegister & errorMask) | errorRegister; - - /* inhibit time */ - if(emPr->inhibitEmTimer < emInhTime){ - emPr->inhibitEmTimer += timeDifference_100us; - } - - /* send Emergency message. */ - if( NMTisPreOrOperational && - !emPr->CANtxBuff->bufferFull && - (em->bufReadPtr != em->bufWritePtr || em->bufFull)) - { - uint32_t preDEF; /* preDefinedErrorField */ - uint16_t diff; - - if (emPr->inhibitEmTimer >= emInhTime) { - /* inhibit time elapsed, send message */ - - /* add error register */ - em->bufReadPtr[2] = *emPr->errorRegister; - - /* copy data to CAN emergency message */ - CO_memcpy(emPr->CANtxBuff->data, em->bufReadPtr, 8U); - CO_memcpy((uint8_t*)&preDEF, em->bufReadPtr, 4U); - em->bufReadPtr += 8; - - /* Update read buffer pointer and reset inhibit timer */ - if(em->bufReadPtr == em->bufEnd){ - em->bufReadPtr = em->buf; - } - emPr->inhibitEmTimer = 0U; - - /* verify message buffer overflow, then clear full flag */ - if(em->bufFull == 2U){ - em->bufFull = 0U; /* will be updated below */ - CO_errorReport(em, CO_EM_EMERGENCY_BUFFER_FULL, CO_EMC_GENERIC, 0U); - } - else{ - em->bufFull = 0; - CO_errorReset(em, CO_EM_EMERGENCY_BUFFER_FULL, 0); - } - - /* write to 'pre-defined error field' (object dictionary, index 0x1003) */ - if(emPr->preDefErr){ - uint8_t j; - - if(emPr->preDefErrNoOfErrors < emPr->preDefErrSize) - emPr->preDefErrNoOfErrors++; - for(j=emPr->preDefErrNoOfErrors-1; j>0; j--) - emPr->preDefErr[j] = emPr->preDefErr[j-1]; - emPr->preDefErr[0] = preDEF; - } - - /* send CAN message */ - CO_CANsend(emPr->CANdev, emPr->CANtxBuff); - } - - /* check again after inhibit time elapsed */ - diff = (emInhTime + 9) / 10; /* time difference in ms, always round up */ - if (timerNext_ms != NULL && *timerNext_ms > diff) { - *timerNext_ms = diff; - } - } - - return; -} - - -/******************************************************************************/ -void CO_errorReport(CO_EM_t *em, const uint8_t errorBit, const uint16_t errorCode, const uint32_t infoCode){ - uint8_t index = errorBit >> 3; - uint8_t bitmask = 1 << (errorBit & 0x7); - uint8_t *errorStatusBits = 0; - bool_t sendEmergency = true; - - if(em == NULL){ - sendEmergency = false; - } - else if(index >= em->errorStatusBitsSize){ - /* if errorBit value not supported, send emergency 'CO_EM_WRONG_ERROR_REPORT' */ - em->wrongErrorReport = errorBit; - sendEmergency = false; - } - else{ - errorStatusBits = &em->errorStatusBits[index]; - /* if error was already reported, do nothing */ - if((*errorStatusBits & bitmask) != 0){ - sendEmergency = false; - } - } - - if(sendEmergency){ - /* set error bit */ - if(errorBit){ - /* any error except NO_ERROR */ - *errorStatusBits |= bitmask; - } - - /* verify buffer full, set overflow */ - if(em->bufFull){ - em->bufFull = 2; - } - else{ - uint8_t bufCopy[8]; - - /* prepare data for emergency message */ - CO_memcpySwap2(&bufCopy[0], &errorCode); - bufCopy[2] = 0; /* error register will be set later */ - bufCopy[3] = errorBit; - CO_memcpySwap4(&bufCopy[4], &infoCode); - - /* copy data to the buffer, increment writePtr and verify buffer full */ - CO_LOCK_EMCY(); - CO_memcpy(em->bufWritePtr, &bufCopy[0], 8); - em->bufWritePtr += 8; - - if(em->bufWritePtr == em->bufEnd) em->bufWritePtr = em->buf; - if(em->bufWritePtr == em->bufReadPtr) em->bufFull = 1; - CO_UNLOCK_EMCY(); - - /* Optional signal to RTOS, which can resume task, which handles CO_EM_process */ - if(em->pFunctSignal != NULL) { - em->pFunctSignal(); - } - } - } -} - - -/******************************************************************************/ -void CO_errorReset(CO_EM_t *em, const uint8_t errorBit, const uint32_t infoCode){ - uint8_t index = errorBit >> 3; - uint8_t bitmask = 1 << (errorBit & 0x7); - uint8_t *errorStatusBits = 0; - bool_t sendEmergency = true; - - if(em == NULL){ - sendEmergency = false; - } - else if(index >= em->errorStatusBitsSize){ - /* if errorBit value not supported, send emergency 'CO_EM_WRONG_ERROR_REPORT' */ - em->wrongErrorReport = errorBit; - sendEmergency = false; - } - else{ - errorStatusBits = &em->errorStatusBits[index]; - /* if error was allready cleared, do nothing */ - if((*errorStatusBits & bitmask) == 0){ - sendEmergency = false; - } - } - - if(sendEmergency){ - /* erase error bit */ - *errorStatusBits &= ~bitmask; - - /* verify buffer full */ - if(em->bufFull){ - em->bufFull = 2; - } - else{ - uint8_t bufCopy[8]; - - /* prepare data for emergency message */ - bufCopy[0] = 0; - bufCopy[1] = 0; - bufCopy[2] = 0; /* error register will be set later */ - bufCopy[3] = errorBit; - CO_memcpySwap4(&bufCopy[4], &infoCode); - - /* copy data to the buffer, increment writePtr and verify buffer full */ - CO_LOCK_EMCY(); - CO_memcpy(em->bufWritePtr, &bufCopy[0], 8); - em->bufWritePtr += 8; - - if(em->bufWritePtr == em->bufEnd) em->bufWritePtr = em->buf; - if(em->bufWritePtr == em->bufReadPtr) em->bufFull = 1; - CO_UNLOCK_EMCY(); - - /* Optional signal to RTOS, which can resume task, which handles CO_EM_process */ - if(em->pFunctSignal != NULL) { - em->pFunctSignal(); - } - } - } -} - - -/******************************************************************************/ -bool_t CO_isError(CO_EM_t *em, const uint8_t errorBit){ - uint8_t index = errorBit >> 3; - uint8_t bitmask = 1 << (errorBit & 0x7); - bool_t ret = false; - - if(em != NULL && index < em->errorStatusBitsSize){ - if((em->errorStatusBits[index] & bitmask) != 0){ - ret = true; - } - } - - return ret; -} diff --git a/stack/CO_Emergency.h b/stack/CO_Emergency.h deleted file mode 100644 index 84abac8f..00000000 --- a/stack/CO_Emergency.h +++ /dev/null @@ -1,454 +0,0 @@ -/** - * CANopen Emergency protocol. - * - * @file CO_Emergency.h - * @ingroup CO_Emergency - * @author Janez Paternoster - * @copyright 2004 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#ifndef CO_EMERGENCY_H -#define CO_EMERGENCY_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @defgroup CO_Emergency Emergency - * @ingroup CO_CANopen - * @{ - * - * CANopen Emergency protocol. - * - * Error control and Emergency is used for control internal error state - * and for sending a CANopen Emergency message. - * - * In case of error condition stack or application calls CO_errorReport() - * function with indication of the error. Specific error condition is reported - * (with CANopen Emergency message) only the first time after it occurs. - * Internal state of the error condition is controlled with - * @ref CO_EM_errorStatusBits. Specific error condition can also be reset by - * CO_errorReset() function. If so, Emergency message is sent with - * CO_EM_NO_ERROR indication. - * - * Some error conditions are informative and some are critical. Critical error - * conditions sets the #CO_errorRegisterBitmask_t. - * - * Latest errors can be read from _Pre Defined Error Field_ (object dictionary, - * index 0x1003). @ref CO_EM_errorStatusBits can also be read form CANopen - * object dictionary. - * - * ###Emergency message contents: - * - * Byte | Description - * -----|----------------------------------------------------------- - * 0..1 | @ref CO_EM_errorCodes. - * 2 | #CO_errorRegisterBitmask_t. - * 3 | Index of error condition (see @ref CO_EM_errorStatusBits). - * 4..7 | Additional argument informative to CO_errorReport() function. - * - * ####Contents of _Pre Defined Error Field_ (object dictionary, index 0x1003): - * bytes 0..3 are equal to bytes 0..3 in the Emergency message. - * - * @see #CO_Default_CAN_ID_t - */ - - -/** - * CANopen Error register. - * - * In object dictionary on index 0x1001. - * - * Error register is calculated from critical internal @ref CO_EM_errorStatusBits. - * Generic and communication bits are calculated in CO_EM_process - * function, device profile or manufacturer specific bits may be calculated - * inside the application. - * - * Internal errors may prevent device to stay in NMT Operational state. Details - * are described in _Error Behavior_ object in Object Dictionary at index 0x1029. - */ -typedef enum{ - CO_ERR_REG_GENERIC_ERR = 0x01U, /**< bit 0, generic error */ - CO_ERR_REG_CURRENT = 0x02U, /**< bit 1, current */ - CO_ERR_REG_VOLTAGE = 0x04U, /**< bit 2, voltage */ - CO_ERR_REG_TEMPERATURE = 0x08U, /**< bit 3, temperature */ - CO_ERR_REG_COMM_ERR = 0x10U, /**< bit 4, communication error (overrun, error state) */ - CO_ERR_REG_DEV_PROFILE = 0x20U, /**< bit 5, device profile specific */ - CO_ERR_REG_RESERVED = 0x40U, /**< bit 6, reserved (always 0) */ - CO_ERR_REG_MANUFACTURER = 0x80U /**< bit 7, manufacturer specific */ -}CO_errorRegisterBitmask_t; - - -/** - * @defgroup CO_EM_errorCodes CANopen Error codes - * @{ - * - * Standard error codes according to CiA DS-301 and DS-401. - */ -#define CO_EMC_NO_ERROR 0x0000U /**< 0x00xx, error Reset or No Error */ -#define CO_EMC_GENERIC 0x1000U /**< 0x10xx, Generic Error */ -#define CO_EMC_CURRENT 0x2000U /**< 0x20xx, Current */ -#define CO_EMC_CURRENT_INPUT 0x2100U /**< 0x21xx, Current, device input side */ -#define CO_EMC_CURRENT_INSIDE 0x2200U /**< 0x22xx, Current inside the device */ -#define CO_EMC_CURRENT_OUTPUT 0x2300U /**< 0x23xx, Current, device output side */ -#define CO_EMC_VOLTAGE 0x3000U /**< 0x30xx, Voltage */ -#define CO_EMC_VOLTAGE_MAINS 0x3100U /**< 0x31xx, Mains Voltage */ -#define CO_EMC_VOLTAGE_INSIDE 0x3200U /**< 0x32xx, Voltage inside the device */ -#define CO_EMC_VOLTAGE_OUTPUT 0x3300U /**< 0x33xx, Output Voltage */ -#define CO_EMC_TEMPERATURE 0x4000U /**< 0x40xx, Temperature */ -#define CO_EMC_TEMP_AMBIENT 0x4100U /**< 0x41xx, Ambient Temperature */ -#define CO_EMC_TEMP_DEVICE 0x4200U /**< 0x42xx, Device Temperature */ -#define CO_EMC_HARDWARE 0x5000U /**< 0x50xx, Device Hardware */ -#define CO_EMC_SOFTWARE_DEVICE 0x6000U /**< 0x60xx, Device Software */ -#define CO_EMC_SOFTWARE_INTERNAL 0x6100U /**< 0x61xx, Internal Software */ -#define CO_EMC_SOFTWARE_USER 0x6200U /**< 0x62xx, User Software */ -#define CO_EMC_DATA_SET 0x6300U /**< 0x63xx, Data Set */ -#define CO_EMC_ADDITIONAL_MODUL 0x7000U /**< 0x70xx, Additional Modules */ -#define CO_EMC_MONITORING 0x8000U /**< 0x80xx, Monitoring */ -#define CO_EMC_COMMUNICATION 0x8100U /**< 0x81xx, Communication */ -#define CO_EMC_CAN_OVERRUN 0x8110U /**< 0x8110, CAN Overrun (Objects lost) */ -#define CO_EMC_CAN_PASSIVE 0x8120U /**< 0x8120, CAN in Error Passive Mode */ -#define CO_EMC_HEARTBEAT 0x8130U /**< 0x8130, Life Guard Error or Heartbeat Error */ -#define CO_EMC_BUS_OFF_RECOVERED 0x8140U /**< 0x8140, recovered from bus off */ -#define CO_EMC_CAN_ID_COLLISION 0x8150U /**< 0x8150, CAN-ID collision */ -#define CO_EMC_PROTOCOL_ERROR 0x8200U /**< 0x82xx, Protocol Error */ -#define CO_EMC_PDO_LENGTH 0x8210U /**< 0x8210, PDO not processed due to length error */ -#define CO_EMC_PDO_LENGTH_EXC 0x8220U /**< 0x8220, PDO length exceeded */ -#define CO_EMC_DAM_MPDO 0x8230U /**< 0x8230, DAM MPDO not processed, destination object not available */ -#define CO_EMC_SYNC_DATA_LENGTH 0x8240U /**< 0x8240, Unexpected SYNC data length */ -#define CO_EMC_RPDO_TIMEOUT 0x8250U /**< 0x8250, RPDO timeout */ -#define CO_EMC_TIME_DATA_LENGTH 0x8260U /**< 0x8260, Unexpected TIME data length */ -#define CO_EMC_EXTERNAL_ERROR 0x9000U /**< 0x90xx, External Error */ -#define CO_EMC_ADDITIONAL_FUNC 0xF000U /**< 0xF0xx, Additional Functions */ -#define CO_EMC_DEVICE_SPECIFIC 0xFF00U /**< 0xFFxx, Device specific */ - -#define CO_EMC401_OUT_CUR_HI 0x2310U /**< 0x2310, DS401, Current at outputs too high (overload) */ -#define CO_EMC401_OUT_SHORTED 0x2320U /**< 0x2320, DS401, Short circuit at outputs */ -#define CO_EMC401_OUT_LOAD_DUMP 0x2330U /**< 0x2330, DS401, Load dump at outputs */ -#define CO_EMC401_IN_VOLT_HI 0x3110U /**< 0x3110, DS401, Input voltage too high */ -#define CO_EMC401_IN_VOLT_LOW 0x3120U /**< 0x3120, DS401, Input voltage too low */ -#define CO_EMC401_INTERN_VOLT_HI 0x3210U /**< 0x3210, DS401, Internal voltage too high */ -#define CO_EMC401_INTERN_VOLT_LO 0x3220U /**< 0x3220, DS401, Internal voltage too low */ -#define CO_EMC401_OUT_VOLT_HIGH 0x3310U /**< 0x3310, DS401, Output voltage too high */ -#define CO_EMC401_OUT_VOLT_LOW 0x3320U /**< 0x3320, DS401, Output voltage too low */ -/** @} */ - - -/** - * @defgroup CO_EM_errorStatusBits Error status bits - * @{ - * - * Internal indication of the error condition. - * - * Each error condition is specified by unique index from 0x00 up to 0xFF. - * Variable (from manufacturer section in the Object - * Dictionary) contains up to 0xFF bits (32bytes) for the identification of the - * specific error condition. (Type of the variable is CANopen OCTET_STRING.) - * - * If specific error occurs in the stack or in the application, CO_errorReport() - * sets specific bit in the _Error Status Bits_ variable. If bit was already - * set, function returns without any action. Otherwise it prepares emergency - * message. - * - * CO_errorReport(), CO_errorReset() or CO_isError() functions are called - * with unique index as an argument. (However CO_errorReport(), for example, may - * be used with the same index on multiple places in the code.) - * - * Macros defined below are combination of two constants: index and - * @ref CO_EM_errorCodes. They represents specific error conditions. They are - * used as double argument for CO_errorReport(), CO_errorReset() and - * CO_isError() functions. - * - * Stack uses first 6 bytes of the _Error Status Bits_ variable. Device profile - * or application may define own macros for Error status bits using - * @ref CO_EM_MANUFACTURER_START and @ref CO_EM_MANUFACTURER_END values. Note that - * _Error Status Bits_ must be large enough (up to 32 bytes). - */ -#define CO_EM_NO_ERROR 0x00U /**< 0x00, Error Reset or No Error */ -#define CO_EM_CAN_BUS_WARNING 0x01U /**< 0x01, communication, info, CAN bus warning limit reached */ -#define CO_EM_RXMSG_WRONG_LENGTH 0x02U /**< 0x02, communication, info, Wrong data length of the received CAN message */ -#define CO_EM_RXMSG_OVERFLOW 0x03U /**< 0x03, communication, info, Previous received CAN message wasn't processed yet */ -#define CO_EM_RPDO_WRONG_LENGTH 0x04U /**< 0x04, communication, info, Wrong data length of received PDO */ -#define CO_EM_RPDO_OVERFLOW 0x05U /**< 0x05, communication, info, Previous received PDO wasn't processed yet */ -#define CO_EM_CAN_RX_BUS_PASSIVE 0x06U /**< 0x06, communication, info, CAN receive bus is passive */ -#define CO_EM_CAN_TX_BUS_PASSIVE 0x07U /**< 0x07, communication, info, CAN transmit bus is passive */ -#define CO_EM_NMT_WRONG_COMMAND 0x08U /**< 0x08, communication, info, Wrong NMT command received */ -#define CO_EM_TIME_TIMEOUT 0x09U /**< 0x09, communication, info, TIME message timeout */ -#define CO_EM_TIME_LENGTH 0x0AU /**< 0x0A, communication, info, Unexpected TIME data length */ -#define CO_EM_0B_unused 0x0BU /**< 0x0B, (unused) */ -#define CO_EM_0C_unused 0x0CU /**< 0x0C, (unused) */ -#define CO_EM_0D_unused 0x0DU /**< 0x0D, (unused) */ -#define CO_EM_0E_unused 0x0EU /**< 0x0E, (unused) */ -#define CO_EM_0F_unused 0x0FU /**< 0x0F, (unused) */ - -#define CO_EM_10_unused 0x10U /**< 0x10, (unused) */ -#define CO_EM_11_unused 0x11U /**< 0x11, (unused) */ -#define CO_EM_CAN_TX_BUS_OFF 0x12U /**< 0x12, communication, critical, CAN transmit bus is off */ -#define CO_EM_CAN_RXB_OVERFLOW 0x13U /**< 0x13, communication, critical, CAN module receive buffer has overflowed */ -#define CO_EM_CAN_TX_OVERFLOW 0x14U /**< 0x14, communication, critical, CAN transmit buffer has overflowed */ -#define CO_EM_TPDO_OUTSIDE_WINDOW 0x15U /**< 0x15, communication, critical, TPDO is outside SYNC window */ -#define CO_EM_16_unused 0x16U /**< 0x16, (unused) */ -#define CO_EM_17_unused 0x17U /**< 0x17, (unused) */ -#define CO_EM_SYNC_TIME_OUT 0x18U /**< 0x18, communication, critical, SYNC message timeout */ -#define CO_EM_SYNC_LENGTH 0x19U /**< 0x19, communication, critical, Unexpected SYNC data length */ -#define CO_EM_PDO_WRONG_MAPPING 0x1AU /**< 0x1A, communication, critical, Error with PDO mapping */ -#define CO_EM_HEARTBEAT_CONSUMER 0x1BU /**< 0x1B, communication, critical, Heartbeat consumer timeout */ -#define CO_EM_HB_CONSUMER_REMOTE_RESET 0x1CU /**< 0x1C, communication, critical, Heartbeat consumer detected remote node reset */ -#define CO_EM_1D_unused 0x1DU /**< 0x1D, (unused) */ -#define CO_EM_1E_unused 0x1EU /**< 0x1E, (unused) */ -#define CO_EM_1F_unused 0x1FU /**< 0x1F, (unused) */ - -#define CO_EM_EMERGENCY_BUFFER_FULL 0x20U /**< 0x20, generic, info, Emergency buffer is full, Emergency message wasn't sent */ -#define CO_EM_21_unused 0x21U /**< 0x21, (unused) */ -#define CO_EM_MICROCONTROLLER_RESET 0x22U /**< 0x22, generic, info, Microcontroller has just started */ -#define CO_EM_23_unused 0x23U /**< 0x23, (unused) */ -#define CO_EM_24_unused 0x24U /**< 0x24, (unused) */ -#define CO_EM_25_unused 0x25U /**< 0x25, (unused) */ -#define CO_EM_26_unused 0x26U /**< 0x26, (unused) */ -#define CO_EM_27_unused 0x27U /**< 0x27, (unused) */ - -#define CO_EM_WRONG_ERROR_REPORT 0x28U /**< 0x28, generic, critical, Wrong parameters to CO_errorReport() function */ -#define CO_EM_ISR_TIMER_OVERFLOW 0x29U /**< 0x29, generic, critical, Timer task has overflowed */ -#define CO_EM_MEMORY_ALLOCATION_ERROR 0x2AU /**< 0x2A, generic, critical, Unable to allocate memory for objects */ -#define CO_EM_GENERIC_ERROR 0x2BU /**< 0x2B, generic, critical, Generic error, test usage */ -#define CO_EM_GENERIC_SOFTWARE_ERROR 0x2CU /**< 0x2C, generic, critical, Software error */ -#define CO_EM_INCONSISTENT_OBJECT_DICT 0x2DU /**< 0x2D, generic, critical, Object dictionary does not match the software */ -#define CO_EM_CALCULATION_OF_PARAMETERS 0x2EU /**< 0x2E, generic, critical, Error in calculation of device parameters */ -#define CO_EM_NON_VOLATILE_MEMORY 0x2FU /**< 0x2F, generic, critical, Error with access to non volatile device memory */ - -#define CO_EM_MANUFACTURER_START 0x30U /**< 0x30, manufacturer, info, This can be used by macros to calculate error codes */ -#define CO_EM_MANUFACTURER_END 0xFFU /**< 0xFF, manufacturer, info, This can be used by macros to check error codes */ -/** @} */ - - -/** - * Size of internal buffer, whwre emergencies are stored after CO_errorReport(). - * Buffer is cleared by CO_EM_process(). - */ -#define CO_EM_INTERNAL_BUFFER_SIZE 10 - - -/** - * Emergerncy object for CO_errorReport(). It contains error buffer, to which new emergency - * messages are written, when CO_errorReport() is called. This object is included in - * CO_EMpr_t object. - */ -typedef struct{ - uint8_t *errorStatusBits; /**< From CO_EM_init() */ - uint8_t errorStatusBitsSize; /**< From CO_EM_init() */ - - /** Internal buffer for storing unsent emergency messages.*/ - uint8_t buf[CO_EM_INTERNAL_BUFFER_SIZE * 8]; - uint8_t *bufEnd; /**< End+1 address of the above buffer */ - uint8_t *bufWritePtr; /**< Write pointer in the above buffer */ - uint8_t *bufReadPtr; /**< Read pointer in the above buffer */ - uint8_t bufFull; /**< True if above buffer is full */ - uint8_t wrongErrorReport; /**< Error in arguments to CO_errorReport() */ - - /** From CO_EM_initCallback() or NULL */ - void (*pFunctSignal)(void); - /** From CO_EM_initCallbackRx() or NULL */ - void (*pFunctSignalRx)(const uint16_t ident, - const uint16_t errorCode, - const uint8_t errorRegister, - const uint8_t errorBit, - const uint32_t infoCode); -}CO_EM_t; - - -/** - * Report error condition. - * - * Function is called on any error condition inside CANopen stack and may also - * be called by application on custom error condition. Emergency message is sent - * after the first occurance of specific error. In case of critical error, device - * will not be able to stay in NMT_OPERATIONAL state. - * - * Function is short and may be used form any task or interrupt. - * - * @param em Emergency object. - * @param errorBit from @ref CO_EM_errorStatusBits. - * @param errorCode from @ref CO_EM_errorCodes. - * @param infoCode 32 bit value is passed to bytes 4...7 of the Emergency message. - * It contains optional additional information inside emergency message. - */ -void CO_errorReport(CO_EM_t *em, const uint8_t errorBit, const uint16_t errorCode, const uint32_t infoCode); - - -/** - * Reset error condition. - * - * Function is called if any error condition is solved. Emergency message is sent - * with @ref CO_EM_errorCodes 0x0000. - * - * Function is short and may be used form any task or interrupt. - * - * @param em Emergency object. - * @param errorBit from @ref CO_EM_errorStatusBits. - * @param infoCode 32 bit value is passed to bytes 4...7 of the Emergency message. - */ -void CO_errorReset(CO_EM_t *em, const uint8_t errorBit, const uint32_t infoCode); - - -/** - * Check specific error condition. - * - * Function returns 1, if specific internal error is present. Otherwise it returns 0. - * - * @param em Emergency object. - * @param errorBit from @ref CO_EM_errorStatusBits. - * - * @return false: Error is not present. - * @return true: Error is present. - */ -bool_t CO_isError(CO_EM_t *em, const uint8_t errorBit); - - -#ifdef CO_DOXYGEN -/** Skip section, if CO_SDO.h is not included */ - #define CO_SDO_H -#endif -#ifdef CO_SDO_H - - -/** - * Error control and Emergency object. It controls internal error state and - * sends emergency message, if error condition was reported. Object is initialized - * by CO_EM_init(). It contains CO_EM_t object. - */ -typedef struct{ - uint8_t *errorRegister; /**< From CO_EM_init() */ - uint32_t *preDefErr; /**< From CO_EM_init() */ - uint8_t preDefErrSize; /**< From CO_EM_init() */ - uint8_t preDefErrNoOfErrors;/**< Number of active errors in preDefErr */ - uint16_t inhibitEmTimer; /**< Internal timer for emergency message */ - CO_EM_t *em; /**< CO_EM_t sub object is included here */ - CO_CANmodule_t *CANdev; /**< From CO_EM_init() */ - CO_CANtx_t *CANtxBuff; /**< CAN transmit buffer */ -}CO_EMpr_t; - - -/** - * Initialize Error control and Emergency object. - * - * Function must be called in the communication reset section. - * - * @param emPr This object will be initialized. - * @param em Emergency object defined separately. Will be included in emPr and - * initialized too. - * @param SDO SDO server object. - * @param errorStatusBits Pointer to _Error Status Bits_ array from Object Dictionary - * (manufacturer specific section). See @ref CO_EM_errorStatusBits. - * @param errorStatusBitsSize Total size of the above array. Must be >= 6. - * @param errorRegister Pointer to _Error Register_ (Object dictionary, index 0x1001). - * @param preDefErr Pointer to _Pre defined error field_ array from Object - * dictionary, index 0x1003. - * @param preDefErrSize Size of the above array. - * @param CANdevRx CAN device for Emergency reception. - * @param CANdevRxIdx Index of receive buffer in the above CAN device. - * @param CANdevTx CAN device for Emergency transmission. - * @param CANdevTxIdx Index of transmit buffer in the above CAN device. - * @param CANidTxEM CAN identifier for Emergency message. - * - * @return #CO_ReturnError_t CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT. - */ -CO_ReturnError_t CO_EM_init( - CO_EM_t *em, - CO_EMpr_t *emPr, - CO_SDO_t *SDO, - uint8_t *errorStatusBits, - uint8_t errorStatusBitsSize, - uint8_t *errorRegister, - uint32_t *preDefErr, - uint8_t preDefErrSize, - CO_CANmodule_t *CANdevRx, - uint16_t CANdevRxIdx, - CO_CANmodule_t *CANdevTx, - uint16_t CANdevTxIdx, - uint16_t CANidTxEM); - - -/** - * Initialize Emergency callback function. - * - * Function initializes optional callback function, which executes after - * error condition is changed. Function may wake up external task, - * which processes mainline CANopen functions. - * - * @param em This object. - * @param pFunctSignal Pointer to the callback function. Not called if NULL. - */ -void CO_EM_initCallback( - CO_EM_t *em, - void (*pFunctSignal)(void)); - - -/** - * Initialize Emergency received callback function. - * - * Function initializes optional callback function, which executes after - * error condition is received. Function may wake up external task, - * which processes mainline CANopen functions. - * - * @remark Depending on the CAN driver implementation, this function is called - * inside an ISR - * - * @param em This object. - * @param pFunctSignal Pointer to the callback function. Not called if NULL. - */ -void CO_EM_initCallbackRx( - CO_EM_t *em, - void (*pFunctSignalRx)(const uint16_t ident, - const uint16_t errorCode, - const uint8_t errorRegister, - const uint8_t errorBit, - const uint32_t infoCode)); - - -/** - * Process Error control and Emergency object. - * - * Function must be called cyclically. It verifies some communication errors, - * calculates bit 0 and bit 4 from _Error register_ and sends emergency message - * if necessary. - * - * @param emPr This object. - * @param NMTisPreOrOperational True if this node is NMT_PRE_OPERATIONAL or NMT_OPERATIONAL. - * @param timeDifference_100us Time difference from previous function call in [100 * microseconds]. - * @param emInhTime _Inhibit time EMCY_ (object dictionary, index 0x1015). - * @param timerNext_ms Return value - info to OS - see CO_process(). - */ -void CO_EM_process( - CO_EMpr_t *emPr, - bool_t NMTisPreOrOperational, - uint16_t timeDifference_100us, - uint16_t emInhTime, - uint16_t *timerNext_ms); - - -#endif - -#ifdef __cplusplus -} -#endif /*__cplusplus*/ - -/** @} */ -#endif diff --git a/stack/CO_HBconsumer.c b/stack/CO_HBconsumer.c deleted file mode 100644 index ec4dbf2c..00000000 --- a/stack/CO_HBconsumer.c +++ /dev/null @@ -1,420 +0,0 @@ -/* - * CANopen Heartbeat consumer object. - * - * @file CO_HBconsumer.c - * @ingroup CO_HBconsumer - * @author Janez Paternoster - * @copyright 2004 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "CANopen.h" -#include "CO_HBconsumer.h" - -/* - * Read received message from CAN module. - * - * Function will be called (by CAN receive interrupt) every time, when CAN - * message with correct identifier will be received. For more information and - * description of parameters see file CO_driver.h. - */ -static void CO_HBcons_receive(void *object, const CO_CANrxMsg_t *msg){ - CO_HBconsNode_t *HBconsNode; - - HBconsNode = (CO_HBconsNode_t*) object; /* this is the correct pointer type of the first argument */ - - /* verify message length */ - if(msg->DLC == 1){ - /* copy data and set 'new message' flag. */ - HBconsNode->NMTstate = (CO_NMT_internalState_t)msg->data[0]; - SET_CANrxNew(HBconsNode->CANrxNew); - } -} - - -/* - * Configure one monitored node. - */ -static void CO_HBcons_monitoredNodeConfig( - CO_HBconsumer_t *HBcons, - uint8_t idx, - uint8_t nodeId, - uint16_t time) -{ - uint16_t COB_ID; - CO_HBconsNode_t *monitoredNode; - - if(idx >= HBcons->numberOfMonitoredNodes) return; - - monitoredNode = &HBcons->monitoredNodes[idx]; - monitoredNode->nodeId = nodeId; - monitoredNode->time = time; - monitoredNode->NMTstate = CO_NMT_INITIALIZING; - monitoredNode->HBstate = CO_HBconsumer_UNCONFIGURED; - - /* is channel used */ - if(monitoredNode->nodeId && monitoredNode->time){ - COB_ID = monitoredNode->nodeId + CO_CAN_ID_HEARTBEAT; - monitoredNode->HBstate = CO_HBconsumer_UNKNOWN; - - } - else{ - COB_ID = 0; - monitoredNode->time = 0; - } - - /* configure Heartbeat consumer CAN reception */ - if (monitoredNode->HBstate != CO_HBconsumer_UNCONFIGURED) { - CO_CANrxBufferInit( - HBcons->CANdevRx, - HBcons->CANdevRxIdxStart + idx, - COB_ID, - 0x7FF, - 0, - (void*)&HBcons->monitoredNodes[idx], - CO_HBcons_receive); - } -} - - -/* - * OD function for accessing _Consumer Heartbeat Time_ (index 0x1016) from SDO server. - * - * For more information see file CO_SDO.h. - */ -static CO_SDO_abortCode_t CO_ODF_1016(CO_ODF_arg_t *ODF_arg) -{ - CO_HBconsumer_t *HBcons; - uint8_t NodeID; - uint16_t HBconsTime; - uint32_t value; - CO_ReturnError_t ret; - - if(ODF_arg->reading){ - return CO_SDO_AB_NONE; - } - - HBcons = (CO_HBconsumer_t*) ODF_arg->object; - value = CO_getUint32(ODF_arg->data); - NodeID = (value >> 16U) & 0xFFU; - HBconsTime = value & 0xFFFFU; - - if((value & 0xFF800000U) != 0){ - return CO_SDO_AB_PRAM_INCOMPAT; - } - - ret = CO_HBconsumer_initEntry(HBcons, ODF_arg->subIndex-1U, NodeID, HBconsTime); - if (ret != CO_ERROR_NO) { - return CO_SDO_AB_PRAM_INCOMPAT; - } - return CO_SDO_AB_NONE; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_HBconsumer_init( - CO_HBconsumer_t *HBcons, - CO_EM_t *em, - CO_SDO_t *SDO, - const uint32_t HBconsTime[], - CO_HBconsNode_t monitoredNodes[], - uint8_t numberOfMonitoredNodes, - CO_CANmodule_t *CANdevRx, - uint16_t CANdevRxIdxStart) -{ - uint8_t i; - - /* verify arguments */ - if(HBcons==NULL || em==NULL || SDO==NULL || HBconsTime==NULL || - monitoredNodes==NULL || CANdevRx==NULL){ - return CO_ERROR_ILLEGAL_ARGUMENT; - } - - /* Configure object variables */ - HBcons->em = em; - HBcons->HBconsTime = HBconsTime; - HBcons->monitoredNodes = monitoredNodes; - HBcons->numberOfMonitoredNodes = numberOfMonitoredNodes; - HBcons->allMonitoredOperational = 0; - HBcons->CANdevRx = CANdevRx; - HBcons->CANdevRxIdxStart = CANdevRxIdxStart; - - for(i=0; inumberOfMonitoredNodes; i++) { - uint8_t nodeId = (HBcons->HBconsTime[i] >> 16U) & 0xFFU; - uint16_t time = HBcons->HBconsTime[i] & 0xFFFFU; - CO_HBconsumer_initEntry(HBcons, i, nodeId, time); - } - - /* Configure Object dictionary entry at index 0x1016 */ - CO_OD_configure(SDO, OD_H1016_CONSUMER_HB_TIME, CO_ODF_1016, (void*)HBcons, 0, 0); - - return CO_ERROR_NO; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_HBconsumer_initEntry( - CO_HBconsumer_t *HBcons, - uint8_t idx, - uint8_t nodeId, - uint16_t consumerTime) -{ - CO_ReturnError_t ret = CO_ERROR_NO; - - /* verify arguments */ - if(HBcons==NULL){ - return CO_ERROR_ILLEGAL_ARGUMENT; - } - - if((consumerTime != 0) && (nodeId != 0)){ - uint8_t i; - /* there must not be more entries with same index and time different than zero */ - for(i = 0U; inumberOfMonitoredNodes; i++){ - uint32_t objectCopy = HBcons->HBconsTime[i]; - uint8_t NodeIDObj = (objectCopy >> 16U) & 0xFFU; - uint16_t HBconsTimeObj = objectCopy & 0xFFFFU; - if((idx != i) && (HBconsTimeObj != 0) && (nodeId == NodeIDObj)){ - ret = CO_ERROR_ILLEGAL_ARGUMENT; - } - } - } - - /* Configure */ - if(ret == CO_ERROR_NO){ - CO_HBcons_monitoredNodeConfig(HBcons, idx, nodeId, consumerTime); - } - return ret; -} - - -/******************************************************************************/ -void CO_HBconsumer_initCallbackHeartbeatStarted( - CO_HBconsumer_t *HBcons, - uint8_t idx, - void *object, - void (*pFunctSignal)(uint8_t nodeId, uint8_t idx, void *object)) -{ - CO_HBconsNode_t *monitoredNode; - - if (HBcons==NULL || idx>HBcons->numberOfMonitoredNodes) { - return; - } - - monitoredNode = &HBcons->monitoredNodes[idx]; - monitoredNode->pFunctSignalHbStarted = pFunctSignal; - monitoredNode->functSignalObjectHbStarted = object; -} - - -/******************************************************************************/ -void CO_HBconsumer_initCallbackTimeout( - CO_HBconsumer_t *HBcons, - uint8_t idx, - void *object, - void (*pFunctSignal)(uint8_t nodeId, uint8_t idx, void *object)) -{ - CO_HBconsNode_t *monitoredNode; - - if (HBcons==NULL || idx>HBcons->numberOfMonitoredNodes) { - return; - } - - monitoredNode = &HBcons->monitoredNodes[idx]; - monitoredNode->pFunctSignalTimeout = pFunctSignal; - monitoredNode->functSignalObjectTimeout = object; -} - - -/******************************************************************************/ -void CO_HBconsumer_initCallbackRemoteReset( - CO_HBconsumer_t *HBcons, - uint8_t idx, - void *object, - void (*pFunctSignal)(uint8_t nodeId, uint8_t idx, void *object)) -{ - CO_HBconsNode_t *monitoredNode; - - if (HBcons==NULL || idx>HBcons->numberOfMonitoredNodes) { - return; - } - - monitoredNode = &HBcons->monitoredNodes[idx]; - monitoredNode->pFunctSignalRemoteReset = pFunctSignal; - monitoredNode->functSignalObjectRemoteReset = object; -} - -/******************************************************************************/ -void CO_HBconsumer_process( - CO_HBconsumer_t *HBcons, - bool_t NMTisPreOrOperational, - uint16_t timeDifference_ms) -{ - uint8_t i; - uint8_t emcyHeartbeatTimeoutActive = 0; - uint8_t emcyRemoteResetActive = 0; - uint8_t AllMonitoredOperationalCopy; - CO_HBconsNode_t *monitoredNode; - - AllMonitoredOperationalCopy = 5; - monitoredNode = &HBcons->monitoredNodes[0]; - - if(NMTisPreOrOperational){ - for(i=0; inumberOfMonitoredNodes; i++){ - if(monitoredNode->time > 0){/* is node monitored */ - /* Verify if received message is heartbeat or bootup */ - if(IS_CANrxNew(monitoredNode->CANrxNew)){ - if(monitoredNode->NMTstate == CO_NMT_INITIALIZING){ - /* bootup message, call callback */ - if (monitoredNode->pFunctSignalRemoteReset != NULL) { - monitoredNode->pFunctSignalRemoteReset(monitoredNode->nodeId, i, - monitoredNode->functSignalObjectRemoteReset); - } - } - else { - /* heartbeat message */ - if (monitoredNode->HBstate!=CO_HBconsumer_ACTIVE && - monitoredNode->pFunctSignalHbStarted!=NULL) { - monitoredNode->pFunctSignalHbStarted(monitoredNode->nodeId, i, - monitoredNode->functSignalObjectHbStarted); - } - monitoredNode->HBstate = CO_HBconsumer_ACTIVE; - monitoredNode->timeoutTimer = 0; /* reset timer */ - timeDifference_ms = 0; - } - CLEAR_CANrxNew(monitoredNode->CANrxNew); - } - - /* Verify timeout */ - if(monitoredNode->timeoutTimer < monitoredNode->time) { - monitoredNode->timeoutTimer += timeDifference_ms; - } - if(monitoredNode->HBstate!=CO_HBconsumer_UNCONFIGURED && - monitoredNode->HBstate!=CO_HBconsumer_UNKNOWN) { - if(monitoredNode->timeoutTimer >= monitoredNode->time){ - /* timeout expired */ - CO_errorReport(HBcons->em, CO_EM_HEARTBEAT_CONSUMER, CO_EMC_HEARTBEAT, i); - emcyHeartbeatTimeoutActive = 1; - - monitoredNode->NMTstate = CO_NMT_INITIALIZING; - if (monitoredNode->HBstate!=CO_HBconsumer_TIMEOUT && - monitoredNode->pFunctSignalTimeout!=NULL) { - monitoredNode->pFunctSignalTimeout(monitoredNode->nodeId, i, - monitoredNode->functSignalObjectTimeout); - } - monitoredNode->HBstate = CO_HBconsumer_TIMEOUT; - } - else if(monitoredNode->NMTstate == CO_NMT_INITIALIZING){ - /* there was a bootup message */ - CO_errorReport(HBcons->em, CO_EM_HB_CONSUMER_REMOTE_RESET, CO_EMC_HEARTBEAT, i); - emcyRemoteResetActive = 1; - - monitoredNode->HBstate = CO_HBconsumer_UNKNOWN; - } - } - if(monitoredNode->NMTstate != CO_NMT_OPERATIONAL) { - AllMonitoredOperationalCopy = 0; - } - } - monitoredNode++; - } - } - else{ /* not in (pre)operational state */ - for(i=0; inumberOfMonitoredNodes; i++){ - monitoredNode->NMTstate = CO_NMT_INITIALIZING; - CLEAR_CANrxNew(monitoredNode->CANrxNew); - if(monitoredNode->HBstate != CO_HBconsumer_UNCONFIGURED){ - monitoredNode->HBstate = CO_HBconsumer_UNKNOWN; - } - monitoredNode++; - } - AllMonitoredOperationalCopy = 0; - } - /* clear emergencies. We only have one emergency index for all - * monitored nodes! */ - if ( ! emcyHeartbeatTimeoutActive) { - CO_errorReset(HBcons->em, CO_EM_HEARTBEAT_CONSUMER, 0); - } - if ( ! emcyRemoteResetActive) { - CO_errorReset(HBcons->em, CO_EM_HB_CONSUMER_REMOTE_RESET, 0); - } - - HBcons->allMonitoredOperational = AllMonitoredOperationalCopy; -} - - -/******************************************************************************/ -int8_t CO_HBconsumer_getIdxByNodeId( - CO_HBconsumer_t *HBcons, - uint8_t nodeId) -{ - uint8_t i; - CO_HBconsNode_t *monitoredNode; - - if (HBcons == NULL) { - return -1; - } - - /* linear search for the node */ - monitoredNode = &HBcons->monitoredNodes[0]; - for(i=0; inumberOfMonitoredNodes; i++){ - if (monitoredNode->nodeId == nodeId) { - return i; - } - monitoredNode ++; - } - /* not found */ - return -1; -} - - -/******************************************************************************/ -CO_HBconsumer_state_t CO_HBconsumer_getState( - CO_HBconsumer_t *HBcons, - uint8_t idx) -{ - CO_HBconsNode_t *monitoredNode; - - if (HBcons==NULL || idx>HBcons->numberOfMonitoredNodes) { - return CO_HBconsumer_UNCONFIGURED; - } - - monitoredNode = &HBcons->monitoredNodes[idx]; - return monitoredNode->HBstate; -} - -/******************************************************************************/ -int8_t CO_HBconsumer_getNmtState( - CO_HBconsumer_t *HBcons, - uint8_t idx, - CO_NMT_internalState_t *nmtState) -{ - CO_HBconsNode_t *monitoredNode; - - if (HBcons==NULL || nmtState==NULL || idx>HBcons->numberOfMonitoredNodes) { - return -1; - } - *nmtState = CO_NMT_INITIALIZING; - - monitoredNode = &HBcons->monitoredNodes[idx]; - - if (monitoredNode->HBstate == CO_HBconsumer_ACTIVE) { - *nmtState = monitoredNode->NMTstate; - return 0; - } - return -1; -} diff --git a/stack/CO_HBconsumer.h b/stack/CO_HBconsumer.h deleted file mode 100644 index f0e11e63..00000000 --- a/stack/CO_HBconsumer.h +++ /dev/null @@ -1,267 +0,0 @@ -/** - * CANopen Heartbeat consumer protocol. - * - * @file CO_HBconsumer.h - * @ingroup CO_HBconsumer - * @author Janez Paternoster - * @copyright 2004 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#ifndef CO_HB_CONS_H -#define CO_HB_CONS_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @defgroup CO_HBconsumer Heartbeat consumer - * @ingroup CO_CANopen - * @{ - * - * CANopen Heartbeat consumer protocol. - * - * Heartbeat consumer monitors Heartbeat messages from remote nodes. If any - * monitored node don't send his Heartbeat in specified time, Heartbeat consumer - * sends emergency message. If all monitored nodes are operational, then - * variable _allMonitoredOperational_ inside CO_HBconsumer_t is set to true. - * Monitoring starts after the reception of the first HeartBeat (not bootup). - * - * Heartbeat set up is done by writing to the OD registers 0x1016 or by using - * the function _CO_HBconsumer_initEntry()_ - * - * @see @ref CO_NMT_Heartbeat - */ - -/** - * Heartbeat state of a node - */ -typedef enum { - CO_HBconsumer_UNCONFIGURED = 0x00U, /**< Consumer entry inactive */ - CO_HBconsumer_UNKNOWN = 0x01U, /**< Consumer enabled, but no heartbeat received yet */ - CO_HBconsumer_ACTIVE = 0x02U, /**< Heartbeat received within set time */ - CO_HBconsumer_TIMEOUT = 0x03U, /**< No heatbeat received for set time */ -} CO_HBconsumer_state_t; - - -/** - * One monitored node inside CO_HBconsumer_t. - */ -typedef struct{ - uint8_t nodeId; /**< Node Id of the monitored node */ - CO_NMT_internalState_t NMTstate; /**< Of the remote node (Heartbeat payload) */ - CO_HBconsumer_state_t HBstate; /**< Current heartbeat state */ - uint16_t timeoutTimer; /**< Time since last heartbeat received */ - uint16_t time; /**< Consumer heartbeat time from OD */ - volatile void *CANrxNew; /**< Indication if new Heartbeat message received from the CAN bus */ - /** Callback for heartbeat state change to active event */ - void (*pFunctSignalHbStarted)(uint8_t nodeId, uint8_t idx, void *object); /**< From CO_HBconsumer_initTimeoutCallback() or NULL */ - void *functSignalObjectHbStarted;/**< Pointer to object */ - /** Callback for consumer timeout event */ - void (*pFunctSignalTimeout)(uint8_t nodeId, uint8_t idx, void *object); /**< From CO_HBconsumer_initTimeoutCallback() or NULL */ - void *functSignalObjectTimeout;/**< Pointer to object */ - /** Callback for remote reset event */ - void (*pFunctSignalRemoteReset)(uint8_t nodeId, uint8_t idx, void *object); /**< From CO_HBconsumer_initRemoteResetCallback() or NULL */ - void *functSignalObjectRemoteReset;/**< Pointer to object */ -}CO_HBconsNode_t; - - -/** - * Heartbeat consumer object. - * - * Object is initilaized by CO_HBconsumer_init(). It contains an array of - * CO_HBconsNode_t objects. - */ -typedef struct{ - CO_EM_t *em; /**< From CO_HBconsumer_init() */ - const uint32_t *HBconsTime; /**< From CO_HBconsumer_init() */ - CO_HBconsNode_t *monitoredNodes; /**< From CO_HBconsumer_init() */ - uint8_t numberOfMonitoredNodes; /**< From CO_HBconsumer_init() */ - /** True, if all monitored nodes are NMT operational or no node is - monitored. Can be read by the application */ - uint8_t allMonitoredOperational; - CO_CANmodule_t *CANdevRx; /**< From CO_HBconsumer_init() */ - uint16_t CANdevRxIdxStart; /**< From CO_HBconsumer_init() */ -}CO_HBconsumer_t; - - -/** - * Initialize Heartbeat consumer object. - * - * Function must be called in the communication reset section. - * - * @param HBcons This object will be initialized. - * @param em Emergency object. - * @param SDO SDO server object. - * @param HBconsTime Pointer to _Consumer Heartbeat Time_ array - * from Object Dictionary (index 0x1016). Size of array is equal to numberOfMonitoredNodes. - * @param monitoredNodes Pointer to the externaly defined array of the same size - * as numberOfMonitoredNodes. - * @param numberOfMonitoredNodes Total size of the above arrays. - * @param CANdevRx CAN device for Heartbeat reception. - * @param CANdevRxIdxStart Starting index of receive buffer in the above CAN device. - * Number of used indexes is equal to numberOfMonitoredNodes. - * - * @return #CO_ReturnError_t CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT. - */ -CO_ReturnError_t CO_HBconsumer_init( - CO_HBconsumer_t *HBcons, - CO_EM_t *em, - CO_SDO_t *SDO, - const uint32_t HBconsTime[], - CO_HBconsNode_t monitoredNodes[], - uint8_t numberOfMonitoredNodes, - CO_CANmodule_t *CANdevRx, - uint16_t CANdevRxIdxStart); - -/** - * Initialize one Heartbeat consumer entry - * - * Calling this function has the same affect as writing to the corresponding - * entries in the Object Dictionary (index 0x1016) - * @remark The values in the Object Dictionary must be set manually by the - * calling function so that heartbeat consumer behaviour matches the OD value. - * - * @param HBcons This object. - * @param idx index of the node in HBcons object - * @param nodeId see OD 0x1016 description - * @param consumerTime see OD 0x1016 description - * @return - */ -CO_ReturnError_t CO_HBconsumer_initEntry( - CO_HBconsumer_t *HBcons, - uint8_t idx, - uint8_t nodeId, - uint16_t consumerTime); - -/** - * Initialize Heartbeat consumer started callback function. - * - * Function initializes optional callback function, which is called for the - * first received heartbeat after activating hb consumer or timeout. - * Function may wake up external task, which handles this event. - * - * @param HBcons This object. - * @param idx index of the node in HBcons object - * @param object Pointer to object, which will be passed to pFunctSignal(). Can be NULL - * @param pFunctSignal Pointer to the callback function. Not called if NULL. - */ -void CO_HBconsumer_initCallbackHeartbeatStarted( - CO_HBconsumer_t *HBcons, - uint8_t idx, - void *object, - void (*pFunctSignal)(uint8_t nodeId, uint8_t idx, void *object)); - - -/** - * Initialize Heartbeat consumer timeout callback function. - * - * Function initializes optional callback function, which is called when the node - * state changes from active to timeout. Function may wake up external task, - * which handles this event. - * - * @param HBcons This object. - * @param idx index of the node in HBcons object - * @param object Pointer to object, which will be passed to pFunctSignal(). Can be NULL - * @param pFunctSignal Pointer to the callback function. Not called if NULL. - */ -void CO_HBconsumer_initCallbackTimeout( - CO_HBconsumer_t *HBcons, - uint8_t idx, - void *object, - void (*pFunctSignal)(uint8_t nodeId, uint8_t idx, void *object)); - -/** - * Initialize Heartbeat consumer remote reset detected callback function. - * - * Function initializes optional callback function, which is called when a bootup - * message is received from the remote node. Function may wake up external task, - * which handles this event. - * - * @param HBcons This object. - * @param idx index of the node in HBcons object - * @param object Pointer to object, which will be passed to pFunctSignal(). Can be NULL - * @param pFunctSignal Pointer to the callback function. Not called if NULL. - */ -void CO_HBconsumer_initCallbackRemoteReset( - CO_HBconsumer_t *HBcons, - uint8_t idx, - void *object, - void (*pFunctSignal)(uint8_t nodeId, uint8_t idx, void *object)); - -/** - * Process Heartbeat consumer object. - * - * Function must be called cyclically. - * - * @param HBcons This object. - * @param NMTisPreOrOperational True if this node is NMT_PRE_OPERATIONAL or NMT_OPERATIONAL. - * @param timeDifference_ms Time difference from previous function call in [milliseconds]. - */ -void CO_HBconsumer_process( - CO_HBconsumer_t *HBcons, - bool_t NMTisPreOrOperational, - uint16_t timeDifference_ms); - -/** - * Get the heartbeat producer object index by node ID - * - * @param HBcons This object. - * @param nodeId producer node ID - * @return index. -1 if not found - */ -int8_t CO_HBconsumer_getIdxByNodeId( - CO_HBconsumer_t *HBcons, - uint8_t nodeId); - -/** - * Get the current state of a heartbeat producer by the index in OD 0x1016 - * - * @param HBcons This object. - * @param idx object sub index - * @return #CO_HBconsumer_state_t - */ -CO_HBconsumer_state_t CO_HBconsumer_getState( - CO_HBconsumer_t *HBcons, - uint8_t idx); - -/** - * Get the current NMT state of a heartbeat producer by the index in OD 0x1016 - * - * NMT state is only available when heartbeat is enabled for this index! - * - * @param HBcons This object. - * @param idx object sub index - * @param [out] #CO_NMT_internalState_t of this index - * @retval 0 NMT state has been received and is valid - * @retval -1 not valid - */ -int8_t CO_HBconsumer_getNmtState( - CO_HBconsumer_t *HBcons, - uint8_t idx, - CO_NMT_internalState_t *nmtState); - - -#ifdef __cplusplus -} -#endif /*__cplusplus*/ - -/** @} */ -#endif diff --git a/stack/CO_LSS.h b/stack/CO_LSS.h deleted file mode 100644 index 3139088f..00000000 --- a/stack/CO_LSS.h +++ /dev/null @@ -1,253 +0,0 @@ -/** - * CANopen LSS Master/Slave protocol. - * - * @file CO_LSS.h - * @ingroup CO_LSS - * @author Martin Wagner - * @copyright 2017 - 2020 Neuberger Gebaeudeautomation GmbH - * - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef CO_LSS_H -#define CO_LSS_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @defgroup CO_LSS LSS - * @ingroup CO_CANopen - * @{ - * - * CANopen Layer Setting Services protocol - * - * LSS protocol is according to CiA DSP 305 V3.0.0. - * - * LSS services and protocols are used to inquire or to change the settings - * of three parameters of the physical layer, data link layer, and application - * layer on a CANopen device with LSS slave capability by a CANopen device - * with LSS master capability via the CAN network. - * - * The following parameters may be inquired or changed: - * - Node-ID of the CANopen device - * - Bit timing parameters of the physical layer (bit rate) - * - LSS address compliant to the identity object (1018h) - * - * The connection is established in one of two ways: - * - addressing a node by it's 128 bit LSS address. This requires that the - * master already knows the node's LSS address. - * - scanning the network for unknown nodes (Fastscan). Using this method, - * unknown devices can be found and configured one by one. - * - * Be aware that changing the bit rate is a critical step for the network. A - * failure will render the network unusable! - * - * Using this implementation, only master or slave can be included in one - * node at a time. - * - * For CAN identifiers see #CO_Default_CAN_ID_t - */ - -#if CO_NO_LSS_CLIENT == 1 || CO_NO_LSS_SERVER == 1 - -/** - * LSS protocol command specifiers - * - * The LSS protocols are executed between the LSS master device and the LSS - * slave device(s) to implement the LSS services. Some LSS protocols require - * a sequence of CAN messages. - * - * As identifying method only "LSS fastscan" is supported. - */ -typedef enum { - CO_LSS_SWITCH_STATE_GLOBAL = 0x04U, /**< Switch state global protocol */ - CO_LSS_SWITCH_STATE_SEL_VENDOR = 0x40U, /**< Switch state selective protocol - Vendor ID */ - CO_LSS_SWITCH_STATE_SEL_PRODUCT = 0x41U, /**< Switch state selective protocol - Product code */ - CO_LSS_SWITCH_STATE_SEL_REV = 0x42U, /**< Switch state selective protocol - Revision number */ - CO_LSS_SWITCH_STATE_SEL_SERIAL = 0x43U, /**< Switch state selective protocol - Serial number */ - CO_LSS_SWITCH_STATE_SEL = 0x44U, /**< Switch state selective protocol - Slave response */ - CO_LSS_CFG_NODE_ID = 0x11U, /**< Configure node ID protocol */ - CO_LSS_CFG_BIT_TIMING = 0x13U, /**< Configure bit timing parameter protocol */ - CO_LSS_CFG_ACTIVATE_BIT_TIMING = 0x15U, /**< Activate bit timing parameter protocol */ - CO_LSS_CFG_STORE = 0x17U, /**< Store configuration protocol */ - CO_LSS_IDENT_SLAVE = 0x4FU, /**< LSS Fastscan response */ - CO_LSS_IDENT_FASTSCAN = 0x51U, /**< LSS Fastscan protocol */ - CO_LSS_INQUIRE_VENDOR = 0x5AU, /**< Inquire identity vendor-ID protocol */ - CO_LSS_INQUIRE_PRODUCT = 0x5BU, /**< Inquire identity product-code protocol */ - CO_LSS_INQUIRE_REV = 0x5CU, /**< Inquire identity revision-number protocol */ - CO_LSS_INQUIRE_SERIAL = 0x5DU, /**< Inquire identity serial-number protocol */ - CO_LSS_INQUIRE_NODE_ID = 0x5EU, /**< Inquire node-ID protocol */ -} CO_LSS_cs_t; - -/** - * Macro to get service type group from command specifier - * @{*/ -#define CO_LSS_CS_SERVICE_IS_SWITCH_GLOBAL(cs) (cs == CO_LSS_SWITCH_STATE_GLOBAL) -#define CO_LSS_CS_SERVICE_IS_SWITCH_STATE_SELECTIVE(cs) (cs >= CO_LSS_SWITCH_STATE_SEL_VENDOR && cs <= CO_LSS_SWITCH_STATE_SEL) -#define CO_LSS_CS_SERVICE_IS_CONFIG(cs) (cs >= CO_LSS_CFG_NODE_ID && cs <= CO_LSS_CFG_STORE) -#define CO_LSS_CS_SERVICE_IS_INQUIRE(cs) (cs >= CO_LSS_INQUIRE_VENDOR && cs <= CO_LSS_INQUIRE_NODE_ID) -#define CO_LSS_CS_SERVICE_IS_IDENT(cs) (cs==CO_LSS_IDENT_SLAVE || cs==CO_LSS_IDENT_FASTSCAN) -/**@}*/ - -/** - * Error codes for Configure node ID protocol - */ -typedef enum { - CO_LSS_CFG_NODE_ID_OK = 0x00U,/**< Protocol successfully completed */ - CO_LSS_CFG_NODE_ID_OUT_OF_RANGE = 0x01U,/**< NID out of range */ - CO_LSS_CFG_NODE_ID_MANUFACTURER = 0xFFU /**< Manufacturer specific error. No further support */ -} CO_LSS_cfgNodeId_t; - -/** - * Error codes for Configure bit timing parameters protocol - */ -typedef enum { - CO_LSS_CFG_BIT_TIMING_OK = 0x00U,/**< Protocol successfully completed */ - CO_LSS_CFG_BIT_TIMING_OUT_OF_RANGE = 0x01U,/**< Bit timing / Bit rate not supported */ - CO_LSS_CFG_BIT_TIMING_MANUFACTURER = 0xFFU /**< Manufacturer specific error. No further support */ -} CO_LSS_cfgBitTiming_t; - -/** - * Error codes for Store configuration protocol - */ -typedef enum { - CO_LSS_CFG_STORE_OK = 0x00U, /**< Protocol successfully completed */ - CO_LSS_CFG_STORE_NOT_SUPPORTED = 0x01U, /**< Store configuration not supported */ - CO_LSS_CFG_STORE_FAILED = 0x02U, /**< Storage media access error */ - CO_LSS_CFG_STORE_MANUFACTURER = 0xFFU /**< Manufacturer specific error. No further support */ -} CO_LSS_cfgStore_t; - -/** - * Fastscan BitCheck. BIT0 means all bits are checked for equality by slave. - */ -typedef enum { - CO_LSS_FASTSCAN_BIT0 = 0x00U, /**< Least significant bit of IDnumbners bit area to be checked */ - /* ... */ - CO_LSS_FASTSCAN_BIT31 = 0x1FU, /**< dito */ - CO_LSS_FASTSCAN_CONFIRM = 0x80U /**< All LSS slaves waiting for scan respond and previous scan is reset */ -} CO_LSS_fastscan_bitcheck; - -#define CO_LSS_FASTSCAN_BITCHECK_VALID(bit) ((bit>=CO_LSS_FASTSCAN_BIT0 && bit<=CO_LSS_FASTSCAN_BIT31) || bit==CO_LSS_FASTSCAN_CONFIRM) - -/** - * Fastscan LSSsub, LSSnext - */ -typedef enum { - CO_LSS_FASTSCAN_VENDOR_ID = 0, /**< Vendor ID */ - CO_LSS_FASTSCAN_PRODUCT = 1, /**< Product code */ - CO_LSS_FASTSCAN_REV = 2, /**< Revision number */ - CO_LSS_FASTSCAN_SERIAL = 3 /**< Serial number */ -} CO_LSS_fastscan_lss_sub_next; - -#define CO_LSS_FASTSCAN_LSS_SUB_NEXT_VALID(index) (index>=CO_LSS_FASTSCAN_VENDOR_ID && index<=CO_LSS_FASTSCAN_SERIAL) - -/** - * The LSS address is a 128 bit number, uniquely identifying each node. It - * consists of the values in object 0x1018. - */ -typedef union { - uint32_t addr[4]; - struct { - uint32_t vendorID; - uint32_t productCode; - uint32_t revisionNumber; - uint32_t serialNumber; - } identity; -} CO_LSS_address_t; - -/** - * LSS finite state automaton - * - * The LSS FSA shall provide the following states: - * - Initial: Pseudo state, indicating the activation of the FSA. - * - LSS waiting: In this state, the LSS slave device waits for requests. - * - LSS configuration: In this state variables may be configured in the LSS slave. - * - Final: Pseudo state, indicating the deactivation of the FSA. - */ -typedef enum { - CO_LSS_STATE_WAITING = 0, /**< LSS FSA waiting for requests*/ - CO_LSS_STATE_CONFIGURATION = 1, /**< LSS FSA waiting for configuration*/ -} CO_LSS_state_t; - -/** - * Definition of table_index for /CiA301/ bit timing table - */ -typedef enum { - CO_LSS_BIT_TIMING_1000 = 0, /**< 1000kbit/s */ - CO_LSS_BIT_TIMING_800 = 1, /**< 800kbit/s */ - CO_LSS_BIT_TIMING_500 = 2, /**< 500kbit/s */ - CO_LSS_BIT_TIMING_250 = 3, /**< 250kbit/s */ - CO_LSS_BIT_TIMING_125 = 4, /**< 125kbit/s */ - /* reserved = 5 */ - CO_LSS_BIT_TIMING_50 = 6, /**< 50kbit/s */ - CO_LSS_BIT_TIMING_20 = 7, /**< 20kbit/s */ - CO_LSS_BIT_TIMING_10 = 8, /**< 10kbit/s */ - CO_LSS_BIT_TIMING_AUTO = 9, /**< Automatic bit rate detection */ -} CO_LSS_bitTimingTable_t; - -/** - * Lookup table for conversion between bit timing table and numerical - * bit rate - */ -static const uint16_t CO_LSS_bitTimingTableLookup[] = { - 1000, - 800, - 500, - 250, - 125, - 0, - 50, - 20, - 10, - 0 -}; - -/** - * Macro to check if index contains valid bit timing - */ -#define CO_LSS_BIT_TIMING_VALID(index) (index != 5 && (index >= CO_LSS_BIT_TIMING_1000 && index <= CO_LSS_BIT_TIMING_AUTO)) - -/** - * Invalid node ID triggers node ID assignment - */ -#define CO_LSS_NODE_ID_ASSIGNMENT 0xFFU - -/** - * Macro to check if node id is valid - */ -#define CO_LSS_NODE_ID_VALID(nid) ((nid >= 1 && nid <= 0x7F) || nid == CO_LSS_NODE_ID_ASSIGNMENT) - -/** - * Macro to check if two LSS addresses are equal - */ -#define CO_LSS_ADDRESS_EQUAL(/*CO_LSS_address_t*/ a1, /*CO_LSS_address_t*/ a2) \ - (a1.identity.productCode == a2.identity.productCode && \ - a1.identity.revisionNumber == a2.identity.revisionNumber && \ - a1.identity.serialNumber == a2.identity.serialNumber && \ - a1.identity.vendorID == a2.identity.vendorID) - -#endif /* CO_NO_LSS_CLIENT == 1 || CO_NO_LSS_SERVER == 1 */ - -#ifdef __cplusplus -} -#endif /*__cplusplus*/ - -/** @} */ -#endif diff --git a/stack/CO_LSSmaster.c b/stack/CO_LSSmaster.c deleted file mode 100644 index cce9070f..00000000 --- a/stack/CO_LSSmaster.c +++ /dev/null @@ -1,1107 +0,0 @@ -/* - * CANopen LSS Master protocol. - * - * @file CO_LSSmaster.c - * @ingroup CO_LSS - * @author Martin Wagner - * @copyright 2017 - 2020 Neuberger Gebaeudeautomation GmbH - * - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "CANopen.h" -#include "CO_LSSmaster.h" - -#if CO_NO_LSS_CLIENT == 1 - -/* - * LSS master slave select state machine. Compared to #CO_LSS_state_t this - * has information if we currently have selected one or all slaves. This - * allows for some basic error checking. - */ -typedef enum { - CO_LSSmaster_STATE_WAITING = 0, - CO_LSSmaster_STATE_CFG_SLECTIVE, - CO_LSSmaster_STATE_CFG_GLOBAL, -} CO_LSSmaster_state_t; - -/* - * LSS master slave command state machine - */ -typedef enum { - CO_LSSmaster_COMMAND_WAITING = 0, - CO_LSSmaster_COMMAND_SWITCH_STATE, - CO_LSSmaster_COMMAND_CFG_BIT_TIMING, - CO_LSSmaster_COMMAND_CFG_NODE_ID, - CO_LSSmaster_COMMAND_CFG_STORE, - CO_LSSmaster_COMMAND_INQUIRE_VENDOR, - CO_LSSmaster_COMMAND_INQUIRE_PRODUCT, - CO_LSSmaster_COMMAND_INQUIRE_REV, - CO_LSSmaster_COMMAND_INQUIRE_SERIAL, - CO_LSSmaster_COMMAND_INQUIRE_NODE_ID, - CO_LSSmaster_COMMAND_IDENTIFY_FASTSCAN, -} CO_LSSmaster_command_t; - -/* - * LSS master fastscan state machine - */ -typedef enum { - CO_LSSmaster_FS_STATE_CHECK, - CO_LSSmaster_FS_STATE_SCAN, - CO_LSSmaster_FS_STATE_VERIFY -} CO_LSSmaster_fs_t; - -/* - * Read received message from CAN module. - * - * Function will be called (by CAN receive interrupt) every time, when CAN - * message with correct identifier will be received. For more information and - * description of parameters see file CO_driver.h. - */ -static void CO_LSSmaster_receive(void *object, const CO_CANrxMsg_t *msg) -{ - CO_LSSmaster_t *LSSmaster; - - LSSmaster = (CO_LSSmaster_t*)object; /* this is the correct pointer type of the first argument */ - - /* verify message length and message overflow (previous message was not processed yet) */ - if(msg->DLC==8 && !IS_CANrxNew(LSSmaster->CANrxNew) && - LSSmaster->command!=CO_LSSmaster_COMMAND_WAITING){ - - /* copy data and set 'new message' flag */ - LSSmaster->CANrxData[0] = msg->data[0]; - LSSmaster->CANrxData[1] = msg->data[1]; - LSSmaster->CANrxData[2] = msg->data[2]; - LSSmaster->CANrxData[3] = msg->data[3]; - LSSmaster->CANrxData[4] = msg->data[4]; - LSSmaster->CANrxData[5] = msg->data[5]; - LSSmaster->CANrxData[6] = msg->data[6]; - LSSmaster->CANrxData[7] = msg->data[7]; - - SET_CANrxNew(LSSmaster->CANrxNew); - - /* Optional signal to RTOS, which can resume task, which handles SDO client. */ - if(LSSmaster->pFunctSignal != NULL) { - LSSmaster->pFunctSignal(LSSmaster->functSignalObject); - } - } -} - -/* - * Check LSS timeout. - * - * Generally, we do not really care if the message has been received before - * or after the timeout expired. Only if no message has been received we have - * to check for timeouts - */ -static CO_LSSmaster_return_t CO_LSSmaster_check_timeout( - CO_LSSmaster_t *LSSmaster, - uint16_t timeDifference_ms) -{ - CO_LSSmaster_return_t ret = CO_LSSmaster_WAIT_SLAVE; - - LSSmaster->timeoutTimer += timeDifference_ms; - if (LSSmaster->timeoutTimer >= LSSmaster->timeout) { - LSSmaster->timeoutTimer = 0; - ret = CO_LSSmaster_TIMEOUT; - } - - return ret; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_LSSmaster_init( - CO_LSSmaster_t *LSSmaster, - uint16_t timeout_ms, - CO_CANmodule_t *CANdevRx, - uint16_t CANdevRxIdx, - uint32_t CANidLssSlave, - CO_CANmodule_t *CANdevTx, - uint16_t CANdevTxIdx, - uint32_t CANidLssMaster) -{ - /* verify arguments */ - if (LSSmaster==NULL || CANdevRx==NULL || CANdevTx==NULL){ - return CO_ERROR_ILLEGAL_ARGUMENT; - } - - LSSmaster->timeout = timeout_ms; - LSSmaster->state = CO_LSSmaster_STATE_WAITING; - LSSmaster->command = CO_LSSmaster_COMMAND_WAITING; - LSSmaster->timeoutTimer = 0; - CLEAR_CANrxNew(LSSmaster->CANrxNew); - CO_memset(LSSmaster->CANrxData, 0, sizeof(LSSmaster->CANrxData)); - LSSmaster->pFunctSignal = NULL; - LSSmaster->functSignalObject = NULL; - - /* configure LSS CAN Slave response message reception */ - CO_CANrxBufferInit( - CANdevRx, /* CAN device */ - CANdevRxIdx, /* rx buffer index */ - CANidLssSlave, /* CAN identifier */ - 0x7FF, /* mask */ - 0, /* rtr */ - (void*)LSSmaster, /* object passed to receive function */ - CO_LSSmaster_receive);/* this function will process received message */ - - /* configure LSS CAN Master message transmission */ - LSSmaster->CANdevTx = CANdevTx; - LSSmaster->TXbuff = CO_CANtxBufferInit( - CANdevTx, /* CAN device */ - CANdevTxIdx, /* index of specific buffer inside CAN module */ - CANidLssMaster, /* CAN identifier */ - 0, /* rtr */ - 8, /* number of data bytes */ - 0); /* synchronous message flag bit */ - - return CO_ERROR_NO; -} - - -/******************************************************************************/ -void CO_LSSmaster_changeTimeout( - CO_LSSmaster_t *LSSmaster, - uint16_t timeout_ms) -{ - if (LSSmaster != NULL) { - LSSmaster->timeout = timeout_ms; - } -} - - -/******************************************************************************/ -void CO_LSSmaster_initCallback( - CO_LSSmaster_t *LSSmaster, - void *object, - void (*pFunctSignal)(void *object)) -{ - if(LSSmaster != NULL){ - LSSmaster->functSignalObject = object; - LSSmaster->pFunctSignal = pFunctSignal; - } -} - -/* - * Helper function - initiate switch state - */ -static CO_LSSmaster_return_t CO_LSSmaster_switchStateSelectInitiate( - CO_LSSmaster_t *LSSmaster, - CO_LSS_address_t *lssAddress) -{ - CO_LSSmaster_return_t ret; - - if (lssAddress != NULL) { - /* switch state select specific using LSS address */ - LSSmaster->state = CO_LSSmaster_STATE_CFG_SLECTIVE; - LSSmaster->command = CO_LSSmaster_COMMAND_SWITCH_STATE; - LSSmaster->timeoutTimer = 0; - - CLEAR_CANrxNew(LSSmaster->CANrxNew); - CO_memset(&LSSmaster->TXbuff->data[6], 0, 3); - LSSmaster->TXbuff->data[0] = CO_LSS_SWITCH_STATE_SEL_VENDOR; - CO_setUint32(&LSSmaster->TXbuff->data[1], lssAddress->identity.vendorID); - CO_CANsend(LSSmaster->CANdevTx, LSSmaster->TXbuff); - LSSmaster->TXbuff->data[0] = CO_LSS_SWITCH_STATE_SEL_PRODUCT; - CO_setUint32(&LSSmaster->TXbuff->data[1], lssAddress->identity.productCode); - CO_CANsend(LSSmaster->CANdevTx, LSSmaster->TXbuff); - LSSmaster->TXbuff->data[0] = CO_LSS_SWITCH_STATE_SEL_REV; - CO_setUint32(&LSSmaster->TXbuff->data[1], lssAddress->identity.revisionNumber); - CO_CANsend(LSSmaster->CANdevTx, LSSmaster->TXbuff); - LSSmaster->TXbuff->data[0] = CO_LSS_SWITCH_STATE_SEL_SERIAL; - CO_setUint32(&LSSmaster->TXbuff->data[1], lssAddress->identity.serialNumber); - CO_CANsend(LSSmaster->CANdevTx, LSSmaster->TXbuff); - - ret = CO_LSSmaster_WAIT_SLAVE; - } - else { - /* switch state global */ - LSSmaster->state = CO_LSSmaster_STATE_CFG_GLOBAL; - - CLEAR_CANrxNew(LSSmaster->CANrxNew); - LSSmaster->TXbuff->data[0] = CO_LSS_SWITCH_STATE_GLOBAL; - LSSmaster->TXbuff->data[1] = CO_LSS_STATE_CONFIGURATION; - CO_memset(&LSSmaster->TXbuff->data[2], 0, 6); - CO_CANsend(LSSmaster->CANdevTx, LSSmaster->TXbuff); - - /* This is non-confirmed service! */ - ret = CO_LSSmaster_OK; - } - return ret; -} - -/* - * Helper function - wait for confirmation - */ -static CO_LSSmaster_return_t CO_LSSmaster_switchStateSelectWait( - CO_LSSmaster_t *LSSmaster, - uint16_t timeDifference_ms) -{ - CO_LSSmaster_return_t ret; - - if (IS_CANrxNew(LSSmaster->CANrxNew)) { - uint8_t cs = LSSmaster->CANrxData[0]; - CLEAR_CANrxNew(LSSmaster->CANrxNew); - - if (cs == CO_LSS_SWITCH_STATE_SEL) { - /* confirmation received */ - ret = CO_LSSmaster_OK; - } - else { - ret = CO_LSSmaster_check_timeout(LSSmaster, timeDifference_ms); - } - } - else { - ret = CO_LSSmaster_check_timeout(LSSmaster, timeDifference_ms); - } - - return ret; -} - -/******************************************************************************/ -CO_LSSmaster_return_t CO_LSSmaster_switchStateSelect( - CO_LSSmaster_t *LSSmaster, - uint16_t timeDifference_ms, - CO_LSS_address_t *lssAddress) -{ - CO_LSSmaster_return_t ret = CO_LSSmaster_INVALID_STATE; - - if (LSSmaster == NULL){ - return CO_LSSmaster_ILLEGAL_ARGUMENT; - } - - /* Initiate select */ - if (LSSmaster->state==CO_LSSmaster_STATE_WAITING && - LSSmaster->command==CO_LSSmaster_COMMAND_WAITING){ - - ret = CO_LSSmaster_switchStateSelectInitiate(LSSmaster, lssAddress); - } - /* Wait for confirmation */ - else if (LSSmaster->command == CO_LSSmaster_COMMAND_SWITCH_STATE) { - ret = CO_LSSmaster_switchStateSelectWait(LSSmaster, timeDifference_ms); - } - - if (ret!=CO_LSSmaster_INVALID_STATE && ret!=CO_LSSmaster_WAIT_SLAVE) { - /* finished */ - LSSmaster->command = CO_LSSmaster_COMMAND_WAITING; - } - if (ret < CO_LSSmaster_OK) { - /* switching failed, go back to waiting */ - LSSmaster->state=CO_LSSmaster_STATE_WAITING; - LSSmaster->command = CO_LSSmaster_COMMAND_WAITING; - } - return ret; -} - - -/******************************************************************************/ -CO_LSSmaster_return_t CO_LSSmaster_switchStateDeselect( - CO_LSSmaster_t *LSSmaster) -{ - CO_LSSmaster_return_t ret = CO_LSSmaster_INVALID_STATE; - - if (LSSmaster == NULL){ - return CO_LSSmaster_ILLEGAL_ARGUMENT; - } - - /* We can always send this command to get into a clean state on the network. - * If no slave is selected, this command is ignored. */ - LSSmaster->state = CO_LSSmaster_STATE_WAITING; - LSSmaster->command = CO_LSSmaster_COMMAND_WAITING; - LSSmaster->timeoutTimer = 0; - - /* switch state global */ - CLEAR_CANrxNew(LSSmaster->CANrxNew); - LSSmaster->TXbuff->data[0] = CO_LSS_SWITCH_STATE_GLOBAL; - LSSmaster->TXbuff->data[1] = CO_LSS_STATE_WAITING; - CO_memset(&LSSmaster->TXbuff->data[2], 0, 6); - CO_CANsend(LSSmaster->CANdevTx, LSSmaster->TXbuff); - - /* This is non-confirmed service! */ - ret = CO_LSSmaster_OK; - - return ret; -} - - -/* - * Helper function - wait for confirmation, check for returned error code - * - * This uses the nature of the configure confirmation message design: - * - byte 0 -> cs - * - byte 1 -> Error Code, where - * - 0 = OK - * - 1 .. FE = Values defined by CiA. All currently defined values - * are slave rejects. No further distinction on why the - * slave did reject the request. - * - FF = Manufacturer Error Code in byte 2 - * - byte 2 -> Manufacturer Error, currently not used - * - * enums for the errorCode are - * - CO_LSS_cfgNodeId_t - * - CO_LSS_cfgBitTiming_t - * - CO_LSS_cfgStore_t - */ -static CO_LSSmaster_return_t CO_LSSmaster_configureCheckWait( - CO_LSSmaster_t *LSSmaster, - uint16_t timeDifference_ms, - uint8_t csWait) -{ - CO_LSSmaster_return_t ret; - - if (IS_CANrxNew(LSSmaster->CANrxNew)) { - uint8_t cs = LSSmaster->CANrxData[0]; - uint8_t errorCode = LSSmaster->CANrxData[1]; - CLEAR_CANrxNew(LSSmaster->CANrxNew); - - if (cs == csWait) { - if (errorCode == 0) { - ret = CO_LSSmaster_OK; - } - else if (errorCode == 0xff) { - ret = CO_LSSmaster_OK_MANUFACTURER; - } - else { - ret = CO_LSSmaster_OK_ILLEGAL_ARGUMENT; - } - } - else { - ret = CO_LSSmaster_check_timeout(LSSmaster, timeDifference_ms); - } - } - else { - ret = CO_LSSmaster_check_timeout(LSSmaster, timeDifference_ms); - } - - if (ret!=CO_LSSmaster_INVALID_STATE && ret!=CO_LSSmaster_WAIT_SLAVE) { - /* finished */ - LSSmaster->command = CO_LSSmaster_COMMAND_WAITING; - } - return ret; -} - - -/******************************************************************************/ -CO_LSSmaster_return_t CO_LSSmaster_configureBitTiming( - CO_LSSmaster_t *LSSmaster, - uint16_t timeDifference_ms, - uint16_t bit) -{ - CO_LSSmaster_return_t ret = CO_LSSmaster_INVALID_STATE; - uint8_t bitTiming; - - if (LSSmaster == NULL){ - return CO_LSSmaster_ILLEGAL_ARGUMENT; - } - - switch (bit) { - case 1000: bitTiming = CO_LSS_BIT_TIMING_1000; break; - case 800: bitTiming = CO_LSS_BIT_TIMING_800; break; - case 500: bitTiming = CO_LSS_BIT_TIMING_500; break; - case 250: bitTiming = CO_LSS_BIT_TIMING_250; break; - case 125: bitTiming = CO_LSS_BIT_TIMING_125; break; - case 50: bitTiming = CO_LSS_BIT_TIMING_50; break; - case 20: bitTiming = CO_LSS_BIT_TIMING_20; break; - case 10: bitTiming = CO_LSS_BIT_TIMING_10; break; - case 0: bitTiming = CO_LSS_BIT_TIMING_AUTO; break; - default: return CO_LSSmaster_ILLEGAL_ARGUMENT; - } - - /* Initiate config bit */ - if (LSSmaster->state==CO_LSSmaster_STATE_CFG_SLECTIVE && - LSSmaster->command==CO_LSSmaster_COMMAND_WAITING){ - - LSSmaster->command = CO_LSSmaster_COMMAND_CFG_BIT_TIMING; - LSSmaster->timeoutTimer = 0; - - CLEAR_CANrxNew(LSSmaster->CANrxNew); - LSSmaster->TXbuff->data[0] = CO_LSS_CFG_BIT_TIMING; - LSSmaster->TXbuff->data[1] = 0; - LSSmaster->TXbuff->data[2] = bitTiming; - CO_memset(&LSSmaster->TXbuff->data[3], 0, 5); - CO_CANsend(LSSmaster->CANdevTx, LSSmaster->TXbuff); - - ret = CO_LSSmaster_WAIT_SLAVE; - } - /* Wait for confirmation */ - else if (LSSmaster->command == CO_LSSmaster_COMMAND_CFG_BIT_TIMING) { - - ret = CO_LSSmaster_configureCheckWait(LSSmaster, timeDifference_ms, - CO_LSS_CFG_BIT_TIMING); - } - - if (ret!=CO_LSSmaster_INVALID_STATE && ret!=CO_LSSmaster_WAIT_SLAVE) { - /* finished */ - LSSmaster->command = CO_LSSmaster_COMMAND_WAITING; - } - return ret; -} - - -/******************************************************************************/ -CO_LSSmaster_return_t CO_LSSmaster_configureNodeId( - CO_LSSmaster_t *LSSmaster, - uint16_t timeDifference_ms, - uint8_t nodeId) -{ - CO_LSSmaster_return_t ret = CO_LSSmaster_INVALID_STATE; - - if (LSSmaster==NULL || !CO_LSS_NODE_ID_VALID(nodeId)){ - return CO_LSSmaster_ILLEGAL_ARGUMENT; - } - - /* Initiate config node ID */ - if ((LSSmaster->state==CO_LSSmaster_STATE_CFG_SLECTIVE || - /* Let un-config node ID also be run in global mode for unconfiguring all nodes */ - (LSSmaster->state==CO_LSSmaster_STATE_CFG_GLOBAL && - nodeId == CO_LSS_NODE_ID_ASSIGNMENT)) && - LSSmaster->command==CO_LSSmaster_COMMAND_WAITING) { - - LSSmaster->command = CO_LSSmaster_COMMAND_CFG_NODE_ID; - LSSmaster->timeoutTimer = 0; - - CLEAR_CANrxNew(LSSmaster->CANrxNew); - LSSmaster->TXbuff->data[0] = CO_LSS_CFG_NODE_ID; - LSSmaster->TXbuff->data[1] = nodeId; - CO_memset(&LSSmaster->TXbuff->data[2], 0, 6); - CO_CANsend(LSSmaster->CANdevTx, LSSmaster->TXbuff); - - ret = CO_LSSmaster_WAIT_SLAVE; - } - /* Wait for confirmation */ - else if (LSSmaster->command == CO_LSSmaster_COMMAND_CFG_NODE_ID) { - - ret = CO_LSSmaster_configureCheckWait(LSSmaster, timeDifference_ms, - CO_LSS_CFG_NODE_ID); - } - - if (ret!=CO_LSSmaster_INVALID_STATE && ret!=CO_LSSmaster_WAIT_SLAVE) { - /* finished */ - LSSmaster->command = CO_LSSmaster_COMMAND_WAITING; - } - return ret; -} - - -/******************************************************************************/ -CO_LSSmaster_return_t CO_LSSmaster_configureStore( - CO_LSSmaster_t *LSSmaster, - uint16_t timeDifference_ms) -{ - CO_LSSmaster_return_t ret = CO_LSSmaster_INVALID_STATE; - - if (LSSmaster == NULL){ - return CO_LSSmaster_ILLEGAL_ARGUMENT; - } - - /* Initiate config store */ - if (LSSmaster->state==CO_LSSmaster_STATE_CFG_SLECTIVE && - LSSmaster->command==CO_LSSmaster_COMMAND_WAITING){ - - LSSmaster->command = CO_LSSmaster_COMMAND_CFG_STORE; - LSSmaster->timeoutTimer = 0; - - CLEAR_CANrxNew(LSSmaster->CANrxNew); - LSSmaster->TXbuff->data[0] = CO_LSS_CFG_STORE; - CO_memset(&LSSmaster->TXbuff->data[1], 0, 7); - CO_CANsend(LSSmaster->CANdevTx, LSSmaster->TXbuff); - - ret = CO_LSSmaster_WAIT_SLAVE; - } - /* Wait for confirmation */ - else if (LSSmaster->command == CO_LSSmaster_COMMAND_CFG_STORE) { - - ret = CO_LSSmaster_configureCheckWait(LSSmaster, timeDifference_ms, - CO_LSS_CFG_STORE); - } - - if (ret!=CO_LSSmaster_INVALID_STATE && ret!=CO_LSSmaster_WAIT_SLAVE) { - /* finished */ - LSSmaster->command = CO_LSSmaster_COMMAND_WAITING; - } - return ret; -} - - -/******************************************************************************/ -CO_LSSmaster_return_t CO_LSSmaster_ActivateBit( - CO_LSSmaster_t *LSSmaster, - uint16_t switchDelay_ms) -{ - CO_LSSmaster_return_t ret = CO_LSSmaster_INVALID_STATE; - - if (LSSmaster == NULL){ - return CO_LSSmaster_ILLEGAL_ARGUMENT; - } - - /* for activating bit timing, we need to have all slaves set to config - * state. This check makes it a bit harder to shoot ourselves in the foot */ - if (LSSmaster->state==CO_LSSmaster_STATE_CFG_GLOBAL && - LSSmaster->command==CO_LSSmaster_COMMAND_WAITING){ - - CLEAR_CANrxNew(LSSmaster->CANrxNew); - LSSmaster->TXbuff->data[0] = CO_LSS_CFG_ACTIVATE_BIT_TIMING; - CO_setUint16(&LSSmaster->TXbuff->data[1], switchDelay_ms); - CO_memset(&LSSmaster->TXbuff->data[3], 0, 5); - CO_CANsend(LSSmaster->CANdevTx, LSSmaster->TXbuff); - - /* This is non-confirmed service! */ - ret = CO_LSSmaster_OK; - } - - return ret; -} - -/* - * Helper function - send request - */ -static CO_LSSmaster_return_t CO_LSSmaster_inquireInitiate( - CO_LSSmaster_t *LSSmaster, - uint8_t cs) -{ - CLEAR_CANrxNew(LSSmaster->CANrxNew); - LSSmaster->TXbuff->data[0] = cs; - CO_memset(&LSSmaster->TXbuff->data[1], 0, 7); - CO_CANsend(LSSmaster->CANdevTx, LSSmaster->TXbuff); - - return CO_LSSmaster_WAIT_SLAVE; -} - -/* - * Helper function - wait for confirmation - */ -static CO_LSSmaster_return_t CO_LSSmaster_inquireCheckWait( - CO_LSSmaster_t *LSSmaster, - uint16_t timeDifference_ms, - uint8_t csWait, - uint32_t *value) -{ - CO_LSSmaster_return_t ret; - - if (IS_CANrxNew(LSSmaster->CANrxNew)) { - uint8_t cs = LSSmaster->CANrxData[0]; - *value = CO_getUint32(&LSSmaster->CANrxData[1]); - CLEAR_CANrxNew(LSSmaster->CANrxNew); - - if (cs == csWait) { - ret = CO_LSSmaster_OK; - } - else { - ret = CO_LSSmaster_check_timeout(LSSmaster, timeDifference_ms); - } - } - else { - ret = CO_LSSmaster_check_timeout(LSSmaster, timeDifference_ms); - } - - return ret; -} - -/******************************************************************************/ -CO_LSSmaster_return_t CO_LSSmaster_InquireLssAddress( - CO_LSSmaster_t *LSSmaster, - uint16_t timeDifference_ms, - CO_LSS_address_t *lssAddress) -{ - CO_LSSmaster_return_t ret = CO_LSSmaster_INVALID_STATE; - CO_LSSmaster_command_t next = CO_LSSmaster_COMMAND_WAITING; - - if (LSSmaster==NULL || lssAddress==NULL){ - return CO_LSSmaster_ILLEGAL_ARGUMENT; - } - - /* Check for reply */ - if (LSSmaster->command == CO_LSSmaster_COMMAND_INQUIRE_VENDOR) { - - ret = CO_LSSmaster_inquireCheckWait(LSSmaster, timeDifference_ms, - CO_LSS_INQUIRE_VENDOR, &lssAddress->identity.vendorID); - if (ret == CO_LSSmaster_OK) { - /* Start next request */ - next = CO_LSSmaster_COMMAND_INQUIRE_PRODUCT; - ret = CO_LSSmaster_WAIT_SLAVE; - } - } - else if (LSSmaster->command == CO_LSSmaster_COMMAND_INQUIRE_PRODUCT) { - - ret = CO_LSSmaster_inquireCheckWait(LSSmaster, timeDifference_ms, - CO_LSS_INQUIRE_PRODUCT, &lssAddress->identity.productCode); - if (ret == CO_LSSmaster_OK) { - /* Start next request */ - next = CO_LSSmaster_COMMAND_INQUIRE_REV; - ret = CO_LSSmaster_WAIT_SLAVE; - } - } - else if (LSSmaster->command == CO_LSSmaster_COMMAND_INQUIRE_REV) { - - ret = CO_LSSmaster_inquireCheckWait(LSSmaster, timeDifference_ms, - CO_LSS_INQUIRE_REV, &lssAddress->identity.revisionNumber); - if (ret == CO_LSSmaster_OK) { - /* Start next request */ - next = CO_LSSmaster_COMMAND_INQUIRE_SERIAL; - ret = CO_LSSmaster_WAIT_SLAVE; - } - } - else if (LSSmaster->command == CO_LSSmaster_COMMAND_INQUIRE_SERIAL) { - - ret = CO_LSSmaster_inquireCheckWait(LSSmaster, timeDifference_ms, - CO_LSS_INQUIRE_SERIAL, &lssAddress->identity.serialNumber); - } - /* Check for next request */ - if (LSSmaster->state == CO_LSSmaster_STATE_CFG_SLECTIVE || - LSSmaster->state == CO_LSSmaster_STATE_CFG_GLOBAL) { - if (LSSmaster->command == CO_LSSmaster_COMMAND_WAITING) { - - LSSmaster->command = CO_LSSmaster_COMMAND_INQUIRE_VENDOR; - LSSmaster->timeoutTimer = 0; - - ret = CO_LSSmaster_inquireInitiate(LSSmaster, CO_LSS_INQUIRE_VENDOR); - } - else if (next == CO_LSSmaster_COMMAND_INQUIRE_PRODUCT) { - LSSmaster->command = CO_LSSmaster_COMMAND_INQUIRE_PRODUCT; - LSSmaster->timeoutTimer = 0; - - ret = CO_LSSmaster_inquireInitiate(LSSmaster, CO_LSS_INQUIRE_PRODUCT); - } - else if (next == CO_LSSmaster_COMMAND_INQUIRE_REV) { - LSSmaster->command = CO_LSSmaster_COMMAND_INQUIRE_REV; - LSSmaster->timeoutTimer = 0; - - ret = CO_LSSmaster_inquireInitiate(LSSmaster, CO_LSS_INQUIRE_REV); - } - else if (next == CO_LSSmaster_COMMAND_INQUIRE_SERIAL) { - LSSmaster->command = CO_LSSmaster_COMMAND_INQUIRE_SERIAL; - LSSmaster->timeoutTimer = 0; - - ret = CO_LSSmaster_inquireInitiate(LSSmaster, CO_LSS_INQUIRE_SERIAL); - } - } - - if (ret!=CO_LSSmaster_INVALID_STATE && ret!=CO_LSSmaster_WAIT_SLAVE) { - /* finished */ - LSSmaster->command = CO_LSSmaster_COMMAND_WAITING; - } - return ret; -} - - -/******************************************************************************/ -CO_LSSmaster_return_t CO_LSSmaster_InquireNodeId( - CO_LSSmaster_t *LSSmaster, - uint16_t timeDifference_ms, - uint8_t *nodeId) -{ - CO_LSSmaster_return_t ret = CO_LSSmaster_INVALID_STATE; - - if (LSSmaster==NULL || nodeId==NULL){ - return CO_LSSmaster_ILLEGAL_ARGUMENT; - } - - /* send request */ - if ((LSSmaster->state==CO_LSSmaster_STATE_CFG_SLECTIVE || - LSSmaster->state==CO_LSSmaster_STATE_CFG_GLOBAL) && - LSSmaster->command == CO_LSSmaster_COMMAND_WAITING) { - - LSSmaster->command = CO_LSSmaster_COMMAND_INQUIRE_NODE_ID; - LSSmaster->timeoutTimer = 0; - - ret = CO_LSSmaster_inquireInitiate(LSSmaster, CO_LSS_INQUIRE_NODE_ID); - } - /* Check for reply */ - else if (LSSmaster->command == CO_LSSmaster_COMMAND_INQUIRE_NODE_ID) { - uint32_t tmp = 0; - - ret = CO_LSSmaster_inquireCheckWait(LSSmaster, timeDifference_ms, - CO_LSS_INQUIRE_NODE_ID, &tmp); - - *nodeId = tmp & 0xff; - } - - if (ret != CO_LSSmaster_WAIT_SLAVE) { - LSSmaster->command = CO_LSSmaster_COMMAND_WAITING; - } - return ret; -} - -/* - * Helper function - send request - */ -static void CO_LSSmaster_FsSendMsg( - CO_LSSmaster_t *LSSmaster, - uint32_t idNumber, - uint8_t bitCheck, - uint8_t lssSub, - uint8_t lssNext) -{ - LSSmaster->timeoutTimer = 0; - - CLEAR_CANrxNew(LSSmaster->CANrxNew); - LSSmaster->TXbuff->data[0] = CO_LSS_IDENT_FASTSCAN; - CO_setUint32(&LSSmaster->TXbuff->data[1], idNumber); - LSSmaster->TXbuff->data[5] = bitCheck; - LSSmaster->TXbuff->data[6] = lssSub; - LSSmaster->TXbuff->data[7] = lssNext; - - CO_CANsend(LSSmaster->CANdevTx, LSSmaster->TXbuff); -} - -/* - * Helper function - wait for confirmation - */ -static CO_LSSmaster_return_t CO_LSSmaster_FsCheckWait( - CO_LSSmaster_t *LSSmaster, - uint16_t timeDifference_ms) -{ - CO_LSSmaster_return_t ret; - - ret = CO_LSSmaster_check_timeout(LSSmaster, timeDifference_ms); - if (ret == CO_LSSmaster_TIMEOUT) { - ret = CO_LSSmaster_SCAN_NOACK; - - if (IS_CANrxNew(LSSmaster->CANrxNew)) { - uint8_t cs = LSSmaster->CANrxData[0]; - CLEAR_CANrxNew(LSSmaster->CANrxNew); - - if (cs == CO_LSS_IDENT_SLAVE) { - /* At least one node is waiting for fastscan */ - ret = CO_LSSmaster_SCAN_FINISHED; - } - } - } - - return ret; -} - -/* - * Helper function - initiate scan for 32 bit part of LSS address - */ -static CO_LSSmaster_return_t CO_LSSmaster_FsScanInitiate( - CO_LSSmaster_t *LSSmaster, - uint16_t timeDifference_ms, - CO_LSSmaster_scantype_t scan, - CO_LSS_fastscan_lss_sub_next lssSub) -{ - LSSmaster->fsLssSub = lssSub; - LSSmaster->fsIdNumber = 0; - - switch (scan) { - case CO_LSSmaster_FS_SCAN: - break; - case CO_LSSmaster_FS_MATCH: - /* No scanning requested */ - return CO_LSSmaster_SCAN_FINISHED; - case CO_LSSmaster_FS_SKIP: - default: - return CO_LSSmaster_SCAN_FAILED; - } - - LSSmaster->fsBitChecked = CO_LSS_FASTSCAN_BIT31; - - /* trigger scan procedure by sending first message */ - CO_LSSmaster_FsSendMsg(LSSmaster, LSSmaster->fsIdNumber, - LSSmaster->fsBitChecked, LSSmaster->fsLssSub, LSSmaster->fsLssSub); - - return CO_LSSmaster_WAIT_SLAVE; -} - -/* - * Helper function - scan for 32 bits of LSS address, one by one - */ -static CO_LSSmaster_return_t CO_LSSmaster_FsScanWait( - CO_LSSmaster_t *LSSmaster, - uint16_t timeDifference_ms, - CO_LSSmaster_scantype_t scan) -{ - CO_LSSmaster_return_t ret; - - switch (scan) { - case CO_LSSmaster_FS_SCAN: - break; - case CO_LSSmaster_FS_MATCH: - /* No scanning requested */ - return CO_LSSmaster_SCAN_FINISHED; - case CO_LSSmaster_FS_SKIP: - default: - return CO_LSSmaster_SCAN_FAILED; - } - - ret = CO_LSSmaster_check_timeout(LSSmaster, timeDifference_ms); - if (ret == CO_LSSmaster_TIMEOUT) { - - ret = CO_LSSmaster_WAIT_SLAVE; - - if (IS_CANrxNew(LSSmaster->CANrxNew)) { - uint8_t cs = LSSmaster->CANrxData[0]; - CLEAR_CANrxNew(LSSmaster->CANrxNew); - - if (cs != CO_LSS_IDENT_SLAVE) { - /* wrong response received. Can not continue */ - return CO_LSSmaster_SCAN_FAILED; - } - } - else { - /* no response received, assumption is wrong */ - LSSmaster->fsIdNumber |= 1UL << LSSmaster->fsBitChecked; - } - - if (LSSmaster->fsBitChecked == CO_LSS_FASTSCAN_BIT0) { - /* Scanning cycle is finished, we now have 32 bit address data */ - ret = CO_LSSmaster_SCAN_FINISHED; - } - else { - LSSmaster->fsBitChecked --; - - CO_LSSmaster_FsSendMsg(LSSmaster, - LSSmaster->fsIdNumber, LSSmaster->fsBitChecked, - LSSmaster->fsLssSub, LSSmaster->fsLssSub); - } - } - - return ret; -} - -/* - * Helper function - initiate check for 32 bit part of LSS address - */ -static CO_LSSmaster_return_t CO_LSSmaster_FsVerifyInitiate( - CO_LSSmaster_t *LSSmaster, - uint16_t timeDifference_ms, - CO_LSSmaster_scantype_t scan, - uint32_t idNumberCheck, - CO_LSS_fastscan_lss_sub_next lssNext) -{ - switch (scan) { - case CO_LSSmaster_FS_SCAN: - /* ID obtained by scan */ - break; - case CO_LSSmaster_FS_MATCH: - /* ID given by user */ - LSSmaster->fsIdNumber = idNumberCheck; - break; - case CO_LSSmaster_FS_SKIP: - default: - return CO_LSSmaster_SCAN_FAILED; - } - - LSSmaster->fsBitChecked = CO_LSS_FASTSCAN_BIT0; - - /* send request */ - CO_LSSmaster_FsSendMsg(LSSmaster, LSSmaster->fsIdNumber, - LSSmaster->fsBitChecked, LSSmaster->fsLssSub, lssNext); - - return CO_LSSmaster_WAIT_SLAVE; -} - -/* - * Helper function - verify 32 bit LSS address, request node(s) to switch - * their state machine to the next state - */ -static CO_LSSmaster_return_t CO_LSSmaster_FsVerifyWait( - CO_LSSmaster_t *LSSmaster, - uint16_t timeDifference_ms, - CO_LSSmaster_scantype_t scan, - uint32_t *idNumberRet) -{ - CO_LSSmaster_return_t ret; - - if (scan == CO_LSSmaster_FS_SKIP) { - return CO_LSSmaster_SCAN_FAILED; - } - - ret = CO_LSSmaster_check_timeout(LSSmaster, timeDifference_ms); - if (ret == CO_LSSmaster_TIMEOUT) { - - *idNumberRet = 0; - ret = CO_LSSmaster_SCAN_NOACK; - - if (IS_CANrxNew(LSSmaster->CANrxNew)) { - uint8_t cs = LSSmaster->CANrxData[0]; - CLEAR_CANrxNew(LSSmaster->CANrxNew); - - if (cs == CO_LSS_IDENT_SLAVE) { - *idNumberRet = LSSmaster->fsIdNumber; - ret = CO_LSSmaster_SCAN_FINISHED; - } else { - ret = CO_LSSmaster_SCAN_FAILED; - } - } - } - - return ret; -} - -/* - * Helper function - check which 32 bit to scan for next, if any - */ -static CO_LSS_fastscan_lss_sub_next CO_LSSmaster_FsSearchNext( - CO_LSSmaster_t *LSSmaster, - const CO_LSSmaster_fastscan_t *fastscan) -{ - int i; - - /* we search for the next LSS address part to scan for, beginning with the - * one after the current one. If there is none remaining, scanning is - * finished */ - for (i = LSSmaster->fsLssSub + 1; i <= CO_LSS_FASTSCAN_SERIAL; i++) { - if (fastscan->scan[i] != CO_LSSmaster_FS_SKIP) { - return (CO_LSS_fastscan_lss_sub_next)i; - } - } - /* node selection is triggered by switching node state machine back - * to initial state */ - return CO_LSS_FASTSCAN_VENDOR_ID; -} - -/******************************************************************************/ -CO_LSSmaster_return_t CO_LSSmaster_IdentifyFastscan( - CO_LSSmaster_t *LSSmaster, - uint16_t timeDifference_ms, - CO_LSSmaster_fastscan_t *fastscan) -{ - uint8_t i; - uint8_t count; - CO_LSSmaster_return_t ret = CO_LSSmaster_INVALID_STATE; - CO_LSS_fastscan_lss_sub_next next; - - /* parameter validation */ - if (LSSmaster==NULL || fastscan==NULL){ - return CO_LSSmaster_ILLEGAL_ARGUMENT; - } - if (fastscan->scan[0] == CO_LSSmaster_FS_SKIP) { - /* vendor ID scan cannot be skipped */ - return CO_LSSmaster_ILLEGAL_ARGUMENT; - } - count = 0; - for (i = 0; i < (sizeof(fastscan->scan) / sizeof(fastscan->scan[0])); i++) { - if (fastscan->scan[i] == CO_LSSmaster_FS_SKIP) { - count ++; - } - if (count > 2) { - /* Node selection needs the Vendor ID and at least one other value */ - return CO_LSSmaster_ILLEGAL_ARGUMENT; - } - } - - /* state machine validation */ - if (LSSmaster->state!=CO_LSSmaster_STATE_WAITING || - (LSSmaster->command!=CO_LSSmaster_COMMAND_WAITING && - LSSmaster->command!=CO_LSSmaster_COMMAND_IDENTIFY_FASTSCAN)) { - /* state machine not ready, other command is already processed */ - return CO_LSSmaster_INVALID_STATE; - } - - /* evaluate LSS state machine */ - switch (LSSmaster->command) { - case CO_LSSmaster_COMMAND_WAITING: - /* start fastscan */ - LSSmaster->command = CO_LSSmaster_COMMAND_IDENTIFY_FASTSCAN; - - /* check if any nodes are waiting, if yes fastscan is reset */ - LSSmaster->fsState = CO_LSSmaster_FS_STATE_CHECK; - CO_LSSmaster_FsSendMsg(LSSmaster, 0, CO_LSS_FASTSCAN_CONFIRM, 0, 0); - - return CO_LSSmaster_WAIT_SLAVE; - default: - /* continue with evaluating fastscan state machine */ - break; - } - - /* evaluate fastscan state machine. The state machine is evaluated as following - * - check for non-configured nodes - * - scan for vendor ID - * - verify vendor ID, switch node state - * - scan for product code - * - verify product code, switch node state - * - scan for revision number - * - verify revision number, switch node state - * - scan for serial number - * - verify serial number, switch node to LSS configuration mode - * Certain steps can be skipped as mentioned in the function description. - * If one step is not ack'ed by a node, the scanning process is terminated - * and the correspondign error is returned. */ - switch (LSSmaster->fsState) { - case CO_LSSmaster_FS_STATE_CHECK: - ret = CO_LSSmaster_FsCheckWait(LSSmaster, timeDifference_ms); - if (ret == CO_LSSmaster_SCAN_FINISHED) { - CO_memset((uint8_t*)&fastscan->found, 0, sizeof(fastscan->found)); - - /* start scanning procedure by triggering vendor ID scan */ - CO_LSSmaster_FsScanInitiate(LSSmaster, timeDifference_ms, - fastscan->scan[CO_LSS_FASTSCAN_VENDOR_ID], - CO_LSS_FASTSCAN_VENDOR_ID); - ret = CO_LSSmaster_WAIT_SLAVE; - - LSSmaster->fsState = CO_LSSmaster_FS_STATE_SCAN; - } - break; - case CO_LSSmaster_FS_STATE_SCAN: - ret = CO_LSSmaster_FsScanWait(LSSmaster, timeDifference_ms, - fastscan->scan[LSSmaster->fsLssSub]); - if (ret == CO_LSSmaster_SCAN_FINISHED) { - /* scanning finished, initiate verifcation. The verification - * message also contains the node state machine "switch to - * next state" request */ - next = CO_LSSmaster_FsSearchNext(LSSmaster, fastscan); - ret = CO_LSSmaster_FsVerifyInitiate(LSSmaster, timeDifference_ms, - fastscan->scan[LSSmaster->fsLssSub], - fastscan->match.addr[LSSmaster->fsLssSub], next); - - LSSmaster->fsState = CO_LSSmaster_FS_STATE_VERIFY; - } - break; - case CO_LSSmaster_FS_STATE_VERIFY: - ret = CO_LSSmaster_FsVerifyWait(LSSmaster, timeDifference_ms, - fastscan->scan[LSSmaster->fsLssSub], - &fastscan->found.addr[LSSmaster->fsLssSub]); - if (ret == CO_LSSmaster_SCAN_FINISHED) { - /* verification successful: - * - assumed node id is correct - * - node state machine has switched to the requested state, - * mirror that in the local copy */ - next = CO_LSSmaster_FsSearchNext(LSSmaster, fastscan); - if (next == CO_LSS_FASTSCAN_VENDOR_ID) { - /* fastscan finished, one node is now in LSS configuration - * mode */ - LSSmaster->state = CO_LSSmaster_STATE_CFG_SLECTIVE; - } - else { - /* initiate scan for next part of LSS address */ - ret = CO_LSSmaster_FsScanInitiate(LSSmaster, - timeDifference_ms, fastscan->scan[next], next); - if (ret == CO_LSSmaster_SCAN_FINISHED) { - /* Scanning is not requested. Initiate verification - * step in next function call */ - ret = CO_LSSmaster_WAIT_SLAVE; - } - - LSSmaster->fsState = CO_LSSmaster_FS_STATE_SCAN; - } - } - break; - default: - break; - } - - if (ret != CO_LSSmaster_WAIT_SLAVE) { - /* finished */ - LSSmaster->command = CO_LSSmaster_COMMAND_WAITING; - } - return ret; -} - - -#endif diff --git a/stack/CO_LSSmaster.h b/stack/CO_LSSmaster.h deleted file mode 100644 index 69b0d319..00000000 --- a/stack/CO_LSSmaster.h +++ /dev/null @@ -1,484 +0,0 @@ -/** - * CANopen LSS Master/Slave protocol. - * - * @file CO_LSSmaster.h - * @ingroup CO_LSS - * @author Martin Wagner - * @copyright 2017 - 2020 Neuberger Gebaeudeautomation GmbH - * - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#ifndef CO_LSSmaster_H -#define CO_LSSmaster_H - -#ifdef __cplusplus -extern "C" { -#endif - -#if CO_NO_LSS_CLIENT == 1 - -#include "CO_LSS.h" - -/** - * @addtogroup CO_LSS - * @defgroup CO_LSSmaster LSS Master - * @ingroup CO_LSS - * @{ - * - * CANopen Layer Setting Service - client protocol - * - * The client/master can use the following services - * - node selection via LSS address - * - node selection via LSS fastscan - * - Inquire LSS address of currently selected node - * - Inquire node ID - * - Configure bit timing - * - Configure node ID - * - Activate bit timing parameters - * - Store configuration - * - * The LSS master is initalized during the CANopenNode initialization process. - * Except for enabling the LSS master in the configurator, no further - * run-time configuration is needed for basic operation. - * The LSS master does basic checking of commands and command sequence. - * - * ###Usage - * - * Usage of the CANopen LSS master is demonstrated in CANopenSocket application, - * see CO_LSS_master.c / CO_LSS_master.h files. - * - * It essentially is always as following: - * - select node(s) - * - call master command(s) - * - evaluate return value - * - deselect nodes - * - * All commands need to be run cyclically, e.g. like this - * \code{.c} - - interval = 0; - do { - ret = CO_LSSmaster_InquireNodeId(LSSmaster, interval, &outval); - - interval = 1; ms - sleep(interval); - } while (ret == CO_LSSmaster_WAIT_SLAVE); - - * \endcode - * - * A more advanced implementation can make use of the callback function to - * shorten waiting times. - */ - -/** - * Return values of LSS master functions. - */ -typedef enum { - CO_LSSmaster_SCAN_FINISHED = 2, /**< Scanning finished successful */ - CO_LSSmaster_WAIT_SLAVE = 1, /**< No response arrived from server yet */ - CO_LSSmaster_OK = 0, /**< Success, end of communication */ - CO_LSSmaster_TIMEOUT = -1, /**< No reply received */ - CO_LSSmaster_ILLEGAL_ARGUMENT = -2, /**< Invalid argument */ - CO_LSSmaster_INVALID_STATE = -3, /**< State machine not ready or already processing a request */ - CO_LSSmaster_SCAN_NOACK = -4, /**< No node found that matches scan request */ - CO_LSSmaster_SCAN_FAILED = -5, /**< An error occurred while scanning. Try again */ - CO_LSSmaster_OK_ILLEGAL_ARGUMENT = -101, /**< LSS success, node rejected argument because of non-supported value */ - CO_LSSmaster_OK_MANUFACTURER = -102, /**< LSS success, node rejected argument with manufacturer error code */ -} CO_LSSmaster_return_t; - - -/** - * LSS master object. - */ -typedef struct{ - uint16_t timeout; /**< LSS response timeout in ms */ - - uint8_t state; /**< Node is currently selected */ - uint8_t command; /**< Active command */ - uint16_t timeoutTimer; /**< Timeout timer for LSS communication */ - - uint8_t fsState; /**< Current state of fastscan master state machine */ - uint8_t fsLssSub; /**< Current state of node state machine */ - uint8_t fsBitChecked; /**< Current scan bit position */ - uint32_t fsIdNumber; /**< Current scan result */ - - volatile void *CANrxNew; /**< Indication if new LSS message is received from CAN bus. It needs to be cleared when received message is completely processed. */ - uint8_t CANrxData[8]; /**< 8 data bytes of the received message */ - - void (*pFunctSignal)(void *object); /**< From CO_LSSmaster_initCallback() or NULL */ - void *functSignalObject;/**< Pointer to object */ - - CO_CANmodule_t *CANdevTx; /**< From #CO_LSSslave_init() */ - CO_CANtx_t *TXbuff; /**< CAN transmit buffer */ -}CO_LSSmaster_t; - - -/** - * Default timeout for LSS slave in ms. This is the same as for SDO. For more - * info about LSS timeout see #CO_LSSmaster_changeTimeout() - */ -#define CO_LSSmaster_DEFAULT_TIMEOUT 1000U /* ms */ - - -/** - * Initialize LSS object. - * - * Function must be called in the communication reset section. - * - * @param LSSmaster This object will be initialized. - * @param timeout_ms slave response timeout in ms, for more detail see - * #CO_LSSmaster_changeTimeout() - * @param CANdevRx CAN device for LSS master reception. - * @param CANdevRxIdx Index of receive buffer in the above CAN device. - * @param CANidLssSlave COB ID for reception. - * @param CANdevTx CAN device for LSS master transmission. - * @param CANdevTxIdx Index of transmit buffer in the above CAN device. - * @param CANidLssMaster COB ID for transmission. - * @return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT. - */ -CO_ReturnError_t CO_LSSmaster_init( - CO_LSSmaster_t *LSSmaster, - uint16_t timeout_ms, - CO_CANmodule_t *CANdevRx, - uint16_t CANdevRxIdx, - uint32_t CANidLssSlave, - CO_CANmodule_t *CANdevTx, - uint16_t CANdevTxIdx, - uint32_t CANidLssMaster); - -/** - * Change LSS master timeout - * - * On LSS, a "negative ack" is signaled by the slave not answering. Because of - * that, a low timeout value can significantly increase protocol speed in some - * cases (e.g. fastscan). However, as soon as there is activity on the bus, - * LSS messages can be delayed because of their low CAN network priority (see - * #CO_Default_CAN_ID_t). - * - * @remark Be aware that a "late response" will seriously mess up LSS, so this - * value must be selected "as high as necessary and as low as possible". CiA does - * neither specify nor recommend a value. - * - * @remark This timeout is per-transfer. If a command internally needs multiple - * transfers to complete, this timeout is applied on each transfer. - * - * @param LSSmaster This object. - * @param timeout_ms timeout value in ms - */ -void CO_LSSmaster_changeTimeout( - CO_LSSmaster_t *LSSmaster, - uint16_t timeout_ms); - - -/** - * Initialize LSSserverRx callback function. - * - * Function initializes optional callback function, which is called after new - * message is received from the CAN bus. Function may wake up external task, - * which processes mainline CANopen functions. - * - * @param LSSmaster This object. - * @param object Pointer to object, which will be passed to pFunctSignal(). Can be NULL - * @param pFunctSignal Pointer to the callback function. Not called if NULL. - */ -void CO_LSSmaster_initCallback( - CO_LSSmaster_t *LSSmaster, - void *object, - void (*pFunctSignal)(void *object)); - - -/** - * Request LSS switch state select - * - * This function can select one specific or all nodes. - * - * Function must be called cyclically until it returns != #CO_LSSmaster_WAIT_SLAVE - * Function is non-blocking. - * - * @remark Only one selection can be active at any time. - * - * @param LSSmaster This object. - * @param timeDifference_ms Time difference from previous function call in - * [milliseconds]. Zero when request is started. - * @param lssAddress LSS target address. If NULL, all nodes are selected - * @return #CO_LSSmaster_ILLEGAL_ARGUMENT, #CO_LSSmaster_INVALID_STATE, - * #CO_LSSmaster_WAIT_SLAVE, #CO_LSSmaster_OK, #CO_LSSmaster_TIMEOUT - */ -CO_LSSmaster_return_t CO_LSSmaster_switchStateSelect( - CO_LSSmaster_t *LSSmaster, - uint16_t timeDifference_ms, - CO_LSS_address_t *lssAddress); - - -/** - * Request LSS switch state deselect - * - * This function deselects all nodes, so it doesn't matter if a specific - * node is selected. - * - * This function also resets the LSS master state machine to a clean state - * - * @param LSSmaster This object. - * @return #CO_LSSmaster_ILLEGAL_ARGUMENT, #CO_LSSmaster_INVALID_STATE, - * #CO_LSSmaster_OK - */ -CO_LSSmaster_return_t CO_LSSmaster_switchStateDeselect( - CO_LSSmaster_t *LSSmaster); - - -/** - * Request LSS configure Bit Timing - * - * The new bit rate is set as new pending value. - * - * This function needs one specific node to be selected. - * - * Function must be called cyclically until it returns != #CO_LSSmaster_WAIT_SLAVE. - * Function is non-blocking. - * - * @param LSSmaster This object. - * @param timeDifference_ms Time difference from previous function call in - * [milliseconds]. Zero when request is started. - * @param bit new bit rate - * @return #CO_LSSmaster_ILLEGAL_ARGUMENT, #CO_LSSmaster_INVALID_STATE, - * #CO_LSSmaster_WAIT_SLAVE, #CO_LSSmaster_OK, #CO_LSSmaster_TIMEOUT, - * #CO_LSSmaster_OK_MANUFACTURER, #CO_LSSmaster_OK_ILLEGAL_ARGUMENT - */ -CO_LSSmaster_return_t CO_LSSmaster_configureBitTiming( - CO_LSSmaster_t *LSSmaster, - uint16_t timeDifference_ms, - uint16_t bit); - - -/** - * Request LSS configure node ID - * - * The new node id is set as new pending node ID. - * - * This function needs one specific node to be selected. - * - * Function must be called cyclically until it returns != #CO_LSSmaster_WAIT_SLAVE. - * Function is non-blocking. - * - * @param LSSmaster This object. - * @param timeDifference_ms Time difference from previous function call in - * [milliseconds]. Zero when request is started. - * @param nodeId new node ID. Special value #CO_LSS_NODE_ID_ASSIGNMENT can be - * used to invalidate node ID. - * @return #CO_LSSmaster_ILLEGAL_ARGUMENT, #CO_LSSmaster_INVALID_STATE, - * #CO_LSSmaster_WAIT_SLAVE, #CO_LSSmaster_OK, #CO_LSSmaster_TIMEOUT, - * #CO_LSSmaster_OK_MANUFACTURER, #CO_LSSmaster_OK_ILLEGAL_ARGUMENT - */ -CO_LSSmaster_return_t CO_LSSmaster_configureNodeId( - CO_LSSmaster_t *LSSmaster, - uint16_t timeDifference_ms, - uint8_t nodeId); - - -/** - * Request LSS store configuration - * - * The current "pending" values for bit rate and node ID in LSS slave are - * stored as "permanent" values. - * - * This function needs one specific node to be selected. - * - * Function must be called cyclically until it returns != #CO_LSSmaster_WAIT_SLAVE. - * Function is non-blocking. - * - * @param LSSmaster This object. - * @param timeDifference_ms Time difference from previous function call in - * [milliseconds]. Zero when request is started. - * @return #CO_LSSmaster_ILLEGAL_ARGUMENT, #CO_LSSmaster_INVALID_STATE, - * #CO_LSSmaster_WAIT_SLAVE, #CO_LSSmaster_OK, #CO_LSSmaster_TIMEOUT, - * #CO_LSSmaster_OK_MANUFACTURER, #CO_LSSmaster_OK_ILLEGAL_ARGUMENT - */ -CO_LSSmaster_return_t CO_LSSmaster_configureStore( - CO_LSSmaster_t *LSSmaster, - uint16_t timeDifference_ms); - - -/** - * Request LSS activate bit timing - * - * The current "pending" bit rate in LSS slave is applied. - * - * Be aware that changing the bit rate is a critical step for the network. A - * failure will render the network unusable! Therefore, this function only - * should be called if the following conditions are met: - * - all nodes support changing bit timing - * - new bit timing is successfully set as "pending" in all nodes - * - all nodes have to activate the new bit timing roughly at the same time. - * Therefore this function needs all nodes to be selected. - * - * @param LSSmaster This object. - * @param switchDelay_ms delay that is applied by the slave once before and - * once after switching in ms. - * @return #CO_LSSmaster_ILLEGAL_ARGUMENT, #CO_LSSmaster_INVALID_STATE, - * #CO_LSSmaster_OK - */ -CO_LSSmaster_return_t CO_LSSmaster_ActivateBit( - CO_LSSmaster_t *LSSmaster, - uint16_t switchDelay_ms); - - -/** - * Request LSS inquire LSS address - * - * The LSS address value is read from the node. This is useful when the node - * was selected by fastscan. - * - * This function needs one specific node to be selected. - * - * Function must be called cyclically until it returns != #CO_LSSmaster_WAIT_SLAVE. - * Function is non-blocking. - * - * @param LSSmaster This object. - * @param timeDifference_ms Time difference from previous function call in - * [milliseconds]. Zero when request is started. - * @param lssAddress [out] read result when function returns successfully - * @return #CO_LSSmaster_ILLEGAL_ARGUMENT, #CO_LSSmaster_INVALID_STATE, - * #CO_LSSmaster_WAIT_SLAVE, #CO_LSSmaster_OK, #CO_LSSmaster_TIMEOUT - */ -CO_LSSmaster_return_t CO_LSSmaster_InquireLssAddress( - CO_LSSmaster_t *LSSmaster, - uint16_t timeDifference_ms, - CO_LSS_address_t *lssAddress); - - -/** - * Request LSS inquire node ID - * - * The node ID value is read from the node. - * - * This function needs one specific node to be selected. - * - * Function must be called cyclically until it returns != #CO_LSSmaster_WAIT_SLAVE. - * Function is non-blocking. - * - * @param LSSmaster This object. - * @param timeDifference_ms Time difference from previous function call in - * [milliseconds]. Zero when request is started. - * @param nodeId [out] read result when function returns successfully - * @return #CO_LSSmaster_ILLEGAL_ARGUMENT, #CO_LSSmaster_INVALID_STATE, - * #CO_LSSmaster_WAIT_SLAVE, #CO_LSSmaster_OK, #CO_LSSmaster_TIMEOUT - */ -CO_LSSmaster_return_t CO_LSSmaster_InquireNodeId( - CO_LSSmaster_t *LSSmaster, - uint16_t timeDifference_ms, - uint8_t *nodeId); - - -/** - * Scan type for #CO_LSSmaster_fastscan_t scan - */ -typedef enum { - CO_LSSmaster_FS_SCAN = 0, /**< Do full 32 bit scan */ - CO_LSSmaster_FS_SKIP = 1, /**< Skip this value */ - CO_LSSmaster_FS_MATCH = 2, /**< Full 32 bit value is given as argument, just verify */ -} CO_LSSmaster_scantype_t; - -/** - * Parameters for LSS fastscan #CO_LSSmaster_IdentifyFastscan - */ -typedef struct{ - CO_LSSmaster_scantype_t scan[4]; /**< Scan type for each part of the LSS address */ - CO_LSS_address_t match; /**< Value to match in case of #CO_LSSmaster_FS_MATCH */ - CO_LSS_address_t found; /**< Scan result */ -} CO_LSSmaster_fastscan_t; - -/** - * Select a node by LSS identify fastscan - * - * This initiates searching for a unconfigured node by the means of LSS fastscan - * mechanism. When this function is finished - * - a (more or less) arbitrary node is selected and ready for node ID assingment - * - no node is selected because the given criteria do not match a node - * - no node is selected because all nodes are already configured - * - * There are multiple ways to scan for a node. Depending on those, the scan - * will take different amounts of time: - * - full scan - * - partial scan - * - verification - * - * Most of the time, those are used in combination. Consider the following example: - * - Vendor ID and product code are known - * - Software version doesn't matter - * - Serial number is unknown - * - * In this case, the fastscan structure should be set up as following: - * \code{.c} -CO_LSSmaster_fastscan_t fastscan; -fastscan.scan[CO_LSS_FASTSCAN_VENDOR_ID] = CO_LSSmaster_FS_MATCH; -fastscan.match.vendorID = YOUR_VENDOR_ID; -fastscan.scan[CO_LSS_FASTSCAN_PRODUCT] = CO_LSSmaster_FS_MATCH; -fastscan.match.productCode = YOUR_PRODUCT_CODE; -fastscan.scan[CO_LSS_FASTSCAN_REV] = CO_LSSmaster_FS_SKIP; -fastscan.scan[CO_LSS_FASTSCAN_SERIAL] = CO_LSSmaster_FS_SCAN; - * \endcode - * - * This example will take 2 scan cyles for verifying vendor ID and product code - * and 33 scan cycles to find the serial number. - * - * For scanning, the following limitations apply: - * - No more than two values can be skipped - * - Vendor ID cannot be skipped - * - * @remark When doing partial scans, it is in the responsibility of the user - * that the LSS address is unique. - * - * This function needs that no node is selected when starting the scan process. - * - * Function must be called cyclically until it returns != #CO_LSSmaster_WAIT_SLAVE. - * Function is non-blocking. - * - * @param LSSmaster This object. - * @param timeDifference_ms Time difference from previous function call in - * [milliseconds]. Zero when request is started. - * @param fastscan struct according to #CO_LSSmaster_fastscan_t. - * @return #CO_LSSmaster_ILLEGAL_ARGUMENT, #CO_LSSmaster_INVALID_STATE, - * #CO_LSSmaster_WAIT_SLAVE, #CO_LSSmaster_SCAN_FINISHED, #CO_LSSmaster_SCAN_NOACK, - * #CO_LSSmaster_SCAN_FAILED - */ -CO_LSSmaster_return_t CO_LSSmaster_IdentifyFastscan( - CO_LSSmaster_t *LSSmaster, - uint16_t timeDifference_ms, - CO_LSSmaster_fastscan_t *fastscan); - - -#else /* CO_NO_LSS_CLIENT == 1 */ - -/** - * @addtogroup CO_LSS - * @{ - * If you need documetation for LSS master usage, add "CO_NO_LSS_CLIENT=1" to doxygen - * "PREDEFINED" variable. - * - */ - -#endif /* CO_NO_LSS_CLIENT == 1 */ - -#ifdef __cplusplus -} -#endif /*__cplusplus*/ - -/** @} */ -#endif diff --git a/stack/CO_LSSslave.c b/stack/CO_LSSslave.c deleted file mode 100644 index c472eab0..00000000 --- a/stack/CO_LSSslave.c +++ /dev/null @@ -1,529 +0,0 @@ -/* - * CANopen LSS Slave protocol. - * - * @file CO_LSSslave.c - * @ingroup CO_LSS - * @author Martin Wagner - * @copyright 2017 - 2020 Neuberger Gebaeudeautomation GmbH - * - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "CANopen.h" -#include "CO_LSSslave.h" - -#if CO_NO_LSS_SERVER == 1 - -/* - * Helper function - Handle service "switch state global" - */ -static void CO_LSSslave_serviceSwitchStateGlobal( - CO_LSSslave_t *LSSslave, - CO_LSS_cs_t service, - const CO_CANrxMsg_t *msg) -{ - uint8_t mode = msg->data[1]; - - switch (mode) { - case CO_LSS_STATE_WAITING: - LSSslave->lssState = CO_LSS_STATE_WAITING; - CO_memset((uint8_t*)&LSSslave->lssSelect, 0, sizeof(LSSslave->lssSelect)); - break; - case CO_LSS_STATE_CONFIGURATION: - LSSslave->lssState = CO_LSS_STATE_CONFIGURATION; - break; - default: - break; - } -} - -/* - * Helper function - Handle service "switch state selective" - */ -static void CO_LSSslave_serviceSwitchStateSelective( - CO_LSSslave_t *LSSslave, - CO_LSS_cs_t service, - const CO_CANrxMsg_t *msg) -{ - uint32_t value; - CO_memcpySwap4(&value, &msg->data[1]); - - if(LSSslave->lssState != CO_LSS_STATE_WAITING) { - return; - } - - switch (service) { - case CO_LSS_SWITCH_STATE_SEL_VENDOR: - LSSslave->lssSelect.identity.vendorID = value; - break; - case CO_LSS_SWITCH_STATE_SEL_PRODUCT: - LSSslave->lssSelect.identity.productCode = value; - break; - case CO_LSS_SWITCH_STATE_SEL_REV: - LSSslave->lssSelect.identity.revisionNumber = value; - break; - case CO_LSS_SWITCH_STATE_SEL_SERIAL: - LSSslave->lssSelect.identity.serialNumber = value; - - if (CO_LSS_ADDRESS_EQUAL(LSSslave->lssAddress, LSSslave->lssSelect)) { - LSSslave->lssState = CO_LSS_STATE_CONFIGURATION; - - /* send confirmation */ - LSSslave->TXbuff->data[0] = CO_LSS_SWITCH_STATE_SEL; - CO_memset(&LSSslave->TXbuff->data[1], 0, 7); - CO_CANsend(LSSslave->CANdevTx, LSSslave->TXbuff); - } - break; - default: - break; - } -} - -/* - * Helper function - Handle service "configure" - * - * values inside message have different meaning, depending on the selected - * configuration type - */ -static void CO_LSSslave_serviceConfig( - CO_LSSslave_t *LSSslave, - CO_LSS_cs_t service, - const CO_CANrxMsg_t *msg) -{ - uint8_t nid; - uint8_t tableSelector; - uint8_t tableIndex; - uint8_t errorCode; - - if(LSSslave->lssState != CO_LSS_STATE_CONFIGURATION) { - return; - } - - switch (service) { - case CO_LSS_CFG_NODE_ID: - nid = msg->data[1]; - errorCode = CO_LSS_CFG_NODE_ID_OK; - - if (CO_LSS_NODE_ID_VALID(nid)) { - LSSslave->pendingNodeID = nid; - } - else { - errorCode = CO_LSS_CFG_NODE_ID_OUT_OF_RANGE; - } - - /* send confirmation */ - LSSslave->TXbuff->data[0] = CO_LSS_CFG_NODE_ID; - LSSslave->TXbuff->data[1] = errorCode; - /* we do not use spec-error, always 0 */ - CO_memset(&LSSslave->TXbuff->data[2], 0, 6); - CO_CANsend(LSSslave->CANdevTx, LSSslave->TXbuff); - break; - case CO_LSS_CFG_BIT_TIMING: - if (LSSslave->pFunctLSScheckBitRate == NULL) { - /* setting bit timing is not supported. Drop request */ - break; - } - - tableSelector = msg->data[1]; - tableIndex = msg->data[2]; - errorCode = CO_LSS_CFG_BIT_TIMING_OK; - - if (tableSelector==0 && CO_LSS_BIT_TIMING_VALID(tableIndex)) { - uint16_t bit = CO_LSS_bitTimingTableLookup[tableIndex]; - bool_t bit_rate_supported = LSSslave->pFunctLSScheckBitRate( - LSSslave->functLSScheckBitRateObject, bit); - - if (bit_rate_supported) { - LSSslave->pendingBitRate = bit; - } - else { - errorCode = CO_LSS_CFG_BIT_TIMING_OUT_OF_RANGE; - } - } - else { - /* we currently only support CiA301 bit timing table */ - errorCode = CO_LSS_CFG_BIT_TIMING_OUT_OF_RANGE; - } - - /* send confirmation */ - LSSslave->TXbuff->data[0] = CO_LSS_CFG_BIT_TIMING; - LSSslave->TXbuff->data[1] = errorCode; - /* we do not use spec-error, always 0 */ - CO_memset(&LSSslave->TXbuff->data[2], 0, 6); - CO_CANsend(LSSslave->CANdevTx, LSSslave->TXbuff); - break; - case CO_LSS_CFG_ACTIVATE_BIT_TIMING: - if (LSSslave->pFunctLSScheckBitRate == NULL) { - /* setting bit timing is not supported. Drop request */ - break; - } - - /* notify application */ - if (LSSslave->pFunctLSSactivateBitRate != NULL) { - uint16_t delay; - CO_memcpySwap2(&delay, &msg->data[1]); - LSSslave->pFunctLSSactivateBitRate( - LSSslave->functLSSactivateBitRateObject, delay); - } - break; - case CO_LSS_CFG_STORE: - errorCode = CO_LSS_CFG_STORE_OK; - - if (LSSslave->pFunctLSScfgStore == NULL) { - /* storing is not supported. Reply error */ - errorCode = CO_LSS_CFG_STORE_NOT_SUPPORTED; - } - else { - bool_t result; - /* Store "pending" to "persistent" */ - result = LSSslave->pFunctLSScfgStore(LSSslave->functLSScfgStore, - LSSslave->pendingNodeID, LSSslave->pendingBitRate); - if (!result) { - errorCode = CO_LSS_CFG_STORE_FAILED; - } - } - - /* send confirmation */ - LSSslave->TXbuff->data[0] = CO_LSS_CFG_STORE; - LSSslave->TXbuff->data[1] = errorCode; - /* we do not use spec-error, always 0 */ - CO_memset(&LSSslave->TXbuff->data[2], 0, 6); - CO_CANsend(LSSslave->CANdevTx, LSSslave->TXbuff); - break; - default: - break; - } -} - -/* - * Helper function - Handle service "inquire" - */ -static void CO_LSSslave_serviceInquire( - CO_LSSslave_t *LSSslave, - CO_LSS_cs_t service, - const CO_CANrxMsg_t *msg) -{ - uint32_t value; - - if(LSSslave->lssState != CO_LSS_STATE_CONFIGURATION) { - return; - } - - switch (service) { - case CO_LSS_INQUIRE_VENDOR: - value = LSSslave->lssAddress.identity.vendorID; - break; - case CO_LSS_INQUIRE_PRODUCT: - value = LSSslave->lssAddress.identity.productCode; - break; - case CO_LSS_INQUIRE_REV: - value = LSSslave->lssAddress.identity.revisionNumber; - break; - case CO_LSS_INQUIRE_SERIAL: - value = LSSslave->lssAddress.identity.serialNumber; - break; - case CO_LSS_INQUIRE_NODE_ID: - value = (uint32_t)LSSslave->activeNodeID; - break; - default: - return; - } - /* send response */ - LSSslave->TXbuff->data[0] = service; - CO_memcpySwap4(&LSSslave->TXbuff->data[1], &value); - CO_memset(&LSSslave->TXbuff->data[5], 0, 4); - CO_CANsend(LSSslave->CANdevTx, LSSslave->TXbuff); -} - -/* - * Helper function - Handle service "identify" - */ -static void CO_LSSslave_serviceIdent( - CO_LSSslave_t *LSSslave, - CO_LSS_cs_t service, - const CO_CANrxMsg_t *msg) -{ - uint32_t idNumber; - uint8_t bitCheck; - uint8_t lssSub; - uint8_t lssNext; - bool_t ack; - - if (LSSslave->lssState != CO_LSS_STATE_WAITING) { - /* fastscan is only allowed in waiting state */ - return; - } - if (service != CO_LSS_IDENT_FASTSCAN) { - /* we only support "fastscan" identification */ - return; - } - if (LSSslave->pendingNodeID!=CO_LSS_NODE_ID_ASSIGNMENT || - LSSslave->activeNodeID!=CO_LSS_NODE_ID_ASSIGNMENT) { - /* fastscan is only active on unconfigured nodes */ - return; - } - - CO_memcpySwap4(&idNumber, &msg->data[1]); - bitCheck = msg->data[5]; - lssSub = msg->data[6]; - lssNext = msg->data[7]; - - if (!CO_LSS_FASTSCAN_BITCHECK_VALID(bitCheck) || - !CO_LSS_FASTSCAN_LSS_SUB_NEXT_VALID(lssSub) || - !CO_LSS_FASTSCAN_LSS_SUB_NEXT_VALID(lssNext)) { - /* Invalid request */ - return; - } - - ack = false; - if (bitCheck == CO_LSS_FASTSCAN_CONFIRM) { - /* Confirm, Reset */ - ack = true; - LSSslave->fastscanPos = CO_LSS_FASTSCAN_VENDOR_ID; - CO_memset((uint8_t*)&LSSslave->lssFastscan, 0, - sizeof(LSSslave->lssFastscan)); - } - else if (LSSslave->fastscanPos == lssSub) { - uint32_t mask = 0xFFFFFFFF << bitCheck; - - if ((LSSslave->lssAddress.addr[lssSub] & mask) == (idNumber & mask)) { - /* all requested bits match */ - ack = true; - LSSslave->fastscanPos = lssNext; - - if (bitCheck==0 && lssNextlssState = CO_LSS_STATE_CONFIGURATION; - } - } - } - if (ack) { - LSSslave->TXbuff->data[0] = CO_LSS_IDENT_SLAVE; - CO_memset(&LSSslave->TXbuff->data[1], 0, 7); - CO_CANsend(LSSslave->CANdevTx, LSSslave->TXbuff); - } -} - - -/* - * Read received message from CAN module. - * - * Function will be called (by CAN receive interrupt) every time, when CAN - * message with correct identifier will be received. For more information and - * description of parameters see file CO_driver.h. - */ -static void CO_LSSslave_receive(void *object, const CO_CANrxMsg_t *msg) -{ - CO_LSSslave_t *LSSslave; - - LSSslave = (CO_LSSslave_t*)object; /* this is the correct pointer type of the first argument */ - - if(msg->DLC == 8){ - CO_LSS_cs_t cs = msg->data[0]; - - if (CO_LSS_CS_SERVICE_IS_SWITCH_GLOBAL(cs)) { - CO_LSSslave_serviceSwitchStateGlobal(LSSslave, cs, msg); - } - else if (CO_LSS_CS_SERVICE_IS_SWITCH_STATE_SELECTIVE(cs)) { - CO_LSSslave_serviceSwitchStateSelective(LSSslave, cs, msg); - } - else if (CO_LSS_CS_SERVICE_IS_CONFIG(cs)) { - CO_LSSslave_serviceConfig(LSSslave, cs, msg); - } - else if (CO_LSS_CS_SERVICE_IS_INQUIRE(cs)) { - CO_LSSslave_serviceInquire(LSSslave, cs, msg); - } - else if (CO_LSS_CS_SERVICE_IS_IDENT(cs)) { - CO_LSSslave_serviceIdent(LSSslave, cs, msg); - } - else { - /* No Ack -> Unsupported commands are dropped */ - } - } -} - - -/******************************************************************************/ -CO_ReturnError_t CO_LSSslave_init( - CO_LSSslave_t *LSSslave, - CO_LSS_address_t lssAddress, - uint16_t pendingBitRate, - uint8_t pendingNodeID, - CO_CANmodule_t *CANdevRx, - uint16_t CANdevRxIdx, - uint32_t CANidLssMaster, - CO_CANmodule_t *CANdevTx, - uint16_t CANdevTxIdx, - uint32_t CANidLssSlave) -{ - /* verify arguments */ - if (LSSslave==NULL || CANdevRx==NULL || CANdevTx==NULL || - !CO_LSS_NODE_ID_VALID(pendingNodeID)) { - return CO_ERROR_ILLEGAL_ARGUMENT; - } - - /* check LSS address for plausibility. As a bare minimum, the vendor - * ID and serial number must be set */ - if (lssAddress.identity.vendorID==0 || lssAddress.identity.serialNumber==0) { - return CO_ERROR_ILLEGAL_ARGUMENT; - } - - CO_memcpy((uint8_t*)&LSSslave->lssAddress, (uint8_t*)&lssAddress, sizeof(LSSslave->lssAddress)); - LSSslave->lssState = CO_LSS_STATE_WAITING; - CO_memset((uint8_t*)&LSSslave->lssSelect, 0, sizeof(LSSslave->lssSelect)); - - CO_memset((uint8_t*)&LSSslave->lssFastscan, 0, sizeof(LSSslave->lssFastscan)); - LSSslave->fastscanPos = CO_LSS_FASTSCAN_VENDOR_ID; - - LSSslave->pendingBitRate = pendingBitRate; - LSSslave->pendingNodeID = pendingNodeID; - LSSslave->activeNodeID = CO_LSS_NODE_ID_ASSIGNMENT; - LSSslave->pFunctLSScheckBitRate = NULL; - LSSslave->functLSScheckBitRateObject = NULL; - LSSslave->pFunctLSSactivateBitRate = NULL; - LSSslave->functLSSactivateBitRateObject = NULL; - LSSslave->pFunctLSScfgStore = NULL; - LSSslave->functLSScfgStore = NULL; - - /* configure LSS CAN Master message reception */ - CO_CANrxBufferInit( - CANdevRx, /* CAN device */ - CANdevRxIdx, /* rx buffer index */ - CANidLssMaster, /* CAN identifier */ - 0x7FF, /* mask */ - 0, /* rtr */ - (void*)LSSslave, /* object passed to receive function */ - CO_LSSslave_receive); /* this function will process received message */ - - /* configure LSS CAN Slave response message transmission */ - LSSslave->CANdevTx = CANdevTx; - LSSslave->TXbuff = CO_CANtxBufferInit( - CANdevTx, /* CAN device */ - CANdevTxIdx, /* index of specific buffer inside CAN module */ - CANidLssSlave, /* CAN identifier */ - 0, /* rtr */ - 8, /* number of data bytes */ - 0); /* synchronous message flag bit */ - - return CO_ERROR_NO; -} - - -/******************************************************************************/ -void CO_LSSslave_initCheckBitRateCallback( - CO_LSSslave_t *LSSslave, - void *object, - bool_t (*pFunctLSScheckBitRate)(void *object, uint16_t bitRate)) -{ - if(LSSslave != NULL){ - LSSslave->functLSScheckBitRateObject = object; - LSSslave->pFunctLSScheckBitRate = pFunctLSScheckBitRate; - } -} - - -/******************************************************************************/ -void CO_LSSslave_initActivateBitRateCallback( - CO_LSSslave_t *LSSslave, - void *object, - void (*pFunctLSSactivateBitRate)(void *object, uint16_t delay)) -{ - if(LSSslave != NULL){ - LSSslave->functLSSactivateBitRateObject = object; - LSSslave->pFunctLSSactivateBitRate = pFunctLSSactivateBitRate; - } -} - - -/******************************************************************************/ -void CO_LSSslave_initCfgStoreCallback( - CO_LSSslave_t *LSSslave, - void *object, - bool_t (*pFunctLSScfgStore)(void *object, uint8_t id, uint16_t bitRate)) -{ - if(LSSslave != NULL){ - LSSslave->functLSScfgStore = object; - LSSslave->pFunctLSScfgStore = pFunctLSScfgStore; - } -} - - -/******************************************************************************/ -void CO_LSSslave_process( - CO_LSSslave_t *LSSslave, - uint16_t activeBitRate, - uint8_t activeNodeId, - uint16_t *pendingBitRate, - uint8_t *pendingNodeId) -{ - LSSslave->activeNodeID = activeNodeId; - *pendingBitRate = LSSslave->pendingBitRate; - *pendingNodeId = LSSslave->pendingNodeID; -} - - -/******************************************************************************/ -CO_LSS_state_t CO_LSSslave_getState( - CO_LSSslave_t *LSSslave) -{ - if(LSSslave != NULL){ - return LSSslave->lssState; - } - return CO_LSS_STATE_WAITING; -} - - -/******************************************************************************/ -bool_t CO_LSSslave_LEDprocess( - CO_LSSslave_t *LSSslave, - uint16_t timeDifference_ms, - bool_t *LEDon) -{ - static uint16_t ms50 = 0; - static int8_t flash1, flash2; - - if (LSSslave == NULL || LEDon == NULL) - return false; - ms50 += timeDifference_ms; - if(ms50 >= 50) { - ms50 -= 50; - /* 4 cycles on, 50 cycles off */ - if(++flash1 >= 4) flash1 = -50; - - /* 4 cycles on, 4 cycles off, 4 cycles on, 50 cycles off */ - switch(++flash2){ - case 4: flash2 = -104; break; - case -100: flash2 = 100; break; - case 104: flash2 = -50; break; - } - } - if (LSSslave->lssState == CO_LSS_STATE_CONFIGURATION) - { - *LEDon = (flash2 >= 0); - return true; - } - else if (LSSslave->activeNodeID == CO_LSS_NODE_ID_ASSIGNMENT) - { - *LEDon = (flash1 >= 0); - return true; - } - return false; -} - - -#endif diff --git a/stack/CO_LSSslave.h b/stack/CO_LSSslave.h deleted file mode 100644 index dd235d74..00000000 --- a/stack/CO_LSSslave.h +++ /dev/null @@ -1,430 +0,0 @@ -/** - * CANopen LSS Master/Slave protocol. - * - * @file CO_LSSslave.h - * @ingroup CO_LSS - * @author Martin Wagner - * @copyright 2017 - 2020 Neuberger Gebaeudeautomation GmbH - * - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#ifndef CO_LSSslave_H -#define CO_LSSslave_H - -#ifdef __cplusplus -extern "C" { -#endif - -#if CO_NO_LSS_SERVER == 1 - -#include "CO_LSS.h" - -/** - * @addtogroup CO_LSS - * @defgroup CO_LSSslave LSS Slave - * @ingroup CO_LSS - * @{ - * - * CANopen Layer Setting Service - server protocol - * - * The server/slave provides the following services - * - node selection via LSS address - * - node selection via LSS fastscan - * - Inquire LSS address of currently selected node - * - Inquire node ID - * - Configure bit timing - * - Configure node ID - * - Activate bit timing parameters - * - Store configuration (bit rate and node ID) - * - * After CAN module start, the LSS server and NMT server are started and then - * coexist alongside each other. To achieve this behaviour, the CANopen node - * startup process has to be conrolled more detailled. Therefore, the function - * CO_init() is split up into the functions #CO_new(), #CO_CANinit(), #CO_LSSinit() - * and #CO_CANopenInit(). - * Moreover, the LSS server needs to pause the NMT server initialization in case - * no valid node ID is available at start up. - * - * ###Example - * - * It is strongly recommended that the user already has a fully working application - * running with the standard (non LSS) version of CANopenNode. This is required - * to understand what this example does and where you need to change it for your - * requirements. - * - * The following code is only a suggestion on how to use the LSS server. It is - * not a working example! To simplify the code, no error handling is - * included. For stable code, proper error handling has to be added to the user - * code. - * - * This example is not intended for bare metal targets. If you intend to do CAN - * message receiving inside interrupt, be aware that the callback functions - * will be called inside the interrupt handler context! - * - * \code{.c} - - const uint16_t FIRST_BIT = 125; - queue changeBitRate; - uint8_t activeNid; - uint16_t activeBit; - - bool_t checkBitRateCallback(void *object, uint16_t bitRate) - { - if (validBit(bitRate)) { - return true; - } - return false; - } - - void activateBitRateCallback(void *object, uint16_t delay) - { - int time = getCurrentTime(); - queueSend(&changeBitRate, time, delay); - } - - bool_t cfgStoreCallback(void *object, uint8_t id, uint16_t bitRate) - { - savePersistent(id, bitRate); - return true; - } - - void start_canopen(uint8_t nid) - { - uint8_t persistentNid; - uint8_t pendingNid; - uint16_t persistentBit; - uint16_t pendingBit; - - loadPersistent(&persistentNid, &persistentBit); - - if ( ! validBit(persistentBit)) { - printf("no bit rate found, defaulting to %d", FIRST_BIT); - pendingBit = FIRST_BIT; - } - else { - printf("loaded bit rate from nvm: %d", persistentBit); - pendingBit = persistentBit; - } - - if (nid == 0) { - if ( ! validNid(persistentNid)) { - pendingNid = CO_LSS_NODE_ID_ASSIGNMENT; - printf("no node id found, needs to be set by LSS. NMT will" - "not be started until valid node id is set"); - } - else { - printf("loaded node id from nvm: %d", persistentNid); - pendingNid = persistentNid; - } - } - else { - printf("node id provided by application: %d", nid); - pendingNid = nid; - } - - CO_new(); - CO_CANinit(0, pendingBit); - CO_LSSinit(pendingNid, pendingBit); - CO_CANsetNormalMode(CO->CANmodule[0]); - activeBit = pendingBit; - - CO_LSSslave_initCheckBitRateCallback(CO->LSSslave, NULL, checkBitRateCallback); - CO_LSSslave_initActivateBitRateCallback(CO->LSSslave, NULL, activateBitRateCallback); - CO_LSSslave_initCfgStoreCallback(CO->LSSslave, NULL, cfgStoreCallback); - - while (1) { - CO_LSSslave_process(CO->LSSslave, activeBit, activeNid, - &pendingBit, &pendingNid); - if (pendingNid!=CO_LSS_NODE_ID_ASSIGNMENT && - CO_LSSslave_getState(CO->LSSslave)==CO_LSS_STATE_WAITING) { - printf("node ID has been found: %d", pendingNid); - break; - } - - if ( ! queueEmpty(&changeBitRate)) { - printf("bit rate change requested: %d", pendingBit); - int time; - uint16_t delay; - queueReceive(&changeBitRate, time, delay); - delayUntil(time + delay); - CO_CANsetBitrate(CO->CANmodule[0], pendingBit); - delay(delay); - } - - printf("waiting for node id"); - CO_CANrxWait(CO->CANmodule[0]); - } - - CO_CANopenInit(pendingNid); - activeNid = pendingNid; - - printf("from this on, initialization doesn't differ to non-LSS version" - "You can now intialize your CO_CANrxWait() thread or interrupt"); - } - - void main(void) - { - uint8_t pendingNid; - uint16_t pendingBit; - - printf("like example in dir \"example\""); - - CO_NMT_reset_cmd_t reset = CO_RESET_NOT; - uint16_t timer1msPrevious; - - start_canopen(0); - - reset = CO_RESET_NOT; - timer1msPrevious = CO_timer1ms; - while(reset == CO_RESET_NOT){ - printf("loop for normal program execution"); - uint16_t timer1msCopy, timer1msDiff; - - timer1msCopy = CO_timer1ms; - timer1msDiff = timer1msCopy - timer1msPrevious; - timer1msPrevious = timer1msCopy; - - reset = CO_process(CO, timer1msDiff, NULL); - - CO_LSSslave_process(CO->LSSslave, activeBit, activeNid, - &pendingBit, &pendingNid); - if (reset == CO_RESET_COMM) { - printf("restarting CANopen using pending node ID %d", pendingNid); - CO_delete(0); - start_canopen(pendingNid); - reset = CO_RESET_NOT; - } - if ( ! queueEmpty(&changeBitRate)) { - printf("bit rate change requested: %d", pendingBit); - int time; - uint16_t delay; - queueReceive(&changeBitRate, time, delay); - printf("Disabling CANopen for givent time"); - pauseReceiveThread(); - delayUntil(time + delay); - CO_CANsetBitrate(CO->CANmodule[0], pendingBit); - delay(delay); - resumeReceiveThread(); - printf("Re-enabling CANopen after bit rate switch"); - } - } - } - - * \endcode - */ - -/** - * LSS slave object. - */ -typedef struct{ - CO_LSS_address_t lssAddress; /**< From #CO_LSSslave_init */ - CO_LSS_state_t lssState; /**< #CO_LSS_state_t */ - CO_LSS_address_t lssSelect; /**< Received LSS Address by select */ - - CO_LSS_address_t lssFastscan; /**< Received LSS Address by fastscan */ - uint8_t fastscanPos; /**< Current state of fastscan */ - - uint16_t pendingBitRate; /**< Bit rate value that is temporarily configured in volatile memory */ - uint8_t pendingNodeID; /**< Node ID that is temporarily configured in volatile memory */ - uint8_t activeNodeID; /**< Node ID used at the CAN interface */ - - bool_t (*pFunctLSScheckBitRate)(void *object, uint16_t bitRate); /**< From CO_LSSslave_initCheckBitRateCallback() or NULL */ - void *functLSScheckBitRateObject; /** Pointer to object */ - void (*pFunctLSSactivateBitRate)(void *object, uint16_t delay); /**< From CO_LSSslave_initActivateBitRateCallback() or NULL. Delay is in ms */ - void *functLSSactivateBitRateObject; /** Pointer to object */ - bool_t (*pFunctLSScfgStore)(void *object, uint8_t id, uint16_t bitRate); /**< From CO_LSSslave_initCfgStoreCallback() or NULL */ - void *functLSScfgStore; /** Pointer to object */ - - CO_CANmodule_t *CANdevTx; /**< From #CO_LSSslave_init() */ - CO_CANtx_t *TXbuff; /**< CAN transmit buffer */ -}CO_LSSslave_t; - -/** - * Initialize LSS object. - * - * Function must be called in the communication reset section. - * - * Depending on the startup type, pending bit rate and node ID have to be - * supplied differently. After #CO_NMT_RESET_NODE or at power up they should - * be restored from persitent bit rate and node id. After #CO_NMT_RESET_COMMUNICATION - * they have to be supplied from the application and are generally the values - * that have been last returned by #CO_LSSslave_process() before resetting. - * - * @remark The LSS address needs to be unique on the network. For this, the 128 - * bit wide identity object (1018h) is used. Therefore, this object has to be fully - * initalized before passing it to this function. - * - * @param LSSslave This object will be initialized. - * @param lssAddress LSS address - * @param pendingBitRate Bit rate of the CAN interface. - * @param pendingNodeID Node ID or 0xFF - invalid. - * @param CANdevRx CAN device for LSS slave reception. - * @param CANdevRxIdx Index of receive buffer in the above CAN device. - * @param CANidLssMaster COB ID for reception. - * @param CANdevTx CAN device for LSS slave transmission. - * @param CANdevTxIdx Index of transmit buffer in the above CAN device. - * @param CANidLssSlave COB ID for transmission. - * @return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT. - */ -CO_ReturnError_t CO_LSSslave_init( - CO_LSSslave_t *LSSslave, - CO_LSS_address_t lssAddress, - uint16_t pendingBitRate, - uint8_t pendingNodeID, - CO_CANmodule_t *CANdevRx, - uint16_t CANdevRxIdx, - uint32_t CANidLssMaster, - CO_CANmodule_t *CANdevTx, - uint16_t CANdevTxIdx, - uint32_t CANidLssSlave); - -/** - * Process LSS communication - * - * - sets currently active node ID and bit rate so master can read it - * - hands over pending node ID and bit rate to user application - * - * @param LSSslave This object. - * @param activeBitRate Currently active bit rate - * @param activeNodeId Currently active node ID - * @param pendingBitRate [out] Requested bit rate - * @param pendingNodeId [out] Requested node id - */ -void CO_LSSslave_process( - CO_LSSslave_t *LSSslave, - uint16_t activeBitRate, - uint8_t activeNodeId, - uint16_t *pendingBitRate, - uint8_t *pendingNodeId); - -/** - * Get current LSS state - * - * @param LSSslave This object. - * @return #CO_LSS_state_t - */ -CO_LSS_state_t CO_LSSslave_getState( - CO_LSSslave_t *LSSslave); - -/** - * Process LSS LED - * - * Returns the status of the LSS LED (if LSS is involved) - * with the following meaning: - * - * UNCONFIGURED (activeNodeId is unconfigured) --> single flash - * SELECTED --> double flash - * - * If none of above conditions apply, returns false. - * - * @param LSSslave This object. - * @param timeDifference_ms The amount of time elapsed since the last call - * @param LEDon [out] LED state - * - * @return true if LSS is involved (unconfigured node or selected node) - */ -bool_t CO_LSSslave_LEDprocess( - CO_LSSslave_t *LSSslave, - uint16_t timeDifference_ms, - bool_t *LEDon); - -/** - * Initialize verify bit rate callback - * - * Function initializes callback function, which is called when "config bit - * timing parameters" is used. The callback function needs to check if the new bit - * rate is supported by the CANopen device. Callback returns "true" if supported. - * When no callback is set the LSS server will no-ack the request, indicating to - * the master that bit rate change is not supported. - * - * @remark Depending on the CAN driver implementation, this function is called - * inside an ISR - * - * @param LSSslave This object. - * @param object Pointer to object, which will be passed to pFunctLSScheckBitRate(). Can be NULL - * @param pFunctLSScheckBitRate Pointer to the callback function. Not called if NULL. - */ -void CO_LSSslave_initCheckBitRateCallback( - CO_LSSslave_t *LSSslave, - void *object, - bool_t (*pFunctLSScheckBitRate)(void *object, uint16_t bitRate)); - -/** - * Initialize activate bit rate callback - * - * Function initializes callback function, which is called when "activate bit - * timing parameters" is used. The callback function gives the user an event to - * allow setting a timer or do calculations based on the exact time the request - * arrived. - * According to DSP 305 6.4.4, the delay has to be applied once before and once after - * switching bit rates. During this time, a device musn't send any messages. - * - * @remark Depending on the CAN driver implementation, this function is called - * inside an ISR - * - * @param LSSslave This object. - * @param object Pointer to object, which will be passed to pFunctLSSactivateBitRate(). Can be NULL - * @param pFunctLSSactivateBitRate Pointer to the callback function. Not called if NULL. - */ -void CO_LSSslave_initActivateBitRateCallback( - CO_LSSslave_t *LSSslave, - void *object, - void (*pFunctLSSactivateBitRate)(void *object, uint16_t delay)); - -/** - * Store configuration callback - * - * Function initializes callback function, which is called when "store configuration" is used. - * The callback function gives the user an event to store the corresponding node id and bit rate - * to NVM. Those values have to be supplied to the init function as "persistent values" - * after reset. If callback returns "true", success is send to the LSS master. When no - * callback is set the LSS server will no-ack the request, indicating to the master - * that storing is not supported. - * - * @remark Depending on the CAN driver implementation, this function is called - * inside an ISR - * - * @param LSSslave This object. - * @param object Pointer to object, which will be passed to pFunctLSScfgStore(). Can be NULL - * @param pFunctLSScfgStore Pointer to the callback function. Not called if NULL. - */ -void CO_LSSslave_initCfgStoreCallback( - CO_LSSslave_t *LSSslave, - void *object, - bool_t (*pFunctLSScfgStore)(void *object, uint8_t id, uint16_t bitRate)); - -#else /* CO_NO_LSS_SERVER == 1 */ - -/** - * @addtogroup CO_LSS - * @{ - * If you need documetation for LSS slave usage, add "CO_NO_LSS_SERVER=1" to doxygen - * "PREDEFINED" variable. - * - */ - -#endif /* CO_NO_LSS_SERVER == 1 */ - -#ifdef __cplusplus -} -#endif /*__cplusplus*/ - -/** @} */ -#endif diff --git a/stack/CO_NMT_Heartbeat.c b/stack/CO_NMT_Heartbeat.c deleted file mode 100644 index 523dba28..00000000 --- a/stack/CO_NMT_Heartbeat.c +++ /dev/null @@ -1,353 +0,0 @@ -/* - * CANopen NMT and Heartbeat producer object. - * - * @file CO_NMT_Heartbeat.c - * @ingroup CO_NMT_Heartbeat - * @author Janez Paternoster - * @copyright 2004 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include "CO_driver.h" -#include "CO_SDO.h" -#include "CO_Emergency.h" -#include "CO_NMT_Heartbeat.h" - -/* - * Read received message from CAN module. - * - * Function will be called (by CAN receive interrupt) every time, when CAN - * message with correct identifier will be received. For more information and - * description of parameters see file CO_driver.h. - */ -static void CO_NMT_receive(void *object, const CO_CANrxMsg_t *msg){ - CO_NMT_t *NMT; - uint8_t nodeId; - - NMT = (CO_NMT_t*)object; /* this is the correct pointer type of the first argument */ - - nodeId = msg->data[1]; - - if((msg->DLC == 2) && ((nodeId == 0) || (nodeId == NMT->nodeId))){ - uint8_t command = msg->data[0]; - uint8_t currentOperatingState = NMT->operatingState; - - switch(command){ - case CO_NMT_ENTER_OPERATIONAL: - if((*NMT->emPr->errorRegister) == 0U){ - NMT->operatingState = CO_NMT_OPERATIONAL; - } - break; - case CO_NMT_ENTER_STOPPED: - NMT->operatingState = CO_NMT_STOPPED; - break; - case CO_NMT_ENTER_PRE_OPERATIONAL: - NMT->operatingState = CO_NMT_PRE_OPERATIONAL; - break; - case CO_NMT_RESET_NODE: - NMT->resetCommand = CO_RESET_APP; - break; - case CO_NMT_RESET_COMMUNICATION: - NMT->resetCommand = CO_RESET_COMM; - break; - default: - break; - } - - if(NMT->pFunctNMT!=NULL && currentOperatingState!=NMT->operatingState){ - NMT->pFunctNMT(NMT->operatingState); - } - } -} - - -/******************************************************************************/ -CO_ReturnError_t CO_NMT_init( - CO_NMT_t *NMT, - CO_EMpr_t *emPr, - uint8_t nodeId, - uint16_t firstHBTime, - CO_CANmodule_t *NMT_CANdev, - uint16_t NMT_rxIdx, - uint16_t CANidRxNMT, - CO_CANmodule_t *HB_CANdev, - uint16_t HB_txIdx, - uint16_t CANidTxHB) -{ - /* verify arguments */ - if(NMT==NULL || emPr==NULL || NMT_CANdev==NULL || HB_CANdev==NULL){ - return CO_ERROR_ILLEGAL_ARGUMENT; - } - - /* blinking bytes */ -#ifdef CO_USE_LEDS - NMT->LEDflickering = 0; - NMT->LEDblinking = 0; - NMT->LEDsingleFlash = 0; - NMT->LEDdoubleFlash = 0; - NMT->LEDtripleFlash = 0; - NMT->LEDquadrupleFlash = 0; -#endif /* CO_USE_LEDS */ - - /* Configure object variables */ - NMT->operatingState = CO_NMT_INITIALIZING; -#ifdef CO_USE_LEDS - NMT->LEDgreenRun = -1; - NMT->LEDredError = 1; -#endif /* CO_USE_LEDS */ - NMT->nodeId = nodeId; - NMT->firstHBTime = firstHBTime; - NMT->resetCommand = 0; - NMT->HBproducerTimer = 0xFFFF; - NMT->emPr = emPr; - NMT->pFunctNMT = NULL; - - /* configure NMT CAN reception */ - CO_CANrxBufferInit( - NMT_CANdev, /* CAN device */ - NMT_rxIdx, /* rx buffer index */ - CANidRxNMT, /* CAN identifier */ - 0x7FF, /* mask */ - 0, /* rtr */ - (void*)NMT, /* object passed to receive function */ - CO_NMT_receive); /* this function will process received message */ - - /* configure HB CAN transmission */ - NMT->HB_CANdev = HB_CANdev; - NMT->HB_TXbuff = CO_CANtxBufferInit( - HB_CANdev, /* CAN device */ - HB_txIdx, /* index of specific buffer inside CAN module */ - CANidTxHB, /* CAN identifier */ - 0, /* rtr */ - 1, /* number of data bytes */ - 0); /* synchronous message flag bit */ - - return CO_ERROR_NO; -} - - -/******************************************************************************/ -void CO_NMT_initCallback( - CO_NMT_t *NMT, - void (*pFunctNMT)(CO_NMT_internalState_t state)) -{ - if(NMT != NULL){ - NMT->pFunctNMT = pFunctNMT; - if(NMT->pFunctNMT != NULL){ - NMT->pFunctNMT(NMT->operatingState); - } - } -} - - -/******************************************************************************/ -#ifdef CO_USE_LEDS -void CO_NMT_blinkingProcess50ms(CO_NMT_t *NMT){ - - if(++NMT->LEDflickering >= 1) NMT->LEDflickering = -1; - - if(++NMT->LEDblinking >= 4) NMT->LEDblinking = -4; - - if(++NMT->LEDsingleFlash >= 4) NMT->LEDsingleFlash = -20; - - switch(++NMT->LEDdoubleFlash){ - case 4: NMT->LEDdoubleFlash = -104; break; - case -100: NMT->LEDdoubleFlash = 100; break; - case 104: NMT->LEDdoubleFlash = -20; break; - default: break; - } - - switch(++NMT->LEDtripleFlash){ - case 4: NMT->LEDtripleFlash = -104; break; - case -100: NMT->LEDtripleFlash = 100; break; - case 104: NMT->LEDtripleFlash = -114; break; - case -110: NMT->LEDtripleFlash = 110; break; - case 114: NMT->LEDtripleFlash = -20; break; - default: break; - } - - switch(++NMT->LEDquadrupleFlash){ - case 4: NMT->LEDquadrupleFlash = -104; break; - case -100: NMT->LEDquadrupleFlash = 100; break; - case 104: NMT->LEDquadrupleFlash = -114; break; - case -110: NMT->LEDquadrupleFlash = 110; break; - case 114: NMT->LEDquadrupleFlash = -124; break; - case -120: NMT->LEDquadrupleFlash = 120; break; - case 124: NMT->LEDquadrupleFlash = -20; break; - default: break; - } -} -#endif /* CO_USE_LEDS */ - - -/******************************************************************************/ -CO_NMT_reset_cmd_t CO_NMT_process( - CO_NMT_t *NMT, - uint16_t timeDifference_ms, - uint16_t HBtime, - uint32_t NMTstartup, - uint8_t errorRegister, - const uint8_t errorBehavior[], - uint16_t *timerNext_ms) -{ - uint8_t CANpassive; - - uint8_t currentOperatingState = NMT->operatingState; - - NMT->HBproducerTimer += timeDifference_ms; - - /* Heartbeat producer message & Bootup message */ - if((HBtime != 0 && NMT->HBproducerTimer >= HBtime) || NMT->operatingState == CO_NMT_INITIALIZING){ - - /* Start from the beginning. If OS is slow, time sliding may occur. However, heartbeat is - * not for synchronization, it is for health report. */ - NMT->HBproducerTimer = 0; - - NMT->HB_TXbuff->data[0] = NMT->operatingState; - CO_CANsend(NMT->HB_CANdev, NMT->HB_TXbuff); - - if(NMT->operatingState == CO_NMT_INITIALIZING){ - if(HBtime > NMT->firstHBTime) NMT->HBproducerTimer = HBtime - NMT->firstHBTime; - else NMT->HBproducerTimer = 0; - - /* NMT slave self starting */ - if (NMTstartup == 0x00000008U) NMT->operatingState = CO_NMT_OPERATIONAL; - else NMT->operatingState = CO_NMT_PRE_OPERATIONAL; - } - } - - - /* Calculate, when next Heartbeat needs to be send and lower timerNext_ms if necessary. */ - if(HBtime != 0 && timerNext_ms != NULL){ - if(NMT->HBproducerTimer < HBtime){ - uint16_t diff = HBtime - NMT->HBproducerTimer; - if(*timerNext_ms > diff){ - *timerNext_ms = diff; - } - }else{ - *timerNext_ms = 0; - } - } - - - /* CAN passive flag */ - CANpassive = 0; - if(CO_isError(NMT->emPr->em, CO_EM_CAN_TX_BUS_PASSIVE) || CO_isError(NMT->emPr->em, CO_EM_CAN_RX_BUS_PASSIVE)) - CANpassive = 1; - - -#ifdef CO_USE_LEDS - /* CANopen green RUN LED (DR 303-3) */ - switch(NMT->operatingState){ - case CO_NMT_STOPPED: NMT->LEDgreenRun = NMT->LEDsingleFlash; break; - case CO_NMT_PRE_OPERATIONAL: NMT->LEDgreenRun = NMT->LEDblinking; break; - case CO_NMT_OPERATIONAL: NMT->LEDgreenRun = 1; break; - default: break; - } - - - /* CANopen red ERROR LED (DR 303-3) */ - if(CO_isError(NMT->emPr->em, CO_EM_CAN_TX_BUS_OFF)) - NMT->LEDredError = 1; - - else if(CO_isError(NMT->emPr->em, CO_EM_SYNC_TIME_OUT)) - NMT->LEDredError = NMT->LEDtripleFlash; - - else if(CO_isError(NMT->emPr->em, CO_EM_HEARTBEAT_CONSUMER) || CO_isError(NMT->emPr->em, CO_EM_HB_CONSUMER_REMOTE_RESET)) - NMT->LEDredError = NMT->LEDdoubleFlash; - - else if(CANpassive || CO_isError(NMT->emPr->em, CO_EM_CAN_BUS_WARNING)) - NMT->LEDredError = NMT->LEDsingleFlash; - - else if(errorRegister) - NMT->LEDredError = (NMT->LEDblinking>=0)?-1:1; - - else - NMT->LEDredError = -1; -#endif /* CO_USE_LEDS */ - - - /* in case of error enter pre-operational state */ - if(errorBehavior && (NMT->operatingState == CO_NMT_OPERATIONAL)){ - if(CANpassive && (errorBehavior[2] == 0 || errorBehavior[2] == 2)) errorRegister |= 0x10; - - if(errorRegister){ - /* Communication error */ - if(errorRegister & CO_ERR_REG_COMM_ERR){ - if(errorBehavior[1] == 0){ - NMT->operatingState = CO_NMT_PRE_OPERATIONAL; - } - else if(errorBehavior[1] == 2){ - NMT->operatingState = CO_NMT_STOPPED; - } - else if(CO_isError(NMT->emPr->em, CO_EM_CAN_TX_BUS_OFF) - || CO_isError(NMT->emPr->em, CO_EM_HEARTBEAT_CONSUMER) - || CO_isError(NMT->emPr->em, CO_EM_HB_CONSUMER_REMOTE_RESET)) - { - if(errorBehavior[0] == 0){ - NMT->operatingState = CO_NMT_PRE_OPERATIONAL; - } - else if(errorBehavior[0] == 2){ - NMT->operatingState = CO_NMT_STOPPED; - } - } - } - - /* Generic error */ - if(errorRegister & CO_ERR_REG_GENERIC_ERR){ - if (errorBehavior[3] == 0) NMT->operatingState = CO_NMT_PRE_OPERATIONAL; - else if (errorBehavior[3] == 2) NMT->operatingState = CO_NMT_STOPPED; - } - - /* Device profile error */ - if(errorRegister & CO_ERR_REG_DEV_PROFILE){ - if (errorBehavior[4] == 0) NMT->operatingState = CO_NMT_PRE_OPERATIONAL; - else if (errorBehavior[4] == 2) NMT->operatingState = CO_NMT_STOPPED; - } - - /* Manufacturer specific error */ - if(errorRegister & CO_ERR_REG_MANUFACTURER){ - if (errorBehavior[5] == 0) NMT->operatingState = CO_NMT_PRE_OPERATIONAL; - else if (errorBehavior[5] == 2) NMT->operatingState = CO_NMT_STOPPED; - } - - /* if operational state is lost, send HB immediately. */ - if(NMT->operatingState != CO_NMT_OPERATIONAL) - NMT->HBproducerTimer = HBtime; - } - } - - if(NMT->pFunctNMT!=NULL && currentOperatingState!=NMT->operatingState){ - NMT->pFunctNMT(NMT->operatingState); - } - - return NMT->resetCommand; -} - - -/******************************************************************************/ -CO_NMT_internalState_t CO_NMT_getInternalState( - CO_NMT_t *NMT) -{ - if(NMT != NULL){ - return NMT->operatingState; - } - return CO_NMT_INITIALIZING; -} - diff --git a/stack/CO_NMT_Heartbeat.h b/stack/CO_NMT_Heartbeat.h deleted file mode 100644 index 9199fde8..00000000 --- a/stack/CO_NMT_Heartbeat.h +++ /dev/null @@ -1,261 +0,0 @@ -/** - * CANopen Network management and Heartbeat producer protocol. - * - * @file CO_NMT_Heartbeat.h - * @ingroup CO_NMT_Heartbeat - * @author Janez Paternoster - * @copyright 2004 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#ifndef CO_NMT_HEARTBEAT_H -#define CO_NMT_HEARTBEAT_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @defgroup CO_NMT_Heartbeat NMT and Heartbeat - * @ingroup CO_CANopen - * @{ - * - * CANopen Network management and Heartbeat producer protocol. - * - * CANopen device can be in one of the #CO_NMT_internalState_t - * - Initializing. It is active before CANopen is initialized. - * - Pre-operational. All CANopen objects are active, except PDOs. - * - Operational. Process data objects (PDOs) are active too. - * - Stopped. Only Heartbeat producer and NMT consumer are active. - * - * NMT master can change the internal state of the devices by sending - * #CO_NMT_command_t. - * - * ###NMT message contents: - * - * Byte | Description - * -----|----------------------------------------------------------- - * 0 | #CO_NMT_command_t - * 1 | Node ID. If zero, command addresses all nodes. - * - * ###Heartbeat message contents: - * - * Byte | Description - * -----|----------------------------------------------------------- - * 0 | #CO_NMT_internalState_t - * - * @see #CO_Default_CAN_ID_t - * - * ###Status LED diodes - * Macros for @ref CO_NMT_statusLEDdiodes are also implemented in this object. - */ - - -/** - * @defgroup CO_NMT_statusLEDdiodes Status LED diodes - * @{ - * - * Macros for status LED diodes. - * - * Helper macros for implementing status LED diodes are used by stack and can - * also be used by the application. If macro returns 1 LED should be ON, - * otherwise OFF. Function CO_NMT_blinkingProcess50ms() must be called cyclically - * to update the variables. - */ - #define LED_FLICKERING(NMT) (((NMT)->LEDflickering>=0) ? 1 : 0) /**< 10HZ (100MS INTERVAL) */ - #define LED_BLINKING(NMT) (((NMT)->LEDblinking>=0) ? 1 : 0) /**< 2.5HZ (400MS INTERVAL) */ - #define LED_SINGLE_FLASH(NMT) (((NMT)->LEDsingleFlash>=0) ? 1 : 0) /**< 200MS ON, 1000MS OFF */ - #define LED_DOUBLE_FLASH(NMT) (((NMT)->LEDdoubleFlash>=0) ? 1 : 0) /**< 200MS ON, 200MS OFF, 200MS ON, 1000MS OFF */ - #define LED_TRIPLE_FLASH(NMT) (((NMT)->LEDtripleFlash>=0) ? 1 : 0) /**< 200MS ON, 200MS OFF, 200MS ON, 200MS OFF, 200MS ON, 1000MS OFF */ - #define LED_QUADRUPLE_FLASH(NMT)(((NMT)->LEDquadrupleFlash>=0) ? 1 : 0) /**< 200MS ON, 200MS OFF, 200MS ON, 200MS OFF, 200MS ON, 200MS OFF, 200MS ON, 1000MS OFF */ - #define LED_GREEN_RUN(NMT) (((NMT)->LEDgreenRun>=0) ? 1 : 0) /**< CANOPEN RUN LED ACCORDING TO CIA DR 303-3 */ - #define LED_RED_ERROR(NMT) (((NMT)->LEDredError>=0) ? 1 : 0) /**< CANopen error LED according to CiA DR 303-3 */ -/** @} */ - - -/** - * Internal network state of the CANopen node - */ -typedef enum{ - CO_NMT_INITIALIZING = 0, /**< Device is initializing */ - CO_NMT_PRE_OPERATIONAL = 127, /**< Device is in pre-operational state */ - CO_NMT_OPERATIONAL = 5, /**< Device is in operational state */ - CO_NMT_STOPPED = 4 /**< Device is stopped */ -}CO_NMT_internalState_t; - - -/** - * Commands from NMT master. - */ -typedef enum{ - CO_NMT_ENTER_OPERATIONAL = 1, /**< Start device */ - CO_NMT_ENTER_STOPPED = 2, /**< Stop device */ - CO_NMT_ENTER_PRE_OPERATIONAL = 128, /**< Put device into pre-operational */ - CO_NMT_RESET_NODE = 129, /**< Reset device */ - CO_NMT_RESET_COMMUNICATION = 130 /**< Reset CANopen communication on device */ -}CO_NMT_command_t; - - -/** - * Return code for CO_NMT_process() that tells application code what to - * reset. - */ -typedef enum{ - CO_RESET_NOT = 0,/**< Normal return, no action */ - CO_RESET_COMM = 1,/**< Application must provide communication reset. */ - CO_RESET_APP = 2,/**< Application must provide complete device reset */ - CO_RESET_QUIT = 3 /**< Application must quit, no reset of microcontroller (command is not requested by the stack.) */ -}CO_NMT_reset_cmd_t; - - -/** - * NMT consumer and Heartbeat producer object. It includes also variables for - * @ref CO_NMT_statusLEDdiodes. Object is initialized by CO_NMT_init(). - */ -typedef struct{ - uint8_t operatingState; /**< See @ref CO_NMT_internalState_t */ -#ifdef CO_USE_LEDS - int8_t LEDflickering; /**< See @ref CO_NMT_statusLEDdiodes */ - int8_t LEDblinking; /**< See @ref CO_NMT_statusLEDdiodes */ - int8_t LEDsingleFlash; /**< See @ref CO_NMT_statusLEDdiodes */ - int8_t LEDdoubleFlash; /**< See @ref CO_NMT_statusLEDdiodes */ - int8_t LEDtripleFlash; /**< See @ref CO_NMT_statusLEDdiodes */ - int8_t LEDquadrupleFlash; /**< See @ref CO_NMT_statusLEDdiodes */ - int8_t LEDgreenRun; /**< See @ref CO_NMT_statusLEDdiodes */ - int8_t LEDredError; /**< See @ref CO_NMT_statusLEDdiodes */ -#endif /* CO_USE_LEDS */ - - uint8_t resetCommand; /**< If different than zero, device will reset */ - uint8_t nodeId; /**< CANopen Node ID of this device */ - uint16_t HBproducerTimer;/**< Internal timer for HB producer */ - uint16_t firstHBTime; /**< From CO_NMT_init() */ - CO_EMpr_t *emPr; /**< From CO_NMT_init() */ - CO_CANmodule_t *HB_CANdev; /**< From CO_NMT_init() */ - void (*pFunctNMT)(CO_NMT_internalState_t state); /**< From CO_NMT_initCallback() or NULL */ - CO_CANtx_t *HB_TXbuff; /**< CAN transmit buffer */ -}CO_NMT_t; - - -/** - * Initialize NMT and Heartbeat producer object. - * - * Function must be called in the communication reset section. - * - * @param NMT This object will be initialized. - * @param emPr Emergency main object. - * @param nodeId CANopen Node ID of this device. - * @param firstHBTime Time between bootup and first heartbeat message in milliseconds. - * If firstHBTime is greater than _Producer Heartbeat time_ - * (object dictionary, index 0x1017), latter is used instead. - * @param NMT_CANdev CAN device for NMT reception. - * @param NMT_rxIdx Index of receive buffer in above CAN device. - * @param CANidRxNMT CAN identifier for NMT message. - * @param HB_CANdev CAN device for HB transmission. - * @param HB_txIdx Index of transmit buffer in the above CAN device. - * @param CANidTxHB CAN identifier for HB message. - * - * @return #CO_ReturnError_t CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT. - */ -CO_ReturnError_t CO_NMT_init( - CO_NMT_t *NMT, - CO_EMpr_t *emPr, - uint8_t nodeId, - uint16_t firstHBTime, - CO_CANmodule_t *NMT_CANdev, - uint16_t NMT_rxIdx, - uint16_t CANidRxNMT, - CO_CANmodule_t *HB_CANdev, - uint16_t HB_txIdx, - uint16_t CANidTxHB); - -/** - * Initialize NMT callback function. - * - * Function initializes optional callback function, which is called after - * NMT State change has occured. Function may wake up external task which - * handles NMT events. - * The first call is made immediately to give the consumer the current NMT state. - * - * @remark Be aware that the callback function is run inside the CAN receive - * function context. Depending on the driver, this might be inside an interrupt! - * - * @param NMT This object. - * @param pFunctNMT Pointer to the callback function. Not called if NULL. - */ -void CO_NMT_initCallback( - CO_NMT_t *NMT, - void (*pFunctNMT)(CO_NMT_internalState_t state)); - - -/** - * Calculate blinking bytes. - * - * Function must be called cyclically every 50 milliseconds. See @ref CO_NMT_statusLEDdiodes. - * - * @param NMT NMT object. - */ -#ifdef CO_USE_LEDS -void CO_NMT_blinkingProcess50ms(CO_NMT_t *NMT); -#endif /* CO_USE_LEDS */ - - -/** - * Process received NMT and produce Heartbeat messages. - * - * Function must be called cyclically. - * - * @param NMT This object. - * @param timeDifference_ms Time difference from previous function call in [milliseconds]. - * @param HBtime _Producer Heartbeat time_ (object dictionary, index 0x1017). - * @param NMTstartup _NMT startup behavior_ (object dictionary, index 0x1F80). - * @param errorRegister _Error register_ (object dictionary, index 0x1001). - * @param errorBehavior pointer to _Error behavior_ array (object dictionary, index 0x1029). - * Object controls, if device should leave NMT operational state. - * Length of array must be 6. If pointer is NULL, no calculation is made. - * @param timerNext_ms Return value - info to OS - see CO_process(). - * - * @return #CO_NMT_reset_cmd_t - */ -CO_NMT_reset_cmd_t CO_NMT_process( - CO_NMT_t *NMT, - uint16_t timeDifference_ms, - uint16_t HBtime, - uint32_t NMTstartup, - uint8_t errorRegister, - const uint8_t errorBehavior[], - uint16_t *timerNext_ms); - - -/** - * Query current NMT state - * - * @param NMT This object. - * - * @return #CO_NMT_internalState_t - */ -CO_NMT_internalState_t CO_NMT_getInternalState( - CO_NMT_t *NMT); - - -#ifdef __cplusplus -} -#endif /*__cplusplus*/ - -/** @} */ -#endif diff --git a/stack/CO_PDO.c b/stack/CO_PDO.c deleted file mode 100644 index 1696d394..00000000 --- a/stack/CO_PDO.c +++ /dev/null @@ -1,1029 +0,0 @@ -/* - * CANopen Process Data Object. - * - * @file CO_PDO.c - * @ingroup CO_PDO - * @author Janez Paternoster - * @copyright 2004 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include "CO_driver.h" -#include "CO_SDO.h" -#include "CO_Emergency.h" -#include "CO_NMT_Heartbeat.h" -#include "CO_SYNC.h" -#include "CO_PDO.h" - -/* - * Read received message from CAN module. - * - * Function will be called (by CAN receive interrupt) every time, when CAN - * message with correct identifier will be received. For more information and - * description of parameters see file CO_driver.h. - * If new message arrives and previous message wasn't processed yet, then - * previous message will be lost and overwritten by new message. That's OK with PDOs. - */ -static void CO_PDO_receive(void *object, const CO_CANrxMsg_t *msg){ - CO_RPDO_t *RPDO; - - RPDO = (CO_RPDO_t*)object; /* this is the correct pointer type of the first argument */ - - if( (RPDO->valid) && - (*RPDO->operatingState == CO_NMT_OPERATIONAL) && - (msg->DLC >= RPDO->dataLength)) - { - if(RPDO->SYNC && RPDO->synchronous && RPDO->SYNC->CANrxToggle) { - /* copy data into second buffer and set 'new message' flag */ - RPDO->CANrxData[1][0] = msg->data[0]; - RPDO->CANrxData[1][1] = msg->data[1]; - RPDO->CANrxData[1][2] = msg->data[2]; - RPDO->CANrxData[1][3] = msg->data[3]; - RPDO->CANrxData[1][4] = msg->data[4]; - RPDO->CANrxData[1][5] = msg->data[5]; - RPDO->CANrxData[1][6] = msg->data[6]; - RPDO->CANrxData[1][7] = msg->data[7]; - - SET_CANrxNew(RPDO->CANrxNew[1]); - } - else { - /* copy data into default buffer and set 'new message' flag */ - RPDO->CANrxData[0][0] = msg->data[0]; - RPDO->CANrxData[0][1] = msg->data[1]; - RPDO->CANrxData[0][2] = msg->data[2]; - RPDO->CANrxData[0][3] = msg->data[3]; - RPDO->CANrxData[0][4] = msg->data[4]; - RPDO->CANrxData[0][5] = msg->data[5]; - RPDO->CANrxData[0][6] = msg->data[6]; - RPDO->CANrxData[0][7] = msg->data[7]; - - SET_CANrxNew(RPDO->CANrxNew[0]); - } - } -} - - -/* - * Configure RPDO Communication parameter. - * - * Function is called from commuincation reset or when parameter changes. - * - * Function configures following variable from CO_RPDO_t: _valid_. It also - * configures CAN rx buffer. If configuration fails, emergency message is send - * and device is not able to enter NMT operational. - * - * @param RPDO RPDO object. - * @param COB_IDUsedByRPDO _RPDO communication parameter_, _COB-ID for PDO_ variable - * from Object dictionary (index 0x1400+, subindex 1). - */ -static void CO_RPDOconfigCom(CO_RPDO_t* RPDO, uint32_t COB_IDUsedByRPDO){ - uint16_t ID; - CO_ReturnError_t r; - - ID = (uint16_t)COB_IDUsedByRPDO; - - /* is RPDO used? */ - if((COB_IDUsedByRPDO & 0xBFFFF800L) == 0 && RPDO->dataLength && ID){ - /* is used default COB-ID? */ - if(ID == RPDO->defaultCOB_ID) ID += RPDO->nodeId; - RPDO->valid = true; - RPDO->synchronous = (RPDO->RPDOCommPar->transmissionType <= 240) ? true : false; - } - else{ - ID = 0; - RPDO->valid = false; - CLEAR_CANrxNew(RPDO->CANrxNew[0]); - CLEAR_CANrxNew(RPDO->CANrxNew[1]); - } - r = CO_CANrxBufferInit( - RPDO->CANdevRx, /* CAN device */ - RPDO->CANdevRxIdx, /* rx buffer index */ - ID, /* CAN identifier */ - 0x7FF, /* mask */ - 0, /* rtr */ - (void*)RPDO, /* object passed to receive function */ - CO_PDO_receive); /* this function will process received message */ - if(r != CO_ERROR_NO){ - RPDO->valid = false; - CLEAR_CANrxNew(RPDO->CANrxNew[0]); - CLEAR_CANrxNew(RPDO->CANrxNew[1]); - } -} - - -/* - * Configure TPDO Communication parameter. - * - * Function is called from commuincation reset or when parameter changes. - * - * Function configures following variable from CO_TPDO_t: _valid_. It also - * configures CAN tx buffer. If configuration fails, emergency message is send - * and device is not able to enter NMT operational. - * - * @param TPDO TPDO object. - * @param COB_IDUsedByTPDO _TPDO communication parameter_, _COB-ID for PDO_ variable - * from Object dictionary (index 0x1400+, subindex 1). - * @param syncFlag Indicate, if TPDO is synchronous. - */ -static void CO_TPDOconfigCom(CO_TPDO_t* TPDO, uint32_t COB_IDUsedByTPDO, uint8_t syncFlag){ - uint16_t ID; - - ID = (uint16_t)COB_IDUsedByTPDO; - - /* is TPDO used? */ - if((COB_IDUsedByTPDO & 0xBFFFF800L) == 0 && TPDO->dataLength && ID){ - /* is used default COB-ID? */ - if(ID == TPDO->defaultCOB_ID) ID += TPDO->nodeId; - TPDO->valid = true; - } - else{ - ID = 0; - TPDO->valid = false; - } - - TPDO->CANtxBuff = CO_CANtxBufferInit( - TPDO->CANdevTx, /* CAN device */ - TPDO->CANdevTxIdx, /* index of specific buffer inside CAN module */ - ID, /* CAN identifier */ - 0, /* rtr */ - TPDO->dataLength, /* number of data bytes */ - syncFlag); /* synchronous message flag bit */ - - if(TPDO->CANtxBuff == 0){ - TPDO->valid = false; - } -} - - -/* - * Find mapped variable in Object Dictionary. - * - * Function is called from CO_R(T)PDOconfigMap or when mapping parameter changes. - * - * @param SDO SDO object. - * @param map PDO mapping parameter. - * @param R_T 0 for RPDO map, 1 for TPDO map. - * @param ppData Pointer to returning parameter: pointer to data of mapped variable. - * @param pLength Pointer to returning parameter: *add* length of mapped variable. - * @param pSendIfCOSFlags Pointer to returning parameter: sendIfCOSFlags variable. - * @param pIsMultibyteVar Pointer to returning parameter: true for multibyte variable. - * - * @return 0 on success, otherwise SDO abort code. - */ -static uint32_t CO_PDOfindMap( - CO_SDO_t *SDO, - uint32_t map, - uint8_t R_T, - uint8_t **ppData, - uint8_t *pLength, - uint8_t *pSendIfCOSFlags, - uint8_t *pIsMultibyteVar) -{ - uint16_t entryNo; - uint16_t index; - uint8_t subIndex; - uint8_t dataLen; - uint8_t objectLen; - uint8_t attr; - - index = (uint16_t)(map>>16); - subIndex = (uint8_t)(map>>8); - dataLen = (uint8_t) map; /* data length in bits */ - - /* data length must be byte aligned */ - if(dataLen&0x07) return CO_SDO_AB_NO_MAP; /* Object cannot be mapped to the PDO. */ - - dataLen >>= 3; /* new data length is in bytes */ - *pLength += dataLen; - - /* total PDO length can not be more than 8 bytes */ - if(*pLength > 8) return CO_SDO_AB_MAP_LEN; /* The number and length of the objects to be mapped would exceed PDO length. */ - - /* is there a reference to dummy entries */ - if(index <=7 && subIndex == 0){ - static uint32_t dummyTX = 0; - static uint32_t dummyRX; - uint8_t dummySize = 4; - - if(index<2) dummySize = 0; - else if(index==2 || index==5) dummySize = 1; - else if(index==3 || index==6) dummySize = 2; - - /* is size of variable big enough for map */ - if(dummySize < dataLen) return CO_SDO_AB_NO_MAP; /* Object cannot be mapped to the PDO. */ - - /* Data and ODE pointer */ - if(R_T == 0) *ppData = (uint8_t*) &dummyRX; - else *ppData = (uint8_t*) &dummyTX; - - return 0; - } - - /* find object in Object Dictionary */ - entryNo = CO_OD_find(SDO, index); - - /* Does object exist in OD? */ - if(entryNo == 0xFFFF || subIndex > SDO->OD[entryNo].maxSubIndex) - return CO_SDO_AB_NOT_EXIST; /* Object does not exist in the object dictionary. */ - - attr = CO_OD_getAttribute(SDO, entryNo, subIndex); - /* Is object Mappable for RPDO? */ - if(R_T==0 && !((attr&CO_ODA_RPDO_MAPABLE) && (attr&CO_ODA_WRITEABLE))) return CO_SDO_AB_NO_MAP; /* Object cannot be mapped to the PDO. */ - /* Is object Mappable for TPDO? */ - if(R_T!=0 && !((attr&CO_ODA_TPDO_MAPABLE) && (attr&CO_ODA_READABLE))) return CO_SDO_AB_NO_MAP; /* Object cannot be mapped to the PDO. */ - - /* is size of variable big enough for map */ - objectLen = CO_OD_getLength(SDO, entryNo, subIndex); - if(objectLen < dataLen) return CO_SDO_AB_NO_MAP; /* Object cannot be mapped to the PDO. */ - - /* mark multibyte variable */ - *pIsMultibyteVar = (attr&CO_ODA_MB_VALUE) ? 1 : 0; - - /* pointer to data */ - *ppData = (uint8_t*) CO_OD_getDataPointer(SDO, entryNo, subIndex); -#ifdef CO_BIG_ENDIAN - /* skip unused MSB bytes */ - if(*pIsMultibyteVar){ - *ppData += objectLen - dataLen; - } -#endif - - /* setup change of state flags */ - if(attr&CO_ODA_TPDO_DETECT_COS){ - int16_t i; - for(i=*pLength-dataLen; i<*pLength; i++){ - *pSendIfCOSFlags |= 1<RPDOMapPar->mappedObject1; - - for(i=noOfMappedObjects; i>0; i--){ - int16_t j; - uint8_t* pData; - uint8_t dummy = 0; - uint8_t prevLength = length; - uint8_t MBvar; - uint32_t map = *(pMap++); - - /* function do much checking of errors in map */ - ret = CO_PDOfindMap( - RPDO->SDO, - map, - 0, - &pData, - &length, - &dummy, - &MBvar); - if(ret){ - length = 0; - CO_errorReport(RPDO->em, CO_EM_PDO_WRONG_MAPPING, CO_EMC_PROTOCOL_ERROR, map); - break; - } - - /* write PDO data pointers */ -#ifdef CO_BIG_ENDIAN - if(MBvar){ - for(j=length-1; j>=prevLength; j--) - RPDO->mapPointer[j] = pData++; - } - else{ - for(j=prevLength; jmapPointer[j] = pData++; - } -#else - for(j=prevLength; jmapPointer[j] = pData++; - } -#endif - - } - - RPDO->dataLength = length; - - return ret; -} - - -/* - * Configure TPDO Mapping parameter. - * - * Function is called from communication reset or when parameter changes. - * - * Function configures following variables from CO_TPDO_t: _dataLength_, - * _mapPointer_ and _sendIfCOSFlags_. - * - * @param TPDO TPDO object. - * @param noOfMappedObjects Number of mapped object (from OD). - * - * @return 0 on success, otherwise SDO abort code. - */ -static uint32_t CO_TPDOconfigMap(CO_TPDO_t* TPDO, uint8_t noOfMappedObjects){ - int16_t i; - uint8_t length = 0; - uint32_t ret = 0; - const uint32_t* pMap = &TPDO->TPDOMapPar->mappedObject1; - - TPDO->sendIfCOSFlags = 0; - - for(i=noOfMappedObjects; i>0; i--){ - int16_t j; - uint8_t* pData; - uint8_t prevLength = length; - uint8_t MBvar; - uint32_t map = *(pMap++); - - /* function do much checking of errors in map */ - ret = CO_PDOfindMap( - TPDO->SDO, - map, - 1, - &pData, - &length, - &TPDO->sendIfCOSFlags, - &MBvar); - if(ret){ - length = 0; - CO_errorReport(TPDO->em, CO_EM_PDO_WRONG_MAPPING, CO_EMC_PROTOCOL_ERROR, map); - break; - } - - /* write PDO data pointers */ -#ifdef CO_BIG_ENDIAN - if(MBvar){ - for(j=length-1; j>=prevLength; j--) - TPDO->mapPointer[j] = pData++; - } - else{ - for(j=prevLength; jmapPointer[j] = pData++; - } -#else - for(j=prevLength; jmapPointer[j] = pData++; - } -#endif - - } - - TPDO->dataLength = length; - - return ret; -} - - -/* - * Function for accessing _RPDO communication parameter_ (index 0x1400+) from SDO server. - * - * For more information see file CO_SDO.h. - */ -static CO_SDO_abortCode_t CO_ODF_RPDOcom(CO_ODF_arg_t *ODF_arg){ - CO_RPDO_t *RPDO; - - RPDO = (CO_RPDO_t*) ODF_arg->object; - - /* Reading Object Dictionary variable */ - if(ODF_arg->reading){ - if(ODF_arg->subIndex == 1){ - uint32_t value = CO_getUint32(ODF_arg->data); - - /* if default COB ID is used, write default value here */ - if(((value)&0xFFFF) == RPDO->defaultCOB_ID && RPDO->defaultCOB_ID) - value += RPDO->nodeId; - - /* If PDO is not valid, set bit 31 */ - if(!RPDO->valid) value |= 0x80000000L; - - CO_setUint32(ODF_arg->data, value); - } - return CO_SDO_AB_NONE; - } - - /* Writing Object Dictionary variable */ - if(RPDO->restrictionFlags & 0x04) - return CO_SDO_AB_READONLY; /* Attempt to write a read only object. */ - if(*RPDO->operatingState == CO_NMT_OPERATIONAL && (RPDO->restrictionFlags & 0x01)) - return CO_SDO_AB_DATA_DEV_STATE; /* Data cannot be transferred or stored to the application because of the present device state. */ - - if(ODF_arg->subIndex == 1){ /* COB_ID */ - uint32_t value = CO_getUint32(ODF_arg->data); - - /* bits 11...29 must be zero */ - if(value & 0x3FFF8000L) - return CO_SDO_AB_INVALID_VALUE; /* Invalid value for parameter (download only). */ - - /* if default COB-ID is being written, write defaultCOB_ID without nodeId */ - if(((value)&0xFFFF) == (RPDO->defaultCOB_ID + RPDO->nodeId)){ - value &= 0xC0000000L; - value += RPDO->defaultCOB_ID; - CO_setUint32(ODF_arg->data, value); - } - - /* if PDO is valid, bits 0..29 can not be changed */ - if(RPDO->valid && ((value ^ RPDO->RPDOCommPar->COB_IDUsedByRPDO) & 0x3FFFFFFFL)) - return CO_SDO_AB_INVALID_VALUE; /* Invalid value for parameter (download only). */ - - /* configure RPDO */ - CO_RPDOconfigCom(RPDO, value); - } - else if(ODF_arg->subIndex == 2){ /* Transmission_type */ - uint8_t *value = (uint8_t*) ODF_arg->data; - bool_t synchronousPrev = RPDO->synchronous; - - /* values from 241...253 are not valid */ - if(*value >= 241 && *value <= 253) - return CO_SDO_AB_INVALID_VALUE; /* Invalid value for parameter (download only). */ - - RPDO->synchronous = (*value <= 240) ? true : false; - - /* Remove old message from second buffer. */ - if(RPDO->synchronous != synchronousPrev) { - CLEAR_CANrxNew(RPDO->CANrxNew[1]); - } - } - - return CO_SDO_AB_NONE; -} - - -/* - * Function for accessing _TPDO communication parameter_ (index 0x1800+) from SDO server. - * - * For more information see file CO_SDO.h. - */ -static CO_SDO_abortCode_t CO_ODF_TPDOcom(CO_ODF_arg_t *ODF_arg){ - CO_TPDO_t *TPDO; - - TPDO = (CO_TPDO_t*) ODF_arg->object; - - if(ODF_arg->subIndex == 4) return CO_SDO_AB_SUB_UNKNOWN; /* Sub-index does not exist. */ - - /* Reading Object Dictionary variable */ - if(ODF_arg->reading){ - if(ODF_arg->subIndex == 1){ /* COB_ID */ - uint32_t value = CO_getUint32(ODF_arg->data); - - /* if default COB ID is used, write default value here */ - if(((value)&0xFFFF) == TPDO->defaultCOB_ID && TPDO->defaultCOB_ID) - value += TPDO->nodeId; - - /* If PDO is not valid, set bit 31 */ - if(!TPDO->valid) value |= 0x80000000L; - - CO_setUint32(ODF_arg->data, value); - } - return CO_SDO_AB_NONE; - } - - /* Writing Object Dictionary variable */ - if(TPDO->restrictionFlags & 0x04) - return CO_SDO_AB_READONLY; /* Attempt to write a read only object. */ - if(*TPDO->operatingState == CO_NMT_OPERATIONAL && (TPDO->restrictionFlags & 0x01)) - return CO_SDO_AB_DATA_DEV_STATE; /* Data cannot be transferred or stored to the application because of the present device state. */ - - if(ODF_arg->subIndex == 1){ /* COB_ID */ - uint32_t value = CO_getUint32(ODF_arg->data); - - /* bits 11...29 must be zero */ - if(value & 0x3FFF8000L) - return CO_SDO_AB_INVALID_VALUE; /* Invalid value for parameter (download only). */ - - /* if default COB-ID is being written, write defaultCOB_ID without nodeId */ - if(((value)&0xFFFF) == (TPDO->defaultCOB_ID + TPDO->nodeId)){ - value &= 0xC0000000L; - value += TPDO->defaultCOB_ID; - - CO_setUint32(ODF_arg->data, value); - } - - /* if PDO is valid, bits 0..29 can not be changed */ - if(TPDO->valid && ((value ^ TPDO->TPDOCommPar->COB_IDUsedByTPDO) & 0x3FFFFFFFL)) - return CO_SDO_AB_INVALID_VALUE; /* Invalid value for parameter (download only). */ - - /* configure TPDO */ - CO_TPDOconfigCom(TPDO, value, TPDO->CANtxBuff->syncFlag); - TPDO->syncCounter = 255; - } - else if(ODF_arg->subIndex == 2){ /* Transmission_type */ - uint8_t *value = (uint8_t*) ODF_arg->data; - - /* values from 241...253 are not valid */ - if(*value >= 241 && *value <= 253) - return CO_SDO_AB_INVALID_VALUE; /* Invalid value for parameter (download only). */ - TPDO->CANtxBuff->syncFlag = (*value <= 240) ? 1 : 0; - TPDO->syncCounter = 255; - } - else if(ODF_arg->subIndex == 3){ /* Inhibit_Time */ - /* if PDO is valid, value can not be changed */ - if(TPDO->valid) - return CO_SDO_AB_INVALID_VALUE; /* Invalid value for parameter (download only). */ - - TPDO->inhibitTimer = 0; - } - else if(ODF_arg->subIndex == 5){ /* Event_Timer */ - uint16_t value = CO_getUint16(ODF_arg->data); - - TPDO->eventTimer = ((uint32_t) value) * 1000; - } - else if(ODF_arg->subIndex == 6){ /* SYNC start value */ - uint8_t *value = (uint8_t*) ODF_arg->data; - - /* if PDO is valid, value can not be changed */ - if(TPDO->valid) - return CO_SDO_AB_INVALID_VALUE; /* Invalid value for parameter (download only). */ - - /* values from 240...255 are not valid */ - if(*value > 240) - return CO_SDO_AB_INVALID_VALUE; /* Invalid value for parameter (download only). */ - } - - return CO_SDO_AB_NONE; -} - - -/* - * Function for accessing _RPDO mapping parameter_ (index 0x1600+) from SDO server. - * - * For more information see file CO_SDO.h. - */ -static CO_SDO_abortCode_t CO_ODF_RPDOmap(CO_ODF_arg_t *ODF_arg){ - CO_RPDO_t *RPDO; - - RPDO = (CO_RPDO_t*) ODF_arg->object; - - /* Reading Object Dictionary variable */ - if(ODF_arg->reading){ - uint8_t *value = (uint8_t*) ODF_arg->data; - - if(ODF_arg->subIndex == 0){ - /* If there is error in mapping, dataLength is 0, so numberOfMappedObjects is 0. */ - if(!RPDO->dataLength) *value = 0; - } - return CO_SDO_AB_NONE; - } - - /* Writing Object Dictionary variable */ - if(RPDO->restrictionFlags & 0x08) - return CO_SDO_AB_READONLY; /* Attempt to write a read only object. */ - if(*RPDO->operatingState == CO_NMT_OPERATIONAL && (RPDO->restrictionFlags & 0x02)) - return CO_SDO_AB_DATA_DEV_STATE; /* Data cannot be transferred or stored to the application because of the present device state. */ - if(RPDO->valid) - return CO_SDO_AB_UNSUPPORTED_ACCESS; /* Unsupported access to an object. */ - - /* numberOfMappedObjects */ - if(ODF_arg->subIndex == 0){ - uint8_t *value = (uint8_t*) ODF_arg->data; - - if(*value > 8) - return CO_SDO_AB_MAP_LEN; /* Number and length of object to be mapped exceeds PDO length. */ - - /* configure mapping */ - return CO_RPDOconfigMap(RPDO, *value); - } - - /* mappedObject */ - else{ - uint32_t value = CO_getUint32(ODF_arg->data); - uint8_t* pData; - uint8_t length = 0; - uint8_t dummy = 0; - uint8_t MBvar; - - if(RPDO->dataLength) - return CO_SDO_AB_UNSUPPORTED_ACCESS; /* Unsupported access to an object. */ - - /* verify if mapping is correct */ - return CO_PDOfindMap( - RPDO->SDO, - value, - 0, - &pData, - &length, - &dummy, - &MBvar); - } - - return CO_SDO_AB_NONE; -} - - -/* - * Function for accessing _TPDO mapping parameter_ (index 0x1A00+) from SDO server. - * - * For more information see file CO_SDO.h. - */ -static CO_SDO_abortCode_t CO_ODF_TPDOmap(CO_ODF_arg_t *ODF_arg){ - CO_TPDO_t *TPDO; - - TPDO = (CO_TPDO_t*) ODF_arg->object; - - /* Reading Object Dictionary variable */ - if(ODF_arg->reading){ - uint8_t *value = (uint8_t*) ODF_arg->data; - - if(ODF_arg->subIndex == 0){ - /* If there is error in mapping, dataLength is 0, so numberOfMappedObjects is 0. */ - if(!TPDO->dataLength) *value = 0; - } - return CO_SDO_AB_NONE; - } - - /* Writing Object Dictionary variable */ - if(TPDO->restrictionFlags & 0x08) - return CO_SDO_AB_READONLY; /* Attempt to write a read only object. */ - if(*TPDO->operatingState == CO_NMT_OPERATIONAL && (TPDO->restrictionFlags & 0x02)) - return CO_SDO_AB_DATA_DEV_STATE; /* Data cannot be transferred or stored to the application because of the present device state. */ - if(TPDO->valid) - return CO_SDO_AB_UNSUPPORTED_ACCESS; /* Unsupported access to an object. */ - - /* numberOfMappedObjects */ - if(ODF_arg->subIndex == 0){ - uint8_t *value = (uint8_t*) ODF_arg->data; - - if(*value > 8) - return CO_SDO_AB_MAP_LEN; /* Number and length of object to be mapped exceeds PDO length. */ - - /* configure mapping */ - return CO_TPDOconfigMap(TPDO, *value); - } - - /* mappedObject */ - else{ - uint32_t value = CO_getUint32(ODF_arg->data); - uint8_t* pData; - uint8_t length = 0; - uint8_t dummy = 0; - uint8_t MBvar; - - if(TPDO->dataLength) - return CO_SDO_AB_UNSUPPORTED_ACCESS; /* Unsupported access to an object. */ - - /* verify if mapping is correct */ - return CO_PDOfindMap( - TPDO->SDO, - value, - 1, - &pData, - &length, - &dummy, - &MBvar); - } - - return CO_SDO_AB_NONE; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_RPDO_init( - CO_RPDO_t *RPDO, - CO_EM_t *em, - CO_SDO_t *SDO, - CO_SYNC_t *SYNC, - uint8_t *operatingState, - uint8_t nodeId, - uint16_t defaultCOB_ID, - uint8_t restrictionFlags, - const CO_RPDOCommPar_t *RPDOCommPar, - const CO_RPDOMapPar_t *RPDOMapPar, - uint16_t idx_RPDOCommPar, - uint16_t idx_RPDOMapPar, - CO_CANmodule_t *CANdevRx, - uint16_t CANdevRxIdx) -{ - /* verify arguments */ - if(RPDO==NULL || em==NULL || SDO==NULL || operatingState==NULL || - RPDOCommPar==NULL || RPDOMapPar==NULL || CANdevRx==NULL){ - return CO_ERROR_ILLEGAL_ARGUMENT; - } - - /* Configure object variables */ - RPDO->em = em; - RPDO->SDO = SDO; - RPDO->SYNC = SYNC; - RPDO->RPDOCommPar = RPDOCommPar; - RPDO->RPDOMapPar = RPDOMapPar; - RPDO->operatingState = operatingState; - RPDO->nodeId = nodeId; - RPDO->defaultCOB_ID = defaultCOB_ID; - RPDO->restrictionFlags = restrictionFlags; - - /* Configure Object dictionary entry at index 0x1400+ and 0x1600+ */ - CO_OD_configure(SDO, idx_RPDOCommPar, CO_ODF_RPDOcom, (void*)RPDO, 0, 0); - CO_OD_configure(SDO, idx_RPDOMapPar, CO_ODF_RPDOmap, (void*)RPDO, 0, 0); - - /* configure communication and mapping */ - CLEAR_CANrxNew(RPDO->CANrxNew[0]); - CLEAR_CANrxNew(RPDO->CANrxNew[1]); - RPDO->CANdevRx = CANdevRx; - RPDO->CANdevRxIdx = CANdevRxIdx; - - CO_RPDOconfigMap(RPDO, RPDOMapPar->numberOfMappedObjects); - CO_RPDOconfigCom(RPDO, RPDOCommPar->COB_IDUsedByRPDO); - - return CO_ERROR_NO; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_TPDO_init( - CO_TPDO_t *TPDO, - CO_EM_t *em, - CO_SDO_t *SDO, - CO_SYNC_t *SYNC, - uint8_t *operatingState, - uint8_t nodeId, - uint16_t defaultCOB_ID, - uint8_t restrictionFlags, - const CO_TPDOCommPar_t *TPDOCommPar, - const CO_TPDOMapPar_t *TPDOMapPar, - uint16_t idx_TPDOCommPar, - uint16_t idx_TPDOMapPar, - CO_CANmodule_t *CANdevTx, - uint16_t CANdevTxIdx) -{ - /* verify arguments */ - if(TPDO==NULL || em==NULL || SDO==NULL || operatingState==NULL || - TPDOCommPar==NULL || TPDOMapPar==NULL || CANdevTx==NULL){ - return CO_ERROR_ILLEGAL_ARGUMENT; - } - - /* Configure object variables */ - TPDO->em = em; - TPDO->SDO = SDO; - TPDO->SYNC = SYNC; - TPDO->TPDOCommPar = TPDOCommPar; - TPDO->TPDOMapPar = TPDOMapPar; - TPDO->operatingState = operatingState; - TPDO->nodeId = nodeId; - TPDO->defaultCOB_ID = defaultCOB_ID; - TPDO->restrictionFlags = restrictionFlags; - - /* Configure Object dictionary entry at index 0x1800+ and 0x1A00+ */ - CO_OD_configure(SDO, idx_TPDOCommPar, CO_ODF_TPDOcom, (void*)TPDO, 0, 0); - CO_OD_configure(SDO, idx_TPDOMapPar, CO_ODF_TPDOmap, (void*)TPDO, 0, 0); - - /* configure communication and mapping */ - TPDO->CANdevTx = CANdevTx; - TPDO->CANdevTxIdx = CANdevTxIdx; - TPDO->syncCounter = 255; - TPDO->inhibitTimer = 0; - TPDO->eventTimer = ((uint32_t) TPDOCommPar->eventTimer) * 1000; - if(TPDOCommPar->transmissionType>=254) TPDO->sendRequest = 1; - - CO_TPDOconfigMap(TPDO, TPDOMapPar->numberOfMappedObjects); - CO_TPDOconfigCom(TPDO, TPDOCommPar->COB_IDUsedByTPDO, ((TPDOCommPar->transmissionType<=240) ? 1 : 0)); - - if((TPDOCommPar->transmissionType>240 && - TPDOCommPar->transmissionType<254) || - TPDOCommPar->SYNCStartValue>240){ - TPDO->valid = false; - } - - return CO_ERROR_NO; -} - - -/******************************************************************************/ -uint8_t CO_TPDOisCOS(CO_TPDO_t *TPDO){ - - /* Prepare TPDO data automatically from Object Dictionary variables */ - uint8_t* pPDOdataByte; - uint8_t** ppODdataByte; - - pPDOdataByte = &TPDO->CANtxBuff->data[TPDO->dataLength]; - ppODdataByte = &TPDO->mapPointer[TPDO->dataLength]; - - switch(TPDO->dataLength){ - case 8: if(*(--pPDOdataByte) != **(--ppODdataByte) && (TPDO->sendIfCOSFlags&0x80)) return 1; // fallthrough - case 7: if(*(--pPDOdataByte) != **(--ppODdataByte) && (TPDO->sendIfCOSFlags&0x40)) return 1; // fallthrough - case 6: if(*(--pPDOdataByte) != **(--ppODdataByte) && (TPDO->sendIfCOSFlags&0x20)) return 1; // fallthrough - case 5: if(*(--pPDOdataByte) != **(--ppODdataByte) && (TPDO->sendIfCOSFlags&0x10)) return 1; // fallthrough - case 4: if(*(--pPDOdataByte) != **(--ppODdataByte) && (TPDO->sendIfCOSFlags&0x08)) return 1; // fallthrough - case 3: if(*(--pPDOdataByte) != **(--ppODdataByte) && (TPDO->sendIfCOSFlags&0x04)) return 1; // fallthrough - case 2: if(*(--pPDOdataByte) != **(--ppODdataByte) && (TPDO->sendIfCOSFlags&0x02)) return 1; // fallthrough - case 1: if(*(--pPDOdataByte) != **(--ppODdataByte) && (TPDO->sendIfCOSFlags&0x01)) return 1; // fallthrough - } - - return 0; -} - -//#define TPDO_CALLS_EXTENSION -/******************************************************************************/ -int16_t CO_TPDOsend(CO_TPDO_t *TPDO){ - int16_t i; - uint8_t* pPDOdataByte; - uint8_t** ppODdataByte; - -#ifdef TPDO_CALLS_EXTENSION - if(TPDO->SDO->ODExtensions){ - /* for each mapped OD, check mapping to see if an OD extension is available, and call it if it is */ - const uint32_t* pMap = &TPDO->TPDOMapPar->mappedObject1; - CO_SDO_t *pSDO = TPDO->SDO; - - for(i=TPDO->TPDOMapPar->numberOfMappedObjects; i>0; i--){ - uint32_t map = *(pMap++); - uint16_t index = (uint16_t)(map>>16); - uint8_t subIndex = (uint8_t)(map>>8); - uint16_t entryNo = CO_OD_find(pSDO, index); - if ( entryNo == 0xFFFF ) continue; - CO_OD_extension_t *ext = &pSDO->ODExtensions[entryNo]; - if( ext->pODFunc == NULL) continue; - CO_ODF_arg_t ODF_arg; - memset((void*)&ODF_arg, 0, sizeof(CO_ODF_arg_t)); - ODF_arg.reading = true; - ODF_arg.index = index; - ODF_arg.subIndex = subIndex; - ODF_arg.object = ext->object; - ODF_arg.attribute = CO_OD_getAttribute(pSDO, entryNo, subIndex); - ODF_arg.pFlags = CO_OD_getFlagsPointer(pSDO, entryNo, subIndex); - ODF_arg.data = CO_OD_getDataPointer(pSDO, entryNo, subIndex); //https://github.com/CANopenNode/CANopenNode/issues/100 - ODF_arg.dataLength = CO_OD_getLength(pSDO, entryNo, subIndex); - ext->pODFunc(&ODF_arg); - } - } -#endif - i = TPDO->dataLength; - pPDOdataByte = &TPDO->CANtxBuff->data[0]; - ppODdataByte = &TPDO->mapPointer[0]; - - /* Copy data from Object dictionary. */ - for(; i>0; i--) { - *(pPDOdataByte++) = **(ppODdataByte++); - } - - TPDO->sendRequest = 0; - - return CO_CANsend(TPDO->CANdevTx, TPDO->CANtxBuff); -} - -//#define RPDO_CALLS_EXTENSION -/******************************************************************************/ -void CO_RPDO_process(CO_RPDO_t *RPDO, bool_t syncWas){ - - if(!RPDO->valid || !(*RPDO->operatingState == CO_NMT_OPERATIONAL)) - { - CLEAR_CANrxNew(RPDO->CANrxNew[0]); - CLEAR_CANrxNew(RPDO->CANrxNew[1]); - } - else if(!RPDO->synchronous || syncWas) - { -#if defined(RPDO_CALLS_EXTENSION) - bool_t update = false; -#endif /* defined(RPDO_CALLS_EXTENSION) */ - - uint8_t bufNo = 0; - - /* Determine, which of the two rx buffers, contains relevant message. */ - if(RPDO->SYNC && RPDO->synchronous && !RPDO->SYNC->CANrxToggle) { - bufNo = 1; - } - - while(IS_CANrxNew(RPDO->CANrxNew[bufNo])){ - int16_t i; - uint8_t* pPDOdataByte; - uint8_t** ppODdataByte; - - i = RPDO->dataLength; - pPDOdataByte = &RPDO->CANrxData[bufNo][0]; - ppODdataByte = &RPDO->mapPointer[0]; - - /* Copy data to Object dictionary. If between the copy operation CANrxNew - * is set to true by receive thread, then copy the latest data again. */ - CLEAR_CANrxNew(RPDO->CANrxNew[bufNo]); - for(; i>0; i--) { - **(ppODdataByte++) = *(pPDOdataByte++); - } -#if defined(RPDO_CALLS_EXTENSION) - update = true; -#endif /* defined(RPDO_CALLS_EXTENSION) */ - } -#ifdef RPDO_CALLS_EXTENSION - if(update && RPDO->SDO->ODExtensions){ - int16_t i; - /* for each mapped OD, check mapping to see if an OD extension is available, and call it if it is */ - const uint32_t* pMap = &RPDO->RPDOMapPar->mappedObject1; - CO_SDO_t *pSDO = RPDO->SDO; - - for(i=RPDO->RPDOMapPar->numberOfMappedObjects; i>0; i--){ - uint32_t map = *(pMap++); - uint16_t index = (uint16_t)(map>>16); - uint8_t subIndex = (uint8_t)(map>>8); - uint16_t entryNo = CO_OD_find(pSDO, index); - if ( entryNo == 0xFFFF ) continue; - CO_OD_extension_t *ext = &pSDO->ODExtensions[entryNo]; - if( ext->pODFunc == NULL) continue; - CO_ODF_arg_t ODF_arg; - memset((void*)&ODF_arg, 0, sizeof(CO_ODF_arg_t)); - ODF_arg.reading = false; - ODF_arg.index = index; - ODF_arg.subIndex = subIndex; - ODF_arg.object = ext->object; - ODF_arg.attribute = CO_OD_getAttribute(pSDO, entryNo, subIndex); - ODF_arg.pFlags = CO_OD_getFlagsPointer(pSDO, entryNo, subIndex); - ODF_arg.data = CO_OD_getDataPointer(pSDO, entryNo, subIndex); //https://github.com/CANopenNode/CANopenNode/issues/100 - ODF_arg.dataLength = CO_OD_getLength(pSDO, entryNo, subIndex); - ext->pODFunc(&ODF_arg); - } - } -#endif - } -} - - -/******************************************************************************/ -void CO_TPDO_process( - CO_TPDO_t *TPDO, - bool_t syncWas, - uint32_t timeDifference_us) -{ - if(TPDO->valid && *TPDO->operatingState == CO_NMT_OPERATIONAL){ - - /* Send PDO by application request or by Event timer */ - if(TPDO->TPDOCommPar->transmissionType >= 253){ - if(TPDO->inhibitTimer == 0 && (TPDO->sendRequest || (TPDO->TPDOCommPar->eventTimer && TPDO->eventTimer == 0))){ - if(CO_TPDOsend(TPDO) == CO_ERROR_NO){ - /* successfully sent */ - TPDO->inhibitTimer = ((uint32_t) TPDO->TPDOCommPar->inhibitTime) * 100; - TPDO->eventTimer = ((uint32_t) TPDO->TPDOCommPar->eventTimer) * 1000; - } - } - } - - /* Synchronous PDOs */ - else if(TPDO->SYNC && syncWas){ - /* send synchronous acyclic PDO */ - if(TPDO->TPDOCommPar->transmissionType == 0){ - if(TPDO->sendRequest) CO_TPDOsend(TPDO); - } - /* send synchronous cyclic PDO */ - else{ - /* is the start of synchronous TPDO transmission */ - if(TPDO->syncCounter == 255){ - if(TPDO->SYNC->counterOverflowValue && TPDO->TPDOCommPar->SYNCStartValue) - TPDO->syncCounter = 254; /* SYNCStartValue is in use */ - else - TPDO->syncCounter = TPDO->TPDOCommPar->transmissionType; - } - /* if the SYNCStartValue is in use, start first TPDO after SYNC with matched SYNCStartValue. */ - if(TPDO->syncCounter == 254){ - if(TPDO->SYNC->counter == TPDO->TPDOCommPar->SYNCStartValue){ - TPDO->syncCounter = TPDO->TPDOCommPar->transmissionType; - CO_TPDOsend(TPDO); - } - } - /* Send PDO after every N-th Sync */ - else if(--TPDO->syncCounter == 0){ - TPDO->syncCounter = TPDO->TPDOCommPar->transmissionType; - CO_TPDOsend(TPDO); - } - } - } - - } - else{ - /* Not operational or valid. Force TPDO first send after operational or valid. */ - if(TPDO->TPDOCommPar->transmissionType>=254) TPDO->sendRequest = 1; - else TPDO->sendRequest = 0; - } - - /* update timers */ - TPDO->inhibitTimer = (TPDO->inhibitTimer > timeDifference_us) ? (TPDO->inhibitTimer - timeDifference_us) : 0; - TPDO->eventTimer = (TPDO->eventTimer > timeDifference_us) ? (TPDO->eventTimer - timeDifference_us) : 0; -} diff --git a/stack/CO_PDO.h b/stack/CO_PDO.h deleted file mode 100644 index a2743f61..00000000 --- a/stack/CO_PDO.h +++ /dev/null @@ -1,387 +0,0 @@ -/** - * CANopen Process Data Object protocol. - * - * @file CO_PDO.h - * @ingroup CO_PDO - * @author Janez Paternoster - * @copyright 2004 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#ifndef CO_PDO_H -#define CO_PDO_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @defgroup CO_PDO PDO - * @ingroup CO_CANopen - * @{ - * - * CANopen Process Data Object protocol. - * - * Process data objects are used for real-time data transfer with no protocol - * overhead. - * - * TPDO with specific identifier is transmitted by one device and recieved by - * zero or more devices as RPDO. PDO communication parameters(COB-ID, - * transmission type, etc.) are in Object Dictionary at index 0x1400+ and - * 0x1800+. PDO mapping parameters (size and contents of the PDO) are in Object - * Dictionary at index 0x1600+ and 0x1A00+. - * - * Features of the PDO as implemented here, in CANopenNode: - * - Dynamic PDO mapping. - * - Map granularity of one byte. - * - After RPDO is received from CAN bus, its data are copied to buffer. - * Function CO_RPDO_process() (called by application) copies data to - * mapped objects in Object Dictionary. Synchronous RPDOs are processed AFTER - * reception of the next SYNC message. - * - Function CO_TPDO_process() (called by application) sends TPDO if - * necessary. There are possible different transmission types, including - * automatic detection of Change of State of specific variable. - */ - - -/** - * RPDO communication parameter. The same as record from Object dictionary (index 0x1400+). - */ -typedef struct{ - uint8_t maxSubIndex; /**< Equal to 2 */ - /** Communication object identifier for message received. Meaning of the specific bits: - - Bit 0-10: COB-ID for PDO, to change it bit 31 must be set. - - Bit 11-29: set to 0 for 11 bit COB-ID. - - Bit 30: If true, rtr are NOT allowed for PDO. - - Bit 31: If true, node does NOT use the PDO. */ - uint32_t COB_IDUsedByRPDO; - /** Transmission type. Values: - - 0-240: Reciving is synchronous, process after next reception of the SYNC object. - - 241-253: Not used. - - 254: Manufacturer specific. - - 255: Asynchronous. */ - uint8_t transmissionType; -}CO_RPDOCommPar_t; - - -/** - * RPDO mapping parameter. The same as record from Object dictionary (index 0x1600+). - */ -typedef struct{ - /** Actual number of mapped objects from 0 to 8. To change mapped object, - this value must be 0. */ - uint8_t numberOfMappedObjects; - /** Location and size of the mapped object. Bit meanings `0xIIIISSLL`: - - Bit 0-7: Data Length in bits. - - Bit 8-15: Subindex from object distionary. - - Bit 16-31: Index from object distionary. */ - uint32_t mappedObject1; - uint32_t mappedObject2; /**< Same */ - uint32_t mappedObject3; /**< Same */ - uint32_t mappedObject4; /**< Same */ - uint32_t mappedObject5; /**< Same */ - uint32_t mappedObject6; /**< Same */ - uint32_t mappedObject7; /**< Same */ - uint32_t mappedObject8; /**< Same */ -}CO_RPDOMapPar_t; - - -/** - * TPDO communication parameter. The same as record from Object dictionary (index 0x1800+). - */ -typedef struct{ - uint8_t maxSubIndex; /**< Equal to 6 */ - /** Communication object identifier for transmitting message. Meaning of the specific bits: - - Bit 0-10: COB-ID for PDO, to change it bit 31 must be set. - - Bit 11-29: set to 0 for 11 bit COB-ID. - - Bit 30: If true, rtr are NOT allowed for PDO. - - Bit 31: If true, node does NOT use the PDO. */ - uint32_t COB_IDUsedByTPDO; - /** Transmission type. Values: - - 0: Transmiting is synchronous, specification in device profile. - - 1-240: Transmiting is synchronous after every N-th SYNC object. - - 241-251: Not used. - - 252-253: Transmited only on reception of Remote Transmission Request. - - 254: Manufacturer specific. - - 255: Asinchronous, specification in device profile. */ - uint8_t transmissionType; - /** Minimum time between transmissions of the PDO in 100micro seconds. - Zero disables functionality. */ - uint16_t inhibitTime; - /** Not used */ - uint8_t compatibilityEntry; - /** Time between periodic transmissions of the PDO in milliseconds. - Zero disables functionality. */ - uint16_t eventTimer; - /** Used with numbered SYNC messages. Values: - - 0: Counter of the SYNC message shall not be processed. - - 1-240: The SYNC message with the counter value equal to this value - shall be regarded as the first received SYNC message. */ - uint8_t SYNCStartValue; -}CO_TPDOCommPar_t; - - -/** - * TPDO mapping parameter. The same as record from Object dictionary (index 0x1A00+). - */ -typedef struct{ - /** Actual number of mapped objects from 0 to 8. To change mapped object, - this value must be 0. */ - uint8_t numberOfMappedObjects; - /** Location and size of the mapped object. Bit meanings `0xIIIISSLL`: - - Bit 0-7: Data Length in bits. - - Bit 8-15: Subindex from object distionary. - - Bit 16-31: Index from object distionary. */ - uint32_t mappedObject1; - uint32_t mappedObject2; /**< Same */ - uint32_t mappedObject3; /**< Same */ - uint32_t mappedObject4; /**< Same */ - uint32_t mappedObject5; /**< Same */ - uint32_t mappedObject6; /**< Same */ - uint32_t mappedObject7; /**< Same */ - uint32_t mappedObject8; /**< Same */ -}CO_TPDOMapPar_t; - - -/** - * RPDO object. - */ -typedef struct{ - CO_EM_t *em; /**< From CO_RPDO_init() */ - CO_SDO_t *SDO; /**< From CO_RPDO_init() */ - CO_SYNC_t *SYNC; /**< From CO_RPDO_init() */ - const CO_RPDOCommPar_t *RPDOCommPar;/**< From CO_RPDO_init() */ - const CO_RPDOMapPar_t *RPDOMapPar; /**< From CO_RPDO_init() */ - uint8_t *operatingState; /**< From CO_RPDO_init() */ - uint8_t nodeId; /**< From CO_RPDO_init() */ - uint16_t defaultCOB_ID; /**< From CO_RPDO_init() */ - uint8_t restrictionFlags;/**< From CO_RPDO_init() */ - /** True, if PDO is enabled and valid */ - bool_t valid; - /** True, if PDO synchronous (transmissionType <= 240) */ - bool_t synchronous; - /** Data length of the received PDO message. Calculated from mapping */ - uint8_t dataLength; - /** Pointers to 8 data objects, where PDO will be copied */ - uint8_t *mapPointer[8]; - /** Variable indicates, if new PDO message received from CAN bus. */ - volatile void *CANrxNew[2]; - /** 8 data bytes of the received message. */ - uint8_t CANrxData[2][8]; - CO_CANmodule_t *CANdevRx; /**< From CO_RPDO_init() */ - uint16_t CANdevRxIdx; /**< From CO_RPDO_init() */ -}CO_RPDO_t; - - -/** - * TPDO object. - */ -typedef struct{ - CO_EM_t *em; /**< From CO_TPDO_init() */ - CO_SDO_t *SDO; /**< From CO_TPDO_init() */ - CO_SYNC_t *SYNC; /**< From CO_TPDO_init() */ - const CO_TPDOCommPar_t *TPDOCommPar;/**< From CO_TPDO_init() */ - const CO_TPDOMapPar_t *TPDOMapPar; /**< From CO_TPDO_init() */ - uint8_t *operatingState; /**< From CO_TPDO_init() */ - uint8_t nodeId; /**< From CO_TPDO_init() */ - uint16_t defaultCOB_ID; /**< From CO_TPDO_init() */ - uint8_t restrictionFlags;/**< From CO_TPDO_init() */ - bool_t valid; /**< True, if PDO is enabled and valid */ - /** Data length of the transmitting PDO message. Calculated from mapping */ - uint8_t dataLength; - /** If application set this flag, PDO will be later sent by - function CO_TPDO_process(). Depends on transmission type. */ - uint8_t sendRequest; - /** Pointers to 8 data objects, where PDO will be copied */ - uint8_t *mapPointer[8]; - /** Each flag bit is connected with one mapPointer. If flag bit - is true, CO_TPDO_process() functiuon will send PDO if - Change of State is detected on value pointed by that mapPointer */ - uint8_t sendIfCOSFlags; - /** SYNC counter used for PDO sending */ - uint8_t syncCounter; - /** Inhibit timer used for inhibit PDO sending translated to microseconds */ - uint32_t inhibitTimer; - /** Event timer used for PDO sending translated to microseconds */ - uint32_t eventTimer; - CO_CANmodule_t *CANdevTx; /**< From CO_TPDO_init() */ - CO_CANtx_t *CANtxBuff; /**< CAN transmit buffer inside CANdev */ - uint16_t CANdevTxIdx; /**< From CO_TPDO_init() */ -}CO_TPDO_t; - - -/** - * Initialize RPDO object. - * - * Function must be called in the communication reset section. - * - * @param RPDO This object will be initialized. - * @param em Emergency object. - * @param SDO SDO server object. - * @param operatingState Pointer to variable indicating CANopen device NMT internal state. - * @param nodeId CANopen Node ID of this device. If default COB_ID is used, value will be added. - * @param defaultCOB_ID Default COB ID for this PDO (without NodeId). - * See #CO_Default_CAN_ID_t - * @param restrictionFlags Flag bits indicates, how PDO communication - * and mapping parameters are handled: - * - Bit1: If true, communication parameters are writeable only in pre-operational NMT state. - * - Bit2: If true, mapping parameters are writeable only in pre-operational NMT state. - * - Bit3: If true, communication parameters are read-only. - * - Bit4: If true, mapping parameters are read-only. - * @param RPDOCommPar Pointer to _RPDO communication parameter_ record from Object - * dictionary (index 0x1400+). - * @param RPDOMapPar Pointer to _RPDO mapping parameter_ record from Object - * dictionary (index 0x1600+). - * @param idx_RPDOCommPar Index in Object Dictionary. - * @param idx_RPDOMapPar Index in Object Dictionary. - * @param CANdevRx CAN device for PDO reception. - * @param CANdevRxIdx Index of receive buffer in the above CAN device. - * - * @return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT. - */ -CO_ReturnError_t CO_RPDO_init( - CO_RPDO_t *RPDO, - CO_EM_t *em, - CO_SDO_t *SDO, - CO_SYNC_t *SYNC, - uint8_t *operatingState, - uint8_t nodeId, - uint16_t defaultCOB_ID, - uint8_t restrictionFlags, - const CO_RPDOCommPar_t *RPDOCommPar, - const CO_RPDOMapPar_t *RPDOMapPar, - uint16_t idx_RPDOCommPar, - uint16_t idx_RPDOMapPar, - CO_CANmodule_t *CANdevRx, - uint16_t CANdevRxIdx); - - -/** - * Initialize TPDO object. - * - * Function must be called in the communication reset section. - * - * @param TPDO This object will be initialized. - * @param em Emergency object. - * @param SDO SDO object. - * @param operatingState Pointer to variable indicating CANopen device NMT internal state. - * @param nodeId CANopen Node ID of this device. If default COB_ID is used, value will be added. - * @param defaultCOB_ID Default COB ID for this PDO (without NodeId). - * See #CO_Default_CAN_ID_t - * @param restrictionFlags Flag bits indicates, how PDO communication - * and mapping parameters are handled: - * - Bit1: If true, communication parameters are writeable only in pre-operational NMT state. - * - Bit2: If true, mapping parameters are writeable only in pre-operational NMT state. - * - Bit3: If true, communication parameters are read-only. - * - Bit4: If true, mapping parameters are read-only. - * @param TPDOCommPar Pointer to _TPDO communication parameter_ record from Object - * dictionary (index 0x1400+). - * @param TPDOMapPar Pointer to _TPDO mapping parameter_ record from Object - * dictionary (index 0x1600+). - * @param idx_TPDOCommPar Index in Object Dictionary. - * @param idx_TPDOMapPar Index in Object Dictionary. - * @param CANdevTx CAN device used for PDO transmission. - * @param CANdevTxIdx Index of transmit buffer in the above CAN device. - * - * @return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT. - */ -CO_ReturnError_t CO_TPDO_init( - CO_TPDO_t *TPDO, - CO_EM_t *em, - CO_SDO_t *SDO, - CO_SYNC_t *SYNC, - uint8_t *operatingState, - uint8_t nodeId, - uint16_t defaultCOB_ID, - uint8_t restrictionFlags, - const CO_TPDOCommPar_t *TPDOCommPar, - const CO_TPDOMapPar_t *TPDOMapPar, - uint16_t idx_TPDOCommPar, - uint16_t idx_TPDOMapPar, - CO_CANmodule_t *CANdevTx, - uint16_t CANdevTxIdx); - - -/** - * Verify Change of State of the PDO. - * - * Function verifies if variable mapped to TPDO has changed its value. Verified - * are only variables, which has set attribute _CO_ODA_TPDO_DETECT_COS_ in - * #CO_SDO_OD_attributes_t. - * - * Function may be called by application just before CO_TPDO_process() function, - * for example: `TPDOx->sendRequest = CO_TPDOisCOS(TPDOx); CO_TPDO_process(TPDOx, ....` - * - * @param TPDO TPDO object. - * - * @return True if COS was detected. - */ -uint8_t CO_TPDOisCOS(CO_TPDO_t *TPDO); - - -/** - * Send TPDO message. - * - * Function prepares TPDO data from Object Dictionary variables. It should not - * be called by application, it is called from CO_TPDO_process(). - * - * - * @param TPDO TPDO object. - * - * @return Same as CO_CANsend(). - */ -int16_t CO_TPDOsend(CO_TPDO_t *TPDO); - - -/** - * Process received PDO messages. - * - * Function must be called cyclically in any NMT state. It copies data from RPDO - * to Object Dictionary variables if: new PDO receives and PDO is valid and NMT - * operating state is operational. It does not verify _transmission type_. - * - * @param RPDO This object. - * @param syncWas True, if CANopen SYNC message was just received or transmitted. - */ -void CO_RPDO_process(CO_RPDO_t *RPDO, bool_t syncWas); - - -/** - * Process transmitting PDO messages. - * - * Function must be called cyclically in any NMT state. It prepares and sends - * TPDO if necessary. If Change of State needs to be detected, function - * CO_TPDOisCOS() must be called before. - * - * @param TPDO This object. - * @param SYNC SYNC object. Ignored if NULL. - * @param syncWas True, if CANopen SYNC message was just received or transmitted. - * @param timeDifference_us Time difference from previous function call in [microseconds]. - */ -void CO_TPDO_process( - CO_TPDO_t *TPDO, - bool_t syncWas, - uint32_t timeDifference_us); - -#ifdef __cplusplus -} -#endif /*__cplusplus*/ - -/** @} */ -#endif diff --git a/stack/CO_SDO.c b/stack/CO_SDO.c deleted file mode 100644 index f3e55069..00000000 --- a/stack/CO_SDO.c +++ /dev/null @@ -1,1488 +0,0 @@ -/* - * CANopen Service Data Object - server. - * - * @file CO_SDO.c - * @ingroup CO_SDO - * @author Janez Paternoster - * @copyright 2004 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include "CO_driver.h" -#include "CO_SDO.h" -#include "crc16-ccitt.h" - - -/* Client command specifier, see DS301 */ -#define CCS_DOWNLOAD_INITIATE 1U -#define CCS_DOWNLOAD_SEGMENT 0U -#define CCS_UPLOAD_INITIATE 2U -#define CCS_UPLOAD_SEGMENT 3U -#define CCS_DOWNLOAD_BLOCK 6U -#define CCS_UPLOAD_BLOCK 5U -#define CCS_ABORT 0x80U - - -#if CO_SDO_BUFFER_SIZE < 7 - #error CO_SDO_BUFFER_SIZE must be greater than 7 -#endif - - -/* Helper functions. **********************************************************/ -void CO_memcpy(uint8_t dest[], const uint8_t src[], const uint16_t size){ - uint16_t i; - for(i = 0; i < size; i++){ - dest[i] = src[i]; - } -} - -void CO_memset(uint8_t dest[], uint8_t c, const uint16_t size){ - uint16_t i; - for(i = 0; i < size; i++){ - dest[i] = c; - } -} - -uint16_t CO_getUint16(const uint8_t data[]){ - CO_bytes_t b; - b.u8[0] = data[0]; - b.u8[1] = data[1]; - return b.u16[0]; -} - -uint32_t CO_getUint32(const uint8_t data[]){ - CO_bytes_t b; - b.u8[0] = data[0]; - b.u8[1] = data[1]; - b.u8[2] = data[2]; - b.u8[3] = data[3]; - return b.u32[0]; -} - -void CO_setUint16(uint8_t data[], const uint16_t value){ - CO_bytes_t b; - b.u16[0] = value; - data[0] = b.u8[0]; - data[1] = b.u8[1]; -} - -void CO_setUint32(uint8_t data[], const uint32_t value){ - CO_bytes_t b; - b.u32[0] = value; - data[0] = b.u8[0]; - data[1] = b.u8[1]; - data[2] = b.u8[2]; - data[3] = b.u8[3]; -} - -#ifdef CO_LITTLE_ENDIAN -void CO_memcpySwap2(void* dest, const void* src){ - char *cdest; - char *csrc; - cdest = (char *) dest; - csrc = (char *) src; - cdest[0] = csrc[0]; - cdest[1] = csrc[1]; -} -void CO_memcpySwap4(void* dest, const void* src){ - char *cdest; - char *csrc; - cdest = (char *) dest; - csrc = (char *) src; - cdest[0] = csrc[0]; - cdest[1] = csrc[1]; - cdest[2] = csrc[2]; - cdest[3] = csrc[3]; -} -void CO_memcpySwap8(void* dest, const void* src){ - char *cdest; - char *csrc; - cdest = (char *) dest; - csrc = (char *) src; - cdest[0] = csrc[0]; - cdest[1] = csrc[1]; - cdest[2] = csrc[2]; - cdest[3] = csrc[3]; - cdest[4] = csrc[4]; - cdest[5] = csrc[5]; - cdest[6] = csrc[6]; - cdest[7] = csrc[7]; -} -#endif -#ifdef CO_BIG_ENDIAN -void CO_memcpySwap2(void* dest, const void* src){ - char *cdest; - char *csrc; - cdest = (char *) dest; - csrc = (char *) src; - cdest[0] = csrc[1]; - cdest[1] = csrc[0]; -} -void CO_memcpySwap4(void* dest, const void* src){ - char *cdest; - char *csrc; - cdest = (char *) dest; - csrc = (char *) src; - cdest[0] = csrc[3]; - cdest[1] = csrc[2]; - cdest[2] = csrc[1]; - cdest[3] = csrc[0]; -} -void CO_memcpySwap8(void* dest, const void* src){ - char *cdest; - char *csrc; - cdest = (char *) dest; - csrc = (char *) src; - cdest[0] = csrc[7]; - cdest[1] = csrc[6]; - cdest[2] = csrc[5]; - cdest[3] = csrc[4]; - cdest[4] = csrc[3]; - cdest[5] = csrc[2]; - cdest[6] = csrc[1]; - cdest[7] = csrc[0]; -} -#endif - - -/* - * Read received message from CAN module. - * - * Function will be called (by CAN receive interrupt) every time, when CAN - * message with correct identifier will be received. For more information and - * description of parameters see file CO_driver.h. - */ -static void CO_SDO_receive(void *object, const CO_CANrxMsg_t *msg); -static void CO_SDO_receive(void *object, const CO_CANrxMsg_t *msg){ - CO_SDO_t *SDO; - - SDO = (CO_SDO_t*)object; /* this is the correct pointer type of the first argument */ - - /* WARNING: When doing a SDO block upload and immediately after that - * starting another SDO request, this request is dropped. Especially if - * processing function has slow response. - * See: https://github.com/CANopenNode/CANopenNode/issues/39 */ - - /* verify message length and message overflow (previous message was not processed yet) */ - if((msg->DLC == 8U) && (!IS_CANrxNew(SDO->CANrxNew))){ - if(SDO->state != CO_SDO_ST_DOWNLOAD_BL_SUBBLOCK) { - /* copy data and set 'new message' flag */ - SDO->CANrxData[0] = msg->data[0]; - SDO->CANrxData[1] = msg->data[1]; - SDO->CANrxData[2] = msg->data[2]; - SDO->CANrxData[3] = msg->data[3]; - SDO->CANrxData[4] = msg->data[4]; - SDO->CANrxData[5] = msg->data[5]; - SDO->CANrxData[6] = msg->data[6]; - SDO->CANrxData[7] = msg->data[7]; - - SET_CANrxNew(SDO->CANrxNew); - } - else { - /* block download, copy data directly */ - uint8_t seqno; - - SDO->CANrxData[0] = msg->data[0]; - seqno = SDO->CANrxData[0] & 0x7fU; - SDO->timeoutTimer = 0; - /* clear timeout in sub-block transfer indication if set before */ - if (SDO->timeoutSubblockDownolad) - SDO->timeoutSubblockDownolad = false; - - /* check correct sequence number. */ - if(seqno == (SDO->sequence + 1U)) { - /* sequence is correct */ - uint8_t i; - - SDO->sequence++; - - /* copy data */ - for(i=1; i<8; i++) { - SDO->ODF_arg.data[SDO->bufferOffset++] = msg->data[i]; //SDO->ODF_arg.data is equal as SDO->databuffer - if(SDO->bufferOffset >= CO_SDO_BUFFER_SIZE) { - /* buffer full, break reception */ - SDO->state = CO_SDO_ST_DOWNLOAD_BL_SUB_RESP; - SET_CANrxNew(SDO->CANrxNew); - break; - } - } - - /* break reception if last segment, block ends or block sequence is too large */ - if(((SDO->CANrxData[0] & 0x80U) == 0x80U) || (SDO->sequence >= SDO->blksize)) { - SDO->state = CO_SDO_ST_DOWNLOAD_BL_SUB_RESP; - SET_CANrxNew(SDO->CANrxNew); - } - } - else if((seqno == SDO->sequence) || (SDO->sequence == 0U)){ - /* Ignore message, if it is duplicate or if sequence didn't started yet. */ - } - else { - /* seqno is wrong, send response without resetting sequence */ - SDO->state = CO_SDO_ST_DOWNLOAD_BL_SUB_RESP_2; - SET_CANrxNew(SDO->CANrxNew); - } - } - - /* Optional signal to RTOS, which can resume task, which handles SDO server. */ - if(IS_CANrxNew(SDO->CANrxNew) && SDO->pFunctSignal != NULL) { - SDO->pFunctSignal(); - } - } -} - - -/* - * Function for accessing _SDO server parameter_ for default SDO (index 0x1200) - * from SDO server. - * - * For more information see file CO_SDO.h. - */ -static CO_SDO_abortCode_t CO_ODF_1200(CO_ODF_arg_t *ODF_arg); -static CO_SDO_abortCode_t CO_ODF_1200(CO_ODF_arg_t *ODF_arg){ - uint8_t *nodeId; - uint32_t value; - CO_SDO_abortCode_t ret = CO_SDO_AB_NONE; - - nodeId = (uint8_t*) ODF_arg->object; - value = CO_getUint32(ODF_arg->data); - - /* if SDO reading Object dictionary 0x1200, add nodeId to the value */ - if((ODF_arg->reading) && (ODF_arg->subIndex > 0U)){ - CO_setUint32(ODF_arg->data, value + *nodeId); - } - - return ret; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_SDO_init( - CO_SDO_t *SDO, - uint32_t COB_IDClientToServer, - uint32_t COB_IDServerToClient, - uint16_t ObjDictIndex_SDOServerParameter, - CO_SDO_t *parentSDO, - const CO_OD_entry_t OD[], - uint16_t ODSize, - CO_OD_extension_t *ODExtensions, - uint8_t nodeId, - CO_CANmodule_t *CANdevRx, - uint16_t CANdevRxIdx, - CO_CANmodule_t *CANdevTx, - uint16_t CANdevTxIdx) -{ - /* verify arguments */ - if(SDO==NULL || CANdevRx==NULL || CANdevTx==NULL){ - return CO_ERROR_ILLEGAL_ARGUMENT; - } - - /* configure own object dictionary */ - if(parentSDO == NULL){ - uint16_t i; - - SDO->ownOD = true; - SDO->OD = OD; - SDO->ODSize = ODSize; - SDO->ODExtensions = ODExtensions; - - /* clear pointers in ODExtensions */ - for(i=0U; iODExtensions[i].pODFunc = NULL; - SDO->ODExtensions[i].object = NULL; - SDO->ODExtensions[i].flags = NULL; - } - } - /* copy object dictionary from parent */ - else{ - SDO->ownOD = false; - SDO->OD = parentSDO->OD; - SDO->ODSize = parentSDO->ODSize; - SDO->ODExtensions = parentSDO->ODExtensions; - } - - /* Configure object variables */ - SDO->nodeId = nodeId; - SDO->state = CO_SDO_ST_IDLE; - CLEAR_CANrxNew(SDO->CANrxNew); - SDO->pFunctSignal = NULL; - - - /* Configure Object dictionary entry at index 0x1200 */ - if(ObjDictIndex_SDOServerParameter == OD_H1200_SDO_SERVER_PARAM){ - CO_OD_configure(SDO, ObjDictIndex_SDOServerParameter, CO_ODF_1200, (void*)&SDO->nodeId, 0U, 0U); - } - - if((COB_IDClientToServer & 0x80000000) != 0 || (COB_IDServerToClient & 0x80000000) != 0 ){ - // SDO is invalid - COB_IDClientToServer = 0; - COB_IDServerToClient = 0; - } - /* configure SDO server CAN reception */ - CO_CANrxBufferInit( - CANdevRx, /* CAN device */ - CANdevRxIdx, /* rx buffer index */ - COB_IDClientToServer, /* CAN identifier */ - 0x7FF, /* mask */ - 0, /* rtr */ - (void*)SDO, /* object passed to receive function */ - CO_SDO_receive); /* this function will process received message */ - - /* configure SDO server CAN transmission */ - SDO->CANdevTx = CANdevTx; - SDO->CANtxBuff = CO_CANtxBufferInit( - CANdevTx, /* CAN device */ - CANdevTxIdx, /* index of specific buffer inside CAN module */ - COB_IDServerToClient, /* CAN identifier */ - 0, /* rtr */ - 8, /* number of data bytes */ - 0); /* synchronous message flag bit */ - - return CO_ERROR_NO; -} - - -/******************************************************************************/ -void CO_SDO_initCallback( - CO_SDO_t *SDO, - void (*pFunctSignal)(void)) -{ - if(SDO != NULL){ - SDO->pFunctSignal = pFunctSignal; - } -} - - -/******************************************************************************/ -void CO_OD_configure( - CO_SDO_t *SDO, - uint16_t index, - CO_SDO_abortCode_t (*pODFunc)(CO_ODF_arg_t *ODF_arg), - void *object, - uint8_t *flags, - uint8_t flagsSize) -{ - uint16_t entryNo; - - entryNo = CO_OD_find(SDO, index); - if(entryNo < 0xFFFFU){ - CO_OD_extension_t *ext = &SDO->ODExtensions[entryNo]; - uint8_t maxSubIndex = SDO->OD[entryNo].maxSubIndex; - - ext->pODFunc = pODFunc; - ext->object = object; - if((flags != NULL) && (flagsSize != 0U) && (flagsSize == maxSubIndex)){ - uint16_t i; - ext->flags = flags; - for(i=0U; i<=maxSubIndex; i++){ - ext->flags[i] = 0U; - } - } - else{ - ext->flags = NULL; - } - } -} - - -/******************************************************************************/ -uint16_t CO_OD_find(CO_SDO_t *SDO, uint16_t index){ - /* Fast search in ordered Object Dictionary. If indexes are mixed, this won't work. */ - /* If Object Dictionary has up to 2^N entries, then N is max number of loop passes. */ - uint16_t cur, min, max; - const CO_OD_entry_t* object; - - min = 0U; - max = SDO->ODSize - 1U; - while(min < max){ - cur = (min + max) / 2; - object = &SDO->OD[cur]; - /* Is object matched */ - if(index == object->index){ - return cur; - } - if(index < object->index){ - max = cur; - if(max) max--; - } - else - min = cur + 1U; - } - - if(min == max){ - object = &SDO->OD[min]; - /* Is object matched */ - if(index == object->index){ - return min; - } - } - - return 0xFFFFU; /* object does not exist in OD */ -} - - -/******************************************************************************/ -uint16_t CO_OD_getLength(CO_SDO_t *SDO, uint16_t entryNo, uint8_t subIndex){ - const CO_OD_entry_t* object = &SDO->OD[entryNo]; - - if(entryNo == 0xFFFFU){ - return 0U; - } - - if(object->maxSubIndex == 0U){ /* Object type is Var */ - if(object->pData == 0){ /* data type is domain */ - return CO_SDO_BUFFER_SIZE; - } - else{ - return object->length; - } - } - else if(object->attribute != 0U){ /* Object type is Array */ - if(subIndex == 0U){ - return 1U; - } - else if(object->pData == 0){ - /* data type is domain */ - return CO_SDO_BUFFER_SIZE; - } - else{ - return object->length; - } - } - else{ /* Object type is Record */ - if(((const CO_OD_entryRecord_t*)(object->pData))[subIndex].pData == 0){ - /* data type is domain */ - return CO_SDO_BUFFER_SIZE; - } - else{ - return ((const CO_OD_entryRecord_t*)(object->pData))[subIndex].length; - } - } -} - - -/******************************************************************************/ -uint16_t CO_OD_getAttribute(CO_SDO_t *SDO, uint16_t entryNo, uint8_t subIndex){ - const CO_OD_entry_t* object = &SDO->OD[entryNo]; - - if(entryNo == 0xFFFFU){ - return 0U; - } - - if(object->maxSubIndex == 0U){ /* Object type is Var */ - return object->attribute; - } - else if(object->attribute != 0U){/* Object type is Array */ - bool_t exception_1003 = false; - uint16_t attr = object->attribute; - - /* Special exception: Object 1003,00 should be writable */ - if(object->index == 0x1003 && subIndex == 0) { - exception_1003 = true; - attr |= CO_ODA_WRITEABLE; - } - - if(subIndex == 0U && !exception_1003){ - /* First subIndex is readonly */ - attr &= ~(CO_ODA_WRITEABLE | CO_ODA_RPDO_MAPABLE); - attr |= CO_ODA_READABLE; - } - return attr; - } - else{ /* Object type is Record */ - return ((const CO_OD_entryRecord_t*)(object->pData))[subIndex].attribute; - } -} - - -/******************************************************************************/ -void* CO_OD_getDataPointer(CO_SDO_t *SDO, uint16_t entryNo, uint8_t subIndex){ - const CO_OD_entry_t* object = &SDO->OD[entryNo]; - - if(entryNo == 0xFFFFU){ - return 0; - } - - if(object->maxSubIndex == 0U){ /* Object type is Var */ - return object->pData; - } - else if(object->maxSubIndex < subIndex){ - /* Object type Array/Record, request is out of bounds */ - return 0; - } - else if(object->attribute != 0U){/* Object type is Array */ - if(subIndex==0){ - /* this is the data, for the subIndex 0 in the array */ - return (void*) &object->maxSubIndex; - } - else if(object->pData == 0){ - /* data type is domain */ - return 0; - } - else{ - return (void*)(((int8_t*)object->pData) + ((subIndex-1) * object->length)); - } - } - else{ /* Object Type is Record */ - return ((const CO_OD_entryRecord_t*)(object->pData))[subIndex].pData; - } -} - - -/******************************************************************************/ -uint8_t* CO_OD_getFlagsPointer(CO_SDO_t *SDO, uint16_t entryNo, uint8_t subIndex){ - CO_OD_extension_t* ext; - - if((entryNo == 0xFFFFU) || (SDO->ODExtensions == 0)){ - return 0; - } - - ext = &SDO->ODExtensions[entryNo]; - - return &ext->flags[subIndex]; -} - - -/******************************************************************************/ -uint32_t CO_SDO_initTransfer(CO_SDO_t *SDO, uint16_t index, uint8_t subIndex){ - - SDO->ODF_arg.index = index; - SDO->ODF_arg.subIndex = subIndex; - - /* find object in Object Dictionary */ - SDO->entryNo = CO_OD_find(SDO, index); - if(SDO->entryNo == 0xFFFFU){ - return CO_SDO_AB_NOT_EXIST ; /* object does not exist in OD */ - } - - /* verify existance of subIndex */ - if(subIndex > SDO->OD[SDO->entryNo].maxSubIndex && - SDO->OD[SDO->entryNo].pData != NULL) - { - return CO_SDO_AB_SUB_UNKNOWN; /* Sub-index does not exist. */ - } - - /* pointer to data in Object dictionary */ - SDO->ODF_arg.ODdataStorage = CO_OD_getDataPointer(SDO, SDO->entryNo, subIndex); - - /* fill ODF_arg */ - SDO->ODF_arg.object = NULL; - if(SDO->ODExtensions){ - CO_OD_extension_t *ext = &SDO->ODExtensions[SDO->entryNo]; - SDO->ODF_arg.object = ext->object; - } - SDO->ODF_arg.data = SDO->databuffer; - SDO->ODF_arg.dataLength = CO_OD_getLength(SDO, SDO->entryNo, subIndex); - SDO->ODF_arg.attribute = CO_OD_getAttribute(SDO, SDO->entryNo, subIndex); - SDO->ODF_arg.pFlags = CO_OD_getFlagsPointer(SDO, SDO->entryNo, subIndex); - - SDO->ODF_arg.firstSegment = true; - SDO->ODF_arg.lastSegment = true; - - /* indicate total data length, if not domain */ - SDO->ODF_arg.dataLengthTotal = (SDO->ODF_arg.ODdataStorage) ? SDO->ODF_arg.dataLength : 0U; - - SDO->ODF_arg.offset = 0U; - - /* verify length */ - if(SDO->ODF_arg.dataLength > CO_SDO_BUFFER_SIZE){ - return CO_SDO_AB_DEVICE_INCOMPAT; /* general internal incompatibility in the device */ - } - - return 0U; -} - - -/******************************************************************************/ -uint32_t CO_SDO_readOD(CO_SDO_t *SDO, uint16_t SDOBufferSize){ - uint8_t *SDObuffer = SDO->ODF_arg.data; - uint8_t *ODdata = (uint8_t*)SDO->ODF_arg.ODdataStorage; - uint16_t length = SDO->ODF_arg.dataLength; - CO_OD_extension_t *ext = 0; - - /* is object readable? */ - if((SDO->ODF_arg.attribute & CO_ODA_READABLE) == 0) - return CO_SDO_AB_WRITEONLY; /* attempt to read a write-only object */ - - /* find extension */ - if(SDO->ODExtensions != NULL){ - ext = &SDO->ODExtensions[SDO->entryNo]; - } - - CO_LOCK_OD(); - - /* copy data from OD to SDO buffer if not domain */ - if(ODdata != NULL){ - while(length--) *(SDObuffer++) = *(ODdata++); - } - /* if domain, Object dictionary function MUST exist */ - else{ - if(ext->pODFunc == NULL){ - CO_UNLOCK_OD(); - return CO_SDO_AB_DEVICE_INCOMPAT; /* general internal incompatibility in the device */ - } - } - - /* call Object dictionary function if registered */ - SDO->ODF_arg.reading = true; - if(ext->pODFunc != NULL){ - uint32_t abortCode = ext->pODFunc(&SDO->ODF_arg); - if(abortCode != 0U){ - CO_UNLOCK_OD(); - return abortCode; - } - - /* dataLength (upadted by pODFunc) must be inside limits */ - if((SDO->ODF_arg.dataLength == 0U) || (SDO->ODF_arg.dataLength > SDOBufferSize)){ - CO_UNLOCK_OD(); - return CO_SDO_AB_DEVICE_INCOMPAT; /* general internal incompatibility in the device */ - } - } - - CO_UNLOCK_OD(); - - SDO->ODF_arg.offset += SDO->ODF_arg.dataLength; - SDO->ODF_arg.firstSegment = false; - - /* swap data if processor is not little endian (CANopen is) */ -#ifdef CO_BIG_ENDIAN - if((SDO->ODF_arg.attribute & CO_ODA_MB_VALUE) != 0){ - uint16_t len = SDO->ODF_arg.dataLength; - uint8_t *buf1 = SDO->ODF_arg.data; - uint8_t *buf2 = buf1 + len - 1; - - len /= 2; - while(len--){ - uint8_t b = *buf1; - *(buf1++) = *buf2; - *(buf2--) = b; - } - } -#endif - - return 0U; -} - - -/******************************************************************************/ -uint32_t CO_SDO_writeOD(CO_SDO_t *SDO, uint16_t length){ - uint8_t *SDObuffer = SDO->ODF_arg.data; - uint8_t *ODdata = (uint8_t*)SDO->ODF_arg.ODdataStorage; - bool_t exception_1003 = false; - - /* is object writeable? */ - if((SDO->ODF_arg.attribute & CO_ODA_WRITEABLE) == 0){ - return CO_SDO_AB_READONLY; /* attempt to write a read-only object */ - } - - /* length of domain data is application specific and not verified */ - if(ODdata == 0){ - SDO->ODF_arg.dataLength = length; - } - - /* verify length except for domain data type */ - else if(SDO->ODF_arg.dataLength != length){ - return CO_SDO_AB_TYPE_MISMATCH; /* Length of service parameter does not match */ - } - - /* swap data if processor is not little endian (CANopen is) */ -#ifdef CO_BIG_ENDIAN - if((SDO->ODF_arg.attribute & CO_ODA_MB_VALUE) != 0){ - uint16_t len = SDO->ODF_arg.dataLength; - uint8_t *buf1 = SDO->ODF_arg.data; - uint8_t *buf2 = buf1 + len - 1; - - len /= 2; - while(len--){ - uint8_t b = *buf1; - *(buf1++) = *buf2; - *(buf2--) = b; - } - } -#endif - - CO_LOCK_OD(); - - /* call Object dictionary function if registered */ - SDO->ODF_arg.reading = false; - if(SDO->ODExtensions != NULL){ - CO_OD_extension_t *ext = &SDO->ODExtensions[SDO->entryNo]; - - if(ext->pODFunc != NULL){ - uint32_t abortCode = ext->pODFunc(&SDO->ODF_arg); - if(abortCode != 0U){ - CO_UNLOCK_OD(); - return abortCode; - } - } - } - SDO->ODF_arg.offset += SDO->ODF_arg.dataLength; - SDO->ODF_arg.firstSegment = false; - - /* Special exception: 1003,00 is writable from network, but not in OD */ - if(SDO->ODF_arg.index == 0x1003 && SDO->ODF_arg.subIndex == 0) { - exception_1003 = true; - } - - /* copy data from SDO buffer to OD if not domain */ - if((ODdata != NULL) && !exception_1003){ - while(length--){ - *(ODdata++) = *(SDObuffer++); - } - } - - CO_UNLOCK_OD(); - - return 0; -} - - -/******************************************************************************/ -static void CO_SDO_abort(CO_SDO_t *SDO, uint32_t code){ - SDO->CANtxBuff->data[0] = 0x80; - SDO->CANtxBuff->data[1] = SDO->ODF_arg.index & 0xFF; - SDO->CANtxBuff->data[2] = (SDO->ODF_arg.index>>8) & 0xFF; - SDO->CANtxBuff->data[3] = SDO->ODF_arg.subIndex; - CO_memcpySwap4(&SDO->CANtxBuff->data[4], &code); - SDO->state = CO_SDO_ST_IDLE; - CLEAR_CANrxNew(SDO->CANrxNew); - CO_CANsend(SDO->CANdevTx, SDO->CANtxBuff); -} - - -/******************************************************************************/ -int8_t CO_SDO_process( - CO_SDO_t *SDO, - bool_t NMTisPreOrOperational, - uint16_t timeDifference_ms, - uint16_t SDOtimeoutTime, - uint16_t *timerNext_ms) -{ - CO_SDO_state_t state = CO_SDO_ST_IDLE; - bool_t sendResponse = false; - - /* return if idle */ - if((SDO->state == CO_SDO_ST_IDLE) && (!IS_CANrxNew(SDO->CANrxNew))){ - return 0; - } - - /* SDO is allowed to work only in operational or pre-operational NMT state */ - if(!NMTisPreOrOperational){ - SDO->state = CO_SDO_ST_IDLE; - CLEAR_CANrxNew(SDO->CANrxNew); - return 0; - } - - /* Is something new to process? */ - if((!SDO->CANtxBuff->bufferFull) && ((IS_CANrxNew(SDO->CANrxNew)) || (SDO->state == CO_SDO_ST_UPLOAD_BL_SUBBLOCK))){ - uint8_t CCS = SDO->CANrxData[0] >> 5; /* Client command specifier */ - - /* reset timeout */ - if(SDO->state != CO_SDO_ST_UPLOAD_BL_SUBBLOCK) - SDO->timeoutTimer = 0; - - /* clear response buffer */ - SDO->CANtxBuff->data[0] = SDO->CANtxBuff->data[1] = SDO->CANtxBuff->data[2] = SDO->CANtxBuff->data[3] = 0; - SDO->CANtxBuff->data[4] = SDO->CANtxBuff->data[5] = SDO->CANtxBuff->data[6] = SDO->CANtxBuff->data[7] = 0; - - /* Is abort from client? */ - if((IS_CANrxNew(SDO->CANrxNew)) && (SDO->CANrxData[0] == CCS_ABORT)){ - SDO->state = CO_SDO_ST_IDLE; - CLEAR_CANrxNew(SDO->CANrxNew); - return -1; - } - - /* continue with previous SDO communication or start new */ - if(SDO->state != CO_SDO_ST_IDLE){ - state = SDO->state; - } - else{ - uint32_t abortCode; - uint16_t index; - - /* Is client command specifier valid */ - if((CCS != CCS_DOWNLOAD_INITIATE) && (CCS != CCS_UPLOAD_INITIATE) && - (CCS != CCS_DOWNLOAD_BLOCK) && (CCS != CCS_UPLOAD_BLOCK)){ - CO_SDO_abort(SDO, CO_SDO_AB_CMD);/* Client command specifier not valid or unknown. */ - return -1; - } - - /* init ODF_arg */ - index = SDO->CANrxData[2]; - index = index << 8 | SDO->CANrxData[1]; - abortCode = CO_SDO_initTransfer(SDO, index, SDO->CANrxData[3]); - if(abortCode != 0U){ - CO_SDO_abort(SDO, abortCode); - return -1; - } - - /* download */ - if((CCS == CCS_DOWNLOAD_INITIATE) || (CCS == CCS_DOWNLOAD_BLOCK)){ - if((SDO->ODF_arg.attribute & CO_ODA_WRITEABLE) == 0U){ - CO_SDO_abort(SDO, CO_SDO_AB_READONLY); /* attempt to write a read-only object */ - return -1; - } - - /* set state machine to normal or block download */ - if(CCS == CCS_DOWNLOAD_INITIATE){ - state = CO_SDO_ST_DOWNLOAD_INITIATE; - } - else{ - state = CO_SDO_ST_DOWNLOAD_BL_INITIATE; - } - } - - /* upload */ - else{ - abortCode = CO_SDO_readOD(SDO, CO_SDO_BUFFER_SIZE); - if(abortCode != 0U){ - CO_SDO_abort(SDO, abortCode); - return -1; - } - - /* if data size is large enough set state machine to block upload, otherwise set to normal transfer */ - if((CCS == CCS_UPLOAD_BLOCK) && (SDO->ODF_arg.dataLength > SDO->CANrxData[5])){ - state = CO_SDO_ST_UPLOAD_BL_INITIATE; - } - else{ - state = CO_SDO_ST_UPLOAD_INITIATE; - } - } - } - } - - /* verify SDO timeout */ - if(SDO->timeoutTimer < SDOtimeoutTime){ - SDO->timeoutTimer += timeDifference_ms; - } - if(SDO->timeoutTimer >= SDOtimeoutTime){ - if((SDO->state == CO_SDO_ST_DOWNLOAD_BL_SUBBLOCK) && (!SDO->timeoutSubblockDownolad) && (!SDO->CANtxBuff->bufferFull)){ - /* set indication timeout in sub-block transfer and reset timeout */ - SDO->timeoutSubblockDownolad = true; - SDO->timeoutTimer = 0; - /* send response without resetting sequence */ - state = CO_SDO_ST_DOWNLOAD_BL_SUB_RESP_2; - } - else{ - CO_SDO_abort(SDO, CO_SDO_AB_TIMEOUT); /* SDO protocol timed out */ - return -1; - } - } - - /* return immediately if still idle */ - if(state == CO_SDO_ST_IDLE){ - return 0; - } - - /* state machine (buffer is freed (CLEAR_CANrxNew()) at the end) */ - switch(state){ - uint32_t abortCode; - uint16_t len, i; - bool_t lastSegmentInSubblock; - - case CO_SDO_ST_DOWNLOAD_INITIATE:{ - /* default response */ - SDO->CANtxBuff->data[0] = 0x60; - SDO->CANtxBuff->data[1] = SDO->CANrxData[1]; - SDO->CANtxBuff->data[2] = SDO->CANrxData[2]; - SDO->CANtxBuff->data[3] = SDO->CANrxData[3]; - - /* Expedited transfer */ - if((SDO->CANrxData[0] & 0x02U) != 0U){ - /* is size indicated? Get message length */ - if((SDO->CANrxData[0] & 0x01U) != 0U){ - len = 4U - ((SDO->CANrxData[0] >> 2U) & 0x03U); - } - else{ - len = SDO->ODF_arg.dataLength; - } - - /* copy data to SDO buffer */ - SDO->ODF_arg.data[0] = SDO->CANrxData[4]; - SDO->ODF_arg.data[1] = SDO->CANrxData[5]; - SDO->ODF_arg.data[2] = SDO->CANrxData[6]; - SDO->ODF_arg.data[3] = SDO->CANrxData[7]; - - /* write data to the Object dictionary */ - abortCode = CO_SDO_writeOD(SDO, len); - if(abortCode != 0U){ - CO_SDO_abort(SDO, abortCode); - return -1; - } - - /* finish the communication */ - SDO->state = CO_SDO_ST_IDLE; - sendResponse = true; - } - - /* Segmented transfer */ - else{ - /* verify length if size is indicated */ - if((SDO->CANrxData[0]&0x01) != 0){ - uint32_t lenRx; - CO_memcpySwap4(&lenRx, &SDO->CANrxData[4]); - SDO->ODF_arg.dataLengthTotal = lenRx; - - /* verify length except for domain data type */ - if((lenRx != SDO->ODF_arg.dataLength) && (SDO->ODF_arg.ODdataStorage != 0)){ - CO_SDO_abort(SDO, CO_SDO_AB_TYPE_MISMATCH); /* Length of service parameter does not match */ - return -1; - } - } - SDO->bufferOffset = 0U; - SDO->sequence = 0U; - SDO->state = CO_SDO_ST_DOWNLOAD_SEGMENTED; - sendResponse = true; - } - break; - } - - case CO_SDO_ST_DOWNLOAD_SEGMENTED:{ - /* verify client command specifier */ - if((SDO->CANrxData[0]&0xE0) != 0x00U){ - CO_SDO_abort(SDO, CO_SDO_AB_CMD);/* Client command specifier not valid or unknown. */ - return -1; - } - - /* verify toggle bit */ - i = (SDO->CANrxData[0]&0x10U) ? 1U : 0U; - if(i != SDO->sequence){ - CO_SDO_abort(SDO, CO_SDO_AB_TOGGLE_BIT);/* toggle bit not alternated */ - return -1; - } - - /* get size of data in message */ - len = 7U - ((SDO->CANrxData[0] >> 1U) & 0x07U); - - /* verify length. Domain data type enables length larger than SDO buffer size */ - if((SDO->bufferOffset + len) > SDO->ODF_arg.dataLength){ - if(SDO->ODF_arg.ODdataStorage != 0){ - CO_SDO_abort(SDO, CO_SDO_AB_DATA_LONG); /* Length of service parameter too high */ - return -1; - } - else{ - /* empty buffer in domain data type */ - SDO->ODF_arg.lastSegment = false; - abortCode = CO_SDO_writeOD(SDO, SDO->bufferOffset); - if(abortCode != 0U){ - CO_SDO_abort(SDO, abortCode); - return -1; - } - - SDO->ODF_arg.dataLength = CO_SDO_BUFFER_SIZE; - SDO->bufferOffset = 0U; - } - } - - /* copy data to buffer */ - for(i=0U; iODF_arg.data[SDO->bufferOffset++] = SDO->CANrxData[i+1]; - - /* If no more segments to be downloaded, write data to the Object dictionary */ - if((SDO->CANrxData[0] & 0x01U) != 0U){ - SDO->ODF_arg.lastSegment = true; - abortCode = CO_SDO_writeOD(SDO, SDO->bufferOffset); - if(abortCode != 0U){ - CO_SDO_abort(SDO, abortCode); - return -1; - } - - /* finish */ - SDO->state = CO_SDO_ST_IDLE; - } - - /* download segment response and alternate toggle bit */ - SDO->CANtxBuff->data[0] = 0x20 | (SDO->sequence ? 0x10 : 0x00); - SDO->sequence = (SDO->sequence) ? 0 : 1; - sendResponse = true; - break; - } - - case CO_SDO_ST_DOWNLOAD_BL_INITIATE:{ - /* verify client command specifier and subcommand */ - if((SDO->CANrxData[0]&0xE1U) != 0xC0U){ - CO_SDO_abort(SDO, CO_SDO_AB_CMD);/* Client command specifier not valid or unknown. */ - return -1; - } - - /* prepare response */ - SDO->CANtxBuff->data[0] = 0xA4; - SDO->CANtxBuff->data[1] = SDO->CANrxData[1]; - SDO->CANtxBuff->data[2] = SDO->CANrxData[2]; - SDO->CANtxBuff->data[3] = SDO->CANrxData[3]; - - /* blksize */ - SDO->blksize = (CO_SDO_BUFFER_SIZE > (7*127)) ? 127 : (CO_SDO_BUFFER_SIZE / 7); - SDO->CANtxBuff->data[4] = SDO->blksize; - - /* is CRC enabled */ - SDO->crcEnabled = (SDO->CANrxData[0] & 0x04) ? true : false; - SDO->crc = 0; - - /* verify length if size is indicated */ - if((SDO->CANrxData[0]&0x02) != 0U){ - uint32_t lenRx; - CO_memcpySwap4(&lenRx, &SDO->CANrxData[4]); - SDO->ODF_arg.dataLengthTotal = lenRx; - - /* verify length except for domain data type */ - if((lenRx != SDO->ODF_arg.dataLength) && (SDO->ODF_arg.ODdataStorage != 0)){ - CO_SDO_abort(SDO, CO_SDO_AB_TYPE_MISMATCH); /* Length of service parameter does not match */ - return -1; - } - } - - SDO->bufferOffset = 0U; - SDO->sequence = 0U; - SDO->timeoutSubblockDownolad = false; - SDO->state = CO_SDO_ST_DOWNLOAD_BL_SUBBLOCK; - - /* send response */ - sendResponse = true; - break; - } - - case CO_SDO_ST_DOWNLOAD_BL_SUBBLOCK:{ - /* data are copied directly in receive function */ - break; - } - - case CO_SDO_ST_DOWNLOAD_BL_SUB_RESP: - case CO_SDO_ST_DOWNLOAD_BL_SUB_RESP_2:{ - /* check if last segment received */ - lastSegmentInSubblock = (!SDO->timeoutSubblockDownolad && - ((SDO->CANrxData[0] & 0x80U) == 0x80U)) ? true : false; - - /* prepare response */ - SDO->CANtxBuff->data[0] = 0xA2; - SDO->CANtxBuff->data[1] = SDO->sequence; - - /* reset sequence on reception break */ - if (state == CO_SDO_ST_DOWNLOAD_BL_SUB_RESP) - SDO->sequence = 0U; - - /* empty buffer in domain data type if not last segment */ - if((SDO->ODF_arg.ODdataStorage == 0) && (SDO->bufferOffset != 0) && !lastSegmentInSubblock){ - /* calculate CRC on next bytes, if enabled */ - if(SDO->crcEnabled){ - SDO->crc = crc16_ccitt(SDO->ODF_arg.data, SDO->bufferOffset, SDO->crc); - } - - /* write data to the Object dictionary */ - SDO->ODF_arg.lastSegment = false; - abortCode = CO_SDO_writeOD(SDO, SDO->bufferOffset); - if(abortCode != 0U){ - CO_SDO_abort(SDO, abortCode); - return -1; - } - - SDO->ODF_arg.dataLength = CO_SDO_BUFFER_SIZE; - SDO->bufferOffset = 0U; - } - - /* blksize */ - len = CO_SDO_BUFFER_SIZE - SDO->bufferOffset; - SDO->blksize = (len > (7*127)) ? 127 : (len / 7); - SDO->CANtxBuff->data[2] = SDO->blksize; - - /* set next state */ - if(lastSegmentInSubblock) { - SDO->state = CO_SDO_ST_DOWNLOAD_BL_END; - } - else if(SDO->bufferOffset >= CO_SDO_BUFFER_SIZE) { - CO_SDO_abort(SDO, CO_SDO_AB_DEVICE_INCOMPAT); - return -1; - } - else { - SDO->state = CO_SDO_ST_DOWNLOAD_BL_SUBBLOCK; - } - - /* send response */ - sendResponse = true; - - break; - } - - case CO_SDO_ST_DOWNLOAD_BL_END:{ - /* verify client command specifier and subcommand */ - if((SDO->CANrxData[0]&0xE1U) != 0xC1U){ - CO_SDO_abort(SDO, CO_SDO_AB_CMD);/* Client command specifier not valid or unknown. */ - return -1; - } - - /* number of bytes in the last segment of the last block that do not contain data. */ - len = (SDO->CANrxData[0]>>2U) & 0x07U; - SDO->bufferOffset -= len; - - /* calculate and verify CRC, if enabled */ - if(SDO->crcEnabled){ - uint16_t crc; - SDO->crc = crc16_ccitt(SDO->ODF_arg.data, SDO->bufferOffset, SDO->crc); - - CO_memcpySwap2(&crc, &SDO->CANrxData[1]); - - if(SDO->crc != crc){ - CO_SDO_abort(SDO, CO_SDO_AB_CRC); /* CRC error (block mode only). */ - return -1; - } - } - - /* write data to the Object dictionary */ - SDO->ODF_arg.lastSegment = true; - abortCode = CO_SDO_writeOD(SDO, SDO->bufferOffset); - if(abortCode != 0U){ - CO_SDO_abort(SDO, abortCode); - return -1; - } - - /* send response */ - SDO->CANtxBuff->data[0] = 0xA1; - SDO->state = CO_SDO_ST_IDLE; - sendResponse = true; - break; - } - - case CO_SDO_ST_UPLOAD_INITIATE:{ - /* default response */ - SDO->CANtxBuff->data[1] = SDO->CANrxData[1]; - SDO->CANtxBuff->data[2] = SDO->CANrxData[2]; - SDO->CANtxBuff->data[3] = SDO->CANrxData[3]; - - /* Expedited transfer */ - if(SDO->ODF_arg.dataLength <= 4U){ - for(i=0U; iODF_arg.dataLength; i++) - SDO->CANtxBuff->data[4U+i] = SDO->ODF_arg.data[i]; - - SDO->CANtxBuff->data[0] = 0x43U | ((4U-SDO->ODF_arg.dataLength) << 2U); - SDO->state = CO_SDO_ST_IDLE; - - sendResponse = true; - } - - /* Segmented transfer */ - else{ - SDO->bufferOffset = 0U; - SDO->sequence = 0U; - SDO->state = CO_SDO_ST_UPLOAD_SEGMENTED; - - /* indicate data size, if known */ - if(SDO->ODF_arg.dataLengthTotal != 0U){ - uint32_t dlentot = SDO->ODF_arg.dataLengthTotal; - CO_memcpySwap4(&SDO->CANtxBuff->data[4], &dlentot); - SDO->CANtxBuff->data[0] = 0x41U; - } - else{ - SDO->CANtxBuff->data[0] = 0x40U; - } - - /* send response */ - sendResponse = true; - } - break; - } - - case CO_SDO_ST_UPLOAD_SEGMENTED:{ - /* verify client command specifier */ - if((SDO->CANrxData[0]&0xE0U) != 0x60U){ - CO_SDO_abort(SDO, CO_SDO_AB_CMD);/* Client command specifier not valid or unknown. */ - return -1; - } - - /* verify toggle bit */ - i = ((SDO->CANrxData[0]&0x10U) != 0) ? 1U : 0U; - if(i != SDO->sequence){ - CO_SDO_abort(SDO, CO_SDO_AB_TOGGLE_BIT);/* toggle bit not alternated */ - return -1; - } - - /* calculate length to be sent */ - len = SDO->ODF_arg.dataLength - SDO->bufferOffset; - if(len > 7U) len = 7U; - - /* If data type is domain, re-fill the data buffer if neccessary and indicated so. */ - if((SDO->ODF_arg.ODdataStorage == 0) && (len < 7U) && (!SDO->ODF_arg.lastSegment)){ - /* copy previous data to the beginning */ - for(i=0U; iODF_arg.data[i] = SDO->ODF_arg.data[SDO->bufferOffset+i]; - } - - /* move the beginning of the data buffer */ - SDO->ODF_arg.data += len; - SDO->ODF_arg.dataLength = CO_OD_getLength(SDO, SDO->entryNo, SDO->ODF_arg.subIndex) - len; - - /* read next data from Object dictionary function */ - abortCode = CO_SDO_readOD(SDO, CO_SDO_BUFFER_SIZE); - if(abortCode != 0U){ - CO_SDO_abort(SDO, abortCode); - return -1; - } - - /* return to the original data buffer */ - SDO->ODF_arg.data -= len; - SDO->ODF_arg.dataLength += len; - SDO->bufferOffset = 0; - - /* re-calculate the length */ - len = SDO->ODF_arg.dataLength; - if(len > 7U) len = 7U; - } - - /* fill response data bytes */ - for(i=0U; iCANtxBuff->data[i+1] = SDO->ODF_arg.data[SDO->bufferOffset++]; - - /* first response byte */ - SDO->CANtxBuff->data[0] = 0x00 | (SDO->sequence ? 0x10 : 0x00) | ((7-len)<<1); - SDO->sequence = (SDO->sequence) ? 0 : 1; - - /* verify end of transfer */ - if((SDO->bufferOffset == SDO->ODF_arg.dataLength) && (SDO->ODF_arg.lastSegment)){ - SDO->CANtxBuff->data[0] |= 0x01; - SDO->state = CO_SDO_ST_IDLE; - } - - /* send response */ - sendResponse = true; - break; - } - - case CO_SDO_ST_UPLOAD_BL_INITIATE:{ - /* default response */ - SDO->CANtxBuff->data[1] = SDO->CANrxData[1]; - SDO->CANtxBuff->data[2] = SDO->CANrxData[2]; - SDO->CANtxBuff->data[3] = SDO->CANrxData[3]; - - /* calculate CRC, if enabled */ - if((SDO->CANrxData[0] & 0x04U) != 0U){ - SDO->crcEnabled = true; - SDO->crc = crc16_ccitt(SDO->ODF_arg.data, SDO->ODF_arg.dataLength, 0); - } - else{ - SDO->crcEnabled = false; - SDO->crc = 0; - } - - /* Number of segments per block */ - SDO->blksize = SDO->CANrxData[4]; - - /* verify client subcommand */ - if((SDO->CANrxData[0]&0x03U) != 0x00U){ - CO_SDO_abort(SDO, CO_SDO_AB_CMD);/* Client command specifier not valid or unknown. */ - return -1; - } - - /* verify blksize and if SDO data buffer is large enough */ - if((SDO->blksize < 1U) || (SDO->blksize > 127U) || - (((SDO->blksize*7U) > SDO->ODF_arg.dataLength) && (!SDO->ODF_arg.lastSegment))){ - CO_SDO_abort(SDO, CO_SDO_AB_BLOCK_SIZE); /* Invalid block size (block mode only). */ - return -1; - } - - /* indicate data size, if known */ - if(SDO->ODF_arg.dataLengthTotal != 0U){ - uint32_t dlentot = SDO->ODF_arg.dataLengthTotal; - CO_memcpySwap4(&SDO->CANtxBuff->data[4], &dlentot); - SDO->CANtxBuff->data[0] = 0xC6U; - } - else{ - SDO->CANtxBuff->data[0] = 0xC4U; - } - - /* send response */ - SDO->state = CO_SDO_ST_UPLOAD_BL_INITIATE_2; - sendResponse = true; - break; - } - - case CO_SDO_ST_UPLOAD_BL_INITIATE_2:{ - /* verify client command specifier and subcommand */ - if((SDO->CANrxData[0]&0xE3U) != 0xA3U){ - CO_SDO_abort(SDO, CO_SDO_AB_CMD);/* Client command specifier not valid or unknown. */ - return -1; - } - - SDO->bufferOffset = 0U; - SDO->sequence = 0U; - SDO->endOfTransfer = false; - CLEAR_CANrxNew(SDO->CANrxNew); - SDO->state = CO_SDO_ST_UPLOAD_BL_SUBBLOCK; - /* continue in next case */ - } - // fallthrough - - case CO_SDO_ST_UPLOAD_BL_SUBBLOCK:{ - /* is block confirmation received */ - if(IS_CANrxNew(SDO->CANrxNew)){ - uint8_t ackseq; - uint16_t j; - - /* verify client command specifier and subcommand */ - if((SDO->CANrxData[0]&0xE3U) != 0xA2U){ - CO_SDO_abort(SDO, CO_SDO_AB_CMD);/* Client command specifier not valid or unknown. */ - return -1; - } - - ackseq = SDO->CANrxData[1]; /* sequence number of the last segment, that was received correctly. */ - - /* verify if response is too early */ - if(ackseq > SDO->sequence){ - CO_SDO_abort(SDO, CO_SDO_AB_SEQ_NUM); /* Invalid sequence */ - return -1; - } - - /* end of transfer */ - if((SDO->endOfTransfer) && (ackseq == SDO->blksize)){ - /* first response byte */ - SDO->CANtxBuff->data[0] = 0xC1 | ((7 - SDO->lastLen) << 2); - - /* CRC */ - if(SDO->crcEnabled) - CO_memcpySwap2(&SDO->CANtxBuff->data[1], &SDO->crc); - - SDO->state = CO_SDO_ST_UPLOAD_BL_END; - - /* send response */ - sendResponse = true; - break; - } - - /* move remaining data to the beginning */ - for(i=ackseq*7, j=0; iODF_arg.dataLength; i++, j++) - SDO->ODF_arg.data[j] = SDO->ODF_arg.data[i]; - - /* set remaining data length in buffer */ - SDO->ODF_arg.dataLength -= ackseq * 7U; - - /* new block size */ - SDO->blksize = SDO->CANrxData[2]; - - /* If data type is domain, re-fill the data buffer if necessary and indicated so. */ - if((SDO->ODF_arg.ODdataStorage == 0) && (SDO->ODF_arg.dataLength < (SDO->blksize*7U)) && (!SDO->ODF_arg.lastSegment)){ - /* move the beginning of the data buffer */ - len = SDO->ODF_arg.dataLength; /* length of valid data in buffer */ - SDO->ODF_arg.data += len; - SDO->ODF_arg.dataLength = CO_OD_getLength(SDO, SDO->entryNo, SDO->ODF_arg.subIndex) - len; - - /* read next data from Object dictionary function */ - abortCode = CO_SDO_readOD(SDO, CO_SDO_BUFFER_SIZE); - if(abortCode != 0U){ - CO_SDO_abort(SDO, abortCode); - return -1; - } - - /* calculate CRC on next bytes, if enabled */ - if(SDO->crcEnabled){ - SDO->crc = crc16_ccitt(SDO->ODF_arg.data, SDO->ODF_arg.dataLength, SDO->crc); - } - - /* return to the original data buffer */ - SDO->ODF_arg.data -= len; - SDO->ODF_arg.dataLength += len; - } - - /* verify if SDO data buffer is large enough */ - if(((SDO->blksize*7U) > SDO->ODF_arg.dataLength) && (!SDO->ODF_arg.lastSegment)){ - CO_SDO_abort(SDO, CO_SDO_AB_BLOCK_SIZE); /* Invalid block size (block mode only). */ - return -1; - } - - SDO->bufferOffset = 0U; - SDO->sequence = 0U; - SDO->endOfTransfer = false; - - /* clear flag here */ - CLEAR_CANrxNew(SDO->CANrxNew); - } - - /* return, if all segments was already transfered or on end of transfer */ - if((SDO->sequence == SDO->blksize) || (SDO->endOfTransfer)){ - return 1;/* don't call CLEAR_CANrxNew, so return directly */ - } - - /* reset timeout */ - SDO->timeoutTimer = 0; - - /* calculate length to be sent */ - len = SDO->ODF_arg.dataLength - SDO->bufferOffset; - if(len > 7U){ - len = 7U; - } - - /* fill response data bytes */ - for(i=0U; iCANtxBuff->data[i+1] = SDO->ODF_arg.data[SDO->bufferOffset++]; - } - - /* first response byte */ - SDO->CANtxBuff->data[0] = ++SDO->sequence; - - /* verify end of transfer */ - if((SDO->bufferOffset == SDO->ODF_arg.dataLength) && (SDO->ODF_arg.lastSegment)){ - SDO->CANtxBuff->data[0] |= 0x80; - SDO->lastLen = len; - SDO->blksize = SDO->sequence; - SDO->endOfTransfer = true; - } - - /* send response */ - CO_CANsend(SDO->CANdevTx, SDO->CANtxBuff); - - /* Set timerNext_ms to 0 to inform OS to call this function again without delay. */ - if(timerNext_ms != NULL){ - *timerNext_ms = 0; - } - - /* don't call CLEAR_CANrxNew, so return directly */ - return 1; - } - - case CO_SDO_ST_UPLOAD_BL_END:{ - /* verify client command specifier */ - if((SDO->CANrxData[0]&0xE1U) != 0xA1U){ - CO_SDO_abort(SDO, CO_SDO_AB_CMD);/* Client command specifier not valid or unknown. */ - return -1; - } - - SDO->state = CO_SDO_ST_IDLE; - break; - } - - case CO_SDO_ST_IDLE: - { - /* Nothing to do it seems */ - break; - } - - default:{ - CO_SDO_abort(SDO, CO_SDO_AB_DEVICE_INCOMPAT);/* general internal incompatibility in the device */ - return -1; - } - } - - /* free buffer and send message */ - CLEAR_CANrxNew(SDO->CANrxNew); - if(sendResponse) { - CO_CANsend(SDO->CANdevTx, SDO->CANtxBuff); - } - - if(SDO->state != CO_SDO_ST_IDLE){ - return 1; - } - - return 0; -} diff --git a/stack/CO_SDO.h b/stack/CO_SDO.h deleted file mode 100644 index 9e2c68f2..00000000 --- a/stack/CO_SDO.h +++ /dev/null @@ -1,961 +0,0 @@ -/** - * CANopen Service Data Object - server protocol. - * - * @file CO_SDO.h - * @ingroup CO_SDO - * @author Janez Paternoster - * @copyright 2004 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#ifndef CO_SDO_H -#define CO_SDO_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @defgroup CO_SDO SDO server - * @ingroup CO_CANopen - * @{ - * - * CANopen Service Data Object - server protocol. - * - * Service data objects (SDOs) allow the access to any entry of the CANopen - * Object dictionary. An SDO establishes a peer-to-peer communication channel - * between two devices. In addition, the SDO protocol enables to transfer any - * amount of data in a segmented way. Therefore the SDO protocol is mainly used - * in order to communicate configuration data. - * - * All CANopen devices must have implemented SDO server and first SDO server - * channel. Servers serves data from Object dictionary. Object dictionary - * is a collection of variables, arrays or records (structures), which can be - * used by the stack or by the application. This file (CO_SDO.h) implements - * SDO server. - * - * SDO client can be (optionally) implemented on one (or multiple, if multiple - * SDO channels are used) device in CANopen network. Usually this is master - * device and provides also some kind of user interface, so configuration of - * the network is possible. Code for the SDO client is in file CO_SDOmaster.h. - * - * SDO communication cycle is initiated by the client. Client can upload (read) data - * from device or can download (write) data to device. If data are less or equal - * of 4 bytes long, communication is finished by one server response (expedited - * transfer). If data are longer, they are split into multiple segments of - * request/response pairs (normal or segmented transfer). For longer data there - * is also a block transfer protocol, which transfers larger block of data in - * secure way with little protocol overhead. If error occurs during SDO transfer - * #CO_SDO_abortCode_t is send by client or server and transfer is terminated. - */ - - -/** - * @defgroup CO_SDO_messageContents SDO message contents - * - * Excerpt from CiA DS301, V4.2. - * - * For CAN identifier see #CO_Default_CAN_ID_t - * - * Expedited transfer is used for transmission of up to 4 data bytes. It consists - * of one SDO request and one response. For longer variables is used segmented - * or block transfer. - * - * ####Initiate SDO download (client request) - * - byte 0: SDO command specifier. 8 bits: `0010nnes` (nn: if e=s=1, - * number of data bytes, that do *not* contain data; e=1 for - * expedited transfer; s=1 if data size is indicated). - * - byte 1..2: Object index. - * - byte 3: Object subIndex. - * - byte 4..7: Expedited data or data size if segmented transfer. - * - * ####Initiate SDO download (server response) - * - byte 0: SDO command specifier. 8 bits: `01100000`. - * - byte 1..2: Object index. - * - byte 3: Object subIndex. - * - byte 4..7: reserved. - * - * ####Download SDO segment (client request) - * - byte 0: SDO command specifier. 8 bits: `000tnnnc` (t: toggle bit set - * to 0 in first segment; nnn: number of data bytes, that do - * *not* contain data; c=1 if this is the last segment). - * - byte 1..7: Data segment. - * - * ####Download SDO segment (server response) - * - byte 0: SDO command specifier. 8 bits: `001t0000` (t: toggle bit set - * to 0 in first segment). - * - byte 1..7: Reserved. - * - * ####Initiate SDO upload (client request) - * - byte 0: SDO command specifier. 8 bits: `01000000`. - * - byte 1..2: Object index. - * - byte 3: Object subIndex. - * - byte 4..7: Reserved. - * - * ####Initiate SDO upload (server response) - * - byte 0: SDO command specifier. 8 bits: `0100nnes` (nn: if e=s=1, - * number of data bytes, that do *not* contain data; e=1 for - * expedited transfer; s=1 if data size is indicated). - * - byte 1..2: Object index. - * - byte 3: Object subIndex. - * - byte 4..7: reserved. - * - * ####Upload SDO segment (client request) - * - byte 0: SDO command specifier. 8 bits: `011t0000` (t: toggle bit set - * to 0 in first segment). - * - byte 1..7: Reserved. - * - * ####Upload SDO segment (server response) - * - byte 0: SDO command specifier. 8 bits: `000tnnnc` (t: toggle bit set - * to 0 in first segment; nnn: number of data bytes, that do - * *not* contain data; c=1 if this is the last segment). - * - byte 1..7: Data segment. - * - * ####Abort SDO transfer (client or server) - * - byte 0: SDO command specifier. 8 bits: `10000000`. - * - byte 1..2: Object index. - * - byte 3: Object subIndex. - * - byte 4..7: #CO_SDO_abortCode_t. - * - * ####Block transfer - * See DS301 V4.2. - */ - - -/** - * SDO abort codes. - * - * Send with Abort SDO transfer message. - * - * The abort codes not listed here are reserved. - */ -typedef enum{ - CO_SDO_AB_NONE = 0x00000000UL, /**< 0x00000000, No abort */ - CO_SDO_AB_TOGGLE_BIT = 0x05030000UL, /**< 0x05030000, Toggle bit not altered */ - CO_SDO_AB_TIMEOUT = 0x05040000UL, /**< 0x05040000, SDO protocol timed out */ - CO_SDO_AB_CMD = 0x05040001UL, /**< 0x05040001, Command specifier not valid or unknown */ - CO_SDO_AB_BLOCK_SIZE = 0x05040002UL, /**< 0x05040002, Invalid block size in block mode */ - CO_SDO_AB_SEQ_NUM = 0x05040003UL, /**< 0x05040003, Invalid sequence number in block mode */ - CO_SDO_AB_CRC = 0x05040004UL, /**< 0x05040004, CRC error (block mode only) */ - CO_SDO_AB_OUT_OF_MEM = 0x05040005UL, /**< 0x05040005, Out of memory */ - CO_SDO_AB_UNSUPPORTED_ACCESS = 0x06010000UL, /**< 0x06010000, Unsupported access to an object */ - CO_SDO_AB_WRITEONLY = 0x06010001UL, /**< 0x06010001, Attempt to read a write only object */ - CO_SDO_AB_READONLY = 0x06010002UL, /**< 0x06010002, Attempt to write a read only object */ - CO_SDO_AB_NOT_EXIST = 0x06020000UL, /**< 0x06020000, Object does not exist */ - CO_SDO_AB_NO_MAP = 0x06040041UL, /**< 0x06040041, Object cannot be mapped to the PDO */ - CO_SDO_AB_MAP_LEN = 0x06040042UL, /**< 0x06040042, Number and length of object to be mapped exceeds PDO length */ - CO_SDO_AB_PRAM_INCOMPAT = 0x06040043UL, /**< 0x06040043, General parameter incompatibility reasons */ - CO_SDO_AB_DEVICE_INCOMPAT = 0x06040047UL, /**< 0x06040047, General internal incompatibility in device */ - CO_SDO_AB_HW = 0x06060000UL, /**< 0x06060000, Access failed due to hardware error */ - CO_SDO_AB_TYPE_MISMATCH = 0x06070010UL, /**< 0x06070010, Data type does not match, length of service parameter does not match */ - CO_SDO_AB_DATA_LONG = 0x06070012UL, /**< 0x06070012, Data type does not match, length of service parameter too high */ - CO_SDO_AB_DATA_SHORT = 0x06070013UL, /**< 0x06070013, Data type does not match, length of service parameter too short */ - CO_SDO_AB_SUB_UNKNOWN = 0x06090011UL, /**< 0x06090011, Sub index does not exist */ - CO_SDO_AB_INVALID_VALUE = 0x06090030UL, /**< 0x06090030, Invalid value for parameter (download only). */ - CO_SDO_AB_VALUE_HIGH = 0x06090031UL, /**< 0x06090031, Value range of parameter written too high */ - CO_SDO_AB_VALUE_LOW = 0x06090032UL, /**< 0x06090032, Value range of parameter written too low */ - CO_SDO_AB_MAX_LESS_MIN = 0x06090036UL, /**< 0x06090036, Maximum value is less than minimum value. */ - CO_SDO_AB_NO_RESOURCE = 0x060A0023UL, /**< 0x060A0023, Resource not available: SDO connection */ - CO_SDO_AB_GENERAL = 0x08000000UL, /**< 0x08000000, General error */ - CO_SDO_AB_DATA_TRANSF = 0x08000020UL, /**< 0x08000020, Data cannot be transferred or stored to application */ - CO_SDO_AB_DATA_LOC_CTRL = 0x08000021UL, /**< 0x08000021, Data cannot be transferred or stored to application because of local control */ - CO_SDO_AB_DATA_DEV_STATE = 0x08000022UL, /**< 0x08000022, Data cannot be transferred or stored to application because of present device state */ - CO_SDO_AB_DATA_OD = 0x08000023UL, /**< 0x08000023, Object dictionary not present or dynamic generation fails */ - CO_SDO_AB_NO_DATA = 0x08000024UL /**< 0x08000024, No data available */ -}CO_SDO_abortCode_t; - - -/** - * @defgroup CO_SDO_objectDictionary Object dictionary - * - * CANopen Object dictionary implementation in CANopenNode. - * - * CANopen Object dictionary is a collection of different data items, which can - * be used by the stack or by the application. - * - * Each Object dictionary entry is located under 16-bit index, as specified - * by the CANopen: - * - 0x0001..0x025F: Data type definitions. - * - 0x1000..0x1FFF: Communication profile area. - * - 0x2000..0x5FFF: Manufacturer-specific profile area. - * - 0x6000..0x9FFF: Standardized device profile area for eight logical devices. - * - 0xA000..0xAFFF: Standardized network variable area. - * - 0xB000..0xBFFF: Standardized system variable area. - * - Other: Reserved. - * - * If Object dictionary entry has complex data type (array or structure), - * then 8-bit subIndex specifies the sub-member of the entry. In that case - * subIndex 0x00 is encoded as uint8_t and specifies the highest available - * subIndex with that entry. Subindex 0xFF has special meaning in the standard - * and is not supported by CANopenNode. - * - * ####Object type of one Object dictionary entry - * - NULL: Not used by CANopenNode. - * - DOMAIN: Block of data of variable length. Data and length are - * under control of the application. - * - DEFTYPE: Definition of CANopen basic data type, for example - * INTEGER16. - * - DEFSTRUCT: Definition of complex data type - structure, which is - * used with RECORD. - * - VAR: Variable of CANopen basic data type. Subindex is 0. - * - ARRAY: Array of multiple variables of the same CANopen basic - * data type. Subindex 1..arrayLength specifies sub-member. - * - RECORD: Record or structure of multiple variables of different - * CANopen basic data type. Subindex specifies sub-member. - * - * - * ####Implementation in CANopenNode - * Object dictionary in CANopenNode is implemented in CO_OD.h and CO_OD.c files. - * These files are application specific and must be generated by Object - * dictionary editor (application is included by the stack). - * - * CO_OD.h and CO_OD.c files include: - * - Structure definitions for records. - * - Global declaration and initialization of all variables, arrays and records - * mapped to Object dictionary. Variables are distributed in multiple objects, - * depending on memory location. This eases storage to different memories in - * microcontroller, like eeprom or flash. - * - Constant array of multiple Object dictionary entries of type - * CO_OD_entry_t. If object type is record, then entry includes additional - * constant array with members of type CO_OD_entryRecord_t. Each OD entry - * includes information: index, maxSubIndex, #CO_SDO_OD_attributes_t, data size and - * pointer to variable. - * - * - * Function CO_SDO_init() initializes object CO_SDO_t, which includes SDO - * server and Object dictionary. - * - * Application doesn't need to know anything about the Object dictionary. It can - * use variables specified in CO_OD.h file directly. If it needs more control - * over the CANopen communication with the variables, it can configure additional - * functionality with function CO_OD_configure(). Additional functionality - * include: @ref CO_SDO_OD_function and #CO_SDO_OD_flags_t. - * - * Interface to Object dictionary is provided by following functions: CO_OD_find() - * finds OD entry by index, CO_OD_getLength() returns length of variable, - * CO_OD_getAttribute returns attribute and CO_OD_getDataPointer() returns pointer - * to data. These functions are used by SDO server and by PDO configuration. They - * can also be used to access the OD by index like this. - * - * \code{.c} - * index = CO_OD_find(CO->SDO[0], OD_H1001_ERR_REG); - * if (index == 0xffff) { - * return; - * } - * length = CO_OD_getLength(CO->SDO[0], index, 1); - * if (length != sizeof(new_data)) { - * return; - * } - * - * p = CO_OD_getDataPointer(CO->SDO[0], index, 1); - * if (p == NULL) { - * return; - * } - * CO_LOCK_OD(); - * *p = new_data; - * CO_UNLOCK_OD(); - * \endcode - * - * Be aware that accessing the OD directly using CO_OD.h files is more CPU - * efficient as CO_OD_find() has to do a search everytime it is called. - * - */ - - -/** - * @defgroup CO_SDO_OD_function Object Dictionary function - * - * Optional application specific function, which may manipulate data downloaded - * or uploaded via SDO. - * - * Object dictionary function is external function defined by application or - * by other stack files. It may be registered for specific Object dictionary - * entry (with specific index). If it is registered, it is called (through - * function pointer) from SDO server. It may verify and manipulate data during - * SDO transfer. Object dictionary function can be registered by function - * CO_OD_configure(). - * - * ####SDO download (writing to Object dictionary) - * After SDO client transfers data to the server, data are stored in internal - * buffer. If data contains multibyte variable and processor is big endian, - * then data bytes are swapped. Object dictionary function is called if - * registered. Data may be verified and manipulated inside that function. After - * function exits, data are copied to location as specified in CO_OD_entry_t. - * - * ####SDO upload (reading from Object dictionary) - * Before start of SDO upload, data are read from Object dictionary into - * internal buffer. If necessary, bytes are swapped. - * Object dictionary function is called if registered. Data may be - * manipulated inside that function. After function exits, data are - * transferred via SDO server. - * - * ####Domain data type - * If data type is domain, then length is not specified by Object dictionary. - * In that case Object dictionary function must be used. In case of - * download it must store the data in own location. In case of upload it must - * write the data (maximum size is specified by length) into data buffer and - * specify actual length. With domain data type it is possible to transfer - * data, which are longer than #CO_SDO_BUFFER_SIZE. In that case - * Object dictionary function is called multiple times between SDO transfer. - * - * ####Parameter to function: - * ODF_arg - Pointer to CO_ODF_arg_t object filled before function call. - * - * ####Return from function: - * - 0: Data transfer is successful - * - Different than 0: Failure. See #CO_SDO_abortCode_t. - */ - - -/** - * SDO buffer size. - * - * Size of the internal SDO buffer. - * - * Size must be at least equal to size of largest variable in @ref CO_SDO_objectDictionary. - * If data type is domain, data length is not limited to SDO buffer size. If - * block transfer is implemented, value should be set to 889. - * - * Value can be in range from 7 to 889 bytes. - */ - #ifndef CO_SDO_BUFFER_SIZE - #define CO_SDO_BUFFER_SIZE 32 - #endif - - -/** - * Object Dictionary attributes. Bit masks for attribute in CO_OD_entry_t. - */ -typedef enum{ - CO_ODA_MEM_ROM = 0x0001U, /**< Variable is located in ROM memory */ - CO_ODA_MEM_RAM = 0x0002U, /**< Variable is located in RAM memory */ - CO_ODA_MEM_EEPROM = 0x0003U, /**< Variable is located in EEPROM memory */ - CO_ODA_READABLE = 0x0004U, /**< SDO server may read from the variable */ - CO_ODA_WRITEABLE = 0x0008U, /**< SDO server may write to the variable */ - CO_ODA_RPDO_MAPABLE = 0x0010U, /**< Variable is mappable for RPDO */ - CO_ODA_TPDO_MAPABLE = 0x0020U, /**< Variable is mappable for TPDO */ - CO_ODA_TPDO_DETECT_COS = 0x0040U, /**< If variable is mapped to any PDO, then - PDO is automatically send, if variable - changes its value */ - CO_ODA_MB_VALUE = 0x0080U /**< True when variable is a multibyte value */ -}CO_SDO_OD_attributes_t; - - -/** - * Common DS301 object dictionary entries. - */ -typedef enum{ - OD_H1000_DEV_TYPE = 0x1000U,/**< Device type */ - OD_H1001_ERR_REG = 0x1001U,/**< Error register */ - OD_H1002_MANUF_STATUS_REG = 0x1002U,/**< Manufacturer status register */ - OD_H1003_PREDEF_ERR_FIELD = 0x1003U,/**< Predefined error field */ - OD_H1004_RSV = 0x1004U,/**< Reserved */ - OD_H1005_COBID_SYNC = 0x1005U,/**< Sync message cob-id */ - OD_H1006_COMM_CYCL_PERIOD = 0x1006U,/**< Communication cycle period */ - OD_H1007_SYNC_WINDOW_LEN = 0x1007U,/**< Sync windows length */ - OD_H1008_MANUF_DEV_NAME = 0x1008U,/**< Manufacturer device name */ - OD_H1009_MANUF_HW_VERSION = 0x1009U,/**< Manufacturer hardware version */ - OD_H100A_MANUF_SW_VERSION = 0x100AU,/**< Manufacturer software version */ - OD_H100B_RSV = 0x100BU,/**< Reserved */ - OD_H100C_GUARD_TIME = 0x100CU,/**< Guard time */ - OD_H100D_LIFETIME_FACTOR = 0x100DU,/**< Life time factor */ - OD_H100E_RSV = 0x100EU,/**< Reserved */ - OD_H100F_RSV = 0x100FU,/**< Reserved */ - OD_H1010_STORE_PARAM_FUNC = 0x1010U,/**< Store parameter in persistent memory function */ - OD_H1011_REST_PARAM_FUNC = 0x1011U,/**< Restore default parameter function */ - OD_H1012_COBID_TIME = 0x1012U,/**< Timestamp message cob-id */ - OD_H1013_HIGH_RES_TIMESTAMP = 0x1013U,/**< High resolution timestamp */ - OD_H1014_COBID_EMERGENCY = 0x1014U,/**< Emergency message cob-id */ - OD_H1015_INHIBIT_TIME_MSG = 0x1015U,/**< Inhibit time message */ - OD_H1016_CONSUMER_HB_TIME = 0x1016U,/**< Consumer heartbeat time */ - OD_H1017_PRODUCER_HB_TIME = 0x1017U,/**< Producer heartbeat time */ - OD_H1018_IDENTITY_OBJECT = 0x1018U,/**< Identity object */ - OD_H1019_SYNC_CNT_OVERFLOW = 0x1019U,/**< Sync counter overflow value */ - OD_H1020_VERIFY_CONFIG = 0x1020U,/**< Verify configuration */ - OD_H1021_STORE_EDS = 0x1021U,/**< Store EDS */ - OD_H1022_STORE_FORMAT = 0x1022U,/**< Store format */ - OD_H1023_OS_CMD = 0x1023U,/**< OS command */ - OD_H1024_OS_CMD_MODE = 0x1024U,/**< OS command mode */ - OD_H1025_OS_DBG_INTERFACE = 0x1025U,/**< OS debug interface */ - OD_H1026_OS_PROMPT = 0x1026U,/**< OS prompt */ - OD_H1027_MODULE_LIST = 0x1027U,/**< Module list */ - OD_H1028_EMCY_CONSUMER = 0x1028U,/**< Emergency consumer object */ - OD_H1029_ERR_BEHAVIOR = 0x1029U,/**< Error behaviour */ - OD_H1200_SDO_SERVER_PARAM = 0x1200U,/**< SDO server parameters */ - OD_H1280_SDO_CLIENT_PARAM = 0x1280U,/**< SDO client parameters */ - OD_H1400_RXPDO_1_PARAM = 0x1400U,/**< RXPDO communication parameter */ - OD_H1401_RXPDO_2_PARAM = 0x1401U,/**< RXPDO communication parameter */ - OD_H1402_RXPDO_3_PARAM = 0x1402U,/**< RXPDO communication parameter */ - OD_H1403_RXPDO_4_PARAM = 0x1403U,/**< RXPDO communication parameter */ - OD_H1600_RXPDO_1_MAPPING = 0x1600U,/**< RXPDO mapping parameters */ - OD_H1601_RXPDO_2_MAPPING = 0x1601U,/**< RXPDO mapping parameters */ - OD_H1602_RXPDO_3_MAPPING = 0x1602U,/**< RXPDO mapping parameters */ - OD_H1603_RXPDO_4_MAPPING = 0x1603U,/**< RXPDO mapping parameters */ - OD_H1800_TXPDO_1_PARAM = 0x1800U,/**< TXPDO communication parameter */ - OD_H1801_TXPDO_2_PARAM = 0x1801U,/**< TXPDO communication parameter */ - OD_H1802_TXPDO_3_PARAM = 0x1802U,/**< TXPDO communication parameter */ - OD_H1803_TXPDO_4_PARAM = 0x1803U,/**< TXPDO communication parameter */ - OD_H1A00_TXPDO_1_MAPPING = 0x1A00U,/**< TXPDO mapping parameters */ - OD_H1A01_TXPDO_2_MAPPING = 0x1A01U,/**< TXPDO mapping parameters */ - OD_H1A02_TXPDO_3_MAPPING = 0x1A02U,/**< TXPDO mapping parameters */ - OD_H1A03_TXPDO_4_MAPPING = 0x1A03U /**< TXPDO mapping parameters */ -}CO_ObjDicId_t; - - -/** - * Bit masks for flags associated with variable from @ref CO_SDO_objectDictionary. - * - * This additional functionality of any variable in @ref CO_SDO_objectDictionary can be - * enabled by function CO_OD_configure(). Location of the flag byte can be - * get from function CO_OD_getFlagsPointer(). - */ -typedef enum{ - /** Variable was written by RPDO. Flag can be cleared by application */ - CO_ODFL_RPDO_WRITTEN = 0x01U, - /** Variable is mapped to TPDO */ - CO_ODFL_TPDO_MAPPED = 0x02U, - /** Change of state bit, initially copy of attribute from CO_OD_entry_t. - If set and variable is mapped to TPDO, TPDO will be automatically send, - if variable changed */ - CO_ODFL_TPDO_COS_ENABLE = 0x04U, - /** PDO send bit, can be set by application. If variable is mapped into - TPDO, TPDO will be send and bit will be cleared. */ - CO_ODFL_TPDO_SEND = 0x08U, - /** Variable was accessed by SDO download */ - CO_ODFL_SDO_DOWNLOADED = 0x10U, - /** Variable was accessed by SDO upload */ - CO_ODFL_SDO_UPLOADED = 0x20U, - /** Reserved */ - CO_ODFL_BIT_6 = 0x40U, - /** Reserved */ - CO_ODFL_BIT_7 = 0x80U -}CO_SDO_OD_flags_t; - - -/** - * Internal states of the SDO server state machine - */ -typedef enum { - CO_SDO_ST_IDLE = 0x00U, - CO_SDO_ST_DOWNLOAD_INITIATE = 0x11U, - CO_SDO_ST_DOWNLOAD_SEGMENTED = 0x12U, - CO_SDO_ST_DOWNLOAD_BL_INITIATE = 0x14U, - CO_SDO_ST_DOWNLOAD_BL_SUBBLOCK = 0x15U, - CO_SDO_ST_DOWNLOAD_BL_SUB_RESP = 0x16U, - CO_SDO_ST_DOWNLOAD_BL_SUB_RESP_2 = 0x17U, - CO_SDO_ST_DOWNLOAD_BL_END = 0x18U, - CO_SDO_ST_UPLOAD_INITIATE = 0x21U, - CO_SDO_ST_UPLOAD_SEGMENTED = 0x22U, - CO_SDO_ST_UPLOAD_BL_INITIATE = 0x24U, - CO_SDO_ST_UPLOAD_BL_INITIATE_2 = 0x25U, - CO_SDO_ST_UPLOAD_BL_SUBBLOCK = 0x26U, - CO_SDO_ST_UPLOAD_BL_END = 0x27U -} CO_SDO_state_t; - - -/** - * Object for one entry with specific index in @ref CO_SDO_objectDictionary. - */ -typedef struct { - /** The index of Object from 0x1000 to 0xFFFF */ - uint16_t index; - /** Number of (sub-objects - 1). If Object Type is variable, then - maxSubIndex is 0, otherwise maxSubIndex is equal or greater than 1. */ - uint8_t maxSubIndex; - /** If Object Type is record, attribute is set to zero. Attribute for - each member is then set in special array with members of type - CO_OD_entryRecord_t. If Object Type is Array, attribute is common for - all array members. See #CO_SDO_OD_attributes_t. */ - uint16_t attribute; - /** If Object Type is Variable, length is the length of variable in bytes. - If Object Type is Array, length is the length of one array member. - If Object Type is Record, length is zero. Length for each member is - set in special array with members of type CO_OD_entryRecord_t. - If Object Type is Domain, length is zero. Length is specified - by application in @ref CO_SDO_OD_function. */ - uint16_t length; - /** If Object Type is Variable, pData is pointer to data. - If Object Type is Array, pData is pointer to data. Data doesn't - include Sub-Object 0. - If object type is Record, pData is pointer to special array - with members of type CO_OD_entryRecord_t. - If object type is Domain, pData is null. */ - void *pData; -}CO_OD_entry_t; - - -/** - * Object for record type entry in @ref CO_SDO_objectDictionary. - * - * See CO_OD_entry_t. - */ -typedef struct{ - /** See #CO_SDO_OD_attributes_t */ - void *pData; - /** Length of variable in bytes. If object type is Domain, length is zero */ - uint16_t attribute; - /** Pointer to data. If object type is Domain, pData is null */ - uint16_t length; -}CO_OD_entryRecord_t; - - -/** - * Object contains all information about the object being transferred by SDO server. - * - * Object is used as an argument to @ref CO_SDO_OD_function. It is also - * part of the CO_SDO_t object. - */ -typedef struct{ - /** Informative parameter. It may point to object, which is connected - with this OD entry. It can be used inside @ref CO_SDO_OD_function, ONLY - if it was registered by CO_OD_configure() function before. */ - void *object; - /** SDO data buffer contains data, which are exchanged in SDO transfer. - @ref CO_SDO_OD_function may verify or manipulate that data before (after) - they are written to (read from) Object dictionary. Data have the same - endianes as processor. Pointer must NOT be changed. (Data up to length - can be changed.) */ - uint8_t *data; - /** Pointer to location in object dictionary, where data are stored. - (informative reference to old data, read only). Data have the same - endianes as processor. If data type is Domain, this variable is null. */ - const void *ODdataStorage; - /** Length of data in the above buffer. Read only, except for domain. If - data type is domain see @ref CO_SDO_OD_function for special rules by upload. */ - uint16_t dataLength; - /** Attribute of object in Object dictionary (informative, must NOT be changed). */ - uint16_t attribute; - /** Pointer to the #CO_SDO_OD_flags_t byte. */ - uint8_t *pFlags; - /** Index of object in Object dictionary (informative, must NOT be changed). */ - uint16_t index; - /** Subindex of object in Object dictionary (informative, must NOT be changed). */ - uint8_t subIndex; - /** True, if SDO upload is in progress, false if SDO download is in progress. */ - bool_t reading; - /** Used by domain data type. Indicates the first segment. Variable is informative. */ - bool_t firstSegment; - /** Used by domain data type. If false by download, then application will - receive more segments during SDO communication cycle. If uploading, - application may set variable to false, so SDO server will call - @ref CO_SDO_OD_function again for filling the next data. */ - bool_t lastSegment; - /** Used by domain data type. By upload @ref CO_SDO_OD_function may write total - data length, so this information will be send in SDO upload initiate phase. It - is not necessary to specify this variable. By download this variable contains - total data size, if size is indicated in SDO download initiate phase */ - uint32_t dataLengthTotal; - /** Used by domain data type. In case of multiple segments, this indicates the offset - into the buffer this segment starts at. */ - uint32_t offset; -}CO_ODF_arg_t; - - -/** - * Object is used as array inside CO_SDO_t, parallel to @ref CO_SDO_objectDictionary. - * - * Object is generated by function CO_OD_configure(). It is then used as - * extension to Object dictionary entry at specific index. - */ -typedef struct{ - /** Pointer to @ref CO_SDO_OD_function */ - CO_SDO_abortCode_t (*pODFunc)(CO_ODF_arg_t *ODF_arg); - /** Pointer to object, which will be passed to @ref CO_SDO_OD_function */ - void *object; - /** Pointer to #CO_SDO_OD_flags_t. If object type is array or record, this - variable points to array with length equal to number of subindexes. */ - uint8_t *flags; -}CO_OD_extension_t; - - -/** - * SDO server object. - */ -typedef struct{ - /** 8 data bytes of the received message. */ - uint8_t CANrxData[8]; - /** SDO data buffer of size #CO_SDO_BUFFER_SIZE. */ - uint8_t databuffer[CO_SDO_BUFFER_SIZE]; - /** Internal flag indicates, that this object has own OD */ - bool_t ownOD; - /** Pointer to the @ref CO_SDO_objectDictionary (array) */ - const CO_OD_entry_t *OD; - /** Size of the @ref CO_SDO_objectDictionary */ - uint16_t ODSize; - /** Pointer to array of CO_OD_extension_t objects. Size of the array is - equal to ODSize. */ - CO_OD_extension_t *ODExtensions; - /** Offset in buffer of next data segment being read/written */ - uint16_t bufferOffset; - /** Sequence number of OD entry as returned from CO_OD_find() */ - uint16_t entryNo; - /** CO_ODF_arg_t object with additional variables. Reference to this object - is passed to @ref CO_SDO_OD_function */ - CO_ODF_arg_t ODF_arg; - /** From CO_SDO_init() */ - uint8_t nodeId; - /** Current internal state of the SDO server state machine #CO_SDO_state_t */ - CO_SDO_state_t state; - /** Toggle bit in segmented transfer or block sequence in block transfer */ - uint8_t sequence; - /** Timeout timer for SDO communication */ - uint16_t timeoutTimer; - /** Number of segments per block with 1 <= blksize <= 127 */ - uint8_t blksize; - /** True, if CRC calculation by block transfer is enabled */ - bool_t crcEnabled; - /** Calculated CRC code */ - uint16_t crc; - /** Length of data in the last segment in block upload */ - uint8_t lastLen; - /** Indication timeout in sub-block transfer */ - bool_t timeoutSubblockDownolad; - /** Indication end of block transfer */ - bool_t endOfTransfer; - /** Variable indicates, if new SDO message received from CAN bus */ - volatile void *CANrxNew; - /** From CO_SDO_initCallback() or NULL */ - void (*pFunctSignal)(void); - /** From CO_SDO_init() */ - CO_CANmodule_t *CANdevTx; - /** CAN transmit buffer inside CANdev for CAN tx message */ - CO_CANtx_t *CANtxBuff; -}CO_SDO_t; - - -/** - * Helper union for manipulating data bytes. - */ -typedef union{ - uint8_t u8[8]; /**< 8 bytes */ - uint16_t u16[4]; /**< 4 words */ - uint32_t u32[2]; /**< 2 double words */ -}CO_bytes_t; - - -/** - * Helper function like memcpy. - * - * Function copies n data bytes from source to destination. - * - * @param dest Destination location. - * @param src Source location. - * @param size Number of data bytes to be copied (max 0xFFFF). - */ -void CO_memcpy(uint8_t dest[], const uint8_t src[], const uint16_t size); - -/** - * Helper function like memset. - * - * Function fills destination with char "c". - * - * @param dest Destination location. - * @param c set value. - * @param size Number of data bytes to be copied (max 0xFFFF). - */ -void CO_memset(uint8_t dest[], uint8_t c, const uint16_t size); - - -/** - * Helper function returns uint16 from byte array. - * - * @param data Location of source data. - * @return Variable of type uint16_t. - */ -uint16_t CO_getUint16(const uint8_t data[]); - - -/** - * Helper function returns uint32 from byte array. - * - * @param data Location of source data. - * @return Variable of type uint32_t. - */ -uint32_t CO_getUint32(const uint8_t data[]); - - -/** - * Helper function writes uint16 to byte array. - * - * @param data Location of destination data. - * @param value Variable of type uint16_t to be written into data. - */ -void CO_setUint16(uint8_t data[], const uint16_t value); - - -/** - * Helper function writes uint32 to byte array. - * - * @param data Location of destination data. - * @param value Variable of type uint32_t to be written into data. - */ -void CO_setUint32(uint8_t data[], const uint32_t value); - - -/** - * Copy 2 data bytes from source to destination. Swap bytes if - * microcontroller is big-endian. - * - * @param dest Destination location. - * @param src Source location. - */ -void CO_memcpySwap2(void* dest, const void* src); - - -/** - * Copy 4 data bytes from source to destination. Swap bytes if - * microcontroller is big-endian. - * - * @param dest Destination location. - * @param src Source location. - */ -void CO_memcpySwap4(void* dest, const void* src); - - -/** - * Copy 8 data bytes from source to destination. Swap bytes if - * microcontroller is big-endian. - * - * @param dest Destination location. - * @param src Source location. - */ -void CO_memcpySwap8(void* dest, const void* src); - - -/** - * Initialize SDO object. - * - * Function must be called in the communication reset section. - * - * @param SDO This object will be initialized. - * @param COB_IDClientToServer COB ID for client to server for this SDO object. - * @param COB_IDServerToClient COB ID for server to client for this SDO object. - * @param ObjDictIndex_SDOServerParameter Index in Object dictionary. - * @param parentSDO Pointer to SDO object, which contains object dictionary and - * its extension. For first (default) SDO object this argument must be NULL. - * If this argument is specified, then OD, ODSize and ODExtensions arguments - * are ignored. - * @param OD Pointer to @ref CO_SDO_objectDictionary array defined externally. - * @param ODSize Size of the above array. - * @param ODExtensions Pointer to the externally defined array of the same size - * as ODSize. - * @param nodeId CANopen Node ID of this device. - * @param CANdevRx CAN device for SDO server reception. - * @param CANdevRxIdx Index of receive buffer in the above CAN device. - * @param CANdevTx CAN device for SDO server transmission. - * @param CANdevTxIdx Index of transmit buffer in the above CAN device. - * - * @return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT. - */ -CO_ReturnError_t CO_SDO_init( - CO_SDO_t *SDO, - uint32_t COB_IDClientToServer, - uint32_t COB_IDServerToClient, - uint16_t ObjDictIndex_SDOServerParameter, - CO_SDO_t *parentSDO, - const CO_OD_entry_t OD[], - uint16_t ODSize, - CO_OD_extension_t ODExtensions[], - uint8_t nodeId, - CO_CANmodule_t *CANdevRx, - uint16_t CANdevRxIdx, - CO_CANmodule_t *CANdevTx, - uint16_t CANdevTxIdx); - - -/** - * Initialize SDOrx callback function. - * - * Function initializes optional callback function, which is called after new - * message is received from the CAN bus. Function may wake up external task, - * which processes mainline CANopen functions. - * - * @param SDO This object. - * @param pFunctSignal Pointer to the callback function. Not called if NULL. - */ -void CO_SDO_initCallback( - CO_SDO_t *SDO, - void (*pFunctSignal)(void)); - - -/** - * Process SDO communication. - * - * Function must be called cyclically. - * - * @param SDO This object. - * @param NMTisPreOrOperational Different than zero, if #CO_NMT_internalState_t is - * NMT_PRE_OPERATIONAL or NMT_OPERATIONAL. - * @param timeDifference_ms Time difference from previous function call in [milliseconds]. - * @param SDOtimeoutTime Timeout time for SDO communication in milliseconds. - * @param timerNext_ms Return value - info to OS - see CO_process(). - * - * @return 0: SDO server is idle. - * @return 1: SDO server is in transfer state. - * @return -1: SDO abort just occurred. - */ -int8_t CO_SDO_process( - CO_SDO_t *SDO, - bool_t NMTisPreOrOperational, - uint16_t timeDifference_ms, - uint16_t SDOtimeoutTime, - uint16_t *timerNext_ms); - - -/** - * Configure additional functionality to one @ref CO_SDO_objectDictionary entry. - * - * Additional functionality include: @ref CO_SDO_OD_function and - * #CO_SDO_OD_flags_t. It is optional feature and can be used on any object in - * Object dictionary. If OD entry does not exist, function returns silently. - * - * @param SDO This object. - * @param index Index of object in the Object dictionary. - * @param pODFunc Pointer to @ref CO_SDO_OD_function, specified by application. - * If NULL, @ref CO_SDO_OD_function will not be used on this object. - * @param object Pointer to object, which will be passed to @ref CO_SDO_OD_function. - * @param flags Pointer to array of #CO_SDO_OD_flags_t defined externally. If - * zero, #CO_SDO_OD_flags_t will not be used on this OD entry. - * @param flagsSize Size of the above array. It must be equal to number - * of sub-objects in object dictionary entry. Otherwise #CO_SDO_OD_flags_t will - * not be used on this OD entry. - */ -void CO_OD_configure( - CO_SDO_t *SDO, - uint16_t index, - CO_SDO_abortCode_t (*pODFunc)(CO_ODF_arg_t *ODF_arg), - void *object, - uint8_t *flags, - uint8_t flagsSize); - - -/** - * Find object with specific index in Object dictionary. - * - * @param SDO This object. - * @param index Index of the object in Object dictionary. - * - * @return Sequence number of the @ref CO_SDO_objectDictionary entry, 0xFFFF if not found. - */ -uint16_t CO_OD_find(CO_SDO_t *SDO, uint16_t index); - - -/** - * Get length of the given object with specific subIndex. - * - * @param SDO This object. - * @param entryNo Sequence number of OD entry as returned from CO_OD_find(). - * @param subIndex Sub-index of the object in Object dictionary. - * - * @return Data length of the variable. - */ -uint16_t CO_OD_getLength(CO_SDO_t *SDO, uint16_t entryNo, uint8_t subIndex); - - -/** - * Get attribute of the given object with specific subIndex. See #CO_SDO_OD_attributes_t. - * - * If Object Type is array and subIndex is zero, function always returns - * 'read-only' attribute. An exception to this rule is ID1003 (Error field). - * However, this is supposed to be only written by network. - * - * @param SDO This object. - * @param entryNo Sequence number of OD entry as returned from CO_OD_find(). - * @param subIndex Sub-index of the object in Object dictionary. - * - * @return Attribute of the variable. - */ -uint16_t CO_OD_getAttribute(CO_SDO_t *SDO, uint16_t entryNo, uint8_t subIndex); - - -/** - * Get pointer to data of the given object with specific subIndex. - * - * If Object Type is array and subIndex is zero, function returns pointer to - * object->maxSubIndex variable. - * - * @param SDO This object. - * @param entryNo Sequence number of OD entry as returned from CO_OD_find(). - * @param subIndex Sub-index of the object in Object dictionary. - * - * @return Pointer to the variable in @ref CO_SDO_objectDictionary. - */ -void* CO_OD_getDataPointer(CO_SDO_t *SDO, uint16_t entryNo, uint8_t subIndex); - - -/** - * Get pointer to the #CO_SDO_OD_flags_t byte of the given object with - * specific subIndex. - * - * @param SDO This object. - * @param entryNo Sequence number of OD entry as returned from CO_OD_find(). - * @param subIndex Sub-index of the object in Object dictionary. - * - * @return Pointer to the #CO_SDO_OD_flags_t of the variable. - */ -uint8_t* CO_OD_getFlagsPointer(CO_SDO_t *SDO, uint16_t entryNo, uint8_t subIndex); - - -/** - * Initialize SDO transfer. - * - * Find object in OD, verify, fill ODF_arg s. - * - * @param SDO This object. - * @param index Index of the object in Object dictionary. - * @param subIndex subIndex of the object in Object dictionary. - * - * @return 0 on success, otherwise #CO_SDO_abortCode_t. - */ -uint32_t CO_SDO_initTransfer(CO_SDO_t *SDO, uint16_t index, uint8_t subIndex); - - -/** - * Read data from @ref CO_SDO_objectDictionary to internal buffer. - * - * ODF_arg s must be initialized before with CO_SDO_initTransfer(). - * @ref CO_SDO_OD_function is called if configured. - * - * @param SDO This object. - * @param SDOBufferSize Total size of the SDO buffer. - * - * @return 0 on success, otherwise #CO_SDO_abortCode_t. - */ -uint32_t CO_SDO_readOD(CO_SDO_t *SDO, uint16_t SDOBufferSize); - - -/** - * Write data from internal buffer to @ref CO_SDO_objectDictionary. - * - * ODF_arg s must be initialized before with CO_SDO_initTransfer(). - * @ref CO_SDO_OD_function is called if configured. - * - * @param SDO This object. - * @param length Length of data (received from network) to write. - * - * @return 0 on success, otherwise #CO_SDO_abortCode_t. - */ -uint32_t CO_SDO_writeOD(CO_SDO_t *SDO, uint16_t length); - -#ifdef __cplusplus -} -#endif /*__cplusplus*/ - -/** @} */ -#endif diff --git a/stack/CO_SDOmaster.c b/stack/CO_SDOmaster.c deleted file mode 100644 index d0a7036a..00000000 --- a/stack/CO_SDOmaster.c +++ /dev/null @@ -1,1245 +0,0 @@ -/* - * CANopen Service Data Object - client. - * - * @file CO_SDOmaster.c - * @ingroup CO_SDOmaster - * @author Janez Paternoster - * @author Matej Severkar - * @copyright 2004 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include "CO_driver.h" -#include "CO_SDO.h" -#include "CO_SDOmaster.h" -#include "crc16-ccitt.h" - - -/* Client command specifier */ -#define CCS_DOWNLOAD_INITIATE 1 -#define CCS_DOWNLOAD_SEGMENT 0 - -#define CCS_UPLOAD_INITIATE 2 -#define CCS_UPLOAD_SEGMENT 3 - -#define CCS_ABORT 4 - -#define CCS_UPLOAD_BLOCK 5 -#define CCS_DOWNLOAD_BLOCK 6 - -/* Server Command Specifier */ -#define SCS_UPLOAD_INITIATE 2 -#define SCS_UPLOAD_SEGMENT 0 - -#define SCS_DOWNLOAD_INITIATED 3 -#define SCS_DOWNLOAD_SEGMENT 1 - -#define SCS_ABORT 4 - -#define SCS_DOWNLOAD_BLOCK 5 -#define SCS_UPLOAD_BLOCK 6 - - -/* client states */ -#define SDO_STATE_NOTDEFINED 0 -#define SDO_STATE_ABORT 1 - -/* DOWNLOAD EXPEDITED/SEGMENTED */ -#define SDO_STATE_DOWNLOAD_INITIATE 10 -#define SDO_STATE_DOWNLOAD_REQUEST 11 -#define SDO_STATE_DOWNLOAD_RESPONSE 12 - -/* UPLOAD EXPEDITED/SEGMENTED */ -#define SDO_STATE_UPLOAD_INITIATED 20 -#define SDO_STATE_UPLOAD_REQUEST 21 -#define SDO_STATE_UPLOAD_RESPONSE 22 - -/* DOWNLOAD BLOCK */ -#define SDO_STATE_BLOCKDOWNLOAD_INITIATE 100 -#define SDO_STATE_BLOCKDOWNLOAD_INPROGRES 101 -#define SDO_STATE_BLOCKDOWNLOAD_BLOCK_ACK 102 -#define SDO_STATE_BLOCKDOWNLOAD_CRC 103 -#define SDO_STATE_BLOCKDOWNLOAD_CRC_ACK 104 - -/* UPLOAD BLOCK */ -#define SDO_STATE_BLOCKUPLOAD_INITIATE 200 -#define SDO_STATE_BLOCKUPLOAD_INITIATE_ACK 201 -#define SDO_STATE_BLOCKUPLOAD_INPROGRES 202 -#define SDO_STATE_BLOCKUPLOAD_SUB_END 203 -#define SDO_STATE_BLOCKUPLOAD_BLOCK_ACK 204 -#define SDO_STATE_BLOCKUPLOAD_BLOCK_ACK_LAST 205 -#define SDO_STATE_BLOCKUPLOAD_BLOCK_CRC 206 -#define SDO_STATE_BLOCKUPLOAD_BLOCK_END 207 - - -/* - * Read received message from CAN module. - * - * Function will be called (by CAN receive interrupt) every time, when CAN - * message with correct identifier will be received. For more information and - * description of parameters see file CO_driver.h. - */ -static void CO_SDOclient_receive(void *object, const CO_CANrxMsg_t *msg){ - CO_SDOclient_t *SDO_C; - - SDO_C = (CO_SDOclient_t*)object; /* this is the correct pointer type of the first argument */ - - /* verify message length and message overflow (previous message was not processed yet) */ - if((msg->DLC == 8U) && (!IS_CANrxNew(SDO_C->CANrxNew)) && (SDO_C->state != SDO_STATE_NOTDEFINED)){ - if(SDO_C->state != SDO_STATE_BLOCKUPLOAD_INPROGRES) { - /* copy data and set 'new message' flag */ - SDO_C->CANrxData[0] = msg->data[0]; - SDO_C->CANrxData[1] = msg->data[1]; - SDO_C->CANrxData[2] = msg->data[2]; - SDO_C->CANrxData[3] = msg->data[3]; - SDO_C->CANrxData[4] = msg->data[4]; - SDO_C->CANrxData[5] = msg->data[5]; - SDO_C->CANrxData[6] = msg->data[6]; - SDO_C->CANrxData[7] = msg->data[7]; - - SET_CANrxNew(SDO_C->CANrxNew); - } - else { - /* block upload, copy data directly */ - uint8_t seqno; - - SDO_C->CANrxData[0] = msg->data[0]; - seqno = SDO_C->CANrxData[0] & 0x7f; - SDO_C->timeoutTimer = 0; - SDO_C->timeoutTimerBLOCK = 0; - - /* check correct sequence number. */ - if(seqno == (SDO_C->block_seqno + 1)) { - /* block_seqno is correct */ - uint8_t i; - - SDO_C->block_seqno++; - - /* copy data */ - for(i=1; i<8; i++) { - SDO_C->buffer[SDO_C->dataSizeTransfered++] = msg->data[i]; - if(SDO_C->dataSizeTransfered >= SDO_C->bufferSize) { - /* buffer full, break reception */ - SDO_C->state = SDO_STATE_BLOCKUPLOAD_SUB_END; - SET_CANrxNew(SDO_C->CANrxNew); - break; - } - } - - /* break reception if last segment or block sequence is too large */ - if(((SDO_C->CANrxData[0] & 0x80U) == 0x80U) || (SDO_C->block_seqno >= SDO_C->block_blksize)) { - SDO_C->state = SDO_STATE_BLOCKUPLOAD_SUB_END; - SET_CANrxNew(SDO_C->CANrxNew); - } - } - else if((seqno == SDO_C->block_seqno) || (SDO_C->block_seqno == 0U)){ - /* Ignore message, if it is duplicate or if sequence didn't started yet. */ - } - else { - /* seqno is totally wrong, break reception. */ - SDO_C->state = SDO_STATE_BLOCKUPLOAD_SUB_END; - SET_CANrxNew(SDO_C->CANrxNew); - } - } - - /* Optional signal to RTOS, which can resume task, which handles SDO client. */ - if(IS_CANrxNew(SDO_C->CANrxNew) && SDO_C->pFunctSignal != NULL) { - SDO_C->pFunctSignal(); - } - } -} - - -/******************************************************************************/ -CO_ReturnError_t CO_SDOclient_init( - CO_SDOclient_t *SDO_C, - CO_SDO_t *SDO, - CO_SDOclientPar_t *SDOClientPar, - CO_CANmodule_t *CANdevRx, - uint16_t CANdevRxIdx, - CO_CANmodule_t *CANdevTx, - uint16_t CANdevTxIdx) -{ - /* verify arguments */ - if(SDO_C==NULL || SDO==NULL || SDOClientPar==NULL || SDOClientPar->maxSubIndex!=3 || - CANdevRx==NULL || CANdevTx==NULL){ - return CO_ERROR_ILLEGAL_ARGUMENT; - } - - /* Configure object variables */ - SDO_C->state = SDO_STATE_NOTDEFINED; - CLEAR_CANrxNew(SDO_C->CANrxNew); - - SDO_C->pst = 21; /* block transfer */ - SDO_C->block_size_max = 127; /* block transfer */ - - SDO_C->SDO = SDO; - SDO_C->SDOClientPar = SDOClientPar; - - SDO_C->pFunctSignal = NULL; - - SDO_C->CANdevRx = CANdevRx; - SDO_C->CANdevRxIdx = CANdevRxIdx; - SDO_C->CANdevTx = CANdevTx; - SDO_C->CANdevTxIdx = CANdevTxIdx; - - SDO_C->COB_IDClientToServerPrev = 0; - SDO_C->COB_IDServerToClientPrev = 0; - CO_SDOclient_setup(SDO_C, SDO_C->SDOClientPar->COB_IDClientToServer, - SDO_C->SDOClientPar->COB_IDServerToClient, - SDO_C->SDOClientPar->nodeIDOfTheSDOServer); - - return CO_ERROR_NO; -} - - -/******************************************************************************/ -void CO_SDOclient_initCallback( - CO_SDOclient_t *SDOclient, - void (*pFunctSignal)(void)) -{ - if(SDOclient != NULL){ - SDOclient->pFunctSignal = pFunctSignal; - } -} - - -/******************************************************************************/ -CO_SDOclient_return_t CO_SDOclient_setup( - CO_SDOclient_t *SDO_C, - uint32_t COB_IDClientToServer, - uint32_t COB_IDServerToClient, - uint8_t nodeIDOfTheSDOServer) -{ - uint32_t idCtoS, idStoC; - uint8_t idNode; - - /* verify parameters */ - if(SDO_C == NULL || (COB_IDClientToServer&0x7FFFF800L) != 0 || - (COB_IDServerToClient&0x7FFFF800L) != 0 || nodeIDOfTheSDOServer > 127) - { - return CO_SDOcli_wrongArguments; - } - - /* Configure object variables */ - SDO_C->state = SDO_STATE_NOTDEFINED; - CLEAR_CANrxNew(SDO_C->CANrxNew); - - /* setup Object Dictionary variables */ - if((COB_IDClientToServer & 0x80000000L) != 0 || (COB_IDServerToClient & 0x80000000L) != 0 || nodeIDOfTheSDOServer == 0){ - /* SDO is NOT used */ - idCtoS = 0x80000000L; - idStoC = 0x80000000L; - idNode = 0; - } - else{ - if(COB_IDClientToServer == 0 || COB_IDServerToClient == 0){ - idCtoS = 0x600 + nodeIDOfTheSDOServer; - idStoC = 0x580 + nodeIDOfTheSDOServer; - } - else{ - idCtoS = COB_IDClientToServer; - idStoC = COB_IDServerToClient; - } - idNode = nodeIDOfTheSDOServer; - } - - SDO_C->SDOClientPar->COB_IDClientToServer = idCtoS; - SDO_C->SDOClientPar->COB_IDServerToClient = idStoC; - SDO_C->SDOClientPar->nodeIDOfTheSDOServer = idNode; - - /* configure SDO client CAN reception, if differs. */ - if(SDO_C->COB_IDClientToServerPrev != idCtoS || SDO_C->COB_IDServerToClientPrev != idStoC) { - CO_CANrxBufferInit( - SDO_C->CANdevRx, /* CAN device */ - SDO_C->CANdevRxIdx, /* rx buffer index */ - (uint16_t)idStoC, /* CAN identifier */ - 0x7FF, /* mask */ - 0, /* rtr */ - (void*)SDO_C, /* object passed to receive function */ - CO_SDOclient_receive); /* this function will process received message */ - - /* configure SDO client CAN transmission */ - SDO_C->CANtxBuff = CO_CANtxBufferInit( - SDO_C->CANdevTx, /* CAN device */ - SDO_C->CANdevTxIdx, /* index of specific buffer inside CAN module */ - (uint16_t)idCtoS, /* CAN identifier */ - 0, /* rtr */ - 8, /* number of data bytes */ - 0); /* synchronous message flag bit */ - - SDO_C->COB_IDClientToServerPrev = idCtoS; - SDO_C->COB_IDServerToClientPrev = idStoC; - } - - return CO_SDOcli_ok_communicationEnd; -} - - -/******************************************************************************/ -static void CO_SDOclient_abort(CO_SDOclient_t *SDO_C, uint32_t code){ - SDO_C->CANtxBuff->data[0] = 0x80; - SDO_C->CANtxBuff->data[1] = SDO_C->index & 0xFF; - SDO_C->CANtxBuff->data[2] = (SDO_C->index>>8) & 0xFF; - SDO_C->CANtxBuff->data[3] = SDO_C->subIndex; - CO_memcpySwap4(&SDO_C->CANtxBuff->data[4], &code); - CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff); - SDO_C->state = SDO_STATE_NOTDEFINED; - CLEAR_CANrxNew(SDO_C->CANrxNew); -} - - -/******************************************************************************/ -static void CO_SDOTxBufferClear(CO_SDOclient_t *SDO_C) { - uint16_t i; - - for(i=0; i<8; i++) { - SDO_C->CANtxBuff->data[i] = 0; - } - SDO_C->CANtxBuff->bufferFull = 0; -} - - -/******************************************************************************* - * - * DOWNLOAD - * - * - ******************************************************************************/ -CO_SDOclient_return_t CO_SDOclientDownloadInitiate( - CO_SDOclient_t *SDO_C, - uint16_t index, - uint8_t subIndex, - uint8_t *dataTx, - uint32_t dataSize, - uint8_t blockEnable) -{ - /* verify parameters */ - if(SDO_C == NULL || dataTx == 0 || dataSize == 0) { - return CO_SDOcli_wrongArguments; - } - - /* save parameters */ - SDO_C->buffer = dataTx; - SDO_C->bufferSize = dataSize; - - SDO_C->state = SDO_STATE_DOWNLOAD_INITIATE; - - /* prepare CAN tx message */ - CO_SDOTxBufferClear(SDO_C); - - SDO_C->index = index; - SDO_C->subIndex = subIndex; - SDO_C->CANtxBuff->data[1] = index & 0xFF; - SDO_C->CANtxBuff->data[2] = index >> 8; - SDO_C->CANtxBuff->data[3] = subIndex; - - /* if nodeIDOfTheSDOServer == node-ID of this node, then exchange data with this node */ - if(SDO_C->SDOClientPar->nodeIDOfTheSDOServer == SDO_C->SDO->nodeId){ - - /* Optional signal to RTOS. We can immediately continue SDO Client */ - if(SDO_C->pFunctSignal != NULL) { - SDO_C->pFunctSignal(); - } - - return CO_SDOcli_ok_communicationEnd; - } - - if(dataSize <= 4){ - uint16_t i; - /* expedited transfer */ - SDO_C->CANtxBuff->data[0] = 0x23 | ((4-dataSize) << 2); - - /* copy data */ - for(i=dataSize+3; i>=4; i--) SDO_C->CANtxBuff->data[i] = dataTx[i-4]; - } - else if((SDO_C->bufferSize > SDO_C->pst) && blockEnable != 0){ /* BLOCK transfer */ - /* set state of block transfer */ - SDO_C->state = SDO_STATE_BLOCKDOWNLOAD_INITIATE; - - /* init HEAD of packet */ - SDO_C->CANtxBuff->data[0] = CCS_DOWNLOAD_BLOCK<<5; - /* set flag for CRC */ - SDO_C->CANtxBuff->data[0] |= 0x01<<2; - - /* size indicator: */ - SDO_C->CANtxBuff->data[0] |= 0x01<<1; - /* set length of data */ - SDO_C->CANtxBuff->data[4] = (uint8_t) dataSize; - SDO_C->CANtxBuff->data[5] = (uint8_t) (dataSize >> 8); - SDO_C->CANtxBuff->data[6] = (uint8_t) (dataSize >> 16); - SDO_C->CANtxBuff->data[7] = (uint8_t) (dataSize >> 24); - - } - else{ - uint32_t len; - /* segmented transfer */ - SDO_C->CANtxBuff->data[0] = 0x21; - len = dataSize; - CO_memcpySwap4(&SDO_C->CANtxBuff->data[4], &len); - } - - /* empty receive buffer, reset timeout timer and send message */ - CLEAR_CANrxNew(SDO_C->CANrxNew); - SDO_C->timeoutTimer = 0; - CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff); - - return CO_SDOcli_ok_communicationEnd; -} - - -/******************************************************************************/ -CO_SDOclient_return_t CO_SDOclientDownload( - CO_SDOclient_t *SDO_C, - uint16_t timeDifference_ms, - uint16_t SDOtimeoutTime, - uint32_t *pSDOabortCode) -{ - CO_SDOclient_return_t ret = CO_SDOcli_waitingServerResponse; - - /* verify parameters */ - if(SDO_C == NULL) { - return CO_SDOcli_wrongArguments; - } - - /* clear abort code */ - *pSDOabortCode = CO_SDO_AB_NONE; - - /* if nodeIDOfTheSDOServer == node-ID of this node, then exchange data with this node */ - if(SDO_C->SDO && SDO_C->SDOClientPar->nodeIDOfTheSDOServer == SDO_C->SDO->nodeId){ - SDO_C->state = SDO_STATE_NOTDEFINED; - CLEAR_CANrxNew(SDO_C->CANrxNew); - - /* If SDO server is busy return error */ - if(SDO_C->SDO->state != 0){ - return CO_SDOcli_endedWithClientAbort; - } - - /* init ODF_arg */ - *pSDOabortCode = CO_SDO_initTransfer(SDO_C->SDO, SDO_C->index, SDO_C->subIndex); - if((*pSDOabortCode) != CO_SDO_AB_NONE){ - return CO_SDOcli_endedWithServerAbort; - } - - /* set buffer */ - SDO_C->SDO->ODF_arg.data = SDO_C->buffer; - - /* write data to the Object dictionary */ - *pSDOabortCode = CO_SDO_writeOD(SDO_C->SDO, SDO_C->bufferSize); - if((*pSDOabortCode) != CO_SDO_AB_NONE){ - return CO_SDOcli_endedWithServerAbort; - } - - return CO_SDOcli_ok_communicationEnd; - } - - -/* RX data ****************************************************************************************** */ - if(IS_CANrxNew(SDO_C->CANrxNew)){ - uint8_t SCS = SDO_C->CANrxData[0]>>5; /* Client command specifier */ - - /* ABORT */ - if (SDO_C->CANrxData[0] == (SCS_ABORT<<5)){ - SDO_C->state = SDO_STATE_NOTDEFINED; - CO_memcpySwap4(pSDOabortCode , &SDO_C->CANrxData[4]); - CLEAR_CANrxNew(SDO_C->CANrxNew); - return CO_SDOcli_endedWithServerAbort; - } - - switch (SDO_C->state){ - - case SDO_STATE_DOWNLOAD_INITIATE:{ - - if (SCS == SCS_DOWNLOAD_INITIATED){ - if(SDO_C->bufferSize <= 4){ - /* expedited transfer */ - SDO_C->state = SDO_STATE_NOTDEFINED; - CLEAR_CANrxNew(SDO_C->CANrxNew); - return CO_SDOcli_ok_communicationEnd; - } - else{ - /* segmented transfer - prepare the first segment */ - SDO_C->bufferOffset = 0; - SDO_C->toggle =0; - SDO_C->state = SDO_STATE_DOWNLOAD_REQUEST; - } - } - else{ - *pSDOabortCode = CO_SDO_AB_CMD; - SDO_C->state = SDO_STATE_ABORT; - } - break; - } - - case SDO_STATE_DOWNLOAD_RESPONSE:{ - - if (SCS == SCS_DOWNLOAD_SEGMENT){ - /* verify toggle bit */ - if((SDO_C->CANrxData[0]&0x10) != (SDO_C->toggle<<4)){ - *pSDOabortCode = CO_SDO_AB_TOGGLE_BIT; - SDO_C->state = SDO_STATE_ABORT; - break; - } - /* alternate toggle bit */ - if (SDO_C->toggle ==0x00) - SDO_C->toggle =0x01; - else - SDO_C->toggle =0x00; - - /* is end of transfer? */ - if(SDO_C->bufferOffset == SDO_C->bufferSize){ - SDO_C->state = SDO_STATE_NOTDEFINED; - CLEAR_CANrxNew(SDO_C->CANrxNew); - return CO_SDOcli_ok_communicationEnd; - } - SDO_C->state = SDO_STATE_DOWNLOAD_REQUEST; - } - else{ - *pSDOabortCode = CO_SDO_AB_CMD; - SDO_C->state = SDO_STATE_ABORT; - } - break; - } - - case SDO_STATE_BLOCKDOWNLOAD_INITIATE:{ /* waiting on reply on block download initiated */ - if (SCS == SCS_DOWNLOAD_BLOCK){ - uint16_t IndexTmp; - IndexTmp = SDO_C->CANrxData[2]; - IndexTmp = IndexTmp << 8; - IndexTmp |= SDO_C->CANrxData[1]; - - if(IndexTmp != SDO_C->index || SDO_C->CANrxData[3] != SDO_C->subIndex) { - /* wrong index */ - *pSDOabortCode = CO_SDO_AB_PRAM_INCOMPAT; - SDO_C->state = SDO_STATE_ABORT; - break; - } - /* set blksize */ - SDO_C->block_blksize = SDO_C->CANrxData[4]; - - SDO_C->block_seqno = 0; - SDO_C->bufferOffset = 0; - SDO_C->bufferOffsetACK = 0; - SDO_C->state = SDO_STATE_BLOCKDOWNLOAD_INPROGRES; - } - else{ - *pSDOabortCode = CO_SDO_AB_CMD; - SDO_C->state = SDO_STATE_ABORT; - } - break; - } - - case SDO_STATE_BLOCKDOWNLOAD_INPROGRES: - case SDO_STATE_BLOCKDOWNLOAD_BLOCK_ACK:{ /* waiting block ACK */ - if (SCS == SCS_DOWNLOAD_BLOCK){ - /* check server subcommand */ - if((SDO_C->CANrxData[0] & 0x02) == 0){ - /* wrong server sub command */ - *pSDOabortCode = CO_SDO_AB_CMD; - SDO_C->state = SDO_STATE_ABORT; - break; - } - /* check number of segments */ - if(SDO_C->CANrxData[1] != SDO_C->block_blksize){ - /* NOT all segments transferred successfully */ - SDO_C->bufferOffsetACK += SDO_C->CANrxData[1] * 7; - SDO_C->bufferOffset = SDO_C->bufferOffsetACK; - } - else{ - SDO_C->bufferOffsetACK = SDO_C->bufferOffset; - } - /* set size of next block */ - SDO_C->block_blksize = SDO_C->CANrxData[2]; - SDO_C->block_seqno = 0; - - if(SDO_C->bufferOffset >= SDO_C->bufferSize) - SDO_C->state = SDO_STATE_BLOCKDOWNLOAD_CRC; - else - SDO_C->state = SDO_STATE_BLOCKDOWNLOAD_INPROGRES; - } - else{ - *pSDOabortCode = CO_SDO_AB_CMD; - SDO_C->state = SDO_STATE_ABORT; - } - break; - } - - case SDO_STATE_BLOCKDOWNLOAD_CRC_ACK:{ - if (SCS == SCS_DOWNLOAD_BLOCK){ - if((SDO_C->CANrxData[0] & 0x01) == 0){ - /* wrong server sub command */ - *pSDOabortCode = CO_SDO_AB_CMD; - SDO_C->state = SDO_STATE_ABORT; - break; - } - /* SDO block download successfully transferred */ - SDO_C->state = SDO_STATE_NOTDEFINED; - SDO_C->timeoutTimer = 0; - CLEAR_CANrxNew(SDO_C->CANrxNew); - return CO_SDOcli_ok_communicationEnd; - } - else{ - *pSDOabortCode = CO_SDO_AB_CMD; - SDO_C->state = SDO_STATE_ABORT; - break; - } - } - - default:{ - *pSDOabortCode = CO_SDO_AB_CMD; - SDO_C->state = SDO_STATE_ABORT; - break; - } - } - SDO_C->timeoutTimer = 0; - CLEAR_CANrxNew(SDO_C->CANrxNew); - } - -/* TMO *********************************************************************************************** */ - if(SDO_C->timeoutTimer < SDOtimeoutTime){ - SDO_C->timeoutTimer += timeDifference_ms; - } - if(SDO_C->timeoutTimer >= SDOtimeoutTime){ /* communication TMO */ - *pSDOabortCode = CO_SDO_AB_TIMEOUT; - CO_SDOclient_abort(SDO_C, *pSDOabortCode); - return CO_SDOcli_endedWithTimeout; - } - -/* TX data ******************************************************************************************* */ - if(SDO_C->CANtxBuff->bufferFull) { - return CO_SDOcli_transmittBufferFull; - } - - CO_SDOTxBufferClear(SDO_C); - switch (SDO_C->state){ - /* ABORT */ - case SDO_STATE_ABORT:{ - SDO_C->state = SDO_STATE_NOTDEFINED; - CO_SDOclient_abort (SDO_C, *pSDOabortCode); - ret = CO_SDOcli_endedWithClientAbort; - break; - } - /* SEGMENTED */ - case SDO_STATE_DOWNLOAD_REQUEST:{ - uint16_t i, j; - /* calculate length to be sent */ - j = SDO_C->bufferSize - SDO_C->bufferOffset; - if(j > 7) j = 7; - /* fill data bytes */ - for(i=0; iCANtxBuff->data[i+1] = SDO_C->buffer[SDO_C->bufferOffset + i]; - - for(; i<7; i++) - SDO_C->CANtxBuff->data[i+1] = 0; - - SDO_C->bufferOffset += j; - /* SDO command specifier */ - SDO_C->CANtxBuff->data[0] = CCS_DOWNLOAD_SEGMENT | ((SDO_C->toggle)<<4) | ((7-j)<<1); - /* is end of transfer? */ - if(SDO_C->bufferOffset == SDO_C->bufferSize){ - SDO_C->CANtxBuff->data[0] |= 1; - } - /* Send next SDO message */ - CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff); - SDO_C->state = SDO_STATE_DOWNLOAD_RESPONSE; - break; - } - - /* BLOCK */ - case SDO_STATE_BLOCKDOWNLOAD_INPROGRES:{ - SDO_C->block_seqno += 1; - SDO_C->CANtxBuff->data[0] = SDO_C->block_seqno; - - if(SDO_C->block_seqno >= SDO_C->block_blksize){ - SDO_C->state = SDO_STATE_BLOCKDOWNLOAD_BLOCK_ACK; - } - /* set data */ - SDO_C->block_noData = 0; - - uint8_t i; - for(i = 1; i < 8; i++){ - if(SDO_C->bufferOffset < SDO_C->bufferSize){ - SDO_C->CANtxBuff->data[i] = *(SDO_C->buffer + SDO_C->bufferOffset); - } - else{ - SDO_C->CANtxBuff->data[i] = 0; - SDO_C->block_noData += 1; - } - - SDO_C->bufferOffset += 1; - } - - if(SDO_C->bufferOffset >= SDO_C->bufferSize){ - SDO_C->CANtxBuff->data[0] |= 0x80; - SDO_C->block_blksize = SDO_C->block_seqno; - SDO_C->state = SDO_STATE_BLOCKDOWNLOAD_BLOCK_ACK; - } - - /* tx data */ - SDO_C->timeoutTimer = 0; - CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff); - - break; - } - - case SDO_STATE_BLOCKDOWNLOAD_CRC:{ - SDO_C->CANtxBuff->data[0] = (CCS_DOWNLOAD_BLOCK<<5) | (SDO_C->block_noData << 2) | 0x01; - - uint16_t tmp16; - - tmp16 = crc16_ccitt((unsigned char *)SDO_C->buffer, (unsigned int)SDO_C->bufferSize, 0); - - SDO_C->CANtxBuff->data[1] = (uint8_t) tmp16; - SDO_C->CANtxBuff->data[2] = (uint8_t) (tmp16>>8); - - /* set state */ - SDO_C->state = SDO_STATE_BLOCKDOWNLOAD_CRC_ACK; - /* tx data */ - SDO_C->timeoutTimer = 0; - CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff); - - break; - } - - default:{ - break; - } - } - - if(SDO_C->state == SDO_STATE_BLOCKDOWNLOAD_INPROGRES) { - ret = CO_SDOcli_blockDownldInProgress; - } - - return ret; -} - - -/******************************************************************************* - * - * UPLOAD - * - ******************************************************************************/ -CO_SDOclient_return_t CO_SDOclientUploadInitiate( - CO_SDOclient_t *SDO_C, - uint16_t index, - uint8_t subIndex, - uint8_t *dataRx, - uint32_t dataRxSize, - uint8_t blockEnable) -{ - /* verify parameters */ - if(SDO_C == NULL || dataRx == 0 || dataRxSize < 4) { - return CO_SDOcli_wrongArguments; - } - - /* save parameters */ - SDO_C->buffer = dataRx; - SDO_C->bufferSize = dataRxSize; - - /* prepare CAN tx message */ - CO_SDOTxBufferClear(SDO_C); - - SDO_C->index = index; - SDO_C->subIndex = subIndex; - - SDO_C->CANtxBuff->data[1] = index & 0xFF; - SDO_C->CANtxBuff->data[2] = index >> 8; - SDO_C->CANtxBuff->data[3] = subIndex; - - - if(blockEnable == 0){ - SDO_C->state = SDO_STATE_UPLOAD_INITIATED; - SDO_C->CANtxBuff->data[0] = (CCS_UPLOAD_INITIATE<<5); - } - else{ - SDO_C->state = SDO_STATE_BLOCKUPLOAD_INITIATE; - - /* header */ - SDO_C->CANtxBuff->data[0] = (CCS_UPLOAD_BLOCK<<5); - - /* set CRC */ - SDO_C->CANtxBuff->data[0] |= 0x04; - - /* set number of segments in block */ - SDO_C->block_blksize = SDO_C->block_size_max; - if ((SDO_C->block_blksize *7) > SDO_C->bufferSize){ - return CO_SDOcli_wrongArguments; - } - - SDO_C->CANtxBuff->data[4] = SDO_C->block_blksize; - SDO_C->CANtxBuff->data[5] = SDO_C->pst; - - - SDO_C->block_seqno = 0; - } - - /* if nodeIDOfTheSDOServer == node-ID of this node, then exchange data with this node */ - if(SDO_C->SDOClientPar->nodeIDOfTheSDOServer == SDO_C->SDO->nodeId){ - - /* Optional signal to RTOS. We can immediately continue SDO Client */ - if(SDO_C->pFunctSignal != NULL) { - SDO_C->pFunctSignal(); - } - - return CO_SDOcli_ok_communicationEnd; - } - - /* empty receive buffer, reset timeout timer and send message */ - CLEAR_CANrxNew(SDO_C->CANrxNew); - SDO_C->timeoutTimer = 0; - SDO_C->timeoutTimerBLOCK =0; - CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff); - - return CO_SDOcli_ok_communicationEnd; -} - - -/******************************************************************************/ -CO_SDOclient_return_t CO_SDOclientUpload( - CO_SDOclient_t *SDO_C, - uint16_t timeDifference_ms, - uint16_t SDOtimeoutTime, - uint32_t *pDataSize, - uint32_t *pSDOabortCode) -{ - uint16_t indexTmp; - uint32_t tmp32; - CO_SDOclient_return_t ret = CO_SDOcli_waitingServerResponse; - - /* verify parameters */ - if(SDO_C == NULL) { - return CO_SDOcli_wrongArguments; - } - - /* clear abort code */ - *pSDOabortCode = CO_SDO_AB_NONE; - - /* if nodeIDOfTheSDOServer == node-ID of this node, then exchange data with this node */ - if(SDO_C->SDO && SDO_C->SDOClientPar->nodeIDOfTheSDOServer == SDO_C->SDO->nodeId){ - SDO_C->state = SDO_STATE_NOTDEFINED; - CLEAR_CANrxNew(SDO_C->CANrxNew); - - /* If SDO server is busy return error */ - if(SDO_C->SDO->state != 0){ - *pSDOabortCode = CO_SDO_AB_DEVICE_INCOMPAT; - return CO_SDOcli_endedWithClientAbort; - } - - /* init ODF_arg */ - *pSDOabortCode = CO_SDO_initTransfer(SDO_C->SDO, SDO_C->index, SDO_C->subIndex); - if((*pSDOabortCode) != CO_SDO_AB_NONE){ - return CO_SDOcli_endedWithServerAbort; - } - - /* set buffer and length if domain */ - SDO_C->SDO->ODF_arg.data = SDO_C->buffer; - if(SDO_C->SDO->ODF_arg.ODdataStorage == 0) - SDO_C->SDO->ODF_arg.dataLength = SDO_C->bufferSize; - - /* read data from the Object dictionary */ - *pSDOabortCode = CO_SDO_readOD(SDO_C->SDO, SDO_C->bufferSize); - if((*pSDOabortCode) != CO_SDO_AB_NONE){ - return CO_SDOcli_endedWithServerAbort; - } - - /* set data size */ - *pDataSize = SDO_C->SDO->ODF_arg.dataLength; - - /* is SDO buffer too small */ - if(SDO_C->SDO->ODF_arg.lastSegment == 0){ - *pSDOabortCode = CO_SDO_AB_OUT_OF_MEM; /* Out of memory */ - return CO_SDOcli_endedWithServerAbort; - } - - return CO_SDOcli_ok_communicationEnd; - } - - -/* RX data ******************************************************************************** */ - if(IS_CANrxNew(SDO_C->CANrxNew)){ - uint8_t SCS = SDO_C->CANrxData[0]>>5; /* Client command specifier */ - - /* ABORT */ - if (SDO_C->CANrxData[0] == (SCS_ABORT<<5)){ - SDO_C->state = SDO_STATE_NOTDEFINED; - CLEAR_CANrxNew(SDO_C->CANrxNew); - CO_memcpySwap4(pSDOabortCode , &SDO_C->CANrxData[4]); - return CO_SDOcli_endedWithServerAbort; - } - switch (SDO_C->state){ - case SDO_STATE_UPLOAD_INITIATED:{ - - - - if (SCS == SCS_UPLOAD_INITIATE){ - if(SDO_C->CANrxData[0] & 0x02){ - uint8_t size; - /* Expedited transfer */ - if(SDO_C->CANrxData[0] & 0x01)/* is size indicated */ - size = 4 - ((SDO_C->CANrxData[0]>>2)&0x03); /* size */ - else - size = 4; - - *pDataSize = size; - - /* copy data */ - while(size--) SDO_C->buffer[size] = SDO_C->CANrxData[4+size]; - SDO_C->state = SDO_STATE_NOTDEFINED; - CLEAR_CANrxNew(SDO_C->CANrxNew); - - return CO_SDOcli_ok_communicationEnd; - } - else{ - /* segmented transfer - prepare first segment */ - SDO_C->bufferOffset = 0; - SDO_C->state = SDO_STATE_UPLOAD_REQUEST; - - SDO_C->toggle =0; - /* continue with segmented upload */ - } - } - else{ - *pSDOabortCode = CO_SDO_AB_CMD; - SDO_C->state = SDO_STATE_ABORT; - } - break; - } - - case SDO_STATE_UPLOAD_RESPONSE:{ - if (SCS == SCS_UPLOAD_SEGMENT){ - uint16_t size, i; - /* verify toggle bit */ - if((SDO_C->CANrxData[0] &0x10) != (~SDO_C->toggle &0x10)){ - *pSDOabortCode = CO_SDO_AB_TOGGLE_BIT; - SDO_C->state = SDO_STATE_ABORT; - break; - } - /* get size */ - size = 7 - ((SDO_C->CANrxData[0]>>1)&0x07); - /* verify length */ - if((SDO_C->bufferOffset + size) > SDO_C->bufferSize){ - *pSDOabortCode = CO_SDO_AB_OUT_OF_MEM; /* Out of memory */ - SDO_C->state = SDO_STATE_ABORT; - break; - } - /* copy data to buffer */ - for(i=0; ibuffer[SDO_C->bufferOffset + i] = SDO_C->CANrxData[1 + i]; - SDO_C->bufferOffset += size; - /* If no more segments to be uploaded, finish communication */ - if(SDO_C->CANrxData[0] & 0x01){ - *pDataSize = SDO_C->bufferOffset; - SDO_C->state = SDO_STATE_NOTDEFINED; - CLEAR_CANrxNew(SDO_C->CANrxNew); - return CO_SDOcli_ok_communicationEnd; - } - /* set state */ - SDO_C->state = SDO_STATE_UPLOAD_REQUEST; - break; - } - else{ - *pSDOabortCode = CO_SDO_AB_CMD; - SDO_C->state = SDO_STATE_ABORT; - } - break; - } - - case SDO_STATE_BLOCKUPLOAD_INITIATE:{ - if (SCS == SCS_UPLOAD_BLOCK){ /* block upload initiate response */ - - SDO_C->state = SDO_STATE_BLOCKUPLOAD_INITIATE_ACK; - - /* SCR support */ - if((SDO_C->CANrxData[0] & 0x04) != 0) - SDO_C->crcEnabled = 1; /* CRC suported */ - else - SDO_C->crcEnabled = 0; /* CRC not suported */ - - /* chech Index ans subnindex */ - indexTmp = SDO_C->CANrxData[2]; - indexTmp = indexTmp << 8; - indexTmp |= SDO_C->CANrxData[1]; - - if(indexTmp != SDO_C->index || SDO_C->CANrxData[3] != SDO_C->subIndex){ - *pSDOabortCode = CO_SDO_AB_PRAM_INCOMPAT; - SDO_C->state = SDO_STATE_ABORT; - } - - /* set length */ - if(SDO_C->CANrxData[0]&0x02){ - uint32_t len; - CO_memcpySwap4(&len, &SDO_C->CANrxData[4]); - SDO_C->dataSize = len; - } - else{ - SDO_C->dataSize = 0; - } - - /* check available buffer size */ - if (SDO_C->dataSize > SDO_C->bufferSize){ - *pSDOabortCode = CO_SDO_AB_OUT_OF_MEM; - SDO_C->state = SDO_STATE_ABORT; - } - - SDO_C->dataSizeTransfered =0; - } - else if (SCS == SCS_UPLOAD_INITIATE){ /* switch to regular segmented transfer */ - if(SDO_C->CANrxData[0] & 0x02){ - uint8_t size; - /* Expedited transfer */ - if(SDO_C->CANrxData[0] & 0x01)/* is size indicated */ - size = 4 - ((SDO_C->CANrxData[0]>>2)&0x03); /* size */ - else - size = 4; - - *pDataSize = size; - - /* copy data */ - while(size--) SDO_C->buffer[size] = SDO_C->CANrxData[4+size]; - SDO_C->state = SDO_STATE_NOTDEFINED; - CLEAR_CANrxNew(SDO_C->CANrxNew); - - return CO_SDOcli_ok_communicationEnd; - } - else{ - /* segmented transfer - prepare first segment */ - SDO_C->bufferOffset = 0; - SDO_C->state = SDO_STATE_UPLOAD_REQUEST; - - SDO_C->toggle =0; - /* continue with segmented upload */ - } - } - else{ /* unknown SCS */ - *pSDOabortCode = CO_SDO_AB_CMD; - SDO_C->state = SDO_STATE_ABORT; - } - break; - } - - case SDO_STATE_BLOCKUPLOAD_INPROGRES:{ - /* data are copied directly in receive function */ - break; - } - - case SDO_STATE_BLOCKUPLOAD_SUB_END:{ - /* data was copied by receive function, sub-block is finished */ - /* Is last segment? */ - if(SDO_C->CANrxData[0] & 0x80) { - /* Is data size indicated and wrong? */ - if((SDO_C->dataSize != 0) && (SDO_C->dataSize > SDO_C->dataSizeTransfered)) { - *pSDOabortCode = CO_SDO_AB_TYPE_MISMATCH; - SDO_C->state = SDO_STATE_ABORT; - } - else { - SDO_C->state = SDO_STATE_BLOCKUPLOAD_BLOCK_ACK_LAST; - } - } - else { - /* Is SDO buffer overflow? */ - if(SDO_C->dataSizeTransfered >= SDO_C->bufferSize) { - *pSDOabortCode = CO_SDO_AB_OUT_OF_MEM; - SDO_C->state = SDO_STATE_ABORT; - } - else { - SDO_C->state = SDO_STATE_BLOCKUPLOAD_BLOCK_ACK; - } - } - break; - } - - case SDO_STATE_BLOCKUPLOAD_BLOCK_CRC:{ - if (SCS == SCS_UPLOAD_BLOCK){ - tmp32 = ((SDO_C->CANrxData[0]>>2) & 0x07); - SDO_C->dataSizeTransfered -= tmp32; - - SDO_C->state = SDO_STATE_BLOCKUPLOAD_BLOCK_END; - if (SDO_C->crcEnabled){ - uint16_t tmp16; - CO_memcpySwap2(&tmp16, &SDO_C->CANrxData[1]); - - if (tmp16 != crc16_ccitt((unsigned char *)SDO_C->buffer, (unsigned int)SDO_C->dataSizeTransfered, 0)){ - *pSDOabortCode = CO_SDO_AB_CRC; - SDO_C->state = SDO_STATE_ABORT; - } - else{ - SDO_C->state = SDO_STATE_BLOCKUPLOAD_BLOCK_END; - } - } - else{ - SDO_C->state = SDO_STATE_BLOCKUPLOAD_BLOCK_END; - } - - } - else{ - *pSDOabortCode = CO_SDO_AB_GENERAL; - SDO_C->state = SDO_STATE_ABORT; - } - break; - } - - default:{ - *pSDOabortCode = CO_SDO_AB_CMD; - SDO_C->state = SDO_STATE_ABORT; - break; - } - } - SDO_C->timeoutTimer = 0; - CLEAR_CANrxNew(SDO_C->CANrxNew); - } - -/* TMO *************************************************************************************************** */ - if(SDO_C->timeoutTimer < SDOtimeoutTime){ - SDO_C->timeoutTimer += timeDifference_ms; - if (SDO_C->state == SDO_STATE_BLOCKUPLOAD_INPROGRES) - SDO_C->timeoutTimerBLOCK += timeDifference_ms; - } - if(SDO_C->timeoutTimer >= SDOtimeoutTime){ /* communication TMO */ - *pSDOabortCode = CO_SDO_AB_TIMEOUT; - CO_SDOclient_abort(SDO_C, *pSDOabortCode); - return CO_SDOcli_endedWithTimeout; - } - if(SDO_C->timeoutTimerBLOCK >= (SDOtimeoutTime/2)){ /* block TMO */ - SDO_C->state = SDO_STATE_BLOCKUPLOAD_BLOCK_ACK; - } - - -/* TX data ******************************************************************************** */ - if(SDO_C->CANtxBuff->bufferFull) { - return CO_SDOcli_transmittBufferFull; - } - - CO_SDOTxBufferClear(SDO_C); - switch (SDO_C->state){ - case SDO_STATE_ABORT:{ - SDO_C->state = SDO_STATE_NOTDEFINED; - CO_SDOclient_abort (SDO_C, *pSDOabortCode); - ret = CO_SDOcli_endedWithClientAbort; - break; - } - - /* SEGMENTED UPLOAD */ - case SDO_STATE_UPLOAD_REQUEST:{ - SDO_C->CANtxBuff->data[0] = (CCS_UPLOAD_SEGMENT<<5) | (SDO_C->toggle & 0x10); - CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff); - - SDO_C->state = SDO_STATE_UPLOAD_RESPONSE; - SDO_C->toggle = ~SDO_C->toggle; - - break; - } - /* BLOCK */ - case SDO_STATE_BLOCKUPLOAD_INITIATE_ACK:{ - SDO_C->timeoutTimerBLOCK = 0; - SDO_C->block_seqno = 0; - SDO_C->state = SDO_STATE_BLOCKUPLOAD_INPROGRES; - - /* header */ - SDO_C->CANtxBuff->data[0] = (CCS_UPLOAD_BLOCK<<5) | 0x03; - CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff); - - break; - } - - case SDO_STATE_BLOCKUPLOAD_BLOCK_ACK_LAST:{ - /* header */ - SDO_C->CANtxBuff->data[0] = (CCS_UPLOAD_BLOCK<<5) | 0x02; - SDO_C->CANtxBuff->data[1] = SDO_C->block_seqno; - - SDO_C->block_seqno = 0; - SDO_C->timeoutTimerBLOCK = 0; - - SDO_C->state = SDO_STATE_BLOCKUPLOAD_BLOCK_CRC; - - SDO_C->CANtxBuff->data[2] = SDO_C->block_blksize; - CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff); - - break; - } - - case SDO_STATE_BLOCKUPLOAD_BLOCK_ACK:{ - /* header */ - SDO_C->CANtxBuff->data[0] = (CCS_UPLOAD_BLOCK<<5) | 0x02; - SDO_C->CANtxBuff->data[1] = SDO_C->block_seqno; - - /* set next block size */ - if (SDO_C->dataSize != 0){ - if(SDO_C->dataSizeTransfered >= SDO_C->dataSize){ - SDO_C->block_blksize = 0; - SDO_C->state = SDO_STATE_BLOCKUPLOAD_BLOCK_CRC; - } - else{ - tmp32 = ((SDO_C->dataSize - SDO_C->dataSizeTransfered) / 7); - if(tmp32 >= SDO_C->block_size_max){ - SDO_C->block_blksize = SDO_C->block_size_max; - } - else{ - if((SDO_C->dataSize - SDO_C->dataSizeTransfered) % 7 == 0) - SDO_C->block_blksize = tmp32; - else - SDO_C->block_blksize = tmp32 + 1; - } - SDO_C->block_seqno = 0; - SDO_C->timeoutTimerBLOCK = 0; - SDO_C->state = SDO_STATE_BLOCKUPLOAD_INPROGRES; - } - } - else{ - SDO_C->block_seqno = 0; - SDO_C->timeoutTimerBLOCK = 0; - - SDO_C->state = SDO_STATE_BLOCKUPLOAD_INPROGRES; - } - SDO_C->CANtxBuff->data[2] = SDO_C->block_blksize; - CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff); - - break; - } - - case SDO_STATE_BLOCKUPLOAD_BLOCK_END:{ - SDO_C->CANtxBuff->data[0] = (CCS_UPLOAD_BLOCK<<5) | 0x01; - - CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff); - - *pDataSize = SDO_C->dataSizeTransfered; - - SDO_C->state = SDO_STATE_NOTDEFINED; - - ret = CO_SDOcli_ok_communicationEnd; - break; - } - - default: - break; - } - - if(SDO_C->state == SDO_STATE_BLOCKUPLOAD_INPROGRES) { - ret = CO_SDOcli_blockUploadInProgress; - } - - return ret; -} - - -/******************************************************************************/ -void CO_SDOclientClose(CO_SDOclient_t *SDO_C){ - if(SDO_C != NULL) { - SDO_C->state = SDO_STATE_NOTDEFINED; - } -} diff --git a/stack/CO_SDOmaster.h b/stack/CO_SDOmaster.h deleted file mode 100644 index bf592144..00000000 --- a/stack/CO_SDOmaster.h +++ /dev/null @@ -1,351 +0,0 @@ -/** - * CANopen Service Data Object - client protocol. - * - * @file CO_SDOmaster.h - * @ingroup CO_SDOmaster - * @author Janez Paternoster - * @author Matej Severkar - * @copyright 2004 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#ifndef CO_SDO_CLIENT_H -#define CO_SDO_CLIENT_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @defgroup CO_SDOmaster SDO client - * @ingroup CO_CANopen - * @{ - * - * CANopen Service Data Object - client protocol. - * - * @see @ref CO_SDO - */ - - -/** - * Return values of SDO client functions. - */ -typedef enum{ - /** Transmit buffer is full. Waiting */ - CO_SDOcli_transmittBufferFull = 4, - /** Block download is in progress. Sending train of messages */ - CO_SDOcli_blockDownldInProgress = 3, - /** Block upload in progress. Receiving train of messages */ - CO_SDOcli_blockUploadInProgress = 2, - /** Waiting server response */ - CO_SDOcli_waitingServerResponse = 1, - /** Success, end of communication */ - CO_SDOcli_ok_communicationEnd = 0, - /** Error in arguments */ - CO_SDOcli_wrongArguments = -2, - /** Communication ended with client abort */ - CO_SDOcli_endedWithClientAbort = -9, - /** Communication ended with server abort */ - CO_SDOcli_endedWithServerAbort = -10, - /** Communication ended with timeout */ - CO_SDOcli_endedWithTimeout = -11 -}CO_SDOclient_return_t; - - -/** - * SDO Client Parameter. The same as record from Object dictionary (index 0x1280+). - */ -typedef struct{ - /** Equal to 3 */ - uint8_t maxSubIndex; - /** Communication object identifier for client transmission. Meaning of the specific bits: - - Bit 0...10: 11-bit CAN identifier. - - Bit 11..30: reserved, set to 0. - - Bit 31: if 1, SDO client object is not used. */ - uint32_t COB_IDClientToServer; - /** Communication object identifier for message received from server. Meaning of the specific bits: - - Bit 0...10: 11-bit CAN identifier. - - Bit 11..30: reserved, set to 0. - - Bit 31: if 1, SDO client object is not used. */ - uint32_t COB_IDServerToClient; - /** Node-ID of the SDO server */ - uint8_t nodeIDOfTheSDOServer; -}CO_SDOclientPar_t; - - -/** - * SDO client object - */ -typedef struct{ - /** From CO_SDOclient_init() */ - CO_SDOclientPar_t *SDOClientPar; - /** From CO_SDOclient_init() */ - CO_SDO_t *SDO; - /** Internal state of the SDO client */ - uint8_t state; - /** Pointer to data buffer supplied by user */ - uint8_t *buffer; - /** By download application indicates data size in buffer. - By upload application indicates buffer size */ - uint32_t bufferSize; - /** Offset in buffer of next data segment being read/written */ - uint32_t bufferOffset; - /** Acknowledgement */ - uint32_t bufferOffsetACK; - /** data length to be uploaded in block transfer */ - uint32_t dataSize; - /** Data length transferred in block transfer */ - uint32_t dataSizeTransfered; - /** Timeout timer for SDO communication */ - uint16_t timeoutTimer; - /** Timeout timer for SDO block transfer */ - uint16_t timeoutTimerBLOCK; - /** Index of current object in Object Dictionary */ - uint16_t index; - /** Subindex of current object in Object Dictionary */ - uint8_t subIndex; - /** From CO_SDOclient_init() */ - CO_CANmodule_t *CANdevRx; - /** From CO_SDOclient_init() */ - uint16_t CANdevRxIdx; - /** Indicates, if new SDO message received from CAN bus. - It is not cleared, until received message is completely processed. */ - volatile void *CANrxNew; - /** 8 data bytes of the received message */ - uint8_t CANrxData[8]; - /** From CO_SDOclient_initCallback() or NULL */ - void (*pFunctSignal)(void); - /** From CO_SDOclient_init() */ - CO_CANmodule_t *CANdevTx; - /** CAN transmit buffer inside CANdevTx for CAN tx message */ - CO_CANtx_t *CANtxBuff; - /** From CO_SDOclient_init() */ - uint16_t CANdevTxIdx; - /** Toggle bit toggled with each subsequent in segmented transfer */ - uint8_t toggle; - /** Server threshold for switch back to segmented transfer, if data size is small. - Set in CO_SDOclient_init(). Can be changed by application. 0 Disables switching. */ - uint8_t pst; - /** Maximum number of segments in one block. Set in CO_SDOclient_init(). Can - be changed by application to 2 .. 127. */ - uint8_t block_size_max; - /** Last sector number */ - uint8_t block_seqno; - /** Block size in current transfer */ - uint8_t block_blksize; - /** Number of bytes in last segment that do not contain data */ - uint8_t block_noData; - /** Server CRC support in block transfer */ - uint8_t crcEnabled; - /** Previous value of the COB_IDClientToServer */ - uint32_t COB_IDClientToServerPrev; - /** Previous value of the COB_IDServerToClient */ - uint32_t COB_IDServerToClientPrev; - -}CO_SDOclient_t; - - -/** - * Initialize SDO client object. - * - * Function must be called in the communication reset section. - * - * @param SDO_C This object will be initialized. - * @param SDO SDO server object. It is used in case, if client is accessing - * object dictionary from its own device. If NULL, it will be ignored. - * @param SDOClientPar Pointer to _SDO Client Parameter_ record from Object - * dictionary (index 0x1280+). Will be written. - * @param CANdevRx CAN device for SDO client reception. - * @param CANdevRxIdx Index of receive buffer in the above CAN device. - * @param CANdevTx CAN device for SDO client transmission. - * @param CANdevTxIdx Index of transmit buffer in the above CAN device. - * - * @return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT. - */ -CO_ReturnError_t CO_SDOclient_init( - CO_SDOclient_t *SDO_C, - CO_SDO_t *SDO, - CO_SDOclientPar_t *SDOClientPar, - CO_CANmodule_t *CANdevRx, - uint16_t CANdevRxIdx, - CO_CANmodule_t *CANdevTx, - uint16_t CANdevTxIdx); - - -/** - * Initialize SDOclientRx callback function. - * - * Function initializes optional callback function, which is called after new - * message is received from the CAN bus. Function may wake up external task, - * which processes mainline CANopen functions. - * - * @param SDOclient This object. - * @param pFunctSignal Pointer to the callback function. Not called if NULL. - */ -void CO_SDOclient_initCallback( - CO_SDOclient_t *SDOclient, - void (*pFunctSignal)(void)); - - -/** - * Setup SDO client object. - * - * Function must be called before new SDO communication. If previous SDO - * communication was with the same node, function does not need to be called. - * - * @remark If configuring SDO client from network is required, this function - * should be set as callback for the corresponding SDO client parameter OD - * entry. - * - * @param SDO_C This object. - * @param COB_IDClientToServer See CO_SDOclientPar_t. If zero, then - * nodeIDOfTheSDOServer is used with default COB-ID. - * @param COB_IDServerToClient See CO_SDOclientPar_t. If zero, then - * nodeIDOfTheSDOServer is used with default COB-ID. - * @param nodeIDOfTheSDOServer Node-ID of the SDO server. If zero, SDO client - * object is not used. If it is the same as node-ID of this node, then data will - * be exchanged with this node (without CAN communication). - * - * @return #CO_SDOclient_return_t - */ -CO_SDOclient_return_t CO_SDOclient_setup( - CO_SDOclient_t *SDO_C, - uint32_t COB_IDClientToServer, - uint32_t COB_IDServerToClient, - uint8_t nodeIDOfTheSDOServer); - - -/** - * Initiate SDO download communication. - * - * Function initiates SDO download communication with server specified in - * CO_SDOclient_init() function. Data will be written to remote node. - * Function is non-blocking. - * - * @param SDO_C This object. - * @param index Index of object in object dictionary in remote node. - * @param subIndex Subindex of object in object dictionary in remote node. - * @param dataTx Pointer to data to be written. Data must be valid until end - * of communication. Note that data are aligned in little-endian - * format, because CANopen itself uses little-endian. Take care, - * when using processors with big-endian. - * @param dataSize Size of data in dataTx. - * @param blockEnable Try to initiate block transfer. - * - * @return #CO_SDOclient_return_t - */ -CO_SDOclient_return_t CO_SDOclientDownloadInitiate( - CO_SDOclient_t *SDO_C, - uint16_t index, - uint8_t subIndex, - uint8_t *dataTx, - uint32_t dataSize, - uint8_t blockEnable); - - -/** - * Process SDO download communication. - * - * Function must be called cyclically until it returns <=0. It Proceeds SDO - * download communication initiated with CO_SDOclientDownloadInitiate(). - * Function is non-blocking. - * - * @param SDO_C This object. - * @param timeDifference_ms Time difference from previous function call in [milliseconds]. - * @param SDOtimeoutTime Timeout time for SDO communication in milliseconds. - * @param pSDOabortCode Pointer to external variable written by this function - * in case of error in communication. - * - * @return #CO_SDOclient_return_t - */ -CO_SDOclient_return_t CO_SDOclientDownload( - CO_SDOclient_t *SDO_C, - uint16_t timeDifference_ms, - uint16_t SDOtimeoutTime, - uint32_t *pSDOabortCode); - - -/** - * Initiate SDO upload communication. - * - * Function initiates SDO upload communication with server specified in - * CO_SDOclient_init() function. Data will be read from remote node. - * Function is non-blocking. - * - * @param SDO_C This object. - * @param index Index of object in object dictionary in remote node. - * @param subIndex Subindex of object in object dictionary in remote node. - * @param dataRx Pointer to data buffer, into which received data will be written. - * Buffer must be valid until end of communication. Note that data are aligned - * in little-endian format, because CANopen itself uses - * little-endian. Take care, when using processors with big-endian. - * @param dataRxSize Size of dataRx. - * @param blockEnable Try to initiate block transfer. - * - * @return #CO_SDOclient_return_t - */ -CO_SDOclient_return_t CO_SDOclientUploadInitiate( - CO_SDOclient_t *SDO_C, - uint16_t index, - uint8_t subIndex, - uint8_t *dataRx, - uint32_t dataRxSize, - uint8_t blockEnable); - - -/** - * Process SDO upload communication. - * - * Function must be called cyclically until it returns <=0. It Proceeds SDO - * upload communication initiated with CO_SDOclientUploadInitiate(). - * Function is non-blocking. - * - * @param SDO_C This object. - * @param timeDifference_ms Time difference from previous function call in [milliseconds]. - * @param SDOtimeoutTime Timeout time for SDO communication in milliseconds. - * @param pDataSize pointer to external variable, where size of received - * data will be written. - * @param pSDOabortCode Pointer to external variable written by this function - * in case of error in communication. - * - * @return #CO_SDOclient_return_t - */ -CO_SDOclient_return_t CO_SDOclientUpload( - CO_SDOclient_t *SDO_C, - uint16_t timeDifference_ms, - uint16_t SDOtimeoutTime, - uint32_t *pDataSize, - uint32_t *pSDOabortCode); - - -/** - * Close SDO communication temporary. - * - * Function must be called after finish of each SDO client communication cycle. - * It disables reception of SDO client CAN messages. It is necessary, because - * CO_SDOclient_receive function may otherwise write into undefined SDO buffer. - */ -void CO_SDOclientClose(CO_SDOclient_t *SDO_C); - -#ifdef __cplusplus -} -#endif /*__cplusplus*/ - -/** @} */ -#endif diff --git a/stack/CO_SYNC.c b/stack/CO_SYNC.c deleted file mode 100644 index 557ac110..00000000 --- a/stack/CO_SYNC.c +++ /dev/null @@ -1,361 +0,0 @@ -/* - * CANopen SYNC object. - * - * @file CO_SYNC.c - * @ingroup CO_SYNC - * @author Janez Paternoster - * @copyright 2004 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include "CO_driver.h" -#include "CO_SDO.h" -#include "CO_Emergency.h" -#include "CO_NMT_Heartbeat.h" -#include "CO_SYNC.h" - -/* - * Read received message from CAN module. - * - * Function will be called (by CAN receive interrupt) every time, when CAN - * message with correct identifier will be received. For more information and - * description of parameters see file CO_driver.h. - */ -static void CO_SYNC_receive(void *object, const CO_CANrxMsg_t *msg){ - CO_SYNC_t *SYNC; - uint8_t operState; - - SYNC = (CO_SYNC_t*)object; /* this is the correct pointer type of the first argument */ - operState = *SYNC->operatingState; - - if((operState == CO_NMT_OPERATIONAL) || (operState == CO_NMT_PRE_OPERATIONAL)){ - if(SYNC->counterOverflowValue == 0){ - if(msg->DLC == 0U){ - SET_CANrxNew(SYNC->CANrxNew); - } - else{ - SYNC->receiveError = (uint16_t)msg->DLC | 0x0100U; - } - } - else{ - if(msg->DLC == 1U){ - SYNC->counter = msg->data[0]; - SET_CANrxNew(SYNC->CANrxNew); - } - else{ - SYNC->receiveError = (uint16_t)msg->DLC | 0x0200U; - } - } - if(IS_CANrxNew(SYNC->CANrxNew)) { - SYNC->CANrxToggle = SYNC->CANrxToggle ? false : true; - } - } -} - - -/* - * Function for accessing _COB ID SYNC Message_ (index 0x1005) from SDO server. - * - * For more information see file CO_SDO.h. - */ -static CO_SDO_abortCode_t CO_ODF_1005(CO_ODF_arg_t *ODF_arg){ - CO_SYNC_t *SYNC; - uint32_t value; - CO_SDO_abortCode_t ret = CO_SDO_AB_NONE; - - SYNC = (CO_SYNC_t*) ODF_arg->object; - value = CO_getUint32(ODF_arg->data); - - if(!ODF_arg->reading){ - uint8_t configureSyncProducer = 0; - - /* only 11-bit CAN identifier is supported */ - if(value & 0x20000000UL){ - ret = CO_SDO_AB_INVALID_VALUE; - } - else{ - /* is 'generate Sync messge' bit set? */ - if(value & 0x40000000UL){ - /* if bit was set before, value can not be changed */ - if(SYNC->isProducer){ - ret = CO_SDO_AB_DATA_DEV_STATE; - } - else{ - configureSyncProducer = 1; - } - } - } - - /* configure sync producer and consumer */ - if(ret == CO_SDO_AB_NONE){ - SYNC->COB_ID = (uint16_t)(value & 0x7FFU); - - if(configureSyncProducer){ - uint8_t len = 0U; - if(SYNC->counterOverflowValue != 0U){ - len = 1U; - SYNC->counter = 0U; - SYNC->timer = 0U; - } - SYNC->CANtxBuff = CO_CANtxBufferInit( - SYNC->CANdevTx, /* CAN device */ - SYNC->CANdevTxIdx, /* index of specific buffer inside CAN module */ - SYNC->COB_ID, /* CAN identifier */ - 0, /* rtr */ - len, /* number of data bytes */ - 0); /* synchronous message flag bit */ - SYNC->isProducer = true; - } - else{ - SYNC->isProducer = false; - } - - CO_CANrxBufferInit( - SYNC->CANdevRx, /* CAN device */ - SYNC->CANdevRxIdx, /* rx buffer index */ - SYNC->COB_ID, /* CAN identifier */ - 0x7FF, /* mask */ - 0, /* rtr */ - (void*)SYNC, /* object passed to receive function */ - CO_SYNC_receive); /* this function will process received message */ - } - } - - return ret; -} - - -/* - * Function for accessing _Communication cycle period_ (index 0x1006) from SDO server. - * - * For more information see file CO_SDO.h. - */ -static CO_SDO_abortCode_t CO_ODF_1006(CO_ODF_arg_t *ODF_arg){ - CO_SYNC_t *SYNC; - uint32_t value; - CO_SDO_abortCode_t ret = CO_SDO_AB_NONE; - - SYNC = (CO_SYNC_t*) ODF_arg->object; - value = CO_getUint32(ODF_arg->data); - - if(!ODF_arg->reading){ - /* period transition from 0 to something */ - if((SYNC->periodTime == 0) && (value != 0)){ - SYNC->counter = 0; - } - - SYNC->periodTime = value; - SYNC->periodTimeoutTime = (value / 2U) * 3U; - /* overflow? */ - if(SYNC->periodTimeoutTime < value){ - SYNC->periodTimeoutTime = 0xFFFFFFFFUL; - } - - SYNC->timer = 0; - } - - return ret; -} - - -/** - * Function for accessing _Synchronous counter overflow value_ (index 0x1019) from SDO server. - * - * For more information see file CO_SDO.h. - */ -static CO_SDO_abortCode_t CO_ODF_1019(CO_ODF_arg_t *ODF_arg){ - CO_SYNC_t *SYNC; - uint8_t value; - CO_SDO_abortCode_t ret = CO_SDO_AB_NONE; - - SYNC = (CO_SYNC_t*) ODF_arg->object; - value = ODF_arg->data[0]; - - if(!ODF_arg->reading){ - uint8_t len = 0U; - - if(SYNC->periodTime){ - ret = CO_SDO_AB_DATA_DEV_STATE; - } - else if((value == 1) || (value > 240)){ - ret = CO_SDO_AB_INVALID_VALUE; - } - else{ - SYNC->counterOverflowValue = value; - if(value != 0){ - len = 1U; - } - - SYNC->CANtxBuff = CO_CANtxBufferInit( - SYNC->CANdevTx, /* CAN device */ - SYNC->CANdevTxIdx, /* index of specific buffer inside CAN module */ - SYNC->COB_ID, /* CAN identifier */ - 0, /* rtr */ - len, /* number of data bytes */ - 0); /* synchronous message flag bit */ - } - } - - return ret; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_SYNC_init( - CO_SYNC_t *SYNC, - CO_EM_t *em, - CO_SDO_t *SDO, - uint8_t *operatingState, - uint32_t COB_ID_SYNCMessage, - uint32_t communicationCyclePeriod, - uint8_t synchronousCounterOverflowValue, - CO_CANmodule_t *CANdevRx, - uint16_t CANdevRxIdx, - CO_CANmodule_t *CANdevTx, - uint16_t CANdevTxIdx) -{ - uint8_t len = 0; - - /* verify arguments */ - if(SYNC==NULL || em==NULL || SDO==NULL || operatingState==NULL || - CANdevRx==NULL || CANdevTx==NULL){ - return CO_ERROR_ILLEGAL_ARGUMENT; - } - - /* Configure object variables */ - SYNC->isProducer = (COB_ID_SYNCMessage&0x40000000L) ? true : false; - SYNC->COB_ID = COB_ID_SYNCMessage&0x7FF; - - SYNC->periodTime = communicationCyclePeriod; - SYNC->periodTimeoutTime = communicationCyclePeriod / 2 * 3; - /* overflow? */ - if(SYNC->periodTimeoutTime < communicationCyclePeriod) SYNC->periodTimeoutTime = 0xFFFFFFFFL; - - SYNC->counterOverflowValue = synchronousCounterOverflowValue; - if(synchronousCounterOverflowValue) len = 1; - - SYNC->curentSyncTimeIsInsideWindow = true; - - CLEAR_CANrxNew(SYNC->CANrxNew); - SYNC->CANrxToggle = false; - SYNC->timer = 0; - SYNC->counter = 0; - SYNC->receiveError = 0U; - - SYNC->em = em; - SYNC->operatingState = operatingState; - - SYNC->CANdevRx = CANdevRx; - SYNC->CANdevRxIdx = CANdevRxIdx; - - /* Configure Object dictionary entry at index 0x1005, 0x1006 and 0x1019 */ - CO_OD_configure(SDO, OD_H1005_COBID_SYNC, CO_ODF_1005, (void*)SYNC, 0, 0); - CO_OD_configure(SDO, OD_H1006_COMM_CYCL_PERIOD, CO_ODF_1006, (void*)SYNC, 0, 0); - CO_OD_configure(SDO, OD_H1019_SYNC_CNT_OVERFLOW, CO_ODF_1019, (void*)SYNC, 0, 0); - - /* configure SYNC CAN reception */ - CO_CANrxBufferInit( - CANdevRx, /* CAN device */ - CANdevRxIdx, /* rx buffer index */ - SYNC->COB_ID, /* CAN identifier */ - 0x7FF, /* mask */ - 0, /* rtr */ - (void*)SYNC, /* object passed to receive function */ - CO_SYNC_receive); /* this function will process received message */ - - /* configure SYNC CAN transmission */ - SYNC->CANdevTx = CANdevTx; - SYNC->CANdevTxIdx = CANdevTxIdx; - SYNC->CANtxBuff = CO_CANtxBufferInit( - CANdevTx, /* CAN device */ - CANdevTxIdx, /* index of specific buffer inside CAN module */ - SYNC->COB_ID, /* CAN identifier */ - 0, /* rtr */ - len, /* number of data bytes */ - 0); /* synchronous message flag bit */ - - return CO_ERROR_NO; -} - - -/******************************************************************************/ -uint8_t CO_SYNC_process( - CO_SYNC_t *SYNC, - uint32_t timeDifference_us, - uint32_t ObjDict_synchronousWindowLength) -{ - uint8_t ret = 0; - uint32_t timerNew; - - if(*SYNC->operatingState == CO_NMT_OPERATIONAL || *SYNC->operatingState == CO_NMT_PRE_OPERATIONAL){ - /* update sync timer, no overflow */ - timerNew = SYNC->timer + timeDifference_us; - if(timerNew > SYNC->timer) SYNC->timer = timerNew; - - /* was SYNC just received */ - if(IS_CANrxNew(SYNC->CANrxNew)){ - SYNC->timer = 0; - ret = 1; - CLEAR_CANrxNew(SYNC->CANrxNew); - } - - /* SYNC producer */ - if(SYNC->isProducer && SYNC->periodTime){ - if(SYNC->timer >= SYNC->periodTime){ - if(++SYNC->counter > SYNC->counterOverflowValue) SYNC->counter = 1; - SYNC->timer = 0; - ret = 1; - SYNC->CANrxToggle = SYNC->CANrxToggle ? false : true; - SYNC->CANtxBuff->data[0] = SYNC->counter; - CO_CANsend(SYNC->CANdevTx, SYNC->CANtxBuff); - } - } - - /* Synchronous PDOs are allowed only inside time window */ - if(ObjDict_synchronousWindowLength){ - if(SYNC->timer > ObjDict_synchronousWindowLength){ - if(SYNC->curentSyncTimeIsInsideWindow){ - ret = 2; - } - SYNC->curentSyncTimeIsInsideWindow = false; - } - else{ - SYNC->curentSyncTimeIsInsideWindow = true; - } - } - else{ - SYNC->curentSyncTimeIsInsideWindow = true; - } - - /* Verify timeout of SYNC */ - if(SYNC->periodTime && SYNC->timer > SYNC->periodTimeoutTime && *SYNC->operatingState == CO_NMT_OPERATIONAL) - CO_errorReport(SYNC->em, CO_EM_SYNC_TIME_OUT, CO_EMC_COMMUNICATION, SYNC->timer); - } - else { - CLEAR_CANrxNew(SYNC->CANrxNew); - } - - /* verify error from receive function */ - if(SYNC->receiveError != 0U){ - CO_errorReport(SYNC->em, CO_EM_SYNC_LENGTH, CO_EMC_SYNC_DATA_LENGTH, (uint32_t)SYNC->receiveError); - SYNC->receiveError = 0U; - } - - return ret; -} diff --git a/stack/CO_SYNC.h b/stack/CO_SYNC.h deleted file mode 100644 index 82463f57..00000000 --- a/stack/CO_SYNC.h +++ /dev/null @@ -1,164 +0,0 @@ -/** - * CANopen SYNC object protocol. - * - * @file CO_SYNC.h - * @ingroup CO_SYNC - * @author Janez Paternoster - * @copyright 2004 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#ifndef CO_SYNC_H -#define CO_SYNC_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @defgroup CO_SYNC SYNC - * @ingroup CO_CANopen - * @{ - * - * CANopen SYNC object protocol. - * - * For CAN identifier see #CO_Default_CAN_ID_t - * - * SYNC message is used for synchronization of the nodes on network. One node - * can be SYNC producer, others can be SYNC consumers. Synchronous TPDOs are - * transmitted after the CANopen SYNC message. Synchronous received PDOs are - * accepted(copied to OD) immediatelly after the reception of the next SYNC - * message. - * - * ####Contents of SYNC message - * By default SYNC message has no data. If _Synchronous counter overflow value_ - * from Object dictionary (index 0x1019) is different than 0, SYNC message has - * one data byte: _counter_ incremented by 1 with every SYNC transmission. - * - * ####SYNC in CANopenNode - * According to CANopen, synchronous RPDOs must be processed after reception of - * the next sync messsage. For that reason, there is a double receive buffer - * for each synchronous RPDO. At the moment, when SYNC is received or - * transmitted, internal variable CANrxToggle toggles. That variable is then - * used by synchronous RPDO to determine, which of the two buffers is used for - * RPDO reception and which for RPDO processing. - */ - - -/** - * SYNC producer and consumer object. - */ -typedef struct{ - CO_EM_t *em; /**< From CO_SYNC_init() */ - uint8_t *operatingState; /**< From CO_SYNC_init() */ - /** True, if device is SYNC producer. Calculated from _COB ID SYNC Message_ - variable from Object dictionary (index 0x1005). */ - bool_t isProducer; - /** COB_ID of SYNC message. Calculated from _COB ID SYNC Message_ - variable from Object dictionary (index 0x1005). */ - uint16_t COB_ID; - /** Sync period time in [microseconds]. Calculated from _Communication cycle period_ - variable from Object dictionary (index 0x1006). */ - uint32_t periodTime; - /** Sync period timeout time in [microseconds]. - (periodTimeoutTime = periodTime * 1,5) */ - uint32_t periodTimeoutTime; - /** Value from _Synchronous counter overflow value_ variable from Object - dictionary (index 0x1019) */ - uint8_t counterOverflowValue; - /** True, if current time is inside synchronous window. - In this case synchronous PDO may be sent. */ - bool_t curentSyncTimeIsInsideWindow; - /** Indicates, if new SYNC message received from CAN bus */ - volatile void *CANrxNew; - /** Variable toggles, if new SYNC message received from CAN bus */ - bool_t CANrxToggle; - /** Counter of the SYNC message if counterOverflowValue is different than zero */ - uint8_t counter; - /** Timer for the SYNC message in [microseconds]. - Set to zero after received or transmitted SYNC message */ - uint32_t timer; - /** Set to nonzero value, if SYNC with wrong data length is received from CAN */ - uint16_t receiveError; - CO_CANmodule_t *CANdevRx; /**< From CO_SYNC_init() */ - uint16_t CANdevRxIdx; /**< From CO_SYNC_init() */ - CO_CANmodule_t *CANdevTx; /**< From CO_SYNC_init() */ - CO_CANtx_t *CANtxBuff; /**< CAN transmit buffer inside CANdevTx */ - uint16_t CANdevTxIdx; /**< From CO_SYNC_init() */ -}CO_SYNC_t; - - -/** - * Initialize SYNC object. - * - * Function must be called in the communication reset section. - * - * @param SYNC This object will be initialized. - * @param em Emergency object. - * @param SDO SDO server object. - * @param operatingState Pointer to variable indicating CANopen device NMT internal state. - * @param COB_ID_SYNCMessage From Object dictionary (index 0x1005). - * @param communicationCyclePeriod From Object dictionary (index 0x1006). - * @param synchronousCounterOverflowValue From Object dictionary (index 0x1019). - * @param CANdevRx CAN device for SYNC reception. - * @param CANdevRxIdx Index of receive buffer in the above CAN device. - * @param CANdevTx CAN device for SYNC transmission. - * @param CANdevTxIdx Index of transmit buffer in the above CAN device. - * - * @return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT. - */ -CO_ReturnError_t CO_SYNC_init( - CO_SYNC_t *SYNC, - CO_EM_t *em, - CO_SDO_t *SDO, - uint8_t *operatingState, - uint32_t COB_ID_SYNCMessage, - uint32_t communicationCyclePeriod, - uint8_t synchronousCounterOverflowValue, - CO_CANmodule_t *CANdevRx, - uint16_t CANdevRxIdx, - CO_CANmodule_t *CANdevTx, - uint16_t CANdevTxIdx); - - -/** - * Process SYNC communication. - * - * Function must be called cyclically. - * - * @param SYNC This object. - * @param timeDifference_us Time difference from previous function call in [microseconds]. - * @param ObjDict_synchronousWindowLength _Synchronous window length_ variable from - * Object dictionary (index 0x1007). - * - * @return 0: No special meaning. - * @return 1: New SYNC message recently received or was just transmitted. - * @return 2: SYNC time was just passed out of window. - */ -uint8_t CO_SYNC_process( - CO_SYNC_t *SYNC, - uint32_t timeDifference_us, - uint32_t ObjDict_synchronousWindowLength); - -#ifdef __cplusplus -} -#endif /*__cplusplus*/ - -/** @} */ -#endif diff --git a/stack/CO_TIME.c b/stack/CO_TIME.c deleted file mode 100644 index 2f6949b0..00000000 --- a/stack/CO_TIME.c +++ /dev/null @@ -1,165 +0,0 @@ -/* - * CANopen TIME object. - * - * @file CO_TIME.c - * @ingroup CO_TIME - * @author Julien PEYREGNE - * @copyright 2019 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "CANopen.h" - -/* - * Read received message from CAN module. - * - * Function will be called (by CAN receive interrupt) every time, when CAN - * message with correct identifier will be received. - */ -static void CO_TIME_receive(void *object, const CO_CANrxMsg_t *msg){ - CO_TIME_t *TIME; - uint8_t operState; - - TIME = (CO_TIME_t*)object; /* this is the correct pointer type of the first argument */ - operState = *TIME->operatingState; - - if((operState == CO_NMT_OPERATIONAL) || (operState == CO_NMT_PRE_OPERATIONAL)){ - SET_CANrxNew(TIME->CANrxNew); - // Process Time from msg buffer - CO_memcpy((uint8_t*)&TIME->Time.ullValue, msg->data, msg->DLC); - } - else{ - TIME->receiveError = (uint16_t)msg->DLC; - } -} - -/******************************************************************************/ -CO_ReturnError_t CO_TIME_init( - CO_TIME_t *TIME, - CO_EM_t *em, - CO_SDO_t *SDO, - uint8_t *operatingState, - uint32_t COB_ID_TIMEMessage, - uint32_t TIMECyclePeriod, - CO_CANmodule_t *CANdevRx, - uint16_t CANdevRxIdx, - CO_CANmodule_t *CANdevTx, - uint16_t CANdevTxIdx) -{ - /* verify arguments */ - if(TIME==NULL || em==NULL || SDO==NULL || operatingState==NULL || - CANdevRx==NULL || CANdevTx==NULL){ - return CO_ERROR_ILLEGAL_ARGUMENT; - } - - /* Configure object variables */ - TIME->isConsumer = (COB_ID_TIMEMessage&0x80000000L) ? true : false; - TIME->isProducer = (COB_ID_TIMEMessage&0x40000000L) ? true : false; - TIME->COB_ID = COB_ID_TIMEMessage&0x7FF; // 11 bit ID - - TIME->periodTime = TIMECyclePeriod; - TIME->periodTimeoutTime = TIMECyclePeriod / 2 * 3; - /* overflow? */ - if(TIME->periodTimeoutTime < TIMECyclePeriod) - TIME->periodTimeoutTime = 0xFFFFFFFFL; - - CLEAR_CANrxNew(TIME->CANrxNew); - TIME->timer = 0; - TIME->receiveError = 0U; - - TIME->em = em; - TIME->operatingState = operatingState; - - - /* configure TIME consumer message reception */ - TIME->CANdevRx = CANdevRx; - TIME->CANdevRxIdx = CANdevRxIdx; - if(TIME->isConsumer) - CO_CANrxBufferInit( - CANdevRx, /* CAN device */ - CANdevRxIdx, /* rx buffer index */ - TIME->COB_ID, /* CAN identifier */ - 0x7FF, /* mask */ - 0, /* rtr */ - (void*)TIME, /* object passed to receive function */ - CO_TIME_receive); /* this function will process received message */ - - - /* configure TIME producer message transmission */ - TIME->CANdevTx = CANdevTx; - TIME->CANdevTxIdx = CANdevTxIdx; - if(TIME->isProducer) - TIME->TXbuff = CO_CANtxBufferInit( - CANdevTx, /* CAN device */ - CANdevTxIdx, /* index of specific buffer inside CAN module */ - TIME->COB_ID, /* CAN identifier */ - 0, /* rtr */ - TIME_MSG_LENGTH, /* number of data bytes */ - 0); /* synchronous message flag bit */ - - return CO_ERROR_NO; -} - -/******************************************************************************/ -uint8_t CO_TIME_process( - CO_TIME_t *TIME, - uint32_t timeDifference_ms) -{ - uint8_t ret = 0; - uint32_t timerNew; - - if(*TIME->operatingState == CO_NMT_OPERATIONAL || *TIME->operatingState == CO_NMT_PRE_OPERATIONAL){ - /* update TIME timer, no overflow */ - timerNew = TIME->timer + timeDifference_ms; - if(timerNew > TIME->timer) - TIME->timer = timerNew; - - /* was TIME just received */ - if(TIME->CANrxNew){ - TIME->timer = 0; - ret = 1; - CLEAR_CANrxNew(TIME->CANrxNew); - } - - /* TIME producer */ - if(TIME->isProducer && TIME->periodTime){ - if(TIME->timer >= TIME->periodTime){ - TIME->timer = 0; - ret = 1; - CO_memcpy(TIME->TXbuff->data, (const uint8_t*)&TIME->Time.ullValue, TIME_MSG_LENGTH); - CO_CANsend(TIME->CANdevTx, TIME->TXbuff); - } - } - - /* Verify TIME timeout if node is consumer */ - if(TIME->isConsumer && TIME->periodTime && TIME->timer > TIME->periodTimeoutTime - && *TIME->operatingState == CO_NMT_OPERATIONAL) - CO_errorReport(TIME->em, CO_EM_TIME_TIMEOUT, CO_EMC_COMMUNICATION, TIME->timer); - } - else { - CLEAR_CANrxNew(TIME->CANrxNew); - } - - /* verify error from receive function */ - if(TIME->receiveError != 0U){ - CO_errorReport(TIME->em, CO_EM_TIME_LENGTH, CO_EMC_TIME_DATA_LENGTH, (uint32_t)TIME->receiveError); - TIME->receiveError = 0U; - } - - return ret; -} diff --git a/stack/CO_TIME.h b/stack/CO_TIME.h deleted file mode 100644 index 1c80acaf..00000000 --- a/stack/CO_TIME.h +++ /dev/null @@ -1,158 +0,0 @@ -/** - * CANopen TIME object protocol. - * - * @file CO_TIME.c - * @ingroup CO_TIME - * @author Julien PEYREGNE - * @copyright 2019 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#ifndef CO_TIME_H -#define CO_TIME_H - -#include "CO_OD.h" - - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @defgroup CO_TIME TIME - * @ingroup CO_CANopen - * @{ - * - * CANopen TIME object protocol. - * - * For CAN identifier see #CO_Default_CAN_ID_t - * - * TIME message is used for time synchronization of the nodes on network. One node - * should be TIME producer, others can be TIME consumers. This is configured with - * COB_ID_TIME object 0x1012 : - * - * - bit 31 should be set for a consumer - * - bit 30 should be set for a producer - * - * - * ###TIME CONSUMER - * - * CO_TIME_init() configuration : - * - COB_ID_TIME : 0x80000100L -> TIME consumer with TIME_COB_ID = 0x100 - * - TIMECyclePeriod : - * - 0 -> no EMCY will be transmitted in case of TIME timeout - * - X -> an EMCY will be transmitted in case of TIME timeout (X * 1.5) ms - * - * Latest time value is stored in \p CO->TIME->Time variable. - * - * - * ###TIME PRODUCER - * - * CO_TIME_init() configuration : - * - COB_ID_TIME : 0x40000100L -> TIME producer with TIME_COB_ID = 0x100 - * - TIMECyclePeriod : Time transmit period in ms - * - * Write time value in \p CO->TIME->Time variable, this will be sent at TIMECyclePeriod. - */ - -#define TIME_MSG_LENGTH 6U - -/** - * TIME producer and consumer object. - */ -typedef struct{ - CO_EM_t *em; /**< From CO_TIME_init() */ - uint8_t *operatingState; /**< From CO_TIME_init() */ - /** True, if device is TIME consumer. Calculated from _COB ID TIME Message_ - variable from Object dictionary (index 0x1012). */ - bool_t isConsumer; - /** True, if device is TIME producer. Calculated from _COB ID TIME Message_ - variable from Object dictionary (index 0x1012). */ - bool_t isProducer; - uint16_t COB_ID; /**< From CO_TIME_init() */ - /** TIME period time in [milliseconds]. Set to TIME period to enable - timeout detection */ - uint32_t periodTime; - /** TIME period timeout time in [milliseconds]. - (periodTimeoutTime = periodTime * 1,5) */ - uint32_t periodTimeoutTime; - /** Variable indicates, if new TIME message received from CAN bus */ - volatile void *CANrxNew; - /** Timer for the TIME message in [microseconds]. - Set to zero after received or transmitted TIME message */ - uint32_t timer; - /** Set to nonzero value, if TIME with wrong data length is received from CAN */ - uint16_t receiveError; - CO_CANmodule_t *CANdevRx; /**< From CO_TIME_init() */ - uint16_t CANdevRxIdx; /**< From CO_TIME_init() */ - CO_CANmodule_t *CANdevTx; /**< From CO_TIME_init() */ - uint16_t CANdevTxIdx; /**< From CO_TIME_init() */ - CO_CANtx_t *TXbuff; /**< CAN transmit buffer */ - TIME_OF_DAY Time; -}CO_TIME_t; - -/** - * Initialize TIME object. - * - * Function must be called in the communication reset section. - * - * @param TIME This object will be initialized. - * @param em Emergency object. - * @param SDO SDO server object. - * @param operatingState Pointer to variable indicating CANopen device NMT internal state. - * @param COB_ID_TIMEMessage Should be intialized with CO_CAN_ID_TIME_STAMP - * @param TIMECyclePeriod TIME period in ms (may also be used in consumer mode for timeout detection (1.5x period)). - * @param CANdevRx CAN device for TIME reception. - * @param CANdevRxIdx Index of receive buffer in the above CAN device. - * - * @return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT. - */ -CO_ReturnError_t CO_TIME_init( - CO_TIME_t *TIME, - CO_EM_t *em, - CO_SDO_t *SDO, - uint8_t *operatingState, - uint32_t COB_ID_TIMEMessage, - uint32_t TIMECyclePeriod, - CO_CANmodule_t *CANdevRx, - uint16_t CANdevRxIdx, - CO_CANmodule_t *CANdevTx, - uint16_t CANdevTxIdx); - -/** - * Process TIME communication. - * - * Function must be called cyclically. - * - * @param TIME This object. - * @param timeDifference_ms Time difference from previous function call in [milliseconds]. - * - * @return 0: No special meaning. - * @return 1: New TIME message recently received (consumer) / transmited (producer). - */ -uint8_t CO_TIME_process( - CO_TIME_t *TIME, - uint32_t timeDifference_ms); - -#ifdef __cplusplus -} -#endif /*__cplusplus*/ - -/** @} */ -#endif diff --git a/stack/LPC1768/CO_CAN.h b/stack/LPC1768/CO_CAN.h deleted file mode 100644 index 3858aa66..00000000 --- a/stack/LPC1768/CO_CAN.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * CAN module object for LPC1768 microcontroller using Mbed SDK. - * - * @file CO_CAN.h - * @author Benoit Rapidel - * @copyright 2016 Benoit Rapidel - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * CANopenNode is free and open source software: you can redistribute - * it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Following clarification and special exception to the GNU General Public - * License is included to the distribution terms of CANopenNode: - * - * Linking this library statically or dynamically with other modules is - * making a combined work based on this library. Thus, the terms and - * conditions of the GNU General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this library give - * you permission to link this library with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also meet, - * for each linked independent module, the terms and conditions of the - * license of that module. An independent module is a module which is - * not derived from or based on this library. If you modify this - * library, you may extend this exception to your version of the - * library, but you are not obliged to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -#ifndef CO_CAN_H -#define CO_CAN_H - -#ifndef MBED_CAN -#error "MBED_CAN must be defined to select CAN port" -#endif - -#include "mbed.h" - -#if (MBED_CAN == 0) -#define MBED_CAN_RX p9 -#define MBED_CAN_TX p10 -#define MBED_CAN_REG LPC_CAN1 -#else -#define MBED_CAN_RX (p30) -#define MBED_CAN_TX (p29) -#define MBED_CAN_REG (LPC_CAN2) -#endif - -#define MBED_CHECK_TX_BUFFERS (MBED_CAN_REG->SR & 0x4 || MBED_CAN_REG->SR & 0x400 || MBED_CAN_REG->SR & 0x40000) -#define MBED_CHECK_TX_INTERRUPTS(intStatus) (intStatus & 0x2 || intStatus & 0x200 || intStatus & 0x400) - -extern CAN *CANport; - -#endif // CO_CAN_H diff --git a/stack/LPC1768/CO_driver.cpp b/stack/LPC1768/CO_driver.cpp deleted file mode 100644 index e82d1de0..00000000 --- a/stack/LPC1768/CO_driver.cpp +++ /dev/null @@ -1,485 +0,0 @@ -/* - * CAN module object for LPC1768 microcontroller using Mbed SDK. - * - * @file CO_driver.cpp - * @author Benoit Rapidel - * @copyright 2016 Benoit Rapidel - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * CANopenNode is free and open source software: you can redistribute - * it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Following clarification and special exception to the GNU General Public - * License is included to the distribution terms of CANopenNode: - * - * Linking this library statically or dynamically with other modules is - * making a combined work based on this library. Thus, the terms and - * conditions of the GNU General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this library give - * you permission to link this library with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also meet, - * for each linked independent module, the terms and conditions of the - * license of that module. An independent module is a module which is - * not derived from or based on this library. If you modify this - * library, you may extend this exception to your version of the - * library, but you are not obliged to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - - -#include "mbed.h" -#include "LPC17xx.h" -#include "CAN.h" - -extern "C" { -#include "CO_driver.h" -#include "CO_Emergency.h" - -#include "CO_CAN.h" -} - -CAN CANport0(MBED_CAN_RX, MBED_CAN_TX); -CAN *CANport = NULL; - - -/* helper functions */ - -CANMessage toCANMessage(CO_CANtx_t *CO_msg) { - CANMessage msg; - msg.id = (uint32_t) CO_msg->ident & 0x07FFU; - msg.len = (uint32_t) CO_msg->DLC; - msg.type = - ((uint32_t) CO_msg->ident & 0x8000U) == 0x8000 ? - CANRemote : CANData; - memcpy(msg.data, CO_msg->data, CO_msg->DLC); - - return msg; -} - -void fromCANMessage(CANMessage *msg, CO_CANrxMsg_t *CO_msg) { - CO_msg->ident = (uint32_t) msg->id & 0x07FFU; - CO_msg->DLC = (uint32_t) msg->len; - CO_msg->ident = - (msg->type == CANRemote) ? CO_msg->ident | 0x8000 : CO_msg->ident; - memcpy(CO_msg->data, msg->data, CO_msg->DLC); -} - -/******************************************************************************/ -void CO_CANsetConfigurationMode(void *CANdriverState){ - /* Put CAN module in configuration mode */ -} - - -/******************************************************************************/ -void CO_CANsetNormalMode(CO_CANmodule_t *CANmodule){ - /* Put CAN module in normal mode */ - // CANport->mode(CAN::Reset); - - // can_mode(LPC_CAN2->, CAN::Normal); - // LPC_IOCON->PIO0_4 = PIN_INPUT P - CANmodule->CANnormal = true; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_CANmodule_init( - CO_CANmodule_t *CANmodule, - void *CANdriverState, - CO_CANrx_t rxArray[], - uint16_t rxSize, - CO_CANtx_t txArray[], - uint16_t txSize, - uint16_t CANbitRate) -{ - uint16_t i; - uint16_t freq_err = 0; - - /* verify arguments */ - if(CANmodule==NULL || rxArray==NULL || txArray==NULL){ - return CO_ERROR_ILLEGAL_ARGUMENT; - } - - /* Configure object variables */ - CANmodule->CANdriverState = CANdriverState; - CANmodule->rxArray = rxArray; - CANmodule->rxSize = rxSize; - CANmodule->txArray = txArray; - CANmodule->txSize = txSize; - CANmodule->CANnormal = false; - CANmodule->useCANrxFilters = false; //(rxSize <= 32U) ? true : false;/* microcontroller dependent */ - CANmodule->bufferInhibitFlag = false; - CANmodule->firstCANtxMessage = true; - CANmodule->CANtxCount = 0U; - CANmodule->errOld = 0U; - CANmodule->em = NULL; - - for(i=0U; ireset(); - - - /* Configure CAN timing */ - int CANbaudRate = CANbitRate * 1000; - freq_err = CANport->frequency(CANbaudRate); - if(freq_err != 1) { - return CO_ERROR_PARAMETERS; - } - - - /* Configure CAN module hardware filters */ - if(CANmodule->useCANrxFilters){ - /* CAN module filters are used, they will be configured with */ - /* CO_CANrxBufferInit() functions, called by separate CANopen */ - /* init functions. */ - /* Configure all masks so, that received message must match filter */ - // TODO - } - else{ - /* CAN module filters are not used, all messages with standard 11-bit */ - /* identifier will be received */ - /* Configure mask 0 so, that all messages with standard identifier are accepted */ - // CANport->filter(0, 0, CANAny); - } - - - /* configure CAN interrupt registers */ - - - return CO_ERROR_NO; -} - - -/******************************************************************************/ -void CO_CANmodule_disable(CO_CANmodule_t *CANmodule){ - /* turn off the module */ -} - - -/******************************************************************************/ -uint16_t CO_CANrxMsg_readIdent(const CO_CANrxMsg_t *rxMsg){ - return (uint16_t) rxMsg->ident; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_CANrxBufferInit( - CO_CANmodule_t *CANmodule, - uint16_t index, - uint16_t ident, - uint16_t mask, - bool_t rtr, - void *object, - void (*pFunct)(void *object, const CO_CANrxMsg_t *message)) -{ - CO_ReturnError_t ret = CO_ERROR_NO; - - if((CANmodule!=NULL) && (object!=NULL) && (pFunct!=NULL) && (index < CANmodule->rxSize)){ - /* buffer, which will be configured */ - CO_CANrx_t *buffer = &CANmodule->rxArray[index]; - - /* Configure object variables */ - buffer->object = object; - buffer->pFunct = pFunct; - - /* CAN identifier and CAN mask, bit aligned with CAN module. Different on different microcontrollers. */ - buffer->ident = ident & 0x07FFU; - if(rtr){ - buffer->ident |= 0x0800U; - } - buffer->mask = (mask & 0x07FFU) | 0x0800U; - - /* Set CAN hardware module filter and mask. */ - if(CANmodule->useCANrxFilters){ - // TODO - } - } - else{ - ret = CO_ERROR_ILLEGAL_ARGUMENT; - } - - return ret; -} - - -/******************************************************************************/ -CO_CANtx_t *CO_CANtxBufferInit( - CO_CANmodule_t *CANmodule, - uint16_t index, - uint16_t ident, - bool_t rtr, - uint8_t noOfBytes, - bool_t syncFlag) -{ - CO_CANtx_t *buffer = NULL; - - if((CANmodule != NULL) && (index < CANmodule->txSize)){ - /* get specific buffer */ - buffer = &CANmodule->txArray[index]; - - /* CAN identifier, DLC and rtr, bit aligned with CAN module transmit buffer. - * Microcontroller specific. */ - buffer->ident = ((uint32_t) ident & 0x07FFU); - buffer->DLC = ((uint32_t) noOfBytes & 0xFU); - /* toggle RTR bit if CAN message is remote type */ - if (rtr) buffer->ident |= 0x8000U; - - buffer->bufferFull = false; - buffer->syncFlag = syncFlag; - } - - return buffer; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_CANsend(CO_CANmodule_t *CANmodule, CO_CANtx_t *buffer){ - CO_ReturnError_t err = CO_ERROR_NO; - - CANMessage msg; - - /* Verify overflow */ - if(buffer->bufferFull){ - if(!CANmodule->firstCANtxMessage){ - /* don't set error, if bootup message is still on buffers */ - CO_errorReport((CO_EM_t*)CANmodule->em, CO_EM_CAN_TX_OVERFLOW, CO_EMC_CAN_OVERRUN, buffer->ident); - } - err = CO_ERROR_TX_OVERFLOW; - // USBport.printf("BUFF_FULL OVERFLOW\r\n"); - } - - CO_LOCK_CAN_SEND(); - /* if CAN TX buffer is free, copy message to it */ - if ((MBED_CHECK_TX_BUFFERS) && CANmodule->CANtxCount == 0){ - CANmodule->bufferInhibitFlag = buffer->syncFlag; - /* copy message and txRequest */ - // USBport.printf("SEND\r\n"); - CANport->write(toCANMessage(buffer)); - } - /* if no buffer is free, message will be sent by interrupt */ - else{ - // USBport.printf("BUFF_FULL\r\n"); - buffer->bufferFull = true; - CANmodule->CANtxCount++; - } - CO_UNLOCK_CAN_SEND(); - - return err; -} - - -/******************************************************************************/ -void CO_CANclearPendingSyncPDOs(CO_CANmodule_t *CANmodule){ - uint32_t tpdoDeleted = 0U; - - CO_LOCK_CAN_SEND(); - /* Abort message from CAN module, if there is synchronous TPDO. - * Take special care with this functionality. */ - if (((MBED_CAN_REG->GSR & (1 << 2)) == 0) && CANmodule->bufferInhibitFlag){ - /* clear TXREQ */ - MBED_CAN_REG->CMR = (1 << 5) | (1 << 1); // Select TX buffer 1 and run AT command (abort) - MBED_CAN_REG->CMR = (1 << 6) | (1 << 1); // Select TX buffer 2 and run AT command (abort) - MBED_CAN_REG->CMR = (1 << 7) | (1 << 1); // Select TX buffer 3 and run AT command (abort) - - CANmodule->bufferInhibitFlag = false; - tpdoDeleted = 1U; - } - /* delete also pending synchronous TPDOs in TX buffers */ - if(CANmodule->CANtxCount != 0U){ - uint16_t i; - CO_CANtx_t *buffer = &CANmodule->txArray[0]; - for(i = CANmodule->txSize; i > 0U; i--){ - if(buffer->bufferFull){ - if(buffer->syncFlag){ - buffer->bufferFull = false; - CANmodule->CANtxCount--; - tpdoDeleted = 2U; - } - } - buffer++; - } - } - CO_UNLOCK_CAN_SEND(); - - - if(tpdoDeleted != 0U){ - CO_errorReport((CO_EM_t*)CANmodule->em, CO_EM_TPDO_OUTSIDE_WINDOW, CO_EMC_COMMUNICATION, tpdoDeleted); - } -} - - -/******************************************************************************/ -void CO_CANverifyErrors(CO_CANmodule_t *CANmodule){ - uint16_t rxErrors, txErrors, overflow; - CO_EM_t* em = (CO_EM_t*)CANmodule->em; - uint32_t err; - - /* get error counters from module. Id possible, function may use different way to - * determine errors. */ - rxErrors = CANport->rderror(); - txErrors = CANport->tderror(); - overflow = (MBED_CAN_REG->GSR & 0x2); - - err = ((uint32_t)txErrors << 16) | ((uint32_t)rxErrors << 8) | overflow; - - if(CANmodule->errOld != err){ - CANmodule->errOld = err; - - if(txErrors >= 256U){ /* bus off */ - CO_errorReport(em, CO_EM_CAN_TX_BUS_OFF, CO_EMC_BUS_OFF_RECOVERED, err); - } - else{ /* not bus off */ - CO_errorReset(em, CO_EM_CAN_TX_BUS_OFF, err); - - if((rxErrors >= 96U) || (txErrors >= 96U)){ /* bus warning */ - CO_errorReport(em, CO_EM_CAN_BUS_WARNING, CO_EMC_NO_ERROR, err); - } - - if(rxErrors >= 128U){ /* RX bus passive */ - CO_errorReport(em, CO_EM_CAN_RX_BUS_PASSIVE, CO_EMC_CAN_PASSIVE, err); - } - else{ - CO_errorReset(em, CO_EM_CAN_RX_BUS_PASSIVE, err); - } - - if(txErrors >= 128U){ /* TX bus passive */ - if(!CANmodule->firstCANtxMessage){ - CO_errorReport(em, CO_EM_CAN_TX_BUS_PASSIVE, CO_EMC_CAN_PASSIVE, err); - } - } - else{ - bool_t isError = CO_isError(em, CO_EM_CAN_TX_BUS_PASSIVE); - if(isError){ - CO_errorReset(em, CO_EM_CAN_TX_BUS_PASSIVE, err); - CO_errorReset(em, CO_EM_CAN_TX_OVERFLOW, err); - } - } - - if((rxErrors < 96U) && (txErrors < 96U)){ /* no error */ - CO_errorReset(em, CO_EM_CAN_BUS_WARNING, err); - } - } - - if(overflow != 0U){ /* CAN RX bus overflow */ - CO_errorReport(em, CO_EM_CAN_RXB_OVERFLOW, CO_EMC_CAN_OVERRUN, err); - } - } -} - - -/******************************************************************************/ -void CO_CANinterrupt(CO_CANmodule_t *CANmodule){ - uint16_t intStatus; - - intStatus = MBED_CAN_REG->ICR; - - /* receive interrupt */ - if (intStatus & 0x1) { - CO_CANrxMsg_t rcvMsgBuf; /* buffer for the received message in CAN module */ - CO_CANrxMsg_t *rcvMsg; /* pointer to received message in CAN module */ - uint16_t index; /* index of received message */ - uint32_t rcvMsgIdent; /* identifier of the received message */ - CO_CANrx_t *buffer = NULL; /* receive message buffer from CO_CANmodule_t object. */ - bool_t msgMatched = false; - CANMessage msg; - - CANport->read(msg); - fromCANMessage(&msg, &rcvMsgBuf); /* get message from module here */ - rcvMsg = &rcvMsgBuf; - rcvMsgIdent = rcvMsg->ident; - if(CANmodule->useCANrxFilters){ - /* CAN module filters are used. Message with known 11-bit identifier has */ - /* been received */ - index = 0; /* get index of the received message here. Or something similar */ - if(index < CANmodule->rxSize){ - buffer = &CANmodule->rxArray[index]; - /* verify also RTR */ - if(((rcvMsgIdent ^ buffer->ident) & buffer->mask) == 0U){ - msgMatched = true; - } - } - } - else{ - /* CAN module filters are not used, message with any standard 11-bit identifier */ - /* has been received. Search rxArray form CANmodule for the same CAN-ID. */ - buffer = &CANmodule->rxArray[0]; - for(index = CANmodule->rxSize; index > 0U; index--){ - if(((rcvMsgIdent ^ buffer->ident) & buffer->mask) == 0U){ - msgMatched = true; - break; - } - buffer++; - } - } - - /* Call specific function, which will process the message */ - if(msgMatched && (buffer != NULL) && (buffer->pFunct != NULL)){ - buffer->pFunct(buffer->object, rcvMsg); - } - - /* Clear interrupt flag */ - /* The interrupt flag is cleaned by CANport.read() function call */ - } - - - /* transmit interrupt */ - else if ((intStatus & 0x2) || (intStatus & 0x200) || (intStatus & 0x400)) { - /* Clear interrupt flag */ - - /* First CAN message (bootup) was sent successfully */ - CANmodule->firstCANtxMessage = false; - /* clear flag from previous message */ - CANmodule->bufferInhibitFlag = false; - /* Are there any new messages waiting to be send */ - if(CANmodule->CANtxCount > 0U){ - uint16_t i; /* index of transmitting message */ - - /* first buffer */ - CO_CANtx_t *buffer = &CANmodule->txArray[0]; - /* search through whole array of pointers to transmit message buffers. */ - for(i = CANmodule->txSize; i > 0U; i--){ - /* if message buffer is full, send it. */ - if(buffer->bufferFull){ - buffer->bufferFull = false; - CANmodule->CANtxCount--; - - /* Copy message to CAN buffer */ - CANmodule->bufferInhibitFlag = buffer->syncFlag; - /* canSend... */ - CANport->write(toCANMessage(buffer)); - break; /* exit for loop */ - } - buffer++; - }/* end of for loop */ - - /* Clear counter if no more messages */ - if(i == 0U){ - CANmodule->CANtxCount = 0U; - } - } - } -} diff --git a/stack/LPC1768/CO_driver_target.h b/stack/LPC1768/CO_driver_target.h deleted file mode 100644 index 19b87ea5..00000000 --- a/stack/LPC1768/CO_driver_target.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * CAN module object for LPC1768 microcontroller using Mbed SDK. - * - * @file CO_driver_target.h - * @ingroup CO_driver - * @author Benoit Rapidel - * @copyright 2016 Benoit Rapidel - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * CANopenNode is free and open source software: you can redistribute - * it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Following clarification and special exception to the GNU General Public - * License is included to the distribution terms of CANopenNode: - * - * Linking this library statically or dynamically with other modules is - * making a combined work based on this library. Thus, the terms and - * conditions of the GNU General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this library give - * you permission to link this library with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also meet, - * for each linked independent module, the terms and conditions of the - * license of that module. An independent module is a module which is - * not derived from or based on this library. If you modify this - * library, you may extend this exception to your version of the - * library, but you are not obliged to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - - -#ifndef CO_DRIVER_TARGET_H -#define CO_DRIVER_TARGET_H - - -/* For documentation see file drvTemplate/CO_driver.h */ - - -#include /* for 'NULL' */ -#include /* for 'int8_t' to 'uint64_t' */ -#include /* for 'true', 'false' */ - - -/* Endianness */ -#define CO_LITTLE_ENDIAN - -/* CAN module base address */ -#define MBED_CAN 1 - - -/* Critical sections */ -#define CO_LOCK_CAN_SEND() -#define CO_UNLOCK_CAN_SEND() - -#define CO_LOCK_EMCY() -#define CO_UNLOCK_EMCY() - -#define CO_LOCK_OD() -#define CO_UNLOCK_OD() - - -/* Data types */ -/* int8_t to uint64_t are defined in stdint.h */ -typedef unsigned char bool_t; -typedef float float32_t; -typedef long double float64_t; -typedef char char_t; -typedef unsigned char oChar_t; -typedef unsigned char domain_t; - - -/* CAN receive message structure as aligned in CAN module. */ -typedef struct{ - uint32_t ident; - uint8_t DLC ; - uint8_t data[8]; -}CO_CANrxMsg_t; - - -/* Received message object */ -typedef struct{ - uint16_t ident; - uint16_t mask; - void *object; - void (*pFunct)(void *object, const CO_CANrxMsg_t *message); -}CO_CANrx_t; - - -/* Transmit message object. */ -typedef struct{ - uint32_t ident; - uint8_t DLC ; - uint8_t data[8]; - volatile bool_t bufferFull; - volatile bool_t syncFlag; -}CO_CANtx_t; - - -/* CAN module object. */ -typedef struct{ - void *CANdriverState; - CO_CANrx_t *rxArray; - uint16_t rxSize; - CO_CANtx_t *txArray; - uint16_t txSize; - volatile bool_t CANnormal; - volatile bool_t useCANrxFilters; - volatile bool_t bufferInhibitFlag; - volatile bool_t firstCANtxMessage; - volatile uint16_t CANtxCount; - uint32_t errOld; - void *em; -}CO_CANmodule_t; - - -/* Receives and transmits CAN messages. */ -void CO_CANinterrupt(CO_CANmodule_t *CANmodule); - - -#endif /* CO_DRIVER_TARGET_H */ diff --git a/stack/LPC177x_8x/CANOpenTask.c b/stack/LPC177x_8x/CANOpenTask.c deleted file mode 100644 index 6c8a3555..00000000 --- a/stack/LPC177x_8x/CANOpenTask.c +++ /dev/null @@ -1,211 +0,0 @@ -/*--------------------------------------------------------------*/ -/* GENESYS ELECTRONICS DESIGN PTY LTD */ -/*--------------------------------------------------------------*/ -/* */ -/* FILE NAME : CANOpenTask.h */ -/* */ -/* REVISION NUMBER: 1.0 */ -/* */ -/* PURPOSE : This is the implementation of the CANOpen main task*/ -/* */ -/* */ -/*--------------------------------------------------------------*/ -/* */ -/* AUTHOR: */ -/* Harshah Sagar */ -/* GENESYS ELECTRONICS DESIGN Pty Ltd */ -/* Unit 5/33 Ryde Road */ -/* Pymble NSW 2073 */ -/* Australia. */ -/* Telephone # 61-02-9496 8900 */ -/*--------------------------------------------------------------*/ -/* Revision Number : */ -/* Revision By : Amit Hergass */ -/* Date : 30-Jun-2014 */ -/* Description : Original Issue */ -/* */ -/* !--------------------------------------------------------! */ -/* ! Revisions to be entered in reverse chronological order ! */ -/* !--------------------------------------------------------! */ -/* */ -/*--------------------------------------------------------------*/ - -#include "FreeRTOS.h" -#include "task.h" - -#include "board.h" -#include "Watchdog.h" -#include "CANopen.h" -#include "application.h" -#include "CANOpenTask.h" - -/* Global variables and objects */ -volatile uint16_t CO_timer1ms = 0U; /* variable increments each millisecond */ -static bool canStackEnabled = true; -void CO_TimerProcess(void); - -#ifdef TASK_WATCHDOG_ENABLED -static hWatchdog CanOpenWdHandler; -#endif -/* main ***********************************************************************/ -void CANOpenTask ( void *pvParameters ) -{ - CO_NMT_reset_cmd_t reset = CO_RESET_NOT; - - /* Configure microcontroller. */ -#ifdef TASK_WATCHDOG_ENABLED - /* wait number of the task iteration delay time (#seconds until WD expires * (1000ms/WD task frequency)) - Give the task 20 seconds to recover*/ - CanOpenWdHandler = watchogTaskRegister( "CANOpen", (20 * (1000 / WATCHDOG_TASK_FREQUENCY_MS))); -#endif - while(true) - { - reset = CO_RESET_NOT; - /* TODO: initialize EEPROM */ - - /* Todo: Loading COD */ - DEBUGOUT("CANOpenTask() Loading COD\n\r"); - - /* Verify, if OD structures have proper alignment of initial values */ - DEBUGOUT("Checking COD in RAM (size=%d)\n\r", &CO_OD_RAM.LastWord - &CO_OD_RAM.FirstWord); - if (CO_OD_RAM.FirstWord != CO_OD_RAM.LastWord) - DEBUGOUT("Err COD in RAM\n\r"); - DEBUGOUT("Checking COD in EEPROM (size=%d)\n\r", &CO_OD_EEPROM.LastWord - &CO_OD_EEPROM.FirstWord); - if (CO_OD_EEPROM.FirstWord != CO_OD_EEPROM.LastWord) - DEBUGOUT("Err COD in EEPROM\n\r"); - DEBUGOUT("Checking COD in ROM (size=%d)\n\r", &CO_OD_ROM.LastWord - &CO_OD_ROM.FirstWord); - if (CO_OD_ROM.FirstWord != CO_OD_ROM.LastWord) - DEBUGOUT("Err COD in ROM\n\r"); - - /* Application interface */ - programStart(); - - - /* increase variable each startup. Variable is stored in EEPROM. */ - OD_powerOnCounter++; - - - DEBUGOUT("CO power-on (BTR=%dk Node=0x%x)\n\r", CO_OD_ROM.CANBitRate, CO_OD_ROM.CANNodeID); - - while((reset != CO_RESET_APP)&& (true == canStackEnabled)){ - /* CANopen communication reset - initialize CANopen objects *******************/ - CO_ReturnError_t err; - uint16_t timer1msPrevious=0; - - /* disable timer and CAN interrupts */ - NVIC_DisableIRQ(CAN_IRQn); - - /* initialize CANopen */ - err = CO_init(); - if(err != CO_ERROR_NO){ - DEBUGOUT("CANOpenTask CO_init() Failed!!!\n\r"); - CO_errorReport(CO->em, CO_EM_MEMORY_ALLOCATION_ERROR, CO_EMC_SOFTWARE_INTERNAL, err); - while(1) - { - vTaskDelay(1); - }; - } - /* initialize variables */ - timer1msPrevious = CO_timer1ms; - reset = CO_RESET_NOT; - - /* Application interface */ - communicationReset(); - - /* start CAN and enable interrupts */ - CO_CANsetNormalMode(ADDR_CAN1); - - - while((reset == CO_RESET_NOT) && (true == canStackEnabled)){ - /* loop for normal program execution ******************************************/ - uint16_t timer1msDiff; - - CO_DISABLE_INTERRUPTS(); - /*execution every 1 millisecond */ - timer1msDiff = CO_timer1ms - timer1msPrevious; - if(0 < timer1msDiff) - { - CO_TimerProcess(); - } - else - { - vTaskDelay(1); - } - timer1msPrevious = CO_timer1ms; - CO_ENABLE_INTERRUPTS(); - - /* Application interface */ - programAsync(timer1msDiff); - - /* CANopen process */ - reset = CO_process(CO, timer1msDiff); - /* Process EEPROM */ - -#ifdef TASK_WATCHDOG_ENABLED - watchogTaskFeed(CanOpenWdHandler); -#endif - } - } - - DEBUGOUT("CANOpenTask Terminated reset= %d ,canStackEnabled= %d !!!\n\r", reset,canStackEnabled); - - /* program exit ***************************************************************/ - - /* Application interface */ - programEnd(); - - /* delete objects from memory */ - CO_delete(); - } - - //Exit the task - vTaskDelete( NULL ); - -} - - -/* timer interrupt every millisecond ************************/ -/*void vApplicationTickHook(void) since the tick is 1ms we can use the FreeRTOS tick hook*/ -void /* interrupt */ CO_TimerInterruptHandler(void){ - - /* clear interrupt flag */ - CO_timer1ms++; -} - -/* function executes every millisecond ************************/ -void CO_TimerProcess(void){ - - CO_process_RPDO(CO); - - /* Application interface */ - program1ms(); - - CO_process_TPDO(CO); - - /* verify timer overflow (is flag set again?) */ - if(0){ - CO_errorReport(CO->em, CO_EM_ISR_TIMER_OVERFLOW, CO_EMC_SOFTWARE_INTERNAL, 0U); - } -} - - -/* CAN interrupt function *****************************************************/ -void /* interrupt */ CAN_IRQHandler(void){ /* CO_CAN1InterruptHandler(void){*/ - CO_CANinterrupt(CO->CANmodule[0]); - /* clear interrupt flag */ -} - -/* CAN_StackEnable() CAN Task Enable/Disable *****************************************************/ -/*enableSwitch: true- Task enables, false- Task disabled */ -void CAN_StackEnable(bool enableSwitch){ - /* CAN Task can only be terminated, not restarted at this stage*/ - if(canStackEnabled) - { - canStackEnabled = enableSwitch; - } -} -/* CAN_GetStackEnable *****************************************************/ -/*Return: true- Task enables, false- Task disabled */ -bool CAN_GetStackEnable(void){ - return canStackEnabled; -} diff --git a/stack/LPC177x_8x/CANOpenTask.h b/stack/LPC177x_8x/CANOpenTask.h deleted file mode 100644 index 028c364b..00000000 --- a/stack/LPC177x_8x/CANOpenTask.h +++ /dev/null @@ -1,36 +0,0 @@ -/*--------------------------------------------------------------*/ -/* GENESYS ELECTRONICS DESIGN PTY LTD */ -/*--------------------------------------------------------------*/ -/* */ -/* FILE NAME : CANOpenTask.h */ -/* */ -/* REVISION NUMBER: 1.0 */ -/* */ -/* PURPOSE : This is the implementation of the CANOpen main task*/ -/* */ -/* */ -/*--------------------------------------------------------------*/ -/* */ -/* AUTHOR: */ -/* Harshah Sagar */ -/* GENESYS ELECTRONICS DESIGN Pty Ltd */ -/* Unit 5/33 Ryde Road */ -/* Pymble NSW 2073 */ -/* Australia. */ -/* Telephone # 61-02-9496 8900 */ -/*--------------------------------------------------------------*/ -/* Revision Number : */ -/* Revision By : Amit Hergass */ -/* Date : 30-Jun-2014 */ -/* Description : Original Issue */ -/* */ -/* !--------------------------------------------------------! */ -/* ! Revisions to be entered in reverse chronological order ! */ -/* !--------------------------------------------------------! */ -/* */ -/*--------------------------------------------------------------*/ - -void CANOpenTask ( void *pvParameters ); -void CAN_StackEnable(bool enableSwitch); -bool CAN_GetStackEnable(void); - diff --git a/stack/LPC177x_8x/CO_driver.c b/stack/LPC177x_8x/CO_driver.c deleted file mode 100644 index 83e09bba..00000000 --- a/stack/LPC177x_8x/CO_driver.c +++ /dev/null @@ -1,842 +0,0 @@ -/* - * CAN module object for NXP LPC177x (Cortex M3) and FreeRTOS. - * - * This file is a template for other microcontrollers. - * - * @file CO_driver.c - * @ingroup CO_driver - * @author Janez Paternoster - * @author Amit H - * @copyright 2004 - 2014 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * CANopenNode is free and open source software: you can redistribute - * it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Following clarification and special exception to the GNU General Public - * License is included to the distribution terms of CANopenNode: - * - * Linking this library statically or dynamically with other modules is - * making a combined work based on this library. Thus, the terms and - * conditions of the GNU General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this library give - * you permission to link this library with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also meet, - * for each linked independent module, the terms and conditions of the - * license of that module. An independent module is a module which is - * not derived from or based on this library. If you modify this - * library, you may extend this exception to your version of the - * library, but you are not obliged to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ -/*Kernel Includes. ----------------------------------------------------------*/ -#include "FreeRTOS.h" -#include "task.h" - -#include "board.h" - -#include "CO_driver.h" -#include "CO_Emergency.h" -#include "IMAXEON_Config.h" - -#include "CO_OD.h" -/***************************************************************************** - * Private types/enumerations/variables - ****************************************************************************/ - -#define CAN_CTRL_NO 1 - -#if (CAN_CTRL_NO == 0) -#define LPC_CAN (LPC_CAN1) -#else -#define LPC_CAN (LPC_CAN2) -#endif - -#define AF_LUT_USED 1 -#if AF_LUT_USED -#define FULL_CAN_AF_USED 0 -#define REMOVE_CAN_AF_ENTRIES 0 -#endif - -#define CAN_TX_MSG_STD_ID (0x200) -#define CAN_TX_MSG_REMOTE_STD_ID (0x300) -#define CAN_TX_MSG_EXT_ID (0x10000200) -#define CAN_RX_MSG_ID (0x100) - -/***************************************************************************** - * Public types/enumerations/variables - ****************************************************************************/ - -#if AF_LUT_USED -#if FULL_CAN_AF_USED -CAN_STD_ID_ENTRY_T FullCANSection[] = { - {CAN_CTRL_NO, 0, 0x03}, - {CAN_CTRL_NO, 0, 0x05}, - {CAN_CTRL_NO, 0, 0x07}, - {CAN_CTRL_NO, 0, 0x09}, -}; -#endif -CAN_STD_ID_ENTRY_T SFFSection[] = { - {CAN_CTRL_NO, 0, 0x00}, - {CAN_CTRL_NO, 0, 0x01}, - {CAN_CTRL_NO, 0, 0x02}, - {CAN_CTRL_NO, 0, 0x08}, - {CAN_CTRL_NO, 0, 0x10}, - {CAN_CTRL_NO, 0, 0x30}, - {CAN_CTRL_NO, 0, 0x50}, - {CAN_CTRL_NO, 0, 0x70}, - {CAN_CTRL_NO, 0, 0x90}, - {CAN_CTRL_NO, 0, 0xB0}, -}; -CAN_STD_ID_RANGE_ENTRY_T SffGrpSection[] = { - {{CAN_CTRL_NO, 0, 0x100}, {CAN_CTRL_NO, 0, 0x400}}, - {{CAN_CTRL_NO, 0, 0x500}, {CAN_CTRL_NO, 0, 0x6FF}}, - {{CAN_CTRL_NO, 0, 0x700}, {CAN_CTRL_NO, 0, 0x780}}, -}; -CAN_EXT_ID_ENTRY_T EFFSection[] = { - {CAN_CTRL_NO, ((1 << 11) | 0x03)}, - {CAN_CTRL_NO, ((1 << 11) | 0x05)}, - {CAN_CTRL_NO, ((1 << 11) | 0x07)}, - {CAN_CTRL_NO, ((1 << 11) | 0x09)}, -}; -CAN_EXT_ID_RANGE_ENTRY_T EffGrpSection[] = { - {{CAN_CTRL_NO, ((1 << 11) | 0x300)}, {CAN_CTRL_NO, ((1 << 11) | 0x400)}}, - {{CAN_CTRL_NO, ((1 << 11) | 0x500)}, {CAN_CTRL_NO, ((1 << 11) | 0x6FF)}}, - {{CAN_CTRL_NO, ((1 << 11) | 0x700)}, {CAN_CTRL_NO, ((1 << 11) | 0x780)}}, -}; -CANAF_LUT_T AFSections = { -#if FULL_CAN_AF_USED - FullCANSection, sizeof(FullCANSection) / sizeof(CAN_STD_ID_ENTRY_T), -#else - NULL, 0, -#endif - SFFSection, sizeof(SFFSection) / sizeof(CAN_STD_ID_ENTRY_T), - SffGrpSection, sizeof(SffGrpSection) / sizeof(CAN_STD_ID_RANGE_ENTRY_T), - EFFSection, sizeof(EFFSection) / sizeof(CAN_EXT_ID_ENTRY_T), - EffGrpSection, sizeof(EffGrpSection) / sizeof(CAN_EXT_ID_RANGE_ENTRY_T), -}; -#endif /*AF_LUT_USED*/ - -/***************************************************************************** - * Private functions - ****************************************************************************/ -static void PrintCANErrorInfo(uint32_t Status); -static void PrintCANMsg(CAN_MSG_T *pMsg); -static void PrintAFLUT(void); -static void SetupAFLUT(void); -/* Insert/Remove entries to/from AF LUT */ -static void ChangeAFLUT(void); - -/******************************************************************************/ -void CO_CANrxMsgHandler(void *object, const CO_CANrxMsg_t *message){ - - DEBUGOUT("CO_CANrxMsgHandler Default Rx handler called \r\n"); -/* PrintCANMsg((CAN_MSG_T*)message); */ -} - -/******************************************************************************/ -void CO_CANsetConfigurationMode(void *CANdriverState){ -} - - -/******************************************************************************/ -void CO_CANsetNormalMode(CO_CANmodule_t *CANmodule){ - - Chip_CAN_SetAFMode(LPC_CANAF, CAN_AF_NORMAL_MODE); - - CANmodule->CANnormal = true; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_CANmodule_init( - CO_CANmodule_t *CANmodule, - void *CANdriverState, - CO_CANrx_t rxArray[], - uint16_t rxSize, - CO_CANtx_t txArray[], - uint16_t txSize, - uint16_t CANbitRate) -{ - uint16_t i; - uint8_t nodeId=0; - - /* verify arguments */ - if(CANmodule==NULL || rxArray==NULL || txArray==NULL){ - return CO_ERROR_ILLEGAL_ARGUMENT; - } - - /* Configure object variables */ - CANmodule->CANdriverState = CANdriverState; - CANmodule->rxArray = rxArray; - CANmodule->rxSize = rxSize; - CANmodule->txArray = txArray; - CANmodule->txSize = txSize; - CANmodule->CANnormal = false; -#if AF_LUT_USED - CANmodule->useCANrxFilters = (rxSize <= 32U) ? true : false;/* microcontroller dependent */ -#else - CANmodule->useCANrxFilters = false; -#endif - CANmodule->bufferInhibitFlag = false; - CANmodule->firstCANtxMessage = true; - CANmodule->CANtxCount = 0U; - CANmodule->errOld = 0U; - CANmodule->em = NULL; - - - DEBUGOUT("CO_CANmodule_init Baud: %d\r\n",(CANbitRate * 1000)); - - for(i=0U; iuseCANrxFilters){ - DEBUGOUT("\tCAN Rx Acceptance Filters ON \r\n"); -#if AF_LUT_USED - /* CAN module filters are used, they will be configured with */ - /* CO_CANrxBufferInit() functions, called by separate CANopen */ - /* init functions. */ - /* Configure all masks so, that received message must match filter */ - SetupAFLUT(); - /*ChangeAFLUT();*/ - /* print the configured LUT*/ - PrintAFLUT(); -#if FULL_CAN_AF_USED - Chip_CAN_ConfigFullCANInt(LPC_CANAF, ENABLE); - Chip_CAN_SetAFMode(LPC_CANAF, CAN_AF_FULL_MODE); -#else - Chip_CAN_SetAFMode(LPC_CANAF, CAN_AF_NORMAL_MODE); -#endif /*FULL_CAN_AF_USED*/ - -#else - DEBUGOUT("\tCAN Rx Acceptance Filters NOT OPERATIONAL for the debug stages\r\n"); - Chip_CAN_SetAFMode(LPC_CANAF, CAN_AF_BYBASS_MODE); -#endif - } - else - { - /* CAN module filters are not used, all messages with standard 11-bit */ - /* identifier will be received */ - /* Configure mask 0 so, that all messages with standard identifier are accepted */ - Chip_CAN_SetAFMode(LPC_CANAF, CAN_AF_BYBASS_MODE); - - DEBUGOUT("\tCAN Rx Acceptance Filters Bypass \r\n"); - } - /* configure CAN interrupt registers */ - NVIC_EnableIRQ(CAN_IRQn); - - return CO_ERROR_NO; -} - - -/******************************************************************************/ -void CO_CANmodule_disable(CO_CANmodule_t *CANmodule){ - /* turn off the module */ - NVIC_DisableIRQ(CAN_IRQn); -} - - -/******************************************************************************/ -uint16_t CO_CANrxMsg_readIdent(const CO_CANrxMsg_t *rxMsg){ - return (uint16_t) rxMsg->ident; -} - -/******************************************************************************/ -uint16_t CO_CAN_Get_My_NodeID(void){ - - uint16_t myNodeId=0; - - myNodeId = Chip_GPIO_ReadPortBit(LPC_GPIO, CAN_NODE_ID_0_PORT, CAN_NODE_ID_0_PIN); - - return myNodeId; -} -/******************************************************************************/ -CO_ReturnError_t CO_CANrxBufferInit( - CO_CANmodule_t *CANmodule, - uint16_t index, - uint16_t ident, - uint16_t mask, - bool_t rtr, - void *object, - void (*pFunct)(void *object, const CO_CANrxMsg_t *message)) -{ - CO_ReturnError_t ret = CO_ERROR_NO; - - if((CANmodule!=NULL) && (object!=NULL) && (pFunct!=NULL) && (index < CANmodule->rxSize)){ - /* buffer, which will be configured */ - CO_CANrx_t *buffer = &CANmodule->rxArray[index]; - - /* Configure object variables */ - buffer->object = object; - buffer->pFunct = pFunct; - - /* CAN identifier and CAN mask, bit aligned with CAN module. Different on different microcontrollers. */ - buffer->ident = ident & 0x07FFU; - if(rtr){ - buffer->ident |= 0x0800U; - } - buffer->mask = (mask & 0x07FFU) | 0x0800U; - - } - else{ - ret = CO_ERROR_ILLEGAL_ARGUMENT; - } - - return ret; -} - - -/******************************************************************************/ -CO_CANtx_t *CO_CANtxBufferInit( - CO_CANmodule_t *CANmodule, - uint16_t index, - uint16_t ident, - bool_t rtr, - uint8_t noOfBytes, - bool_t syncFlag) -{ - CO_CANtx_t *buffer = NULL; - - if((CANmodule != NULL) && (index < CANmodule->txSize)){ - /* get specific buffer */ - buffer = &CANmodule->txArray[index]; - - /* CAN identifier, DLC and rtr, bit aligned with CAN module transmit buffer. - * Microcontroller specific. */ - buffer->ident = ((uint32_t)ident & 0x07FFU); - buffer->DLC = ((uint32_t)noOfBytes); - buffer->Type = ((uint32_t)(rtr ? (CAN_REMOTE_MSG) : 0U)); - - buffer->bufferFull = false; - buffer->syncFlag = syncFlag; - } - - return buffer; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_CANsend(CO_CANmodule_t *CANmodule, CO_CANtx_t *buffer){ - CO_ReturnError_t err = CO_ERROR_NO; - CAN_BUFFER_ID_T TxBuf; - - /* Verify overflow */ - if(buffer->bufferFull){ - if(!CANmodule->firstCANtxMessage){ - /* don't set error, if bootup message is still on buffers */ - CO_errorReport((CO_EM_t*)CANmodule->em, CO_EM_CAN_TX_OVERFLOW, CO_EMC_CAN_OVERRUN, buffer->ident); - } - err = CO_ERROR_TX_OVERFLOW; - } - - CO_LOCK_CAN_SEND(); - - /* if CAN TX buffer is free, copy message to it */ - TxBuf = Chip_CAN_GetFreeTxBuf(LPC_CAN); - if( TxBuf < CAN_BUFFER_LAST && CANmodule->CANtxCount == 0){ - CANmodule->bufferInhibitFlag = buffer->syncFlag; - /* copy message and txRequest */ - Chip_CAN_Send(LPC_CAN, TxBuf,(CAN_MSG_T*)buffer); - - /*DEBUGOUT("CO_CANsend!!!\r\n");*/ - /*PrintCANMsg((CAN_MSG_T*)buffer);*/ - } - /* if no buffer is free, message will be sent by interrupt */ - else - { - DEBUGOUT("CO_CANsend buffer Full!!!\r\n"); - buffer->bufferFull = true; - CANmodule->CANtxCount++; - } - CO_UNLOCK_CAN_SEND(); - - return err; -} - - -/******************************************************************************/ -void CO_CANclearPendingSyncPDOs(CO_CANmodule_t *CANmodule){ - uint32_t tpdoDeleted = 0U; - - CO_LOCK_CAN_SEND(); - - /* Abort message from CAN module, if there is synchronous TPDO. - * Take special care with this functionality. */ - /*Check if any of the buffer is pending transmition and bufferInhibitFlag is true*/ - if(( 0 == (Chip_CAN_GetGlobalStatus(LPC_CAN) & CAN_GSR_TBS)) && CANmodule->bufferInhibitFlag){ - - /* if not already in progress, a pending Transmission Request for - the selected Transmit Buffer is cancelled. */ - Chip_CAN_SetCmd(LPC_CAN, CAN_CMR_STB(CAN_BUFFER_1) | CAN_CMR_AT); - Chip_CAN_SetCmd(LPC_CAN, CAN_CMR_STB(CAN_BUFFER_2) | CAN_CMR_AT); - Chip_CAN_SetCmd(LPC_CAN, CAN_CMR_STB(CAN_BUFFER_3) | CAN_CMR_AT); - - /* clear TXREQ */ - CANmodule->bufferInhibitFlag = false; - tpdoDeleted = 1U; - } - /* delete also pending synchronous TPDOs in TX buffers */ - if(CANmodule->CANtxCount != 0U){ - uint16_t i; - CO_CANtx_t *buffer = &CANmodule->txArray[0]; - for(i = CANmodule->txSize; i > 0U; i--){ - if(buffer->bufferFull){ - if(buffer->syncFlag){ - buffer->bufferFull = false; - CANmodule->CANtxCount--; - tpdoDeleted = 2U; - } - } - buffer++; - } - } - CO_UNLOCK_CAN_SEND(); - - - if(tpdoDeleted != 0U){ - CO_errorReport((CO_EM_t*)CANmodule->em, CO_EM_TPDO_OUTSIDE_WINDOW, CO_EMC_COMMUNICATION, tpdoDeleted); - } -} - - -/******************************************************************************/ -void CO_CANverifyErrors(CO_CANmodule_t *CANmodule){ - uint16_t rxErrors, txErrors, overflow; - CO_EM_t* em = (CO_EM_t*)CANmodule->em; - uint32_t err; - - /* get error counters from module. Id possible, function may use different way to - * determine errors. */ - rxErrors = CAN_GSR_RXERR(Chip_CAN_GetGlobalStatus(LPC_CAN)); - txErrors = CAN_GSR_TXERR(Chip_CAN_GetGlobalStatus(LPC_CAN)); - overflow = (Chip_CAN_GetGlobalStatus(LPC_CAN) & CAN_GSR_DOS) ; /*CAN Data Overrun Status */ CANmodule->txSize; - - err = ((uint32_t)txErrors << 16) | ((uint32_t)rxErrors << 8) | overflow; - - if(CANmodule->errOld != err){ - CANmodule->errOld = err; - - if(txErrors >= 256U){ /* bus off */ - CO_errorReport(em, CO_EM_CAN_TX_BUS_OFF, CO_EMC_BUS_OFF_RECOVERED, err); - } - else{ /* not bus off */ - CO_errorReset(em, CO_EM_CAN_TX_BUS_OFF, err); - - if((rxErrors >= 96U) || (txErrors >= 96U)){ /* bus warning */ - CO_errorReport(em, CO_EM_CAN_BUS_WARNING, CO_EMC_NO_ERROR, err); - } - - if(rxErrors >= 128U){ /* RX bus passive */ - CO_errorReport(em, CO_EM_CAN_RX_BUS_PASSIVE, CO_EMC_CAN_PASSIVE, err); - } - else{ - CO_errorReset(em, CO_EM_CAN_RX_BUS_PASSIVE, err); - } - - if(txErrors >= 128U){ /* TX bus passive */ - if(!CANmodule->firstCANtxMessage){ - CO_errorReport(em, CO_EM_CAN_TX_BUS_PASSIVE, CO_EMC_CAN_PASSIVE, err); - } - } - else{ - bool_t isError = CO_isError(em, CO_EM_CAN_TX_BUS_PASSIVE); - if(isError){ - CO_errorReset(em, CO_EM_CAN_TX_BUS_PASSIVE, err); - CO_errorReset(em, CO_EM_CAN_TX_OVERFLOW, err); - } - } - - if((rxErrors < 96U) && (txErrors < 96U)){ /* no error */ - CO_errorReset(em, CO_EM_CAN_BUS_WARNING, err); - } - } - - if(overflow != 0U){ /* CAN RX bus overflow */ - CO_errorReport(em, CO_EM_CAN_RXB_OVERFLOW, CO_EMC_CAN_OVERRUN, err); - } - } -} - - -/******************************************************************************/ -void CO_CANinterrupt(CO_CANmodule_t *CANmodule){ - - uint32_t IntStatus; - CO_CANrxMsg_t RcvMsgBuf; - CO_CANrxMsg_t *rcvMsg; /* pointer to received message in CAN module */ - uint16_t index; /* index of received message */ - uint32_t rcvMsgIdent; /* identifier of the received message */ - CO_CANrx_t *buffer = NULL; /* receive message buffer from CO_CANmodule_t object. */ - bool_t msgMatched = false; - CAN_BUFFER_ID_T TxBuf; - - /*Read the int status register */ - IntStatus = Chip_CAN_GetIntStatus(LPC_CAN); - - - /* receive interrupt */ - if(IntStatus & CAN_ICR_RI){ - - /* get message from module */ - Chip_CAN_Receive(LPC_CAN,(CAN_MSG_T*)&RcvMsgBuf); - rcvMsg = &RcvMsgBuf; - rcvMsgIdent = rcvMsg->ident; - -#if AF_LUT_USED /*Implement once AF will be used*/ - if(CANmodule->useCANrxFilters){ - - /* Since the HW not providing the filter index, ignore this code and run through the rxArray to find a match.*/ - /* Search rxArray form CANmodule for the same CAN-ID. */ - buffer = &CANmodule->rxArray[0]; - for(index = CANmodule->rxSize; index > 0U; index--){ - if(((rcvMsgIdent ^ buffer->ident) & buffer->mask) == 0U){ - msgMatched = true; - break; - } - buffer++; - } - } - else -#endif - { - /* CAN module filters are not used, message with any standard 11-bit identifier */ - /* has been received. Search rxArray form CANmodule for the same CAN-ID. */ - buffer = &CANmodule->rxArray[0]; - for(index = CANmodule->rxSize; index > 0U; index--){ - if(((rcvMsgIdent ^ buffer->ident) & buffer->mask) == 0U){ - msgMatched = true; - break; - } - buffer++; - } - - } - - /* Call specific function, which will process the message */ - if(msgMatched && (buffer != NULL) && (buffer->pFunct != NULL)){ - - buffer->pFunct(buffer->object, rcvMsg); - } - else - { - DEBUGOUT("Unsupported Message Received!!!\r\n"); - PrintCANMsg((CAN_MSG_T*)rcvMsg); - } - - /* Clear interrupt flag */ - /*Giving the Command “Release Receive Buffer” will clear RI. done in Chip_CAN_Receive()*/ - } - /* transmit interrupt */ - else if((IntStatus & CAN_ICR_TI1)||(IntStatus & CAN_ICR_TI2)||(IntStatus & CAN_ICR_TI3)){ - /* Clear interrupt flag */ - - /* First CAN message (bootup) was sent successfully */ - CANmodule->firstCANtxMessage = false; - /* clear flag from previous message */ - CANmodule->bufferInhibitFlag = false; - /* Are there any new messages waiting to be send */ - if(CANmodule->CANtxCount > 0U){ - uint16_t i; /* index of transmitting message */ - - /* first buffer */ - CO_CANtx_t *buffer = &CANmodule->txArray[0]; - /* search through whole array of pointers to transmit message buffers. */ - for(i = CANmodule->txSize; i > 0U; i--){ - /* if message buffer is full, send it. */ - if(buffer->bufferFull){ - buffer->bufferFull = false; - CANmodule->CANtxCount--; - - /* Copy message to CAN buffer */ - CANmodule->bufferInhibitFlag = buffer->syncFlag; - /* canSend... */ - /* if CAN TX buffer is free, copy message to it */ - TxBuf = Chip_CAN_GetFreeTxBuf(LPC_CAN); - if( TxBuf < CAN_BUFFER_LAST){ - CANmodule->bufferInhibitFlag = buffer->syncFlag; - /* copy message and txRequest */ - Chip_CAN_Send(LPC_CAN, TxBuf,(CAN_MSG_T*)buffer); - } - break; /* exit for loop */ - } - buffer++; - }/* end of for loop */ - - /* Clear counter if no more messages */ - if(i == 0U){ - CANmodule->CANtxCount = 0U; - } - } - } - else{ - /* some other interrupt reason */ - } -} - -/***************************************************************************** - * Private functions - ****************************************************************************/ -/* Print error */ -static void PrintCANErrorInfo(uint32_t Status) -{ - if (Status & CAN_ICR_EI) { - DEBUGOUT("Error Warning!\r\n"); - } - if (Status & CAN_ICR_DOI) { - DEBUGOUT("Data Overrun!\r\n"); - } - if (Status & CAN_ICR_EPI) { - DEBUGOUT("Error Passive!\r\n"); - } - if (Status & CAN_ICR_ALI) { - DEBUGOUT("Arbitration lost in the bit: %d(th)\r\n", CAN_ICR_ALCBIT_VAL(Status)); - } - if (Status & CAN_ICR_BEI) { - - DEBUGOUT("CAN Bus error !!!\r\n"); - - if (Status & CAN_ICR_ERRDIR_RECEIVE) { - DEBUGOUT("\t Error Direction: Transmiting\r\n"); - } - else { - DEBUGOUT("\t Error Direction: Receiving\r\n"); - } - - DEBUGOUT("\t Error Location: 0x%2x\r\n", CAN_ICR_ERRBIT_VAL(Status)); - DEBUGOUT("\t Error Type: 0x%1x\r\n", CAN_ICR_ERRC_VAL(Status)); - } -} - -/* Print CAN Message */ -static void PrintCANMsg(CAN_MSG_T *pMsg) -{ - uint8_t i; - DEBUGOUT("\t**************************\r\n"); - DEBUGOUT("\tMessage Information: \r\n"); - DEBUGOUT("\tMessage Type: "); - if (pMsg->ID & CAN_EXTEND_ID_USAGE) { - DEBUGOUT(" Extend ID Message"); - } - else { - DEBUGOUT(" Standard ID Message"); - } - if (pMsg->Type & CAN_REMOTE_MSG) { - DEBUGOUT(", Remote Message"); - } - DEBUGOUT("\r\n"); - DEBUGOUT("\tMessage ID :0x%x\r\n", (pMsg->ID & (~CAN_EXTEND_ID_USAGE))); - DEBUGOUT("\tMessage Data :"); - for (i = 0; i < pMsg->DLC; i++) - DEBUGOUT("%x ", pMsg->Data[i]); - DEBUGOUT("\r\n\t**************************\r\n"); -} - -#if AF_LUT_USED -/* Print entries in AF LUT */ -static void PrintAFLUT(void) -{ - uint16_t i, num; - CAN_STD_ID_ENTRY_T StdEntry; - CAN_EXT_ID_ENTRY_T ExtEntry; - CAN_STD_ID_RANGE_ENTRY_T StdGrpEntry; - CAN_EXT_ID_RANGE_ENTRY_T ExtGrpEntry; - - DEBUGOUT("Print AF LUT... \r\n"); -#if FULL_CAN_AF_USED - /* Full CAN Table */ - DEBUGOUT("\tFULL CAN Table: \r\n"); - num = Chip_CAN_GetEntriesNum(LPC_CANAF, LPC_CANAF_RAM, CANAF_RAM_FULLCAN_SEC); - for (i = 0; i < num; i++) { - Chip_CAN_ReadFullCANEntry(LPC_CANAF, LPC_CANAF_RAM, i, &StdEntry); - DEBUGOUT("\t\t%d: Controller ID: %d, ID: 0x%x, Dis: %1d\r\n", - i, StdEntry.CtrlNo, StdEntry.ID_11, StdEntry.Disable); - } -#endif - /* Standard ID Table */ - DEBUGOUT("\tIndividual Standard ID Table: \r\n"); - num = Chip_CAN_GetEntriesNum(LPC_CANAF, LPC_CANAF_RAM, CANAF_RAM_SFF_SEC); - for (i = 0; i < num; i++) { - Chip_CAN_ReadSTDEntry(LPC_CANAF, LPC_CANAF_RAM, i, &StdEntry); - DEBUGOUT("\t\t%d: Controller ID: %d, ID: 0x%x, Dis: %1d\r\n", - i, StdEntry.CtrlNo, StdEntry.ID_11, StdEntry.Disable); - } - /* Group Standard ID Table */ - DEBUGOUT("\tGroup Standard ID Table: \r\n"); - num = Chip_CAN_GetEntriesNum(LPC_CANAF, LPC_CANAF_RAM, CANAF_RAM_SFF_GRP_SEC); - for (i = 0; i < num; i++) { - Chip_CAN_ReadGroupSTDEntry(LPC_CANAF, LPC_CANAF_RAM, i, &StdGrpEntry); - DEBUGOUT("\t\t%d: Controller ID: %d, ID: 0x%x-0x%x, Dis: %1d\r\n", - i, StdGrpEntry.LowerID.CtrlNo, StdGrpEntry.LowerID.ID_11, - StdGrpEntry.UpperID.ID_11, StdGrpEntry.LowerID.Disable); - } - /* Extended ID Table */ - DEBUGOUT("\tExtended ID Table: \r\n"); - num = Chip_CAN_GetEntriesNum(LPC_CANAF, LPC_CANAF_RAM, CANAF_RAM_EFF_SEC); - for (i = 0; i < num; i++) { - Chip_CAN_ReadEXTEntry(LPC_CANAF, LPC_CANAF_RAM, i, &ExtEntry); - DEBUGOUT("\t\t%d: Controller ID: %d, ID: 0x%x,\r\n", - i, ExtEntry.CtrlNo, ExtEntry.ID_29); - } - /* Group Extended ID Table */ - DEBUGOUT("\tGroup Extended ID Table: \r\n"); - num = Chip_CAN_GetEntriesNum(LPC_CANAF, LPC_CANAF_RAM, CANAF_RAM_EFF_GRP_SEC); - for (i = 0; i < num; i++) { - Chip_CAN_ReadGroupEXTEntry(LPC_CANAF, LPC_CANAF_RAM, i, &ExtGrpEntry); - DEBUGOUT("\t\t%d: Controller ID: %d, ID: 0x%x-0x%x\r\n", - i, ExtGrpEntry.LowerID.CtrlNo, ExtGrpEntry.LowerID.ID_29, - ExtGrpEntry.UpperID.ID_29); - } - -} - -/* Setup AF LUT */ -static void SetupAFLUT(void) -{ - DEBUGOUT("Setup AF LUT... \r\n"); - Chip_CAN_SetAFLUT(LPC_CANAF, LPC_CANAF_RAM, &AFSections); -/* PrintAFLUT();*/ -} - -/* Insert/Remove entries to/from AF LUT */ -static void ChangeAFLUT(void) -{ -#if FULL_CAN_AF_USED - CAN_STD_ID_ENTRY_T FullEntry = {CAN_CTRL_NO, 0, 0x02}; -#endif - CAN_STD_ID_ENTRY_T StdEntry = {CAN_CTRL_NO, 0, 0xC0}; - CAN_EXT_ID_ENTRY_T ExtEntry = {CAN_CTRL_NO, ((1 << 11) | 0x0A)}; - CAN_STD_ID_RANGE_ENTRY_T StdGrpEntry = {{CAN_CTRL_NO, 0, 0x7A0}, {CAN_CTRL_NO, 0, 0x7B0}}; - CAN_EXT_ID_RANGE_ENTRY_T ExtGrpEntry = {{CAN_CTRL_NO, ((1 << 11) | 0x7A0)}, {CAN_CTRL_NO, ((1 << 11) | 0x7B0)}}; - - -#if FULL_CAN_AF_USED - /* Edit Full CAN Table */ - Chip_CAN_InsertFullCANEntry(LPC_CANAF, LPC_CANAF_RAM, &FullEntry); - FullEntry.ID_11 = 2; - Chip_CAN_InsertFullCANEntry(LPC_CANAF, LPC_CANAF_RAM, &FullEntry); - FullEntry.ID_11 = 4; - Chip_CAN_InsertFullCANEntry(LPC_CANAF, LPC_CANAF_RAM, &FullEntry); -#endif /*FULL_CAN_AF_USED*/ - - /* Edit Individual STD ID Table */ - Chip_CAN_InsertSTDEntry(LPC_CANAF, LPC_CANAF_RAM, &StdEntry); - StdEntry.ID_11 = 0x20; - Chip_CAN_InsertSTDEntry(LPC_CANAF, LPC_CANAF_RAM, &StdEntry); - StdEntry.ID_11 = 0x40; - Chip_CAN_InsertSTDEntry(LPC_CANAF, LPC_CANAF_RAM, &StdEntry); - - /* Edit Individual EXT ID Table */ - Chip_CAN_InsertEXTEntry(LPC_CANAF, LPC_CANAF_RAM, &ExtEntry); - ExtEntry.ID_29 = (1 << 11) | 0x02; - Chip_CAN_InsertEXTEntry(LPC_CANAF, LPC_CANAF_RAM, &ExtEntry); - ExtEntry.ID_29 = (1 << 11) | 0x04; - Chip_CAN_InsertEXTEntry(LPC_CANAF, LPC_CANAF_RAM, &ExtEntry); - - /* Edit STD ID Group Table */ - Chip_CAN_InsertGroupSTDEntry(LPC_CANAF, LPC_CANAF_RAM, &StdGrpEntry); - StdGrpEntry.LowerID.ID_11 = 0x200; - StdGrpEntry.UpperID.ID_11 = 0x300; - Chip_CAN_InsertGroupSTDEntry(LPC_CANAF, LPC_CANAF_RAM, &StdGrpEntry); - StdGrpEntry.LowerID.ID_11 = 0x400; - StdGrpEntry.UpperID.ID_11 = 0x500; - Chip_CAN_InsertGroupSTDEntry(LPC_CANAF, LPC_CANAF_RAM, &StdGrpEntry); - - /* Edit EXT ID Group Table */ - Chip_CAN_InsertGroupEXTEntry(LPC_CANAF, LPC_CANAF_RAM, &ExtGrpEntry); - ExtGrpEntry.LowerID.ID_29 = (1 << 11) | 0x200; - ExtGrpEntry.UpperID.ID_29 = (1 << 11) | 0x300; - Chip_CAN_InsertGroupEXTEntry(LPC_CANAF, LPC_CANAF_RAM, &ExtGrpEntry); - ExtGrpEntry.LowerID.ID_29 = (1 << 11) | 0x400; - ExtGrpEntry.UpperID.ID_29 = (1 << 11) | 0x500; - Chip_CAN_InsertGroupEXTEntry(LPC_CANAF, LPC_CANAF_RAM, &ExtGrpEntry); - - PrintAFLUT(); -#if REMOVE_CAN_AF_ENTRIES - DEBUGOUT("Remove entries into the current LUT... \r\n"); - /* Remove entries from the current LUT */ -#if FULL_CAN_AF_USED - Chip_CAN_RemoveFullCANEntry(LPC_CANAF, LPC_CANAF_RAM, 0); - Chip_CAN_RemoveFullCANEntry(LPC_CANAF, LPC_CANAF_RAM, Chip_CAN_GetEntriesNum(LPC_CANAF, LPC_CANAF_RAM, CANAF_RAM_FULLCAN_SEC) - 1); - Chip_CAN_RemoveFullCANEntry(LPC_CANAF, LPC_CANAF_RAM, Chip_CAN_GetEntriesNum(LPC_CANAF, LPC_CANAF_RAM, CANAF_RAM_FULLCAN_SEC) / 2); -#endif - Chip_CAN_RemoveSTDEntry(LPC_CANAF, LPC_CANAF_RAM, 0); - Chip_CAN_RemoveSTDEntry(LPC_CANAF, LPC_CANAF_RAM, Chip_CAN_GetEntriesNum(LPC_CANAF, LPC_CANAF_RAM, CANAF_RAM_SFF_SEC) - 1); - Chip_CAN_RemoveSTDEntry(LPC_CANAF, LPC_CANAF_RAM, Chip_CAN_GetEntriesNum(LPC_CANAF, LPC_CANAF_RAM, CANAF_RAM_SFF_SEC) / 2); - Chip_CAN_RemoveGroupSTDEntry(LPC_CANAF, LPC_CANAF_RAM, 0); - Chip_CAN_RemoveGroupSTDEntry(LPC_CANAF, LPC_CANAF_RAM, Chip_CAN_GetEntriesNum(LPC_CANAF, LPC_CANAF_RAM, CANAF_RAM_SFF_GRP_SEC) - 1); - Chip_CAN_RemoveGroupSTDEntry(LPC_CANAF, LPC_CANAF_RAM, Chip_CAN_GetEntriesNum(LPC_CANAF, LPC_CANAF_RAM, CANAF_RAM_SFF_GRP_SEC) / 2); - Chip_CAN_RemoveEXTEntry(LPC_CANAF, LPC_CANAF_RAM, 0); - Chip_CAN_RemoveEXTEntry(LPC_CANAF, LPC_CANAF_RAM, Chip_CAN_GetEntriesNum(LPC_CANAF, LPC_CANAF_RAM, CANAF_RAM_EFF_SEC) - 1); - Chip_CAN_RemoveEXTEntry(LPC_CANAF, LPC_CANAF_RAM, Chip_CAN_GetEntriesNum(LPC_CANAF, LPC_CANAF_RAM, CANAF_RAM_EFF_SEC) / 2); - Chip_CAN_RemoveGroupEXTEntry(LPC_CANAF, LPC_CANAF_RAM, 0); - Chip_CAN_RemoveGroupEXTEntry(LPC_CANAF, LPC_CANAF_RAM, Chip_CAN_GetEntriesNum(LPC_CANAF, LPC_CANAF_RAM, CANAF_RAM_EFF_GRP_SEC) - 1); - Chip_CAN_RemoveGroupEXTEntry(LPC_CANAF, LPC_CANAF_RAM, Chip_CAN_GetEntriesNum(LPC_CANAF, LPC_CANAF_RAM, CANAF_RAM_EFF_GRP_SEC) / 2); - PrintAFLUT(); -#endif -} - -#endif diff --git a/stack/LPC177x_8x/CO_driver_target.h b/stack/LPC177x_8x/CO_driver_target.h deleted file mode 100644 index fbd628d8..00000000 --- a/stack/LPC177x_8x/CO_driver_target.h +++ /dev/null @@ -1,154 +0,0 @@ -/* - * CAN module object for NXP LPC177x (Cortex M3) and FreeRTOS. - * - * This file is a template for other microcontrollers. - * - * @file CO_driver_target.h - * @author Janez Paternoster - * @author Amit H - * @copyright 2004 - 2014 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * CANopenNode is free and open source software: you can redistribute - * it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Following clarification and special exception to the GNU General Public - * License is included to the distribution terms of CANopenNode: - * - * Linking this library statically or dynamically with other modules is - * making a combined work based on this library. Thus, the terms and - * conditions of the GNU General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this library give - * you permission to link this library with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also meet, - * for each linked independent module, the terms and conditions of the - * license of that module. An independent module is a module which is - * not derived from or based on this library. If you modify this - * library, you may extend this exception to your version of the - * library, but you are not obliged to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - - -#ifndef CO_DRIVER_TARGET_H -#define CO_DRIVER_TARGET_H - - -#include "FreeRTOS.h" -#include "task.h" -#include "board.h" -#include /* for 'NULL' */ -#include /* for 'int8_t' to 'uint64_t' */ - - -/* Endianness */ -#define CO_LITTLE_ENDIAN - -/* CAN module base address */ -#define ADDR_CAN1 0 -#define ADDR_CAN2 1 - - -#define CAN_NODE_ID_0_PORT 1 -#define CAN_NODE_ID_0_PIN 23 -#define CAN_NODE_ID_1_PORT 1 -#define CAN_NODE_ID_1_PIN 24 -#define CAN_NODE_ID_2_PORT 1 -#define CAN_NODE_ID_2_PIN 25 -#define CAN_NODE_ID_3_PORT 1 -#define CAN_NODE_ID_3_PIN 26 -#define CAN_NODE_ID_4_PORT 1 -#define CAN_NODE_ID_4_PIN 28 - -#define CAN_RUN_LED_PORT 1 -#define CAN_RUN_LED_PIN 20 - - -/* Critical sections */ -#define CO_LOCK_CAN_SEND() taskENTER_CRITICAL() -#define CO_UNLOCK_CAN_SEND() taskEXIT_CRITICAL() - -#define CO_LOCK_EMCY() taskENTER_CRITICAL() -#define CO_UNLOCK_EMCY() taskEXIT_CRITICAL() - -#define CO_LOCK_OD() taskENTER_CRITICAL() -#define CO_UNLOCK_OD() taskEXIT_CRITICAL() - - -/* Data types */ -/* int8_t to uint64_t are defined in stdint.h */ -typedef unsigned char bool_t; -typedef float float32_t; -typedef long double float64_t; -typedef char char_t; -typedef unsigned char oChar_t; -typedef unsigned char domain_t; - - -/* CAN receive message structure as aligned in CAN module. */ -typedef struct{ - /** CAN identifier. It must be read through CO_CANrxMsg_readIdent() function. */ - uint32_t ident; /*!< Message Identifier. If 30th-bit is set, this is 29-bit ID, othewise 11-bit ID */ - uint32_t Type; /*!< Message Type. which can include: - CAN_REMOTE_MSG type*/ - uint32_t DLC; /*!< Message Data Length: 0~8 */ - uint8_t data[CAN_MSG_MAX_DATA_LEN];/*!< Message Data */ -}CO_CANrxMsg_t; - - -/* Received message object */ -typedef struct{ - uint16_t ident; - uint16_t mask; - void *object; - void (*pFunct)(void *object, const CO_CANrxMsg_t *message); -}CO_CANrx_t; - - -/* Transmit message object. */ -typedef struct{ - uint32_t ident; /*!< Message Identifier. If 30th-bit is set, this is 29-bit ID, othewise 11-bit ID */ - uint32_t Type; /*!< Message Type. which can include: - CAN_REMOTE_MSG type*/ - uint32_t DLC; /*!< Message Data Length: 0~8 */ - uint8_t data[CAN_MSG_MAX_DATA_LEN];/*!< Message Data */ - volatile bool_t bufferFull; - volatile bool_t syncFlag; -}CO_CANtx_t; - - -/* CAN module object. */ -typedef struct{ - void *CANdriverState; - CO_CANrx_t *rxArray; - uint16_t rxSize; - CO_CANtx_t *txArray; - uint16_t txSize; - volatile bool_t CANnormal; - volatile bool_t useCANrxFilters; - volatile bool_t bufferInhibitFlag; - volatile bool_t firstCANtxMessage; - volatile uint16_t CANtxCount; - uint32_t errOld; - void *em; -}CO_CANmodule_t; - -/* CAN interrupt receives and transmits CAN messages. */ -void CO_CANinterrupt(CO_CANmodule_t *CANmodule); - - -#endif /* CO_DRIVER_TARGET_H */ diff --git a/stack/LPC177x_8x/application.c b/stack/LPC177x_8x/application.c deleted file mode 100644 index f07a9335..00000000 --- a/stack/LPC177x_8x/application.c +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Header for application interface for CANopenNode stack. - * - * @file application.c - * @ingroup application - * @author Janez Paternoster - * @copyright 2012 - 2013 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * CANopenNode is free and open source software: you can redistribute - * it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Following clarification and special exception to the GNU General Public - * License is included to the distribution terms of CANopenNode: - * - * Linking this library statically or dynamically with other modules is - * making a combined work based on this library. Thus, the terms and - * conditions of the GNU General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this library give - * you permission to link this library with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also meet, - * for each linked independent module, the terms and conditions of the - * license of that module. An independent module is a module which is - * not derived from or based on this library. If you modify this - * library, you may extend this exception to your version of the - * library, but you are not obliged to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ -/*System includes------------------------------------------------------------*/ -#include -#include -#include -#include -#include -#include -#include -/*Board includes-------------------------------------------------------------*/ -#include "board.h" - -/*App includes---------------------------------------------------------------*/ -#include "COMPSW.H" -#include "CANopen.h" - - -#define CAN_RUN_LED_ON() Chip_GPIO_WritePortBit(LPC_GPIO, CAN_RUN_LED_PORT, CAN_RUN_LED_PIN,false ) -#define CAN_RUN_LED_OFF() Chip_GPIO_WritePortBit(LPC_GPIO, CAN_RUN_LED_PORT, CAN_RUN_LED_PIN,true) -// No HW allocation for Error LED -#define CAN_ERROR_LED_ON() -#define CAN_ERROR_LED_OFF() - - -/*******************************************************************************/ -void programStart(void){ - - // turn OFF the LEDs - CAN_RUN_LED_OFF(); - CAN_ERROR_LED_OFF(); -} - - -/*******************************************************************************/ -void communicationReset(void){ - - // turn OFF the LEDs - CAN_RUN_LED_OFF(); - CAN_ERROR_LED_OFF(); -} - - -/*******************************************************************************/ -void programEnd(void){ - - // turn OFF the LEDs - CAN_RUN_LED_OFF(); - CAN_ERROR_LED_OFF(); -} - - -/*******************************************************************************/ -void programAsync(uint16_t timer1msDiff){ - - if(LED_GREEN_RUN(CO->NMT))CAN_RUN_LED_ON(); else CAN_RUN_LED_OFF(); - if(LED_RED_ERROR(CO->NMT))CAN_ERROR_LED_ON(); else CAN_ERROR_LED_OFF(); -} - - -/*******************************************************************************/ -void program1ms(void){ - -} diff --git a/stack/LPC177x_8x/application.h b/stack/LPC177x_8x/application.h deleted file mode 100644 index a8816e98..00000000 --- a/stack/LPC177x_8x/application.h +++ /dev/null @@ -1,173 +0,0 @@ -/** - * Application interface for CANopenNode stack. - * - * @file application.h - * @ingroup CO_application - * @author Janez Paternoster - * @copyright 2012 - 2013 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * CANopenNode is free and open source software: you can redistribute - * it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Following clarification and special exception to the GNU General Public - * License is included to the distribution terms of CANopenNode: - * - * Linking this library statically or dynamically with other modules is - * making a combined work based on this library. Thus, the terms and - * conditions of the GNU General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this library give - * you permission to link this library with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also meet, - * for each linked independent module, the terms and conditions of the - * license of that module. An independent module is a module which is - * not derived from or based on this library. If you modify this - * library, you may extend this exception to your version of the - * library, but you are not obliged to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - - -#ifndef CO_APPLICATION_H -#define CO_APPLICATION_H - - -/** - * @defgroup CO_application Application interface - * @ingroup CO_CANopen - * @{ - * - * Application interface for CANopenNode stack. Function is called - * from file main_xxx.c (if implemented). - * - * ###Main program flow chart - * - * @code - (Program Start) - | - V - +------------------------------------+ - | programStart() | - +------------------------------------+ - | - |<-------------------------+ - | | - V | - (Initialze CANopen) | - | | - V | - +------------------------------------+ | - | communicationReset() | | - +------------------------------------+ | - | | - V | - (Enable CAN and interrupts) | - | | - |<----------------------+ | - | | | - V | | - +------------------------------------+ | | - | programAsync() | | | - +------------------------------------+ | | - | | | - V | | - (Process CANopen asynchronous) | | - | | | - +- infinite loop -------+ | - | | - +- reset communication ----+ - | - V - +------------------------------------+ - | programEnd() | - +------------------------------------+ - | - V - (delete CANopen) - | - V - (Program end) - @endcode - * - * - * ###Timer program flow chart - * - * @code - (Timer interrupt 1 millisecond) - | - V - (CANopen read RPDOs) - | - V - +------------------------------------+ - | program1ms() | - +------------------------------------+ - | - V - (CANopen write TPDOs) - @endcode - * - * - * ###Receive and transmit high priority interrupt flow chart - * - * @code - (CAN receive event or) - (CAN transmit buffer empty event) - | - V - (Process received CAN message or) - (copy next message to CAN transmit buffer) - @endcode - */ - - -/** - * Called after microcontroller reset. - */ -void programStart(void); - - -/** - * Called after communication reset. - */ -void communicationReset(void); - - -/** - * Called before program end. - */ -void programEnd(void); - - -/** - * Called cyclically from main. - * - * @param timer1msDiff Time difference since last call - */ -void programAsync(uint16_t timer1msDiff); - - -/** - * Called cyclically from 1ms timer task. - */ -void program1ms(void); - - -/** @} */ -#endif diff --git a/stack/LPC177x_8x/readme b/stack/LPC177x_8x/readme deleted file mode 100644 index 7cf38f43..00000000 --- a/stack/LPC177x_8x/readme +++ /dev/null @@ -1,4 +0,0 @@ -NXP LPC177x (Cortex M3) and FreeRTOS. - -Contributed by AmitH(sourceforge) (17.9.2014): -http://sourceforge.net/p/canopennode/discussion/387151/thread/d1b43992 diff --git a/stack/MCF5282/CO_driver.c b/stack/MCF5282/CO_driver.c deleted file mode 100644 index 158afec8..00000000 --- a/stack/MCF5282/CO_driver.c +++ /dev/null @@ -1,491 +0,0 @@ -/* - * CAN module object for Freescale MCF5282 ColdFire V2 microcontroller. - * - * @file CO_driver.c - * @author Janez Paternoster - * @author Laurent Grosbois - * @copyright 2004 - 2012 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * CANopenNode is free and open source software: you can redistribute - * it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Following clarification and special exception to the GNU General Public - * License is included to the distribution terms of CANopenNode: - * - * Linking this library statically or dynamically with other modules is - * making a combined work based on this library. Thus, the terms and - * conditions of the GNU General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this library give - * you permission to link this library with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also meet, - * for each linked independent module, the terms and conditions of the - * license of that module. An independent module is a module which is - * not derived from or based on this library. If you modify this - * library, you may extend this exception to your version of the - * library, but you are not obliged to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - - -#include "CO_driver.h" -#include "CO_Emergency.h" - - -extern const CO_CANbitRateData_t CO_CANbitRateData[8]; - - -/******************************************************************************/ -void CO_CANsetConfigurationMode(void *CANdriverState){ - - /* sets the module as running */ - MCF_FlexCAN_CANMCR &= ~MCF_FlexCAN_CANMCR_STOP; - - /* enter debug mode */ - MCF_FlexCAN_CANMCR |= MCF_FlexCAN_CANMCR_FRZ | MCF_FlexCAN_CANMCR_HALT; - - /* wait for entering in the mode */ - while(!(MCF_FlexCAN_CANMCR&MCF_FlexCAN_CANMCR_FRZACK)){}; -} - - -/******************************************************************************/ -void CO_CANsetNormalMode(CO_CANmodule_t *CANmodule){ - /* sets the module as running & exit debug mode */ - MCF_FlexCAN_CANMCR &= ~MCF_FlexCAN_CANMCR_STOP & ~MCF_FlexCAN_CANMCR_FRZ & ~MCF_FlexCAN_CANMCR_HALT; - - /* wait for entering in the mode */ - while(!(MCF_FlexCAN_CANMCR&&MCF_FlexCAN_CANMCR_NOTRDY)){}; - - CANmodule->CANnormal = true; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_CANmodule_init( - CO_CANmodule_t *CANmodule, - void *CANdriverState, - CO_CANrx_t rxArray[], - uint16_t rxSize, - CO_CANtx_t txArray[], - uint16_t txSize, - uint16_t CANbitRate) -{ - uint16_t i; - uint8_t nb_CANbuff = 16; //16 CAN buffers - - /* verify arguments */ - if(CANmodule==NULL || rxArray==NULL || txArray==NULL){ - return CO_ERROR_ILLEGAL_ARGUMENT; - } - - /* Configure object variables */ - CANmodule->CANdriverState = CANdriverState; - CANmodule->CANmsgBuffSize = nb_CANbuff; - CANmodule->rxArray = rxArray; - CANmodule->rxSize = rxSize; - CANmodule->txArray = txArray; - CANmodule->txSize = txSize; - CANmodule->CANnormal = false; - CANmodule->useCANrxFilters = false; //no filters or ((rxSize <= xx) ? true : false;) - CANmodule->bufferInhibitFlag = false; - CANmodule->firstCANtxMessage = true; - CANmodule->CANtxCount = 0U; - CANmodule->errOld = 0U; - CANmodule->em = NULL; - - for(i=0U; iuseCANrxFilters){ - //CAN module filters are used, they will be configured with - //CO_CANrxBufferInit() functions, called by separate CANopen - //init functions. - //Configure all masks so, that received message must match filter - MCF_FlexCAN_RXGMASK = 0xFFE80000; - MCF_FlexCAN_RX14MASK = 0xFFE80000; - MCF_FlexCAN_RX15MASK = 0xFFE80000; - } - else{ - //CAN module filters are not used, all messages with standard 11-bit - //identifier will be received - //Configure all mask so, that all messages with standard identifier are accepted - MCF_FlexCAN_RXGMASK = 0x00080000; - MCF_FlexCAN_RX14MASK = 0x00080000; - MCF_FlexCAN_RX15MASK = 0x00080000; - }*/ - - - /* CAN Module configuration register */ - MCF_FlexCAN_CANMCR &= ~MCF_FlexCAN_CANMCR_STOP & ~MCF_FlexCAN_CANMCR_FRZ & ~MCF_FlexCAN_CANMCR_HALT; - - - /* configure buffers 0-13 as input buffers */ - for(i=0;i<14;i++) - { - MCF_CANMB_CTRL(i) = MCF_CANMB_CTRL_CODE(0b0100); - } - - /* configure buffers 14-15 as output buffers */ - MCF_CANMB_CTRL14 = MCF_CANMB_CTRL_CODE(0b1000); - MCF_CANMB_CTRL15 = MCF_CANMB_CTRL_CODE(0b1000); - - - /* CAN interrupt registers */ - //Enable all buffer interrupts (can be either Rx or Tx interrupt depending on buffer configuration) - MCF_FlexCAN_IMASK = 0xFF; - MCF_FlexCAN_IFLAG = 0xFF; - - return CO_ERROR_NO; -} - - -/******************************************************************************/ -void CO_CANmodule_disable(CO_CANmodule_t *CANmodule){ - CO_CANsetConfigurationMode(CANmodule->CANdriverState); -} - - -/******************************************************************************/ -uint16_t CO_CANrxMsg_readIdent(const CO_CANrxMsg_t *rxMsg){ - return (uint16_t) (rxMsg->sid>>5); -} - - -/******************************************************************************/ -CO_ReturnError_t CO_CANrxBufferInit( - CO_CANmodule_t *CANmodule, - uint16_t index, - uint16_t ident, - uint16_t mask, - bool_t rtr, - void *object, - void (*pFunct)(void *object, const CO_CANrxMsg_t *message)) -{ - CO_ReturnError_t ret = CO_ERROR_NO; - - if((CANmodule!=NULL) && (object!=NULL) && (pFunct!=NULL) && (index < CANmodule->rxSize)){ - /* buffer, which will be configured */ - CO_CANrx_t *buffer = &CANmodule->rxArray[index]; - - /* Configure object variables */ - buffer->object = object; - buffer->pFunct = pFunct; - - /* CAN identifier and CAN mask, bit aligned with CAN module */ - buffer->ident = (uint16_t) ((ident & 0x07FF)<<5); - if(rtr){ - buffer->ident |= 0x0010; - } - buffer->mask = (uint16_t) (((mask & 0x07FF)<<5) | 0x0080); - - /* Set CAN hardware module filter and mask. */ - if(CANmodule->useCANrxFilters){ - //filters are not used. - } - } - else{ - ret = CO_ERROR_ILLEGAL_ARGUMENT; - } - - return ret; -} - - -/******************************************************************************/ -CO_CANtx_t *CO_CANtxBufferInit( - CO_CANmodule_t *CANmodule, - uint16_t index, - uint16_t ident, - bool_t rtr, - uint8_t noOfBytes, - bool_t syncFlag) -{ - CO_CANtx_t *buffer = NULL; - - if((CANmodule != NULL) && (index < CANmodule->txSize)){ - /* get specific buffer */ - buffer = &CANmodule->txArray[index]; - - /* CAN identifier, DLC and rtr, bit aligned with CAN module transmit buffer */ - buffer->ident = (uint16_t) ((ident & 0x07FF)<<5); - if(rtr) buffer->ident |= 0x0010; - - buffer->DLC = noOfBytes; - buffer->bufferFull = false; - buffer->syncFlag = syncFlag; - } - - return buffer; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_CANsend(CO_CANmodule_t *CANmodule, CO_CANtx_t *buffer){ - CO_ReturnError_t err = CO_ERROR_NO; - uint8_t i; - - /* Verify overflow */ - if(buffer->bufferFull){ - if(!CANmodule->firstCANtxMessage){ - /* don't set error, if bootup message is still on buffers */ - CO_errorReport((CO_EM_t*)CANmodule->em, CO_EM_CAN_TX_OVERFLOW, CO_EMC_CAN_OVERRUN, buffer->CMSGSID); - } - err = CO_ERROR_TX_OVERFLOW; - } - - /* Try to find a free sending buffer */ - /* Here, we use buffer 14 & 15 for Tx */ - i=14; //initial sending buffer index - while( (MCF_CANMB_CTRL(i) != MCF_CANMB_CTRL_CODE(0b1000))&& - (MCF_CANMB_CTRL(i) != MCF_CANMB_CTRL_CODE(0b0100))&& - (MCF_CANMB_CTRL(i) != MCF_CANMB_CTRL_CODE(0b1010))&& - i<16)//end sending buffer index+1 - { - i++; - } - - CO_LOCK_CAN_SEND(); - /* if CAN TX buffer is free, copy message to it */ - if(i<16){ - MCF_CANMB_CTRL(i) = 0x0000|MCF_CANMB_CTRL_CODE(0b1000); //Tx MB inactive - MCF_CANMB_SID(i) = buffer->ident; - MCF_CANMB_DATA_WORD_1(i) = (uint16_t) (((buffer->data[0])<<8) | (buffer->data[1])); - MCF_CANMB_DATA_WORD_2(i) = (uint16_t) (((buffer->data[2])<<8) | (buffer->data[3])); - MCF_CANMB_DATA_WORD_3(i) = (uint16_t) (((buffer->data[4])<<8) | (buffer->data[5])); - MCF_CANMB_DATA_WORD_4(i) = (uint16_t) (((buffer->data[6])<<8) | (buffer->data[7])); - MCF_CANMB_CTRL(i) = (uint8_t) (MCF_CANMB_CTRL_CODE(0b1000) | MCF_CANMB_CTRL_LENGTH(buffer->DLC) ); //Tx MB active - } - /* if no buffer is free, message will be sent by interrupt */ - else{ - buffer->bufferFull = true; - CANmodule->CANtxCount++; - } - CO_UNLOCK_CAN_SEND(); - - return err; -} - - -/******************************************************************************/ -void CO_CANclearPendingSyncPDOs(CO_CANmodule_t *CANmodule){ - - CO_LOCK_CAN_SEND(); - if(CANmodule->bufferInhibitFlag){ - MCF_CANMB_CTRL14 = MCF_CANMB_CTRL_CODE(0b1000); //clear TXREQ - MCF_CANMB_CTRL15 = MCF_CANMB_CTRL_CODE(0b1000); //clear TXREQ - CO_UNLOCK_CAN_SEND(); - CO_errorReport((CO_emergencyReport_t*)CANmodule->em, ERROR_TPDO_OUTSIDE_WINDOW, 0); - } - else{ - CO_UNLOCK_CAN_SEND(); - } -} - - -/******************************************************************************/ -void CO_CANverifyErrors(CO_CANmodule_t *CANmodule){ - unsigned rxErrors, txErrors, ESTAT; - uint32_t err; - CO_emergencyReport_t* em = (CO_emergencyReport_t*)CANmodule->em; - - rxErrors = MCF_FlexCAN_RXECTR; - txErrors = MCF_FlexCAN_TXECTR; - ESTAT = MCF_FlexCAN_ESTAT & 0xFF00; - - if(txErrors > 0xFFFF) txErrors = 0xFFFF; - if(rxErrors > 0xFF) rxErrors = 0xFF; - - err = (uint32_t) (txErrors << 16 | rxErrors << 8 | ESTAT>>8); - - if(CANmodule->errOld != err){ - CANmodule->errOld = err; - - if(txErrors >= 256){ //bus off - CO_errorReport(em, ERROR_CAN_TX_BUS_OFF, err); - } - else{ //not bus off - CO_errorReset(em, ERROR_CAN_TX_BUS_OFF, err); - - if(rxErrors >= 96 || txErrors >= 96){ //bus warning - CO_errorReport(em, ERROR_CAN_BUS_WARNING, err); - } - - if(rxErrors >= 128){ //RX bus passive - CO_errorReport(em, ERROR_CAN_RX_BUS_PASSIVE, err); - } - else{ - CO_errorReset(em, ERROR_CAN_RX_BUS_PASSIVE, err); - } - - if(txErrors >= 128){ //TX bus passive - if(!CANmodule->firstCANtxMessage){ - CO_errorReport(em, ERROR_CAN_TX_BUS_PASSIVE, err); - } - } - else{ - int16_t wasCleared; - wasCleared = CO_errorReset(em, ERROR_CAN_TX_BUS_PASSIVE, err); - if(wasCleared == 1) CO_errorReset(em, ERROR_CAN_TX_OVERFLOW, err); - } - - if(rxErrors < 96 && txErrors < 96){ //no error - int16_t wasCleared; - wasCleared = CO_errorReset(em, ERROR_CAN_BUS_WARNING, err); - if(wasCleared == 1) CO_errorReset(em, ERROR_CAN_TX_OVERFLOW, err); - } - } - - if(ESTAT&MCF_FlexCAN_ESTAT_TXWARN && ESTAT&MCF_FlexCAN_ESTAT_RXWARN){//bus warning - CO_errorReport(em, ERROR_CAN_BUS_WARNING, err); - } - else{ - CO_errorReset(em, ERROR_CAN_BUS_WARNING, err); - } - } -} - - -/******************************************************************************/ -void CO_CANinterrupt(CO_CANmodule_t *CANmodule, uint16_t ICODE){ - - /* receive interrupt (New CAN message is available in one of the Rx buffers) */ - if(ICODE <= 13){ - CO_CANrxMsg_t *rcvMsg; /* pointer to received message in CAN module */ - uint16_t index; /* index of received message */ - uint16_t rcvMsgIdent; /* identifier of the received message */ - CO_CANrx_t *buffer = NULL; /* receive message buffer from CO_CANmodule_t object. */ - bool_t msgMatched = false; - - rcvMsg = &MCF_CANMB_MSG(ICODE); //structures are aligned - rcvMsgIdent = (uint16_t) rcvMsg->sid; - if(rcvMsg->RTR) rcvMsgIdent |= 0x0800; - - /* CAN module filters are not used, message with any standard 11-bit identifier */ - /* has been received. Search rxArray form CANmodule for the same CAN-ID. */ - buffer = &CANmodule->rxArray[0]; - for(index = CANmodule->rxSize; index > 0U; index--){ - if(((rcvMsgIdent ^ buffer->ident) & buffer->mask) == 0U){ - msgMatched = true; - break; - } - buffer++; - } - - /* Call specific function, which will process the message */ - if(msgMatched && (buffer != NULL) && (buffer->pFunct != NULL)){ - buffer->pFunct(buffer->object, rcvMsg); - } - - } - - - /* transmit interrupt (TX buffer 14 or 15 has finished sending) */ - else if(ICODE >14 ){ - /* First CAN message (bootup) was sent successfully */ - CANmodule->firstCANtxMessage = false; - /* clear flag from previous message */ - CANmodule->bufferInhibitFlag = false; - /* Are there any new messages waiting to be send */ - if(CANmodule->CANtxCount > 0U){ - uint16_t i; /* index of transmitting message */ - CO_CANtx_t *buffer; /* Tx buffer */ - - /* first buffer */ - buffer = &CANmodule->txArray[0]; - /* search through whole array of pointers to transmit message buffers. */ - for(i = CANmodule->txSize; i > 0U; i--){ - /* if message buffer is full, send it. */ - if(buffer->bufferFull){ - buffer->bufferFull = false; - CANmodule->CANtxCount--; - - /* Copy message to CAN buffer */ - CANmodule->bufferInhibitFlag = buffer->syncFlag; - MCF_CANMB_CTRL(ICODE) = 0x0000|MCF_CANMB_CTRL_CODE(0b1000); //Tx MB inactive - MCF_CANMB_SID(ICODE) = buffer->ident; - MCF_CANMB_DATA_WORD_1(ICODE) = (uint16_t) (((buffer->data[0])<<8) | (buffer->data[1])); - MCF_CANMB_DATA_WORD_2(ICODE) = (uint16_t) (((buffer->data[2])<<8) | (buffer->data[3])); - MCF_CANMB_DATA_WORD_3(ICODE) = (uint16_t) (((buffer->data[4])<<8) | (buffer->data[5])); - MCF_CANMB_DATA_WORD_4(ICODE) = (uint16_t) (((buffer->data[6])<<8) | (buffer->data[7])); - MCF_CANMB_CTRL(ICODE) = (uint8_t) (MCF_CANMB_CTRL_CODE(0b1000) | MCF_CANMB_CTRL_LENGTH(buffer->DLC) ); //Tx MB active - break; /* exit for loop */ - } - buffer++; - }/* end of for loop */ - - /* Clear counter if no more messages */ - if(i == 0U){ - CANmodule->CANtxCount = 0U; - } - } - - } -} diff --git a/stack/MCF5282/CO_driver_target.h b/stack/MCF5282/CO_driver_target.h deleted file mode 100644 index 8a4aedff..00000000 --- a/stack/MCF5282/CO_driver_target.h +++ /dev/null @@ -1,363 +0,0 @@ -/* - * CAN module object for Freescale MCF5282 ColdFire V2 microcontroller. - * - * @file CO_driver_target.h - * @author Janez Paternoster - * @author Laurent Grosbois - * @copyright 2004 - 2014 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * CANopenNode is free and open source software: you can redistribute - * it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Following clarification and special exception to the GNU General Public - * License is included to the distribution terms of CANopenNode: - * - * Linking this library statically or dynamically with other modules is - * making a combined work based on this library. Thus, the terms and - * conditions of the GNU General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this library give - * you permission to link this library with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also meet, - * for each linked independent module, the terms and conditions of the - * license of that module. An independent module is a module which is - * not derived from or based on this library. If you modify this - * library, you may extend this exception to your version of the - * library, but you are not obliged to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - - -#ifndef CO_DRIVER_TARGET_H -#define CO_DRIVER_TARGET_H - - -#include "mcf5282.h" /* processor header file */ -#include /* for 'NULL' */ -#include /* for 'int8_t' to 'uint64_t' */ - - -/* Endianness */ -#define CO_LITTLE_ENDIAN - -/* CAN module base address */ -#define ADDR_CAN1 0 -#define ADDR_CAN2 (_CAN2_BASE_ADDRESS - _CAN1_BASE_ADDRESS) - - -/* Critical sections */ -#define CO_LOCK_CAN_SEND() asm{ move.w #0x2700,sr}; -#define CO_UNLOCK_CAN_SEND() asm{ move.w #0x2000,sr}; - -#define CO_LOCK_EMCY() asm{ move.w #0x2700,sr}; -#define CO_UNLOCK_EMCY() asm{ move.w #0x2000,sr}; - -#define CO_LOCK_OD() asm{ move.w #0x2700,sr}; -#define CO_UNLOCK_OD() asm{ move.w #0x2000,sr}; - - -/* MACRO : get information from Rx buffer */ -#define MCF_CANMB_MSG(x) (*(CO_CANrxMsg_t *)(&__IPSBAR[0x1C0080 + ((x)*0x10)])) - - -/* Data types */ -/* int8_t to uint64_t are defined in stdint.h */ -typedef unsigned char bool_t; -typedef float float32_t; -typedef long double float64_t; -typedef char char_t; -typedef unsigned char oChar_t; -typedef unsigned char domain_t; - - -/* CAN bit rates - * - * CAN bit rates are initializers for array of eight CO_CANbitRateData_t - * objects. - * - * Macros are not used by driver itself, they may be used by application with - * combination with object CO_CANbitRateData_t. - * Application must declare following global variable depending on CO_FSYS used: - * const CO_CANbitRateData_t CO_CANbitRateData[8] = {CO_CANbitRateDataInitializers}; - * - * There are initializers for eight objects, which corresponds to following - * CAN bit rates (in kbps): 10, 20, 50, 125, 250, 500, 800, 1000. - * - * CO_FSYS is internal instruction cycle clock frequency in kHz units. See - * PIC32MX documentation for more information on FSYS. - * - * Available values for FSYS: - * kbps = | 10 | 20 | 50 | 125 | 250 | 500 | 800 | 1000 - * -------+----+----+----+-----+-----+-----+-----+----- - * 4 Mhz | O | O | O | O | p | - | - | - - * 8 Mhz | O | O | O | O | O | p | - | - - * 12 Mhz | O | O | O | O | p | p | - | - - * 16 Mhz | O | O | O | O | O | O | p | p - * 20 Mhz | O | O | O | O | O | O | - | p - * 24 Mhz | O | O | O | O | O | p | O | p - * 32 Mhz | p | O | O | O | O | O | p | O - * 36 Mhz | - | O | O | O | O | O | - | O - * 40 Mhz | - | O | O | O | O | O | p | O - * 48 Mhz | - | O | O | O | O | O | O | p - * 56 Mhz | - | p | O | O | O | p | (p) | p - * 64 Mhz | - | p | O | O | O | O | O | O - * 72 Mhz | - | - | O | O | O | O | O | O - * 80 Mhz | - | - | O | O | O | O | p | O - * ---------------------------------------------------- - * (O=optimal; p=possible; -=not possible) - */ -#ifdef CO_FSYS - /* Macros, which divides K into (SJW + PROP + PhSeg1 + PhSeg2) */ - #define TQ_x_7 1, 2, 3, 1 - #define TQ_x_8 1, 2, 3, 2 - #define TQ_x_9 1, 2, 4, 2 - #define TQ_x_10 1, 3, 4, 2 - #define TQ_x_12 1, 3, 6, 2 - #define TQ_x_14 1, 4, 7, 2 - #define TQ_x_15 1, 4, 8, 2 /* good timing */ - #define TQ_x_16 1, 5, 8, 2 /* good timing */ - #define TQ_x_17 1, 6, 8, 2 /* good timing */ - #define TQ_x_18 1, 7, 8, 2 /* good timing */ - #define TQ_x_19 1, 8, 8, 2 /* good timing */ - #define TQ_x_20 1, 8, 8, 3 /* good timing */ - #define TQ_x_21 1, 8, 8, 4 - #define TQ_x_22 1, 8, 8, 5 - #define TQ_x_23 1, 8, 8, 6 - #define TQ_x_24 1, 8, 8, 7 - #define TQ_x_25 1, 8, 8, 8 - - #if CO_FSYS == 4000 - #define CO_CANbitRateDataInitializers \ - {10, TQ_x_20}, /*CAN=10kbps*/ \ - {5, TQ_x_20}, /*CAN=20kbps*/ \ - {2, TQ_x_20}, /*CAN=50kbps*/ \ - {1, TQ_x_16}, /*CAN=125kbps*/ \ - {1, TQ_x_8 }, /*CAN=250kbps*/ \ - {1, TQ_x_8 }, /*Not possible*/ \ - {1, TQ_x_8 }, /*Not possible*/ \ - {1, TQ_x_8 } /*Not possible*/ - #elif CO_FSYS == 8000 - #define CO_CANbitRateDataInitializers \ - {25, TQ_x_16}, /*CAN=10kbps*/ \ - {10, TQ_x_20}, /*CAN=20kbps*/ \ - {5, TQ_x_16}, /*CAN=50kbps*/ \ - {2, TQ_x_16}, /*CAN=125kbps*/ \ - {1, TQ_x_16}, /*CAN=250kbps*/ \ - {1, TQ_x_8 }, /*CAN=500kbps*/ \ - {1, TQ_x_8 }, /*Not possible*/ \ - {1, TQ_x_8 } /*Not possible*/ - #elif CO_FSYS == 12000 - #define CO_CANbitRateDataInitializers \ - {40, TQ_x_15}, /*CAN=10kbps*/ \ - {20, TQ_x_15}, /*CAN=20kbps*/ \ - {8, TQ_x_15}, /*CAN=50kbps*/ \ - {3, TQ_x_16}, /*CAN=125kbps*/ \ - {2, TQ_x_12}, /*CAN=250kbps*/ \ - {1, TQ_x_12}, /*CAN=500kbps*/ \ - {1, TQ_x_12}, /*Not possible*/ \ - {1, TQ_x_12} /*Not possible*/ - #elif CO_FSYS == 16000 - #define CO_CANbitRateDataInitializers \ - {50, TQ_x_16}, /*CAN=10kbps*/ \ - {25, TQ_x_16}, /*CAN=20kbps*/ \ - {10, TQ_x_16}, /*CAN=50kbps*/ \ - {4, TQ_x_16}, /*CAN=125kbps*/ \ - {2, TQ_x_16}, /*CAN=250kbps*/ \ - {1, TQ_x_16}, /*CAN=500kbps*/ \ - {1, TQ_x_10}, /*CAN=800kbps*/ \ - {1, TQ_x_8 } /*CAN=1000kbps*/ - #elif CO_FSYS == 20000 - #define CO_CANbitRateDataInitializers \ - {50, TQ_x_20}, /*CAN=10kbps*/ \ - {25, TQ_x_20}, /*CAN=20kbps*/ \ - {10, TQ_x_20}, /*CAN=50kbps*/ \ - {5, TQ_x_16}, /*CAN=125kbps*/ \ - {2, TQ_x_20}, /*CAN=250kbps*/ \ - {1, TQ_x_20}, /*CAN=500kbps*/ \ - {1, TQ_x_20}, /*Not possible*/ \ - {1, TQ_x_10} /*CAN=1000kbps*/ - #elif CO_FSYS == 24000 - #define CO_CANbitRateDataInitializers \ - {63, TQ_x_19}, /*CAN=10kbps*/ \ - {40, TQ_x_15}, /*CAN=20kbps*/ \ - {15, TQ_x_16}, /*CAN=50kbps*/ \ - {6, TQ_x_16}, /*CAN=125kbps*/ \ - {3, TQ_x_16}, /*CAN=250kbps*/ \ - {2, TQ_x_12}, /*CAN=500kbps*/ \ - {1, TQ_x_15}, /*CAN=800kbps*/ \ - {1, TQ_x_12} /*CAN=1000kbps*/ - #elif CO_FSYS == 32000 - #define CO_CANbitRateDataInitializers \ - {64, TQ_x_25}, /*CAN=10kbps*/ \ - {50, TQ_x_16}, /*CAN=20kbps*/ \ - {20, TQ_x_16}, /*CAN=50kbps*/ \ - {8, TQ_x_16}, /*CAN=125kbps*/ \ - {4, TQ_x_16}, /*CAN=250kbps*/ \ - {2, TQ_x_16}, /*CAN=500kbps*/ \ - {2, TQ_x_10}, /*CAN=800kbps*/ \ - {1, TQ_x_16} /*CAN=1000kbps*/ - #elif CO_FSYS == 36000 - #define CO_CANbitRateDataInitializers \ - {50, TQ_x_18}, /*CAN=10kbps*/ \ - {50, TQ_x_18}, /*CAN=20kbps*/ \ - {20, TQ_x_18}, /*CAN=50kbps*/ \ - {8, TQ_x_18}, /*CAN=125kbps*/ \ - {4, TQ_x_18}, /*CAN=250kbps*/ \ - {2, TQ_x_18}, /*CAN=500kbps*/ \ - {2, TQ_x_18}, /*Not possible*/ \ - {1, TQ_x_18} /*CAN=1000kbps*/ - #elif CO_FSYS == 40000 - #define CO_CANbitRateDataInitializers \ - {50, TQ_x_20}, /*Not possible*/ \ - {50, TQ_x_20}, /*CAN=20kbps*/ \ - {25, TQ_x_16}, /*CAN=50kbps*/ \ - {10, TQ_x_16}, /*CAN=125kbps*/ \ - {5, TQ_x_16}, /*CAN=250kbps*/ \ - {2, TQ_x_20}, /*CAN=500kbps*/ \ - {1, TQ_x_25}, /*CAN=800kbps*/ \ - {1, TQ_x_20} /*CAN=1000kbps*/ - #elif CO_FSYS == 48000 - #define CO_CANbitRateDataInitializers \ - {63, TQ_x_19}, /*Not possible*/ \ - {63, TQ_x_19}, /*CAN=20kbps*/ \ - {30, TQ_x_16}, /*CAN=50kbps*/ \ - {12, TQ_x_16}, /*CAN=125kbps*/ \ - {6, TQ_x_16}, /*CAN=250kbps*/ \ - {3, TQ_x_16}, /*CAN=500kbps*/ \ - {2, TQ_x_15}, /*CAN=800kbps*/ \ - {2, TQ_x_12} /*CAN=1000kbps*/ - #elif CO_FSYS == 56000 - #define CO_CANbitRateDataInitializers \ - {61, TQ_x_23}, /*Not possible*/ \ - {61, TQ_x_23}, /*CAN=20kbps*/ \ - {35, TQ_x_16}, /*CAN=50kbps*/ \ - {14, TQ_x_16}, /*CAN=125kbps*/ \ - {7, TQ_x_16}, /*CAN=250kbps*/ \ - {4, TQ_x_14}, /*CAN=500kbps*/ \ - {5, TQ_x_7 }, /*CAN=800kbps*/ \ - {2, TQ_x_14} /*CAN=1000kbps*/ - #elif CO_FSYS == 64000 - #define CO_CANbitRateDataInitializers \ - {64, TQ_x_25}, /*Not possible*/ \ - {64, TQ_x_25}, /*CAN=20kbps*/ \ - {40, TQ_x_16}, /*CAN=50kbps*/ \ - {16, TQ_x_16}, /*CAN=125kbps*/ \ - {8, TQ_x_16}, /*CAN=250kbps*/ \ - {4, TQ_x_16}, /*CAN=500kbps*/ \ - {2, TQ_x_20}, /*CAN=800kbps*/ \ - {2, TQ_x_16} /*CAN=1000kbps*/ - #elif CO_FSYS == 72000 - #define CO_CANbitRateDataInitializers \ - {40, TQ_x_18}, /*Not possible*/ \ - {40, TQ_x_18}, /*Not possible*/ \ - {40, TQ_x_18}, /*CAN=50kbps*/ \ - {16, TQ_x_18}, /*CAN=125kbps*/ \ - {8, TQ_x_18}, /*CAN=250kbps*/ \ - {4, TQ_x_18}, /*CAN=500kbps*/ \ - {3, TQ_x_15}, /*CAN=800kbps*/ \ - {2, TQ_x_18} /*CAN=1000kbps*/ - #elif CO_FSYS == 80000 - #define CO_CANbitRateDataInitializers \ - {40, TQ_x_20}, /*Not possible*/ \ - {40, TQ_x_20}, /*Not possible*/ \ - {40, TQ_x_20}, /*CAN=50kbps*/ \ - {16, TQ_x_20}, /*CAN=125kbps*/ \ - {8, TQ_x_20}, /*CAN=250kbps*/ \ - {4, TQ_x_20}, /*CAN=500kbps*/ \ - {2, TQ_x_25}, /*CAN=800kbps*/ \ - {2, TQ_x_20} /*CAN=1000kbps*/ - #else - #error define_CO_FSYS CO_FSYS not supported - #endif -#endif - - -/* Structure contains timing coefficients for CAN module. - * - * CAN baud rate is calculated from following equations: - * Fsys - System clock (MAX 80MHz for PIC32MX) - * TQ = 2 * BRP / Fsys - Time Quanta - * BaudRate = 1 / (TQ * K) - Can bus Baud Rate - * K = SJW + PROP + PhSeg1 + PhSeg2 - Number of Time Quantas - */ -typedef struct{ - uint8_t BRP; /* (1...64) Baud Rate Prescaler */ - uint8_t SJW; /* (1...4) SJW time */ - uint8_t PROP; /* (1...8) PROP time */ - uint8_t phSeg1; /* (1...8) Phase Segment 1 time */ - uint8_t phSeg2; /* (1...8) Phase Segment 2 time */ -}CO_CANbitRateData_t; - - -/* CAN receive message structure as aligned in CAN module. */ -typedef struct{ - unsigned timestamp :8; /* 8 bits timestamp, see MCF5282 documentation */ - unsigned code :4; /* Message Buffer code. see MCF52825 documentation */ - unsigned DLC :4; /* Data length code */ - unsigned sid :11;/* Standard Identifier - 11bits */ - unsigned RTR :1; /* Remote Transmission Request bit */ - unsigned :4; - unsigned timestamp16 :16;/* See MCF5282 documentation */ - uint8_t data[8]; /* 8 data bytes */ -}CO_CANrxMsg_t; - - -/* Received message object */ -typedef struct{ - uint16_t ident; - uint16_t mask; - void *object; - void (*pFunct)(void *object, const CO_CANrxMsg_t *message); -}CO_CANrx_t; - - -/* Transmit message object. */ -typedef struct{ - uint8_t DLC; - uint16_t ident; - uint8_t data[8]; - volatile bool_t bufferFull; - volatile bool_t syncFlag; -}CO_CANtx_t; - - -/* CAN module object. */ -typedef struct{ - void *CANdriverState; - CO_CANrxMsg_t *CANmsgBuff; - uint8_t CANmsgBuffSize; - CO_CANrx_t *rxArray; - uint16_t rxSize; - CO_CANtx_t *txArray; - uint16_t txSize; - volatile bool_t CANnormal; - volatile bool_t useCANrxFilters; - volatile bool_t bufferInhibitFlag; - volatile bool_t firstCANtxMessage; - volatile uint16_t CANtxCount; - uint32_t errOld; - void *em; -}CO_CANmodule_t; - - -/* CAN interrupt receives and transmits CAN messages. */ -void CO_CANinterrupt(CO_CANmodule_t *CANmodule, uint16_t ICODE); - - -#endif /* CO_DRIVER_TARGET_H */ diff --git a/stack/MCF5282/readme b/stack/MCF5282/readme deleted file mode 100644 index db54c28a..00000000 --- a/stack/MCF5282/readme +++ /dev/null @@ -1,4 +0,0 @@ -Freescale MCF5282 (ColdFire V2) - -Contributed by Laurent Grosbois (mar 2012): -http://sourceforge.net/p/canopennode/code_complete/ci/master/tree/mcf5282_ongoingdev/ diff --git a/stack/PIC24_dsPIC33/CO_driver.c b/stack/PIC24_dsPIC33/CO_driver.c deleted file mode 100644 index 72fe0468..00000000 --- a/stack/PIC24_dsPIC33/CO_driver.c +++ /dev/null @@ -1,809 +0,0 @@ -/* - * CAN module object for Microchip dsPIC33F or PIC24H microcontroller. - * - * @file CO_driver.c - * @author Janez Paternoster - * @author Peter Rozsahegyi (EDS) - * @author Jens Nielsen (CAN receive) - * @copyright 2004 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include "CO_driver.h" -#include "CO_Emergency.h" - - -/* Globals */ - extern const CO_CANbitRateData_t CO_CANbitRateData[8]; - -#if CO_CAN1msgBuffSize > 0 - __eds__ CO_CANrxMsg_t CO_CAN1msg[CO_CAN1msgBuffSize] __eds __dma __attribute__((aligned(128))); -#endif -#if CO_CAN2msgBuffSize > 0 - __eds__ CO_CANrxMsg_t CO_CAN2msg[CO_CAN1msgBuffSize] __eds __dma __attribute__((aligned(128))); -#endif - - -/* Macro and Constants - CAN module registers and DMA registers - offset. */ - #define CAN_REG(base, offset) (*((volatile uint16_t *) (((uintptr_t) base) + offset))) - - #define C_CTRL1 0x00 - #define C_CTRL2 0x02 - #define C_VEC 0x04 - #define C_FCTRL 0x06 - #define C_FIFO 0x08 - #define C_INTF 0x0A - #define C_INTE 0x0C - #define C_EC 0x0E - #define C_CFG1 0x10 - #define C_CFG2 0x12 - #define C_FEN1 0x14 - #define C_FMSKSEL1 0x18 - #define C_FMSKSEL2 0x1A - - /* WIN == 0 */ - #define C_RXFUL1 0x20 - #define C_RXFUL2 0x22 - #define C_RXOVF1 0x28 - #define C_RXOVF2 0x2A - #define C_TR01CON 0x30 - #define C_TR23CON 0x32 - #define C_TR45CON 0x34 - #define C_TR67CON 0x36 - #define C_RXD 0x40 - #define C_TXD 0x42 - - /* WIN == 1 */ - #define C_BUFPNT1 0x20 - #define C_BUFPNT2 0x22 - #define C_BUFPNT3 0x24 - #define C_BUFPNT4 0x26 - #define C_RXM0SID 0x30 - #define C_RXM1SID 0x34 - #define C_RXM2SID 0x38 - #define C_RXF0SID 0x40 /* filter1 = +4, ...., filter 15 = +4*15 */ - - - #define DMA_REG(base, offset) (*((volatile uint16_t *) (base + offset))) - - #define DMA_CON 0x0 - #define DMA_REQ 0x2 -#ifndef __HAS_EDS__ - #define DMA_STA 0x4 - #define DMA_STB 0x6 - #define DMA_PAD 0x8 - #define DMA_CNT 0xA -#else - #define DMA_STAL 0x4 - #define DMA_STAH 0x6 - #define DMA_STBL 0x8 - #define DMA_STBH 0xA - #define DMA_PAD 0xC - #define DMA_CNT 0xE -#endif - - -/******************************************************************************/ -void CO_CANsetConfigurationMode(void *CANdriverState){ - uint16_t C_CTRL1copy = CAN_REG(CANdriverState, C_CTRL1); - - /* set REQOP = 0x4 */ - C_CTRL1copy &= 0xFCFF; - C_CTRL1copy |= 0x0400; - CAN_REG(CANdriverState, C_CTRL1) = C_CTRL1copy; - - /* while OPMODE != 4 */ - while((CAN_REG(CANdriverState, C_CTRL1) & 0x00E0) != 0x0080); -} - - -/******************************************************************************/ -void CO_CANsetNormalMode(CO_CANmodule_t *CANmodule){ - uint16_t C_CTRL1copy = CAN_REG(CANmodule->CANdriverState, C_CTRL1); - - /* set REQOP = 0x0 */ - C_CTRL1copy &= 0xF8FF; - CAN_REG(CANmodule->CANdriverState, C_CTRL1) = C_CTRL1copy; - - /* while OPMODE != 0 */ - while((CAN_REG(CANmodule->CANdriverState, C_CTRL1) & 0x00E0) != 0x0000); - - CANmodule->CANnormal = true; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_CANmodule_init( - CO_CANmodule_t *CANmodule, - void *CANdriverState, - CO_CANrx_t rxArray[], - uint16_t rxSize, - CO_CANtx_t txArray[], - uint16_t txSize, - uint16_t CANbitRate) -{ - uint16_t i; - volatile uint16_t *pRXF; - - uint16_t DMArxBaseAddress; - uint16_t DMAtxBaseAddress; - __eds__ CO_CANrxMsg_t *CANmsgBuff; - uint8_t CANmsgBuffSize; - uint16_t CANmsgBuffDMAoffset; -#if defined(__HAS_EDS__) - uint16_t CANmsgBuffDMApage; -#endif - - /* verify arguments */ - if(CANmodule==NULL || rxArray==NULL || txArray==NULL){ - return CO_ERROR_ILLEGAL_ARGUMENT; - } - - /* Get global addresses for CAN module 1 or 2. */ - if(CANdriverState == ADDR_CAN1) { - DMArxBaseAddress = CO_CAN1_DMA0; - DMAtxBaseAddress = CO_CAN1_DMA1; - CANmsgBuff = &CO_CAN1msg[0]; - CANmsgBuffSize = CO_CAN1msgBuffSize; - CANmsgBuffDMAoffset = __builtin_dmaoffset(&CO_CAN1msg[0]); - #if defined(__HAS_EDS__) - CANmsgBuffDMApage = __builtin_dmapage(&CO_CAN1msg[0]); - #endif - } -#if CO_CAN2msgBuffSize > 0 - else if(((uintptr_t) CANdriverState) == ADDR_CAN2) { - DMArxBaseAddress = CO_CAN2_DMA0; - DMAtxBaseAddress = CO_CAN2_DMA1; - CANmsgBuff = &CO_CAN2msg[0]; - CANmsgBuffSize = CO_CAN2msgBuffSize; - CANmsgBuffDMAoffset = __builtin_dmaoffset(&CO_CAN2msg[0]); - #if defined(__HAS_EDS__) - CANmsgBuffDMApage = __builtin_dmapage(&CO_CAN2msg[0]); - #endif - } -#endif - else { - return CO_ERROR_ILLEGAL_ARGUMENT; - } - - /* Configure object variables */ - CANmodule->CANdriverState = CANdriverState; - CANmodule->CANmsgBuff = CANmsgBuff; - CANmodule->rxArray = rxArray; - CANmodule->rxSize = rxSize; - CANmodule->txArray = txArray; - CANmodule->txSize = txSize; - CANmodule->CANnormal = false; - CANmodule->useCANrxFilters = (rxSize <= 16U) ? true : false; - CANmodule->bufferInhibitFlag = false; - CANmodule->firstCANtxMessage = true; - CANmodule->CANtxCount = 0U; - CANmodule->errOld = 0U; - CANmodule->em = NULL; - - for(i=0U; iuseCANrxFilters){ - /* CAN module filters are used, they will be configured with */ - /* CO_CANrxBufferInit() functions, called by separate CANopen */ - /* init functions. */ - /* All mask bits are 1, so received message must match filter */ - CAN_REG(CANdriverState, C_RXM0SID) = 0xFFE8; - CAN_REG(CANdriverState, C_RXM1SID) = 0xFFE8; - CAN_REG(CANdriverState, C_RXM2SID) = 0xFFE8; - } - else{ - /* CAN module filters are not used, all messages with standard 11-bit */ - /* identifier will be received */ - /* Set masks so, that all messages with standard identifier are accepted */ - CAN_REG(CANdriverState, C_RXM0SID) = 0x0008; - CAN_REG(CANdriverState, C_RXM1SID) = 0x0008; - CAN_REG(CANdriverState, C_RXM2SID) = 0x0008; - } - - /* WIN = 0 - use buffer registers for default */ - CAN_REG(CANdriverState, C_CTRL1) &= 0xFFFE; - - - /* Configure DMA controller */ - /* Set size of buffer in DMA RAM (FIFO Area Starts with Tx/Rx buffer TRB1 (FSA = 1)) */ - /* Use maximum 16 buffers, because we have 16-bit system. */ - if (CANmsgBuffSize >= 16) { - CAN_REG(CANdriverState, C_FCTRL) = 0x8001; - CANmodule->CANmsgBuffSize = 16; - } - else if(CANmsgBuffSize >= 12) { - CAN_REG(CANdriverState, C_FCTRL) = 0x6001; - CANmodule->CANmsgBuffSize = 12; - } - else if(CANmsgBuffSize >= 8) { - CAN_REG(CANdriverState, C_FCTRL) = 0x4001; - CANmodule->CANmsgBuffSize = 8; - } - else if(CANmsgBuffSize >= 6) { - CAN_REG(CANdriverState, C_FCTRL) = 0x2001; - CANmodule->CANmsgBuffSize = 6; - } - else if(CANmsgBuffSize >= 4) { - CAN_REG(CANdriverState, C_FCTRL) = 0x0001; - CANmodule->CANmsgBuffSize = 4; - } - else { - return CO_ERROR_ILLEGAL_ARGUMENT; - } - - /* DMA chanel initialization for ECAN reception */ - DMA_REG(DMArxBaseAddress, DMA_CON) = 0x0020; - DMA_REG(DMArxBaseAddress, DMA_PAD) = (volatile uint16_t) &CAN_REG(CANdriverState, C_RXD); - DMA_REG(DMArxBaseAddress, DMA_CNT) = 7; - DMA_REG(DMArxBaseAddress, DMA_REQ) = (CANdriverState==ADDR_CAN1) ? 34 : 55; - -#ifndef __HAS_EDS__ - DMA_REG(DMArxBaseAddress, DMA_STA) = CANmsgBuffDMAoffset; -#else - DMA_REG(DMArxBaseAddress, DMA_STAL) = CANmsgBuffDMAoffset; - DMA_REG(DMArxBaseAddress, DMA_STAH) = CANmsgBuffDMApage; -#endif - - DMA_REG(DMArxBaseAddress, DMA_CON) = 0x8020; - - /* DMA chanel initialization for ECAN transmission */ - DMA_REG(DMAtxBaseAddress, DMA_CON) = 0x2020; - DMA_REG(DMAtxBaseAddress, DMA_PAD) = (volatile uint16_t) &CAN_REG(CANdriverState, C_TXD); - DMA_REG(DMAtxBaseAddress, DMA_CNT) = 7; - DMA_REG(DMAtxBaseAddress, DMA_REQ) = (CANdriverState==ADDR_CAN1) ? 70 : 71; - -#ifndef __HAS_EDS__ - DMA_REG(DMAtxBaseAddress, DMA_STA) = CANmsgBuffDMAoffset; -#else - DMA_REG(DMAtxBaseAddress, DMA_STAL) = CANmsgBuffDMAoffset; - DMA_REG(DMAtxBaseAddress, DMA_STAH) = CANmsgBuffDMApage; -#endif - - DMA_REG(DMAtxBaseAddress, DMA_CON) = 0xA020; - - - /* CAN interrupt registers */ - /* clear interrupt flags */ - CAN_REG(CANdriverState, C_INTF) = 0x0000; - /* enable receive and transmit interrupt */ - CAN_REG(CANdriverState, C_INTE) = 0x0003; - /* CAN interrupt (combined) must be configured by application */ - - return CO_ERROR_NO; -} - - -/******************************************************************************/ -void CO_CANmodule_disable(CO_CANmodule_t *CANmodule){ - CO_CANsetConfigurationMode(CANmodule->CANdriverState); -} - - -/******************************************************************************/ -uint16_t CO_CANrxMsg_readIdent(const CO_CANrxMsg_t *rxMsg){ - return (rxMsg->ident >> 2) & 0x7FF; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_CANrxBufferInit( - CO_CANmodule_t *CANmodule, - uint16_t index, - uint16_t ident, - uint16_t mask, - bool_t rtr, - void *object, - void (*pFunct)(void *object, const CO_CANrxMsg_t *message)) -{ - CO_ReturnError_t ret = CO_ERROR_NO; - - if((CANmodule!=NULL) && (object!=NULL) && (pFunct!=NULL) && (index < CANmodule->rxSize)){ - /* buffer, which will be configured */ - CO_CANrx_t *buffer = &CANmodule->rxArray[index]; - uint16_t RXF, RXM; - uint16_t addr = CANmodule->CANdriverState; - - /* Configure object variables */ - buffer->object = object; - buffer->pFunct = pFunct; - - - /* CAN identifier and CAN mask, bit aligned with CAN module registers (in DMA RAM) */ - RXF = (ident & 0x07FF) << 2; - if(rtr){ - RXF |= 0x02; - } - RXM = (mask & 0x07FF) << 2; - RXM |= 0x02; - - /* configure filter and mask */ - if(RXF != buffer->ident || RXM != buffer->mask){ - volatile uint16_t C_CTRL1old = CAN_REG(addr, C_CTRL1); - CAN_REG(addr, C_CTRL1) = C_CTRL1old | 0x0001; /* WIN = 1 - use filter registers */ - buffer->ident = RXF; - buffer->mask = RXM; - - /* Set CAN hardware module filter and mask. */ - if(CANmodule->useCANrxFilters){ - volatile uint16_t *pRXF; - volatile uint16_t *pRXM0, *pRXM1, *pRXM2; - uint16_t selectMask; - - /* align RXF and RXM with C_RXF_SID and C_RXM_SID registers */ - RXF &= 0xFFFD; RXF <<= 3; - RXM &= 0xFFFD; RXM <<= 3; RXM |= 0x0008; - - /* write to filter */ - pRXF = &CAN_REG(addr, C_RXF0SID); /* pointer to first filter register */ - pRXF += index * 2; /* now points to C_RXFiSID (i == index) */ - *pRXF = RXF; /* write value to filter */ - - /* configure mask (There are three masks, each of them can be asigned to any filter. */ - /* First mask has always the value 0xFFE8 - all 11 bits must match). */ - pRXM0 = &CAN_REG(addr, C_RXM0SID); - pRXM1 = &CAN_REG(addr, C_RXM1SID); - pRXM2 = &CAN_REG(addr, C_RXM2SID); - if(RXM == 0xFFE8){ - selectMask = 0; - } - else if(RXM == *pRXM1 || *pRXM1 == 0xFFE8){ - /* RXM is equal to mask 1 or mask 1 was not yet configured. */ - *pRXM1 = RXM; - selectMask = 1; - } - else if(RXM == *pRXM2 || *pRXM2 == 0xFFE8){ - /* RXM is equal to mask 2 or mask 2 was not yet configured. */ - *pRXM2 = RXM; - selectMask = 2; - } - else{ - /* not enough masks */ - ret = CO_ERROR_OUT_OF_MEMORY; - selectMask = 0; - } - if(ret == CO_ERROR_NO){ - /* now write to appropriate mask select register */ - if(index<8){ - uint16_t clearMask = ~(0x0003 << (index << 1)); - selectMask = selectMask << (index << 1); - CAN_REG(addr, C_FMSKSEL1) = - (CAN_REG(addr, C_FMSKSEL1) & clearMask) | selectMask; - } - else{ - uint16_t clearMask = ~(0x0003 << ((index-8) << 1)); - selectMask = selectMask << ((index-8) << 1); - CAN_REG(addr, C_FMSKSEL2) = - (CAN_REG(addr, C_FMSKSEL2) & clearMask) | selectMask; - } - } - } - CAN_REG(addr, C_CTRL1) = C_CTRL1old; - } - } - else{ - ret = CO_ERROR_ILLEGAL_ARGUMENT; - } - - return ret; -} - - -/******************************************************************************/ -CO_CANtx_t *CO_CANtxBufferInit( - CO_CANmodule_t *CANmodule, - uint16_t index, - uint16_t ident, - bool_t rtr, - uint8_t noOfBytes, - bool_t syncFlag) -{ - CO_CANtx_t *buffer = NULL; - - if((CANmodule != NULL) && (index < CANmodule->txSize)){ - /* get specific buffer */ - buffer = &CANmodule->txArray[index]; - - /* CAN identifier, bit aligned with CAN module registers */ - uint16_t TXF; - TXF = (ident & 0x07FF) << 2; - if(rtr){ - TXF |= 0x02; - } - - /* write to buffer */ - buffer->ident = TXF; - buffer->DLC = noOfBytes; - buffer->bufferFull = false; - buffer->syncFlag = syncFlag; - } - - return buffer; -} - - -/* Copy message to CAN module - internal usage only. - * - * @param CANdriverState CAN module base address - * @param dest Pointer to CAN module transmit buffer - * @param src Pointer to source message - */ -static void CO_CANsendToModule(void *CANdriverState, __eds__ CO_CANrxMsg_t *dest, CO_CANtx_t *src){ - uint8_t DLC; - __eds__ uint8_t *CANdataBuffer; - uint8_t *pData; - volatile uint16_t C_CTRL1old; - - /* CAN-ID + RTR */ - dest->ident = src->ident; - - /* Data lenght */ - DLC = src->DLC; - if(DLC > 8) DLC = 8; - dest->DLC = DLC; - - /* copy data */ - CANdataBuffer = &(dest->data[0]); - pData = src->data; - for(; DLC>0; DLC--) *(CANdataBuffer++) = *(pData++); - - /* control register, transmit request */ - C_CTRL1old = CAN_REG(CANdriverState, C_CTRL1); - CAN_REG(CANdriverState, C_CTRL1) = C_CTRL1old & 0xFFFE; /* WIN = 0 - use buffer registers */ - CAN_REG(CANdriverState, C_TR01CON) |= 0x08; - CAN_REG(CANdriverState, C_CTRL1) = C_CTRL1old; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_CANsend(CO_CANmodule_t *CANmodule, CO_CANtx_t *buffer){ - CO_ReturnError_t err = CO_ERROR_NO; - uint16_t addr = CANmodule->CANdriverState; - volatile uint16_t C_CTRL1old, C_TR01CONcopy; - - /* Verify overflow */ - if(buffer->bufferFull){ - if(!CANmodule->firstCANtxMessage){ - /* don't set error, if bootup message is still on buffers */ - CO_errorReport((CO_EM_t*)CANmodule->em, CO_EM_CAN_TX_OVERFLOW, CO_EMC_CAN_OVERRUN, (buffer->ident >> 2) & 0x7FF); - } - err = CO_ERROR_TX_OVERFLOW; - } - - CO_LOCK_CAN_SEND(); - /* read C_TR01CON */ - C_CTRL1old = CAN_REG(addr, C_CTRL1); - CAN_REG(addr, C_CTRL1) = C_CTRL1old & 0xFFFE; /* WIN = 0 - use buffer registers */ - C_TR01CONcopy = CAN_REG(addr, C_TR01CON); - CAN_REG(addr, C_CTRL1) = C_CTRL1old; - - /* if CAN TX buffer is free, copy message to it */ - if((C_TR01CONcopy & 0x8) == 0 && CANmodule->CANtxCount == 0){ - CANmodule->bufferInhibitFlag = buffer->syncFlag; - CO_CANsendToModule(addr, CANmodule->CANmsgBuff, buffer); - } - /* if no buffer is free, message will be sent by interrupt */ - else{ - buffer->bufferFull = true; - CANmodule->CANtxCount++; - } - CO_UNLOCK_CAN_SEND(); - - return err; -} - - -/******************************************************************************/ -void CO_CANclearPendingSyncPDOs(CO_CANmodule_t *CANmodule){ - uint32_t tpdoDeleted = 0U; - - CO_LOCK_CAN_SEND(); - /* Abort message from CAN module, if there is synchronous TPDO. - * Take special care with this functionality. */ - if(CANmodule->bufferInhibitFlag){ - volatile uint16_t C_CTRL1old = CAN_REG(CANmodule->CANdriverState, C_CTRL1); - CAN_REG(CANmodule->CANdriverState, C_CTRL1) = C_CTRL1old & 0xFFFE; /* WIN = 0 - use buffer registers */ - CAN_REG(CANmodule->CANdriverState, C_TR01CON) &= 0xFFF7; /* clear TXREQ */ - CAN_REG(CANmodule->CANdriverState, C_CTRL1) = C_CTRL1old; - CANmodule->bufferInhibitFlag = false; - tpdoDeleted = 1U; - } - /* delete also pending synchronous TPDOs in TX buffers */ - if(CANmodule->CANtxCount != 0U){ - uint16_t i; - CO_CANtx_t *buffer = &CANmodule->txArray[0]; - for(i = CANmodule->txSize; i > 0U; i--){ - if(buffer->bufferFull){ - if(buffer->syncFlag){ - buffer->bufferFull = false; - CANmodule->CANtxCount--; - tpdoDeleted = 2U; - } - } - buffer++; - } - } - CO_UNLOCK_CAN_SEND(); - - - if(tpdoDeleted != 0U){ - CO_errorReport((CO_EM_t*)CANmodule->em, CO_EM_TPDO_OUTSIDE_WINDOW, CO_EMC_COMMUNICATION, tpdoDeleted); - } -} - - -/******************************************************************************/ -void CO_CANverifyErrors(CO_CANmodule_t *CANmodule){ - uint16_t err; - CO_EM_t* em = (CO_EM_t*)CANmodule->em; - - err = CAN_REG(CANmodule->CANdriverState, C_INTF) >> 8; - if(CAN_REG(CANmodule->CANdriverState, C_INTF) & 4){ - err |= 0x80; - } - - if(CANmodule->errOld != err){ - CANmodule->errOld = err; - - /* CAN RX bus overflow */ - if(err & 0xC0){ - CO_errorReport(em, CO_EM_CAN_RXB_OVERFLOW, CO_EMC_CAN_OVERRUN, err); - CAN_REG(CANmodule->CANdriverState, C_INTF) &= 0xFFFB;/* clear bits */ - } - - /* CAN TX bus off */ - if(err & 0x20){ - CO_errorReport(em, CO_EM_CAN_TX_BUS_OFF, CO_EMC_BUS_OFF_RECOVERED, err); - } - else{ - CO_errorReset(em, CO_EM_CAN_TX_BUS_OFF, err); - } - - /* CAN TX bus passive */ - if(err & 0x10){ - if(!CANmodule->firstCANtxMessage) CO_errorReport(em, CO_EM_CAN_TX_BUS_PASSIVE, CO_EMC_CAN_PASSIVE, err); - } - else{ - int8_t isError = CO_isError(em, CO_EM_CAN_TX_BUS_PASSIVE); - if(isError){ - CO_errorReset(em, CO_EM_CAN_TX_BUS_PASSIVE, err); - CO_errorReset(em, CO_EM_CAN_TX_OVERFLOW, err); - } - } - - /* CAN RX bus passive */ - if(err & 0x08){ - CO_errorReport(em, CO_EM_CAN_RX_BUS_PASSIVE, CO_EMC_CAN_PASSIVE, err); - } - else{ - CO_errorReset(em, CO_EM_CAN_RX_BUS_PASSIVE, err); - } - - /* CAN TX or RX bus warning */ - if(err & 0x19){ - CO_errorReport(em, CO_EM_CAN_BUS_WARNING, CO_EMC_NO_ERROR, err); - } - else{ - CO_errorReset(em, CO_EM_CAN_BUS_WARNING, err); - } - } -} - - -/******************************************************************************/ -void CO_CANinterrupt(CO_CANmodule_t *CANmodule) { - - /* receive interrupt (New CAN message is available in RX FIFO buffer) */ - if(CAN_REG(CANmodule->CANdriverState, C_INTF) & 0x02) { - uint16_t C_CTRL1old; - uint16_t C_RXFUL1copy; - uint16_t C_FIFOcopy; - uint8_t FNRB, FBP; - - CO_DISABLE_INTERRUPTS(); - C_CTRL1old = CAN_REG(CANmodule->CANdriverState, C_CTRL1); - CAN_REG(CANmodule->CANdriverState, C_CTRL1) = C_CTRL1old & 0xFFFE; /* WIN = 0 - use buffer registers */ - C_RXFUL1copy = CAN_REG(CANmodule->CANdriverState, C_RXFUL1); - CAN_REG(CANmodule->CANdriverState, C_CTRL1) = C_CTRL1old; - - /* We will service the buffers indicated by RXFUL copy, clear interrupt - * flag now and let interrupt hit again if more messages are received */ - CAN_REG(CANmodule->CANdriverState, C_INTF) &= 0xFFFD; - C_FIFOcopy = CAN_REG(CANmodule->CANdriverState, C_FIFO); - CO_ENABLE_INTERRUPTS(); - - /* FNRB tells us which buffer to read in FIFO */ - FNRB = C_FIFOcopy & 0x3F; - /* FBP tells us the next FIFO entry that will be written */ - FBP = C_FIFOcopy >> 8; - - while(C_RXFUL1copy != 0) { - __eds__ CO_CANrxMsg_t *rcvMsg;/* pointer to received message in CAN module */ - uint16_t index; /* index of received message */ - uint16_t rcvMsgIdent; /* identifier of the received message */ - CO_CANrx_t *buffer = NULL; /* receive message buffer from CO_CANmodule_t object. */ - bool_t msgMatched = false; - uint16_t mask; - - mask = 1 << FNRB; - - if((C_RXFUL1copy & mask) == 0) { - /* This should not happen. However, if it does happen - * (in case of debugging), get FNRB from loop. */ - for(FNRB=1; FNRBCANmsgBuffSize; FNRB++) { - mask = 1 << FNRB; - if((C_RXFUL1copy & mask)) { - break; - } - } - } - - /* RXFUL is set for this buffer, service it */ - rcvMsg = &CANmodule->CANmsgBuff[FNRB]; - rcvMsgIdent = rcvMsg->ident; - if(CANmodule->useCANrxFilters) { - /* CAN module filters are used. Message with known 11-bit identifier has */ - /* been received */ - index = rcvMsg->FILHIT; - if(index < CANmodule->rxSize) { - buffer = &CANmodule->rxArray[index]; - /* verify also RTR */ - if(((rcvMsgIdent ^ buffer->ident) & buffer->mask) == 0U) { - msgMatched = true; - } - } - } - else { - /* CAN module filters are not used, message with any standard 11-bit identifier */ - /* has been received. Search rxArray form CANmodule for the same CAN-ID. */ - buffer = &CANmodule->rxArray[0]; - for(index = CANmodule->rxSize; index > 0U; index--) { - if(((rcvMsgIdent ^ buffer->ident) & buffer->mask) == 0U) { - msgMatched = true; - break; - } - buffer++; - } - } - - /* Call specific function, which will process the message */ - if(msgMatched && (buffer != NULL) && (buffer->pFunct != NULL)) { - #ifdef __HAS_EDS__ - CO_CANrxMsg_t _rcvMsg = *rcvMsg; - buffer->pFunct(buffer->object, &_rcvMsg); - #else - buffer->pFunct(buffer->object, rcvMsg); - #endif - } - - /* Clear RXFUL flag */ - CO_DISABLE_INTERRUPTS(); - C_CTRL1old = CAN_REG(CANmodule->CANdriverState, C_CTRL1); - CAN_REG(CANmodule->CANdriverState, C_CTRL1) = C_CTRL1old & 0xFFFE; /* WIN = 0 - use buffer registers */ - CAN_REG(CANmodule->CANdriverState, C_RXFUL1) &= ~(mask); - CAN_REG(CANmodule->CANdriverState, C_CTRL1) = C_CTRL1old; - CO_ENABLE_INTERRUPTS(); - C_RXFUL1copy &= ~(mask); - - /* Now update FNRB, it will point to a new buffer after RXFUL was cleared */ - FNRB = (CAN_REG(CANmodule->CANdriverState, C_FIFO) & 0x3F); - } - } - - /* transmit interrupt (TX buffer is free) */ - if(CAN_REG(CANmodule->CANdriverState, C_INTF) & 0x01) { - - /* Clear interrupt flag */ - CAN_REG(CANmodule->CANdriverState, C_INTF) &= 0xFFFE; - /* First CAN message (bootup) was sent successfully */ - CANmodule->firstCANtxMessage = false; - /* clear flag from previous message */ - CANmodule->bufferInhibitFlag = false; - /* Are there any new messages waiting to be send */ - if(CANmodule->CANtxCount > 0U){ - uint16_t i; /* index of transmitting message */ - - /* first buffer */ - CO_CANtx_t *buffer = &CANmodule->txArray[0]; - /* search through whole array of pointers to transmit message buffers. */ - for(i = CANmodule->txSize; i > 0U; i--){ - /* if message buffer is full, send it. */ - if(buffer->bufferFull){ - buffer->bufferFull = false; - CANmodule->CANtxCount--; - - /* Copy message to CAN buffer */ - CANmodule->bufferInhibitFlag = buffer->syncFlag; - CO_CANsendToModule(CANmodule->CANdriverState, CANmodule->CANmsgBuff, buffer); - break; /* exit for loop */ - } - buffer++; - }/* end of for loop */ - - /* Clear counter if no more messages */ - if(i == 0U){ - CANmodule->CANtxCount = 0U; - } - } - } -} diff --git a/stack/PIC24_dsPIC33/CO_driver_target.h b/stack/PIC24_dsPIC33/CO_driver_target.h deleted file mode 100644 index fc02b733..00000000 --- a/stack/PIC24_dsPIC33/CO_driver_target.h +++ /dev/null @@ -1,431 +0,0 @@ -/* - * CAN module object for Microchip dsPIC33 or PIC24 microcontroller. - * - * @file CO_driver_target.h - * @author Janez Paternoster - * @author Peter Rozsahegyi (EDS) - * @author Jens Nielsen (CAN receive) - * @copyright 2004 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#ifndef CO_DRIVER_TARGET_H -#define CO_DRIVER_TARGET_H - - -#if defined(__dsPIC33F__) || defined(__PIC24H__) -#include /* processor header file */ -#elif defined(__dsPIC33E__) || defined(__PIC24E__) -#include /* processor header file */ -#endif -#include /* for 'NULL' */ -#include /* for 'int8_t' to 'uint64_t' */ -#include /* for 'true' and 'false' */ - -/* Endianness */ -#define CO_LITTLE_ENDIAN - -/* CAN message buffer sizes for CAN module 1 and 2. Valid values - * are 0, 4, 6, 8, 12, 16. Default is one TX and seven RX messages (FIFO). */ -#ifndef CO_CAN1msgBuffSize - #define CO_CAN1msgBuffSize 8 -#endif /* CO_CAN1msgBuffSize */ -#ifndef CO_CAN2msgBuffSize - #define CO_CAN2msgBuffSize 0 //CAN module 2 not used by default -#endif /* CO_CAN2msgBuffSize */ - - -/* Default DMA addresses for CAN modules. */ -#ifndef CO_CAN1_DMA0 - #define CO_CAN1_DMA0 ADDR_DMA0 -#endif /* CO_CAN1_DMA0 */ -#ifndef CO_CAN1_DMA1 - #define CO_CAN1_DMA1 ADDR_DMA1 -#endif /* CO_CAN1_DMA1 */ -#ifndef CO_CAN2_DMA0 - #define CO_CAN2_DMA0 ADDR_DMA2 -#endif /* CO_CAN2_DMA0 */ -#ifndef CO_CAN2_DMA1 - #define CO_CAN2_DMA1 ADDR_DMA3 -#endif /* CO_CAN2_DMA1 */ - - -/* Define DMA attribute on supported platforms */ -#if defined(__dsPIC33F__) || defined(__PIC24H__) || defined(__DMA_BASE) - #define __dma __attribute__((space(dma))) -#else - #define __dma - #if defined(__C30_VERSION__) && !defined(__XC16_VERSION__) - #define __builtin_dmaoffset(V) (uint16_t)V - #endif -#endif - -/* Define EDS attribute on supported platforms */ -#if defined(__HAS_EDS__) - #define __eds __attribute__((eds)) - #if defined(__C30_VERSION__) && !defined(__XC16_VERSION__) - #define __builtin_dmapage(V) (uint16_t)0 - #endif -#else - #define __eds - #define __eds__ -#endif - - -/* CAN module base addresses */ -#define ADDR_CAN1 ((uint16_t)&C1CTRL1) -#define ADDR_CAN2 ((uint16_t)&C2CTRL1) - -/* DMA addresses */ -#define ADDR_DMA0 ((uint16_t)&DMA0CON) -#define ADDR_DMA1 ((uint16_t)&DMA1CON) -#define ADDR_DMA2 ((uint16_t)&DMA2CON) -#define ADDR_DMA3 ((uint16_t)&DMA3CON) -#define ADDR_DMA4 ((uint16_t)&DMA4CON) -#define ADDR_DMA5 ((uint16_t)&DMA5CON) -#define ADDR_DMA6 ((uint16_t)&DMA6CON) -#define ADDR_DMA7 ((uint16_t)&DMA7CON) - - -/* Critical sections */ -#define CO_LOCK_CAN_SEND() asm volatile ("disi #0x3FFF") -#define CO_UNLOCK_CAN_SEND() asm volatile ("disi #0x0000") - -#define CO_LOCK_EMCY() asm volatile ("disi #0x3FFF") -#define CO_UNLOCK_EMCY() asm volatile ("disi #0x0000") - -#define CO_LOCK_OD() asm volatile ("disi #0x3FFF") -#define CO_UNLOCK_OD() asm volatile ("disi #0x0000") - -#define CO_DISABLE_INTERRUPTS() asm volatile ("disi #0x3FFF") -#define CO_ENABLE_INTERRUPTS() asm volatile ("disi #0x0000") - - -/* Data types */ -/* int8_t to uint64_t are defined in stdint.h */ -typedef unsigned char bool_t; -typedef float float32_t; -typedef long double float64_t; -typedef char char_t; -typedef unsigned char oChar_t; -typedef unsigned char domain_t; - - -/* CAN bit rates - * - * CAN bit rates are initializers for array of eight CO_CANbitRateData_t - * objects. - * - * Macros are not used by driver itself, they may be used by application with - * combination with object CO_CANbitRateData_t. - * Application must declare following global variable depending on CO_FCY used: - * const CO_CANbitRateData_t CO_CANbitRateData[8] = {CO_CANbitRateDataInitializers}; - * - * There are initializers for eight objects, which corresponds to following - * CAN bit rates (in kbps): 10, 20, 50, 125, 250, 500, 800, 1000. - * - * CO_FCY is internal instruction cycle clock frequency in kHz units. See - * dsPIC33F documentation for more information on FCY. - * - * Possible values for FCY are (in three groups): - * - Optimal CAN bit timing on all Baud Rates: 8000, 12000, 16000, 24000. - * - Not so optimal CAN bit timing on all Baud Rates: 4000, 32000. - * - not all CANopen Baud Rates possible: 2000, 3000, 5000, 6000, 10000, - * 20000, 40000, 48000, 56000, 64000, 70000. - * - * IMPORTANT: For FCY<=12000 there is unresolved bug; CANCKS configuration - * bit on ECAN does not work, so some baudrates are not possible. - */ -#ifdef CO_FCY - /* Macros, which divides K into (SJW + PROP + PhSeg1 + PhSeg2) */ - #define TQ_x_4 1, 1, 1, 1 - #define TQ_x_5 1, 1, 2, 1 - #define TQ_x_6 1, 1, 3, 1 - #define TQ_x_8 1, 2, 3, 2 - #define TQ_x_9 1, 2, 4, 2 - #define TQ_x_10 1, 3, 4, 2 - #define TQ_x_12 1, 3, 6, 2 - #define TQ_x_14 1, 4, 7, 2 - #define TQ_x_15 1, 4, 8, 2 /* good timing */ - #define TQ_x_16 1, 5, 8, 2 /* good timing */ - #define TQ_x_17 1, 6, 8, 2 /* good timing */ - #define TQ_x_18 1, 7, 8, 2 /* good timing */ - #define TQ_x_19 1, 8, 8, 2 /* good timing */ - #define TQ_x_20 1, 8, 8, 3 /* good timing */ - #define TQ_x_21 1, 8, 8, 4 - #define TQ_x_25 1, 8, 8, 8 - - #if CO_FCY == 2000 - #define CO_CANbitRateDataInitializers \ - {1, 5, TQ_x_20}, /*CAN=10kbps*/ \ - {2, 5, TQ_x_20}, /*CAN=20kbps*/ \ - {1, 1, TQ_x_20}, /*CAN=50kbps*/ \ - {2, 1, TQ_x_16}, /*CAN=125kbps*/ \ - {2, 1, TQ_x_8 }, /*CAN=250kbps*/ \ - {2, 1, TQ_x_4 }, /*CAN=500kbps*/ \ - {2, 1, TQ_x_4 }, /*Not possible*/ \ - {2, 1, TQ_x_4 } /*Not possible*/ - #elif CO_FCY == 3000 - #define CO_CANbitRateDataInitializers \ - {2, 15, TQ_x_20}, /*CAN=10kbps*/ \ - {1, 5, TQ_x_15}, /*CAN=20kbps*/ \ - {1, 2, TQ_x_15}, /*CAN=50kbps*/ \ - {1, 1, TQ_x_12}, /*CAN=125kbps*/ \ - {2, 1, TQ_x_12}, /*CAN=250kbps*/ \ - {2, 1, TQ_x_6 }, /*CAN=500kbps*/ \ - {2, 1, TQ_x_6 }, /*Not possible*/ \ - {2, 1, TQ_x_6 } /*Not possible*/ - #elif CO_FCY == 4000 - #define CO_CANbitRateDataInitializers \ - {2, 25, TQ_x_16}, /*CAN=10kbps*/ \ - {1, 5, TQ_x_20}, /*CAN=20kbps*/ \ - {2, 5, TQ_x_16}, /*CAN=50kbps*/ \ - {1, 1, TQ_x_16}, /*CAN=125kbps*/ \ - {2, 1, TQ_x_16}, /*CAN=250kbps*/ \ - {2, 1, TQ_x_8 }, /*CAN=500kbps*/ \ - {2, 1, TQ_x_5 }, /*CAN=800kbps*/ \ - {2, 1, TQ_x_4 } /*CAN=1000kbps*/ - #elif CO_FCY == 5000 - #define CO_CANbitRateDataInitializers \ - {2, 25, TQ_x_20}, /*CAN=10kbps*/ \ - {1, 5, TQ_x_25}, /*CAN=20kbps*/ \ - {2, 5, TQ_x_20}, /*CAN=50kbps*/ \ - {1, 1, TQ_x_20}, /*CAN=125kbps*/ \ - {2, 1, TQ_x_20}, /*CAN=250kbps*/ \ - {2, 1, TQ_x_10}, /*CAN=500kbps*/ \ - {2, 1, TQ_x_10}, /*Not possible*/ \ - {2, 1, TQ_x_5 } /*CAN=1000kbps*/ - #elif CO_FCY == 6000 - #define CO_CANbitRateDataInitializers \ - {1, 20, TQ_x_15}, /*CAN=10kbps*/ \ - {1, 10, TQ_x_15}, /*CAN=20kbps*/ \ - {1, 4, TQ_x_15}, /*CAN=50kbps*/ \ - {2, 3, TQ_x_16}, /*CAN=125kbps*/ \ - {1, 1, TQ_x_12}, /*CAN=250kbps*/ \ - {2, 1, TQ_x_12}, /*CAN=500kbps*/ \ - {2, 1, TQ_x_12}, /*Not possible*/ \ - {2, 1, TQ_x_6 } /*CAN=1000kbps*/ - #elif CO_FCY == 8000 - #define CO_CANbitRateDataInitializers \ - {1, 25, TQ_x_16}, /*CAN=10kbps*/ \ - {2, 25, TQ_x_16}, /*CAN=20kbps*/ \ - {1, 5, TQ_x_16}, /*CAN=50kbps*/ \ - {1, 2, TQ_x_16}, /*CAN=125kbps*/ \ - {1, 1, TQ_x_16}, /*CAN=250kbps*/ \ - {2, 1, TQ_x_16}, /*CAN=500kbps*/ \ - {2, 1, TQ_x_10}, /*CAN=800kbps*/ \ - {2, 1, TQ_x_8 } /*CAN=1000kbps*/ - #elif CO_FCY == 10000 - #define CO_CANbitRateDataInitializers \ - {1, 25, TQ_x_20}, /*CAN=10kbps*/ \ - {2, 25, TQ_x_20}, /*CAN=20kbps*/ \ - {1, 5, TQ_x_20}, /*CAN=50kbps*/ \ - {2, 5, TQ_x_16}, /*CAN=125kbps*/ \ - {1, 1, TQ_x_20}, /*CAN=250kbps*/ \ - {2, 1, TQ_x_20}, /*CAN=500kbps*/ \ - {2, 1, TQ_x_20}, /*Not possible*/ \ - {2, 1, TQ_x_10} /*CAN=1000kbps*/ - #elif CO_FCY == 12000 - #define CO_CANbitRateDataInitializers \ - {2, 63, TQ_x_19}, /*CAN=10kbps*/ \ - {1, 20, TQ_x_15}, /*CAN=20kbps*/ \ - {2, 15, TQ_x_16}, /*CAN=50kbps*/ \ - {1, 3, TQ_x_16}, /*CAN=125kbps*/ \ - {2, 3, TQ_x_16}, /*CAN=250kbps*/ \ - {1, 1, TQ_x_12}, /*CAN=500kbps*/ \ - {2, 1, TQ_x_15}, /*CAN=800kbps*/ \ - {2, 1, TQ_x_12} /*CAN=1000kbps*/ - #elif CO_FCY == 16000 - #define CO_CANbitRateDataInitializers \ - {1, 50, TQ_x_16}, /*CAN=10kbps*/ \ - {1, 25, TQ_x_16}, /*CAN=20kbps*/ \ - {1, 10, TQ_x_16}, /*CAN=50kbps*/ \ - {1, 4, TQ_x_16}, /*CAN=125kbps*/ \ - {1, 2, TQ_x_16}, /*CAN=250kbps*/ \ - {1, 1, TQ_x_16}, /*CAN=500kbps*/ \ - {1, 1, TQ_x_10}, /*CAN=800kbps*/ \ - {1, 1, TQ_x_8 } /*CAN=1000kbps*/ - #elif CO_FCY == 20000 - #define CO_CANbitRateDataInitializers \ - {1, 50, TQ_x_20}, /*CAN=10kbps*/ \ - {1, 25, TQ_x_20}, /*CAN=20kbps*/ \ - {1, 10, TQ_x_20}, /*CAN=50kbps*/ \ - {1, 5, TQ_x_16}, /*CAN=125kbps*/ \ - {1, 2, TQ_x_20}, /*CAN=250kbps*/ \ - {1, 1, TQ_x_20}, /*CAN=500kbps*/ \ - {1, 1, TQ_x_20}, /*Not possible*/ \ - {1, 1, TQ_x_10} /*CAN=1000kbps*/ - #elif CO_FCY == 24000 - #define CO_CANbitRateDataInitializers \ - {1, 63, TQ_x_19}, /*CAN=10kbps*/ \ - {1, 40, TQ_x_15}, /*CAN=20kbps*/ \ - {1, 15, TQ_x_16}, /*CAN=50kbps*/ \ - {1, 6, TQ_x_16}, /*CAN=125kbps*/ \ - {1, 3, TQ_x_16}, /*CAN=250kbps*/ \ - {1, 2, TQ_x_12}, /*CAN=500kbps*/ \ - {1, 1, TQ_x_15}, /*CAN=800kbps*/ \ - {1, 1, TQ_x_12} /*CAN=1000kbps*/ - #elif CO_FCY == 32000 - #define CO_CANbitRateDataInitializers \ - {1, 64, TQ_x_25}, /*CAN=10kbps*/ \ - {1, 50, TQ_x_16}, /*CAN=20kbps*/ \ - {1, 20, TQ_x_16}, /*CAN=50kbps*/ \ - {1, 8, TQ_x_16}, /*CAN=125kbps*/ \ - {1, 4, TQ_x_16}, /*CAN=250kbps*/ \ - {1, 2, TQ_x_16}, /*CAN=500kbps*/ \ - {1, 2, TQ_x_10}, /*CAN=800kbps*/ \ - {1, 1, TQ_x_16} /*CAN=1000kbps*/ - #elif CO_FCY == 40000 - #define CO_CANbitRateDataInitializers \ - {1, 50, TQ_x_20}, /*Not possible*/ \ - {1, 50, TQ_x_20}, /*CAN=20kbps*/ \ - {1, 25, TQ_x_16}, /*CAN=50kbps*/ \ - {1, 10, TQ_x_16}, /*CAN=125kbps*/ \ - {1, 5, TQ_x_16}, /*CAN=250kbps*/ \ - {1, 2, TQ_x_20}, /*CAN=500kbps*/ \ - {1, 1, TQ_x_25}, /*CAN=800kbps*/ \ - {1, 1, TQ_x_20} /*CAN=1000kbps*/ - #elif CO_FCY == 48000 - #define CO_CANbitRateDataInitializers \ - {1, 63, TQ_x_19}, /*Not possible*/ \ - {1, 63, TQ_x_19}, /*CAN=20kbps*/ \ - {1, 30, TQ_x_16}, /*CAN=50kbps*/ \ - {1, 12, TQ_x_16}, /*CAN=125kbps*/ \ - {1, 6, TQ_x_16}, /*CAN=250kbps*/ \ - {1, 3, TQ_x_16}, /*CAN=500kbps*/ \ - {1, 2, TQ_x_15}, /*CAN=800kbps*/ \ - {1, 2, TQ_x_12} /*CAN=1000kbps*/ - #elif CO_FCY == 56000 - #define CO_CANbitRateDataInitializers \ - {1, 61, TQ_x_23}, /*Not possible*/ \ - {1, 61, TQ_x_23}, /*CAN=20kbps*/ \ - {1, 35, TQ_x_16}, /*CAN=50kbps*/ \ - {1, 14, TQ_x_16}, /*CAN=125kbps*/ \ - {1, 7, TQ_x_16}, /*CAN=250kbps*/ \ - {1, 4, TQ_x_14}, /*CAN=500kbps*/ \ - {1, 5, TQ_x_7 }, /*CAN=800kbps*/ \ - {1, 2, TQ_x_14} /*CAN=1000kbps*/ - #elif CO_FCY == 64000 - #define CO_CANbitRateDataInitializers \ - {1, 64, TQ_x_25}, /*Not possible*/ \ - {1, 64, TQ_x_25}, /*CAN=20kbps*/ \ - {1, 40, TQ_x_16}, /*CAN=50kbps*/ \ - {1, 16, TQ_x_16}, /*CAN=125kbps*/ \ - {1, 8, TQ_x_16}, /*CAN=250kbps*/ \ - {1, 4, TQ_x_16}, /*CAN=500kbps*/ \ - {1, 2, TQ_x_20}, /*CAN=800kbps*/ \ - {1, 2, TQ_x_16} /*CAN=1000kbps*/ - #elif CO_FCY == 70000 - #define CO_CANbitRateDataInitializers \ - {1, 64, TQ_x_25}, /*Not possible*/ \ - {1, 64, TQ_x_25}, /*CAN=20kbps*/ \ - {1, 35, TQ_x_20}, /*CAN=50kbps*/ \ - {1, 14, TQ_x_20}, /*CAN=125kbps*/ \ - {1, 7, TQ_x_20}, /*CAN=250kbps*/ \ - {1, 5, TQ_x_14}, /*CAN=500kbps*/ \ - {1, 3, TQ_x_15}, /*Not working*/ \ - {1, 2, TQ_x_17} /*Not working*/ - #else - #error define_CO_FCY CO_FCY not supported - #endif /* CO_FCY == */ -#endif /* CO_FCY */ - - -/* Structure contains timing coefficients for CAN module. - * - * CAN baud rate is calculated from following equations: - * FCAN = FCY * Scale - Input frequency to CAN module (MAX 40MHz for dsPIC33F/PIC24H and 70MHz for dsPIC33E/PIC24E) - * TQ = 2 * BRP / FCAN - Time Quanta - * BaudRate = 1 / (TQ * K) - Can bus Baud Rate - * K = SJW + PROP + PhSeg1 + PhSeg2 - Number of Time Quantas - */ -typedef struct{ - uint8_t scale; /* (1 or 2) Scales FCY clock - dsPIC33F and PIC24H specific */ - uint8_t BRP; /* (1...64) Baud Rate Prescaler */ - uint8_t SJW; /* (1...4) SJW time */ - uint8_t PROP; /* (1...8) PROP time */ - uint8_t phSeg1; /* (1...8) Phase Segment 1 time */ - uint8_t phSeg2; /* (1...8) Phase Segment 2 time */ -}CO_CANbitRateData_t; - - -/* CAN receive message structure as aligned in CAN module. - * In dsPIC33F and PIC24H this structure is used for both: transmitting and - * receiving to and from CAN module. (Object is ownded by CAN module). - */ -typedef struct{ - uint16_t ident; /* Standard Identifier as aligned in CAN module. 16 bits: - 'UUUSSSSS SSSSSSRE' (U: unused; S: SID; R=SRR; E=IDE). */ - uint16_t extIdent; /* Extended identifier, not used here */ - uint16_t DLC :4; /* Data length code (bits 0...3) */ - uint16_t DLCrest :12; /* Not used here (bits 4..15) */ - uint8_t data[8]; /* 8 data bytes */ - uint8_t dummy; /* Not used */ - uint8_t FILHIT; /* Filter hit */ -}CO_CANrxMsg_t; - - -/* Received message object */ -typedef struct{ - uint16_t ident; - uint16_t mask; - void *object; - void (*pFunct)(void *object, const CO_CANrxMsg_t *message); -}CO_CANrx_t; - - -/* Transmit message object. */ -typedef struct{ - uint16_t ident; /* Standard Identifier as aligned in CAN module. 16 bits: - 'SSSSSUUU SSSSSSRE' (U: unused; S: SID; R=SRR; E=IDE). */ - uint8_t DLC; - uint8_t data[8]; - volatile bool_t bufferFull; - volatile bool_t syncFlag; -}CO_CANtx_t; - - -/* CAN module object. */ -typedef struct{ - void *CANdriverState; - __eds__ CO_CANrxMsg_t *CANmsgBuff; /* dsPIC33F specific: CAN message buffer for CAN module */ - uint8_t CANmsgBuffSize; /* dsPIC33F specific: Size of the above buffer */ - CO_CANrx_t *rxArray; - uint16_t rxSize; - CO_CANtx_t *txArray; - uint16_t txSize; - volatile bool_t CANnormal; - volatile bool_t useCANrxFilters; - volatile bool_t bufferInhibitFlag; - volatile bool_t firstCANtxMessage; - volatile uint16_t CANtxCount; - uint16_t errOld; - void *em; -}CO_CANmodule_t; - - -/* CAN interrupt receives and transmits CAN messages. - * - * Function must be called directly from _C1Interrupt or _C2Interrupt with - * high priority. - */ -void CO_CANinterrupt(CO_CANmodule_t *CANmodule); - - -#endif /* CO_DRIVER_TARGET_H */ diff --git a/stack/PIC32/CO_driver.c b/stack/PIC32/CO_driver.c deleted file mode 100644 index 68a6f28b..00000000 --- a/stack/PIC32/CO_driver.c +++ /dev/null @@ -1,595 +0,0 @@ -/* - * CAN module object for Microchip PIC32MX microcontroller. - * - * @file CO_driver.c - * @author Janez Paternoster - * @copyright 2004 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include "CO_driver.h" -#include "CO_Emergency.h" - - -extern const CO_CANbitRateData_t CO_CANbitRateData[8]; -unsigned int CO_interruptStatus = 0; - - -/** - * Macro and Constants - CAN module registers - */ - #define CAN_REG(base, offset) (*((volatile uint32_t *) (((uintptr_t) base) + _CAN1_BASE_ADDRESS + (offset)))) - - #define CLR 0x04 - #define SET 0x08 - #define INV 0x0C - - #define C_CON 0x000 /* Control Register */ - #define C_CFG 0x010 /* Baud Rate Configuration Register */ - #define C_INT 0x020 /* Interrupt Register */ - #define C_VEC 0x030 /* Interrupt Code Register */ - #define C_TREC 0x040 /* Transmit/Receive Error Counter Register */ - #define C_FSTAT 0x050 /* FIFO Status Register */ - #define C_RXOVF 0x060 /* Receive FIFO Overflow Status Register */ - #define C_TMR 0x070 /* CAN Timer Register */ - #define C_RXM 0x080 /* + (0..3 x 0x10) //Acceptance Filter Mask Register */ - #define C_FLTCON 0x0C0 /* + (0..7(3) x 0x10) //Filter Control Register */ - #define C_RXF 0x140 /* + (0..31(15) x 0x10) //Acceptance Filter Register */ - #define C_FIFOBA 0x340 /* Message Buffer Base Address Register */ - #define C_FIFOCON 0x350 /* + (0..31(15) x 0x40) //FIFO Control Register */ - #define C_FIFOINT 0x360 /* + (0..31(15) x 0x40) //FIFO Interrupt Register */ - #define C_FIFOUA 0x370 /* + (0..31(15) x 0x40) //FIFO User Address Register */ - #define C_FIFOCI 0x380 /* + (0..31(15) x 0x40) //Module Message Index Register */ - - -/* Number of hardware filters */ -/* device PIC32MX530, 550 and 570 has only 16 registers for CAN reception (not 32). */ -#ifdef __PIC32MX -#if (__PIC32_FEATURE_SET__ == 530) || (__PIC32_FEATURE_SET__ == 550) || (__PIC32_FEATURE_SET__ == 570) - #define NO_CAN_RXF 16 -#endif -#endif -#ifndef NO_CAN_RXF - #define NO_CAN_RXF 32 -#endif - - -/******************************************************************************/ -void CO_CANsetConfigurationMode(void *CANdriverState){ - uint32_t C_CONcopy = CAN_REG(CANdriverState, C_CON); - - /* switch ON can module */ - C_CONcopy |= 0x00008000; - CAN_REG(CANdriverState, C_CON) = C_CONcopy; - - /* request configuration mode */ - C_CONcopy &= 0xF8FFFFFF; - C_CONcopy |= 0x04000000; - CAN_REG(CANdriverState, C_CON) = C_CONcopy; - - /* wait for configuration mode */ - while((CAN_REG(CANdriverState, C_CON) & 0x00E00000) != 0x00800000); -} - - -/******************************************************************************/ -void CO_CANsetNormalMode(CO_CANmodule_t *CANmodule){ - - /* request normal mode */ - CAN_REG(CANmodule->CANdriverState, C_CON+CLR) = 0x07000000; - - /* wait for normal mode */ - while((CAN_REG(CANmodule->CANdriverState, C_CON) & 0x00E00000) != 0x00000000); - - CANmodule->CANnormal = true; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_CANmodule_init( - CO_CANmodule_t *CANmodule, - void *CANdriverState, - CO_CANrx_t rxArray[], - uint16_t rxSize, - CO_CANtx_t txArray[], - uint16_t txSize, - uint16_t CANbitRate) -{ - uint16_t i; - - /* verify arguments */ - if(CANmodule==NULL || rxArray==NULL || txArray==NULL){ - return CO_ERROR_ILLEGAL_ARGUMENT; - } - - /* Configure object variables */ - CANmodule->CANdriverState = CANdriverState; - CANmodule->CANmsgBuffSize = 33; /* Must be the same as size of CANmodule->CANmsgBuff array. */ - CANmodule->rxArray = rxArray; - CANmodule->rxSize = rxSize; - CANmodule->txArray = txArray; - CANmodule->txSize = txSize; - CANmodule->CANnormal = false; - CANmodule->useCANrxFilters = (rxSize <= NO_CAN_RXF) ? true : false; - CANmodule->bufferInhibitFlag = false; - CANmodule->firstCANtxMessage = true; - CANmodule->CANtxCount = 0U; - CANmodule->errOld = 0U; - CANmodule->em = NULL; - - for(i=0U; iCANmsgBuff; - for(i=0; i<(CANmodule->CANmsgBuffSize*4); i++){ - *(f++) = 0; - } - - - /* Configure control register (configuration mode, receive timer stamp is enabled, module is on) */ - CAN_REG(CANdriverState, C_CON) = 0x04108000; - - - /* Configure FIFO */ - CAN_REG(CANdriverState, C_FIFOBA) = CO_KVA_TO_PA(CANmodule->CANmsgBuff);/* FIFO base address */ - CAN_REG(CANdriverState, C_FIFOCON) = (NO_CAN_RXF==32) ? 0x001F0000 : 0x000F0000; /* FIFO0: receive FIFO, 32(16) buffers */ - CAN_REG(CANdriverState, C_FIFOCON+0x40) = 0x00000080;/* FIFO1: transmit FIFO, 1 buffer */ - - - /* Configure CAN timing */ - switch(CANbitRate){ - case 10: i=0; break; - case 20: i=1; break; - case 50: i=2; break; - default: - case 125: i=3; break; - case 250: i=4; break; - case 500: i=5; break; - case 800: i=6; break; - case 1000: i=7; break; - } - CAN_REG(CANdriverState, C_CFG) = - ((uint32_t)(CO_CANbitRateData[i].phSeg2 - 1)) << 16 | /* SEG2PH */ - 0x00008000 | /* SEG2PHTS = 1, SAM = 0 */ - ((uint32_t)(CO_CANbitRateData[i].phSeg1 - 1)) << 11 | /* SEG1PH */ - ((uint32_t)(CO_CANbitRateData[i].PROP - 1)) << 8 | /* PRSEG */ - ((uint32_t)(CO_CANbitRateData[i].SJW - 1)) << 6 | /* SJW */ - ((uint32_t)(CO_CANbitRateData[i].BRP - 1)); /* BRP */ - - - /* CAN module hardware filters */ - /* clear all filter control registers (disable filters, mask 0 and FIFO 0 selected for all filters) */ - for(i=0; i<(NO_CAN_RXF/4); i++) - CAN_REG(CANdriverState, C_FLTCON+i*0x10) = 0x00000000; - if(CANmodule->useCANrxFilters){ - /* CAN module filters are used, they will be configured with */ - /* CO_CANrxBufferInit() functions, called by separate CANopen */ - /* init functions. */ - /* Configure all masks so, that received message must match filter */ - CAN_REG(CANdriverState, C_RXM) = 0xFFE80000; - CAN_REG(CANdriverState, C_RXM+0x10) = 0xFFE80000; - CAN_REG(CANdriverState, C_RXM+0x20) = 0xFFE80000; - CAN_REG(CANdriverState, C_RXM+0x30) = 0xFFE80000; - } - else{ - /* CAN module filters are not used, all messages with standard 11-bit */ - /* identifier will be received */ - /* Configure mask 0 so, that all messages with standard identifier are accepted */ - CAN_REG(CANdriverState, C_RXM) = 0x00080000; - /* configure one filter for FIFO 0 and enable it */ - CAN_REG(CANdriverState, C_RXF) = 0x00000000; - CAN_REG(CANdriverState, C_FLTCON) = 0x00000080; - } - - - /* CAN interrupt registers */ - /* Enable 'RX buffer not empty' (RXNEMPTYIE) interrupt in FIFO 0 (third layer interrupt) */ - CAN_REG(CANdriverState, C_FIFOINT) = 0x00010000; - /* Enable 'Tx buffer empty' (TXEMPTYIE) interrupt in FIFO 1 (third layer interrupt) */ - CAN_REG(CANdriverState, C_FIFOINT+0x40) = 0x00000000; /* will be enabled in CO_CANsend */ - /* Enable receive (RBIE) and transmit (TBIE) buffer interrupt (secont layer interrupt) */ - CAN_REG(CANdriverState, C_INT) = 0x00030000; - /* CAN interrupt (first layer) must be configured by application */ - - return CO_ERROR_NO; -} - - -/******************************************************************************/ -void CO_CANmodule_disable(CO_CANmodule_t *CANmodule){ - CO_CANsetConfigurationMode(CANmodule->CANdriverState); -} - - -/******************************************************************************/ -uint16_t CO_CANrxMsg_readIdent(const CO_CANrxMsg_t *rxMsg){ - return rxMsg->ident; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_CANrxBufferInit( - CO_CANmodule_t *CANmodule, - uint16_t index, - uint16_t ident, - uint16_t mask, - bool_t rtr, - void *object, - void (*pFunct)(void *object, const CO_CANrxMsg_t *message)) -{ - CO_ReturnError_t ret = CO_ERROR_NO; - - if((CANmodule!=NULL) && (object!=NULL) && (pFunct!=NULL) && (index < CANmodule->rxSize)){ - /* buffer, which will be configured */ - CO_CANrx_t *buffer = &CANmodule->rxArray[index]; - - /* Configure object variables */ - buffer->object = object; - buffer->pFunct = pFunct; - - /* CAN identifier and CAN mask, bit aligned with CAN module FIFO buffers (RTR is extra) */ - buffer->ident = ident & 0x07FFU; - if(rtr){ - buffer->ident |= 0x0800U; - } - buffer->mask = (mask & 0x07FFU) | 0x0800U; - - /* Set CAN hardware module filter and mask. */ - if(CANmodule->useCANrxFilters){ - uint32_t RXF, RXM; - volatile uint32_t *pRXF; - volatile uint32_t *pRXM0, *pRXM1, *pRXM2, *pRXM3; - volatile uint8_t *pFLTCON; - uint8_t selectMask; - uint16_t addr = CANmodule->CANdriverState; - - /* get correct part of the filter control register */ - pFLTCON = (volatile uint8_t*)(&CAN_REG(addr, C_FLTCON)); /* pointer to first filter control register */ - pFLTCON += (index/4) * 0x10; /* now points to the correct C_FLTCONi */ - pFLTCON += index%4; /* now points to correct part of the correct C_FLTCONi */ - - /* disable filter and wait if necessary */ - while(*pFLTCON & 0x80) *pFLTCON &= 0x7F; - - /* align RXF and RXM with C_RXF and C_RXM registers */ - RXF = (uint32_t)ident << 21; - RXM = (uint32_t)mask << 21 | 0x00080000; - - /* write to filter */ - pRXF = &CAN_REG(addr, C_RXF); /* pointer to first filter register */ - pRXF += index * (0x10/4); /* now points to C_RXFi (i == index) */ - *pRXF = RXF; /* write value to filter */ - - /* configure mask (There are four masks, each of them can be asigned to any filter. */ - /* First mask has always the value 0xFFE80000 - all 11 bits must match). */ - pRXM0 = &CAN_REG(addr, C_RXM); - pRXM1 = &CAN_REG(addr, C_RXM+0x10); - pRXM2 = &CAN_REG(addr, C_RXM+0x20); - pRXM3 = &CAN_REG(addr, C_RXM+0x30); - if(RXM == *pRXM0){ - selectMask = 0; - } - else if(RXM == *pRXM1 || *pRXM1 == 0xFFE80000){ - /* RXM is equal to mask 1 or mask 1 was not yet configured. */ - *pRXM1 = RXM; - selectMask = 1; - } - else if(RXM == *pRXM2 || *pRXM2 == 0xFFE80000){ - /* RXM is equal to mask 2 or mask 2 was not yet configured. */ - *pRXM2 = RXM; - selectMask = 2; - } - else if(RXM == *pRXM3 || *pRXM3 == 0xFFE80000){ - /* RXM is equal to mask 3 or mask 3 was not yet configured. */ - *pRXM3 = RXM; - selectMask = 3; - } - else{ - /* not enough masks */ - selectMask = 0; - ret = CO_ERROR_OUT_OF_MEMORY; - } - /* write to appropriate filter control register */ - *pFLTCON = 0x80 | (selectMask << 5); /* enable filter and write filter mask select bit */ - } - } - else{ - ret = CO_ERROR_ILLEGAL_ARGUMENT; - } - - return ret; -} - - -/******************************************************************************/ -CO_CANtx_t *CO_CANtxBufferInit( - CO_CANmodule_t *CANmodule, - uint16_t index, - uint16_t ident, - bool_t rtr, - uint8_t noOfBytes, - bool_t syncFlag) -{ - CO_CANtx_t *buffer = NULL; - - if((CANmodule != NULL) && (index < CANmodule->txSize)){ - /* get specific buffer */ - buffer = &CANmodule->txArray[index]; - - /* CAN identifier, DLC and rtr, bit aligned with CAN module transmit buffer */ - buffer->CMSGSID = ident & 0x07FF; - buffer->CMSGEID = (noOfBytes & 0xF) | (rtr?0x0200:0); - - buffer->bufferFull = false; - buffer->syncFlag = syncFlag; - } - - return buffer; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_CANsend(CO_CANmodule_t *CANmodule, CO_CANtx_t *buffer){ - CO_ReturnError_t err = CO_ERROR_NO; - uint16_t addr = CANmodule->CANdriverState; - volatile uint32_t* TX_FIFOcon = &CAN_REG(addr, C_FIFOCON+0x40); - volatile uint32_t* TX_FIFOconSet = &CAN_REG(addr, C_FIFOCON+0x48); - uint32_t* TXmsgBuffer = CO_PA_TO_KVA1(CAN_REG(addr, C_FIFOUA+0x40)); - uint32_t* message = (uint32_t*) buffer; - uint32_t TX_FIFOconCopy; - - /* Verify overflow */ - if(buffer->bufferFull){ - if(!CANmodule->firstCANtxMessage){ - /* don't set error, if bootup message is still on buffers */ - CO_errorReport((CO_EM_t*)CANmodule->em, CO_EM_CAN_TX_OVERFLOW, CO_EMC_CAN_OVERRUN, buffer->CMSGSID); - } - err = CO_ERROR_TX_OVERFLOW; - } - - CO_LOCK_CAN_SEND(); - TX_FIFOconCopy = *TX_FIFOcon; - /* if CAN TX buffer is free, copy message to it */ - if((TX_FIFOconCopy & 0x8) == 0 && CANmodule->CANtxCount == 0){ - CANmodule->bufferInhibitFlag = buffer->syncFlag; - *(TXmsgBuffer++) = *(message++); - *(TXmsgBuffer++) = *(message++); - *(TXmsgBuffer++) = *(message++); - *(TXmsgBuffer++) = *(message++); - /* if message was aborted, don't set UINC */ - if((TX_FIFOconCopy & 0x40) == 0) - *TX_FIFOconSet = 0x2000; /* set UINC */ - *TX_FIFOconSet = 0x0008; /* set TXREQ */ - } - /* if no buffer is free, message will be sent by interrupt */ - else{ - buffer->bufferFull = true; - CANmodule->CANtxCount++; - } - /* Enable 'Tx buffer empty' (TXEMPTYIE) interrupt in FIFO 1 (third layer interrupt) */ - CAN_REG(addr, C_FIFOINT+0x48) = 0x01000000; - CO_UNLOCK_CAN_SEND(); - - return err; -} - - -/******************************************************************************/ -void CO_CANclearPendingSyncPDOs(CO_CANmodule_t *CANmodule){ - uint32_t tpdoDeleted = 0U; - volatile uint32_t* TX_FIFOcon = &CAN_REG(CANmodule->CANdriverState, C_FIFOCON+0x40); - volatile uint32_t* TX_FIFOconClr = &CAN_REG(CANmodule->CANdriverState, C_FIFOCON+0x44); - - CO_LOCK_CAN_SEND(); - /* Abort message from CAN module, if there is synchronous TPDO. - * Take special care with this functionality. */ - if((*TX_FIFOcon & 0x8) && CANmodule->bufferInhibitFlag){ - *TX_FIFOconClr = 0x0008; /* clear TXREQ */ - CANmodule->bufferInhibitFlag = false; - tpdoDeleted = 1U; - } - /* delete also pending synchronous TPDOs in TX buffers */ - if(CANmodule->CANtxCount != 0U){ - uint16_t i; - CO_CANtx_t *buffer = &CANmodule->txArray[0]; - for(i = CANmodule->txSize; i > 0U; i--){ - if(buffer->bufferFull){ - if(buffer->syncFlag){ - buffer->bufferFull = false; - CANmodule->CANtxCount--; - tpdoDeleted = 2U; - } - } - buffer++; - } - } - CO_UNLOCK_CAN_SEND(); - - - if(tpdoDeleted != 0U){ - CO_errorReport((CO_EM_t*)CANmodule->em, CO_EM_TPDO_OUTSIDE_WINDOW, CO_EMC_COMMUNICATION, tpdoDeleted); - } -} - - -/******************************************************************************/ -void CO_CANverifyErrors(CO_CANmodule_t *CANmodule){ - uint16_t rxErrors, txErrors, overflow; - uint32_t TREC; - CO_EM_t* em = (CO_EM_t*)CANmodule->em; - uint32_t err; - - TREC = CAN_REG(CANmodule->CANdriverState, C_TREC); - rxErrors = (uint8_t) TREC; - txErrors = (uint8_t) (TREC>>8); - if(TREC&0x00200000) txErrors = 256; /* bus off */ - overflow = (CAN_REG(CANmodule->CANdriverState, C_FIFOINT)&0x8) ? 1 : 0; - - err = ((uint32_t)txErrors << 16) | ((uint32_t)rxErrors << 8) | overflow; - - if(CANmodule->errOld != err){ - CANmodule->errOld = err; - - if(txErrors >= 256U){ /* bus off */ - CO_errorReport(em, CO_EM_CAN_TX_BUS_OFF, CO_EMC_BUS_OFF_RECOVERED, err); - } - else{ /* not bus off */ - CO_errorReset(em, CO_EM_CAN_TX_BUS_OFF, err); - - if((rxErrors >= 96U) || (txErrors >= 96U)){ /* bus warning */ - CO_errorReport(em, CO_EM_CAN_BUS_WARNING, CO_EMC_NO_ERROR, err); - } - - if(rxErrors >= 128U){ /* RX bus passive */ - CO_errorReport(em, CO_EM_CAN_RX_BUS_PASSIVE, CO_EMC_CAN_PASSIVE, err); - } - else{ - CO_errorReset(em, CO_EM_CAN_RX_BUS_PASSIVE, err); - } - - if(txErrors >= 128U){ /* TX bus passive */ - if(!CANmodule->firstCANtxMessage){ - CO_errorReport(em, CO_EM_CAN_TX_BUS_PASSIVE, CO_EMC_CAN_PASSIVE, err); - } - } - else{ - bool_t isError = CO_isError(em, CO_EM_CAN_TX_BUS_PASSIVE); - if(isError){ - CO_errorReset(em, CO_EM_CAN_TX_BUS_PASSIVE, err); - CO_errorReset(em, CO_EM_CAN_TX_OVERFLOW, err); - } - } - - if((rxErrors < 96U) && (txErrors < 96U)){ /* no error */ - CO_errorReset(em, CO_EM_CAN_BUS_WARNING, err); - } - } - - if(overflow != 0U){ /* CAN RX bus overflow */ - CO_errorReport(em, CO_EM_CAN_RXB_OVERFLOW, CO_EMC_CAN_OVERRUN, err); - } - } -} - - -/******************************************************************************/ -void CO_CANinterrupt(CO_CANmodule_t *CANmodule){ - uint8_t ICODE; - ICODE = (uint8_t) CAN_REG(CANmodule->CANdriverState, C_VEC) & 0x7F; - - /* receive interrupt (New CAN message is available in RX FIFO 0 buffer) */ - if(ICODE == 0){ - CO_CANrxMsg_t *rcvMsg; /* pointer to received message in CAN module */ - uint16_t index; /* index of received message */ - uint16_t rcvMsgIdent; /* identifier of the received message */ - CO_CANrx_t *buffer = NULL; /* receive message buffer from CO_CANmodule_t object. */ - bool_t msgMatched = false; - - rcvMsg = (CO_CANrxMsg_t*) CO_PA_TO_KVA1(CAN_REG(CANmodule->CANdriverState, C_FIFOUA)); - rcvMsgIdent = rcvMsg->ident; - if(rcvMsg->RTR) rcvMsgIdent |= 0x0800; - if(CANmodule->useCANrxFilters){ - /* CAN module filters are used. Message with known 11-bit identifier has */ - /* been received */ - index = rcvMsg->FILHIT; - if(index < CANmodule->rxSize){ - buffer = &CANmodule->rxArray[index]; - /* verify also RTR */ - if(((rcvMsgIdent ^ buffer->ident) & buffer->mask) == 0U){ - msgMatched = true; - } - } - } - else{ - /* CAN module filters are not used, message with any standard 11-bit identifier */ - /* has been received. Search rxArray form CANmodule for the same CAN-ID. */ - buffer = &CANmodule->rxArray[0]; - for(index = CANmodule->rxSize; index > 0U; index--){ - if(((rcvMsgIdent ^ buffer->ident) & buffer->mask) == 0U){ - msgMatched = true; - break; - } - buffer++; - } - } - - /* Call specific function, which will process the message */ - if(msgMatched && (buffer != NULL) && (buffer->pFunct != NULL)){ - buffer->pFunct(buffer->object, rcvMsg); - } - - /* Update the message buffer pointer */ - CAN_REG(CANmodule->CANdriverState, C_FIFOCON+0x08) = 0x2000; /* set UINC */ - } - - - /* transmit interrupt (TX buffer FIFO 1 is free) */ - else if(ICODE == 1){ - /* First CAN message (bootup) was sent successfully */ - CANmodule->firstCANtxMessage = false; - /* clear flag from previous message */ - CANmodule->bufferInhibitFlag = false; - /* Are there any new messages waiting to be send */ - if(CANmodule->CANtxCount > 0U){ - uint16_t i; /* index of transmitting message */ - - /* first buffer */ - CO_CANtx_t *buffer = &CANmodule->txArray[0]; - /* search through whole array of pointers to transmit message buffers. */ - for(i = CANmodule->txSize; i > 0U; i--){ - /* if message buffer is full, send it. */ - if(buffer->bufferFull){ - buffer->bufferFull = false; - CANmodule->CANtxCount--; - - /* Copy message to CAN buffer */ - CANmodule->bufferInhibitFlag = buffer->syncFlag; - uint32_t* TXmsgBuffer = CO_PA_TO_KVA1(CAN_REG(CANmodule->CANdriverState, C_FIFOUA+0x40)); - uint32_t* message = (uint32_t*) buffer; - volatile uint32_t* TX_FIFOconSet = &CAN_REG(CANmodule->CANdriverState, C_FIFOCON+0x48); - *(TXmsgBuffer++) = *(message++); - *(TXmsgBuffer++) = *(message++); - *(TXmsgBuffer++) = *(message++); - *(TXmsgBuffer++) = *(message++); - *TX_FIFOconSet = 0x2000; /* set UINC */ - *TX_FIFOconSet = 0x0008; /* set TXREQ */ - break; /* exit for loop */ - } - buffer++; - }/* end of for loop */ - - /* Clear counter if no more messages */ - if(i == 0U){ - CANmodule->CANtxCount = 0U; - } - } - - /* if no more messages, disable 'Tx buffer empty' (TXEMPTYIE) interrupt */ - if(CANmodule->CANtxCount == 0U){ - CAN_REG(CANmodule->CANdriverState, C_FIFOINT+0x44) = 0x01000000; - } - } -} diff --git a/stack/PIC32/CO_driver_target.h b/stack/PIC32/CO_driver_target.h deleted file mode 100644 index e0e9c7fa..00000000 --- a/stack/PIC32/CO_driver_target.h +++ /dev/null @@ -1,352 +0,0 @@ -/* - * CAN module object for Microchip PIC32MX microcontroller. - * - * @file CO_driver_target.h - * @author Janez Paternoster - * @copyright 2004 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#ifndef CO_DRIVER_TARGET_H -#define CO_DRIVER_TARGET_H - - -#include /* processor header file */ -#include /* for 'NULL' */ -#include /* for 'int8_t' to 'uint64_t' */ -#include /* for 'true', 'false' */ - -/* Endianness */ -#define CO_LITTLE_ENDIAN - -/* CAN module base address */ -#define ADDR_CAN1 0 -#define ADDR_CAN2 (_CAN2_BASE_ADDRESS - _CAN1_BASE_ADDRESS) - - -/* Translate a kernel virtual address in KSEG0 or KSEG1 to a real -* physical address and back. */ -typedef unsigned long CO_paddr_t; /* a physical address */ -typedef unsigned long CO_vaddr_t; /* a virtual address */ -#define CO_KVA_TO_PA(v) ((CO_paddr_t)(v) & 0x1fffffff) -#define CO_PA_TO_KVA0(pa) ((void *) ((pa) | 0x80000000)) -#define CO_PA_TO_KVA1(pa) ((void *) ((pa) | 0xa0000000)) - - -/* Critical sections */ -extern unsigned int CO_interruptStatus; -#define CO_LOCK_CAN_SEND() CO_interruptStatus = __builtin_disable_interrupts() -#define CO_UNLOCK_CAN_SEND() if(CO_interruptStatus & 0x00000001) {__builtin_enable_interrupts();} - -#define CO_LOCK_EMCY() CO_interruptStatus = __builtin_disable_interrupts() -#define CO_UNLOCK_EMCY() if(CO_interruptStatus & 0x00000001) {__builtin_enable_interrupts();} - -#define CO_LOCK_OD() CO_interruptStatus = __builtin_disable_interrupts() -#define CO_UNLOCK_OD() if(CO_interruptStatus & 0x00000001) {__builtin_enable_interrupts();} - - -/* Data types */ -/* int8_t to uint64_t are defined in stdint.h */ -typedef unsigned char bool_t; -typedef float float32_t; -typedef long double float64_t; -typedef char char_t; -typedef unsigned char oChar_t; -typedef unsigned char domain_t; - - -/* CAN bit rates - * - * CAN bit rates are initializers for array of eight CO_CANbitRateData_t - * objects. - * - * Macros are not used by driver itself, they may be used by application with - * combination with object CO_CANbitRateData_t. - * Application must declare following global variable depending on CO_FSYS used: - * const CO_CANbitRateData_t CO_CANbitRateData[8] = {CO_CANbitRateDataInitializers}; - * - * There are initializers for eight objects, which corresponds to following - * CAN bit rates (in kbps): 10, 20, 50, 125, 250, 500, 800, 1000. - * - * CO_FSYS is internal instruction cycle clock frequency in kHz units. See - * PIC32MX documentation for more information on FSYS. - * - * Available values for FSYS: - * kbps = | 10 | 20 | 50 | 125 | 250 | 500 | 800 | 1000 - * -------+----+----+----+-----+-----+-----+-----+----- - * 4 Mhz | O | O | O | O | p | - | - | - - * 8 Mhz | O | O | O | O | O | p | - | - - * 12 Mhz | O | O | O | O | p | p | - | - - * 16 Mhz | O | O | O | O | O | O | p | p - * 20 Mhz | O | O | O | O | O | O | - | p - * 24 Mhz | O | O | O | O | O | p | O | p - * 32 Mhz | p | O | O | O | O | O | p | O - * 36 Mhz | - | O | O | O | O | O | - | O - * 40 Mhz | - | O | O | O | O | O | p | O - * 48 Mhz | - | O | O | O | O | O | O | p - * 56 Mhz | - | p | O | O | O | p | (p) | p - * 64 Mhz | - | p | O | O | O | O | O | O - * 72 Mhz | - | - | O | O | O | O | O | O - * 80 Mhz | - | - | O | O | O | O | p | O - * ---------------------------------------------------- - * (O=optimal; p=possible; -=not possible) - */ -#ifdef CO_FSYS - /* Macros, which divides K into (SJW + PROP + PhSeg1 + PhSeg2) */ - #define TQ_x_7 1, 2, 3, 1 - #define TQ_x_8 1, 2, 3, 2 - #define TQ_x_9 1, 2, 4, 2 - #define TQ_x_10 1, 3, 4, 2 - #define TQ_x_12 1, 3, 6, 2 - #define TQ_x_14 1, 4, 7, 2 - #define TQ_x_15 1, 4, 8, 2 /* good timing */ - #define TQ_x_16 1, 5, 8, 2 /* good timing */ - #define TQ_x_17 1, 6, 8, 2 /* good timing */ - #define TQ_x_18 1, 7, 8, 2 /* good timing */ - #define TQ_x_19 1, 8, 8, 2 /* good timing */ - #define TQ_x_20 1, 8, 8, 3 /* good timing */ - #define TQ_x_21 1, 8, 8, 4 - #define TQ_x_22 1, 8, 8, 5 - #define TQ_x_23 1, 8, 8, 6 - #define TQ_x_24 1, 8, 8, 7 - #define TQ_x_25 1, 8, 8, 8 - - #if CO_FSYS == 4000 - #define CO_CANbitRateDataInitializers \ - {10, TQ_x_20}, /*CAN=10kbps*/ \ - {5, TQ_x_20}, /*CAN=20kbps*/ \ - {2, TQ_x_20}, /*CAN=50kbps*/ \ - {1, TQ_x_16}, /*CAN=125kbps*/ \ - {1, TQ_x_8 }, /*CAN=250kbps*/ \ - {1, TQ_x_8 }, /*Not possible*/ \ - {1, TQ_x_8 }, /*Not possible*/ \ - {1, TQ_x_8 } /*Not possible*/ - #elif CO_FSYS == 8000 - #define CO_CANbitRateDataInitializers \ - {25, TQ_x_16}, /*CAN=10kbps*/ \ - {10, TQ_x_20}, /*CAN=20kbps*/ \ - {5, TQ_x_16}, /*CAN=50kbps*/ \ - {2, TQ_x_16}, /*CAN=125kbps*/ \ - {1, TQ_x_16}, /*CAN=250kbps*/ \ - {1, TQ_x_8 }, /*CAN=500kbps*/ \ - {1, TQ_x_8 }, /*Not possible*/ \ - {1, TQ_x_8 } /*Not possible*/ - #elif CO_FSYS == 12000 - #define CO_CANbitRateDataInitializers \ - {40, TQ_x_15}, /*CAN=10kbps*/ \ - {20, TQ_x_15}, /*CAN=20kbps*/ \ - {8, TQ_x_15}, /*CAN=50kbps*/ \ - {3, TQ_x_16}, /*CAN=125kbps*/ \ - {2, TQ_x_12}, /*CAN=250kbps*/ \ - {1, TQ_x_12}, /*CAN=500kbps*/ \ - {1, TQ_x_12}, /*Not possible*/ \ - {1, TQ_x_12} /*Not possible*/ - #elif CO_FSYS == 16000 - #define CO_CANbitRateDataInitializers \ - {50, TQ_x_16}, /*CAN=10kbps*/ \ - {25, TQ_x_16}, /*CAN=20kbps*/ \ - {10, TQ_x_16}, /*CAN=50kbps*/ \ - {4, TQ_x_16}, /*CAN=125kbps*/ \ - {2, TQ_x_16}, /*CAN=250kbps*/ \ - {1, TQ_x_16}, /*CAN=500kbps*/ \ - {1, TQ_x_10}, /*CAN=800kbps*/ \ - {1, TQ_x_8 } /*CAN=1000kbps*/ - #elif CO_FSYS == 20000 - #define CO_CANbitRateDataInitializers \ - {50, TQ_x_20}, /*CAN=10kbps*/ \ - {25, TQ_x_20}, /*CAN=20kbps*/ \ - {10, TQ_x_20}, /*CAN=50kbps*/ \ - {5, TQ_x_16}, /*CAN=125kbps*/ \ - {2, TQ_x_20}, /*CAN=250kbps*/ \ - {1, TQ_x_20}, /*CAN=500kbps*/ \ - {1, TQ_x_20}, /*Not possible*/ \ - {1, TQ_x_10} /*CAN=1000kbps*/ - #elif CO_FSYS == 24000 - #define CO_CANbitRateDataInitializers \ - {63, TQ_x_19}, /*CAN=10kbps*/ \ - {40, TQ_x_15}, /*CAN=20kbps*/ \ - {15, TQ_x_16}, /*CAN=50kbps*/ \ - {6, TQ_x_16}, /*CAN=125kbps*/ \ - {3, TQ_x_16}, /*CAN=250kbps*/ \ - {2, TQ_x_12}, /*CAN=500kbps*/ \ - {1, TQ_x_15}, /*CAN=800kbps*/ \ - {1, TQ_x_12} /*CAN=1000kbps*/ - #elif CO_FSYS == 32000 - #define CO_CANbitRateDataInitializers \ - {64, TQ_x_25}, /*CAN=10kbps*/ \ - {50, TQ_x_16}, /*CAN=20kbps*/ \ - {20, TQ_x_16}, /*CAN=50kbps*/ \ - {8, TQ_x_16}, /*CAN=125kbps*/ \ - {4, TQ_x_16}, /*CAN=250kbps*/ \ - {2, TQ_x_16}, /*CAN=500kbps*/ \ - {2, TQ_x_10}, /*CAN=800kbps*/ \ - {1, TQ_x_16} /*CAN=1000kbps*/ - #elif CO_FSYS == 36000 - #define CO_CANbitRateDataInitializers \ - {50, TQ_x_18}, /*CAN=10kbps*/ \ - {50, TQ_x_18}, /*CAN=20kbps*/ \ - {20, TQ_x_18}, /*CAN=50kbps*/ \ - {8, TQ_x_18}, /*CAN=125kbps*/ \ - {4, TQ_x_18}, /*CAN=250kbps*/ \ - {2, TQ_x_18}, /*CAN=500kbps*/ \ - {2, TQ_x_18}, /*Not possible*/ \ - {1, TQ_x_18} /*CAN=1000kbps*/ - #elif CO_FSYS == 40000 - #define CO_CANbitRateDataInitializers \ - {50, TQ_x_20}, /*Not possible*/ \ - {50, TQ_x_20}, /*CAN=20kbps*/ \ - {25, TQ_x_16}, /*CAN=50kbps*/ \ - {10, TQ_x_16}, /*CAN=125kbps*/ \ - {5, TQ_x_16}, /*CAN=250kbps*/ \ - {2, TQ_x_20}, /*CAN=500kbps*/ \ - {1, TQ_x_25}, /*CAN=800kbps*/ \ - {1, TQ_x_20} /*CAN=1000kbps*/ - #elif CO_FSYS == 48000 - #define CO_CANbitRateDataInitializers \ - {63, TQ_x_19}, /*Not possible*/ \ - {63, TQ_x_19}, /*CAN=20kbps*/ \ - {30, TQ_x_16}, /*CAN=50kbps*/ \ - {12, TQ_x_16}, /*CAN=125kbps*/ \ - {6, TQ_x_16}, /*CAN=250kbps*/ \ - {3, TQ_x_16}, /*CAN=500kbps*/ \ - {2, TQ_x_15}, /*CAN=800kbps*/ \ - {2, TQ_x_12} /*CAN=1000kbps*/ - #elif CO_FSYS == 56000 - #define CO_CANbitRateDataInitializers \ - {61, TQ_x_23}, /*Not possible*/ \ - {61, TQ_x_23}, /*CAN=20kbps*/ \ - {35, TQ_x_16}, /*CAN=50kbps*/ \ - {14, TQ_x_16}, /*CAN=125kbps*/ \ - {7, TQ_x_16}, /*CAN=250kbps*/ \ - {4, TQ_x_14}, /*CAN=500kbps*/ \ - {5, TQ_x_7 }, /*CAN=800kbps*/ \ - {2, TQ_x_14} /*CAN=1000kbps*/ - #elif CO_FSYS == 64000 - #define CO_CANbitRateDataInitializers \ - {64, TQ_x_25}, /*Not possible*/ \ - {64, TQ_x_25}, /*CAN=20kbps*/ \ - {40, TQ_x_16}, /*CAN=50kbps*/ \ - {16, TQ_x_16}, /*CAN=125kbps*/ \ - {8, TQ_x_16}, /*CAN=250kbps*/ \ - {4, TQ_x_16}, /*CAN=500kbps*/ \ - {2, TQ_x_20}, /*CAN=800kbps*/ \ - {2, TQ_x_16} /*CAN=1000kbps*/ - #elif CO_FSYS == 72000 - #define CO_CANbitRateDataInitializers \ - {40, TQ_x_18}, /*Not possible*/ \ - {40, TQ_x_18}, /*Not possible*/ \ - {40, TQ_x_18}, /*CAN=50kbps*/ \ - {16, TQ_x_18}, /*CAN=125kbps*/ \ - {8, TQ_x_18}, /*CAN=250kbps*/ \ - {4, TQ_x_18}, /*CAN=500kbps*/ \ - {3, TQ_x_15}, /*CAN=800kbps*/ \ - {2, TQ_x_18} /*CAN=1000kbps*/ - #elif CO_FSYS == 80000 - #define CO_CANbitRateDataInitializers \ - {40, TQ_x_20}, /*Not possible*/ \ - {40, TQ_x_20}, /*Not possible*/ \ - {40, TQ_x_20}, /*CAN=50kbps*/ \ - {16, TQ_x_20}, /*CAN=125kbps*/ \ - {8, TQ_x_20}, /*CAN=250kbps*/ \ - {4, TQ_x_20}, /*CAN=500kbps*/ \ - {2, TQ_x_25}, /*CAN=800kbps*/ \ - {2, TQ_x_20} /*CAN=1000kbps*/ - #else - #error define_CO_FSYS CO_FSYS not supported - #endif -#endif - - -/* Structure contains timing coefficients for CAN module. - * - * CAN baud rate is calculated from following equations: - * Fsys - System clock (MAX 80MHz for PIC32MX) - * TQ = 2 * BRP / Fsys - Time Quanta - * BaudRate = 1 / (TQ * K) - Can bus Baud Rate - * K = SJW + PROP + PhSeg1 + PhSeg2 - Number of Time Quantas - */ -typedef struct{ - uint8_t BRP; /* (1...64) Baud Rate Prescaler */ - uint8_t SJW; /* (1...4) SJW time */ - uint8_t PROP; /* (1...8) PROP time */ - uint8_t phSeg1; /* (1...8) Phase Segment 1 time */ - uint8_t phSeg2; /* (1...8) Phase Segment 2 time */ -}CO_CANbitRateData_t; - - -/* CAN receive message structure as aligned in CAN module. */ -typedef struct{ - unsigned ident :11; /* Standard Identifier */ - unsigned FILHIT :5; /* Filter hit, see PIC32MX documentation */ - unsigned CMSGTS :16; /* CAN message timestamp, see PIC32MX documentation */ - unsigned DLC :4; /* Data length code (bits 0...3) */ - unsigned :5; - unsigned RTR :1; /* Remote Transmission Request bit */ - unsigned :22; - uint8_t data[8]; /* 8 data bytes */ -}CO_CANrxMsg_t; - - -/* Received message object */ -typedef struct{ - uint16_t ident; - uint16_t mask; - void *object; - void (*pFunct)(void *object, const CO_CANrxMsg_t *message); -}CO_CANrx_t; - - -/* Transmit message object. */ -typedef struct{ - uint32_t CMSGSID; /* Equal to register in transmit message buffer. Includes standard Identifier */ - uint32_t CMSGEID; /* Equal to register in transmit message buffer. Includes data length code and RTR */ - uint8_t data[8]; - volatile bool_t bufferFull; - volatile bool_t syncFlag; -}CO_CANtx_t; - - -/* CAN module object. */ -typedef struct{ - void *CANdriverState; - CO_CANrxMsg_t CANmsgBuff[33]; /* PIC32 specific: CAN message buffer for CAN module. 32 buffers for receive, 1 buffer for transmit */ - uint8_t CANmsgBuffSize; /* PIC32 specific: Size of the above buffer == 33. Take care initial value! */ - CO_CANrx_t *rxArray; - uint16_t rxSize; - CO_CANtx_t *txArray; - uint16_t txSize; - volatile bool_t CANnormal; - volatile bool_t useCANrxFilters; - volatile bool_t bufferInhibitFlag; - volatile bool_t firstCANtxMessage; - volatile uint16_t CANtxCount; - uint32_t errOld; - void *em; -}CO_CANmodule_t; - - -/* CAN interrupt receives and transmits CAN messages. - * - * Function must be called directly from _C1Interrupt or _C2Interrupt with - * high priority. - */ -void CO_CANinterrupt(CO_CANmodule_t *CANmodule); - - -#endif /* CO_DRIVER_TARGET_H */ diff --git a/stack/PIC32/eeprom.c b/stack/PIC32/eeprom.c deleted file mode 100644 index c5829f3c..00000000 --- a/stack/PIC32/eeprom.c +++ /dev/null @@ -1,534 +0,0 @@ -/* - * Eeprom object for Microchip PIC32MX microcontroller. - * - * @file eeprom.c - * @author Janez Paternoster - * @copyright 2004 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include "CO_driver.h" -#include "CO_SDO.h" -#include "CO_Emergency.h" -#include "eeprom.h" -#include "crc16-ccitt.h" - - -/* Eeprom *********************************************************************/ -#define EE_SS_TRIS() TRISGCLR = 0x0200 -#define EE_SSLow() PORTGCLR = 0x0200 -#define EE_SSHigh() PORTGSET = 0x0200 -#define SPIBUF SPI2ABUF -#define SPICON SPI2ACON -#define SPISTAT SPI2ASTAT -#define SPISTATbits SPI2ASTATbits -#define SPIBRG SPI2ABRG -static void EE_SPIwrite(uint8_t *tx, uint8_t *rx, uint8_t len); -static void EE_writeEnable(); -static void EE_writeByteNoWait(uint8_t data, uint32_t addr); -static uint8_t EE_readByte(uint32_t addr); -static void EE_writeBlock(uint8_t *data, uint32_t addr, uint32_t len); -static void EE_readBlock(uint8_t *data, uint32_t addr, uint32_t len); -static uint8_t EE_verifyBlock(uint8_t *data, uint32_t addr, uint32_t len); -static void EE_writeStatus(uint8_t data); -static uint8_t EE_readStatus(); -#define EE_isWriteInProcess() (EE_readStatus() & 0x01) /* True if write is in process. */ - -static uint32_t tmpU32; - - -/* Store parameters ***********************************************************/ -static CO_SDO_abortCode_t CO_ODF_1010(CO_ODF_arg_t *ODF_arg){ - CO_EE_t *ee; - uint32_t value; - CO_SDO_abortCode_t ret = CO_SDO_AB_NONE; - - ee = (CO_EE_t*) ODF_arg->object; - value = CO_getUint32(ODF_arg->data); - - if(!ODF_arg->reading){ - /* don't change the old value */ - CO_memcpy(ODF_arg->data, (const uint8_t*)ODF_arg->ODdataStorage, 4U); - - if(ODF_arg->subIndex == 1){ - if(value == 0x65766173UL){ - EE_MBR_t MBR; - - /* read the master boot record from the last page in eeprom */ - EE_readBlock((uint8_t*)&MBR, EE_SIZE - EE_PAGE_SIZE, sizeof(MBR)); - /* if EEPROM is not yet initilalized, enable it now */ - if(MBR.OD_EEPROMSize != ee->OD_EEPROMSize) - ee->OD_EEPROMWriteEnable = true; - - /* prepare MBR */ - MBR.CRC = crc16_ccitt(ee->OD_ROMAddress, ee->OD_ROMSize, 0); - MBR.OD_EEPROMSize = ee->OD_EEPROMSize; - MBR.OD_ROMSize = ee->OD_ROMSize; - - /* write to eeprom (blocking function) */ - EE_writeStatus(0); /* unprotect data */ - EE_writeBlock((uint8_t*)&MBR, EE_SIZE - EE_PAGE_SIZE, sizeof(MBR)); - EE_writeBlock(ee->OD_ROMAddress, EE_SIZE/2, ee->OD_ROMSize); - EE_writeStatus(0x88); /* protect data */ - - /* verify data and MBR and status register */ - if( EE_verifyBlock(ee->OD_ROMAddress, EE_SIZE/2, ee->OD_ROMSize) == 1 - && EE_verifyBlock((uint8_t*)&MBR, EE_SIZE - EE_PAGE_SIZE, sizeof(MBR)) == 1 - && (EE_readStatus()&0x8C) == 0x88){ - /* write successfull */ - return CO_SDO_AB_NONE; - } - return CO_SDO_AB_HW; - } - else - return CO_SDO_AB_DATA_TRANSF; - } - } - - return ret; -} - - -/* Restore default parameters *************************************************/ -static CO_SDO_abortCode_t CO_ODF_1011(CO_ODF_arg_t *ODF_arg){ - CO_EE_t *ee; - uint32_t value; - CO_SDO_abortCode_t ret = CO_SDO_AB_NONE; - - ee = (CO_EE_t*) ODF_arg->object; - value = CO_getUint32(ODF_arg->data); - - if(!ODF_arg->reading){ - /* don't change the old value */ - CO_memcpy(ODF_arg->data, (const uint8_t*)ODF_arg->ODdataStorage, 4U); - - if(ODF_arg->subIndex >= 1U){ - if(value == 0x64616F6CUL){ - EE_MBR_t MBR; - - /* read the master boot record from the last page in eeprom */ - EE_readBlock((uint8_t*)&MBR, EE_SIZE - EE_PAGE_SIZE, sizeof(MBR)); - /* verify MBR for safety */ - if(EE_verifyBlock((uint8_t*)&MBR, EE_SIZE - EE_PAGE_SIZE, sizeof(MBR)) == 0) - return CO_SDO_AB_HW; - - switch(ODF_arg->subIndex){ - case 0x01: MBR.OD_ROMSize = 0; break; /* clear the ROM */ - /* following don't work, if not enabled in object dictionary */ - case 0x77: MBR.OD_ROMSize = ee->OD_ROMSize; break; /* restore the ROM back */ - case 0x7F: MBR.OD_EEPROMSize = 0; break; /* clear EEPROM */ - default: return 0x06090011; /* Sub-index does not exist. */ - } - - /* write changed MBR */ - EE_writeStatus(0); /* unprotect data */ - EE_writeBlock((uint8_t*)&MBR, EE_SIZE - EE_PAGE_SIZE, sizeof(MBR)); - EE_writeStatus(0x88); /* protect data */ - - /* verify MBR and status register */ - if(EE_verifyBlock((uint8_t*)&MBR, EE_SIZE - EE_PAGE_SIZE, sizeof(MBR)) == 1 - && (EE_readStatus()&0x8C) == 0x88){ - /* write successfull */ - return CO_SDO_AB_NONE; - } - else{ - return CO_SDO_AB_HW; - } - } - else - return CO_SDO_AB_DATA_TRANSF; - } - } - - return ret; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_EE_init_1( - CO_EE_t *ee, - uint8_t *OD_EEPROMAddress, - uint32_t OD_EEPROMSize, - uint8_t *OD_ROMAddress, - uint32_t OD_ROMSize) -{ - - /* verify arguments */ - if(ee==NULL || OD_EEPROMAddress==NULL || OD_ROMAddress==NULL){ - return CO_ERROR_ILLEGAL_ARGUMENT; - } - - /* Configure SPI port for use with eeprom */ - SPICON = 0; /* Stops and restes the SPI */ - SPISTAT = 0; - tmpU32 = SPIBUF; /* Clear the receive buffer */ - SPIBRG = 4; /* Clock = FPB / ((4+1) * 2) */ - SPICON = 0x00018120; /* MSSEN = 0 - Master mode slave select enable bit */ - /* ENHBUF(bit16) = 1 - Enhanced buffer enable bit */ - /* Enable SPI, 8-bit mode */ - /* SMP = 0, CKE = 1, CKP = 0 */ - /* MSTEN = 1 - master mode enable bit */ - - /* Set IOs directions for EEPROM SPI */ - EE_SSHigh(); - EE_SS_TRIS(); - - /* verify variables */ - if(OD_ROMSize > (EE_SIZE/2 - EE_PAGE_SIZE)) OD_ROMSize = EE_SIZE/2 - EE_PAGE_SIZE; - if(OD_EEPROMSize > EE_SIZE/2) OD_EEPROMSize = EE_SIZE/2; - - /* configure object variables */ - ee->OD_EEPROMAddress = OD_EEPROMAddress; - ee->OD_EEPROMSize = OD_EEPROMSize; - ee->OD_ROMAddress = OD_ROMAddress; - ee->OD_ROMSize = OD_ROMSize; - ee->OD_EEPROMCurrentIndex = 0; - ee->OD_EEPROMWriteEnable = false; - - /* read the master boot record from the last page in eeprom */ - EE_MBR_t MBR; - EE_readBlock((uint8_t*)&MBR, EE_SIZE - EE_PAGE_SIZE, sizeof(MBR)); - - /* read the CO_OD_EEPROM from EEPROM, first verify, if data are OK */ - if(MBR.OD_EEPROMSize == OD_EEPROMSize && (MBR.OD_ROMSize == OD_ROMSize || MBR.OD_ROMSize == 0)){ - uint32_t firstWordRAM = *((uint32_t*)OD_EEPROMAddress); - uint32_t firstWordEE, lastWordEE; - EE_readBlock((uint8_t*)&firstWordEE, 0, 4); - EE_readBlock((uint8_t*)&lastWordEE, OD_EEPROMSize-4, 4); - if(firstWordRAM == firstWordEE && firstWordRAM == lastWordEE){ - EE_readBlock(OD_EEPROMAddress, 0, OD_EEPROMSize); - ee->OD_EEPROMWriteEnable = true; - } - else{ - return CO_ERROR_DATA_CORRUPT; - } - } - else{ - return CO_ERROR_DATA_CORRUPT; - } - - /* read the CO_OD_ROM from EEPROM and verify CRC */ - if(MBR.OD_ROMSize == OD_ROMSize){ - EE_readBlock(OD_ROMAddress, EE_SIZE/2, OD_ROMSize); - if(crc16_ccitt(OD_ROMAddress, OD_ROMSize, 0) != MBR.CRC){ - return CO_ERROR_CRC; - } - } - - return CO_ERROR_NO; -} - - -/******************************************************************************/ -void CO_EE_init_2( - CO_EE_t *ee, - CO_ReturnError_t eeStatus, - CO_SDO_t *SDO, - CO_EM_t *em) -{ - CO_OD_configure(SDO, OD_H1010_STORE_PARAM_FUNC, CO_ODF_1010, (void*)ee, 0, 0U); - CO_OD_configure(SDO, OD_H1011_REST_PARAM_FUNC, CO_ODF_1011, (void*)ee, 0, 0U); - if(eeStatus != CO_ERROR_NO){ - CO_errorReport(em, CO_EM_NON_VOLATILE_MEMORY, CO_EMC_HARDWARE, (uint32_t)eeStatus); - } -} - - -/******************************************************************************/ -void CO_EE_process(CO_EE_t *ee){ - if(ee && ee->OD_EEPROMWriteEnable && !EE_isWriteInProcess()){ - /* verify next word */ - if(++ee->OD_EEPROMCurrentIndex == ee->OD_EEPROMSize) ee->OD_EEPROMCurrentIndex = 0; - unsigned int i = ee->OD_EEPROMCurrentIndex; - - /* read eeprom */ - uint8_t RAMdata = ee->OD_EEPROMAddress[i]; - uint8_t EEdata = EE_readByte(i); - - /* if bytes in EEPROM and in RAM are different, then write to EEPROM */ - if(EEdata != RAMdata) - EE_writeByteNoWait(RAMdata, i); - } -} - - -/******************************************************************************/ -/* EEPROM 25LC128 on SPI ******************************************************/ -/******************************************************************************/ -#define EE_CMD_READ (unsigned)0b00000011 -#define EE_CMD_WRITE (unsigned)0b00000010 -#define EE_CMD_WRDI (unsigned)0b00000100 -#define EE_CMD_WREN (unsigned)0b00000110 -#define EE_CMD_RDSR (unsigned)0b00000101 -#define EE_CMD_WRSR (unsigned)0b00000001 - - -/** - * Function - EE_SPIwrite - * - * Write to SPI and at the same time read from SPI. - * - * PIC32 used 16bytes long FIFO buffer with SPI. SPI module is initailized in - * CO_EE_init. - * - * @param tx Ponter to transmitting data. If NULL, zeroes will be transmitted. - * @param rx Ponter to data buffer, where received data wile be stored. - * If null, Received data will be disregarded. - * @param len Length of data buffers. Max 16. - */ -static void EE_SPIwrite(uint8_t *tx, uint8_t *rx, uint8_t len){ - uint32_t i; - - /* write bytes into SPI_TXB fifo buffer */ - if(tx) for(i=0; i> 8); - buf[2] = (uint8_t) addr; - buf[3] = data; - - EE_SSLow(); - EE_SPIwrite(buf, 0, 4); - EE_SSHigh(); -} - - -/* - * Read one byte of data from eeprom. - * - * @param addr Address in eeprom, where data will be written. - * - * @return Data byte read. - */ -static uint8_t EE_readByte(uint32_t addr){ - uint8_t bufTx[4]; - uint8_t bufRx[4]; - - bufTx[0] = EE_CMD_READ; - bufTx[1] = (uint8_t) (addr >> 8); - bufTx[2] = (uint8_t) addr; - bufTx[3] = 0; - - EE_SSLow(); - EE_SPIwrite(bufTx, bufRx, 4); - EE_SSHigh(); - - return bufRx[3]; -} - - -/* - * Write block of data to eeprom. It is blockung function, so it waits, untill - * all data are written. - * - * @param data Pointer to data to be written. - * @param addr Address in eeprom, where data will be written. *If data are - * stored accross multiple pages, address must be aligned with page.* - * @param len Length of the data block. - */ -static void EE_writeBlock(uint8_t *data, uint32_t addr, uint32_t len){ - - #if EE_PAGE_SIZE != 64 - #error incompatibility in function - #endif - - while(EE_isWriteInProcess()); - - while(len){ - uint8_t buf[3]; - uint32_t i; - - EE_writeEnable(); - - buf[0] = EE_CMD_WRITE; - buf[1] = (uint8_t) (addr >> 8); - buf[2] = (uint8_t) addr; - - EE_SSLow(); - EE_SPIwrite(buf, 0, 3); - - for(i=0; i<4; i++){ - if(len > 16){ - EE_SPIwrite(data, 0, 16); - len -= 16; - data += 16; - } - else{ - EE_SPIwrite(data, 0, len); - len = 0; - break; - } - } - EE_SSHigh(); - - /* wait for completion of the write operation */ - i=EE_readStatus(); - while(EE_isWriteInProcess()); - addr += EE_PAGE_SIZE; - } -} - - -/* - * Read block of data from eeprom. - * - * @param data Pointer to data buffer, where data will be stored. - * @param addr Address in eeprom, from where data will be read. - * @param len Length of the data block to be read. - */ -static void EE_readBlock(uint8_t *data, uint32_t addr, uint32_t len){ - uint8_t buf[3]; - - buf[0] = EE_CMD_READ; - buf[1] = (uint8_t) (addr >> 8); - buf[2] = (uint8_t) addr; - - EE_SSLow(); - EE_SPIwrite(buf, 0, 3); - - while(len){ - if(len > 16){ - EE_SPIwrite(0, data, 16); - len -= 16; - data += 16; - } - else{ - EE_SPIwrite(0, data, len); - len = 0; - } - } - - EE_SSHigh(); -} - - -/* - * Compare block of data with data stored in eeprom. - * - * @param data Pointer to data buffer, which will be compared. - * @param addr Address of data in eeprom, which will be compared. - * @param len Length of the data block to be compared. - * - * @return 0 - comparision failed. - * @return 1 - data are equal. - */ -static uint8_t EE_verifyBlock(uint8_t *data, uint32_t addr, uint32_t len){ - uint8_t buf[16]; - uint8_t equal = 1; - - buf[0] = EE_CMD_READ; - buf[1] = (uint8_t) (addr >> 8); - buf[2] = (uint8_t) addr; - - EE_SSLow(); - EE_SPIwrite(buf, 0, 3); - - while(len){ - if(len > 16){ - uint8_t i; - EE_SPIwrite(0, buf, 16); - for(i=0; i<16; i++) if(buf[i] != data[i]) equal = 0; - len -= 16; - data += 16; - } - else{ - uint8_t i; - EE_SPIwrite(0, buf, len); - for(i=0; i. - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#ifndef EEPROM_H -#define EEPROM_H - - -/* For documentation see file drvTemplate/eeprom.h */ - - -/* - * PIC32MX specific - * - * 25LC128 eeprom from Microchip is used connected on SPI2A. - * - * Two blocks of CANopen Object Dictionary data are stored in eeprom: - * OD_EEPROM - Stored is from eeprom address 0. Data are stored automatically on - * change. No data corruption control is made. - * OD_ROM - Stored from upper half eeprom address. Data are protected from - * accidental write, can also be hardware protected. Data integrity - * is verified with CRC. - * Data are stored on special CANopen command - Writing 0x65766173 - * into Object dictionary (index 1010, subindex 1). Default values - * are restored after reset, if writing 0x64616F6C into (1011, 1). - */ - - -/* Constants */ -#define EE_SIZE 0x4000 -#define EE_PAGE_SIZE 64 - - -/* Master boot record is stored on the last page in eeprom */ -typedef struct{ - uint32_t CRC; /* CRC code of the OD_ROM block */ - uint32_t OD_EEPROMSize; /* Size of OD_EEPROM block */ - uint32_t OD_ROMSize; /* Size of OD_ROM block */ -}EE_MBR_t; - - -/* Eeprom object */ -typedef struct{ - uint8_t *OD_EEPROMAddress; - uint32_t OD_EEPROMSize; - uint8_t *OD_ROMAddress; - uint32_t OD_ROMSize; - uint32_t OD_EEPROMCurrentIndex; - bool_t OD_EEPROMWriteEnable; - -}CO_EE_t; - - -/* First part of eeprom initialization. - * - * Allocate memory for object, configure SPI port for use with 25LCxxx, read - * eeprom and store to OD_EEPROM and OD_ROM. - */ -CO_ReturnError_t CO_EE_init_1( - CO_EE_t *ee, - uint8_t *OD_EEPROMAddress, - uint32_t OD_EEPROMSize, - uint8_t *OD_ROMAddress, - uint32_t OD_ROMSize); - - -/* Second part of eeprom initialization. */ -void CO_EE_init_2( - CO_EE_t *ee, - CO_ReturnError_t eeStatus, - CO_SDO_t *SDO, - CO_EM_t *em); - - -/* Process eeprom object. */ -void CO_EE_process(CO_EE_t *ee); - - -#endif diff --git a/stack/PIC32/main_PIC32.c b/stack/PIC32/main_PIC32.c deleted file mode 100644 index c06e9058..00000000 --- a/stack/PIC32/main_PIC32.c +++ /dev/null @@ -1,330 +0,0 @@ -/* - * CANopen main program file for PIC32 microcontroller. - * - * @file main_PIC32.c - * @author Janez Paternoster - * @copyright 2010 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#define CO_FSYS 64000 /* (8MHz Quartz used) */ -#define CO_PBCLK 32000 /* peripheral bus clock */ - - -#include "CANopen.h" -#include "application.h" -#ifdef USE_EEPROM - #include "eeprom.h" /* 25LC128 eeprom chip connected to SPI2A port. */ -#endif -#include /* for interrupts */ -#include /* for interrupts */ - - -/* Configuration bits */ - #pragma config FVBUSONIO = OFF /* USB VBUS_ON Selection (OFF = pin is controlled by the port function) */ - #pragma config FUSBIDIO = OFF /* USB USBID Selection (OFF = pin is controlled by the port function) */ - #pragma config UPLLEN = OFF /* USB PLL Enable */ - #pragma config UPLLIDIV = DIV_12 /* USB PLL Input Divider */ - #pragma config FCANIO = ON /* CAN IO Pin Selection (ON = default CAN IO Pins) */ - #pragma config FETHIO = ON /* Ethernet IO Pin Selection (ON = default Ethernet IO Pins) */ - #pragma config FMIIEN = ON /* Ethernet MII Enable (ON = MII enabled) */ - #pragma config FSRSSEL = PRIORITY_7 /* SRS (Shadow registers set) Select */ - #pragma config POSCMOD = XT /* Primary Oscillator */ - #pragma config FSOSCEN = OFF /* Secondary oscillator Enable */ - #pragma config FNOSC = PRIPLL /* Oscillator Selection */ - #pragma config FPLLIDIV = DIV_2 /* PLL Input Divider */ - #pragma config FPLLMUL = MUL_16 /* PLL Multiplier */ - #pragma config FPLLODIV = DIV_1 /* PLL Output Divider Value */ - #pragma config FPBDIV = DIV_2 /* Bootup PBCLK divider */ - #pragma config FCKSM = CSDCMD /* Clock Switching and Monitor Selection */ - #pragma config OSCIOFNC = OFF /* CLKO Enable */ - #pragma config IESO = OFF /* Internal External Switch Over */ -#pragma config FWDTEN = OFF /* Watchdog Timer Enable */ - #pragma config WDTPS = PS1024 /* Watchdog Timer Postscale Select (in milliseconds) */ -#pragma config CP = OFF /* Code Protect Enable */ - #pragma config BWP = ON /* Boot Flash Write Protect */ - #pragma config PWP = PWP256K /* Program Flash Write Protect */ -#ifdef CO_ICS_PGx1 - #pragma config ICESEL = ICS_PGx1 /* ICE/ICD Comm Channel Select */ -#else - #pragma config ICESEL = ICS_PGx2 /* ICE/ICD Comm Channel Select (2 for Explorer16 board) */ -#endif - #pragma config DEBUG = ON /* Background Debugger Enable */ - - -/* macros */ - #define CO_TMR_TMR TMR2 /* TMR register */ - #define CO_TMR_PR PR2 /* Period register */ - #define CO_TMR_CON T2CON /* Control register */ - #define CO_TMR_ISR_FLAG IFS0bits.T2IF /* Interrupt Flag bit */ - #define CO_TMR_ISR_PRIORITY IPC2bits.T2IP /* Interrupt Priority */ - #define CO_TMR_ISR_ENABLE IEC0bits.T2IE /* Interrupt Enable bit */ - - #define CO_CAN_ISR() void __ISR(_CAN_1_VECTOR, IPL5SOFT) CO_CAN1InterruptHandler(void) - #define CO_CAN_ISR_FLAG IFS1bits.CAN1IF /* Interrupt Flag bit */ - #define CO_CAN_ISR_PRIORITY IPC11bits.CAN1IP /* Interrupt Priority */ - #define CO_CAN_ISR_ENABLE IEC1bits.CAN1IE /* Interrupt Enable bit */ - - #define CO_CAN_ISR2() void __ISR(_CAN_2_VECTOR, IPL5SOFT) CO_CAN2InterruptHandler(void) - #define CO_CAN_ISR2_FLAG IFS1bits.CAN2IF /* Interrupt Flag bit */ - #define CO_CAN_ISR2_PRIORITY IPC11bits.CAN2IP /* Interrupt Priority */ - #define CO_CAN_ISR2_ENABLE IEC1bits.CAN2IE /* Interrupt Enable bit */ - - #define CO_clearWDT() (WDTCONSET = _WDTCON_WDTCLR_MASK) - -/* Global variables and objects */ - volatile uint16_t CO_timer1ms = 0U; /* variable increments each millisecond */ - const CO_CANbitRateData_t CO_CANbitRateData[8] = {CO_CANbitRateDataInitializers}; - static uint32_t tmpU32; -#ifdef USE_EEPROM - CO_EE_t CO_EEO; /* Eeprom object */ -#endif - - -/* main ***********************************************************************/ -int main (void){ - CO_NMT_reset_cmd_t reset = CO_RESET_NOT; - - /* Configure system for maximum performance. plib is necessary for that.*/ - /* SYSTEMConfig(CO_FSYS*1000, SYS_CFG_WAIT_STATES | SYS_CFG_PCACHE); */ - - /* Enable system multi vectored interrupts */ - INTCONbits.MVEC = 1; - __builtin_enable_interrupts(); - - /* Disable JTAG and trace port */ - DDPCONbits.JTAGEN = 0; - DDPCONbits.TROEN = 0; - - - /* Verify, if OD structures have proper alignment of initial values */ - if(CO_OD_RAM.FirstWord != CO_OD_RAM.LastWord) while(1) CO_clearWDT(); - if(CO_OD_EEPROM.FirstWord != CO_OD_EEPROM.LastWord) while(1) CO_clearWDT(); - if(CO_OD_ROM.FirstWord != CO_OD_ROM.LastWord) while(1) CO_clearWDT(); - - - /* initialize EEPROM - part 1 */ -#ifdef USE_EEPROM - CO_ReturnError_t eeStatus = CO_EE_init_1(&CO_EEO, (uint8_t*) &CO_OD_EEPROM, sizeof(CO_OD_EEPROM), - (uint8_t*) &CO_OD_ROM, sizeof(CO_OD_ROM)); -#endif - - - programStart(); - - - /* increase variable each startup. Variable is stored in eeprom. */ - OD_powerOnCounter++; - - - while(reset != CO_RESET_APP){ -/* CANopen communication reset - initialize CANopen objects *******************/ - CO_ReturnError_t err; - uint16_t timer1msPrevious; - uint16_t TMR_TMR_PREV = 0; - uint8_t nodeId; - uint16_t CANBitRate; - - /* disable CAN and CAN interrupts */ - CO_CAN_ISR_ENABLE = 0; - CO_CAN_ISR2_ENABLE = 0; - - /* Read CANopen Node-ID and CAN bit-rate from object dictionary */ - nodeId = OD_CANNodeID; - if(nodeId<1 || nodeId>127) nodeId = 0x10; - CANBitRate = OD_CANBitRate;/* in kbps */ - - /* initialize CANopen */ - err = CO_init(ADDR_CAN1, nodeId, CANBitRate); - if(err != CO_ERROR_NO){ - while(1) CO_clearWDT(); - /* CO_errorReport(CO->em, CO_EM_MEMORY_ALLOCATION_ERROR, CO_EMC_SOFTWARE_INTERNAL, err); */ - } - - - /* initialize eeprom - part 2 */ -#ifdef USE_EEPROM - CO_EE_init_2(&CO_EEO, eeStatus, CO->SDO[0], CO->em); -#endif - - - /* initialize variables */ - timer1msPrevious = CO_timer1ms; - OD_performance[ODA_performance_mainCycleMaxTime] = 0; - OD_performance[ODA_performance_timerCycleMaxTime] = 0; - reset = CO_RESET_NOT; - - - - /* Configure Timer interrupt function for execution every 1 millisecond */ - CO_TMR_CON = 0; - CO_TMR_TMR = 0; - #if CO_PBCLK > 65000 - #error wrong timer configuration - #endif - CO_TMR_PR = CO_PBCLK - 1; /* Period register */ - CO_TMR_CON = 0x8000; /* start timer (TON=1) */ - CO_TMR_ISR_FLAG = 0; /* clear interrupt flag */ - CO_TMR_ISR_PRIORITY = 3; /* interrupt - set lower priority than CAN (set the same value in interrupt) */ - - /* Configure CAN1 Interrupt (Combined) */ - CO_CAN_ISR_FLAG = 0; /* CAN1 Interrupt - Clear flag */ - CO_CAN_ISR_PRIORITY = 5; /* CAN1 Interrupt - Set higher priority than timer (set the same value in '#define CO_CAN_ISR_PRIORITY') */ - CO_CAN_ISR2_FLAG = 0; /* CAN2 Interrupt - Clear flag */ - CO_CAN_ISR2_PRIORITY = 5; /* CAN Interrupt - Set higher priority than timer (set the same value in '#define CO_CAN_ISR_PRIORITY') */ - - - communicationReset(); - - - /* start CAN and enable interrupts */ - CO_CANsetNormalMode(CO->CANmodule[0]); - CO_TMR_ISR_ENABLE = 1; - CO_CAN_ISR_ENABLE = 1; - -#if CO_NO_CAN_MODULES >= 2 - CO_CANsetNormalMode(CO->CANmodule[1]); - CO_CAN_ISR2_ENABLE = 1; -#endif - - - while(reset == CO_RESET_NOT){ -/* loop for normal program execution ******************************************/ - uint16_t timer1msCopy, timer1msDiff; - - CO_clearWDT(); - - - /* calculate cycle time for performance measurement */ - timer1msCopy = CO_timer1ms; - timer1msDiff = timer1msCopy - timer1msPrevious; - timer1msPrevious = timer1msCopy; - uint16_t t0 = CO_TMR_TMR; - uint16_t t = t0; - if(t >= TMR_TMR_PREV){ - t = t - TMR_TMR_PREV; - t = (timer1msDiff * 100) + (t / (CO_PBCLK / 100)); - } - else if(timer1msDiff){ - t = TMR_TMR_PREV - t; - t = (timer1msDiff * 100) - (t / (CO_PBCLK / 100)); - } - else t = 0; - OD_performance[ODA_performance_mainCycleTime] = t; - if(t > OD_performance[ODA_performance_mainCycleMaxTime]) - OD_performance[ODA_performance_mainCycleMaxTime] = t; - TMR_TMR_PREV = t0; - - - /* Application asynchronous program */ - programAsync(timer1msDiff); - - CO_clearWDT(); - - - /* CANopen process */ - reset = CO_process(CO, timer1msDiff, NULL); - - CO_clearWDT(); - - -#ifdef USE_EEPROM - CO_EE_process(&CO_EEO); -#endif - } - } - - -/* program exit ***************************************************************/ -// CO_DISABLE_INTERRUPTS(); - - /* delete objects from memory */ - programEnd(); - CO_delete(ADDR_CAN1); - - /* reset */ - SYSKEY = 0x00000000; - SYSKEY = 0xAA996655; - SYSKEY = 0x556699AA; - RSWRSTSET = 1; - tmpU32 = RSWRST; - while(1); -} - - -/* timer interrupt function executes every millisecond ************************/ -#ifndef USE_EXTERNAL_TIMER_1MS_INTERRUPT -void __ISR(_TIMER_2_VECTOR, IPL3SOFT) CO_TimerInterruptHandler(void){ - - CO_TMR_ISR_FLAG = 0; - - CO_timer1ms++; - - if(CO->CANmodule[0]->CANnormal) { - bool_t syncWas; - int i; - - /* Process Sync */ - syncWas = CO_process_SYNC(CO, 1000); - - /* Read inputs */ - CO_process_RPDO(CO, syncWas); - - /* Further I/O or nonblocking application code may go here. */ -#if CO_NO_TRACE > 0 - OD_time.epochTimeOffsetMs++; - for(i=0; itrace[i], OD_time.epochTimeOffsetMs); - } -#endif - program1ms(); - - /* Write outputs */ - CO_process_TPDO(CO, syncWas, 1000); - - /* verify timer overflow */ - if(CO_TMR_ISR_FLAG == 1){ - CO_errorReport(CO->em, CO_EM_ISR_TIMER_OVERFLOW, CO_EMC_SOFTWARE_INTERNAL, 0); - CO_TMR_ISR_FLAG = 0; - } - } - - /* calculate cycle time for performance measurement */ - uint16_t t = CO_TMR_TMR / (CO_PBCLK / 100); - OD_performance[ODA_performance_timerCycleTime] = t; - if(t > OD_performance[ODA_performance_timerCycleMaxTime]) - OD_performance[ODA_performance_timerCycleMaxTime] = t; -} -#endif - - -/* CAN interrupt function *****************************************************/ -CO_CAN_ISR(){ - CO_CANinterrupt(CO->CANmodule[0]); - /* Clear combined Interrupt flag */ - CO_CAN_ISR_FLAG = 0; -} - -#if CO_NO_CAN_MODULES >= 2 -CO_CAN_ISR2(){ - CO_CANinterrupt(CO->CANmodule[1]); - /* Clear combined Interrupt flag */ - CO_CAN_ISR2_FLAG = 0; -} -#endif diff --git a/stack/SAM3X/CO_Flash.c b/stack/SAM3X/CO_Flash.c deleted file mode 100644 index d204ad17..00000000 --- a/stack/SAM3X/CO_Flash.c +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Atmel SAM3 flash support for CANopen stack - * - * @file CO_Flash.c - * @author Janez Paternoster - * @author Olof Larsson - * @copyright 2014 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * CANopenNode is free and open source software: you can redistribute - * it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Following clarification and special exception to the GNU General Public - * License is included to the distribution terms of CANopenNode: - * - * Linking this library statically or dynamically with other modules is - * making a combined work based on this library. Thus, the terms and - * conditions of the GNU General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this library give - * you permission to link this library with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also meet, - * for each linked independent module, the terms and conditions of the - * license of that module. An independent module is a module which is - * not derived from or based on this library. If you modify this - * library, you may extend this exception to your version of the - * library, but you are not obliged to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -//============================================================================ -// INCLUDES -//============================================================================ -#include "CANopen.h" -#include "asf.h" - -//============================================================================ -// DEFINES -//============================================================================ -#define PARAM_STORE_PASSWORD 0x65766173 -#define PARAM_RESTORE_PASSWORD 0x64616F6C - -#define DEBUG 0 -#define CO_DBG_PRINT(...) do { if (DEBUG) printf(__VA_ARGS__); } while (0) - -#define LAST_PAGE_ADDRESS (IFLASH1_ADDR + IFLASH1_SIZE - (2*IFLASH1_PAGE_SIZE)) -#define PAGES_PER_FLASH_AREA 6 -#define CO_OD_FLASH_PARAM_DEFAULT LAST_PAGE_ADDRESS - (1*PAGES_PER_FLASH_AREA*IFLASH1_PAGE_SIZE) -#define CO_OD_FLASH_PARAM_RUNTIME LAST_PAGE_ADDRESS - (2*PAGES_PER_FLASH_AREA*IFLASH1_PAGE_SIZE) -#define IFLASH_PAGE_SIZE IFLASH1_PAGE_SIZE - -#define CO_UNUSED(v) (void)(v) - -//============================================================================ -// LOCAL DATA -//============================================================================ -extern struct sCO_OD_ROM CO_OD_ROM; - -enum CO_OD_H1010_StoreParam_Sub -{ - OD_H1010_STORE_PARAM_COUNT, - OD_H1010_STORE_PARAM_ALL, - OD_H1010_STORE_PARAM_COMM, - OD_H1010_STORE_PARAM_APP, - OD_H1010_STORE_PARAM_MANUFACTURER, - OD_H1010_STORE_PARAM_RESERVED = 0x80 -}; - -enum CO_OD_H1011_RestoreDefaultParam_Sub -{ - OD_H1011_RESTORE_PARAM_COUNT, - OD_H1011_RESTORE_PARAM_ALL, - OD_H1011_RESTORE_PARAM_COMM, - OD_H1011_RESTORE_PARAM_APP, - OD_H1011_RESTORE_PARAM_MANUFACTURER, - OD_H1011_RESTORE_PARAM_RESERVED = 0x80 -}; - -enum CO_StorageFunctionality_Flags -{ - SAVES_PARAM_ON_COMMAND = 0x01, - SAVES_PARAM_AUTONOMOUSLY = 0x02 -}; - -enum CO_RestoreFunctionality_Flags -{ - RESTORES_PARAMETERS = 0x01 -}; - -//============================================================================ -/** -* Store parameters of object dictionary into flash memory. -* \param[in] FlashAddress Use CO_OD_Flash_Adress for the normal parameter -* block and CO_OD_Flash_Default_Param for the -* default parameters -*/ -static CO_SDO_abortCode_t storeParameters(uint32_t FlashAddress, uint8_t ParametersSub) -{ - uint32_t ret; - - CO_UNUSED(ParametersSub); - CO_DBG_PRINT("Store parameters\n"); - - // Initialize flash, 6 wait states for flash writing. - if ((ret = flash_init(FLASH_ACCESS_MODE_128, 6)) != FLASH_RC_OK) - { - CO_DBG_PRINT("Flash initialization error %u\n\r", ret); - return CO_SDO_AB_HW; - } - - // Unlock flash - if ((ret = flash_unlock(FlashAddress, FlashAddress + (IFLASH_PAGE_SIZE*PAGES_PER_FLASH_AREA), 0, 0)) != FLASH_RC_OK) - { - CO_DBG_PRINT("Flash unlock error %u\n\r", ret); - return CO_SDO_AB_HW; - } - - // Erase page - uint32_t erase_flag = 1; - - // Check flash allocation - int32_t bytes_to_write = sizeof(CO_OD_ROM); - - if ((uint32_t)bytes_to_write > (IFLASH_PAGE_SIZE*PAGES_PER_FLASH_AREA)) - { - CO_DBG_PRINT("Flash allocation is %u bytes shy\n\r", (uint32_t)bytes_to_write-(IFLASH_PAGE_SIZE*PAGES_PER_FLASH_AREA)); - return CO_SDO_AB_HW; - } - - // Program data into flash - if (flash_write(FlashAddress, &CO_OD_ROM, min((int32_t)(IFLASH_PAGE_SIZE*PAGES_PER_FLASH_AREA), bytes_to_write), erase_flag) != FLASH_RC_OK) - { - CO_DBG_PRINT("Flash programming error %u\n\r", ret); - return CO_SDO_AB_HW; - } - - return CO_SDO_AB_NONE; -} - -//============================================================================ -/** -* Restore parameters of object dictionary from flash memory. -* \param[in] FlashAddress Use CO_OD_Flash_Adress for the normal parameter -* block and CO_OD_Flash_Default_Param for the -* default parameters -*/ -void flash_read(uint32_t FlashAddress, void* RamAddress, size_t len) -{ - uint8_t *p_flash = (uint8_t *) FlashAddress; - uint32_t idx; - - CO_DBG_PRINT("Restoring parameters..."); - for (idx = 0; idx < min(IFLASH_PAGE_SIZE*PAGES_PER_FLASH_AREA, len); idx++) - { - - ((uint8_t *) RamAddress)[idx] = p_flash[idx]; - } - CO_DBG_PRINT(" %u bytes from 0x%08x\n\r", idx, FlashAddress); -} - -static CO_SDO_abortCode_t restoreParameters(uint32_t FlashAddress, uint8_t ParametersSub) -{ - CO_UNUSED(ParametersSub); - - flash_read(FlashAddress, &CO_OD_ROM, sizeof(CO_OD_ROM)); - - return CO_SDO_AB_NONE; -} - -//============================================================================ -/** -* Access to object dictionary OD_H1010_STORE_PARAM_FUNC -*/ -static CO_SDO_abortCode_t CO_ODF_1010_StoreParam(CO_ODF_arg_t *ODF_arg) -{ - CO_DBG_PRINT("CO_ODF_1010 Sub: %d\n\r", ODF_arg->subIndex); - - uint32_t* value = (uint32_t*)ODF_arg->data; - - if (ODF_arg->reading) - { - if(OD_H1010_STORE_PARAM_ALL == ODF_arg->subIndex) - { - *value = SAVES_PARAM_ON_COMMAND; - } - return CO_SDO_AB_NONE; - } - - if(OD_H1010_STORE_PARAM_ALL != ODF_arg->subIndex) - { - return CO_SDO_AB_NONE; - } - - if (*value != PARAM_STORE_PASSWORD) - { - return CO_SDO_AB_DATA_TRANSF; - } - - return storeParameters(CO_OD_FLASH_PARAM_RUNTIME, ODF_arg->subIndex); -} - -//============================================================================ -/** -* Access to object dictionary OD_H1010_STORE_PARAM_FUNC -*/ -static CO_SDO_abortCode_t CO_ODF_1011_RestoreParam(CO_ODF_arg_t *ODF_arg) -{ - CO_DBG_PRINT("CO_ODF_1010 Sub: %d\n\r", ODF_arg->subIndex); - - uint32_t* value = (uint32_t*)ODF_arg->data; - - if (ODF_arg->reading) - { - if (OD_H1011_RESTORE_PARAM_ALL == ODF_arg->subIndex) - { - *value = RESTORES_PARAMETERS; - } - return CO_SDO_AB_NONE; - } - - if (OD_H1011_RESTORE_PARAM_ALL != ODF_arg->subIndex) - { - return CO_SDO_AB_NONE; - } - - if (*value != PARAM_RESTORE_PASSWORD) - { - return CO_SDO_AB_DATA_TRANSF; - } - - CO_SDO_abortCode_t Result = restoreParameters(CO_OD_FLASH_PARAM_DEFAULT, - ODF_arg->subIndex); - if (Result != CO_SDO_AB_NONE) - { - CO_DBG_PRINT("restoreParameters returned error"); - return Result; - } - - return storeParameters(CO_OD_FLASH_PARAM_RUNTIME, OD_H1011_RESTORE_PARAM_ALL); -} - -//=========================================================================== -/** -* Initialize flash library and data storage in flash -* We use two blocks in flash for data storage. One block is used for the -* default data that will be restored. The default parameters are stored -* at address CO_OD_Flash_Default_Param. The data that will be loaded at -* startup or saved if user modifies data is store at CO_OD_Flash_Adress. -*/ -void CO_FlashInit(void) -{ - // Info on addresses for flash data and default flash data - - CO_DBG_PRINT("Runtime OD flash, address 0x%08x, %u bytes\n\r", CO_OD_FLASH_PARAM_DEFAULT, PAGES_PER_FLASH_AREA*IFLASH1_PAGE_SIZE); - CO_DBG_PRINT("Default OD flash, address 0x%08x, %u bytes\n\r", CO_OD_FLASH_PARAM_DEFAULT, PAGES_PER_FLASH_AREA*IFLASH1_PAGE_SIZE); - - /* Before we can access the data, we need to make sure, that the flash - block are properly initialized. We do this by reading the block into - a local sCO_OD_ROM variable and verifying the FirstWord and LastWord - members. */ - - struct sCO_OD_ROM DefaultObjDicParam; - flash_read(CO_OD_FLASH_PARAM_DEFAULT, &DefaultObjDicParam, sizeof(DefaultObjDicParam)); - - /* If the default parameters are not present in flash, then we know that - we need to create them for later restore. */ - - if ((DefaultObjDicParam.FirstWord != CO_OD_FIRST_LAST_WORD) || - (DefaultObjDicParam.LastWord != CO_OD_FIRST_LAST_WORD)) - { - storeParameters(CO_OD_FLASH_PARAM_RUNTIME, OD_H1010_STORE_PARAM_ALL); - storeParameters(CO_OD_FLASH_PARAM_DEFAULT, OD_H1010_STORE_PARAM_ALL); - } - else - { - restoreParameters(CO_OD_FLASH_PARAM_RUNTIME, OD_H1010_STORE_PARAM_ALL); - } -} - -//=========================================================================== -void CO_FlashRegisterODFunctions(CO_t* CO) -{ - CO_OD_configure(CO->SDO, OD_H1010_STORE_PARAM_FUNC, - CO_ODF_1010_StoreParam, (void*)0, 0, 0); - CO_OD_configure(CO->SDO, OD_H1011_REST_PARAM_FUNC, - CO_ODF_1011_RestoreParam, (void*)0, 0, 0); -} diff --git a/stack/SAM3X/CO_Flash.h b/stack/SAM3X/CO_Flash.h deleted file mode 100644 index ed6a70b0..00000000 --- a/stack/SAM3X/CO_Flash.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Atmel SAM3 flash support for CANopen stack - * - * @file CO_Flash.h - * @author Janez Paternoster - * @author Olof Larsson - * @copyright 2014 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * CANopenNode is free and open source software: you can redistribute - * it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Following clarification and special exception to the GNU General Public - * License is included to the distribution terms of CANopenNode: - * - * Linking this library statically or dynamically with other modules is - * making a combined work based on this library. Thus, the terms and - * conditions of the GNU General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this library give - * you permission to link this library with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also meet, - * for each linked independent module, the terms and conditions of the - * license of that module. An independent module is a module which is - * not derived from or based on this library. If you modify this - * library, you may extend this exception to your version of the - * library, but you are not obliged to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -#ifndef CO_FLASH_H -#define CO_FLASH_H - -//============================================================================ -// INCLUDES -//============================================================================ -#include "CANopen.h" - -/** - * Initialize flash library and data storage in flash - * We use two blocks in flash for data storage. One block is used for the - * default data that will be restored. The default parameters are stored - * at address CO_OD_Flash_Default_Param. The data that will be loaded at - * startup or saved if user modifies data. - */ -void CO_FlashInit(void); - -/** - * Register object dictionary functions for parameter storage and restoring - * parameters (Object dictionary index 0x1010 Store Param and 0x1011 Restore - * default param. - */ -void CO_FlashRegisterODFunctions(CO_t* CO); - -#endif diff --git a/stack/SAM3X/CO_driver.c b/stack/SAM3X/CO_driver.c deleted file mode 100644 index b5fa19c9..00000000 --- a/stack/SAM3X/CO_driver.c +++ /dev/null @@ -1,665 +0,0 @@ -/* - * CAN module object for the Atmel SAM3X microcontroller. - * - * @file CO_driver.c - * @author Janez Paternoster - * @author Olof Larsson - * @copyright 2014 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * CANopenNode is free and open source software: you can redistribute - * it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Following clarification and special exception to the GNU General Public - * License is included to the distribution terms of CANopenNode: - * - * Linking this library statically or dynamically with other modules is - * making a combined work based on this library. Thus, the terms and - * conditions of the GNU General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this library give - * you permission to link this library with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also meet, - * for each linked independent module, the terms and conditions of the - * license of that module. An independent module is a module which is - * not derived from or based on this library. If you modify this - * library, you may extend this exception to your version of the - * library, but you are not obliged to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -#include "CO_driver.h" -#include "CO_Emergency.h" - -#include "asf.h" -#include "htc.h" - -#include "sn65hvd234.h" - -#define CO_UNUSED(v) (void)(v) -#define CANMB_TX (CANMB_NUMBER - 1) - -void reset_mailbox_conf(can_mb_conf_t *p_mailbox) -{ - p_mailbox->ul_mb_idx = 0; - p_mailbox->uc_obj_type = 0; - p_mailbox->uc_id_ver = 0; - p_mailbox->uc_length = 0; - p_mailbox->uc_tx_prio = 0; - p_mailbox->ul_status = 0; - p_mailbox->ul_id_msk = 0; - p_mailbox->ul_id = 0; - p_mailbox->ul_fid = 0; - p_mailbox->ul_datal = 0; - p_mailbox->ul_datah = 0; -} - - -/******************************************************************************/ -void CO_CANsetConfigurationMode(void *CANdriverState) -{ - CO_UNUSED(CANdriverState); -} - - -/******************************************************************************/ -void CO_CANsetNormalMode(CO_CANmodule_t *CANmodule) -{ - CO_UNUSED(CANmodule->CANdriverState); - - CANmodule->CANnormal = true; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_CANmodule_init( - CO_CANmodule_t *CANmodule, - void *CANdriverState, - CO_CANrx_t rxArray[], - uint16_t rxSize, - CO_CANtx_t txArray[], - uint16_t txSize, - uint16_t CANbitRate) -{ - uint16_t i; - uint32_t ul_sysclk; - - /* verify arguments */ - if(CANmodule==NULL || rxArray==NULL || txArray==NULL){ - return CO_ERROR_ILLEGAL_ARGUMENT; - } - - /* Configure object variables */ - CANmodule->CANdriverState = CANdriverState; - CANmodule->rxArray = rxArray; - CANmodule->rxSize = rxSize; - CANmodule->txArray = txArray; - CANmodule->txSize = txSize; - CANmodule->CANnormal = false; - CANmodule->useCANrxFilters = false; - CANmodule->bufferInhibitFlag = false; - CANmodule->firstCANtxMessage = true; - CANmodule->CANtxCount = 0U; - CANmodule->errOld = 0U; - CANmodule->em = NULL; - - for(i=0U; iCANdriverState) == CAN0) - { - /* CAN0 Transceiver */ - static sn65hvd234_ctrl_t can0_transceiver; - - /* Initialize CAN0 Transceiver. */ - sn65hvd234_set_rs(&can0_transceiver, PIN_CAN0_TR_RS_IDX); - sn65hvd234_set_en(&can0_transceiver, PIN_CAN0_TR_EN_IDX); - - /* Low power mode is listening only */ - sn65hvd234_disable_low_power(&can0_transceiver); - - /* Enable CAN0 Transceiver */ - sn65hvd234_enable(&can0_transceiver); - - /* Configure CAN timing */ - - /* Enable CAN0 & CAN0 clock. */ - pmc_enable_periph_clk(ID_CAN0); - - ul_sysclk = sysclk_get_cpu_hz(); - - if (can_init(CAN0, ul_sysclk, CANbitRate)) - { - printf("CAN0 initialization is completed\n\r"); - - /* Disable all CAN0 interrupts */ - can_disable_interrupt(CAN0, CAN_DISABLE_ALL_INTERRUPT_MASK); - - /* Configure and enable interrupt of CAN0 */ - NVIC_EnableIRQ(CAN0_IRQn); - } - - can_reset_all_mailbox(CANmodule->CANdriverState); - - /* CAN module filters are not used, all messages with standard 11-bit */ - /* identifier will be received */ - /* Configure mask 0 so that all messages with standard identifier are accepted */ - - /* Init CAN0 mailbox 0-6 as reception mailboxes */ - for (i = 0; i <= (CANMB_NUMBER-2); i++) - { - reset_mailbox_conf(&CANmodule->rxMbConf[i]); - CANmodule->rxMbConf[i].ul_mb_idx = i; - CANmodule->rxMbConf[i].uc_obj_type = CAN_MB_RX_MODE; - - if (i == (CANMB_NUMBER-2)) - CANmodule->rxMbConf[i].uc_obj_type = CAN_MB_RX_OVER_WR_MODE; - - /* Standard mode only, not extended mode */ - CANmodule->rxMbConf[i].ul_id_msk = CAN_MAM_MIDvA_Msk; - CANmodule->rxMbConf[i].ul_id = CAN_MID_MIDvA(0); - can_mailbox_init(CANmodule->CANdriverState, &CANmodule->rxMbConf[i]); - - /* Enable CAN0 mailbox number i interrupt. */ - can_enable_interrupt(CANmodule->CANdriverState, (0x1u << i)); - } - - /* Init last CAN0 mailbox, number 7, as transmit mailbox */ - reset_mailbox_conf(&CANmodule->txMbConf); - CANmodule->txMbConf.ul_mb_idx = (CANMB_TX); - CANmodule->txMbConf.uc_obj_type = CAN_MB_TX_MODE; - CANmodule->txMbConf.uc_tx_prio = 14; - CANmodule->txMbConf.uc_id_ver = 0; - CANmodule->txMbConf.ul_id_msk = 0; - can_mailbox_init(CANmodule->CANdriverState, &CANmodule->txMbConf); - } - - if (CANmodule->CANdriverState == CAN1) - { - /* CAN1 Transceiver */ - static sn65hvd234_ctrl_t can1_transceiver; - - /* Initialize CAN1 Transceiver */ - sn65hvd234_set_rs(&can1_transceiver, PIN_CAN1_TR_RS_IDX); - sn65hvd234_set_en(&can1_transceiver, PIN_CAN1_TR_EN_IDX); - - /* Enable CAN1 Transceiver */ - sn65hvd234_disable_low_power(&can1_transceiver); //Low power mode == listening only mode - sn65hvd234_enable(&can1_transceiver); - - /* Configure CAN timing */ - - /* Enable CAN1 & CAN1 clock */ - pmc_enable_periph_clk(ID_CAN1); - - ul_sysclk = sysclk_get_cpu_hz(); - - if (can_init(CAN1, ul_sysclk, CAN_BPS_250K)) - { - printf("CAN1 initialization is completed\n\r"); - - /* Disable all CAN1 interrupts */ - can_disable_interrupt(CAN1, CAN_DISABLE_ALL_INTERRUPT_MASK); - - /* Configure and enable interrupt of CAN1 */ - NVIC_EnableIRQ(CAN1_IRQn); - } - - can_reset_all_mailbox(CANmodule->CANdriverState); - - /* CAN module filters are not used, all messages with standard 11-bit */ - /* identifier will be received */ - /* Configure mask 0 so that all messages with standard identifier are accepted */ - - /* Init CAN1 mailbox 0-6 as reception mailboxes */ - - for (i = 0; i <= (CANMB_NUMBER-2); i++) - { - reset_mailbox_conf(&CANmodule->rxMbConf[i]); - CANmodule->rxMbConf[i].ul_mb_idx = i; - CANmodule->rxMbConf[i].uc_obj_type = CAN_MB_RX_MODE; - - if (i == (CANMB_NUMBER-2)) - CANmodule->rxMbConf[i].uc_obj_type = CAN_MB_RX_OVER_WR_MODE; - - /* Standard mode only, not extended mode */ - CANmodule->rxMbConf[i].ul_id_msk = CAN_MAM_MIDvA_Msk; - CANmodule->rxMbConf[i].ul_id = CAN_MID_MIDvA(0); - can_mailbox_init(CANmodule->CANdriverState, &CANmodule->rxMbConf[i]); - - /* Enable CAN1 mailbox number i interrupt. */ - can_enable_interrupt(CANmodule->CANdriverState, (0x1u << i)); - } - - /* Init last CAN1 mailbox, number 7, as transmit mailbox */ - reset_mailbox_conf(&CANmodule->txMbConf); - CANmodule->txMbConf.ul_mb_idx = (CANMB_TX); - CANmodule->txMbConf.uc_obj_type = CAN_MB_TX_MODE; - CANmodule->txMbConf.uc_tx_prio = 14; - CANmodule->txMbConf.uc_id_ver = 0; - CANmodule->txMbConf.ul_id_msk = 0; - can_mailbox_init(CANmodule->CANdriverState, &CANmodule->txMbConf); - } - - return CO_ERROR_NO; -} - - -/******************************************************************************/ -void CO_CANmodule_disable(CO_CANmodule_t *CANmodule) -{ - /* Turn the module off */ - can_disable(CANmodule->CANdriverState); -} - - -/******************************************************************************/ -uint16_t CO_CANrxMsg_readIdent(const CO_CANrxMsg_t *rxMsg) -{ - return (uint16_t) rxMsg->ident; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_CANrxBufferInit( - CO_CANmodule_t *CANmodule, - uint16_t index, - uint16_t ident, - uint16_t mask, - bool_t rtr, - void *object, - void (*pFunct)(void *object, const CO_CANrxMsg_t *message)) -{ - CO_ReturnError_t ret = CO_ERROR_NO; - - if((CANmodule!=NULL) && (object!=NULL) && (pFunct!=NULL) && (index < CANmodule->rxSize)) - { - /* Buffer, which will be configured */ - CO_CANrx_t *buffer = &CANmodule->rxArray[index]; - - /* Configure object variables */ - buffer->object = object; - buffer->pFunct = pFunct; - - /* CAN identifier and CAN mask, bit aligned with CAN module. Different on different microcontrollers. */ - buffer->ident = ident & 0x07FFU; - if(rtr){ - buffer->ident |= 0x0800U; - } - buffer->mask = (mask & 0x07FFU) | 0x0800U; - - /* Set CAN hardware module filter and mask. */ - if(CANmodule->useCANrxFilters) - { - } - } - else - { - ret = CO_ERROR_ILLEGAL_ARGUMENT; - } - - return ret; -} - - -/******************************************************************************/ -CO_CANtx_t *CO_CANtxBufferInit( - CO_CANmodule_t *CANmodule, - uint16_t index, - uint16_t ident, - bool_t rtr, - uint8_t noOfBytes, - bool_t syncFlag) -{ - CO_CANtx_t *buffer = NULL; - - if((CANmodule != NULL) && (index < CANmodule->txSize)) - { - /* Get specific buffer */ - buffer = &CANmodule->txArray[index]; - - /* CAN identifier, DLC and rtr, bit aligned with CAN module transmit buffer. */ - buffer->ident = ident; - buffer->rtr = rtr; - - buffer->bufferFull = false; - buffer->syncFlag = syncFlag; - buffer->DLC = noOfBytes; - } - - return buffer; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_CANsend(CO_CANmodule_t *CANmodule, CO_CANtx_t *buffer) -{ - CO_ReturnError_t err = CO_ERROR_NO; - - /* Verify overflow */ - if(buffer->bufferFull){ - if(!CANmodule->firstCANtxMessage){ - /* Don't set error, if bootup message is still on buffers */ - CO_errorReport((CO_EM_t*)CANmodule->em, CO_EM_CAN_TX_OVERFLOW, CO_EMC_CAN_OVERRUN, buffer->ident); - } - err = CO_ERROR_TX_OVERFLOW; - } - - CO_LOCK_CAN_SEND(); - - /* If CAN TX buffer is free, copy message to it */ - if (((can_mailbox_get_status(CANmodule->CANdriverState, CANMB_TX) & CAN_MSR_MRDY) == CAN_MSR_MRDY) && (CANmodule->CANtxCount == 0)) - { - CANmodule->bufferInhibitFlag = buffer->syncFlag; - - /* Copy message and txRequest */ - CANmodule->txMbConf.ul_id = CAN_MID_MIDvA(buffer->ident); - CANmodule->txMbConf.ul_datal = *((uint32_t *) &(buffer->data[0])); - CANmodule->txMbConf.ul_datah = *((uint32_t *) &(buffer->data[4])); - CANmodule->txMbConf.uc_length = buffer->DLC; - - if (buffer->rtr) - can_mailbox_tx_remote_frame(CANmodule->CANdriverState, &CANmodule->txMbConf); - else - can_mailbox_write(CANmodule->CANdriverState, &CANmodule->txMbConf); - - can_global_send_transfer_cmd(CANmodule->CANdriverState, 1 << CANMB_TX); - } - else /* If no buffer is free, message will be sent by interrupt */ - { - buffer->bufferFull = true; - CANmodule->CANtxCount++; - } - can_enable_interrupt(CANmodule->CANdriverState, 0x1u << CANMB_TX); - CO_UNLOCK_CAN_SEND(); - - return err; -} - - -/******************************************************************************/ -void CO_CANclearPendingSyncPDOs(CO_CANmodule_t *CANmodule) -{ - uint32_t tpdoDeleted = 0U; - - CO_LOCK_CAN_SEND(); - /* Abort message from CAN module, if there is synchronous TPDO. - * Take special care with this functionality. */ - if(/*messageIsOnCanBuffer && */CANmodule->bufferInhibitFlag){ - /* clear TXREQ */ - CANmodule->bufferInhibitFlag = false; - tpdoDeleted = 1U; - } - /* delete also pending synchronous TPDOs in TX buffers */ - if(CANmodule->CANtxCount != 0U){ - uint16_t i; - CO_CANtx_t *buffer = &CANmodule->txArray[0]; - for(i = CANmodule->txSize; i > 0U; i--){ - if(buffer->bufferFull){ - if(buffer->syncFlag){ - buffer->bufferFull = false; - CANmodule->CANtxCount--; - tpdoDeleted = 2U; - } - } - buffer++; - } - } - CO_UNLOCK_CAN_SEND(); - - if(tpdoDeleted != 0U){ - CO_errorReport((CO_EM_t*)CANmodule->em, CO_EM_TPDO_OUTSIDE_WINDOW, CO_EMC_COMMUNICATION, tpdoDeleted); - } -} - - -/******************************************************************************/ -void CO_CANverifyErrors(CO_CANmodule_t *CANmodule){ - uint16_t rxErrors = 0, txErrors = 0, overflow = 0; - CO_EM_t* em = (CO_EM_t*)CANmodule->em; - uint32_t err; - - /* get error counters from module. Id possible, function may use different way to - * determine errors. */ - - rxErrors = can_get_rx_error_cnt(CANmodule->CANdriverState); - - txErrors = can_get_tx_error_cnt(CANmodule->CANdriverState); - - //overflow = CANmodule->txSize; - - err = ((uint32_t)txErrors << 16) | ((uint32_t)rxErrors << 8) | overflow; - - if(CANmodule->errOld != err) - { - CANmodule->errOld = err; - - if(txErrors >= 256U) /* bus off */ - { - CO_errorReport(em, CO_EM_CAN_TX_BUS_OFF, CO_EMC_BUS_OFF_RECOVERED, err); - } - else /* not bus off */ - { - CO_errorReset(em, CO_EM_CAN_TX_BUS_OFF, err); - - if((rxErrors >= 96U) || (txErrors >= 96U)) - { /* bus warning */ - CO_errorReport(em, CO_EM_CAN_BUS_WARNING, CO_EMC_NO_ERROR, err); - } - - if(rxErrors >= 128U) /* RX bus passive */ - { - CO_errorReport(em, CO_EM_CAN_RX_BUS_PASSIVE, CO_EMC_CAN_PASSIVE, err); - } - else - { - CO_errorReset(em, CO_EM_CAN_RX_BUS_PASSIVE, err); - } - - if(txErrors >= 128U) /* TX bus passive */ - { - if(!CANmodule->firstCANtxMessage) - { - CO_errorReport(em, CO_EM_CAN_TX_BUS_PASSIVE, CO_EMC_CAN_PASSIVE, err); - } - } - else - { - bool_t isError = CO_isError(em, CO_EM_CAN_TX_BUS_PASSIVE); - if(isError) - { - CO_errorReset(em, CO_EM_CAN_TX_BUS_PASSIVE, err); - CO_errorReset(em, CO_EM_CAN_TX_OVERFLOW, err); - } - } - - if((rxErrors < 96U) && (txErrors < 96U)) /* no error */ - { - CO_errorReset(em, CO_EM_CAN_BUS_WARNING, err); - } - } - - if(overflow != 0U) /* CAN RX bus overflow */ - { - CO_errorReport(em, CO_EM_CAN_RXB_OVERFLOW, CO_EMC_CAN_OVERRUN, err); - } - } -} - - -/******************************************************************************/ -void CO_CANinterrupt(CO_CANmodule_t *CANmodule) -{ - uint32_t ul_status; - - ul_status = can_get_status(CANmodule->CANdriverState); - if (ul_status & GLOBAL_MAILBOX_MASK) - { - for (uint8_t i = 0; i < CANMB_NUMBER; i++) - { - ul_status = can_mailbox_get_status(CANmodule->CANdriverState, i); - if ((ul_status & CAN_MSR_MRDY) == CAN_MSR_MRDY) //Mailbox Ready Bit - { - //Handle interrupt - if (i != CANMB_TX) - { - //Receive interrupt - CO_CANrxMsg_t *rcvMsg; /* pointer to received message in CAN module */ - CO_CANrxMsg_t rcvMsgBuf; - uint16_t index; /* index of received message */ - uint32_t rcvMsgIdent; /* identifier of the received message */ - CO_CANrx_t *buffer = NULL; /* receive message buffer from CO_CANmodule_t object. */ - bool_t msgMatched = false; - - CANmodule->rxMbConf[i].ul_mb_idx = i; - CANmodule->rxMbConf[i].ul_status = ul_status; - can_mailbox_read(CANmodule->CANdriverState, &CANmodule->rxMbConf[i]); - - - /* Get message from module here */ - memset(rcvMsgBuf.data, 0, 8); - memcpy(rcvMsgBuf.data, &CANmodule->rxMbConf[i].ul_datal, CANmodule->rxMbConf[i].uc_length); - rcvMsgBuf.ident = CANmodule->rxMbConf[i].ul_id; - rcvMsgBuf.DLC = CANmodule->rxMbConf[i].uc_length; - - rcvMsg = &rcvMsgBuf; - - rcvMsgIdent = rcvMsg->ident; - if(CANmodule->useCANrxFilters) - { - /* CAN module filters are used. Message with known 11-bit identifier has */ - /* been received */ - index = 0; /* get index of the received message here. Or something similar */ - if(index < CANmodule->rxSize) - { - buffer = &CANmodule->rxArray[index]; - /* verify also RTR */ - if(((rcvMsgIdent ^ buffer->ident) & buffer->mask) == 0U) - { - msgMatched = true; - } - } - } - else - { - /* CAN module filters are not used, message with any standard 11-bit identifier */ - /* has been received. Search rxArray from CANmodule for the same CAN-ID. */ - buffer = &CANmodule->rxArray[0]; - for(index = CANmodule->rxSize; index > 0U; index--) - { - if(((rcvMsgIdent ^ buffer->ident) & buffer->mask) == 0U) - { - msgMatched = true; - break; - } - buffer++; - } - } - - /* Call specific function, which will process the message */ - if(msgMatched && (buffer != NULL) && (buffer->pFunct != NULL)) - { - buffer->pFunct(buffer->object, rcvMsg); - } - } - else - { - /* First CAN message (bootup) was sent successfully */ - CANmodule->firstCANtxMessage = false; - /* Clear flag from previous message */ - CANmodule->bufferInhibitFlag = false; - /* Are there any new messages waiting to be send */ - if(CANmodule->CANtxCount > 0U) - { - uint16_t j; /* Index of transmitting message */ - - /* First buffer */ - CO_CANtx_t *buffer = &CANmodule->txArray[0]; - /* Search through whole array of pointers to transmit message buffers. */ - for(j = CANmodule->txSize; j > 0U; j--) - { - /* If message buffer is full, send it. */ - if(buffer->bufferFull) - { - buffer->bufferFull = false; - CANmodule->CANtxCount--; - - /* Copy message to CAN buffer */ - CANmodule->txMbConf.ul_datal = *((uint32_t *) &(buffer->data[0])); - CANmodule->txMbConf.ul_datah = *((uint32_t *) &(buffer->data[4])); - - /* Write transmit information into mailbox. */ - CANmodule->txMbConf.ul_id = CAN_MID_MIDvA(buffer->ident); - CANmodule->txMbConf.uc_length = buffer->DLC; - - if (buffer->rtr) - can_mailbox_tx_remote_frame(CANmodule->CANdriverState, &CANmodule->txMbConf); - else - can_mailbox_write(CANmodule->CANdriverState, &CANmodule->txMbConf); - - can_global_send_transfer_cmd(CANmodule->CANdriverState, 1 << CANMB_TX); - - break; /* Exit for loop */ - } - buffer++; - } /* End of for loop */ - - /* Clear counter if no more messages */ - if(j == 0U){ - CANmodule->CANtxCount = 0U; - } - } - else - { - /* Nothing more to send */ - can_disable_interrupt(CANmodule->CANdriverState, 0x1u << CANMB_TX); - } - } - break; - } - } - } - else - { - if (ul_status & CAN_SR_ERRA); //error active - if (ul_status & CAN_SR_WARN); //warning limit - //CO_EM_CAN_BUS_WARNING - if (ul_status & CAN_SR_ERRP); //error passive - //CO_EM_CAN_TX_BUS_PASSIVE - if (ul_status & CAN_SR_BOFF); //bus off - //CO_EM_CAN_TX_BUS_OFF - if (ul_status & CAN_SR_SLEEP); //controller in sleep mode - if (ul_status & CAN_SR_WAKEUP); //controller woke up - if (ul_status & CAN_SR_TOVF); //timer overflow - if (ul_status & CAN_SR_TSTP); //timestamp - start or end of frame - if (ul_status & CAN_SR_CERR); //CRC error in mailbox - if (ul_status & CAN_SR_SERR); //stuffing error in mailbox - if (ul_status & CAN_SR_AERR); //ack error - if (ul_status & CAN_SR_FERR); //form error - if (ul_status & CAN_SR_BERR); //bit error - } -} diff --git a/stack/SAM3X/CO_driver_target.h b/stack/SAM3X/CO_driver_target.h deleted file mode 100644 index 4776d858..00000000 --- a/stack/SAM3X/CO_driver_target.h +++ /dev/null @@ -1,148 +0,0 @@ -/* - * CAN module object for the Atmel SAM3X microcontroller. - * - * @file CO_driver_target.h - * @author Janez Paternoster - * @author Olof Larsson - * @copyright 2014 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * CANopenNode is free and open source software: you can redistribute - * it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Following clarification and special exception to the GNU General Public - * License is included to the distribution terms of CANopenNode: - * - * Linking this library statically or dynamically with other modules is - * making a combined work based on this library. Thus, the terms and - * conditions of the GNU General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this library give - * you permission to link this library with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also meet, - * for each linked independent module, the terms and conditions of the - * license of that module. An independent module is a module which is - * not derived from or based on this library. If you modify this - * library, you may extend this exception to your version of the - * library, but you are not obliged to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - - -#ifndef CO_DRIVER_TARGET_H -#define CO_DRIVER_TARGET_H - - -#include /* for 'NULL' */ -#include /* for 'int8_t' to 'uint64_t' */ - -#include "asf.h" -#include -#include - - -/** Endianness */ -#define CO_LITTLE_ENDIAN - - -/* CAN module base address */ -#define ADDR_CAN1 CAN0 -#define ADDR_CAN2 CAN1 -/* -Remember to set: - #define CONF_BOARD_CAN0 - #define CONF_BOARD_CAN1 -in conf_board.h -*/ - - -/* Critical sections */ -#define CO_LOCK_CAN_SEND() //taskENTER_CRITICAL() -#define CO_UNLOCK_CAN_SEND() //taskEXIT_CRITICAL() - -#define CO_LOCK_EMCY() //taskENTER_CRITICAL() -#define CO_UNLOCK_EMCY() //taskEXIT_CRITICAL() - -#define CO_LOCK_OD() //taskENTER_CRITICAL() -#define CO_UNLOCK_OD() //taskEXIT_CRITICAL() - - -/* Data types */ -/* int8_t to uint64_t are defined in stdint.h */ -typedef unsigned char bool_t; -typedef float float32_t; -typedef long double float64_t; -typedef char char_t; -typedef unsigned char oChar_t; -typedef unsigned char domain_t; - - -/* CAN receive message structure as aligned in CAN module. */ -typedef struct{ - /** CAN identifier. It must be read through CO_CANrxMsg_readIdent() function. */ - uint32_t ident; - uint8_t DLC ; - uint8_t data[8]; - can_mb_conf_t mbConf; /* Reference to controller's mailboxes */ -}CO_CANrxMsg_t; - - -/* Received message object */ -typedef struct{ - uint16_t ident; - uint16_t mask; - void *object; - void (*pFunct)(void *object, const CO_CANrxMsg_t *message); -}CO_CANrx_t; - - -/* Transmit message object. */ -typedef struct{ - uint32_t ident; - uint8_t DLC; - uint8_t data[8]; - volatile bool_t bufferFull; - volatile bool_t syncFlag; - bool_t rtr; -}CO_CANtx_t; - - -/* CAN module object. */ -typedef struct{ - Can *CANdriverState; - CO_CANrx_t *rxArray; - uint16_t rxSize; - CO_CANtx_t *txArray; - uint16_t txSize; - volatile bool_t CANnormal; - volatile bool_t useCANrxFilters; - volatile bool_t bufferInhibitFlag; - volatile bool_t firstCANtxMessage; - volatile uint16_t CANtxCount; - uint32_t errOld; - void *em; - can_mb_conf_t rxMbConf[CANMB_NUMBER-1]; /* Reference to controller's mailboxes */ - can_mb_conf_t txMbConf; -}CO_CANmodule_t; - - -/* CAN interrupt receives and transmits CAN messages. */ -void CO_CANinterrupt(CO_CANmodule_t *CANmodule); - - -#endif diff --git a/stack/SAM3X/readme b/stack/SAM3X/readme deleted file mode 100644 index f0964415..00000000 --- a/stack/SAM3X/readme +++ /dev/null @@ -1,6 +0,0 @@ -Used with Atmel ASF library. - -http://www.atmel.com/asf - -Contributed by Olof Larsson (dec 2014): -http://sourceforge.net/p/canopennode/discussion/387151/thread/8e789d60/ diff --git a/stack/STM32/CO_driver.c b/stack/STM32/CO_driver.c deleted file mode 100644 index 8666ba35..00000000 --- a/stack/STM32/CO_driver.c +++ /dev/null @@ -1,550 +0,0 @@ -/* - * CAN module object for ST STM32F103 microcontroller. - * - * @file CO_driver.c - * @author Janez Paternoster - * @author Ondrej Netik - * @author Vijayendra - * @author Jan van Lienden - * @copyright 2013 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * CANopenNode is free and open source software: you can redistribute - * it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Following clarification and special exception to the GNU General Public - * License is included to the distribution terms of CANopenNode: - * - * Linking this library statically or dynamically with other modules is - * making a combined work based on this library. Thus, the terms and - * conditions of the GNU General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this library give - * you permission to link this library with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also meet, - * for each linked independent module, the terms and conditions of the - * license of that module. An independent module is a module which is - * not derived from or based on this library. If you modify this - * library, you may extend this exception to your version of the - * library, but you are not obliged to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -/* Includes ------------------------------------------------------------------*/ -#include "stm32f10x_conf.h" -#include "CO_driver.h" -#include "CO_Emergency.h" -#include "led.h" -#include - -/* Private macro -------------------------------------------------------------*/ -/* Private define ------------------------------------------------------------*/ -/* Private variable ----------------------------------------------------------*/ -/* Private function ----------------------------------------------------------*/ -static void CO_CANClkSetting (void); -static void CO_CANconfigGPIO (void); -static void CO_CANsendToModule(CO_CANmodule_t *CANmodule, CO_CANtx_t *buffer, uint8_t transmit_mailbox); - -#ifdef CO_USE_LEDS -void InitCanLeds(void) -{ - vLED_InitRCC(); - vLED_InitPort(); -} - -void CanLedsSet(eCoLeds led) -{ - if (led & eCoLed_Green) - vLED_OnPB14Led(); - else - vLED_OffPB14Led(); - - if (led & eCoLed_Red) - vLED_OnPB15Led(); - else - vLED_OffPB15Led(); - -} -#endif /* CO_USE_LEDS */ - -/******************************************************************************* - Macro and Constants - CAN module registers - *******************************************************************************/ - - -/******************************************************************************/ -void CO_CANsetConfigurationMode(void *CANdriverState){ -} - - -/******************************************************************************/ -void CO_CANsetNormalMode(CO_CANmodule_t *CANmodule){ - CANmodule->CANnormal = true; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_CANmodule_init( - CO_CANmodule_t *CANmodule, - void *CANdriverState, - CO_CANrx_t rxArray[], - uint16_t rxSize, - CO_CANtx_t txArray[], - uint16_t txSize, - uint16_t CANbitRate) -{ - CAN_InitTypeDef CAN_InitStruct; - CAN_FilterInitTypeDef CAN_FilterInitStruct; - NVIC_InitTypeDef NVIC_InitStructure; - int i; - uint8_t result; - - /* verify arguments */ - if(CANmodule==NULL || rxArray==NULL || txArray==NULL){ - return CO_ERROR_ILLEGAL_ARGUMENT; - } - - CANmodule->CANdriverState = CANdriverState; - CANmodule->rxArray = rxArray; - CANmodule->rxSize = rxSize; - CANmodule->txArray = txArray; - CANmodule->txSize = txSize; - CANmodule->CANnormal = false; - CANmodule->useCANrxFilters = false; - CANmodule->bufferInhibitFlag = 0; - CANmodule->firstCANtxMessage = 1; - CANmodule->CANtxCount = 0; - CANmodule->errOld = 0; - CANmodule->em = 0; - - // Replaced magic number 0x03 by (CAN_IT_TME | CAN_IT_FMP0) JvL - CAN_ITConfig(CANmodule->CANdriverState, (CAN_IT_TME | CAN_IT_FMP0), DISABLE); - - for (i = 0; i < rxSize; i++) - { - CANmodule->rxArray[i].ident = 0; - CANmodule->rxArray[i].pFunct = 0; - } - for (i = 0; i < txSize; i++) - { - CANmodule->txArray[i].bufferFull = 0; - } - - /* CO - VJ Changed */ - /* Setting Clock of CAN HW */ - CO_CANClkSetting(); - - /* GPIO Config for CAN */ - CO_CANconfigGPIO(); - - /* Init CAN controler */ - CAN_DeInit(CANmodule->CANdriverState); - CAN_StructInit(&CAN_InitStruct); - switch (CANbitRate) - { - case 1000: CAN_InitStruct.CAN_Prescaler = 2; - break; - case 500: CAN_InitStruct.CAN_Prescaler = 4; - break; - default: - case 250: CAN_InitStruct.CAN_Prescaler = 8; - break; - case 125: CAN_InitStruct.CAN_Prescaler = 16; - break; - case 100: CAN_InitStruct.CAN_Prescaler = 20; - break; - case 50: CAN_InitStruct.CAN_Prescaler = 40; - break; - case 20: CAN_InitStruct.CAN_Prescaler = 100; - break; - case 10: CAN_InitStruct.CAN_Prescaler = 200; - break; - } - CAN_InitStruct.CAN_SJW = CAN_SJW_4tq; // changed by VJ, old value = CAN_SJW_1tq; - CAN_InitStruct.CAN_BS1 = CAN_BS1_12tq; // changed by VJ, old value = CAN_BS1_3tq; - CAN_InitStruct.CAN_BS2 = CAN_BS2_5tq; // changed by VJ, old value = CAN_BS2_2tq; - CAN_InitStruct.CAN_NART = ENABLE; // No Automatic retransmision - - /* CO - Changed VJ Start */ - result = CAN_Init(CANmodule->CANdriverState, &CAN_InitStruct); - if (result == 0) - { - // TRACE_DEBUG_WP("res=%d\n\r", result); - return CO_ERROR_TIMEOUT; /* CO- Return Init failed */ - } - /* CO - Changed VJ End */ - - memset(&CAN_FilterInitStruct, 0, sizeof (CAN_FilterInitStruct)); - CAN_FilterInitStruct.CAN_FilterNumber = 0; - CAN_FilterInitStruct.CAN_FilterIdHigh = 0; - CAN_FilterInitStruct.CAN_FilterIdLow = 0; - CAN_FilterInitStruct.CAN_FilterMaskIdHigh = 0; - CAN_FilterInitStruct.CAN_FilterMaskIdLow = 0; - CAN_FilterInitStruct.CAN_FilterFIFOAssignment = 0; // pouzivame jen FIFO0 - CAN_FilterInitStruct.CAN_FilterMode = CAN_FilterMode_IdMask; - CAN_FilterInitStruct.CAN_FilterScale = CAN_FilterScale_32bit; - CAN_FilterInitStruct.CAN_FilterActivation = ENABLE; - CAN_FilterInit(&CAN_FilterInitStruct); - - - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; - // preruseni od prijimace == interrupts from receiver - NVIC_InitStructure.NVIC_IRQChannel = CAN1_RX0_INTERRUPTS; - NVIC_Init(&NVIC_InitStructure); - // preruseni od vysilace == interrupts from transmitter - NVIC_InitStructure.NVIC_IRQChannel = CAN1_TX_INTERRUPTS; - NVIC_Init(&NVIC_InitStructure); - - // CAN_OperatingModeRequest(CANmodule->CANdriverState, CAN_Mode_Normal); // Not needed as after init Can_init functions puts the controller in normal mode - VJ - - CAN_ITConfig(CANmodule->CANdriverState, 0x03, ENABLE); - - return CO_ERROR_NO; -} - -/******************************************************************************/ -void CO_CANmodule_disable(CO_CANmodule_t *CANmodule) -{ - CAN_DeInit(CANmodule->CANdriverState); -} - -/******************************************************************************/ -CO_ReturnError_t CO_CANrxBufferInit( - CO_CANmodule_t *CANmodule, - uint16_t index, - uint16_t ident, - uint16_t mask, - int8_t rtr, - void *object, - void (*pFunct)(void *object, CO_CANrxMsg_t *message)) -{ - CO_CANrx_t *rxBuffer; - //CanRxMsg *rxBuffer; - uint16_t RXF, RXM; - - //safety - if (!CANmodule || !object || !pFunct || index >= CANmodule->rxSize) - { - return CO_ERROR_ILLEGAL_ARGUMENT; - } - - //buffer, which will be configured - rxBuffer = CANmodule->rxArray + index; - - //Configure object variables - rxBuffer->object = object; - rxBuffer->pFunct = pFunct; - - - //CAN identifier and CAN mask, bit aligned with CAN module registers - RXF = (ident & 0x07FF) << 2; - if (rtr) RXF |= 0x02; - RXM = (mask & 0x07FF) << 2; - RXM |= 0x02; - - //configure filter and mask - if (RXF != rxBuffer->ident || RXM != rxBuffer->mask) - { - rxBuffer->ident = RXF; - rxBuffer->mask = RXM; - } - - return CO_ERROR_NO; -} - -/******************************************************************************/ -CO_CANtx_t *CO_CANtxBufferInit( - CO_CANmodule_t *CANmodule, - uint16_t index, - uint16_t ident, - int8_t rtr, - uint8_t noOfBytes, - int8_t syncFlag) -{ - uint32_t TXF; - CO_CANtx_t *buffer; - - //safety - if (!CANmodule || CANmodule->txSize <= index) return 0; - - //get specific buffer - buffer = &CANmodule->txArray[index]; - - //CAN identifier, bit aligned with CAN module registers - - TXF = ident << 21; - TXF &= 0xFFE00000; - if (rtr) TXF |= 0x02; - - //write to buffer - buffer->ident = TXF; - buffer->DLC = noOfBytes; - buffer->bufferFull = 0; - buffer->syncFlag = syncFlag ? 1 : 0; - - return buffer; -} - -int8_t getFreeTxBuff(CO_CANmodule_t *CANmodule) -{ - uint8_t txBuff = CAN_TXMAILBOX_0; - - //if (CAN_TransmitStatus(CANmodule->CANdriverState, txBuff) == CAN_TxStatus_Ok) - for (txBuff = CAN_TXMAILBOX_0; txBuff <= (CAN_TXMAILBOX_2 + 1); txBuff++) - { - switch (txBuff) - { - case (CAN_TXMAILBOX_0 ): - if (CANmodule->CANdriverState->TSR & CAN_TSR_TME0 ) - return txBuff; - else - break; - case (CAN_TXMAILBOX_1 ): - if (CANmodule->CANdriverState->TSR & CAN_TSR_TME1 ) - return txBuff; - else - break; - case (CAN_TXMAILBOX_2 ): - if (CANmodule->CANdriverState->TSR & CAN_TSR_TME2 ) - return txBuff; - else - break; - default: - break; - } - } - return -1; -} - -/******************************************************************************/ -CO_ReturnError_t CO_CANsend(CO_CANmodule_t *CANmodule, CO_CANtx_t *buffer) -{ - CO_ReturnError_t err = CO_ERROR_NO; - int8_t txBuff; - - /* Verify overflow */ - if(buffer->bufferFull) - { - if(!CANmodule->firstCANtxMessage)/* don't set error, if bootup message is still on buffers */ - CO_errorReport((CO_EM_t*)CANmodule->em, CO_EM_CAN_TX_OVERFLOW, CO_EMC_CAN_OVERRUN, 0); - err = CO_ERROR_TX_OVERFLOW; - } - - CO_LOCK_CAN_SEND(); - //if CAN TB buffer0 is free, copy message to it - txBuff = getFreeTxBuff(CANmodule); - // #error change this - use only one buffer for transmission - see generic driver - if(txBuff != -1 && CANmodule->CANtxCount == 0) - { - CANmodule->bufferInhibitFlag = buffer->syncFlag; - CO_CANsendToModule(CANmodule, buffer, txBuff); - } - //if no buffer is free, message will be sent by interrupt - else - { - buffer->bufferFull = 1; - CANmodule->CANtxCount++; - // vsechny buffery jsou plny, musime povolit preruseni od vysilace, odvysilat az v preruseni - CAN_ITConfig(CANmodule->CANdriverState, CAN_IT_TME, ENABLE); - } - CO_UNLOCK_CAN_SEND(); - - return err; -} - -/******************************************************************************/ -void CO_CANclearPendingSyncPDOs(CO_CANmodule_t *CANmodule) -{ - - /* See generic driver for implemetation. */ -} - -/******************************************************************************/ -void CO_CANverifyErrors(CO_CANmodule_t *CANmodule) -{ - uint32_t err; - CO_EM_t* em = (CO_EM_t*)CANmodule->em; - - err = CANmodule->CANdriverState->ESR; - // if(CAN_REG(CANmodule->CANdriverState, C_INTF) & 4) err |= 0x80; - - if(CANmodule->errOld != err) - { - CANmodule->errOld = err; - - //CAN RX bus overflow - if(CANmodule->CANdriverState->RF0R & 0x08) - { - CO_errorReport(em, CO_EM_CAN_RXB_OVERFLOW, CO_EMC_CAN_OVERRUN, err); - CANmodule->CANdriverState->RF0R &=~0x08;//clear bits - } - - //CAN TX bus off - if(err & 0x04) CO_errorReport(em, CO_EM_CAN_TX_BUS_OFF, CO_EMC_BUS_OFF_RECOVERED, err); - else CO_errorReset(em, CO_EM_CAN_TX_BUS_OFF, err); - - //CAN TX or RX bus passive - if(err & 0x02) - { - if(!CANmodule->firstCANtxMessage) CO_errorReport(em, CO_EM_CAN_TX_BUS_PASSIVE, CO_EMC_CAN_PASSIVE, err); - } - else - { - // int16_t wasCleared; - /* wasCleared = */CO_errorReset(em, CO_EM_CAN_TX_BUS_PASSIVE, err); - /* if(wasCleared == 1) */CO_errorReset(em, CO_EM_CAN_TX_OVERFLOW, err); - } - - - //CAN TX or RX bus warning - if(err & 0x01) - { - CO_errorReport(em, CO_EM_CAN_BUS_WARNING, CO_EMC_NO_ERROR, err); - } - else - { - CO_errorReset(em, CO_EM_CAN_BUS_WARNING, err); - } - } -} - -/******************************************************************************/ -// Interrupt from Receiver -void CO_CANinterrupt_Rx(CO_CANmodule_t *CANmodule) -{ - CanRxMsg CAN1_RxMsg; - - CAN_Receive(CANmodule->CANdriverState, CAN_FilterFIFO0, &CAN1_RxMsg); - { - uint16_t index; - uint8_t msgMatched = 0; - CO_CANrx_t *msgBuff = CANmodule->rxArray; - for (index = 0; index < CANmodule->rxSize; index++) - { - uint16_t msg = (CAN1_RxMsg.StdId << 2) | (CAN1_RxMsg.RTR ? 2 : 0); - if (((msg ^ msgBuff->ident) & msgBuff->mask) == 0) - { - msgMatched = 1; - break; - } - msgBuff++; - } - //Call specific function, which will process the message - if (msgMatched && msgBuff->pFunct) - msgBuff->pFunct(msgBuff->object, &CAN1_RxMsg); - } -} - -/******************************************************************************/ -// Interrupt from Transeiver -void CO_CANinterrupt_Tx(CO_CANmodule_t *CANmodule) -{ - - int8_t txBuff; - /* Clear interrupt flag */ - CAN_ITConfig(CANmodule->CANdriverState, CAN_IT_TME, DISABLE); // Transmit mailbox empty interrupt - /* First CAN message (bootup) was sent successfully */ - CANmodule->firstCANtxMessage = 0; - /* clear flag from previous message */ - CANmodule->bufferInhibitFlag = 0; - /* Are there any new messages waiting to be send */ - if(CANmodule->CANtxCount > 0) - { - uint16_t i; /* index of transmitting message */ - - /* first buffer */ - CO_CANtx_t *buffer = CANmodule->txArray; - /* search through whole array of pointers to transmit message buffers. */ - for(i = CANmodule->txSize; i > 0; i--) - { - /* if message buffer is full, send it. */ - if(buffer->bufferFull) - { - buffer->bufferFull = 0; - CANmodule->CANtxCount--; - txBuff = getFreeTxBuff(CANmodule); //VJ - /* Copy message to CAN buffer */ - CANmodule->bufferInhibitFlag = buffer->syncFlag; - CO_CANsendToModule(CANmodule, buffer, txBuff); - break; /* exit for loop */ - } - buffer++; - }/* end of for loop */ - - /* Clear counter if no more messages */ - if(i == 0) CANmodule->CANtxCount = 0; - } -} - -/******************************************************************************/ -void CO_CANinterrupt_Status(CO_CANmodule_t *CANmodule) -{ - // status is evalved with pooling -} - -/******************************************************************************/ -static void CO_CANsendToModule(CO_CANmodule_t *CANmodule, CO_CANtx_t *buffer, uint8_t transmit_mailbox) -{ - - CanTxMsg CAN1_TxMsg; - int i; - - /*Test code, VJ using Drivers Start*/ - CAN1_TxMsg.IDE = CAN_ID_STD; - CAN1_TxMsg.DLC = buffer->DLC; - for (i = 0; i < 8; i++) CAN1_TxMsg.Data[i] = buffer->data[i]; - CAN1_TxMsg.StdId = ((buffer->ident) >> 21); - CAN1_TxMsg.RTR = CAN_RTR_DATA; - - CAN_Transmit(CANmodule->CANdriverState, &CAN1_TxMsg); - CAN_ITConfig(CANmodule->CANdriverState, CAN_IT_TME, ENABLE); - - /*Test code, VJ using Drivers End*/ - -} - -/* CO- VJ Changed Start */ -/******************************************************************************/ -static void CO_CANClkSetting (void) -{ - RCC_APB2PeriphClockCmd(CLOCK_GPIO_CAN | RCC_APB2Periph_AFIO, ENABLE); - RCC_APB1PeriphClockCmd(CLOCK_CAN, ENABLE); -} - -/******************************************************************************/ -static void CO_CANconfigGPIO (void) -{ - - GPIO_InitTypeDef GPIO_InitStructure; - - /* Remap */ - GPIO_PinRemapConfig(GPIO_Remapping_CAN, GPIO_CAN_Remap_State); - /* Configure CAN pin: RX */ - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_CAN_RX; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; - GPIO_Init(GPIO_CAN, &GPIO_InitStructure); - /* Configure CAN pin: TX */ - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_CAN_TX; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; - GPIO_Init(GPIO_CAN, &GPIO_InitStructure); - -} -/* CO- VJ Change End */ diff --git a/stack/STM32/CO_driver_target.h b/stack/STM32/CO_driver_target.h deleted file mode 100644 index bae49e4b..00000000 --- a/stack/STM32/CO_driver_target.h +++ /dev/null @@ -1,201 +0,0 @@ -/* - * CAN module object for ST STM32F103 microcontroller. - * - * @file CO_driver_target.h - * @author Janez Paternoster - * @author Ondrej Netik - * @author Vijayendra - * @author Jan van Lienden - * @copyright 2013 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * CANopenNode is free and open source software: you can redistribute - * it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Following clarification and special exception to the GNU General Public - * License is included to the distribution terms of CANopenNode: - * - * Linking this library statically or dynamically with other modules is - * making a combined work based on this library. Thus, the terms and - * conditions of the GNU General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this library give - * you permission to link this library with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also meet, - * for each linked independent module, the terms and conditions of the - * license of that module. An independent module is a module which is - * not derived from or based on this library. If you modify this - * library, you may extend this exception to your version of the - * library, but you are not obliged to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - - -#ifndef CO_DRIVER_TARGET_H -#define CO_DRIVER_TARGET_H - - -/* Includes ------------------------------------------------------------------*/ -// #include "common.h" -// #include "stm32f10x_conf.h" - -/* Exported define -----------------------------------------------------------*/ -#define PACKED_STRUCT __attribute__((packed)) -#define ALIGN_STRUCT_DWORD __attribute__((aligned(4))) - -/* Peripheral addresses */ -#define ADDR_CAN1 CAN1 -#define TMIDxR_TXRQ ((uint32_t)0x00000001) /* Transmit mailbox request */ - -/* Critical sections */ -#define CO_LOCK_CAN_SEND() __set_PRIMASK(1); -#define CO_UNLOCK_CAN_SEND() __set_PRIMASK(0); - -#define CO_LOCK_EMCY() __set_PRIMASK(1); -#define CO_UNLOCK_EMCY() __set_PRIMASK(0); - -#define CO_LOCK_OD() __set_PRIMASK(1); -#define CO_UNLOCK_OD() __set_PRIMASK(0); - - -#define CLOCK_CAN RCC_APB1Periph_CAN1 - -#define CAN_REMAP_2 /* Select CAN1 remap 2 */ -#ifdef CAN1_NO_REMAP /* CAN1 not remapped */ -#define CLOCK_GPIO_CAN RCC_APB2Periph_GPIOA -#define GPIO_Remapping_CAN (0) -#define GPIO_CAN GPIOA -#define GPIO_Pin_CAN_RX GPIO_Pin_11 -#define GPIO_Pin_CAN_TX GPIO_Pin_12 -#define GPIO_CAN_Remap_State DISABLE -#endif -#ifdef CAN_REMAP1 /* CAN1 remap 1 */ -#define CLOCK_GPIO_CAN RCC_APB2Periph_GPIOB -#define GPIO_Remapping_CAN GPIO_Remap1_CAN1 -#define GPIO_CAN GPIOB -#define GPIO_Pin_CAN_RX GPIO_Pin_8 -#define GPIO_Pin_CAN_TX GPIO_Pin_9 -#define GPIO_CAN_Remap_State ENABLE -#endif -#ifdef CAN_REMAP_2 /* CAN1 remap 2 */ -#define CLOCK_GPIO_CAN RCC_APB2Periph_GPIOD -#define GPIO_Remapping_CAN GPIO_Remap2_CAN1 -#define GPIO_CAN GPIOD -#define GPIO_Pin_CAN_RX GPIO_Pin_0 -#define GPIO_Pin_CAN_TX GPIO_Pin_1 -#define GPIO_CAN_Remap_State ENABLE -#endif - -#ifdef STM32F10X_CL -#define CAN1_TX_INTERRUPTS CAN1_TX_IRQn -#define CAN1_RX0_INTERRUPTS CAN1_RX0_IRQn -#else -#define CAN1_TX_INTERRUPTS USB_HP_CAN1_TX_IRQn -#define CAN1_RX0_INTERRUPTS USB_LP_CAN1_RX0_IRQn -#endif - -#define CAN_TXMAILBOX_0 ((uint8_t)0x00) -#define CAN_TXMAILBOX_1 ((uint8_t)0x01) -#define CAN_TXMAILBOX_2 ((uint8_t)0x02) - -/* Timeout for initialization */ - -#define INAK_TIMEOUT ((uint32_t)0x0000FFFF) -/* Data types */ -typedef float float32_t; -typedef long double float64_t; -typedef char char_t; -typedef unsigned char oChar_t; -typedef unsigned char domain_t; - -/* CAN receive message structure as aligned in CAN module. - * prevzato z stm32f10_can.h - velikostne polozky a poradi odpovidaji. */ -typedef struct{ - uint32_t ident; /* Standard Identifier */ - uint32_t ExtId; /* Specifies the extended identifier */ - uint8_t IDE; /* Specifies the type of identifier for the - message that will be received */ - uint8_t RTR; /* Remote Transmission Request bit */ - uint8_t DLC; /* Data length code (bits 0...3) */ - uint8_t data[8]; /* 8 data bytes */ - uint8_t FMI; /* Specifies the index of the filter the message - stored in the mailbox passes through */ -}CO_CANrxMsg_t; - - -/* Received message object */ -typedef struct{ - uint16_t ident; - uint16_t mask; - void *object; - void (*pFunct)(void *object, CanRxMsg *message); // Changed by VJ -}CO_CANrx_t; - - -/* Transmit message object. */ -typedef struct{ - uint32_t ident; - uint8_t DLC; - uint8_t data[8]; - volatile uint8_t bufferFull; - volatile uint8_t syncFlag; -}CO_CANtx_t;/* ALIGN_STRUCT_DWORD; */ - - -/* CAN module object. */ -typedef struct{ - CAN_TypeDef *CANdriverState; /* STM32F4xx specific */ - CO_CANrx_t *rxArray; - uint16_t rxSize; - CO_CANtx_t *txArray; - uint16_t txSize; - volatile bool_t CANnormal; - volatile bool_t useCANrxFilters; - volatile uint8_t useCANrxFilters; - volatile uint8_t bufferInhibitFlag; - volatile uint8_t firstCANtxMessage; - volatile uint16_t CANtxCount; - uint32_t errOld; - void *em; -}CO_CANmodule_t; - -#ifdef CO_USE_LEDS -/* Init CAN Led Interface */ -typedef enum { - eCoLed_None = 0, - eCoLed_Green = 1, - eCoLed_Red = 2, -} eCoLeds; - -/* Exported variables -----------------------------------------------------------*/ - -/* Exported functions -----------------------------------------------------------*/ -void InitCanLeds(void); -void CanLedsOn(eCoLeds led); -void CanLedsOff(eCoLeds led); -void CanLedsSet(eCoLeds led); -#endif /* CO_USE_LEDS */ - - -/* CAN interrupts receives and transmits CAN messages. */ -void CO_CANinterrupt_Rx(CO_CANmodule_t *CANmodule); -void CO_CANinterrupt_Tx(CO_CANmodule_t *CANmodule); -void CO_CANinterrupt_Status(CO_CANmodule_t *CANmodule); - - -#endif /* CO_DRIVER_TARGET_H */ diff --git a/stack/STM32F3/CO_Flash.c b/stack/STM32F3/CO_Flash.c deleted file mode 100644 index 280b51d9..00000000 --- a/stack/STM32F3/CO_Flash.c +++ /dev/null @@ -1,276 +0,0 @@ -/* - * STM32F3 flash support for CANopen stack - * - * @file CO_Flash.c - * @author Janez Paternoster - * @author Olof Larsson - * @author Petteri Mustonen - * @copyright 2014 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * CANopenNode is free and open source software: you can redistribute - * it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Following clarification and special exception to the GNU General Public - * License is included to the distribution terms of CANopenNode: - * - * Linking this library statically or dynamically with other modules is - * making a combined work based on this library. Thus, the terms and - * conditions of the GNU General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this library give - * you permission to link this library with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also meet, - * for each linked independent module, the terms and conditions of the - * license of that module. An independent module is a module which is - * not derived from or based on this library. If you modify this - * library, you may extend this exception to your version of the - * library, but you are not obliged to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -//============================================================================ -// INCLUDES -//============================================================================ -#include "CANopen.h" -#include "stm32f30x.h" - -//============================================================================ -// DEFINES -//============================================================================ -#define PARAM_STORE_PASSWORD 0x65766173 -#define PARAM_RESTORE_PASSWORD 0x64616F6C - -#define DEBUG 0 - -#define LAST_PAGE_ADDRESS 0x0800F800 -#define PAGES_PER_FLASH_AREA 1 -#define FLASH_PAGE_SIZE 0x800 -#define CO_OD_FLASH_PARAM_DEFAULT LAST_PAGE_ADDRESS - (1*PAGES_PER_FLASH_AREA*FLASH_PAGE_SIZE) -#define CO_OD_FLASH_PARAM_RUNTIME LAST_PAGE_ADDRESS - (2*PAGES_PER_FLASH_AREA*FLASH_PAGE_SIZE) - -#define CO_UNUSED(v) (void)(v) - -//============================================================================ -// LOCAL DATA -//============================================================================ -extern struct sCO_OD_ROM CO_OD_ROM; - -enum CO_OD_H1010_StoreParam_Sub -{ - OD_H1010_STORE_PARAM_COUNT, - OD_H1010_STORE_PARAM_ALL, - OD_H1010_STORE_PARAM_COMM, - OD_H1010_STORE_PARAM_APP, - OD_H1010_STORE_PARAM_MANUFACTURER, - OD_H1010_STORE_PARAM_RESERVED = 0x80 -}; - -enum CO_OD_H1011_RestoreDefaultParam_Sub -{ - OD_H1011_RESTORE_PARAM_COUNT, - OD_H1011_RESTORE_PARAM_ALL, - OD_H1011_RESTORE_PARAM_COMM, - OD_H1011_RESTORE_PARAM_APP, - OD_H1011_RESTORE_PARAM_MANUFACTURER, - OD_H1011_RESTORE_PARAM_RESERVED = 0x80 -}; - -enum CO_StorageFunctionality_Flags -{ - SAVES_PARAM_ON_COMMAND = 0x01, - SAVES_PARAM_AUTONOMOUSLY = 0x02 -}; - -enum CO_RestoreFunctionality_Flags -{ - RESTORES_PARAMETERS = 0x01 -}; - -//============================================================================ -/** -* Store parameters of object dictionary into flash memory. -* \param[in] FlashAddress Use CO_OD_Flash_Adress for the normal parameter -* block and CO_OD_Flash_Default_Param for the -* default parameters -*/ -static CO_SDO_abortCode_t storeParameters(uint32_t FlashAddress, uint8_t ParametersSub) -{ - uint32_t addressPtr = 0x00; - uint32_t addressFinal = 0x00; - uint32_t* chunk; - - CO_UNUSED(ParametersSub); - - /* Unlock flash*/ - FLASH_Unlock(); - - /* Check flash allocation */ - int32_t bytes_to_write = sizeof(CO_OD_ROM); - if ((uint32_t)bytes_to_write > (FLASH_PAGE_SIZE*PAGES_PER_FLASH_AREA)) { - return CO_SDO_AB_HW; - } - - addressFinal = FlashAddress + bytes_to_write; - /* Clear pending flakgs (if any) */ - FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPERR); - - /* Erase page */ - FLASH_ErasePage(FlashAddress); - - /* Program data into flash word by word */ - chunk = (uint32_t*) &CO_OD_ROM; - for (addressPtr = FlashAddress; addressPtr < addressFinal; addressPtr += 4) { - if (FLASH_ProgramWord(addressPtr, *chunk) != FLASH_COMPLETE) { - return CO_SDO_AB_HW; - } - chunk++; - } - FLASH_Lock(); - - return CO_SDO_AB_NONE; -} - -//============================================================================ -/** -* Restore parameters of object dictionary from flash memory. -* \param[in] FlashAddress Use CO_OD_Flash_Adress for the normal parameter -* block and CO_OD_Flash_Default_Param for the -* default parameters -*/ -void flash_read(uint32_t FlashAddress, void* RamAddress, size_t len) -{ - uint8_t* p_flash = (uint8_t *) FlashAddress; - uint8_t* p_ram = (uint8_t *) RamAddress; - uint32_t idx; - - for (idx = 0; idx < len; idx++) { - *p_ram = *(__IO uint8_t *)p_flash; - p_ram++; - p_flash++; - } -} - -static CO_SDO_abortCode_t restoreParameters(uint32_t FlashAddress, uint8_t ParametersSub) -{ - CO_UNUSED(ParametersSub); - - flash_read(FlashAddress, &CO_OD_ROM, sizeof(CO_OD_ROM)); - - return CO_SDO_AB_NONE; -} - -//============================================================================ -/** -* Access to object dictionary OD_H1010_STORE_PARAM_FUNC -*/ -static CO_SDO_abortCode_t CO_ODF_1010_StoreParam(CO_ODF_arg_t *ODF_arg) -{ - - uint32_t* value = (uint32_t*)ODF_arg->data; - - if (ODF_arg->reading) { - if(OD_H1010_STORE_PARAM_ALL == ODF_arg->subIndex) { - *value = SAVES_PARAM_ON_COMMAND; - } - return CO_SDO_AB_NONE; - } - - if(OD_H1010_STORE_PARAM_ALL != ODF_arg->subIndex) { - return CO_SDO_AB_NONE; - } - - if (*value != PARAM_STORE_PASSWORD) { - return CO_SDO_AB_DATA_TRANSF; - } - - return storeParameters(CO_OD_FLASH_PARAM_RUNTIME, ODF_arg->subIndex); -} - -//============================================================================ -/** -* Access to object dictionary OD_H1010_STORE_PARAM_FUNC -*/ -static CO_SDO_abortCode_t CO_ODF_1011_RestoreParam(CO_ODF_arg_t *ODF_arg) -{ - uint32_t* value = (uint32_t*)ODF_arg->data; - - if (ODF_arg->reading) { - if (OD_H1011_RESTORE_PARAM_ALL == ODF_arg->subIndex) { - *value = RESTORES_PARAMETERS; - } - return CO_SDO_AB_NONE; - } - - if (OD_H1011_RESTORE_PARAM_ALL != ODF_arg->subIndex) { - return CO_SDO_AB_NONE; - } - - if (*value != PARAM_RESTORE_PASSWORD) { - return CO_SDO_AB_DATA_TRANSF; - } - - CO_SDO_abortCode_t Result = restoreParameters(CO_OD_FLASH_PARAM_DEFAULT, ODF_arg->subIndex); - - if (Result != CO_SDO_AB_NONE) { - return Result; - } - - return storeParameters(CO_OD_FLASH_PARAM_RUNTIME, OD_H1011_RESTORE_PARAM_ALL); -} - -//=========================================================================== -/** -* Initialize flash library and data storage in flash -* We use two blocks in flash for data storage. One block is used for the -* default data that will be restored. The default parameters are stored -* at address CO_OD_Flash_Default_Param. The data that will be loaded at -* startup or saved if user modifies data is store at CO_OD_Flash_Adress. -*/ -void CO_FlashInit(void) -{ - /* Before we can access the data, we need to make sure, that the flash - block are properly initialized. We do this by reading the block into - a local sCO_OD_ROM variable and verifying the FirstWord and LastWord - members. */ - - struct sCO_OD_ROM DefaultObjDicParam; - flash_read(CO_OD_FLASH_PARAM_DEFAULT, &DefaultObjDicParam, sizeof(DefaultObjDicParam)); - - /* If the default parameters are not present in flash, then we know that - we need to create them for later restore. */ - - if ((DefaultObjDicParam.FirstWord != CO_OD_FIRST_LAST_WORD) || - (DefaultObjDicParam.LastWord != CO_OD_FIRST_LAST_WORD)) { - storeParameters(CO_OD_FLASH_PARAM_RUNTIME, OD_H1010_STORE_PARAM_ALL); - storeParameters(CO_OD_FLASH_PARAM_DEFAULT, OD_H1010_STORE_PARAM_ALL); - } - else { - restoreParameters(CO_OD_FLASH_PARAM_RUNTIME, OD_H1010_STORE_PARAM_ALL); - } -} - -//=========================================================================== -void CO_FlashRegisterODFunctions(CO_t* CO) -{ - CO_OD_configure(*(CO->SDO), OD_H1010_STORE_PARAM_FUNC, - CO_ODF_1010_StoreParam, (void*)0, 0, 0); - - CO_OD_configure(*(CO->SDO), OD_H1011_REST_PARAM_FUNC, - CO_ODF_1011_RestoreParam, (void*)0, 0, 0); -} diff --git a/stack/STM32F3/CO_Flash.h b/stack/STM32F3/CO_Flash.h deleted file mode 100644 index 2e3c7cf9..00000000 --- a/stack/STM32F3/CO_Flash.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * STM32F3 flash support for CANopen stack - * - * @file CO_Flash.h - * @author Janez Paternoster - * @author Olof Larsson - * @author Petteri Mustonen - * @copyright 2014 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * CANopenNode is free and open source software: you can redistribute - * it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Following clarification and special exception to the GNU General Public - * License is included to the distribution terms of CANopenNode: - * - * Linking this library statically or dynamically with other modules is - * making a combined work based on this library. Thus, the terms and - * conditions of the GNU General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this library give - * you permission to link this library with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also meet, - * for each linked independent module, the terms and conditions of the - * license of that module. An independent module is a module which is - * not derived from or based on this library. If you modify this - * library, you may extend this exception to your version of the - * library, but you are not obliged to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -#ifndef CO_FLASH_H -#define CO_FLASH_H - -//============================================================================ -// INCLUDES -//============================================================================ -#include "CANopen.h" - -/** - * Initialize flash library and data storage in flash - * We use two blocks in flash for data storage. One block is used for the - * default data that will be restored. The default parameters are stored - * at address CO_OD_Flash_Default_Param. The data that will be loaded at - * startup or saved if user modifies data. - */ -void CO_FlashInit(void); - -/** - * Register object dictionary functions for parameter storage and restoring - * parameters (Object dictionary index 0x1010 Store Param and 0x1011 Restore - * default param. - */ -void CO_FlashRegisterODFunctions(CO_t* CO); - -#endif diff --git a/stack/STM32F3/CO_driver.c b/stack/STM32F3/CO_driver.c deleted file mode 100644 index 713c9ff9..00000000 --- a/stack/STM32F3/CO_driver.c +++ /dev/null @@ -1,511 +0,0 @@ -/* - * CAN module object for ST STM32F334 microcontroller. - * - * @file CO_driver.c - * @author Janez Paternoster - * @author Ondrej Netik - * @author Vijayendra - * @author Jan van Lienden - * @author Petteri Mustonen - * @copyright 2013 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * CANopenNode is free and open source software: you can redistribute - * it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Following clarification and special exception to the GNU General Public - * License is included to the distribution terms of CANopenNode: - * - * Linking this library statically or dynamically with other modules is - * making a combined work based on this library. Thus, the terms and - * conditions of the GNU General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this library give - * you permission to link this library with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also meet, - * for each linked independent module, the terms and conditions of the - * license of that module. An independent module is a module which is - * not derived from or based on this library. If you modify this - * library, you may extend this exception to your version of the - * library, but you are not obliged to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -/* Includes ------------------------------------------------------------------*/ -#include "stm32f30x.h" -#include "CO_driver.h" -#include "CO_Emergency.h" -#include - -/* Private macro -------------------------------------------------------------*/ -/* Private define ------------------------------------------------------------*/ -/* Private variable ----------------------------------------------------------*/ -/* Private function ----------------------------------------------------------*/ -static void CO_CANClkSetting (void); -static void CO_CANconfigGPIO (void); -static uint8_t CO_CANsendToModule(CO_CANmodule_t *CANmodule, CO_CANtx_t *buffer); - -/******************************************************************************* - Macro and Constants - CAN module registers - *******************************************************************************/ - - -/******************************************************************************/ -void CO_CANsetConfigurationMode(void *CANdriverState){ - (void) CANdriverState; -} - - -/******************************************************************************/ -void CO_CANsetNormalMode(CO_CANmodule_t *CANmodule){ - CANmodule->CANnormal = true; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_CANmodule_init( - CO_CANmodule_t *CANmodule, - void *CANdriverState, - CO_CANrx_t rxArray[], - uint16_t rxSize, - CO_CANtx_t txArray[], - uint16_t txSize, - uint16_t CANbitRate) -{ - CAN_InitTypeDef CAN_InitStruct; - CAN_FilterInitTypeDef CAN_FilterInitStruct; - NVIC_InitTypeDef NVIC_InitStructure; - int i; - uint8_t result; - - /* verify arguments */ - if(CANmodule==NULL || rxArray==NULL || txArray==NULL) { - return CO_ERROR_ILLEGAL_ARGUMENT; - } - - CANmodule->CANdriverState = CANdriverState; - CANmodule->rxArray = rxArray; - CANmodule->rxSize = rxSize; - CANmodule->txArray = txArray; - CANmodule->txSize = txSize; - CANmodule->CANnormal = false; - CANmodule->useCANrxFilters = false; - CANmodule->bufferInhibitFlag = 0; - CANmodule->firstCANtxMessage = 1; - CANmodule->CANtxCount = 0; - CANmodule->errOld = 0; - CANmodule->em = 0; - - CAN_ITConfig(CANmodule->CANdriverState, (CAN_IT_TME | CAN_IT_FMP0), DISABLE); - - for (i = 0; i < rxSize; i++) { - CANmodule->rxArray[i].ident = 0; - CANmodule->rxArray[i].pFunct = 0; - } - - for (i = 0; i < txSize; i++) { - CANmodule->txArray[i].bufferFull = 0; - } - - /* Setting Clock of CAN HW */ - CO_CANClkSetting(); - - /* GPIO Config for CAN */ - CO_CANconfigGPIO(); - - /* Init CAN controler */ - CAN_DeInit(CANmodule->CANdriverState); - CAN_StructInit(&CAN_InitStruct); - - switch (CANbitRate) { - case 1000: CAN_InitStruct.CAN_Prescaler = 2; - break; - case 500: CAN_InitStruct.CAN_Prescaler = 4; - break; - default: - case 250: CAN_InitStruct.CAN_Prescaler = 8; - break; - case 125: CAN_InitStruct.CAN_Prescaler = 16; - break; - case 100: CAN_InitStruct.CAN_Prescaler = 20; - break; - case 50: CAN_InitStruct.CAN_Prescaler = 40; - break; - case 20: CAN_InitStruct.CAN_Prescaler = 100; - break; - case 10: CAN_InitStruct.CAN_Prescaler = 200; - break; - } - - CAN_InitStruct.CAN_SJW = CAN_SJW_1tq; // changed by VJ, old value = CAN_SJW_1tq; - CAN_InitStruct.CAN_BS1 = CAN_BS1_13tq; // changed by VJ, old value = CAN_BS1_3tq; - CAN_InitStruct.CAN_BS2 = CAN_BS2_2tq; // changed by VJ, old value = CAN_BS2_2tq; - CAN_InitStruct.CAN_NART = ENABLE; // No Automatic retransmision - - result = CAN_Init(CANmodule->CANdriverState, &CAN_InitStruct); - if (result == 0) { - return CO_ERROR_TIMEOUT; /* CO- Return Init failed */ - } - - memset(&CAN_FilterInitStruct, 0, sizeof (CAN_FilterInitStruct)); - CAN_FilterInitStruct.CAN_FilterNumber = 0; - CAN_FilterInitStruct.CAN_FilterIdHigh = 0; - CAN_FilterInitStruct.CAN_FilterIdLow = 0; - CAN_FilterInitStruct.CAN_FilterMaskIdHigh = 0; - CAN_FilterInitStruct.CAN_FilterMaskIdLow = 0; - CAN_FilterInitStruct.CAN_FilterFIFOAssignment = 0; // pouzivame jen FIFO0 - CAN_FilterInitStruct.CAN_FilterMode = CAN_FilterMode_IdMask; - CAN_FilterInitStruct.CAN_FilterScale = CAN_FilterScale_32bit; - CAN_FilterInitStruct.CAN_FilterActivation = ENABLE; - CAN_FilterInit(&CAN_FilterInitStruct); - - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; - /* interrupts from receiver */ - NVIC_InitStructure.NVIC_IRQChannel = CAN1_RX0_INTERRUPTS; - NVIC_Init(&NVIC_InitStructure); - /* interrupts from transmitter */ - NVIC_InitStructure.NVIC_IRQChannel = CAN1_TX_INTERRUPTS; - NVIC_Init(&NVIC_InitStructure); - - /* Can_init function of ST Driver puts the controller into the normal mode */ - - CAN_ITConfig(CANmodule->CANdriverState, (CAN_IT_TME | CAN_IT_FMP0), ENABLE); - - return CO_ERROR_NO; -} - -/******************************************************************************/ -void CO_CANmodule_disable(CO_CANmodule_t *CANmodule) -{ - CAN_DeInit(CANmodule->CANdriverState); -} - -/******************************************************************************/ -CO_ReturnError_t CO_CANrxBufferInit( - CO_CANmodule_t *CANmodule, - uint16_t index, - uint16_t ident, - uint16_t mask, - int8_t rtr, - void *object, - void (*pFunct)(void *object, const CO_CANrxMsg_t *message)) -{ - CO_CANrx_t *rxBuffer; - uint16_t RXF, RXM; - - //safety - if (!CANmodule || !object || !pFunct || index >= CANmodule->rxSize) { - return CO_ERROR_ILLEGAL_ARGUMENT; - } - - /* buffer, which will be configured */ - rxBuffer = CANmodule->rxArray + index; - - /* Configure object variables */ - rxBuffer->object = object; - rxBuffer->pFunct = pFunct; - - /* CAN identifier and CAN mask, bit aligned with CAN module registers */ - RXF = (ident & 0x07FF) << 2; - if (rtr) RXF |= 0x02; - RXM = (mask & 0x07FF) << 2; - RXM |= 0x02; - - /* configure filter and mask */ - if (RXF != rxBuffer->ident || RXM != rxBuffer->mask) { - rxBuffer->ident = RXF; - rxBuffer->mask = RXM; - } - - return CO_ERROR_NO; -} - -/******************************************************************************/ -CO_CANtx_t *CO_CANtxBufferInit( - CO_CANmodule_t *CANmodule, - uint16_t index, - uint16_t ident, - int8_t rtr, - uint8_t noOfBytes, - int8_t syncFlag) -{ - uint32_t TXF; - CO_CANtx_t *buffer; - - /* safety */ - if (!CANmodule || CANmodule->txSize <= index) return 0; - - /* get specific buffer */ - buffer = &CANmodule->txArray[index]; - - /* CAN identifier, bit aligned with CAN module registers */ - TXF = ident << 21; - TXF &= 0xFFE00000; - if (rtr) TXF |= 0x02; - - /* write to buffer */ - buffer->ident = TXF; - buffer->DLC = noOfBytes; - buffer->bufferFull = 0; - buffer->syncFlag = syncFlag ? 1 : 0; - - return buffer; -} - -/******************************************************************************/ -CO_ReturnError_t CO_CANsend(CO_CANmodule_t *CANmodule, CO_CANtx_t *buffer) -{ - CO_ReturnError_t err = CO_ERROR_NO; - uint8_t txBuff; - - /* Verify overflow */ - if (buffer->bufferFull) { - if(!CANmodule->firstCANtxMessage) /* don't set error, if bootup message is still on buffers */ - CO_errorReport((CO_EM_t*)CANmodule->em, CO_EM_CAN_TX_OVERFLOW, CO_EMC_CAN_OVERRUN, 0); - err = CO_ERROR_TX_OVERFLOW; - } - - CO_LOCK_CAN_SEND(); - - /* First try to transmit the message immediately if mailbox is free. - * Only one TX mailbox is used of the three available in the hardware */ - CANmodule->bufferInhibitFlag = buffer->syncFlag; - txBuff = CO_CANsendToModule(CANmodule, buffer); - - /* No free mailbox -> use interrupt for transmission */ - if (txBuff == CAN_TxStatus_NoMailBox) { - buffer->bufferFull = 1; - CANmodule->CANtxCount++; - } - CO_UNLOCK_CAN_SEND(); - - return err; -} - -/******************************************************************************/ -void CO_CANclearPendingSyncPDOs(CO_CANmodule_t *CANmodule) -{ - uint32_t tpdoDeleted = 0U; - uint8_t state = 0; - - CO_LOCK_CAN_SEND(); - /* Abort message from CAN module, if there is synchronous TPDO. */ - state = CAN_TransmitStatus(CANmodule->CANdriverState, CO_CAN_TXMAILBOX); - if((state == CAN_TxStatus_Pending) && (CANmodule->bufferInhibitFlag)) { - CAN_CancelTransmit(CANmodule->CANdriverState, CO_CAN_TXMAILBOX); - CANmodule->bufferInhibitFlag = false; - tpdoDeleted = 1U; - } - - /* delete also pending synchronous TPDOs in TX buffers */ - if(CANmodule->CANtxCount != 0U){ - uint16_t i; - CO_CANtx_t *buffer = &CANmodule->txArray[0]; - for(i = CANmodule->txSize; i > 0U; i--){ - if(buffer->bufferFull){ - if(buffer->syncFlag){ - buffer->bufferFull = false; - CANmodule->CANtxCount--; - tpdoDeleted = 2U; - } - } - buffer++; - } - } - CO_UNLOCK_CAN_SEND(); - - - if(tpdoDeleted != 0U){ - CO_errorReport((CO_EM_t*)CANmodule->em, CO_EM_TPDO_OUTSIDE_WINDOW, CO_EMC_COMMUNICATION, tpdoDeleted); - } -} - -/******************************************************************************/ -void CO_CANverifyErrors(CO_CANmodule_t *CANmodule) -{ - uint32_t err; - CO_EM_t* em = (CO_EM_t*)CANmodule->em; - - err = CANmodule->CANdriverState->ESR; - - if(CANmodule->errOld != err) { - CANmodule->errOld = err; - - /* CAN RX bus overflow */ - if(CANmodule->CANdriverState->RF0R & 0x10) { - CO_errorReport(em, CO_EM_CAN_RXB_OVERFLOW, CO_EMC_CAN_OVERRUN, err); - CANmodule->CANdriverState->RF0R &=~0x10;//clear bits - } - - /* CAN TX bus off */ - if(err & 0x04) { - CO_errorReport(em, CO_EM_CAN_TX_BUS_OFF, CO_EMC_BUS_OFF_RECOVERED, err); - } - else { - CO_errorReset(em, CO_EM_CAN_TX_BUS_OFF, err); - } - - /* CAN TX or RX bus passive */ - if(err & 0x02) { - if(!CANmodule->firstCANtxMessage) CO_errorReport(em, CO_EM_CAN_TX_BUS_PASSIVE, CO_EMC_CAN_PASSIVE, err); - } - else { - // int16_t wasCleared; - /* wasCleared = */CO_errorReset(em, CO_EM_CAN_TX_BUS_PASSIVE, err); - /* if(wasCleared == 1) */CO_errorReset(em, CO_EM_CAN_TX_OVERFLOW, err); - } - - - /* CAN TX or RX bus warning */ - if(err & 0x01) { - CO_errorReport(em, CO_EM_CAN_BUS_WARNING, CO_EMC_NO_ERROR, err); - } - else { - CO_errorReset(em, CO_EM_CAN_BUS_WARNING, err); - } - } -} - -/******************************************************************************/ -/* Interrupt from receiver */ -void CO_CANinterrupt_Rx(CO_CANmodule_t *CANmodule) -{ - CanRxMsg CAN1_RxMsg; - uint16_t index; - uint8_t msgMatched = 0; - CO_CANrx_t *msgBuff = CANmodule->rxArray; - - CAN_Receive(CANmodule->CANdriverState, CAN_FilterFIFO0, &CAN1_RxMsg); - - for (index = 0; index < CANmodule->rxSize; index++) { - uint16_t msg = (CAN1_RxMsg.StdId << 2) | (CAN1_RxMsg.RTR ? 2 : 0); - - if (((msg ^ msgBuff->ident) & msgBuff->mask) == 0) { - msgMatched = 1; - break; - } - msgBuff++; - } - - /* Call specific function, which will process the message */ - if (msgMatched && msgBuff->pFunct) { - msgBuff->pFunct(msgBuff->object, (CO_CANrxMsg_t*) &CAN1_RxMsg); - } -} - -/******************************************************************************/ -/* Interrupt from trasmitter */ -void CO_CANinterrupt_Tx(CO_CANmodule_t *CANmodule) -{ - /* First CAN message (bootup) was sent successfully */ - CANmodule->firstCANtxMessage = 0; - - /* clear flag from previous message */ - CANmodule->bufferInhibitFlag = 0; - - /* Are there any new messages waiting to be send */ - if (CANmodule->CANtxCount > 0) { - uint16_t i; /* index of transmitting message */ - - /* first buffer */ - CO_CANtx_t *buffer = CANmodule->txArray; - /* search through whole array of pointers to transmit message buffers. */ - for(i = CANmodule->txSize; i > 0; i--) { - /* if message buffer is full, send it. */ - if(buffer->bufferFull) { - buffer->bufferFull = 0; - CANmodule->CANtxCount--; - - /* Copy message to CAN buffer */ - CANmodule->bufferInhibitFlag = buffer->syncFlag; - CO_CANsendToModule(CANmodule, buffer); - break; /* exit for loop */ - } - buffer++; - }/* end of for loop */ - - /* Clear counter if no more messages */ - if(i == 0) CANmodule->CANtxCount = 0; - } -} - -/******************************************************************************/ -static uint8_t CO_CANsendToModule(CO_CANmodule_t *CANmodule, CO_CANtx_t *buffer) -{ - CAN_TxMailBox_TypeDef* txMbox; - - /* Checks if the transmit mailbox is available */ - if ((CANmodule->CANdriverState->TSR & CAN_TSR_TME0) == CAN_TSR_TME0) { - txMbox = &CANmodule->CANdriverState->sTxMailBox[CO_CAN_TXMAILBOX]; - } - else { - return CAN_TxStatus_NoMailBox; - } - - /* ID: always assuming standard 11-bit ID */ - txMbox->TIR &= 1; - txMbox->TIR |= ((buffer->ident) | CAN_RTR_DATA); - - /* DLC */ - buffer->DLC &= (uint8_t)0x0000000F; - txMbox->TDTR &= (uint32_t)0xFFFFFFF0; - txMbox->TDTR |= buffer->DLC; - - /* Data field */ - txMbox->TDLR = (((uint32_t)buffer->data[3] << 24) | - ((uint32_t)buffer->data[2] << 16) | - ((uint32_t)buffer->data[1] << 8) | - ((uint32_t)buffer->data[0])); - - txMbox->TDHR = (((uint32_t)buffer->data[7] << 24) | - ((uint32_t)buffer->data[6] << 16) | - ((uint32_t)buffer->data[5] << 8) | - ((uint32_t)buffer->data[4])); - - /* Request transmission */ - txMbox->TIR |= 1; - - return 0; -} - -/******************************************************************************/ -static void CO_CANClkSetting (void) -{ - RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE); - RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE); -} - -/******************************************************************************/ -static void CO_CANconfigGPIO (void) -{ - GPIO_InitTypeDef GPIO_InitStruct; - - GPIO_InitStruct.GPIO_Pin = GPIO_Pin_CAN_RX | GPIO_Pin_CAN_TX; - GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; - GPIO_InitStruct.GPIO_Speed = GPIO_Speed_Level_1; - GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; - GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; - - GPIO_Init(GPIO_CAN, &GPIO_InitStruct); - - /* Map to correct alternative function (9 == CAN) */ - GPIO_PinAFConfig(GPIO_CAN, GPIO_PinSource_CAN_RX, GPIO_AF_9); - GPIO_PinAFConfig(GPIO_CAN, GPIO_PinSource_CAN_TX, GPIO_AF_9); -} diff --git a/stack/STM32F3/CO_driver_target.h b/stack/STM32F3/CO_driver_target.h deleted file mode 100644 index c1680410..00000000 --- a/stack/STM32F3/CO_driver_target.h +++ /dev/null @@ -1,179 +0,0 @@ -/* - * CAN module object for ST STM32F334 microcontroller. - * - * @file CO_driver_target.h - * @author Janez Paternoster - * @author Ondrej Netik - * @author Vijayendra - * @author Jan van Lienden - * @author Petteri Mustonen - * @copyright 2013 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * CANopenNode is free and open source software: you can redistribute - * it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Following clarification and special exception to the GNU General Public - * License is included to the distribution terms of CANopenNode: - * - * Linking this library statically or dynamically with other modules is - * making a combined work based on this library. Thus, the terms and - * conditions of the GNU General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this library give - * you permission to link this library with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also meet, - * for each linked independent module, the terms and conditions of the - * license of that module. An independent module is a module which is - * not derived from or based on this library. If you modify this - * library, you may extend this exception to your version of the - * library, but you are not obliged to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - - -#ifndef CO_DRIVER_H -#define CO_DRIVER_H - - -/* For documentation see file drvTemplate/CO_driver.h */ - - -/* Includes ------------------------------------------------------------------*/ -#include -#include /* for 'NULL' */ -#include /* for 'int8_t' to 'uint64_t' */ -#include "stm32f30x.h" - -#define bool_t bool -#define CO_LITTLE_ENDIAN - -/* Exported define -----------------------------------------------------------*/ -#define PACKED_STRUCT __attribute__((packed)) -#define ALIGN_STRUCT_DWORD __attribute__((aligned(4))) - -/* Peripheral addresses */ -#define ADDR_CAN1 CAN1 - -/* Critical sections */ -#define CO_LOCK_CAN_SEND() __set_PRIMASK(1); -#define CO_UNLOCK_CAN_SEND() __set_PRIMASK(0); - -#define CO_LOCK_EMCY() __set_PRIMASK(1); -#define CO_UNLOCK_EMCY() __set_PRIMASK(0); - -#define CO_LOCK_OD() __set_PRIMASK(1); -#define CO_UNLOCK_OD() __set_PRIMASK(0); - -#define CLOCK_CAN RCC_APB1Periph_CAN1 - -#define CAN_REMAP_1 /* Select CAN1 remap 1 */ -#ifdef CAN1_NO_REMAP /* CAN1 not remapped */ -#define CLOCK_GPIO_CAN RCC_APB2Periph_GPIOA -#define GPIO_Remapping_CAN (0) -#define GPIO_CAN GPIOA -#define GPIO_Pin_CAN_RX GPIO_Pin_11 -#define GPIO_Pin_CAN_TX GPIO_Pin_12 -#define GPIO_PinSource_CAN_RX GPIO_PinSource11 -#define GPIO_PinSource_CAN_TX GPIO_PinSource12 -#define GPIO_CAN_Remap_State DISABLE -#endif -#ifdef CAN_REMAP_1 /* CAN1 remap 1 */ -#define CLOCK_GPIO_CAN RCC_AHBPeriph_GPIOB -#define GPIO_Remapping_CAN GPIO_Remap1_CAN1 -#define GPIO_CAN GPIOB -#define GPIO_Pin_CAN_RX GPIO_Pin_8 -#define GPIO_Pin_CAN_TX GPIO_Pin_9 -#define GPIO_PinSource_CAN_RX GPIO_PinSource8 -#define GPIO_PinSource_CAN_TX GPIO_PinSource9 -#define GPIO_CAN_Remap_State ENABLE -#endif - -#define CAN1_TX_INTERRUPTS CAN1_TX_IRQn -#define CAN1_RX0_INTERRUPTS CAN1_RX0_IRQn - -#define CO_CAN_TXMAILBOX ((uint8_t)0x00) - -/* Timeout for initialization */ - -#define INAK_TIMEOUT ((uint32_t)0x0000FFFF) -/* Data types */ -typedef float float32_t; -typedef long double float64_t; -typedef char char_t; -typedef unsigned char oChar_t; -typedef unsigned char domain_t; - - -/* CAN receive message structure as aligned in CAN module. - * prevzato z stm32f10_can.h - velikostne polozky a poradi odpovidaji. */ -typedef struct{ - uint32_t ident; /* Standard Identifier */ - uint32_t ExtId; /* Specifies the extended identifier */ - uint8_t IDE; /* Specifies the type of identifier for the - message that will be received */ - uint8_t RTR; /* Remote Transmission Request bit */ - uint8_t DLC; /* Data length code (bits 0...3) */ - uint8_t data[8]; /* 8 data bytes */ - uint8_t FMI; /* Specifies the index of the filter the message - stored in the mailbox passes through */ -}CO_CANrxMsg_t; - - -/* Received message object */ -typedef struct{ - uint16_t ident; - uint16_t mask; - void *object; - void (*pFunct)(void *object, const CO_CANrxMsg_t *message); // Changed by VJ -}CO_CANrx_t; - - -/* Transmit message object. */ -typedef struct{ - uint32_t ident; - uint8_t DLC; - uint8_t data[8]; - volatile uint8_t bufferFull; - volatile uint8_t syncFlag; -}CO_CANtx_t;/* ALIGN_STRUCT_DWORD; */ - - -/* CAN module object. */ -typedef struct{ - CAN_TypeDef *CANdriverState; /* STM32F4xx specific */ - CO_CANrx_t *rxArray; - uint16_t rxSize; - CO_CANtx_t *txArray; - uint16_t txSize; - volatile bool CANnormal; - volatile bool useCANrxFilters; - //volatile uint8_t useCANrxFilters; - volatile uint8_t bufferInhibitFlag; - volatile uint8_t firstCANtxMessage; - volatile uint16_t CANtxCount; - uint32_t errOld; - void *em; -}CO_CANmodule_t; - - -/* CAN interrupts receives and transmits CAN messages. */ -void CO_CANinterrupt_Rx(CO_CANmodule_t *CANmodule); -void CO_CANinterrupt_Tx(CO_CANmodule_t *CANmodule); - -#endif /* CO_DRIVER_TARGET_H */ diff --git a/stack/crc16-ccitt.c b/stack/crc16-ccitt.c deleted file mode 100644 index bff5d069..00000000 --- a/stack/crc16-ccitt.c +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Calculation of CRC 16 CCITT polynomial, x^16 + x^12 + x^5 + 1. - * - * @file crc16-ccitt.c - * @ingroup crc16-ccitt - * @author Janez Paternoster - * @copyright 2012 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef CO_USE_OWN_CRC16 - -#include "crc16-ccitt.h" - - -/* - * CRC table calculated by the following algorithm: - * - * void crc16_ccitt_table_init(void){ - * unsigned short i, j; - * for(i=0; i<256; i++){ - * unsigned short crc = 0; - * unsigned short c = i << 8; - * for(j=0; j<8; j++){ - * if((crc ^ c) & 0x8000) crc = (crc << 1) ^ 0x1021; - * else crc = crc << 1; - * c = c << 1; - * } - * crc16_ccitt_table[i] = crc; - * } - * } - */ -static const unsigned short crc16_ccitt_table[256] = { - 0x0000U, 0x1021U, 0x2042U, 0x3063U, 0x4084U, 0x50A5U, 0x60C6U, 0x70E7U, - 0x8108U, 0x9129U, 0xA14AU, 0xB16BU, 0xC18CU, 0xD1ADU, 0xE1CEU, 0xF1EFU, - 0x1231U, 0x0210U, 0x3273U, 0x2252U, 0x52B5U, 0x4294U, 0x72F7U, 0x62D6U, - 0x9339U, 0x8318U, 0xB37BU, 0xA35AU, 0xD3BDU, 0xC39CU, 0xF3FFU, 0xE3DEU, - 0x2462U, 0x3443U, 0x0420U, 0x1401U, 0x64E6U, 0x74C7U, 0x44A4U, 0x5485U, - 0xA56AU, 0xB54BU, 0x8528U, 0x9509U, 0xE5EEU, 0xF5CFU, 0xC5ACU, 0xD58DU, - 0x3653U, 0x2672U, 0x1611U, 0x0630U, 0x76D7U, 0x66F6U, 0x5695U, 0x46B4U, - 0xB75BU, 0xA77AU, 0x9719U, 0x8738U, 0xF7DFU, 0xE7FEU, 0xD79DU, 0xC7BCU, - 0x48C4U, 0x58E5U, 0x6886U, 0x78A7U, 0x0840U, 0x1861U, 0x2802U, 0x3823U, - 0xC9CCU, 0xD9EDU, 0xE98EU, 0xF9AFU, 0x8948U, 0x9969U, 0xA90AU, 0xB92BU, - 0x5AF5U, 0x4AD4U, 0x7AB7U, 0x6A96U, 0x1A71U, 0x0A50U, 0x3A33U, 0x2A12U, - 0xDBFDU, 0xCBDCU, 0xFBBFU, 0xEB9EU, 0x9B79U, 0x8B58U, 0xBB3BU, 0xAB1AU, - 0x6CA6U, 0x7C87U, 0x4CE4U, 0x5CC5U, 0x2C22U, 0x3C03U, 0x0C60U, 0x1C41U, - 0xEDAEU, 0xFD8FU, 0xCDECU, 0xDDCDU, 0xAD2AU, 0xBD0BU, 0x8D68U, 0x9D49U, - 0x7E97U, 0x6EB6U, 0x5ED5U, 0x4EF4U, 0x3E13U, 0x2E32U, 0x1E51U, 0x0E70U, - 0xFF9FU, 0xEFBEU, 0xDFDDU, 0xCFFCU, 0xBF1BU, 0xAF3AU, 0x9F59U, 0x8F78U, - 0x9188U, 0x81A9U, 0xB1CAU, 0xA1EBU, 0xD10CU, 0xC12DU, 0xF14EU, 0xE16FU, - 0x1080U, 0x00A1U, 0x30C2U, 0x20E3U, 0x5004U, 0x4025U, 0x7046U, 0x6067U, - 0x83B9U, 0x9398U, 0xA3FBU, 0xB3DAU, 0xC33DU, 0xD31CU, 0xE37FU, 0xF35EU, - 0x02B1U, 0x1290U, 0x22F3U, 0x32D2U, 0x4235U, 0x5214U, 0x6277U, 0x7256U, - 0xB5EAU, 0xA5CBU, 0x95A8U, 0x8589U, 0xF56EU, 0xE54FU, 0xD52CU, 0xC50DU, - 0x34E2U, 0x24C3U, 0x14A0U, 0x0481U, 0x7466U, 0x6447U, 0x5424U, 0x4405U, - 0xA7DBU, 0xB7FAU, 0x8799U, 0x97B8U, 0xE75FU, 0xF77EU, 0xC71DU, 0xD73CU, - 0x26D3U, 0x36F2U, 0x0691U, 0x16B0U, 0x6657U, 0x7676U, 0x4615U, 0x5634U, - 0xD94CU, 0xC96DU, 0xF90EU, 0xE92FU, 0x99C8U, 0x89E9U, 0xB98AU, 0xA9ABU, - 0x5844U, 0x4865U, 0x7806U, 0x6827U, 0x18C0U, 0x08E1U, 0x3882U, 0x28A3U, - 0xCB7DU, 0xDB5CU, 0xEB3FU, 0xFB1EU, 0x8BF9U, 0x9BD8U, 0xABBBU, 0xBB9AU, - 0x4A75U, 0x5A54U, 0x6A37U, 0x7A16U, 0x0AF1U, 0x1AD0U, 0x2AB3U, 0x3A92U, - 0xFD2EU, 0xED0FU, 0xDD6CU, 0xCD4DU, 0xBDAAU, 0xAD8BU, 0x9DE8U, 0x8DC9U, - 0x7C26U, 0x6C07U, 0x5C64U, 0x4C45U, 0x3CA2U, 0x2C83U, 0x1CE0U, 0x0CC1U, - 0xEF1FU, 0xFF3EU, 0xCF5DU, 0xDF7CU, 0xAF9BU, 0xBFBAU, 0x8FD9U, 0x9FF8U, - 0x6E17U, 0x7E36U, 0x4E55U, 0x5E74U, 0x2E93U, 0x3EB2U, 0x0ED1U, 0x1EF0U -}; - - -/******************************************************************************/ -unsigned short crc16_ccitt( - const unsigned char block[], - unsigned int blockLength, - unsigned short crc) -{ - unsigned int i; - - for(i=0U; i> 8) ^ (unsigned short) block[i]; - crc = ((unsigned short)(crc << 8U)) ^ crc16_ccitt_table[tmp]; - } - return crc; -} - -#endif /* CO_USE_OWN_CRC16 */ diff --git a/stack/crc16-ccitt.h b/stack/crc16-ccitt.h deleted file mode 100644 index 9d3344f5..00000000 --- a/stack/crc16-ccitt.h +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Calculation of CRC 16 CCITT polynomial. - * - * @file crc16-ccitt.h - * @ingroup CO_crc16_ccitt - * @author Lammert Bies - * @author Janez Paternoster - * @copyright 2012 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#ifndef CRC16_CCITT_H -#define CRC16_CCITT_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @defgroup CO_crc16_ccitt CRC 16 CCITT - * @ingroup CO_CANopen - * @{ - * - * Calculation of CRC 16 CCITT polynomial. - * - * Equation: - * - * `x^16 + x^12 + x^5 + 1` - */ - - -/** - * Calculate CRC sum on block of data. - * - * @param block Pointer to block of data. - * @param blockLength Length of data in bytes; - * @param crc Initial value (zero for xmodem). If block is split into - * multiple segments, previous CRC is used as initial. - * - * @return Calculated CRC. - */ -#ifdef CO_USE_OWN_CRC16 -extern -#endif -unsigned short crc16_ccitt( - const unsigned char block[], - unsigned int blockLength, - unsigned short crc); - -#ifdef __cplusplus -} -#endif /*__cplusplus*/ - -/** @} */ -#endif diff --git a/stack/drvTemplate/CO_driver.c b/stack/drvTemplate/CO_driver.c deleted file mode 100644 index bd03f992..00000000 --- a/stack/drvTemplate/CO_driver.c +++ /dev/null @@ -1,407 +0,0 @@ -/* - * CAN module object for generic microcontroller. - * - * This file is a template for other microcontrollers. - * - * @file CO_driver.c - * @ingroup CO_driver - * @author Janez Paternoster - * @copyright 2004 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include "CO_driver.h" -#include "CO_Emergency.h" - - -/******************************************************************************/ -void CO_CANsetConfigurationMode(void *CANdriverState){ - /* Put CAN module in configuration mode */ -} - - -/******************************************************************************/ -void CO_CANsetNormalMode(CO_CANmodule_t *CANmodule){ - /* Put CAN module in normal mode */ - - CANmodule->CANnormal = true; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_CANmodule_init( - CO_CANmodule_t *CANmodule, - void *CANdriverState, - CO_CANrx_t rxArray[], - uint16_t rxSize, - CO_CANtx_t txArray[], - uint16_t txSize, - uint16_t CANbitRate) -{ - uint16_t i; - - /* verify arguments */ - if(CANmodule==NULL || rxArray==NULL || txArray==NULL){ - return CO_ERROR_ILLEGAL_ARGUMENT; - } - - /* Configure object variables */ - CANmodule->CANdriverState = CANdriverState; - CANmodule->rxArray = rxArray; - CANmodule->rxSize = rxSize; - CANmodule->txArray = txArray; - CANmodule->txSize = txSize; - CANmodule->CANnormal = false; - CANmodule->useCANrxFilters = (rxSize <= 32U) ? true : false;/* microcontroller dependent */ - CANmodule->bufferInhibitFlag = false; - CANmodule->firstCANtxMessage = true; - CANmodule->CANtxCount = 0U; - CANmodule->errOld = 0U; - CANmodule->em = NULL; - - for(i=0U; iuseCANrxFilters){ - /* CAN module filters are used, they will be configured with */ - /* CO_CANrxBufferInit() functions, called by separate CANopen */ - /* init functions. */ - /* Configure all masks so, that received message must match filter */ - } - else{ - /* CAN module filters are not used, all messages with standard 11-bit */ - /* identifier will be received */ - /* Configure mask 0 so, that all messages with standard identifier are accepted */ - } - - - /* configure CAN interrupt registers */ - - - return CO_ERROR_NO; -} - - -/******************************************************************************/ -void CO_CANmodule_disable(CO_CANmodule_t *CANmodule){ - /* turn off the module */ -} - - -/******************************************************************************/ -uint16_t CO_CANrxMsg_readIdent(const CO_CANrxMsg_t *rxMsg){ - return (uint16_t) rxMsg->ident; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_CANrxBufferInit( - CO_CANmodule_t *CANmodule, - uint16_t index, - uint16_t ident, - uint16_t mask, - bool_t rtr, - void *object, - void (*pFunct)(void *object, const CO_CANrxMsg_t *message)) -{ - CO_ReturnError_t ret = CO_ERROR_NO; - - if((CANmodule!=NULL) && (object!=NULL) && (pFunct!=NULL) && (index < CANmodule->rxSize)){ - /* buffer, which will be configured */ - CO_CANrx_t *buffer = &CANmodule->rxArray[index]; - - /* Configure object variables */ - buffer->object = object; - buffer->pFunct = pFunct; - - /* CAN identifier and CAN mask, bit aligned with CAN module. Different on different microcontrollers. */ - buffer->ident = ident & 0x07FFU; - if(rtr){ - buffer->ident |= 0x0800U; - } - buffer->mask = (mask & 0x07FFU) | 0x0800U; - - /* Set CAN hardware module filter and mask. */ - if(CANmodule->useCANrxFilters){ - - } - } - else{ - ret = CO_ERROR_ILLEGAL_ARGUMENT; - } - - return ret; -} - - -/******************************************************************************/ -CO_CANtx_t *CO_CANtxBufferInit( - CO_CANmodule_t *CANmodule, - uint16_t index, - uint16_t ident, - bool_t rtr, - uint8_t noOfBytes, - bool_t syncFlag) -{ - CO_CANtx_t *buffer = NULL; - - if((CANmodule != NULL) && (index < CANmodule->txSize)){ - /* get specific buffer */ - buffer = &CANmodule->txArray[index]; - - /* CAN identifier, DLC and rtr, bit aligned with CAN module transmit buffer. - * Microcontroller specific. */ - buffer->ident = ((uint32_t)ident & 0x07FFU) - | ((uint32_t)(((uint32_t)noOfBytes & 0xFU) << 12U)) - | ((uint32_t)(rtr ? 0x8000U : 0U)); - - buffer->bufferFull = false; - buffer->syncFlag = syncFlag; - } - - return buffer; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_CANsend(CO_CANmodule_t *CANmodule, CO_CANtx_t *buffer){ - CO_ReturnError_t err = CO_ERROR_NO; - - /* Verify overflow */ - if(buffer->bufferFull){ - if(!CANmodule->firstCANtxMessage){ - /* don't set error, if bootup message is still on buffers */ - CO_errorReport((CO_EM_t*)CANmodule->em, CO_EM_CAN_TX_OVERFLOW, CO_EMC_CAN_OVERRUN, buffer->ident); - } - err = CO_ERROR_TX_OVERFLOW; - } - - CO_LOCK_CAN_SEND(); - /* if CAN TX buffer is free, copy message to it */ - if(1 && CANmodule->CANtxCount == 0){ - CANmodule->bufferInhibitFlag = buffer->syncFlag; - /* copy message and txRequest */ - } - /* if no buffer is free, message will be sent by interrupt */ - else{ - buffer->bufferFull = true; - CANmodule->CANtxCount++; - } - CO_UNLOCK_CAN_SEND(); - - return err; -} - - -/******************************************************************************/ -void CO_CANclearPendingSyncPDOs(CO_CANmodule_t *CANmodule){ - uint32_t tpdoDeleted = 0U; - - CO_LOCK_CAN_SEND(); - /* Abort message from CAN module, if there is synchronous TPDO. - * Take special care with this functionality. */ - if(/*messageIsOnCanBuffer && */CANmodule->bufferInhibitFlag){ - /* clear TXREQ */ - CANmodule->bufferInhibitFlag = false; - tpdoDeleted = 1U; - } - /* delete also pending synchronous TPDOs in TX buffers */ - if(CANmodule->CANtxCount != 0U){ - uint16_t i; - CO_CANtx_t *buffer = &CANmodule->txArray[0]; - for(i = CANmodule->txSize; i > 0U; i--){ - if(buffer->bufferFull){ - if(buffer->syncFlag){ - buffer->bufferFull = false; - CANmodule->CANtxCount--; - tpdoDeleted = 2U; - } - } - buffer++; - } - } - CO_UNLOCK_CAN_SEND(); - - - if(tpdoDeleted != 0U){ - CO_errorReport((CO_EM_t*)CANmodule->em, CO_EM_TPDO_OUTSIDE_WINDOW, CO_EMC_COMMUNICATION, tpdoDeleted); - } -} - - -/******************************************************************************/ -void CO_CANverifyErrors(CO_CANmodule_t *CANmodule){ - uint16_t rxErrors, txErrors, overflow; - CO_EM_t* em = (CO_EM_t*)CANmodule->em; - uint32_t err; - - /* get error counters from module. Id possible, function may use different way to - * determine errors. */ - rxErrors = CANmodule->txSize; - txErrors = CANmodule->txSize; - overflow = CANmodule->txSize; - - err = ((uint32_t)txErrors << 16) | ((uint32_t)rxErrors << 8) | overflow; - - if(CANmodule->errOld != err){ - CANmodule->errOld = err; - - if(txErrors >= 256U){ /* bus off */ - CO_errorReport(em, CO_EM_CAN_TX_BUS_OFF, CO_EMC_BUS_OFF_RECOVERED, err); - } - else{ /* not bus off */ - CO_errorReset(em, CO_EM_CAN_TX_BUS_OFF, err); - - if((rxErrors >= 96U) || (txErrors >= 96U)){ /* bus warning */ - CO_errorReport(em, CO_EM_CAN_BUS_WARNING, CO_EMC_NO_ERROR, err); - } - - if(rxErrors >= 128U){ /* RX bus passive */ - CO_errorReport(em, CO_EM_CAN_RX_BUS_PASSIVE, CO_EMC_CAN_PASSIVE, err); - } - else{ - CO_errorReset(em, CO_EM_CAN_RX_BUS_PASSIVE, err); - } - - if(txErrors >= 128U){ /* TX bus passive */ - if(!CANmodule->firstCANtxMessage){ - CO_errorReport(em, CO_EM_CAN_TX_BUS_PASSIVE, CO_EMC_CAN_PASSIVE, err); - } - } - else{ - bool_t isError = CO_isError(em, CO_EM_CAN_TX_BUS_PASSIVE); - if(isError){ - CO_errorReset(em, CO_EM_CAN_TX_BUS_PASSIVE, err); - CO_errorReset(em, CO_EM_CAN_TX_OVERFLOW, err); - } - } - - if((rxErrors < 96U) && (txErrors < 96U)){ /* no error */ - CO_errorReset(em, CO_EM_CAN_BUS_WARNING, err); - } - } - - if(overflow != 0U){ /* CAN RX bus overflow */ - CO_errorReport(em, CO_EM_CAN_RXB_OVERFLOW, CO_EMC_CAN_OVERRUN, err); - } - } -} - - -/******************************************************************************/ -void CO_CANinterrupt(CO_CANmodule_t *CANmodule){ - - /* receive interrupt */ - if(1){ - CO_CANrxMsg_t *rcvMsg; /* pointer to received message in CAN module */ - uint16_t index; /* index of received message */ - uint32_t rcvMsgIdent; /* identifier of the received message */ - CO_CANrx_t *buffer = NULL; /* receive message buffer from CO_CANmodule_t object. */ - bool_t msgMatched = false; - - rcvMsg = 0; /* get message from module here */ - rcvMsgIdent = rcvMsg->ident; - if(CANmodule->useCANrxFilters){ - /* CAN module filters are used. Message with known 11-bit identifier has */ - /* been received */ - index = 0; /* get index of the received message here. Or something similar */ - if(index < CANmodule->rxSize){ - buffer = &CANmodule->rxArray[index]; - /* verify also RTR */ - if(((rcvMsgIdent ^ buffer->ident) & buffer->mask) == 0U){ - msgMatched = true; - } - } - } - else{ - /* CAN module filters are not used, message with any standard 11-bit identifier */ - /* has been received. Search rxArray form CANmodule for the same CAN-ID. */ - buffer = &CANmodule->rxArray[0]; - for(index = CANmodule->rxSize; index > 0U; index--){ - if(((rcvMsgIdent ^ buffer->ident) & buffer->mask) == 0U){ - msgMatched = true; - break; - } - buffer++; - } - } - - /* Call specific function, which will process the message */ - if(msgMatched && (buffer != NULL) && (buffer->pFunct != NULL)){ - buffer->pFunct(buffer->object, rcvMsg); - } - - /* Clear interrupt flag */ - } - - - /* transmit interrupt */ - else if(0){ - /* Clear interrupt flag */ - - /* First CAN message (bootup) was sent successfully */ - CANmodule->firstCANtxMessage = false; - /* clear flag from previous message */ - CANmodule->bufferInhibitFlag = false; - /* Are there any new messages waiting to be send */ - if(CANmodule->CANtxCount > 0U){ - uint16_t i; /* index of transmitting message */ - - /* first buffer */ - CO_CANtx_t *buffer = &CANmodule->txArray[0]; - /* search through whole array of pointers to transmit message buffers. */ - for(i = CANmodule->txSize; i > 0U; i--){ - /* if message buffer is full, send it. */ - if(buffer->bufferFull){ - buffer->bufferFull = false; - CANmodule->CANtxCount--; - - /* Copy message to CAN buffer */ - CANmodule->bufferInhibitFlag = buffer->syncFlag; - /* canSend... */ - break; /* exit for loop */ - } - buffer++; - }/* end of for loop */ - - /* Clear counter if no more messages */ - if(i == 0U){ - CANmodule->CANtxCount = 0U; - } - } - } - else{ - /* some other interrupt reason */ - } -} diff --git a/stack/drvTemplate/CO_driver_target.h b/stack/drvTemplate/CO_driver_target.h deleted file mode 100644 index 9d40a74d..00000000 --- a/stack/drvTemplate/CO_driver_target.h +++ /dev/null @@ -1,291 +0,0 @@ -/** - * CAN module object for generic microcontroller. - * - * This file is a template for other microcontrollers. - * - * @file CO_driver_target.h - * @ingroup CO_driver - * @author Janez Paternoster - * @copyright 2004 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#ifndef CO_DRIVER_TARGET_H -#define CO_DRIVER_TARGET_H - -#ifdef __cplusplus -extern "C" { -#endif - -/* Include processor header file */ -#include /* for 'NULL' */ -#include /* for 'int8_t' to 'uint64_t' */ -#include /* for 'true', 'false' */ - - -/** - * Endianness. - * - * Depending on processor or compiler architecture, one of the two macros must - * be defined: CO_LITTLE_ENDIAN or CO_BIG_ENDIAN. CANopen itself is little endian. - */ -#define CO_LITTLE_ENDIAN - - -/** - * @defgroup CO_driver Driver - * @ingroup CO_CANopen - * @{ - * - * Microcontroller specific code for CANopenNode. - * - * This file contains type definitions, functions and macros for: - * - Basic data types. - * - Receive and transmit buffers for CANopen messages. - * - Interaction with CAN module on the microcontroller. - * - CAN receive and transmit interrupts. - * - * This file is not only a CAN driver. There are no classic CAN queues for CAN - * messages. This file provides direct connection with other CANopen - * objects. It tries to provide fast responses and tries to avoid unnecessary - * calculations and memory consumptions. - * - * CO_CANmodule_t contains an array of _Received message objects_ (of type - * CO_CANrx_t) and an array of _Transmit message objects_ (of type CO_CANtx_t). - * Each CANopen communication object owns one member in one of the arrays. - * For example Heartbeat producer generates one CANopen transmitting object, - * so it has reserved one member in CO_CANtx_t array. - * SYNC module may produce sync or consume sync, so it has reserved one member - * in CO_CANtx_t and one member in CO_CANrx_t array. - * - * ###Reception of CAN messages. - * Before CAN messages can be received, each member in CO_CANrx_t must be - * initialized. CO_CANrxBufferInit() is called by CANopen module, which - * uses specific member. For example @ref CO_HBconsumer uses multiple members - * in CO_CANrx_t array. (It monitors multiple heartbeat messages from remote - * nodes.) It must call CO_CANrxBufferInit() multiple times. - * - * Main arguments to the CO_CANrxBufferInit() function are CAN identifier - * and a pointer to callback function. Those two arguments (and some others) - * are copied to the member of the CO_CANrx_t array. - * - * Callback function is a function, specified by specific CANopen module - * (for example by @ref CO_HBconsumer). Each CANopen module defines own - * callback function. Callback function will process the received CAN message. - * It will copy the necessary data from CAN message to proper place. It may - * also trigger additional task, which will further process the received message. - * Callback function must be fast and must only make the necessary calculations - * and copying. - * - * Received CAN messages are processed by CAN receive interrupt function. - * After CAN message is received, function first tries to find matching CAN - * identifier from CO_CANrx_t array. If found, then a corresponding callback - * function is called. - * - * Callback function accepts two parameters: - * - object is pointer to object registered by CO_CANrxBufferInit(). - * - msg is pointer to CAN message of type CO_CANrxMsg_t. - * - * Callback function must return #CO_ReturnError_t: CO_ERROR_NO, - * CO_ERROR_RX_OVERFLOW, CO_ERROR_RX_PDO_OVERFLOW, CO_ERROR_RX_MSG_LENGTH or - * CO_ERROR_RX_PDO_LENGTH. - * - * - * ###Transmission of CAN messages. - * Before CAN messages can be transmitted, each member in CO_CANtx_t must be - * initialized. CO_CANtxBufferInit() is called by CANopen module, which - * uses specific member. For example Heartbeat producer must initialize it's - * member in CO_CANtx_t array. - * - * CO_CANtxBufferInit() returns a pointer of type CO_CANtx_t, which contains buffer - * where CAN message data can be written. CAN message is send with calling - * CO_CANsend() function. If at that moment CAN transmit buffer inside - * microcontroller's CAN module is free, message is copied directly to CAN module. - * Otherwise CO_CANsend() function sets _bufferFull_ flag to true. Message will be - * then sent by CAN TX interrupt as soon as CAN module is freed. Until message is - * not copied to CAN module, its contents must not change. There may be multiple - * _bufferFull_ flags in CO_CANtx_t array set to true. In that case messages with - * lower index inside array will be sent first. - */ - - -/** - * @name Critical sections - * CANopenNode is designed to run in different threads, as described in README. - * Threads are implemented differently in different systems. In microcontrollers - * threads are interrupts with different priorities, for example. - * It is necessary to protect sections, where different threads access to the - * same resource. In simple systems interrupts or scheduler may be temporary - * disabled between access to the shared resource. Otherwise mutexes or - * semaphores can be used. - * - * ####Reentrant functions. - * Functions CO_CANsend() from C_driver.h, CO_errorReport() from CO_Emergency.h - * and CO_errorReset() from CO_Emergency.h may be called from different threads. - * Critical sections must be protected. Eather by disabling scheduler or - * interrupts or by mutexes or semaphores. - * - * ####Object Dictionary variables. - * In general, there are two threads, which accesses OD variables: mainline and - * timer. CANopenNode initialization and SDO server runs in mainline. PDOs runs - * in faster timer thread. Processing of PDOs must not be interrupted by - * mainline. Mainline thread must protect sections, which accesses the same OD - * variables as timer thread. This care must also take the application. Note - * that not all variables are allowed to be mapped to PDOs, so they may not need - * to be protected. SDO server protects sections with access to OD variables. - * - * ####CAN receive thread. - * It partially processes received CAN data and puts them into appropriate - * objects. Objects are later processed. It does not need protection of - * critical sections. There is one circumstance, where CANrx should be disabled: - * After presence of SYNC message on CANopen bus, CANrx should be temporary - * disabled until all receive PDOs are processed. See also CO_SYNC.h file and - * CO_SYNC_initCallback() function. - * @{ - */ -#define CO_LOCK_CAN_SEND() /**< Lock critical section in CO_CANsend() */ -#define CO_UNLOCK_CAN_SEND()/**< Unlock critical section in CO_CANsend() */ - -#define CO_LOCK_EMCY() /**< Lock critical section in CO_errorReport() or CO_errorReset() */ -#define CO_UNLOCK_EMCY() /**< Unlock critical section in CO_errorReport() or CO_errorReset() */ - -#define CO_LOCK_OD() /**< Lock critical section when accessing Object Dictionary */ -#define CO_UNLOCK_OD() /**< Unock critical section when accessing Object Dictionary */ -/** @} */ - -/** - * @name Syncronisation functions - * syncronisation for message buffer for communication between CAN receive and - * message processing threads. - * - * If receive function runs inside IRQ, no further synchronsiation is needed. - * Otherwise, some kind of synchronsiation has to be included. The following - * example uses GCC builtin memory barrier __sync_synchronize(). A comprehensive - * list can be found here: https://gist.github.com/leo-yuriev/ba186a6bf5cf3a27bae7 - * \code{.c} - #define CANrxMemoryBarrier() {__sync_synchronize();} - * \endcode - * @{ - */ -/** Memory barrier */ -#define CANrxMemoryBarrier() -/** Check if new message has arrived */ -#define IS_CANrxNew(rxNew) ((uintptr_t)rxNew) -/** Set new message flag */ -#define SET_CANrxNew(rxNew) {CANrxMemoryBarrier(); rxNew = (void*)1L;} -/** Clear new message flag */ -#define CLEAR_CANrxNew(rxNew) {CANrxMemoryBarrier(); rxNew = (void*)0L;} -/** @} */ - -/** - * @defgroup CO_dataTypes Data types - * @{ - * - * According to Misra C - */ -/* int8_t to uint64_t are defined in stdint.h */ -typedef unsigned char bool_t; /**< bool_t */ -typedef float float32_t; /**< float32_t */ -typedef long double float64_t; /**< float64_t */ -typedef char char_t; /**< char_t */ -typedef unsigned char oChar_t; /**< oChar_t */ -typedef unsigned char domain_t; /**< domain_t */ -/** @} */ - - -/** - * CAN receive message structure as aligned in CAN module. It is different in - * different microcontrollers. It usually contains other variables. - */ -typedef struct{ - /** CAN identifier. It must be read through CO_CANrxMsg_readIdent() function. */ - uint32_t ident; - uint8_t DLC ; /**< Length of CAN message */ - uint8_t data[8]; /**< 8 data bytes */ -}CO_CANrxMsg_t; - - -/** - * Received message object - */ -typedef struct{ - uint16_t ident; /**< Standard CAN Identifier (bits 0..10) + RTR (bit 11) */ - uint16_t mask; /**< Standard Identifier mask with same alignment as ident */ - void *object; /**< From CO_CANrxBufferInit() */ - void (*pFunct)(void *object, const CO_CANrxMsg_t *message); /**< From CO_CANrxBufferInit() */ -}CO_CANrx_t; - - -/** - * Transmit message object. - */ -typedef struct{ - uint32_t ident; /**< CAN identifier as aligned in CAN module */ - uint8_t DLC ; /**< Length of CAN message. (DLC may also be part of ident) */ - uint8_t data[8]; /**< 8 data bytes */ - volatile bool_t bufferFull; /**< True if previous message is still in buffer */ - /** Synchronous PDO messages has this flag set. It prevents them to be sent outside the synchronous window */ - volatile bool_t syncFlag; -}CO_CANtx_t; - - -/** - * CAN module object. It may be different in different microcontrollers. - */ -typedef struct{ - void *CANdriverState; /**< From CO_CANmodule_init() */ - CO_CANrx_t *rxArray; /**< From CO_CANmodule_init() */ - uint16_t rxSize; /**< From CO_CANmodule_init() */ - CO_CANtx_t *txArray; /**< From CO_CANmodule_init() */ - uint16_t txSize; /**< From CO_CANmodule_init() */ - volatile bool_t CANnormal; /**< CAN module is in normal mode */ - /** Value different than zero indicates, that CAN module hardware filters - * are used for CAN reception. If there is not enough hardware filters, - * they won't be used. In this case will be *all* received CAN messages - * processed by software. */ - volatile bool_t useCANrxFilters; - /** If flag is true, then message in transmitt buffer is synchronous PDO - * message, which will be aborted, if CO_clearPendingSyncPDOs() function - * will be called by application. This may be necessary if Synchronous - * window time was expired. */ - volatile bool_t bufferInhibitFlag; - /** Equal to 1, when the first transmitted message (bootup message) is in CAN TX buffers */ - volatile bool_t firstCANtxMessage; - /** Number of messages in transmit buffer, which are waiting to be copied to the CAN module */ - volatile uint16_t CANtxCount; - uint32_t errOld; /**< Previous state of CAN errors */ - void *em; /**< Emergency object */ -}CO_CANmodule_t; - - -/** - * Receives and transmits CAN messages. - * - * Function must be called directly from high priority CAN interrupt. - * - * @param CANmodule This object. - */ -void CO_CANinterrupt(CO_CANmodule_t *CANmodule); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -/** @} */ -#endif /* CO_DRIVER_TARGET_H */ diff --git a/stack/drvTemplate/eeprom.c b/stack/drvTemplate/eeprom.c deleted file mode 100644 index 81a231ef..00000000 --- a/stack/drvTemplate/eeprom.c +++ /dev/null @@ -1,174 +0,0 @@ -/** - * Microcontroller specific code for CANopenNode nonvolatile variables. - * - * This file is a template for other microcontrollers. - * - * @file eeprom.h - * @ingroup CO_eeprom - * @author Janez Paternoster - * @copyright 2004 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include "CO_driver.h" -#include "CO_SDO.h" -#include "CO_Emergency.h" -#include "eeprom.h" -#include "crc16-ccitt.h" - - -/** - * OD function for accessing _Store parameters_ (index 0x1010) from SDO server. - * - * For more information see file CO_SDO.h. - */ -static CO_SDO_abortCode_t CO_ODF_1010(CO_ODF_arg_t *ODF_arg); -static CO_SDO_abortCode_t CO_ODF_1010(CO_ODF_arg_t *ODF_arg){ - //CO_EE_t *ee; - uint32_t value; - CO_SDO_abortCode_t ret = CO_SDO_AB_NONE; - - //ee = (CO_EE_t*) ODF_arg->object; - value = CO_getUint32(ODF_arg->data); - - if(!ODF_arg->reading){ - /* don't change the old value */ - CO_memcpy(ODF_arg->data, (const uint8_t*)ODF_arg->ODdataStorage, 4U); - - if(ODF_arg->subIndex == 1U){ - if(value == 0x65766173UL){ - /* write ee->OD_ROMAddress, ee->OD_ROMSize to eeprom (blocking function) */ - - /* verify data */ - if(0/*error*/){ - ret = CO_SDO_AB_HW; - } - } - else{ - ret = CO_SDO_AB_DATA_TRANSF; - } - } - } - - return ret; -} - - -/** - * OD function for accessing _Restore default parameters_ (index 0x1011) from SDO server. - * - * For more information see file CO_SDO.h. - */ -static CO_SDO_abortCode_t CO_ODF_1011(CO_ODF_arg_t *ODF_arg); -static CO_SDO_abortCode_t CO_ODF_1011(CO_ODF_arg_t *ODF_arg){ - //CO_EE_t *ee; - uint32_t value; - CO_SDO_abortCode_t ret = CO_SDO_AB_NONE; - - //ee = (CO_EE_t*) ODF_arg->object; - value = CO_getUint32(ODF_arg->data); - - if(!ODF_arg->reading){ - /* don't change the old value */ - CO_memcpy(ODF_arg->data, (const uint8_t*)ODF_arg->ODdataStorage, 4U); - - if(ODF_arg->subIndex >= 1U){ - if(value == 0x64616F6CUL){ - /* Clear the eeprom */ - - } - else{ - ret = CO_SDO_AB_DATA_TRANSF; - } - } - } - - return ret; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_EE_init_1( - CO_EE_t *ee, - uint8_t *OD_EEPROMAddress, - uint32_t OD_EEPROMSize, - uint8_t *OD_ROMAddress, - uint32_t OD_ROMSize) -{ - /* verify arguments */ - if(ee==NULL || OD_EEPROMAddress==NULL || OD_ROMAddress==NULL){ - return CO_ERROR_ILLEGAL_ARGUMENT; - } - - /* Configure eeprom */ - - - /* configure object variables */ - ee->OD_EEPROMAddress = OD_EEPROMAddress; - ee->OD_EEPROMSize = OD_EEPROMSize; - ee->OD_ROMAddress = OD_ROMAddress; - ee->OD_ROMSize = OD_ROMSize; - ee->OD_EEPROMCurrentIndex = 0U; - ee->OD_EEPROMWriteEnable = false; - - /* read the CO_OD_EEPROM from EEPROM, first verify, if data are OK */ - - /* read the CO_OD_ROM from EEPROM and verify CRC */ - - return CO_ERROR_NO; -} - - -/******************************************************************************/ -void CO_EE_init_2( - CO_EE_t *ee, - CO_ReturnError_t eeStatus, - CO_SDO_t *SDO, - CO_EM_t *em) -{ - CO_OD_configure(SDO, OD_H1010_STORE_PARAM_FUNC, CO_ODF_1010, (void*)ee, 0, 0U); - CO_OD_configure(SDO, OD_H1011_REST_PARAM_FUNC, CO_ODF_1011, (void*)ee, 0, 0U); - if(eeStatus != CO_ERROR_NO){ - CO_errorReport(em, CO_EM_NON_VOLATILE_MEMORY, CO_EMC_HARDWARE, (uint32_t)eeStatus); - } -} - - -/******************************************************************************/ -void CO_EE_process(CO_EE_t *ee){ - if((ee != 0) && (ee->OD_EEPROMWriteEnable)/* && !isWriteInProcess()*/){ - uint32_t i; - uint8_t RAMdata, eeData; - - /* verify next word */ - if(++ee->OD_EEPROMCurrentIndex == ee->OD_EEPROMSize){ - ee->OD_EEPROMCurrentIndex = 0U; - } - i = ee->OD_EEPROMCurrentIndex; - - /* read eeprom */ - RAMdata = ee->OD_EEPROMAddress[i]; - eeData = 0/*EE_readByte(i)*/; - - /* if bytes in EEPROM and in RAM are different, then write to EEPROM */ - if(eeData != RAMdata){ - /* EE_writeByteNoWait(RAMdata, i);*/ - } - } -} diff --git a/stack/drvTemplate/eeprom.h b/stack/drvTemplate/eeprom.h deleted file mode 100644 index 5f6d981d..00000000 --- a/stack/drvTemplate/eeprom.h +++ /dev/null @@ -1,108 +0,0 @@ -/** - * Eeprom object for generic microcontroller. - * - * This file is a template for other microcontrollers. - * - * @file eeprom.h - * @ingroup CO_eeprom - * @author Janez Paternoster - * @copyright 2004 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#ifndef EEPROM_H -#define EEPROM_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @defgroup CO_eeprom Nonvolatile storage - * @ingroup CO_CANopen - * @{ - * - * Storage of nonvolatile CANopen variables into the eeprom. - */ - - -/** - * Eeprom object. - */ -typedef struct{ - uint8_t *OD_EEPROMAddress; /**< From CO_EE_init_1() */ - uint32_t OD_EEPROMSize; /**< From CO_EE_init_1() */ - uint8_t *OD_ROMAddress; /**< From CO_EE_init_1() */ - uint32_t OD_ROMSize; /**< From CO_EE_init_1() */ - uint32_t OD_EEPROMCurrentIndex; /**< Internal variable controls the OD_EEPROM vrite */ - bool_t OD_EEPROMWriteEnable; /**< Writing to EEPROM is enabled */ -}CO_EE_t; - - -/** - * First part of eeprom initialization. Called after microcontroller reset. - * - * @param ee This object will be initialized. - * @param OD_EEPROMAddress Address of OD_EEPROM structure from object dictionary. - * @param OD_EEPROMSize Size of OD_EEPROM structure from object dictionary. - * @param OD_ROMAddress Address of OD_ROM structure from object dictionary. - * @param OD_ROMSize Size of OD_ROM structure from object dictionary. - * - * @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_DATA_CORRUPT (Data in eeprom corrupt) or - * CO_ERROR_CRC (CRC from MBR does not match the CRC of OD_ROM block in eeprom). - */ -CO_ReturnError_t CO_EE_init_1( - CO_EE_t *ee, - uint8_t *OD_EEPROMAddress, - uint32_t OD_EEPROMSize, - uint8_t *OD_ROMAddress, - uint32_t OD_ROMSize); - - -/** - * Second part of eeprom initialization. Called after CANopen communication reset. - * - * @param ee - This object. - * @param eeStatus - Return value from CO_EE_init_1(). - * @param SDO - SDO object. - * @param em - Emergency object. - */ -void CO_EE_init_2( - CO_EE_t *ee, - CO_ReturnError_t eeStatus, - CO_SDO_t *SDO, - CO_EM_t *em); - - -/** - * Process eeprom object. - * - * Function must be called cyclically. It strores variables from OD_EEPROM data - * block into eeprom byte by byte (only if values are different). - * - * @param ee This object. - */ -void CO_EE_process(CO_EE_t *ee); - -#ifdef __cplusplus -} -#endif /*__cplusplus*/ - -/** @} */ -#endif diff --git a/stack/dsPIC30F/CO_driver.c b/stack/dsPIC30F/CO_driver.c deleted file mode 100644 index d1d08b57..00000000 --- a/stack/dsPIC30F/CO_driver.c +++ /dev/null @@ -1,535 +0,0 @@ -/* - * CAN module object for Microchip dsPIC30F microcontroller. - * - * @file CO_driver.c - * @author Janez Paternoster - * @copyright 2004 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include "CO_driver.h" -#include "CO_Emergency.h" - - -extern const CO_CANbitRateData_t CO_CANbitRateData[8]; - -/** - * Macro and Constants - CAN module registers - offset. - */ - #define CAN_REG(base, offset) (*((volatile uint16_t *) (((uintptr_t) base) + offset))) - - #define C_RXF0SID 0x00 - #define C_RXF0EIDH 0x02 - #define C_RXF0EIDL 0x04 - #define C_RXF1SID 0x08 - #define C_RXF1EIDH 0x0A - #define C_RXF1EIDL 0x0C - #define C_RXF2SID 0x10 - #define C_RXF2EIDH 0x12 - #define C_RXF2EIDL 0x14 - #define C_RXF3SID 0x18 - #define C_RXF3EIDH 0x1A - #define C_RXF3EIDL 0x1C - #define C_RXF4SID 0x20 - #define C_RXF4EIDH 0x22 - #define C_RXF4EIDL 0x24 - #define C_RXF5SID 0x28 - #define C_RXF5EIDH 0x2A - #define C_RXF5EIDL 0x2C - #define C_RXM0SID 0x30 - #define C_RXM0EIDH 0x32 - #define C_RXM0EIDL 0x34 - #define C_RXM1SID 0x38 - #define C_RXM1EIDH 0x3A - #define C_RXM1EIDL 0x3C - - #define C_TXBUF2 0x40 - #define C_TXBUF1 0x50 - #define C_TXBUF0 0x60 - #define C_TXSID 0x0 - #define C_TXEID 0x2 - #define C_TXDLC 0x4 - #define C_TXB 0x6 - #define C_TXCON 0xE - - #define C_RXBUF1 0x70 - #define C_RXBUF0 0x80 - #define C_RXCON 0xE - /* register alignment as in CO_CANrxMsg_t */ - - #define C_CTRL 0x90 - #define C_CFG1 0x92 - #define C_CFG2 0x94 - #define C_INTF 0x96 - #define C_INTE 0x98 - #define C_EC 0x9A - - -/******************************************************************************/ -void CO_CANsetConfigurationMode(void *CANdriverState){ - uint16_t C_CTRLcopy = CAN_REG(CANdriverState, C_CTRL); - - /* set REQOP = 0x4 */ - C_CTRLcopy &= 0xFCFF; - C_CTRLcopy |= 0x0400; - CAN_REG(CANdriverState, C_CTRL) = C_CTRLcopy; - - /* while OPMODE != 4 */ - while((CAN_REG(CANdriverState, C_CTRL) & 0x00E0) != 0x0080); -} - - -/******************************************************************************/ -void CO_CANsetNormalMode(CO_CANmodule_t *CANmodule){ - uint16_t C_CTRLcopy = CAN_REG(CANmodule->CANdriverState, C_CTRL); - - /* set REQOP = 0x0 */ - C_CTRLcopy &= 0xF8FF; - CAN_REG(CANmodule->CANdriverState, C_CTRL) = C_CTRLcopy; - - /* while OPMODE != 0 */ - while((CAN_REG(CANmodule->CANdriverState, C_CTRL) & 0x00E0) != 0x0000); - - CANmodule->CANnormal = true; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_CANmodule_init( - CO_CANmodule_t *CANmodule, - void *CANdriverState, - CO_CANrx_t rxArray[], - uint16_t rxSize, - CO_CANtx_t txArray[], - uint16_t txSize, - uint16_t CANbitRate) -{ - uint16_t i; - - /* verify arguments */ - if(CANmodule==NULL || rxArray==NULL || txArray==NULL){ - return CO_ERROR_ILLEGAL_ARGUMENT; - } - - /* Configure object variables */ - CANmodule->CANdriverState = CANdriverState; - CANmodule->rxArray = rxArray; - CANmodule->rxSize = rxSize; - CANmodule->txArray = txArray; - CANmodule->txSize = txSize; - CANmodule->CANnormal = false; - CANmodule->useCANrxFilters = false; - CANmodule->bufferInhibitFlag = false; - CANmodule->firstCANtxMessage = true; - CANmodule->CANtxCount = 0U; - CANmodule->errOld = 0U; - CANmodule->em = NULL; - - for(i=0U; iCANdriverState); -} - - -/******************************************************************************/ -uint16_t CO_CANrxMsg_readIdent(const CO_CANrxMsg_t *rxMsg){ - return (rxMsg->ident >> 2) & 0x7FF; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_CANrxBufferInit( - CO_CANmodule_t *CANmodule, - uint16_t index, - uint16_t ident, - uint16_t mask, - bool_t rtr, - void *object, - void (*pFunct)(void *object, const CO_CANrxMsg_t *message)) -{ - CO_ReturnError_t ret = CO_ERROR_NO; - - if((CANmodule!=NULL) && (object!=NULL) && (pFunct!=NULL) && (index < CANmodule->rxSize)){ - /* buffer, which will be configured */ - CO_CANrx_t *buffer = &CANmodule->rxArray[index]; - uint16_t RXF, RXM; - - /* Configure object variables */ - buffer->object = object; - buffer->pFunct = pFunct; - - - /* CAN identifier and CAN mask, bit aligned with CAN module registers */ - RXF = (ident & 0x07FF) << 2; - if(rtr){ - RXF |= 0x02; - } - RXM = (mask & 0x07FF) << 2; - RXM |= 0x02; - - /* configure filter and mask */ - if(RXF != buffer->ident || RXM != buffer->mask){ - buffer->ident = RXF; - buffer->mask = RXM; - } - } - else{ - ret = CO_ERROR_ILLEGAL_ARGUMENT; - } - - return ret; -} - - -/******************************************************************************/ -CO_CANtx_t *CO_CANtxBufferInit( - CO_CANmodule_t *CANmodule, - uint16_t index, - uint16_t ident, - bool_t rtr, - uint8_t noOfBytes, - bool_t syncFlag) -{ - CO_CANtx_t *buffer = NULL; - - if((CANmodule != NULL) && (index < CANmodule->txSize)){ - /* get specific buffer */ - buffer = &CANmodule->txArray[index]; - - /* CAN identifier, bit aligned with CAN module registers */ - uint16_t TXF; - TXF = ident << 5; - TXF &= 0xF800; - TXF |= (ident & 0x003F) << 2; - if(rtr){ - TXF |= 0x02; - } - - /* write to buffer */ - buffer->ident = TXF; - buffer->DLC = noOfBytes; - buffer->bufferFull = false; - buffer->syncFlag = syncFlag; - } - - return buffer; -} - - -/* Copy message to CAN module - internal usage only. - * - * @param dest Destination address of CAN module transmit buffer. - * @param src Pointer to source message . - */ -static void CO_CANsendToModule(uint16_t dest, CO_CANtx_t *src){ - uint8_t DLC; - volatile uint8_t *CANdataBuffer; - uint8_t *pData; - - /* CAN-ID + RTR */ - CAN_REG(dest, C_TXSID) = src->ident; - - /* Data lenght */ - DLC = src->DLC; - if(DLC > 8) DLC = 8; - CAN_REG(dest, C_TXDLC) = DLC << 3; - - /* copy data */ - CANdataBuffer = (volatile uint8_t*)(dest + C_TXB); - pData = src->data; - for(; DLC>0; DLC--) *(CANdataBuffer++) = *(pData++); - - /* control register, transmit request */ - CAN_REG(dest, C_TXCON) |= 0x08; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_CANsend(CO_CANmodule_t *CANmodule, CO_CANtx_t *buffer){ - CO_ReturnError_t err = CO_ERROR_NO; - uint16_t addr = CANmodule->CANdriverState; - - /* Verify overflow */ - if(buffer->bufferFull){ - if(!CANmodule->firstCANtxMessage){ - /* don't set error, if bootup message is still on buffers */ - CO_errorReport((CO_EM_t*)CANmodule->em, CO_EM_CAN_TX_OVERFLOW, CO_EMC_CAN_OVERRUN, 0); - } - err = CO_ERROR_TX_OVERFLOW; - } - - CO_LOCK_CAN_SEND(); - /* if CAN TB buffer is free, copy message to it */ - if((CAN_REG(addr, C_TXBUF0 + C_TXCON) & 0x8) == 0 && CANmodule->CANtxCount == 0){ - CANmodule->bufferInhibitFlag = buffer->syncFlag; - CO_CANsendToModule(addr + C_TXBUF0, buffer); - } - /* if no buffer is free, message will be sent by interrupt */ - else{ - buffer->bufferFull = true; - CANmodule->CANtxCount++; - } - CO_UNLOCK_CAN_SEND(); - - return err; -} - - -/******************************************************************************/ -void CO_CANclearPendingSyncPDOs(CO_CANmodule_t *CANmodule){ - - /* See generic driver for implemetation. */ -} - - -/******************************************************************************/ -void CO_CANverifyErrors(CO_CANmodule_t *CANmodule){ - uint8_t err; - CO_EM_t* em = (CO_EM_t*)CANmodule->em; - - err = CAN_REG(CANmodule->CANdriverState, C_INTF)>>8; - - if(CANmodule->errOld != err){ - CANmodule->errOld = err; - - /* CAN RX bus overflow */ - if(err & 0xC0){ - CO_errorReport(em, CO_EM_CAN_RXB_OVERFLOW, CO_EMC_CAN_OVERRUN, err); - CAN_REG(CANmodule->CANdriverState, C_INTF) &= 0x3FFF;/* clear bits */ - } - - /* CAN TX bus off */ - if(err & 0x20){ - CO_errorReport(em, CO_EM_CAN_TX_BUS_OFF, CO_EMC_BUS_OFF_RECOVERED, err); - } - else{ - CO_errorReset(em, CO_EM_CAN_TX_BUS_OFF, err); - } - - /* CAN TX bus passive */ - if(err & 0x10){ - if(!CANmodule->firstCANtxMessage) CO_errorReport(em, CO_EM_CAN_TX_BUS_PASSIVE, CO_EMC_CAN_PASSIVE, err); - } - else{ - int8_t isError = CO_isError(em, CO_EM_CAN_TX_BUS_PASSIVE); - if(isError){ - CO_errorReset(em, CO_EM_CAN_TX_BUS_PASSIVE, err); - CO_errorReset(em, CO_EM_CAN_TX_OVERFLOW, err); - } - } - - /* CAN RX bus passive */ - if(err & 0x08){ - CO_errorReport(em, CO_EM_CAN_RX_BUS_PASSIVE, CO_EMC_CAN_PASSIVE, err); - } - else{ - CO_errorReset(em, CO_EM_CAN_RX_BUS_PASSIVE, err); - } - - /* CAN TX or RX bus warning */ - if(err & 0x19){ - CO_errorReport(em, CO_EM_CAN_BUS_WARNING, CO_EMC_NO_ERROR, err); - } - else{ - CO_errorReset(em, CO_EM_CAN_BUS_WARNING, err); - } - } -} - - -/******************************************************************************/ -void CO_CANinterrupt(CO_CANmodule_t *CANmodule){ - uint16_t ICODE; - ICODE = CAN_REG(CANmodule->CANdriverState, C_CTRL) & 0xE; - - /* receive interrupt 0 (New CAN messagge is available in RX buffer 0) */ - if(ICODE == 0xC){ - CO_CANrxMsg_t *rcvMsg; /* pointer to received message in CAN module */ - uint16_t index; /* index of received message */ - uint16_t rcvMsgIdent; /* identifier of the received message */ - CO_CANrx_t *buffer = NULL; /* receive message buffer from CO_CANmodule_t object. */ - bool_t msgMatched = false; - - rcvMsg = (CO_CANrxMsg_t*) (CANmodule->CANdriverState + C_RXBUF0); - rcvMsgIdent = rcvMsg->ident; - /* CAN module filters are not used, message with any standard 11-bit identifier */ - /* has been received. Search rxArray form CANmodule for the same CAN-ID. */ - buffer = &CANmodule->rxArray[0]; - for(index = CANmodule->rxSize; index > 0U; index--){ - if(((rcvMsgIdent ^ buffer->ident) & buffer->mask) == 0U){ - msgMatched = true; - break; - } - buffer++; - } - - /* Call specific function, which will process the message */ - if(msgMatched && (buffer != NULL) && (buffer->pFunct != NULL)){ - buffer->pFunct(buffer->object, rcvMsg); - } - - /* Clear RXFUL flag */ - rcvMsg->CON &= 0xFF7F; - - /* Clear interrupt flag */ - CAN_REG(CANmodule->CANdriverState, C_INTF) &= 0xFFFE; - } - - - /* receive interrupt 1 (New CAN messagge is available in RX buffer 1) */ - else if(ICODE == 0xA){ - CO_CANrxMsg_t *rcvMsg; /* pointer to received message in CAN module */ - uint16_t index; /* index of received message */ - uint16_t rcvMsgIdent; /* identifier of the received message */ - CO_CANrx_t *buffer = NULL; /* receive message buffer from CO_CANmodule_t object. */ - bool_t msgMatched = false; - - rcvMsg = (CO_CANrxMsg_t*) (CANmodule->CANdriverState + C_RXBUF1); - rcvMsgIdent = rcvMsg->ident; - /* CAN module filters are not used, message with any standard 11-bit identifier */ - /* has been received. Search rxArray form CANmodule for the same CAN-ID. */ - buffer = &CANmodule->rxArray[0]; - for(index = CANmodule->rxSize; index > 0U; index--){ - if(((rcvMsgIdent ^ buffer->ident) & buffer->mask) == 0U){ - msgMatched = true; - break; - } - buffer++; - } - - /* Call specific function, which will process the message */ - if(msgMatched && (buffer != NULL) && (buffer->pFunct != NULL)){ - buffer->pFunct(buffer->object, rcvMsg); - } - - /* Clear RXFUL flag */ - rcvMsg->CON &= 0xFF7F; - - /* Clear interrupt flag */ - CAN_REG(CANmodule->CANdriverState, C_INTF) &= 0xFFFD; - } - - - /* transmit interrupt (TX buffer is free) */ - else if(ICODE == 0x8){ - /* Clear interrupt flag */ - CAN_REG(CANmodule->CANdriverState, C_INTF) &= 0xFFFB; - /* First CAN message (bootup) was sent successfully */ - CANmodule->firstCANtxMessage = false; - /* clear flag from previous message */ - CANmodule->bufferInhibitFlag = false; - /* Are there any new messages waiting to be send and buffer is free */ - if(CANmodule->CANtxCount > 0U && (CAN_REG(CANmodule->CANdriverState, C_TXBUF0 + C_TXCON) & 0x8) == 0){ - uint16_t i; /* index of transmitting message */ - - /* first buffer */ - CO_CANtx_t *buffer = &CANmodule->txArray[0]; - /* search through whole array of pointers to transmit message buffers. */ - for(i = CANmodule->txSize; i > 0U; i--){ - /* if message buffer is full, send it. */ - if(buffer->bufferFull){ - buffer->bufferFull = false; - CANmodule->CANtxCount--; - - /* Copy message to CAN buffer */ - CANmodule->bufferInhibitFlag = buffer->syncFlag; - CO_CANsendToModule(CANmodule->CANdriverState + C_TXBUF0, buffer); - break; /* exit for loop */ - } - buffer++; - }/* end of for loop */ - - /* Clear counter if no more messages */ - if(i == 0U){ - CANmodule->CANtxCount = 0U; - } - } - } -} diff --git a/stack/dsPIC30F/CO_driver_target.h b/stack/dsPIC30F/CO_driver_target.h deleted file mode 100644 index af8c8aec..00000000 --- a/stack/dsPIC30F/CO_driver_target.h +++ /dev/null @@ -1,371 +0,0 @@ -/* - * CAN module object for Microchip dsPIC30F microcontroller. - * - * @file CO_driver_target.h - * @author Janez Paternoster - * @copyright 2004 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#ifndef CO_DRIVER_TARGET_H -#define CO_DRIVER_TARGET_H - - -#include /* processor header file */ -#include /* for 'NULL' */ -#include /* for 'int8_t' to 'uint64_t' */ -#include /* for true and false */ - -/* Endianness */ -#define CO_LITTLE_ENDIAN - - -/* CAN module base address */ -#define ADDR_CAN1 0x300 -#define ADDR_CAN2 0x3C0 - - -/* Critical sections */ -#define CO_LOCK_CAN_SEND() asm volatile ("disi #0x3FFF") -#define CO_UNLOCK_CAN_SEND() asm volatile ("disi #0x0000") - -#define CO_LOCK_EMCY() asm volatile ("disi #0x3FFF") -#define CO_UNLOCK_EMCY() asm volatile ("disi #0x0000") - -#define CO_LOCK_OD() asm volatile ("disi #0x3FFF") -#define CO_UNLOCK_OD() asm volatile ("disi #0x0000") - - -/* Data types */ -/* int8_t to uint64_t are defined in stdint.h */ -typedef unsigned char bool_t; -typedef float float32_t; -typedef long double float64_t; -typedef char char_t; -typedef unsigned char oChar_t; -typedef unsigned char domain_t; - - -/* CAN bit rates - * - * CAN bit rates are initializers for array of eight CO_CANbitRateData_t - * objects. - * - * Macros are not used by driver itself, they may be used by application with - * combination with object CO_CANbitRateData_t. - * Application must declare following global variable depending on CO_FCY used: - * const CO_CANbitRateData_t CO_CANbitRateData[8] = {CO_CANbitRateDataInitializers}; - * - * There are initializers for eight objects, which corresponds to following - * CAN bit rates (in kbps): 10, 20, 50, 125, 250, 500, 800, 1000. - * - * CO_FCY is internal instruction cycle clock frequency in kHz units. It is - * calculated from oscillator frequency (FOSC [in kHz]) and PLL mode: - * - If PLL is not used -> FCY = FOSC / 4, - * - If PLL x 4 is used -> FCY = FOSC, - * - If PLL x 16 is used -> FCY = FOSC * 4 - * - * Possible values for FCY are (in three groups): - * - Optimal CAN bit timing on all Baud Rates: 4000, 6000, 16000, 24000. - * - Not so optimal CAN bit timing on all Baud Rates: 2000, 8000. - * - not all CANopen Baud Rates possible: 1000, 1500, 2500, 3000, 5000, - * 10000, 12000, 20000, 28000, 30000, 1843 (internal FRC, no PLL), - * 7372 (internal FRC + 4*PLL). - */ -#ifdef CO_FCY - /* Macros, which divides K into (SJW + PROP + PhSeg1 + PhSeg2) */ - #define TQ_x_4 1, 1, 1, 1 - #define TQ_x_5 1, 1, 2, 1 - #define TQ_x_6 1, 1, 3, 1 - #define TQ_x_8 1, 2, 3, 2 - #define TQ_x_9 1, 2, 4, 2 - #define TQ_x_10 1, 3, 4, 2 - #define TQ_x_12 1, 3, 6, 2 - #define TQ_x_14 1, 4, 7, 2 - #define TQ_x_15 1, 4, 8, 2 /* good timing */ - #define TQ_x_16 1, 5, 8, 2 /* good timing */ - #define TQ_x_17 1, 6, 8, 2 /* good timing */ - #define TQ_x_18 1, 7, 8, 2 /* good timing */ - #define TQ_x_19 1, 8, 8, 2 /* good timing */ - #define TQ_x_20 1, 8, 8, 3 /* good timing */ - #define TQ_x_21 1, 8, 8, 4 - #define TQ_x_25 1, 8, 8, 8 - - #if CO_FCY == 1000 - #define CO_CANbitRateDataInitializers \ - {4, 10, TQ_x_20}, /*CAN=10kbps*/ \ - {4, 5, TQ_x_20}, /*CAN=20kbps*/ \ - {4, 2, TQ_x_20}, /*CAN=50kbps*/ \ - {4, 1, TQ_x_16}, /*CAN=125kbps*/ \ - {4, 1, TQ_x_8 }, /*CAN=250kbps*/ \ - {4, 1, TQ_x_4 }, /*CAN=500kbps*/ \ - {4, 1, TQ_x_4 }, /*Not possible*/ \ - {4, 1, TQ_x_4 } /*Not possible*/ - #elif CO_FCY == 1500 - #define CO_CANbitRateDataInitializers \ - {4, 15, TQ_x_20}, /*CAN=10kbps*/ \ - {4, 10, TQ_x_15}, /*CAN=20kbps*/ \ - {4, 4, TQ_x_15}, /*CAN=50kbps*/ \ - {4, 2, TQ_x_12}, /*CAN=125kbps*/ \ - {4, 1, TQ_x_12}, /*CAN=250kbps*/ \ - {4, 1, TQ_x_6 }, /*CAN=500kbps*/ \ - {4, 1, TQ_x_6 }, /*Not possible*/ \ - {4, 1, TQ_x_6 } /*Not possible*/ - #elif CO_FCY == 1843 /* internal FRC, no PLL */ - #define CO_CANbitRateDataInitializers \ - {4, 23, TQ_x_16}, /*CAN=10kbps*/ \ - {4, 23, TQ_x_8 }, /*CAN=20kbps*/ \ - {4, 23, TQ_x_8 }, /*Not possible*/ \ - {4, 23, TQ_x_8 }, /*Not possible*/ \ - {4, 23, TQ_x_8 }, /*Not possible*/ \ - {4, 23, TQ_x_8 }, /*Not possible*/ \ - {4, 23, TQ_x_8 }, /*Not possible*/ \ - {4, 23, TQ_x_8 } /*Not possible*/ - #elif CO_FCY == 2000 - #define CO_CANbitRateDataInitializers \ - {4, 25, TQ_x_16}, /*CAN=10kbps*/ \ - {4, 10, TQ_x_20}, /*CAN=20kbps*/ \ - {4, 5, TQ_x_16}, /*CAN=50kbps*/ \ - {4, 2, TQ_x_16}, /*CAN=125kbps*/ \ - {4, 1, TQ_x_16}, /*CAN=250kbps*/ \ - {4, 1, TQ_x_8 }, /*CAN=500kbps*/ \ - {4, 1, TQ_x_5 }, /*CAN=800kbps*/ \ - {4, 1, TQ_x_4 } /*CAN=1000kbps*/ - #elif CO_FCY == 2500 - #define CO_CANbitRateDataInitializers \ - {4, 25, TQ_x_20}, /*CAN=10kbps*/ \ - {4, 10, TQ_x_25}, /*CAN=20kbps*/ \ - {4, 5, TQ_x_20}, /*CAN=50kbps*/ \ - {4, 2, TQ_x_20}, /*CAN=125kbps*/ \ - {4, 1, TQ_x_20}, /*CAN=250kbps*/ \ - {4, 1, TQ_x_10}, /*CAN=500kbps*/ \ - {4, 1, TQ_x_10}, /*Not possible*/ \ - {4, 1, TQ_x_5 } /*CAN=1000kbps*/ - #elif CO_FCY == 3000 - #define CO_CANbitRateDataInitializers \ - {4, 40, TQ_x_15}, /*CAN=10kbps*/ \ - {4, 20, TQ_x_15}, /*CAN=20kbps*/ \ - {4, 8, TQ_x_15}, /*CAN=50kbps*/ \ - {4, 3, TQ_x_16}, /*CAN=125kbps*/ \ - {4, 2, TQ_x_12}, /*CAN=250kbps*/ \ - {4, 1, TQ_x_12}, /*CAN=500kbps*/ \ - {4, 1, TQ_x_12}, /*Not possible*/ \ - {4, 1, TQ_x_6 } /*CAN=1000kbps*/ - #elif CO_FCY == 4000 - #define CO_CANbitRateDataInitializers \ - {4, 50, TQ_x_16}, /*CAN=10kbps*/ \ - {4, 25, TQ_x_16}, /*CAN=20kbps*/ \ - {4, 10, TQ_x_16}, /*CAN=50kbps*/ \ - {4, 4, TQ_x_16}, /*CAN=125kbps*/ \ - {4, 2, TQ_x_16}, /*CAN=250kbps*/ \ - {4, 1, TQ_x_16}, /*CAN=500kbps*/ \ - {4, 1, TQ_x_10}, /*CAN=800kbps*/ \ - {4, 1, TQ_x_8 } /*CAN=1000kbps*/ - #elif CO_FCY == 5000 - #define CO_CANbitRateDataInitializers \ - {4, 50, TQ_x_20}, /*CAN=10kbps*/ \ - {4, 25, TQ_x_20}, /*CAN=20kbps*/ \ - {4, 10, TQ_x_20}, /*CAN=50kbps*/ \ - {4, 5, TQ_x_16}, /*CAN=125kbps*/ \ - {4, 2, TQ_x_20}, /*CAN=250kbps*/ \ - {4, 1, TQ_x_20}, /*CAN=500kbps*/ \ - {4, 1, TQ_x_20}, /*Not possible*/ \ - {4, 1, TQ_x_10} /*CAN=1000kbps*/ - #elif CO_FCY == 6000 - #define CO_CANbitRateDataInitializers \ - {4, 63, TQ_x_19}, /*CAN=10kbps*/ \ - {4, 40, TQ_x_15}, /*CAN=20kbps*/ \ - {4, 15, TQ_x_16}, /*CAN=50kbps*/ \ - {4, 6, TQ_x_16}, /*CAN=125kbps*/ \ - {4, 3, TQ_x_16}, /*CAN=250kbps*/ \ - {4, 2, TQ_x_12}, /*CAN=500kbps*/ \ - {4, 1, TQ_x_15}, /*CAN=800kbps*/ \ - {4, 1, TQ_x_12} /*CAN=1000kbps*/ - #elif CO_FCY == 7372 /* internal FRC + 4*PLL */ - #define CO_CANbitRateDataInitializers \ - {1, 23, TQ_x_16}, /*CAN=10kbps*/ \ - {4, 46, TQ_x_16}, /*CAN=20kbps*/ \ - {4, 14, TQ_x_21}, /*CAN=50kbps*/ \ - {4, 13, TQ_x_9 }, /*CAN=125kbps*/ \ - {4, 13, TQ_x_9 }, /*Not possible*/ \ - {4, 13, TQ_x_9 }, /*Not possible*/ \ - {4, 13, TQ_x_9 }, /*Not possible*/ \ - {4, 13, TQ_x_9 } /*Not possible*/ - #elif CO_FCY == 8000 - #define CO_CANbitRateDataInitializers \ - {1, 25, TQ_x_16}, /*CAN=10kbps*/ \ - {1, 10, TQ_x_20}, /*CAN=20kbps*/ \ - {1, 5, TQ_x_16}, /*CAN=50kbps*/ \ - {1, 2, TQ_x_16}, /*CAN=125kbps*/ \ - {1, 1, TQ_x_16}, /*CAN=250kbps*/ \ - {1, 1, TQ_x_8 }, /*CAN=500kbps*/ \ - {1, 1, TQ_x_5 }, /*CAN=800kbps*/ \ - {1, 1, TQ_x_4 } /*CAN=1000kbps*/ - #elif CO_FCY == 10000 - #define CO_CANbitRateDataInitializers \ - {1, 25, TQ_x_20}, /*CAN=10kbps*/ \ - {1, 10, TQ_x_25}, /*CAN=20kbps*/ \ - {1, 5, TQ_x_20}, /*CAN=50kbps*/ \ - {1, 2, TQ_x_20}, /*CAN=125kbps*/ \ - {1, 1, TQ_x_20}, /*CAN=250kbps*/ \ - {1, 1, TQ_x_10}, /*CAN=500kbps*/ \ - {1, 1, TQ_x_10}, /*Not possible*/ \ - {1, 1, TQ_x_5 } /*CAN=1000kbps*/ - #elif CO_FCY == 12000 - #define CO_CANbitRateDataInitializers \ - {1, 40, TQ_x_15}, /*CAN=10kbps*/ \ - {1, 20, TQ_x_15}, /*CAN=20kbps*/ \ - {1, 8, TQ_x_15}, /*CAN=50kbps*/ \ - {1, 3, TQ_x_16}, /*CAN=125kbps*/ \ - {1, 2, TQ_x_12}, /*CAN=250kbps*/ \ - {1, 1, TQ_x_12}, /*CAN=500kbps*/ \ - {1, 1, TQ_x_12}, /*Not possible*/ \ - {1, 1, TQ_x_6 } /*CAN=1000kbps*/ - #elif CO_FCY == 16000 - #define CO_CANbitRateDataInitializers \ - {1, 50, TQ_x_16}, /*CAN=10kbps*/ \ - {1, 25, TQ_x_16}, /*CAN=20kbps*/ \ - {1, 10, TQ_x_16}, /*CAN=50kbps*/ \ - {1, 4, TQ_x_16}, /*CAN=125kbps*/ \ - {1, 2, TQ_x_16}, /*CAN=250kbps*/ \ - {1, 1, TQ_x_16}, /*CAN=500kbps*/ \ - {1, 1, TQ_x_10}, /*CAN=800kbps*/ \ - {1, 1, TQ_x_8 } /*CAN=1000kbps*/ - #elif CO_FCY == 20000 - #define CO_CANbitRateDataInitializers \ - {1, 50, TQ_x_20}, /*CAN=10kbps*/ \ - {1, 25, TQ_x_20}, /*CAN=20kbps*/ \ - {1, 10, TQ_x_20}, /*CAN=50kbps*/ \ - {1, 5, TQ_x_16}, /*CAN=125kbps*/ \ - {1, 2, TQ_x_20}, /*CAN=250kbps*/ \ - {1, 1, TQ_x_20}, /*CAN=500kbps*/ \ - {1, 1, TQ_x_20}, /*Not possible*/ \ - {1, 1, TQ_x_10} /*CAN=1000kbps*/ - #elif CO_FCY == 24000 - #define CO_CANbitRateDataInitializers \ - {1, 63, TQ_x_19}, /*CAN=10kbps*/ \ - {1, 40, TQ_x_15}, /*CAN=20kbps*/ \ - {1, 15, TQ_x_16}, /*CAN=50kbps*/ \ - {1, 6, TQ_x_16}, /*CAN=125kbps*/ \ - {1, 3, TQ_x_16}, /*CAN=250kbps*/ \ - {1, 2, TQ_x_12}, /*CAN=500kbps*/ \ - {1, 1, TQ_x_15}, /*CAN=800kbps*/ \ - {1, 1, TQ_x_12} /*CAN=1000kbps*/ - #elif CO_FCY == 28000 - #define CO_CANbitRateDataInitializers \ - {1, 56, TQ_x_25}, /*CAN=10kbps*/ \ - {1, 35, TQ_x_20}, /*CAN=20kbps*/ \ - {1, 14, TQ_x_20}, /*CAN=50kbps*/ \ - {1, 7, TQ_x_16}, /*CAN=125kbps*/ \ - {1, 4, TQ_x_14}, /*CAN=250kbps*/ \ - {1, 2, TQ_x_14}, /*CAN=500kbps*/ \ - {1, 2, TQ_x_14}, /*Not possible*/ \ - {1, 1, TQ_x_14} /*CAN=1000kbps*/ - #elif CO_FCY == 30000 - #define CO_CANbitRateDataInitializers \ - {1, 60, TQ_x_25}, /*CAN=10kbps*/ \ - {1, 50, TQ_x_15}, /*CAN=20kbps*/ \ - {1, 20, TQ_x_15}, /*CAN=50kbps*/ \ - {1, 8, TQ_x_15}, /*CAN=125kbps*/ \ - {1, 4, TQ_x_15}, /*CAN=250kbps*/ \ - {1, 2, TQ_x_15}, /*CAN=500kbps*/ \ - {1, 2, TQ_x_15}, /*Not possible*/ \ - {1, 1, TQ_x_15} /*CAN=1000kbps*/ - #else - #error define_CO_FCY CO_FCY not supported - #endif -#endif - - -/* Structure contains timing coefficients for CAN module. - * - * CAN baud rate is calculated from following equations: - * FCAN = FCY * Scale - Input frequency to CAN module (MAX 30MHz for dsPIC30F) - * TQ = 2 * BRP / FCAN - Time Quanta - * BaudRate = 1 / (TQ * K) - Can bus Baud Rate - * K = SJW + PROP + PhSeg1 + PhSeg2 - Number of Time Quantas - */ -typedef struct{ - uint8_t scale; /* (1 or 4) Scales FCY clock - dsPIC30F specific */ - uint8_t BRP; /* (1...64) Baud Rate Prescaler */ - uint8_t SJW; /* (1...4) SJW time */ - uint8_t PROP; /* (1...8) PROP time */ - uint8_t phSeg1; /* (1...8) Phase Segment 1 time */ - uint8_t phSeg2; /* (1...8) Phase Segment 2 time */ -}CO_CANbitRateData_t; - - -/* CAN receive message structure as aligned in CAN module. */ -typedef struct{ - uint16_t ident; /* Standard Identifier as aligned in CAN module. 16 bits: - 'UUUSSSSS SSSSSSRE' (U: unused; S: SID; R=SRR; E=IDE). */ - uint16_t extIdent; /* Extended identifier, not used here */ - uint16_t DLC :4; /* Data length code (bits 0...3) */ - uint16_t DLCrest :12; /* Not used here (bits 4..15) */ - uint8_t data[8]; /* 8 data bytes */ - uint16_t CON; /* Control word */ -}CO_CANrxMsg_t; - - -/* Received message object */ -typedef struct{ - uint16_t ident; - uint16_t mask; - void *object; - void (*pFunct)(void *object, const CO_CANrxMsg_t *message); -}CO_CANrx_t; - - -/* Transmit message object. */ -typedef struct{ - uint16_t ident; /* Standard Identifier as aligned in CAN module. 16 bits: - 'SSSSSUUU SSSSSSRE' (U: unused; S: SID; R=SRR; E=IDE). */ - uint8_t DLC; - uint8_t data[8]; - volatile bool_t bufferFull; - volatile bool_t syncFlag; -}CO_CANtx_t; - - -/* CAN module object. */ -typedef struct{ - void *CANdriverState; - CO_CANrx_t *rxArray; - uint16_t rxSize; - CO_CANtx_t *txArray; - uint16_t txSize; - volatile bool_t CANnormal; - volatile bool_t useCANrxFilters; - volatile bool_t bufferInhibitFlag; - volatile bool_t firstCANtxMessage; - volatile uint16_t CANtxCount; - uint32_t errOld; - void *em; -}CO_CANmodule_t; - - -/* CAN interrupt receives and transmits CAN messages. - * - * Function must be called directly from _C1Interrupt or _C2Interrupt with - * high priority. dsPIC30F uses two receive buffers and one transmit buffer. - */ -void CO_CANinterrupt(CO_CANmodule_t *CANmodule); - - -#endif /* CO_DRIVER_TARGET_H */ diff --git a/stack/eCos/CO_Flash.c b/stack/eCos/CO_Flash.c deleted file mode 100644 index 91e1a4e9..00000000 --- a/stack/eCos/CO_Flash.c +++ /dev/null @@ -1,335 +0,0 @@ -/** - * eCos flash support for CANopen stack - * - * @file CO_Flash.c - * @author Uwe Kindler - * @copyright 2013 Uwe Kindler - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * CANopenNode is free and open source software: you can redistribute - * it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Following clarification and special exception to the GNU General Public - * License is included to the distribution terms of CANopenNode: - * - * Linking this library statically or dynamically with other modules is - * making a combined work based on this library. Thus, the terms and - * conditions of the GNU General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this library give - * you permission to link this library with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also meet, - * for each linked independent module, the terms and conditions of the - * license of that module. An independent module is a module which is - * not derived from or based on this library. If you modify this - * library, you may extend this exception to your version of the - * library, but you are not obliged to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - - -//============================================================================ -// INCLUDES -//============================================================================ -#include -#include - -#include "CANopen.h" - - -//============================================================================ -// DEFINES -//============================================================================ -#define CHECK_FLASH_RESULT(_result_) \ - if ((_result_) != CYG_FLASH_ERR_OK) { \ - CO_DBG_PRINT("Flash operation error: %s", cyg_flash_errmsg(_result_)); } -#define PARAM_STORE_PASSWORD 0x65766173 -#define PARAM_RESTORE_PASSWORD 0x64616F6C - - -//============================================================================ -// LOCAL DATA -//============================================================================ -static cyg_flash_info_t flash_info; -static const int CYGNUM_CANOPEN_FLASH_DATA_BLOCK = -3; -static cyg_flashaddr_t CO_OD_Flash_Adress; -static cyg_flashaddr_t CO_OD_Flash_Default_Param; -extern struct sCO_OD_ROM CO_OD_ROM; - - -enum CO_OD_H1010_StoreParam_Sub -{ - OD_H1010_STORE_PARAM_COUNT, - OD_H1010_STORE_PARAM_ALL, - OD_H1010_STORE_PARAM_COMM, - OD_H1010_STORE_PARAM_APP, - OD_H1010_STORE_PARAM_MANUFACTURER, - OD_H1010_STORE_PARAM_RESERVED = 0x80 -}; - -enum CO_OD_H1011_RestoreDefaultParam_Sub -{ - OD_H1011_RESTORE_PARAM_COUNT, - OD_H1011_RESTORE_PARAM_ALL, - OD_H1011_RESTORE_PARAM_COMM, - OD_H1011_RESTORE_PARAM_APP, - OD_H1011_RESTORE_PARAM_MANUFACTURER, - OD_H1011_RESTORE_PARAM_RESERVED = 0x80 -}; - - -enum CO_StorageFunctionality_Flags -{ - SAVES_PARAM_ON_COMMAND = 0x01, - SAVES_PARAM_AUTONOMOUSLY = 0x02 -}; - -enum CO_RestoreFunctionality_Flags -{ - RESTORES_PARAMETERS = 0x01 -}; - - -//============================================================================ -/** - * Store parameters of object dictionary into flash memory. - * \param[in] FlashAddress Use CO_OD_Flash_Adress for the normal parameter - * block and CO_OD_Flash_Default_Param for the - * default parameters - */ -static CO_SDO_abortCode_t storeParameters(cyg_flashaddr_t FlashAddress, - uint8_t ParametersSub) -{ - CO_DBG_PRINT("Store parameters\n"); - - cyg_flashaddr_t ErrorAddress; - - //unlock flash -#ifdef CYGHWR_IO_FLASH_BLOCK_LOCKING - int Result = cyg_flash_unlock(FlashAddress, sizeof(struct sCO_OD_ROM), - &ErrorAddress); - CHECK_FLASH_RESULT(Result); - if (CYG_FLASH_ERR_OK != Result) - { - return CO_SDO_AB_HW; - } -#endif - - // erase block - Result = cyg_flash_erase(FlashAddress, sizeof(struct sCO_OD_ROM), - &ErrorAddress); - CHECK_FLASH_RESULT(Result); - if (CYG_FLASH_ERR_OK != Result) - { - return CO_SDO_AB_HW; - } - - // program data into flash - Result = cyg_flash_program(FlashAddress, &CO_OD_ROM, - sizeof(CO_OD_ROM), &ErrorAddress); - CHECK_FLASH_RESULT(Result); - if (CYG_FLASH_ERR_OK != Result) - { - return CO_SDO_AB_HW; - } - - return CO_SDO_AB_NONE; -} - - -//============================================================================ -/** - * Restore parameters of object dictionary from flash memory. - * \param[in] FlashAddress Use CO_OD_Flash_Adress for the normal parameter - * block and CO_OD_Flash_Default_Param for the - * default parameters - */ -static CO_SDO_abortCode_t restoreParameters(cyg_flashaddr_t FlashAddress, - uint8_t ParametersSub) -{ - CO_DBG_PRINT("Restore parameters\n"); - cyg_flashaddr_t ErrorAddress; - int Result = cyg_flash_read(FlashAddress, &CO_OD_ROM, - sizeof(CO_OD_ROM), &ErrorAddress); - CHECK_FLASH_RESULT(Result); - if (CYG_FLASH_ERR_OK != Result) - { - return CO_SDO_AB_HW; - } - return CO_SDO_AB_NONE; -} - - -//============================================================================ -/** - * Access to object dictionary OD_H1010_STORE_PARAM_FUNC - */ -static CO_SDO_abortCode_t CO_ODF_1010_StoreParam(CO_ODF_arg_t *ODF_arg) -{ - CO_DBG_PRINT("CO_ODF_1010 Sub: %d\n", ODF_arg->subIndex); - CO_DBG_PRINT("sizeof(sCO_OD_ROM): %d", sizeof(CO_OD_ROM)); - uint32_t* value = (uint32_t*)ODF_arg->data; - if (ODF_arg->reading) - { - if(OD_H1010_STORE_PARAM_ALL == ODF_arg->subIndex) - { - *value = SAVES_PARAM_ON_COMMAND; - } - return CO_SDO_AB_NONE; - } - - if(OD_H1010_STORE_PARAM_ALL != ODF_arg->subIndex) - { - return CO_SDO_AB_NONE; - } - - if (*value != PARAM_STORE_PASSWORD) - { - return CO_SDO_AB_DATA_TRANSF; - } - - return storeParameters(CO_OD_Flash_Adress, ODF_arg->subIndex); -} - -//============================================================================ -/** - * Access to object dictionary OD_H1010_STORE_PARAM_FUNC - */ -static CO_SDO_abortCode_t CO_ODF_1011_RestoreParam(CO_ODF_arg_t *ODF_arg) -{ - CO_DBG_PRINT("CO_ODF_1010 Sub: %d\n", ODF_arg->subIndex); - CO_DBG_PRINT("sizeof(sCO_OD_ROM): %d", sizeof(CO_OD_ROM)); - uint32_t* value = (uint32_t*)ODF_arg->data; - if (ODF_arg->reading) - { - if (OD_H1011_RESTORE_PARAM_ALL == ODF_arg->subIndex) - { - *value = RESTORES_PARAMETERS; - } - return CO_SDO_AB_NONE; - } - - if (OD_H1011_RESTORE_PARAM_ALL != ODF_arg->subIndex) - { - return CO_SDO_AB_NONE; - } - - if (*value != PARAM_RESTORE_PASSWORD) - { - return CO_SDO_AB_DATA_TRANSF; - } - - CO_SDO_abortCode_t Result = restoreParameters(CO_OD_Flash_Default_Param, - ODF_arg->subIndex); - if (Result != CO_SDO_AB_NONE) - { - CO_DBG_PRINT("restoreParameters returned error"); - return Result; - } - - return storeParameters(CO_OD_Flash_Adress, OD_H1011_RESTORE_PARAM_ALL); -} - - -//=========================================================================== -/** - * Initialize flash library and data storage in flash - * We use two blocks in flash for data storage. One block is used for the - * default data that will be restored. The default parameters are stored - * at address CO_OD_Flash_Default_Param. The data that will be loaded at - * startup or saved if user modifies data is store at CO_OD_Flash_Adress. - */ -void CO_FlashInit(void) -{ - int i = 0; - // - // Initialize flash library - // - int Result = cyg_flash_init(0); - CHECK_FLASH_RESULT(Result); -#ifdef CYGDBG_IO_CANOPEN_DEBUG - cyg_flash_set_global_printf(diag_printf); -#endif - - // - // Read info about flash device (number of blocks. block size) - // - Result = cyg_flash_get_info(0, &flash_info); - CHECK_FLASH_RESULT(Result); - CO_DBG_PRINT("Flash info dev %d: 0x%x - 0x%x, %d blocks\n", 0, flash_info.start, - flash_info.end, flash_info.num_block_infos); - const cyg_flash_block_info_t* block_info = flash_info.block_info; - for (i = 0; i < flash_info.num_block_infos; ++i) - { - CO_DBG_PRINT("Block %d: block size: %d blocks: %d\n", i, - block_info->block_size, block_info->blocks); - block_info++; - } - - // - // Calculate addresses for flash data and default flash data - // - block_info = &flash_info.block_info[flash_info.num_block_infos - 1]; - CO_DBG_PRINT("Last block - block size: %d blocks: %d\n", - block_info->block_size, block_info->blocks); - CO_OD_Flash_Adress = flash_info.end + 1 + - (CYGNUM_CANOPEN_FLASH_DATA_BLOCK * block_info->block_size); - CO_DBG_PRINT("CO_OD_Flash_Adress 0x%8x\n", CO_OD_Flash_Adress); - CO_OD_Flash_Default_Param = CO_OD_Flash_Adress - block_info->block_size; - CO_DBG_PRINT("CO_OD_Flash_Default_Param 0x%8x\n", CO_OD_Flash_Default_Param); - - // - // Before we can access the data, we need to make sure, that the flash - // block are properly initialized. We do this by reading the block into - // a local sCO_OD_ROM variable and verifying the FirstWord and LastWord - // members - // - cyg_flashaddr_t ErrorAddress; - struct sCO_OD_ROM DefaultObjDicParam; - Result = cyg_flash_read(CO_OD_Flash_Default_Param, &DefaultObjDicParam, - sizeof(DefaultObjDicParam), &ErrorAddress); - CHECK_FLASH_RESULT(Result); - - // - // If the default parameters are not present in flash, then we know that - // we need to create them for later restore - // - if ((DefaultObjDicParam.FirstWord != CO_OD_FIRST_LAST_WORD) - ||(DefaultObjDicParam.LastWord != CO_OD_FIRST_LAST_WORD)) - { - storeParameters(CO_OD_Flash_Adress, OD_H1010_STORE_PARAM_ALL); - storeParameters(CO_OD_Flash_Default_Param, OD_H1010_STORE_PARAM_ALL); - } - else - { - restoreParameters(CO_OD_Flash_Adress, OD_H1010_STORE_PARAM_ALL); - } -} - - -//=========================================================================== -void CO_FlashRegisterODFunctions(CO_t* CO) -{ - CO_OD_configure(CO->SDO, OD_H1010_STORE_PARAM_FUNC, - CO_ODF_1010_StoreParam, (void*)0, 0, 0); - CO_OD_configure(CO->SDO, OD_H1011_REST_PARAM_FUNC, - CO_ODF_1011_RestoreParam, (void*)0, 0, 0); -} - -//--------------------------------------------------------------------------- -// EOF CO_flash.c diff --git a/stack/eCos/CO_Flash.h b/stack/eCos/CO_Flash.h deleted file mode 100644 index f5c514c6..00000000 --- a/stack/eCos/CO_Flash.h +++ /dev/null @@ -1,71 +0,0 @@ -#ifndef CO_FlashH -#define CO_FlashH -/** - * eCos flash support for CANopen stack - * - * @file CO_Flash.h - * @author Uwe Kindler - * @copyright 2013 Uwe Kindler - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * CANopenNode is free and open source software: you can redistribute - * it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Following clarification and special exception to the GNU General Public - * License is included to the distribution terms of CANopenNode: - * - * Linking this library statically or dynamically with other modules is - * making a combined work based on this library. Thus, the terms and - * conditions of the GNU General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this library give - * you permission to link this library with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also meet, - * for each linked independent module, the terms and conditions of the - * license of that module. An independent module is a module which is - * not derived from or based on this library. If you modify this - * library, you may extend this exception to your version of the - * library, but you are not obliged to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - - -//============================================================================ -// INCLUDES -//============================================================================ -#include "CANopen.h" - -/** - * Initialize flash library and data storage in flash - * We use two blocks in flash for data storage. One block is used for the - * default data that will be restored. The default parameters are stored - * at address CO_OD_Flash_Default_Param. The data that will be loaded at - * startup or saved if user modifies data. - */ -void CO_FlashInit(void); - -/** - * Register object dictionary functions for parameter storage and restoring - * parameters (Object dictionary index 0x1010 Store Param and 0x1011 Restore - * default param. - */ -void CO_FlashRegisterODFunctions(CO_t* CO); - - -//--------------------------------------------------------------------------- -#endif // CO_FlashH diff --git a/stack/eCos/CO_PollingTimer.c b/stack/eCos/CO_PollingTimer.c deleted file mode 100644 index 8e62ee55..00000000 --- a/stack/eCos/CO_PollingTimer.c +++ /dev/null @@ -1,88 +0,0 @@ -//=========================================================================== -/// \file CO_PollingTimer.c -/// \author Uwe Kindler (UK) -/// \date 2013/08/28 -/// \brief Implementation of simple polling timer -//=========================================================================== - - -//=========================================================================== -// INCLUDES -//=========================================================================== -#include -#include "CO_PollingTimer.h" -#include "ecos_helper.h" - - - -//=========================================================================== -cyg_uint64 CO_TmrGetMilliSec(void) -{ - return convertTicksToMs(cyg_current_time()); -} - - -//=========================================================================== -cyg_uint64 CO_TmrGetElapsedMsecs(cyg_uint64 LastTimeStamp) -{ - return CO_TmrGetMilliSec() - LastTimeStamp; -} - - -//=========================================================================== -bool CO_TmrIsExpired(cyg_uint64 LastTimeStamp) -{ - cyg_uint64 dwTimeNow = CO_TmrGetMilliSec(); - cyg_uint64 dwTimestamp = (LastTimeStamp + 1); - - if (dwTimeNow > dwTimestamp) - { - if ((dwTimeNow - dwTimestamp) < 0x8000000000000000) - { - return true; - } - else - { - return false; - } - } - else // if (dwTimeNow <= dwTimestamp) - { - if ((dwTimestamp - dwTimeNow) > 0x8000000000000000) - { - return true; - } - else - { - return false; - } - } // if (time_now <= timestamp) -} - - -//=========================================================================== -cyg_uint64 CO_TmrStartFrom(cyg_uint64 dwStartTime, cyg_uint64 dwPeriod) -{ - cyg_uint64 TimeStamp = dwStartTime + dwPeriod; - // if resulting time stamp is in the past, then we fix the time stamp value - // and move it to now - if (TimeStamp < CO_TmrGetMilliSec()) - { - return CO_TmrGetMilliSec(); - } - else - { - return TimeStamp; - } -} - -//=========================================================================== -cyg_uint64 CO_TmrStartFromNow(cyg_uint64 dwPeriod) -{ - return CO_TmrGetMilliSec() + dwPeriod; -} - - -//---------------------------------------------------------------------------- -// end of CO_PollingTimer.c - diff --git a/stack/eCos/CO_PollingTimer.h b/stack/eCos/CO_PollingTimer.h deleted file mode 100644 index 87af5e1b..00000000 --- a/stack/eCos/CO_PollingTimer.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef CO_PollingTimerH -#define CO_PollingTimerH -//=========================================================================== -/// \file PollingTimer.h -/// \author Uwe Kindler (UK) -/// \date 2013/08/28 -/// \brief Declaration of simple polling timer class. -//=========================================================================== - - -//=========================================================================== -// INCLUDES -//=========================================================================== -#include - - -/// -/// Query actual time in milliseconds. -/// \return Actual time in milliseconds since system start -/// An implementation for a certain platform needs to implement this -/// static function -/// -cyg_uint64 CO_TmrGetMilliSec(void); - -/** - * Returns the expired time since given timestamp - * \return getMilliSec() - LastTimeStamp - */ -cyg_uint64 CO_TmrGetElapsedMsecs(cyg_uint64 LastTimeStamp); - -/// -/// Returns true if timer is expired -/// -bool CO_TmrIsExpired(cyg_uint64 LastTimeStamp); - - -/// -/// Set timer expiration time starting from given dwStartTime. -/// \param[in] dwStartTime Time in milliseconds when timer starts -/// \param[in] dwPeriod Timer period in milliseconds -/// -cyg_uint64 CO_TmrStartFrom(cyg_uint64 dwStartTime, cyg_uint64 dwPeriod); - -/// -/// Set timer expiration time starting from now. -/// Function sets a new expiration time. Expiration time is calculated from -/// actual time + timer period. -/// \param[in] dwPeriod Timer period in milliseconds -/// -cyg_uint64 CO_TmrStartFromNow(cyg_uint64 dwPeriod); - - -//---------------------------------------------------------------------------- -#endif // CO_PollingTimerH diff --git a/stack/eCos/CO_driver.c b/stack/eCos/CO_driver.c deleted file mode 100644 index c883c4e0..00000000 --- a/stack/eCos/CO_driver.c +++ /dev/null @@ -1,886 +0,0 @@ -/* - * CAN module object for eCos CAN framework - * - * @file CO_driver_eCos.c - * @ingroup CO_driver - * @author Uwe Kindler - * @copyright 2013 Uwe Kindler - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * CANopenNode is free and open source software: you can redistribute - * it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Following clarification and special exception to the GNU General Public - * License is included to the distribution terms of CANopenNode: - * - * Linking this library statically or dynamically with other modules is - * making a combined work based on this library. Thus, the terms and - * conditions of the GNU General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this library give - * you permission to link this library with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also meet, - * for each linked independent module, the terms and conditions of the - * license of that module. An independent module is a module which is - * not derived from or based on this library. If you modify this - * library, you may extend this exception to your version of the - * library, but you are not obliged to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - - -//=========================================================================== -// INCLUDES -//=========================================================================== -#include "CO_driver.h" -#include "CO_Emergency.h" -#include "ecos_helper.h" - -#include -#include -#include -#include -#include -#include - -// Package option requirements -#if defined(CYGFUN_KERNEL_API_C) -#if defined(CYGPKG_IO_CAN_DEVICES) -#if defined(CYGOPT_IO_CAN_RUNTIME_MBOX_CFG) -#if defined(CYGOPT_IO_CAN_STD_CAN_ID) -#if defined(CYGOPT_IO_CAN_SUPPORT_NONBLOCKING) -#if defined(CYGOPT_IO_CAN_SUPPORT_TIMEOUTS) - - -//=========================================================================== -// DEFINES -//=========================================================================== -#define UNUSED_ENTRY 0xFFFF // indicates unused entry in rx buffer array - - -//=========================================================================== -// DATA TYPES -//=========================================================================== -/** - * Stores thread data of a single thread object - */ -typedef struct st_thread_data -{ - cyg_thread obj; - long stack[CYGNUM_HAL_STACK_SIZE_TYPICAL]; - cyg_handle_t hdl; -} thread_data_t; - - -//=========================================================================== -// LOCAL DATA -//=========================================================================== -static cyg_thread_entry_t can_rx_thread;///< thread entry for RX thread -static thread_data_t can_rx_thread_data = { - hdl : 0 -}; -CO_CANmodule_t* can_module = 0;///< local CAN module object - if it is 0 then the module is not initialized - - -//=========================================================================== -/** - * This is a thin wrapper around CO_errorReport to enable some diagnostic output - */ -void CO_eCos_errorReport(CO_EM_t *em, const uint8_t errorBit, - const uint16_t errorCode, const uint32_t infoCode) -{ - CO_DBG_PRINT("CO_eCos_errorReport: errorBit %x errorCode %x infoCode %x\n", - errorBit, errorCode, infoCode); - CO_errorReport(em, errorBit, errorCode, infoCode); -} - - - -//=========================================================================== -/** - * This is a thin wrapper around CO_errorReset to enable some diagnostic output - */ -void CO_eCos_errorReset(CO_EM_t *em, const uint8_t errorBit, const uint32_t infoCode) -{ - CO_DBG_PRINT("CO_errorReset: errorBit %x infoCode %x\n", errorBit, infoCode); - CO_errorReset(em, errorBit, infoCode); -} - - -//============================================================================= -/** - * Call this function if you have a error return code != ENOERR. This function - * reports the error as an emergency message and prints an optional debug - * message - * \param[in] ErrCode Error code returned by eCos CAN function - * \param[in] CANmodule The CAN module object that caused the error - * \param[in] DebugMessage Optional debug message. If this is not 0, then a - * diagnostic message is printed with CO_DBG_PRINT - */ -static void reportErrorReturnCode(Cyg_ErrNo ErrCode, CO_CANmodule_t *CANmodule, - const char* DebugMessage) -{ - CO_EM_t* EM = (CO_EM_t*)CANmodule->em; - if (ENOERR == ErrCode) - { - return; - } - - CO_errorReport(EM, CO_EM_GENERIC_SOFTWARE_ERROR, - CO_EMC_SOFTWARE_DEVICE, ErrCode); - - if (DebugMessage) - { - CO_DBG_PRINT(DebugMessage, ErrCode); - } -} - - -//============================================================================= -/** - * Set mode of CAN controller (configuration, active...) - * This function properly handles errors when setting mode - */ -static void setCAN_Mode(cyg_can_mode mode, void *CANdriverState) -{ - if (!can_module) - { - return; - } - - cyg_uint32 len = sizeof(mode); - Cyg_ErrNo Result = cyg_io_set_config(can_module->ioHandle, - CYG_IO_SET_CONFIG_CAN_MODE ,&mode, &len); - if (ENOERR != Result) - { - reportErrorReturnCode(Result, can_module, 0); - CO_DBG_PRINT("Set CAN mode %d returned error %x\n", - mode, Result); - } -} - - -//============================================================================= -void CO_CANsetConfigurationMode(void *CANdriverState) -{ - setCAN_Mode(CYGNUM_CAN_MODE_CONFIG, CANdriverState); -} - - -//============================================================================= -void CO_CANsetNormalMode(CO_CANmodule_t *CANmodule) -{ - setCAN_Mode(CYGNUM_CAN_MODE_START, CANmodule->CANdriverState); - - CANmodule->CANnormal = true; -} - - -//============================================================================= -/** - * Translates CANopen node bitrate into eCos bitrate identifier - */ -cyg_can_baud_rate_t translateBaudRate(uint16_t CANbitRate) -{ - switch (CANbitRate) - { - case 10: return CYGNUM_CAN_KBAUD_10; - case 20: return CYGNUM_CAN_KBAUD_20; - case 50: return CYGNUM_CAN_KBAUD_50; - case 100: return CYGNUM_CAN_KBAUD_100; - case 125: return CYGNUM_CAN_KBAUD_125; - case 250: return CYGNUM_CAN_KBAUD_250; - case 500: return CYGNUM_CAN_KBAUD_500; - case 800: return CYGNUM_CAN_KBAUD_800; - case 1000: return CYGNUM_CAN_KBAUD_1000; - } - - return CYGNUM_CAN_KBAUD_1000; -} - - - -//=========================================================================== -/** - * Prints CAN event via diagnostic output channel - */ -static void print_can_msg(cyg_can_message *pmsg, char *pMsg) -{ - char *pmsg_str; - static char* msg_tbl[] = - { - "%s [ID:%03X] [RTR:%d] [EXT:%d] [DATA:]\n", - "%s [ID:%03X] [RTR:%d] [EXT:%d] [DATA:%02X]\n", - "%s [ID:%03X] [RTR:%d] [EXT:%d] [DATA:%02X %02X]\n", - "%s [ID:%03X] [RTR:%d] [EXT:%d] [DATA:%02X %02X %02X]\n", - "%s [ID:%03X] [RTR:%d] [EXT:%d] [DATA:%02X %02X %02X %02X]\n", - "%s [ID:%03X] [RTR:%d] [EXT:%d] [DATA:%02X %02X %02X %02X %02X]\n", - "%s [ID:%03X] [RTR:%d] [EXT:%d] [DATA:%02X %02X %02X %02X %02X %02X]\n", - "%s [ID:%03X] [RTR:%d] [EXT:%d] [DATA:%02X %02X %02X %02X %02X %02X %02X]\n", - "%s [ID:%03X] [RTR:%d] [EXT:%d] [DATA:%02X %02X %02X %02X %02X %02X %02X %02X]\n" - }; - - if (pmsg->rtr) - { - CO_DBG_PRINT("%s [ID:%03X] [RTR:%d] [EXT:%d] [DLC:%d]\n", - pMsg, - pmsg->id, - pmsg->rtr, - pmsg->ext, - pmsg->dlc); - - return; - } - - if (pmsg->dlc > 8) - { - pmsg_str = msg_tbl[8]; - } - else - { - pmsg_str = msg_tbl[pmsg->dlc]; - } - - CO_DBG_PRINT(pmsg_str, - pMsg, - pmsg->id, - pmsg->rtr, - pmsg->ext, - pmsg->data.bytes[0], - pmsg->data.bytes[1], - pmsg->data.bytes[2], - pmsg->data.bytes[3], - pmsg->data.bytes[4], - pmsg->data.bytes[5], - pmsg->data.bytes[6], - pmsg->data.bytes[7]); -} - - -//=========================================================================== -/** - * Prints CAN event flags via diagnostic output channel - */ -static void print_can_flags(cyg_uint16 flags, char *pMsg) -{ - char *pmsg_str; - cyg_uint8 i ; - static char* msg_tbl[] = - { - "RX ", - "TX ", - "WRX ", - "WTX ", - "ERRP ", - "BOFF ", - "OVRX ", - "OVTX ", - "CERR ", - "LSTY ", - "ESTY ", - "ALOS ", - "DEVC ", - "PHYF ", - "PHYH ", - "PHYL ", - "ERRA ", - "OVRXHW " - }; - - i = 0; - while (flags && (i < 16)) - { - if (flags & 0x0001) - { - pmsg_str = msg_tbl[i]; - CO_DBG_PRINT(pmsg_str); - } - flags >>=1; - i++; - } - - CO_DBG_PRINT("\n"); -} - - -//=========================================================================== -/** - * The receive thread the reads the messages from the eCos CAN driver and - * calls the receive buffer callback functions to process the received messages - */ -void can_rx_thread(cyg_addrword_t data) -{ - cyg_uint32 len; - cyg_can_event rx_event; - CO_CANmodule_t* CANmodule = (CO_CANmodule_t*)data; - CO_EM_t* EM = (CO_EM_t*)CANmodule->em; - Cyg_ErrNo Result; - CO_DBG_PRINT("can_rx_thread started\n"); - - while (1) - { - // - // First receive CAN event from real CAN hardware and report an error - // if something goes wrong - // - len = sizeof(rx_event); - Result = cyg_io_read(CANmodule->ioHandle, &rx_event, &len); - if (ENOERR != Result) - { - reportErrorReturnCode(Result, CANmodule, - "cyg_io_read() returned error %x\n"); - continue; - } - - print_can_flags(rx_event.flags, "Received event"); - // - // Check if we received an RX event - if we have an RX event then we - // copy the received message data and call the receive buffer callback - // function - // - if (rx_event.flags & CYGNUM_CAN_EVENT_RX) - { - uint16_t BufferIndex = CANmodule->rxBufferIndexArray[rx_event.msg.id]; - if (UNUSED_ENTRY != BufferIndex) - { - CO_CANrx_t *msgBuff = &CANmodule->rxArray[BufferIndex]; - CO_CANrxMsg_t rcvMsg; - rcvMsg.ID = rx_event.msg.id; - rcvMsg.DLC = rx_event.msg.dlc; - rcvMsg.RTR = rx_event.msg.rtr; - memcpy(rcvMsg.data, rx_event.msg.data.bytes, rx_event.msg.dlc); - print_can_msg(&rx_event.msg, "Rx: "); - msgBuff->pFunct(msgBuff->object, &rcvMsg); - } - } // if (rx_event.flags & CYGNUM_CAN_EVENT_RX) - - if (rx_event.flags & CYGNUM_CAN_EVENT_OVERRUN_RX) - { - CO_eCos_errorReport(EM, CO_EM_RXMSG_OVERFLOW, CO_EMC_CAN_OVERRUN, 0); - } - - if (rx_event.flags & CYGNUM_CAN_EVENT_OVERRUN_RX_HW) - { - CO_eCos_errorReport(EM, CO_EM_RXMSG_OVERFLOW, CO_EMC_CAN_OVERRUN, 0); - } - CO_DBG_PRINT("processing can_rx_thread\n"); - } // while (1) -} - - -//============================================================================ -Cyg_ErrNo canInit(CO_CANmodule_t* CANmodule, uint16_t CANbitRate) -{ - cyg_uint32 len; - cyg_can_info_t can_info; - cyg_can_msgbuf_cfg msgbox_cfg; - - // - // Get a valid device handle for CAN device 0 - // - Cyg_ErrNo Result = cyg_io_lookup("/dev/can0", &CANmodule->ioHandle); - if (ENOERR != Result) - { - reportErrorReturnCode(Result, CANmodule, - "cyg_io_lookup(/dev/can0) returned error: %x\n"); - return Result; - } - - // - // Set sending CAN messages to non blocking mode because it happens in - // main thread and main thread should never be blocked - // - cyg_uint32 blocking = 0; - len = sizeof(blocking); - Result = cyg_io_set_config(CANmodule->ioHandle, - CYG_IO_SET_CONFIG_WRITE_BLOCKING, &blocking, &len); - if (ENOERR != Result) - { - reportErrorReturnCode(Result, CANmodule, - "CYG_IO_SET_CONFIG_WRITE_BLOCKING %x\n"); - return Result; - } - - // - // Set receiving CAN events to blocking mode because it happens in - // dedicated receive thread - // - blocking = 1; - len = sizeof(blocking); - Result = cyg_io_set_config(CANmodule->ioHandle, - CYG_IO_SET_CONFIG_READ_BLOCKING, &blocking, &len); - if (ENOERR != Result) - { - reportErrorReturnCode(Result, CANmodule, - "CYG_IO_SET_CONFIG_READ_BLOCKING %x\n"); - return Result; - } - - // - // Set TX timeout to 0 because function shall return immediately if - // send queue is full - // - cyg_can_timeout_info_t timeouts; - timeouts.rx_timeout = convertMsToTicks(1000);// 1000 ms converted to clock ticks - timeouts.tx_timeout = 0; - len = sizeof(timeouts); - Result = cyg_io_set_config(CANmodule->ioHandle, - CYG_IO_SET_CONFIG_CAN_TIMEOUT, &timeouts, &len); - if (ENOERR != Result) - { - reportErrorReturnCode(Result, CANmodule, - "CYG_IO_SET_CONFIG_CAN_TIMEOUT %x\n"); - return Result; - } - - // - // Flush output - this is required in case of reset - // - Result = cyg_io_set_config(CANmodule->ioHandle, - CYG_IO_SET_CONFIG_CAN_OUTPUT_FLUSH, 0, 0); - if (ENOERR != Result) - { - reportErrorReturnCode(Result, CANmodule, - "CYG_IO_SET_CONFIG_CAN_OUTPUT_FLUSH returned error: %x\n"); - return Result; - } - - // - // Set baudrate - // - can_info.baud = translateBaudRate(CANbitRate); - len = sizeof(can_info); - Result = cyg_io_set_config(CANmodule->ioHandle, CYG_IO_SET_CONFIG_CAN_INFO, &can_info, &len); - if (ENOERR != Result) - { - reportErrorReturnCode(Result, CANmodule, - "Setting baudrate returned error: %x\n"); - return Result; - } - - // - // Now reset message buffer configuration - this is mandatory before starting - // message buffer runtime configuration - // - msgbox_cfg.cfg_id = CYGNUM_CAN_MSGBUF_RESET_ALL; - len = sizeof(msgbox_cfg); - Result = cyg_io_set_config(CANmodule->ioHandle, CYG_IO_SET_CONFIG_CAN_MSGBUF ,&msgbox_cfg, &len); - if (ENOERR != Result) - { - reportErrorReturnCode(Result, CANmodule, - "CYGNUM_CAN_MSGBUF_RESET_ALL returned error: %x\n"); - return Result; - } - - if (!can_rx_thread_data.hdl) - { - // - // create receive thread if it has not been created yet - // - cyg_thread_create(4, can_rx_thread, - (cyg_addrword_t)CANmodule, - "can_rx_thread", - (void *) can_rx_thread_data.stack, - CYGNUM_HAL_STACK_SIZE_TYPICAL * sizeof(long), - &can_rx_thread_data.hdl, - &can_rx_thread_data.obj); - - cyg_thread_resume(can_rx_thread_data.hdl); - } - CO_DBG_PRINT("CAN driver initialised\n"); - return ENOERR; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_CANmodule_init( - CO_CANmodule_t *CANmodule, - void *CANdriverState, - CO_CANrx_t rxArray[], - uint16_t rxSize, - CO_CANtx_t txArray[], - uint16_t txSize, - uint16_t CANbitRate) -{ - uint16_t i; - - /* verify arguments */ - if(CANmodule==NULL || rxArray==NULL || txArray==NULL){ - return CO_ERROR_ILLEGAL_ARGUMENT; - } - - /* Configure object variables */ - CANmodule->rxArray = rxArray; - CANmodule->rxSize = rxSize; - CANmodule->txArray = txArray; - CANmodule->txSize = txSize; - CANmodule->curentSyncTimeIsInsideWindow = 0; - CANmodule->CANnormal = false; - CANmodule->useCANrxFilters = 1;// we can make this configurable - CANmodule->bufferInhibitFlag = 0; - CANmodule->firstCANtxMessage = 1; - CANmodule->CANtxCount = 0; - CANmodule->errOld = 0; - CANmodule->em = 0; - memset(CANmodule->rxBufferIndexArray, UNUSED_ENTRY, sizeof(CANmodule->rxBufferIndexArray)); - - for(i=0; irxArray[i].ident = 0; - CANmodule->rxArray[i].pFunct = 0; - } - for(i=0; itxArray[i].bufferFull = 0; - } - - Cyg_ErrNo Result = canInit(CANmodule, CANbitRate); - if (ENOERR != Result) - { - return CO_ERROR_ILLEGAL_ARGUMENT; - } - - return CO_ERROR_NO; -} - - -//============================================================================= -void CO_CANmodule_disable(CO_CANmodule_t *CANmodule) -{ - setCAN_Mode(CYGNUM_CAN_MODE_STOP, ADDR_CAN1); -} - - -//============================================================================= -uint16_t CO_CANrxMsg_readIdent(CO_CANrxMsg_t *rxMsg) -{ - return rxMsg->ID; -} - - -//============================================================================= -Cyg_ErrNo hwCANrxBufferInit(CO_CANmodule_t *CANmodule, uint16_t ident) -{ - CO_DBG_PRINT("hwCANrxBufferInit %x\n", ident); - cyg_can_filter rx_filter; - cyg_uint32 len; - rx_filter.cfg_id = CYGNUM_CAN_MSGBUF_RX_FILTER_ADD; - rx_filter.msg.id = ident & 0x07FF; - rx_filter.msg.ext = CYGNUM_CAN_ID_STD; - len = sizeof(rx_filter); - return cyg_io_set_config(CANmodule->ioHandle, - CYG_IO_SET_CONFIG_CAN_MSGBUF ,&rx_filter, &len); -} - - -//============================================================================= -void updateHardwareFilters(CO_CANmodule_t *CANmodule) -{ - cyg_uint32 len; - cyg_can_msgbuf_cfg msgbox_cfg; - uint16_t i = 0; - - CO_DBG_PRINT("updateHardwareFilters()\n"); - // - // Now reset message buffer configuration - this is mandatory before starting - // message buffer runtime configuration - // - msgbox_cfg.cfg_id = CYGNUM_CAN_MSGBUF_RESET_ALL; - len = sizeof(msgbox_cfg); - Cyg_ErrNo Result = cyg_io_set_config(CANmodule->ioHandle, - CYG_IO_SET_CONFIG_CAN_MSGBUF ,&msgbox_cfg, &len); - if (ENOERR != Result) - { - reportErrorReturnCode(Result, CANmodule, - "CYGNUM_CAN_MSGBUF_RESET_ALL returned error: %x\n"); - return; - } - - memset(CANmodule->rxBufferIndexArray, UNUSED_ENTRY, sizeof(CANmodule->rxBufferIndexArray)); - // new iterate through all buffers and set all filters - for (i = 0; i < CANmodule->rxSize; ++i) - { - CO_CANrx_t* rxBuffer = &CANmodule->rxArray[i]; - if (!rxBuffer->pFunct) - { - continue; - } - - uint16_t CAN_ID = rxBuffer->ident &= ~0x800; - Result = hwCANrxBufferInit(CANmodule, CAN_ID); - if (ENOERR != Result) - { - reportErrorReturnCode(Result, CANmodule, - "CYGNUM_CAN_MSGBUF_RX_FILTER_ADD returned error: %x\n"); - return; - } - CANmodule->rxBufferIndexArray[CAN_ID] = i; - } -} - - -//============================================================================= -int16_t CO_CANrxBufferInit( - CO_CANmodule_t *CANmodule, - uint16_t index, - uint16_t ident, - uint16_t mask, - uint8_t rtr, - void *object, - void (*pFunct)(void *object, const CO_CANrxMsg_t *message)) -{ - CO_CANrx_t *rxBuffer; - int16_t ret = CO_ERROR_NO; - bool enable_buffer = true; - - /* safety */ - if(!CANmodule || !object || !pFunct || index >= CANmodule->rxSize){ - return CO_ERROR_ILLEGAL_ARGUMENT; - } - - /* buffer, which will be configured */ - rxBuffer = &CANmodule->rxArray[index]; - - /* Configure object variables */ - rxBuffer->object = object; - - /* CAN identifier and CAN mask, bit aligned with CAN module. Different on different microcontrollers. */ - rxBuffer->ident = ident & 0x07FF; - if(rtr) rxBuffer->ident |= 0x0800; - - if(!CANmodule->useCANrxFilters) - { - return ret; - } - - enable_buffer = (0 != ident) || (index == 0); - - if (!enable_buffer) - { - if (!rxBuffer->pFunct) - { - return ret; - } - - rxBuffer->pFunct = 0; - updateHardwareFilters(CANmodule); - return ret; - } - - rxBuffer->pFunct = pFunct; - CO_DBG_PRINT("Setting hardware filter ID: %x Mask: %x Buffer: %d\n", ident, mask,index); - CYG_ASSERT(ident < 0x800, "Illegal CAN identifier > 0x800"); - if (UNUSED_ENTRY != CANmodule->rxBufferIndexArray[ident]) - { - return CO_ERROR_NO; - } - - CANmodule->rxBufferIndexArray[ident] = index; - Cyg_ErrNo Result = hwCANrxBufferInit(CANmodule, ident); - if (ENOERR != Result) - { - ret = CO_ERROR_OUT_OF_MEMORY; - } - can_module = CANmodule; - - return ret; -} - - -//============================================================================= -CO_CANtx_t *CO_CANtxBufferInit( - CO_CANmodule_t *CANmodule, - uint16_t index, - uint16_t ident, - uint8_t rtr, - uint8_t noOfBytes, - uint8_t syncFlag) -{ - /* safety */ - if(!CANmodule || CANmodule->txSize <= index) return 0; - - /* get specific buffer */ - CO_CANtx_t *buffer = &CANmodule->txArray[index]; - - buffer->ID = ident; - buffer->DLC = noOfBytes; - buffer->RTR = rtr; - buffer->bufferFull = 0; - buffer->syncFlag = syncFlag ? 1 : 0; - - return buffer; -} - - -//============================================================================= -int16_t CO_CANsend(CO_CANmodule_t *CANmodule, CO_CANtx_t *buffer) -{ - int16_t Ret = CO_ERROR_NO; - - // messages with syncFlag set (synchronous PDOs) must be transmited inside preset time window - if(CANmodule->curentSyncTimeIsInsideWindow && buffer->syncFlag && !(*CANmodule->curentSyncTimeIsInsideWindow)) - { - CO_errorReport((CO_EM_t*)CANmodule->em, CO_EM_TPDO_OUTSIDE_WINDOW, CO_EMC_COMMUNICATION, 0); - return CO_ERROR_TX_PDO_WINDOW; - } - - cyg_uint32 len; - cyg_can_message tx_msg; - - len = sizeof(tx_msg); - tx_msg.id = buffer->ID; - tx_msg.dlc = buffer->DLC; - tx_msg.rtr = buffer->RTR ? CYGNUM_CAN_FRAME_RTR : CYGNUM_CAN_FRAME_DATA; - tx_msg.ext = CYGNUM_CAN_ID_STD; - memcpy(tx_msg.data.bytes, buffer->data, buffer->DLC); - Cyg_ErrNo Result = cyg_io_write(CANmodule->ioHandle, &tx_msg, &len); - if (ENOERR == Result) - { - CANmodule->firstCANtxMessage = 0; - buffer->bufferFull = 0; - } - else - { - CO_eCos_errorReport((CO_EM_t*)CANmodule->em, - CO_EM_CAN_TX_OVERFLOW, CO_EMC_CAN_OVERRUN, 0); - CO_DBG_PRINT("cyg_io_write() returned error %x\n", Result); - Ret = CO_ERROR_TIMEOUT; - } - - return Ret; -} - - -//============================================================================= -void CO_CANclearPendingSyncPDOs(CO_CANmodule_t *CANmodule) -{ - // we do nothing here. eCos has a transmit queue and if the messages - // are in the queue then we can't do anything - the message will get - // transmitted -} - - - -//============================================================================= -void CO_CANverifyErrors(CO_CANmodule_t *CANmodule){ - uint16_t rxErrors = 0; - uint16_t txErrors = 0; - CO_EM_t* EM = (CO_EM_t*)CANmodule->em; - Cyg_ErrNo Result; - cyg_uint32 len; - cyg_can_err_count_info err_info; - - len = sizeof(err_info); - Result = cyg_io_get_config(CANmodule->ioHandle, CYG_IO_GET_CONFIG_CAN_ERR_COUNTERS, - &err_info, &len); - if (ENOERR != Result) - { - reportErrorReturnCode(Result, CANmodule, - "CYG_IO_GET_CONFIG_CAN_ERR_COUNTERS returned error: %x\n"); - return; - } - - txErrors = err_info.tx_err_count; - rxErrors = err_info.rx_err_count; - - cyg_can_state can_state; - len = sizeof(cyg_can_state); - Result = cyg_io_get_config(CANmodule->ioHandle, CYG_IO_GET_CONFIG_CAN_STATE, - &can_state, &len); - if (ENOERR != Result) - { - reportErrorReturnCode(Result, CANmodule, - "CYG_IO_GET_CONFIG_CAN_STATE returned error: %x\n"); - return; - } - - if (CYGNUM_CAN_STATE_BUS_OFF == can_state) - { - txErrors = 256; - } - - uint32_t err = txErrors << 8 | rxErrors; - if (CANmodule->errOld == err) - { - return; - } - - CANmodule->errOld = err; - - // Bus off - if(txErrors >= 256) - { - CO_eCos_errorReport(EM, CO_EM_CAN_TX_BUS_OFF, CO_EMC_BUS_OFF_RECOVERED, err); - return; - } - CO_eCos_errorReset(EM, CO_EM_CAN_TX_BUS_OFF, err); - - // bus warning - if ((rxErrors >= 96) || (txErrors >= 96)) - { - CO_eCos_errorReport(EM, CO_EM_CAN_BUS_WARNING, CO_EMC_NO_ERROR, err); - } - - // rx bus passive - if (rxErrors >= 128) - { - CO_eCos_errorReport(EM, CO_EM_CAN_RX_BUS_PASSIVE, CO_EMC_CAN_PASSIVE, err); - } - else - { - CO_eCos_errorReset(EM, CO_EM_CAN_RX_BUS_PASSIVE, err); - } - - // tx bus passive - if (txErrors >= 128) - { - CO_eCos_errorReport(EM, CO_EM_CAN_TX_BUS_PASSIVE, CO_EMC_CAN_PASSIVE, err); - } - else - { - CO_eCos_errorReset(EM, CO_EM_CAN_TX_BUS_PASSIVE, err); - } - - // no error - if ((rxErrors < 96) && (txErrors < 96)) - { - CO_eCos_errorReset(EM, CO_EM_CAN_BUS_WARNING, err); - } -} - -#else // CYGOPT_IO_CAN_SUPPORT_TIMEOUTS -#error "Needs support for CAN timeouts - CYGOPT_IO_CAN_SUPPORT_TIMEOUTS" -#endif - -#else // CYGOPT_IO_CAN_SUPPORT_NONBLOCKING -#error "Needs support for non blocking read / write calls - CYGOPT_IO_CAN_SUPPORT_NONBLOCKING" -#endif - -#else // CYGOPT_IO_CAN_STD_CAN_ID -#error "Needs support for CAN standard 11 Bit identifiers - CYGOPT_IO_CAN_STD_CAN_ID" -#endif - -#else // CYGOPT_IO_CAN_RUNTIME_MBOX_CFG -#error "Needs support for CAN message buffer runtime configuration - CYGOPT_IO_CAN_RUNTIME_MBOX_CFG" -#endif - -#else // CYGPKG_IO_CAN_DEVICES -#error "Needs CAN hardware device drivers - CYGPKG_IO_CAN_DEVICES" -#endif - -#else // CYGFUN_KERNEL_API_C) -#error "Needs kernel C API" -#endif diff --git a/stack/eCos/CO_driver_target.h b/stack/eCos/CO_driver_target.h deleted file mode 100644 index 0d19932b..00000000 --- a/stack/eCos/CO_driver_target.h +++ /dev/null @@ -1,179 +0,0 @@ -/* - * CAN module object for eCos RTOS CAN layer - * - * @file CO_driver_target.h - * @author Uwe Kindler - * @copyright 2013 Uwe Kindler - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * CANopenNode is free and open source software: you can redistribute - * it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Following clarification and special exception to the GNU General Public - * License is included to the distribution terms of CANopenNode: - * - * Linking this library statically or dynamically with other modules is - * making a combined work based on this library. Thus, the terms and - * conditions of the GNU General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this library give - * you permission to link this library with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also meet, - * for each linked independent module, the terms and conditions of the - * license of that module. An independent module is a module which is - * not derived from or based on this library. If you modify this - * library, you may extend this exception to your version of the - * library, but you are not obliged to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - - -#ifndef CO_DRIVER_TARGET_H -#define CO_DRIVER_TARGET_H - - -/* For documentation see file drvTemplate/CO_driver.h */ - - -//=========================================================================== -// INCLUDES -//=========================================================================== -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - -//=========================================================================== -// CONFIGURATION -//=========================================================================== -#define CYGDBG_IO_CANOPEN_DEBUG 1 // define this if you want debug output via diagnostic channel - - -//=========================================================================== -// DEFINES -//=========================================================================== -#if (CYG_BYTEORDER == CYG_MSBFIRST) -#define CO_BIG_ENDIAN 1 -#else -#define CO_LITTLE_ENDIAN 1 -#endif - -// -// Support debug output if this option is enabled in CDL file -// -#ifdef CYGDBG_IO_CANOPEN_DEBUG -#define CO_DBG_PRINT diag_printf -#else -#define CO_DBG_PRINT( fmt, ... ) -#endif - -/* CAN module base address */ -// we don't really care about the addresses here because the eCos port -// uses I/O handles for accessing its CAN devices -#define ADDR_CAN1 0 -#define ADDR_CAN2 1 - - -/* Critical sections */ -// shared data is accessed only from thread level code (not from ISR or DSR) -// so we simply do a scheduler lock here to prevent access from different -// threads -#define CO_LOCK_CAN_SEND() cyg_scheduler_lock() -#define CO_UNLOCK_CAN_SEND() cyg_scheduler_unlock() - -#define CO_LOCK_EMCY() cyg_scheduler_lock() -#define CO_UNLOCK_EMCY() cyg_scheduler_unlock() - -#define CO_LOCK_OD() cyg_scheduler_lock() -#define CO_UNLOCK_OD() cyg_scheduler_unlock() - - - -/* Data types */ -typedef unsigned char bool_t; -typedef cyg_uint8 uint8_t; -typedef cyg_uint16 uint16_t; -typedef cyg_uint32 uint32_t; -typedef cyg_uint64 uint64_t; -typedef cyg_int8 int8_t; -typedef cyg_int16 int16_t; -typedef cyg_int32 int32_t; -typedef cyg_int64 int64_t; -typedef float float32_t; -typedef long double float64_t; -typedef char char_t; -typedef unsigned char oChar_t; -typedef unsigned char domain_t; - - -/* CAN receive message structure as aligned in CAN module. */ -typedef struct{ - uint32_t ID; - uint8_t DLC; /* Length of CAN message */ - uint8_t RTR; - uint8_t data[8]; /* 8 data bytes */ -}CO_CANrxMsg_t; - - -/* Received message object */ -typedef struct{ - uint16_t ident; - void *object; - void (*pFunct)(void *object, const CO_CANrxMsg_t *message); -}CO_CANrx_t; - - -/* Transmit message object. */ -typedef struct{ - uint16_t ID; - uint8_t DLC; - uint8_t RTR; - uint8_t data[8]; - volatile uint8_t bufferFull; - volatile uint8_t syncFlag; -}CO_CANtx_t; - - -/* CAN module object. */ -typedef struct{ - CO_CANrx_t *rxArray; - uint16_t rxSize; - CO_CANtx_t *txArray; - uint16_t txSize; - volatile bool_t CANnormal; - volatile uint8_t *curentSyncTimeIsInsideWindow; - volatile uint8_t useCANrxFilters; - volatile uint8_t bufferInhibitFlag; - volatile uint8_t firstCANtxMessage; - volatile uint16_t CANtxCount; - uint32_t errOld; - void *em; - void *driverPrivate; - uint16_t rxBufferIndexArray[0x800]; ///< Array of pointers to rx buffers - cyg_io_handle_t ioHandle; -}CO_CANmodule_t; - -#ifdef __cplusplus -} //extern "C" -#endif /* __cplusplus */ -#endif /* CO_DRIVER_TARGET_H */ diff --git a/stack/eCos/Makefile b/stack/eCos/Makefile deleted file mode 100644 index 5736b7f5..00000000 --- a/stack/eCos/Makefile +++ /dev/null @@ -1,87 +0,0 @@ -# Mostly written by Jonathan Larmour, Red Hat, Inc. -# Reference to ecos.mak added by John Dallaway, eCosCentric Limited, 2003-01-20 -# This file is in the public domain and may be used for any purpose - -# Usage: make INSTALL_DIR=/path/to/ecos/install - -#INSTALL_DIR=$$(INSTALL_DIR) # override on make command line -#INSTALL_DIR=/tmp/lpc-e2294_default_CAN_04_debug_install -#INSTALL_DIR=/tmp/lpc-e2294_ustl_CAN_04_debug_install -INSTALL_DIR=/tmp/lpc-l2294_ustl_CAN_02_debug_install - -# path to CANopen node source code -CANOPENNODE_SRC = /cygdrive/c/CodingXP/CANopenNode_SVN/CANopen_stack - - -# Include directories -INCLUDE_DIRS = $(CANOPENNODE_SRC) \ - -I"src" - -# source files -SOURCES = $(CANOPENNODE_SRC)/CANopen.c \ - $(CANOPENNODE_SRC)/CO_Emergency.c \ - $(CANOPENNODE_SRC)/CO_HBconsumer.c \ - $(CANOPENNODE_SRC)/CO_NMT_Heartbeat.c \ - $(CANOPENNODE_SRC)/CO_PDO.c \ - $(CANOPENNODE_SRC)/CO_SDO.c \ - $(CANOPENNODE_SRC)/CO_SDOmaster.c \ - $(CANOPENNODE_SRC)/CO_SYNC.c \ - $(CANOPENNODE_SRC)/crc16-ccitt.c \ - src/application.cpp \ - src/CO_driver_eCos.c \ - src/main.c \ - src/CO_OD.c \ - src/CO_PollingTimer.c \ - src/CO_Flash.c \ - src/ecos_helper.c - - -OBJSC=${SOURCES:%.c=%.o} -OBJS=${OBJSC:%.cpp=%.o} - -include $(INSTALL_DIR)/include/pkgconf/ecos.mak - -XCC = $(ECOS_COMMAND_PREFIX)gcc -OBJCOPY = $(ECOS_COMMAND_PREFIX)objcopy -XCXX = $(XCC) -XLD = $(XCC) - -CFLAGS = -I$(INSTALL_DIR)/include -I$(INCLUDE_DIRS) -CXXFLAGS = $(CFLAGS) -LDFLAGS = -nostartfiles -L$(INSTALL_DIR)/lib -Ttarget.ld - -# RULES - -.PHONY: all clean - -all: ecos_canopennode ecos_canopennode.bin - -clean: - -rm -f src/*.o - -rm -f $(CANOPENNODE_SRC)/*.o - -%.o: %.c - $(XCC) -c -o $*.o $(CFLAGS) $(ECOS_GLOBAL_CFLAGS) $< - -%.o: %.cxx - $(XCXX) -c -o $*.o $(CFLAGS) $(ECOS_GLOBAL_CFLAGS) $< - -%.o: %.cpp - $(XCXX) -c -o $*.o $(CXXFLAGS) $(ECOS_GLOBAL_CFLAGS) $< - -%.o: %.C - $(XCXX) -c -o $*.o $(CXXFLAGS) $(ECOS_GLOBAL_CFLAGS) $< - -%.o: %.cc - $(XCXX) -c -o $*.o $(CXXFLAGS) $(ECOS_GLOBAL_CFLAGS) $< - - -ecos_canopennode: $(OBJS) - $(XLD) $(LDFLAGS) $(ECOS_GLOBAL_LDFLAGS) $(OBJS) -o $@ - - -ecos_canopennode.bin : ecos_canopennode - $(OBJCOPY) --strip-debug $< $(@:.bin=.img) - $(OBJCOPY) -O srec $< $(@:.bin=.srec) - $(OBJCOPY) -O binary $< $@ - diff --git a/stack/eCos/application.cpp b/stack/eCos/application.cpp deleted file mode 100644 index e4b8c4f4..00000000 --- a/stack/eCos/application.cpp +++ /dev/null @@ -1,69 +0,0 @@ -//=========================================================================== -// INCLUDES -//=========================================================================== -#include "CANopen.h" -#include - - -//=========================================================================== -extern "C" void programStart(void) -{ - diag_printf("programStart\n"); -} - - -//=========================================================================== -extern "C" void communicationReset(void) -{ - diag_printf("communicationReset\n"); -} - - -//=========================================================================== -extern "C" void programEnd(void) -{ - diag_printf("programEnd\n"); -} - - -//=========================================================================== -extern "C" void programAsync(uint16_t timer1msDiff) -{ - static uint32_t MsCount = 0; - static uint8_t SecCount = 0; - static uint8_t Output0 = OD_writeOutput8Bit[0]; - - MsCount += timer1msDiff; - OD_readInput8Bit[1] = MsCount; - if (MsCount >= 1000) - { - diag_printf("programAsync\n"); - MsCount -= 1000; - SecCount++; - } - - OD_readInput8Bit[0] = SecCount; - if (Output0 != OD_writeOutput8Bit[0]) - { - Output0 = OD_writeOutput8Bit[0]; - diag_printf("Output0 changed: %x\n", Output0); - } -} - - -//=========================================================================== -extern "C" void program1ms(void) -{ - static uint32_t MsCount = 0; - MsCount ++; - if (MsCount >= 1000) - { - diag_printf("program1ms\n"); - MsCount -= 1000; - } -} - - -//--------------------------------------------------------------------------- -// EOF application.cpp - diff --git a/stack/eCos/ecos_helper.cpp b/stack/eCos/ecos_helper.cpp deleted file mode 100644 index 5d5893ee..00000000 --- a/stack/eCos/ecos_helper.cpp +++ /dev/null @@ -1,56 +0,0 @@ -//============================================================================ -/// \file ecos_helper.cpp -/// \author Uwe Kindler -/// \date 05.09.2013 -/// \brief Implementation of ecos_helper -//============================================================================ - -//============================================================================ -// INCLUDES -//============================================================================ -#include - - -//============================================================================ -extern "C" cyg_tick_count convertMsToTicks(cyg_tick_count Milliseconds) -{ - static struct Cyg_Clock::converter ms_converter; - static volatile cyg_atomic conv_init = 0; - if (!conv_init) - { - struct Cyg_Clock::converter temp_ms_converter; - Cyg_Clock::real_time_clock->get_other_to_clock_converter( - 1000000, &temp_ms_converter); - Cyg_Scheduler::lock(); - ms_converter = temp_ms_converter; - conv_init = 1; - Cyg_Scheduler::unlock(); - } - - return Cyg_Clock::convert(Milliseconds, &ms_converter); -} - - -//============================================================================ -extern "C" cyg_tick_count convertTicksToMs(cyg_tick_count ClockTicks) -{ - static struct Cyg_Clock::converter ms_converter; - static volatile cyg_atomic conv_init = 0; - if (!conv_init) - { - struct Cyg_Clock::converter temp_ms_converter; - Cyg_Clock::real_time_clock->get_clock_to_other_converter( - 1000000, &temp_ms_converter); - Cyg_Scheduler::lock(); - ms_converter = temp_ms_converter; - conv_init = 1; - Cyg_Scheduler::unlock(); - } - - return Cyg_Clock::convert(ClockTicks, &ms_converter); -} - - - -//--------------------------------------------------------------------------- -// EOF ecos_helper.cpp diff --git a/stack/eCos/ecos_helper.h b/stack/eCos/ecos_helper.h deleted file mode 100644 index 5369c687..00000000 --- a/stack/eCos/ecos_helper.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef ecos_helperH -#define ecos_helperH -//============================================================================ -/// \file ecos_helper.h -/// \author Uwe Kindler -/// \date 05.09.2013 -/// \brief Declaration of ecos_helper -//============================================================================ - -//============================================================================ -// INCLUDES -//============================================================================ -#include -#include - - -/** - * Converts milliseconds to eCos clock ticks - */ -cyg_tick_count convertMsToTicks(cyg_tick_count Milliseconds); - -/** - * Converts eCos clock ticks to milliseconds - */ -cyg_tick_count convertTicksToMs(cyg_tick_count ClockTicks); - - -//--------------------------------------------------------------------------- -#endif // ecos_helperH diff --git a/stack/eCos/main.c b/stack/eCos/main.c deleted file mode 100644 index ec3fe48f..00000000 --- a/stack/eCos/main.c +++ /dev/null @@ -1,109 +0,0 @@ -//============================================================================ -// Name : CANopenNodeEcos.cpp -// Author : Uwe Kindler -// Version : -// Copyright : Your copyright notice -// Description : Hello World in C, Ansi-style -//============================================================================ - - -//============================================================================ -// INCLUDES -//============================================================================ -#include // kernel API -#include -#include - -#include "CANopen.h" -#include "application.h" -#include "CO_PollingTimer.h" -#include "CO_Flash.h" - - -//============================================================================ -// Global variables and objects -//============================================================================ -static cyg_uint64 CANopenPollingTimer = 1; -extern struct sCO_OD_ROM CO_OD_ROM; -static uint8_t reset = CO_RESET_NOT; -extern void CO_eCos_errorReport(CO_EM_t *em, const uint8_t errorBit, - const uint16_t errorCode, const uint32_t infoCode); - - -//=========================================================================== -// CANopen stack main entry point -//=========================================================================== -void CO_main(void) -{ - CO_FlashInit(); - while (1) - { - reset = CO_RESET_NOT; - // Application interface - programStart(); - - // increase variable each startup. Variable is stored in EEPROM. - OD_powerOnCounter++; - while (reset < CO_RESET_APP) - { - // CANopen communication reset - initialize CANopen objects - int16_t err; - cyg_uint64 timer1msPrevious = CO_TmrGetMilliSec(); - - // initialize CANopen - err = CO_init(); - if(err) - { - CO_eCos_errorReport(CO->em, CO_EM_MEMORY_ALLOCATION_ERROR, - CO_EMC_SOFTWARE_INTERNAL, err); - while(1); - } - // register object dictionary functions to support store and restore - // of parameters via objects 0x1010 and 0x1011 - CO_FlashRegisterODFunctions(CO); - - // initialize variables - reset = CO_RESET_NOT; - - // Application interface - communicationReset(); - - // start CAN and enable interrupts - CO_CANsetNormalMode(ADDR_CAN1); - - while (CO_RESET_NOT == reset) - { - //diag_print_reg("CAN GSR", CAN_CTRL_1_REG_BASE + CANREG_GSR); - // loop for normal program execution - cyg_uint64 timer1ms = CO_TmrGetMilliSec(); - uint16_t timer1msDiff = timer1ms - timer1msPrevious; - timer1msPrevious = timer1ms; - - // Application interface - programAsync(timer1msDiff); - - // CANopen process - reset = CO_process(CO, timer1msDiff); - if (CO_TmrIsExpired(CANopenPollingTimer)) - { - CANopenPollingTimer = CO_TmrStartFrom(CANopenPollingTimer, 1); - CO_process_RPDO(CO); - program1ms(); - CO_process_TPDO(CO); - } - } - } - } -} - - -//=========================================================================== -// Application main entry point -//=========================================================================== -int main( int argc, char *argv[]) -{ - CO_main();// this function will never return - return 0; -} - -//--------------------------------------------------------------------------- diff --git a/stack/eCos/readme b/stack/eCos/readme deleted file mode 100644 index dd874868..00000000 --- a/stack/eCos/readme +++ /dev/null @@ -1,12 +0,0 @@ -CANopenNode driver for eCos CAN framework. The driver supports any hardware that is supported by the eCos CAN framework. -See http://ecos.sourceware.org/ - -The driver was developed and tested with the Olimex LCP-E2294-1MB board: -https://www.olimex.com/Products/ARM/NXP/LPC-E2294-1MB/ - -and with the Olimex LPC-L2294-1MB board: -https://www.olimex.com/Products/ARM/NXP/LPC-L2294-1MB/ - -The driver also utilizes the eCos generic flash support to implement parameter storage and default parameter restore functionality via objects 0x1010 and 0x1011. - -Contributed by Uwe Kindler: http://sourceforge.net/p/canopennode/discussion/387151/thread/7603e3b5/ diff --git a/stack/neuberger-socketCAN/CO_Linux_threads.c b/stack/neuberger-socketCAN/CO_Linux_threads.c deleted file mode 100644 index b35acf8e..00000000 --- a/stack/neuberger-socketCAN/CO_Linux_threads.c +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Helper functions for implementing CANopen threads in Linux - * - * @file Linux_threads.c - * @ingroup CO_driver - * @author Janez Paternoster, Martin Wagner - * @copyright 2004 - 2015 Janez Paternoster, 2017 - 2020 Neuberger Gebaeudeautomation GmbH - * - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include - -#include "CO_driver.h" -#include "CANopen.h" - -/* Helper function - get monotonic clock time in ms */ -static uint64_t CO_LinuxThreads_clock_gettime_ms(void) -{ - struct timespec ts; - - (void)clock_gettime(CLOCK_MONOTONIC, &ts); - return ts.tv_sec * 1000 + ts.tv_nsec / 1000000; -} - -/* Mainline thread (threadMain) ***************************************************/ -static struct -{ - uint64_t start; /* time value CO_process() was called last time in ms */ - void (*pFunct)(void* object); /* Callback function */ - void *object; -} threadMain; - -/** - * This function notifies the user application after an event happened - * - * This is necessary because not all stack callbacks support object pointers. - * It is not used for those callbacks that have this pointer! - */ -static void threadMain_resumeCallback(void) -{ - if (threadMain.pFunct != NULL) { - threadMain.pFunct(threadMain.object); - } -} - -void threadMain_init(void (*callback)(void*), void *object) -{ - threadMain.start = CO_LinuxThreads_clock_gettime_ms(); - threadMain.pFunct = callback; - threadMain.object = object; - - CO_SDO_initCallback(CO->SDO[0], threadMain_resumeCallback); - CO_EM_initCallback(CO->em, threadMain_resumeCallback); -#if CO_NO_LSS_CLIENT == 1 - CO_LSSmaster_initCallback(CO->LSSmaster, threadMain.object, threadMain.pFunct); -#endif -#if CO_NO_SDO_CLIENT != 0 - for (int i = 0; i < CO_NO_SDO_CLIENT; i++) { - CO_SDOclient_initCallback(CO->SDOclient[i], threadMain_resumeCallback); - } -#endif -} - -void threadMain_close(void) -{ - threadMain.pFunct = NULL; - threadMain.object = NULL; -} - -void threadMain_process(CO_NMT_reset_cmd_t *reset) -{ - uint16_t finished; - uint16_t diff; - uint64_t now; - - now = CO_LinuxThreads_clock_gettime_ms(); - diff = (uint16_t)(now - threadMain.start); - - /* we use timerNext_ms in CO_process() as indication if processing is - * finished. We ignore any calculated values for maximum delay times. */ - do { - finished = 1; - *reset = CO_process(CO, diff, &finished); - diff = 0; - } while ((*reset == CO_RESET_NOT) && (finished == 0)); - - /* prepare next call */ - threadMain.start = now; -} - -/* Realtime thread (threadRT) *****************************************************/ -static struct { - uint32_t us_interval; /* configured interval in us */ - int interval_fd; /* timer fd */ -} threadRT; - -void CANrx_threadTmr_init(uint16_t interval) -{ - struct itimerspec itval; - - threadRT.us_interval = interval * 1000; - /* set up non-blocking interval timer */ - threadRT.interval_fd = timerfd_create(CLOCK_MONOTONIC, 0); - (void)fcntl(threadRT.interval_fd, F_SETFL, O_NONBLOCK); - itval.it_interval.tv_sec = 0; - itval.it_interval.tv_nsec = interval * 1000000; - itval.it_value = itval.it_interval; - (void)timerfd_settime(threadRT.interval_fd, 0, &itval, NULL); -} - -void CANrx_threadTmr_close(void) -{ - (void)close(threadRT.interval_fd); - threadRT.interval_fd = -1; -} - -void CANrx_threadTmr_process(void) -{ - int32_t result; - int32_t i; - bool_t syncWas; - unsigned long long missed; - - result = CO_CANrxWait(CO->CANmodule[0], threadRT.interval_fd, NULL); - if (result < 0) { - result = read(threadRT.interval_fd, &missed, sizeof(missed)); - if (result > 0) { - /* at least one timer interval occured */ - CO_LOCK_OD(); - - if(CO->CANmodule[0]->CANnormal) { - - for (i = 0; i <= missed; i++) { - -#if CO_NO_SYNC == 1 - /* Process Sync */ - syncWas = CO_process_SYNC(CO, threadRT.us_interval); -#else - syncWas = false; -#endif - /* Read inputs */ - CO_process_RPDO(CO, syncWas); - - /* Write outputs */ - CO_process_TPDO(CO, syncWas, threadRT.us_interval); - } - } - - CO_UNLOCK_OD(); - } - } -} diff --git a/stack/neuberger-socketCAN/CO_Linux_threads.h b/stack/neuberger-socketCAN/CO_Linux_threads.h deleted file mode 100644 index 2bbff23e..00000000 --- a/stack/neuberger-socketCAN/CO_Linux_threads.h +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Helper functions for implementing CANopen threads in Linux. - * - * @file CO_Linux_threads.h - * @ingroup CO_driver - * @author Janez Paternoster, Martin Wagner - * @copyright 2004 - 2015 Janez Paternoster, 2018 - 2020 Neuberger Gebaeudeautomation GmbH - * - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef CO_LINUX_TASKS_H -#define CO_LINUX_TASKS_H - -#ifdef __cplusplus -extern "C" { -#endif - -/* This driver is loosely based upon the CO socketCAN driver - * The "threads" inside this driver do not fork threads themselve, but require - * that two threads are provided by the calling application. - * - * Like the CO socketCAN driver implementation, this driver uses the global CO - * object and has one thread-local struct for variables. */ - -/** - * Initialize mainline thread. - * - * threadMain is non-realtime thread for CANopenNode processing. It is nonblocking - * and should be called cyclically in 50 ms intervals or less if necessary. This - * is indicated by the callback function. - * This thread processes CO_process() function from CANopen.c file. - * - * @param callback this function is called to indicate #threadMain_process() has - * work to do - * @param object this pointer is given to _callback()_ - */ -extern void threadMain_init(void (*callback)(void*), void *object); - -/** - * Cleanup mainline thread. - */ -extern void threadMain_close(void); - -/** - * Process mainline thread. - * - * Function must be called cyclically and after callback - * - * @param reset return value from CO_process() function. - */ -extern void threadMain_process(CO_NMT_reset_cmd_t *reset); - -/** - * Initialize realtime thread. - * - * CANrx_threadTmr is realtime thread for CANopenNode processing. It is nonblocking - * and must be executed at CAN message receive or periodically in 1ms (or something) - * intervals. Inside interval is processed CANopen SYNC message, RPDOs(inputs) - * and TPDOs(outputs). - * CANrx_threadTmr uses CAN socket from CO_driver.c - * - * @remark If realtime is required, this thread must be registred as such in the Linux - * kernel. - * - * @param interval Interval of periodic timer in ms, recommended value for - * realtime response: 1ms - */ -extern void CANrx_threadTmr_init(uint16_t interval); - -/** - * Terminate realtime thread. - */ -extern void CANrx_threadTmr_close(void); - -/** - * Process realtime thread. - * - * This function must be called inside an infinite loop. It blocks until either - * some event happens or a timer runs out. - */ -extern void CANrx_threadTmr_process(); - -#ifdef __cplusplus -} -#endif /*__cplusplus*/ - -#endif diff --git a/stack/neuberger-socketCAN/CO_driver.c b/stack/neuberger-socketCAN/CO_driver.c deleted file mode 100644 index 73584b3d..00000000 --- a/stack/neuberger-socketCAN/CO_driver.c +++ /dev/null @@ -1,994 +0,0 @@ -/* - * CAN module object for Linux socketCAN. - * - * This file is a template for other microcontrollers. - * - * @ingroup CO_driver - * @author Janez Paternoster, Martin Wagner - * @copyright 2004 - 2015 Janez Paternoster, 2017 - 2020 Neuberger Gebaeudeautomation GmbH - * - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "CO_driver.h" - -#if defined CO_DRIVER_ERROR_REPORTING - #if __has_include("syslog1/log.h") - #include "syslog/log.h" - #include "msgs.h" - #else - #include "CO_msgs.h" - #endif -#else - #define log_printf(macropar_prio, macropar_message, ...) -#endif - -#if __has_include("CO_Emergency.h") - #include "CO_Emergency.h" - #define USE_EMERGENCY_OBJECT -#endif - -pthread_mutex_t CO_EMCY_mutex = PTHREAD_MUTEX_INITIALIZER; -pthread_mutex_t CO_OD_mutex = PTHREAD_MUTEX_INITIALIZER; - -#ifndef CO_DRIVER_MULTI_INTERFACE -static CO_ReturnError_t CO_CANmodule_addInterface(CO_CANmodule_t *CANmodule, const void *CANdriverState); -#endif - -#ifdef CO_DRIVER_MULTI_INTERFACE - -static const uint32_t CO_INVALID_COB_ID = 0xffffffff; - -/******************************************************************************/ -void CO_CANsetIdentToIndex( - uint32_t *lookup, - uint32_t index, - uint32_t identNew, - uint32_t identCurrent) -{ - /* entry changed, remove old one */ - if (identCurrent CO_CAN_MSG_SFF_MAX_COB_ID) { - return; - } - - /* Special case COB ID "0" -> valid value in *xArray[0] (CO_*CAN_NMT), - * "entry unconfigured" for all others */ - if (identNew == 0) { - if (index == 0) { - lookup[0] = 0; - } - } - else { - lookup[identNew] = index; - } -} - - -/******************************************************************************/ -static uint32_t CO_CANgetIndexFromIdent( - uint32_t *lookup, - uint32_t ident) -{ - /* check if this COB ID is part of the table */ - if (ident > CO_CAN_MSG_SFF_MAX_COB_ID) { - return CO_INVALID_COB_ID; - } - - return lookup[ident]; -} - -#endif - - -/** Disable socketCAN rx *****************************************************/ -static CO_ReturnError_t disableRx(CO_CANmodule_t *CANmodule) -{ - int ret; - uint32_t i; - CO_ReturnError_t retval; - - /* insert a filter that doesn't match any messages */ - retval = CO_ERROR_NO; - for (i = 0; i < CANmodule->CANinterfaceCount; i ++) { - ret = setsockopt(CANmodule->CANinterfaces[i].fd, SOL_CAN_RAW, CAN_RAW_FILTER, - NULL, 0); - if(ret < 0){ - log_printf(LOG_ERR, CAN_FILTER_FAILED, - CANmodule->CANinterfaces[i].ifName); - log_printf(LOG_DEBUG, DBG_ERRNO, "setsockopt()"); - retval = CO_ERROR_SYSCALL; - } - } - - return retval; -} - -/** Set up or update socketCAN rx filters *************************************/ -static CO_ReturnError_t setRxFilters(CO_CANmodule_t *CANmodule) -{ - int ret; - int i; - int count; - CO_ReturnError_t retval; - - struct can_filter rxFiltersCpy[CANmodule->rxSize]; - - count = 0; - /* remove unused entries ( id == 0 and mask == 0 ) as they would act as - * "pass all" filter */ - for (i = 0; i < CANmodule->rxSize; i ++) { - if ((CANmodule->rxFilter[i].can_id != 0) || - (CANmodule->rxFilter[i].can_mask != 0)) { - - rxFiltersCpy[count] = CANmodule->rxFilter[i]; - - count ++; - } - } - - if (count == 0) { - /* No filter is set, disable RX */ - return disableRx(CANmodule); - } - - retval = CO_ERROR_NO; - for (i = 0; i < CANmodule->CANinterfaceCount; i ++) { - ret = setsockopt(CANmodule->CANinterfaces[i].fd, SOL_CAN_RAW, CAN_RAW_FILTER, - rxFiltersCpy, sizeof(struct can_filter) * count); - if(ret < 0){ - log_printf(LOG_ERR, CAN_FILTER_FAILED, - CANmodule->CANinterfaces[i].ifName); - log_printf(LOG_DEBUG, DBG_ERRNO, "setsockopt()"); - retval = CO_ERROR_SYSCALL; - } - } - - return retval; -} - - -/******************************************************************************/ -void CO_CANsetConfigurationMode(void *CANdriverState) -{ - /* Can't do anything because no reference to CANmodule_t is provided */ -} - - -/******************************************************************************/ -void CO_CANsetNormalMode(CO_CANmodule_t *CANmodule) -{ - CO_ReturnError_t ret; - - CANmodule->CANnormal = false; - - if(CANmodule != NULL) { - ret = setRxFilters(CANmodule); - if (ret == CO_ERROR_NO) { - /* Put CAN module in normal mode */ - CANmodule->CANnormal = true; - } - } -} - - -/******************************************************************************/ -CO_ReturnError_t CO_CANmodule_init( - CO_CANmodule_t *CANmodule, - void *CANdriverState, - CO_CANrx_t rxArray[], - uint16_t rxSize, - CO_CANtx_t txArray[], - uint16_t txSize, - uint16_t CANbitRate) -{ - int32_t ret; - uint16_t i; - struct epoll_event ev; - - /* verify arguments */ - if(CANmodule==NULL || rxArray==NULL || txArray==NULL){ - return CO_ERROR_ILLEGAL_ARGUMENT; - } - - /* Create epoll FD */ - CANmodule->fdEpoll = epoll_create(1); - if(CANmodule->fdEpoll < 0){ - log_printf(LOG_DEBUG, DBG_ERRNO, "epoll_create()"); - CO_CANmodule_disable(CANmodule); - return CO_ERROR_SYSCALL; - } - - /* Create notification pipe */ - CANmodule->pipe = CO_NotifyPipeCreate(); - if (CANmodule->pipe==NULL) { - log_printf(LOG_DEBUG, DBG_ERRNO, "pipe"); - CO_CANmodule_disable(CANmodule); - return CO_ERROR_OUT_OF_MEMORY; - } - /* ...and add it to epoll */ - ev.events = EPOLLIN; - ev.data.fd = CO_NotifyPipeGetFd(CANmodule->pipe); - ret = epoll_ctl(CANmodule->fdEpoll, EPOLL_CTL_ADD, ev.data.fd, &ev); - if(ret < 0){ - log_printf(LOG_DEBUG, DBG_ERRNO, "epoll_ctl(pipe)"); - CO_CANmodule_disable(CANmodule); - return CO_ERROR_SYSCALL; - } - - /* Configure object variables */ - CANmodule->CANinterfaces = NULL; - CANmodule->CANinterfaceCount = 0; - CANmodule->rxArray = rxArray; - CANmodule->rxSize = rxSize; - CANmodule->txArray = txArray; - CANmodule->txSize = txSize; - CANmodule->CANnormal = false; - CANmodule->em = NULL; //this is set inside CO_Emergency.c init function! - CANmodule->fdTimerRead = -1; -#ifdef CO_DRIVER_MULTI_INTERFACE - for (i = 0; i < CO_CAN_MSG_SFF_MAX_COB_ID; i++) { - CANmodule->rxIdentToIndex[i] = CO_INVALID_COB_ID; - CANmodule->txIdentToIndex[i] = CO_INVALID_COB_ID; - } -#endif - - /* initialize socketCAN filters - * CAN module filters will be configured with CO_CANrxBufferInit() - * functions, called by separate CANopen init functions */ - CANmodule->rxFilter = calloc(CANmodule->rxSize, sizeof(struct can_filter)); - if(CANmodule->rxFilter == NULL){ - log_printf(LOG_DEBUG, DBG_ERRNO, "malloc()"); - return CO_ERROR_OUT_OF_MEMORY; - } - - for(i=0U; iCANnormal != false) { - /* can't change config now! */ - return CO_ERROR_INVALID_STATE; - } - - /* Add interface to interface list */ - CANmodule->CANinterfaceCount ++; - CANmodule->CANinterfaces = realloc(CANmodule->CANinterfaces, - ((CANmodule->CANinterfaceCount) * sizeof(*CANmodule->CANinterfaces))); - if (CANmodule->CANinterfaces == NULL) { - log_printf(LOG_DEBUG, DBG_ERRNO, "malloc()"); - return CO_ERROR_OUT_OF_MEMORY; - } - interface = &CANmodule->CANinterfaces[CANmodule->CANinterfaceCount - 1]; - - interface->CANdriverState = CANdriverState; - ifName = if_indextoname((uintptr_t)interface->CANdriverState, interface->ifName); - if (ifName == NULL) { - log_printf(LOG_DEBUG, DBG_ERRNO, "if_indextoname()"); - return CO_ERROR_ILLEGAL_ARGUMENT; - } - - /* Create socket */ - interface->fd = socket(PF_CAN, SOCK_RAW, CAN_RAW); - if(interface->fd < 0){ - log_printf(LOG_DEBUG, DBG_ERRNO, "socket(can)"); - return CO_ERROR_SYSCALL; - } - - /* enable socket rx queue overflow detection */ - tmp = 1; - ret = setsockopt(interface->fd, SOL_SOCKET, SO_RXQ_OVFL, &tmp, sizeof(tmp)); - if(ret < 0){ - log_printf(LOG_DEBUG, DBG_ERRNO, "setsockopt(ovfl)"); - return CO_ERROR_SYSCALL; - } -#ifdef CO_DRIVER_MULTI_INTERFACE - /* enable software time stamp mode (hardware timestamps do not work properly - * on all devices)*/ - tmp = (SOF_TIMESTAMPING_SOFTWARE | - SOF_TIMESTAMPING_RX_SOFTWARE); - ret = setsockopt(interface->fd, SOL_SOCKET, SO_TIMESTAMPING, &tmp, sizeof(tmp)); - if (ret < 0) { - log_printf(LOG_DEBUG, DBG_ERRNO, "setsockopt(timestamping)"); - return CO_ERROR_SYSCALL; - } -#endif - - //todo - modify rx buffer size? first one needs root - //ret = setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, (void *)&bytes, sLen); - //ret = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void *)&bytes, sLen); - - /* print socket rx buffer size in bytes (In my experience, the kernel reserves - * around 450 bytes for each CAN message) */ - sLen = sizeof(bytes); - getsockopt(interface->fd, SOL_SOCKET, SO_RCVBUF, (void *)&bytes, &sLen); - if (sLen == sizeof(bytes)) { - log_printf(LOG_INFO, CAN_SOCKET_BUF_SIZE, interface->ifName, - bytes / 446, bytes); - } - - /* bind socket */ - memset(&sockAddr, 0, sizeof(sockAddr)); - sockAddr.can_family = AF_CAN; - sockAddr.can_ifindex = (uintptr_t)interface->CANdriverState; - ret = bind(interface->fd, (struct sockaddr*)&sockAddr, sizeof(sockAddr)); - if(ret < 0){ - log_printf(LOG_ERR, CAN_BINDING_FAILED, interface->ifName); - log_printf(LOG_DEBUG, DBG_ERRNO, "bind()"); - return CO_ERROR_SYSCALL; - } - -#ifdef CO_DRIVER_ERROR_REPORTING - CO_CANerror_init(&interface->errorhandler, interface->fd, interface->ifName); - /* set up error frame generation. What actually is available depends on your - * CAN kernel driver */ -#ifdef DEBUG - err_mask = CAN_ERR_MASK; //enable ALL error frames -#else - err_mask = CAN_ERR_ACK | CAN_ERR_CRTL | CAN_ERR_BUSOFF | CAN_ERR_BUSERROR; -#endif - ret = setsockopt(interface->fd, SOL_CAN_RAW, CAN_RAW_ERR_FILTER, &err_mask, - sizeof(err_mask)); - if(ret < 0){ - log_printf(LOG_ERR, CAN_ERROR_FILTER_FAILED, interface->ifName); - log_printf(LOG_DEBUG, DBG_ERRNO, "setsockopt(can err)"); - return CO_ERROR_SYSCALL; - } -#endif - - /* Add socket to epoll */ - ev.events = EPOLLIN; - ev.data.fd = interface->fd; - ret = epoll_ctl(CANmodule->fdEpoll, EPOLL_CTL_ADD, ev.data.fd, &ev); - if(ret < 0){ - log_printf(LOG_DEBUG, DBG_ERRNO, "epoll_ctl(can)"); - return CO_ERROR_SYSCALL; - } - - /* rx is started by calling #CO_CANsetNormalMode() */ - ret = disableRx(CANmodule); - - return ret; -} - - -/******************************************************************************/ -void CO_CANmodule_disable(CO_CANmodule_t *CANmodule) -{ - uint32_t i; - struct timespec wait; - - if (CANmodule == NULL) { - return; - } - - /* clear interfaces */ - for (i = 0; i < CANmodule->CANinterfaceCount; i++) { - CO_CANinterface_t *interface = &CANmodule->CANinterfaces[i]; - -#ifdef CO_DRIVER_ERROR_REPORTING - CO_CANerror_disable(&interface->errorhandler); -#endif - - epoll_ctl(CANmodule->fdEpoll, EPOLL_CTL_DEL, interface->fd, NULL); - close(interface->fd); - interface->fd = -1; - } - if (CANmodule->CANinterfaces != NULL) { - free(CANmodule->CANinterfaces); - } - CANmodule->CANinterfaceCount = 0; - - /* cancel rx */ - if (CANmodule->pipe != NULL) { - CO_NotifyPipeSend(CANmodule->pipe); - /* give some time for delivery */ - wait.tv_sec = 0; - wait.tv_nsec = 50 /* ms */ * 1000000; - nanosleep(&wait, NULL); - CO_NotifyPipeFree(CANmodule->pipe); - } - - if (CANmodule->fdEpoll >= 0) { - close(CANmodule->fdEpoll); - } - CANmodule->fdEpoll = -1; - - if (CANmodule->rxFilter != NULL) { - free(CANmodule->rxFilter); - } - CANmodule->rxFilter = NULL; -} - - -/******************************************************************************/ -uint16_t CO_CANrxMsg_readIdent(const CO_CANrxMsg_t *rxMsg) -{ - /* remove socketCAN flags */ - return (uint16_t) (rxMsg->ident & CAN_SFF_MASK); -} - - -/******************************************************************************/ -CO_ReturnError_t CO_CANrxBufferInit( - CO_CANmodule_t *CANmodule, - uint16_t index, - uint16_t ident, - uint16_t mask, - bool_t rtr, - void *object, - void (*pFunct)(void *object, const CO_CANrxMsg_t *message)) -{ - CO_ReturnError_t ret = CO_ERROR_NO; - - if((CANmodule!=NULL) && (index < CANmodule->rxSize)){ - uint16_t i; - CO_CANrx_t *buffer; - - /* check if COB ID is already used */ - for (i = 0; i < CANmodule->rxSize; i ++) { - buffer = &CANmodule->rxArray[i]; - - if (i!=index && ident>0 && ident==buffer->ident) { - log_printf(LOG_DEBUG, DBG_CAN_RX_PARAM_FAILED, "duplicate entry"); - ret = CO_ERROR_ILLEGAL_ARGUMENT; - } - } - - if (ret == CO_ERROR_NO) { - /* buffer, which will be configured */ - buffer = &CANmodule->rxArray[index]; - -#ifdef CO_DRIVER_MULTI_INTERFACE - CO_CANsetIdentToIndex(CANmodule->rxIdentToIndex, index, ident, - buffer->ident); -#endif - - /* Configure object variables */ - buffer->object = object; - buffer->pFunct = pFunct; -#ifdef CO_DRIVER_MULTI_INTERFACE - buffer->CANdriverState = NULL; - buffer->timestamp.tv_nsec = 0; - buffer->timestamp.tv_sec = 0; -#endif - - /* CAN identifier and CAN mask, bit aligned with CAN module */ - buffer->ident = ident & CAN_SFF_MASK; - if(rtr){ - buffer->ident |= CAN_RTR_FLAG; - } - buffer->mask = (mask & CAN_SFF_MASK) | CAN_EFF_FLAG | CAN_RTR_FLAG; - - /* Set CAN hardware module filter and mask. */ - CANmodule->rxFilter[index].can_id = buffer->ident; - CANmodule->rxFilter[index].can_mask = buffer->mask; - if(CANmodule->CANnormal){ - ret = setRxFilters(CANmodule); - } - } - } - else { - ret = CO_ERROR_ILLEGAL_ARGUMENT; - } - - return ret; -} - -#ifdef CO_DRIVER_MULTI_INTERFACE - -/******************************************************************************/ -bool_t CO_CANrxBuffer_getInterface( - CO_CANmodule_t *CANmodule, - uint16_t ident, - const void **const CANdriverStateRx, - struct timespec *timestamp) -{ - CO_CANrx_t *buffer; - - if (CANmodule == NULL){ - return false; - } - - const uint32_t index = CO_CANgetIndexFromIdent(CANmodule->rxIdentToIndex, ident); - if ((index == CO_INVALID_COB_ID) || (index > CANmodule->rxSize)) { - return false; - } - buffer = &CANmodule->rxArray[index]; - - /* return values */ - if (CANdriverStateRx != NULL) { - *CANdriverStateRx = buffer->CANdriverState; - } - if (timestamp != NULL) { - *timestamp = buffer->timestamp; - } - if (buffer->CANdriverState != NULL) { - return true; - } - else { - return false; - } -} - -#endif - - -/******************************************************************************/ -CO_CANtx_t *CO_CANtxBufferInit( - CO_CANmodule_t *CANmodule, - uint16_t index, - uint16_t ident, - bool_t rtr, - uint8_t noOfBytes, - bool_t syncFlag) -{ - CO_CANtx_t *buffer = NULL; - - if((CANmodule != NULL) && (index < CANmodule->txSize)){ - /* get specific buffer */ - buffer = &CANmodule->txArray[index]; - -#ifdef CO_DRIVER_MULTI_INTERFACE - CO_CANsetIdentToIndex(CANmodule->txIdentToIndex, index, ident, buffer->ident); -#endif - - buffer->CANdriverState = NULL; - - /* CAN identifier and rtr */ - buffer->ident = ident & CAN_SFF_MASK; - if(rtr){ - buffer->ident |= CAN_RTR_FLAG; - } - buffer->DLC = noOfBytes; - buffer->bufferFull = false; - buffer->syncFlag = syncFlag; - } - - return buffer; -} - -#ifdef CO_DRIVER_MULTI_INTERFACE - -/******************************************************************************/ -CO_ReturnError_t CO_CANtxBuffer_setInterface( - CO_CANmodule_t *CANmodule, - uint16_t ident, - const void *CANdriverStateTx) -{ - if (CANmodule != NULL) { - uint32_t index; - - index = CO_CANgetIndexFromIdent(CANmodule->txIdentToIndex, ident); - if ((index == CO_INVALID_COB_ID) || (index > CANmodule->txSize)) { - return CO_ERROR_PARAMETERS; - } - CANmodule->txArray[index].CANdriverState = CANdriverStateTx; - - return CO_ERROR_NO; - } - return CO_ERROR_PARAMETERS; -} - -#endif - -/******************************************************************************/ -static CO_ReturnError_t CO_CANCheckSendInterface( - CO_CANmodule_t *CANmodule, - CO_CANtx_t *buffer, - CO_CANinterface_t *interface) -{ - CO_ReturnError_t err = CO_ERROR_NO; -#ifdef CO_DRIVER_ERROR_REPORTING - CO_CANinterfaceState_t ifState; -#endif - ssize_t n; - - if (CANmodule==NULL || interface==NULL || interface->fd < 0) { - return CO_ERROR_PARAMETERS; - } - -#ifdef CO_DRIVER_ERROR_REPORTING - ifState = CO_CANerror_txMsg(&interface->errorhandler); - switch (ifState) { - case CO_INTERFACE_ACTIVE: - /* continue */ - break; - case CO_INTERFACE_LISTEN_ONLY: - /* silently drop message */ - return CO_ERROR_NO; - default: - return CO_ERROR_INVALID_STATE; - } -#endif - - do { - errno = 0; - n = send(interface->fd, buffer, CAN_MTU, MSG_DONTWAIT); - if (errno == EINTR) { - /* try again */ - continue; - } - else if (errno == EAGAIN) { - /* socket queue full */ - break; - } - else if (errno == ENOBUFS) { - /* socketCAN doesn't support blocking write. You can wait here for - * a few hundred us and then try again */ - return CO_ERROR_TX_BUSY; - } - else if (n != CAN_MTU) { - break; - } - } while (errno != 0); - - if(n != CAN_MTU){ -#ifdef USE_EMERGENCY_OBJECT - CO_errorReport((CO_EM_t*)CANmodule->em, CO_EM_CAN_TX_OVERFLOW, CO_EMC_CAN_OVERRUN, 0); -#endif - log_printf(LOG_ERR, DBG_CAN_TX_FAILED, buffer->ident, interface->ifName); - log_printf(LOG_DEBUG, DBG_ERRNO, "send()"); - err = CO_ERROR_TX_OVERFLOW; - } - - return err; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_CANsend(CO_CANmodule_t *CANmodule, CO_CANtx_t *buffer) -{ - CO_ReturnError_t err; - err = CO_CANCheckSend(CANmodule, buffer); - if (err == CO_ERROR_TX_BUSY) { - /* send doesn't have "busy" */ -#ifdef USE_EMERGENCY_OBJECT - CO_errorReport((CO_EM_t*)CANmodule->em, CO_EM_CAN_TX_OVERFLOW, CO_EMC_CAN_OVERRUN, 0); -#endif - log_printf(LOG_ERR, DBG_CAN_TX_FAILED, buffer->ident, "CANx"); - log_printf(LOG_DEBUG, DBG_ERRNO, "send()"); - err = CO_ERROR_TX_OVERFLOW; - } - return err; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_CANCheckSend(CO_CANmodule_t *CANmodule, CO_CANtx_t *buffer) -{ - uint32_t i; - CO_ReturnError_t err = CO_ERROR_NO; - - /* check on which interfaces to send this messages */ - for (i = 0; i < CANmodule->CANinterfaceCount; i++) { - CO_CANinterface_t *interface = &CANmodule->CANinterfaces[i]; - - if ((buffer->CANdriverState == NULL) || - buffer->CANdriverState == interface->CANdriverState) { - - CO_ReturnError_t tmp; - - /* match, use this one */ - tmp = CO_CANCheckSendInterface(CANmodule, buffer, interface); - if (tmp) { - /* only last error is returned to callee */ - err = tmp; - } - } - } - - return err; -} - - -/******************************************************************************/ -void CO_CANclearPendingSyncPDOs(CO_CANmodule_t *CANmodule) -{ - /* Messages are either written to the socket queue or dropped */ -} - - -/******************************************************************************/ -void CO_CANverifyErrors(CO_CANmodule_t *CANmodule) -{ - /* socketCAN doesn't support microcontroller-like error counters. If an - * error has occured, a special can message is created by the driver and - * received by the application like a regular message. - * Therefore, error counter evaluation is included in rx function.*/ -} - -/******************************************************************************/ -static CO_ReturnError_t CO_CANread( - CO_CANmodule_t *CANmodule, - CO_CANinterface_t *interface, - struct can_frame *msg, - struct timespec *timestamp) -{ - int32_t n; - uint32_t dropped; - /* recvmsg - like read, but generates statistics about the socket - * example in berlios candump.c */ - struct iovec iov; - struct msghdr msghdr; - char ctrlmsg[CMSG_SPACE(sizeof(struct timeval)) + CMSG_SPACE(sizeof(dropped))]; - struct cmsghdr *cmsg; - - iov.iov_base = msg; - iov.iov_len = sizeof(*msg); - - msghdr.msg_name = NULL; - msghdr.msg_namelen = 0; - msghdr.msg_iov = &iov; - msghdr.msg_iovlen = 1; - msghdr.msg_control = &ctrlmsg; - msghdr.msg_controllen = sizeof(ctrlmsg); - msghdr.msg_flags = 0; - - n = recvmsg(interface->fd, &msghdr, 0); - if (n != CAN_MTU) { -#ifdef USE_EMERGENCY_OBJECT - CO_errorReport((CO_EM_t*)CANmodule->em, CO_EM_CAN_RXB_OVERFLOW, - CO_EMC_CAN_OVERRUN, n); -#endif - log_printf(LOG_DEBUG, DBG_CAN_RX_FAILED, interface->ifName); - log_printf(LOG_DEBUG, DBG_ERRNO, "recvmsg()"); - return CO_ERROR_SYSCALL; - } - - /* check for rx queue overflow, get rx time */ - for (cmsg = CMSG_FIRSTHDR(&msghdr); - cmsg && (cmsg->cmsg_level == SOL_SOCKET); - cmsg = CMSG_NXTHDR(&msghdr, cmsg)) { - if (cmsg->cmsg_type == SO_TIMESTAMPING) { - /* this is system time, not monotonic time! */ - *timestamp = ((struct timespec*)CMSG_DATA(cmsg))[0]; - } - else if (cmsg->cmsg_type == SO_RXQ_OVFL) { - dropped = *(uint32_t*)CMSG_DATA(cmsg); - if (dropped > CANmodule->rxDropCount) { -#ifdef USE_EMERGENCY_OBJECT - CO_errorReport((CO_EM_t*)CANmodule->em, CO_EM_CAN_RXB_OVERFLOW, - CO_EMC_COMMUNICATION, 0); -#endif - log_printf(LOG_ERR, CAN_RX_SOCKET_QUEUE_OVERFLOW, - interface->ifName, dropped); - } - CANmodule->rxDropCount = dropped; - //todo use this info! - } - } - - return CO_ERROR_NO; -} - -static int32_t CO_CANrxMsg( - CO_CANmodule_t *CANmodule, - struct can_frame *msg, - CO_CANrxMsg_t *buffer) -{ - int32_t retval; - const CO_CANrxMsg_t *rcvMsg; /* pointer to received message in CAN module */ - uint16_t index; /* index of received message */ - CO_CANrx_t *rcvMsgObj = NULL; /* receive message object from CO_CANmodule_t object. */ - bool_t msgMatched = false; - - /* CANopenNode can message is binary compatible to the socketCAN one, except - * for extension flags */ - msg->can_id &= CAN_EFF_MASK; - rcvMsg = (CO_CANrxMsg_t *)msg; - - /* Message has been received. Search rxArray from CANmodule for the - * same CAN-ID. */ - rcvMsgObj = &CANmodule->rxArray[0]; - for (index = 0; index < CANmodule->rxSize; index ++) { - if(((rcvMsg->ident ^ rcvMsgObj->ident) & rcvMsgObj->mask) == 0U){ - msgMatched = true; - break; - } - rcvMsgObj++; - } - if(msgMatched) { - /* Call specific function, which will process the message */ - if ((rcvMsgObj != NULL) && (rcvMsgObj->pFunct != NULL)){ - rcvMsgObj->pFunct(rcvMsgObj->object, rcvMsg); - } - /* return message */ - if (buffer != NULL) { - memcpy(buffer, rcvMsg, sizeof(*buffer)); - } - retval = index; - } - else { - retval = -1; - } - - return retval; -} - -/******************************************************************************/ -int32_t CO_CANrxWait(CO_CANmodule_t *CANmodule, int fdTimer, CO_CANrxMsg_t *buffer) -{ - int32_t retval; - int32_t ret; -#ifdef CO_DRIVER_MULTI_INTERFACE - const void *CANdriverState; -#endif - CO_ReturnError_t err; - CO_CANinterface_t *interface = NULL; - struct epoll_event ev[1]; - struct can_frame msg; - struct timespec timestamp; - - if (CANmodule==NULL || CANmodule->CANinterfaceCount==0) { - return -1; - } - - if (fdTimer>=0 && fdTimer!=CANmodule->fdTimerRead) { - /* new timer, timer changed */ - epoll_ctl(CANmodule->fdEpoll, EPOLL_CTL_DEL, CANmodule->fdTimerRead, NULL); - ev[0].events = EPOLLIN; - ev[0].data.fd = fdTimer; - ret = epoll_ctl(CANmodule->fdEpoll, EPOLL_CTL_ADD, ev[0].data.fd, &ev[0]); - if(ret < 0){ - return -1; - } - CANmodule->fdTimerRead = fdTimer; - } - - /* - * blocking read using epoll - */ - do { - errno = 0; - ret = epoll_wait(CANmodule->fdEpoll, ev, sizeof(ev) / sizeof(ev[0]), -1); - if (errno == EINTR) { - /* try again */ - continue; - } - else if (ret < 0) { - /* epoll failed */ - return -1; - } - else if ((ev[0].events & (EPOLLERR | EPOLLHUP)) != 0) { - /* epoll detected close/error on socket. Try to pull event */ - errno = 0; - recv(ev[0].data.fd, &msg, sizeof(msg), MSG_DONTWAIT); - log_printf(LOG_DEBUG, DBG_CAN_RX_EPOLL, ev[0].events, strerror(errno)); - continue; - } - else if ((ev[0].events & EPOLLIN) != 0) { - /* one of the sockets is ready */ - if ((ev[0].data.fd == CO_NotifyPipeGetFd(CANmodule->pipe)) || - (ev[0].data.fd == fdTimer)) { - /* timer/pipe socket */ - return -1; - } - else { - /* CAN socket */ - uint32_t i; - - for (i = 0; i < CANmodule->CANinterfaceCount; i ++) { - interface = &CANmodule->CANinterfaces[i]; - - if (ev[0].data.fd == interface->fd) { -#ifdef CO_DRIVER_MULTI_INTERFACE - /* get interface handle */ - CANdriverState = interface->CANdriverState; -#endif - /* get message */ - err = CO_CANread(CANmodule, interface, &msg, ×tamp); - if (err != CO_ERROR_NO) { - return -1; - } - /* no need to continue search */ - break; - } - } - } - } - } while (errno != 0); - - /* - * evaluate Rx - */ - retval = -1; - if(CANmodule->CANnormal){ - - if (msg.can_id & CAN_ERR_FLAG) { - /* error msg */ -#ifdef CO_DRIVER_ERROR_REPORTING - CO_CANerror_rxMsgError(&interface->errorhandler, &msg); -#endif - } - else { - /* data msg */ - int32_t msgIndex; - -#ifdef CO_DRIVER_ERROR_REPORTING - CO_CANerror_rxMsg(&interface->errorhandler); -#endif - - msgIndex = CO_CANrxMsg(CANmodule, &msg, buffer); - if (msgIndex > -1) { -#ifdef CO_DRIVER_MULTI_INTERFACE - /* Store message info */ - CANmodule->rxArray[msgIndex].timestamp = timestamp; - CANmodule->rxArray[msgIndex].CANdriverState = CANdriverState; -#endif - } - retval = msgIndex; - } - } - return retval; -} diff --git a/stack/neuberger-socketCAN/CO_driver_base.h b/stack/neuberger-socketCAN/CO_driver_base.h deleted file mode 100644 index 01597d39..00000000 --- a/stack/neuberger-socketCAN/CO_driver_base.h +++ /dev/null @@ -1,281 +0,0 @@ -/** - * CAN module object for Linux socketCAN. - * - * @file CO_driver_target.h - * @ingroup CO_driver - * @author Janez Paternoster, Martin Wagner - * @copyright 2004 - 2015 Janez Paternoster, 2018 - 2020 Neuberger Gebaeudeautomation GmbH - * - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef CO_DRIVER_BASE_H -#define CO_DRIVER_BASE_H - -/* Include processor header file */ -#include /* for 'NULL' */ -#include /* for 'int8_t' to 'uint64_t' */ -#include /* for 'true', 'false' */ -#include /* for 'struct timespec' */ -#include -#include -#include -#include - -#include "CO_types.h" - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/** - * Endianness. - * - * Depending on processor or compiler architecture, one of the two macros must - * be defined: CO_LITTLE_ENDIAN or CO_BIG_ENDIAN. CANopen itself is little endian. - */ -#ifdef __BYTE_ORDER -#if __BYTE_ORDER == __LITTLE_ENDIAN - #define CO_LITTLE_ENDIAN -#else - #define CO_BIG_ENDIAN -#endif /* __BYTE_ORDER == __LITTLE_ENDIAN */ -#endif /* __BYTE_ORDER */ - -/** - * @defgroup CO_driver Driver - * @ingroup CO_CANopen - * @{ - * - * socketCAN specific code for CANopenNode. - * - * This file contains type definitions, functions and macros for: - * - Basic data types. - * - Receive and transmit buffers for CANopen messages. - * - Interaction with CAN module on the microcontroller. - * - CAN receive and transmit interrupts. - * - * This file is not only a CAN driver. There are no classic CAN queues for CAN - * messages. This file provides direct connection with other CANopen - * objects. It tries to provide fast responses and tries to avoid unnecessary - * calculations and memory consumptions. - * - * CO_CANmodule_t contains an array of _Received message objects_ (of type - * CO_CANrx_t) and an array of _Transmit message objects_ (of type CO_CANtx_t). - * Each CANopen communication object owns one member in one of the arrays. - * For example Heartbeat producer generates one CANopen transmitting object, - * so it has reserved one member in CO_CANtx_t array. - * SYNC module may produce sync or consume sync, so it has reserved one member - * in CO_CANtx_t and one member in CO_CANrx_t array. - * - * ###Reception of CAN messages. - * Before CAN messages can be received, each member in CO_CANrx_t must be - * initialized. CO_CANrxBufferInit() is called by CANopen module, which - * uses specific member. For example @ref CO_HBconsumer uses multiple members - * in CO_CANrx_t array. (It monitors multiple heartbeat messages from remote - * nodes.) It must call CO_CANrxBufferInit() multiple times. - * - * Main arguments to the CO_CANrxBufferInit() function are CAN identifier - * and a pointer to callback function. Those two arguments (and some others) - * are copied to the member of the CO_CANrx_t array. - * - * Callback function is a function, specified by specific CANopen module - * (for example by @ref CO_HBconsumer). Each CANopen module defines own - * callback function. Callback function will process the received CAN message. - * It will copy the necessary data from CAN message to proper place. It may - * also trigger additional task, which will further process the received message. - * Callback function must be fast and must only make the necessary calculations - * and copying. - * - * Received CAN messages are processed by CAN receive interrupt function. - * After CAN message is received, function first tries to find matching CAN - * identifier from CO_CANrx_t array. If found, then a corresponding callback - * function is called. - * - * Callback function accepts two parameters: - * - object is pointer to object registered by CO_CANrxBufferInit(). - * - msg is pointer to CAN message of type CO_CANrxMsg_t. - * - * Callback function must return #CO_ReturnError_t: CO_ERROR_NO, - * CO_ERROR_RX_OVERFLOW, CO_ERROR_RX_PDO_OVERFLOW, CO_ERROR_RX_MSG_LENGTH or - * CO_ERROR_RX_PDO_LENGTH. - * - * - * ###Transmission of CAN messages. - * Before CAN messages can be transmitted, each member in CO_CANtx_t must be - * initialized. CO_CANtxBufferInit() is called by CANopen module, which - * uses specific member. For example Heartbeat producer must initialize it's - * member in CO_CANtx_t array. - * - * CO_CANtxBufferInit() returns a pointer of type CO_CANtx_t, which contains buffer - * where CAN message data can be written. CAN message is send with calling - * CO_CANsend() function. If at that moment CAN transmit buffer inside - * microcontroller's CAN module is free, message is copied directly to CAN module. - * Otherwise CO_CANsend() function sets _bufferFull_ flag to true. Message will be - * then sent by CAN TX interrupt as soon as CAN module is freed. Until message is - * not copied to CAN module, its contents must not change. There may be multiple - * _bufferFull_ flags in CO_CANtx_t array set to true. In that case messages with - * lower index inside array will be sent first. - */ - - -/** - * @name Critical sections - * CANopenNode is designed to run in different threads, as described in README. - * Threads are implemented differently in different systems. In microcontrollers - * threads are interrupts with different priorities, for example. - * It is necessary to protect sections, where different threads access to the - * same resource. In simple systems interrupts or scheduler may be temporary - * disabled between access to the shared resource. Otherwise mutexes or - * semaphores can be used. - * - * ####Reentrant functions. - * Functions CO_CANsend() from C_driver.h, CO_errorReport() from CO_Emergency.h - * and CO_errorReset() from CO_Emergency.h may be called from different threads. - * Critical sections must be protected. Eather by disabling scheduler or - * interrupts or by mutexes or semaphores. - * - * ####Object Dictionary variables. - * In general, there are two threads, which accesses OD variables: mainline and - * timer. CANopenNode initialization and SDO server runs in mainline. PDOs runs - * in faster timer thread. Processing of PDOs must not be interrupted by - * mainline. Mainline thread must protect sections, which accesses the same OD - * variables as timer thread. This care must also take the application. Note - * that not all variables are allowed to be mapped to PDOs, so they may not need - * to be protected. SDO server protects sections with access to OD variables. - * - * ####CAN receive thread. - * It partially processes received CAN data and puts them into appropriate - * objects. Objects are later processed. It does not need protection of - * critical sections. There is one circumstance, where CANrx should be disabled: - * After presence of SYNC message on CANopen bus, CANrx should be temporary - * disabled until all receive PDOs are processed. See also CO_SYNC.h file and - * CO_SYNC_initCallback() function. - * @{ - */ - -/* unused */ -#define CO_LOCK_CAN_SEND() /**< Lock critical section in CO_CANsend() */ -#define CO_UNLOCK_CAN_SEND()/**< Unlock critical section in CO_CANsend() */ - -extern pthread_mutex_t CO_EMCY_mutex; -static inline int CO_LOCK_EMCY() { return pthread_mutex_lock(&CO_EMCY_mutex); } /**< Lock critical section in CO_errorReport() or CO_errorReset() */ -static inline void CO_UNLOCK_EMCY() { (void)pthread_mutex_unlock(&CO_EMCY_mutex); } /**< Unlock critical section in CO_errorReport() or CO_errorReset() */ - -extern pthread_mutex_t CO_OD_mutex; -static inline int CO_LOCK_OD() { return pthread_mutex_lock(&CO_OD_mutex); } /**< Lock critical section when accessing Object Dictionary */ -static inline void CO_UNLOCK_OD() { (void)pthread_mutex_unlock(&CO_OD_mutex); } /**< Unock critical section when accessing Object Dictionary */ - -/** @} */ - -/** - * @name Syncronisation functions - * syncronisation for message buffer for communication between CAN receive and - * message processing threads. - * - * If receive function runs inside IRQ, no further synchronsiation is needed. - * Otherwise, some kind of synchronsiation has to be included. The following - * example uses GCC builtin memory barrier __sync_synchronize(). A comprehensive - * list can be found here: https://gist.github.com/leo-yuriev/ba186a6bf5cf3a27bae7 - * \code{.c} - #define CANrxMemoryBarrier() {__sync_synchronize();} - * \endcode - * @{ - */ -/** Memory barrier */ -#define CANrxMemoryBarrier() {__sync_synchronize();} -/** Check if new message has arrived */ -#define IS_CANrxNew(rxNew) ((uintptr_t)rxNew) -/** Set new message flag */ -#define SET_CANrxNew(rxNew) {CANrxMemoryBarrier(); rxNew = (void*)1L;} -/** Clear new message flag */ -#define CLEAR_CANrxNew(rxNew) {CANrxMemoryBarrier(); rxNew = (void*)0L;} -/** @} */ - -/** - * @defgroup CO_dataTypes Data types - * @{ - * - * According to Misra C - */ -/* int8_t to uint64_t are defined in stdint.h */ -typedef unsigned char bool_t; /**< bool_t */ -typedef float float32_t; /**< float32_t */ -typedef long double float64_t; /**< float64_t */ -typedef char char_t; /**< char_t */ -typedef unsigned char oChar_t; /**< oChar_t */ -typedef unsigned char domain_t; /**< domain_t */ -/** @} */ - - -/** - * Max COB ID for standard frame format - */ -#define CO_CAN_MSG_SFF_MAX_COB_ID (1 << CAN_SFF_ID_BITS) - -/** - * CAN receive message structure as aligned in socketCAN. - */ -typedef struct{ - /** CAN identifier. It must be read through CO_CANrxMsg_readIdent() function. */ - uint32_t ident; - uint8_t DLC ; /**< Length of CAN message */ - uint8_t padding[3]; /**< ensure alignment */ - uint8_t data[8]; /**< 8 data bytes */ -}CO_CANrxMsg_t; - -/** - * Received message object - */ -typedef struct{ - uint32_t ident; /**< Standard CAN Identifier (bits 0..10) + RTR (bit 11) */ - uint32_t mask; /**< Standard Identifier mask with same alignment as ident */ - void *object; /**< From CO_CANrxBufferInit() */ - void (*pFunct)(void *object, const CO_CANrxMsg_t *message); /**< From CO_CANrxBufferInit() */ - -#ifdef CO_DRIVER_MULTI_INTERFACE - /** info about last received message */ - const void *CANdriverState; /**< CAN Interface identifier */ - struct timespec timestamp; /**< time of reception */ -#endif -}CO_CANrx_t; - - -/** - * Transmit message object as aligned in socketCAN. - */ -typedef struct{ - /** CAN identifier. It must be read through CO_CANrxMsg_readIdent() function. */ - uint32_t ident; - uint8_t DLC ; /**< Length of CAN message */ - uint8_t padding[3]; /**< ensure alignment */ - uint8_t data[8]; /**< 8 data bytes */ - volatile bool_t bufferFull; /**< True if previous message is still in buffer (not used in this driver) */ - /** Synchronous PDO messages has this flag set. It prevents them to be sent outside the synchronous window */ - volatile bool_t syncFlag; - - /** info about transmit message */ - const void *CANdriverState; /**< CAN Interface identifier to use */ -} CO_CANtx_t; - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -/** @} */ -#endif diff --git a/stack/neuberger-socketCAN/CO_driver_target.h b/stack/neuberger-socketCAN/CO_driver_target.h deleted file mode 100644 index 62763d00..00000000 --- a/stack/neuberger-socketCAN/CO_driver_target.h +++ /dev/null @@ -1,296 +0,0 @@ -/** - * CAN module object for Linux socketCAN. - * - * This file is a template for other microcontrollers. - * - * @file CO_driver_target.h - * @ingroup CO_driver - * @author Janez Paternoster, Martin Wagner - * @copyright 2004 - 2015 Janez Paternoster, 2017 - 2020 Neuberger Gebaeudeautomation GmbH - * - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#ifndef CO_DRIVER_TARGET_H -#define CO_DRIVER_TARGET_H - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - - -/** - * @name multi interface support - * - * Enable this to use interface combining at driver level. This - * adds functions to broadcast/selective transmit messages on the - * given interfaces as well as combining all received message into - * one queue. - * - * This is not intended to realize interface redundancy!!! - */ -//#define CO_DRIVER_MULTI_INTERFACE - -/** - * @name CAN bus error reporting - * - * Enable this to add support for socketCAN error detection- and - * handling functions inside the driver. This is needed when you have - * CANopen with "0" connected nodes as a use case, as this is normally - * forbidden in CAN. - * - * you need to enable error reporting in your kernel driver using - * "ip link set canX type can berr-reporting on". Of course, the kernel - * driver for your hardware needs this functionallity to be implemented... - */ -//#define CO_DRIVER_ERROR_REPORTING - - -#include "CO_driver_base.h" -#include "CO_notify_pipe.h" - -#ifdef CO_DRIVER_ERROR_REPORTING - #include "CO_error.h" -#endif /* CO_DRIVER_ERROR_REPORTING */ - -/** - * socketCAN interface object - */ -typedef struct { - const void *CANdriverState; /**< CAN Interface identifier */ - char ifName[IFNAMSIZ]; /**< CAN Interface name */ - int fd; /**< socketCAN file descriptor */ -#ifdef CO_DRIVER_ERROR_REPORTING - CO_CANinterfaceErrorhandler_t errorhandler; -#endif -} CO_CANinterface_t; - -/** - * CAN module object. It may be different in different microcontrollers. - */ -typedef struct{ - /** List of can interfaces. From CO_CANmodule_init()/ one per CO_CANmodule_addInterface() call */ - CO_CANinterface_t *CANinterfaces; - uint32_t CANinterfaceCount; /** interface count */ - CO_CANrx_t *rxArray; /**< From CO_CANmodule_init() */ - uint16_t rxSize; /**< From CO_CANmodule_init() */ - struct can_filter *rxFilter; /**< socketCAN filter list, one per rx buffer */ - uint32_t rxDropCount; /**< messages dropped on rx socket queue */ - CO_CANtx_t *txArray; /**< From CO_CANmodule_init() */ - uint16_t txSize; /**< From CO_CANmodule_init() */ - volatile bool_t CANnormal; /**< CAN module is in normal mode */ - void *em; /**< Emergency object */ - CO_NotifyPipe_t *pipe; /**< Notification Pipe */ - int fdEpoll; /**< epoll FD */ - int fdTimerRead; /**< timer handle from CANrxWait() */ -#ifdef CO_DRIVER_MULTI_INTERFACE - /** - * Lookup tables Cob ID to rx/tx array index. Only feasible for SFF Messages. - */ - uint32_t rxIdentToIndex[CO_CAN_MSG_SFF_MAX_COB_ID]; /**< COB ID to index assignment */ - uint32_t txIdentToIndex[CO_CAN_MSG_SFF_MAX_COB_ID]; /**< COB ID to index assignment */ -#endif /* CO_DRIVER_MULTI_INTERFACE */ -}CO_CANmodule_t; - - -#ifdef CO_DRIVER_MULTI_INTERFACE -/** - * Initialize CAN module object - * - * Function must be called in the communication reset section. CAN module must - * be in Configuration Mode before. - * - * @param CANmodule This object will be initialized. - * @param CANdriverState unused - * @param rxArray Array for handling received CAN messages - * @param rxSize Size of the above array. Must be equal to number of receiving CAN objects. - * @param txArray Array for handling transmitting CAN messages - * @param txSize Size of the above array. Must be equal to number of transmitting CAN objects. - * @param CANbitRate not supported in this driver. Needs to be set by OS - * - * @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_ILLEGAL_ARGUMENT. - */ -#else -/** - * Initialize CAN module object and open socketCAN connection. - * - * Function must be called in the communication reset section. CAN module must - * be in Configuration Mode before. - * - * @param CANmodule This object will be initialized. - * @param CANdriverState CAN module interface index (return value if_nametoindex(), NO pointer!). - * @param rxArray Array for handling received CAN messages - * @param rxSize Size of the above array. Must be equal to number of receiving CAN objects. - * @param txArray Array for handling transmitting CAN messages - * @param txSize Size of the above array. Must be equal to number of transmitting CAN objects. - * @param CANbitRate not supported in this driver. Needs to be set by OS - * - * @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_ILLEGAL_ARGUMENT or - * CO_ERROR_SYSCALL. - */ -#endif /* CO_DRIVER_MULTI_INTERFACE */ -CO_ReturnError_t CO_CANmodule_init( - CO_CANmodule_t *CANmodule, - void *CANdriverState, - CO_CANrx_t rxArray[], - uint16_t rxSize, - CO_CANtx_t txArray[], - uint16_t txSize, - uint16_t CANbitRate); - -#ifdef CO_DRIVER_MULTI_INTERFACE - -/** - * Add socketCAN interface to can driver - * - * Function must be called after CO_CANmodule_init. - * - * @param CANmodule This object will be initialized. - * @param CANdriverState CAN module interface index (return value if_nametoindex(), NO pointer!). - * @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_ILLEGAL_ARGUMENT, - * CO_ERROR_SYSCALL or CO_ERROR_INVALID_STATE. - */ -CO_ReturnError_t CO_CANmodule_addInterface( - CO_CANmodule_t *CANmodule, - const void *CANdriverState); - -#endif /* CO_DRIVER_MULTI_INTERFACE */ - - -/** - * Configure CAN message receive buffer. - * - * Function configures specific CAN receive buffer. It sets CAN identifier - * and connects buffer with specific object. Function must be called for each - * member in _rxArray_ from CO_CANmodule_t. - * - * @param CANmodule This object. - * @param index Index of the specific buffer in _rxArray_. - * @param ident 11-bit standard CAN Identifier. - * @param mask 11-bit mask for identifier. Most usually set to 0x7FF. - * Received message (rcvMsg) will be accepted if the following - * condition is true: (((rcvMsgId ^ ident) & mask) == 0). - * @param rtr If true, 'Remote Transmit Request' messages will be accepted. - * @param object CANopen object, to which buffer is connected. It will be used as - * an argument to pFunct. Its type is (void), pFunct will change its - * type back to the correct object type. - * @param pFunct Pointer to function, which will be called, if received CAN - * message matches the identifier. It must be fast function. - * - * @return #CO_ReturnError_t: CO_ERROR_NO CO_ERROR_ILLEGAL_ARGUMENT or - * CO_ERROR_OUT_OF_MEMORY (not enough masks for configuration). - */ -CO_ReturnError_t CO_CANrxBufferInit( - CO_CANmodule_t *CANmodule, - uint16_t index, - uint16_t ident, - uint16_t mask, - bool_t rtr, - void *object, - void (*pFunct)(void *object, const CO_CANrxMsg_t *message)); - -#ifdef CO_DRIVER_MULTI_INTERFACE - -/** - * Check on which interface the last message for one message buffer was received - * - * It is in the responsibility of the user to check that this information is - * useful as some messages can be received at any time on any bus. - * - * @param CANmodule This object. - * @param ident 11-bit standard CAN Identifier. - * @param [out] CANdriverStateRx message was received on this interface - * @param [out] timestamp message was received at this time (system clock) - * - * @retval false message has never been received, therefore no base address - * and timestamp are available - * @retval true base address and timestamp are valid - */ -bool_t CO_CANrxBuffer_getInterface( - CO_CANmodule_t *CANmodule, - uint16_t ident, - const void **const CANdriverStateRx, - struct timespec *timestamp); - -/** - * Set which interface should be used for message buffer transmission - * - * It is in the responsibility of the user to ensure that the correct interface - * is used. Some messages need to be transmitted on all interfaces. - * - * If given interface is unknown or NULL is used, a message is transmitted on - * all available interfaces. - * - * @param CANmodule This object. - * @param ident 11-bit standard CAN Identifier. - * @param CANdriverStateTx use this interface. NULL = not specified - * - * @return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT. - */ -CO_ReturnError_t CO_CANtxBuffer_setInterface( - CO_CANmodule_t *CANmodule, - uint16_t ident, - const void *CANdriverStateTx); - -#endif /* CO_DRIVER_MULTI_INTERFACE */ - - -/** - * The same as #CO_CANsend(), but ensures that there is enough space remaining - * in the driver for more important messages. - * - * The default threshold is 50%, or at least 1 message buffer. If sending - * would violate those limits, #CO_ERROR_TX_OVERFLOW is returned and the - * message will not be sent. - * - * @param CANmodule This object. - * @param buffer Pointer to transmit buffer, returned by CO_CANtxBufferInit(). - * Data bytes must be written in buffer before function call. - * - * @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_TX_OVERFLOW, CO_ERROR_TX_BUSY or - * CO_ERROR_TX_PDO_WINDOW (Synchronous TPDO is outside window). - */ -CO_ReturnError_t CO_CANCheckSend(CO_CANmodule_t *CANmodule, CO_CANtx_t *buffer); - - -/** - * Functions receives CAN messages. It is blocking. - * - * This function can be used in two ways - * - automatic mode (call callback that is set by #CO_CANrxBufferInit() function) - * - manual mode (evaluate message filters, return received message) - * - * Both modes can be combined. - * - * @param CANmodule This object. - * @param fdTimer file descriptor with activated timeout. fd is not read after - * expiring! -1 if not used. - * @param buffer [out] storage for received message or _NULL_ - * @retval >= 0 index of received message in array set by #CO_CANmodule_init() - * _rxArray_, copy available in _buffer_ - * @retval -1 no message received - */ -int32_t CO_CANrxWait(CO_CANmodule_t *CANmodule, int fdTimer, CO_CANrxMsg_t *buffer); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -/** @} */ -#endif diff --git a/stack/neuberger-socketCAN/CO_error.c b/stack/neuberger-socketCAN/CO_error.c deleted file mode 100644 index 841df2f3..00000000 --- a/stack/neuberger-socketCAN/CO_error.c +++ /dev/null @@ -1,294 +0,0 @@ -/* - * CAN module object for Linux socketCAN Error handling. - * - * @file CO_error.c - * @ingroup CO_driver - * @author Martin Wagner - * @copyright 2018 - 2020 Neuberger Gebaeudeautomation GmbH - * - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include - -#include "CO_error.h" - -#if defined CO_DRIVER_ERROR_REPORTING - #if __has_include("syslog/log.h") - #include "syslog/log.h" - #include "msgs.h" - #else - #include "CO_msgs.h" - #endif -#else - #define log_printf(macropar_prio, macropar_message, ...) -#endif - - -/** - * Reset CAN interface and set to listen only mode - */ -static CO_CANinterfaceState_t CO_CANerrorSetListenOnly( - CO_CANinterfaceErrorhandler_t *CANerrorhandler, - bool_t resetIf) -{ - char command[100]; - - log_printf(LOG_DEBUG, DBG_CAN_SET_LISTEN_ONLY, CANerrorhandler->ifName); - - clock_gettime(CLOCK_MONOTONIC, &CANerrorhandler->timestamp); - CANerrorhandler->listenOnly = true; - - if (resetIf) { - snprintf(command, sizeof(command), "ip link set %s down && " - "ip link set %s up " - "&", - CANerrorhandler->ifName, - CANerrorhandler->ifName); - system(command); - } - - return CO_INTERFACE_LISTEN_ONLY; -} - - -/** - * Clear listen only - */ -static void CO_CANerrorClearListenOnly( - CO_CANinterfaceErrorhandler_t *CANerrorhandler) -{ - log_printf(LOG_DEBUG, DBG_CAN_CLR_LISTEN_ONLY, CANerrorhandler->ifName); - - CANerrorhandler->listenOnly = false; - CANerrorhandler->timestamp.tv_sec = 0; - CANerrorhandler->timestamp.tv_nsec = 0; -} - - -/** - * Check and handle "bus off" state - */ -static CO_CANinterfaceState_t CO_CANerrorBusoff( - CO_CANinterfaceErrorhandler_t *CANerrorhandler, - const struct can_frame *msg) -{ - CO_CANinterfaceState_t result = CO_INTERFACE_ACTIVE; - - if ((msg->can_id & CAN_ERR_BUSOFF) != 0) { - log_printf(LOG_NOTICE, CAN_BUSOFF, CANerrorhandler->ifName); - - /* The can interface changed it's state to "bus off" (e.g. because of - * a short on the can wires). We re-start the interface and mark it - * "listen only". - * Restarting the interface is the only way to clear kernel and hardware - * tx queues */ - result = CO_CANerrorSetListenOnly(CANerrorhandler, true); - } - return result; -} - - -/** - * Check and handle controller problems - */ -static CO_CANinterfaceState_t CO_CANerrorCrtl( - CO_CANinterfaceErrorhandler_t *CANerrorhandler, - const struct can_frame *msg) -{ - CO_CANinterfaceState_t result = CO_INTERFACE_ACTIVE; - - /* Control - * - error counters (rec/tec) are handled inside CAN hardware, nothing - * to do in here - * - we can't really do anything about buffer overflows here. Confirmed - * CANopen protocols will detect the error, non-confirmed protocols - * need to be error tolerant */ - if ((msg->can_id & CAN_ERR_CRTL) != 0) { - if ((msg->data[1] & CAN_ERR_CRTL_RX_PASSIVE) != 0) { - log_printf(LOG_NOTICE, CAN_RX_PASSIVE, CANerrorhandler->ifName); - } - else if ((msg->data[1] & CAN_ERR_CRTL_TX_PASSIVE) != 0) { - log_printf(LOG_NOTICE, CAN_TX_PASSIVE, CANerrorhandler->ifName); - } - else if ((msg->data[1] & CAN_ERR_CRTL_RX_OVERFLOW) != 0) { - log_printf(LOG_NOTICE, CAN_RX_BUF_OVERFLOW, CANerrorhandler->ifName); - } - else if ((msg->data[1] & CAN_ERR_CRTL_TX_OVERFLOW) != 0) { - log_printf(LOG_NOTICE, CAN_TX_BUF_OVERFLOW, CANerrorhandler->ifName); - } - else if ((msg->data[1] & CAN_ERR_CRTL_RX_WARNING) != 0) { - log_printf(LOG_INFO, CAN_RX_LEVEL_WARNING, CANerrorhandler->ifName); - } - else if ((msg->data[1] & CAN_ERR_CRTL_TX_WARNING) != 0) { - log_printf(LOG_INFO, CAN_TX_LEVEL_WARNING, CANerrorhandler->ifName); - } - else if ((msg->data[1] & CAN_ERR_CRTL_ACTIVE) != 0) { - log_printf(LOG_NOTICE, CAN_TX_LEVEL_ACTIVE, CANerrorhandler->ifName); - } - } - return result; -} - - -/** - * Check and handle controller problems - */ -static CO_CANinterfaceState_t CO_CANerrorNoack( - CO_CANinterfaceErrorhandler_t *CANerrorhandler, - const struct can_frame *msg) -{ - CO_CANinterfaceState_t result = CO_INTERFACE_ACTIVE; - - if (CANerrorhandler->listenOnly) { - return CO_INTERFACE_LISTEN_ONLY; - } - - /* received no ACK on transmission */ - if ((msg->can_id & CAN_ERR_ACK) != 0) { - CANerrorhandler->noackCounter ++; - if (CANerrorhandler->noackCounter > CO_CANerror_NOACK_MAX) { - log_printf(LOG_INFO, CAN_NOACK, CANerrorhandler->ifName); - - /* We get the NO-ACK error continuously when no other CAN node - * is active on the bus (Error Counting exception 1 in CAN spec). - * todo - you need to pull the message causing no-ack from the CAN - * hardware buffer. This can be done by either resetting interface - * in here or deleting it within Linux Kernel can driver (set "false"). */ - result = CO_CANerrorSetListenOnly(CANerrorhandler, true); - } - } - else { - CANerrorhandler->noackCounter = 0; - } - return result; -} - - -/******************************************************************************/ -void CO_CANerror_init( - CO_CANinterfaceErrorhandler_t *CANerrorhandler, - int fd, - const char *ifName) -{ - if (CANerrorhandler == NULL) { - return; - } - - CANerrorhandler->fd = fd; - memcpy(CANerrorhandler->ifName, ifName, sizeof(CANerrorhandler->ifName)); - CANerrorhandler->noackCounter = 0; - CANerrorhandler->listenOnly = false; - CANerrorhandler->timestamp.tv_sec = 0; - CANerrorhandler->timestamp.tv_nsec = 0; -} - - -/******************************************************************************/ -void CO_CANerror_disable( - CO_CANinterfaceErrorhandler_t *CANerrorhandler) -{ - if (CANerrorhandler == NULL) { - return; - } - - memset(CANerrorhandler, 0, sizeof(*CANerrorhandler)); - CANerrorhandler->fd = -1; -} - - -/******************************************************************************/ -void CO_CANerror_rxMsg( - CO_CANinterfaceErrorhandler_t *CANerrorhandler) -{ - if (CANerrorhandler == NULL) { - return; - } - - /* someone is active, we can leave listen only immediately */ - if (CANerrorhandler->listenOnly) { - CO_CANerrorClearListenOnly(CANerrorhandler); - } - CANerrorhandler->noackCounter = 0; -} - - -/******************************************************************************/ -CO_CANinterfaceState_t CO_CANerror_txMsg( - CO_CANinterfaceErrorhandler_t *CANerrorhandler) -{ - struct timespec now; - - if (CANerrorhandler == NULL) { - return CO_INTERFACE_BUS_OFF; - } - - if (CANerrorhandler->listenOnly) { - clock_gettime(CLOCK_MONOTONIC, &now); - if (CANerrorhandler->timestamp.tv_sec + CO_CANerror_LISTEN_ONLY < now.tv_sec) { - /* let's try that again. Maybe someone is waiting for LSS now. It - * doesn't matter which message is sent, as all messages are ACKed. */ - CO_CANerrorClearListenOnly(CANerrorhandler); - return CO_INTERFACE_ACTIVE; - } - return CO_INTERFACE_LISTEN_ONLY; - } - return CO_INTERFACE_ACTIVE; -} - - -/******************************************************************************/ -CO_CANinterfaceState_t CO_CANerror_rxMsgError( - CO_CANinterfaceErrorhandler_t *CANerrorhandler, - const struct can_frame *msg) -{ - if (CANerrorhandler == NULL) { - return CO_INTERFACE_BUS_OFF; - } - - CO_CANinterfaceState_t result; - - /* Log all error messages in full to debug log, even if analysis is done - * further on. */ - log_printf(LOG_DEBUG, DBG_CAN_ERROR_GENERAL, (int)msg->can_id, - msg->data[0], msg->data[1], msg->data[2], msg->data[3], - msg->data[4], msg->data[5], msg->data[6], msg->data[7], - CANerrorhandler->ifName); - - /* Process errors - start with the most unambiguous one */ - - result = CO_CANerrorBusoff(CANerrorhandler, msg); - if (result != CO_INTERFACE_ACTIVE) { - return result; - } - - result = CO_CANerrorCrtl(CANerrorhandler, msg); - if (result != CO_INTERFACE_ACTIVE) { - return result; - } - - result = CO_CANerrorNoack(CANerrorhandler, msg); - if (result != CO_INTERFACE_ACTIVE) { - return result; - } - - return CO_INTERFACE_ACTIVE; -} diff --git a/stack/neuberger-socketCAN/CO_error.h b/stack/neuberger-socketCAN/CO_error.h deleted file mode 100644 index c00c7195..00000000 --- a/stack/neuberger-socketCAN/CO_error.h +++ /dev/null @@ -1,145 +0,0 @@ -/** - * CAN module object for Linux socketCAN Error handling. - * - * @file CO_error.h - * @ingroup CO_driver - * @author Martin Wagner - * @copyright 2018 - 2020 Neuberger Gebaeudeautomation GmbH - * - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#ifndef CO_ERROR_H -#define CO_ERROR_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include "CO_driver_base.h" - -/** - * driver interface state - * - * CAN hardware can be in the followning states: - * - error active (OK) - * - error passive (Can't generate error flags) - * - bus off (no influence on bus) - */ -typedef enum { - CO_INTERFACE_ACTIVE, /**< CAN error passive/active */ - CO_INTERFACE_LISTEN_ONLY, /**< CAN error passive/active, but currently no other device on bus */ - CO_INTERFACE_BUS_OFF /**< CAN bus off */ -} CO_CANinterfaceState_t; - - -/** - * This is how many NO-ACKs need to be received in a row to assume - * that no other nodes are connected to a bus and therefore are - * assuming listen-only - */ -#define CO_CANerror_NOACK_MAX 16 - - -/** - * This is how long we are going to block transmission if listen-only - * mode is active - * - * Time is in seconds. - */ -#define CO_CANerror_LISTEN_ONLY 10 - - -/** - * socketCAN interface error handling - */ -typedef struct { - int fd; /**< interface FD */ - char ifName[IFNAMSIZ]; /**< interface name as string */ - - uint32_t noackCounter; - - volatile bool_t listenOnly; /**< set to listen only mode */ - struct timespec timestamp; /**< listen only mode started at this time */ -} CO_CANinterfaceErrorhandler_t; - -/** - * Initialize CAN error handler - * - * We need one error handler per interface - * - * @param CANerrorhandler This object will be initialized. - * @param fd interface file descriptor - * @param ifname interface name as string - */ -void CO_CANerror_init( - CO_CANinterfaceErrorhandler_t *CANerrorhandler, - int fd, - const char *ifname); - -/** - * Reset CAN error handler - * - * @param CANerrorhandler CAN error object. - */ -void CO_CANerror_disable( - CO_CANinterfaceErrorhandler_t *CANerrorhandler); - -/** - * Message received event - * - * when a message is received at least one other CAN module is connected - * - * @param CANerrorhandler CAN error object. - */ -void CO_CANerror_rxMsg( - CO_CANinterfaceErrorhandler_t *CANerrorhandler); - - -/** - * Check if interface is ready for message transmission - * - * message musn't be transmitted if not ready - * - * @param CANerrorhandler CAN error object. - * @return CO_INTERFACE_ACTIVE message transmission ready - */ -CO_CANinterfaceState_t CO_CANerror_txMsg( - CO_CANinterfaceErrorhandler_t *CANerrorhandler); - -/** - * Error message received event - * - * this handles all received error messages - * - * @param CANerrorhandler CAN error object. - * @param msg received error message - * @return #CO_CANinterfaceState_t - */ -CO_CANinterfaceState_t CO_CANerror_rxMsgError( - CO_CANinterfaceErrorhandler_t *CANerrorhandler, - const struct can_frame *msg); - - -#ifdef __cplusplus -} -#endif /*__cplusplus*/ - -/** @} */ -#endif diff --git a/stack/neuberger-socketCAN/CO_msgs.h b/stack/neuberger-socketCAN/CO_msgs.h deleted file mode 100644 index 9a3d8369..00000000 --- a/stack/neuberger-socketCAN/CO_msgs.h +++ /dev/null @@ -1,95 +0,0 @@ -/** - * CAN module object for Linux socketCAN. - * - * This file is a template for other microcontrollers. - * - * @file CO_msgs.h - * @ingroup CO_driver - * @author Martin Wagner - * @copyright 2020 Neuberger Gebaeudeautomation GmbH - * - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#ifndef CO_MSGS_H -#define CO_MSGS_H - -#include -#include -#include "CO_msgs.h" - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/* - * message printing function - */ -#define log_printf(macropar_prio, macropar_message, ...) \ - if (macropar_prio < LOG_DEBUG) { \ - printf(macropar_message, ##__VA_ARGS__); \ - } - -/* - * message logging function. You need to open the log previous to this. - */ -//#define log_printf(macropar_prio, macropar_message, ...) syslog(macropar_prio, macropar_message, ##__VA_ARGS__); - - - -/* - * Message definitions for Linux CANopen socket driver (notice and errors) - */ -#define CAN_NOT_FOUND "CAN Interface \"%s\" not found" -#define CAN_INIT_FAILED "CAN Interface \"%s\" Init failed" -#define CAN_NAMETOINDEX "Interface \"%s\" -> Index %d" -#define CAN_SOCKET_BUF_SIZE "CAN Interface \"%s\" Buffer set to %d messages (%d Bytes)" -#define CAN_BINDING_FAILED "Binding CAN Interface \"%s\" failed" -#define CAN_ERROR_FILTER_FAILED "Setting CAN Interface \"%s\" error filter failed" -#define CAN_FILTER_FAILED "Setting CAN Interface \"%s\" message filter failed" -#define CAN_RX_SOCKET_QUEUE_OVERFLOW "CAN Interface \"%s\" has lost %d messages" -#define CAN_BUSOFF "CAN Interface \"%s\" changed to \"Bus Off\". Switching to Listen Only mode..." -#define CAN_NOACK "CAN Interface \"%s\" no \"ACK\" received. Switching to Listen Only mode..." -#define CAN_RX_PASSIVE "CAN Interface \"%s\" changed state to \"Rx Passive\"" -#define CAN_TX_PASSIVE "CAN Interface \"%s\" changed state to \"Tx Passive\"" -#define CAN_TX_LEVEL_ACTIVE "CAN Interface \"%s\" changed state to \"Active\"" -#define CAN_RX_BUF_OVERFLOW "CAN Interface \"%s\" Rx buffer overflow. Message dropped" -#define CAN_TX_BUF_OVERFLOW "CAN Interface \"%s\" Tx buffer overflow. Message dropped" -#define CAN_RX_LEVEL_WARNING "CAN Interface \"%s\" reached Rx Warning Level" -#define CAN_TX_LEVEL_WARNING "CAN Interface \"%s\" reached Tx Warning Level" - -/* - * Debug - */ -#define DBG_ERRNO "(%s) OS error \"%s\" in %s", __func__, strerror(errno) -#define DBG_CAN_TX_FAILED "(%s) Transmitting CAN msg OID 0x%08x failed(%s)", __func__ -#define DBG_CAN_RX_PARAM_FAILED "(%s) Setting CAN rx buffer failed (%s)", __func__ -#define DBG_CAN_RX_FAILED "(%s) Receiving CAN msg failed (%s)", __func__ -#define DBG_CAN_ERROR_GENERAL "(%s) Socket error msg ID: 0x%08x, Data[0..7]: 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x (%s)", __func__ -#define DBG_CAN_RX_EPOLL "(%s) CAN Epoll error (0x%02x - %s)", __func__ -#define DBG_CAN_SET_LISTEN_ONLY "(%s) %s Set Listen Only", __func__ -#define DBG_CAN_CLR_LISTEN_ONLY "(%s) %s Leave Listen Only", __func__ - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -/** @} */ -#endif diff --git a/stack/neuberger-socketCAN/CO_notify_pipe.c b/stack/neuberger-socketCAN/CO_notify_pipe.c deleted file mode 100644 index a3988285..00000000 --- a/stack/neuberger-socketCAN/CO_notify_pipe.c +++ /dev/null @@ -1,60 +0,0 @@ -/* Snipped from https://stackoverflow.com/a/2486353 */ - -#include -#include -#include -#include - -#include "CO_notify_pipe.h" - -struct CO_NotifyPipe { - int m_receiveFd; - int m_sendFd; -}; - -CO_NotifyPipe_t *CO_NotifyPipeCreate(void) -{ - int pipefd[2]; - CO_NotifyPipe_t *p; - int ret = pipe(pipefd); - - if (ret < 0) { - return NULL; - } - p = calloc(1, sizeof(CO_NotifyPipe_t)); - if (p == NULL) { - return NULL; - } - p->m_receiveFd = pipefd[0]; - p->m_sendFd = pipefd[1]; - fcntl(p->m_sendFd,F_SETFL,O_NONBLOCK); - return p; -} - -void CO_NotifyPipeFree(CO_NotifyPipe_t *p) -{ - if (p == NULL) { - return; - } - close(p->m_sendFd); - close(p->m_receiveFd); - free(p); -} - - -int CO_NotifyPipeGetFd(CO_NotifyPipe_t *p) -{ - if (p == NULL) { - return -1; - } - return p->m_receiveFd; -} - - -void CO_NotifyPipeSend(CO_NotifyPipe_t *p) -{ - if (p == NULL) { - return; - } - write(p->m_sendFd,"1",1); -} diff --git a/stack/neuberger-socketCAN/CO_notify_pipe.h b/stack/neuberger-socketCAN/CO_notify_pipe.h deleted file mode 100644 index b7bea177..00000000 --- a/stack/neuberger-socketCAN/CO_notify_pipe.h +++ /dev/null @@ -1,87 +0,0 @@ -/** - * CAN module object for Linux socketCAN. - * - * This file is a template for other microcontrollers. - * - * @file CO_notify_pipe.h - * @ingroup CO_driver - * @author Martin Wagner - * @copyright 2017 - 2020 Neuberger Gebaeudeautomation GmbH - * - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef CO_NOTIFY_PIPE_H_ -#define CO_NOTIFY_PIPE_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @defgroup CO_pipe Pipe - * @ingroup CO_driver - * @{ - * - * This is needed to wake up the can socket when blocking in select - */ - -/** - * Object - */ -typedef struct CO_NotifyPipe CO_NotifyPipe_t; - -/** - * Create Pipe - * - * @return != null if successfully created - */ -CO_NotifyPipe_t *CO_NotifyPipeCreate(void); - -/** - * Delete Pipe - * - * @param p pointer to object - */ -void CO_NotifyPipeFree(CO_NotifyPipe_t *p); - -/** - * Get file descriptor for select - * - * @param p pointer to object - */ -int CO_NotifyPipeGetFd(CO_NotifyPipe_t *p); - -/** - * Send event - * - * @param p pointer to object - */ -void CO_NotifyPipeSend(CO_NotifyPipe_t *p); - -/** @} */ - -#ifdef __cplusplus -} -#endif /*__cplusplus*/ - -#endif /* CO_NOTIFY_PIPE_H_ */ - -/** -* @} @} -**/ diff --git a/stack/socketCAN/CO_Linux_tasks.c b/stack/socketCAN/CO_Linux_tasks.c deleted file mode 100644 index 33b560c0..00000000 --- a/stack/socketCAN/CO_Linux_tasks.c +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Helper functions for implementing CANopen tasks in Linux using epoll. - * - * @file Linux_tasks.c - * @author Janez Paternoster - * @copyright 2015 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include "CANopen.h" -#include -#include -#include -#include - - -#define NSEC_PER_SEC (1000000000) /* The number of nanoseconds per second. */ -#define NSEC_PER_MSEC (1000000) /* The number of nanoseconds per millisecond. */ - - -/* External helper function ***************************************************/ -void CO_errExit(char* msg); -void CO_error(const uint32_t info); - - -/* Mainline task (taskMain) ***************************************************/ -static struct { - int fdTmr; /* file descriptor for taskTmr */ - int fdPipe[2]; /* file descriptors for pipe [0]=read, [1]=write */ - struct itimerspec tmrSpec; - uint16_t tmr1msPrev; - uint16_t *maxTime; -} taskMain; - - -void taskMain_init(int fdEpoll, uint16_t *maxTime) { - struct epoll_event ev; - int flags; - - /* Prepare pipe for triggering events. For example, if new SDO request - * arrives from CAN network, CANrx callback writes a byte into the pipe. - * This immediately triggers (via epoll) processing of SDO server, which - * generates response. Read and write ends of pipe are nonblocking. - * (See 'self pipe trick'.) */ - if(pipe(taskMain.fdPipe) == -1) - CO_errExit("taskMain_init - pipe failed"); - - flags = fcntl(taskMain.fdPipe[0], F_GETFL); - if(flags == -1) - CO_errExit("taskMain_init - fcntl-F_GETFL[0] failed"); - flags |= O_NONBLOCK; - if(fcntl(taskMain.fdPipe[0], F_SETFL, flags) == -1) - CO_errExit("taskMain_init - fcntl-F_SETFL[0] failed"); - - flags = fcntl(taskMain.fdPipe[1], F_GETFL); - if(flags == -1) - CO_errExit("taskMain_init - fcntl-F_GETFL[1] failed"); - flags |= O_NONBLOCK; - if(fcntl(taskMain.fdPipe[1], F_SETFL, flags) == -1) - CO_errExit("taskMain_init - fcntl-F_SETFL[1] failed"); - - /* get file descriptor for timer */ - taskMain.fdTmr = timerfd_create(CLOCK_MONOTONIC, 0); - if(taskMain.fdTmr == -1) - CO_errExit("taskMain_init - timerfd_create failed"); - - /* add events for epoll */ - ev.events = EPOLLIN; - ev.data.fd = taskMain.fdPipe[0]; - if(epoll_ctl(fdEpoll, EPOLL_CTL_ADD, taskMain.fdPipe[0], &ev) == -1) - CO_errExit("taskMain_init - epoll_ctl CANrx failed"); - - ev.events = EPOLLIN; - ev.data.fd = taskMain.fdTmr; - if(epoll_ctl(fdEpoll, EPOLL_CTL_ADD, taskMain.fdTmr, &ev) == -1) - CO_errExit("taskMain_init - epoll_ctl taskTmr failed"); - - /* Prepare timer, use no interval, delay time will be set each cycle. */ - taskMain.tmrSpec.it_interval.tv_sec = 0; - taskMain.tmrSpec.it_interval.tv_nsec = 0; - - taskMain.tmrSpec.it_value.tv_sec = 0; - taskMain.tmrSpec.it_value.tv_nsec = 1; - - if(timerfd_settime(taskMain.fdTmr, 0, &taskMain.tmrSpec, NULL) != 0) - CO_errExit("taskMain_init - timerfd_settime failed"); - - taskMain.tmr1msPrev = 0; - taskMain.maxTime = maxTime; -} - - -void taskMain_close(void) { - close(taskMain.fdPipe[0]); - close(taskMain.fdPipe[1]); - close(taskMain.fdTmr); -} - - -bool_t taskMain_process(int fd, CO_NMT_reset_cmd_t *reset, uint16_t timer1ms) { - bool_t wasProcessed = true; - - /* Signal from pipe, consume all bytes. */ - if(fd == taskMain.fdPipe[0]) { - for(;;) { - char ch; - if(read(taskMain.fdPipe[0], &ch, 1) == -1) { - if (errno == EAGAIN) - break; /* No more bytes. */ - else - CO_error(0x21100000L + errno); - } - } - } - - /* Timer expired. */ - else if(fd == taskMain.fdTmr) { - uint64_t tmrExp; - if(read(taskMain.fdTmr, &tmrExp, sizeof(tmrExp)) != sizeof(uint64_t)) - CO_error(0x21200000L + errno); - } - else { - wasProcessed = false; - } - - /* Process mainline. */ - if(wasProcessed) { - uint16_t timer1msDiff; - uint16_t timerNext = 50; - - /* Calculate time difference */ - timer1msDiff = timer1ms - taskMain.tmr1msPrev; - taskMain.tmr1msPrev = timer1ms; - - /* Calculate maximum interval in milliseconds (informative) */ - if(taskMain.maxTime != NULL) { - if(timer1msDiff > *taskMain.maxTime) { - *taskMain.maxTime = timer1msDiff; - } - } - - - /* CANopen process */ - *reset = CO_process(CO, timer1msDiff, &timerNext); - - - /* Set delay for next sleep. */ - taskMain.tmrSpec.it_value.tv_nsec = (long)(++timerNext) * NSEC_PER_MSEC; - if(timerfd_settime(taskMain.fdTmr, 0, &taskMain.tmrSpec, NULL) == -1) - CO_error(0x21500000L + errno); - - } - - return wasProcessed; -} - - -void taskMain_cbSignal(void) { - if(write(taskMain.fdPipe[1], "x", 1) == -1) - CO_error(0x23100000L + errno); -} - - -/* Realtime task (taskRT) *****************************************************/ -static struct { - int fdRx0; /* file descriptor for CANrx */ - int fdTmr; /* file descriptor for taskTmr */ - struct itimerspec tmrSpec; - struct timespec *tmrVal; - long intervalns; - long intervalus; - uint16_t *maxTime; -} taskRT; - - -void CANrx_taskTmr_init(int fdEpoll, long intervalns, uint16_t *maxTime) { - struct epoll_event ev; - - /* get file descriptors */ - taskRT.fdRx0 = CO->CANmodule[0]->fd; - - taskRT.fdTmr = timerfd_create(CLOCK_MONOTONIC, 0); - if(taskRT.fdTmr == -1) - CO_errExit("CANrx_taskTmr_init - timerfd_create failed"); - - /* add events for epoll */ - ev.events = EPOLLIN; - ev.data.fd = taskRT.fdRx0; - if(epoll_ctl(fdEpoll, EPOLL_CTL_ADD, taskRT.fdRx0, &ev) == -1) - CO_errExit("CANrx_taskTmr_init - epoll_ctl CANrx failed"); - - ev.events = EPOLLIN; - ev.data.fd = taskRT.fdTmr; - if(epoll_ctl(fdEpoll, EPOLL_CTL_ADD, taskRT.fdTmr, &ev) == -1) - CO_errExit("CANrx_taskTmr_init - epoll_ctl taskTmr failed"); - - /* Prepare timer (one shot, each time calculate new expiration time) It is - * necessary not to use taskRT.tmrSpec.it_interval, because it is sliding. */ - taskRT.tmrSpec.it_interval.tv_sec = 0; - taskRT.tmrSpec.it_interval.tv_nsec = 0; - - taskRT.tmrVal = &taskRT.tmrSpec.it_value; - if(clock_gettime(CLOCK_MONOTONIC, taskRT.tmrVal) != 0) - CO_errExit("CANrx_taskTmr_init - clock_gettime failed"); - - if(timerfd_settime(taskRT.fdTmr, TFD_TIMER_ABSTIME, &taskRT.tmrSpec, NULL) != 0) - CO_errExit("CANrx_taskTmr_init - timerfd_settime failed"); - - taskRT.intervalns = intervalns; - taskRT.intervalus = intervalns / 1000; - taskRT.maxTime = maxTime; -} - - -void CANrx_taskTmr_close(void) { - close(taskRT.fdTmr); -} - - -bool_t CANrx_taskTmr_process(int fd) { - bool_t wasProcessed = true; - - /* Get received CAN message. */ - if(fd == taskRT.fdRx0) { - CO_CANrxWait(CO->CANmodule[0]); - } - - /* Execute taskTmr */ - else if(fd == taskRT.fdTmr) { - uint64_t tmrExp; - - /* Wait for timer to expire */ - if(read(taskRT.fdTmr, &tmrExp, sizeof(tmrExp)) != sizeof(uint64_t)) - CO_error(0x22100000L + errno); - - /* Calculate maximum interval in microseconds (informative) */ - if(taskRT.maxTime != NULL) { - struct timespec tmrMeasure; - if(clock_gettime(CLOCK_MONOTONIC, &tmrMeasure) == -1) - CO_error(0x22200000L + errno); - if(tmrMeasure.tv_sec == taskRT.tmrVal->tv_sec) { - long dt = tmrMeasure.tv_nsec - taskRT.tmrVal->tv_nsec; - dt /= 1000; - dt += taskRT.intervalus; - if(dt > 0xFFFF) { - *taskRT.maxTime = 0xFFFF; - }else if(dt > *taskRT.maxTime) { - *taskRT.maxTime = (uint16_t) dt; - } - } - } - - /* Calculate next shot for the timer */ - taskRT.tmrVal->tv_nsec += taskRT.intervalns; - if(taskRT.tmrVal->tv_nsec >= NSEC_PER_SEC) { - taskRT.tmrVal->tv_nsec -= NSEC_PER_SEC; - taskRT.tmrVal->tv_sec++; - } - if(timerfd_settime(taskRT.fdTmr, TFD_TIMER_ABSTIME, &taskRT.tmrSpec, NULL) == -1) - CO_error(0x22300000L + errno); - - - /* Lock PDOs and OD */ - CO_LOCK_OD(); - - if(CO->CANmodule[0]->CANnormal) { - bool_t syncWas; - - /* Process Sync */ - syncWas = CO_process_SYNC(CO, taskRT.intervalus); - - /* Read inputs */ - CO_process_RPDO(CO, syncWas); - - /* Further I/O or nonblocking application code may go here. */ - - /* Write outputs */ - CO_process_TPDO(CO, syncWas, taskRT.intervalus); - } - - /* Unlock */ - CO_UNLOCK_OD(); - } - - else { - wasProcessed = false; - } - - return wasProcessed; -} diff --git a/stack/socketCAN/CO_Linux_tasks.h b/stack/socketCAN/CO_Linux_tasks.h deleted file mode 100644 index fdf9fb82..00000000 --- a/stack/socketCAN/CO_Linux_tasks.h +++ /dev/null @@ -1,113 +0,0 @@ -/** - * Helper functions for implementing CANopen tasks in Linux using epoll. - * - * @file Linux_tasks.h - * @author Janez Paternoster - * @copyright 2015 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#ifndef CO_LINUX_TASKS_H -#define CO_LINUX_TASKS_H - - -/** - * Initialize mainline task. - * - * taskMain is non-realtime task for CANopenNode processing. It is nonblocking - * and is executing cyclically in 50 ms intervals or less if necessary. - * It uses Linux epoll, timerfd for interval and pipe for task triggering. - * This task processes CO_process() function from CANopen.c file. - * - * @param fdEpoll File descriptor for Linux epoll API. - * @param maxTime Pointer to variable, where longest interval will be written - * [in milliseconds]. If NULL, calculations won't be made. - */ -void taskMain_init(int fdEpoll, uint16_t *maxTime); - -/** - * Cleanup mainline task. - */ -void taskMain_close(void); - -/** - * Process mainline task. - * - * Function must be called after epoll. - * - * @param fd Available file descriptor from epoll(). - * @param reset return value from CO_process() function. - * @param timer1ms variable, which must increment each millisecond. - * - * @return True, if fd was matched. - */ -bool_t taskMain_process(int fd, CO_NMT_reset_cmd_t *reset, uint16_t timer1ms); - -/** - * Signal function, which triggers mainline task. - * - * It is used from some CANopenNode objects as callback. - */ -void taskMain_cbSignal(void); - - -/** - * Initialize realtime task. - * - * CANrx_taskTmr is realtime task for CANopenNode processing. It is nonblocking - * and is executing on CAN message receive or periodically in 1ms (or something) - * intervals. Inside interval is processed CANopen SYNC message, RPDOs(inputs) - * and TPDOs(outputs). Between inputs and outputs can also be executed some - * realtime application code. - * CANrx_taskTmr uses Linux epoll, CAN socket form CO_driver.c and timerfd for - * interval. - * - * - * @param fdEpoll File descriptor for Linux epoll API. - * @param intervalns Interval of periodic timer in nanoseconds. - * @param maxTime Pointer to variable, where longest interval will be written - * [in milliseconds]. If NULL, calculations won't be made. - */ -void CANrx_taskTmr_init(int fdEpoll, long intervalns, uint16_t *maxTime); - -/** - * Cleanup realtime task. - */ -void CANrx_taskTmr_close(void); - -/** - * Process realtime task. - * - * Function must be called after epoll. - * - * @param fd Available file descriptor from epoll(). - * - * @return True, if fd was matched. - */ -bool_t CANrx_taskTmr_process(int fd); - -/** - * Disable CAN receive thread temporary. - * - * Function is called at SYNC message on CAN bus. - * It disables CAN receive thread until RPDOs are processed. - */ -void CANrx_lockCbSync(bool_t syncReceived); - -#endif diff --git a/stack/socketCAN/CO_OD_storage.c b/stack/socketCAN/CO_OD_storage.c deleted file mode 100644 index bce6e701..00000000 --- a/stack/socketCAN/CO_OD_storage.c +++ /dev/null @@ -1,378 +0,0 @@ -/* - * CANopen Object Dictionary storage object for Linux SocketCAN. - * - * @file CO_OD_storage.c - * @author Janez Paternoster - * @copyright 2015 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include "CO_driver.h" -#include "CO_SDO.h" -#include "CO_Emergency.h" -#include "CO_OD_storage.h" -#include "crc16-ccitt.h" - -#include -#include /* for memcpy */ -#include /* for malloc, free */ - - -#define RETURN_SUCCESS 0 -#define RETURN_ERROR -1 - - -/******************************************************************************/ -CO_SDO_abortCode_t CO_ODF_1010(CO_ODF_arg_t *ODF_arg) { - CO_OD_storage_t *odStor; - uint32_t value; - CO_SDO_abortCode_t ret = CO_SDO_AB_NONE; - - odStor = (CO_OD_storage_t*) ODF_arg->object; - value = CO_getUint32(ODF_arg->data); - - if(!ODF_arg->reading) { - /* don't change the old value */ - CO_memcpy(ODF_arg->data, (const uint8_t*)ODF_arg->ODdataStorage, 4U); - - if(ODF_arg->subIndex == 1) { - /* store parameters */ - if(value == 0x65766173UL) { - if(CO_OD_storage_saveSecure(odStor->odAddress, odStor->odSize, odStor->filename) != 0) { - ret = CO_SDO_AB_HW; - } - } - else { - ret = CO_SDO_AB_DATA_TRANSF; - } - } - } - - return ret; -} - - -/******************************************************************************/ -CO_SDO_abortCode_t CO_ODF_1011(CO_ODF_arg_t *ODF_arg) { - CO_OD_storage_t *odStor; - uint32_t value; - CO_SDO_abortCode_t ret = CO_SDO_AB_NONE; - - odStor = (CO_OD_storage_t*) ODF_arg->object; - value = CO_getUint32(ODF_arg->data); - - if(!ODF_arg->reading) { - /* don't change the old value */ - CO_memcpy(ODF_arg->data, (const uint8_t*)ODF_arg->ODdataStorage, 4U); - - if(ODF_arg->subIndex >= 1) { - /* restore default parameters */ - if(value == 0x64616F6CUL) { - if(CO_OD_storage_restoreSecure(odStor->filename) != 0) { - ret = CO_SDO_AB_HW; - } - } - else { - ret = CO_SDO_AB_DATA_TRANSF; - } - } - } - - return ret; -} - - -/******************************************************************************/ -int CO_OD_storage_saveSecure( - uint8_t *odAddress, - uint32_t odSize, - char *filename) -{ - int ret = RETURN_SUCCESS; - - char *filename_old = NULL; - uint16_t CRC = 0; - - /* Generate new string with extension '.old' and rename current file to it. */ - filename_old = malloc(strlen(filename)+10); - if(filename_old != NULL) { - strcpy(filename_old, filename); - strcat(filename_old, ".old"); - - remove(filename_old); - if(rename(filename, filename_old) != 0) { - ret = RETURN_ERROR; - } - } else { - ret = RETURN_ERROR; - } - - /* Open a new file and write data to it, including CRC. */ - if(ret == RETURN_SUCCESS) { - FILE *fp = fopen(filename, "w"); - if(fp != NULL) { - - CO_LOCK_OD(); - fwrite((const void *)odAddress, 1, odSize, fp); - CRC = crc16_ccitt((unsigned char*)odAddress, odSize, 0); - CO_UNLOCK_OD(); - - fwrite((const void *)&CRC, 1, 2, fp); - fclose(fp); - } else { - ret = RETURN_ERROR; - } - } - - /* Verify data */ - if(ret == RETURN_SUCCESS) { - void *buf = NULL; - FILE *fp = NULL; - uint32_t cnt = 0; - uint16_t CRC2 = 0; - - buf = malloc(odSize + 4); - if(buf != NULL) { - fp = fopen(filename, "r"); - if(fp != NULL) { - cnt = fread(buf, 1, odSize, fp); - CRC2 = crc16_ccitt((unsigned char*)buf, odSize, 0); - /* read also two bytes of CRC */ - cnt += fread(buf, 1, 4, fp); - fclose(fp); - } - free(buf); - } - /* If size or CRC differs, report error */ - if(buf == NULL || fp == NULL || cnt != (odSize + 2) || CRC != CRC2) { - ret = RETURN_ERROR; - } - } - - /* In case of error, set back the old file. */ - if(ret != RETURN_SUCCESS && filename_old != NULL) { - remove(filename); - rename(filename_old, filename); - } - - free(filename_old); - - return ret; -} - - -/******************************************************************************/ -int CO_OD_storage_restoreSecure(char *filename) { - int ret = RETURN_SUCCESS; - FILE *fp = NULL; - - /* If filename already exists, rename it to '.old'. */ - fp = fopen(filename, "r"); - if(fp != NULL) { - char *filename_old = NULL; - - fclose(fp); - - filename_old = malloc(strlen(filename)+10); - if(filename_old != NULL) { - strcpy(filename_old, filename); - strcat(filename_old, ".old"); - - remove(filename_old); - if(rename(filename, filename_old) != 0) { - ret = RETURN_ERROR; - } - free(filename_old); - } - else { - ret = RETURN_ERROR; - } - } - - /* create an empty file and write "-\n" to it. */ - if(ret == RETURN_SUCCESS) { - fp = fopen(filename, "w"); - if(fp != NULL) { - fputs("-\n", fp); - fclose(fp); - } else { - ret = RETURN_ERROR; - } - } - - return ret; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_OD_storage_init( - CO_OD_storage_t *odStor, - uint8_t *odAddress, - uint32_t odSize, - char *filename) -{ - CO_ReturnError_t ret = CO_ERROR_NO; - void *buf = NULL; - - /* verify arguments */ - if(odStor==NULL || odAddress==NULL) { - ret = CO_ERROR_ILLEGAL_ARGUMENT; - } - - /* configure object variables and allocate buffer */ - if(ret == CO_ERROR_NO) { - odStor->odAddress = odAddress; - odStor->odSize = odSize; - odStor->filename = filename; - odStor->fp = NULL; - odStor->tmr1msPrev = 0; - odStor->lastSavedMs = 0; - - buf = malloc(odStor->odSize); - if(buf == NULL) { - ret = CO_ERROR_OUT_OF_MEMORY; - } - } - - /* read data from the file and verify CRC */ - if(ret == CO_ERROR_NO) { - FILE *fp; - uint32_t cnt = 0; - uint16_t CRC[2]; - - fp = fopen(odStor->filename, "r"); - if(fp) { - cnt = fread(buf, 1, odStor->odSize, fp); - /* read also two bytes of CRC from file */ - cnt += fread(&CRC[0], 1, 4, fp); - CRC[1] = crc16_ccitt((unsigned char*)buf, odStor->odSize, 0); - fclose(fp); - } - - if(cnt == 2 && *((char*)buf) == '-') { - /* file is empty, default values will be used, no error */ - ret = CO_ERROR_NO; - } - else if(cnt != (odStor->odSize + 2)) { - /* file length does not match */ - ret = CO_ERROR_DATA_CORRUPT; - } - else if(CRC[0] != CRC[1]) { - /* CRC does not match */ - ret = CO_ERROR_CRC; - } - else { - /* no errors, copy data into Object dictionary */ - memcpy(odStor->odAddress, buf, odStor->odSize); - } - } - - free(buf); - - return ret; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_OD_storage_autoSave( - CO_OD_storage_t *odStor, - uint16_t timer1ms, - uint16_t delay) -{ - CO_ReturnError_t ret = CO_ERROR_NO; - - /* verify arguments */ - if(odStor==NULL || odStor->odAddress==NULL) { - ret = CO_ERROR_ILLEGAL_ARGUMENT; - } - - /* don't save file more often than delay */ - if(odStor->lastSavedMs < delay) { - odStor->lastSavedMs += timer1ms - odStor->tmr1msPrev; - } - else { - void *buf = NULL; - bool_t saveData = false; - - /* allocate buffer and open file if necessary */ - if(ret == CO_ERROR_NO) { - buf = malloc(odStor->odSize); - if(odStor->fp == NULL) { - odStor->fp = fopen(odStor->filename, "r+"); - } - if(buf == NULL || odStor->fp == NULL) { - ret = CO_ERROR_OUT_OF_MEMORY; - } - } - - /* read data from the beginning of the file */ - if(ret == CO_ERROR_NO) { - uint32_t cnt = 0; - - rewind(odStor->fp); - cnt = fread(buf, 1, odStor->odSize, odStor->fp); - - if(cnt == 2 && *((char*)buf) == '-') { - /* file is empty, data will be saved. */ - saveData = true; - } - else if(cnt == odStor->odSize) { - /* verify, if data differs */ - if(memcmp((const void *)buf, (const void *)odStor->odAddress, odStor->odSize) != 0) { - saveData = true; - } - } - else { - /* file length does not match */ - ret = CO_ERROR_DATA_CORRUPT; - } - } - - /* Save the data to the file only if data differs. */ - if(ret == CO_ERROR_NO && saveData) { - uint16_t CRC; - - /* copy data to temporary buffer */ - memcpy(buf, odStor->odAddress, odStor->odSize); - - rewind(odStor->fp); - fwrite((const void *)buf, 1, odStor->odSize, odStor->fp); - - /* write also CRC */ - CRC = crc16_ccitt((unsigned char*)buf, odStor->odSize, 0); - fwrite((const void *)&CRC, 1, 2, odStor->fp); - - fflush(odStor->fp); - - odStor->lastSavedMs = 0; - } - - free(buf); - } - - odStor->tmr1msPrev = timer1ms; - - return ret; -} - -void CO_OD_storage_autoSaveClose(CO_OD_storage_t *odStor) { - if(odStor->fp != NULL) { - fclose(odStor->fp); - } -} diff --git a/stack/socketCAN/CO_OD_storage.h b/stack/socketCAN/CO_OD_storage.h deleted file mode 100644 index 23aab080..00000000 --- a/stack/socketCAN/CO_OD_storage.h +++ /dev/null @@ -1,151 +0,0 @@ -/** - * CANopen Object Dictionary storage object for Linux SocketCAN. - * - * @file CO_OD_storage.h - * @author Janez Paternoster - * @copyright 2015 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#ifndef CO_OD_STORAGE_H -#define CO_OD_STORAGE_H - - -#include "CO_driver.h" -#include "CO_SDO.h" - -#include - - -/* For documentation see file drvTemplate/CO_OD_storage.h */ - - -/** - * Callbacks for using inside @ref CO_OD_configure() function (for OD objects 1010 and 1011). - */ -CO_SDO_abortCode_t CO_ODF_1010(CO_ODF_arg_t *ODF_arg); -CO_SDO_abortCode_t CO_ODF_1011(CO_ODF_arg_t *ODF_arg); - - -/** - * Save memory block to a file. - * - * Function renames current file to filename.old, copies contents from odAddress - * to filename, adds two bytes of CRC code. It then verifies the written file and - * in case of errors sets back the old file and returns error. - * - * Function is used with CANopen OD object at index 1010. - * - * @param odAddress Address of the memory block, which will be stored. - * @param odSize Size of the above memory block. - * @param filename Name of the file, where data will be stored. - * - * @return 0 on success, -1 on error. - */ -int CO_OD_storage_saveSecure( - uint8_t *odAddress, - uint32_t odSize, - char *filename); - - -/** - * Remove OD storage file. - * - * Function renames current file to filename.old, then creates empty file and - * writes two bytes "-\n" to it. When program will start next time, default values - * are used for Object Dictionary. In case of error in renaming to .old it - * keeps the original file and returns error. - * - * Writing data to file is secured with mutex CO_LOCK_OD. - * - * Function is used with CANopen OD object at index 1011. - * - * @param filename Name of the file. - * - * @return 0 on success, -1 on error. - */ -int CO_OD_storage_restoreSecure(char *filename); - - -/** - * Object Dictionary storage object. - * - * Object is used with CANopen OD objects at index 1010 and 1011. - */ -typedef struct { - uint8_t *odAddress; /**< From CO_OD_storage_init() */ - uint32_t odSize; /**< From CO_OD_storage_init() */ - char *filename; /**< From CO_OD_storage_init() */ - /** If CO_OD_storage_autoSave() is used, file stays opened and fp is stored here. */ - FILE *fp; - uint16_t tmr1msPrev; /**< used with CO_OD_storage_autoSave. */ - uint32_t lastSavedMs; /**< used with CO_OD_storage_autoSave. */ -} CO_OD_storage_t; - - -/** - * Initialize OD storage object and load data from file. - * - * Called after program startup. Load storage file and copy data to Object - * Dictionary variables. - * - * @param odStor This object will be initialized. - * @param odAddress Address of the memory block from Object dictionary, where data will be copied. - * @param odSize Size of the above memory block. - * @param filename Name of the file, where data are stored. - * - * @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_DATA_CORRUPT (Data in file corrupt), - * CO_ERROR_CRC (CRC from MBR does not match the CRC of OD_ROM block in file), - * CO_ERROR_ILLEGAL_ARGUMENT or CO_ERROR_OUT_OF_MEMORY (malloc failed). - */ -CO_ReturnError_t CO_OD_storage_init( - CO_OD_storage_t *odStor, - uint8_t *odAddress, - uint32_t odSize, - char *filename); - - -/** - * Automatically save memory block if differs from file. - * - * Should be called cyclically by program. It first verifies, if memory block - * differs from file and if it does, it saves it to file with two additional - * CRC bytes. File remains opened. - * - * @param odStor OD storage object. - * @param timer1ms Variable, which must increment each millisecond. - * @param delay Delay (inhibit) time between writes to disk in milliseconds (60000 for example). - * - * @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_DATA_CORRUPT (Data in file corrupt), - * CO_ERROR_ILLEGAL_ARGUMENT or CO_ERROR_OUT_OF_MEMORY (malloc failed). - */ -CO_ReturnError_t CO_OD_storage_autoSave( - CO_OD_storage_t *odStor, - uint16_t timer1ms, - uint16_t delay); - - -/** - * Closes file opened by CO_OD_storage_autoSave. - * - * @param odStor OD storage object. - */ -void CO_OD_storage_autoSaveClose(CO_OD_storage_t *odStor); - -#endif diff --git a/stack/socketCAN/CO_driver.c b/stack/socketCAN/CO_driver.c deleted file mode 100644 index 60c861f1..00000000 --- a/stack/socketCAN/CO_driver.c +++ /dev/null @@ -1,444 +0,0 @@ -/* - * CAN module object for Linux SocketCAN. - * - * @file CO_driver.c - * @author Janez Paternoster - * @copyright 2015 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include "CO_driver.h" -#include "CO_Emergency.h" -#include /* for memcpy */ -#include /* for malloc, free */ -#include -#include - - -/******************************************************************************/ -#ifndef CO_SINGLE_THREAD - pthread_mutex_t CO_EMCY_mtx = PTHREAD_MUTEX_INITIALIZER; - pthread_mutex_t CO_OD_mtx = PTHREAD_MUTEX_INITIALIZER; -#endif - - -/** Set socketCAN filters *****************************************************/ -static CO_ReturnError_t setFilters(CO_CANmodule_t *CANmodule){ - CO_ReturnError_t ret = CO_ERROR_NO; - - if(CANmodule->useCANrxFilters){ - int nFiltersIn, nFiltersOut; - struct can_filter *filtersOut; - - nFiltersIn = CANmodule->rxSize; - nFiltersOut = 0; - filtersOut = (struct can_filter *) calloc(nFiltersIn, sizeof(struct can_filter)); - - if(filtersOut == NULL){ - ret = CO_ERROR_OUT_OF_MEMORY; - }else{ - int i; - int idZeroCnt = 0; - - /* Copy filterIn to filtersOut. Accept only first filter with - * can_id=0, omit others. */ - for(i=0; ifilter[i]; - if(fin->can_id == 0){ - idZeroCnt++; - } - if(fin->can_id != 0 || idZeroCnt == 1){ - struct can_filter *fout; - - fout = &filtersOut[nFiltersOut++]; - fout->can_id = fin->can_id; - fout->can_mask = fin->can_mask; - } - } - - if(setsockopt(CANmodule->fd, SOL_CAN_RAW, CAN_RAW_FILTER, - filtersOut, sizeof(struct can_filter) * nFiltersOut) != 0) - { - ret = CO_ERROR_ILLEGAL_ARGUMENT; - } - - free(filtersOut); - } - }else{ - /* Use one socketCAN filter, match any CAN address, including extended and rtr. */ - CANmodule->filter[0].can_id = 0; - CANmodule->filter[0].can_mask = 0; - if(setsockopt(CANmodule->fd, SOL_CAN_RAW, CAN_RAW_FILTER, - &CANmodule->filter[0], sizeof(struct can_filter)) != 0) - { - ret = CO_ERROR_ILLEGAL_ARGUMENT; - } - } - - return ret; -} - - -/******************************************************************************/ -void CO_CANsetConfigurationMode(void *CANdriverState){ -} - - -/******************************************************************************/ -void CO_CANsetNormalMode(CO_CANmodule_t *CANmodule){ - /* set CAN filters */ - if(CANmodule == NULL || setFilters(CANmodule) != CO_ERROR_NO){ - CO_errExit("CO_CANsetNormalMode failed"); - } - CANmodule->CANnormal = true; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_CANmodule_init( - CO_CANmodule_t *CANmodule, - void *CANdriverState, - CO_CANrx_t rxArray[], - uint16_t rxSize, - CO_CANtx_t txArray[], - uint16_t txSize, - uint16_t CANbitRate) -{ - CO_ReturnError_t ret = CO_ERROR_NO; - uint16_t i; - - /* verify arguments */ - if(CANmodule==NULL || CANdriverState==NULL || rxArray==NULL || txArray==NULL){ - ret = CO_ERROR_ILLEGAL_ARGUMENT; - } - - /* Configure object variables */ - if(ret == CO_ERROR_NO){ - CANmodule->CANdriverState = CANdriverState; - CANmodule->rxArray = rxArray; - CANmodule->rxSize = rxSize; - CANmodule->txArray = txArray; - CANmodule->txSize = txSize; - CANmodule->CANnormal = false; - CANmodule->useCANrxFilters = true; - CANmodule->bufferInhibitFlag = false; - CANmodule->firstCANtxMessage = true; - CANmodule->error = 0; - CANmodule->CANtxCount = 0U; - CANmodule->errOld = 0U; - CANmodule->em = NULL; - -#ifdef CO_LOG_CAN_MESSAGES - CANmodule->useCANrxFilters = false; -#endif - - for(i=0U; iwasConfigured == 0){ - struct sockaddr_can sockAddr; - - CANmodule->wasConfigured = 1; - - /* Create and bind socket */ - CANmodule->fd = socket(AF_CAN, SOCK_RAW, CAN_RAW); - if(CANmodule->fd < 0){ - ret = CO_ERROR_ILLEGAL_ARGUMENT; - }else{ - const int * const ifindex_ptr = CANdriverState; - sockAddr.can_family = AF_CAN; - sockAddr.can_ifindex = *ifindex_ptr; - if(bind(CANmodule->fd, (struct sockaddr*)&sockAddr, sizeof(sockAddr)) != 0){ - ret = CO_ERROR_ILLEGAL_ARGUMENT; - } - } - - /* allocate memory for filter array */ - if(ret == CO_ERROR_NO){ - CANmodule->filter = (struct can_filter *) calloc(rxSize, sizeof(struct can_filter)); - if(CANmodule->filter == NULL){ - ret = CO_ERROR_OUT_OF_MEMORY; - } - } - } - - /* Additional check. */ - if(ret == CO_ERROR_NO && CANmodule->filter == NULL){ - ret = CO_ERROR_ILLEGAL_ARGUMENT; - } - - /* Configure CAN module hardware filters */ - if(ret == CO_ERROR_NO && CANmodule->useCANrxFilters){ - /* Match filter, standard 11 bit CAN address only, no rtr */ - for(i=0U; ifilter[i].can_id = 0; - CANmodule->filter[i].can_mask = CAN_SFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG; - } - } - - /* close CAN module filters for now. */ - if(ret == CO_ERROR_NO){ - setsockopt(CANmodule->fd, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0); - } - - return ret; -} - - -/******************************************************************************/ -void CO_CANmodule_disable(CO_CANmodule_t *CANmodule){ - close(CANmodule->fd); - free(CANmodule->filter); - CANmodule->filter = NULL; -} - - -/******************************************************************************/ -uint16_t CO_CANrxMsg_readIdent(const CO_CANrxMsg_t *rxMsg){ - return (uint16_t) rxMsg->ident; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_CANrxBufferInit( - CO_CANmodule_t *CANmodule, - uint16_t index, - uint16_t ident, - uint16_t mask, - bool_t rtr, - void *object, - void (*pFunct)(void *object, const CO_CANrxMsg_t *message)) -{ - CO_ReturnError_t ret = CO_ERROR_NO; - - if((CANmodule!=NULL) && (object!=NULL) && (pFunct!=NULL) && - (CANmodule->filter!=NULL) && (index < CANmodule->rxSize)){ - /* buffer, which will be configured */ - CO_CANrx_t *buffer = &CANmodule->rxArray[index]; - - /* Configure object variables */ - buffer->object = object; - buffer->pFunct = pFunct; - - /* Configure CAN identifier and CAN mask, bit aligned with CAN module. */ - buffer->ident = ident & CAN_SFF_MASK; - if(rtr){ - buffer->ident |= CAN_RTR_FLAG; - } - buffer->mask = (mask & CAN_SFF_MASK) | CAN_EFF_FLAG | CAN_RTR_FLAG; - - /* Set CAN hardware module filter and mask. */ - if(CANmodule->useCANrxFilters){ - CANmodule->filter[index].can_id = buffer->ident; - CANmodule->filter[index].can_mask = buffer->mask; - if(CANmodule->CANnormal){ - ret = setFilters(CANmodule); - } - } - } - else{ - ret = CO_ERROR_ILLEGAL_ARGUMENT; - } - - return ret; -} - - -/******************************************************************************/ -CO_CANtx_t *CO_CANtxBufferInit( - CO_CANmodule_t *CANmodule, - uint16_t index, - uint16_t ident, - bool_t rtr, - uint8_t noOfBytes, - bool_t syncFlag) -{ - CO_CANtx_t *buffer = NULL; - - if((CANmodule != NULL) && (index < CANmodule->txSize)){ - /* get specific buffer */ - buffer = &CANmodule->txArray[index]; - - /* CAN identifier, bit aligned with CAN module registers */ - buffer->ident = ident & CAN_SFF_MASK; - if(rtr){ - buffer->ident |= CAN_RTR_FLAG; - } - - buffer->DLC = noOfBytes; - buffer->bufferFull = false; - buffer->syncFlag = syncFlag; - } - - return buffer; -} - - -/******************************************************************************/ -CO_ReturnError_t CO_CANsend(CO_CANmodule_t *CANmodule, CO_CANtx_t *buffer){ - CO_ReturnError_t err = CO_ERROR_NO; - ssize_t n; - size_t count = sizeof(struct can_frame); - - n = write(CANmodule->fd, buffer, count); -#ifdef CO_LOG_CAN_MESSAGES - void CO_logMessage(const CanMsg *msg); - CO_logMessage((const CanMsg*) buffer); -#endif - - if(n != count){ - CO_errorReport((CO_EM_t*)CANmodule->em, CO_EM_CAN_TX_OVERFLOW, CO_EMC_CAN_OVERRUN, n); - err = CO_ERROR_TX_OVERFLOW; - } - - return err; -} - - -/******************************************************************************/ -void CO_CANclearPendingSyncPDOs(CO_CANmodule_t *CANmodule){ - /* Messages can not be cleared, because they are allready in kernel */ -} - - -/******************************************************************************/ -void CO_CANverifyErrors(CO_CANmodule_t *CANmodule){ -#if 0 - unsigned rxErrors, txErrors; - CO_EM_t* em = (CO_EM_t*)CANmodule->em; - uint32_t err; - - canGetErrorCounters(CANmodule->CANdriverState, &rxErrors, &txErrors); - if(txErrors > 0xFFFF) txErrors = 0xFFFF; - if(rxErrors > 0xFF) rxErrors = 0xFF; - - err = ((uint32_t)txErrors << 16) | ((uint32_t)rxErrors << 8) | CANmodule->error; - - if(CANmodule->errOld != err){ - CANmodule->errOld = err; - - if(txErrors >= 256U){ /* bus off */ - CO_errorReport(em, CO_EM_CAN_TX_BUS_OFF, CO_EMC_BUS_OFF_RECOVERED, err); - } - else{ /* not bus off */ - CO_errorReset(em, CO_EM_CAN_TX_BUS_OFF, err); - - if((rxErrors >= 96U) || (txErrors >= 96U)){ /* bus warning */ - CO_errorReport(em, CO_EM_CAN_BUS_WARNING, CO_EMC_NO_ERROR, err); - } - - if(rxErrors >= 128U){ /* RX bus passive */ - CO_errorReport(em, CO_EM_CAN_RX_BUS_PASSIVE, CO_EMC_CAN_PASSIVE, err); - } - else{ - CO_errorReset(em, CO_EM_CAN_RX_BUS_PASSIVE, err); - } - - if(txErrors >= 128U){ /* TX bus passive */ - if(!CANmodule->firstCANtxMessage){ - CO_errorReport(em, CO_EM_CAN_TX_BUS_PASSIVE, CO_EMC_CAN_PASSIVE, err); - } - } - else{ - bool_t isError = CO_isError(em, CO_EM_CAN_TX_BUS_PASSIVE); - if(isError){ - CO_errorReset(em, CO_EM_CAN_TX_BUS_PASSIVE, err); - CO_errorReset(em, CO_EM_CAN_TX_OVERFLOW, err); - } - } - - if((rxErrors < 96U) && (txErrors < 96U)){ /* no error */ - bool_t isError = CO_isError(em, CO_EM_CAN_BUS_WARNING); - if(isError){ - CO_errorReset(em, CO_EM_CAN_BUS_WARNING, err); - CO_errorReset(em, CO_EM_CAN_TX_OVERFLOW, err); - } - } - } - - if(CANmodule->error & 0x02){ /* CAN RX bus overflow */ - CO_errorReport(em, CO_EM_CAN_RXB_OVERFLOW, CO_EMC_CAN_OVERRUN, err); - } - } -#endif -} - - -/******************************************************************************/ -void CO_CANrxWait(CO_CANmodule_t *CANmodule){ - struct can_frame msg; - int n, size; - - if(CANmodule == NULL){ - errno = EFAULT; - CO_errExit("CO_CANreceive - CANmodule not configured."); - } - - /* Read socket and pre-process message */ - size = sizeof(struct can_frame); - n = read(CANmodule->fd, &msg, size); - - if(CANmodule->CANnormal){ - if(n != size){ - /* This happens only once after error occurred (network down or something). */ - CO_errorReport((CO_EM_t*)CANmodule->em, CO_EM_CAN_RXB_OVERFLOW, CO_EMC_COMMUNICATION, n); - } - else{ - CO_CANrxMsg_t *rcvMsg; /* pointer to received message in CAN module */ - uint32_t rcvMsgIdent; /* identifier of the received message */ - CO_CANrx_t *buffer; /* receive message buffer from CO_CANmodule_t object. */ - int i; - bool_t msgMatched = false; - - rcvMsg = (CO_CANrxMsg_t *) &msg; - rcvMsgIdent = rcvMsg->ident; - - /* Search rxArray form CANmodule for the matching CAN-ID. */ - buffer = &CANmodule->rxArray[0]; - for(i = CANmodule->rxSize; i > 0U; i--){ - if(((rcvMsgIdent ^ buffer->ident) & buffer->mask) == 0U){ - msgMatched = true; - break; - } - buffer++; - } - - /* Call specific function, which will process the message */ - if(msgMatched && (buffer->pFunct != NULL)){ - buffer->pFunct(buffer->object, rcvMsg); - } - -#ifdef CO_LOG_CAN_MESSAGES - void CO_logMessage(const CanMsg *msg); - CO_logMessage((CanMsg*)&rcvMsg); -#endif - } - } -} diff --git a/stack/socketCAN/CO_driver_target.h b/stack/socketCAN/CO_driver_target.h deleted file mode 100644 index bb816967..00000000 --- a/stack/socketCAN/CO_driver_target.h +++ /dev/null @@ -1,167 +0,0 @@ -/* - * CAN module object for Linux SocketCAN. - * - * @file CO_driver.h - * @author Janez Paternoster - * @copyright 2015 - 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#ifndef CO_DRIVER_TARGET_H -#define CO_DRIVER_TARGET_H - - -/* For documentation see file drvTemplate/CO_driver.h */ - - -#include /* for 'NULL' */ -#include /* for 'int8_t' to 'uint64_t' */ -#include /* for 'true', 'false' */ -#include -#include - -#ifndef CO_SINGLE_THREAD -#include -#endif - -#include -#include -#include - - -/* Endianness */ -#ifdef BYTE_ORDER -#if BYTE_ORDER == LITTLE_ENDIAN - #define CO_LITTLE_ENDIAN -#else - #define CO_BIG_ENDIAN -#endif /* BYTE_ORDER == LITTLE_ENDIAN */ -#endif /* BYTE_ORDER */ - - -/* general configuration */ -// #define CO_LOG_CAN_MESSAGES /* Call external function for each received or transmitted CAN message. */ - #define CO_SDO_BUFFER_SIZE 889 /* Override default SDO buffer size. */ - - -/* Critical sections */ -#ifdef CO_SINGLE_THREAD - #define CO_LOCK_CAN_SEND() - #define CO_UNLOCK_CAN_SEND() - - #define CO_LOCK_EMCY() - #define CO_UNLOCK_EMCY() - - #define CO_LOCK_OD() - #define CO_UNLOCK_OD() - - #define CANrxMemoryBarrier() -#else - #define CO_LOCK_CAN_SEND() /* not needed */ - #define CO_UNLOCK_CAN_SEND() - - extern pthread_mutex_t CO_EMCY_mtx; - #define CO_LOCK_EMCY() {if(pthread_mutex_lock(&CO_EMCY_mtx) != 0) CO_errExit("Mutex lock CO_EMCY_mtx failed");} - #define CO_UNLOCK_EMCY() {if(pthread_mutex_unlock(&CO_EMCY_mtx) != 0) CO_errExit("Mutex unlock CO_EMCY_mtx failed");} - - extern pthread_mutex_t CO_OD_mtx; - #define CO_LOCK_OD() {if(pthread_mutex_lock(&CO_OD_mtx) != 0) CO_errExit("Mutex lock CO_OD_mtx failed");} - #define CO_UNLOCK_OD() {if(pthread_mutex_unlock(&CO_OD_mtx) != 0) CO_errExit("Mutex unlock CO_OD_mtx failed");} - - #define CANrxMemoryBarrier() {__sync_synchronize();} -#endif /* CO_SINGLE_THREAD */ - -/* Syncronisation functions */ -#define IS_CANrxNew(rxNew) ((uintptr_t)rxNew) -#define SET_CANrxNew(rxNew) {CANrxMemoryBarrier(); rxNew = (void*)1L;} -#define CLEAR_CANrxNew(rxNew) {CANrxMemoryBarrier(); rxNew = (void*)0L;} - - -/* Data types */ -/* int8_t to uint64_t are defined in stdint.h */ -typedef _Bool bool_t; -typedef float float32_t; -typedef double float64_t; -typedef char char_t; -typedef unsigned char oChar_t; -typedef unsigned char domain_t; - - -/* CAN receive message structure as aligned in CAN module. */ -typedef struct{ - uint32_t ident; - uint8_t DLC; - uint8_t data[8] __attribute__((aligned(8))); -}CO_CANrxMsg_t; - - -/* Received message object */ -typedef struct{ - uint32_t ident; - uint32_t mask; - void *object; - void (*pFunct)(void *object, const CO_CANrxMsg_t *message); -}CO_CANrx_t; - - -/* Transmit message object as aligned in CAN module. */ -typedef struct{ - uint32_t ident; - uint8_t DLC; - uint8_t data[8] __attribute__((aligned(8))); - volatile bool_t bufferFull; - volatile bool_t syncFlag; -}CO_CANtx_t; - - -/* CAN module object. */ -typedef struct{ - void *CANdriverState; -#ifdef CO_LOG_CAN_MESSAGES - CO_CANtx_t txRecord; -#endif - CO_CANrx_t *rxArray; - uint16_t rxSize; - CO_CANtx_t *txArray; - uint16_t txSize; - uint16_t wasConfigured;/* Zero only on first run of CO_CANmodule_init */ - int fd; /* CAN_RAW socket file descriptor */ - struct can_filter *filter; /* array of CAN filters of size rxSize */ - volatile bool_t CANnormal; - volatile bool_t useCANrxFilters; - volatile bool_t bufferInhibitFlag; - volatile bool_t firstCANtxMessage; - volatile uint8_t error; - volatile uint16_t CANtxCount; - uint32_t errOld; - void *em; -}CO_CANmodule_t; - -/* Helper function, must be defined externally. */ -void CO_errExit(char* msg); - - -/* Functions receives CAN messages. It is blocking. - * - * @param CANmodule This object. - */ -void CO_CANrxWait(CO_CANmodule_t *CANmodule); - - -#endif /* CO_DRIVER_TARGET_H */ diff --git a/storage/CO_eeprom.h b/storage/CO_eeprom.h new file mode 100644 index 00000000..f28e4fd6 --- /dev/null +++ b/storage/CO_eeprom.h @@ -0,0 +1,113 @@ +/** + * Eeprom interface for use with CO_storageEeprom + * + * @file CO_eeprom.h + * @ingroup CO_storage_eeprom + * @author Janez Paternoster + * @copyright 2021 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#ifndef CO_EEPROM_H +#define CO_EEPROM_H + +#include "301/CO_driver.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup CO_storage_eeprom + * @{ + */ + +/** + * Initialize eeprom device, target system specific function. + * + * @param storageModule Pointer to storage module. + * + * @return True on success + */ +bool_t CO_eeprom_init(void* storageModule); + +/** + * Get free address inside eeprom, target system specific function. + * + * Function is called several times for each storage block in the initialization phase after CO_eeprom_init(). + * + * @param storageModule Pointer to storage module. + * @param isAuto True, if variable is auto stored or false if protected + * @param len Length of data, which will be stored to that location + * @param [out] overflow set to true, if not enough eeprom memory + * + * @return Asigned eeprom address + */ +size_t CO_eeprom_getAddr(void* storageModule, bool_t isAuto, size_t len, bool_t* overflow); + +/** + * Read block of data from the eeprom, target system specific function. + * + * @param storageModule Pointer to storage module. + * @param data Pointer to data buffer, where data will be stored. + * @param eepromAddr Address in eeprom, from where data will be read. + * @param len Length of the data block to be read. + */ +void CO_eeprom_readBlock(void* storageModule, uint8_t* data, size_t eepromAddr, size_t len); + +/** + * Write block of data to the eeprom, target system specific function. + * + * It is blocking function, so it waits, until all data is written. + * + * @param storageModule Pointer to storage module. + * @param data Pointer to data buffer which will be written. + * @param eepromAddr Address in eeprom, where data will be written. If data is stored across multiple pages, address + * must be aligned with page. + * @param len Length of the data block. + * + * @return true on success + */ +bool_t CO_eeprom_writeBlock(void* storageModule, uint8_t* data, size_t eepromAddr, size_t len); + +/** + * Get CRC checksum of the block of data stored in the eeprom, target system specific function. + * + * @param storageModule Pointer to storage module. + * @param eepromAddr Address of data in eeprom. + * @param len Length of the data. + * + * @return CRC checksum + */ +uint16_t CO_eeprom_getCrcBlock(void* storageModule, size_t eepromAddr, size_t len); + +/** + * Update one byte of data in the eeprom, target system specific function. + * + * Function is used by automatic storage. It updates byte in eeprom only if differs from data. + * + * @param storageModule Pointer to storage module. + * @param data Data byte to be written + * @param eepromAddr Address in eeprom, from where data will be updated. + * + * @return true if write was successful or false, if still waiting previous data to finish writing. + */ +bool_t CO_eeprom_updateByte(void* storageModule, uint8_t data, size_t eepromAddr); + +/** @} */ /* CO_storage_eeprom */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CO_EEPROM_H */ diff --git a/storage/CO_storage.c b/storage/CO_storage.c new file mode 100644 index 00000000..4a1ffff1 --- /dev/null +++ b/storage/CO_storage.c @@ -0,0 +1,165 @@ +/* + * CANopen data storage base object + * + * @file CO_storage.c + * @author Janez Paternoster + * @copyright 2021 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#include "storage/CO_storage.h" + +#if ((CO_CONFIG_STORAGE)&CO_CONFIG_STORAGE_ENABLE) != 0 + +/* + * Custom function for writing OD object "Store parameters" + * + * For more information see file CO_ODinterface.h, OD_IO_t. + */ +static ODR_t +OD_write_1010(OD_stream_t* stream, const void* buf, OD_size_t count, OD_size_t* countWritten) { + /* verify arguments */ + if ((stream == NULL) || (stream->subIndex == 0U) || (buf == NULL) || (count != 4U) || (countWritten == NULL)) { + return ODR_DEV_INCOMPAT; + } + + CO_storage_t* storage = stream->object; + + if ((stream->subIndex == 0U) || (storage->store == NULL) || !storage->enabled) { + return ODR_READONLY; + } + + uint32_t val = CO_getUint32(buf); + if (val != 0x65766173U) { + return ODR_DATA_TRANSF; + } + + /* loop through entries and store relevant */ + uint8_t found = 0; + ODR_t returnCode = ODR_OK; + + for (uint8_t i = 0; i < storage->entriesCount; i++) { + CO_storage_entry_t* entry = &storage->entries[i]; + + if ((stream->subIndex == 1U) || (entry->subIndexOD == stream->subIndex)) { + if (found == 0U) { + found = 1; + } + if ((entry->attr & (uint8_t)CO_storage_cmd) != 0U) { + ODR_t code = storage->store(entry, storage->CANmodule); + if (code != ODR_OK) { + returnCode = code; + } + found = 2; + } + } + } + + if (found != 2U) { + returnCode = (found == 0U) ? ODR_SUB_NOT_EXIST : ODR_READONLY; + } + + if (returnCode == ODR_OK) { + *countWritten = sizeof(uint32_t); + } + return returnCode; +} + +/* + * Custom function for writing OD object "Restore default parameters" + * + * For more information see file CO_ODinterface.h, OD_IO_t. + */ +static ODR_t +OD_write_1011(OD_stream_t* stream, const void* buf, OD_size_t count, OD_size_t* countWritten) { + /* verify arguments */ + if ((stream == NULL) || (stream->subIndex == 0U) || (buf == NULL) || (count != 4U) || (countWritten == NULL)) { + return ODR_DEV_INCOMPAT; + } + + CO_storage_t* storage = stream->object; + + if ((stream->subIndex == 0U) || (storage->restore == NULL) || !storage->enabled) { + return ODR_READONLY; + } + + uint32_t val = CO_getUint32(buf); + if (val != 0x64616F6CU) { + return ODR_DATA_TRANSF; + } + + /* loop through entries and store relevant */ + uint8_t found = 0; + ODR_t returnCode = ODR_OK; + + for (uint8_t i = 0; i < storage->entriesCount; i++) { + CO_storage_entry_t* entry = &storage->entries[i]; + + if ((stream->subIndex == 1U) || (entry->subIndexOD == stream->subIndex)) { + if (found == 0U) { + found = 1; + } + if ((entry->attr & (uint8_t)CO_storage_restore) != 0U) { + ODR_t code = storage->restore(entry, storage->CANmodule); + if (code != ODR_OK) { + returnCode = code; + } + found = 2; + } + } + } + + if (found != 2U) { + returnCode = (found == 0U) ? ODR_SUB_NOT_EXIST : ODR_READONLY; + } + + if (returnCode == ODR_OK) { + *countWritten = sizeof(uint32_t); + } + return returnCode; +} + +CO_ReturnError_t +CO_storage_init(CO_storage_t* storage, CO_CANmodule_t* CANmodule, OD_entry_t* OD_1010_StoreParameters, + OD_entry_t* OD_1011_RestoreDefaultParameters, + ODR_t (*store)(CO_storage_entry_t* entry, CO_CANmodule_t* CANmodule), + ODR_t (*restore)(CO_storage_entry_t* entry, CO_CANmodule_t* CANmodule), CO_storage_entry_t* entries, + uint8_t entriesCount) { + /* verify arguments */ + if ((storage == NULL) || (CANmodule == NULL) || (OD_1010_StoreParameters == NULL) + || (OD_1011_RestoreDefaultParameters == NULL) || (store == NULL) || (restore == NULL) || (entries == NULL)) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + /* Configure object variables */ + storage->CANmodule = CANmodule; + storage->store = store; + storage->restore = restore; + storage->entries = entries; + storage->entriesCount = entriesCount; + + /* configure extensions */ + storage->OD_1010_extension.object = storage; + storage->OD_1010_extension.read = OD_readOriginal; + storage->OD_1010_extension.write = OD_write_1010; + (void)OD_extension_init(OD_1010_StoreParameters, &storage->OD_1010_extension); + + storage->OD_1011_extension.object = storage; + storage->OD_1011_extension.read = OD_readOriginal; + storage->OD_1011_extension.write = OD_write_1011; + (void)OD_extension_init(OD_1011_RestoreDefaultParameters, &storage->OD_1011_extension); + + return CO_ERROR_NO; +} + +#endif /* (CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE */ diff --git a/storage/CO_storage.h b/storage/CO_storage.h new file mode 100644 index 00000000..d11302d6 --- /dev/null +++ b/storage/CO_storage.h @@ -0,0 +1,142 @@ +/** + * CANopen data storage base object + * + * @file CO_storage.h + * @ingroup CO_storage + * @author Janez Paternoster + * @copyright 2021 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#ifndef CO_STORAGE_H +#define CO_STORAGE_H + +#include "301/CO_driver.h" +#include "301/CO_ODinterface.h" + +/* default configuration, see CO_config.h */ +#ifndef CO_CONFIG_STORAGE +#define CO_CONFIG_STORAGE (CO_CONFIG_STORAGE_ENABLE) +#endif + +#if (((CO_CONFIG_STORAGE)&CO_CONFIG_STORAGE_ENABLE) != 0) || defined CO_DOXYGEN + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup CO_storage Data storage base + * Base module for Data storage. + * + * @ingroup CO_CANopen_storage + * @{ + * + * CANopen provides OD objects 0x1010 and 0x1011 for control of storing and restoring data. Data source is usually a + * group of variables inside object dictionary, but it is not limited to OD. + * + * When object dictionary is generated (OD.h and OD.c files), OD variables are grouped into structures according to + * 'Storage group' parameter. + * + * Autonomous data storing must be implemented target specific, if in use. + * + * ### OD object 0x1010 - Store parameters: + * - Sub index 0: Highest sub-index supported + * - Sub index 1: Save all parameters, UNSIGNED32 + * - Sub index 2: Save communication parameters, UNSIGNED32 + * - Sub index 3: Save application parameters, UNSIGNED32 + * - Sub index 4 - 127: Manufacturer specific, UNSIGNED32 + * + * Sub-indexes 1 and above: + * - Reading provides information about its storage functionality: + * - bit 0: If set, CANopen device saves parameters on command + * - bit 1: If set, CANopen device saves parameters autonomously + * - Writing value 0x65766173 ('s','a','v','e' from LSB to MSB) stores corresponding data. + * + * ### OD object 0x1011 - Restore default parameters + * - Sub index 0: Highest sub-index supported + * - Sub index 1: Restore all default parameters, UNSIGNED32 + * - Sub index 2: Restore communication default parameters, UNSIGNED32 + * - Sub index 3: Restore application default parameters, UNSIGNED32 + * - Sub index 4 - 127: Manufacturer specific, UNSIGNED32 + * + * Sub-indexes 1 and above: + * - Reading provides information about its restoring capability: + * - bit 0: If set, CANopen device restores parameters + * - Writing value 0x64616F6C ('l','o','a','d' from LSB to MSB) restores corresponding data. + */ + +/** + * Attributes (bit masks) for Data storage object. + */ +typedef enum { + CO_storage_cmd = 0x01, /**< CANopen device saves parameters on OD 1010 command */ + CO_storage_auto = 0x02, /**< CANopen device saves parameters autonomously */ + CO_storage_restore = 0x04 /**< CANopen device restores parameters on OD 1011 command */ +} CO_storage_attributes_t; + +/** + * Data storage object. + * + * Object is used with CANopen OD objects at index 1010 and 1011. + */ +typedef struct { + OD_extension_t OD_1010_extension; /**< Extension for OD object */ + OD_extension_t OD_1011_extension; /**< Extension for OD object */ + CO_CANmodule_t* CANmodule; /**< From CO_storage_init() */ + ODR_t (*store)(CO_storage_entry_t* entry, CO_CANmodule_t* CANmodule); /**< From CO_storage_init() */ + ODR_t (*restore)(CO_storage_entry_t* entry, CO_CANmodule_t* CANmodule); /**< From CO_storage_init() */ + CO_storage_entry_t* entries; /**< From CO_storage_init() */ + uint8_t entriesCount; /**< From CO_storage_init() */ + bool_t enabled; /**< true, if storage is enabled. Setting of this variable is implementation specific. */ +} CO_storage_t; + +/** + * Initialize data storage object + * + * This function should be called by application after the program startup, before @ref CO_CANopenInit(). This function + * initializes storage object and OD extensions on objects 1010 and 1011. Function does not load stored data on startup, + * because loading data is target specific. + * + * @param storage This object will be initialized. It must be defined by application and must exist permanently. + * @param CANmodule CAN device, for optional usage. + * @param OD_1010_StoreParameters OD entry for 0x1010 -"Store parameters". Entry is optional, may be NULL. + * @param OD_1011_RestoreDefaultParameters OD entry for 0x1011 -"Restore default parameters". Entry is optional, may be + * NULL. + * @param store Pointer to externally defined function, which will store data specified by @ref CO_storage_entry_t. + * Function will be called when OD variable 0x1010 will be written. Argument to function is entry, where + * 'entry->subIndexOD' equals accessed subIndex. Function returns value from + * @ref ODR_t : "ODR_OK" in case of success, "ODR_HW" in case of hardware error. + * @param restore Same as 'store', but for restoring default data. + * @param entries Pointer to array of storage entries. Array must be defined and initialized by application and must + * exist permanently. Structure @ref CO_storage_entry_t is target specific and must be defined by CO_driver_target.h. + * See CO_driver.h for required parameters. + * @param entriesCount Count of storage entries + * + * @return CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT. + */ +CO_ReturnError_t CO_storage_init(CO_storage_t* storage, CO_CANmodule_t* CANmodule, OD_entry_t* OD_1010_StoreParameters, + OD_entry_t* OD_1011_RestoreDefaultParameters, + ODR_t (*store)(CO_storage_entry_t* entry, CO_CANmodule_t* CANmodule), + ODR_t (*restore)(CO_storage_entry_t* entry, CO_CANmodule_t* CANmodule), + CO_storage_entry_t* entries, uint8_t entriesCount); + +/** @} */ /* CO_storage */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* (CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE */ + +#endif /* CO_STORAGE_H */ diff --git a/storage/CO_storageEeprom.c b/storage/CO_storageEeprom.c new file mode 100644 index 00000000..d2720dd4 --- /dev/null +++ b/storage/CO_storageEeprom.c @@ -0,0 +1,222 @@ +/* + * CANopen data storage object for storing data into block device (eeprom) + * + * @file CO_storageEeprom.c + * @author Janez Paternoster + * @copyright 2021 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#include "storage/CO_storageEeprom.h" +#include "storage/CO_eeprom.h" +#include "301/CO_crc16-ccitt.h" + +#if ((CO_CONFIG_STORAGE)&CO_CONFIG_STORAGE_ENABLE) != 0 + +/* + * Function for writing data on "Store parameters" command - OD object 1010 + * + * For more information see file CO_storage.h, CO_storage_entry_t. + */ +static ODR_t +storeEeprom(CO_storage_entry_t* entry, CO_CANmodule_t* CANmodule) { + (void)CANmodule; + bool_t writeOk; + + /* save data to the eeprom */ + writeOk = CO_eeprom_writeBlock(entry->storageModule, entry->addr, entry->eepromAddr, entry->len); + entry->crc = CO_crc16_ccitt(entry->addr, entry->len, 0); + + /* Verify, if data in eeprom are equal */ + uint16_t crc_read = CO_eeprom_getCrcBlock(entry->storageModule, entry->eepromAddr, entry->len); + if ((entry->crc != crc_read) || !writeOk) { + return ODR_HW; + } + + /* Write signature (see CO_storageEeprom_init() for info) */ + uint16_t signatureOfEntry = (uint16_t)entry->len; + uint32_t signature = (((uint32_t)entry->crc) << 16) | signatureOfEntry; + writeOk = CO_eeprom_writeBlock(entry->storageModule, (uint8_t*)&signature, entry->eepromAddrSignature, + sizeof(signature)); + + /* verify signature and write */ + uint32_t signatureRead; + CO_eeprom_readBlock(entry->storageModule, (uint8_t*)&signatureRead, entry->eepromAddrSignature, + sizeof(signatureRead)); + if ((signature != signatureRead) || !writeOk) { + return ODR_HW; + } + + return ODR_OK; +} + +/* + * Function for restoring data on "Restore default parameters" command - OD 1011 + * + * For more information see file CO_storage.h, CO_storage_entry_t. + */ +static ODR_t +restoreEeprom(CO_storage_entry_t* entry, CO_CANmodule_t* CANmodule) { + (void)CANmodule; + bool_t writeOk; + + /* Write empty signature */ + uint32_t signature = 0xFFFFFFFFU; + writeOk = CO_eeprom_writeBlock(entry->storageModule, (uint8_t*)&signature, entry->eepromAddrSignature, + sizeof(signature)); + + /* verify signature and protection */ + uint32_t signatureRead; + CO_eeprom_readBlock(entry->storageModule, (uint8_t*)&signatureRead, entry->eepromAddrSignature, + sizeof(signatureRead)); + if ((signature != signatureRead) || !writeOk) { + return ODR_HW; + } + + return ODR_OK; +} + +CO_ReturnError_t +CO_storageEeprom_init(CO_storage_t* storage, CO_CANmodule_t* CANmodule, void* storageModule, + OD_entry_t* OD_1010_StoreParameters, OD_entry_t* OD_1011_RestoreDefaultParam, + CO_storage_entry_t* entries, uint8_t entriesCount, uint32_t* storageInitError) { + CO_ReturnError_t ret; + bool_t eepromOvf = false; + + /* verify arguments */ + if ((storage == NULL) || (entries == NULL) || (entriesCount == 0U) + || (entriesCount > CO_CONFIG_STORAGE_MAX_ENTRIES_COUNT) || (storageInitError == NULL)) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + storage->enabled = false; + + /* Initialize storage hardware */ + if (!CO_eeprom_init(storageModule)) { + *storageInitError = 0xFFFFFFFFU; + return CO_ERROR_DATA_CORRUPT; + } + + /* initialize storage and OD extensions */ + ret = CO_storage_init(storage, CANmodule, OD_1010_StoreParameters, OD_1011_RestoreDefaultParam, storeEeprom, + restoreEeprom, entries, entriesCount); + if (ret != CO_ERROR_NO) { + return ret; + } + + /* Read entry signatures from the eeprom */ + uint32_t signatures[CO_CONFIG_STORAGE_MAX_ENTRIES_COUNT]; + size_t signaturesAddress = CO_eeprom_getAddr(storageModule, false, sizeof(signatures), &eepromOvf); + CO_eeprom_readBlock(storageModule, (uint8_t*)signatures, signaturesAddress, sizeof(signatures)); + + /* initialize entries */ + *storageInitError = 0; + for (uint8_t i = 0; i < entriesCount; i++) { + CO_storage_entry_t* entry = &entries[i]; + bool_t isAuto = (entry->attr & (uint8_t)CO_storage_auto) != 0U; + + /* verify arguments */ + if ((entry->addr == NULL) || (entry->len == 0U) || (entry->subIndexOD < 2U)) { + *storageInitError = i; + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + /* calculate addresses inside eeprom */ + entry->eepromAddrSignature = signaturesAddress + (sizeof(uint32_t) * i); + entry->eepromAddr = CO_eeprom_getAddr(storageModule, isAuto, entry->len, &eepromOvf); + entry->offset = 0; + + /* verify if eeprom is too small */ + if (eepromOvf) { + *storageInitError = i; + return CO_ERROR_OUT_OF_MEMORY; + } + + /* 32bit signature (which was stored in eeprom) is combined from + * 16bit signature of the entry and 16bit CRC checksum of the data + * block. 16bit signature of the entry is entry->len. */ + uint32_t signature = signatures[i]; + uint16_t signatureInEeprom = (uint16_t)signature; + entry->crc = (uint16_t)(signature >> 16); + uint16_t signatureOfEntry = (uint16_t)entry->len; + + /* Verify two signatures */ + bool_t dataCorrupt = false; + if (signatureInEeprom != signatureOfEntry) { + dataCorrupt = true; + } else { + /* Read data into storage location */ + CO_eeprom_readBlock(entry->storageModule, entry->addr, entry->eepromAddr, entry->len); + + /* Verify CRC, except for auto storage variables */ + if (!isAuto) { + uint16_t crc = CO_crc16_ccitt(entry->addr, entry->len, 0); + if (crc != entry->crc) { + dataCorrupt = true; + } + } + } + + /* additional info in case of error */ + if (dataCorrupt) { + uint32_t errorBit = entry->subIndexOD; + if (errorBit > 31U) { + errorBit = 31; + } + *storageInitError |= ((uint32_t)1) << errorBit; + ret = CO_ERROR_DATA_CORRUPT; + } + } /* for (entries) */ + + storage->enabled = true; + return ret; +} + +void +CO_storageEeprom_auto_process(CO_storage_t* storage, bool_t saveAll) { + /* verify arguments */ + if ((storage == NULL) || !storage->enabled) { + return; + } + + /* loop through entries */ + for (uint8_t n = 0; n < storage->entriesCount; n++) { + CO_storage_entry_t* entry = &storage->entries[n]; + + if ((entry->attr & (uint8_t)CO_storage_auto) == 0U) { + continue; + } + + if (saveAll) { + /* update all bytes */ + for (size_t i = 0; i < entry->len;) { + uint8_t dataByteToUpdate = ((uint8_t*)(entry->addr))[i]; + size_t eepromAddr = entry->eepromAddr + i; + if (CO_eeprom_updateByte(entry->storageModule, dataByteToUpdate, eepromAddr)) { + i++; + } + } + } else { + /* update one data byte and if successful increment to next */ + uint8_t dataByteToUpdate = ((uint8_t*)(entry->addr))[entry->offset]; + size_t eepromAddr = entry->eepromAddr + entry->offset; + if (CO_eeprom_updateByte(entry->storageModule, dataByteToUpdate, eepromAddr)) { + if (++entry->offset >= entry->len) { + entry->offset = 0; + } + } + } + } +} + +#endif /* (CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE */ diff --git a/storage/CO_storageEeprom.h b/storage/CO_storageEeprom.h new file mode 100644 index 00000000..f964f512 --- /dev/null +++ b/storage/CO_storageEeprom.h @@ -0,0 +1,109 @@ +/** + * CANopen data storage object for storing data into block device (eeprom) + * + * @file CO_storageEeprom.h + * @ingroup CO_storage_eeprom + * @author Janez Paternoster + * @copyright 2021 Janez Paternoster + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +#ifndef CO_STORAGE_EEPROM_H +#define CO_STORAGE_EEPROM_H + +#include "storage/CO_storage.h" + +#ifndef CO_CONFIG_STORAGE_MAX_ENTRIES_COUNT +#define CO_CONFIG_STORAGE_MAX_ENTRIES_COUNT 5U +#endif + +#if (((CO_CONFIG_STORAGE)&CO_CONFIG_STORAGE_ENABLE) != 0) || defined CO_DOXYGEN + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup CO_storage_eeprom Data storage in eeprom + * Eeprom specific data storage functions. + * + * @ingroup CO_CANopen_storage + * @{ + * This is an interface into generic CANopenNode @ref CO_storage for usage with eeprom chip like 25LC256. Functions @ref + * CO_storageEeprom_init() and @ref CO_storageEeprom_auto_process are target system independent. Functions specified by + * @ref CO_eeprom.h file, must be defined by target system. For example implementation see CANopenPIC/PIC32. + * + * Storage principle: + * This function first reads 'signatures' for all entries from the known address from the eeprom. If signature for each + * entry is correct, then data is read from correct address from the eeprom into storage location. If signature is + * wrong, then data for that entry is indicated as corrupt and CANopen emergency message is sent. + * + * Signature also includes 16-bit CRC checksum of the data stored in eeprom. If it differs from CRC checksum calculated + * from the data actually loaded (on program startup), then entry is indicated as corrupt and CANopen emergency message + * is sent. + * + * Signature is written to eeprom, when data block is stored via CANopen SDO write command to object 0x1010. Signature + * is erased, with CANopen SDO write command to object 0x1011. If signature is not valid or is erased for any entry, + * emergency message is sent. If eeprom is new, then all signatures are wrong, so it is best to store all parameters by + * writing to 0x1010, sub 1. + * + * If entry attribute has CO_storage_auto set, then data block is stored autonomously, byte by byte, on change, during + * program run. Those data blocks are stored into write unprotected location. For auto storage to work, its signature in + * eeprom must be correct. CRC checksum for the data is not used. + */ + +/** + * Initialize data storage object (block device (eeprom) specific) + * + * This function should be called by application after the program startup, before @ref CO_CANopenInit(). This function + * initializes storage object, OD extensions on objects 1010 and 1011, reads data from file, verifies them and writes + * data to addresses specified inside entries. This function internally calls @ref CO_storage_init(). + * + * @param storage This object will be initialized. It must be defined by application and must exist permanently. + * @param CANmodule CAN device, for optional usage. + * @param storageModule Pointer to storage module passed to CO_eeprom functions. + * @param OD_1010_StoreParameters OD entry for 0x1010 -"Store parameters". Entry is optional, may be NULL. + * @param OD_1011_RestoreDefaultParam OD entry for 0x1011 -"Restore default parameters". Entry is optional, may be NULL. + * @param entries Pointer to array of storage entries, see @ref CO_storage_init. + * @param entriesCount Count of storage entries, must not be larger than CO_CONFIG_STORAGE_MAX_ENTRIES_COUNT. + * @param [out] storageInitError If function returns CO_ERROR_DATA_CORRUPT, then this variable contains a bit mask from + * subIndexOD values, where data was not properly initialized. If other error, then this variable contains index or + * erroneous entry. If there is hardware error like missing eeprom, then storageInitError is 0xFFFFFFFF and function + * returns CO_ERROR_DATA_CORRUPT. + * + * @return CO_ERROR_NO, CO_ERROR_DATA_CORRUPT if data can not be initialized, CO_ERROR_ILLEGAL_ARGUMENT or + * CO_ERROR_OUT_OF_MEMORY. + */ +CO_ReturnError_t CO_storageEeprom_init(CO_storage_t* storage, CO_CANmodule_t* CANmodule, void* storageModule, + OD_entry_t* OD_1010_StoreParameters, OD_entry_t* OD_1011_RestoreDefaultParam, + CO_storage_entry_t* entries, uint8_t entriesCount, uint32_t* storageInitError); + +/** + * Automatically update data if differs inside eeprom. + * + * Should be called cyclically by program. Each interval it updates one byte. + * + * @param storage This object + * @param saveAll If true, all bytes are updated, useful on program end. + */ +void CO_storageEeprom_auto_process(CO_storage_t* storage, bool_t saveAll); + +/** @} */ /* CO_storage_eeprom */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* (CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE */ + +#endif /* CO_STORAGE_EEPROM_H */ diff --git a/tools/eds2c_wrapper.py b/tools/eds2c_wrapper.py new file mode 100644 index 00000000..6242f3d5 --- /dev/null +++ b/tools/eds2c_wrapper.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2025 BitConcepts, LLC +# Author: BitConcepts, LLC +# +# This file is part of , a CANopen stack. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +EDS to C Conversion Script (CLI Wrapper) + +This script provides a minimal command-line interface (CLI) for invoking the +`eds2c` module from the `eds-utils` Python package. It allows Windows users +to generate C source files from an EDS (Electronic Data Sheet) file without +requiring GTK or other GUI dependencies, which are typically only supported +on Linux or macOS. + +Usage: + python eds2c_wrapper.py + +Arguments are passed directly to the `eds2c` entry point. For example: + python eds2c_wrapper.py generate path/to/file.eds -o output/dir + +Notes: +- This bypasses GUI-related modules like `gi` and `eds_editor.main`, which + often cause issues in Windows environments lacking GTK. +- Make sure `eds-utils` is installed in your Python environment. +""" + + +import sys +from eds_utils import eds2c + + +# Entry point for script execution +if __name__ == "__main__": + # Pass command-line arguments to the eds2c CLI function + eds2c.eds2c(sys.argv[1:]) diff --git a/zephyr/.clang-format b/zephyr/.clang-format new file mode 100644 index 00000000..e0982b80 --- /dev/null +++ b/zephyr/.clang-format @@ -0,0 +1,116 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Note: The list of ForEachMacros can be obtained using: +# +# git grep -h '^#define [^[:space:]]*FOR_EACH[^[:space:]]*(' include/ \ +# | sed "s,^#define \([^[:space:]]*FOR_EACH[^[:space:]]*\)(.*$, - '\1'," \ +# | sort | uniq +# +# References: +# - https://clang.llvm.org/docs/ClangFormatStyleOptions.html + +--- +BasedOnStyle: LLVM +AlignConsecutiveMacros: AcrossComments +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AttributeMacros: + - __aligned + - __deprecated + - __packed + - __printf_like + - __syscall + - __syscall_always_inline + - __subsystem +BitFieldColonSpacing: After +BreakBeforeBraces: Linux +ColumnLimit: 100 +ConstructorInitializerIndentWidth: 8 +ContinuationIndentWidth: 8 +ForEachMacros: + - 'ARRAY_FOR_EACH' + - 'ARRAY_FOR_EACH_PTR' + - 'FOR_EACH' + - 'FOR_EACH_FIXED_ARG' + - 'FOR_EACH_IDX' + - 'FOR_EACH_IDX_FIXED_ARG' + - 'FOR_EACH_NONEMPTY_TERM' + - 'FOR_EACH_FIXED_ARG_NONEMPTY_TERM' + - 'RB_FOR_EACH' + - 'RB_FOR_EACH_CONTAINER' + - 'SYS_DLIST_FOR_EACH_CONTAINER' + - 'SYS_DLIST_FOR_EACH_CONTAINER_SAFE' + - 'SYS_DLIST_FOR_EACH_NODE' + - 'SYS_DLIST_FOR_EACH_NODE_SAFE' + - 'SYS_SEM_LOCK' + - 'SYS_SFLIST_FOR_EACH_CONTAINER' + - 'SYS_SFLIST_FOR_EACH_CONTAINER_SAFE' + - 'SYS_SFLIST_FOR_EACH_NODE' + - 'SYS_SFLIST_FOR_EACH_NODE_SAFE' + - 'SYS_SLIST_FOR_EACH_CONTAINER' + - 'SYS_SLIST_FOR_EACH_CONTAINER_SAFE' + - 'SYS_SLIST_FOR_EACH_NODE' + - 'SYS_SLIST_FOR_EACH_NODE_SAFE' + - '_WAIT_Q_FOR_EACH' + - 'Z_FOR_EACH' + - 'Z_FOR_EACH_ENGINE' + - 'Z_FOR_EACH_EXEC' + - 'Z_FOR_EACH_FIXED_ARG' + - 'Z_FOR_EACH_FIXED_ARG_EXEC' + - 'Z_FOR_EACH_IDX' + - 'Z_FOR_EACH_IDX_EXEC' + - 'Z_FOR_EACH_IDX_FIXED_ARG' + - 'Z_FOR_EACH_IDX_FIXED_ARG_EXEC' + - 'Z_GENLIST_FOR_EACH_CONTAINER' + - 'Z_GENLIST_FOR_EACH_CONTAINER_SAFE' + - 'Z_GENLIST_FOR_EACH_NODE' + - 'Z_GENLIST_FOR_EACH_NODE_SAFE' + - 'STRUCT_SECTION_FOREACH' + - 'STRUCT_SECTION_FOREACH_ALTERNATE' + - 'TYPE_SECTION_FOREACH' + - 'K_SPINLOCK' + - 'COAP_RESOURCE_FOREACH' + - 'COAP_SERVICE_FOREACH' + - 'COAP_SERVICE_FOREACH_RESOURCE' + - 'HTTP_RESOURCE_FOREACH' + - 'HTTP_SERVER_CONTENT_TYPE_FOREACH' + - 'HTTP_SERVICE_FOREACH' + - 'HTTP_SERVICE_FOREACH_RESOURCE' + - 'I3C_BUS_FOR_EACH_I3CDEV' + - 'I3C_BUS_FOR_EACH_I2CDEV' + - 'MIN_HEAP_FOREACH' +IfMacros: + - 'CHECKIF' +# Disabled for now, see bug https://github.com/zephyrproject-rtos/zephyr/issues/48520 +#IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^".*\.h"$' + Priority: 0 + - Regex: '^<(assert|complex|ctype|errno|fenv|float|inttypes|limits|locale|math|setjmp|signal|stdarg|stdbool|stddef|stdint|stdio|stdlib|string|tgmath|time|wchar|wctype)\.h>$' + Priority: 1 + - Regex: '^\$' + Priority: 2 + - Regex: '.*' + Priority: 3 +IndentCaseLabels: false +IndentGotoLabels: false +IndentWidth: 8 +InsertBraces: true +InsertNewlineAtEOF: true +SpaceBeforeInheritanceColon: False +SpaceBeforeParens: ControlStatementsExceptControlMacros +SortIncludes: Never +UseTab: ForContinuationAndIndentation +WhitespaceSensitiveMacros: + - COND_CODE_0 + - COND_CODE_1 + - IF_DISABLED + - IF_ENABLED + - LISTIFY + - STRINGIFY + - Z_STRINGIFY + - DT_FOREACH_PROP_ELEM_SEP diff --git a/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt new file mode 100644 index 00000000..797497f7 --- /dev/null +++ b/zephyr/CMakeLists.txt @@ -0,0 +1,284 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Zephyr module for CANopenNode. +# +# Adds Zephyr module support to CANopenNode: sets include paths, builds the +# Zephyr port sources, and conditionally builds core/optional CANopenNode +# components based on Kconfig. Generates the Object Dictionary (OD.c/OD.h) +# from an EDS using tools/eds2c_wrapper.py, once per build, and wires the +# generated sources into the module library. +# +# @file CMakeLists.txt +# @author BitConcepts, LLC +# @copyright 2025 BitConcepts, LLC +# +# This file is part of , a CANopen Stack. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software distributed under the License +# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +# or implied. See the License for the specific language governing permissions and limitations under +# the License. +# +# Usage notes: +# - Expected to live at the root of the CANopenNode Zephyr module. +# - EDS source path is taken from CONFIG_CANOPENNODE_EDS_FILE_PATH; when empty, +# a sample profile EDS is used (example/DS301_profile.eds). +# - Requires Python 3 to run tools/eds2c_wrapper.py during the build. +# - Generated files are placed under ${CMAKE_CURRENT_BINARY_DIR}/generated and +# added to the module via zephyr_library_sources(). +# - Conditional compilation is driven entirely by CONFIG_CANOPENNODE_* options. + +if(CONFIG_CANOPENNODE) + +# Discover Zephyr and initialize the build system for a Zephyr application module. +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) + +# Name this Zephyr build 'canopennode' (module scope). +project(canopennode) + +# ------------------------------------------------------------------------------ +# Paths and generated-file locations +# ------------------------------------------------------------------------------ + +# Absolute path to the root of this module (CANopenNode checkout). +set(CANOPENNODE_DIR "${ZEPHYR_CURRENT_MODULE_DIR}") + +# Directory where Object Dictionary generated files will be emitted. +set(CANOPENNODE_OD_GEN_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated") + +# Generated OD C and header outputs from the EDS converter. +set(CANOPENNODE_OD_GEN_C "${CANOPENNODE_OD_GEN_DIR}/OD.c") +set(CANOPENNODE_OD_GEN_H "${CANOPENNODE_OD_GEN_DIR}/OD.h") + +# Wrapper script that drives the EDS->C/H conversion (calls eds2c). +set(CANOPENNODE_OD_GEN_SCRIPT "${CANOPENNODE_DIR}/tools/eds2c_wrapper.py") + +# ------------------------------------------------------------------------------ +# Include directories exported to the module and users +# ------------------------------------------------------------------------------ + +# Make module root, public Zephyr include shim, and generated OD include dir visible. +zephyr_include_directories( + "${CANOPENNODE_DIR}" + "${CANOPENNODE_DIR}/zephyr/include" + "${CANOPENNODE_OD_GEN_DIR}" +) + +# ------------------------------------------------------------------------------ +# Always-built Zephyr port sources +# ------------------------------------------------------------------------------ + +# Core Zephyr-facing integration and driver glue (always compiled). +zephyr_library_sources( + "${CANOPENNODE_DIR}/zephyr/CO_zephyr_integration.c" + "${CANOPENNODE_DIR}/zephyr/CO_zephyr_driver.c" +) + +# ------------------------------------------------------------------------------ +# Object Dictionary (EDS -> OD.c/.h) generation +# ------------------------------------------------------------------------------ + +# The EDS path can be specified via Kconfig (CONFIG_CANOPENNODE_EDS_FILE_PATH). +# If not provided, fall back to a sample profile shipped with the module. +find_package(Python3 REQUIRED COMPONENTS Interpreter) + +if("${CONFIG_CANOPENNODE_EDS_FILE_PATH}" STREQUAL "") + # Default to the example DS301 EDS located in the module. + set(CANOPENNODE_EDS_FILE "${CANOPENNODE_DIR}/example/DS301_profile.eds") + set(CANOPENNODE_EDS_FILE_DISPLAY "${CANOPENNODE_EDS_FILE}") +else() + # Interpret the Kconfig path as relative to the application source dir. + set(CANOPENNODE_EDS_FILE_REL "${CONFIG_CANOPENNODE_EDS_FILE_PATH}") + set(CANOPENNODE_EDS_FILE "${APPLICATION_SOURCE_DIR}/${CANOPENNODE_EDS_FILE_REL}") + # Normalize to CMake-style path separators for portability. + file(TO_CMAKE_PATH "${CANOPENNODE_EDS_FILE}" CANOPENNODE_EDS_FILE) + set(CANOPENNODE_EDS_FILE_DISPLAY "${CANOPENNODE_EDS_FILE_REL}") +endif() + +# Ensure the output directory exists. +file(MAKE_DIRECTORY "${CANOPENNODE_OD_GEN_DIR}") + +# Hard fail if the requested EDS file cannot be found to avoid confusing builds. +if(NOT EXISTS "${CANOPENNODE_EDS_FILE}") + message(FATAL_ERROR "CANopenNode: EDS file not found: ${CANOPENNODE_EDS_FILE}") +endif() + +# Friendly status messages to help diagnose generation paths. +message(STATUS "CANopenNode: Using EDS file: ${CANOPENNODE_EDS_FILE}") +message(STATUS "CANopenNode: Will generate OD headers to: ${CANOPENNODE_OD_GEN_H}") +message(STATUS "CANopenNode: Will generate OD sources to: ${CANOPENNODE_OD_GEN_C}") + +# Ensure we only ever add this rule once in the whole build, even if the module +# is pulled in multiple times (e.g., by samples/tests that include it repeatedly). +get_property(_co_od_rule_defined GLOBAL PROPERTY CANOPENNODE_OD_RULE_DEFINED SET) +if(NOT _co_od_rule_defined) + # Custom command that invokes the wrapper to generate OD.c/.h from the EDS. + add_custom_command( + OUTPUT "${CANOPENNODE_OD_GEN_C}" "${CANOPENNODE_OD_GEN_H}" + COMMAND ${Python3_EXECUTABLE} "${CANOPENNODE_OD_GEN_SCRIPT}" + "${CANOPENNODE_EDS_FILE}" + -o "${CANOPENNODE_OD_GEN_DIR}" + DEPENDS "${CANOPENNODE_OD_GEN_SCRIPT}" "${CANOPENNODE_EDS_FILE}" + COMMENT "CANopenNode: Generating Object Dictionary from ${CANOPENNODE_EDS_FILE_DISPLAY}" + VERBATIM + ) + + # Phony target representing the successful generation of the OD sources. + add_custom_target(canopennode_od_gen + DEPENDS "${CANOPENNODE_OD_GEN_C}" "${CANOPENNODE_OD_GEN_H}" + ) + + # Mark generated files so IDEs and Ninja know they come from a command. + set_source_files_properties( + "${CANOPENNODE_OD_GEN_C}" "${CANOPENNODE_OD_GEN_H}" + PROPERTIES GENERATED TRUE + ) + + # Add generated OD sources to the Zephyr library; this also establishes build-ordering. + zephyr_library_sources("${CANOPENNODE_OD_GEN_C}" "${CANOPENNODE_OD_GEN_H}") + + # Ensure the module's library waits for OD generation before compiling sources that include OD.h. + add_dependencies(${ZEPHYR_CURRENT_LIBRARY} canopennode_od_gen) + + # Remember that the rule has been defined to avoid duplicate add_custom_command/target redefinition. + set_property(GLOBAL PROPERTY CANOPENNODE_OD_RULE_DEFINED TRUE) +endif() + +# Export the generated include directory so headers can be resolved by users and module code. +zephyr_include_directories("${CANOPENNODE_OD_GEN_DIR}") + +# ------------------------------------------------------------------------------ +# Core/common sources (built regardless of feature flags) +# ------------------------------------------------------------------------------ + +# Core CANopenNode runtime, OD interface, and SDO server always compiled. +zephyr_library_sources( + "${CANOPENNODE_DIR}/CANopen.c" + "${CANOPENNODE_DIR}/301/CO_ODinterface.c" + "${CANOPENNODE_DIR}/301/CO_SDOserver.c" +) + +# Emergency producer/consumer module (feature selection happens at compile/runtime). +zephyr_library_sources("${CANOPENNODE_DIR}/301/CO_Emergency.c") + +# ------------------------------------------------------------------------------ +# Optional components (guarded by Kconfig symbols) +# ------------------------------------------------------------------------------ + +# FIFO utilities are needed by several features; include only when required. +if(CONFIG_CANOPENNODE_FIFO_ENABLE + OR CONFIG_CANOPENNODE_SDO_CLIENT_ENABLE + OR CONFIG_CANOPENNODE_GTW_ASCII + OR CONFIG_CANOPENNODE_GTW_ASCII_SDO) + zephyr_library_sources("${CANOPENNODE_DIR}/301/CO_fifo.c") +endif() + +# CRC16 helper selection: +# - If CONFIG_CANOPENNODE_CRC16_EXTERNAL: use Zephyr shim (module file) +# - Otherwise: use CANopenNode's built-in CRC16 implementation +if(CONFIG_CANOPENNODE_CRC16_ENABLE) + if(CONFIG_CANOPENNODE_CRC16_EXTERNAL) + zephyr_library_sources( + "${CANOPENNODE_DIR}/zephyr/CO_zephyr_crc16-ccitt.c" + ) + else() + zephyr_library_sources("${CANOPENNODE_DIR}/301/CO_crc16-ccitt.c") + endif() +endif() + +# Heartbeat consumer (CiA 301, section HB consumer). +if(CONFIG_CANOPENNODE_HB_CONS_ENABLE) + zephyr_library_sources("${CANOPENNODE_DIR}/301/CO_HBconsumer.c") +endif() + +# NMT / Heartbeat producer (always present; behavior gated by config). +zephyr_library_sources("${CANOPENNODE_DIR}/301/CO_NMT_Heartbeat.c") + +# Node Guarding master/slave (CiA 301): include when either role is enabled. +if(CONFIG_CANOPENNODE_NODE_GUARDING_SLAVE_ENABLE OR CONFIG_CANOPENNODE_NODE_GUARDING_MASTER_ENABLE) + zephyr_library_sources("${CANOPENNODE_DIR}/301/CO_Node_Guarding.c") +endif() + +# PDO (RPDO/TPDO) processing. +if(CONFIG_CANOPENNODE_RPDO_ENABLE OR CONFIG_CANOPENNODE_TPDO_ENABLE) + zephyr_library_sources("${CANOPENNODE_DIR}/301/CO_PDO.c") +endif() + +# SDO Client. +if(CONFIG_CANOPENNODE_SDO_CLIENT_ENABLE) + zephyr_library_sources("${CANOPENNODE_DIR}/301/CO_SDOclient.c") +endif() + +# SYNC producer/consumer. +if(CONFIG_CANOPENNODE_SYNC_ENABLE) + zephyr_library_sources("${CANOPENNODE_DIR}/301/CO_SYNC.c") +endif() + +# TIME object. +if(CONFIG_CANOPENNODE_TIME_ENABLE) + zephyr_library_sources("${CANOPENNODE_DIR}/301/CO_TIME.c") +endif() + +# Program Download (CiA 302-3) +if(CONFIG_CANOPENNODE_PROG_DOWNLOAD) + zephyr_library_sources( + "${CANOPENNODE_DIR}/302/CO_Prog_Download.c" + "${CANOPENNODE_DIR}/zephyr/CO_zephyr_prog_download.c" + ) +endif() + +# LEDs (CiA 303-3): include stack LED state machine and Zephyr GPIO bridge. +if(CONFIG_CANOPENNODE_LEDS_ENABLE) + zephyr_library_sources("${CANOPENNODE_DIR}/303/CO_LEDs.c") + zephyr_library_sources("${CANOPENNODE_DIR}/zephyr/CO_zephyr_leds.c") +endif() + +# GFC / SRDO (CiA 304). +if(CONFIG_CANOPENNODE_GFC_ENABLE) + zephyr_library_sources("${CANOPENNODE_DIR}/304/CO_GFC.c") +endif() +if(CONFIG_CANOPENNODE_SRDO_ENABLE) + zephyr_library_sources("${CANOPENNODE_DIR}/304/CO_SRDO.c") +endif() + +# LSS (CiA 305) master/slave. +if(CONFIG_CANOPENNODE_LSS_MASTER) + zephyr_library_sources("${CANOPENNODE_DIR}/305/CO_LSSmaster.c") +endif() +if(CONFIG_CANOPENNODE_LSS_SLAVE) + zephyr_library_sources("${CANOPENNODE_DIR}/305/CO_LSSslave.c") +endif() + +# ASCII Gateway (CiA 309). +if(CONFIG_CANOPENNODE_GTW_ASCII) + zephyr_library_sources("${CANOPENNODE_DIR}/309/CO_gateway_ascii.c") +endif() + +# Trace (non-standard extension). +if(CONFIG_CANOPENNODE_TRACE_ENABLE) + zephyr_library_sources("${CANOPENNODE_DIR}/extra/CO_trace.c") +endif() + +# ------------------------------------------------------------------------------ +# Storage backends (Zephyr-specific) +# ------------------------------------------------------------------------------ + +# Select storage backend glue based on Kconfig: +# - SETTINGS: full storage stack with EEPROM mapping + Zephyr storage bridge +# - RAM: lightweight volatile storage bridge +if(CONFIG_CANOPENNODE_STORAGE_ENABLE) + zephyr_library_sources( + "${CANOPENNODE_DIR}/storage/CO_storage.c" + "${CANOPENNODE_DIR}/zephyr/CO_zephyr_storage.c" + ) + if(CONFIG_CANOPENNODE_STORAGE_BACKEND_SETTINGS) + zephyr_library_sources("${CANOPENNODE_DIR}/storage/CO_storageEeprom.c") + endif() +endif() + +endif() # CONFIG_CANOPENNODE diff --git a/zephyr/CO_zephyr_crc16-ccitt.c b/zephyr/CO_zephyr_crc16-ccitt.c new file mode 100644 index 00000000..ad435265 --- /dev/null +++ b/zephyr/CO_zephyr_crc16-ccitt.c @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: Apache-2.0 */ +/* + * CANopenNode CRC-16/CCITT shim to Zephyr. + * + * Bridges CANopenNode's CRC API to Zephyr's CRC. + * + * @file CO_zephyr_crc16-ccitt.c + * @author BitConcepts, LLC + * @copyright 2025 BitConcepts, LLC + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +#include "301/CO_crc16-ccitt.h" +#include +#include +#include + +/* + * This companion helper matches CANopenNode’s expected single-byte update form. + * When crc is NULL, the call is ignored. + */ +void CO_crc16_ccitt_single(uint16_t *crc, const uint8_t chr) +{ + if (crc == NULL) { + return; + } + *crc = crc16_ccitt(*crc, &chr, 1U); +} + +/* + * Compatible wrapper that calls into Zephyr's implementation while keeping + * CANopenNode's parameter order. + */ +uint16_t CO_crc16_ccitt(const uint8_t block[], size_t blockLength, uint16_t crc) +{ + if ((block == NULL) && (blockLength != 0U)) { + /* Defensive: if caller passed NULL with non-zero length, just return seed. */ + return crc; + } + return crc16_ccitt(crc, block, blockLength); +} diff --git a/zephyr/CO_zephyr_driver.c b/zephyr/CO_zephyr_driver.c new file mode 100644 index 00000000..a9bd6b26 --- /dev/null +++ b/zephyr/CO_zephyr_driver.c @@ -0,0 +1,738 @@ +/* SPDX-License-Identifier: Apache-2.0 */ +/* + * Zephyr CAN backend for CANopenNode driver (CO_driver). + * + * Zephyr-specific implementation that adapts CANopenNode’s generic CAN + * driver to the Zephyr CAN API. It configures bitrate/mode, installs RX + * filters, handles TX queueing and completion callbacks, and propagates + * bus/error status into CO_CANmodule for the CANopen stack. + * + * @file CO_zephyr_driver.c + * @author Janez Paternoster (original template) + * @author BitConcepts, LLC + * @copyright 2004 - 2020 Janez Paternoster + * @copyright 2025 BitConcepts, LLC + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +#include "301/CO_driver.h" + +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(canopen_driver, CONFIG_CANOPEN_LOG_LEVEL); + +#define CANPTR_TO_DEV(ptr) ((const struct device *)(ptr)) + +K_KERNEL_STACK_DEFINE(canopen_tx_workq_stack, CONFIG_CANOPENNODE_TX_WORKQUEUE_STACK_SIZE); + +/* Retry backoff tunables (very small; CAN is fast and we only need to yield briefly) */ +#ifndef CONFIG_CANOPENNODE_TX_RETRY_INITIAL_MS +#define CONFIG_CANOPENNODE_TX_RETRY_INITIAL_MS 1 +#endif +#ifndef CONFIG_CANOPENNODE_TX_RETRY_MAX_MS +#define CONFIG_CANOPENNODE_TX_RETRY_MAX_MS 64 +#endif + +struct k_work_q canopen_tx_workq; + +struct canopen_tx_work_container { + struct k_work_delayable dwork; + CO_CANmodule_t *CANmodule; + uint32_t backoff_ms; /* current backoff (0 means “unset”) */ +}; + +atomic_t canopen_tx_shutdown = ATOMIC_INIT(0); + +struct canopen_tx_work_container canopen_tx_queue; + +K_MUTEX_DEFINE(canopen_send_mutex); +K_MUTEX_DEFINE(canopen_emcy_mutex); +K_MUTEX_DEFINE(canopen_co_mutex); + +inline void z_co_send_lock(void) +{ + k_mutex_lock(&canopen_send_mutex, K_FOREVER); +} + +inline void z_co_send_unlock(void) +{ + k_mutex_unlock(&canopen_send_mutex); +} + +inline void z_co_emcy_lock(void) +{ + k_mutex_lock(&canopen_emcy_mutex, K_FOREVER); +} + +inline void z_co_emcy_unlock(void) +{ + k_mutex_unlock(&canopen_emcy_mutex); +} + +inline void z_co_od_lock(void) +{ + k_mutex_lock(&canopen_co_mutex, K_FOREVER); +} + +inline void z_co_od_unlock(void) +{ + k_mutex_unlock(&canopen_co_mutex); +} + +/* + * Detach all installed RX filters from the Zephyr CAN device for this module. + * + * Notes: + * - Uses filter_id == -ENOSPC as a sentinel for "no filter installed". + * - Safe to call multiple times; non-installed entries are skipped. + */ +static void z_co_detach_all_rx_filters(CO_CANmodule_t *CANmodule) +{ + uint_fast16_t i; + + if (!CANmodule || !CANmodule->CANptr || !CANmodule->rxArray) { + return; + } + + const struct device *dev = CANPTR_TO_DEV(CANmodule->CANptr); + + for (i = 0U; i < CANmodule->rxSize; i++) { + if (CANmodule->rxArray[i].filter_id != -ENOSPC) { + can_remove_rx_filter(dev, CANmodule->rxArray[i].filter_id); + CANmodule->rxArray[i].filter_id = -ENOSPC; + } + } +} + +/* + * RX callback bound to hardware filters. + * + * Matches incoming frames against the software buffer table (priority order), + * performs optional RTR handling, repacks data into CO_CANrxMsg_t, and invokes + * the registered CANopen rx callback for the first matching buffer. + * + * Context: + * - Called from Zephyr CAN driver context (may be IRQ or thread depending on driver). + * - Keep it short; heavy work is done later by the stack. + */ +static void z_co_rx_callback(const struct device *dev, struct can_frame *frame, void *user_data) +{ + CO_CANmodule_t *CANmodule = (CO_CANmodule_t *)user_data; + CO_CANrxMsg_t rxMsg; + CO_CANrx_t *buffer; + int i; + + ARG_UNUSED(dev); + + /* Loop through registered rx buffers in priority order */ + for (i = 0; i < CANmodule->rxSize; i++) { + buffer = &CANmodule->rxArray[i]; + + if (buffer->filter_id == -ENOSPC || buffer->CANrx_callback == NULL) { + continue; + } + + /* Masked identifier match */ + if (((frame->id ^ buffer->ident) & buffer->mask) == 0U) { +#ifdef CONFIG_CAN_ACCEPT_RTR + /* If buffer expects RTR, ignore non-RTR frames. */ + if ((buffer->ident & 0x800) && ((frame->flags & CAN_FRAME_RTR) == 0U)) { + continue; + } +#endif /* CONFIG_CAN_ACCEPT_RTR */ + rxMsg.ident = frame->id; + rxMsg.DLC = frame->dlc; + memcpy(rxMsg.data, frame->data, frame->dlc); + if (buffer->CANrx_callback != NULL) { + buffer->CANrx_callback(buffer->object, &rxMsg); + } + break; /* first match wins */ + } + } +} + +static inline uint32_t z_co_next_backoff_ms(struct canopen_tx_work_container *c) +{ + if (c->backoff_ms == 0) { + c->backoff_ms = CONFIG_CANOPENNODE_TX_RETRY_INITIAL_MS; + } else if (c->backoff_ms < CONFIG_CANOPENNODE_TX_RETRY_MAX_MS) { + uint32_t next = c->backoff_ms << 1; + c->backoff_ms = (next > CONFIG_CANOPENNODE_TX_RETRY_MAX_MS) + ? CONFIG_CANOPENNODE_TX_RETRY_MAX_MS + : next; + } + return c->backoff_ms; +} + +static inline void z_co_retry_schedule(uint32_t delay_ms) +{ + if (atomic_get(&canopen_tx_shutdown)) { + return; + } + k_work_schedule_for_queue(&canopen_tx_workq, &canopen_tx_queue.dwork, K_MSEC(delay_ms)); +} + +static inline void z_co_retry_schedule_backoff(void) +{ + if (atomic_get(&canopen_tx_shutdown)) { + return; + } + if (canopen_tx_queue.backoff_ms < CONFIG_CANOPENNODE_TX_RETRY_MAX_MS) { + z_co_retry_schedule(z_co_next_backoff_ms(&canopen_tx_queue)); + } +} + +static inline void z_co_retry_schedule_now(void) +{ + if (atomic_get(&canopen_tx_shutdown)) { + return; + } + canopen_tx_queue.backoff_ms = 0; /* reset on success/explicit wake */ + z_co_retry_schedule(0); +} + +static void z_flush_tx_work(void) +{ + struct k_work_sync sync = {0}; + + (void)k_work_cancel_delayable(&canopen_tx_queue.dwork); + (void)k_work_flush_delayable(&canopen_tx_queue.dwork, &sync); + canopen_tx_queue.backoff_ms = 0; +} + +/* + * TX completion callback from the Zephyr CAN driver. + * + * On success: marks that the first TX was sent and schedules a work item to + * retry any queued frames. On error: logs a warning and still schedules retry + * so buffered frames get another chance. + */ +static void z_co_tx_callback(const struct device *dev, int error, void *arg) +{ + CO_CANmodule_t *CANmodule = arg; + + ARG_UNUSED(dev); + + if (!CANmodule) { + LOG_ERR("tx callback arg invalid"); + return; + } + + if (error != 0) { + LOG_WRN("tx callback error (err %d)", error); + } else { + CANmodule->firstCANtxMessage = false; + } + + if (atomic_get(&canopen_tx_shutdown)) { + return; + } + + /* A slot just freed up—reset backoff and drain now */ + z_co_retry_schedule_now(); +} + +/* + * Work-queue handler that retries sending buffered CAN frames. + * + * Attempts to flush the software TX ring into the controller. If the driver + * reports -EAGAIN (mailbox busy), we stop and the next completion will resubmit + * this work again. + * + * Concurrency: + * - Protected by CO_LOCK_CAN_SEND() to synchronize with CO_CANsend(). + */ +static void z_co_tx_retry(struct k_work *item) +{ + struct canopen_tx_work_container *container = + CONTAINER_OF(item, struct canopen_tx_work_container, dwork.work); + CO_CANmodule_t *CANmodule = container->CANmodule; + const struct device *dev = CANPTR_TO_DEV(CANmodule->CANptr); + struct can_frame frame; + CO_CANtx_t *buffer; + int err; + uint_fast16_t i; + + memset(&frame, 0, sizeof(frame)); + + CO_LOCK_CAN_SEND(); + + for (i = 0; i < CANmodule->txSize; i++) { + buffer = &CANmodule->txArray[i]; + if (buffer->bufferFull) { + frame.id = buffer->ident; + frame.dlc = buffer->DLC; + frame.flags = buffer->flags; + memcpy(frame.data, buffer->data, buffer->DLC); + err = can_send(dev, &frame, K_NO_WAIT, z_co_tx_callback, CANmodule); + if (err == 0) { + /* accepted; reset backoff and clear buffered copy */ + canopen_tx_queue.backoff_ms = 0; + buffer->bufferFull = false; + continue; + } + + switch (err) { + case -EAGAIN: + /* busy / no slot right now */ + __fallthrough; + case -ENETDOWN: + /* stopped */ + __fallthrough; + case -ENETUNREACH: + /* bus-off */ + __fallthrough; + case -EBUSY: + /* arbitration lost (no auto-retry) */ + __fallthrough; + case -EIO: + /* Keep buffered; back off and try again */ + z_co_retry_schedule_backoff(); + /* Stop early so we don’t spin while saturated */ + CO_UNLOCK_CAN_SEND(); + return; + case -EINVAL: + __fallthrough; + case -ENOTSUP: + /* Unrecoverable param/config: drop */ + LOG_ERR("retry send invalid/unsupported (rc %d); dropping frame", + err); + buffer->bufferFull = false; + /* Continue to try other slots */ + break; + default: + LOG_WRN("retry send unexpected rc=%d; backoff", err); + z_co_retry_schedule_backoff(); + CO_UNLOCK_CAN_SEND(); + return; + } + /* Exit the for-loop on first backpressure to yield CPU and avoid thrash */ + break; + } + } + + CO_UNLOCK_CAN_SEND(); +} + +void CO_CANsetConfigurationMode(void *CANptr) +{ + if (!CANptr) { + return; + } + + const struct device *dev = CANPTR_TO_DEV(CANptr); + int err = can_stop(dev); + if (err != 0 && err != -EALREADY) { + LOG_ERR("can stop failed (err %d)", err); + } +} + +void CO_CANsetNormalMode(CO_CANmodule_t *CANmodule) +{ + if (!CANmodule || !CANmodule->CANptr) { + return; + } + + const struct device *dev = CANPTR_TO_DEV(CANmodule->CANptr); + int err = can_start(dev); + if (err != 0 && err != -EALREADY) { + LOG_ERR("can start failed (err %d)", err); + return; + } + + CANmodule->CANnormal = true; +} + +CO_ReturnError_t CO_CANmodule_init(CO_CANmodule_t *CANmodule, void *CANptr, CO_CANrx_t rxArray[], + uint16_t rxSize, CO_CANtx_t txArray[], uint16_t txSize, + uint16_t CANbitRate) +{ + const struct device *dev = CANPTR_TO_DEV(CANptr); + uint_fast16_t i; + int err; + int max_filters; + + LOG_DBG("init: rx_size=%u tx_size=%u", rxSize, txSize); + + /* verify arguments */ + if (CANmodule == NULL || CANptr == NULL || rxArray == NULL || txArray == NULL) { + LOG_ERR("init failed: invalid args"); + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + max_filters = can_get_max_filters(dev, false); + if (max_filters != -ENOSYS) { + if (max_filters < 0) { + LOG_ERR("rx filter count query failed (err %d)", max_filters); + return CO_ERROR_SYSCALL; + } + + if (rxSize > max_filters) { + LOG_ERR("rx filters insufficient: need=%u avail=%d", rxSize, max_filters); + return CO_ERROR_OUT_OF_MEMORY; + } else if (rxSize < max_filters) { + LOG_DBG("rx filters: need=%u avail=%d", rxSize, max_filters); + } + } + + z_co_detach_all_rx_filters(CANmodule); + canopen_tx_queue.CANmodule = CANmodule; + + /* Configure object variables */ + CANmodule->CANptr = CANptr; + CANmodule->rxArray = rxArray; + CANmodule->rxSize = rxSize; + CANmodule->txArray = txArray; + CANmodule->txSize = txSize; + CANmodule->CANerrorStatus = 0; + CANmodule->CANnormal = false; + CANmodule->useCANrxFilters = (rxSize <= max_filters) ? true : false; + CANmodule->firstCANtxMessage = true; + CANmodule->errOld = 0U; + + if (CANmodule->useCANrxFilters) { + /* Filters will be configured by CO_CANrxBufferInit() */ + for (i = 0U; i < rxSize; i++) { + rxArray[i].ident = 0U; + rxArray[i].CANrx_callback = NULL; + rxArray[i].filter_id = -ENOSPC; + } + } else { + /* If filters aren't used, all 11-bit IDs will be received */ + } + + for (i = 0U; i < txSize; i++) { + txArray[i].bufferFull = false; + } + + err = can_set_bitrate(dev, KHZ(CANbitRate)); + if (err) { + LOG_ERR("bitrate set failed (err %d)", err); + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + err = can_set_mode(dev, CAN_MODE_NORMAL); + if (err) { + LOG_ERR("mode set failed (err %d)", err); + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + return CO_ERROR_NO; +} + +void CO_CANmodule_disable(CO_CANmodule_t *CANmodule) +{ + if (!CANmodule || !CANmodule->CANptr) { + return; + } + + const struct device *dev = CANPTR_TO_DEV(CANmodule->CANptr); + + /* Flush TX queue */ + atomic_set(&canopen_tx_shutdown, 1); + z_flush_tx_work(); + + /* Remove all RX filters */ + z_co_detach_all_rx_filters(CANmodule); + + /* Stop the CAN bus */ + int err = can_stop(dev); + if (err != 0 && err != -EALREADY) { + LOG_ERR("can stop failed (err %d)", err); + } +} + +CO_ReturnError_t CO_CANrxBufferInit(CO_CANmodule_t *CANmodule, uint16_t index, uint16_t ident, + uint16_t mask, bool_t rtr, void *object, + void (*CANrx_callback)(void *object, void *message)) +{ + struct can_filter filter; + CO_ReturnError_t ret = CO_ERROR_NO; + + if ((CANmodule != NULL) && (CANmodule->CANptr != NULL) && (object != NULL) && + (CANrx_callback != NULL) && (index < CANmodule->rxSize)) { + + /* Buffer to configure */ + CO_CANrx_t *buffer = &CANmodule->rxArray[index]; + const struct device *dev = CANPTR_TO_DEV(CANmodule->CANptr); + + /* Configure object variables */ + buffer->object = object; + buffer->CANrx_callback = CANrx_callback; + + /* CAN identifier and mask, bit-aligned with CAN module */ + buffer->ident = ident & CAN_STD_ID_MASK; + buffer->mask = (mask & CAN_STD_ID_MASK) | 0x0800U; + +#ifndef CONFIG_CAN_ACCEPT_RTR + if (rtr) { + LOG_WRN("rtr requested but disabled"); + return CO_ERROR_ILLEGAL_ARGUMENT; + } +#else /* CONFIG_CAN_ACCEPT_RTR */ + if (rtr) { + buffer->ident |= 0x0800U; + } +#endif /* CONFIG_CAN_ACCEPT_RTR */ + + /* Set CAN hardware module filter and mask */ + if (CANmodule->useCANrxFilters) { + filter.flags = 0U; + filter.id = ident; + filter.mask = mask; + + if (buffer->filter_id != -ENOSPC) { + can_remove_rx_filter(dev, buffer->filter_id); + } + buffer->filter_id = + can_add_rx_filter(dev, z_co_rx_callback, CANmodule, &filter); + if (buffer->filter_id == -ENOSPC) { + LOG_ERR("rx filter add failed: no slots"); + ret = CO_ERROR_OUT_OF_MEMORY; + } + } + } else { + LOG_ERR("rx buffer init failed: invalid args"); + ret = CO_ERROR_ILLEGAL_ARGUMENT; + } + + return ret; +} + +CO_CANtx_t *CO_CANtxBufferInit(CO_CANmodule_t *CANmodule, uint16_t index, uint16_t ident, + bool_t rtr, uint8_t noOfBytes, bool_t syncFlag) +{ + CO_CANtx_t *buffer = NULL; + + if ((CANmodule != NULL) && (index < CANmodule->txSize)) { + /* specific buffer */ + buffer = &CANmodule->txArray[index]; + buffer->ident = ident & CAN_STD_ID_MASK; + buffer->DLC = noOfBytes; + buffer->flags = rtr ? CAN_FRAME_RTR : 0U; + buffer->bufferFull = false; + buffer->syncFlag = syncFlag; + } + + return buffer; +} + +CO_ReturnError_t CO_CANsend(CO_CANmodule_t *CANmodule, CO_CANtx_t *buffer) +{ + CO_ReturnError_t err = CO_ERROR_NO; + struct can_frame frame; + bool wasFullBefore = false; + + if (!CANmodule || !CANmodule->CANptr || !buffer) { + LOG_ERR("tx send failed: invalid args"); + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + const struct device *dev = CANPTR_TO_DEV(CANmodule->CANptr); + + memset(&frame, 0, sizeof(frame)); + frame.id = buffer->ident; + frame.dlc = buffer->DLC; + frame.flags = buffer->flags; + memcpy(frame.data, buffer->data, buffer->DLC); + + CO_LOCK_CAN_SEND(CANmodule); + wasFullBefore = buffer->bufferFull; + + int rc = can_send(dev, &frame, K_NO_WAIT, z_co_tx_callback, CANmodule); + if (rc == 0) { + /* sent or accepted by driver; callback will fire later */ + canopen_tx_queue.backoff_ms = 0; + err = CO_ERROR_NO; + } else { + switch (rc) { + case -EAGAIN: + /* timeout (mailboxes busy / no immediate slot) */ + __fallthrough; + case -ENETDOWN: + /* controller stopped */ + __fallthrough; + case -ENETUNREACH: + /* bus-off */ + __fallthrough; + case -EBUSY: + /* arbitration lost (auto-retry off) */ + __fallthrough; + case -EIO: + /* general TX error (e.g., missing ACK w/ no auto-retry) */ + /* Buffer and ensure retry work runs */ + buffer->bufferFull = true; + z_co_retry_schedule_backoff(); + /* Only report overflow if we had no software room BEFORE this call */ + err = wasFullBefore ? CO_ERROR_TX_OVERFLOW : CO_ERROR_NO; + break; + case -EINVAL: + __fallthrough; + case -ENOTSUP: + /* Programming/config issue: retrying won’t help. Drop and surface. */ + LOG_ERR("can_send invalid/unsupported params (rc %d); dropping frame", rc); + buffer->bufferFull = false; + err = CO_ERROR_TX_UNCONFIGURED; + break; + default: + /* Unknown negative errno: keep it and retry */ + LOG_WRN("can_send unexpected err %d; will retry", rc); + buffer->bufferFull = true; + z_co_retry_schedule_backoff(); + err = wasFullBefore ? CO_ERROR_TX_OVERFLOW : CO_ERROR_NO; + break; + } + } + + CO_UNLOCK_CAN_SEND(CANmodule); + + return err; +} + +void CO_CANclearPendingSyncPDOs(CO_CANmodule_t *CANmodule) +{ + bool_t tpdoDeleted = false; + CO_CANtx_t *buffer; + uint_fast16_t i; + + if (!CANmodule || !CANmodule->CANptr || !CANmodule->txArray) { + return; + } + + CO_LOCK_CAN_SEND(CANmodule); + + for (i = 0; i < CANmodule->txSize; i++) { + buffer = &CANmodule->txArray[i]; + if (buffer->bufferFull && buffer->syncFlag) { + buffer->bufferFull = false; + tpdoDeleted = true; + } + } + + CO_UNLOCK_CAN_SEND(CANmodule); + + if (tpdoDeleted == true) { + CANmodule->CANerrorStatus |= CO_CAN_ERRTX_PDO_LATE; + } +} + +void CO_CANmodule_process(CO_CANmodule_t *CANmodule) +{ + uint32_t err; + + if (!CANmodule || !CANmodule->CANptr) { + return; + } + + const struct device *dev = CANPTR_TO_DEV(CANmodule->CANptr); + + /* Read controller state and error counters. + * If your can_get_state() takes values (not pointers), drop the '&'. + */ + enum can_state state; + struct can_bus_err_cnt err_cnt; + if (can_get_state(dev, &state, &err_cnt) != 0) { + /* Keep previous status if state can't be read this cycle. */ + return; + } + + /* Map driver counters/state into CANopenNode error inputs. */ + uint16_t txErrors = err_cnt.tx_err_cnt; /* 0..255 from controller */ + uint16_t rxErrors = err_cnt.rx_err_cnt; /* 0..255 from controller */ +#if CONFIG_CAN_STATS + uint8_t overflow = (can_stats_get_rx_overruns(dev) > 0); +#else + uint8_t overflow = 0U; +#endif + /* Ensure BUS-OFF handling even if counters aren't >= 256 yet. */ + if (state == CAN_STATE_BUS_OFF) { + txErrors = 256U; + } + + err = ((uint32_t)txErrors << 16) | ((uint32_t)rxErrors << 8) | overflow; + + if (CANmodule->errOld != err) { + uint_fast16_t status = CANmodule->CANerrorStatus; + + CANmodule->errOld = err; + + if (txErrors >= 256U) { + /* bus off */ + status |= CO_CAN_ERRTX_BUS_OFF; + } else { + /* recalculate CANerrorStatus, first clear some flags */ + status &= 0xFFFFU ^ (CO_CAN_ERRTX_BUS_OFF | CO_CAN_ERRRX_WARNING | + CO_CAN_ERRRX_PASSIVE | CO_CAN_ERRTX_WARNING | + CO_CAN_ERRTX_PASSIVE); + + /* rx bus warning or passive */ + if (rxErrors >= 128U) { + status |= CO_CAN_ERRRX_WARNING | CO_CAN_ERRRX_PASSIVE; + } else if (rxErrors >= 96U) { + status |= CO_CAN_ERRRX_WARNING; + } + + /* tx bus warning or passive */ + if (txErrors >= 128U) { + status |= CO_CAN_ERRTX_WARNING | CO_CAN_ERRTX_PASSIVE; + } else if (txErrors >= 96U) { + status |= CO_CAN_ERRTX_WARNING; + } + + /* if not tx passive clear also overflow */ + if ((status & CO_CAN_ERRTX_PASSIVE) == 0U) { + status &= 0xFFFFU ^ CO_CAN_ERRTX_OVERFLOW; + } + } + + if (overflow != 0U) { + /* CAN RX bus overflow */ + status |= CO_CAN_ERRRX_OVERFLOW; + } + + CANmodule->CANerrorStatus = status; + } +} + +/* + * Module initialization hook. + * + * Creates and starts the dedicated work queue used for deferred TX retries, + * names its thread (handy for debugging), and initializes the work item. + * Runs at SYS_INIT(APPLICATION) stage. + */ +static int z_co_init(void) +{ + k_work_queue_start(&canopen_tx_workq, canopen_tx_workq_stack, + K_KERNEL_STACK_SIZEOF(canopen_tx_workq_stack), + CONFIG_CANOPENNODE_TX_WORKQUEUE_PRIORITY, NULL); + + k_thread_name_set(&canopen_tx_workq.thread, "canopen_tx_workq"); + + k_work_init_delayable(&canopen_tx_queue.dwork, z_co_tx_retry); + + canopen_tx_queue.backoff_ms = 0; + + atomic_clear(&canopen_tx_shutdown); + + return 0; +} + +SYS_INIT(z_co_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); diff --git a/zephyr/CO_zephyr_integration.c b/zephyr/CO_zephyr_integration.c new file mode 100644 index 00000000..601ca0f1 --- /dev/null +++ b/zephyr/CO_zephyr_integration.c @@ -0,0 +1,469 @@ +/* SPDX-License-Identifier: Apache-2.0 */ +/* + * Zephyr-to-CANopenNode integration runtime. + * + * Provides a runtime bridge to start/stop the CANopen stack with a selected + * Zephyr CAN device, Node-ID, and bitrate, enabling control from code in + * addition to prj.conf and devicetree. + * + * @file CO_zephyr_integration.c + * @author BitConcepts, LLC + * @copyright 2025 BitConcepts, LLC + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +#include "CO_zephyr_integration.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "OD.h" + +#if IS_ENABLED(CONFIG_CANOPENNODE_STORAGE_ENABLE) +#include "CO_zephyr_storage.h" +#endif + +#if IS_ENABLED(CONFIG_CANOPENNODE_PROG_DOWNLOAD) +#include "CO_zephyr_prog_download.h" +#endif + +LOG_MODULE_REGISTER(canopen_zephyr, CONFIG_CANOPEN_LOG_LEVEL); + +#define CAN_NODE DT_CHOSEN(zephyr_canbus) +#define CAN_BITRATE_KBPS (DT_PROP(CAN_NODE, bitrate) / 1000U) + +#define DEV_TO_CANPTR(d) ((void *)(uintptr_t)(d)) +#define CANPTR_TO_DEV(p) ((const struct device *)(p)) + +/* ---------- Module state ---------- + * CO / CO_storage are the runtime stack handles. + * g_running is an atomic "stack is active" flag used by the RT thread and signalers. + * rt_sem wakes the RT thread from pre-callbacks and periodic processing. + */ +CO_t *CO = NULL; +#if IS_ENABLED(CONFIG_CANOPENNODE_STORAGE_ENABLE) +static CO_storage_t CO_storage; +#endif +atomic_t g_running; +static const struct device *g_last_can_dev = NULL; +static uint8_t g_last_node_id = 0; +static uint16_t g_last_bitrate_kbps = 0; + +#if IS_ENABLED(CONFIG_CANOPENNODE_PROG_DOWNLOAD) +static CO_ProgDL_t pdl; +static CO_ProgDL_Zephyr_t zb_ctx; +#endif + +K_SEM_DEFINE(rt_sem, 0, UINT_MAX); /* RT thread wake signal */ + +/* ---------- Helpers ---------- */ + +#ifdef CO_DEBUG_COMMON +void z_canopen_log(const char *msg) +{ + LOG_DBG("%s", msg); +} +#endif + +static void z_canopen_nmt_state_cb(CO_NMT_internalState_t state) +{ + const char *state_str = "UNKNOWN"; + switch (state) { + case CO_NMT_INITIALIZING: + state_str = "INITIALIZING"; + break; + case CO_NMT_PRE_OPERATIONAL: + state_str = "PRE-OPERATIONAL"; + break; + case CO_NMT_OPERATIONAL: + state_str = "OPERATIONAL"; + break; + case CO_NMT_STOPPED: + state_str = "STOPPED"; + break; + default: + break; + } + LOG_INF("NMT state changed to %s", state_str); +} + +/* + * Pre-callback used by SYNC/RPDO to poke the RT thread. + * Gives the semaphore only when the stack is marked running. + */ +static void z_rt_signal_cb(void *object) +{ + ARG_UNUSED(object); + if (atomic_get(&g_running)) { + k_sem_give(&rt_sem); + } +} + +/* + * Register a common pre-callback for SYNC and all RPDOs. + * This lets incoming traffic promptly wake the RT thread. + */ +static void z_enable_pre_signals(CO_t *co, void (*pre_cb)(void *), void *arg) +{ +#if IS_ENABLED(CONFIG_CANOPENNODE_SYNC_ENABLE) && IS_ENABLED(CONFIG_CANOPENNODE_SYNC_CALLBACK) + CO_SYNC_initCallbackPre(co->SYNC, pre_cb, arg); +#endif +#if IS_ENABLED(CONFIG_CANOPENNODE_RPDO_ENABLE) && IS_ENABLED(CONFIG_CANOPENNODE_RPDO_CALLBACK) + for (uint16_t i = 0; i < OD_CNT_RPDO; i++) { + CO_RPDO_initCallbackPre(&co->RPDO[i], pre_cb, arg); + } +#endif +} + +/* ---------- RT Thread: SYNC/RPDO/TPDO ---------- */ +#if IS_ENABLED(CONFIG_CANOPENNODE_RT_THREAD) + +__weak uint8_t canopen_get_node_id(); + +/* + * Function used to restart CANopen stack with last known parameters. + */ +static int z_canopen_restart(void) +{ + const struct device *can_dev = g_last_can_dev ? g_last_can_dev : DEVICE_DT_GET(CAN_NODE); + uint8_t node_id = g_last_node_id ? g_last_node_id : (uint8_t)canopen_get_node_id(); + uint16_t bitrate = g_last_bitrate_kbps ? g_last_bitrate_kbps : CAN_BITRATE_KBPS; + + if (atomic_get(&g_running)) { + canopen_stop(); + /* small settle time for CAN driver/filters */ + k_sleep(K_MSEC(10)); + } + return canopen_start(can_dev, node_id, bitrate); +} + +/* + * Real-time CANopen processing thread. + * + * Responsibilities: + * - Waits on a semaphore or timeout. + * - Calls CO_process() (mainline timing and housekeeping). + * - Runs SYNC/RPDO/TPDO processing in a tight, low-latency section. + * + * Timing: + * - Uses uptime (ms) for CO_process() dt. + * - Uses CPU cycle delta for RT section dt to improve precision. + * + * Concurrency: + * - OD access is protected with CO_LOCK_OD()/CO_UNLOCK_OD(). + * - Thread runs continuously but only acts when the stack is running. + */ +static void z_canopen_rt_thread(void *p1, void *p2, void *p3) +{ + ARG_UNUSED(p1); + ARG_UNUSED(p2); + ARG_UNUSED(p3); + + int64_t last_ms = 0; + uint32_t prev_cyc = k_cycle_get_32(); + const uint32_t fallback_us = 1000U; + uint32_t timeout_us = fallback_us; + + while (true) { + k_sem_take(&rt_sem, K_USEC(timeout_us)); + + if (!atomic_get(&g_running) || CO == NULL) { + continue; + } + + /* Mainline: CO_process() */ + CO_NMT_reset_cmd_t reset = CO_RESET_NOT; + int64_t now_ms = k_uptime_get(); + uint32_t dt_us = + (last_ms == 0) ? fallback_us : (uint32_t)MAX(now_ms - last_ms, 0) * 1000U; + last_ms = now_ms; + +#if IS_ENABLED(CONFIG_CANOPENNODE_RT_THREAD_TIMERNEXT) + uint32_t next_main_us = UINT32_MAX; + reset = CO_process(CO, false, dt_us, &next_main_us); +#else + reset = CO_process(CO, false, dt_us, NULL); +#endif + /* RT part: SYNC, RPDO, TPDO */ + uint32_t now_cyc = k_cycle_get_32(); + uint32_t delta_cyc = now_cyc - prev_cyc; + prev_cyc = now_cyc; + + uint32_t dt_rt_us = (uint32_t)(k_cyc_to_ns_floor64(delta_cyc) / 1000U); + + CO_LOCK_OD(); + + uint32_t next_sync_us = UINT32_MAX; + uint32_t next_rpdo_us = UINT32_MAX; + uint32_t next_tpdo_us = UINT32_MAX; + +#if IS_ENABLED(CONFIG_CANOPENNODE_SYNC_ENABLE) + bool_t sync = CO_process_SYNC(CO, dt_rt_us, &next_sync_us); +#else + bool_t sync = false; +#endif +#if IS_ENABLED(CONFIG_CANOPENNODE_RPDO_ENABLE) + CO_process_RPDO(CO, sync, dt_rt_us, &next_rpdo_us); +#endif +#if IS_ENABLED(CONFIG_CANOPENNODE_TPDO_ENABLE) + CO_process_TPDO(CO, sync, dt_rt_us, &next_tpdo_us); +#endif + + CO_UNLOCK_OD(); + + /* Compute next wakeup based on returned timers */ + uint32_t next_rt_us = MIN(next_sync_us, MIN(next_rpdo_us, next_tpdo_us)); + +#if IS_ENABLED(CONFIG_CANOPENNODE_RT_THREAD_TIMERNEXT) + uint32_t next_all = MIN(next_rt_us, next_main_us); + timeout_us = (next_all == 0U || next_all == UINT32_MAX) ? fallback_us : next_all; +#else + timeout_us = fallback_us; +#endif + if (reset == CO_RESET_NOT) { + /* No action */ + continue; + } else if (reset == CO_RESET_COMM) { + /* Reset CANopen stack */ + LOG_INF("Restarting CANopen stack"); + z_canopen_restart(); + } else if (reset == CO_RESET_APP) { + /* Stop CANopen stack and reboot device */ + LOG_INF("Rebooting device"); + canopen_stop(); + k_sleep(K_MSEC(100)); + sys_reboot(SYS_REBOOT_COLD); + } else if (reset == CO_RESET_QUIT) { + /* Stop CANopen stack and exit thread */ + LOG_INF("Stopping CANopen stack"); + canopen_stop(); + break; + } else { + LOG_WRN("Unexpected reset code: %d", reset); + } + } +} + +/* Spawn the real-time processing thread at boot; priority/stack are Kconfig-driven. */ +K_THREAD_DEFINE(canopen_rt, CONFIG_CANOPENNODE_RT_THREAD_STACK_SIZE, z_canopen_rt_thread, NULL, + NULL, NULL, CONFIG_CANOPENNODE_RT_THREAD_PRIORITY, 0, 0); + +#endif /* IS_ENABLED(CANOPENNODE_RT_THREAD) */ + +/* ---------- Public API ---------- */ + +int canopen_start(const struct device *can_dev, uint8_t node_id, uint16_t bitrate_kbps) +{ + if (atomic_get(&g_running)) { + return -EALREADY; + } + if (!can_dev) { + can_dev = DEVICE_DT_GET(CAN_NODE); + } + if (!can_dev || !device_is_ready(can_dev)) { + return -ENODEV; + } + + if (node_id == 0 || node_id > 127) { + return -EINVAL; + } + + if (bitrate_kbps == 0) { + bitrate_kbps = CAN_BITRATE_KBPS; + } + + if (CO != NULL) { + CO_delete(CO); + CO = NULL; + } + + uint32_t heap_used = 0; + CO = CO_new(NULL, &heap_used); + if (CO == NULL) { + LOG_ERR("Memory allocation failed"); + return -ENOMEM; + } + LOG_INF("Allocated %u bytes for CANopen", heap_used); + + int ret = 0; + CO_ReturnError_t err; + uint32_t errInfo = 0; + +#if IS_ENABLED(CONFIG_CANOPENNODE_STORAGE_ENABLE) + CO_storage_entry_t storageEntries[] = {{ + .addr = &OD_PERSIST_COMM, + .len = sizeof(OD_PERSIST_COMM), + .subIndexOD = 2, + .attr = CO_storage_cmd | CO_storage_restore, + }}; + uint8_t entryCount = ARRAY_SIZE(storageEntries); + uint32_t storageErr = 0; + + err = co_zephyr_storage_init(&CO_storage, CO->CANmodule, OD_ENTRY_H1010_storeParameterField, + OD_ENTRY_H1011_restoreDefaultParameters, storageEntries, + entryCount, &storageErr); + + if (err != CO_ERROR_NO) { + LOG_ERR("Storage init failed: %d", err); + ret = -ENOMEM; + goto error; + } + if (storageErr != 0) { + LOG_ERR("Storage error: 0x%X", storageErr); + ret = -EIO; + goto error; + } +#endif + + err = CO_CANinit(CO, DEV_TO_CANPTR(can_dev), bitrate_kbps); + if (err != CO_ERROR_NO) { + LOG_ERR("CAN init failed: %d", err); + ret = -EINVAL; + goto error; + } + +#if IS_ENABLED(CONFIG_CANOPENNODE_LSS_SLAVE) + CO_LSS_address_t lssAddr = { + .identity = {.vendorID = OD_ROM.x1018_identity.vendor_ID, + .productCode = OD_ROM.x1018_identity.productCode, + .revisionNumber = OD_ROM.x1018_identity.revisionNumber, + .serialNumber = OD_ROM.x1018_identity.serialNumber}}; + err = CO_LSSinit(CO, &lssAddr, &node_id, &bitrate_kbps); + if (err != CO_ERROR_NO) { + LOG_ERR("LSS init failed: %d", err); + ret = -EINVAL; + goto error; + } +#endif + + err = CO_CANopenInit(CO, NULL, NULL, OD, NULL, CO_CONFIG_NMT_CONTROL, + CO_NMT_FIRST_HB_TIME_MS, CO_CONFIG_SDO_SRV_TIMEOUT_MS, + CO_CONFIG_SDO_CLI_TIMEOUT_MS, CO_CONFIG_SDO_CLI_BLOCK, node_id, + &errInfo); + + if (err != CO_ERROR_NO && err != CO_ERROR_NODE_ID_UNCONFIGURED_LSS) { + LOG_ERR("CANopen init failed: %d (OD entry 0x%X)", err, errInfo); + ret = -EIO; + goto error; + } + + err = CO_CANopenInitPDO(CO, CO->em, OD, node_id, &errInfo); + if (err != CO_ERROR_NO && err != CO_ERROR_NODE_ID_UNCONFIGURED_LSS) { + LOG_ERR("PDO init failed: %d (OD entry 0x%X)", err, errInfo); + ret = -EIO; + goto error; + } + +#if IS_ENABLED(CONFIG_CANOPENNODE_PROG_DOWNLOAD) + { + /* If your binder takes a partition ID and optional CO_storage handle: */ + err = CO_Prog_Download_zephyr_bind_default(&pdl, &zb_ctx); + if (err != CO_ERROR_NO) { + LOG_ERR("Program Download bind failed: %d", err); + ret = -EINVAL; + goto error; + } + } +#endif + + if (!CO->nodeIdUnconfigured) { +#if IS_ENABLED(CONFIG_CANOPENNODE_STORAGE_ENABLE) + if (storageErr != 0) { + CO_errorReport(CO->em, CO_EM_NON_VOLATILE_MEMORY, CO_EMC_HARDWARE, + storageErr); + } +#endif + } else { + LOG_INF("Node-ID not configured (LSS active)"); + } + +#if IS_ENABLED(CONFIG_CANOPENNODE_NMT_CALLBACK) + CO_NMT_initCallbackChanged(CO->NMT, z_canopen_nmt_state_cb); +#endif + + z_enable_pre_signals(CO, z_rt_signal_cb, NULL); + atomic_set(&g_running, 1); + CO_CANsetNormalMode(CO->CANmodule); + + /* Save previous values */ + g_last_can_dev = can_dev; + g_last_node_id = node_id; + g_last_bitrate_kbps = bitrate_kbps; + + LOG_INF("CANopenNode running"); + return 0; + +error: + if (CO) { + CO_delete(CO); + CO = NULL; + } + return ret; +} + +void canopen_stop(void) +{ + if (!atomic_get(&g_running)) { + return; + } + + atomic_clear(&g_running); + + if (CO != NULL) { + CO_CANmodule_disable(CO->CANmodule); + CO_delete(CO); + CO = NULL; + LOG_INF("CANopenNode stopped"); + } +} + +void canopen_error_report(uint8_t errorBit, uint16_t errorCode, uint32_t infoCode) +{ + if (CO && CO->em) { + CO_errorReport(CO->em, errorBit, errorCode, infoCode); + } +} + +void canopen_error_reset(uint8_t errorBit, uint32_t infoCode) +{ + if (CO && CO->em) { + CO_errorReset(CO->em, errorBit, infoCode); + } +} + +__weak uint8_t canopen_get_node_id() +{ + return CONFIG_CANOPENNODE_INIT_NODE_ID; +} + +/* + * System init hook. + * Optionally auto-starts CANopen at POST_KERNEL if configured via Kconfig. + */ +static int z_co_init_sys(void) +{ +#if IS_ENABLED(CONFIG_CANOPENNODE_RT_THREAD_AUTO_START) + (void)canopen_start(NULL, canopen_get_node_id(), CAN_BITRATE_KBPS); +#endif + + return 0; +} +SYS_INIT(z_co_init_sys, POST_KERNEL, CONFIG_CANOPENNODE_INIT_PRIORITY); diff --git a/zephyr/CO_zephyr_leds.c b/zephyr/CO_zephyr_leds.c new file mode 100644 index 00000000..b538c0b8 --- /dev/null +++ b/zephyr/CO_zephyr_leds.c @@ -0,0 +1,105 @@ +/* SPDX-License-Identifier: Apache-2.0 */ +/* + * Zephyr-to-CANopenNode LED bridge. + * + * Mirrors the computed CANopen RUN/ERR LED states (from CO_LEDs) to Zephyr GPIOs + * using devicetree aliases. Respects ACTIVE_LOW/HIGH via DT flags and provides + * a callback-based hookup and one-shot sync utility. + * + * @file CO_zephyr_leds.c + * @author BitConcepts, LLC + * @copyright 2025 BitConcepts, LLC + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +#include "CO_zephyr_leds.h" + +#include +#include +#include +#include + +#define RUN_NODE DT_ALIAS(co_led_run) +#define ERR_NODE DT_ALIAS(co_led_err) + +/* Ensure the expected DT aliases exist and are enabled. */ +BUILD_ASSERT(DT_NODE_HAS_STATUS(RUN_NODE, okay), "DT alias 'co-led-run' missing or disabled"); +BUILD_ASSERT(DT_NODE_HAS_STATUS(ERR_NODE, okay), "DT alias 'co-led-err' missing or disabled"); + +/* File-local GPIO descriptors resolved from devicetree. */ +static const struct gpio_dt_spec LED_RUN = GPIO_DT_SPEC_GET(RUN_NODE, gpios); +static const struct gpio_dt_spec LED_ERR = GPIO_DT_SPEC_GET(ERR_NODE, gpios); + +/* Set true after successful pin configuration; guards callbacks before init. */ +static bool hw_ready; + +/* + * CANopen LED callback. + * Maps CO_LEDs_t state bits to the two GPIOs. Safe to call anytime after init. + */ +static void z_leds_cb(CO_LEDs_t *leds, void *user_arg) +{ + ARG_UNUSED(user_arg); + if (!hw_ready || leds == NULL) { + return; + } + + const bool run_on = (leds->LEDgreen & CO_LED_CANopen) != 0; + const bool err_on = (leds->LEDred & CO_LED_CANopen) != 0; + + (void)gpio_pin_set_dt(&LED_RUN, run_on); + (void)gpio_pin_set_dt(&LED_ERR, err_on); +} + +int co_zephyr_leds_init_dt_aliases(void) +{ + if (!device_is_ready(LED_RUN.port) || !device_is_ready(LED_ERR.port)) { + return -ENODEV; + } + + int ret = gpio_pin_configure_dt(&LED_RUN, GPIO_OUTPUT_INACTIVE); + if (ret) { + return ret; + } + + ret = gpio_pin_configure_dt(&LED_ERR, GPIO_OUTPUT_INACTIVE); + if (ret) { + return ret; + } + + hw_ready = true; + return 0; +} + +void co_zephyr_leds_connect_callback(CO_LEDs_t *leds) +{ + if (!leds) { + return; + } + CO_LEDs_registerCallback(leds, z_leds_cb, NULL); + co_zephyr_leds_sync_once(leds); +} + +void co_zephyr_leds_sync_once(CO_LEDs_t *leds) +{ + if (!hw_ready || !leds) { + return; + } + + const bool run_on = (leds->LEDgreen & CO_LED_CANopen) != 0; + const bool err_on = (leds->LEDred & CO_LED_CANopen) != 0; + + (void)gpio_pin_set_dt(&LED_RUN, run_on); + (void)gpio_pin_set_dt(&LED_ERR, err_on); +} diff --git a/zephyr/CO_zephyr_prog_download.c b/zephyr/CO_zephyr_prog_download.c new file mode 100644 index 00000000..158984c7 --- /dev/null +++ b/zephyr/CO_zephyr_prog_download.c @@ -0,0 +1,243 @@ +/* SPDX-License-Identifier: Apache-2.0 */ +/* + * Zephyr backend (ops) for CANopen Program Download (CiA 302-3 oriented). + * + * See header for details. This file implements CO_ProgDL_StreamOps_t using + * Zephyr flash areas and MCUBoot upgrade APIs. + */ + +#include "CO_zephyr_prog_download.h" +#include + +LOG_MODULE_REGISTER(canopen_prog_download, CONFIG_CANOPEN_LOG_LEVEL); + +/* -------- Internal helpers -------- */ + +static bool_t z_pdl_begin(uint32_t size_hint); +static bool_t z_pdl_write(const uint8_t *data, uint32_t len); +static bool_t z_pdl_commit(void); +static void z_pdl_abort(void); +static void z_pdl_jump_to_boot(void); + +/* We record a back-pointer from ops to our context. */ +typedef struct { + CO_ProgDL_Zephyr_t *zb; +} z_ctx_holder_t; + +static z_ctx_holder_t g_ctx = {0}; /* One instance per app; adjust if you need multiple. */ + +static inline CO_ProgDL_Zephyr_t *ctx(void) +{ + return g_ctx.zb; +} + +/* -------- Ops implementation -------- */ + +static bool_t z_open_and_erase(CO_ProgDL_Zephyr_t *zb, uint32_t size_hint) +{ + int rc; + + /* Open the flash area for the secondary slot. */ + rc = flash_area_open(zb->area_id, &zb->fa); + if (rc != 0 || zb->fa == NULL) { + LOG_ERR("flash_area_open(%u) failed: %d", zb->area_id, rc); + zb->fa = NULL; + return false; + } + + /* Bounds check: ensure image fits in area. */ + if (size_hint != 0U && size_hint > zb->fa->fa_size) { + LOG_ERR("size_hint (%u) exceeds area size (%u)", size_hint, zb->fa->fa_size); + flash_area_close(zb->fa); + zb->fa = NULL; + return false; + } + + /* Full erase of the target area. */ + rc = flash_area_erase(zb->fa, 0, zb->fa->fa_size); + if (rc != 0) { + LOG_ERR("flash_area_erase failed: %d", rc); + flash_area_close(zb->fa); + zb->fa = NULL; + return false; + } + + zb->off = 0U; + zb->expected_size = size_hint; + return true; +} + +static bool_t z_pdl_begin(uint32_t size_hint) +{ + CO_ProgDL_Zephyr_t *zb = ctx(); + if (zb == NULL) { + return false; + } + + k_mutex_lock(&zb->lock, K_FOREVER); + + if (zb->active) { + /* If a previous session is active, abort/close it. */ + if (zb->fa) { + flash_area_close(zb->fa); + zb->fa = NULL; + } + zb->active = false; + zb->off = 0; + } + + bool ok = z_open_and_erase(zb, size_hint); + if (ok) { + zb->active = true; + LOG_INF("BEGIN OK: area=%u size_hint=%u", zb->area_id, size_hint); + } + + k_mutex_unlock(&zb->lock); + return ok; +} + +static bool_t z_pdl_write(const uint8_t *data, uint32_t len) +{ + CO_ProgDL_Zephyr_t *zb = ctx(); + if (zb == NULL || !zb->active || zb->fa == NULL || data == NULL || len == 0U) { + return false; + } + + k_mutex_lock(&zb->lock, K_FOREVER); + + /* Honor device alignment if needed. */ + uint32_t align = flash_area_align(zb->fa); + if (align > 1U) { + /* We accept any length; Zephyr will handle internal alignment as long as + * controller supports it. If your SoC requires aligned writes, you can + * add a small staging buffer here. For most MCUBoot targets, unaligned + * chunks are OK. + */ + (void)align; + } + + /* Check bounds if expected_size is known. */ + if (zb->expected_size && (zb->off + len > zb->expected_size)) { + LOG_ERR("Write would exceed expected_size: off=%u len=%u exp=%u", zb->off, len, + zb->expected_size); + k_mutex_unlock(&zb->lock); + return false; + } + + int rc = flash_area_write(zb->fa, zb->off, data, len); + if (rc != 0) { + LOG_ERR("flash_area_write off=%u len=%u failed: %d", zb->off, len, rc); + k_mutex_unlock(&zb->lock); + return false; + } + + zb->off += len; + + k_mutex_unlock(&zb->lock); + return true; +} + +static bool_t z_pdl_commit(void) +{ + CO_ProgDL_Zephyr_t *zb = ctx(); + if (zb == NULL || !zb->active) { + return false; + } + + k_mutex_lock(&zb->lock, K_FOREVER); + + /* Optional: sanity check if expected_size was given. */ + if (zb->expected_size && zb->off != zb->expected_size) { + LOG_WRN("COMMIT with size mismatch: written=%u expected=%u", zb->off, + zb->expected_size); + /* Not fatal — MCUBoot will validate the image anyway. */ + } + + /* Close area before requesting upgrade. */ + if (zb->fa) { + flash_area_close(zb->fa); + zb->fa = NULL; + } + + /* Ask MCUBoot to upgrade on next boot. */ + int rc = boot_request_upgrade(zb->permanent_upgrade); + if (rc != 0) { + LOG_ERR("boot_request_upgrade(permanent=%d) failed: %d", + zb->permanent_upgrade ? 1 : 0, rc); + zb->active = false; + k_mutex_unlock(&zb->lock); + return false; + } + + /* Mark session closed. */ + zb->active = false; + LOG_INF("COMMIT OK: bytes=%u, upgrade=%s", zb->off, + zb->permanent_upgrade ? "permanent" : "test"); + + k_mutex_unlock(&zb->lock); + return true; +} + +static void z_pdl_abort(void) +{ + CO_ProgDL_Zephyr_t *zb = ctx(); + if (zb == NULL) { + return; + } + + k_mutex_lock(&zb->lock, K_FOREVER); + + if (zb->fa) { + /* Just close; we keep the area contents (already erased) as-is. */ + flash_area_close(zb->fa); + zb->fa = NULL; + } + + zb->active = false; + zb->off = 0; + zb->expected_size = 0; + + LOG_INF("ABORT done"); + + k_mutex_unlock(&zb->lock); +} + +static void z_pdl_jump_to_boot(void) +{ + sys_reboot(SYS_REBOOT_WARM); +} + +/* -------- Public binding -------- */ + +int CO_zephyr_prog_download_bind(CO_ProgDL_t *pdl, CO_ProgDL_Zephyr_t *zb, uint8_t area_id, + bool permanent_upgrade) +{ + if (pdl == NULL || zb == NULL) { + return -EINVAL; + } + + (void)memset(zb, 0, sizeof(*zb)); + zb->area_id = area_id; + zb->permanent_upgrade = permanent_upgrade; + k_mutex_init(&zb->lock); + + /* Publish ops into PDL. */ + CO_ProgDL_StreamOps_t ops = { + .begin = z_pdl_begin, + .write = z_pdl_write, + .commit = z_pdl_commit, + .abort = z_pdl_abort, + .jumpToBootloader = z_pdl_jump_to_boot, + }; + + g_ctx.zb = zb; + + if (CO_Prog_Download_registerStreamOps(pdl, &ops) != 0) { + g_ctx.zb = NULL; + return -EIO; + } + + LOG_INF("CO_Prog_Download Zephyr backend bound: area=%u, %s", zb->area_id, + zb->permanent_upgrade ? "permanent" : "test"); + return 0; +} diff --git a/zephyr/CO_zephyr_storage.c b/zephyr/CO_zephyr_storage.c new file mode 100644 index 00000000..31f627fb --- /dev/null +++ b/zephyr/CO_zephyr_storage.c @@ -0,0 +1,133 @@ +/* SPDX-License-Identifier: Apache-2.0 */ +/* + * CANopen Object Dictionary storage for Zephyr backends. + * + * Zephyr-backed implementation of the CANopenNode storage object that + * provides persistent parameter handling (load / store / restore) suitable + * for production systems. Integrates with Zephyr subsystems (e.g. Settings + * or NVS/flash), as selected by the application. + * + * @file CO_zephyr_storage.c + * @author Janez Paternoster (original template) + * @author BitConcepts, LLC + * @copyright 2021 Janez Paternoster + * @copyright 2025 BitConcepts, LLC + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +#include "CO_zephyr_storage.h" + +#include +#include +#include +#include + +LOG_MODULE_REGISTER(canopen_storage, CONFIG_CANOPEN_LOG_LEVEL); + +#ifdef CONFIG_CANOPEN_STORAGE_BACKEND_SETTINGS +#include +#endif + +/* Store callback for OD 0x1010 "Store Parameters". + * Persists one configured storage entry using the selected backend. + * Returns ODR_OK on success, ODR_HW on backend failure. + */ +static ODR_t z_store(CO_storage_entry_t *entry, CO_CANmodule_t *CANmodule) +{ +#if defined(CONFIG_CANOPEN_STORAGE_BACKEND_SETTINGS) + char key[64]; + snprintf(key, sizeof(key), "canopen/od/%04X", entry->subIndexOD); + int err = settings_save_one(key, entry->addr, entry->len); + if (err) { + LOG_ERR("Settings save failed (%d) for key %s", err, key); + return ODR_HW; + } +#elif defined(CONFIG_CANOPEN_STORAGE_BACKEND_RAM) + LOG_DBG("Skipping store (RAM-only backend)"); +#else + LOG_WRN("No valid storage backend selected — store operation skipped"); +#endif + return ODR_OK; +} + +/* Restore callback for OD 0x1011 "Restore Default Parameters". + * Clears any persisted value for the entry (or no-ops for RAM), + * so defaults take effect after the next load. + */ +static ODR_t z_restore(CO_storage_entry_t *entry, CO_CANmodule_t *CANmodule) +{ +#if defined(CONFIG_CANOPEN_STORAGE_BACKEND_SETTINGS) + char key[64]; + snprintf(key, sizeof(key), "canopen/od/%04X", entry->subIndexOD); + int err = settings_delete(key); + if (err) { + LOG_WRN("Settings delete failed (%d) for key %s", err, key); + } +#elif defined(CONFIG_CANOPEN_STORAGE_BACKEND_RAM) + LOG_DBG("Skipping restore (RAM-only backend)"); +#else + LOG_WRN("No valid storage backend selected — restore operation skipped"); +#endif + return ODR_OK; +} + +/* Initialize CO_storage with Zephyr-backed store/restore hooks and + * optionally load initial values from the selected backend. + */ +CO_ReturnError_t co_zephyr_storage_init(CO_storage_t *storage, CO_CANmodule_t *CANmodule, + OD_entry_t *OD_1010_StoreParameters, + OD_entry_t *OD_1011_RestoreDefaultParam, + CO_storage_entry_t *entries, uint8_t entriesCount, + uint32_t *storageInitError) +{ + if (storage == NULL || entries == NULL || entriesCount == 0 || storageInitError == NULL) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + CO_ReturnError_t ret = CO_storage_init(storage, CANmodule, OD_1010_StoreParameters, + OD_1011_RestoreDefaultParam, z_store, z_restore, + entries, entriesCount); + if (ret != CO_ERROR_NO) { + return ret; + } + + /* Mark "no index error" unless we detect a bad entry below. */ + *storageInitError = 0; + + /* Optionally preload values from the backend into RAM. */ + for (uint8_t i = 0; i < entriesCount; i++) { + CO_storage_entry_t *entry = &entries[i]; + + /* Basic entry validation: require addr/len and subindex >= 2. */ + if (entry->addr == NULL || entry->len == 0 || entry->subIndexOD < 2) { + *storageInitError = i; + return CO_ERROR_ILLEGAL_ARGUMENT; + } + +#if defined(CONFIG_CANOPEN_STORAGE_BACKEND_SETTINGS) + char key[64]; + snprintf(key, sizeof(key), "canopen/od/%04X", entry->subIndexOD); + int rc = settings_load_subtree_direct(key, entry->addr, entry->len); + if (rc < 0) { + LOG_DBG("No settings found for %s", key); + } +#elif defined(CONFIG_CANOPEN_STORAGE_BACKEND_RAM) + /* RAM-only backend: nothing to load; defaults already in place. */ +#else + LOG_WRN("No valid storage backend selected — skipping restore for 0x%02X", + entry->subIndexOD); +#endif + } + return ret; +} diff --git a/zephyr/Kconfig b/zephyr/Kconfig new file mode 100644 index 00000000..ffae1f3d --- /dev/null +++ b/zephyr/Kconfig @@ -0,0 +1,877 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright (c) 2025 BitConcepts, LLC +# +# CANopenNode — Zephyr module Kconfig +# +# This Kconfig file exposes CANopenNode features to Zephyr as CONFIG_* options. +# It lets you enable/disable protocol modules (NMT, SDO, PDO, etc.), choose +# Zephyr-specific integrations (storage backend, LED GPIO bridge), and set +# module-level parameters (buffers, timeouts, thread options). +# +# @file Kconfig +# @author BitConcepts, LLC +# +# This file is part of , a CANopen Stack. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software distributed under the License +# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +# or implied. See the License for the specific language governing permissions and limitations under +# the License. +# +# Usage notes: +# - Enable top-level CONFIG_CANOPENNODE=y in prj.conf to include the stack. +# - Most options default to sensible values; adjust per application needs. +# - Some features imply others via 'select' (see help for each symbol). +# - For LED helpers (CiA 303-3), provide devicetree aliases: +# aliases { +# co_led_run = &led0; +# co_led_err = &led1; +# }; +# - For Object Dictionary generation, set CONFIG_CANOPENNODE_EDS_FILE_PATH +# to an EDS relative to your application root (see CMakeLists.txt logic). + +menuconfig CANOPENNODE + bool "CANopenNode protocol stack support" + depends on CAN + default n + help + Enable the CANopenNode stack integration for Zephyr. + +if CANOPENNODE + +menu "NMT / Heartbeat producer - Specified in standard CiA 301" + +config CANOPENNODE_NMT_CALLBACK_CHANGE + bool "Enable custom callback after NMT state changes" + default y + help + Enable a custom callback invoked after the NMT state changes. + Configure via CO_NMT_initCallbackChanged(). + +config CANOPENNODE_NMT_MASTER + bool "Enable simple NMT master" + default n + help + Enable a simple NMT master object to control other nodes' + NMT states (start/stop/reset). + +config CANOPENNODE_NMT_CALLBACK + bool "Enable custom callback after preprocessing received NMT message" + default y + help + Enable a custom callback invoked after a received NMT message + is preprocessed. Configure via CO_NMT_initCallbackPre(). + +config CANOPENNODE_NMT_TIMERNEXT + bool "Enable NMT timerNext_us calculation" + default y + help + Enable calculation of timerNext_us for NMT to schedule next processing. + +config CANOPENNODE_NMT_FIRST_HB_TIME_MS + int "First heartbeat delay (ms)" + range 0 65535 + default 0 + help + Delay before the first Heartbeat after boot/reset. + 0 = no extra delay (start HB with the normal 0x1017 period). + Typical values: 200-1000 ms to let peers install filters and avoid + false HB-consumer timeouts during bus bring-up. + +endmenu + +menu "NMT startup/control" + +config CANOPENNODE_NMT_STARTUP_TO_OPERATIONAL + bool "Start in Operational after init (CO_NMT_STARTUP_TO_OPERATIONAL)" + default y + +config CANOPENNODE_NMT_ERR_ON_BUSOFF_HB + bool "Error on bus-off / HB timeout (CO_NMT_ERR_ON_BUSOFF_HB)" + default y + +config CANOPENNODE_NMT_ERR_ON_ERR_REG + bool "Error on nonzero masked error register (CO_NMT_ERR_ON_ERR_REG)" + default y + +config CANOPENNODE_NMT_ERR_TO_STOPPED + bool "On error -> go to Stopped (else Pre-op) (CO_NMT_ERR_TO_STOPPED)" + default n + +config CANOPENNODE_NMT_ERR_FREE_TO_OPERATIONAL + bool "Auto-return to Operational when errors clear (CO_NMT_ERR_FREE_TO_OPERATIONAL)" + default n + +comment "Error register mask (low 8 bits of NMT control)" + +endmenu + +menu "Heartbeat consumer - Specified in standard CiA 301" + +config CANOPENNODE_HB_CONS_ENABLE + bool "Enable heartbeat consumer" + default n + help + Monitor heartbeat messages from other nodes and trigger actions + based on their status. + +choice CANOPENNODE_HB_CONS_CALLBACK_MODE + prompt "Heartbeat consumer callback mode" + depends on CANOPENNODE_HB_CONS_ENABLE + optional + default CANOPENNODE_HB_CONS_CALLBACK_CHANGE + help + Select how heartbeat/NMT-change callbacks are handled. + Leave unset for no callbacks. Only one option can be active. + +config CANOPENNODE_HB_CONS_CALLBACK_CHANGE + bool "Single common callback after NMT state change" + help + Enable a single common callback invoked on NMT state changes. + Configure via CO_HBconsumer_initCallbackNmtChanged(). + +config CANOPENNODE_HB_CONS_CALLBACK_MULTI + bool "Per-node callbacks" + help + Enable per-node callbacks for NMT change, heartbeat started, + timeout, and remote reset. Configure via: + CO_HBconsumer_initCallbackNmtChanged(), + CO_HBconsumer_initCallbackHeartbeatStarted(), + CO_HBconsumer_initCallbackTimeout(), + CO_HBconsumer_initCallbackRemoteReset(). + +endchoice + +config CANOPENNODE_HB_CONS_QUERY_FUNCT + bool "Enable heartbeat and NMT query functions" + depends on CANOPENNODE_HB_CONS_ENABLE + default n + +config CANOPENNODE_HB_CONS_CALLBACK + bool "Enable custom callback after heartbeat message received" + depends on CANOPENNODE_HB_CONS_ENABLE + default n + help + Enable a callback invoked after a heartbeat message is preprocessed. + Configure via CO_HBconsumer_initCallbackPre(). + +config CANOPENNODE_HB_CONS_TIMERNEXT + bool "Enable heartbeat consumer timerNext_us calculation" + depends on CANOPENNODE_HB_CONS_ENABLE + default y + +config CANOPENNODE_HB_CONS_OD_DYNAMIC + bool "Enable dynamic behaviour of heartbeat consumer OD variables" + depends on CANOPENNODE_HB_CONS_ENABLE + default y + +endmenu + +menu "CANopen Node Guarding slave and master objects - Specified in standard CiA 301" + +config CANOPENNODE_NODE_GUARDING_SLAVE_ENABLE + bool "Enable Node guarding slave" + default n + +config CANOPENNODE_NODE_GUARDING_MASTER_ENABLE + bool "Enable Node guarding master" + default n + +config CANOPENNODE_NODE_GUARDING_MASTER_COUNT + int "Max nodes monitored by guarding master" + range 1 127 + default 127 + +config CANOPENNODE_NODE_GUARDING_TIMERNEXT + bool "Enable Node guarding timerNext_us calculation" + depends on CANOPENNODE_NODE_GUARDING_SLAVE_ENABLE || CANOPENNODE_NODE_GUARDING_MASTER_ENABLE + default n + +endmenu + +menu "Emergency producer/consumer - Specified in standard CiA 301" + +config CANOPENNODE_EM_PRODUCER + bool "Enable emergency producer" + default n + +config CANOPENNODE_EM_PROD_CONFIGURABLE + bool "Enable configurable COB-ID for emergency producer" + default n + +config CANOPENNODE_EM_PROD_INHIBIT + bool "Enable inhibit time for emergency producer" + default n + +config CANOPENNODE_EM_HISTORY + bool "Enable emergency error history" + default y + +config CANOPENNODE_EM_CONSUMER + bool "Enable emergency consumer" + default n + +config CANOPENNODE_EM_STATUS_BITS + bool "Enable emergency status bits in Object Dictionary" + default n + +config CANOPENNODE_EM_CALLBACK + bool "Enable custom callback after emergency message received" + default y + +config CANOPENNODE_EM_TIMERNEXT + bool "Enable emergency timerNext_us calculation" + default y + +config CANOPENNODE_EM_ERR_STATUS_BITS_COUNT + int "Emergency error status bits (multiple of 8)" + range 48 256 + default 80 + +# Optional error-register conditions (enable default expressions in mapper) +config CANOPENNODE_ERR_CONDITION_CURRENT + bool "Condition for calculating Error register: CURRENT" + default n + +config CANOPENNODE_ERR_CONDITION_VOLTAGE + bool "Condition for calculating Error register: VOLTAGE" + default n + +config CANOPENNODE_ERR_CONDITION_TEMPERATURE + bool "Condition for calculating Error register: TEMPERATURE" + default n + +config CANOPENNODE_ERR_CONDITION_DEV_PROFILE + bool "Condition for calculating Error register: DEVICE PROFILE" + default n + +endmenu + +menu "SDO server - Specified in standard CiA 301" + +config CANOPENNODE_SDO_SERVER_SEGMENTED + bool "Enable segmented SDO server" + default n + +config CANOPENNODE_SDO_SERVER_BLOCK + bool "Enable block SDO server" + select CANOPENNODE_SDO_SERVER_SEGMENTED + select CANOPENNODE_CRC16_ENABLE + default n + help + Use 127-segment block transfers with optional CRC for large, efficient transfers. + +config CANOPENNODE_SDO_SERVER_CALLBACK + bool "Enable SDO server callback" + default y + +config CANOPENNODE_SDO_SERVER_TIMERNEXT + bool "Enable SDO server timerNext_us calculation" + default y + +config CANOPENNODE_SDO_SERVER_OD_DYNAMIC + bool "Enable dynamic behaviour of SDO server OD variables" + default y + +config CANOPENNODE_SDO_SERVER_BUFFER_SIZE + int "SDO server buffer size" + range 900 4096 if CANOPENNODE_SDO_SERVER_BLOCK + range 20 4096 if !CANOPENNODE_SDO_SERVER_BLOCK + default 900 if CANOPENNODE_SDO_SERVER_BLOCK + default 32 + help + Internal buffer for SDO server. + - >=20 for segmented + - >=900 recommended for block (127 x 7 bytes) + +config CANOPENNODE_SDO_SERVER_TIMEOUT_MS + int "SDO server timeout (ms)" + range 50 60000 + default 1000 + +endmenu + +menu "SDO client - Specified in standard CiA 301" + +config CANOPENNODE_SDO_CLIENT_ENABLE + bool "Enable SDO client" + select CANOPENNODE_FIFO_ENABLE + default n + +config CANOPENNODE_SDO_CLIENT_SEGMENTED + bool "Enable SDO client segmented transfer" + depends on CANOPENNODE_SDO_CLIENT_ENABLE + default n + +config CANOPENNODE_SDO_CLIENT_BLOCK + bool "Enable SDO client block transfer" + depends on CANOPENNODE_SDO_CLIENT_ENABLE + select CANOPENNODE_SDO_CLIENT_SEGMENTED + select CANOPENNODE_FIFO_ALT_READ + select CANOPENNODE_FIFO_CRC16_CCITT + default n + +config CANOPENNODE_SDO_CLIENT_LOCAL + bool "Enable local transfer (client/server same node)" + depends on CANOPENNODE_SDO_CLIENT_ENABLE + default n + +config CANOPENNODE_SDO_CLIENT_CALLBACK + bool "Enable SDO client callback" + depends on CANOPENNODE_SDO_CLIENT_ENABLE + default y + +config CANOPENNODE_SDO_CLIENT_TIMERNEXT + bool "Enable SDO client timerNext_us calculation" + depends on CANOPENNODE_SDO_CLIENT_ENABLE + default y + +config CANOPENNODE_SDO_CLIENT_OD_DYNAMIC + bool "Enable dynamic behaviour of SDO client OD variables" + depends on CANOPENNODE_SDO_CLIENT_ENABLE + default y + +config CANOPENNODE_SDO_CLIENT_BUFFER_SIZE + int "SDO client buffer size" + range 1000 4096 if CANOPENNODE_SDO_CLIENT_BLOCK + range 7 4096 if CANOPENNODE_SDO_CLIENT_SEGMENTED && !CANOPENNODE_SDO_CLIENT_BLOCK + default 1000 if CANOPENNODE_SDO_CLIENT_BLOCK + default 32 if !CANOPENNODE_SDO_CLIENT_BLOCK + help + Circular buffer used by SDO client. Can be <= object size since it is + processed over multiple calls. >=7 for segmented; >=1000 recommended for block. + +config CANOPENNODE_SDO_CLIENT_TIMEOUT_MS + int "SDO client timeout (ms)" + range 50 60000 + default 1000 + +endmenu + +menu "Time producer/consumer - Specified in standard CiA 301" + +config CANOPENNODE_TIME_ENABLE + bool "Enable TIME object and TIME consumer" + default n + +config CANOPENNODE_TIME_PRODUCER + bool "Enable TIME producer" + depends on CANOPENNODE_TIME_ENABLE + default n + +config CANOPENNODE_TIME_CALLBACK + bool "Enable TIME callback" + depends on CANOPENNODE_TIME_ENABLE + default y + +config CANOPENNODE_TIME_OD_DYNAMIC + bool "Enable dynamic behaviour of TIME OD variables" + depends on CANOPENNODE_TIME_ENABLE + default y + +endmenu + +menu "SYNC and PDO producer/consumer - Specified in standard CiA 301" + +config CANOPENNODE_SYNC_ENABLE + bool "Enable SYNC object and SYNC consumer" + default n + +config CANOPENNODE_SYNC_PRODUCER + bool "Enable SYNC producer" + depends on CANOPENNODE_SYNC_ENABLE + default n + +config CANOPENNODE_SYNC_CALLBACK + bool "Enable SYNC callback" + depends on CANOPENNODE_SYNC_ENABLE + default y + +config CANOPENNODE_SYNC_TIMERNEXT + bool "Enable SYNC timerNext_us calculation" + depends on CANOPENNODE_SYNC_ENABLE + default y + +config CANOPENNODE_SYNC_OD_DYNAMIC + bool "Enable dynamic behaviour of SYNC OD variables" + depends on CANOPENNODE_SYNC_ENABLE + default y + +config CANOPENNODE_RPDO_ENABLE + bool "Enable receive PDO objects (RPDOs)" + default n + +config CANOPENNODE_TPDO_ENABLE + bool "Enable transmit PDO objects (TPDOs)" + default n + +config CANOPENNODE_RPDO_TIMERS_ENABLE + bool "Enable RPDO timers" + depends on CANOPENNODE_RPDO_ENABLE + default y if CANOPENNODE_RPDO_ENABLE + default n + +config CANOPENNODE_TPDO_TIMERS_ENABLE + bool "Enable TPDO timers" + depends on CANOPENNODE_TPDO_ENABLE + default y if CANOPENNODE_TPDO_ENABLE + default n + +config CANOPENNODE_PDO_SYNC_ENABLE + bool "Enable PDO SYNC" + depends on CANOPENNODE_RPDO_ENABLE || CANOPENNODE_TPDO_ENABLE + select CANOPENNODE_SYNC_ENABLE + default y + help + Synchronize PDO processing with network SYNC messages. + +config CANOPENNODE_PDO_OD_IO_ACCESS + bool "Enable PDO OD IO access" + depends on CANOPENNODE_RPDO_ENABLE || CANOPENNODE_TPDO_ENABLE + select CANOPENNODE_PDO_OD_DYNAMIC + default y + help + Use OD IO accessors (OD_IO_t) for mapped PDO variables. + +config CANOPENNODE_PDO_CALLBACK + bool "Enable PDO callback" + depends on CANOPENNODE_RPDO_ENABLE || CANOPENNODE_TPDO_ENABLE + default y + +config CANOPENNODE_PDO_TIMERNEXT + bool "Enable PDO timerNext_us calculation" + depends on CANOPENNODE_RPDO_ENABLE || CANOPENNODE_TPDO_ENABLE + default y + +config CANOPENNODE_PDO_OD_DYNAMIC + bool "Enable dynamic behaviour of PDO OD variables" + depends on CANOPENNODE_RPDO_ENABLE || CANOPENNODE_TPDO_ENABLE + default y + +endmenu + +menu "Data storage - Data storage with CANopen OD objects 1010 and 1011, CiA 301" + +config CANOPENNODE_STORAGE_ENABLE + bool "Enable data storage" + default n + help + Persist OD entries via 0x1010/0x1011. + +endmenu + +menu "Storage Backend" + depends on CANOPENNODE_STORAGE_ENABLE + +choice CANOPENNODE_STORAGE_BACKEND_CHOICE + prompt "Select CANopenNode OD storage backend" + default CANOPENNODE_STORAGE_BACKEND_RAM + +config CANOPENNODE_STORAGE_BACKEND_SETTINGS + bool "Zephyr Settings subsystem" + select SETTINGS + select CANOPENNODE_CRC16_ENABLE + help + Use Zephyr's Settings subsystem to persist OD entries. + +config CANOPENNODE_STORAGE_BACKEND_RAM + bool "RAM-only (no persistence)" + help + Store OD values in RAM only. Lost on reset. + +config CANOPENNODE_STORAGE_BACKEND_NONE + bool "None (disable OD storage)" + help + Disable OD storage completely. + +endchoice + +endmenu + +menu "Program Download - Specified in standard CiA 302-3" + +config CANOPENNODE_PROG_DOWNLOAD + bool "Enable Program Download object (CiA 302-3)" + select CANOPENNODE_CRC16_ENABLE + select FLASH_MAP + depends on FLASH + default n + help + Enables CO_Prog_Download: OD entries 0x1F21, 0x1F23, 0x1F24, 0x1F50, + 0x1F51, 0x1F56, 0x1F57. Streams data via registered backend and can + optionally bind to CO_storage for persistence. + +config CANOPENNODE_PROG_DOWNLOAD_EDS_MAX_SIZE + int "Maximum EDS file size (bytes)" + depends on CANOPENNODE_PROG_DOWNLOAD + default 2048 + range 256 1048576 + help + Upper limit for OD 0x1F23 (“Store EDS NMT slave”) data length. + If the received EDS exceeds this size, the transfer should be + rejected/aborted. Keep this reasonably small to avoid large + RAM allocations during transfer on constrained targets. + +config CANOPENNODE_PROG_DOWNLOAD_PERMANENT + bool "Program commit is permanent upgrade" + default y + help + If enabled, COMMIT (0x1F51 command) will request a permanent + upgrade of the programmed image. + If disabled, COMMIT will request a test upgrade instead, + requiring a later confirmation to keep the image. + +endmenu + +menu "CANopen LED Indicators - Specified in standard CiA 303-3" + +config CANOPENNODE_LEDS_ENABLE + bool "Enable calculation of the CANopen LED indicators" + default n + +config CANOPENNODE_LEDS_CALLBACK + bool "Enable custom callback after LED state changes" + default y if CANOPENNODE_LEDS_ENABLE + default n if !CANOPENNODE_LEDS_ENABLE + +config CANOPENNODE_LEDS_TIMERNEXT + bool "Enable calculation of timerNext_us for LED indicators" + default y if CANOPENNODE_LEDS_ENABLE + default n if !CANOPENNODE_LEDS_ENABLE + +endmenu + +menu "Safety Related Data Objects (SRDO) - Specified in standard EN 50325-5 (CiA 304)" + +config CANOPENNODE_GFC_ENABLE + bool "Enable GFC (Guarding Function Control)" + default n + +config CANOPENNODE_GFC_CONSUMER + bool "Enable GFC consumer" + depends on CANOPENNODE_GFC_ENABLE + default n + +config CANOPENNODE_GFC_PRODUCER + bool "Enable GFC producer" + depends on CANOPENNODE_GFC_ENABLE + default n + +config CANOPENNODE_SRDO_ENABLE + bool "Enable SRDO (Safety Related Data Objects)" + default n + +config CANOPENNODE_SRDO_CHECK_TX + bool "Enable SRDO TX data check" + depends on CANOPENNODE_SRDO_ENABLE + default n + +config CANOPENNODE_SRDO_CALLBACK + bool "Enable SRDO callback" + depends on CANOPENNODE_SRDO_ENABLE + default n + +config CANOPENNODE_SRDO_TIMERNEXT + bool "Enable SRDO timerNext_us calculation" + depends on CANOPENNODE_SRDO_ENABLE + default n + +config CANOPENNODE_SRDO_MINIMUM_DELAY + int "Minimum time between the first and second SRDO (Tx) message (us)" + range 0 1000000 + default 0 + +endmenu + +menu "LSS master/slave - Specified in standard CiA 305" + +config CANOPENNODE_LSS_SLAVE + bool "Enable LSS slave" + default n + +config CANOPENNODE_LSS_SLAVE_FASTSCAN_DIRECT_RESPOND + bool "Enable fast-scan direct response" + depends on CANOPENNODE_LSS_SLAVE + default n + +config CANOPENNODE_LSS_MASTER + bool "Enable LSS master" + default n + +config CANOPENNODE_LSS_CALLBACK + bool "Enable LSS callback" + default n + +endmenu + +menu "Gateways - Specified in standard CiA 309" + +config CANOPENNODE_GTW_MULTI_NET + bool "Enable multiple network interfaces in gateway device" + depends on 0 + default n + +config CANOPENNODE_GTW_ASCII + bool "Enable gateway device with ASCII mapping" + select CANOPENNODE_FIFO_ENABLE + select CANOPENNODE_FIFO_ASCII_COMMANDS + default n + +config CANOPENNODE_GTW_ASCII_SDO + bool "Enable SDO client in gateway-ascii" + depends on CANOPENNODE_GTW_ASCII + select CANOPENNODE_SDO_CLIENT_ENABLE + select CANOPENNODE_FIFO_ASCII_DATATYPES + default n + +config CANOPENNODE_GTW_ASCII_NMT + bool "Enable NMT master in gateway-ascii" + depends on CANOPENNODE_GTW_ASCII + select CANOPENNODE_NMT_MASTER + default n + +config CANOPENNODE_GTW_ASCII_LSS + bool "Enable LSS master in gateway-ascii" + depends on CANOPENNODE_GTW_ASCII + select CANOPENNODE_LSS_MASTER + default n + +config CANOPENNODE_GTW_ASCII_LOG + bool "Enable non-standard message log read" + depends on CANOPENNODE_GTW_ASCII + default n + +config CANOPENNODE_GTW_ASCII_ERROR_DESC + bool "Print error descriptions as comments in gateway-ascii" + depends on CANOPENNODE_GTW_ASCII + default n + +config CANOPENNODE_GTW_ASCII_PRINT_HELP + bool "Enable non-standard \"help\" command" + depends on CANOPENNODE_GTW_ASCII + default n + +config CANOPENNODE_GTW_ASCII_PRINT_LEDS + bool "Display CANopen status LEDs on terminal" + depends on CANOPENNODE_GTW_ASCII + select CANOPENNODE_LEDS_ENABLE + default n + +config CANOPENNODE_GTW_BLOCK_DL_LOOP + int "Number of process loops in case of block download" + range 1 127 + default 1 + help + Additional local loop iterations while a block download is in progress + to speed up transfer (up to 127). + +config CANOPENNODE_GTWA_COMM_BUF_SIZE + int "Size of command buffer in ASCII gateway object" + range 16 4096 + default 200 + help + Increase to >=1000 when doing large block transfers. + +config CANOPENNODE_GTWA_LOG_BUF_SIZE + int "Gateway: log buffer size" + range 128 4096 + default 2000 + +endmenu + +menu "CRC 16 calculation" + +config CANOPENNODE_CRC16_ENABLE + bool "Enable CRC16 calculation" + default n + +config CANOPENNODE_CRC16_EXTERNAL + bool "CRC functions are defined externally" + depends on CANOPENNODE_CRC16_ENABLE + select CRC + default y if CANOPENNODE_CRC16_ENABLE + help + If enabled, the application must provide: + void crc16_ccitt_single(uint16_t *crc, uint8_t chr); + uint16_t crc16_ccitt(const uint8_t block[], size_t len, uint16_t crc); + If disabled, the internal CRC16 functions will be used. + +endmenu + +menu "FIFO buffer" + +config CANOPENNODE_FIFO_ENABLE + bool "Enable FIFO buffer" + default n + +config CANOPENNODE_FIFO_ALT_READ + bool "FIFO alternate read (needed by SDO client block)" + depends on CANOPENNODE_FIFO_ENABLE + default n + +config CANOPENNODE_FIFO_CRC16_CCITT + bool "FIFO CRC16-CCITT (needed by SDO client block)" + depends on CANOPENNODE_FIFO_ENABLE + select CANOPENNODE_CRC16_ENABLE + default n + +config CANOPENNODE_FIFO_ASCII_COMMANDS + bool "FIFO ASCII command helpers" + depends on CANOPENNODE_FIFO_ENABLE + default n + +config CANOPENNODE_FIFO_ASCII_DATATYPES + bool "FIFO ASCII datatype helpers" + depends on CANOPENNODE_FIFO_ENABLE && CANOPENNODE_FIFO_ASCII_COMMANDS + default n + help + Helpers to read/write integers/floats/strings in ASCII. + +endmenu + +menu "Trace recorder" + +config CANOPENNODE_TRACE_ENABLE + bool "Enable Trace recorder" + default n + +config CANOPENNODE_TRACE_OWN_INTTYPES + bool "Provide custom PRIu32/PRId32 macros (no )" + depends on CANOPENNODE_TRACE_ENABLE + default n + +endmenu + +menu "Debug messages" + +config CANOPENNODE_DEBUG_COMMON + bool "Define default CO_DEBUG_COMMON(msg) macro" + default n + +config CANOPENNODE_DEBUG_SDO_CLIENT + bool "Define default CO_DEBUG_SDO_CLIENT(msg) macro" + select CANOPENNODE_DEBUG_COMMON + default n + +config CANOPENNODE_DEBUG_SDO_SERVER + bool "Define default CO_DEBUG_SDO_SERVER(msg) macro" + select CANOPENNODE_DEBUG_COMMON + default n + +endmenu + +menu "Object Dictionary (EDS)" + +config CANOPENNODE_EDS_FILE_PATH + string "EDS file (path relative to application root)" + default "" + help + Path to the EDS used to generate OD.c/OD.h. + Leave empty to use: + example/DS301_profile.eds + +endmenu + +menu "CANopenNode TX Workqueue Options" + +config CANOPENNODE_TX_WORKQUEUE_STACK_SIZE + int "TX workqueue stack size" + default 1024 + range 256 16384 + help + Stack size (in bytes) for the CANopenNode TX workqueue thread. + +config CANOPENNODE_TX_WORKQUEUE_PRIORITY + int "TX workqueue priority" + default 0 + help + Thread priority for the CANopenNode TX workqueue. + Lower numbers mean higher priority. + +endmenu + +menu "CANopenNode Thread Options" + +config CANOPENNODE_RT_THREAD + bool "Use real-time thread for CANopen processing" + default y + help + If enabled, a dedicated real-time thread will be created for CANopen + processing. Otherwise, the application must call CO_process() periodically. + +config CANOPENNODE_RT_THREAD_STACK_SIZE + int "Real-time thread stack size" + default 1024 + help + Stack size (bytes) for the CANopenNode real-time thread. + Only used if CANOPENNODE_RT_THREAD is enabled. + Increase if you use many PDOs, SDOs, or large OD entries. + Decrease if you have memory constraints and use few CANopen features. + Typical values: 1024-4096 bytes. + Measure actual stack usage to optimize. + +config CANOPENNODE_RT_THREAD_PRIORITY + int "Real-time thread priority" + default -1 + help + Thread priority for the CANopenNode real-time thread. + Only used if CANOPENNODE_RT_THREAD is enabled. + Lower numbers mean higher priority. + Typical values: -1 to -3 for real-time processing. + +config CANOPENNODE_RT_THREAD_IDLE_MS + int "Real-time thread idle time (ms)" + default 10 + help + Time (ms) the CANopenNode real-time thread sleeps when idle. + Only used if CANOPENNODE_RT_THREAD is enabled. + Lower values mean more CPU usage but lower latency. + Typical values: 1-100 ms. + +config CANOPENNODE_RT_THREAD_TIMERNEXT + bool "Enable timerNext_us calculation in real-time thread" + default y + help + If enabled, timerNext_us will be calculated in the real-time thread. + Only used if CANOPENNODE_RT_THREAD is enabled. + Disable to save CPU if you don't use features that need timerNext_us. + +config CANOPENNODE_RT_THREAD_AUTO_START + bool "Auto-start CANopenNode real-time thread" + default y + help + If enabled, the CANopenNode real-time thread will be started automatically + at POST_KERNEL. Otherwise, you must call CO_RT_threadStart() manually. +endmenu + +menu "CANopenNode Initialization" + +config CANOPENNODE_INIT_PRIORITY + int "CANopenNode initialization priority" + default 90 + help + Priority for CANopenNode initialization. + Lower numbers mean earlier initialization. + Typical values: 80-100. + Must be lower than the application's main thread priority. + +config CANOPENNODE_INIT_NODE_ID + int "CANopenNode node ID" + default 1 + range 0 127 + help + Node ID for the CANopenNode instance. + Must be unique on the CAN bus. + +endmenu + +module = CANOPEN +module-str = canopen +source "subsys/logging/Kconfig.template.log_config" + +endif # CANOPENNODE diff --git a/zephyr/include/CO_driver_target.h b/zephyr/include/CO_driver_target.h new file mode 100644 index 00000000..bc2d50cb --- /dev/null +++ b/zephyr/include/CO_driver_target.h @@ -0,0 +1,374 @@ +/* SPDX-License-Identifier: Apache-2.0 */ +/* + * Zephyr target configuration for CANopenNode driver. + * + * Device- and application-specific definitions used by CANopenNode’s + * driver/porting layer on Zephyr. Provides target constants and macros + * (locking, timing, endian helpers, limits, etc.) tailored to the SoC/board + * and build configuration. + * + * @file CO_driver_target.h + * @author Janez Paternoster (original template) + * @author BitConcepts, LLC + * @copyright 2021 Janez Paternoster + * @copyright 2025 BitConcepts, LLC + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +#ifndef ZEPHYR_MODULES_CANOPENNODE_CO_DRIVER_TARGET_H +#define ZEPHYR_MODULES_CANOPENNODE_CO_DRIVER_TARGET_H + +#include +#include +#include +#include +#include +#include + +#include "CO_zephyr_config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup co_driver_target Zephyr driver target (porting layer) + * @brief Zephyr-specific targets, types, and primitives for CANopenNode. + * + * This header supplies the minimal target abstraction used by the CANopenNode + * driver core when running on Zephyr. It defines endianness helpers, memory + * allocation hooks, fundamental types, CAN message/container structures, and a + * few synchronization/locking primitives the stack expects. + * + * Include this header in the target-specific CAN driver and any integration + * units that call directly into CANopenNode’s low-level driver API. + * + * @note Some items here reflect CANopenNode’s historic target template and are + * retained for source compatibility even if they are no-ops on Zephyr. + * @{ + */ + +/* -------------------------------------------------------------------------- */ +/* Configuration */ +/* -------------------------------------------------------------------------- */ + +/** + * @name Endianness selection and byte-swap helpers + * @brief Configure byte order and provide swap macros required by CANopenNode. + * + * If the build is little-endian (@c CONFIG_LITTLE_ENDIAN), no swapping is + * performed. Otherwise, Zephyr byte-order helpers are used to convert to big + * endian. + * @{ + */ +#ifdef CONFIG_LITTLE_ENDIAN +/** @brief Defined when the target CPU is little-endian. */ +#define CO_LITTLE_ENDIAN +/** @brief 16-bit no-op swap on little-endian targets. */ +#define CO_SWAP_16(x) x +/** @brief 32-bit no-op swap on little-endian targets. */ +#define CO_SWAP_32(x) x +/** @brief 64-bit no-op swap on little-endian targets. */ +#define CO_SWAP_64(x) x +#else +/** @brief Defined when the target CPU is big-endian. */ +#define CO_BIG_ENDIAN +/** @brief 16-bit host→big-endian conversion using Zephyr helper. */ +#define CO_SWAP_16(x) sys_cpu_to_be16(x) +/** @brief 32-bit host→big-endian conversion using Zephyr helper. */ +#define CO_SWAP_32(x) sys_cpu_to_be32(x) +/** @brief 64-bit host→big-endian conversion using Zephyr helper. */ +#define CO_SWAP_64(x) sys_cpu_to_be64(x) +#endif +/** @} */ + +/** + * @name Memory allocation hooks + * @brief Thin wrappers around Zephyr heap allocation used by CANopenNode. + * @{ + */ +/** @brief Allocate @p num objects of size @p size bytes using Zephyr heap. */ +#define CO_alloc(num, size) k_calloc((num), (size)) +/** @brief Free memory previously returned by CO_alloc(). */ +#define CO_free(ptr) k_free((ptr)) +/** @} */ + +/** + * @name Debug log hook + * @brief Thin wrapper around Zephyr debug logging used by CANopenNode. + * @{ + */ +#ifdef CO_CONFIG_DEBUG +extern void z_canopen_log(const char *msg); +#define CO_DEBUG_COMMON(msg) canopen_log(msg) +#endif +/** @} */ + +/* -------------------------------------------------------------------------- */ +/* Fundamental types */ +/* -------------------------------------------------------------------------- */ + +/** + * @name Base typedefs expected by CANopenNode + * @brief Provide canonical types with the names used by the stack. + * + * @note Zephyr already defines @c float32_t and @c float64_t in + * @c . These aliases are kept for compatibility. + * @{ + */ +/** @brief Boolean type used by CANopenNode (fast unsigned 8-bit). */ +typedef uint_fast8_t bool_t; +/** @brief 32-bit floating point (alias). */ +typedef float float32_t; +/** @brief 64-bit floating point (alias). */ +typedef double float64_t; +/** @brief Character type used by the stack. */ +typedef char char_t; +/** @brief Octet-string character type (unsigned 8-bit). */ +typedef unsigned char oChar_t; +/** @brief DOMAIN data type alias (raw byte). */ +typedef unsigned char domain_t; +/** @} */ + +/* -------------------------------------------------------------------------- */ +/* CAN message accessors */ +/* -------------------------------------------------------------------------- */ + +/** + * @name Receive message accessors + */ +/** @brief Return CAN identifier from RX message. */ +#define CO_CANrxMsg_readIdent(msg) (((CO_CANrxMsg_t *)msg)->ident) +/** @brief Return ID from packed RX message CAN identifier. */ +#define CO_CANrxMsg_readID(msg) (CO_CANrxMsg_readIdent(msg) & CAN_STD_ID_MASK) +/** @brief Return DLC from packed RX message CAN identifier. */ +#define CO_CANrxMsg_readDLC(msg) (((CO_CANrxMsg_t *)msg)->DLC) +/** @brief Return data pointer from RX message. */ +#define CO_CANrxMsg_readData(msg) (((CO_CANrxMsg_t *)msg)->data) +/** @} */ + +/* -------------------------------------------------------------------------- */ +/* Driver objects */ +/* -------------------------------------------------------------------------- */ + +/** + * @brief Receive message object (driver-side). + * + * Describes a CAN receive object thunk used by the stack. + */ +typedef struct { + uint32_t ident; + uint8_t DLC; + uint8_t data[8]; +} CO_CANrxMsg_t; + +/** + * @brief Receive message object (driver-side). + * + * Describes a CAN receive filter and its callback thunk used by the stack. + */ +typedef struct { + /** Filter identifier (driver-specific). */ + int filter_id; + /** 11/29-bit identifier to match (masked by @ref mask). */ + uint16_t ident; + /** Identifier mask: set bits are compared. */ + uint16_t mask; + /** User object passed to the callback. */ + void *object; + /** + * @brief RX callback invoked on a matching frame. + * @param object User object as registered in @ref object. + * @param message Driver-specific frame pointer/handle. + */ + void (*CANrx_callback)(void *object, void *message); +} CO_CANrx_t; + +/** + * @brief Transmit message object (driver-side). + * + * Represents a queued CAN frame and its state flags as used by the stack. + */ +typedef struct { + /** Standard (11-bit) or extended (29-bit) CAN identifier. */ + uint32_t ident; + /** Data length code (0–8). */ + uint8_t DLC; + /** Payload buffer (0–8 bytes valid per @ref DLC). */ + uint8_t data[8]; + /** Flags. @see @ref CAN_FRAME_FLAGS. */ + uint8_t flags; + /** Set when the buffer holds a pending frame. */ + volatile bool_t bufferFull; + /** Set for frames transmitted during SYNC window. */ + volatile bool_t syncFlag; +} CO_CANtx_t; + +/** + * @brief CAN module container used by CANopenNode. + * + * Holds RX/TX arrays, sizes, error state, and runtime flags. + */ +typedef struct { + /** Opaque driver CAN pointer (e.g., @c const struct device*). */ + void *CANptr; + /** Array of RX filter objects. */ + CO_CANrx_t *rxArray; + /** Number of RX elements in @ref rxArray. */ + uint16_t rxSize; + /** Array of TX buffer objects. */ + CO_CANtx_t *txArray; + /** Number of TX elements in @ref txArray. */ + uint16_t txSize; + /** Last CAN error/status flags (implementation-defined). */ + uint16_t CANerrorStatus; + /** Module is in normal operating mode when true. */ + volatile bool_t CANnormal; + /** Whether hardware RX filters are used. */ + volatile bool_t useCANrxFilters; + /** Set until the first TX frame is sent after init. */ + volatile bool_t firstCANtxMessage; + /** Cached copy of last error flags for change detection. */ + uint32_t errOld; +} CO_CANmodule_t; + +/** + * @brief Storage entry descriptor (target-specific extension). + * + * Describes a single non-volatile storage mapping for an OD entry or + * application variable. + * + * @note Fields beyond the common subset are target-specific and may be used + * by the Zephyr storage glue (e.g., EEPROM/flash helpers). + */ +typedef struct { + void *addr; /**< Address of data to store, always required. */ + size_t len; /**< Length of data to store, always required. */ + uint8_t subIndexOD; /**< Sub index in OD objects 1010 and 1011, from 2 to 127. Writing + 0x65766173 to 1010,subIndexOD will store data to non-volatile memory + Writing 0x64616F6C to 1011,subIndexOD will restore default data, + always required. */ + uint8_t attr; /**< Attribute from @ref CO_storage_attributes_t, always required. */ + void *storageModule; /**< Pointer to storage module, target system specific usage, required + with @ref CO_storage_eeprom. */ + uint16_t crc; /**< CRC checksum of the data stored in eeprom, set on store, required with + @ref CO_storage_eeprom. */ + size_t eepromAddrSignature; /**< Address of entry signature inside eeprom, set by init, + required with @ref CO_storage_eeprom. */ + size_t eepromAddr; /**< Address of data inside eeprom, set by init, required with @ref + CO_storage_eeprom. */ + size_t offset; /**< Offset of next byte being updated by automatic storage, required with + @ref CO_storage_eeprom. */ + void *additionalParameters; /**< Additional target specific parameters, optional. */ +} CO_storage_entry_t; + +/* -------------------------------------------------------------------------- */ +/* Locking primitives */ +/* -------------------------------------------------------------------------- */ + +/** + * @name Critical-section helpers + * @brief Locks used by CANopenNode around specific critical regions. + * + * These symbols must be provided by the integration layer. They may map to + * mutexes, IRQ locks, or other Zephyr primitives as appropriate. + * @{ + */ + +/** + * @brief Lock critical section around CO_CANsend(). + * + * Blocks concurrent access to the TX path while the stack composes and enqueues + * a frame. + */ +void z_co_send_lock(void); +/** @brief Unlock critical section started by @ref z_co_send_lock. */ +void z_co_send_unlock(void); + +/** @brief Enter critical section for EMCY reporting/reset. */ +void z_co_emcy_lock(void); +/** @brief Exit critical section for EMCY reporting/reset. */ +void z_co_emcy_unlock(void); + +/** @brief Enter critical section for Object Dictionary access. */ +void z_co_od_lock(void); +/** @brief Exit critical section for Object Dictionary access. */ +void z_co_od_unlock(void); + +/** @brief Macro used by CANopenNode to guard TX critical section. */ +#define CO_LOCK_CAN_SEND(CAN_MODULE) z_co_send_lock() +/** @brief Macro used by CANopenNode to release TX critical section. */ +#define CO_UNLOCK_CAN_SEND(CAN_MODULE) z_co_send_unlock() +/** @brief Macro used by CANopenNode to guard EMCY critical section. */ +#define CO_LOCK_EMCY(CAN_MODULE) z_co_emcy_lock() +/** @brief Macro used by CANopenNode to release EMCY critical section. */ +#define CO_UNLOCK_EMCY(CAN_MODULE) z_co_emcy_unlock() +/** @brief Macro used by CANopenNode to guard OD critical section. */ +#define CO_LOCK_OD(CAN_MODULE) z_co_od_lock() +/** @brief Macro used by CANopenNode to release OD critical section. */ +#define CO_UNLOCK_OD(CAN_MODULE) z_co_od_unlock() +/** @} */ + +/* -------------------------------------------------------------------------- */ +/* Inter-thread synchronization */ +/* -------------------------------------------------------------------------- */ + +/** + * @name RX/TX synchronization flags + * @brief Simple flag protocol between ISR/worker contexts. + * + * These macros implement a trivial "new data" flag with a memory barrier + * placeholder. On Zephyr, @ref CO_MemoryBarrier resolves to an empty macro by + * default; define it to an architecture-appropriate barrier if needed. + * @{ + */ +/** @brief Memory barrier used by flag setters/getters (no-op by default). */ +#define CO_MemoryBarrier() + +/** + * @brief Test the "new data" flag. + * @param rxNew An lvalue flag variable (pointer-sized). + * @retval true New data is available. + * @retval false No new data. + */ +#define CO_FLAG_READ(rxNew) ((rxNew) != NULL) + +/** + * @brief Set the "new data" flag (with barrier). + * @param rxNew An lvalue flag variable (pointer-sized). + */ +#define CO_FLAG_SET(rxNew) \ + { \ + CO_MemoryBarrier(); \ + rxNew = (void *)1L; \ + } + +/** + * @brief Clear the "new data" flag (with barrier). + * @param rxNew An lvalue flag variable (pointer-sized). + */ +#define CO_FLAG_CLEAR(rxNew) \ + { \ + CO_MemoryBarrier(); \ + rxNew = NULL; \ + } +/** @} */ + +/** @} */ /* end of co_driver_target */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* ZEPHYR_MODULES_CANOPENNODE_CO_DRIVER_TARGET_H */ diff --git a/zephyr/include/CO_zephyr_config.h b/zephyr/include/CO_zephyr_config.h new file mode 100644 index 00000000..5219da89 --- /dev/null +++ b/zephyr/include/CO_zephyr_config.h @@ -0,0 +1,405 @@ +/* SPDX-License-Identifier: Apache-2.0 */ +/* + * Zephyr-to-CANopenNode configuration bridge. + * + * Aggregates Zephyr Kconfig (CONFIG_*) options into CANopenNode's + * CO_CONFIG_* compile-time flags and related values, so the stack can + * be configured from prj.conf and devicetree. + * + * @file CO_zephyr_config.h + * @author BitConcepts, LLC + * @copyright 2025 BitConcepts, LLC + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +#ifndef ZEPHYR_MODULES_CANOPENNODE_CO_ZEPHYR_CONFIG_H +#define ZEPHYR_MODULES_CANOPENNODE_CO_ZEPHYR_CONFIG_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup co_zephyr_config Zephyr ↔ CANopenNode configuration bridge + * @brief Map Zephyr Kconfig to CANopenNode @c CO_CONFIG_* flags and constants. + * + * This header is a thin preprocessor bridge. It reads Zephyr @c CONFIG_* symbols + * and produces the @c CO_CONFIG_* bitmasks and values that CANopenNode expects at + * compile time. Include this header before including CANopenNode modules (or in a + * central integration translation unit) so that the configuration takes effect. + * + * ### Helpers + * - ::ZBIT(flag, cfgsym) — Expands to @p flag when @p cfgsym is enabled, otherwise 0. + * - ::ZVAL(cfgsym) — Expands to the value of @p cfgsym (numeric or 0 if unset). + * + * ### Notes + * - Requires Zephyr’s @c . + * - All @c CONFIG_CANOPENNODE_* options come from your project’s @c prj.conf/Kconfig. + * + * @{ + */ + +/** @name Helper macros + * @brief Small utilities for conditional bit composition. + * @{ + */ +/** @brief Conditionally OR a flag when a Kconfig symbol is enabled. */ +#define ZBIT(flag, cfgsym) (IS_ENABLED(cfgsym) ? (flag) : 0) +/** @brief Yield the value of a Kconfig symbol (or 0 if not defined). */ +#define ZVAL(cfgsym) (cfgsym) +/** @} */ + +/** @def CO_USE_GLOBALS + * @brief Selects storage model for CANopenNode objects. + * + * If not provided by the build, this header auto-derives the value from + * Zephyr’s heap setting: + * - `1` — Use global/static objects; no dynamic allocation. + * - `0` — Use dynamic allocation; requires a non-zero + * `CONFIG_HEAP_MEM_POOL_SIZE`. + * + * You may override by defining `CO_USE_GLOBALS` before including this header + * or via the build system (e.g., `-DCO_USE_GLOBALS=0`). + * + * @see CONFIG_HEAP_MEM_POOL_SIZE + */ +#ifndef CO_USE_GLOBALS +/* Treat “heap present” (non-zero) as “use dynamic allocation”. */ +#if defined(CONFIG_HEAP_MEM_POOL_SIZE) && (CONFIG_HEAP_MEM_POOL_SIZE > 0) +#define CO_USE_GLOBALS 0 +#else +#define CO_USE_GLOBALS 1 +#endif +#endif + +/** @brief Build-time sanity check for dynamic mode. + * + * Triggers a compile-time error if `CO_USE_GLOBALS==0` (dynamic allocation) + * but Zephyr’s heap is not enabled or has size 0. + */ +#if (CO_USE_GLOBALS == 0) && \ + !(defined(CONFIG_HEAP_MEM_POOL_SIZE) && (CONFIG_HEAP_MEM_POOL_SIZE > 0)) +#error "CO_USE_GLOBALS=0 requires a nonzero CONFIG_HEAP_MEM_POOL_SIZE" +#endif + +/** @name NMT / Heartbeat Producer + * @brief Configure CANopen NMT producer behavior and control bits. + * @{ + */ +/** @brief Bitmask for CANopenNode NMT configuration derived from Kconfig. */ +#define CO_CONFIG_NMT \ + (ZBIT(CO_CONFIG_NMT_CALLBACK_CHANGE, CONFIG_CANOPENNODE_NMT_CALLBACK_CHANGE) | \ + ZBIT(CO_CONFIG_NMT_MASTER, CONFIG_CANOPENNODE_NMT_MASTER) | \ + ZBIT(CO_CONFIG_FLAG_CALLBACK_PRE, CONFIG_CANOPENNODE_NMT_CALLBACK) | \ + ZBIT(CO_CONFIG_FLAG_TIMERNEXT, CONFIG_CANOPENNODE_NMT_TIMERNEXT)) + +/** @brief Optional initial heartbeat delay in milliseconds. */ +#define CO_NMT_FIRST_HB_TIME_MS CONFIG_CANOPENNODE_NMT_FIRST_HB_TIME_MS + +/** + * @brief Composite NMT startup/control mask you can pass to CANopenNode init. + * + * Combines the policy booleans above into the bitmask expected by CANopenNode. + */ +#define CO_CONFIG_NMT_CONTROL \ + (ZBIT(CO_NMT_STARTUP_TO_OPERATIONAL, CONFIG_CANOPENNODE_NMT_STARTUP_TO_OPERATIONAL) | \ + ZBIT(CO_NMT_ERR_ON_BUSOFF_HB, CONFIG_CANOPENNODE_NMT_ERR_ON_BUSOFF_HB) | \ + ZBIT(CO_NMT_ERR_ON_ERR_REG, CONFIG_CANOPENNODE_NMT_ERR_ON_ERR_REG) | \ + ZBIT(CO_NMT_ERR_TO_STOPPED, CONFIG_CANOPENNODE_NMT_ERR_TO_STOPPED) | \ + ZBIT(CO_NMT_ERR_FREE_TO_OPERATIONAL, CONFIG_CANOPENNODE_NMT_ERR_FREE_TO_OPERATIONAL)) +/** @} */ + +/** @name Heartbeat Consumer + * @brief Configure HB consumer behavior, callbacks, and OD options. + * @{ + */ +#define CO_CONFIG_HB_CONS \ + (ZBIT(CO_CONFIG_HB_CONS_ENABLE, CONFIG_CANOPENNODE_HB_CONS_ENABLE) | \ + ZBIT(CO_CONFIG_HB_CONS_CALLBACK_CHANGE, CONFIG_CANOPENNODE_HB_CONS_CALLBACK_CHANGE) | \ + ZBIT(CO_CONFIG_HB_CONS_CALLBACK_MULTI, CONFIG_CANOPENNODE_HB_CONS_CALLBACK_MULTI) | \ + ZBIT(CO_CONFIG_HB_CONS_QUERY_FUNCT, CONFIG_CANOPENNODE_HB_CONS_QUERY_FUNCT) | \ + ZBIT(CO_CONFIG_FLAG_CALLBACK_PRE, CONFIG_CANOPENNODE_HB_CONS_CALLBACK) | \ + ZBIT(CO_CONFIG_FLAG_TIMERNEXT, CONFIG_CANOPENNODE_HB_CONS_TIMERNEXT) | \ + ZBIT(CO_CONFIG_FLAG_OD_DYNAMIC, CONFIG_CANOPENNODE_HB_CONS_OD_DYNAMIC)) +/** @} */ + +/** @name Node Guarding + * @brief Enable/Configure node guarding master/slave roles and timing. + * @{ + */ +#define CO_CONFIG_NODE_GUARDING \ + (ZBIT(CO_CONFIG_NODE_GUARDING_SLAVE_ENABLE, \ + CONFIG_CANOPENNODE_NODE_GUARDING_SLAVE_ENABLE) | \ + ZBIT(CO_CONFIG_NODE_GUARDING_MASTER_ENABLE, \ + CONFIG_CANOPENNODE_NODE_GUARDING_MASTER_ENABLE) | \ + ZBIT(CO_CONFIG_FLAG_TIMERNEXT, CONFIG_CANOPENNODE_NODE_GUARDING_TIMERNEXT)) + +/** @brief Number of guarded nodes when acting as a master (if provided). */ +#ifdef CONFIG_CANOPENNODE_NODE_GUARDING_MASTER_COUNT +#define CO_CONFIG_NODE_GUARDING_MASTER_COUNT CONFIG_CANOPENNODE_NODE_GUARDING_MASTER_COUNT +#endif +/** @} */ + +/** @name Emergency (EM) + * @brief Configure emergency producer/consumer and status bits. + * @{ + */ +#define CO_CONFIG_EM \ + (ZBIT(CO_CONFIG_EM_PRODUCER, CONFIG_CANOPENNODE_EM_PRODUCER) | \ + ZBIT(CO_CONFIG_EM_PROD_CONFIGURABLE, CONFIG_CANOPENNODE_EM_PROD_CONFIGURABLE) | \ + ZBIT(CO_CONFIG_EM_PROD_INHIBIT, CONFIG_CANOPENNODE_EM_PROD_INHIBIT) | \ + ZBIT(CO_CONFIG_EM_HISTORY, CONFIG_CANOPENNODE_EM_HISTORY) | \ + ZBIT(CO_CONFIG_EM_CONSUMER, CONFIG_CANOPENNODE_EM_CONSUMER) | \ + ZBIT(CO_CONFIG_EM_STATUS_BITS, CONFIG_CANOPENNODE_EM_STATUS_BITS) | \ + ZBIT(CO_CONFIG_FLAG_CALLBACK_PRE, CONFIG_CANOPENNODE_EM_CALLBACK) | \ + ZBIT(CO_CONFIG_FLAG_TIMERNEXT, CONFIG_CANOPENNODE_EM_TIMERNEXT)) + +/** @brief Size of error status bits array. */ +#define CO_CONFIG_EM_ERR_STATUS_BITS_COUNT CONFIG_CANOPENNODE_EM_ERR_STATUS_BITS_COUNT + +/** @brief Optional default error-condition enables (application maps them as needed). */ +#define CO_CONFIG_ERR_CONDITION_CURRENT IS_ENABLED(CONFIG_CANOPENNODE_ERR_CONDITION_CURRENT) +#define CO_CONFIG_ERR_CONDITION_VOLTAGE IS_ENABLED(CONFIG_CANOPENNODE_ERR_CONDITION_VOLTAGE) +#define CO_CONFIG_ERR_CONDITION_TEMPERATURE IS_ENABLED(CONFIG_CANOPENNODE_ERR_CONDITION_TEMPERATURE) +#define CO_CONFIG_ERR_CONDITION_DEV_PROFILE IS_ENABLED(CONFIG_CANOPENNODE_ERR_CONDITION_DEV_PROFILE) +/** @} */ + +/** @name SDO Server + * @brief Configure SDO server features, callbacks, and timeouts. + * @{ + */ +#define CO_CONFIG_SDO_SRV \ + (ZBIT(CO_CONFIG_SDO_SRV_SEGMENTED, CONFIG_CANOPENNODE_SDO_SERVER_SEGMENTED) | \ + ZBIT(CO_CONFIG_SDO_SRV_BLOCK, CONFIG_CANOPENNODE_SDO_SERVER_BLOCK) | \ + ZBIT(CO_CONFIG_FLAG_CALLBACK_PRE, CONFIG_CANOPENNODE_SDO_SERVER_CALLBACK) | \ + ZBIT(CO_CONFIG_FLAG_TIMERNEXT, CONFIG_CANOPENNODE_SDO_SERVER_TIMERNEXT) | \ + ZBIT(CO_CONFIG_FLAG_OD_DYNAMIC, CONFIG_CANOPENNODE_SDO_SERVER_OD_DYNAMIC)) + +/** @brief Server buffer size in bytes. */ +#define CO_CONFIG_SDO_SRV_BUFFER_SIZE CONFIG_CANOPENNODE_SDO_SERVER_BUFFER_SIZE +/** @brief SDO server timeout in ms. */ +#define CO_CONFIG_SDO_SRV_TIMEOUT_MS CONFIG_CANOPENNODE_SDO_SERVER_TIMEOUT_MS +/** @} */ + +/** @name SDO Client + * @brief Configure SDO client features, callbacks, and timeouts. + * @{ + */ +#define CO_CONFIG_SDO_CLI \ + (ZBIT(CO_CONFIG_SDO_CLI_ENABLE, CONFIG_CANOPENNODE_SDO_CLIENT_ENABLE) | \ + ZBIT(CO_CONFIG_SDO_CLI_SEGMENTED, CONFIG_CANOPENNODE_SDO_CLIENT_SEGMENTED) | \ + ZBIT(CO_CONFIG_SDO_CLI_BLOCK, CONFIG_CANOPENNODE_SDO_CLIENT_BLOCK) | \ + ZBIT(CO_CONFIG_SDO_CLI_LOCAL, CONFIG_CANOPENNODE_SDO_CLIENT_LOCAL) | \ + ZBIT(CO_CONFIG_FLAG_CALLBACK_PRE, CONFIG_CANOPENNODE_SDO_CLIENT_CALLBACK) | \ + ZBIT(CO_CONFIG_FLAG_TIMERNEXT, CONFIG_CANOPENNODE_SDO_CLIENT_TIMERNEXT) | \ + ZBIT(CO_CONFIG_FLAG_OD_DYNAMIC, CONFIG_CANOPENNODE_SDO_CLIENT_OD_DYNAMIC)) + +/** @brief Client buffer size in bytes. */ +#define CO_CONFIG_SDO_CLI_BUFFER_SIZE CONFIG_CANOPENNODE_SDO_CLIENT_BUFFER_SIZE +/** @brief SDO client timeout in ms. */ +#define CO_CONFIG_SDO_CLI_TIMEOUT_MS CONFIG_CANOPENNODE_SDO_CLIENT_TIMEOUT_MS +/** @} */ + +/** @name TIME object + * @brief Configure TIME producer/consumer and callbacks. + * @{ + */ +#define CO_CONFIG_TIME \ + (ZBIT(CO_CONFIG_TIME_ENABLE, CONFIG_CANOPENNODE_TIME_ENABLE) | \ + ZBIT(CO_CONFIG_TIME_PRODUCER, CONFIG_CANOPENNODE_TIME_PRODUCER) | \ + ZBIT(CO_CONFIG_FLAG_CALLBACK_PRE, CONFIG_CANOPENNODE_TIME_CALLBACK) | \ + ZBIT(CO_CONFIG_FLAG_OD_DYNAMIC, CONFIG_CANOPENNODE_TIME_OD_DYNAMIC)) +/** @} */ + +/** @name SYNC / PDO + * @brief Configure SYNC and PDO features, timers, and callbacks. + * @{ + */ +#define CO_CONFIG_SYNC \ + (ZBIT(CO_CONFIG_SYNC_ENABLE, CONFIG_CANOPENNODE_SYNC_ENABLE) | \ + ZBIT(CO_CONFIG_SYNC_PRODUCER, CONFIG_CANOPENNODE_SYNC_PRODUCER) | \ + ZBIT(CO_CONFIG_FLAG_CALLBACK_PRE, CONFIG_CANOPENNODE_SYNC_CALLBACK) | \ + ZBIT(CO_CONFIG_FLAG_TIMERNEXT, CONFIG_CANOPENNODE_SYNC_TIMERNEXT) | \ + ZBIT(CO_CONFIG_FLAG_OD_DYNAMIC, CONFIG_CANOPENNODE_SYNC_OD_DYNAMIC)) + +#define CO_CONFIG_PDO \ + (ZBIT(CO_CONFIG_RPDO_ENABLE, CONFIG_CANOPENNODE_RPDO_ENABLE) | \ + ZBIT(CO_CONFIG_TPDO_ENABLE, CONFIG_CANOPENNODE_TPDO_ENABLE) | \ + ZBIT(CO_CONFIG_RPDO_TIMERS_ENABLE, CONFIG_CANOPENNODE_RPDO_TIMERS_ENABLE) | \ + ZBIT(CO_CONFIG_TPDO_TIMERS_ENABLE, CONFIG_CANOPENNODE_TPDO_TIMERS_ENABLE) | \ + ZBIT(CO_CONFIG_PDO_SYNC_ENABLE, CONFIG_CANOPENNODE_PDO_SYNC_ENABLE) | \ + ZBIT(CO_CONFIG_PDO_OD_IO_ACCESS, CONFIG_CANOPENNODE_PDO_OD_IO_ACCESS) | \ + ZBIT(CO_CONFIG_FLAG_CALLBACK_PRE, CONFIG_CANOPENNODE_PDO_CALLBACK) | \ + ZBIT(CO_CONFIG_FLAG_TIMERNEXT, CONFIG_CANOPENNODE_PDO_TIMERNEXT) | \ + ZBIT(CO_CONFIG_FLAG_OD_DYNAMIC, CONFIG_CANOPENNODE_PDO_OD_DYNAMIC)) +/** @} */ + +/** @name Storage + * @brief Enable storage glue and select backends. + * @{ + */ +/** @brief Master switch for CANopenNode storage module. */ +#define CO_CONFIG_STORAGE (ZBIT(CO_CONFIG_STORAGE_ENABLE, CONFIG_CANOPENNODE_STORAGE_ENABLE)) + +/** @brief Backend selections for the integration layer. */ +#define CO_STORAGE_BACKEND_SETTINGS IS_ENABLED(CONFIG_CANOPENNODE_STORAGE_BACKEND_SETTINGS) +#define CO_STORAGE_BACKEND_RAM IS_ENABLED(CONFIG_CANOPENNODE_STORAGE_BACKEND_RAM) +#define CO_STORAGE_BACKEND_NONE IS_ENABLED(CONFIG_CANOPENNODE_STORAGE_BACKEND_NONE) +/** @} */ + +/** @name Program Download (CiA 302-3) + * @brief Configuration and Program download. + * @{ + */ +#define CO_CONFIG_PROG_DOWNLOAD \ + (ZBIT(CO_CONFIG_PROG_DOWNLOAD_ENABLE, CONFIG_CANOPENNODE_PROG_DOWNLOAD) | \ + ZBIT(CO_CONFIG_PROG_DOWNLOAD_PERMANENT, CONFIG_CANOPENNODE_PROG_DOWNLOAD_PERMANENT)) + +/** @brief Maximum EDS file size in bytes. */ +#define CO_CONFIG_PROG_DOWNLOAD_EDS_MAX_SIZE CONFIG_CANOPENNODE_PROG_DOWNLOAD_EDS_MAX_SIZE +/** @} */ + +/** @name LEDs (CiA 303-3) + * @brief Enable LED state machine and optional callback. + * @{ + */ +#define CO_CONFIG_LEDS \ + (ZBIT(CO_CONFIG_LEDS_ENABLE, CONFIG_CANOPENNODE_LEDS_ENABLE) | \ + ZBIT(CO_CONFIG_LEDS_CALLBACK, CONFIG_CANOPENNODE_LEDS_CALLBACK) | \ + ZBIT(CO_CONFIG_FLAG_TIMERNEXT, CONFIG_CANOPENNODE_LEDS_TIMERNEXT)) +/** @} */ + +/** @name SRDO / GFC + * @brief Safety-related data objects and global fail-safe command. + * @{ + */ +#define CO_CONFIG_GFC \ + (ZBIT(CO_CONFIG_GFC_ENABLE, CONFIG_CANOPENNODE_GFC_ENABLE) | \ + ZBIT(CO_CONFIG_GFC_CONSUMER, CONFIG_CANOPENNODE_GFC_CONSUMER) | \ + ZBIT(CO_CONFIG_GFC_PRODUCER, CONFIG_CANOPENNODE_GFC_PRODUCER)) + +#define CO_CONFIG_SRDO \ + (ZBIT(CO_CONFIG_SRDO_ENABLE, CONFIG_CANOPENNODE_SRDO_ENABLE) | \ + ZBIT(CO_CONFIG_SRDO_CHECK_TX, CONFIG_CANOPENNODE_SRDO_CHECK_TX) | \ + ZBIT(CO_CONFIG_FLAG_CALLBACK_PRE, CONFIG_CANOPENNODE_SRDO_CALLBACK) | \ + ZBIT(CO_CONFIG_FLAG_TIMERNEXT, CONFIG_CANOPENNODE_SRDO_TIMERNEXT)) + +/** @brief Minimum SRDO Tx delay in microseconds. */ +#define CO_CONFIG_SRDO_MINIMUM_DELAY CONFIG_CANOPENNODE_SRDO_MINIMUM_DELAY +/** @} */ + +/** @name LSS (Layer Setting Services) + * @brief Configure LSS master/slave and callbacks. + * @{ + */ +#define CO_CONFIG_LSS \ + (ZBIT(CO_CONFIG_LSS_SLAVE, CONFIG_CANOPENNODE_LSS_SLAVE) | \ + ZBIT(CO_CONFIG_LSS_SLAVE_FASTSCAN_DIRECT_RESPOND, \ + CONFIG_CANOPENNODE_LSS_SLAVE_FASTSCAN_DIRECT_RESPOND) | \ + ZBIT(CO_CONFIG_LSS_MASTER, CONFIG_CANOPENNODE_LSS_MASTER) | \ + ZBIT(CO_CONFIG_FLAG_CALLBACK_PRE, CONFIG_CANOPENNODE_LSS_CALLBACK)) +/** @} */ + +/** @name ASCII Gateway (CiA 309) + * @brief Configure ASCII gateway features and buffer sizes. + * @{ + */ +#define CO_CONFIG_GTW \ + (ZBIT(CO_CONFIG_GTW_MULTI_NET, CONFIG_CANOPENNODE_GTW_MULTI_NET) | \ + ZBIT(CO_CONFIG_GTW_ASCII, CONFIG_CANOPENNODE_GTW_ASCII) | \ + ZBIT(CO_CONFIG_GTW_ASCII_SDO, CONFIG_CANOPENNODE_GTW_ASCII_SDO) | \ + ZBIT(CO_CONFIG_GTW_ASCII_NMT, CONFIG_CANOPENNODE_GTW_ASCII_NMT) | \ + ZBIT(CO_CONFIG_GTW_ASCII_LSS, CONFIG_CANOPENNODE_GTW_ASCII_LSS) | \ + ZBIT(CO_CONFIG_GTW_ASCII_LOG, CONFIG_CANOPENNODE_GTW_ASCII_LOG) | \ + ZBIT(CO_CONFIG_GTW_ASCII_ERROR_DESC, CONFIG_CANOPENNODE_GTW_ASCII_ERROR_DESC) | \ + ZBIT(CO_CONFIG_GTW_ASCII_PRINT_HELP, CONFIG_CANOPENNODE_GTW_ASCII_PRINT_HELP) | \ + ZBIT(CO_CONFIG_GTW_ASCII_PRINT_LEDS, CONFIG_CANOPENNODE_GTW_ASCII_PRINT_LEDS)) + +/** @brief Block download loop count for the gateway. */ +#define CO_CONFIG_GTW_BLOCK_DL_LOOP CONFIG_CANOPENNODE_GTW_BLOCK_DL_LOOP +/** @brief ASCII gateway communication buffer size in bytes. */ +#define CO_CONFIG_GTWA_COMM_BUF_SIZE CONFIG_CANOPENNODE_GTWA_COMM_BUF_SIZE +/** @brief ASCII gateway log buffer size in bytes. */ +#define CO_CONFIG_GTWA_LOG_BUF_SIZE CONFIG_CANOPENNODE_GTWA_LOG_BUF_SIZE +/** @} */ + +/** @name CRC16 + * @brief Enable/route CRC16 implementation used by various modules. + * @{ + */ +#define CO_CONFIG_CRC16 \ + (ZBIT(CO_CONFIG_CRC16_ENABLE, CONFIG_CANOPENNODE_CRC16_ENABLE) | \ + ZBIT(CO_CONFIG_CRC16_EXTERNAL, CONFIG_CANOPENNODE_CRC16_EXTERNAL)) +/** @} */ + +/** @name FIFO + * @brief Configure FIFO utilities and optional ASCII helpers. + * @{ + */ +#define CO_CONFIG_FIFO \ + (ZBIT(CO_CONFIG_FIFO_ENABLE, CONFIG_CANOPENNODE_FIFO_ENABLE) | \ + ZBIT(CO_CONFIG_FIFO_ALT_READ, CONFIG_CANOPENNODE_FIFO_ALT_READ) | \ + ZBIT(CO_CONFIG_FIFO_CRC16_CCITT, CONFIG_CANOPENNODE_FIFO_CRC16_CCITT) | \ + ZBIT(CO_CONFIG_FIFO_ASCII_COMMANDS, CONFIG_CANOPENNODE_FIFO_ASCII_COMMANDS) | \ + ZBIT(CO_CONFIG_FIFO_ASCII_DATATYPES, CONFIG_CANOPENNODE_FIFO_ASCII_DATATYPES)) +/** @} */ + +/** @name Trace + * @brief Configure trace recorder and integer type selection. + * @{ + */ +#define CO_CONFIG_TRACE \ + (ZBIT(CO_CONFIG_TRACE_ENABLE, CONFIG_CANOPENNODE_TRACE_ENABLE) | \ + ZBIT(CO_CONFIG_TRACE_OWN_INTTYPES, CONFIG_CANOPENNODE_TRACE_OWN_INTTYPES)) +/** @} */ + +/** @name Debug + * @brief Enable debug features at module granularity. + * @{ + */ +#define CO_CONFIG_DEBUG \ + (ZBIT(CO_CONFIG_DEBUG_COMMON, CONFIG_CANOPENNODE_DEBUG_COMMON) | \ + ZBIT(CO_CONFIG_DEBUG_SDO_CLIENT, CONFIG_CANOPENNODE_DEBUG_SDO_CLIENT) | \ + ZBIT(CO_CONFIG_DEBUG_SDO_SERVER, CONFIG_CANOPENNODE_DEBUG_SDO_SERVER)) + +/** @} */ + +/** @name Zephyr integration (TX workqueue) + * @brief Configure TX worker thread resources used by the integration. + * @{ + */ +/** @brief Stack size for the CAN TX workqueue thread. */ +#define CO_TX_WQ_STACK_SIZE CONFIG_CANOPENNODE_TX_WORKQUEUE_STACK_SIZE +/** @brief Priority for the CAN TX workqueue thread. */ +#define CO_TX_WQ_PRIORITY CONFIG_CANOPENNODE_TX_WORKQUEUE_PRIORITY +/** @} */ + +/** @name EDS/DCF + * @brief Path to the EDS/DCF file used by the application (if any). + * @{ + */ +/** @brief File system path to the node’s EDS file. */ +#define CO_EDS_FILE_PATH CONFIG_CANOPENNODE_EDS_FILE_PATH +/** @} */ + +/** @} */ /* end of co_zephyr_config */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* ZEPHYR_MODULES_CANOPENNODE_CO_ZEPHYR_CONFIG_H */ diff --git a/zephyr/include/CO_zephyr_integration.h b/zephyr/include/CO_zephyr_integration.h new file mode 100644 index 00000000..1cdb0dc0 --- /dev/null +++ b/zephyr/include/CO_zephyr_integration.h @@ -0,0 +1,245 @@ +/* SPDX-License-Identifier: Apache-2.0 */ +/* + * Zephyr-to-CANopenNode integration API. + * + * Provides a runtime bridge to start/stop the CANopen stack with a selected + * Zephyr CAN device, Node-ID, and bitrate, enabling control from code in + * addition to prj.conf and devicetree. + * + * @file CO_zephyr_integration.h + * @author BitConcepts, LLC + * @copyright 2025 BitConcepts, LLC + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +#ifndef ZEPHYR_MODULES_CANOPENNODE_CO_ZEPHYR_INTEGRATION_H +#define ZEPHYR_MODULES_CANOPENNODE_CO_ZEPHYR_INTEGRATION_H + +#include +#include +#include +#include +#include +#include + +#include "CANopen.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup co_zephyr_integration CANopenNode ↔ Zephyr runtime integration + * @brief Start/stop helpers that wire CANopenNode to a Zephyr CAN device. + * + * This API brings up the full CANopenNode stack on Zephyr with an explicitly + * chosen CAN device, Node-ID, and bitrate. It complements Kconfig/devicetree + * configuration by allowing programmatic control at runtime. + * + * ### Behavior + * - Initializes CANopenNode (CAN, NMT, SDO, PDO, etc.) and enters normal mode. + * - Optionally creates/uses an RT processing thread (SYNC/RPDO/TPDO) if enabled + * by Kconfig in the integration module. + * - Supports optional persistent parameter storage when configured. + * + * ### Devicetree / Kconfig + * - If @p can_dev is `NULL`, the default CAN device is resolved from + * `DT_CHOSEN(zephyr_canbus)`. + * - When @p bitrate_kbps is 0, the bitrate falls back to the Kconfig default + * (e.g., `CONFIG_CANOPENNODE_BITRATE_KBPS`). + * + * ### Typical usage + * @code{.c} + * #include "CO_zephyr_integration.h" + * + * void app_start_canopen(void) + * { + * // Use default CAN dev from DT, Node-ID from Kconfig, default bitrate + * int rc = canopen_start(NULL, CONFIG_CANOPENNODE_INIT_NODE_ID, 0); + * if (rc != 0) { + * printk("CANopen start failed: %d\n", rc); + * return; + * } + * + * if (canopen_is_running()) { + * printk("CANopen is up\n"); + * } + * } + * + * void app_stop_canopen(void) + * { + * canopen_stop(); + * } + * @endcode + * + * @{ + */ + +extern atomic_t g_running; +extern CO_t *CO; + +/** + * @brief Start CANopenNode with explicit device, node ID, and bitrate. + * + * Initializes the full CANopen stack and (optionally) the real-time processing + * thread using the given CAN device, CANopen Node-ID (0–127), and bitrate in + * kbps. When @p can_dev is `NULL`, the CAN device is taken from devicetree + * (the `zephyr,canbus` chosen node). When @p bitrate_kbps is 0, the Kconfig + * default is used. + * + * @param[in] can_dev Zephyr CAN device pointer + * (`NULL` = use DT chosen `zephyr,canbus`). + * @param[in] node_id CANopen Node-ID in the range 1–127. + * @param[in] bitrate_kbps CAN bitrate in kbps + * (0 = use Kconfig default, e.g., `CONFIG_CANOPENNODE_BITRATE_KBPS`). + * + * @retval 0 Success. + * @retval -ENODEV Invalid, missing, or not-ready CAN device. + * @retval -EINVAL Invalid arguments (e.g., Node-ID out of range) or CANopen init error. + * @retval -EALREADY Integration is already running. + * @retval -ENOMEM Allocation or storage initialization error. + * + * @note If persistent storage is enabled via Kconfig, initialization may + * perform a one-time load and can surface backend errors via logs. + * @pre Zephyr kernel is initialized; @p can_dev (if non-NULL) is ready. + * @warning Do not call from ISR context. + */ +int canopen_start(const struct device *can_dev, uint8_t node_id, uint16_t bitrate_kbps); + +/** + * @brief Stop the CANopen stack and worker thread. + * + * Tears down all internal structures, disables the CAN controller, and + * stops the integration’s RT processing thread if present. Safe to call + * multiple times; subsequent calls become no-ops. + */ +void canopen_stop(void); + +/** + * @brief Query whether the CANopen stack is running. + * + * @retval true The stack is active and running. + * @retval false The stack is stopped or not yet initialized. + */ +static inline bool canopen_is_running(void) +{ + return (bool)atomic_get(&g_running); +} + +/** + * @brief Check whether a specific application error is currently active. + * + * Thin wrapper around CO_isError(). This tests an error/status bit managed by + * the CANopenNode Emergency/Error Manager. + * + * @param errorBit Error/status selector (one of the CO_EM_* values such as + * CO_EM_GENERIC_ERROR). Note: kept as @c uint8_t to match + * the current signature; consider using @c CO_EM_t for clarity. + * + * @retval true The selected error is active. + * @retval false The selected error is not active, or the stack is not initialized. + */ +static inline bool canopen_is_error(uint8_t errorBit) +{ + return (CO && CO->em) ? CO_isError(CO->em, errorBit) : false; +} + +/** + * @brief Read the CiA 301 Error Register (object 0x1001). + * + * Returns the current 8-bit Error Register maintained by the stack (per CiA 301), + * or 0 if the stack has not been initialized yet. + * + * @return Current value of object 0x1001 (Error Register), or 0 on uninitialized stack. + */ +static inline uint8_t canopen_get_error_register(void) +{ + return (CO && CO->em && CO->em->errorRegister) ? *(CO->em->errorRegister) : (uint8_t)0; +} + +/** + * @brief Report a generic application error via the Emergency (EMCY) object. + * + * Sets the CO_EM_GENERIC_ERROR condition and requests the stack to emit an EMCY + * with the given 16-bit EMCY error code (CiA 301) and a manufacturer-specific + * 32-bit info field. + * + * Typical usage: call when detecting an application fault (e.g., invalid config, + * overtemperature) to make the error visible to the network and to log it in + * 0x1003 (Pre-defined Error Field) according to the stack’s configuration. + * + * @param errorBit Error/status selector (one of the CO_EM_* values such as + * CO_EM_GENERIC_ERROR). Note: kept as @c uint8_t to match the + * current signature; consider using @c CO_EM_t for clarity. + * @param errorCode 16-bit EMCY error code (CiA 301 compliant). + * @param infoCode 32-bit manufacturer-specific info (placed in EMCY data as + * defined by the stack). + * + * @note No-op if the stack (CO/CO->em) is not initialized. + */ +void canopen_error_report(uint8_t errorBit, uint16_t errorCode, uint32_t infoCode); + +/** + * @brief Clear the previously reported generic application error. + * + * Clears the CO_EM_GENERIC_ERROR condition using CO_errorReset(). Depending on + * stack configuration, this may also cause an “error reset/cleared” EMCY + * indication to be sent to the network. + * + * @param errorBit Error/status selector (one of the CO_EM_* values such as + * CO_EM_GENERIC_ERROR). Note: kept as @c uint8_t to match the + * current signature; consider using @c CO_EM_t for clarity. + * @param infoCode 32-bit manufacturer-specific info (placed in EMCY data as + * defined by the stack). + * @note No-op if the stack (CO/CO->em) is not initialized. + */ +void canopen_error_reset(uint8_t errorBit, uint32_t infoCode); + +/** + * @brief Weak hook for providing the CANopen Node-ID. + * + * This function is provided as a weak symbol and may be overridden by the + * application to supply a board- or system-specific Node-ID. If not + * overridden, the default implementation uses a fixed Node-ID from + * @c CONFIG_CANOPENNODE_INIT_NODE_ID or other built-in mechanism. + * + * The Zephyr integration calls this hook when @ref canopen_start() is invoked + * with @p node_id > 127 (meaning "use default resolution"). The hook must + * return a valid CANopen Node-ID in the range 1..127. Returning 0 or a value + * greater than 127 is treated as "unspecified" or "invalid", and the + * integration will fall back to @c CONFIG_CANOPENNODE_INIT_NODE_ID. + * + * The function is invoked in the context of @ref canopen_start() before the + * stack is started (i.e., not from an ISR). Implementations should keep the + * logic fast and non-blocking. It is safe to read from non-volatile storage + * or board configuration straps provided this does not block excessively. + * + * @param[in] ud + * Optional user data passed through from the integration. May be @c NULL. + * + * @retval 1..127 Valid Node-ID to use. + * @retval 0 Invalid/unspecified, fall back to @c CONFIG_CANOPENNODE_INIT_NODE_ID. + * @retval >127 Invalid/unspecified, fall back to @c CONFIG_CANOPENNODE_INIT_NODE_ID. + * + * @see canopen_start() + */ +__weak uint8_t canopen_get_node_id_hook(void *ud); + +/** @} */ /* end of co_zephyr_integration */ + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_MODULES_CANOPENNODE_CO_ZEPHYR_INTEGRATION_H */ diff --git a/zephyr/include/CO_zephyr_leds.h b/zephyr/include/CO_zephyr_leds.h new file mode 100644 index 00000000..a33216ac --- /dev/null +++ b/zephyr/include/CO_zephyr_leds.h @@ -0,0 +1,137 @@ +/* SPDX-License-Identifier: Apache-2.0 */ +/* + * Zephyr-to-CANopenNode LED bridge. + * + * Mirrors the computed CANopen RUN/ERR LED states (from CO_LEDs) to Zephyr GPIOs + * using devicetree aliases. Respects ACTIVE_LOW/HIGH via DT flags and provides + * a callback-based hookup and one-shot sync utility. + * + * @file CO_zephyr_leds.h + * @author BitConcepts, LLC + * @copyright 2025 BitConcepts, LLC + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +#ifndef ZEPHYR_MODULES_CANOPENNODE_CO_ZEPHYR_LEDS_H +#define ZEPHYR_MODULES_CANOPENNODE_CO_ZEPHYR_LEDS_H + +#include +#include +#include +#include +#include "303/CO_LEDs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup co_zephyr_leds CANopenNode ↔ Zephyr LED helpers + * @brief Mirror CANopen RUN/ERR LED states to Zephyr GPIOs. + * + * This helper binds the CANopen LED state machine from @ref CO_LEDs_t (CiA 303-3) + * to two Zephyr-controlled GPIOs selected via **devicetree aliases**: + * + * - `co_led_run` → RUN (green) LED + * - `co_led_err` → ERR (red) LED + * + * The active level and inversion are taken from the GPIO flags on the aliased + * pins (e.g., `GPIO_ACTIVE_LOW`). Pins are configured as `GPIO_OUTPUT_INACTIVE` + * during initialization. After you connect the callback, each call to + * `CO_LEDs_process()` updates the physical pins to match the synthesized + * CANopen RUN/ERR indicators (bit @ref CO_LED_CANopen in `LEDgreen`/`LEDred`). + * + * ### Devicetree requirements + * Provide two DT aliases that point to GPIOs, for example: + * @code{.dts} + * / { + * aliases { + * co_led_run = &led0; + * co_led_err = &led1; + * }; + * }; + * + * &led0 { gpios = <&gpio0 13 GPIO_ACTIVE_LOW>; status = "okay"; }; + * &led1 { gpios = <&gpio0 14 GPIO_ACTIVE_LOW>; status = "okay"; }; + * @endcode + * + * ### Typical usage + * @code{.c} + * // After CO_LEDs_init(&leds, ...): + * int rc = co_zephyr_leds_init_dt_aliases(); + * if (rc == 0) { + * co_zephyr_leds_connect_callback(&leds); + * // Optionally mirror immediately: + * co_zephyr_leds_sync_once(&leds); + * } + * @endcode + * + * @{ + */ + +/** + * @brief Initialize GPIOs for CANopen LEDs using devicetree aliases. + * + * Resolves the `co_led_run` and `co_led_err` DT aliases to GPIO pins, + * validates their controller readiness, and configures both pins as + * `GPIO_OUTPUT_INACTIVE`. Active level and inversion are honored from + * each pin's devicetree flags (e.g., `GPIO_ACTIVE_LOW`). + * + * @note This function does **not** register any callbacks; call + * co_zephyr_leds_connect_callback() after @ref CO_LEDs_init(). + * + * @retval 0 Success. + * @retval -ENODEV A referenced GPIO controller is not ready, or required DT + * aliases are missing/disabled. + * @retval <0 A negative errno returned by @ref gpio_pin_configure_dt. + */ +int co_zephyr_leds_init_dt_aliases(void); + +/** + * @brief Connect the CANopen LED callback to mirror states to hardware. + * + * Registers an internal callback via @ref CO_LEDs_registerCallback so that + * every @ref CO_LEDs_process update pushes the synthesized RUN/ERR states + * to the configured GPIOs. Safe to call once after @ref CO_LEDs_init. + * + * @param[in] leds Pointer to a valid @ref CO_LEDs_t instance. + * + * @note This function does not reconfigure GPIOs; call + * co_zephyr_leds_init_dt_aliases() first. + * @note If @p leds is `NULL`, the function returns immediately. + */ +void co_zephyr_leds_connect_callback(CO_LEDs_t *leds); + +/** + * @brief One-shot mirror of the current CANopen LED state to GPIOs. + * + * Evaluates the current `LEDgreen`/`LEDred` fields and writes the corresponding + * RUN/ERR outputs once. Useful immediately after initialization to ensure the + * physical LEDs reflect the current state without waiting for the next + * @ref CO_LEDs_process call. + * + * @param[in] leds Pointer to a valid @ref CO_LEDs_t instance. + * + * @note If the GPIOs were not initialized (hardware not ready) or @p leds is + * `NULL`, the call is ignored. + */ +void co_zephyr_leds_sync_once(CO_LEDs_t *leds); + +/** @} */ /* end of co_zephyr_leds */ + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_MODULES_CANOPENNODE_CO_ZEPHYR_LEDS_H */ diff --git a/zephyr/include/CO_zephyr_prog_download.h b/zephyr/include/CO_zephyr_prog_download.h new file mode 100644 index 00000000..424f19bb --- /dev/null +++ b/zephyr/include/CO_zephyr_prog_download.h @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: Apache-2.0 */ +/* + * Zephyr backend (ops) for CANopen Program Download (CiA 302-3 oriented). + * + * Streams incoming program bytes to the MCUBoot secondary slot via Zephyr's + * flash area API, then marks the image for upgrade on COMMIT. + * + * Requirements: + * - CONFIG_BOOTLOADER_MCUBOOT=y + * - CONFIG_FLASH=y + * - CONFIG_FLASH_MAP=y + * - CONFIG_IMG_MANAGER=y (recommended) + * + * Optional: + * - CONFIG_CO_PROGDL_ZEPHYR_PERMANENT=y to make upgrade permanent immediately + * (otherwise marks image as "test" and lets app confirm after self-test). + */ + +/** + * @section CO_ProgDL_ZephyrPartition Zephyr partition selection via Devicetree alias + * + * To select which flash partition CO_Prog_Download programs, define a Devicetree + * **alias** and reference it from code. This avoids fragile Kconfig strings and allows + * board- or app-specific DTS overlays to choose the target cleanly. + * + * ### Devicetree (board or application overlay) + * @code{.dts} + * / { + * aliases { + * // Point Program Download at the partition to be programmed + * can-progdl-partition = &image_1; // or &image_0, &slot1, etc. + * }; + * }; + * @endcode + * + * ### C usage + * In your Zephyr integration (e.g. the port glue that implements begin/write/commit), + * use the alias to resolve the partition node: + * @code{.c} + * #include + * + * #if DT_NODE_HAS_STATUS(DT_ALIAS(can_progdl_partition), okay) + * #define CO_PROGDL_PART_NODE DT_ALIAS(can_progdl_partition) + * #else + * #error "DT alias 'can-progdl-partition' is not defined or not okay" + * #endif + * + * // Example: get partition ID for flash_area_open() or FIXED_PARTITION_ID() + * #define CO_PROGDL_PARTITION_ID FIXED_PARTITION_ID(CO_PROGDL_PART_NODE) + * @endcode + * + * With this approach, changing the programmed area is a one - line change in DTS. + * No Kconfig strings are required, and the C preprocessor receives a proper + * Devicetree **token** (not a string), which is compatible with DT macros. + */ + +#ifndef ZEPHYR_MODULES_CANOPENNODE_CO_ZEPHYR_PROG_DOWNLOAD_H +#define ZEPHYR_MODULES_CANOPENNODE_CO_ZEPHYR_PROG_DOWNLOAD_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "302/CO_Prog_Download.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef CONFIG_CO_PROGDL_ZEPHYR_FLASH_AREA_ID +#define CONFIG_CO_PROGDL_ZEPHYR_FLASH_AREA_ID FLASH_AREA_IMAGE_SECONDARY +#endif + +/* Backend context; one instance per PDL. */ +typedef struct { + /* Target flash area for the new image (normally image_1 / secondary). */ + uint8_t area_id; + + /* Flash area handle (opened on BEGIN, closed on ABORT/COMMIT). */ + const struct flash_area *fa; + + /* Running byte offset within the area. */ + uint32_t off; + + /* Expected total image size (hint from PDL). */ + uint32_t expected_size; + + /* True if BEGIN has successfully opened/erased. */ + bool active; + + /* If true, use boot_request_upgrade(permanent=1); else test upgrade. */ + bool permanent_upgrade; + + /* Optional: guard concurrent access if your SDO path is multithreaded. */ + struct k_mutex lock; +} CO_ProgDL_Zephyr_t; + +/** + * Initialize a Zephyr backend and register the stream ops into CO_ProgDL. + * + * @param pdl The CO_ProgDL instance (already initialized). + * @param zb Backend storage (caller supplies memory). + * @param area_id Flash area id (e.g., FLASH_AREA_ID(image_1)). + * @param permanent_upgrade If true, request a permanent upgrade at COMMIT. + * @return 0 on success, negative on error. + */ +int CO_zephyr_prog_download_bind(CO_ProgDL_t *pdl, CO_ProgDL_Zephyr_t *zb, uint8_t area_id, + bool permanent_upgrade); + +/* Convenience: bind with defaults from Kconfig. */ +static inline int CO_Prog_Download_zephyr_bind_default(CO_ProgDL_t *pdl, CO_ProgDL_Zephyr_t *zb) +{ + return CO_zephyr_prog_download_bind(pdl, zb, CONFIG_CO_PROGDL_ZEPHYR_FLASH_AREA_ID, + CONFIG_CANOPENNODE_PROG_DOWNLOAD_PERMANENT ? true + : false); +} + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_MODULES_CANOPENNODE_CO_ZEPHYR_PROG_DOWNLOAD_H */ diff --git a/zephyr/include/CO_zephyr_storage.h b/zephyr/include/CO_zephyr_storage.h new file mode 100644 index 00000000..5e6bbd15 --- /dev/null +++ b/zephyr/include/CO_zephyr_storage.h @@ -0,0 +1,142 @@ +/* SPDX-License-Identifier: Apache-2.0 */ +/* + * CANopen Object Dictionary storage for Zephyr backends. + * + * Zephyr-backed implementation of the CANopenNode storage object that + * provides persistent parameter handling (load / store / restore) suitable + * for production systems. Integrates with Zephyr subsystems (e.g. Settings + * or NVS/flash), as selected by the application. + * + * @file CO_zephyr_storage.h + * @author Janez Paternoster (original template) + * @author BitConcepts, LLC + * @copyright 2021 Janez Paternoster + * @copyright 2025 BitConcepts, LLC + * + * This file is part of , a CANopen Stack. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +#ifndef ZEPHYR_MODULES_CANOPENNODE_CO_ZEPHYR_STORAGE_H +#define ZEPHYR_MODULES_CANOPENNODE_CO_ZEPHYR_STORAGE_H + +#include "storage/CO_storage.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup co_zephyr_storage CANopenNode ↔ Zephyr storage bridge + * @brief Persistence glue for OD parameters using Zephyr backends. + * + * This interface wires CANopenNode's storage object (@ref CO_storage_t) to a Zephyr + * persistence backend chosen via Kconfig (e.g., Settings, RAM). It enables: + * + * - Loading parameters at boot/reset (OD 0x1011 semantics). + * - Storing parameters on request (OD 0x1010 semantics). + * - Optional restore-to-default handling. + * + * ### Kconfig overview + * Select one of the integration backends at build time (symbol names may vary + * per project): + * - `CONFIG_CANOPENNODE_STORAGE_BACKEND_SETTINGS` — Zephyr Settings API + * - `CONFIG_CANOPENNODE_STORAGE_BACKEND_RAM` — RAM-backed mock (volatile) + * - `CONFIG_CANOPENNODE_STORAGE_BACKEND_NONE` — disabled/no-op + * + * Also enable the storage module: + * - `CONFIG_CANOPENNODE_STORAGE_ENABLE` + * + * ### Typical usage + * @code{.c} + * #include "CO_zephyr_storage.h" + * // ... + * CO_storage_t storage; + * uint32_t storage_err = 0; + * + * CO_ReturnError_t ret = co_zephyr_storage_init( + * &storage, + * CO->CANmodule, + * OD_ENTRY_H1010_storeParameters, + * OD_ENTRY_H1011_restoreDefaultParameters, + * storageEntries, entryCount, + * &storage_err + * ); + * + * if (ret != CO_ERROR_NO) { + * // handle init error + * } + * if (storage_err != 0) { + * // optional: report backend-specific error code + * } + * @endcode + * + * @note This header only declares the initialization function. Subsequent + * load/store operations are driven by the stack via OD 0x1010/0x1011 + * access (e.g., from SDO writes, gateway commands, or application code). + * + * @{ + */ + +/** + * @brief Initialize CANopen storage using a Zephyr-selected backend. + * + * Binds a @ref CO_storage_t instance to the configured Zephyr persistence + * provider and connects it to the Object Dictionary entries for: + * - OD 0x1010: Store Parameters + * - OD 0x1011: Restore Default Parameters + * + * Depending on the selected backend, this may create or open storage areas, + * validate metadata, and perform a one-time load of persisted values into RAM. + * + * @param[out] storage Pointer to a @ref CO_storage_t object to + * initialize. + * @param[in] CANmodule Active @ref CO_CANmodule_t used for + * timing/logging. + * @param[in] OD_1010_StoreParameters OD entry pointer for 0x1010 (Store). + * @param[in] OD_1011_RestoreDefaultParam OD entry pointer for 0x1011 (Restore Defaults). + * @param[in] entries Array of @ref CO_storage_entry_t mappings to + * persist. + * @param[in] entriesCount Number of elements in @p entries. + * @param[out] storageInitError Optional backend-specific error code (0 on + * success). + * + * @retval CO_ERROR_NO Success. + * @retval CO_ERROR_ILLEGAL_ARGUMENT + * Any required pointer is NULL or arguments are inconsistent. + * @retval CO_ERROR_OUT_OF_MEMORY + * Allocation failed in the selected backend. + * @retval CO_ERROR_DATA_CORRUPT + * Stored data failed integrity/format checks + * (backend-specific). + * @retval CO_ERROR_INVALID_STATE + * Backend not available or not initialized correctly. + * + * @note When `storageInitError` is non-NULL, it will contain an implementation- + * specific error value that can be logged or surfaced via EMCY/status bits. + * A value of 0 indicates no backend error. + * @note This function does not take ownership of @p entries; they must remain + * valid for the lifetime of @p storage. + */ +CO_ReturnError_t co_zephyr_storage_init(CO_storage_t *storage, CO_CANmodule_t *CANmodule, + OD_entry_t *OD_1010_StoreParameters, + OD_entry_t *OD_1011_RestoreDefaultParam, + CO_storage_entry_t *entries, uint8_t entriesCount, + uint32_t *storageInitError); + +/** @} */ /* end of co_zephyr_storage */ + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_MODULES_CANOPENNODE_CO_ZEPHYR_STORAGE_H */ diff --git a/zephyr/module.yml b/zephyr/module.yml new file mode 100644 index 00000000..c5431465 --- /dev/null +++ b/zephyr/module.yml @@ -0,0 +1,43 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright (c) 2025 BitConcepts, LLC +# +# Zephyr module manifest for CANopenNode. +# +# Declares this repository as a Zephyr module so the build system can: +# - discover the module during west/cmake processing, +# - add the module's CMake/Kconfig entry points, +# - install Python requirements needed by the module tooling. +# +# @file modules/canopennode/zephyr/module.yml +# @author BitConcepts, LLC +# +# This file is part of , a CANopen Stack. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software distributed under the License +# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +# or implied. See the License for the specific language governing permissions and limitations under +# the License. +# +# Usage notes: +# - Place this file at: modules/canopennode/zephyr/module.yml (or equivalent module path). +# - 'build.cmake: zephyr' tells Zephyr to use the module's CMake entry point located at +# ${ZEPHYR_CURRENT_MODULE_DIR}/CMakeLists.txt. +# - 'build.kconfig' points to the module’s top-level Kconfig (relative to the module root). +# - 'package-managers.pip.requirement-files' lists pip requirement files (relative paths from +# the module root) that Zephyr tooling may install to support code generation or other tasks. + +name: canopennode + +build: + cmake: zephyr + kconfig: zephyr/Kconfig + +package-managers: + pip: + requirement-files: + - zephyr/requirements.txt diff --git a/zephyr/requirements.txt b/zephyr/requirements.txt new file mode 100644 index 00000000..35861f0a --- /dev/null +++ b/zephyr/requirements.txt @@ -0,0 +1 @@ +eds-utils