This repository was archived by the owner on Jan 29, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathFlashStorage_SAMD51.h
197 lines (150 loc) · 6.81 KB
/
FlashStorage_SAMD51.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
/******************************************************************************************************************************************
FlashStorage_SAMD51.h
For SAMD21/SAMD51 using Flash emulated-EEPROM
The FlashStorage_SAMD library aims to provide a convenient way to store and retrieve user's data using the non-volatile flash memory
of SAMD21/SAMD51. It now supports writing and reading the whole object, not just byte-and-byte.
Based on and modified from Cristian Maglie's FlashStorage (https://github.com/cmaglie/FlashStorage)
Built by Khoi Hoang https://github.com/khoih-prog/FlashStorage_SAMD
Licensed under LGPLv3 license
Orginally written by Cristian Maglie
Copyright (c) 2015 Arduino LLC. All right reserved.
Copyright (c) 2020 Khoi Hoang.
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License
as published bythe Free Software Foundation, either version 3 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 library.
If not, see (https://www.gnu.org/licenses/)
Version: 1.3.2
Version Modified By Date Comments
------- ----------- ---------- -----------
1.0.0 K Hoang 28/03/2020 Initial coding to add support to SAMD51 besides SAMD21
1.1.0 K Hoang 26/01/2021 Add supports to put() and get() for writing and reading the whole object. Fix bug.
1.2.0 K Hoang 18/08/2021 Optimize code. Add debug option
1.2.1 K Hoang 10/10/2021 Update `platform.ini` and `library.json`
1.3.0 K Hoang 25/01/2022 Fix `multiple-definitions` linker error. Add support to many more boards.
1.3.1 K Hoang 25/01/2022 Reduce number of library files
1.3.2 K Hoang 26/01/2022 Make compatible with old libraries and codes
******************************************************************************************************************************************/
// The .hpp contains only definitions, and can be included as many times as necessary, without `Multiple Definitions` Linker Error
// The .h contains implementations, and can be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error
#pragma once
#ifndef FlashStorage_SAMD51_h
#define FlashStorage_SAMD51_h
#ifndef BOARD_NAME
#define BOARD_NAME "Unknown SAMD51 board"
#endif
static const uint32_t pageSizes[] = { 8, 16, 32, 64, 128, 256, 512, 1024 };
/////////////////////////////////////////////////////
FlashClass::FlashClass(const void *flash_addr, uint32_t size) :
PAGE_SIZE(pageSizes[NVMCTRL->PARAM.bit.PSZ]),
PAGES(NVMCTRL->PARAM.bit.NVMP),
MAX_FLASH(PAGE_SIZE * PAGES),
ROW_SIZE(MAX_FLASH / 64),
flash_address((volatile void *)flash_addr),
flash_size(size)
{
}
/////////////////////////////////////////////////////
static inline uint32_t read_unaligned_uint32(const void *data)
{
union
{
uint32_t u32;
uint8_t u8[4];
} res;
const uint8_t *d = (const uint8_t *)data;
res.u8[0] = d[0];
res.u8[1] = d[1];
res.u8[2] = d[2];
res.u8[3] = d[3];
return res.u32;
}
/////////////////////////////////////////////////////
void FlashClass::write(const volatile void *flash_ptr, const void *data)
{
// Calculate data boundaries
uint32_t size = (flash_size + 3) / 4;
volatile uint32_t *dst_addr = (volatile uint32_t *)flash_ptr;
const uint8_t *src_addr = (uint8_t *)data;
// Disable automatic page write
NVMCTRL->CTRLA.bit.WMODE = 0;
////KH
/*
1. Configure manual write for the NVM using WMODE (NVMCTRL.CTRLA).
2. Make sure the NVM is ready to accept a new command (NVMCTRL.STATUS).
3. Clear page buffer ( NVMCTRL.CTRLB).
4. Make sure NVM is ready to accept a new command (NVMCTRL.STATUS).
5. Clear the DONE Flag (NVMCTRL.INTFLAG).
6. Write data to page buffer with 32-bit accesses at the needed address.
7. Perform page write (NVMCTRL.CTRLB).
8. Make sure NVM is ready to accept a new command (NVMCTRL.STATUS).
9. Clear the DONE Flag (NVMCTRL.INTFLAG).
*/
//KH
// 2. Make sure the NVM is ready to accept a new command (NVMCTRL.STATUS).
while (NVMCTRL->STATUS.bit.READY != NVMCTRL_STATUS_READY ) { }
// Do writes in pages
while (size)
{
// Execute "PBC" Page Buffer Clear
// 3. Clear page buffer ( NVMCTRL.CTRLB).
NVMCTRL->CTRLB.reg = NVMCTRL_CTRLB_CMDEX_KEY | NVMCTRL_CTRLB_CMD_PBC;
// 4. Make sure the NVM is ready to accept a new command (NVMCTRL.STATUS).
//while (NVMCTRL->STATUS.bit.READY != NVMCTRL_STATUS_READY ) { }
// 5. Clear the DONE Flag (NVMCTRL.INTFLAG)
while (NVMCTRL->INTFLAG.bit.DONE == 0) { }
// 6. Write data to page buffer with 32-bit accesses at the needed address.
// Fill page buffer
uint32_t i;
for (i = 0; i < (PAGE_SIZE / 4) && size; i++)
{
*dst_addr = read_unaligned_uint32(src_addr);
src_addr += 4;
dst_addr++;
size--;
}
//7. Perform page write (NVMCTRL.CTRLB).
// Execute "WP" Write Page
NVMCTRL->CTRLB.reg = NVMCTRL_CTRLB_CMDEX_KEY | NVMCTRL_CTRLB_CMD_WP;
// 8. Make sure NVM is ready to accept a new command (NVMCTRL.STATUS).
//while (NVMCTRL->STATUS.bit.READY != NVMCTRL_STATUS_READY ) { }
// 9. Clear the DONE Flag (NVMCTRL.INTFLAG)
while (NVMCTRL->INTFLAG.bit.DONE == 0) { }
}
}
/////////////////////////////////////////////////////
void FlashClass::read(const volatile void *flash_ptr, void *data)
{
FLASH_LOGERROR3(F("MAX_FLASH (KB) = "), MAX_FLASH / 1024, F(", ROW_SIZE ="), ROW_SIZE);
FLASH_LOGERROR1(F("FlashStorage size = "), flash_size);
FLASH_LOGERROR0(F("FlashStorage Start Address: 0x"));
FLASH_HEXLOGERROR0((uint32_t ) flash_address);
FLASH_LOGDEBUG0(F("Read: flash_ptr = 0x"));
FLASH_HEXLOGDEBUG0((uint32_t ) flash_ptr);
FLASH_LOGDEBUG0(F("data = 0x"));
FLASH_HEXLOGDEBUG0(* (uint32_t *) data);
memcpy(data, (const void *)flash_ptr, flash_size);
}
/////////////////////////////////////////////////////
void FlashClass::erase(const volatile void *flash_ptr, uint32_t size)
{
const uint8_t *ptr = (const uint8_t *)flash_ptr;
while (size > ROW_SIZE)
{
erase(ptr);
ptr += ROW_SIZE;
size -= ROW_SIZE;
}
erase(ptr);
}
/////////////////////////////////////////////////////
void FlashClass::erase(const volatile void *flash_ptr)
{
NVMCTRL->ADDR.reg = ((uint32_t)flash_ptr);
// Check, now erase PAGE, instead of ROW !!!
NVMCTRL->CTRLB.reg = NVMCTRL_CTRLB_CMDEX_KEY | NVMCTRL_CTRLB_CMD_EB;
while (NVMCTRL->INTFLAG.bit.DONE == 0) { }
}
/////////////////////////////////////////////////////
#endif //#ifndef FlashStorage_SAMD51_h