Added support for CiA 401 / Driver example / Test example.
User must implement the CANInterface for its own hardware
This commit is contained in:
parent
f454a3d82d
commit
8751c52836
20 changed files with 4576 additions and 0 deletions
18
.vscode/c_cpp_properties.json
vendored
Normal file
18
.vscode/c_cpp_properties.json
vendored
Normal 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
24
.vscode/launch.json
vendored
Normal 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
59
.vscode/settings.json
vendored
Normal 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
|
||||
}
|
||||
43
401/analogInputBlockDiagram.h
Normal file
43
401/analogInputBlockDiagram.h
Normal 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;
|
||||
}
|
||||
38
401/digitalInputBlockDiagram.h
Normal file
38
401/digitalInputBlockDiagram.h
Normal 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;
|
||||
}
|
||||
37
401/digitalOutputBlockDiagram.h
Normal file
37
401/digitalOutputBlockDiagram.h
Normal 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
12
401/invertValue.h
Normal 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
395
CO_driver_example.cpp
Normal 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
23
OD/CO_ident_defs.h
Normal 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
112
OD/CO_identificators.c
Normal 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
50
OD/CO_identificators.h
Normal 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 */
|
||||
157
OD/domainDemo.c
Normal file
157
OD/domainDemo.c
Normal 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
48
OD/domainDemo.h
Normal 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
170
OD/objectAccessOD.c
Normal 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, ¶meterU16, 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
91
OD/objectAccessOD.h
Normal 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
112
drivers/CO_storageBlank.c
Normal 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
55
drivers/CO_storageBlank.h
Normal 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 */
|
||||
67
test_performance_example.c
Normal file
67
test_performance_example.c
Normal 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);
|
||||
|
||||
}
|
||||
Loading…
Reference in a new issue