Skip to content

Commit 1ce9853

Browse files
Use Arcada button-reading and flash-writing functions
1 parent e0add1c commit 1ce9853

4 files changed

Lines changed: 29 additions & 185 deletions

File tree

M4_Eyes/M4_Eyes.ino

Lines changed: 26 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,6 @@ float iris_prev[IRIS_LEVELS] = { 0 };
7070
float iris_next[IRIS_LEVELS] = { 0 };
7171
uint16_t iris_frame = 0;
7272

73-
#if NUM_EYES > 1
74-
uint32_t priorButtonState;
75-
#endif
76-
7773
// Callback invoked after each SPI DMA transfer - sets a flag indicating
7874
// the next line of graphics can be issued as soon as its ready.
7975
static void dma_callback(Adafruit_ZeroDMA *dma) {
@@ -138,22 +134,37 @@ void setup() {
138134
while (1);
139135
}
140136
arcada.displayBegin();
141-
// MUST set up flash filesystem before Serial.begin() or seesaw.begin()
137+
142138
if (!arcada.filesysBeginMSD()) {
143139
fatal("No filesystem found!", 100);
144140
}
145141

146142
Serial.begin(115200);
147-
//while(!Serial) delay(10);
143+
// while(!Serial) delay(10);
148144

149145
Serial.printf("Available RAM at start: %d\n", availableRAM());
150-
Serial.printf("Available flash at start: %d\n", availableNVM());
146+
Serial.printf("Available flash at start: %d\n", arcada.availableFlash());
151147
yield(); // Periodic yield() makes sure mass storage filesystem stays alive
152148

153149
// Backlight(s) off ASAP, they'll switch on after screen(s) init & clear
154150
arcada.setBacklight(0);
155-
#if NUM_EYES > 1
156-
priorButtonState = 0b111000000000;
151+
152+
// No file selector yet. In the meantime, you can override the default
153+
// config file by holding one of the 3 edge buttons at startup (loads
154+
// config1.eye, config2.eye or config3.eye instead). Keep fingers clear
155+
// of the nose booper when doing this...it self-calibrates on startup.
156+
// DO THIS BEFORE THE SPLASH SO IT DOESN'T REQUIRE A LENGTHY HOLD.
157+
char *filename = "config.eye";
158+
#if NUM_EYES > 1 // Only available on MONSTER M4SK
159+
arcada.readButtons();
160+
uint32_t buttonState = arcada.justPressedButtons();
161+
if(buttonState & ARCADA_BUTTONMASK_UP) {
162+
filename = "config1.eye";
163+
} else if(buttonState & ARCADA_BUTTONMASK_A) {
164+
filename = "config2.eye";
165+
} else if(buttonState & ARCADA_BUTTONMASK_DOWN) {
166+
filename = "config3.eye";
167+
}
157168
#endif
158169

159170
yield();
@@ -249,22 +260,6 @@ void setup() {
249260

250261
// LOAD CONFIGURATION FILE -----------------------------------------------
251262

252-
// No file selector yet. In the meantime, you can override the default
253-
// config file by holding one of the 3 edge buttons at startup (loads
254-
// config1.eye, config2.eye or config3.eye instead). Keep fingers clear
255-
// of the nose booper when doing this...it self-calibrates on startup.
256-
char *filename = "config.eye";
257-
#if NUM_EYES > 1 // Only available on MONSTER M4SK
258-
/* MEMEFIX
259-
if(!(priorButtonState & 0b001000000000)) {
260-
filename = "config1.eye";
261-
} else if(!(priorButtonState & 0b010000000000)) {
262-
filename = "config2.eye";
263-
} else if(!(priorButtonState & 0b100000000000)) {
264-
filename = "config3.eye";
265-
}
266-
*/
267-
#endif
268263
loadConfig(filename);
269264

270265
// LOAD EYELIDS AND TEXTURE MAPS -----------------------------------------
@@ -894,23 +889,21 @@ void loop() {
894889
#if defined(ADAFRUIT_MONSTER_M4SK_EXPRESS)
895890
if(voiceOn) {
896891
// Read buttons, change pitch
897-
uint32_t buttonState = arcada.readButtons(); // Bits SET if currently pressed
898-
uint32_t changedState = ~(buttonState ^ priorButtonState); // Bits SET if changed from before
899-
uint32_t newlyPressed = buttonState | changedState; // Bits SET if newly pressed
900-
if( newlyPressed & ARCADA_BUTTONMASK_UP) { // Seesaw pin 9 (inner)
892+
arcada.readButtons();
893+
uint32_t buttonState = arcada.justPressedButtons();
894+
if( buttonState & ARCADA_BUTTONMASK_UP) {
901895
currentPitch *= 1.05;
902-
} else if(newlyPressed & ARCADA_BUTTONMASK_A) { // Seesaw pin 10 (middle)
896+
} else if(buttonState & ARCADA_BUTTONMASK_A) {
903897
currentPitch = defaultPitch;
904-
} else if(newlyPressed & ARCADA_BUTTONMASK_DOWN) { // Seesaw pin 11 (outer)
898+
} else if(buttonState & ARCADA_BUTTONMASK_DOWN) {
905899
currentPitch *= 0.95;
906900
}
907-
if(newlyPressed) {
901+
if(buttonState & (ARCADA_BUTTONMASK_UP | ARCADA_BUTTONMASK_A | ARCADA_BUTTONMASK_DOWN)) {
908902
currentPitch = voicePitch(currentPitch);
909903
if(waveform) voiceMod(modulate, waveform);
910904
Serial.print("Voice pitch: ");
911905
Serial.println(currentPitch);
912906
}
913-
priorButtonState = buttonState;
914907
}
915908
#endif // ADAFRUIT_MONSTER_M4SK_EXPRESS
916909
user_loop();

M4_Eyes/file.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,7 @@ ImageReturnCode loadTexture(char *filename, uint16_t **data,
437437
canvas->byteSwap(); // Match screen endianism for direct DMA xfer
438438
*width = image.width();
439439
*height = image.height();
440-
*data = (uint16_t *)writeDataToFlash((uint8_t *)canvas->getBuffer(),
440+
*data = (uint16_t *)arcada.writeDataToFlash((uint8_t *)canvas->getBuffer(),
441441
(int)*width * (int)*height * 2);
442442
} else {
443443
status = IMAGE_ERR_FORMAT; // Don't just return, need to dealloc...

M4_Eyes/globals.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//34567890123456789012345678901234567890123456789012345678901234567890123456
22

33
#include "Adafruit_Arcada.h"
4-
#include "DMAbuddy.h" // DMA-bug-workaround class
4+
#include "DMAbuddy.h" // DMA-bug-workaround class
55

66
#if defined(GLOBAL_VAR) // #defined in .ino file ONLY!
77
#define GLOBAL_INIT(X) = (X)

M4_Eyes/memory.cpp

Lines changed: 1 addition & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -10,153 +10,4 @@ uint32_t availableRAM(void) {
1010
return &top - (char *)sbrk(0); // Top of stack minus end of heap
1111
}
1212

13-
// NVM (Non-Volatile Memory, i.e. flash) functions -------------------------
14-
15-
#define FLASH_PAGE_SIZE (8 << NVMCTRL->PARAM.bit.PSZ)
16-
#define FLASH_NUM_PAGES NVMCTRL->PARAM.bit.NVMP
17-
#define FLASH_SIZE (FLASH_PAGE_SIZE * FLASH_NUM_PAGES)
18-
#define FLASH_BLOCK_SIZE (FLASH_PAGE_SIZE * 16) // Datasheet 25.6.2
19-
20-
extern uint32_t __etext; // CODE END. Symbol exported from linker script
21-
static uint8_t *flashAddress = NULL; // Initted on first use below
22-
23-
uint32_t availableNVM(void) {
24-
if(flashAddress == NULL) {
25-
// On first call, initialize flashAddress to first block boundary
26-
// following program storage. Code is uploaded page-at-a-time and
27-
// any trailing bytes in the last program block may be gibberish,
28-
// so we can't make use of that for ourselves.
29-
flashAddress = (uint8_t *)&__etext; // OK to overwrite the '0' there
30-
if((uint32_t)flashAddress % FLASH_BLOCK_SIZE) {
31-
flashAddress = &flashAddress[FLASH_BLOCK_SIZE -
32-
((uint32_t)flashAddress % FLASH_BLOCK_SIZE)];
33-
}
34-
} else {
35-
// On subsequent calls, round up to next quadword (16 byte) boundary,
36-
// try packing some data into the trailing bytes of the last-used flash
37-
// block! Saves up to (8K-16) bytes flash per call.
38-
if((uint32_t)flashAddress & 15) {
39-
flashAddress = &flashAddress[16 - ((uint32_t)flashAddress & 15)];
40-
}
41-
}
42-
return FLASH_SIZE - (uint32_t)flashAddress;
43-
}
44-
45-
static inline void wait_ready(void) {
46-
while(!NVMCTRL->STATUS.bit.READY);
47-
}
48-
49-
// Write a block of data to the NEXT AVAILABLE position in flash memory
50-
// (NOT a specific location). Returns pointer to stored data, NULL if
51-
// insufficient space or error.
52-
uint8_t *writeDataToFlash(uint8_t *ramAddress, uint32_t len) {
53-
54-
// availableNVM(), aside from reporting the amount of free flash memory,
55-
// also adjusts flashAddress to the first/next available usable boundary.
56-
// No need to do that manually here.
57-
if(len > availableNVM()) {
58-
Serial.println("Too large!");
59-
return NULL;
60-
}
61-
62-
uint16_t saveCache = NVMCTRL->CTRLA.reg; // Cache in Rev a silicon
63-
NVMCTRL->CTRLA.bit.CACHEDIS0 = true; // isn't reliable when
64-
NVMCTRL->CTRLA.bit.CACHEDIS1 = true; // writing to NVM.
65-
66-
// Set manual write mode - only needed once, not in loop
67-
NVMCTRL->CTRLA.bit.WMODE = NVMCTRL_CTRLA_WMODE_MAN;
68-
69-
// Clear page buffer, only needed once, quadword write also clears it
70-
NVMCTRL->CTRLB.reg = NVMCTRL_CTRLB_CMDEX_KEY | NVMCTRL_CTRLB_CMD_PBC;
71-
72-
for(uint8_t tries = 0;;) { // Repeat write sequence until success or limit
73-
74-
uint8_t *src = (uint8_t *)ramAddress; // Maintain passed-in pointers,
75-
uint32_t *dst = (uint32_t *)flashAddress; // modify these instead.
76-
int32_t bytesThisPass, bytesToGo = len;
77-
78-
Serial.print("Storing");
79-
wait_ready(); // Wait for any NVM write op in progress
80-
81-
while(bytesToGo > 0) {
82-
yield();
83-
// Because dst (via flashAddress) is always quadword-aligned at this
84-
// point, and flash blocks are known to be a quadword-multiple size,
85-
// this comparison is reasonable for checking for start of block...
86-
if(!((uint32_t)dst % FLASH_BLOCK_SIZE)) { // At block boundary
87-
// If ANY changed data within the entire block, it must be erased
88-
bytesThisPass = min(FLASH_BLOCK_SIZE, bytesToGo);
89-
if(memcmp(src, dst, bytesThisPass)) { // >0 if different
90-
Serial.write('-'); // minus = erasing
91-
wait_ready();
92-
NVMCTRL->ADDR.reg = (uint32_t)dst; // Destination address in flash
93-
NVMCTRL->CTRLB.reg = NVMCTRL_CTRLB_CMDEX_KEY | NVMCTRL_CTRLB_CMD_EB;
94-
} else { // Skip entire block
95-
Serial.print(">>"); // >> = skipping, already stored
96-
bytesToGo -= bytesThisPass;
97-
src += FLASH_BLOCK_SIZE; // Advance to next block
98-
dst += FLASH_BLOCK_SIZE / 4;
99-
continue;
100-
}
101-
}
102-
103-
// Examine next quadword, write only if needed (reduce flash wear)
104-
bytesThisPass = min(16, bytesToGo);
105-
if(memcmp(src, dst, bytesThisPass)) { // >0 if different
106-
if(!((uint32_t)dst & 2047)) Serial.write('.'); // One . per 2KB
107-
// src might not be 32-bit aligned and must be read byte-at-a-time.
108-
// dst write ops MUST be 32-bit! Won't work with memcpy().
109-
dst[0] = src[ 0] | (src[ 1]<<8) | (src[ 2]<<16) | (src[ 3]<<24);
110-
dst[1] = src[ 4] | (src[ 5]<<8) | (src[ 6]<<16) | (src[ 7]<<24);
111-
dst[2] = src[ 8] | (src[ 9]<<8) | (src[10]<<16) | (src[11]<<24);
112-
dst[3] = src[12] | (src[13]<<8) | (src[14]<<16) | (src[15]<<24);
113-
// Trigger the quadword write
114-
wait_ready();
115-
NVMCTRL->ADDR.reg = (uint32_t)dst;
116-
NVMCTRL->CTRLB.reg = NVMCTRL_CTRLB_CMDEX_KEY | NVMCTRL_CTRLB_CMD_WQW;
117-
}
118-
bytesToGo -= bytesThisPass;
119-
src += 16; // Advance to next quadword
120-
dst += 4;
121-
}
122-
123-
wait_ready(); // Wait for last write to finish
124-
125-
Serial.print("verify..."); Serial.flush();
126-
if(memcmp(ramAddress, flashAddress, len)) { // nonzero if mismatch
127-
if(++tries >= 4) {
128-
Serial.println("...proceeding anyway");
129-
break; // Give up, run with the data we have
130-
}
131-
// If we didn't start at a block boundary...
132-
if(uint32_t q = (uint32_t)flashAddress % FLASH_BLOCK_SIZE) {
133-
// Get index of first changed byte
134-
uint32_t n;
135-
for(n=0; (ramAddress[n] == flashAddress[n]) &&
136-
(n <= FLASH_BLOCK_SIZE); n++);
137-
// ...and if first mismatched byte is within the region before
138-
// the first block boundary...
139-
q = FLASH_BLOCK_SIZE - q; // Bytes in partial 1st block
140-
if(n < q) {
141-
// ...then flashAddress MUST be advanced to the next block
142-
// boundary before we retry, reason being that we CAN'T erase
143-
// the initial partial block (it may be preceded by other data).
144-
flashAddress = &flashAddress[q];
145-
}
146-
}
147-
Serial.println("...retrying..."); Serial.flush();
148-
} else {
149-
Serial.println("OK");
150-
break;
151-
}
152-
}
153-
154-
NVMCTRL->CTRLA.reg = saveCache; // Restore NVM cache settings
155-
156-
// Return value will be start of newly-written data in flash
157-
uint8_t *returnVal = flashAddress;
158-
// Move next flash address past new data
159-
// No need to align to next boundary, done at top of next call
160-
flashAddress += len;
161-
return returnVal;
162-
}
13+
// All the flash stuff has now been merged into Arcada library.

0 commit comments

Comments
 (0)