Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions CANopenNode_STM32/CO_app_STM32.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
#include <stdio.h>
#include <inttypes.h>

#include "CO_storageBlank.h"
#include "storage/CO_storageEeprom.h"
Comment thread
TheCitrusMan marked this conversation as resolved.
#include "OD.h"

CANopenNodeSTM32*
Expand All @@ -57,6 +57,7 @@ CO_t* CO = NULL; /* CANopen object */
// Global variables
uint32_t time_old, time_current;
CO_ReturnError_t err;
uint32_t storageInitError = 0;

/* This function will basically setup the CANopen node */
int
Expand All @@ -73,7 +74,6 @@ canopen_app_init(CANopenNodeSTM32* _canopenNodeSTM32) {
.attr = CO_storage_cmd | CO_storage_restore,
.addrNV = NULL}};
uint8_t storageEntriesCount = sizeof(storageEntries) / sizeof(storageEntries[0]);
uint32_t storageInitError = 0;
#endif

/* Allocate memory */
Expand All @@ -99,13 +99,15 @@ canopen_app_init(CANopenNodeSTM32* _canopenNodeSTM32) {
canopenNodeSTM32->canOpenStack = CO;

#if (CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE
err = CO_storageBlank_init(&storage, CO->CANmodule, OD_ENTRY_H1010_storeParameters,
OD_ENTRY_H1011_restoreDefaultParameters, storageEntries, storageEntriesCount,
&storageInitError);

if (err != CO_ERROR_NO && err != CO_ERROR_DATA_CORRUPT) {
log_printf("Error: Storage %d\n", storageInitError);
return 2;
err = CO_storageEeprom_init(&storage, CO->CANmodule, NULL,
OD_ENTRY_H1010_storeParameters,
OD_ENTRY_H1011_restoreDefaultParameters, storageEntries,
storageEntriesCount, &storageInitError);

if (err != CO_ERROR_NO && err != CO_ERROR_DATA_CORRUPT)
{
log_printf("Error: Storage %" PRIu32 "\n", storageInitError);
return 2;
}
#endif

Expand Down
2 changes: 1 addition & 1 deletion CANopenNode_STM32/CO_driver_STM32.c
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,7 @@ CO_CANclearPendingSyncPDOs(CO_CANmodule_t* CANmodule) {
/******************************************************************************/
/* Get error counters from the module. If necessary, function may use
* different way to determine errors. */
static uint16_t rxErrors = 0, txErrors = 0, overflow = 0;
// static uint16_t rxErrors = 0, txErrors = 0, overflow = 0;

void
CO_CANmodule_process(CO_CANmodule_t* CANmodule) {
Expand Down
7 changes: 6 additions & 1 deletion CANopenNode_STM32/CO_driver_target.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
#error This STM32 Do not support CAN or FDCAN
#endif

#undef CO_CONFIG_STORAGE_ENABLE // We don't need Storage option, implement based on your use case and remove this line from here
// #undef CO_CONFIG_STORAGE_ENABLE // We don't need Storage option, implement based on your use case and remove this line from here

#ifdef CO_DRIVER_CUSTOM
#include "CO_driver_custom.h"
Expand Down Expand Up @@ -134,6 +134,11 @@ typedef struct {
uint8_t attr;
/* Additional variables (target specific) */
void* addrNV;
void * storageModule;
uint16_t crc;
size_t eepromAddrSignature;
size_t eepromAddr;
size_t offset;
} CO_storage_entry_t;

/* (un)lock critical section in CO_CANsend() */
Expand Down
216 changes: 216 additions & 0 deletions CANopenNode_STM32/CO_eeprom_STM32.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
/*
* Eeprom interface for use with CO_storageEeprom, STM32 specific
*
* @file CO_eeprom_STM32.c
* @author Marc Vandenhende
* @copyright 2025 Marc Vandenhende
*
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
* For more information on CANopen see <http://www.can-cia.org/>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <string.h>

#include "storage/CO_storage.h"
#include "storage/CO_eeprom.h"
#include "301/crc16-ccitt.h"
#include "CO_app_STM32.h"
#include "OD.h"

#include "eeprom.h"
#include "CO_eeprom_STM32.h"

#if ((CO_CONFIG_STORAGE)&CO_CONFIG_STORAGE_ENABLE) != 0

/*
* Eeprom is configured for auto storage variables. Second half of
* memory locations is used for storage on command. Below are two
* internal variables, used for indicating next free address in eeprom, one for
* autonomous storage and one for protected storage
*/
static size_t eepromAddrNextAuto = 0;
static size_t eepromAddrNextProt = 0;

/******************************************************************************/
bool_t CO_eeprom_init(void *storageModule)
{
// initialize canopen eeprom storage
if (!Eeprom_Init())
{
return false;
}

eepromAddrNextAuto = device_start_co_auto;
eepromAddrNextProt = device_start_co_prot;
OD_PERSIST_COMM.x1018_identity.serialNumber = device_serial_number;

/* If eeprom chip is OK, this will return "true" */
return (HAL_I2C_IsDeviceReady(HI2C_EEPROM, device_i2c_address << 1, 3, I2C_TIMEOUT_MS) == HAL_OK);
}

/******************************************************************************/
size_t CO_eeprom_getAddr(void *storageModule, bool_t isAuto, size_t len, bool_t *overflow)
{
size_t addr;

if (isAuto)
{
/* auto storage is processed byte by byte, no alignment necessary */
addr = eepromAddrNextAuto;
eepromAddrNextAuto += len;
if (eepromAddrNextAuto >= device_start_co_auto + (device_size_co / 2))
{
*overflow = true;
}
}
else
{
/* addresses for storage on command must be page aligned */
addr = eepromAddrNextProt;
size_t lenAligned = len & (~(page_size - 1));
if (lenAligned < len)
{
lenAligned += page_size;
}
eepromAddrNextProt += lenAligned;
if (eepromAddrNextProt >= device_start_co_prot + (device_size_co / 2))
{
*overflow = true;
}
}

return addr;
}

/******************************************************************************/
void CO_eeprom_readBlock(void *storageModule, uint8_t *data, size_t eepromAddr, size_t len)
{
HAL_I2C_Mem_Read(HI2C_EEPROM, device_i2c_address << 1, eepromAddr, 2, (uint8_t *) data, len, I2C_TIMEOUT_MS);
}

/******************************************************************************/
bool_t CO_eeprom_writeBlock(void *storageModule, uint8_t *data, size_t eepromAddr, size_t len)
{
uint32_t idx = 0;
uint32_t eep_write_timer = 0;

if (HAL_I2C_IsDeviceReady(HI2C_EEPROM, device_i2c_address << 1, 3, I2C_TIMEOUT_MS) != HAL_OK)
{
// device not ready
return false;
}

while (len > 0)
{
size_t len_x = len;
if (len_x > page_size)
len_x = page_size;

if (HAL_I2C_Mem_Write(HI2C_EEPROM, device_i2c_address << 1, eepromAddr, 2, (uint8_t *) data + idx, len_x,
I2C_TIMEOUT_MS) != HAL_OK)
{
// write command error
return false;
}

eepromAddr += page_size;
idx += page_size;

if (len > page_size)
len -= page_size;
else
len = 0;

/* wait for completion of the write operation */
eep_write_timer = HAL_GetTick();
while (HAL_I2C_IsDeviceReady(HI2C_EEPROM, device_i2c_address << 1, 3, I2C_TIMEOUT_MS) != HAL_OK)
{
// device not ready yet
if (HAL_GetTick() - eep_write_timer >= EEPROM_WRITE_TIME + 1)
// + 1 to compensate for HAL_GetTick() millisecond jitter
{
// time out
return false;
}
}
}
return true;
}

/******************************************************************************/
uint16_t CO_eeprom_getCrcBlock(void *storageModule, size_t eepromAddr, size_t len)
{
#define BUF_SIZE 250

uint16_t crc = 0;
uint8_t buf[BUF_SIZE];
uint8_t subLen;

while (len > 0)
{
if (len <= BUF_SIZE)
subLen = len;
else
subLen = BUF_SIZE;

/* update crc from data part */
HAL_I2C_Mem_Read(HI2C_EEPROM, device_i2c_address << 1, eepromAddr, 2, buf, subLen, I2C_TIMEOUT_MS);
crc = crc16_ccitt(buf, subLen, crc);
eepromAddr += subLen;
len -= subLen;
}

// return invalid CRC when CAN_CFG jumper is placed
if (!HAL_GPIO_ReadPin(CAN_CFG_GPIO_Port, CAN_CFG_Pin))
{
crc = ~crc;
}

return crc;
}

/******************************************************************************/
bool_t CO_eeprom_updateByte(void * storageModule, uint8_t data,
size_t eepromAddr)
{
uint8_t buf;

if (HAL_I2C_IsDeviceReady(HI2C_EEPROM, device_i2c_address << 1, 3, I2C_TIMEOUT_MS)
!= HAL_OK)
{
return false;
}

/* read data byte from eeprom */
if (HAL_I2C_Mem_Read(HI2C_EEPROM, device_i2c_address << 1, eepromAddr, 2, &buf, 1, I2C_TIMEOUT_MS) != HAL_OK)
{
return false;
}

/* If data in EEPROM differs, then write it to EEPROM.
* Don't wait for write to complete */
if (buf != data)
{
if (HAL_I2C_Mem_Write(HI2C_EEPROM, device_i2c_address << 1, eepromAddr, 2, &data, 1, I2C_TIMEOUT_MS) != HAL_OK)
{
return false;
}
}

return true;
}

#endif /* (CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE */
11 changes: 11 additions & 0 deletions CANopenNode_STM32/CO_eeprom_STM32.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* CO_eeprom_STM32.h
*
* Created on: 23 Nov 2025
* Author: Marc Vandenhende
*/

#ifndef CO_EEPROM_STM32_H_
#define CO_EEPROM_STM32_H_

#endif /* CO_EEPROM_STM32_H_ */
Loading