Skip to content

Commit 1f2049d

Browse files
committed
CO_storageFlash.c: Add flash storage example
- To enable add the USE_CO_STORAGE_FLASH preprocessor symbol to your build config. - The example was developed and tested only on STM32F3XX microcontrollers, not guaranteed to work on other series!
1 parent b1277d1 commit 1f2049d

File tree

4 files changed

+350
-1
lines changed

4 files changed

+350
-1
lines changed

CANopenNode_STM32/CO_app_STM32.c

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,11 @@
2929
#include "main.h"
3030
#include <stdio.h>
3131

32+
#ifdef USE_CO_STORAGE_FLASH
33+
#include "CO_storageFlash.h"
34+
#else
3235
#include "CO_storageBlank.h"
36+
#endif
3337
#include "OD.h"
3438

3539
CANopenNodeSTM32*
@@ -54,6 +58,7 @@ CO_t* CO = NULL; /* CANopen object */
5458
// Global variables
5559
uint32_t time_old, time_current;
5660
CO_ReturnError_t err;
61+
uint32_t storageInitError = 0;
5762

5863
/* This function will basically setup the CANopen node */
5964
int
@@ -68,9 +73,13 @@ canopen_app_init(CANopenNodeSTM32* _canopenNodeSTM32) {
6873
.len = sizeof(OD_PERSIST_COMM),
6974
.subIndexOD = 2,
7075
.attr = CO_storage_cmd | CO_storage_restore,
76+
#ifdef USE_CO_STORAGE_FLASH
77+
.reservedSpace = SIZE_OF_PAGE,
78+
.offset = 0}};
79+
#else
7180
.addrNV = NULL}};
81+
#endif
7282
uint8_t storageEntriesCount = sizeof(storageEntries) / sizeof(storageEntries[0]);
73-
uint32_t storageInitError = 0;
7483
#endif
7584

7685
/* Allocate memory */
@@ -96,9 +105,14 @@ canopen_app_init(CANopenNodeSTM32* _canopenNodeSTM32) {
96105
canopenNodeSTM32->canOpenStack = CO;
97106

98107
#if (CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE
108+
#ifdef USE_CO_STORAGE_FLASH
109+
err = CO_storageFlash_init(&storage, CO->CANmodule, OD_ENTRY_H1010_storeParameters, OD_ENTRY_H1011_restoreDefaultParameters, storageEntries, storageEntriesCount, &storageInitError);
110+
#else
111+
99112
err = CO_storageBlank_init(&storage, CO->CANmodule, OD_ENTRY_H1010_storeParameters,
100113
OD_ENTRY_H1011_restoreDefaultParameters, storageEntries, storageEntriesCount,
101114
&storageInitError);
115+
#endif
102116

103117
if (err != CO_ERROR_NO && err != CO_ERROR_DATA_CORRUPT) {
104118
log_printf("Error: Storage %lu\n", storageInitError);

CANopenNode_STM32/CO_driver_target.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,13 @@
5050
#error This STM32 Do not support CAN or FDCAN
5151
#endif
5252

53+
#ifdef USE_CO_STORAGE_FLASH
54+
#define CO_CONFIG_STORAGE CO_CONFIG_STORAGE_ENABLE
55+
#define CO_CONFIG_CRC16 CO_CONFIG_CRC16_ENABLE
56+
#else
5357
#undef CO_CONFIG_STORAGE_ENABLE // We don't need Storage option, implement based on your use case and remove this line from here
58+
#endif
59+
5460

5561
#ifdef CO_DRIVER_CUSTOM
5662
#include "CO_driver_custom.h"
@@ -139,6 +145,11 @@ typedef struct {
139145
uint8_t attr;
140146
/* Additional variables (target specific) */
141147
void* addrNV;
148+
#ifdef USE_CO_STORAGE_FLASH
149+
uint16_t offset;
150+
uint16_t reservedSpace;
151+
uint16_t crc;
152+
#endif
142153
} CO_storage_entry_t;
143154

144155
/* (un)lock critical section in CO_CANsend() */

CANopenNode_STM32/CO_storageFlash.c

Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
/**
2+
* To use this driver:
3+
* - Define USE_CO_STORAGE_FLASH in your build config, this will enable the following defines in CO_driver_target.h:
4+
#define CO_CONFIG_STORAGE CO_CONFIG_STORAGE_ENABLE
5+
#define CO_CONFIG_CRC16 CO_CONFIG_CRC16_ENABLE
6+
7+
* - Exclude CANopenNode/storage/CO_storageEeprom.c from the build, if it causes build errors.
8+
* - Make sure Object Dictionary entries for 0x1010 "Store Parameters" and 0x1011 "Restore default parameters" are enabled
9+
* - Call CO_storageFlash_init in canopen_app_init (where CO_storageBlank_init is called in the example code)
10+
* - Modify your linker script to have a NVM section of at least 2KB and a _co_storage_start symbol (see examples/stm32f3xx_can_rtos/STM32F303VCTX_FLASH.ld)
11+
*/
12+
#ifdef USE_CO_STORAGE_FLASH
13+
#include "CO_storageFlash.h"
14+
#include "301/crc16-ccitt.h"
15+
#include "stm32f3xx_hal_flash.h"
16+
#include "stm32f3xx_hal_flash_ex.h"
17+
#include "stm32f3xx.h"
18+
19+
#if (CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE
20+
21+
#define SIZE_CRC 2
22+
#define SIZE_FIRST_ADDRESS 2 // First uint16_t is used to signal whether the flash was written by writing a '1'
23+
24+
static ODR_t restoreFlash(CO_storage_entry_t *entry, CO_CANmodule_t *CANmodule);
25+
static ODR_t storeFlash(CO_storage_entry_t *entry, CO_CANmodule_t *CANmodule);
26+
static ODR_t writePageBuffer(void);
27+
static ODR_t erasePage(void);
28+
29+
static uint8_t flashPageBuffer[SIZE_OF_PAGE];
30+
31+
/**
32+
* @brief Function for writing data on "Store parameters" command - OD object 1010. For more information see file
33+
* CO_storage.h, CO_storage_entry_t.
34+
* @note This is implemented as read, modify write as multiple storage groups are stored on one page
35+
* @param entry storage entry
36+
* @param CANmodule pointer to CO_CANmodule_t structure
37+
* @return ODR_OK on success
38+
*/
39+
static ODR_t storeFlash(CO_storage_entry_t *entry, CO_CANmodule_t *CANmodule)
40+
{
41+
(void)CANmodule;
42+
// Round up the offset to the nearest even integer
43+
// This is because we write 16 bytes and length could a odd amount of bytes
44+
size_t roundedLength = (entry->len + 1) & ~1;
45+
46+
// Out of memory
47+
if (roundedLength + SIZE_CRC + SIZE_FIRST_ADDRESS > entry->reservedSpace)
48+
{
49+
return ODR_OUT_OF_MEM;
50+
}
51+
52+
uint8_t *flash_pointer = (uint8_t *)&_co_storage_start;
53+
54+
memcpy(flashPageBuffer, flash_pointer, SIZE_OF_PAGE);
55+
56+
// Mark the storage entry as written
57+
flashPageBuffer[entry->offset] = 1;
58+
flashPageBuffer[entry->offset + 1] = 0;
59+
60+
memcpy(&flashPageBuffer[entry->offset + SIZE_FIRST_ADDRESS], (uint8_t *)entry->addr, entry->len);
61+
62+
// Calculate CRC
63+
entry->crc = crc16_ccitt(entry->addr, entry->len, 0);
64+
65+
// Write the CRC after the data
66+
size_t crcAddress = entry->offset + roundedLength + SIZE_FIRST_ADDRESS;
67+
flashPageBuffer[crcAddress] = (uint8_t)(entry->crc & 0xFF);
68+
flashPageBuffer[crcAddress + 1] = (uint8_t)((entry->crc >> 8) & 0xFF);
69+
70+
// Write the modified page back to flash
71+
ODR_t ret = writePageBuffer();
72+
if (ret != ODR_OK)
73+
{
74+
return ret;
75+
}
76+
77+
uint16_t flashCRC = flash_pointer[crcAddress] | (flash_pointer[crcAddress + 1] << 8);
78+
// Compare CRC values
79+
if (flashCRC != entry->crc)
80+
{
81+
ret = ODR_HW; // CRC mismatch, data not written successfully
82+
}
83+
84+
return ret;
85+
}
86+
87+
/**
88+
* @brief Write the page buffer to flash
89+
* @return ODR_OK on success
90+
*/
91+
static ODR_t writePageBuffer(void)
92+
{
93+
if (erasePage() != ODR_OK)
94+
{
95+
return ODR_HW;
96+
}
97+
98+
if (HAL_FLASH_Unlock() != HAL_OK)
99+
{
100+
return ODR_NO_RESOURCE;
101+
}
102+
103+
uint16_t *flash_pointer = (uint16_t *)&_co_storage_start;
104+
105+
for (size_t i = 0; i < SIZE_OF_PAGE; i += 2)
106+
{
107+
uint64_t toWrite = (flashPageBuffer[i + 1] << 8) | flashPageBuffer[i];
108+
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, (uint32_t)flash_pointer++, (uint64_t)toWrite) != HAL_OK)
109+
{
110+
HAL_FLASH_Lock();
111+
return ODR_HW;
112+
}
113+
}
114+
115+
HAL_FLASH_Lock();
116+
117+
return ODR_OK;
118+
}
119+
120+
/**
121+
* @brief Function for restoring data on "Restore default parameters" command - OD 1011. For more information see file
122+
* CO_storage.h, CO_storage_entry_t.
123+
* @note Only restores to default after reset
124+
* @param entry storage entry
125+
* @param CANmodule pointer to CO_CANmodule_t structure
126+
* @return ODR_OK on success
127+
*/
128+
static ODR_t restoreFlash(CO_storage_entry_t *entry, CO_CANmodule_t *CANmodule)
129+
{
130+
(void)CANmodule;
131+
// Read page from flash
132+
uint8_t *flash_pointer = (uint8_t *)&_co_storage_start;
133+
memcpy(flashPageBuffer, flash_pointer, SIZE_OF_PAGE);
134+
135+
// Set entry as not written to
136+
flashPageBuffer[entry->offset] = 0;
137+
138+
// Write the modified page back to flash
139+
return writePageBuffer();
140+
}
141+
142+
static ODR_t erasePage(void)
143+
{
144+
FLASH_EraseInitTypeDef eraseIfo;
145+
eraseIfo.PageAddress = (uint32_t)&_co_storage_start;
146+
eraseIfo.TypeErase = FLASH_TYPEERASE_PAGES;
147+
eraseIfo.NbPages = 1;
148+
149+
uint32_t eraseError = 0;
150+
151+
if (HAL_FLASH_Unlock() != HAL_OK)
152+
{
153+
return ODR_NO_RESOURCE;
154+
}
155+
156+
// Returns 0xFFFFFFFF on success
157+
HAL_FLASHEx_Erase(&eraseIfo, &eraseError);
158+
HAL_FLASH_Lock();
159+
160+
if (eraseError == 0xFFFFFFFF)
161+
{
162+
return ODR_OK;
163+
}
164+
165+
return ODR_HW;
166+
}
167+
168+
/**
169+
* @brief Init the storage flash module
170+
* @param storage CO_storage_t pointer
171+
* @param CANmodule CO_CANmodule_t CO_CANmodule_t
172+
* @param OD_1010_StoreParameters pointer to OD entry for store parameters
173+
* @param OD_1011_RestoreDefaultParam pointer to OD entry for restore parameters
174+
* @param entries pointer to CO storage entries
175+
* @param entriesCount number of CO storage entries
176+
* @param storageInitError output parametet to indicate error
177+
* @return CO_ERROR_NO on success
178+
*/
179+
CO_ReturnError_t CO_storageFlash_init(CO_storage_t *storage, CO_CANmodule_t *CANmodule,
180+
OD_entry_t *OD_1010_StoreParameters, OD_entry_t *OD_1011_RestoreDefaultParam,
181+
CO_storage_entry_t *entries, uint8_t entriesCount, uint32_t *storageInitError)
182+
{
183+
CO_ReturnError_t ret = CO_ERROR_NO;
184+
185+
/* verify arguments */
186+
if (storage == NULL || entries == NULL || entriesCount == 0 || storageInitError == NULL)
187+
{
188+
return CO_ERROR_ILLEGAL_ARGUMENT;
189+
}
190+
191+
storage->enabled = false;
192+
193+
/* initialize storage and OD extensions */
194+
ret = CO_storage_init(storage, CANmodule, OD_1010_StoreParameters, OD_1011_RestoreDefaultParam, storeFlash,
195+
restoreFlash, entries, entriesCount);
196+
197+
if (ret != CO_ERROR_NO)
198+
{
199+
return ret;
200+
}
201+
202+
/* initialize entries */
203+
*storageInitError = 0;
204+
205+
for (uint8_t i = 0; i < entriesCount; i++)
206+
{
207+
CO_storage_entry_t *entry = &entries[i];
208+
209+
/* verify arguments */
210+
if (entry->addr == NULL || entry->len == 0 || entry->subIndexOD < 2)
211+
{
212+
*storageInitError = i;
213+
return CO_ERROR_ILLEGAL_ARGUMENT;
214+
}
215+
216+
// Round up the offset to the nearest even integer
217+
// This is because we write 16 bytes and offset could a odd amount of bytes
218+
size_t roundedLength = (entry->len + 1) & ~1;
219+
220+
if (roundedLength + SIZE_CRC + SIZE_FIRST_ADDRESS > entry->reservedSpace)
221+
{
222+
*storageInitError = i;
223+
return CO_ERROR_OUT_OF_MEMORY;
224+
}
225+
226+
uint16_t *flash_pointer = ((uint16_t *)&_co_storage_start) + (entry->offset / sizeof(uint16_t));
227+
228+
// If a 1 is at the first address than something is written in Flash
229+
// Else keep the defaults
230+
if (*flash_pointer == 1U)
231+
{
232+
// Move the pointer to the second address
233+
flash_pointer++;
234+
235+
// Read the data from flash memory
236+
memcpy((uint8_t *)entry->addr, (uint8_t *)flash_pointer, entry->len);
237+
238+
// Calculate CRC
239+
entry->crc = crc16_ccitt(entry->addr, entry->len, 0);
240+
241+
// Read the CRC to verify
242+
uint16_t read_crc = *(flash_pointer + (roundedLength) / sizeof(uint16_t));
243+
244+
if (read_crc != entry->crc)
245+
{
246+
uint32_t errorBit = entry->subIndexOD;
247+
if (errorBit > 31)
248+
errorBit = 31;
249+
*storageInitError |= (1U) << errorBit;
250+
return CO_ERROR_DATA_CORRUPT;
251+
}
252+
}
253+
}
254+
storage->enabled = true;
255+
256+
return ret;
257+
}
258+
259+
#endif /* (CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE */
260+
261+
#endif

0 commit comments

Comments
 (0)