Object dictionary interface, preview updated.
This commit is contained in:
parent
9446aa5792
commit
6cdc70fb4e
4 changed files with 946 additions and 861 deletions
|
|
@ -27,10 +27,9 @@
|
|||
#include "301/CO_ODinterface.h"
|
||||
|
||||
|
||||
/* Read value from variable from Object Dictionary, see OD_subEntry_t *********/
|
||||
static OD_size_t OD_readDirect(OD_stream_t *stream, uint8_t subIndex,
|
||||
void *buf, OD_size_t count,
|
||||
ODR_t *returnCode)
|
||||
/******************************************************************************/
|
||||
OD_size_t OD_readOriginal(OD_stream_t *stream, uint8_t subIndex,
|
||||
void *buf, OD_size_t count, ODR_t *returnCode)
|
||||
{
|
||||
(void) subIndex;
|
||||
|
||||
|
|
@ -40,7 +39,7 @@ static OD_size_t OD_readDirect(OD_stream_t *stream, uint8_t subIndex,
|
|||
}
|
||||
|
||||
OD_size_t dataLenToCopy = stream->dataLength; /* length of OD variable */
|
||||
const char *odData = (const char *)stream->dataObject;
|
||||
const char *odData = (const char *)stream->dataObjectOriginal;
|
||||
|
||||
if (odData == NULL) {
|
||||
*returnCode = ODR_SUB_NOT_EXIST;
|
||||
|
|
@ -56,7 +55,7 @@ static OD_size_t OD_readDirect(OD_stream_t *stream, uint8_t subIndex,
|
|||
*returnCode = ODR_DEV_INCOMPAT;
|
||||
return 0;
|
||||
}
|
||||
/* reduce for allready copied data */
|
||||
/* reduce for already copied data */
|
||||
dataLenToCopy -= stream->dataOffset;
|
||||
odData += stream->dataOffset;
|
||||
|
||||
|
|
@ -77,10 +76,9 @@ static OD_size_t OD_readDirect(OD_stream_t *stream, uint8_t subIndex,
|
|||
return dataLenToCopy;
|
||||
}
|
||||
|
||||
/* Write value to variable from Object Dictionary, see OD_subEntry_t **********/
|
||||
static OD_size_t OD_writeDirect(OD_stream_t *stream, uint8_t subIndex,
|
||||
const void *buf, OD_size_t count,
|
||||
ODR_t *returnCode)
|
||||
/******************************************************************************/
|
||||
OD_size_t OD_writeOriginal(OD_stream_t *stream, uint8_t subIndex,
|
||||
const void *buf, OD_size_t count, ODR_t *returnCode)
|
||||
{
|
||||
(void) subIndex;
|
||||
|
||||
|
|
@ -90,7 +88,7 @@ static OD_size_t OD_writeDirect(OD_stream_t *stream, uint8_t subIndex,
|
|||
}
|
||||
|
||||
OD_size_t dataLenToCopy = stream->dataLength; /* length of OD variable */
|
||||
char *odData = (char *)stream->dataObject;
|
||||
char *odData = (char *)stream->dataObjectOriginal;
|
||||
|
||||
if (odData == NULL) {
|
||||
*returnCode = ODR_SUB_NOT_EXIST;
|
||||
|
|
@ -106,7 +104,7 @@ static OD_size_t OD_writeDirect(OD_stream_t *stream, uint8_t subIndex,
|
|||
*returnCode = ODR_DEV_INCOMPAT;
|
||||
return 0;
|
||||
}
|
||||
/* reduce for allready copied data */
|
||||
/* reduce for already copied data */
|
||||
dataLenToCopy -= stream->dataOffset;
|
||||
odData += stream->dataOffset;
|
||||
|
||||
|
|
@ -135,10 +133,10 @@ static OD_size_t OD_writeDirect(OD_stream_t *stream, uint8_t subIndex,
|
|||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
OD_size_t OD_readDisabled(OD_stream_t *stream, uint8_t subIndex,
|
||||
void *buf, OD_size_t count,
|
||||
ODR_t *returnCode)
|
||||
/* Read value from variable from Object Dictionary disabled, see OD_subEntry_t*/
|
||||
static OD_size_t OD_readDisabled(OD_stream_t *stream, uint8_t subIndex,
|
||||
void *buf, OD_size_t count,
|
||||
ODR_t *returnCode)
|
||||
{
|
||||
(void) stream; (void) subIndex; (void) buf; (void) count;
|
||||
|
||||
|
|
@ -146,9 +144,10 @@ OD_size_t OD_readDisabled(OD_stream_t *stream, uint8_t subIndex,
|
|||
return 0;
|
||||
}
|
||||
|
||||
OD_size_t OD_writeDisabled(OD_stream_t *stream, uint8_t subIndex,
|
||||
void *buf, OD_size_t count,
|
||||
ODR_t *returnCode)
|
||||
/* Write value to variable from Object Dictionary disabled, see OD_subEntry_t */
|
||||
static OD_size_t OD_writeDisabled(OD_stream_t *stream, uint8_t subIndex,
|
||||
const void *buf, OD_size_t count,
|
||||
ODR_t *returnCode)
|
||||
{
|
||||
(void) stream; (void) subIndex; (void) buf; (void) count;
|
||||
|
||||
|
|
@ -156,6 +155,7 @@ OD_size_t OD_writeDisabled(OD_stream_t *stream, uint8_t subIndex,
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
const OD_entry_t *OD_find(const OD_t *od, uint16_t index) {
|
||||
unsigned int cur;
|
||||
|
|
@ -198,89 +198,83 @@ const OD_entry_t *OD_find(const OD_t *od, uint16_t index) {
|
|||
|
||||
/******************************************************************************/
|
||||
ODR_t OD_getSub(const OD_entry_t *entry, uint8_t subIndex,
|
||||
OD_subEntry_t *subEntry, OD_stream_t *stream)
|
||||
OD_subEntry_t *subEntry, OD_stream_t *stream, bool_t odOrig)
|
||||
{
|
||||
if (entry == NULL) return ODR_IDX_NOT_EXIST;
|
||||
if (entry == NULL || entry->odObject == NULL) return ODR_IDX_NOT_EXIST;
|
||||
else if (subIndex > entry->maxSubIndex) return ODR_SUB_NOT_EXIST;
|
||||
else if (subEntry == NULL || stream == NULL) return ODR_DEV_INCOMPAT;
|
||||
|
||||
const void *odObject = entry->odObject;
|
||||
bool_t io_configured = false;
|
||||
const void *odObjectOrig = entry->odObject;
|
||||
const OD_obj_extended_t *odObjectExt = NULL;
|
||||
uint8_t odBasicType = entry->odObjectType & ODT_TYPE_MASK;
|
||||
|
||||
if (odObject == NULL) return ODR_DEV_INCOMPAT;
|
||||
/* Is object type extended? */
|
||||
if ((entry->odObjectType & ODT_EXTENSION_MASK) != 0) {
|
||||
odObjectExt = (const OD_obj_extended_t *)odObjectOrig;
|
||||
odObjectOrig = odObjectExt->odObjectOriginal;
|
||||
if (odObjectOrig == NULL) return ODR_DEV_INCOMPAT;
|
||||
}
|
||||
|
||||
/* common properties */
|
||||
subEntry->index = entry->index;
|
||||
subEntry->subIndex = subIndex;
|
||||
subEntry->maxSubIndex = entry->maxSubIndex;
|
||||
subEntry->storageGroup = entry->storageGroup;
|
||||
subEntry->flagsPDO = (odObjectExt != NULL ) ? odObjectExt->flagsPDO : NULL;
|
||||
stream->dataOffset = 0;
|
||||
|
||||
/* Object type is extended */
|
||||
if ((entry->odObjectType & ODT_EXTENSION_MASK) != 0) {
|
||||
const OD_obj_extended_t *odoe = (const OD_obj_extended_t *)odObject;
|
||||
|
||||
if (odoe->extIO != NULL && odoe->extIO->object != NULL
|
||||
&& odoe->extIO->read != NULL && odoe->extIO->write != NULL)
|
||||
{
|
||||
subEntry->read = odoe->extIO->read;
|
||||
subEntry->write = odoe->extIO->write;
|
||||
stream->dataObject = odoe->extIO->object;
|
||||
io_configured = true;
|
||||
}
|
||||
subEntry->flagsPDO = odoe->flagsPDO;
|
||||
odObject = odoe->odObjectOriginal;
|
||||
if (odObject == NULL) return ODR_DEV_INCOMPAT;
|
||||
}
|
||||
else {
|
||||
subEntry->flagsPDO = NULL;
|
||||
}
|
||||
|
||||
if (!io_configured) {
|
||||
subEntry->read = OD_readDirect;
|
||||
subEntry->write = OD_writeDirect;
|
||||
}
|
||||
|
||||
/* attribute, dataObjectOriginal and dataLength, depends on object type */
|
||||
if (odBasicType == ODT_VAR) {
|
||||
const OD_obj_var_t *odo = (const OD_obj_var_t *)odObject;
|
||||
const OD_obj_var_t *odo = (const OD_obj_var_t *)odObjectOrig;
|
||||
|
||||
subEntry->attribute = odo->attribute;
|
||||
if (!io_configured) stream->dataObject = odo->data;
|
||||
stream->dataObjectOriginal = odo->data;
|
||||
stream->dataLength = odo->dataLength;
|
||||
}
|
||||
else if (odBasicType == ODT_ARR) {
|
||||
const OD_obj_array_t *odo = (const OD_obj_array_t *)odObject;
|
||||
const OD_obj_array_t *odo = (const OD_obj_array_t *)odObjectOrig;
|
||||
|
||||
if (subIndex == 0) {
|
||||
subEntry->attribute = odo->attribute0;
|
||||
if (!io_configured) stream->dataObject = odo->data0;
|
||||
stream->dataObjectOriginal = odo->data0;
|
||||
stream->dataLength = 1;
|
||||
}
|
||||
else {
|
||||
char *data = (char *)odo->data;
|
||||
int i = subIndex - 1;
|
||||
|
||||
subEntry->attribute = odo->attribute;
|
||||
if (!io_configured) {
|
||||
int i = subIndex - 1;
|
||||
if (data == NULL) return ODR_DEV_INCOMPAT;
|
||||
stream->dataObject = data + odo->dataElementSizeof * i;
|
||||
}
|
||||
if (data == NULL) return ODR_DEV_INCOMPAT;
|
||||
stream->dataObjectOriginal = data + odo->dataElementSizeof * i;
|
||||
stream->dataLength = odo->dataElementLength;
|
||||
}
|
||||
}
|
||||
else if (odBasicType == ODT_REC) {
|
||||
const OD_obj_var_t *odo_rec = (const OD_obj_var_t *)odObject;
|
||||
const OD_obj_var_t *odo_rec = (const OD_obj_var_t *)odObjectOrig;
|
||||
const OD_obj_var_t *odo = &odo_rec[subIndex];
|
||||
|
||||
subEntry->attribute = odo->attribute;
|
||||
if (!io_configured) stream->dataObject = odo->data;
|
||||
stream->dataObjectOriginal = odo->data;
|
||||
stream->dataLength = odo->dataLength;
|
||||
}
|
||||
else {
|
||||
return ODR_DEV_INCOMPAT;
|
||||
}
|
||||
|
||||
/* read, write and dataObject, direct or with IO extension */
|
||||
if (odObjectExt == NULL || odObjectExt->extIO == NULL || odOrig) {
|
||||
subEntry->read = OD_readOriginal;
|
||||
subEntry->write = OD_writeOriginal;
|
||||
stream->object = NULL;
|
||||
}
|
||||
else {
|
||||
subEntry->read = odObjectExt->extIO->read != NULL ?
|
||||
odObjectExt->extIO->read : OD_readDisabled;
|
||||
subEntry->write = odObjectExt->extIO->write != NULL ?
|
||||
odObjectExt->extIO->write : OD_writeDisabled;
|
||||
stream->object = odObjectExt->extIO->object;
|
||||
}
|
||||
|
||||
return ODR_OK;
|
||||
}
|
||||
|
||||
|
|
@ -339,6 +333,10 @@ bool_t OD_extensionIO_init(const OD_entry_t *entry,
|
|||
|
||||
const OD_obj_extended_t *odo = (const OD_obj_extended_t *) entry->odObject;
|
||||
|
||||
if (odo->extIO == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
odo->extIO->object = object;
|
||||
odo->extIO->read = read;
|
||||
odo->extIO->write = write;
|
||||
|
|
@ -346,179 +344,112 @@ bool_t OD_extensionIO_init(const OD_entry_t *entry,
|
|||
return true;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
void OD_updateStorageGroup(OD_t *od, uint8_t storageGroup) {
|
||||
if (od == NULL) return;
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; i < od->size; i++) {
|
||||
const OD_entry_t *entry = &od->list[i];
|
||||
const OD_obj_extended_t *odoe;
|
||||
uint8_t subIndex;
|
||||
uint8_t odBasicType;
|
||||
const void *orig;
|
||||
|
||||
/* skip non-matched storage group and non-extended OD objects */
|
||||
if (entry->storageGroup != storageGroup
|
||||
|| (entry->odObjectType & ODT_EXTENSION_MASK) != 0)
|
||||
continue;
|
||||
|
||||
/* skip if extIO read or original object is not specified */
|
||||
odoe = (const OD_obj_extended_t *)entry->odObject;
|
||||
orig = odoe->odObjectOriginal;
|
||||
if (odoe->extIO == NULL || orig == NULL
|
||||
|| odoe->extIO->object == NULL || odoe->extIO->read == NULL)
|
||||
continue;
|
||||
|
||||
/* update each sub-index separately */
|
||||
odBasicType = entry->odObjectType & ODT_TYPE_MASK;
|
||||
for (subIndex = 0; subIndex < entry->maxSubIndex; subIndex++) {
|
||||
OD_attr_t attr;
|
||||
void *dataObject = NULL;
|
||||
OD_size_t dataLength, dataReadTotal;
|
||||
|
||||
if (odBasicType == ODT_VAR) {
|
||||
const OD_obj_var_t *odo = (const OD_obj_var_t *)orig;
|
||||
|
||||
attr = odo->attribute;
|
||||
dataObject = odo->data;
|
||||
dataLength = odo->dataLength;
|
||||
}
|
||||
else if (odBasicType == ODT_ARR) {
|
||||
const OD_obj_array_t *odo = (const OD_obj_array_t *)orig;
|
||||
|
||||
if (subIndex == 0) {
|
||||
attr = odo->attribute0;
|
||||
dataObject = odo->data0;
|
||||
dataLength = 1;
|
||||
}
|
||||
else {
|
||||
char *data = (char *)odo->data;
|
||||
if (data != NULL) {
|
||||
int i = subIndex - 1;
|
||||
attr = odo->attribute;
|
||||
dataObject = data + odo->dataElementSizeof * i;
|
||||
dataLength = odo->dataElementLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (odBasicType == ODT_REC) {
|
||||
const OD_obj_var_t *odo_rec = (const OD_obj_var_t *)orig;
|
||||
const OD_obj_var_t *odo = &odo_rec[subIndex];
|
||||
|
||||
attr = odo->attribute;
|
||||
dataObject = odo->data;
|
||||
dataLength = odo->dataLength;
|
||||
}
|
||||
|
||||
if (dataObject == NULL || (attr&ODA_NOINIT) != 0 || dataLength == 0)
|
||||
continue;
|
||||
|
||||
/* copy data to group structure, several times if data is large */
|
||||
dataReadTotal = 0;
|
||||
do {
|
||||
char buf[32];
|
||||
ODR_t returnCode;
|
||||
char *dest = (char *)dataObject + dataReadTotal;
|
||||
OD_size_t dataRead = odoe->extIO->read(odoe->extIO->object,
|
||||
subIndex,
|
||||
buf, sizeof(buf),
|
||||
&returnCode);
|
||||
dataReadTotal += dataRead;
|
||||
if (dataRead == 0 || dataReadTotal > dataLength) break;
|
||||
memcpy(dest, buf, dataRead);
|
||||
} while (dataReadTotal == dataLength);
|
||||
} /* for (subIndex) */
|
||||
} /* for (entry) */
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
ODR_t OD_get_i8(const OD_entry_t *entry, uint16_t subIndex, int8_t *val) {
|
||||
ODR_t OD_get_i8(const OD_entry_t *entry, uint8_t subIndex,
|
||||
int8_t *val, bool_t odOrig)
|
||||
{
|
||||
OD_subEntry_t subEntry; OD_stream_t st;
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st);
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig);
|
||||
|
||||
if (ret == ODR_OK && st.dataLength != sizeof(*val)) ret = ODR_TYPE_MISMATCH;
|
||||
if (ret == ODR_OK) subEntry.read(&st, subIndex, val, sizeof(*val), &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ODR_t OD_get_i16(const OD_entry_t *entry, uint16_t subIndex, int16_t *val) {
|
||||
ODR_t OD_get_i16(const OD_entry_t *entry, uint8_t subIndex,
|
||||
int16_t *val, bool_t odOrig)
|
||||
{
|
||||
OD_subEntry_t subEntry; OD_stream_t st;
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st);
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig);
|
||||
|
||||
if (ret == ODR_OK && st.dataLength != sizeof(*val)) ret = ODR_TYPE_MISMATCH;
|
||||
if (ret == ODR_OK) subEntry.read(&st, subIndex, val, sizeof(*val), &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ODR_t OD_get_i32(const OD_entry_t *entry, uint16_t subIndex, int32_t *val) {
|
||||
ODR_t OD_get_i32(const OD_entry_t *entry, uint8_t subIndex,
|
||||
int32_t *val, bool_t odOrig)
|
||||
{
|
||||
OD_subEntry_t subEntry; OD_stream_t st;
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st);
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig);
|
||||
|
||||
if (ret == ODR_OK && st.dataLength != sizeof(*val)) ret = ODR_TYPE_MISMATCH;
|
||||
if (ret == ODR_OK) subEntry.read(&st, subIndex, val, sizeof(*val), &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ODR_t OD_get_i64(const OD_entry_t *entry, uint16_t subIndex, int64_t *val) {
|
||||
ODR_t OD_get_i64(const OD_entry_t *entry, uint8_t subIndex,
|
||||
int64_t *val, bool_t odOrig)
|
||||
{
|
||||
OD_subEntry_t subEntry; OD_stream_t st;
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st);
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig);
|
||||
|
||||
if (ret == ODR_OK && st.dataLength != sizeof(*val)) ret = ODR_TYPE_MISMATCH;
|
||||
if (ret == ODR_OK) subEntry.read(&st, subIndex, val, sizeof(*val), &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ODR_t OD_get_u8(const OD_entry_t *entry, uint16_t subIndex, uint8_t *val) {
|
||||
ODR_t OD_get_u8(const OD_entry_t *entry, uint8_t subIndex,
|
||||
uint8_t *val, bool_t odOrig)
|
||||
{
|
||||
OD_subEntry_t subEntry; OD_stream_t st;
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st);
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig);
|
||||
|
||||
if (ret == ODR_OK && st.dataLength != sizeof(*val)) ret = ODR_TYPE_MISMATCH;
|
||||
if (ret == ODR_OK) subEntry.read(&st, subIndex, val, sizeof(*val), &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ODR_t OD_get_u16(const OD_entry_t *entry, uint16_t subIndex, uint16_t *val) {
|
||||
ODR_t OD_get_u16(const OD_entry_t *entry, uint8_t subIndex,
|
||||
uint16_t *val, bool_t odOrig)
|
||||
{
|
||||
OD_subEntry_t subEntry; OD_stream_t st;
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st);
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig);
|
||||
|
||||
if (ret == ODR_OK && st.dataLength != sizeof(*val)) ret = ODR_TYPE_MISMATCH;
|
||||
if (ret == ODR_OK) subEntry.read(&st, subIndex, val, sizeof(*val), &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ODR_t OD_get_u32(const OD_entry_t *entry, uint16_t subIndex, uint32_t *val) {
|
||||
ODR_t OD_get_u32(const OD_entry_t *entry, uint8_t subIndex,
|
||||
uint32_t *val, bool_t odOrig)
|
||||
{
|
||||
OD_subEntry_t subEntry; OD_stream_t st;
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st);
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig);
|
||||
|
||||
if (ret == ODR_OK && st.dataLength != sizeof(*val)) ret = ODR_TYPE_MISMATCH;
|
||||
if (ret == ODR_OK) subEntry.read(&st, subIndex, val, sizeof(*val), &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ODR_t OD_get_u64(const OD_entry_t *entry, uint16_t subIndex, uint64_t *val) {
|
||||
ODR_t OD_get_u64(const OD_entry_t *entry, uint8_t subIndex,
|
||||
uint64_t *val, bool_t odOrig)
|
||||
{
|
||||
OD_subEntry_t subEntry; OD_stream_t st;
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st);
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig);
|
||||
|
||||
if (ret == ODR_OK && st.dataLength != sizeof(*val)) ret = ODR_TYPE_MISMATCH;
|
||||
if (ret == ODR_OK) subEntry.read(&st, subIndex, val, sizeof(*val), &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ODR_t OD_get_r32(const OD_entry_t *entry, uint16_t subIndex, float32_t *val){
|
||||
ODR_t OD_get_r32(const OD_entry_t *entry, uint8_t subIndex,
|
||||
float32_t *val, bool_t odOrig)
|
||||
{
|
||||
OD_subEntry_t subEntry; OD_stream_t st;
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st);
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig);
|
||||
|
||||
if (ret == ODR_OK && st.dataLength != sizeof(*val)) ret = ODR_TYPE_MISMATCH;
|
||||
if (ret == ODR_OK) subEntry.read(&st, subIndex, val, sizeof(*val), &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ODR_t OD_get_r64(const OD_entry_t *entry, uint16_t subIndex, float64_t *val){
|
||||
ODR_t OD_get_r64(const OD_entry_t *entry, uint8_t subIndex,
|
||||
float64_t *val, bool_t odOrig)
|
||||
{
|
||||
OD_subEntry_t subEntry; OD_stream_t st;
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st);
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig);
|
||||
|
||||
if (ret == ODR_OK && st.dataLength != sizeof(*val)) ret = ODR_TYPE_MISMATCH;
|
||||
if (ret == ODR_OK) subEntry.read(&st, subIndex, val, sizeof(*val), &ret);
|
||||
|
|
@ -526,92 +457,213 @@ ODR_t OD_get_r64(const OD_entry_t *entry, uint16_t subIndex, float64_t *val){
|
|||
}
|
||||
|
||||
/******************************************************************************/
|
||||
ODR_t OD_set_i8(const OD_entry_t *entry, uint16_t subIndex, int8_t val) {
|
||||
ODR_t OD_set_i8(const OD_entry_t *entry, uint8_t subIndex,
|
||||
int8_t val, bool_t odOrig)
|
||||
{
|
||||
OD_subEntry_t subEntry; OD_stream_t st;
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st);
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig);
|
||||
|
||||
if (ret == ODR_OK && st.dataLength != sizeof(val)) ret = ODR_TYPE_MISMATCH;
|
||||
if (ret == ODR_OK) subEntry.write(&st, subIndex, &val, sizeof(val), &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ODR_t OD_set_i16(const OD_entry_t *entry, uint16_t subIndex, int16_t val) {
|
||||
ODR_t OD_set_i16(const OD_entry_t *entry, uint8_t subIndex,
|
||||
int16_t val, bool_t odOrig)
|
||||
{
|
||||
OD_subEntry_t subEntry; OD_stream_t st;
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st);
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig);
|
||||
|
||||
if (ret == ODR_OK && st.dataLength != sizeof(val)) ret = ODR_TYPE_MISMATCH;
|
||||
if (ret == ODR_OK) subEntry.write(&st, subIndex, &val, sizeof(val), &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ODR_t OD_set_i32(const OD_entry_t *entry, uint16_t subIndex, int32_t val) {
|
||||
ODR_t OD_set_i32(const OD_entry_t *entry, uint8_t subIndex,
|
||||
int32_t val, bool_t odOrig)
|
||||
{
|
||||
OD_subEntry_t subEntry; OD_stream_t st;
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st);
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig);
|
||||
|
||||
if (ret == ODR_OK && st.dataLength != sizeof(val)) ret = ODR_TYPE_MISMATCH;
|
||||
if (ret == ODR_OK) subEntry.write(&st, subIndex, &val, sizeof(val), &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ODR_t OD_set_i64(const OD_entry_t *entry, uint16_t subIndex, int64_t val) {
|
||||
ODR_t OD_set_i64(const OD_entry_t *entry, uint8_t subIndex,
|
||||
int64_t val, bool_t odOrig)
|
||||
{
|
||||
OD_subEntry_t subEntry; OD_stream_t st;
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st);
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig);
|
||||
|
||||
if (ret == ODR_OK && st.dataLength != sizeof(val)) ret = ODR_TYPE_MISMATCH;
|
||||
if (ret == ODR_OK) subEntry.write(&st, subIndex, &val, sizeof(val), &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ODR_t OD_set_u8(const OD_entry_t *entry, uint16_t subIndex, uint8_t val) {
|
||||
ODR_t OD_set_u8(const OD_entry_t *entry, uint8_t subIndex,
|
||||
uint8_t val, bool_t odOrig)
|
||||
{
|
||||
OD_subEntry_t subEntry; OD_stream_t st;
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st);
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig);
|
||||
|
||||
if (ret == ODR_OK && st.dataLength != sizeof(val)) ret = ODR_TYPE_MISMATCH;
|
||||
if (ret == ODR_OK) subEntry.write(&st, subIndex, &val, sizeof(val), &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ODR_t OD_set_u16(const OD_entry_t *entry, uint16_t subIndex, uint16_t val) {
|
||||
ODR_t OD_set_u16(const OD_entry_t *entry, uint8_t subIndex,
|
||||
uint16_t val, bool_t odOrig)
|
||||
{
|
||||
OD_subEntry_t subEntry; OD_stream_t st;
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st);
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig);
|
||||
|
||||
if (ret == ODR_OK && st.dataLength != sizeof(val)) ret = ODR_TYPE_MISMATCH;
|
||||
if (ret == ODR_OK) subEntry.write(&st, subIndex, &val, sizeof(val), &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ODR_t OD_set_u32(const OD_entry_t *entry, uint16_t subIndex, uint32_t val) {
|
||||
ODR_t OD_set_u32(const OD_entry_t *entry, uint8_t subIndex,
|
||||
uint32_t val, bool_t odOrig)
|
||||
{
|
||||
OD_subEntry_t subEntry; OD_stream_t st;
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st);
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig);
|
||||
|
||||
if (ret == ODR_OK && st.dataLength != sizeof(val)) ret = ODR_TYPE_MISMATCH;
|
||||
if (ret == ODR_OK) subEntry.write(&st, subIndex, &val, sizeof(val), &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ODR_t OD_set_u64(const OD_entry_t *entry, uint16_t subIndex, uint64_t val) {
|
||||
ODR_t OD_set_u64(const OD_entry_t *entry, uint8_t subIndex,
|
||||
uint64_t val, bool_t odOrig)
|
||||
{
|
||||
OD_subEntry_t subEntry; OD_stream_t st;
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st);
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig);
|
||||
|
||||
if (ret == ODR_OK && st.dataLength != sizeof(val)) ret = ODR_TYPE_MISMATCH;
|
||||
if (ret == ODR_OK) subEntry.write(&st, subIndex, &val, sizeof(val), &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ODR_t OD_set_r32(const OD_entry_t *entry, uint16_t subIndex, float32_t val) {
|
||||
ODR_t OD_set_r32(const OD_entry_t *entry, uint8_t subIndex,
|
||||
float32_t val, bool_t odOrig)
|
||||
{
|
||||
OD_subEntry_t subEntry; OD_stream_t st;
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st);
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig);
|
||||
|
||||
if (ret == ODR_OK && st.dataLength != sizeof(val)) ret = ODR_TYPE_MISMATCH;
|
||||
if (ret == ODR_OK) subEntry.write(&st, subIndex, &val, sizeof(val), &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ODR_t OD_set_r64(const OD_entry_t *entry, uint16_t subIndex, float64_t val) {
|
||||
ODR_t OD_set_r64(const OD_entry_t *entry, uint8_t subIndex,
|
||||
float64_t val, bool_t odOrig)
|
||||
{
|
||||
OD_subEntry_t subEntry; OD_stream_t st;
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st);
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig);
|
||||
|
||||
if (ret == ODR_OK && st.dataLength != sizeof(val)) ret = ODR_TYPE_MISMATCH;
|
||||
if (ret == ODR_OK) subEntry.write(&st, subIndex, &val, sizeof(val), &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
ODR_t OD_getPtr_i8(const OD_entry_t *entry, uint8_t subIndex, int8_t **val) {
|
||||
OD_subEntry_t subEntry; OD_stream_t st;
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, true);
|
||||
|
||||
if (ret == ODR_OK && st.dataLength != sizeof(**val))ret = ODR_TYPE_MISMATCH;
|
||||
if (ret == ODR_OK) *val = (int8_t *)st.dataObjectOriginal;
|
||||
if (*val == NULL) ret = ODR_DEV_INCOMPAT;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ODR_t OD_getPtr_i16(const OD_entry_t *entry, uint8_t subIndex, int16_t **val) {
|
||||
OD_subEntry_t subEntry; OD_stream_t st;
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, true);
|
||||
|
||||
if (ret == ODR_OK && st.dataLength != sizeof(**val))ret = ODR_TYPE_MISMATCH;
|
||||
if (ret == ODR_OK) *val = (int16_t *)st.dataObjectOriginal;
|
||||
if (*val == NULL) ret = ODR_DEV_INCOMPAT;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ODR_t OD_getPtr_i32(const OD_entry_t *entry, uint8_t subIndex, int32_t **val) {
|
||||
OD_subEntry_t subEntry; OD_stream_t st;
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, true);
|
||||
|
||||
if (ret == ODR_OK && st.dataLength != sizeof(**val))ret = ODR_TYPE_MISMATCH;
|
||||
if (ret == ODR_OK) *val = (int32_t *)st.dataObjectOriginal;
|
||||
if (*val == NULL) ret = ODR_DEV_INCOMPAT;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ODR_t OD_getPtr_i64(const OD_entry_t *entry, uint8_t subIndex, int64_t **val) {
|
||||
OD_subEntry_t subEntry; OD_stream_t st;
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, true);
|
||||
|
||||
if (ret == ODR_OK && st.dataLength != sizeof(**val))ret = ODR_TYPE_MISMATCH;
|
||||
if (ret == ODR_OK) *val = (int64_t *)st.dataObjectOriginal;
|
||||
if (*val == NULL) ret = ODR_DEV_INCOMPAT;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ODR_t OD_getPtr_u8(const OD_entry_t *entry, uint8_t subIndex, uint8_t **val) {
|
||||
OD_subEntry_t subEntry; OD_stream_t st;
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, true);
|
||||
|
||||
if (ret == ODR_OK && st.dataLength != sizeof(**val))ret = ODR_TYPE_MISMATCH;
|
||||
if (ret == ODR_OK) *val = (uint8_t *)st.dataObjectOriginal;
|
||||
if (*val == NULL) ret = ODR_DEV_INCOMPAT;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ODR_t OD_getPtr_u16(const OD_entry_t *entry, uint8_t subIndex, uint16_t **val) {
|
||||
OD_subEntry_t subEntry; OD_stream_t st;
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, true);
|
||||
|
||||
if (ret == ODR_OK && st.dataLength != sizeof(**val))ret = ODR_TYPE_MISMATCH;
|
||||
if (ret == ODR_OK) *val = (uint16_t *)st.dataObjectOriginal;
|
||||
if (*val == NULL) ret = ODR_DEV_INCOMPAT;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ODR_t OD_getPtr_u32(const OD_entry_t *entry, uint8_t subIndex, uint32_t **val) {
|
||||
OD_subEntry_t subEntry; OD_stream_t st;
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, true);
|
||||
|
||||
if (ret == ODR_OK && st.dataLength != sizeof(**val))ret = ODR_TYPE_MISMATCH;
|
||||
if (ret == ODR_OK) *val = (uint32_t *)st.dataObjectOriginal;
|
||||
if (*val == NULL) ret = ODR_DEV_INCOMPAT;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ODR_t OD_getPtr_u64(const OD_entry_t *entry, uint8_t subIndex, uint64_t **val) {
|
||||
OD_subEntry_t subEntry; OD_stream_t st;
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, true);
|
||||
|
||||
if (ret == ODR_OK && st.dataLength != sizeof(**val))ret = ODR_TYPE_MISMATCH;
|
||||
if (ret == ODR_OK) *val = (uint64_t *)st.dataObjectOriginal;
|
||||
if (*val == NULL) ret = ODR_DEV_INCOMPAT;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ODR_t OD_getPtr_r32(const OD_entry_t *entry, uint8_t subIndex, float32_t **val){
|
||||
OD_subEntry_t subEntry; OD_stream_t st;
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, true);
|
||||
|
||||
if (ret == ODR_OK && st.dataLength != sizeof(**val))ret = ODR_TYPE_MISMATCH;
|
||||
if (ret == ODR_OK) *val = (float32_t *)st.dataObjectOriginal;
|
||||
if (*val == NULL) ret = ODR_DEV_INCOMPAT;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ODR_t OD_getPtr_r64(const OD_entry_t *entry, uint8_t subIndex, float64_t **val){
|
||||
OD_subEntry_t subEntry; OD_stream_t st;
|
||||
ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, true);
|
||||
|
||||
if (ret == ODR_OK && st.dataLength != sizeof(**val))ret = ODR_TYPE_MISMATCH;
|
||||
if (ret == ODR_OK) *val = (float64_t *)st.dataObjectOriginal;
|
||||
if (*val == NULL) ret = ODR_DEV_INCOMPAT;
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -165,38 +165,61 @@ extern "C" {
|
|||
*
|
||||
* Possible flags, can be ORed:
|
||||
* - CO_CONFIG_EM_PRODUCER - Enable emergency producer.
|
||||
* - CO_CONFIG_EM_HISTORY - Enable error history - OD object 0x1003,
|
||||
* - CO_CONFIG_EM_PROD_CONFIGURABLE - Emergency producer COB-ID is configurable,
|
||||
* OD object 0x1014. If not configurable, then 0x1014 is read-only, COB_ID
|
||||
* is set to CO_CAN_ID_EMERGENCY + nodeId and write is not verified.
|
||||
* - CO_CONFIG_EM_PROD_INHIBIT - Enable inhibit timer on emergency producer,
|
||||
* OD object 0x1015.
|
||||
* - CO_CONFIG_EM_HISTORY - Enable error history, OD object 0x1003,
|
||||
* "Pre-defined error field"
|
||||
* - CO_CONFIG_EM_CONSUMER - Enable emergency consumer.
|
||||
* - CO_CONFIG_EM_STATUS_BITS - Access @ref CO_EM_errorStatusBits_t from OD.
|
||||
* - #CO_CONFIG_FLAG_CALLBACK_PRE - Enable custom callback after preprocessing
|
||||
* emergency condition by CO_errorReport() or CO_errorReset() call.
|
||||
* Callback is configured by CO_EM_initCallbackPre().
|
||||
* - #CO_CONFIG_FLAG_TIMERNEXT - Enable calculation of timerNext_us variable
|
||||
* inside CO_EM_process().
|
||||
* - #CO_CONFIG_FLAG_OD_DYNAMIC - Enable dynamic configuration of Emergency
|
||||
* (Writing to object 0x1014 or 0x1015 re-configures the object).
|
||||
*/
|
||||
#ifdef CO_DOXYGEN
|
||||
#define CO_CONFIG_EM (CO_CONFIG_EM_PRODUCER)
|
||||
#define CO_CONFIG_EM (CO_CONFIG_EM_PRODUCER | CO_CONFIG_EM_HISTORY)
|
||||
#endif
|
||||
#define CO_CONFIG_EM_PRODUCER 0x01
|
||||
#define CO_CONFIG_EM_HISTORY 0x02
|
||||
#define CO_CONFIG_EM_CONSUMER 0x04
|
||||
#define CO_CONFIG_EM_PROD_CONFIGURABLE 0x02
|
||||
#define CO_CONFIG_EM_PROD_INHIBIT 0x04
|
||||
#define CO_CONFIG_EM_HISTORY 0x08
|
||||
#define CO_CONFIG_EM_STATUS_BITS 0x10
|
||||
#define CO_CONFIG_EM_CONSUMER 0x20
|
||||
|
||||
/**
|
||||
* Maximum number of @ref CO_EM_errorStatusBits
|
||||
* Maximum number of @ref CO_EM_errorStatusBits_t
|
||||
*
|
||||
* Stack uses 6*8 = 48 @ref CO_EM_errorStatusBits others are free to use by
|
||||
* manufacturer. Allowable value range is from 48 to 256 bits. Default is 80.
|
||||
* Stack uses 6*8 = 48 @ref CO_EM_errorStatusBits_t, others are free to use by
|
||||
* manufacturer. Allowable value range is from 48 to 256 bits in steps of 8.
|
||||
* Default is 80.
|
||||
*/
|
||||
#ifdef CO_DOXYGEN
|
||||
#define CO_CONFIG_EM_ERR_STATUS_BITS_COUNT (10*8)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Size of the internal buffer, where emergencies are stored after error
|
||||
* indication with @ref CO_error() function. Each emergency has to be post-
|
||||
* processed by the @ref CO_EM_process() function. In case of overflow, error is
|
||||
* indicated but emergency message is not sent.
|
||||
*
|
||||
* The same buffer is also used for OD object 0x1003, "Pre-defined error field".
|
||||
*
|
||||
* Each buffer element consumes 8 bytes. Valid values are 1 to 254.
|
||||
*/
|
||||
#ifdef CO_DOXYGEN
|
||||
#define CO_CONFIG_EM_BUFFER_SIZE 16
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* Condition for calculating CANopen Error register, "generic" error bit.
|
||||
*
|
||||
* Condition must observe suitable @ref CO_EM_errorStatusBits and use
|
||||
* Condition must observe suitable @ref CO_EM_errorStatusBits_t and use
|
||||
* corresponding member of errorStatusBits array from CO_EM_t to calculate the
|
||||
* condition. See also @ref CO_errorRegister_t.
|
||||
*
|
||||
|
|
@ -211,17 +234,6 @@ extern "C" {
|
|||
#define CO_CONFIG_ERR_CONDITION_GENERIC (em->errorStatusBits[5] != 0)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Condition for calculating CANopen Error register, "communication" error bit.
|
||||
* See @ref CO_CONFIG_ERR_CONDITION_GENERIC for description.
|
||||
*
|
||||
* em->errorStatusBits[2] and em->errorStatusBits[3] must be included in the
|
||||
* condition, because they are used by the stack.
|
||||
*/
|
||||
#ifdef CO_DOXYGEN
|
||||
#define CO_CONFIG_ERR_CONDITION_COMMUNICATION (em->errorStatusBits[2] || em->errorStatusBits[3])
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Condition for calculating CANopen Error register, "current" error bit.
|
||||
* See @ref CO_CONFIG_ERR_CONDITION_GENERIC for description.
|
||||
|
|
@ -249,6 +261,17 @@ extern "C" {
|
|||
#define CO_CONFIG_ERR_CONDITION_TEMPERATURE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Condition for calculating CANopen Error register, "communication" error bit.
|
||||
* See @ref CO_CONFIG_ERR_CONDITION_GENERIC for description.
|
||||
*
|
||||
* em->errorStatusBits[2] and em->errorStatusBits[3] must be included in the
|
||||
* condition, because they are used by the stack.
|
||||
*/
|
||||
#ifdef CO_DOXYGEN
|
||||
#define CO_CONFIG_ERR_CONDITION_COMMUNICATION (em->errorStatusBits[2] || em->errorStatusBits[3])
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Condition for calculating CANopen Error register, "device profile" error bit.
|
||||
* See @ref CO_CONFIG_ERR_CONDITION_GENERIC for description.
|
||||
|
|
|
|||
429
doc/objectDictionary.md
Normal file
429
doc/objectDictionary.md
Normal file
|
|
@ -0,0 +1,429 @@
|
|||
Object Dictionary
|
||||
=================
|
||||
|
||||
Definitions from CiA 301
|
||||
------------------------
|
||||
The **Object Dictionary** is a collection of all the data items which have an influence on the behavior of the application objects, the communication objects and the state machine used on this device. It serves as an interface between the communication and the application.
|
||||
The object dictionary is essentially a grouping of objects accessible via the network in an ordered pre-defined fashion. Each object within the object dictionary is addressed using a 16-bit index and a 8-bit sub-index.
|
||||
|
||||
A **SDO** (Service Data Object) is providing direct access to object entries of a CANopen device's object dictionary. As these object entries may contain data of arbitrary size and data type. SDOs may be used to transfer multiple data sets (each containing an arbitrary large block of data) from a client to a server and vice versa. The client shall control via a multiplexer (index and sub-index of the object dictionary) which data set shall be transferred. The content of the data set is defined within the object dictionary.
|
||||
|
||||
A **PDO** (Process Data Object) is providing real-time data transfer of object entries of a CANopen device's object dictionary. The transfer of PDO is performed with no protocol overhead. The PDO correspond to objects in the object dictionary and provide the interface to the application objects. Data type and mapping of application objects into a PDO is determined by a corresponding PDO mapping structure within the object dictionary.
|
||||
|
||||
|
||||
Operation
|
||||
---------
|
||||
### Terms
|
||||
The term **OD object** means object from object dictionary located at specific 16-bit index. There are different types of OD objects in CANopen: variables, arrays and records (structures). Each OD object contains pointer to actual data, data length(s) and attribute(s). See @ref OD_objectTypes_t.
|
||||
|
||||
The term **OD variable** is basic variable of specified type. For example: int8_t, uint32_t, float64_t, ... or just sequence of binary data with known
|
||||
or unknown data length. Each OD variable resides in Object dictionary at specified 16-bit index and 8-bit sub-index.
|
||||
|
||||
The term **OD entry** means structure element, which contains some basic properties of the OD object, indication of type of OD object and pointer to all necessary data for the OD object. An array of OD entries together with information about total number of OD entries represents object dictionary as defined inside CANopenNode. See @ref OD_entry_t and @ref OD_t.
|
||||
|
||||
### Access
|
||||
Application and the stack have access to OD objects via universal @ref OD_t object and @ref OD_find() function. No direct access to custom structures, which define object dictionary, is required. Properties for specific OD variable is fetched with @ref OD_getSub() function. Access to actual variable is via **read** and **write** functions. Pointer to those two functions is fetched by @ref OD_getSub(). See @ref OD_stream_t and @ref OD_subEntry_t. See also shortcuts: @ref CO_ODgetSetters, for access to data of different type.
|
||||
|
||||
### Optional extensions
|
||||
There are some optional extensions to the object dictionary:
|
||||
* **PDO flags** informs application, if specific OD variable was received or sent by PDO. And also gives the application ability to request a TPDO, to which variable is possibly mapped.
|
||||
* **IO extension** gives the application ability to take full control over the OD object. Application can specify own **read** and **write** functions and own object, on which they operate.
|
||||
|
||||
### Example usage
|
||||
```c
|
||||
extern const OD_t ODxyz;
|
||||
|
||||
void myFunc(const OD_t *od) {
|
||||
ODR_t ret;
|
||||
const OD_entry_t *entry;
|
||||
OD_subEntry_t subEntry;
|
||||
OD_IO_t io1008;
|
||||
char buf[50];
|
||||
OD_size_t bytesRd;
|
||||
int error = 0;
|
||||
|
||||
/* Init IO for "Manufacturer device name" at index 0x1008, sub-index 0x00 */
|
||||
entry = OD_find(od, 0x1008);
|
||||
ret = OD_getSub(entry, 0x00, &subEntry, &io1008.stream, false);
|
||||
io1008.read = subEntry.read;
|
||||
/* Read with io1008, subindex = 0x00 */
|
||||
if (ret == ODR_OK)
|
||||
bytesRd = io1008.read(&io1008.stream, 0x00, &buf[0], sizeof(buf), &ret);
|
||||
if (ret != ODR_OK) error++;
|
||||
|
||||
/* Use helper and set "Producer heartbeat time" at index 0x1017, sub 0x00 */
|
||||
ret = OD_set_u16(OD_find(od, 0x1017), 0x00, 500, false);
|
||||
if (ret != ODR_OK) error++;
|
||||
}
|
||||
```
|
||||
There is no need to include ODxyt.h file, it is only necessary to know, we have ODxyz defined somewhere.
|
||||
|
||||
Second example is simpler and use helper function to access OD variable. However it is not very efficient, because it goes through all search procedures.
|
||||
|
||||
If access to the same variable is very frequent, it is better to use first example. After initialization, application has to remember only "io1008" object. Frequent reading of the variable is then very efficient.
|
||||
|
||||
### Simple access to OD via globals
|
||||
Some simple user applications can also access some OD variables directly via globals.
|
||||
|
||||
@warning
|
||||
If OD object has IO extension enabled, then direct access to its OD variables must not be used. Only valid access is via read or write or helper functions.
|
||||
|
||||
```c
|
||||
#include ODxyz.h
|
||||
|
||||
void myFuncGlob(void) {
|
||||
//Direct address instead of OD_find()
|
||||
const OD_entry_t *entry_errReg = ODxyz_1001_errorRegister;
|
||||
|
||||
//Direct access to OD variable
|
||||
uint32_t devType = ODxyz_0.x1000_deviceType;
|
||||
ODxyz_0.x1018_identity.serialNumber = 0x12345678;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Object dictionary example
|
||||
-------------------------
|
||||
Actual Object dictionary for one CANopen device is defined by pair of OD_xyz.h and ODxyz.h files.
|
||||
|
||||
Suffix "xyz" is unique name of the object dictionary. If single default object dictionary is used, suffix is omitted. Such way configuration with multiple object dictionaries is possible.
|
||||
|
||||
Data for OD definition are arranged inside multiple structures. Structures are different for different configuration of OD. Data objects, created with those structures, are constant or are variable.
|
||||
|
||||
Actual OD variables are arranged inside multiple structures, so called storage groups. Selected groups can optionally be stored to non-volatile memory.
|
||||
|
||||
@warning
|
||||
Manual editing of ODxyz.h/.c files is very error-prone.
|
||||
|
||||
Pair of ODxyz.h/.c files can be generated by OD editor tool. The tool can edit standard CANopen device description file in xml format. Xml file may include also some non-standard elements, specific to CANopenNode. Xml file is then used for automatic generation of ODxyz.h/.c files.
|
||||
|
||||
### Example ODxyz.h file
|
||||
```c
|
||||
typedef struct {
|
||||
uint32_t x1000_deviceType;
|
||||
uint8_t x1001_errorRegister;
|
||||
struct {
|
||||
uint8_t maxSubIndex;
|
||||
uint32_t vendorID;
|
||||
uint32_t productCode;
|
||||
uint32_t revisionNumber;
|
||||
uint32_t serialNumber;
|
||||
} x1018_identity;
|
||||
} ODxyz_0_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t x1001_errorRegister;
|
||||
} ODxyz_1_t;
|
||||
|
||||
extern ODxyz_0_t ODxyz_0;
|
||||
extern ODxyz_1_t ODxyz_1;
|
||||
extern const OD_t ODxyz;
|
||||
|
||||
#define ODxyz_1000_deviceType &ODxyz.list[0]
|
||||
#define ODxyz_1001_errorRegister &ODxyz.list[1]
|
||||
#define ODxyz_1018_identity &ODxyz.list[2]
|
||||
```
|
||||
|
||||
### Example ODxyz.c file
|
||||
```c
|
||||
#define OD_DEFINITION
|
||||
#include "301/CO_ODinterface.h"
|
||||
#include "ODxyz.h"
|
||||
|
||||
typedef struct {
|
||||
OD_extensionIO_t xio_1001_errorRegister;
|
||||
} ODxyz_ext_t;
|
||||
|
||||
typedef struct {
|
||||
OD_obj_var_t o_1000_deviceType;
|
||||
OD_obj_var_t orig_1001_errorRegister;
|
||||
OD_obj_extended_t o_1001_errorRegister;
|
||||
OD_obj_var_t o_1018_identity[5];
|
||||
} ODxyz_objs_t;
|
||||
|
||||
ODxyz_0_t ODxyz_0 = {
|
||||
.x1000_deviceType = 0L,
|
||||
.x1018_identity = {
|
||||
.maxSubIndex = 4,
|
||||
.vendorID = 0L,
|
||||
.productCode = 0L,
|
||||
.revisionNumber = 0L,
|
||||
.serialNumber = 0L,
|
||||
},
|
||||
};
|
||||
|
||||
ODxyz_1_t ODxyz_1 = {
|
||||
.x1001_errorRegister = 0,
|
||||
};
|
||||
|
||||
static ODxyz_ext_t ODxyz_ext = {0};
|
||||
|
||||
static const ODxyz_objs_t ODxyz_objs = {
|
||||
.o_1000_deviceType = {
|
||||
.data = &ODxyz_0.x1000_deviceType,
|
||||
.attribute = ODA_SDO_R | ODA_MB,
|
||||
.dataLength = 4,
|
||||
},
|
||||
.orig_1001_errorRegister = {
|
||||
.data = &ODxyz_1.x1001_errorRegister,
|
||||
.attribute = ODA_SDO_R,
|
||||
.dataLength = 1,
|
||||
},
|
||||
.o_1001_errorRegister = {
|
||||
.flagsPDO = NULL,
|
||||
.extIO = &ODxyz_ext.xio_1001_errorRegister,
|
||||
.odObjectOriginal = &ODxyz_objs.orig_1001_errorRegister,
|
||||
},
|
||||
.o_1018_identity = {
|
||||
{
|
||||
.data = &ODxyz_0.x1018_identity.maxSubIndex,
|
||||
.attribute = ODA_SDO_R,
|
||||
.dataLength = 1,
|
||||
},
|
||||
{
|
||||
.data = &ODxyz_0.x1018_identity.vendorID,
|
||||
.attribute = ODA_SDO_R | ODA_MB,
|
||||
.dataLength = 4,
|
||||
},
|
||||
{
|
||||
.data = &ODxyz_0.x1018_identity.productCode,
|
||||
.attribute = ODA_SDO_R | ODA_MB,
|
||||
.dataLength = 4,
|
||||
},
|
||||
{
|
||||
.data = &ODxyz_0.x1018_identity.revisionNumber,
|
||||
.attribute = ODA_SDO_R | ODA_MB,
|
||||
.dataLength = 4,
|
||||
},
|
||||
{
|
||||
.data = &ODxyz_0.x1018_identity.serialNumber,
|
||||
.attribute = ODA_SDO_R | ODA_MB,
|
||||
.dataLength = 4,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
const OD_t ODxyz = {
|
||||
3, {
|
||||
{0x1000, 0, 0, ODT_VAR, &ODxyz_objs.o_1000_deviceType},
|
||||
{0x1001, 0, 1, ODT_EVAR, &ODxyz_objs.o_1001_errorRegister},
|
||||
{0x1018, 4, 0, ODT_REC, &ODxyz_objs.o_1018_identity},
|
||||
{0x0000, 0, 0, 0, NULL}
|
||||
}};
|
||||
```
|
||||
|
||||
|
||||
XML device description
|
||||
----------------------
|
||||
CANopen device description - XML schema definition - is specified by CiA 311 standard.
|
||||
|
||||
CiA 311 complies with standard ISO 15745-1:2005/Amd1 (Industrial automation systems and integration - Open systems application integration framework).
|
||||
|
||||
CANopen device description is basically a XML file with all the information about CANopen device. The larges part of the file is a list of all object dictionary variables with all necessary properties and documentation. This file can be edited with OD editor application and can be used as data source, from which Object dictionary for CANopenNode is generated. Furthermore, this file can be used with CANopen configuration tool, which interacts with CANopen devices on running CANopen network.
|
||||
|
||||
XML schema definitions are available at: http://www.canopen.org/xml/1.1 One of the tools for viewing XML schemas is "xsddiagram" (https://github.com/dgis/xsddiagram).
|
||||
|
||||
CANopen specifies also another type of files for CANopen device description. These are EDS files, which are in INI format. It is possible to convert between those two formats. But CANopenNode uses XML format.
|
||||
|
||||
The device description file has "XDD" file extension. The name of this file shall contain the vendor-ID of the CANopen device in the form of 8 hexadecimal digits in any position of the name and separated with underscores. For example "name1_12345678_name2.XDD".
|
||||
|
||||
CANopenNode includes multiple profile definition files, one for each CANopen object. Those files have "XPD" extension. They are in the same XML format as XDD files. The XML editor tool can use XPD files to insert prepared data into device description file (XDD), which is being edited.
|
||||
|
||||
### XDD, XPD file example
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ISO15745ProfileContainer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.canopen.org/xml/1.0">
|
||||
<ISO15745Profile>
|
||||
<ProfileHeader>...</ProfileHeader>
|
||||
<ProfileBody xsi:type="ProfileBody_Device_CANopen" fileName="..." fileCreator="..." fileCreationDate="..." fileVersion="...">
|
||||
<DeviceIdentity>...</DeviceIdentity>
|
||||
<DeviceFunction>...</DeviceFunction>
|
||||
<ApplicationProcess>
|
||||
<dataTypeList>...</dataTypeList>
|
||||
<parameterList>
|
||||
<parameter uniqueID="UID_PARAM_1000" access="read">
|
||||
<label lang="en">Device type</label>
|
||||
<description lang="en">...</description>
|
||||
<denotation>...</denotation>
|
||||
<UINT/>
|
||||
<defaultValue value="0x00000000" />
|
||||
</parameter>
|
||||
<parameter uniqueID="UID_PARAM_1001" access="read">
|
||||
<label lang="en">Error register</label>
|
||||
<description lang="en">...</description>
|
||||
<denotation>...</denotation>
|
||||
<BYTE/>
|
||||
<defaultValue value="0" />
|
||||
<property name="CO_storageGroup" value="1">
|
||||
<property name="CO_extensionIO" value="true">
|
||||
</parameter>
|
||||
<parameter uniqueID="UID_PARAM_1018">
|
||||
<label lang="en">Identity</label>
|
||||
<description lang="en">...</description>
|
||||
<denotation>...</denotation>
|
||||
<dataTypeIDRef uniqueIDRef="..." />
|
||||
</parameter>
|
||||
<parameter uniqueID="UID_PARAM_101800" access="read">
|
||||
<label lang="en">max sub-index</label>
|
||||
<description lang="en">...</description>
|
||||
<denotation>...</denotation>
|
||||
<USINT/>
|
||||
<defaultValue value="4" />
|
||||
</parameter>
|
||||
<parameter uniqueID="UID_PARAM_101801" access="read">
|
||||
<label lang="en">Vendor-ID</label>
|
||||
<description lang="en">...</description>
|
||||
<denotation>...</denotation>
|
||||
<UINT/>
|
||||
<defaultValue value="0x00000000" />
|
||||
</parameter>
|
||||
<parameter uniqueID="UID_PARAM_101802" access="read">
|
||||
<label lang="en">Product code</label>
|
||||
<description lang="en">...</description>
|
||||
<denotation>...</denotation>
|
||||
<UINT/>
|
||||
<defaultValue value="0x00000000" />
|
||||
</parameter>
|
||||
<parameter uniqueID="UID_PARAM_101803" access="read">
|
||||
<label lang="en">Revision number</label>
|
||||
<description lang="en">...</description>
|
||||
<denotation>...</denotation>
|
||||
<UINT/>
|
||||
<defaultValue value="0x00000000" />
|
||||
</parameter>
|
||||
<parameter uniqueID="UID_PARAM_101804" access="read">
|
||||
<label lang="en">Serial number</label>
|
||||
<description lang="en">...</description>
|
||||
<denotation>...</denotation>
|
||||
<UINT/>
|
||||
<defaultValue value="0x00000000" />
|
||||
</parameter>
|
||||
</parameterList>
|
||||
<parameterGroupList>
|
||||
<parameterGroup uniqueID="UID_PG_CO_COMM">
|
||||
<label lang="en">CANopen Communication Parameters</label>
|
||||
<description lang="en">...</description>
|
||||
<parameterGroup uniqueID="UID_PG_CO_COMM_COMMON">
|
||||
<label lang="en">CANopen Common Communication Parameters</label>
|
||||
<description lang="en">...</description>
|
||||
<parameterRef uniqueIDRef="UID_PARAM_1000" />
|
||||
<parameterRef uniqueIDRef="UID_PARAM_1001" />
|
||||
<parameterRef uniqueIDRef="UID_PARAM_1018" />
|
||||
</parameterGroup>
|
||||
</parameterGroup>
|
||||
</parameterGroupList>
|
||||
</ApplicationProcess>
|
||||
</ProfileBody>
|
||||
</ISO15745Profile>
|
||||
<ISO15745Profile>
|
||||
<ProfileHeader>...</ProfileHeader>
|
||||
<ProfileBody xsi:type="ProfileBody_CommunicationNetwork_CANopen" fileName="..." fileCreator="..." fileCreationDate="..." fileVersion="...">
|
||||
<ApplicationLayers>
|
||||
<CANopenObjectList>
|
||||
<CANopenObject index="1000" name="Device type" objectType="7" PDOmapping="no" uniqueIDRef="UID_PARAM_1000" />
|
||||
<CANopenObject index="1001" name="Error register" objectType="7" PDOmapping="TPDO" uniqueIDRef="UID_PARAM_1001" />
|
||||
<CANopenObject index="1018" name="Identity" objectType="9" subNumber="5" uniqueIDRef="UID_PARAM_1018">
|
||||
<CANopenSubObject subIndex="00" name="max sub-index" objectType="7" PDOmapping="no" uniqueIDRef="UID_PARAM_101800" />
|
||||
<CANopenSubObject subIndex="01" name="Vendor-ID" objectType="7" PDOmapping="no" uniqueIDRef="UID_PARAM_101801" />
|
||||
<CANopenSubObject subIndex="02" name="Product code" objectType="7" PDOmapping="no" uniqueIDRef="UID_PARAM_101802" />
|
||||
<CANopenSubObject subIndex="03" name="Revision number" objectType="7" PDOmapping="no" uniqueIDRef="UID_PARAM_101803" />
|
||||
<CANopenSubObject subIndex="04" name="Serial number" objectType="7" PDOmapping="no" uniqueIDRef="UID_PARAM_101804" />
|
||||
</CANopenObject>
|
||||
</CANopenObjectList>
|
||||
</ApplicationLayers>
|
||||
<TransportLayers>...</TransportLayers>
|
||||
</ProfileBody>
|
||||
</ISO15745Profile>
|
||||
</ISO15745ProfileContainer>
|
||||
```
|
||||
|
||||
### Parameter description
|
||||
Above XML file example shows necessary data for OD interface used by CANopenNode and other parameters required by the standard. Standard specifies many other parameters, which are not used by CANopenNode for simplicity.
|
||||
|
||||
XML file is divided into two parts:
|
||||
1. "ProfileBody_Device_CANopen" - more standardized information
|
||||
2. "ProfileBody_CommunicationNetwork_CANopen" - communication related info
|
||||
|
||||
Most important part of the XML file is definition of each OD object. All OD objects are listed in "CANopenObjectList", which resides in the second part of the XML file. Each "CANopenObject" and "CANopenSubObject" of the list contains a link to parameter ("uniqueIDRef"), which resides in the first part of the XML file. So data for each OD object is split between two parts of the XML file.
|
||||
|
||||
#### <CANopenObject>
|
||||
* "index" (required) - Object dictionary index
|
||||
* "name" (required) - Name of the parameter
|
||||
* "objectType" (required) - "7"=VAR, "8"=ARRAY, "9"=RECORD
|
||||
* "subNumber" (required if "objectType" is "8" or "9")
|
||||
* "PDOmapping" (optional if "objectType" is "7", default is "no"):
|
||||
* "no" - mapping not allowed
|
||||
* "default" - not used, same as "optional"
|
||||
* "optional" - mapping allowed to TPDO or RPDO
|
||||
* "TPDO" - mapping allowed to TPDO
|
||||
* "RPDO" mapping allowed to RPDO
|
||||
* "uniqueIDRef" (required or see below) - Reference to <parameter>
|
||||
|
||||
#### <CANopenSubObject>
|
||||
* "subIndex" (required) - Object dictionary sub-index
|
||||
* "name" (required) - Name of the parameter
|
||||
* "objectType" (required, always "7")
|
||||
* "PDOmapping" (optional, same as above, default is "no")
|
||||
* "uniqueIDRef" (required or see below) - Reference to <parameter>
|
||||
|
||||
#### uniqueIDRef
|
||||
This is required attribute from "CANopenObject" and "CANopenSubObject". It contains reference to <parameter> in "ProfileBody_Device_CANopen" section of the XML file. There are additional necessary properties.
|
||||
|
||||
If "uniqueIDRef" attribute is not specified and "objectType" is 7(VAR), then "CANopenObject" or "CANopenSubObject" must contain additional attributes:
|
||||
* "dataType" (required for VAR) - CANopen basic data type, see below
|
||||
* "accessType" (required for VAR) - "ro", "wo", "rw" or "const"
|
||||
* "defaultValue" (optional) - Default value for the variable.
|
||||
* "denotation" (optional) - Not used by CANopenNode.
|
||||
|
||||
#### <parameter>
|
||||
* "uniqueID" (required)
|
||||
* "access" (required for VAR) - can be one of:
|
||||
* "const" - same as "read"
|
||||
* "read" - only read access with SDO or PDO
|
||||
* "write" - only write access with SDO or PDO
|
||||
* "readWrite" - read or write access with SDO or PDO
|
||||
* "readWriteInput" - same as "readWrite"
|
||||
* "readWriteOutput" - same as "readWrite"
|
||||
* "noAccess" - object will be in object dictionary, but no access.
|
||||
* <label lang="en"> (required)
|
||||
* <description lang="en"> (required)
|
||||
* <UINT and similar/> (required) - Basic or complex data type. Basic data type (for VAR) is specified in IEC 61131-3 (see below). If data type is complex (ARRAY or RECORD), then <dataTypeIDRef> must be specified and entry must be added in the <dataTypeList>. Such definition of complex data types is required by the standard, but it is not required by CANopenNode.
|
||||
* <defaultValue> (optional for VAR) - Default value for the variable.
|
||||
|
||||
Additional, optional, CANopenNode specific properties, which can be used inside parameters describing <CANopenObject>:
|
||||
* <property name="CO_storageGroup" value="..."> - group into which the C variable will belong. Variables from specific storage group may then be stored into non-volatile memory, automatically or by SDO command. Valid value is from 0x00 (default - not stored) to 0x7F.
|
||||
* <property name="CO_extensionIO" value="..."> - Valid value is "false" (default) or "true", if IO extension is enabled.
|
||||
* <property name="CO_flagsPDO" value="..."> - Valid value is "false" (default) or "true", if PDO flags are enabled.
|
||||
* <property name="CO_countLabel" value="..."> - Valid value is any string without spaces. OD exporter will generate a macro for each different string. For example, if four OD objects have "CO_countLabel" set to "TPDO", then macro "#define ODxyz_NO_TPDO 4" will be generated by OD exporter.
|
||||
|
||||
Additional, optional, CANopenNode specific property, which can be used inside parameters describing "VAR":
|
||||
* <property name="CO_accessSRDO" value="..."> - Valid values are: "tx", "rx", "trx", "no"(default).
|
||||
* <property name="CO_byteLength" value="..."> - Length of the variable in bytes, optionally used by string or domain. If CO_byteLength is not specified for string, then length is calculated from string <defaultValue>.
|
||||
|
||||
#### CANopen basic data types
|
||||
| CANopenNode | IEC 61131-3 | CANopen | dataType |
|
||||
| ------------ | ------------ | --------------- | -------- |
|
||||
| bool_t | BOOL | BOOLEAN | 0x01 |
|
||||
| int8_t | CHAR, SINT | INTEGER8 | 0x02 |
|
||||
| int16_t | INT | INTEGER16 | 0x03 |
|
||||
| int32_t | DINT | INTEGER32 | 0x04 |
|
||||
| int64_t | LINT | INTEGER64 | 0x15 |
|
||||
| uint8_t | BYTE, USINT | UNSIGNED8 | 0x05 |
|
||||
| uint16_t | WORD, UINT | UNSIGNED16 | 0x06 |
|
||||
| uint32_t | DWORD, UDINT | UNSIGNED32 | 0x07 |
|
||||
| uint64_t | LWORD, ULINT | UNSIGNED64 | 0x1B |
|
||||
| float32_t | REAL | REAL32 | 0x08 |
|
||||
| float64_t | LREAL | REAL64 | 0x11 |
|
||||
| bytestring_t | STRING | VISIBLE_STRING | 0x09 |
|
||||
| bytestring_t | - | OCTET_STRING | 0x0A |
|
||||
| bytestring_t | WSTRING | UNICODE_STRING | 0x0B |
|
||||
| bytestring_t | - | TIME_OF_DAY | 0x0C |
|
||||
| bytestring_t | - | TIME_DIFFERENCE | 0x0D |
|
||||
| bytestring_t | - | DOMAIN | 0x0F |
|
||||
| bytestring_t | BITSTRING | - | - |
|
||||
|
||||
#### <parameterGroupList>
|
||||
This is optional element and is not required by standard, nor by CANopenNode. This can be very useful for documentation, which can be organised into multiple chapters with multiple levels. CANopen objects can then be organised in any way, not only by index.
|
||||
|
||||
#### Other elements
|
||||
Other elements listed in the above XML example are required by the standard and does not influence the CANopenNode object dictionary generator.
|
||||
Loading…
Reference in a new issue