From c05ab364a17e2bf0efd2e0b8fd800b4ed2cb68dc Mon Sep 17 00:00:00 2001 From: TheCitrusMan Date: Fri, 26 Jun 2026 11:44:21 +0200 Subject: [PATCH 01/14] stm32g0xx_fdcan example project updated with eeprom storage routines Modified Canopen files: CO_app_STM32.c : > updated include references > CO_storageBlank_init replaced by CO_storageEeprom_init > uint32_t storageInitError moved to global scope CO_driver_target.h : > #undef CO_CONFIG_STORAGE_ENABLE commented out > CO_storage_entry_t added extra parameters CO_eeprom_STM32.c and CO_eeprom_STM32.h added to project. These files contain the main eeprom storage routines and are based on the PIC32 example. CO_storageBlank.c and CO_storageBlank.h removed from project. eeprom.c and eeprom.h files added to project. These files contain the low level eeprom routines. i2c.h in core project: > added a couple of defines --- CANopenNode_STM32/CO_app_STM32.c | 18 +- CANopenNode_STM32/CO_driver_target.h | 7 +- CANopenNode_STM32/CO_eeprom_STM32.c | 209 ++++++++++++++++++++++ CANopenNode_STM32/CO_eeprom_STM32.h | 11 ++ CANopenNode_STM32/CO_storageBlank.c | 96 ---------- CANopenNode_STM32/CO_storageBlank.h | 58 ------ CANopenNode_STM32/eeprom.c | 223 ++++++++++++++++++++++++ CANopenNode_STM32/eeprom.h | 40 +++++ examples/stm32g0xx_fdcan/Core/Inc/i2c.h | 3 +- 9 files changed, 501 insertions(+), 164 deletions(-) create mode 100644 CANopenNode_STM32/CO_eeprom_STM32.c create mode 100644 CANopenNode_STM32/CO_eeprom_STM32.h delete mode 100644 CANopenNode_STM32/CO_storageBlank.c delete mode 100644 CANopenNode_STM32/CO_storageBlank.h create mode 100644 CANopenNode_STM32/eeprom.c create mode 100644 CANopenNode_STM32/eeprom.h diff --git a/CANopenNode_STM32/CO_app_STM32.c b/CANopenNode_STM32/CO_app_STM32.c index ec16162..900a997 100644 --- a/CANopenNode_STM32/CO_app_STM32.c +++ b/CANopenNode_STM32/CO_app_STM32.c @@ -30,7 +30,7 @@ #include #include -#include "CO_storageBlank.h" +#include "storage/CO_storageEeprom.h" #include "OD.h" CANopenNodeSTM32* @@ -55,6 +55,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 @@ -71,7 +72,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 */ @@ -97,12 +97,14 @@ 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); + 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 %ld\n", storageInitError); return 2; } #endif diff --git a/CANopenNode_STM32/CO_driver_target.h b/CANopenNode_STM32/CO_driver_target.h index 90855ab..83bf8aa 100644 --- a/CANopenNode_STM32/CO_driver_target.h +++ b/CANopenNode_STM32/CO_driver_target.h @@ -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" @@ -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() */ diff --git a/CANopenNode_STM32/CO_eeprom_STM32.c b/CANopenNode_STM32/CO_eeprom_STM32.c new file mode 100644 index 0000000..183388f --- /dev/null +++ b/CANopenNode_STM32/CO_eeprom_STM32.c @@ -0,0 +1,209 @@ +/* + * 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 . + * 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_storage.h" +#include "storage/CO_eeprom.h" +#include "301/crc16-ccitt.h" +#include "CO_app_STM32.h" +#include "OD.h" + +#include "i2c.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_CO()) + { + return false; + } + + eepromAddrNextAuto = device_start_co_auto; + eepromAddrNextProt = device_start_co_prot; + OD_PERSIST_COMM.x1018_identity.serialNumber = device_serial_number; + + // invalidate eeprom contents if CAN_CFG jumper placed + + if (HAL_GPIO_ReadPin(JOY_DOWN_GPIO_Port, JOY_DOWN_Pin)) + { + uint8_t eraseBuf[32]; + memset(eraseBuf, 0xff, 32); + HAL_I2C_Mem_Write(HI2C, device_i2c_address << 1, eepromAddrNextProt, 2, eraseBuf, 32, I2C_TIMEOUT_MS); + HAL_Delay(5); // write operation timer + } + + /* If eeprom chip is OK, this will pass, otherwise timeout */ + return (HAL_I2C_IsDeviceReady(HI2C, device_i2c_address << 1, 3, I2C_TIMEOUT_MS) == HAL_OK); + // return "true" if device ready +} + +/******************************************************************************/ +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, 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; + + if (HAL_I2C_IsDeviceReady(HI2C, 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, 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 */ + while (HAL_I2C_IsDeviceReady(HI2C, device_i2c_address << 1, 3, I2C_TIMEOUT_MS) != HAL_OK); + } + 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, device_i2c_address << 1, eepromAddr, 2, buf, subLen, I2C_TIMEOUT_MS); + crc = crc16_ccitt(buf, subLen, crc); + eepromAddr += BUF_SIZE; + len -= subLen; + } + + return crc; +} + +/******************************************************************************/ +bool_t CO_eeprom_updateByte(void * storageModule, uint8_t data, + size_t eepromAddr) +{ + uint8_t buf; + + if (HAL_I2C_IsDeviceReady(HI2C, device_i2c_address << 1, 3, I2C_TIMEOUT_MS) + != HAL_OK) + { + return false; + } + + /* read data byte from eeprom */ + if (HAL_I2C_Mem_Read(HI2C, 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, 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 */ diff --git a/CANopenNode_STM32/CO_eeprom_STM32.h b/CANopenNode_STM32/CO_eeprom_STM32.h new file mode 100644 index 0000000..719c792 --- /dev/null +++ b/CANopenNode_STM32/CO_eeprom_STM32.h @@ -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_ */ diff --git a/CANopenNode_STM32/CO_storageBlank.c b/CANopenNode_STM32/CO_storageBlank.c deleted file mode 100644 index dc608e2..0000000 --- a/CANopenNode_STM32/CO_storageBlank.c +++ /dev/null @@ -1,96 +0,0 @@ -/* - * CANopen Object Dictionary storage object (blank example). - * - * @file CO_storageBlank.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 "CO_storageBlank.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 -storeBlank(CO_storage_entry_t* entry, CO_CANmodule_t* CANmodule) { - - /* Open a file and write data to it */ - /* file = open(entry->pathToFileOrPointerToMemory); */ - CO_LOCK_OD(CANmodule); - /* write(entry->addr, entry->len, file); */ - CO_UNLOCK_OD(CANmodule); - - 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 -restoreBlank(CO_storage_entry_t* entry, CO_CANmodule_t* CANmodule) { - - /* disable (delete) the file, so default values will stay after startup */ - - return ODR_OK; -} - -CO_ReturnError_t -CO_storageBlank_init(CO_storage_t* storage, CO_CANmodule_t* CANmodule, 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; - - /* verify arguments */ - if (storage == NULL || entries == NULL || entriesCount == 0 || storageInitError == NULL) { - return CO_ERROR_ILLEGAL_ARGUMENT; - } - - /* initialize storage and OD extensions */ - ret = CO_storage_init(storage, CANmodule, OD_1010_StoreParameters, OD_1011_RestoreDefaultParam, storeBlank, - restoreBlank, entries, entriesCount); - if (ret != CO_ERROR_NO) { - return ret; - } - - /* initialize entries */ - *storageInitError = 0; - for (uint8_t i = 0; i < entriesCount; i++) { - CO_storage_entry_t* entry = &entries[i]; - - /* verify arguments */ - if (entry->addr == NULL || entry->len == 0 || entry->subIndexOD < 2) { - *storageInitError = i; - return CO_ERROR_ILLEGAL_ARGUMENT; - } - - /* Open a file and read data from file to entry->addr */ - /* file = open(entry->pathToFileOrPointerToMemory); */ - /* read(entry->addr, entry->len, file); */ - } - - return ret; -} - -#endif /* (CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE */ diff --git a/CANopenNode_STM32/CO_storageBlank.h b/CANopenNode_STM32/CO_storageBlank.h deleted file mode 100644 index c53f3af..0000000 --- a/CANopenNode_STM32/CO_storageBlank.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * CANopen data storage object (blank example) - * - * @file CO_storageBlank.h - * @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_BLANK_H -#define CO_STORAGE_BLANK_H - -#include "storage/CO_storage.h" - -#if ((CO_CONFIG_STORAGE)&CO_CONFIG_STORAGE_ENABLE) || defined CO_DOXYGEN - -#ifdef __cplusplus -extern "C" { -#endif - -/* 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, - OD_entry_t* OD_1010_StoreParameters, OD_entry_t* OD_1011_RestoreDefaultParam, - CO_storage_entry_t* entries, uint8_t entriesCount, uint32_t* storageInitError); - -uint32_t CO_storageBlank_auto_process(CO_storage_t* storage, bool_t closeFiles); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* (CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE */ - -#endif /* CO_STORAGE_BLANK_H */ diff --git a/CANopenNode_STM32/eeprom.c b/CANopenNode_STM32/eeprom.c new file mode 100644 index 0000000..aea48d2 --- /dev/null +++ b/CANopenNode_STM32/eeprom.c @@ -0,0 +1,223 @@ +/* + * eeprom.c + * + * Created on: 11 jun 2026 + * Author: Marc Vandenhende + */ + +#include +#include "eeprom.h" +#include "i2c.h" +#include "CO_app_STM32.h" +#include "OD.h" + +/* + * Hardware definition + */ +#define CO_EEP_MAX_STORAGE 0x2000 // Max number of bytes reserved for CanOpen storage + +#define EEP_MEM_I2C_ADDR 0x50 +#define EEP_UID_I2C_ADDR 0x58 // For ST eeprom UID + +#define ST_MFR_CODE 0x20 +#define ST_BUS_PROTOCOL 0xE0 +#define ST_UID_SIZE 16 + +#define MC_SERIAL_ADDR 0x7ffa +#define MC_MFR_CODE 0x29 +#define MC_DEVICE_CODE 0x48 +#define MC_EEP_SERIAL_SIZE 6 + +typedef struct +{ + uint8_t density; + size_t storage; + size_t page_size; +} eeprom_st_t; + +/* + * Eeprom device table + * For each recognized type, the table contains the maximum + * device capacity and the page size for eeprom writing + */ +const eeprom_st_t eeprom_st[] = +{ + {0x0c, 0x1000, 32}, // m24c32-u + {0x0d, 0x2000, 32}, // m24c64-u + {0x0e, 0x4000, 64}, // m24128-u + {0x0f, 0x8000, 64}, // m24256-u + {0x10, 0x10000, 128}, // m24512-u + {0x11, 0x20000, 256}, // m24m01e-u + {0x12, 0x40000, 256} // m24m02e-u +}; + +bool eeprom_initialized = false; + +uint16_t device_size = 0; +size_t page_size = 0; +uint16_t device_i2c_address = EEP_MEM_I2C_ADDR; +uint32_t device_serial_number = 0; + +uint16_t data_storage_start = 0; +uint16_t data_storage_size = 0; + +size_t device_start_co_auto = 0; +size_t device_start_co_prot = 0; +size_t device_size_co = 0; + +static bool_t eeprom_init_mc(); +static bool_t eeprom_init_st(); + +/* + * Main EEPROM initialization routine + * returns "true" if initialization is success + * returns "false" if initialization fails or no eeprom present + */ +bool Eeprom_Init() +{ + if (!eeprom_init_mc()) + { + if (!eeprom_init_st()) + { + // no EEPROM found + return false; + } + } + + /* If eeprom chip is OK, this will pass, otherwise timeout */ + eeprom_initialized = HAL_I2C_IsDeviceReady(HI2C, EEP_MEM_I2C_ADDR << 1, 3, I2C_TIMEOUT_MS) == HAL_OK; + + return eeprom_initialized; +} + +/* + * Canopen EEPROM initialization routine + * Assumes eeprom_init() was already called before. + * returns "true" if initialization is success + * returns "false" if initialization fails or no eeprom present + */ +bool Eeprom_Init_CO() +{ + if (!eeprom_initialized) + return false; + + OD_PERSIST_COMM.x1018_identity.serialNumber = device_serial_number; + /* If eeprom chip is OK, this will pass, otherwise timeout */ + return (HAL_I2C_IsDeviceReady(HI2C, EEP_MEM_I2C_ADDR << 1, 3, I2C_TIMEOUT_MS) == HAL_OK); + // return "true" if device ready +} + +/* + * Try to initialize ST eeprom + * Looks for the right device and sets parameters accordingly + * returns "true" if initialization is success + * returns "false" if no ST eeprom found + */ +static bool_t eeprom_init_st() +{ + uint8_t uid[ST_UID_SIZE]; + + /* If eeprom chip is OK, this will pass, otherwise timeout */ + if (HAL_I2C_IsDeviceReady(HI2C, EEP_UID_I2C_ADDR << 1, 3, I2C_TIMEOUT_MS) != HAL_OK) + return false; // return "false" if device not ready + + if (HAL_I2C_Mem_Read(HI2C, EEP_UID_I2C_ADDR << 1, 0, 2, uid, ST_UID_SIZE, I2C_TIMEOUT_MS) != HAL_OK) + return false; // return "false" if device does not respond + + // Check if STM EEPROM device + if ((uid[0] == ST_MFR_CODE) && (uid[1] == ST_BUS_PROTOCOL)) + { + // loop through table, set parameters when match found + for (int i = 0; i < sizeof(eeprom_st) / sizeof(eeprom_st_t); i++) + // loop through table, set parameters when match found + { + if (eeprom_st[i].density == uid[2]) + { + device_size = eeprom_st[i].storage; + page_size = eeprom_st[i].page_size; + + // set serial number + device_serial_number = (uint32_t) uid[12] << 24 + | (uint32_t) uid[13] << 16 + | (uint32_t) uid[14] << 8 + | (uint32_t) uid[15]; + + // If device > 8 kbytes, use first 8 kbytes of storage for CanOpen + if (device_size > CO_EEP_MAX_STORAGE) + { + // canopen device size values + device_size_co = CO_EEP_MAX_STORAGE; + device_start_co_auto = 0x0000; + device_start_co_prot = CO_EEP_MAX_STORAGE / 2; + + // general storage values + data_storage_start = CO_EEP_MAX_STORAGE; + data_storage_size = device_size - CO_EEP_MAX_STORAGE; + } + else + { + // canopen device size values + device_size_co = device_size; + device_start_co_auto = 0x0000; + device_start_co_prot = device_size_co / 2; + + // general storage values -> no storage available + data_storage_start = device_size_co; + data_storage_size = 0x0000; + } + + return true; + } + } + } + + return false; +} + +/* + * Try to initialize Microchip eeprom + * Check if device is 24AA256UID and sets parameters accordingly + * returns "true" if initialization is success + * returns "false" if no ST eeprom found + */ +static bool_t eeprom_init_mc() +{ + uint8_t serial[MC_EEP_SERIAL_SIZE]; + + /* If eeprom chip is OK, this will pass, otherwise timeout */ + if (HAL_I2C_IsDeviceReady(HI2C, EEP_MEM_I2C_ADDR << 1, 3, I2C_TIMEOUT_MS) != HAL_OK) + return false; // return "false" if device not ready + + if (HAL_I2C_Mem_Read(HI2C, EEP_MEM_I2C_ADDR << 1, MC_SERIAL_ADDR, 2, serial, MC_EEP_SERIAL_SIZE, I2C_TIMEOUT_MS) != + HAL_OK) + return false; // return "false" if device does not respond + + // Check if STM EEPROM device + if ((serial[0] == MC_MFR_CODE) && (serial[1] == MC_DEVICE_CODE)) + { + // set serial number + device_serial_number = (uint32_t) serial[2] << 24 + | (uint32_t) serial[3] << 16 + | (uint32_t) serial[4] << 8 + | (uint32_t) serial[5]; + + // device size is 32 kbytes (0x8000) + // but range 0x7000 -> 0x7fff not available for storage + // using first 8 kbytes of storage for CanOpen + device_size = 0x7000; + page_size = 64; + + // canopen device size values + device_size_co = CO_EEP_MAX_STORAGE; + device_start_co_auto = 0x0000; + device_start_co_prot = CO_EEP_MAX_STORAGE / 2; + + // general storage values + data_storage_start = CO_EEP_MAX_STORAGE; + data_storage_size = device_size - CO_EEP_MAX_STORAGE; + + return true; + } + + return false; +} diff --git a/CANopenNode_STM32/eeprom.h b/CANopenNode_STM32/eeprom.h new file mode 100644 index 0000000..e723d63 --- /dev/null +++ b/CANopenNode_STM32/eeprom.h @@ -0,0 +1,40 @@ +/* + * eeprom.h + * + * Created on: 11 jun 2026 + * Author: Marc Vandenhende + */ + +#ifndef INC_EEPROM_H_ +#define INC_EEPROM_H_ + +#ifdef __cplusplus +extern "C" +{ + + +#endif + +/* USER CODE BEGIN Includes */ +#include "main.h" + +bool Eeprom_Init(); +bool Eeprom_Init_CO(); + +extern uint16_t device_size; +extern size_t page_size; +extern uint16_t device_i2c_address; +extern uint32_t device_serial_number; + +extern uint16_t data_storage_start; +extern uint16_t data_storage_size; + +extern size_t device_start_co_auto; +extern size_t device_start_co_prot; +extern size_t device_size_co; + +#ifdef __cplusplus +} +#endif + +#endif /* INC_EEPROM_H_ */ diff --git a/examples/stm32g0xx_fdcan/Core/Inc/i2c.h b/examples/stm32g0xx_fdcan/Core/Inc/i2c.h index 38fae49..de9843f 100644 --- a/examples/stm32g0xx_fdcan/Core/Inc/i2c.h +++ b/examples/stm32g0xx_fdcan/Core/Inc/i2c.h @@ -35,7 +35,8 @@ extern "C" { extern I2C_HandleTypeDef hi2c1; /* USER CODE BEGIN Private defines */ - +#define I2C_TIMEOUT_MS 500 +#define HI2C &hi2c1 /* USER CODE END Private defines */ void MX_I2C1_Init(void); From 99fa8bd0f4e26ac1eebe9c16cf50f3877e412dcf Mon Sep 17 00:00:00 2001 From: TheCitrusMan Date: Fri, 26 Jun 2026 14:32:07 +0200 Subject: [PATCH 02/14] Applied some modifications/fixes based on the Github Copilot code review : CO_eeprom_STM32.c : > #include added > HAL_I2C_IsDeviceReady() timeout eeprom.c : > replaced size_t by uint32_t / uint16_t specifiers > removed support for m24m01e-u and m24m02e-u eeprom types > as they cannot be fully adressed by 16-bit adresses eeprom.h : > adjusted type specifiers for external variables > #include added main.c : > #include "eeprom.h" added > Added missing Eeprom_Init() --- CANopenNode_STM32/CO_eeprom_STM32.c | 8 +++++++- CANopenNode_STM32/eeprom.c | 20 +++++++++----------- CANopenNode_STM32/eeprom.h | 15 ++++++++------- examples/stm32g0xx_fdcan/Core/Src/main.c | 2 ++ 4 files changed, 26 insertions(+), 19 deletions(-) diff --git a/CANopenNode_STM32/CO_eeprom_STM32.c b/CANopenNode_STM32/CO_eeprom_STM32.c index 183388f..002cbe4 100644 --- a/CANopenNode_STM32/CO_eeprom_STM32.c +++ b/CANopenNode_STM32/CO_eeprom_STM32.c @@ -22,6 +22,8 @@ * limitations under the License. */ +#include + #include "storage/CO_storage.h" #include "storage/CO_eeprom.h" #include "301/crc16-ccitt.h" @@ -144,7 +146,11 @@ bool_t CO_eeprom_writeBlock(void *storageModule, uint8_t *data, size_t eepromAdd len = 0; /* wait for completion of the write operation */ - while (HAL_I2C_IsDeviceReady(HI2C, device_i2c_address << 1, 3, I2C_TIMEOUT_MS) != HAL_OK); + if (HAL_I2C_IsDeviceReady(HI2C, device_i2c_address << 1, 10, I2C_TIMEOUT_MS) != HAL_OK); + { + // device not ready + return false; + } } return true; } diff --git a/CANopenNode_STM32/eeprom.c b/CANopenNode_STM32/eeprom.c index aea48d2..518f01d 100644 --- a/CANopenNode_STM32/eeprom.c +++ b/CANopenNode_STM32/eeprom.c @@ -31,8 +31,8 @@ typedef struct { uint8_t density; - size_t storage; - size_t page_size; + uint32_t storage; + uint16_t page_size; } eeprom_st_t; /* @@ -47,23 +47,21 @@ const eeprom_st_t eeprom_st[] = {0x0e, 0x4000, 64}, // m24128-u {0x0f, 0x8000, 64}, // m24256-u {0x10, 0x10000, 128}, // m24512-u - {0x11, 0x20000, 256}, // m24m01e-u - {0x12, 0x40000, 256} // m24m02e-u }; bool eeprom_initialized = false; -uint16_t device_size = 0; -size_t page_size = 0; +uint32_t device_size = 0; +uint16_t page_size = 0; uint16_t device_i2c_address = EEP_MEM_I2C_ADDR; uint32_t device_serial_number = 0; -uint16_t data_storage_start = 0; -uint16_t data_storage_size = 0; +uint32_t data_storage_start = 0; +uint32_t data_storage_size = 0; -size_t device_start_co_auto = 0; -size_t device_start_co_prot = 0; -size_t device_size_co = 0; +uint32_t device_start_co_auto = 0; +uint32_t device_start_co_prot = 0; +uint32_t device_size_co = 0; static bool_t eeprom_init_mc(); static bool_t eeprom_init_st(); diff --git a/CANopenNode_STM32/eeprom.h b/CANopenNode_STM32/eeprom.h index e723d63..a51114c 100644 --- a/CANopenNode_STM32/eeprom.h +++ b/CANopenNode_STM32/eeprom.h @@ -16,22 +16,23 @@ extern "C" #endif /* USER CODE BEGIN Includes */ +#include #include "main.h" bool Eeprom_Init(); bool Eeprom_Init_CO(); -extern uint16_t device_size; -extern size_t page_size; +extern uint32_t device_size; +extern uint16_t page_size; extern uint16_t device_i2c_address; extern uint32_t device_serial_number; -extern uint16_t data_storage_start; -extern uint16_t data_storage_size; +extern uint32_t data_storage_start; +extern uint32_t data_storage_size; -extern size_t device_start_co_auto; -extern size_t device_start_co_prot; -extern size_t device_size_co; +extern uint32_t device_start_co_auto; +extern uint32_t device_start_co_prot; +extern uint32_t device_size_co; #ifdef __cplusplus } diff --git a/examples/stm32g0xx_fdcan/Core/Src/main.c b/examples/stm32g0xx_fdcan/Core/Src/main.c index aeb9398..f6bc2b3 100644 --- a/examples/stm32g0xx_fdcan/Core/Src/main.c +++ b/examples/stm32g0xx_fdcan/Core/Src/main.c @@ -30,6 +30,7 @@ /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "CO_app_STM32.h" +#include "eeprom.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ @@ -107,6 +108,7 @@ int main(void) MX_USB_DRD_FS_PCD_Init(); MX_TIM17_Init(); /* USER CODE BEGIN 2 */ + Eeprom_Init(); CANopenNodeSTM32 canOpenNodeSTM32; canOpenNodeSTM32.CANHandle = &hfdcan1; From 2be3fc1786f184661a561318b38cd2f6cc0b28a2 Mon Sep 17 00:00:00 2001 From: TheCitrusMan Date: Fri, 26 Jun 2026 16:22:57 +0200 Subject: [PATCH 03/14] Correction in CO_eeprom_STM32.c --- CANopenNode_STM32/CO_eeprom_STM32.c | 2 +- CANopenNode_STM32/eeprom.c | 21 ++++++++++----------- CANopenNode_STM32/eeprom.h | 1 - 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/CANopenNode_STM32/CO_eeprom_STM32.c b/CANopenNode_STM32/CO_eeprom_STM32.c index 002cbe4..6acf2f7 100644 --- a/CANopenNode_STM32/CO_eeprom_STM32.c +++ b/CANopenNode_STM32/CO_eeprom_STM32.c @@ -146,7 +146,7 @@ bool_t CO_eeprom_writeBlock(void *storageModule, uint8_t *data, size_t eepromAdd len = 0; /* wait for completion of the write operation */ - if (HAL_I2C_IsDeviceReady(HI2C, device_i2c_address << 1, 10, I2C_TIMEOUT_MS) != HAL_OK); + if (HAL_I2C_IsDeviceReady(HI2C, device_i2c_address << 1, 10, I2C_TIMEOUT_MS) != HAL_OK) { // device not ready return false; diff --git a/CANopenNode_STM32/eeprom.c b/CANopenNode_STM32/eeprom.c index 518f01d..20026ea 100644 --- a/CANopenNode_STM32/eeprom.c +++ b/CANopenNode_STM32/eeprom.c @@ -5,7 +5,6 @@ * Author: Marc Vandenhende */ -#include #include "eeprom.h" #include "i2c.h" #include "CO_app_STM32.h" @@ -16,17 +15,17 @@ */ #define CO_EEP_MAX_STORAGE 0x2000 // Max number of bytes reserved for CanOpen storage -#define EEP_MEM_I2C_ADDR 0x50 -#define EEP_UID_I2C_ADDR 0x58 // For ST eeprom UID +#define EEP_MEM_I2C_ADDR 0x50 // I2C address of eeprom device +#define EEP_UID_I2C_ADDR 0x58 // I2C address of ST eeprom UID -#define ST_MFR_CODE 0x20 -#define ST_BUS_PROTOCOL 0xE0 -#define ST_UID_SIZE 16 +#define ST_MFR_CODE 0x20 // ST manufacturer code +#define ST_BUS_PROTOCOL 0xE0 // ST bus protocol +#define ST_UID_SIZE 16 // ST UID size in #bytes -#define MC_SERIAL_ADDR 0x7ffa -#define MC_MFR_CODE 0x29 -#define MC_DEVICE_CODE 0x48 -#define MC_EEP_SERIAL_SIZE 6 +#define MC_SERIAL_ADDR 0x7ffa // memory location of MC serial number +#define MC_MFR_CODE 0x29 // MC manufacturer code +#define MC_DEVICE_CODE 0x48 // MC device code +#define MC_EEP_SERIAL_SIZE 6 // MC Serial size in #bytes typedef struct { @@ -176,7 +175,7 @@ static bool_t eeprom_init_st() * Try to initialize Microchip eeprom * Check if device is 24AA256UID and sets parameters accordingly * returns "true" if initialization is success - * returns "false" if no ST eeprom found + * returns "false" if no MC eeprom found */ static bool_t eeprom_init_mc() { diff --git a/CANopenNode_STM32/eeprom.h b/CANopenNode_STM32/eeprom.h index a51114c..140c59d 100644 --- a/CANopenNode_STM32/eeprom.h +++ b/CANopenNode_STM32/eeprom.h @@ -15,7 +15,6 @@ extern "C" #endif -/* USER CODE BEGIN Includes */ #include #include "main.h" From f5d0c0547c3d023760676a6d27741c83ee87ea91 Mon Sep 17 00:00:00 2001 From: TheCitrusMan Date: Sat, 27 Jun 2026 08:21:48 +0200 Subject: [PATCH 04/14] Reduced I2C timeout time. --- CANopenNode_STM32/CO_eeprom_STM32.c | 2 +- examples/stm32g0xx_fdcan/Core/Inc/i2c.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CANopenNode_STM32/CO_eeprom_STM32.c b/CANopenNode_STM32/CO_eeprom_STM32.c index 6acf2f7..3760f7c 100644 --- a/CANopenNode_STM32/CO_eeprom_STM32.c +++ b/CANopenNode_STM32/CO_eeprom_STM32.c @@ -146,7 +146,7 @@ bool_t CO_eeprom_writeBlock(void *storageModule, uint8_t *data, size_t eepromAdd len = 0; /* wait for completion of the write operation */ - if (HAL_I2C_IsDeviceReady(HI2C, device_i2c_address << 1, 10, I2C_TIMEOUT_MS) != HAL_OK) + if (HAL_I2C_IsDeviceReady(HI2C, device_i2c_address << 1, 3, I2C_TIMEOUT_MS) != HAL_OK) { // device not ready return false; diff --git a/examples/stm32g0xx_fdcan/Core/Inc/i2c.h b/examples/stm32g0xx_fdcan/Core/Inc/i2c.h index de9843f..4ab233e 100644 --- a/examples/stm32g0xx_fdcan/Core/Inc/i2c.h +++ b/examples/stm32g0xx_fdcan/Core/Inc/i2c.h @@ -35,7 +35,7 @@ extern "C" { extern I2C_HandleTypeDef hi2c1; /* USER CODE BEGIN Private defines */ -#define I2C_TIMEOUT_MS 500 +#define I2C_TIMEOUT_MS 100 #define HI2C &hi2c1 /* USER CODE END Private defines */ From 4bd3ab7b22eb9153f6b898048f05a263d90d46b5 Mon Sep 17 00:00:00 2001 From: TheCitrusMan Date: Sat, 27 Jun 2026 10:46:35 +0200 Subject: [PATCH 05/14] Separate handle for I2C eeprom. --- CANopenNode_STM32/CO_driver_STM32.c | 2 +- CANopenNode_STM32/CO_eeprom_STM32.c | 21 ++++++++++----------- CANopenNode_STM32/eeprom.c | 13 +++++++------ CANopenNode_STM32/eeprom.h | 3 +++ examples/stm32g0xx_fdcan/Core/Inc/i2c.h | 1 - 5 files changed, 21 insertions(+), 19 deletions(-) diff --git a/CANopenNode_STM32/CO_driver_STM32.c b/CANopenNode_STM32/CO_driver_STM32.c index b47c864..9ca794e 100644 --- a/CANopenNode_STM32/CO_driver_STM32.c +++ b/CANopenNode_STM32/CO_driver_STM32.c @@ -403,7 +403,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) { diff --git a/CANopenNode_STM32/CO_eeprom_STM32.c b/CANopenNode_STM32/CO_eeprom_STM32.c index 3760f7c..5ebfd8c 100644 --- a/CANopenNode_STM32/CO_eeprom_STM32.c +++ b/CANopenNode_STM32/CO_eeprom_STM32.c @@ -30,7 +30,6 @@ #include "CO_app_STM32.h" #include "OD.h" -#include "i2c.h" #include "eeprom.h" #include "CO_eeprom_STM32.h" @@ -64,12 +63,12 @@ bool_t CO_eeprom_init(void *storageModule) { uint8_t eraseBuf[32]; memset(eraseBuf, 0xff, 32); - HAL_I2C_Mem_Write(HI2C, device_i2c_address << 1, eepromAddrNextProt, 2, eraseBuf, 32, I2C_TIMEOUT_MS); + HAL_I2C_Mem_Write(HI2C_EEPROM, device_i2c_address << 1, eepromAddrNextProt, 2, eraseBuf, 32, I2C_TIMEOUT_MS); HAL_Delay(5); // write operation timer } /* If eeprom chip is OK, this will pass, otherwise timeout */ - return (HAL_I2C_IsDeviceReady(HI2C, device_i2c_address << 1, 3, I2C_TIMEOUT_MS) == HAL_OK); + return (HAL_I2C_IsDeviceReady(HI2C_EEPROM, device_i2c_address << 1, 3, I2C_TIMEOUT_MS) == HAL_OK); // return "true" if device ready } @@ -110,7 +109,7 @@ size_t CO_eeprom_getAddr(void *storageModule, bool_t isAuto, size_t len, bool_t /******************************************************************************/ void CO_eeprom_readBlock(void *storageModule, uint8_t *data, size_t eepromAddr, size_t len) { - HAL_I2C_Mem_Read(HI2C, device_i2c_address << 1, eepromAddr, 2, (uint8_t *) data, len, I2C_TIMEOUT_MS); + HAL_I2C_Mem_Read(HI2C_EEPROM, device_i2c_address << 1, eepromAddr, 2, (uint8_t *) data, len, I2C_TIMEOUT_MS); } /******************************************************************************/ @@ -118,7 +117,7 @@ bool_t CO_eeprom_writeBlock(void *storageModule, uint8_t *data, size_t eepromAdd { uint32_t idx = 0; - if (HAL_I2C_IsDeviceReady(HI2C, device_i2c_address << 1, 3, I2C_TIMEOUT_MS) != HAL_OK) + if (HAL_I2C_IsDeviceReady(HI2C_EEPROM, device_i2c_address << 1, 3, I2C_TIMEOUT_MS) != HAL_OK) { // device not ready return false; @@ -130,7 +129,7 @@ bool_t CO_eeprom_writeBlock(void *storageModule, uint8_t *data, size_t eepromAdd if (len_x > page_size) len_x = page_size; - if (HAL_I2C_Mem_Write(HI2C, device_i2c_address << 1, eepromAddr, 2, (uint8_t *) data + idx, len_x, + 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 @@ -146,7 +145,7 @@ bool_t CO_eeprom_writeBlock(void *storageModule, uint8_t *data, size_t eepromAdd len = 0; /* wait for completion of the write operation */ - if (HAL_I2C_IsDeviceReady(HI2C, device_i2c_address << 1, 3, I2C_TIMEOUT_MS) != HAL_OK) + if (HAL_I2C_IsDeviceReady(HI2C_EEPROM, device_i2c_address << 1, 3, I2C_TIMEOUT_MS) != HAL_OK) { // device not ready return false; @@ -172,7 +171,7 @@ uint16_t CO_eeprom_getCrcBlock(void *storageModule, size_t eepromAddr, size_t le subLen = BUF_SIZE; /* update crc from data part */ - HAL_I2C_Mem_Read(HI2C, device_i2c_address << 1, eepromAddr, 2, buf, subLen, I2C_TIMEOUT_MS); + HAL_I2C_Mem_Read(HI2C_EEPROM, device_i2c_address << 1, eepromAddr, 2, buf, subLen, I2C_TIMEOUT_MS); crc = crc16_ccitt(buf, subLen, crc); eepromAddr += BUF_SIZE; len -= subLen; @@ -187,14 +186,14 @@ bool_t CO_eeprom_updateByte(void * storageModule, uint8_t data, { uint8_t buf; - if (HAL_I2C_IsDeviceReady(HI2C, device_i2c_address << 1, 3, I2C_TIMEOUT_MS) + 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, device_i2c_address << 1, eepromAddr, 2, &buf, 1, I2C_TIMEOUT_MS) != HAL_OK) + if (HAL_I2C_Mem_Read(HI2C_EEPROM, device_i2c_address << 1, eepromAddr, 2, &buf, 1, I2C_TIMEOUT_MS) != HAL_OK) { return false; } @@ -203,7 +202,7 @@ bool_t CO_eeprom_updateByte(void * storageModule, uint8_t data, * Don't wait for write to complete */ if (buf != data) { - if (HAL_I2C_Mem_Write(HI2C, device_i2c_address << 1, eepromAddr, 2, &data, 1, I2C_TIMEOUT_MS) != HAL_OK) + if (HAL_I2C_Mem_Write(HI2C_EEPROM, device_i2c_address << 1, eepromAddr, 2, &data, 1, I2C_TIMEOUT_MS) != HAL_OK) { return false; } diff --git a/CANopenNode_STM32/eeprom.c b/CANopenNode_STM32/eeprom.c index 20026ea..933199e 100644 --- a/CANopenNode_STM32/eeprom.c +++ b/CANopenNode_STM32/eeprom.c @@ -13,6 +13,7 @@ /* * Hardware definition */ + #define CO_EEP_MAX_STORAGE 0x2000 // Max number of bytes reserved for CanOpen storage #define EEP_MEM_I2C_ADDR 0x50 // I2C address of eeprom device @@ -82,7 +83,7 @@ bool Eeprom_Init() } /* If eeprom chip is OK, this will pass, otherwise timeout */ - eeprom_initialized = HAL_I2C_IsDeviceReady(HI2C, EEP_MEM_I2C_ADDR << 1, 3, I2C_TIMEOUT_MS) == HAL_OK; + eeprom_initialized = HAL_I2C_IsDeviceReady(HI2C_EEPROM, EEP_MEM_I2C_ADDR << 1, 3, I2C_TIMEOUT_MS) == HAL_OK; return eeprom_initialized; } @@ -100,7 +101,7 @@ bool Eeprom_Init_CO() OD_PERSIST_COMM.x1018_identity.serialNumber = device_serial_number; /* If eeprom chip is OK, this will pass, otherwise timeout */ - return (HAL_I2C_IsDeviceReady(HI2C, EEP_MEM_I2C_ADDR << 1, 3, I2C_TIMEOUT_MS) == HAL_OK); + return (HAL_I2C_IsDeviceReady(HI2C_EEPROM, EEP_MEM_I2C_ADDR << 1, 3, I2C_TIMEOUT_MS) == HAL_OK); // return "true" if device ready } @@ -115,10 +116,10 @@ static bool_t eeprom_init_st() uint8_t uid[ST_UID_SIZE]; /* If eeprom chip is OK, this will pass, otherwise timeout */ - if (HAL_I2C_IsDeviceReady(HI2C, EEP_UID_I2C_ADDR << 1, 3, I2C_TIMEOUT_MS) != HAL_OK) + if (HAL_I2C_IsDeviceReady(HI2C_EEPROM, EEP_UID_I2C_ADDR << 1, 3, I2C_TIMEOUT_MS) != HAL_OK) return false; // return "false" if device not ready - if (HAL_I2C_Mem_Read(HI2C, EEP_UID_I2C_ADDR << 1, 0, 2, uid, ST_UID_SIZE, I2C_TIMEOUT_MS) != HAL_OK) + if (HAL_I2C_Mem_Read(HI2C_EEPROM, EEP_UID_I2C_ADDR << 1, 0, 2, uid, ST_UID_SIZE, I2C_TIMEOUT_MS) != HAL_OK) return false; // return "false" if device does not respond // Check if STM EEPROM device @@ -182,10 +183,10 @@ static bool_t eeprom_init_mc() uint8_t serial[MC_EEP_SERIAL_SIZE]; /* If eeprom chip is OK, this will pass, otherwise timeout */ - if (HAL_I2C_IsDeviceReady(HI2C, EEP_MEM_I2C_ADDR << 1, 3, I2C_TIMEOUT_MS) != HAL_OK) + if (HAL_I2C_IsDeviceReady(HI2C_EEPROM, EEP_MEM_I2C_ADDR << 1, 3, I2C_TIMEOUT_MS) != HAL_OK) return false; // return "false" if device not ready - if (HAL_I2C_Mem_Read(HI2C, EEP_MEM_I2C_ADDR << 1, MC_SERIAL_ADDR, 2, serial, MC_EEP_SERIAL_SIZE, I2C_TIMEOUT_MS) != + if (HAL_I2C_Mem_Read(HI2C_EEPROM, EEP_MEM_I2C_ADDR << 1, MC_SERIAL_ADDR, 2, serial, MC_EEP_SERIAL_SIZE, I2C_TIMEOUT_MS) != HAL_OK) return false; // return "false" if device does not respond diff --git a/CANopenNode_STM32/eeprom.h b/CANopenNode_STM32/eeprom.h index 140c59d..f1fed9f 100644 --- a/CANopenNode_STM32/eeprom.h +++ b/CANopenNode_STM32/eeprom.h @@ -17,6 +17,9 @@ extern "C" #include #include "main.h" +#include "i2c.h" + +#define HI2C_EEPROM &hi2c1 // i2c handle used for eeprom device bool Eeprom_Init(); bool Eeprom_Init_CO(); diff --git a/examples/stm32g0xx_fdcan/Core/Inc/i2c.h b/examples/stm32g0xx_fdcan/Core/Inc/i2c.h index 4ab233e..1a20dce 100644 --- a/examples/stm32g0xx_fdcan/Core/Inc/i2c.h +++ b/examples/stm32g0xx_fdcan/Core/Inc/i2c.h @@ -36,7 +36,6 @@ extern I2C_HandleTypeDef hi2c1; /* USER CODE BEGIN Private defines */ #define I2C_TIMEOUT_MS 100 -#define HI2C &hi2c1 /* USER CODE END Private defines */ void MX_I2C1_Init(void); From 300c9df8d76392e179781169499c56213d1e8bd9 Mon Sep 17 00:00:00 2001 From: TheCitrusMan Date: Sun, 28 Jun 2026 10:55:23 +0200 Subject: [PATCH 06/14] Move EEPROM I2C handle to i2c.h --- CANopenNode_STM32/eeprom.c | 1 - CANopenNode_STM32/eeprom.h | 2 -- examples/stm32g0xx_fdcan/Core/Inc/i2c.h | 2 ++ 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/CANopenNode_STM32/eeprom.c b/CANopenNode_STM32/eeprom.c index 933199e..5e33fb2 100644 --- a/CANopenNode_STM32/eeprom.c +++ b/CANopenNode_STM32/eeprom.c @@ -6,7 +6,6 @@ */ #include "eeprom.h" -#include "i2c.h" #include "CO_app_STM32.h" #include "OD.h" diff --git a/CANopenNode_STM32/eeprom.h b/CANopenNode_STM32/eeprom.h index f1fed9f..f74ef37 100644 --- a/CANopenNode_STM32/eeprom.h +++ b/CANopenNode_STM32/eeprom.h @@ -19,8 +19,6 @@ extern "C" #include "main.h" #include "i2c.h" -#define HI2C_EEPROM &hi2c1 // i2c handle used for eeprom device - bool Eeprom_Init(); bool Eeprom_Init_CO(); diff --git a/examples/stm32g0xx_fdcan/Core/Inc/i2c.h b/examples/stm32g0xx_fdcan/Core/Inc/i2c.h index 1a20dce..5b9cc57 100644 --- a/examples/stm32g0xx_fdcan/Core/Inc/i2c.h +++ b/examples/stm32g0xx_fdcan/Core/Inc/i2c.h @@ -36,6 +36,8 @@ extern I2C_HandleTypeDef hi2c1; /* USER CODE BEGIN Private defines */ #define I2C_TIMEOUT_MS 100 + +#define HI2C_EEPROM &hi2c1 // i2c handle used for eeprom device /* USER CODE END Private defines */ void MX_I2C1_Init(void); From 7ceee2fc8e18c702d0f447fe37cd37ba5a01b0a3 Mon Sep 17 00:00:00 2001 From: Marc Date: Mon, 29 Jun 2026 15:06:03 +0200 Subject: [PATCH 07/14] CO_eeprom_writeBlock() timeout error fix. --- CANopenNode_STM32/CO_eeprom_STM32.c | 14 ++++++++++---- CANopenNode_STM32/eeprom.h | 13 ++----------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/CANopenNode_STM32/CO_eeprom_STM32.c b/CANopenNode_STM32/CO_eeprom_STM32.c index 5ebfd8c..0d5fc41 100644 --- a/CANopenNode_STM32/CO_eeprom_STM32.c +++ b/CANopenNode_STM32/CO_eeprom_STM32.c @@ -115,7 +115,8 @@ void CO_eeprom_readBlock(void *storageModule, uint8_t *data, size_t eepromAddr, /******************************************************************************/ bool_t CO_eeprom_writeBlock(void *storageModule, uint8_t *data, size_t eepromAddr, size_t len) { - uint32_t idx = 0; + 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) { @@ -145,10 +146,15 @@ bool_t CO_eeprom_writeBlock(void *storageModule, uint8_t *data, size_t eepromAdd len = 0; /* wait for completion of the write operation */ - if (HAL_I2C_IsDeviceReady(HI2C_EEPROM, device_i2c_address << 1, 3, I2C_TIMEOUT_MS) != HAL_OK) + eep_write_timer = HAL_GetTick(); + while (HAL_I2C_IsDeviceReady(HI2C_EEPROM, device_i2c_address << 1, 3, I2C_TIMEOUT_MS) != HAL_OK) { - // device not ready - return false; + // 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; diff --git a/CANopenNode_STM32/eeprom.h b/CANopenNode_STM32/eeprom.h index f74ef37..a6f4a1e 100644 --- a/CANopenNode_STM32/eeprom.h +++ b/CANopenNode_STM32/eeprom.h @@ -8,17 +8,12 @@ #ifndef INC_EEPROM_H_ #define INC_EEPROM_H_ -#ifdef __cplusplus -extern "C" -{ - - -#endif - #include #include "main.h" #include "i2c.h" +#define EEPROM_WRITE_TIME 5 // maximum write cycle time + bool Eeprom_Init(); bool Eeprom_Init_CO(); @@ -34,8 +29,4 @@ extern uint32_t device_start_co_auto; extern uint32_t device_start_co_prot; extern uint32_t device_size_co; -#ifdef __cplusplus -} -#endif - #endif /* INC_EEPROM_H_ */ From dcc0deca0d4421aab811bdefdc481b5d9a7d3c82 Mon Sep 17 00:00:00 2001 From: Marc Date: Tue, 30 Jun 2026 14:57:52 +0200 Subject: [PATCH 08/14] Changes applied after the second run of github copilot. Using the EEPROM extension in this project: =========================================== The eeprom extension provides support for following eeprom types: ST Microelectronics: - m24c32-u (4 kBytes) - m24c64-u (8 kBytes) - m24128-u (16 kBytes) - m24256-u (32 kBytes) - m24512-u (64 kBytes) Microchip: - 24AA256-UID (32 kBytes) The eeprom interface software assumes the default I2C address 0x50 as defined in EEP_MEM_I2C_ADDR (eeprom.c). This value can be changed as required. The same is valid for the UID address in case a ST eeprom is used, which is assumed 0x58 by default, and is defined in EEP_UID_I2C_ADDR (eeprom.c). These eeprom types were chosen because they contain a serial number. This serial number is used to fill in object OD_PERSIST_COMM.x1018_identity.serialNumber The first 8 kBytes (or 4 kBytes in case of the m24c32-u) of eeprom storage are reserved for CanOpenNode. If the project also requires storage for the application, a 16kByte or larger eeprom should be selected. The starting address and storage size for the user application storage can be found in variables data_storage_start and data_storage_size after initialization. Both variables are defined in eeprom.c When the application is first run, the EEPROM is uninitialized. CanOpenNode will remain in the pre-operational state in that case. To initialize the EEPROM storage, write visible string "save" to object 0x1010 sub-object 0x01 using SDO write. Next, reset the microcontroller. CanOpenNode can now be put in the "running" state. When the application is still in the development stage, it often happens that the number or type of PERSIST_COMM objects needs to be changed. I have found that it may be required to reinitialize the EEPROM storage in that case. This demo application runs on a STM32G0C1E-EV board, which contains a 4 way controller. The EEPROM can be invalidated by keeping the "down" key pressed during power-up, after which it can be initialized again. --- CANopenNode_STM32/CO_app_STM32.c | 4 ++-- CANopenNode_STM32/CO_eeprom_STM32.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CANopenNode_STM32/CO_app_STM32.c b/CANopenNode_STM32/CO_app_STM32.c index 900a997..8deb9fa 100644 --- a/CANopenNode_STM32/CO_app_STM32.c +++ b/CANopenNode_STM32/CO_app_STM32.c @@ -104,8 +104,8 @@ canopen_app_init(CANopenNodeSTM32* _canopenNodeSTM32) { if (err != CO_ERROR_NO && err != CO_ERROR_DATA_CORRUPT) { - log_printf("Error: Storage %ld\n", storageInitError); - return 2; + log_printf("Error: Storage %" PRIu32 "\n", storageInitError); + return 2; } #endif diff --git a/CANopenNode_STM32/CO_eeprom_STM32.c b/CANopenNode_STM32/CO_eeprom_STM32.c index 0d5fc41..3fcd3a5 100644 --- a/CANopenNode_STM32/CO_eeprom_STM32.c +++ b/CANopenNode_STM32/CO_eeprom_STM32.c @@ -179,7 +179,7 @@ uint16_t CO_eeprom_getCrcBlock(void *storageModule, size_t eepromAddr, size_t le /* 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 += BUF_SIZE; + eepromAddr += subLen; len -= subLen; } From 1c16e1fb5f0a287b8fdf71499b3eb39dd182bbed Mon Sep 17 00:00:00 2001 From: TheCitrusMan Date: Wed, 1 Jul 2026 14:20:27 +0200 Subject: [PATCH 09/14] All projects updated with EEPROM functionality. - The conditional EEPROM invalidate functionality in CO_eeprom_STM32.c has been disabled in this case. -> The user should implement a conditional way to perform the action if required. - The stm32f0xx_can and stm32f3xx_can did not have I2C functionality in their original projects. -> I2C was added to the projects. - Definitions have been moved around a bit to make core applications existing of a main.c/.h only and applications existing of .c/.h files for each peripheral work with the updated CANopenNode_STM32 Everything compiles fine but hasn't been tested. Especially RTOS applications may need additional testing. --- CANopenNode_STM32/CO_driver_STM32.c | 2 +- CANopenNode_STM32/CO_eeprom_STM32.c | 3 +- CANopenNode_STM32/eeprom.h | 2 +- examples/stm32f0xx_can/.cproject | 8 +- .../CANOpenNode-NUCLEO-STM32F072.ioc | 46 +- examples/stm32f0xx_can/Core/Inc/i2c.h | 53 + examples/stm32f0xx_can/Core/Inc/main.h | 2 +- examples/stm32f0xx_can/Core/Src/i2c.c | 130 + examples/stm32f0xx_can/Core/Src/main.c | 11 +- .../Inc/stm32f0xx_ll_i2c.h | 2277 +++++++++++++++++ examples/stm32f0xx_can/STM32F072RBTX_FLASH.ld | 4 +- examples/stm32f3xx_can/.cproject | 8 +- .../CANOpenNode-NUCLEO-STM32F303ZE.ioc | 74 +- examples/stm32f3xx_can/Core/Inc/i2c.h | 53 + examples/stm32f3xx_can/Core/Inc/main.h | 2 +- examples/stm32f3xx_can/Core/Src/i2c.c | 130 + examples/stm32f3xx_can/Core/Src/main.c | 13 + examples/stm32f3xx_can/STM32F303ZETX_FLASH.ld | 4 +- examples/stm32f4xx_can/Core/Inc/main.h | 4 +- examples/stm32fh7xx_fdcan/Core/Inc/main.h | 4 +- examples/stm32g0xx_fdcan/Core/Inc/i2c.h | 1 - examples/stm32g0xx_fdcan/Core/Inc/main.h | 2 +- examples/stm32g0xx_fdcan_rtos/Core/Inc/i2c.h | 3 +- examples/stm32g0xx_fdcan_rtos/Core/Inc/main.h | 1 + examples/stm32g0xx_fdcan_rtos/Core/Src/main.c | 3 +- 25 files changed, 2777 insertions(+), 63 deletions(-) create mode 100644 examples/stm32f0xx_can/Core/Inc/i2c.h create mode 100644 examples/stm32f0xx_can/Core/Src/i2c.c create mode 100644 examples/stm32f0xx_can/Drivers/STM32F0xx_HAL_Driver/Inc/stm32f0xx_ll_i2c.h create mode 100644 examples/stm32f3xx_can/Core/Inc/i2c.h create mode 100644 examples/stm32f3xx_can/Core/Src/i2c.c diff --git a/CANopenNode_STM32/CO_driver_STM32.c b/CANopenNode_STM32/CO_driver_STM32.c index 9ca794e..f2a0813 100644 --- a/CANopenNode_STM32/CO_driver_STM32.c +++ b/CANopenNode_STM32/CO_driver_STM32.c @@ -162,7 +162,7 @@ CO_CANmodule_init(CO_CANmodule_t* CANmodule, void* CANptr, CO_CANrx_t rxArray[], | FDCAN_IT_TX_COMPLETE | FDCAN_IT_TX_FIFO_EMPTY | FDCAN_IT_BUS_OFF | FDCAN_IT_ARB_PROTOCOL_ERROR | FDCAN_IT_DATA_PROTOCOL_ERROR | FDCAN_IT_ERROR_PASSIVE | FDCAN_IT_ERROR_WARNING, - 0xFFFFFFFF) + 0xFFFFFFFF) != HAL_OK) { return CO_ERROR_ILLEGAL_ARGUMENT; } diff --git a/CANopenNode_STM32/CO_eeprom_STM32.c b/CANopenNode_STM32/CO_eeprom_STM32.c index 3fcd3a5..99ceee1 100644 --- a/CANopenNode_STM32/CO_eeprom_STM32.c +++ b/CANopenNode_STM32/CO_eeprom_STM32.c @@ -59,7 +59,8 @@ bool_t CO_eeprom_init(void *storageModule) // invalidate eeprom contents if CAN_CFG jumper placed - if (HAL_GPIO_ReadPin(JOY_DOWN_GPIO_Port, JOY_DOWN_Pin)) + //if (HAL_GPIO_ReadPin(JOY_DOWN_GPIO_Port, JOY_DOWN_Pin)) + if (false) { uint8_t eraseBuf[32]; memset(eraseBuf, 0xff, 32); diff --git a/CANopenNode_STM32/eeprom.h b/CANopenNode_STM32/eeprom.h index a6f4a1e..d1f48d5 100644 --- a/CANopenNode_STM32/eeprom.h +++ b/CANopenNode_STM32/eeprom.h @@ -10,9 +10,9 @@ #include #include "main.h" -#include "i2c.h" #define EEPROM_WRITE_TIME 5 // maximum write cycle time +#define I2C_TIMEOUT_MS 100 bool Eeprom_Init(); bool Eeprom_Init_CO(); diff --git a/examples/stm32f0xx_can/.cproject b/examples/stm32f0xx_can/.cproject index bfa9581..5560967 100644 --- a/examples/stm32f0xx_can/.cproject +++ b/examples/stm32f0xx_can/.cproject @@ -21,7 +21,8 @@