From 76b43c88ef6d5490cb2f1518e10646e8dcb45c76 Mon Sep 17 00:00:00 2001 From: Janez Date: Wed, 5 May 2021 10:28:05 +0200 Subject: [PATCH] Create 'storage' directory, move CO_storage.h/c, add CO_storageEeeprom.h/c --- 301/CO_driver.h | 31 +++- CANopen.h | 8 + Doxyfile | 1 + Makefile | 2 +- example/CO_storageBlank.h | 13 +- example/DS301_profile.md | 4 +- example/Makefile | 2 +- socketCAN/CO_storageLinux.c | 5 +- socketCAN/CO_storageLinux.h | 2 +- storage/CO_eeprom.h | 133 +++++++++++++++++ {301 => storage}/CO_storage.c | 2 +- {301 => storage}/CO_storage.h | 16 +- storage/CO_storageEeprom.c | 268 ++++++++++++++++++++++++++++++++++ storage/CO_storageEeprom.h | 131 +++++++++++++++++ 14 files changed, 589 insertions(+), 29 deletions(-) create mode 100644 storage/CO_eeprom.h rename {301 => storage}/CO_storage.c (96%) rename {301 => storage}/CO_storage.h (88%) create mode 100644 storage/CO_storageEeprom.c create mode 100644 storage/CO_storageEeprom.h diff --git a/301/CO_driver.h b/301/CO_driver.h index 7929f18..f881b1b 100644 --- a/301/CO_driver.h +++ b/301/CO_driver.h @@ -354,20 +354,39 @@ typedef struct { * Must be defined in the **CO_driver_target.h** file. * * For more information on Data storage see @ref CO_storage or **CO_storage.h** - * file. Structure members documented here are required. Target system shall add - * own additional, hardware specific variables. + * file. Structure members documented here are always required or required with + * @ref CO_storage_eeprom. Target system may add own additional, hardware + * specific variables. */ typedef struct { - /** Address of data to store */ + /** Address of data to store, always required. */ void *addr; - /** Length of data to store */ + /** Length of data to store, always required. */ size_t len; /** Sub index in OD objects 1010 and 1011, from 2 to 127. Writing * 0x65766173 to 1010,subIndexOD will store data to non-volatile memory. - * Writing 0x64616F6C to 1011,subIndexOD will restore default data. */ + * Writing 0x64616F6C to 1011,subIndexOD will restore default data, always + * required. */ uint8_t subIndexOD; - /** Attribute from @ref CO_storage_attributes_t */ + /** Attribute from @ref CO_storage_attributes_t, always required. */ uint8_t attr; + /** Pointer to storage module, target system specific usage, required with + * @ref CO_storage_eeprom. */ + void *storageModule; + /** CRC checksum of the data stored in eeprom, set on store, required with + * @ref CO_storage_eeprom. */ + uint16_t crc; + /** Address of entry signature inside eeprom, set by init, required with + * @ref CO_storage_eeprom. */ + size_t eepromAddrSignature; + /** Address of data inside eeprom, set by init, required with + * @ref CO_storage_eeprom. */ + size_t eepromAddr; + /** Offset of next byte being updated by automatic storage, required with + * @ref CO_storage_eeprom. */ + size_t offset; + /** Additional target specific parameters, optional. */ + void *additionalParameters; } CO_storage_entry_t; diff --git a/CANopen.h b/CANopen.h index c3819f1..44f6be8 100644 --- a/CANopen.h +++ b/CANopen.h @@ -147,6 +147,14 @@ extern "C" { * @} */ +/** + * @defgroup CO_CANopen_storage CANopen_storage + * @{ + * + * CANopen Object Dictionary and other data storage. + * @} + */ + /** * @defgroup CO_CANopen_extra CANopen_extra * @{ diff --git a/Doxyfile b/Doxyfile index 86c9b45..7bfa59c 100644 --- a/Doxyfile +++ b/Doxyfile @@ -822,6 +822,7 @@ INPUT = README.md \ 304 \ 305 \ 309 \ + storage \ extra \ socketCAN diff --git a/Makefile b/Makefile index 195008a..081a5f9 100644 --- a/Makefile +++ b/Makefile @@ -31,13 +31,13 @@ SOURCES = \ $(CANOPEN_SRC)/301/CO_PDO.c \ $(CANOPEN_SRC)/301/crc16-ccitt.c \ $(CANOPEN_SRC)/301/CO_fifo.c \ - $(CANOPEN_SRC)/301/CO_storage.c \ $(CANOPEN_SRC)/303/CO_LEDs.c \ $(CANOPEN_SRC)/304/CO_GFC.c \ $(CANOPEN_SRC)/304/CO_SRDO.c \ $(CANOPEN_SRC)/305/CO_LSSslave.c \ $(CANOPEN_SRC)/305/CO_LSSmaster.c \ $(CANOPEN_SRC)/309/CO_gateway_ascii.c \ + $(CANOPEN_SRC)/storage/CO_storage.c \ $(CANOPEN_SRC)/extra/CO_trace.c \ $(CANOPEN_SRC)/CANopen.c \ $(APPL_SRC)/OD.c \ diff --git a/example/CO_storageBlank.h b/example/CO_storageBlank.h index de8ddf9..5e562c6 100644 --- a/example/CO_storageBlank.h +++ b/example/CO_storageBlank.h @@ -25,7 +25,7 @@ #ifndef CO_STORAGE_BLANK_H #define CO_STORAGE_BLANK_H -#include "301/CO_storage.h" +#include "storage/CO_storage.h" #if ((CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE) || defined CO_DOXYGEN @@ -33,8 +33,15 @@ extern "C" { #endif -/* See socketCAN/storageLinux.h and 301/CO_storage.h for information and - * full example */ +/* This is very basic example of implementing (object dictionary) data storage. + * Data storage is target specific. CO_storageBlank.h and .c files only shows + * the basic principle, but does nothing. For complete example of storage see: + * - CANopenPIC/PIC32 uses eeprom with CANopenNode/storage/CO_storage.h/.c, + * CANopenNode/storage/CO_storageEeprom.h/.c, CANopenNode/storage/CO_eeprom.h + * and CANopenPIC/PIC32/CO_eepromPIC32.c files. + * - CANopenLinux uses file system with CANopenNode/storage/CO_storage.h/.c and + * CANopenLinux/CO_storageLinux.h files. + */ CO_ReturnError_t CO_storageBlank_init(CO_storage_t *storage, CO_CANmodule_t *CANmodule, diff --git a/example/DS301_profile.md b/example/DS301_profile.md index dde64d2..b3987c2 100644 --- a/example/DS301_profile.md +++ b/example/DS301_profile.md @@ -1,5 +1,5 @@ -CANopen documentation -===================== +CANopen device documentation +============================ **New Product** diff --git a/example/Makefile b/example/Makefile index 2783298..531d4bf 100644 --- a/example/Makefile +++ b/example/Makefile @@ -26,9 +26,9 @@ SOURCES = \ $(CANOPEN_SRC)/301/CO_TIME.c \ $(CANOPEN_SRC)/301/CO_SYNC.c \ $(CANOPEN_SRC)/301/CO_PDO.c \ - $(CANOPEN_SRC)/301/CO_storage.c \ $(CANOPEN_SRC)/303/CO_LEDs.c \ $(CANOPEN_SRC)/305/CO_LSSslave.c \ + $(CANOPEN_SRC)/storage/CO_storage.c \ $(CANOPEN_SRC)/CANopen.c \ $(APPL_SRC)/OD.c \ $(DRV_SRC)/main_blank.c diff --git a/socketCAN/CO_storageLinux.c b/socketCAN/CO_storageLinux.c index cd2a55c..9f0422b 100644 --- a/socketCAN/CO_storageLinux.c +++ b/socketCAN/CO_storageLinux.c @@ -104,9 +104,8 @@ static ODR_t storeLinux(CO_storage_entry_t *entry, CO_CANmodule_t *CANmodule) { /* rename existing file to *.old and *.tmp to existing */ if (ret == ODR_OK) { - if (rename(entry->filename, filename_old) != 0 - || rename(filename_tmp, entry->filename) != 0 - ) { + rename(entry->filename, filename_old); + if (rename(filename_tmp, entry->filename) != 0) { ret = ODR_HW; } } diff --git a/socketCAN/CO_storageLinux.h b/socketCAN/CO_storageLinux.h index 0d92c2c..54a549d 100644 --- a/socketCAN/CO_storageLinux.h +++ b/socketCAN/CO_storageLinux.h @@ -26,7 +26,7 @@ #ifndef CO_STORAGE_LINUX_H #define CO_STORAGE_LINUX_H -#include "301/CO_storage.h" +#include "storage/CO_storage.h" #if ((CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE) || defined CO_DOXYGEN diff --git a/storage/CO_eeprom.h b/storage/CO_eeprom.h new file mode 100644 index 0000000..94d1b30 --- /dev/null +++ b/storage/CO_eeprom.h @@ -0,0 +1,133 @@ +/** + * Eeprom interface for use with CO_storageEeprom + * + * @file CO_eeprom.h + * @ingroup CO_storage_eeprom + * @author Janez Paternoster + * @copyright 2021 Janez Paternoster + * + * This file is part of CANopenNode, an opensource CANopen Stack. + * Project home page is . + * For more information on CANopen see . + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CO_EEPROM_H +#define CO_EEPROM_H + +#include "301/CO_driver.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup CO_storage_eeprom + * @{ + */ + +/** + * Initialize eeprom device, target system specific function. + * + * @param storageModule Pointer to storage module. + * + * @return True on success + */ +bool_t CO_eeprom_init(void *storageModule); + + +/** + * Get free address inside eeprom, target system specific function. + * + * Function is called several times for each storage block in the initialization + * phase after CO_eeprom_init(). + * + * @param storageModule Pointer to storage module. + * @param isAuto True, if variable is auto stored or false if protected + * @param len Length of data, which will be stored to that location + * @param [out] overflow set to true, if not enough eeprom memory + * + * @return Asigned eeprom address + */ +size_t CO_eeprom_getAddr(void *storageModule, bool_t isAuto, + size_t len, bool_t *overflow); + + +/** + * Read block of data from the eeprom, target system specific function. + * + * @param storageModule Pointer to storage module. + * @param data Pointer to data buffer, where data will be stored. + * @param eepromAddr Address in eeprom, from where data will be read. + * @param len Length of the data block to be read. + */ +void CO_eeprom_readBlock(void *storageModule, uint8_t *data, + size_t eepromAddr, size_t len); + + +/** + * Write block of data to the eeprom, target system specific function. + * + * It is blocking function, so it waits, until all data is written. + * + * @param storageModule Pointer to storage module. + * @param data Pointer to data buffer which will be written. + * @param eepromAddr Address in eeprom, where data will be written. If data is + * stored accross multiple pages, address must be aligned with page. + * @param len Length of the data block. + * + * @return true on success + */ +bool_t CO_eeprom_writeBlock(void *storageModule, uint8_t *data, + size_t eepromAddr, size_t len); + + +/** + * Get CRC checksum of the block of data stored in the eeprom, target system + * specific function. + * + * @param storageModule Pointer to storage module. + * @param eepromAddr Address of data in eeprom. + * @param len Length of the data. + * + * @return CRC checksum + */ +uint16_t CO_eeprom_getCrcBlock(void *storageModule, + size_t eepromAddr, size_t len); + + +/** + * Update one byte of data in the eeprom, target system specific function. + * + * Function is used by automatic storage. It updates byte in eeprom only if + * differs from data. + * + * @param storageModule Pointer to storage module. + * @param data Data byte to be written + * @param eepromAddr Address in eeprom, from where data will be updated. + * + * @return true if write was successful or false, if still waiting previous + * data to finish writing. + */ +bool_t CO_eeprom_updateByte(void *storageModule, uint8_t data, + size_t eepromAddr); + + +/** @} */ /* CO_storage_eeprom */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CO_EEPROM_H */ diff --git a/301/CO_storage.c b/storage/CO_storage.c similarity index 96% rename from 301/CO_storage.c rename to storage/CO_storage.c index 503101f..b69f172 100644 --- a/301/CO_storage.c +++ b/storage/CO_storage.c @@ -22,7 +22,7 @@ * limitations under the License. */ -#include "301/CO_storage.h" +#include "storage/CO_storage.h" #if (CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE diff --git a/301/CO_storage.h b/storage/CO_storage.h similarity index 88% rename from 301/CO_storage.h rename to storage/CO_storage.h index b80be72..36b4d8f 100644 --- a/301/CO_storage.h +++ b/storage/CO_storage.h @@ -41,8 +41,8 @@ extern "C" { #endif /** - * @defgroup CO_storage Data storage - * @ingroup CO_CANopen_301 + * @defgroup CO_storage Data storage base + * @ingroup CO_CANopen_storage * @{ * * CANopen provides OD objects 0x1010 and 0x1011 for control of storing and @@ -138,15 +138,9 @@ typedef struct { * @ref ODR_t : "ODR_OK" in case of success, "ODR_HW" in case of hardware error. * @param restore Same as 'store', but for restoring default data. * @param entries Pointer to array of storage entries. Array must be defined and - * initialized by application and must exist permanently. Each array element - * contains: - * - Pointer and length of data, which will be stored or restored, - * - subIndexOD, which binds entry to specific subindex in 1010 and 1011 OD - * objects. Multiple entries with the same subIndexOD are possible. - * - Attribute, which specifies, if data is stored on command or automatically - * and also specifies if data is able to restore. @ref CO_storage_attributes_t - * - Additional target specific parameters. See @ref CO_storage_entry_t in - * CO_driver_target.h file. + * initialized by application and must exist permanently. + * Structure @ref CO_storage_entry_t is target specific and must be defined by + * CO_driver_target.h. See CO_driver.h for required parameters. * @param entriesCount Count of storage entries * * @return CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT. diff --git a/storage/CO_storageEeprom.c b/storage/CO_storageEeprom.c new file mode 100644 index 0000000..e49cd35 --- /dev/null +++ b/storage/CO_storageEeprom.c @@ -0,0 +1,268 @@ +/* + * CANopen data storage object for storing data into block device (eeprom) + * + * @file CO_storageEeprom.c + * @author Janez Paternoster + * @copyright 2021 Janez Paternoster + * + * This file is part of CANopenNode, an opensource CANopen Stack. + * Project home page is . + * For more information on CANopen see . + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "storage/CO_storageEeprom.h" +#include "storage/CO_eeprom.h" +#include "301/crc16-ccitt.h" + +#if (CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE + +/* + * Function for writing data on "Store parameters" command - OD object 1010 + * + * For more information see file CO_storage.h, CO_storage_entry_t. + */ +static ODR_t storeEeprom(CO_storage_entry_t *entry, CO_CANmodule_t *CANmodule) { + bool_t writeOk; + + /* save data to the eeprom */ + CO_LOCK_OD(CANmodule); + writeOk = CO_eeprom_writeBlock(entry->storageModule, entry->addr, + entry->eepromAddr, entry->len); + entry->crc = crc16_ccitt(entry->addr, entry->len, 0); + CO_UNLOCK_OD(CANmodule); + + /* Verify, if data in eeprom are equal */ + uint16_t crc_read = CO_eeprom_getCrcBlock(entry->storageModule, + entry->eepromAddr, entry->len); + if (entry->crc != crc_read || !writeOk) { + return ODR_HW; + } + + /* Write signature (see CO_storageEeprom_init() for info) */ + uint16_t signatureOfEntry = (uint16_t)entry->len; + uint32_t signature = (((uint32_t)entry->crc) << 16) | signatureOfEntry; + writeOk = CO_eeprom_writeBlock(entry->storageModule, + (uint8_t *)&signature, + entry->eepromAddrSignature, + sizeof(signature)); + + /* verify signature and write */ + uint32_t signatureRead; + CO_eeprom_readBlock(entry->storageModule, + (uint8_t *)&signatureRead, + entry->eepromAddrSignature, + sizeof(signatureRead)); + if(signature != signatureRead || !writeOk) { + return ODR_HW; + } + + return ODR_OK; +} + + +/* + * Function for restoring data on "Restore default parameters" command - OD 1011 + * + * For more information see file CO_storage.h, CO_storage_entry_t. + */ +static ODR_t restoreEeprom(CO_storage_entry_t *entry, + CO_CANmodule_t *CANmodule) +{ + (void) CANmodule; + bool_t writeOk; + + /* Write empty signature */ + uint32_t signature = 0xFFFFFFFF; + writeOk = CO_eeprom_writeBlock(entry->storageModule, + (uint8_t *)&signature, + entry->eepromAddrSignature, + sizeof(signature)); + + /* verify signature and protection */ + uint32_t signatureRead; + CO_eeprom_readBlock(entry->storageModule, + (uint8_t *)&signatureRead, + entry->eepromAddrSignature, + sizeof(signatureRead)); + if(signature != signatureRead || !writeOk) { + return ODR_HW; + } + + return ODR_OK; +} + + +/******************************************************************************/ +CO_ReturnError_t CO_storageEeprom_init(CO_storage_t *storage, + CO_CANmodule_t *CANmodule, + void *storageModule, + OD_entry_t *OD_1010_StoreParameters, + OD_entry_t *OD_1011_RestoreDefaultParam, + CO_storage_entry_t *entries, + uint8_t entriesCount, + uint32_t *storageInitError) +{ + CO_ReturnError_t ret; + bool_t eepromOvf = false; + + /* verify arguments */ + if (storage == NULL || entries == NULL || entriesCount == 0 + || storageInitError == NULL + ) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + storage->enabled = false; + + /* Initialize storage hardware */ + if (!CO_eeprom_init(storageModule)) { + *storageInitError = 0xFFFFFFFF; + return CO_ERROR_DATA_CORRUPT; + } + + /* initialize storage and OD extensions */ + ret = CO_storage_init(storage, + CANmodule, + OD_1010_StoreParameters, + OD_1011_RestoreDefaultParam, + storeEeprom, + restoreEeprom, + entries, + entriesCount); + if (ret != CO_ERROR_NO) { + return ret; + } + + /* Read entry signatures from the eeprom */ + uint32_t signatures[entriesCount]; + size_t signaturesAddress = CO_eeprom_getAddr(storageModule, + false, + sizeof(signatures), + &eepromOvf); + CO_eeprom_readBlock(storageModule, + (uint8_t *)signatures, + signaturesAddress, + sizeof(signatures)); + + /* initialize entries */ + *storageInitError = 0; + for (uint8_t i = 0; i < entriesCount; i++) { + CO_storage_entry_t *entry = &entries[i]; + bool_t isAuto = (entry->attr & CO_storage_auto) != 0; + + /* verify arguments */ + if (entry->addr == NULL || entry->len == 0 || entry->subIndexOD < 2) { + *storageInitError = i; + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + /* calculate addresses inside eeprom */ + entry->eepromAddrSignature = signaturesAddress + sizeof(uint32_t) * i; + entry->eepromAddr = CO_eeprom_getAddr(storageModule, + isAuto, + entry->len, + &eepromOvf); + entry->offset = 0; + + /* verify if eeprom is too small */ + if (eepromOvf) { + *storageInitError = i; + return CO_ERROR_OUT_OF_MEMORY; + } + + /* 32bit signature (which was stored in eeprom) is combined from + * 16bit signature of the entry and 16bit CRC checksum of the data + * block. 16bit signature of the entry is entry->len. */ + uint32_t signature = signatures[i]; + uint16_t signatureInEeprom = (uint16_t)signature; + entry->crc = (uint16_t)(signature >> 16); + uint16_t signatureOfEntry = (uint16_t)entry->len; + + /* Verify two signatures */ + bool_t dataCorrupt = false; + if (signatureInEeprom != signatureOfEntry) { + dataCorrupt = true; + } + else { + /* Read data into storage location */ + CO_eeprom_readBlock(entry->storageModule, entry->addr, + entry->eepromAddr, entry->len); + + /* Verify CRC, except for auto storage variables */ + if (!isAuto) { + uint16_t crc = crc16_ccitt(entry->addr, entry->len, 0); + if (crc != entry->crc) { + dataCorrupt = true; + } + } + } + + /* additional info in case of error */ + if (dataCorrupt) { + uint32_t errorBit = entry->subIndexOD; + if (errorBit > 31) errorBit = 31; + *storageInitError |= ((uint32_t) 1) << errorBit; + ret = CO_ERROR_DATA_CORRUPT; + } + } /* for (entries) */ + + storage->enabled = true; + return ret; +} + + +/******************************************************************************/ +void CO_storageEeprom_auto_process(CO_storage_t *storage, bool_t saveAll) { + /* verify arguments */ + if (storage == NULL || !storage->enabled) { + return; + } + + /* loop through entries */ + for (uint8_t i = 0; i < storage->entriesCount; i++) { + CO_storage_entry_t *entry = &storage->entries[i]; + + if ((entry->attr & CO_storage_auto) == 0) + continue; + + if (saveAll) { + /* update all bytes */ + for (size_t i = 0; i < entry->len; ) { + uint8_t dataByteToUpdate = ((uint8_t *)(entry->addr))[i]; + size_t eepromAddr = entry->eepromAddr + i; + if (CO_eeprom_updateByte(entry->storageModule, + dataByteToUpdate, + eepromAddr) + ) { + i++; + } + } + } + else { + /* update one data byte and if successful increment to next */ + uint8_t dataByteToUpdate = ((uint8_t*)(entry->addr))[entry->offset]; + size_t eepromAddr = entry->eepromAddr + entry->offset; + if (CO_eeprom_updateByte(entry->storageModule, dataByteToUpdate, + eepromAddr) + ) { + if (++entry->offset >= entry->len) { + entry->offset = 0; + } + } + } + } +} + +#endif /* (CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE */ diff --git a/storage/CO_storageEeprom.h b/storage/CO_storageEeprom.h new file mode 100644 index 0000000..dcb3de1 --- /dev/null +++ b/storage/CO_storageEeprom.h @@ -0,0 +1,131 @@ +/** + * CANopen data storage object for storing data into block device (eeprom) + * + * @file CO_storageEeprom.h + * @ingroup CO_storage_eeprom + * @author Janez Paternoster + * @copyright 2021 Janez Paternoster + * + * This file is part of CANopenNode, an opensource CANopen Stack. + * Project home page is . + * For more information on CANopen see . + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CO_STORAGE_EEPROM_H +#define CO_STORAGE_EEPROM_H + +#include "storage/CO_storage.h" + +#if ((CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE) || defined CO_DOXYGEN + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup CO_storage_eeprom Data storage in eeprom + * @ingroup CO_CANopen_storage + * @{ + * + * This is an interface into generic CANopenNode @ref CO_storage for usage with + * eeprom chip like 25LC256. Functions @ref CO_storageEeprom_init() and + * @ref CO_storageEeprom_auto_process are target system independent. Functions + * specified by @ref CO_eeprom.h file, must be defined by target system. + * For example implementation see CANopenPIC/PIC32. + * + * Storage principle: + * This function first reads 'signatures' for all entries from the known address + * from the eeprom. If signature for each entry is correct, then data is read + * from correct address from the eeprom into storage location. If signature is + * wrong, then data for that entry is indicated as corrupt and CANopen + * emergency message is sent. + * + * Signature also includes 16-bit CRC checksum of the data stored in eeprom. If + * it differs from CRC checksum calculated from the data actually loaded (on + * program startup), then entry is indicated as corrupt and CANopen emergency + * message is sent. + * + * Signature is written to eeprom, when data block is stored via CANopen SDO + * write command to object 0x1010. Signature is erased, with CANopen SDO write + * command to object 0x1011. If signature is not valid or is erased for any + * entry, emergency message is sent. If eeprom is new, then all signatures are + * wrong, so it is best to store all parameters by writing to 0x1010, sub 1. + * + * If entry attribute has CO_storage_auto set, then data block is stored + * autonomously, byte by byte, on change, during program run. Those data blocks + * are stored into write unprotected location. For auto storage to work, + * its signature in eeprom must be correct. CRC checksum for the data is not + * used. + */ + + +/** + * Initialize data storage object (block device (eeprom) specific) + * + * This function should be called by application after the program startup, + * before @ref CO_CANopenInit(). This function initializes storage object, + * OD extensions on objects 1010 and 1011, reads data from file, verifies them + * and writes data to addresses specified inside entries. This function + * internally calls @ref CO_storage_init(). + * + * @param storage This object will be initialized. It must be defined by + * application and must exist permanently. + * @param CANmodule CAN device, used for @ref CO_LOCK_OD() macro. + * @param storageModule Pointer to storage module passed to CO_eeprom functions. + * @param OD_1010_StoreParameters OD entry for 0x1010 -"Store parameters". + * Entry is optional, may be NULL. + * @param OD_1011_RestoreDefaultParam OD entry for 0x1011 -"Restore default + * parameters". Entry is optional, may be NULL. + * @param entries Pointer to array of storage entries, see @ref CO_storage_init. + * @param entriesCount Count of storage entries + * @param [out] storageInitError If function returns CO_ERROR_DATA_CORRUPT, + * then this variable contains a bit mask from subIndexOD values, where data + * was not properly initialized. If other error, then this variable contains + * index or erroneous entry. If there is hardware error like missing eeprom, + * then storageInitError is 0xFFFFFFFF and function returns + * CO_ERROR_DATA_CORRUPT. + * + * @return CO_ERROR_NO, CO_ERROR_DATA_CORRUPT if data can not be initialized, + * CO_ERROR_ILLEGAL_ARGUMENT or CO_ERROR_OUT_OF_MEMORY. + */ +CO_ReturnError_t CO_storageEeprom_init(CO_storage_t *storage, + CO_CANmodule_t *CANmodule, + void *storageModule, + OD_entry_t *OD_1010_StoreParameters, + OD_entry_t *OD_1011_RestoreDefaultParam, + CO_storage_entry_t *entries, + uint8_t entriesCount, + uint32_t *storageInitError); + + +/** + * Automatically update data if differs inside eeprom. + * + * Should be called cyclically by program. Each interval it updates one byte. + * + * @param storage This object + * @param saveAll If true, all bytes are updated, useful on program end. + */ +void CO_storageEeprom_auto_process(CO_storage_t *storage, bool_t saveAll); + +/** @} */ /* CO_storage_eeprom */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* (CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE */ + +#endif /* CO_STORAGE_EEPROM_H */