From f294a045e08e9af5b977920acf77c20ea00df8ec Mon Sep 17 00:00:00 2001 From: Scott Allen Date: Fri, 24 Mar 2017 15:53:35 -0400 Subject: [PATCH] Fix flashlight() and safeMode() for boot problem - Timer 0 is disabled in flashlight() and safeMode() in case its variables overlap the bootloader "magic key" location. - Flashlight mode never exits if invoked. - Made safeMode() public for use as a smaller code size alternative to flashlight(). --- keywords.txt | 1 + src/Arduboy2.cpp | 18 +++++++++++------- src/Arduboy2.h | 20 +++++++++++++++++--- src/Arduboy2Core.cpp | 18 +++++++++--------- src/Arduboy2Core.h | 34 +++++++++++++++++++--------------- 5 files changed, 57 insertions(+), 34 deletions(-) diff --git a/keywords.txt b/keywords.txt index d51ca64..978b64f 100644 --- a/keywords.txt +++ b/keywords.txt @@ -67,6 +67,7 @@ pollButtons KEYWORD2 pressed KEYWORD2 readUnitID KEYWORD2 readUnitName KEYWORD2 +safeMode KEYWORD2 saveOnOff KEYWORD2 setCursor KEYWORD2 setFrameRate KEYWORD2 diff --git a/src/Arduboy2.cpp b/src/Arduboy2.cpp index 69b6043..d783299 100644 --- a/src/Arduboy2.cpp +++ b/src/Arduboy2.cpp @@ -54,22 +54,25 @@ void Arduboy2Base::begin() void Arduboy2Base::flashlight() { - if(!pressed(UP_BUTTON)) { + if (!pressed(UP_BUTTON)) { return; } sendLCDCommand(OLED_ALL_PIXELS_ON); // smaller than allPixelsOn() digitalWriteRGB(RGB_ON, RGB_ON, RGB_ON); - while (!pressed(DOWN_BUTTON)) { + // prevent the bootloader magic number from being overwritten by timer 0 + // when a timer variable overlaps the magic number location, for when + // flashlight mode is used for upload problem recovery + power_timer0_disable(); + + while (true) { idle(); } - - digitalWriteRGB(RGB_OFF, RGB_OFF, RGB_OFF); - sendLCDCommand(OLED_PIXELS_FROM_RAM); } -void Arduboy2Base::systemButtons() { +void Arduboy2Base::systemButtons() +{ while (pressed(B_BUTTON)) { digitalWriteRGB(BLUE_LED, RGB_ON); // turn on blue LED sysCtrlSound(UP_BUTTON + B_BUTTON, GREEN_LED, 0xff); @@ -80,7 +83,8 @@ void Arduboy2Base::systemButtons() { digitalWriteRGB(BLUE_LED, RGB_OFF); // turn off blue LED } -void Arduboy2Base::sysCtrlSound(uint8_t buttons, uint8_t led, uint8_t eeVal) { +void Arduboy2Base::sysCtrlSound(uint8_t buttons, uint8_t led, uint8_t eeVal) +{ if (pressed(buttons)) { digitalWriteRGB(BLUE_LED, RGB_OFF); // turn off blue LED delay(200); diff --git a/src/Arduboy2.h b/src/Arduboy2.h index d2e90fc..1226a32 100644 --- a/src/Arduboy2.h +++ b/src/Arduboy2.h @@ -195,16 +195,30 @@ class Arduboy2Base : public Arduboy2Core void begin(); /** \brief - * Flashlight mode turns the RGB LED and display fully on. + * Turn the RGB LED and display fully on to act as a small flashlight/torch. * * \details * Checks if the UP button is pressed and if so turns the RGB LED and all - * display pixels fully on. Pressing the DOWN button will exit flashlight mode. + * display pixels fully on. If the UP button is detected, this function + * does not exit. The Arduboy must be restarted after flashlight mode is used. * * This function is called by `begin()` and can be called by a sketch * after `boot()`. * - * \see begin() boot() + * \note + * \parblock + * This function also contains code to address a problem with uploading a new + * sketch, for sketches that interfere with the bootloader "magic number". + * This problem occurs with certain sketches that use large amounts of RAM. + * Being in flashlight mode when uploading a new sketch can fix this problem. + * + * Therefore, for sketches that potentially could cause this problem, and use + * `boot()` instead of `begin()`, it is recommended that a call to + * `flashlight()` be included after calling `boot()`. If program space is + * limited, `safeMode()` can be used instead of `flashlight()`. + * \endparblock + * + * \see begin() boot() safeMode() */ void flashlight(); diff --git a/src/Arduboy2Core.cpp b/src/Arduboy2Core.cpp index 4e333f9..9955aad 100644 --- a/src/Arduboy2Core.cpp +++ b/src/Arduboy2Core.cpp @@ -82,12 +82,6 @@ void Arduboy2Core::boot() bootPins(); bootOLED(); - - #ifdef SAFE_MODE - if (buttonsState() == (LEFT_BUTTON | UP_BUTTON)) - safeMode(); - #endif - bootPowerSaving(); } @@ -240,9 +234,15 @@ void Arduboy2Core::SPItransfer(uint8_t data) void Arduboy2Core::safeMode() { - blank(); // too avoid random gibberish - while (true) { - asm volatile("nop \n"); + if (buttonsState() == UP_BUTTON) + { + digitalWriteRGB(RED_LED, RGB_ON); + + // prevent the bootloader magic number from being overwritten by timer 0 + // when a timer variable overlaps the magic number location + power_timer0_disable(); + + while (true) { } } } diff --git a/src/Arduboy2Core.h b/src/Arduboy2Core.h index 011d3c4..17647ad 100644 --- a/src/Arduboy2Core.h +++ b/src/Arduboy2Core.h @@ -32,11 +32,6 @@ // #define AB_DEVKIT //< compile for the official dev kit #endif - -#ifdef AB_DEVKIT -#define SAFE_MODE //< include safe mode (44 bytes) -#endif - #define RGB_ON LOW /**< For digitially setting an RGB LED on using digitalWriteRGB() */ #define RGB_OFF HIGH /**< For digitially setting an RGB LED off using digitalWriteRGB() */ @@ -619,19 +614,28 @@ class Arduboy2Core */ void static boot(); - protected: - /* - * Safe Mode is engaged by holding down both the LEFT button and UP button - * when plugging the device into USB. It puts your device into a tight - * loop and allows it to be reprogrammed even if you have uploaded a very - * broken sketch that interferes with the normal USB triggered auto-reboot - * functionality of the device. + /** \brief + * Allow upload when the bootloader "magic number" could be corrupted. * - * This is most useful on Devkits because they lack a built-in reset - * button. + * \details + * If the UP button is held when this function is entered, the RGB LED + * will be lit and timer 0 will be disabled, then the sketch will remain + * in a tight loop. This is to address a problem with uploading a new + * sketch, for sketches that interfere with the bootloader "magic number". + * The problem occurs with certain sketches that use large amounts of RAM. + * + * This function should be called after `boot()` in sketches that + * potentially could cause the problem. + * + * It is intended to replace the `flashlight()` function when more + * program space is required. If possible, it is more desirable to use + * `flashlight()`, so that the actual flashlight feature isn't lost. + * + * \see Arduboy2Base::flashlight() boot() */ - void static inline safeMode() __attribute__((always_inline)); + void static safeMode(); + protected: // internals void static inline setCPUSpeed8MHz() __attribute__((always_inline)); void static inline bootOLED() __attribute__((always_inline));