1
0
Fork 0

Added support for CiA 401 / Driver example / Test example.

User must implement the CANInterface for its own hardware
This commit is contained in:
Pedro Rodríguez Vieites 2025-03-07 17:02:55 +01:00
parent f454a3d82d
commit 8751c52836
20 changed files with 4576 additions and 0 deletions

18
.vscode/c_cpp_properties.json vendored Normal file
View file

@ -0,0 +1,18 @@
{
"configurations": [
{
"name": "linux-gcc-x64",
"includePath": [
"${workspaceFolder}/**"
],
"compilerPath": "/usr/bin/gcc",
"cStandard": "gnu17",
"cppStandard": "gnu++17",
"intelliSenseMode": "linux-gcc-x64",
"compilerArgs": [
""
]
}
],
"version": 4
}

24
.vscode/launch.json vendored Normal file
View file

@ -0,0 +1,24 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "C/C++ Runner: Debug Session",
"type": "cppdbg",
"request": "launch",
"args": [],
"stopAtEntry": false,
"externalConsole": false,
"cwd": "/home/pedro/projects/naust/CANopenNode",
"program": "/home/pedro/projects/naust/CANopenNode/build/Debug/outDebug",
"MIMode": "gdb",
"miDebuggerPath": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}

59
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,59 @@
{
"C_Cpp_Runner.cCompilerPath": "gcc",
"C_Cpp_Runner.cppCompilerPath": "g++",
"C_Cpp_Runner.debuggerPath": "gdb",
"C_Cpp_Runner.cStandard": "gnu17",
"C_Cpp_Runner.cppStandard": "gnu++17",
"C_Cpp_Runner.msvcBatchPath": "C:/Program Files/Microsoft Visual Studio/VR_NR/Community/VC/Auxiliary/Build/vcvarsall.bat",
"C_Cpp_Runner.useMsvc": false,
"C_Cpp_Runner.warnings": [
"-Wall",
"-Wextra",
"-Wpedantic",
"-Wshadow",
"-Wformat=2",
"-Wcast-align",
"-Wconversion",
"-Wsign-conversion",
"-Wnull-dereference"
],
"C_Cpp_Runner.msvcWarnings": [
"/W4",
"/permissive-",
"/w14242",
"/w14287",
"/w14296",
"/w14311",
"/w14826",
"/w44062",
"/w44242",
"/w14905",
"/w14906",
"/w14263",
"/w44265",
"/w14928"
],
"C_Cpp_Runner.enableWarnings": true,
"C_Cpp_Runner.warningsAsError": false,
"C_Cpp_Runner.compilerArgs": [],
"C_Cpp_Runner.linkerArgs": [],
"C_Cpp_Runner.includePaths": [],
"C_Cpp_Runner.includeSearch": [
"*",
"**/*"
],
"C_Cpp_Runner.excludeSearch": [
"**/build",
"**/build/**",
"**/.*",
"**/.*/**",
"**/.vscode",
"**/.vscode/**"
],
"C_Cpp_Runner.useAddressSanitizer": false,
"C_Cpp_Runner.useUndefinedSanitizer": false,
"C_Cpp_Runner.useLeakSanitizer": false,
"C_Cpp_Runner.showCompilationTime": false,
"C_Cpp_Runner.useLinkTimeOptimization": false,
"C_Cpp_Runner.msvcSecureNoWarnings": false
}

View file

@ -0,0 +1,43 @@
#pragma once
#include "invertValue.h"
#include "stdint.h"
#include "debug.h"
template <typename T>
struct AnalogInputModule {
T val;
T last_val;
uint16_t od_index;
uint8_t od_subindex;
uint8_t length;
uint8_t channel;
int32_t offset;
int32_t pre_scaling;
int32_t upper_limit;
int32_t lower_limit;
int32_t delta;
int32_t negative_delta;
int32_t positive_delta;
bool interrupt_enable;
};
template <typename T>
T getAnalogInputFiltered(AnalogInputModule<T> module) {
T filtered_val = module.val + module.offset;
// Apply pre-scaling
filtered_val = filtered_val * module.pre_scaling;
const bool upper_limit_triggered = filtered_val >= module.upper_limit;
const bool lower_limit_triggered = filtered_val < module.lower_limit;
const int32_t delta = filtered_val - module.last_val;
const bool delta_triggered = delta > module.delta || delta < module.negative_delta || delta > module.positive_delta;
const bool condition = (upper_limit_triggered ^ lower_limit_triggered) && delta_triggered;
return module.interrupt_enable && condition ? filtered_val : module.last_val;
}

View file

@ -0,0 +1,38 @@
#pragma once
#include "invertValue.h"
#include "stdint.h"
template <typename T>
struct DigitalInputModule {
T val;
uint16_t od_index;
uint8_t od_subindex;
uint8_t length;
uint8_t channel;
bool polarity;
bool filter_constant;
bool any_change;
bool high_to_low;
bool low_to_high;
bool interrupt_enable;
};
template <typename T>
T getDigitalInputFiltered(DigitalInputModule<T> module) {
T val = module.val;
if (module.filter_constant) {
val = 0;
}
if (module.polarity) {
val = invertValue(val);
}
if (!module.interrupt_enable) {
val = 0;
}
return val;
}

View file

@ -0,0 +1,37 @@
#pragma once
#include "invert_value.h"
#include "stdint.h"
template <typename T>
struct DigitalOutputModule {
T val;
uint16_t od_index;
uint8_t od_subindex;
uint8_t length;
uint8_t channel;
bool error_mode;
bool error_value;
bool polarity;
bool filter_mask;
};
template <typename T>
T getDigitalOutputFiltered(DigitalOutputModule<T> module, const bool failure = false) {
T val = module.val;
if (failure) {
if (module.error_mode) {
val = module.error_value;
}
} else {
if (module.polarity) {
val = invertValue(val);
}
if (!module.filter_mask) {
val = 0;
}
}
return val;
}

12
401/invertValue.h Normal file
View file

@ -0,0 +1,12 @@
#pragma once
template <typename T>
static T invertValue(T val) {
return ~val;
}
// Specialization for bool
template <>
bool invertValue(bool val) {
return !val;
}

395
CO_driver_example.cpp Normal file
View file

@ -0,0 +1,395 @@
/*
* 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 <https://github.com/CANopenNode/CANopenNode>, 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 "301/CO_driver_target.h"
#include "can_interface.h"
#include "debug.h"
void CO_CANsetConfigurationMode(void* CANptr) {
/* Put CAN module in configuration mode */
CanInterface* can = (CanInterface*)CANptr;
if (!can->setMode(CAN_MODE::_MODE_CONFIG)) {
LOG_ERROR("CO_CANsetConfigurationMode: setMode failed\n");
} else {
LOG_INFO("CO_CANsetConfigurationMode: setMode success\n");
}
}
void CO_CANsetNormalMode(CO_CANmodule_t* CANmodule) {
/* Put CAN module in normal mode */
CanInterface* can = (CanInterface*)CANmodule->CANptr;
if (!can->setMode(CAN_MODE::_MCP_NORMAL)) {
LOG_ERROR("CO_CANsetNormalMode: setMode failed\n");
}
CANmodule->CANnormal = true;
LOG_INFO("CO_CANsetNormalMode: setMode success\n");
}
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 */
CanInterface* can = (CanInterface*)CANptr;
const CAN_SPEED speed = getSpeed(CANbitRate);
if (!can->setup(speed)) {
LOG_ERROR("CO_CANmodule_init: setup failed\n");
return CO_ERROR_ILLEGAL_ARGUMENT;
}
/* 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;
LOG_DEBUG("0x%x - 0x%x - 0x%x - 0x%x\n", index, ident, mask, rtr);
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;
LOG_DEBUG("CO_CANrxBufferInit: ident: 0x%x - mask: 0x%x\n", buffer->ident, buffer->mask);
/* Set CAN hardware module filter and mask. */
if (CANmodule->useCANrxFilters) {
LOG_DEBUG("CO_CANrxBufferInit: useCANrxFilters\n");
}
} else {
ret = CO_ERROR_ILLEGAL_ARGUMENT;
LOG_DEBUG("CO_CANrxBufferInit: illegal argument ?\n");
}
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];
buffer->ident = (uint32_t)ident & 0x07FFU;
if (rtr) {
buffer->ident |= 0x0800U;
}
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;
/* Verify overflow */
if (buffer->bufferFull) {
LOG_WARNING("CO_CANsend: buffer full\n");
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 */
CanInterface* can = (CanInterface*)CANmodule->CANptr;
const CanOpenMsg msg(buffer->ident, buffer->data, buffer->DLC);
// DEBUG("Ident: 0x%x - DLC: %d", buffer->ident, buffer->DLC);
// for (uint8_t i = 0; i < buffer->DLC; i++) {
// DEBUG(" 0x%x", buffer->data[i]);
// }
// DEBUG("\r\n");
if (1 && CANmodule->CANtxCount == 0) {
// LOG_DEBUG("CO_CANsend\n");
if (can->send(msg)) {
LOG_DEBUG("CO_CANsend: sent - NodeID: 0x%x - Fx: 0x%x - Len: 0x%x", msg.getNodeId(), msg.getFunctionCode(),
msg.getLen());
CANmodule->bufferInhibitFlag = buffer->syncFlag;
} else {
LOG_ERROR("CO_CANsend: failed - CobID: 0x%x NodeID: 0x%x - Fx: 0x%x - Len: 0x%x", msg.getCobId(),
msg.getNodeId(), msg.getFunctionCode(), msg.getLen());
}
/* copy message and txRequest */
}
/* if no buffer is free, message will be sent by interrupt */
else {
LOG_WARNING("CO_CANsend: buffer full\n");
buffer->bufferFull = true;
CANmodule->CANtxCount++;
// can->send(msg);
}
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;
}
}
void CO_CANinterrupt(CO_CANmodule_t* CANmodule) {
/* receive interrupt */
CanInterface* can = (CanInterface*)CANmodule->CANptr;
if (can->read()) {
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;
const CanOpenMsg msg = can->getCanOpenMsg();
const uint32_t id = msg.getCobId();
const uint8_t len = msg.getLen();
rcvMsg.ident = id;
rcvMsg.DLC = len;
// DEBUG("MSG received: id 0x%x - len %d", rcvMsg.ident, rcvMsg.DLC);
for (uint8_t i = 0; i < len; i++) {
rcvMsg.data[i] = msg.getData(i);
// DEBUG(" %d", rcvMsg.data[i]);
}
// DEBUG("\r\n");
LOG_DEBUG("MSG received: id 0x%x - len %d\n", rcvMsg.ident, rcvMsg.DLC);
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 = 0; index < CANmodule->rxSize; index++) {
// LOG_DEBUG("Buffer: 0x%x - 0x%x - 0x%x\n", buffer->ident, buffer->mask, buffer->object);
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)) {
LOG_DEBUG("Message: 0x%x - 0x%x - 0x%x\n", rcvMsg.ident, buffer->ident, buffer->mask);
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 */
// }
}

23
OD/CO_ident_defs.h Normal file
View file

@ -0,0 +1,23 @@
/*
* Device identification definitions for CANopenNode.
*/
/* Initial (newly programmed device) CAN bit rate and CANopen Node Id */
#define CO_BITRATE_INITIAL 500
#define CO_NODE_ID_INITIAL 0xff
/* Manufacturer device name, OD entry 0x1008 */
#define CO_DEVICE_NAME "CANopenDemoRP2040"
/* Manufacturer hardware version, OD entry 0x1009 */
#define CO_HW_VERSION "---"
/* Manufacturer software version, OD entry 0x100A, updated on make from git
* version. It has form `"<tag>-<n>-g<commit>[-dirty]"`, where `<tag>` is
* name of the last tag, `<n>` is number of commits above the tag, `g` is for
* git, `<commit>` is commit ID and `-dirty` shows, if git is not clean. */
#define CO_SW_VERSION "---"
/* Identity, OD entry 0x1018 */
#define CO_IDENTITY_VENDOR_ID 0x00000000
#define CO_IDENTITY_PRODUCT_CODE 0x00000001
#define CO_IDENTITY_REVISION_NUMBER 0x00000000
#define CO_IDENTITY_SERIAL_NUMBER 0x00000003

112
OD/CO_identificators.c Normal file
View file

@ -0,0 +1,112 @@
/*
* Device identificators for CANopenNode.
*
* @file CO_identificators.c
* @author --
* @copyright 2021 --
*
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
* For more information on CANopen see <http://www.can-cia.org/>.
*
* 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_identificators.h"
#include "301/CO_ODinterface.h"
#include "CO_ident_defs.h"
#include "OD.h"
/*
* Custom function for reading OD object _Manufacturer device name_
*
* For more information see file CO_ODinterface.h, OD_IO_t.
*/
static ODR_t OD_read_1008(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 len = strlen(CO_DEVICE_NAME);
if (len > count)
len = count;
memcpy(buf, CO_DEVICE_NAME, len);
*countRead = stream->dataLength = len;
return ODR_OK;
}
/*
* Custom function for reading OD object _Manufacturer hardware version_
*
* For more information see file CO_ODinterface.h, OD_IO_t.
*/
static ODR_t OD_read_1009(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 len = strlen(CO_HW_VERSION);
if (len > count)
len = count;
memcpy(buf, CO_HW_VERSION, len);
*countRead = stream->dataLength = len;
return ODR_OK;
}
/*
* Custom function for reading OD object _Manufacturer software version_
*
* For more information see file CO_ODinterface.h, OD_IO_t.
*/
static ODR_t OD_read_100A(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 len = strlen(CO_SW_VERSION);
if (len > count)
len = count;
memcpy(buf, CO_SW_VERSION, len);
*countRead = stream->dataLength = len;
return ODR_OK;
}
/* Extensions for OD objects */
OD_extension_t OD_1008_extension = {.object = NULL, .read = OD_read_1008, .write = NULL};
OD_extension_t OD_1009_extension = {.object = NULL, .read = OD_read_1009, .write = NULL};
OD_extension_t OD_100A_extension = {.object = NULL, .read = OD_read_100A, .write = NULL};
/******************************************************************************/
void CO_identificators_init(uint16_t* bitRate, uint8_t* nodeId) {
/* Set initial CAN bitRate and CANopen nodeId. May be configured by LSS. */
if (*bitRate == 0)
*bitRate = CO_BITRATE_INITIAL;
if (*nodeId == 0)
*nodeId = CO_NODE_ID_INITIAL;
/* Initialize OD objects 0x1008, 0x1009 and 0x100A. These are device
* specific strings. Object dictionary has no default value for strings,
* so custom read functions are required for objects to work. */
OD_extension_init(OD_ENTRY_H1008_manufacturerDeviceName, &OD_1008_extension);
OD_extension_init(OD_ENTRY_H1009_manufacturerHardwareVersion, &OD_1009_extension);
OD_extension_init(OD_ENTRY_H100A_manufacturerSoftwareVersion, &OD_100A_extension);
/* Write values directly to the Object Dictionary Identity object. */
OD_PERSIST_COMM.x1018_identity.vendor_ID = CO_IDENTITY_VENDOR_ID;
OD_PERSIST_COMM.x1018_identity.productCode = CO_IDENTITY_PRODUCT_CODE;
OD_PERSIST_COMM.x1018_identity.revisionNumber = CO_IDENTITY_REVISION_NUMBER;
OD_PERSIST_COMM.x1018_identity.serialNumber = CO_IDENTITY_SERIAL_NUMBER;
}

50
OD/CO_identificators.h Normal file
View file

@ -0,0 +1,50 @@
/**
* Device identificators for CANopenNode.
*
* @file CO_identificators.h
* @author --
* @copyright 2021 --
*
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
* For more information on CANopen see <http://www.can-cia.org/>.
*
* 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_IDENTIFICATORS_H
#define CO_IDENTIFICATORS_H
#include "301/CO_driver.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* Function configures default CAN bitRate and CANopen nodeId. Further it
* configures OD objects 0x1008(manufacturerDeviceName),
* 0x1009(manufacturerHardwareVersion), 0x100A(manufacturerSoftwareVersion) and
* 0x1018(identity). It reads definitions from target device specified
* CO_ident_defs.h file.
*
* @param [in,out] bitRate CAN bit rate, set if undefined.
* @param [in,out] nodeId CANopen NodeId, set if undefined.
*/
void CO_identificators_init(uint16_t* bitRate, uint8_t* nodeId);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* CO_IDENTIFICATORS_H */

1747
OD/OD.c Normal file

File diff suppressed because it is too large Load diff

1318
OD/OD.h Normal file

File diff suppressed because it is too large Load diff

157
OD/domainDemo.c Normal file
View file

@ -0,0 +1,157 @@
/**
* Example access to the Object Dictionary variable of type domain.
*
* @file domainDemo.c
* @author --
* @copyright 2021 --
*
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
* For more information on CANopen see <http://www.can-cia.org/>.
*
* 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 "domainDemo.h"
#ifndef DOMAIN_DEMO_LENGTH_INDICATE
#define DOMAIN_DEMO_LENGTH_INDICATE 1
#endif
/* Global variables are used here for simplicity. */
/* Data simulation for domain */
static uint8_t dataSimulated = 0;
/* Index of current data byte transferred. */
static OD_size_t dataIndex = 0;
/* Size of data to be transferred. It is used when reading domainDemo
* and updated after writing domainDemo. */
static OD_size_t dataSize = 1024;
/* Extension for OD object OD_domainDemo */
static OD_extension_t domainDemo_extension;
/*
* Custom function for reading OD object _domainDemo_
*
* For more information see file CO_ODinterface.h, OD_IO_t.
*/
static ODR_t OD_read_domainDemo(OD_stream_t* stream, void* buf, OD_size_t count, OD_size_t* countRead) {
if (stream == NULL || buf == NULL || countRead == NULL || stream->subIndex != 0) {
return ODR_DEV_INCOMPAT;
}
/* Data is simple repeating sequence of values 0..255. Data can be much
* longer than count (available size of the buffer). So
* OD_read_domainDemo() may be called multiple times. */
if (stream->dataOffset == 0) {
/* Data offset is 0, so this is the first call of this function
* in current (SDO) communication. Initialize variables. */
dataSimulated = 0;
dataIndex = 0;
#if DOMAIN_DEMO_LENGTH_INDICATE > 0
/* Indicate dataLength */
size_t dataLen = dataSize;
stream->dataLength = dataLen <= 0xFFFFFFFF ? dataLen : 0;
#else
/* It is not required to indicate data length in SDO transfer */
stream->dataLength = 0;
#endif
}
/* copy application data into buf */
OD_size_t i;
for (i = 0; i < count; i++) {
uint8_t* bufU8 = (uint8_t*)buf;
if (dataIndex >= dataSize) {
break;
}
bufU8[i] = dataSimulated++;
dataIndex++;
}
*countRead = i;
/* finished? */
if (dataIndex >= dataSize) {
stream->dataOffset = 0;
return ODR_OK;
}
/* indicate partial read, this function will be called again. */
stream->dataOffset = dataIndex;
return ODR_PARTIAL;
}
/*
* Custom function for reading OD object _domainDemo_
*
* For more information see file CO_ODinterface.h, OD_IO_t.
*/
static ODR_t OD_write_domainDemo(OD_stream_t* stream, const void* buf, OD_size_t count, OD_size_t* countWritten) {
if (stream == NULL || buf == NULL || countWritten == NULL || stream->subIndex != 0) {
return ODR_DEV_INCOMPAT;
}
/* Data will be just verified for correct sequence in this example
* (repeating sequence of values 0..255). Data can be much longer than
* count (current size of data in the buffer). So OD_write_domainDemo() may
* be called multiple times. */
if (stream->dataOffset == 0) {
/* Data offset is 0, so this is the first call of this function
* in current SDO communication. Initialize variables. */
dataSimulated = 0;
dataIndex = 0;
}
/* copy received data into application */
OD_size_t i;
for (i = 0; i < count; i++) {
uint8_t* bufU8 = (uint8_t*)buf;
/* for simulation just verify, if received data is in sequence */
if (bufU8[i] != dataSimulated++) {
return ODR_INVALID_VALUE;
}
dataIndex++;
}
*countWritten = i;
stream->dataOffset = dataIndex;
/* determine, if file write finished or not (dataLength may not yet be
* indicated) */
if (stream->dataLength > 0 && stream->dataOffset >= stream->dataLength) {
stream->dataOffset = 0;
/* Simulation - set data size to data size currently written. */
dataSize = dataIndex;
return ODR_OK;
}
/* indicate partial write, this function will be called again. */
return ODR_PARTIAL;
}
/******************************************************************************/
CO_ReturnError_t domainDemo_init(OD_entry_t* OD_domainDemo, uint32_t* errInfo) {
if (OD_domainDemo == NULL || errInfo == NULL)
return CO_ERROR_ILLEGAL_ARGUMENT;
/* Initialize custom OD object "domainDemo" */
domainDemo_extension.object = NULL;
domainDemo_extension.read = OD_read_domainDemo;
domainDemo_extension.write = OD_write_domainDemo;
ODR_t odRet = OD_extension_init(OD_domainDemo, &domainDemo_extension);
if (odRet != ODR_OK) {
*errInfo = OD_getIndex(OD_domainDemo);
return CO_ERROR_OD_PARAMETERS;
}
return CO_ERROR_NO;
}

48
OD/domainDemo.h Normal file
View file

@ -0,0 +1,48 @@
/**
* Example access to the Object Dictionary variable of type domain.
*
* @file domainDemo.h
* @author --
* @copyright 2021 --
*
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
* For more information on CANopen see <http://www.can-cia.org/>.
*
* 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 domainDemo_H
#define domainDemo_H
#include "301/CO_ODinterface.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* Initialize domainDemo object.
*
* @param OD_domainDemo Object Dictionary entry for domainDemo.
* @param [out] errInfo If OD entry is erroneous, errInfo indicates its index.
*
* @return @ref CO_ReturnError_t CO_ERROR_NO in case of success.
*/
CO_ReturnError_t domainDemo_init(OD_entry_t* OD_domainDemo, uint32_t* errInfo);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* domainDemo_H */

170
OD/objectAccessOD.c Normal file
View file

@ -0,0 +1,170 @@
/*
* Example object oriented access to the Object Dictionary variable.
*
* @file objectAccessOD.c
* @author --
* @copyright 2021 --
*
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
* For more information on CANopen see <http://www.can-cia.org/>.
*
* 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 "objectAccessOD.h"
#define SUBIDX_I64 0x01
#define SUBIDX_U64 0x02
#define SUBIDX_R32 0x03
#define SUBIDX_R64 0x04
#define SUBIDX_AVERAGE 0x05
#define SUBIDX_PARAMETER 0x06
/*
* Custom function for reading OD object _demoRecord_
*
* For more information see file CO_ODinterface.h, OD_IO_t.
*/
static ODR_t OD_read_demoRecord(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;
}
/* Object was passed by OD_extensionIO_init, use correct type. */
objectAccessOD_t* thisObj = (objectAccessOD_t*)stream->object;
switch (stream->subIndex) {
case SUBIDX_AVERAGE: {
OD_size_t varSize = sizeof(float64_t);
if (count < varSize || stream->dataLength != varSize) {
return ODR_DEV_INCOMPAT;
}
float64_t average = (float64_t)*thisObj->i64;
average += (float64_t)*thisObj->u64;
average += (float64_t)*thisObj->r32;
average += *thisObj->r64;
average /= 4;
memcpy(buf, &average, varSize);
*countRead = varSize;
return ODR_OK;
}
case SUBIDX_PARAMETER: {
uint16_t paramU16 = (uint16_t)(thisObj->internalParameter / 1000);
OD_size_t varSize = sizeof(paramU16);
if (count < varSize || stream->dataLength != varSize) {
return ODR_DEV_INCOMPAT;
}
CO_setUint16(buf, paramU16);
*countRead = varSize;
return ODR_OK;
}
default: {
return OD_readOriginal(stream, buf, count, countRead);
}
}
}
/*
* Custom function for reading OD object _demoRecord_
*
* For more information see file CO_ODinterface.h, OD_IO_t.
*/
static ODR_t OD_write_demoRecord(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;
}
/* Object was passed by OD_extensionIO_init, use correct type. */
objectAccessOD_t* thisObj = (objectAccessOD_t*)stream->object;
switch (stream->subIndex) {
case SUBIDX_PARAMETER: {
uint16_t paramU16 = CO_getUint16(buf);
thisObj->internalParameter = (uint32_t)paramU16 * 1000;
/* write value to the original location in the Object Dictionary */
return OD_writeOriginal(stream, buf, count, countWritten);
}
default: {
return OD_writeOriginal(stream, buf, count, countWritten);
}
}
}
/******************************************************************************/
CO_ReturnError_t objectAccessOD_init(objectAccessOD_t* thisObj, OD_entry_t* OD_demoRecord, uint32_t* errInfo) {
if (thisObj == NULL || errInfo == NULL || OD_demoRecord == NULL)
return CO_ERROR_ILLEGAL_ARGUMENT;
CO_ReturnError_t err = CO_ERROR_NO;
ODR_t odRet;
/* initialize object variables */
memset(thisObj, 0, sizeof(objectAccessOD_t));
/* Initialize custom OD object "demoRecord" */
thisObj->OD_demoRecord_extension.object = thisObj;
thisObj->OD_demoRecord_extension.read = OD_read_demoRecord;
thisObj->OD_demoRecord_extension.write = OD_write_demoRecord;
odRet = OD_extension_init(OD_demoRecord, &thisObj->OD_demoRecord_extension);
/* This is strict behavior and will exit the program on error. Error
* checking on all OD functions can also be omitted. In that case program
* will run, but specific OD entry may not be accessible. */
if (odRet != ODR_OK) {
*errInfo = OD_getIndex(OD_demoRecord);
return CO_ERROR_OD_PARAMETERS;
}
/* Get variables from Object dictionary, related to "Average" */
thisObj->i64 = OD_getPtr(OD_demoRecord, SUBIDX_I64, sizeof(int64_t), NULL);
thisObj->u64 = OD_getPtr(OD_demoRecord, SUBIDX_U64, sizeof(uint64_t), NULL);
thisObj->r32 = OD_getPtr(OD_demoRecord, SUBIDX_R32, sizeof(float32_t), NULL);
thisObj->r64 = OD_getPtr(OD_demoRecord, SUBIDX_R64, sizeof(float64_t), NULL);
if (thisObj->i64 == NULL || thisObj->u64 == NULL || thisObj->r32 == NULL || thisObj->r64 == NULL) {
*errInfo = OD_getIndex(OD_demoRecord);
return CO_ERROR_OD_PARAMETERS;
}
/* Sub entry SUBIDX_AVERAGE will be read by application via
* OD_read_demoRecord() function. Initialize structure "io_average" here. */
odRet = OD_getSub(OD_demoRecord, SUBIDX_AVERAGE, &thisObj->io_average, false);
if (odRet != ODR_OK) {
*errInfo = OD_getIndex(OD_demoRecord);
return CO_ERROR_OD_PARAMETERS;
}
/* Get variable 'Parameter with default value' from Object dictionary */
uint16_t parameterU16;
odRet = OD_get_u16(OD_demoRecord, SUBIDX_PARAMETER, &parameterU16, true);
if (odRet != ODR_OK) {
*errInfo = OD_getIndex(OD_demoRecord);
return CO_ERROR_OD_PARAMETERS;
}
thisObj->internalParameter = (uint32_t)parameterU16 * 1000;
return err;
}

91
OD/objectAccessOD.h Normal file
View file

@ -0,0 +1,91 @@
/**
* Example object oriented access to the Object Dictionary variable.
*
* @file objectAccessOD.h
* @author --
* @copyright 2021 --
*
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
* For more information on CANopen see <http://www.can-cia.org/>.
*
* 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 objectAccessOD_H
#define objectAccessOD_H
#include "301/CO_ODinterface.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* Object declaration for objectAccessOD.
*/
typedef struct {
OD_extension_t OD_demoRecord_extension; /**< Extension for OD object */
OD_IO_t io_average; /**< object for read access to sub-OD variable average*/
int64_t* i64; /**< Pointer to variable in object dictionary */
uint64_t* u64; /**< Pointer to variable in object dictionary */
float32_t* r32; /**< Pointer to variable in object dictionary */
float64_t* r64; /**< Pointer to variable in object dictionary */
/** Variable initialised from OD sub-entry 'Parameter with default value' */
uint32_t internalParameter;
} objectAccessOD_t;
/**
* Initialize objectAccessOD object.
*
* @param thisObj This object will be initialized.
* @param OD_demoRecord Object Dictionary entry for demoRecord.
* @param [out] errInfo If OD entry is erroneous, errInfo indicates its index.
*
* @return @ref CO_ReturnError_t CO_ERROR_NO in case of success.
*/
CO_ReturnError_t objectAccessOD_init(objectAccessOD_t* thisObj, OD_entry_t* OD_demoRecord, uint32_t* errInfo);
/**
* Read "average" variable from Object Dictionary
*
* This is a demonstration of extended OD variable. OD variable is not accessed
* from memory location, because it does not exist. Average is calculated from
* internal values, so function access is necessary. "read" function specified
* by OD_extension_init() is called. For "read" function to use,
* "OD_IO_t io_average" structure has been initialized before. If
* objectAccessOD_readAverage() is used from mainline, it has to be protected
* with CO_LOCK_OD / CO_UNLOCK_OD macros as every access to OD variable from
* mainline.
*
* @param thisObj This object contains access information to "average" parameter
*
* @return Value of the parameter.
*/
static inline float64_t objectAccessOD_readAverage(objectAccessOD_t* thisObj) {
float64_t average = 0;
OD_size_t countRd;
ODR_t odRet = thisObj->io_average.read(&thisObj->io_average.stream, &average, sizeof(average), &countRd);
(void)odRet;
(void)countRd; /* unused */
return average;
}
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* objectAccessOD_H */

112
drivers/CO_storageBlank.c Normal file
View file

@ -0,0 +1,112 @@
/*
* CANopen Object Dictionary storage object (blank example).
*
* @file CO_storageBlank.c
* @author Janez Paternoster
* @copyright 2021 Janez Paternoster
*
* This file is part of <https://github.com/CANopenNode/CANopenNode>, 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;
}
CO_ReturnError_t CO_storageBlank_auto_process(CO_storage_t* storage, bool_t saveAll) {
/* verify arguments */
if (storage == NULL || !storage->enabled) {
return CO_ERROR_ILLEGAL_ARGUMENT;
}
/* 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) == 0) {
continue;
}
if (saveAll) {
/* close the file */
} else {
/* Open a file and write data to it */
}
}
return CO_ERROR_NO;
}
#endif /* (CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE */

55
drivers/CO_storageBlank.h Normal file
View file

@ -0,0 +1,55 @@
/*
* CANopen data storage object (blank example)
*
* @file CO_storageBlank.h
* @author Janez Paternoster
* @copyright 2021 Janez Paternoster
*
* This file is part of <https://github.com/CANopenNode/CANopenNode>, 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);
CO_ReturnError_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 */

View file

@ -0,0 +1,67 @@
#include "can_controller_factory.h"
#include "canopen_controller_factory.h"
#include "database_factory.h"
#include "debug.h"
#include "rp2040_regs.h"
#include "timer_controller_factory.h"
int main (){
CanOpenInterface* canopen = getCanOpenFactory();
CanInterface* can = getCanFactory();
TimerInterface* timer = getTimerControllerFactory();
TEST_ASSERT_NOT_NULL(canopen);
TEST_ASSERT_NOT_NULL(can);
TEST_ASSERT_TRUE_MESSAGE(canopen->configure(can, 500, 1), "configure failed");
CO_ReturnError_t err = canopen->appConfigLoop();
TEST_ASSERT_EQUAL_MESSAGE(CO_ERROR_NO, err, "appConfigLoop failed");
// uint64_t iterations = 1'000;
uint64_t iterations = 1;
const uint64_t start_ms = timer->millis();
uint8_t statusLed = 0;
uint8_t errorLed = 0;
CO_NMT_reset_cmd_t reset = CO_RESET_COMM;
uint64_t init_us = timer->micros();
uint64_t total_time_us = 0;
for (uint64_t i = 0; i < iterations; i++) {
uint64_t iter_start_us = timer->micros();
if (reset == CO_RESET_APP) {
canopen->reset();
canopen->end();
LOG_ERROR("reset");
break;
} else if (reset != CO_RESET_NOT) {
timer->delaySec(1);
CO_ReturnError_t err = canopen->appConfigLoop();
if (err != CO_ERROR_NO) {
LOG_ERROR("appConfigLoop failed");
// return err;
}
reset = CO_RESET_NOT;
// continue;
} else if (reset == CO_RESET_NOT) {
reset = canopen->appExecLoop(timer->micros(), statusLed, errorLed);
// continue;
}
uint64_t iter_end_us = timer->micros();
uint64_t iter_time_us = iter_end_us - iter_start_us;
total_time_us += iter_time_us;
if (i % 1000000 == 0) {
uint64_t end_us = timer->micros();
LOG_INFO("Iteration: %lu - %lu us", i, end_us - init_us);
init_us = timer->micros();
}
}
const uint64_t end_ms = timer->millis();
const uint64_t duration_ms = end_ms - start_ms;
LOG_INFO("Duration: %lu ms\n", duration_ms);
LOG_INFO("Average time per iteration: %.2f us\n", static_cast<double>(total_time_us) / iterations);
}