Added trace interface for monitoring variables.

This commit is contained in:
Janez 2016-03-24 22:01:44 +01:00
parent b6672b2500
commit 85fc85482e
12 changed files with 7842 additions and 21 deletions

View File

@ -68,6 +68,15 @@
static CO_CANtx_t *CO_CANmodule_txArray0;
static CO_OD_extension_t *CO_SDO_ODExtensions;
static CO_HBconsNode_t *CO_HBcons_monitoredNodes;
#if CO_NO_TRACE > 0
static uint32_t *CO_traceTimeBuffers[CO_NO_TRACE];
static int32_t *CO_traceValueBuffers[CO_NO_TRACE];
#ifdef CO_USE_GLOBALS
#ifndef CO_TRACE_BUFFER_SIZE_FIXED
#define CO_TRACE_BUFFER_SIZE_FIXED 100
#endif
#endif
#endif
/* Verify features from CO_OD *************************************************/
@ -129,6 +138,11 @@
#if CO_NO_SDO_CLIENT == 1
static CO_SDOclient_t COO_SDOclient;
#endif
#if CO_NO_TRACE > 0
static CO_trace_t COO_trace[CO_NO_TRACE];
static uint32_t COO_traceTimeBuffers[CO_NO_TRACE][CO_TRACE_BUFFER_SIZE_FIXED];
static int32_t COO_traceValueBuffers[CO_NO_TRACE][CO_TRACE_BUFFER_SIZE_FIXED];
#endif
#endif
@ -184,6 +198,9 @@ CO_ReturnError_t CO_init(
#ifndef CO_USE_GLOBALS
uint16_t errCnt;
#endif
#if CO_NO_TRACE > 0
uint32_t CO_traceBufferSize[CO_NO_TRACE];
#endif
/* Verify parameters from CO_OD */
if( sizeof(OD_TPDOCommunicationParameter_t) != sizeof(CO_TPDOCommPar_t)
@ -224,7 +241,14 @@ CO_ReturnError_t CO_init(
#if CO_NO_SDO_CLIENT == 1
CO->SDOclient = &COO_SDOclient;
#endif
#if CO_NO_TRACE > 0
for(i=0; i<CO_NO_TRACE; i++) {
CO->trace[i] = &COO_trace[i];
CO_traceTimeBuffers[i] = &COO_traceTimeBuffers[i][0];
CO_traceValueBuffers[i] = &COO_traceValueBuffers[i][0];
CO_traceBufferSize[i] = CO_TRACE_BUFFER_SIZE_FIXED;
}
#endif
#else
if(CO == NULL){ /* Use malloc only once */
CO = &COO;
@ -250,6 +274,18 @@ CO_ReturnError_t CO_init(
#if CO_NO_SDO_CLIENT == 1
CO->SDOclient = (CO_SDOclient_t *) calloc(1, sizeof(CO_SDOclient_t));
#endif
#if CO_NO_TRACE > 0
for(i=0; i<CO_NO_TRACE; i++) {
CO->trace[i] = (CO_trace_t *) calloc(1, sizeof(CO_trace_t));
CO_traceTimeBuffers[i] = (uint32_t *) calloc(OD_traceConfig[i].size, sizeof(uint32_t));
CO_traceValueBuffers[i] = (int32_t *) calloc(OD_traceConfig[i].size, sizeof(int32_t));
if(CO_traceTimeBuffers[i] != NULL && CO_traceValueBuffers[i] != NULL) {
CO_traceBufferSize[i] = OD_traceConfig[i].size;
} else {
CO_traceBufferSize[i] = 0;
}
}
#endif
}
CO_memoryUsed = sizeof(CO_CANmodule_t)
@ -269,6 +305,12 @@ CO_ReturnError_t CO_init(
+ sizeof(CO_SDOclient_t)
#endif
+ 0;
#if CO_NO_TRACE > 0
CO_memoryUsed += sizeof(CO_trace_t) * CO_NO_TRACE;
for(i=0; i<CO_NO_TRACE; i++) {
CO_memoryUsed += CO_traceBufferSize[i] * 8;
}
#endif
errCnt = 0;
if(CO->CANmodule[0] == NULL) errCnt++;
@ -293,6 +335,11 @@ CO_ReturnError_t CO_init(
#if CO_NO_SDO_CLIENT == 1
if(CO->SDOclient == NULL) errCnt++;
#endif
#if CO_NO_TRACE > 0
for(i=0; i<CO_NO_TRACE; i++) {
if(CO->trace[i] == NULL) errCnt++;
}
#endif
if(errCnt != 0) return CO_ERROR_OUT_OF_MEMORY;
#endif
@ -476,6 +523,29 @@ CO_ReturnError_t CO_init(
#endif
#if CO_NO_TRACE > 0
for(i=0; i<CO_NO_TRACE; i++) {
CO_trace_init(
CO->trace[i],
CO->SDO[0],
OD_traceConfig[i].axisNo,
CO_traceTimeBuffers[i],
CO_traceValueBuffers[i],
CO_traceBufferSize[i],
&OD_traceConfig[i].map,
&OD_traceConfig[i].format,
&OD_traceConfig[i].trigger,
&OD_traceConfig[i].threshold,
&OD_trace[i].value,
&OD_trace[i].min,
&OD_trace[i].max,
&OD_trace[i].triggerTime,
OD_INDEX_TRACE_CONFIG + i,
OD_INDEX_TRACE + i);
}
#endif
return CO_ERROR_NO;
}
@ -490,6 +560,13 @@ void CO_delete(int32_t CANbaseAddress){
CO_CANmodule_disable(CO->CANmodule[0]);
#ifndef CO_USE_GLOBALS
#if CO_NO_TRACE > 0
for(i=0; i<CO_NO_TRACE; i++) {
free(CO->trace[i]);
free(CO_traceTimeBuffers[i]);
free(CO_traceValueBuffers[i]);
}
#endif
#if CO_NO_SDO_CLIENT == 1
free(CO->SDOclient);
#endif

View File

@ -83,6 +83,9 @@
#if CO_NO_SDO_CLIENT == 1
#include "CO_SDOmaster.h"
#endif
#if CO_NO_TRACE > 0
#include "CO_trace.h"
#endif
/**
@ -128,6 +131,9 @@ typedef struct{
#if CO_NO_SDO_CLIENT == 1
CO_SDOclient_t *SDOclient; /**< SDO client object */
#endif
#if CO_NO_TRACE > 0
CO_trace_t *trace[CO_NO_TRACE]; /**< Trace object for monitoring variables */
#endif
}CO_t;

View File

@ -26,6 +26,7 @@ SOURCES = $(STACKDRV_SRC)/CO_driver.c \
$(STACK_SRC)/CO_PDO.c \
$(STACK_SRC)/CO_HBconsumer.c \
$(STACK_SRC)/CO_SDOmaster.c \
$(STACK_SRC)/CO_trace.c \
$(CANOPEN_SRC)/CANopen.c \
$(APPL_SRC)/CO_OD.c \
$(APPL_SRC)/main.c

View File

@ -10,7 +10,7 @@
*
* @file CO_OD.c
* @author Janez Paternoster
* @copyright 2010 - 2015 Janez Paternoster
* @copyright 2010 - 2016 Janez Paternoster
*
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
@ -76,6 +76,9 @@ struct sCO_OD_RAM CO_OD_RAM = {
/*2109*/ {0},
/*2110*/ {0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L},
/*2120*/ {0x5, 0x1234567890ABCDEFLL, 0x234567890ABCDEF1LL, 12.345, 456.789, 0},
/*2130*/ {0x3, {'-', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, 0, 0x0L},
/*2400*/{{0x6, 0x0L, 0L, 0L, 0L, 0, 0x0L},
/*2401*/ {0x6, 0x0L, 0L, 0L, 0L, 0, 0x0L}},
/*6000*/ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
/*6200*/ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
/*6401*/ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
@ -135,6 +138,8 @@ struct sCO_OD_EEPROM CO_OD_EEPROM = {
/*2101*/ 0x30,
/*2102*/ 0xFA,
/*2111*/ {1L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L},
/*2300*/{{0x8, 0x64L, 0x1, {'n', 'a', 'm', 'e', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, {'r', 'e', 'd', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, 0x60000108L, 0x1, 0x0, 0L},
/*2301*/ {0x8, 0x0L, 0x0, {'n', 'a', 'm', 'e', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, {'r', 'e', 'd', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, 0x0L, 0x0, 0x0, 0L}},
CO_OD_FIRST_LAST_WORD
};
@ -288,6 +293,47 @@ struct sCO_OD_EEPROM CO_OD_EEPROM = {
{(void*)&CO_OD_RAM.testVar.R32, 0xBE, 4},
{(void*)&CO_OD_RAM.testVar.R64, 0xBE, 8},
{0, 0x0E, 0}};
/*0x2130*/ const CO_OD_entryRecord_t OD_record2130[4] = {
{(void*)&CO_OD_RAM.time.maxSubIndex, 0x06, 1},
{(void*)&CO_OD_RAM.time.string[0], 0x06, 30},
{(void*)&CO_OD_RAM.time.epochTimeBaseMs, 0x8E, 8},
{(void*)&CO_OD_RAM.time.epochTimeOffsetMs, 0xBE, 4}};
/*0x2300*/ const CO_OD_entryRecord_t OD_record2300[9] = {
{(void*)&CO_OD_ROM.traceConfig[0].maxSubIndex, 0x05, 1},
{(void*)&CO_OD_ROM.traceConfig[0].size, 0x8D, 4},
{(void*)&CO_OD_ROM.traceConfig[0].axisNo, 0x0D, 1},
{(void*)&CO_OD_ROM.traceConfig[0].name[0], 0x0D, 30},
{(void*)&CO_OD_ROM.traceConfig[0].color[0], 0x0D, 20},
{(void*)&CO_OD_ROM.traceConfig[0].map, 0x8D, 4},
{(void*)&CO_OD_ROM.traceConfig[0].format, 0x0D, 1},
{(void*)&CO_OD_ROM.traceConfig[0].trigger, 0x0D, 1},
{(void*)&CO_OD_ROM.traceConfig[0].threshold, 0x8D, 4}};
/*0x2301*/ const CO_OD_entryRecord_t OD_record2301[9] = {
{(void*)&CO_OD_ROM.traceConfig[1].maxSubIndex, 0x05, 1},
{(void*)&CO_OD_ROM.traceConfig[1].size, 0x8D, 4},
{(void*)&CO_OD_ROM.traceConfig[1].axisNo, 0x0D, 1},
{(void*)&CO_OD_ROM.traceConfig[1].name[0], 0x0D, 30},
{(void*)&CO_OD_ROM.traceConfig[1].color[0], 0x0D, 20},
{(void*)&CO_OD_ROM.traceConfig[1].map, 0x8D, 4},
{(void*)&CO_OD_ROM.traceConfig[1].format, 0x0D, 1},
{(void*)&CO_OD_ROM.traceConfig[1].trigger, 0x0D, 1},
{(void*)&CO_OD_ROM.traceConfig[1].threshold, 0x8D, 4}};
/*0x2400*/ const CO_OD_entryRecord_t OD_record2400[7] = {
{(void*)&CO_OD_RAM.trace[0].maxSubIndex, 0x06, 1},
{(void*)&CO_OD_RAM.trace[0].size, 0xBE, 4},
{(void*)&CO_OD_RAM.trace[0].value, 0xA6, 4},
{(void*)&CO_OD_RAM.trace[0].min, 0xBE, 4},
{(void*)&CO_OD_RAM.trace[0].max, 0xBE, 4},
{0, 0x06, 0},
{(void*)&CO_OD_RAM.trace[0].triggerTime, 0xBE, 4}};
/*0x2401*/ const CO_OD_entryRecord_t OD_record2401[7] = {
{(void*)&CO_OD_RAM.trace[1].maxSubIndex, 0x06, 1},
{(void*)&CO_OD_RAM.trace[1].size, 0xBE, 4},
{(void*)&CO_OD_RAM.trace[1].value, 0xA6, 4},
{(void*)&CO_OD_RAM.trace[1].min, 0xBE, 4},
{(void*)&CO_OD_RAM.trace[1].max, 0xBE, 4},
{0, 0x06, 0},
{(void*)&CO_OD_RAM.trace[1].triggerTime, 0xBE, 4}};
/*******************************************************************************
@ -344,8 +390,14 @@ const CO_OD_entry_t CO_OD[CO_OD_NoOfElements] = {
{0x2111, 0x10, 0xFD, 4, (void*)&CO_OD_ROM.variableROMInt32[0]},
{0x2112, 0x10, 0xFF, 4, (void*)&CO_OD_EEPROM.variableNVInt32[0]},
{0x2120, 0x05, 0x00, 0, (void*)&OD_record2120},
{0x2130, 0x03, 0x00, 0, (void*)&OD_record2130},
{0x2300, 0x08, 0x00, 0, (void*)&OD_record2300},
{0x2301, 0x08, 0x00, 0, (void*)&OD_record2301},
{0x2400, 0x06, 0x00, 0, (void*)&OD_record2400},
{0x2401, 0x06, 0x00, 0, (void*)&OD_record2401},
{0x6000, 0x08, 0x76, 1, (void*)&CO_OD_RAM.readInput8Bit[0]},
{0x6200, 0x08, 0x3E, 1, (void*)&CO_OD_RAM.writeOutput8Bit[0]},
{0x6401, 0x0C, 0xB6, 2, (void*)&CO_OD_RAM.readAnalogueInput16Bit[0]},
{0x6411, 0x08, 0xBE, 2, (void*)&CO_OD_RAM.writeAnalogueOutput16Bit[0]},
};

View File

@ -10,7 +10,7 @@
*
* @file CO_OD.h
* @author Janez Paternoster
* @copyright 2010 - 2015 Janez Paternoster
* @copyright 2010 - 2016 Janez Paternoster
*
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
@ -74,11 +74,11 @@
/*******************************************************************************
FILE INFO:
FileName: IO
FileVersion: 3.0
CreationTime: 13:23:28
CreationDate: 2013-03-09
CreatedBy: -
FileName: IO Example
FileVersion: -
CreationTime: 21:37:08
CreationDate: 2016-03-24
CreatedBy: JP
*******************************************************************************/
@ -97,16 +97,17 @@
#define CO_NO_SYNC 1 //Associated objects: 1005, 1006, 1007, 2103, 2104
#define CO_NO_EMERGENCY 1 //Associated objects: 1014, 1015
#define CO_NO_SDO_SERVER 1 //Associated objects: 1200
#define CO_NO_SDO_CLIENT 0
#define CO_NO_SDO_CLIENT 0
#define CO_NO_RPDO 4 //Associated objects: 1400, 1401, 1402, 1403, 1600, 1601, 1602, 1603
#define CO_NO_TPDO 4 //Associated objects: 1800, 1801, 1802, 1803, 1A00, 1A01, 1A02, 1A03
#define CO_NO_NMT_MASTER 0
#define CO_NO_NMT_MASTER 0
#define CO_NO_TRACE 2 //Associated objects: 2300, 2301, 2400, 2401
/*******************************************************************************
OBJECT DICTIONARY
*******************************************************************************/
#define CO_OD_NoOfElements 54
#define CO_OD_NoOfElements 59
/*******************************************************************************
@ -175,6 +176,35 @@
DOMAIN domain;
} OD_testVar_t;
/*2130 */ typedef struct{
UNSIGNED8 maxSubIndex;
VISIBLE_STRING string[30];
UNSIGNED64 epochTimeBaseMs;
UNSIGNED32 epochTimeOffsetMs;
} OD_time_t;
/*2300[2] */ typedef struct{
UNSIGNED8 maxSubIndex;
UNSIGNED32 size;
UNSIGNED8 axisNo;
VISIBLE_STRING name[30];
VISIBLE_STRING color[20];
UNSIGNED32 map;
UNSIGNED8 format;
UNSIGNED8 trigger;
INTEGER32 threshold;
} OD_traceConfig_t;
/*2400[2] */ typedef struct{
UNSIGNED8 maxSubIndex;
UNSIGNED32 size;
INTEGER32 value;
INTEGER32 min;
INTEGER32 max;
DOMAIN plot;
UNSIGNED32 triggerTime;
} OD_trace_t;
/*******************************************************************************
STRUCTURES FOR VARIABLES IN DIFFERENT MEMORY LOCATIONS
@ -198,6 +228,8 @@ struct sCO_OD_RAM{
/*2109 */ INTEGER16 voltage[1];
/*2110 */ INTEGER32 variableInt32[16];
/*2120 */ OD_testVar_t testVar;
/*2130 */ OD_time_t time;
/*2400[2] */ OD_trace_t trace[2];
/*6000 */ UNSIGNED8 readInput8Bit[8];
/*6200 */ UNSIGNED8 writeOutput8Bit[8];
/*6401 */ INTEGER16 readAnalogueInput16Bit[12];
@ -244,6 +276,7 @@ struct sCO_OD_ROM{
/*2101 */ UNSIGNED8 CANNodeID;
/*2102 */ UNSIGNED16 CANBitRate;
/*2111 */ INTEGER32 variableROMInt32[16];
/*2300[2] */ OD_traceConfig_t traceConfig[2];
UNSIGNED32 LastWord;
};
@ -404,6 +437,15 @@ extern struct sCO_OD_ROM CO_OD_ROM;
/*2120, Data Type: OD_testVar_t */
#define OD_testVar CO_OD_RAM.testVar
/*2130, Data Type: OD_time_t */
#define OD_time CO_OD_RAM.time
/*2300[2], Data Type: OD_traceConfig_t, Array[2] */
#define OD_traceConfig CO_OD_ROM.traceConfig
/*2400[2], Data Type: OD_trace_t, Array[2] */
#define OD_trace CO_OD_RAM.trace
/*6000, Data Type: UNSIGNED8, Array[8] */
#define OD_readInput8Bit CO_OD_RAM.readInput8Bit
#define ODL_readInput8Bit_arrayLength 8
@ -422,3 +464,4 @@ extern struct sCO_OD_ROM CO_OD_ROM;
#endif

View File

@ -5,14 +5,14 @@
[FileInfo]
FileName=IO
FileVersion=3.0
FileName=IO Example
FileVersion=-
FileRevision=0
EDSVersion=4.0
Description=Open Source CANopen implementation
CreationTime=13:23:28
CreationDate=2013-03-09
CreatedBy=-
CreationTime=21:37:08
CreationDate=2016-03-24
CreatedBy=JP
[DeviceInfo]
@ -109,7 +109,7 @@ SupportedObjects=38
[ManufacturerObjects]
SupportedObjects=13
SupportedObjects=18
1=0x2100
2=0x2101
3=0x2102
@ -123,6 +123,11 @@ SupportedObjects=13
11=0x2111
12=0x2112
13=0x2120
14=0x2130
15=0x2300
16=0x2301
17=0x2400
18=0x2401
[1000]
ParameterName=Device type
@ -2130,6 +2135,319 @@ AccessType=rw
PDOMapping=0
DefaultValue=0
[2130]
ParameterName=Time
ObjectType=9
SubNumber=4
[2130sub0]
ParameterName=max sub-index
ObjectType=7
DataType=0x0005
AccessType=ro
PDOMapping=0
DefaultValue=3
[2130sub1]
ParameterName=String
ObjectType=7
DataType=0x0009
AccessType=ro
PDOMapping=0
DefaultValue=-
[2130sub2]
ParameterName=Epoch time base ms
ObjectType=7
DataType=0x001B
AccessType=rw
PDOMapping=0
DefaultValue=0
[2130sub3]
ParameterName=Epoch time offset ms
ObjectType=7
DataType=0x0007
AccessType=rww
PDOMapping=1
DefaultValue=0
[2300]
ParameterName=Trace config
ObjectType=9
SubNumber=9
[2300sub0]
ParameterName=max sub-index
ObjectType=7
DataType=0x0005
AccessType=ro
PDOMapping=0
DefaultValue=8
[2300sub1]
ParameterName=Size
ObjectType=7
DataType=0x0007
AccessType=rw
PDOMapping=0
DefaultValue=100
[2300sub2]
ParameterName=Axis no
ObjectType=7
DataType=0x0005
AccessType=rw
PDOMapping=0
DefaultValue=1
[2300sub3]
ParameterName=Name
ObjectType=7
DataType=0x0009
AccessType=rw
PDOMapping=0
DefaultValue=name
[2300sub4]
ParameterName=Color
ObjectType=7
DataType=0x0009
AccessType=rw
PDOMapping=0
DefaultValue=red
[2300sub5]
ParameterName=Map
ObjectType=7
DataType=0x0007
AccessType=rw
PDOMapping=0
DefaultValue=0x60000108
[2300sub6]
ParameterName=Format
ObjectType=7
DataType=0x0005
AccessType=rw
PDOMapping=0
DefaultValue=1
[2300sub7]
ParameterName=Trigger
ObjectType=7
DataType=0x0005
AccessType=rw
PDOMapping=0
DefaultValue=0
[2300sub8]
ParameterName=Threshold
ObjectType=7
DataType=0x0004
AccessType=rw
PDOMapping=0
DefaultValue=0
[2301]
ParameterName=Trace config
ObjectType=9
SubNumber=9
[2301sub0]
ParameterName=max sub-index
ObjectType=7
DataType=0x0005
AccessType=ro
PDOMapping=0
DefaultValue=8
[2301sub1]
ParameterName=Size
ObjectType=7
DataType=0x0007
AccessType=rw
PDOMapping=0
DefaultValue=0
[2301sub2]
ParameterName=Axis no
ObjectType=7
DataType=0x0005
AccessType=rw
PDOMapping=0
DefaultValue=0
[2301sub3]
ParameterName=Name
ObjectType=7
DataType=0x0009
AccessType=rw
PDOMapping=0
DefaultValue=name
[2301sub4]
ParameterName=Color
ObjectType=7
DataType=0x0009
AccessType=rw
PDOMapping=0
DefaultValue=red
[2301sub5]
ParameterName=Map
ObjectType=7
DataType=0x0007
AccessType=rw
PDOMapping=0
DefaultValue=0x00000000
[2301sub6]
ParameterName=Format
ObjectType=7
DataType=0x0005
AccessType=rw
PDOMapping=0
DefaultValue=0
[2301sub7]
ParameterName=Trigger
ObjectType=7
DataType=0x0005
AccessType=rw
PDOMapping=0
DefaultValue=0
[2301sub8]
ParameterName=Threshold
ObjectType=7
DataType=0x0004
AccessType=rw
PDOMapping=0
DefaultValue=0
[2400]
ParameterName=Trace
ObjectType=9
SubNumber=7
[2400sub0]
ParameterName=max sub-index
ObjectType=7
DataType=0x0005
AccessType=ro
PDOMapping=0
DefaultValue=6
[2400sub1]
ParameterName=Size
ObjectType=7
DataType=0x0007
AccessType=rww
PDOMapping=1
DefaultValue=0
[2400sub2]
ParameterName=Value
ObjectType=7
DataType=0x0004
AccessType=ro
PDOMapping=1
DefaultValue=0
[2400sub3]
ParameterName=Min
ObjectType=7
DataType=0x0004
AccessType=rww
PDOMapping=1
DefaultValue=0
[2400sub4]
ParameterName=Max
ObjectType=7
DataType=0x0004
AccessType=rww
PDOMapping=1
DefaultValue=0
[2400sub5]
ParameterName=Plot
ObjectType=7
DataType=0x000F
AccessType=ro
PDOMapping=0
DefaultValue=0
[2400sub6]
ParameterName=Trigger time
ObjectType=7
DataType=0x0007
AccessType=rww
PDOMapping=1
DefaultValue=0
[2401]
ParameterName=Trace
ObjectType=9
SubNumber=7
[2401sub0]
ParameterName=max sub-index
ObjectType=7
DataType=0x0005
AccessType=ro
PDOMapping=0
DefaultValue=6
[2401sub1]
ParameterName=Size
ObjectType=7
DataType=0x0007
AccessType=rww
PDOMapping=1
DefaultValue=0
[2401sub2]
ParameterName=Value
ObjectType=7
DataType=0x0004
AccessType=ro
PDOMapping=1
DefaultValue=0
[2401sub3]
ParameterName=Min
ObjectType=7
DataType=0x0004
AccessType=rww
PDOMapping=1
DefaultValue=0
[2401sub4]
ParameterName=Max
ObjectType=7
DataType=0x0004
AccessType=rww
PDOMapping=1
DefaultValue=0
[2401sub5]
ParameterName=Plot
ObjectType=7
DataType=0x000F
AccessType=ro
PDOMapping=0
DefaultValue=0
[2401sub6]
ParameterName=Trigger time
ObjectType=7
DataType=0x0007
AccessType=rww
PDOMapping=1
DefaultValue=0
[6000]
ParameterName=Read input 8 bit
ObjectType=8
@ -2470,3 +2788,4 @@ AccessType=rww
PDOMapping=1
DefaultValue=0

6593
example/IO.html Normal file

File diff suppressed because it is too large Load Diff

View File

@ -252,7 +252,7 @@
<span id="date" style="margin-left: 2em;">---</span>
<input style="margin-left: 2em; font-size: 1.3em;" value="Open Editor" onclick="openEditor(false);" type="button">
at location:
<input id="locEditor" value="../Object_Dictionary_Editor" onblur="this.setAttribute('value', this.value);" type="text">
<input id="locEditor" value="../../../Object_Dictionary_Editor" onblur="this.setAttribute('value', this.value);" type="text">
<input class="right" style="right: 1em;" value="Help" onclick="openAbout();" type="button">
</div>
<table>
@ -298,7 +298,7 @@
</td>
<td>
<div>
<b>XDD specification file:</b>
<b>Errors and warnings:</b>
<input class="right" value="View source" onclick="viewFile(document.getElementById('textXDD').value, true);" type="button">
</div>
<textarea id="textXDD" fileName="Electronic data sheet.xdd" wrap="off">-</textarea>

View File

@ -56,6 +56,12 @@ Permissible value for TPDO is 0 to 16. For larger value Max Index must be change
<label lang="en">NMT master</label>
<description lang="en">NMT master can send CANopen NMT command, which can start, stop or reset nodes.</description>
</feature>
<feature name="Trace" value="2">
<label lang="en">Trace</label>
<description lang="en">Trace is used for recording variables.</description>
<associatedObject index="2300" indexMax="231F" indexStep="1"/>
<associatedObject index="2400" indexMax="241F" indexStep="1"/>
</feature>
</features>
<CANopenObjectList>
<CANopenObject index="1000" name="Device type" objectType="7" memoryType="ROM" dataType="07" accessType="ro" PDOmapping="no" defaultValue="0x00000000">
@ -761,6 +767,92 @@ main cycle max time - maximum mainline function cycle time in percent of timer p
<CANopenSubObject subIndex="04" name="R64" objectType="7" dataType="11" accessType="rw" PDOmapping="optional" defaultValue="456.789"/>
<CANopenSubObject subIndex="05" name="domain" objectType="7" dataType="0F" accessType="rw" PDOmapping="no" defaultValue="0"/>
</CANopenObject>
<CANopenObject index="2130" name="Time" objectType="9" subNumber="4" memoryType="RAM">
<label lang="en">Time</label>
<description lang="en">Variable displays current time.
String - returns current time as string.
Epoch time ms - Milliseconds since unix epoch (1.1.1970, 00:00:00, UTC). It is calculated as base + offset. Base is calculated on startup of program and offset increments continuously since then. 'Epoch time offset ms' overflows each 49,7 days. 'Epoch time base ms' is recalculated in case of overflow or in case of write to 'Epoch time offset ms'. It is rounded to one minute.</description>
<CANopenSubObject subIndex="00" name="max sub-index" objectType="7" dataType="05" accessType="ro" PDOmapping="no" defaultValue="3"/>
<CANopenSubObject subIndex="01" name="String" objectType="7" dataType="09" accessType="ro" PDOmapping="no" defaultValue="- "/>
<CANopenSubObject subIndex="02" name="Epoch time base ms" objectType="7" dataType="1B" accessType="rw" PDOmapping="no" defaultValue="0"/>
<CANopenSubObject subIndex="03" name="Epoch time offset ms" objectType="7" dataType="07" accessType="rw" PDOmapping="optional" defaultValue="0"/>
</CANopenObject>
<CANopenObject index="2300" name="Trace config" objectType="9" subNumber="9" memoryType="ROM">
<label lang="en">Trace config</label>
<description lang="en">Trace is used for recording variables.
See also Trace.
Size - Maximum number of trace records in circular buffer. By reading it returns actual buffer size (If zero, malloc may had been failed at startup). By writing it sets the size, which will be valid after next reboot. (It is necessary to store parameters before (0x1010).)
Axis no - If value is different than zero, trace is enabled. Value is informative and describes group of traces (Multiple traces can belong to single axis.). By reading it returns zero if trace is disabled or if it is not configured properly. When trace is enabled, internal buffer is cleared.
Name - name of the trace as a string (informative).
Color - color of the trace as a string (informative).
Map - Map to variable in object dictionary. Trace must be disabled, if mapping is written (Axis no set to 0).
Format - If first bit is zero, then value is used as signed integer otherwise as unsigned integer. If format is 0 or 1, text points are generated for time and value: "123;321\n140;345\n..." If format is 2 or 3, binary data is generated: 4-byte timestamp and 4-byte value. If format is 4 or 5, SVG path is generated: "M123,321H140V345...".
Trigger - 0=disabled, 1=rising edge, 2=falling edge, 3=both edges.
Threshold - If integer value passes threshold (based on trigger setting), time is recorded into 'trigger time'.</description>
<CANopenSubObject subIndex="00" name="max sub-index" objectType="7" dataType="05" accessType="ro" PDOmapping="no" defaultValue="8"/>
<CANopenSubObject subIndex="01" name="Size" objectType="7" dataType="07" accessType="rw" PDOmapping="no" defaultValue="100"/>
<CANopenSubObject subIndex="02" name="Axis no" objectType="7" dataType="05" accessType="rw" PDOmapping="no" defaultValue="1"/>
<CANopenSubObject subIndex="03" name="Name" objectType="7" dataType="09" accessType="rw" PDOmapping="no" defaultValue="name "/>
<CANopenSubObject subIndex="04" name="Color" objectType="7" dataType="09" accessType="rw" PDOmapping="no" defaultValue="red "/>
<CANopenSubObject subIndex="05" name="Map" objectType="7" dataType="07" accessType="rw" PDOmapping="no" defaultValue="0x60000108"/>
<CANopenSubObject subIndex="06" name="Format" objectType="7" dataType="05" accessType="rw" PDOmapping="no" defaultValue="1"/>
<CANopenSubObject subIndex="07" name="Trigger" objectType="7" dataType="05" accessType="rw" PDOmapping="no" defaultValue="0"/>
<CANopenSubObject subIndex="08" name="Threshold" objectType="7" dataType="04" accessType="rw" PDOmapping="no" defaultValue="0"/>
</CANopenObject>
<CANopenObject index="2301" name="Trace config" objectType="9" subNumber="9" memoryType="ROM">
<CANopenSubObject subIndex="00" name="max sub-index" objectType="7" dataType="05" accessType="ro" PDOmapping="no" defaultValue="8"/>
<CANopenSubObject subIndex="01" name="Size" objectType="7" dataType="07" accessType="rw" PDOmapping="no" defaultValue="0"/>
<CANopenSubObject subIndex="02" name="Axis no" objectType="7" dataType="05" accessType="rw" PDOmapping="no" defaultValue="0"/>
<CANopenSubObject subIndex="03" name="Name" objectType="7" dataType="09" accessType="rw" PDOmapping="no" defaultValue="name "/>
<CANopenSubObject subIndex="04" name="Color" objectType="7" dataType="09" accessType="rw" PDOmapping="no" defaultValue="red "/>
<CANopenSubObject subIndex="05" name="Map" objectType="7" dataType="07" accessType="rw" PDOmapping="no" defaultValue="0x00000000"/>
<CANopenSubObject subIndex="06" name="Format" objectType="7" dataType="05" accessType="rw" PDOmapping="no" defaultValue="0"/>
<CANopenSubObject subIndex="07" name="Trigger" objectType="7" dataType="05" accessType="rw" PDOmapping="no" defaultValue="0"/>
<CANopenSubObject subIndex="08" name="Threshold" objectType="7" dataType="04" accessType="rw" PDOmapping="no" defaultValue="0"/>
</CANopenObject>
<CANopenObject index="2400" name="Trace" objectType="9" subNumber="7" memoryType="RAM">
<label lang="en">Trace</label>
<description lang="en">Trace is used for recording variables.
See also Trace config.
Size - Number of current records in buffer.
Value - Latest value.
Min, Max - Minimum and maximum value.
Plot - SVG path generated from the values from circular buffer. For time axis is used Time-&gt;'Epoch time offset ms'. When Plot is read, internal buffer is emptied.
Trigger time - If integer value passes 'TraceConfig-&gt;threshold', then time is recorded into this variable.</description>
<CANopenSubObject subIndex="00" name="max sub-index" objectType="7" dataType="05" accessType="ro" PDOmapping="no" defaultValue="6"/>
<CANopenSubObject subIndex="01" name="Size" objectType="7" dataType="07" accessType="rw" PDOmapping="optional" defaultValue="0"/>
<CANopenSubObject subIndex="02" name="Value" objectType="7" dataType="04" accessType="ro" PDOmapping="TPDO" defaultValue="0"/>
<CANopenSubObject subIndex="03" name="Min" objectType="7" dataType="04" accessType="rw" PDOmapping="optional" defaultValue="0"/>
<CANopenSubObject subIndex="04" name="Max" objectType="7" dataType="04" accessType="rw" PDOmapping="optional" defaultValue="0"/>
<CANopenSubObject subIndex="05" name="Plot" objectType="7" dataType="0F" accessType="ro" PDOmapping="no" defaultValue="0"/>
<CANopenSubObject subIndex="06" name="Trigger time" objectType="7" dataType="07" accessType="rw" PDOmapping="optional" defaultValue="0"/>
</CANopenObject>
<CANopenObject index="2401" name="Trace" objectType="9" subNumber="7" memoryType="RAM">
<CANopenSubObject subIndex="00" name="max sub-index" objectType="7" dataType="05" accessType="ro" PDOmapping="no" defaultValue="6"/>
<CANopenSubObject subIndex="01" name="Size" objectType="7" dataType="07" accessType="rw" PDOmapping="optional" defaultValue="0"/>
<CANopenSubObject subIndex="02" name="Value" objectType="7" dataType="04" accessType="ro" PDOmapping="TPDO" defaultValue="0"/>
<CANopenSubObject subIndex="03" name="Min" objectType="7" dataType="04" accessType="rw" PDOmapping="optional" defaultValue="0"/>
<CANopenSubObject subIndex="04" name="Max" objectType="7" dataType="04" accessType="rw" PDOmapping="optional" defaultValue="0"/>
<CANopenSubObject subIndex="05" name="Plot" objectType="7" dataType="0F" accessType="ro" PDOmapping="no" defaultValue="0"/>
<CANopenSubObject subIndex="06" name="Trigger time" objectType="7" dataType="07" accessType="rw" PDOmapping="optional" defaultValue="0"/>
</CANopenObject>
<CANopenObject index="6000" name="Read input 8 bit" objectType="8" subNumber="9" memoryType="RAM" dataType="05" accessType="ro" PDOmapping="optional" TPDOdetectCOS="true">
<label lang="en">Read input 8 bit</label>
<description lang="en">Digital inputs from hardware.</description>
@ -819,7 +911,7 @@ main cycle max time - maximum mainline function cycle time in percent of timer p
</CANopenObject>
</CANopenObjectList>
<other>
<file fileName="IO" fileCreator="-" fileCreationDate="2013-03-09" fileCreationTime="13:23:28" fileVersion="3.0"/>
<file fileName="IO Example" fileCreator="JP" fileCreationDate="2016-03-24" fileCreationTime="21:37:08" fileVersion="-"/>
<DeviceIdentity>
<vendorName>Paternoster</vendorName>
<productName>CANopenNode</productName>
@ -861,4 +953,4 @@ main cycle max time - maximum mainline function cycle time in percent of timer p
<dummy entry="Dummy0007=1"/>
</dummyUsage>
</other>
</device>
</device>

472
stack/CO_trace.c Executable file
View File

@ -0,0 +1,472 @@
/*
* CANopen trace interface.
*
* @file CO_trace.c
* @author Janez Paternoster
* @copyright 2016 Janez Paternoster
*
* This file is part of CANopenSocket, a Linux implementation of CANopen
* stack with master functionality. Project home page is
* <https://github.com/CANopenNode/CANopenSocket>. CANopenSocket is based
* on CANopenNode: <https://github.com/CANopenNode/CANopenNode>.
*
* CANopenSocket is free and open source software: you can redistribute
* it and/or modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CO_trace.h"
#include <stdio.h>
/* Different functions for processing value for different data types. */
static int32_t getValueI8 (void *OD_variable) { return (int32_t) *((int8_t*) OD_variable);}
static int32_t getValueI16(void *OD_variable) { return (int32_t) *((int16_t*) OD_variable);}
static int32_t getValueI32(void *OD_variable) { return *((int32_t*) OD_variable);}
static int32_t getValueU8 (void *OD_variable) { return (int32_t) *((uint8_t*) OD_variable);}
static int32_t getValueU16(void *OD_variable) { return (int32_t) *((uint16_t*) OD_variable);}
static int32_t getValueU32(void *OD_variable) { return *((int32_t*) OD_variable);}
/* Different functions for printing points for different data types. */
static uint32_t printPointCsv(char *s, uint32_t size, uint32_t timeStamp, int32_t value) {
return snprintf(s, size, "%u;%d\n", timeStamp, value);
}
static uint32_t printPointCsvUnsigned(char *s, uint32_t size, uint32_t timeStamp, int32_t value) {
return snprintf(s, size, "%u;%u\n", timeStamp, (uint32_t) value);
}
static uint32_t printPointBinary(char *s, uint32_t size, uint32_t timeStamp, int32_t value) {
if(size < 8) return 0;
CO_memcpySwap4(s, &timeStamp);
CO_memcpySwap4(s+4, &value);
return 8;
}
static uint32_t printPointSvgStart(char *s, uint32_t size, uint32_t timeStamp, int32_t value) {
return snprintf(s, size, "M%u,%d", timeStamp, value);
}
static uint32_t printPointSvgStartUnsigned(char *s, uint32_t size, uint32_t timeStamp, int32_t value) {
return snprintf(s, size, "M%u,%u", timeStamp, (uint32_t) value);
}
static uint32_t printPointSvg(char *s, uint32_t size, uint32_t timeStamp, int32_t value) {
return snprintf(s, size, "H%uV%d", timeStamp, value);
}
static uint32_t printPointSvgUnsigned(char *s, uint32_t size, uint32_t timeStamp, int32_t value) {
return snprintf(s, size, "H%uV%u", timeStamp, (uint32_t) value);
}
/* Collection of function pointers for fast processing based on specific data type. */
/* Rules for the array: There must be groups of six members (I8, I16, I32, U8, U16, U32)
* in correct order and sequence, so findVariable() finds correct member. */
static const CO_trace_dataType_t dataTypes[] = {
{getValueI8, printPointCsv, printPointCsv, printPointCsv},
{getValueI16, printPointCsv, printPointCsv, printPointCsv},
{getValueI32, printPointCsv, printPointCsv, printPointCsv},
{getValueU8, printPointCsvUnsigned, printPointCsvUnsigned, printPointCsvUnsigned},
{getValueU16, printPointCsvUnsigned, printPointCsvUnsigned, printPointCsvUnsigned},
{getValueU32, printPointCsvUnsigned, printPointCsvUnsigned, printPointCsvUnsigned},
{getValueI8, printPointBinary, printPointBinary, printPointBinary},
{getValueI16, printPointBinary, printPointBinary, printPointBinary},
{getValueI32, printPointBinary, printPointBinary, printPointBinary},
{getValueU8, printPointBinary, printPointBinary, printPointBinary},
{getValueU16, printPointBinary, printPointBinary, printPointBinary},
{getValueU32, printPointBinary, printPointBinary, printPointBinary},
{getValueI8, printPointSvgStart, printPointSvg, printPointSvg},
{getValueI16, printPointSvgStart, printPointSvg, printPointSvg},
{getValueI32, printPointSvgStart, printPointSvg, printPointSvg},
{getValueU8, printPointSvgStartUnsigned, printPointSvgUnsigned, printPointSvgUnsigned},
{getValueU16, printPointSvgStartUnsigned, printPointSvgUnsigned, printPointSvgUnsigned},
{getValueU32, printPointSvgStartUnsigned, printPointSvgUnsigned, printPointSvgUnsigned}
};
/* Find variable in Object Dictionary *****************************************/
static void findVariable(CO_trace_t *trace) {
uint16_t index;
uint8_t subIndex;
uint16_t entryNo;
uint16_t len;
trace->dt = NULL;
trace->OD_variable = NULL;
/* get variable from mapping */
index = (uint16_t) ((*trace->map) >> 16);
subIndex = (uint8_t) ((*trace->map) >> 8);
/* find object in Object Dictionary */
entryNo = CO_OD_find(trace->SDO, index);
/* Does object exist in OD? */
if(index >= 0x1000 && entryNo != 0xFFFF && subIndex <= trace->SDO->OD[entryNo].maxSubIndex) {
trace->OD_variable = CO_OD_getDataPointer(trace->SDO, entryNo, subIndex);
if(trace->OD_variable != NULL) {
int dtIndex = 0;
len = CO_OD_getLength(trace->SDO, entryNo, subIndex);
/* Get function pointers for correct data type */
/* first sequence: data length */
switch(len) {
case 1: dtIndex = 0; break;
case 2: dtIndex = 1; break;
case 4: dtIndex = 2; break;
default: trace->OD_variable = NULL; break;
}
/* second sequence: signed or unsigned */
if(((*trace->format) & 1) == 1) {
dtIndex += 3;
}
/* third sequence: Output type */
dtIndex += ((*trace->format) >> 1) * 6;
if(dtIndex < sizeof(dataTypes) / sizeof(CO_trace_dataType_t)) {
trace->dt = &dataTypes[dtIndex];
}
else {
trace->OD_variable = NULL;
}
}
}
}
/* OD function for accessing _OD_traceConfig_ (index 0x2300+) from SDO server.
* For more information see file CO_SDO.h. */
static CO_SDO_abortCode_t CO_ODF_traceConfig(CO_ODF_arg_t *ODF_arg) {
CO_trace_t *trace;
CO_SDO_abortCode_t ret = CO_SDO_AB_NONE;
trace = (CO_trace_t*) ODF_arg->object;
switch(ODF_arg->subIndex) {
case 1: /* size */
if(ODF_arg->reading) {
uint32_t *value = (uint32_t*) ODF_arg->data;
*value = trace->bufferSize;
}
break;
case 2: /* axisNo (trace enabled if nonzero) */
if(ODF_arg->reading) {
uint8_t *value = (uint8_t*) ODF_arg->data;
if(!trace->enabled) {
*value = 0;
}
}
else {
uint8_t *value = (uint8_t*) ODF_arg->data;
if(*value == 0) {
trace->enabled = false;
}
else if(!trace->enabled) {
if(trace->bufferSize == 0) {
ret = CO_SDO_AB_OUT_OF_MEM;
}
else {
/* set trace->OD_variable and trace->dt, based on 'map' and 'format' */
findVariable(trace);
if(trace->OD_variable != NULL) {
*trace->lastValue = 0;
*trace->minValue = 0;
*trace->maxValue = 0;
*trace->triggerTime = 0;
trace->readPtr = 0;
trace->writePtr = 0;
trace->enabled = true;
}
else {
ret = CO_SDO_AB_NO_MAP;
}
}
}
}
break;
case 5: /* map */
case 6: /* format */
if(!ODF_arg->reading) {
if(trace->enabled) {
ret = CO_SDO_AB_INVALID_VALUE;
}
}
break;
}
return ret;
}
/* OD function for accessing _OD_trace_ (index 0x2400+) from SDO server.
* For more information see file CO_SDO.h. */
static CO_SDO_abortCode_t CO_ODF_trace(CO_ODF_arg_t *ODF_arg) {
CO_trace_t *trace;
CO_SDO_abortCode_t ret = CO_SDO_AB_NONE;
trace = (CO_trace_t*) ODF_arg->object;
switch(ODF_arg->subIndex) {
case 1: /* size */
if(ODF_arg->reading) {
uint32_t *value = (uint32_t*) ODF_arg->data;
uint32_t size = trace->bufferSize;
uint32_t wp = trace->writePtr;
uint32_t rp = trace->readPtr;
if(wp >= rp) {
*value = wp - rp;
}
else {
*value = size - rp + wp;
}
}
else {
uint32_t *value = (uint32_t*) ODF_arg->data;
if(*value == 0) {
/* clear buffer, handle race conditions */
while(trace->readPtr != 0 || trace->writePtr != 0) {
trace->readPtr = 0;
trace->writePtr = 0;
*trace->triggerTime = 0;
}
}
else {
ret = CO_SDO_AB_INVALID_VALUE;
}
}
break;
case 5: /* plot */
if(ODF_arg->reading) {
/* This plot will be transmitted as domain data type. String data
* will be printed directly to SDO buffer. If there is more data
* to print, than is the size of SDO buffer, then this function
* will be called multiple times until internal trace buffer is
* empty. Internal trace buffer is circular buffer. It is accessed
* by this function and by higher priority thread. If this buffer
* is full, there is a danger for race condition. First records
* from trace buffer may be overwritten somewhere between. If this
* is detected, then do{}while() loop tries printing again. */
if(trace->bufferSize == 0 || ODF_arg->dataLength < 100) {
ret = CO_SDO_AB_OUT_OF_MEM;
}
else if(trace->readPtr == trace->writePtr) {
ret = CO_SDO_AB_NO_DATA;
}
else {
uint32_t rp, t, v, len, freeLen;
char *s;
bool_t readPtrOverflowed; /* for handling race conditions */
/* repeat everything, if trace->readPtr was overflowed in CO_trace_process */
do {
readPtrOverflowed = false;
s = (char*) ODF_arg->data;
freeLen = ODF_arg->dataLength;
rp = trace->readPtr;
/* start plot, increment variables, verify overflow */
if(ODF_arg->firstSegment) {
t = trace->timeBuffer[rp];
v = trace->valueBuffer[rp];
rp ++;
if(++trace->readPtr == trace->bufferSize) {
trace->readPtr = 0;
if(rp != trace->bufferSize) {
readPtrOverflowed = true;
continue;
}
rp = 0;
}
if(rp != trace->readPtr) {
readPtrOverflowed = true;
continue;
}
len = trace->dt->printPointStart(s, freeLen, t, v);
s += len;
freeLen -= len;
}
/* print other points */
if(rp != trace->writePtr) {
for(;;) {
t = trace->timeBuffer[rp];
v = trace->valueBuffer[rp];
rp ++;
if(++trace->readPtr == trace->bufferSize) {
trace->readPtr = 0;
if(rp != trace->bufferSize && ODF_arg->firstSegment) {
readPtrOverflowed = true;
break;
}
rp = 0;
}
if(rp != trace->readPtr && ODF_arg->firstSegment) {
readPtrOverflowed = true;
break;
}
/* If internal buffer is empty, end transfer */
if(rp == trace->writePtr) {
/* If there is last time stamp, point will be printed at the end */
if(t != trace->lastTimeStamp) {
len = trace->dt->printPoint(s, freeLen, t, v);
s += len;
freeLen -= len;
}
ODF_arg->lastSegment = true;
break;
}
len = trace->dt->printPoint(s, freeLen, t, v);
s += len;
freeLen -= len;
/* if output buffer is full, next data will be sent later */
if(freeLen < 50) {
ODF_arg->lastSegment = false;
break;
}
}
}
/* print last point */
if(!readPtrOverflowed && ODF_arg->lastSegment) {
v = *trace->lastValue;
t = trace->lastTimeStamp;
len = trace->dt->printPointEnd(s, freeLen, t, v);
s += len;
freeLen -= len;
}
} while(readPtrOverflowed);
ODF_arg->dataLength -= freeLen;
}
}
break;
}
return ret;
}
/******************************************************************************/
void CO_trace_init(
CO_trace_t *trace,
CO_SDO_t *SDO,
uint8_t enabled,
uint32_t *timeBuffer,
int32_t *valueBuffer,
uint32_t bufferSize,
uint32_t *map,
uint8_t *format,
uint8_t *trigger,
int32_t *threshold,
int32_t *lastValue,
int32_t *minValue,
int32_t *maxValue,
uint32_t *triggerTime,
uint16_t idx_OD_traceConfig,
uint16_t idx_OD_trace)
{
trace->SDO = SDO;
trace->enabled = (enabled != 0) ? true : false;
trace->timeBuffer = timeBuffer;
trace->valueBuffer = valueBuffer;
trace->bufferSize = bufferSize;
trace->writePtr = 0;
trace->readPtr = 0;
trace->lastTimeStamp = 0;
trace->map = map;
trace->format = format;
trace->trigger = trigger;
trace->threshold = threshold;
trace->lastValue = lastValue;
trace->minValue = minValue;
trace->maxValue = maxValue;
trace->triggerTime = triggerTime;
*trace->lastValue = 0;
*trace->minValue = 0;
*trace->maxValue = 0;
*trace->triggerTime = 0;
/* set trace->OD_variable and trace->dt, based on 'map' and 'format' */
findVariable(trace);
if(timeBuffer == NULL || valueBuffer == NULL) {
trace->bufferSize = 0;
}
if( trace->bufferSize == 0 || trace->OD_variable == NULL) {
trace->enabled = false;
}
CO_OD_configure(SDO, idx_OD_traceConfig, CO_ODF_traceConfig, (void*)trace, 0, 0);
CO_OD_configure(SDO, idx_OD_trace, CO_ODF_trace, (void*)trace, 0, 0);
}
/******************************************************************************/
void CO_trace_process(CO_trace_t *trace, uint32_t timestamp) {
if(trace->enabled) {
int32_t val;
val = trace->dt->pGetValue(trace->OD_variable);
if(*trace->lastValue != val) {
/* Verify, if value passed threshold */
if((*trace->trigger & 1) != 0 && *trace->lastValue < *trace->threshold && val >= *trace->threshold) {
*trace->triggerTime = timestamp;
}
if((*trace->trigger & 2) != 0 && *trace->lastValue < *trace->threshold && val >= *trace->threshold) {
*trace->triggerTime = timestamp;
}
/* Write value and verify min/max */
*trace->lastValue = val;
if(*trace->minValue > val) {
*trace->minValue = val;
}
if(*trace->maxValue < val) {
*trace->maxValue = val;
}
/* write buffers and update pointers */
trace->timeBuffer[trace->writePtr] = timestamp;
trace->valueBuffer[trace->writePtr] = val;
if(++trace->writePtr == trace->bufferSize) {
trace->writePtr = 0;
}
if(trace->writePtr == trace->readPtr) {
if(++trace->readPtr == trace->bufferSize) {
trace->readPtr = 0;
}
}
}
else {
/* if buffer is empty, make first record */
if(trace->writePtr == trace->readPtr) {
/* write buffers and update pointers */
trace->timeBuffer[trace->writePtr] = timestamp;
trace->valueBuffer[trace->writePtr] = val;
if(++trace->writePtr == trace->bufferSize) {
trace->writePtr = 0;
}
}
}
trace->lastTimeStamp = timestamp;
}
}

159
stack/CO_trace.h Executable file
View File

@ -0,0 +1,159 @@
/**
* CANopen trace interface.
*
* @file CO_trace.h
* @author Janez Paternoster
* @copyright 2016 Janez Paternoster
*
* This file is part of CANopenSocket, a Linux implementation of CANopen
* stack with master functionality. Project home page is
* <https://github.com/CANopenNode/CANopenSocket>. CANopenSocket is based
* on CANopenNode: <https://github.com/CANopenNode/CANopenNode>.
*
* CANopenSocket is free and open source software: you can redistribute
* it and/or modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CO_TRACE_H
#define CO_TRACE_H
#include "CO_driver.h"
#include "CO_SDO.h"
#ifndef OD_INDEX_TRACE_CONFIG
#define OD_INDEX_TRACE_CONFIG 0x2300
#define OD_INDEX_TRACE 0x2400
#endif
/**
* Topic: Trace
*
* CANopen trace for recording variables over time.
*
* In embedded systems there is often a need to monitor some variables over time.
* Results are then displayed on graph, similar as in oscilloscope.
*
* CANopen trace is a configurable object, accessible via CANopen Object
* Dictionary, which records chosen variable over time. It generates a curve,
* which can be read via SDO and can then be displayed in a graph.
*
* CO_trace_process() runs in 1 ms intervals and monitors one variable. If it
* changes, it makes a record with timestamp into circular buffer. When trace is
* accessed by CANopen SDO object, it reads latest points from the the circular
* buffer, prints a SVG curve into string and sends it as a SDO response. If a
* SDO request was received from the same device, then no traffic occupies CAN
* network.
*/
/**
* structure for reading variables and printing points for specific data type.
*/
typedef struct {
/** Function pointer for getting the value from OD variable. **/
int32_t (*pGetValue) (void *OD_variable);
/** Function pointer for printing the start point to trace.plot */
uint32_t (*printPointStart)(char *s, uint32_t size, uint32_t timeStamp, int32_t value);
/** Function pointer for printing the point to trace.plot */
uint32_t (*printPoint)(char *s, uint32_t size, uint32_t timeStamp, int32_t value);
/** Function pointer for printing the end point to trace.plot */
uint32_t (*printPointEnd)(char *s, uint32_t size, uint32_t timeStamp, int32_t value);
} CO_trace_dataType_t;
/**
* Trace object.
*/
typedef struct {
bool_t enabled; /**< True, if trace is enabled. */
CO_SDO_t *SDO; /**< From CO_trace_init(). */
uint32_t *timeBuffer; /**< From CO_trace_init(). */
int32_t *valueBuffer; /**< From CO_trace_init(). */
uint32_t bufferSize; /**< From CO_trace_init(). */
volatile uint32_t writePtr; /**< Location in buffer, which will be next written. */
volatile uint32_t readPtr; /**< Location in buffer, which will be next read. */
uint32_t lastTimeStamp; /**< Last time stamp. If zero, then last point contains last timestamp. */
void *OD_variable; /**< Pointer to variable, which is monitored */
const CO_trace_dataType_t *dt; /**< Data type specific function pointers. **/
uint32_t *map; /**< From CO_trace_init(). */
uint8_t *format; /**< From CO_trace_init(). */
int32_t *lastValue; /**< From CO_trace_init(). */
int32_t *minValue; /**< From CO_trace_init(). */
int32_t *maxValue; /**< From CO_trace_init(). */
uint32_t *triggerTime; /**< From CO_trace_init(). */
uint8_t *trigger; /**< From CO_trace_init(). */
int32_t *threshold; /**< From CO_trace_init(). */
} CO_trace_t;
/**
* Initialize trace object.
*
* Function must be called in the communication reset section.
*
* @param trace This object will be initialized.
* @param SDO SDO server object.
* @param enabled Is trace enabled.
* @param timeBuffer Memory block for storing time records.
* @param valueBuffer Memory block for storing value records.
* @param bufferSize Size of the above buffers.
* @param map Map to variable in Object Dictionary, which will be monitored. Same structure as in PDO.
* @param Format Format of the plot. If first bit is 1, above variable is unsigned. For more info see Object Dictionary.
* @param trigger If different than zero, trigger time is recorded, when variable goes through threshold.
* @param threshold Used with trigger.
* @param lastValue Pointer to variable, which will show last value of the variable.
* @param minValue Pointer to variable, which will show minimum value of the variable.
* @param maxValue Pointer to variable, which will show maximum value of the variable.
* @param triggerTime Pointer to variable, which will show last trigger time of the variable.
* @param idx_OD_traceConfig Index in Object Dictionary.
* @param idx_OD_trace Index in Object Dictionary.
*
* @return 0 on success, -1 on error.
*/
void CO_trace_init(
CO_trace_t *trace,
CO_SDO_t *SDO,
uint8_t enabled,
uint32_t *timeBuffer,
int32_t *valueBuffer,
uint32_t bufferSize,
uint32_t *map,
uint8_t *format,
uint8_t *trigger,
int32_t *threshold,
int32_t *lastValue,
int32_t *minValue,
int32_t *maxValue,
uint32_t *triggerTime,
uint16_t idx_OD_traceConfig,
uint16_t idx_OD_trace);
/**
* Process trace object.
*
* Function must be called cyclically in 1ms intervals.
*
* @param trace This object.
* @param timestamp Timestamp (usually in millisecond resolution).
*
* @return 0 on success, -1 on error.
*/
void CO_trace_process(CO_trace_t *trace, uint32_t timestamp);
#endif

View File

@ -311,6 +311,7 @@ void __ISR(_TIMER_2_VECTOR, IPL3SOFT) CO_TimerInterruptHandler(void){
if(CO->CANmodule[0]->CANnormal) {
bool_t syncWas;
int i;
/* Process Sync and read inputs */
syncWas = CO_process_SYNC_RPDO(CO, 1000);
@ -322,6 +323,12 @@ void __ISR(_TIMER_2_VECTOR, IPL3SOFT) CO_TimerInterruptHandler(void){
#endif
/* Further I/O or nonblocking application code may go here. */
#if CO_NO_TRACE > 0
OD_time.epochTimeOffsetMs++;
for(i=0; i<CO_NO_TRACE; i++) {
CO_trace_process(CO->trace[i], *CO_time.epochTimeOffsetMs);
}
#endif
program1ms();
/* Write outputs */