Skip to content

Commit fa512a8

Browse files
Add lots of yield()s, add user.cpp tab
1 parent 2025479 commit fa512a8

6 files changed

Lines changed: 90 additions & 1 deletion

File tree

M4_Eyes/M4_Eyes.ino

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ void setup() {
140140

141141
Serial.printf("Available RAM at start: %d\n", availableRAM());
142142
Serial.printf("Available flash at start: %d\n", availableNVM());
143+
yield(); // Periodic yield() makes sure mass storage filesystem stays alive
143144

144145
// Backlight(s) off ASAP, they'll switch on after screen(s) init & clear
145146
pinMode(BACKLIGHT_PIN, OUTPUT);
@@ -163,6 +164,7 @@ void setup() {
163164
delay(20);
164165
#endif
165166

167+
yield();
166168
uint8_t e, rtna = 0x01; // Screen refresh rate control (datasheet 9.2.18, FRCTRL2)
167169
// Initialize displays
168170
for(e=0; e<NUM_EYES; e++) {
@@ -174,10 +176,12 @@ void setup() {
174176
eye[e].display->setRotation(0);
175177
}
176178

179+
yield();
177180
if (reader.drawBMP("/splash.bmp", *(eye[0].display), 0, 0) == IMAGE_SUCCESS) {
178181
Serial.println("Splashing");
179182
#if NUM_EYES > 1
180183
// other eye
184+
yield();
181185
reader.drawBMP("/splash.bmp", *(eye[1].display), 0, 0);
182186
#endif
183187
// backlight on for a bit
@@ -200,6 +204,7 @@ void setup() {
200204
}
201205

202206
// Initialize DMAs
207+
yield();
203208
for(e=0; e<NUM_EYES; e++) {
204209
eye[e].display->fillScreen(0);
205210
eye[e].dma.allocate();
@@ -309,6 +314,7 @@ void setup() {
309314
// Load texture maps for eyes
310315
uint8_t e2;
311316
for(e=0; e<NUM_EYES; e++) { // For each eye...
317+
yield();
312318
for(e2=0; e2<e; e2++) { // Compare against each prior eye...
313319
// If both eyes have the same iris filename...
314320
if((eye[e].iris.filename && eye[e2].iris.filename) &&
@@ -362,6 +368,7 @@ void setup() {
362368
}
363369

364370
// Load eyelid graphics.
371+
yield();
365372

366373
status = loadEyelid(upperEyelidFilename ?
367374
upperEyelidFilename : (char *)"upper.bmp",
@@ -389,6 +396,7 @@ void setup() {
389396
calcDisplacement();
390397
Serial.printf("Free RAM: %d\n", availableRAM());
391398

399+
yield();
392400
if(boopPin >= 0) {
393401
boopThreshold = 0;
394402
for(i=0; i<240; i++) {
@@ -405,6 +413,9 @@ void setup() {
405413
eye[e].eyeX = eyeOldX; // Set up initial position
406414
eye[e].eyeY = eyeOldY;
407415
}
416+
417+
user_setup();
418+
408419
lastLightReadTime = micros() + 2000000; // Delay initial light reading
409420
}
410421

@@ -890,6 +901,7 @@ void loop() {
890901
irisValue = irisMin + (sum * irisRange); // 0.0-1.0 -> iris min/max
891902
if((++iris_frame) >= (1 << IRIS_LEVELS)) iris_frame = 0;
892903
}
904+
user_loop();
893905
}
894906
} // end first-column check
895907

M4_Eyes/file.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,10 @@ void loadConfig(char *filename) {
190190
if(file = filesys.open(filename, FILE_READ)) {
191191
StaticJsonDocument<2048> doc;
192192

193-
delay(100); // Make sure mass storage handler has a turn first!
193+
yield();
194+
// delay(100); // Make sure mass storage handler has a turn first!
194195
DeserializationError error = deserializeJson(doc, file);
196+
yield();
195197
if(error) {
196198
Serial.println("Config file error, using default settings");
197199
Serial.println(error.c_str());
@@ -430,6 +432,7 @@ ImageReturnCode loadEyelid(char *filename,
430432
// later) are easily removed.
431433
}
432434

435+
yield();
433436
if((status = reader.loadBMP(filename, image)) == IMAGE_SUCCESS) {
434437
if(image.getFormat() == IMAGE_1) { // MUST be 1-bit image
435438
uint16_t *palette = image.getPalette();
@@ -504,6 +507,7 @@ ImageReturnCode loadTexture(char *filename, uint16_t **data,
504507
// later) are easily removed.
505508
}
506509

510+
yield();
507511
if((status = reader.loadBMP(filename, image)) == IMAGE_SUCCESS) {
508512
if(image.getFormat() == IMAGE_16) { // MUST be 16-bit image
509513
Serial.println("Texture loaded!");

M4_Eyes/globals.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,3 +212,7 @@ extern void calcDisplacement(void);
212212
extern void calcMap(void);
213213
extern float screen2map(int in);
214214
extern float map2screen(int in);
215+
216+
// Functions in user.cpp
217+
extern void user_setup(void);
218+
extern void user_loop(void);

M4_Eyes/memory.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ uint8_t *writeDataToFlash(uint8_t *ramAddress, uint32_t len) {
7979
wait_ready(); // Wait for any NVM write op in progress
8080

8181
while(bytesToGo > 0) {
82+
yield();
8283
// Because dst (via flashAddress) is always quadword-aligned at this
8384
// point, and flash blocks are known to be a quadword-multiple size,
8485
// this comparison is reasonable for checking for start of block...

M4_Eyes/tablegen.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ void calcDisplacement() {
2525
// "+Y is up" Cartesian coordinate space; any mirroring or rotation
2626
// is handled in eye rendering code.
2727
for(y=0; y<120; y++) {
28+
yield(); // Periodic yield() makes sure mass storage filesystem stays alive
2829
dy = (float)y + 0.5;
2930
dy *= dy; // Now dy^2
3031
for(x=0; x<120; x++) {
@@ -68,6 +69,7 @@ void calcMap(void) {
6869
int x, y;
6970
float dx, dy, dy2, d2, d, angle, xp;
7071
for(y=0; y<mapRadius; y++) {
72+
yield(); // Periodic yield() makes sure mass storage filesystem stays alive
7173
dy = (float)y + 0.5; // Y distance to map center
7274
dy2 = dy * dy;
7375
for(x=0; x<mapRadius; x++) {
@@ -101,6 +103,7 @@ void calcMap(void) {
101103
if(slitPupilRadius > 0) {
102104
// Iterate over each pixel in the iris section of the polar map...
103105
for(y=0; y < mapRadius; y++) {
106+
yield(); // Periodic yield() makes sure mass storage filesystem stays alive
104107
dy = y + 0.5; // Distance to center, Y component
105108
dy2 = dy * dy;
106109
for(x=0; x < mapRadius; x++) {

M4_Eyes/user.cpp

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// This file provides a crude way to "drop in" user code to the eyes,
2+
// allowing concurrent operations without having to maintain a bunch of
3+
// special derivatives of the eye code (which is still undergoing a lot
4+
// of development). Just replace the source code contents of THIS TAB ONLY,
5+
// compile and upload to board. Shouldn't need to modify other eye code.
6+
7+
// User globals can go here, recommend declaring as static, e.g.:
8+
// static int foo = 42;
9+
10+
// Called once near the end of the setup() function. If your code requires
11+
// a lot of time to initialize, make periodic calls to yield() to keep the
12+
// USB mass storage filesystem alive.
13+
void user_setup(void) {
14+
}
15+
16+
// Called periodically during eye animation. This is invoked in the
17+
// interval before starting drawing on the last eye (left eye on MONSTER
18+
// M4SK, sole eye on HalloWing M0) so it won't exacerbate visible tearing
19+
// in eye rendering. This is also SPI "quiet time" on the MONSTER M4SK so
20+
// it's OK to do I2C or other communication across the bridge.
21+
// This function BLOCKS, it does NOT multitask with the eye animation code,
22+
// and performance here will have a direct impact on overall refresh rates,
23+
// so keep it simple. Avoid loops (e.g. if animating something like a servo
24+
// or NeoPixels in response to some trigger) and instead rely on state
25+
// machines or similar. Additionally, calls to this function are NOT time-
26+
// constant -- eye rendering time can vary frame to frame, so animation or
27+
// other over-time operations won't look very good using simple +/-
28+
// increments, it's better to use millis() or micros() and work
29+
// algebraically with elapsed times instead.
30+
void user_loop(void) {
31+
/*
32+
Suppose we have a global bool "animating" (meaning something is in
33+
motion) and global uint32_t's "startTime" (the initial time at which
34+
something triggered movement) and "transitionTime" (the total time
35+
over which movement should occur, expressed in microseconds).
36+
Maybe it's servos, maybe NeoPixels, or something different altogether.
37+
This function might resemble something like (pseudocode):
38+
39+
if(!animating) {
40+
Not in motion, check sensor for trigger...
41+
if(read some sensor) {
42+
Motion is triggered! Record startTime, set transition
43+
to 1.5 seconds and set animating flag:
44+
startTime = micros();
45+
transitionTime = 1500000;
46+
animating = true;
47+
No motion actually takes place yet, that will begin on
48+
the next pass through this function.
49+
}
50+
} else {
51+
Currently in motion, ignore trigger and move things instead...
52+
uint32_t elapsed = millis() - startTime;
53+
if(elapsed < transitionTime) {
54+
Part way through motion...how far along?
55+
float ratio = (float)elapsed / (float)transitionTime;
56+
Do something here based on ratio, 0.0 = start, 1.0 = end
57+
} else {
58+
End of motion reached.
59+
Take whatever steps here to move into final position (1.0),
60+
and then clear the "animating" flag:
61+
animating = false;
62+
}
63+
}
64+
*/
65+
}

0 commit comments

Comments
 (0)