|
@@ -0,0 +1,317 @@
|
|
1
|
+/**
|
|
2
|
+ * Marlin 3D Printer Firmware
|
|
3
|
+ * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
|
4
|
+ *
|
|
5
|
+ * Based on Sprinter and grbl.
|
|
6
|
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
|
7
|
+ *
|
|
8
|
+ * This program is free software: you can redistribute it and/or modify
|
|
9
|
+ * it under the terms of the GNU General Public License as published by
|
|
10
|
+ * the Free Software Foundation, either version 3 of the License, or
|
|
11
|
+ * (at your option) any later version.
|
|
12
|
+ *
|
|
13
|
+ * This program is distributed in the hope that it will be useful,
|
|
14
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
15
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
16
|
+ * GNU General Public License for more details.
|
|
17
|
+ *
|
|
18
|
+ * You should have received a copy of the GNU General Public License
|
|
19
|
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
20
|
+ *
|
|
21
|
+ */
|
|
22
|
+
|
|
23
|
+#include "../../../../inc/MarlinConfigPre.h"
|
|
24
|
+
|
|
25
|
+#if HAS_TFT_LVGL_UI
|
|
26
|
+
|
|
27
|
+#include "SPIFlashStorage.h"
|
|
28
|
+
|
|
29
|
+uint8_t SPIFlashStorage::::m_pageData[SPI_FLASH_PageSize];
|
|
30
|
+uint32_t SPIFlashStorage::::m_currentPage;
|
|
31
|
+uint16_t SPIFlashStorage::::m_pageDataUsed;
|
|
32
|
+uint32_t SPIFlashStorage::::m_startAddress;
|
|
33
|
+
|
|
34
|
+#if HAS_SPI_FLASH_COMPRESSION
|
|
35
|
+
|
|
36
|
+ uint8_t SPIFlashStorage::m_compressedData[SPI_FLASH_PageSize];
|
|
37
|
+ uint16_t SPIFlashStorage::m_compressedDataUsed;
|
|
38
|
+
|
|
39
|
+ template <typename T>
|
|
40
|
+ static uint32_t rle_compress(T *output, uint32_t outputLength, T *input, uint32_t inputLength, uint32_t& inputProcessed) {
|
|
41
|
+ uint32_t count = 0, out = 0, index, i;
|
|
42
|
+ T pixel;
|
|
43
|
+ //32767 for uint16_t
|
|
44
|
+ //127 for uint16_t
|
|
45
|
+ //calculated at compile time
|
|
46
|
+ constexpr T max = (0xFFFFFFFF >> (8 * (4 - sizeof(T)))) / 2;
|
|
47
|
+
|
|
48
|
+ inputProcessed = 0;
|
|
49
|
+ while (count < inputLength && out < outputLength) {
|
|
50
|
+ index = count;
|
|
51
|
+ pixel = input[index++];
|
|
52
|
+ while (index < inputLength && index - count < max && input[index] == pixel)
|
|
53
|
+ index++;
|
|
54
|
+ if (index - count == 1) {
|
|
55
|
+ /*
|
|
56
|
+ * Failed to "replicate" the current pixel. See how many to copy.
|
|
57
|
+ * Avoid a replicate run of only 2-pixels after a literal run. There
|
|
58
|
+ * is no gain in this, and there is a risK of loss if the run after
|
|
59
|
+ * the two identical pixels is another literal run. So search for
|
|
60
|
+ * 3 identical pixels.
|
|
61
|
+ */
|
|
62
|
+ while (index < inputLength && index - count < max && (input[index] != input[index - 1] || (index > 1 && input[index] != input[index - 2])))
|
|
63
|
+ index++;
|
|
64
|
+ /*
|
|
65
|
+ * Check why this run stopped. If it found two identical pixels, reset
|
|
66
|
+ * the index so we can add a run. Do this twice: the previous run
|
|
67
|
+ * tried to detect a replicate run of at least 3 pixels. So we may be
|
|
68
|
+ * able to back up two pixels if such a replicate run was found.
|
|
69
|
+ */
|
|
70
|
+ while (index < inputLength && input[index] == input[index - 1])
|
|
71
|
+ index--;
|
|
72
|
+ // If the output buffer could overflow, stop at the remaining bytes
|
|
73
|
+ NOMORE(index, count + outputLength - out - 1);
|
|
74
|
+ output[out++] = (uint16_t)(count - index);
|
|
75
|
+ for (i = count; i < index; i++)
|
|
76
|
+ output[out++] = input[i];
|
|
77
|
+ }
|
|
78
|
+ else {
|
|
79
|
+ // Need at least more 2 spaces
|
|
80
|
+ if (out > outputLength - 2) break;
|
|
81
|
+ output[out++] = (uint16_t)(index - count);
|
|
82
|
+ output[out++] = pixel;
|
|
83
|
+ }
|
|
84
|
+ count = index;
|
|
85
|
+ }
|
|
86
|
+ inputProcessed = count;
|
|
87
|
+
|
|
88
|
+ // Padding
|
|
89
|
+ if (out == outputLength - 1) output[out++] = 0;
|
|
90
|
+
|
|
91
|
+ return out;
|
|
92
|
+ }
|
|
93
|
+
|
|
94
|
+ template <typename UT, typename T>
|
|
95
|
+ static uint32_t rle_uncompress(UT *output, uint32_t outputLength, UT *input, uint32_t inputLength, uint32_t &outputFilled) {
|
|
96
|
+ T count;
|
|
97
|
+ UT i;
|
|
98
|
+ uint32_t processedBytes = 0;
|
|
99
|
+ outputFilled = 0;
|
|
100
|
+
|
|
101
|
+ while (outputLength > 0 && inputLength > 0) {
|
|
102
|
+ processedBytes++;
|
|
103
|
+ count = static_cast<T>(*input++);
|
|
104
|
+ inputLength--;
|
|
105
|
+ if (count > 0) { // Replicate run
|
|
106
|
+ for (i = 0; i < count && outputLength > i; i++)
|
|
107
|
+ output[i] = *input;
|
|
108
|
+ outputFilled += i;
|
|
109
|
+ // If copy incomplete, change the input buffer to start with remaining data in the next call
|
|
110
|
+ if (i < count) {
|
|
111
|
+ // Change to process the difference in the next call
|
|
112
|
+ *(input - 1) = static_cast<UT>(count - i);
|
|
113
|
+ return processedBytes - 1;
|
|
114
|
+ }
|
|
115
|
+ input++;
|
|
116
|
+ inputLength--;
|
|
117
|
+ processedBytes++;
|
|
118
|
+ }
|
|
119
|
+ else if (count < 0) { // literal run
|
|
120
|
+ count = static_cast<T>(-count);
|
|
121
|
+ // Copy, validating if the output have enough space
|
|
122
|
+ for (i = 0; i < count && outputLength > i; i++)
|
|
123
|
+ output[i] = input[i];
|
|
124
|
+ outputFilled += i;
|
|
125
|
+ // If copy incomplete, change the input buffer to start with remaining data in the next call
|
|
126
|
+ if (i < count) {
|
|
127
|
+ input[i - 1] = static_cast<UT>((count - i) * -1);
|
|
128
|
+ // Back one
|
|
129
|
+ return processedBytes + i - 1;
|
|
130
|
+ }
|
|
131
|
+ input += count;
|
|
132
|
+ inputLength -= count;
|
|
133
|
+ processedBytes += count;
|
|
134
|
+ }
|
|
135
|
+ output += count;
|
|
136
|
+ outputLength -= count;
|
|
137
|
+ }
|
|
138
|
+
|
|
139
|
+ return processedBytes;
|
|
140
|
+ }
|
|
141
|
+
|
|
142
|
+#endif // HAS_SPI_FLASH_COMPRESSION
|
|
143
|
+
|
|
144
|
+void SPIFlashStorage::beginWrite(uint32_t startAddress) {
|
|
145
|
+ m_pageDataUsed = 0;
|
|
146
|
+ m_currentPage = 0;
|
|
147
|
+ m_startAddress = startAddress;
|
|
148
|
+ #if HAS_SPI_FLASH_COMPRESSION
|
|
149
|
+ // Restart the compressed buffer, keep the pointers of the uncompressed buffer
|
|
150
|
+ m_compressedDataUsed = 0;
|
|
151
|
+ #endif
|
|
152
|
+}
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+void SPIFlashStorage::endWrite() {
|
|
156
|
+ // Flush remaining data
|
|
157
|
+ #if HAS_SPI_FLASH_COMPRESSION
|
|
158
|
+ if (m_compressedDataUsed > 0) {
|
|
159
|
+ flushPage();
|
|
160
|
+ savePage(m_compressedData);
|
|
161
|
+ }
|
|
162
|
+ #else
|
|
163
|
+ if (m_pageDataUsed > 0) flushPage();
|
|
164
|
+ #endif
|
|
165
|
+}
|
|
166
|
+
|
|
167
|
+void SPIFlashStorage::savePage(uint8_t* buffer) {
|
|
168
|
+ W25QXX.SPI_FLASH_BufferWrite(buffer, m_startAddress + (SPI_FLASH_PageSize * m_currentPage), SPI_FLASH_PageSize);
|
|
169
|
+
|
|
170
|
+ // Test env
|
|
171
|
+ // char fname[256];
|
|
172
|
+ // snprintf(fname, sizeof(fname), "./pages/page-%03d.data", m_currentPage);
|
|
173
|
+ // FILE *fp = fopen(fname, "wb");
|
|
174
|
+ // fwrite(buffer, 1, m_compressedDataUsed, fp);
|
|
175
|
+ // fclose(fp);
|
|
176
|
+}
|
|
177
|
+
|
|
178
|
+void SPIFlashStorage::loadPage(uint8_t* buffer) {
|
|
179
|
+ W25QXX.SPI_FLASH_BufferRead(buffer, m_startAddress + (SPI_FLASH_PageSize * m_currentPage), SPI_FLASH_PageSize);
|
|
180
|
+
|
|
181
|
+ // Test env
|
|
182
|
+ // char fname[256];
|
|
183
|
+ // memset(buffer, 0, SPI_FLASH_PageSize);
|
|
184
|
+ // snprintf(fname, sizeof(fname), "./pages/page-%03d.data", m_currentPage);
|
|
185
|
+ // FILE *fp = fopen(fname, "rb");
|
|
186
|
+ // if (fp != NULL) {
|
|
187
|
+ // fread(buffer, 1, SPI_FLASH_PageSize, fp);
|
|
188
|
+ // fclose(fp);
|
|
189
|
+ // }
|
|
190
|
+}
|
|
191
|
+
|
|
192
|
+void SPIFlashStorage::flushPage() {
|
|
193
|
+ #if HAS_SPI_FLASH_COMPRESSION
|
|
194
|
+ // Work com with compressed in memory
|
|
195
|
+ uint32_t inputProcessed;
|
|
196
|
+ uint32_t compressedSize = rle_compress<uint16_t>((uint16_t *)(m_compressedData + m_compressedDataUsed), compressedDataFree() / 2, (uint16_t *)m_pageData, m_pageDataUsed / 2, inputProcessed) * 2;
|
|
197
|
+ inputProcessed *= 2;
|
|
198
|
+ m_compressedDataUsed += compressedSize;
|
|
199
|
+
|
|
200
|
+ // Space remaining in the compressed buffer?
|
|
201
|
+ if (compressedDataFree() > 0) {
|
|
202
|
+ // Free the uncompressed buffer
|
|
203
|
+ m_pageDataUsed = 0;
|
|
204
|
+ return;
|
|
205
|
+ }
|
|
206
|
+
|
|
207
|
+ // Part of the m_pageData was compressed, so ajust the pointers, freeing what was processed, shift the buffer
|
|
208
|
+ // TODO: To avoid this copy, use a circular buffer
|
|
209
|
+ memmove(m_pageData, m_pageData + inputProcessed, m_pageDataUsed - inputProcessed);
|
|
210
|
+ m_pageDataUsed -= inputProcessed;
|
|
211
|
+
|
|
212
|
+ // No? So flush page with compressed data!!
|
|
213
|
+ uint8_t *buffer = m_compressedData;
|
|
214
|
+ #else
|
|
215
|
+ uint8_t *buffer = m_pageData;
|
|
216
|
+ #endif
|
|
217
|
+
|
|
218
|
+ savePage(buffer);
|
|
219
|
+
|
|
220
|
+ #if HAS_SPI_FLASH_COMPRESSION
|
|
221
|
+ // Restart the compressed buffer, keep the pointers of the uncompressed buffer
|
|
222
|
+ m_compressedDataUsed = 0;
|
|
223
|
+ #elif
|
|
224
|
+ m_pageDataUsed = 0;
|
|
225
|
+ #endif
|
|
226
|
+ m_currentPage++;
|
|
227
|
+}
|
|
228
|
+
|
|
229
|
+void SPIFlashStorage::readPage() {
|
|
230
|
+ #if HAS_SPI_FLASH_COMPRESSION
|
|
231
|
+ if (compressedDataFree() == 0) {
|
|
232
|
+ loadPage(m_compressedData);
|
|
233
|
+ m_currentPage++;
|
|
234
|
+ m_compressedDataUsed = 0;
|
|
235
|
+ }
|
|
236
|
+
|
|
237
|
+ // Need to uncompress data
|
|
238
|
+ if (pageDataFree() == 0) {
|
|
239
|
+ m_pageDataUsed = 0;
|
|
240
|
+ uint32_t outpuProcessed = 0;
|
|
241
|
+ uint32_t inputProcessed = rle_uncompress<uint16_t, int16_t>((uint16_t *)(m_pageData + m_pageDataUsed), pageDataFree() / 2, (uint16_t *)(m_compressedData + m_compressedDataUsed), compressedDataFree() / 2, outpuProcessed);
|
|
242
|
+ inputProcessed *= 2;
|
|
243
|
+ outpuProcessed *= 2;
|
|
244
|
+ if (outpuProcessed < pageDataFree()) {
|
|
245
|
+ m_pageDataUsed = SPI_FLASH_PageSize - outpuProcessed;
|
|
246
|
+ // TODO: To avoid this copy, use a circular buffer
|
|
247
|
+ memmove(m_pageData + m_pageDataUsed, m_pageData, outpuProcessed);
|
|
248
|
+ }
|
|
249
|
+
|
|
250
|
+ m_compressedDataUsed += inputProcessed;
|
|
251
|
+ }
|
|
252
|
+ #else
|
|
253
|
+ loadPage(m_pageData);
|
|
254
|
+ m_pageDataUsed = 0;
|
|
255
|
+ m_currentPage++;
|
|
256
|
+ #endif
|
|
257
|
+}
|
|
258
|
+
|
|
259
|
+uint16_t SPIFlashStorage::inData(uint8_t* data, uint16_t size) {
|
|
260
|
+ // Don't write more than we can
|
|
261
|
+ NOMORE(size, pageDataFree());
|
|
262
|
+ memcpy(m_pageData + m_pageDataUsed, data, size);
|
|
263
|
+ m_pageDataUsed += size;
|
|
264
|
+ return size;
|
|
265
|
+}
|
|
266
|
+
|
|
267
|
+void SPIFlashStorage::writeData(uint8_t* data, uint16_t size) {
|
|
268
|
+ // Flush a page if needed
|
|
269
|
+ if (pageDataFree() == 0) flushPage();
|
|
270
|
+
|
|
271
|
+ while (size > 0) {
|
|
272
|
+ uint16_t written = inData(data, size);
|
|
273
|
+ size -= written;
|
|
274
|
+ // Need to write more? Flush page and continue!
|
|
275
|
+ if (size > 0) {
|
|
276
|
+ flushPage();
|
|
277
|
+ data += written;
|
|
278
|
+ }
|
|
279
|
+ }
|
|
280
|
+}
|
|
281
|
+
|
|
282
|
+void SPIFlashStorage::beginRead(uint32_t startAddress) {
|
|
283
|
+ m_startAddress = startAddress;
|
|
284
|
+ m_currentPage = 0;
|
|
285
|
+ // Nothing in memory now
|
|
286
|
+ m_pageDataUsed = SPI_FLASH_PageSize;
|
|
287
|
+ #if HAS_SPI_FLASH_COMPRESSION
|
|
288
|
+ m_compressedDataUsed = sizeof(m_compressedData);
|
|
289
|
+ #endif
|
|
290
|
+}
|
|
291
|
+
|
|
292
|
+uint16_t SPIFlashStorage::outData(uint8_t* data, uint16_t size) {
|
|
293
|
+ // Don't read more than we have
|
|
294
|
+ NOMORE(size > pageDataFree());
|
|
295
|
+ memcpy(data, m_pageData + m_pageDataUsed, size);
|
|
296
|
+ m_pageDataUsed += size;
|
|
297
|
+ return size;
|
|
298
|
+}
|
|
299
|
+
|
|
300
|
+void SPIFlashStorage::readData(uint8_t* data, uint16_t size) {
|
|
301
|
+ // Read a page if needed
|
|
302
|
+ if (pageDataFree() == 0) readPage();
|
|
303
|
+
|
|
304
|
+ while (size > 0) {
|
|
305
|
+ uint16_t read = outData(data, size);
|
|
306
|
+ size -= read;
|
|
307
|
+ // Need to write more? Flush page and continue!
|
|
308
|
+ if (size > 0) {
|
|
309
|
+ readPage();
|
|
310
|
+ data += read;
|
|
311
|
+ }
|
|
312
|
+ }
|
|
313
|
+}
|
|
314
|
+
|
|
315
|
+SPIFlashStorage SPIFlash;
|
|
316
|
+
|
|
317
|
+#endif // HAS_TFT_LVGL_UI
|