Object Dictionary storage implemented for SocketCAN.
This commit is contained in:
parent
ef15b5e182
commit
9bf4fd860f
8 changed files with 569 additions and 377 deletions
|
|
@ -94,7 +94,8 @@ File structure
|
|||
case it is template for new implementations. It is also documented, other
|
||||
directories are not.
|
||||
- **`CO_driver.h/.c`** - Microcontroller specific objects for CAN module.
|
||||
- **`eeprom.h/.c`** - Functions for storage of Object dictionary.
|
||||
- **`eeprom.h/.c`** - Functions for storage of Object dictionary, optional.
|
||||
- **`(helpers.h/.c)`** - Some optional files with specific helper functions.
|
||||
- **`socketCAN`** - Directory for Linux socketCAN interface.
|
||||
- **`PIC32`** - Directory for PIC32 devices from Microchip.
|
||||
- **`PIC24H_dsPIC33F`** - Directory for PIC24H and dsPIC33F devices from Microchip.
|
||||
|
|
|
|||
12
codingStyle
12
codingStyle
|
|
@ -46,12 +46,12 @@
|
|||
* Brief description of the object ends at this dot. Details follow
|
||||
* here.
|
||||
*/
|
||||
typedef struct{
|
||||
typedef struct {
|
||||
int8_t member1; /**< Short description of the member 1 */
|
||||
uint16_t member2; /**< Note the '/**<' sequence after the member 2 */
|
||||
/** Long description of the variable stringMember. More description. */
|
||||
char_t stringMember[5];
|
||||
}object1_t;
|
||||
} object1_t;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -80,15 +80,15 @@ int32_t foo1(
|
|||
* comment.
|
||||
*/
|
||||
|
||||
if(xy == yz){ /* Comment. '//' comments are not allowed by MISRA-C */
|
||||
if(xy == yz) { /* Comment. '//' comments are not allowed */
|
||||
...
|
||||
}
|
||||
else{
|
||||
else {
|
||||
...
|
||||
}
|
||||
|
||||
switch(zx){
|
||||
case 1:{
|
||||
switch(zx) {
|
||||
case 1: {
|
||||
...
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,16 +40,16 @@ void CO_errExit(char* msg);
|
|||
|
||||
|
||||
/* Mainline task (taskMain) ***************************************************/
|
||||
static struct{
|
||||
static struct {
|
||||
int fdTmr; /* file descriptor for taskTmr */
|
||||
int fdPipe[2]; /* file descriptors for pipe [0]=read, [1]=write */
|
||||
struct itimerspec tmrSpec;
|
||||
uint16_t tmr1msPrev;
|
||||
uint16_t *maxTime;
|
||||
}taskMain;
|
||||
} taskMain;
|
||||
|
||||
|
||||
void taskMain_init(int fdEpoll, uint16_t *maxTime){
|
||||
void taskMain_init(int fdEpoll, uint16_t *maxTime) {
|
||||
struct epoll_event ev;
|
||||
int flags;
|
||||
|
||||
|
|
@ -106,21 +106,21 @@ void taskMain_init(int fdEpoll, uint16_t *maxTime){
|
|||
}
|
||||
|
||||
|
||||
void taskMain_close(void){
|
||||
void taskMain_close(void) {
|
||||
close(taskMain.fdPipe[0]);
|
||||
close(taskMain.fdPipe[1]);
|
||||
close(taskMain.fdTmr);
|
||||
}
|
||||
|
||||
|
||||
bool_t taskMain_process(int fd, CO_NMT_reset_cmd_t *reset, uint16_t timer1ms){
|
||||
bool_t taskMain_process(int fd, CO_NMT_reset_cmd_t *reset, uint16_t timer1ms) {
|
||||
bool_t wasProcessed = true;
|
||||
|
||||
/* Signal from pipe, consume all bytes. */
|
||||
if(fd == taskMain.fdPipe[0]){
|
||||
for(;;){
|
||||
if(fd == taskMain.fdPipe[0]) {
|
||||
for(;;) {
|
||||
char ch;
|
||||
if(read(taskMain.fdPipe[0], &ch, 1) == -1){
|
||||
if(read(taskMain.fdPipe[0], &ch, 1) == -1) {
|
||||
if (errno == EAGAIN)
|
||||
break; /* No more bytes. */
|
||||
else
|
||||
|
|
@ -130,17 +130,17 @@ bool_t taskMain_process(int fd, CO_NMT_reset_cmd_t *reset, uint16_t timer1ms){
|
|||
}
|
||||
|
||||
/* Timer expired. */
|
||||
else if(fd == taskMain.fdTmr){
|
||||
else if(fd == taskMain.fdTmr) {
|
||||
uint64_t tmrExp;
|
||||
if(read(taskMain.fdTmr, &tmrExp, sizeof(tmrExp)) != sizeof(uint64_t))
|
||||
CO_errorReport(CO->em, CO_EM_GENERIC_SOFTWARE_ERROR, CO_EMC_SOFTWARE_INTERNAL, 0x21200000L | errno);
|
||||
}
|
||||
else{
|
||||
else {
|
||||
wasProcessed = false;
|
||||
}
|
||||
|
||||
/* Process mainline. */
|
||||
if(wasProcessed){
|
||||
if(wasProcessed) {
|
||||
uint16_t timer1msDiff;
|
||||
uint16_t timerNext = 50;
|
||||
|
||||
|
|
@ -149,8 +149,8 @@ bool_t taskMain_process(int fd, CO_NMT_reset_cmd_t *reset, uint16_t timer1ms){
|
|||
taskMain.tmr1msPrev = timer1ms;
|
||||
|
||||
/* Calculate maximum interval in milliseconds (informative) */
|
||||
if(taskMain.maxTime != NULL){
|
||||
if(timer1msDiff > *taskMain.maxTime){
|
||||
if(taskMain.maxTime != NULL) {
|
||||
if(timer1msDiff > *taskMain.maxTime) {
|
||||
*taskMain.maxTime = timer1msDiff;
|
||||
}
|
||||
}
|
||||
|
|
@ -171,14 +171,14 @@ bool_t taskMain_process(int fd, CO_NMT_reset_cmd_t *reset, uint16_t timer1ms){
|
|||
}
|
||||
|
||||
|
||||
void taskMain_cbSignal(void){
|
||||
void taskMain_cbSignal(void) {
|
||||
if(write(taskMain.fdPipe[1], "x", 1) == -1)
|
||||
CO_errorReport(CO->em, CO_EM_GENERIC_SOFTWARE_ERROR, CO_EMC_SOFTWARE_INTERNAL, 0x23100000L | errno);
|
||||
}
|
||||
|
||||
|
||||
/* Realtime task (taskRT) *****************************************************/
|
||||
static struct{
|
||||
static struct {
|
||||
int fdRx0; /* file descriptor for CANrx */
|
||||
int fdTmr; /* file descriptor for taskTmr */
|
||||
struct itimerspec tmrSpec;
|
||||
|
|
@ -188,10 +188,10 @@ static struct{
|
|||
uint16_t *maxTime;
|
||||
int fdEpoll;
|
||||
bool_t CANrx_locked;
|
||||
}taskRT;
|
||||
} taskRT;
|
||||
|
||||
|
||||
void CANrx_taskTmr_init(int fdEpoll, long intervalns, uint16_t *maxTime){
|
||||
void CANrx_taskTmr_init(int fdEpoll, long intervalns, uint16_t *maxTime) {
|
||||
struct epoll_event ev;
|
||||
|
||||
/* get file descriptors */
|
||||
|
|
@ -234,21 +234,21 @@ void CANrx_taskTmr_init(int fdEpoll, long intervalns, uint16_t *maxTime){
|
|||
}
|
||||
|
||||
|
||||
void CANrx_taskTmr_close(void){
|
||||
void CANrx_taskTmr_close(void) {
|
||||
close(taskRT.fdTmr);
|
||||
}
|
||||
|
||||
|
||||
bool_t CANrx_taskTmr_process(int fd){
|
||||
bool_t CANrx_taskTmr_process(int fd) {
|
||||
bool_t wasProcessed = true;
|
||||
|
||||
/* Get received CAN message. */
|
||||
if(fd == taskRT.fdRx0){
|
||||
if(fd == taskRT.fdRx0) {
|
||||
CO_CANrxWait(CO->CANmodule[0]);
|
||||
}
|
||||
|
||||
/* Execute taskTmr */
|
||||
else if(fd == taskRT.fdTmr){
|
||||
else if(fd == taskRT.fdTmr) {
|
||||
uint64_t tmrExp;
|
||||
|
||||
/* Wait for timer to expire */
|
||||
|
|
@ -256,17 +256,17 @@ bool_t CANrx_taskTmr_process(int fd){
|
|||
CO_errorReport(CO->em, CO_EM_GENERIC_SOFTWARE_ERROR, CO_EMC_SOFTWARE_INTERNAL, 0x22100000L | errno);
|
||||
|
||||
/* Calculate maximum interval in microseconds (informative) */
|
||||
if(taskRT.maxTime != NULL){
|
||||
if(taskRT.maxTime != NULL) {
|
||||
struct timespec tmrMeasure;
|
||||
if(clock_gettime(CLOCK_MONOTONIC, &tmrMeasure) == -1)
|
||||
CO_errorReport(CO->em, CO_EM_GENERIC_SOFTWARE_ERROR, CO_EMC_SOFTWARE_INTERNAL, 0x22200000L | errno);
|
||||
if(tmrMeasure.tv_sec == taskRT.tmrVal->tv_sec){
|
||||
if(tmrMeasure.tv_sec == taskRT.tmrVal->tv_sec) {
|
||||
long dt = tmrMeasure.tv_nsec - taskRT.tmrVal->tv_nsec;
|
||||
dt /= 1000;
|
||||
dt += taskRT.intervalus;
|
||||
if(dt > 0xFFFF){
|
||||
if(dt > 0xFFFF) {
|
||||
*taskRT.maxTime = 0xFFFF;
|
||||
}else if(dt > *taskRT.maxTime){
|
||||
}else if(dt > *taskRT.maxTime) {
|
||||
*taskRT.maxTime = (uint16_t) dt;
|
||||
}
|
||||
}
|
||||
|
|
@ -274,7 +274,7 @@ bool_t CANrx_taskTmr_process(int fd){
|
|||
|
||||
/* Calculate next shot for the timer */
|
||||
taskRT.tmrVal->tv_nsec += taskRT.intervalns;
|
||||
if(taskRT.tmrVal->tv_nsec >= NSEC_PER_SEC){
|
||||
if(taskRT.tmrVal->tv_nsec >= NSEC_PER_SEC) {
|
||||
taskRT.tmrVal->tv_nsec -= NSEC_PER_SEC;
|
||||
taskRT.tmrVal->tv_sec++;
|
||||
}
|
||||
|
|
@ -292,7 +292,7 @@ bool_t CANrx_taskTmr_process(int fd){
|
|||
syncWas = CO_process_SYNC_RPDO(CO, taskRT.intervalus);
|
||||
|
||||
/* Re-enable CANrx, if it was disabled by SYNC callback */
|
||||
if(taskRT.CANrx_locked){
|
||||
if(taskRT.CANrx_locked) {
|
||||
struct epoll_event ev;
|
||||
|
||||
/* enable epool event */
|
||||
|
|
@ -314,7 +314,7 @@ bool_t CANrx_taskTmr_process(int fd){
|
|||
CO_UNLOCK_OD();
|
||||
}
|
||||
|
||||
else{
|
||||
else {
|
||||
wasProcessed = false;
|
||||
}
|
||||
|
||||
|
|
@ -322,8 +322,8 @@ bool_t CANrx_taskTmr_process(int fd){
|
|||
}
|
||||
|
||||
|
||||
void CANrx_lockCbSync(bool_t syncReceived){
|
||||
if(syncReceived){
|
||||
void CANrx_lockCbSync(bool_t syncReceived) {
|
||||
if(syncReceived) {
|
||||
struct epoll_event ev;
|
||||
|
||||
/* disable epool event */
|
||||
379
stack/socketCAN/CO_OD_storage.c
Normal file
379
stack/socketCAN/CO_OD_storage.c
Normal file
|
|
@ -0,0 +1,379 @@
|
|||
/*
|
||||
* CANopen Object Dictionary storage object for Linux SocketCAN.
|
||||
*
|
||||
* @file CO_OD_storage.c
|
||||
* @author Janez Paternoster
|
||||
* @copyright 2015 Janez Paternoster
|
||||
*
|
||||
* This file is part of CANopenNode, an opensource CANopen Stack.
|
||||
* Project home page is <http://canopennode.sourceforge.net>.
|
||||
* For more information on CANopen see <http://www.can-cia.org/>.
|
||||
*
|
||||
* CANopenNode is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "CO_driver.h"
|
||||
#include "CO_SDO.h"
|
||||
#include "CO_Emergency.h"
|
||||
#include "CO_OD_storage.h"
|
||||
#include "crc16-ccitt.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h> /* for memcpy */
|
||||
#include <stdlib.h> /* for malloc, free */
|
||||
|
||||
|
||||
#define RETURN_SUCCESS 0
|
||||
#define RETURN_ERROR -1
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
CO_SDO_abortCode_t CO_ODF_1010(CO_ODF_arg_t *ODF_arg) {
|
||||
CO_OD_storage_t *odStor;
|
||||
uint32_t value;
|
||||
CO_SDO_abortCode_t ret = CO_SDO_AB_NONE;
|
||||
|
||||
odStor = (CO_OD_storage_t*) ODF_arg->object;
|
||||
value = CO_getUint32(ODF_arg->data);
|
||||
|
||||
if(!ODF_arg->reading) {
|
||||
/* don't change the old value */
|
||||
CO_memcpy(ODF_arg->data, (const uint8_t*)ODF_arg->ODdataStorage, 4U);
|
||||
|
||||
if(ODF_arg->subIndex == 1) {
|
||||
/* store parameters */
|
||||
if(value == 0x65766173UL) {
|
||||
if(CO_OD_storage_saveSecure(odStor->odAddress, odStor->odSize, odStor->filename) != 0) {
|
||||
ret = CO_SDO_AB_HW;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ret = CO_SDO_AB_DATA_TRANSF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
CO_SDO_abortCode_t CO_ODF_1011(CO_ODF_arg_t *ODF_arg) {
|
||||
CO_OD_storage_t *odStor;
|
||||
uint32_t value;
|
||||
CO_SDO_abortCode_t ret = CO_SDO_AB_NONE;
|
||||
|
||||
odStor = (CO_OD_storage_t*) ODF_arg->object;
|
||||
value = CO_getUint32(ODF_arg->data);
|
||||
|
||||
if(!ODF_arg->reading) {
|
||||
/* don't change the old value */
|
||||
CO_memcpy(ODF_arg->data, (const uint8_t*)ODF_arg->ODdataStorage, 4U);
|
||||
|
||||
if(ODF_arg->subIndex >= 1) {
|
||||
/* restore default parameters */
|
||||
if(value == 0x64616F6CUL) {
|
||||
if(CO_OD_storage_restoreSecure(odStor->filename) != 0) {
|
||||
ret = CO_SDO_AB_HW;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ret = CO_SDO_AB_DATA_TRANSF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
int CO_OD_storage_saveSecure(
|
||||
uint8_t *odAddress,
|
||||
uint32_t odSize,
|
||||
char *filename)
|
||||
{
|
||||
int ret = RETURN_SUCCESS;
|
||||
|
||||
char *filename_old = NULL;
|
||||
uint16_t CRC = 0;
|
||||
|
||||
/* Generate new string with extension '.old' and rename current file to it. */
|
||||
filename_old = malloc(strlen(filename)+10);
|
||||
if(filename_old != NULL) {
|
||||
strcpy(filename_old, filename);
|
||||
strcat(filename_old, ".old");
|
||||
|
||||
remove(filename_old);
|
||||
if(rename(filename, filename_old) != 0) {
|
||||
ret = RETURN_ERROR;
|
||||
}
|
||||
} else {
|
||||
ret = RETURN_ERROR;
|
||||
}
|
||||
|
||||
/* Open a new file and write data to it, including CRC. */
|
||||
if(ret == RETURN_SUCCESS) {
|
||||
FILE *fp = fopen(filename, "w");
|
||||
if(fp != NULL) {
|
||||
|
||||
CO_LOCK_OD();
|
||||
fwrite((const void *)odAddress, 1, odSize, fp);
|
||||
CRC = crc16_ccitt((unsigned char*)odAddress, odSize, 0);
|
||||
CO_UNLOCK_OD();
|
||||
|
||||
fwrite((const void *)&CRC, 1, 2, fp);
|
||||
fclose(fp);
|
||||
} else {
|
||||
ret = RETURN_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/* Verify data */
|
||||
if(ret == RETURN_SUCCESS) {
|
||||
void *buf = NULL;
|
||||
FILE *fp = NULL;
|
||||
uint32_t cnt = 0;
|
||||
uint16_t CRC2 = 0;
|
||||
|
||||
buf = malloc(odSize + 4);
|
||||
if(buf != NULL) {
|
||||
fp = fopen(filename, "r");
|
||||
if(fp != NULL) {
|
||||
cnt = fread(buf, 1, odSize, fp);
|
||||
CRC2 = crc16_ccitt((unsigned char*)buf, odSize, 0);
|
||||
/* read also two bytes of CRC */
|
||||
cnt += fread(buf, 1, 4, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
free(buf);
|
||||
}
|
||||
/* If size or CRC differs, report error */
|
||||
if(buf == NULL || fp == NULL || cnt != (odSize + 2) || CRC != CRC2) {
|
||||
ret = RETURN_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/* In case of error, set back the old file. */
|
||||
if(ret != RETURN_SUCCESS && filename_old != NULL) {
|
||||
remove(filename);
|
||||
rename(filename_old, filename);
|
||||
}
|
||||
|
||||
free(filename_old);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
int CO_OD_storage_restoreSecure(char *filename) {
|
||||
int ret = RETURN_SUCCESS;
|
||||
FILE *fp = NULL;
|
||||
|
||||
/* If filename already exists, rename it to '.old'. */
|
||||
fp = fopen(filename, "r");
|
||||
if(fp != NULL) {
|
||||
char *filename_old = NULL;
|
||||
|
||||
fclose(fp);
|
||||
|
||||
filename_old = malloc(strlen(filename)+10);
|
||||
if(filename_old != NULL) {
|
||||
strcpy(filename_old, filename);
|
||||
strcat(filename_old, ".old");
|
||||
|
||||
remove(filename_old);
|
||||
if(rename(filename, filename_old) != 0) {
|
||||
ret = RETURN_ERROR;
|
||||
}
|
||||
free(filename_old);
|
||||
}
|
||||
else {
|
||||
ret = RETURN_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/* create an empty file and write "-\n" to it. */
|
||||
if(ret == RETURN_SUCCESS) {
|
||||
fp = fopen(filename, "w");
|
||||
if(fp != NULL) {
|
||||
fputs("-\n", fp);
|
||||
fclose(fp);
|
||||
} else {
|
||||
ret = RETURN_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
CO_ReturnError_t CO_OD_storage_init(
|
||||
CO_OD_storage_t *odStor,
|
||||
uint8_t *odAddress,
|
||||
uint32_t odSize,
|
||||
char *filename)
|
||||
{
|
||||
CO_ReturnError_t ret = CO_ERROR_NO;
|
||||
void *buf = NULL;
|
||||
|
||||
/* verify arguments */
|
||||
if(odStor==NULL || odAddress==NULL) {
|
||||
ret = CO_ERROR_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
|
||||
/* configure object variables and allocate buffer */
|
||||
if(ret == CO_ERROR_NO) {
|
||||
odStor->odAddress = odAddress;
|
||||
odStor->odSize = odSize;
|
||||
odStor->filename = filename;
|
||||
odStor->fp = NULL;
|
||||
odStor->tmr1msPrev = 0;
|
||||
odStor->lastSavedMs = 0;
|
||||
|
||||
buf = malloc(odStor->odSize);
|
||||
if(buf == NULL) {
|
||||
ret = CO_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
/* read data from the file and verify CRC */
|
||||
if(ret == CO_ERROR_NO) {
|
||||
FILE *fp;
|
||||
uint32_t cnt = 0;
|
||||
uint16_t CRC[2];
|
||||
|
||||
fp = fopen(odStor->filename, "r");
|
||||
if(fp) {
|
||||
cnt = fread(buf, 1, odStor->odSize, fp);
|
||||
/* read also two bytes of CRC from file */
|
||||
cnt += fread(&CRC[0], 1, 4, fp);
|
||||
CRC[1] = crc16_ccitt((unsigned char*)buf, odStor->odSize, 0);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
if(cnt == 2 && *((char*)buf) == '-') {
|
||||
/* file is empty, default values will be used, no error */
|
||||
ret = CO_ERROR_NO;
|
||||
}
|
||||
else if(cnt != (odStor->odSize + 2)) {
|
||||
/* file length does not match */
|
||||
ret = CO_ERROR_DATA_CORRUPT;
|
||||
}
|
||||
else if(CRC[0] != CRC[1]) {
|
||||
/* CRC does not match */
|
||||
ret = CO_ERROR_CRC;
|
||||
}
|
||||
else {
|
||||
/* no errors, copy data into Object dictionary */
|
||||
memcpy(odStor->odAddress, buf, odStor->odSize);
|
||||
}
|
||||
}
|
||||
|
||||
free(buf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
CO_ReturnError_t CO_OD_storage_autoSave(
|
||||
CO_OD_storage_t *odStor,
|
||||
uint16_t timer1ms,
|
||||
uint16_t delay)
|
||||
{
|
||||
CO_ReturnError_t ret = CO_ERROR_NO;
|
||||
|
||||
/* verify arguments */
|
||||
if(odStor==NULL || odStor->odAddress==NULL) {
|
||||
ret = CO_ERROR_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
|
||||
/* don't save file more often than delay */
|
||||
if(odStor->lastSavedMs < delay) {
|
||||
odStor->lastSavedMs += timer1ms - odStor->tmr1msPrev;
|
||||
}
|
||||
else {
|
||||
void *buf = NULL;
|
||||
bool_t saveData = false;
|
||||
|
||||
/* allocate buffer and open file if necessary */
|
||||
if(ret == CO_ERROR_NO) {
|
||||
buf = malloc(odStor->odSize);
|
||||
if(odStor->fp == NULL) {
|
||||
odStor->fp = fopen(odStor->filename, "r+");
|
||||
}
|
||||
if(buf == NULL || odStor->fp == NULL) {
|
||||
ret = CO_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
/* read data from the beginning of the file */
|
||||
if(ret == CO_ERROR_NO) {
|
||||
uint32_t cnt = 0;
|
||||
|
||||
rewind(odStor->fp);
|
||||
cnt = fread(buf, 1, odStor->odSize, odStor->fp);
|
||||
|
||||
if(cnt == 2 && *((char*)buf) == '-') {
|
||||
/* file is empty, data will be saved. */
|
||||
saveData = true;
|
||||
}
|
||||
else if(cnt == odStor->odSize) {
|
||||
/* verify, if data differs */
|
||||
if(memcmp((const void *)buf, (const void *)odStor->odAddress, odStor->odSize) != 0) {
|
||||
saveData = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* file length does not match */
|
||||
ret = CO_ERROR_DATA_CORRUPT;
|
||||
}
|
||||
}
|
||||
|
||||
/* Save the data to the file only if data differs. */
|
||||
if(ret == CO_ERROR_NO && saveData) {
|
||||
uint16_t CRC;
|
||||
|
||||
/* copy data to temporary buffer */
|
||||
memcpy(buf, odStor->odAddress, odStor->odSize);
|
||||
|
||||
rewind(odStor->fp);
|
||||
fwrite((const void *)buf, 1, odStor->odSize, odStor->fp);
|
||||
|
||||
/* write also CRC */
|
||||
CRC = crc16_ccitt((unsigned char*)buf, odStor->odSize, 0);
|
||||
fwrite((const void *)&CRC, 1, 2, odStor->fp);
|
||||
|
||||
fflush(odStor->fp);
|
||||
|
||||
odStor->lastSavedMs = 0;
|
||||
}
|
||||
|
||||
free(buf);
|
||||
}
|
||||
|
||||
odStor->tmr1msPrev = timer1ms;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void CO_OD_storage_autoSaveClose(CO_OD_storage_t *odStor) {
|
||||
if(odStor->fp != NULL) {
|
||||
fclose(odStor->fp);
|
||||
}
|
||||
}
|
||||
152
stack/socketCAN/CO_OD_storage.h
Normal file
152
stack/socketCAN/CO_OD_storage.h
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
/**
|
||||
* CANopen Object Dictionary storage object for Linux SocketCAN.
|
||||
*
|
||||
* @file CO_OD_storage.h
|
||||
* @author Janez Paternoster
|
||||
* @copyright 2015 Janez Paternoster
|
||||
*
|
||||
* This file is part of CANopenNode, an opensource CANopen Stack.
|
||||
* Project home page is <http://canopennode.sourceforge.net>.
|
||||
* For more information on CANopen see <http://www.can-cia.org/>.
|
||||
*
|
||||
* CANopenNode is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef CO_OD_STORAGE_H
|
||||
#define CO_OD_STORAGE_H
|
||||
|
||||
|
||||
#include "CO_driver.h"
|
||||
#include "CO_SDO.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
/* For documentation see file drvTemplate/CO_OD_storage.h */
|
||||
|
||||
|
||||
/**
|
||||
* Callbacks for using inside @ref CO_OD_configure() function (for OD objects 1010 and 1011).
|
||||
*/
|
||||
CO_SDO_abortCode_t CO_ODF_1010(CO_ODF_arg_t *ODF_arg);
|
||||
CO_SDO_abortCode_t CO_ODF_1011(CO_ODF_arg_t *ODF_arg);
|
||||
|
||||
|
||||
/**
|
||||
* Save memory block to a file.
|
||||
*
|
||||
* Function renames current file to filename.old, copies contents from odAddress
|
||||
* to filename, adds two bytes of CRC code. It then verifies the written file and
|
||||
* in case of errors sets back the old file and returns error.
|
||||
*
|
||||
* Function is used with CANopen OD object at index 1010.
|
||||
*
|
||||
* @param odAddress Address of the memory block, which will be stored.
|
||||
* @param odSize Size of the above memory block.
|
||||
* @param filename Name of the file, where data will be stored.
|
||||
*
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int CO_OD_storage_saveSecure(
|
||||
uint8_t *odAddress,
|
||||
uint32_t odSize,
|
||||
char *filename);
|
||||
|
||||
|
||||
/**
|
||||
* Remove OD storage file.
|
||||
*
|
||||
* Function renames current file to filename.old, then creates empty file and
|
||||
* writes two bytes "-\n" to it. When program will start next time, default values
|
||||
* are used for Object Dictionary. In case of error in renaming to .old it
|
||||
* keeps the original file and returns error.
|
||||
*
|
||||
* Writing data to file is secured with mutex CO_LOCK_OD.
|
||||
*
|
||||
* Function is used with CANopen OD object at index 1011.
|
||||
*
|
||||
* @param filename Name of the file.
|
||||
*
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int CO_OD_storage_restoreSecure(char *filename);
|
||||
|
||||
|
||||
/**
|
||||
* Object Dictionary storage object.
|
||||
*
|
||||
* Object is used with CANopen OD objects at index 1010 and 1011.
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t *odAddress; /**< From CO_OD_storage_init() */
|
||||
uint32_t odSize; /**< From CO_OD_storage_init() */
|
||||
char *filename; /**< From CO_OD_storage_init() */
|
||||
/** If CO_OD_storage_autoSave() is used, file stays opened and fp is stored here. */
|
||||
FILE *fp;
|
||||
uint16_t tmr1msPrev; /**< used with CO_OD_storage_autoSave. */
|
||||
uint32_t lastSavedMs; /**< used with CO_OD_storage_autoSave. */
|
||||
} CO_OD_storage_t;
|
||||
|
||||
|
||||
/**
|
||||
* Initialize OD storage object and load data from file.
|
||||
*
|
||||
* Called after program startup. Load storage file and copy data to Object
|
||||
* Dictionary variables.
|
||||
*
|
||||
* @param odStor This object will be initialized.
|
||||
* @param odAddress Address of the memory block from Object dictionary, where data will be copied.
|
||||
* @param odSize Size of the above memory block.
|
||||
* @param filename Name of the file, where data are stored.
|
||||
*
|
||||
* @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_DATA_CORRUPT (Data in file corrupt),
|
||||
* CO_ERROR_CRC (CRC from MBR does not match the CRC of OD_ROM block in file),
|
||||
* CO_ERROR_ILLEGAL_ARGUMENT or CO_ERROR_OUT_OF_MEMORY (malloc failed).
|
||||
*/
|
||||
CO_ReturnError_t CO_OD_storage_init(
|
||||
CO_OD_storage_t *odStor,
|
||||
uint8_t *odAddress,
|
||||
uint32_t odSize,
|
||||
char *filename);
|
||||
|
||||
|
||||
/**
|
||||
* Automatically save memory block if differs from file.
|
||||
*
|
||||
* Should be called cyclically by program. It first verifies, if memory block
|
||||
* differs from file and if it does, it saves it to file with two additional
|
||||
* CRC bytes. File remains opened.
|
||||
*
|
||||
* @param odStor OD storage object.
|
||||
* @param timer1ms Variable, which must increment each millisecond.
|
||||
* @param delay Delay (inhibit) time between writes to disk in milliseconds (60000 for example).
|
||||
*
|
||||
* @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_DATA_CORRUPT (Data in file corrupt),
|
||||
* CO_ERROR_ILLEGAL_ARGUMENT or CO_ERROR_OUT_OF_MEMORY (malloc failed).
|
||||
*/
|
||||
CO_ReturnError_t CO_OD_storage_autoSave(
|
||||
CO_OD_storage_t *odStor,
|
||||
uint16_t timer1ms,
|
||||
uint16_t delay);
|
||||
|
||||
|
||||
/**
|
||||
* Closes file opened by CO_OD_storage_autoSave.
|
||||
*
|
||||
* @param odStor OD storage object.
|
||||
*/
|
||||
void CO_OD_storage_autoSaveClose(CO_OD_storage_t *odStor);
|
||||
|
||||
#endif
|
||||
|
|
@ -1,242 +0,0 @@
|
|||
/*
|
||||
* Eeprom object for Linux SocketCAN.
|
||||
*
|
||||
* @file eeprom.c
|
||||
* @author Janez Paternoster
|
||||
* @copyright 2015 Janez Paternoster
|
||||
*
|
||||
* This file is part of CANopenNode, an opensource CANopen Stack.
|
||||
* Project home page is <http://canopennode.sourceforge.net>.
|
||||
* For more information on CANopen see <http://www.can-cia.org/>.
|
||||
*
|
||||
* CANopenNode is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "CO_driver.h"
|
||||
#include "CO_SDO.h"
|
||||
#include "CO_Emergency.h"
|
||||
#include "eeprom.h"
|
||||
#include "crc16-ccitt.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h> /* for memcpy */
|
||||
#include <stdlib.h> /* for malloc, free */
|
||||
|
||||
/* Store parameters ***********************************************************/
|
||||
static CO_SDO_abortCode_t CO_ODF_1010(CO_ODF_arg_t *ODF_arg){
|
||||
CO_EE_t *ee;
|
||||
uint32_t value;
|
||||
CO_SDO_abortCode_t ret = CO_SDO_AB_NONE;
|
||||
|
||||
ee = (CO_EE_t*) ODF_arg->object;
|
||||
value = CO_getUint32(ODF_arg->data);
|
||||
|
||||
if(!ODF_arg->reading){
|
||||
/* don't change the old value */
|
||||
CO_memcpy(ODF_arg->data, (const uint8_t*)ODF_arg->ODdataStorage, 4U);
|
||||
|
||||
if(ODF_arg->subIndex == 1){
|
||||
if(value == 0x65766173UL){
|
||||
/* store parameters */
|
||||
/* rename current file to .old */
|
||||
remove(EE_ROM_FILENAME_OLD);
|
||||
rename(EE_ROM_FILENAME, EE_ROM_FILENAME_OLD);
|
||||
/* open a file */
|
||||
FILE *fp = fopen(EE_ROM_FILENAME, "wb");
|
||||
if(!fp){
|
||||
rename(EE_ROM_FILENAME_OLD, EE_ROM_FILENAME);
|
||||
return CO_SDO_AB_HW;
|
||||
}
|
||||
|
||||
/* write data to the file */
|
||||
fwrite((const void *)ee->OD_ROMAddress, 1, ee->OD_ROMSize, fp);
|
||||
/* write CRC to the end of the file */
|
||||
uint16_t CRC = crc16_ccitt((unsigned char*)ee->OD_ROMAddress, ee->OD_ROMSize, 0);
|
||||
fwrite((const void *)&CRC, 1, 2, fp);
|
||||
fclose(fp);
|
||||
|
||||
/* verify data */
|
||||
void *buf = malloc(ee->OD_ROMSize + 4);
|
||||
if(buf){
|
||||
fp = fopen(EE_ROM_FILENAME, "rb");
|
||||
uint32_t cnt = 0;
|
||||
uint16_t CRC2 = 0;
|
||||
if(fp){
|
||||
cnt = fread(buf, 1, ee->OD_ROMSize, fp);
|
||||
CRC2 = crc16_ccitt((unsigned char*)buf, ee->OD_ROMSize, 0);
|
||||
/* read also two bytes of CRC */
|
||||
cnt += fread(buf, 1, 4, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
free(buf);
|
||||
if(cnt == (ee->OD_ROMSize + 2) && CRC == CRC2){
|
||||
/* write successful */
|
||||
return CO_SDO_AB_NONE;
|
||||
}
|
||||
}
|
||||
/* error, set back the old file */
|
||||
remove(EE_ROM_FILENAME);
|
||||
rename(EE_ROM_FILENAME_OLD, EE_ROM_FILENAME);
|
||||
|
||||
return CO_SDO_AB_HW;
|
||||
}
|
||||
else
|
||||
return CO_SDO_AB_DATA_TRANSF;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Restore default parameters *************************************************/
|
||||
static CO_SDO_abortCode_t CO_ODF_1011(CO_ODF_arg_t *ODF_arg){
|
||||
uint32_t value;
|
||||
CO_SDO_abortCode_t ret = CO_SDO_AB_NONE;
|
||||
|
||||
value = CO_getUint32(ODF_arg->data);
|
||||
|
||||
if(!ODF_arg->reading){
|
||||
/* don't change the old value */
|
||||
CO_memcpy(ODF_arg->data, (const uint8_t*)ODF_arg->ODdataStorage, 4U);
|
||||
|
||||
if(ODF_arg->subIndex >= 1){
|
||||
if(value == 0x64616F6CUL){
|
||||
/* restore default parameters */
|
||||
/* rename current file to .old, so it no longer exist */
|
||||
remove(EE_ROM_FILENAME_OLD);
|
||||
rename(EE_ROM_FILENAME, EE_ROM_FILENAME_OLD);
|
||||
|
||||
/* create an empty file */
|
||||
FILE *fp = fopen(EE_ROM_FILENAME, "wt");
|
||||
if(!fp){
|
||||
rename(EE_ROM_FILENAME_OLD, EE_ROM_FILENAME);
|
||||
return CO_SDO_AB_HW;
|
||||
}
|
||||
/* write one byte '-' to the file */
|
||||
fputc('-', fp);
|
||||
fclose(fp);
|
||||
|
||||
return CO_SDO_AB_NONE;
|
||||
}
|
||||
else
|
||||
return CO_SDO_AB_DATA_TRANSF;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
CO_ReturnError_t CO_EE_init_1(
|
||||
CO_EE_t *ee,
|
||||
uint8_t *SRAMAddress,
|
||||
uint8_t *OD_EEPROMAddress,
|
||||
uint32_t OD_EEPROMSize,
|
||||
uint8_t *OD_ROMAddress,
|
||||
uint32_t OD_ROMSize)
|
||||
{
|
||||
/* verify arguments */
|
||||
if(ee==NULL || OD_EEPROMAddress==NULL || OD_ROMAddress==NULL){
|
||||
return CO_ERROR_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
|
||||
/* configure object variables */
|
||||
ee->pSRAM = (uint32_t*)SRAMAddress;
|
||||
ee->OD_EEPROMAddress = (uint32_t*) OD_EEPROMAddress;
|
||||
ee->OD_EEPROMSize = OD_EEPROMSize / 4;
|
||||
ee->OD_ROMAddress = OD_ROMAddress;
|
||||
ee->OD_ROMSize = OD_ROMSize;
|
||||
ee->OD_EEPROMCurrentIndex = 0;
|
||||
ee->OD_EEPROMWriteEnable = false;
|
||||
|
||||
/* read the CO_OD_EEPROM from SRAM, first verify, if data are OK */
|
||||
if(ee->pSRAM == 0) return CO_ERROR_OUT_OF_MEMORY;
|
||||
uint32_t firstWordRAM = *(ee->OD_EEPROMAddress);
|
||||
uint32_t firstWordEE = *(ee->pSRAM);
|
||||
uint32_t lastWordEE = *(ee->pSRAM + ee->OD_EEPROMSize - 1);
|
||||
if(firstWordRAM == firstWordEE && firstWordRAM == lastWordEE){
|
||||
unsigned int i;
|
||||
for(i=0; i<ee->OD_EEPROMSize; i++)
|
||||
(ee->OD_EEPROMAddress)[i] = (ee->pSRAM)[i];
|
||||
}
|
||||
ee->OD_EEPROMWriteEnable = true;
|
||||
|
||||
/* read the CO_OD_ROM from file and verify CRC */
|
||||
void *buf = malloc(ee->OD_ROMSize);
|
||||
if(buf){
|
||||
CO_ReturnError_t ret = CO_ERROR_NO;
|
||||
FILE *fp = fopen(EE_ROM_FILENAME, "rb");
|
||||
uint32_t cnt = 0;
|
||||
uint16_t CRC[2];
|
||||
if(fp){
|
||||
cnt = fread(buf, 1, ee->OD_ROMSize, fp);
|
||||
/* read also two bytes of CRC from file */
|
||||
cnt += fread(&CRC[0], 1, 4, fp);
|
||||
CRC[1] = crc16_ccitt((unsigned char*)buf, ee->OD_ROMSize, 0);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
if(cnt == 1 && *((char*)buf) == '-'){
|
||||
ret = CO_ERROR_NO; /* file is empty, default values will be used, no error */
|
||||
}
|
||||
else if(cnt != (ee->OD_ROMSize + 2)){
|
||||
ret = CO_ERROR_DATA_CORRUPT; /* file length does not match */
|
||||
}
|
||||
else if(CRC[0] != CRC[1]){
|
||||
ret = CO_ERROR_CRC; /* CRC does not match */
|
||||
}
|
||||
else{
|
||||
/* no errors, copy data into object dictionary */
|
||||
memcpy(ee->OD_ROMAddress, buf, ee->OD_ROMSize);
|
||||
}
|
||||
|
||||
free(buf);
|
||||
return ret;
|
||||
}
|
||||
else return CO_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
return CO_ERROR_NO;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
void CO_EE_init_2(
|
||||
CO_EE_t *ee,
|
||||
CO_ReturnError_t eeStatus,
|
||||
CO_SDO_t *SDO,
|
||||
CO_EM_t *em)
|
||||
{
|
||||
CO_OD_configure(SDO, OD_H1010_STORE_PARAM_FUNC, CO_ODF_1010, (void*)ee, 0, 0U);
|
||||
CO_OD_configure(SDO, OD_H1011_REST_PARAM_FUNC, CO_ODF_1011, (void*)ee, 0, 0U);
|
||||
if(eeStatus != CO_ERROR_NO){
|
||||
CO_errorReport(em, CO_EM_NON_VOLATILE_MEMORY, CO_EMC_HARDWARE, (uint32_t)eeStatus);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
void CO_EE_process(CO_EE_t *ee){
|
||||
if(ee && ee->OD_EEPROMWriteEnable){
|
||||
/* verify next word */
|
||||
unsigned int i = ee->OD_EEPROMCurrentIndex;
|
||||
if(++i == ee->OD_EEPROMSize) i = 0;
|
||||
ee->OD_EEPROMCurrentIndex = i;
|
||||
|
||||
/* update SRAM */
|
||||
(ee->pSRAM)[i] = (ee->OD_EEPROMAddress)[i];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
/*
|
||||
* Eeprom object for Linux SocketCAN.
|
||||
*
|
||||
* @file eeprom.h
|
||||
* @author Janez Paternoster
|
||||
* @copyright 2015 Janez Paternoster
|
||||
*
|
||||
* This file is part of CANopenNode, an opensource CANopen Stack.
|
||||
* Project home page is <http://canopennode.sourceforge.net>.
|
||||
* For more information on CANopen see <http://www.can-cia.org/>.
|
||||
*
|
||||
* CANopenNode is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef EEPROM_H
|
||||
#define EEPROM_H
|
||||
|
||||
|
||||
/* For documentation see file drvTemplate/eeprom.h */
|
||||
|
||||
|
||||
/*
|
||||
* Linux specific
|
||||
*
|
||||
* Usage of device file system or SRAM for storing non-volatile variables.
|
||||
*
|
||||
* Two blocks of CANopen Object Dictionary data are stored as non-volatile:
|
||||
* OD_EEPROM - Stored is in internal battery powered SRAM from address 0. Data
|
||||
* are stored automatically on change. No data corruption control
|
||||
* is made. Data are load on startup.
|
||||
* OD_ROM - Stored in file named "OD_ROM01.dat". Data integrity is
|
||||
* verified with CRC.
|
||||
* Data are stored on special CANopen command - Writing 0x65766173
|
||||
* into Object dictionary (index 1010, subindex 1). Default values
|
||||
* are restored after reset, if writing 0x64616F6C into (1011, 1).
|
||||
* Backup is stored to "OD_ROM01.old".
|
||||
*/
|
||||
|
||||
|
||||
/* Filename */
|
||||
#ifndef EE_ROM_FILENAME
|
||||
#define EE_ROM_FILENAME "OD_ROM01.dat"
|
||||
#endif
|
||||
#ifndef EE_ROM_FILENAME_OLD
|
||||
#define EE_ROM_FILENAME_OLD "OD_ROM01.old"
|
||||
#endif
|
||||
|
||||
|
||||
/* Eeprom object */
|
||||
typedef struct{
|
||||
uint32_t *OD_EEPROMAddress;
|
||||
uint32_t OD_EEPROMSize;
|
||||
uint8_t *OD_ROMAddress;
|
||||
uint32_t OD_ROMSize;
|
||||
uint32_t *pSRAM; /* SC243 specific: Pointer to start
|
||||
address of the battery powered SRAM */
|
||||
uint32_t OD_EEPROMCurrentIndex;
|
||||
bool_t OD_EEPROMWriteEnable;
|
||||
}CO_EE_t;
|
||||
|
||||
|
||||
/* First part of eeprom initialization.
|
||||
* @param SRAMAddress Address of battery powered SRAM memory.
|
||||
*/
|
||||
CO_ReturnError_t CO_EE_init_1(
|
||||
CO_EE_t *ee,
|
||||
uint8_t *SRAMAddress,
|
||||
uint8_t *OD_EEPROMAddress,
|
||||
uint32_t OD_EEPROMSize,
|
||||
uint8_t *OD_ROMAddress,
|
||||
uint32_t OD_ROMSize);
|
||||
|
||||
|
||||
/* Second part of eeprom initialization. */
|
||||
void CO_EE_init_2(
|
||||
CO_EE_t *ee,
|
||||
CO_ReturnError_t eeStatus,
|
||||
CO_SDO_t *SDO,
|
||||
CO_EM_t *em);
|
||||
|
||||
|
||||
/* Process eeprom object. */
|
||||
void CO_EE_process(CO_EE_t *ee);
|
||||
|
||||
|
||||
#endif
|
||||
Loading…
Reference in a new issue