diff --git a/README.md b/README.md index 358a6ae..264fabf 100644 --- a/README.md +++ b/README.md @@ -221,6 +221,12 @@ There are a few functions provided that are roughly equivalent to the standard f - *bootLogoText()* can be used in place *bootLogo()* in the case where the sketch uses text functions. It renders the logo as text instead of as a bitmap (so doesn't look as good). - *safeMode()* can be used in place of *flashlight()* for cases where it's needed to allow uploading a new sketch when the bootloader "magic key" problem is an issue. It only lights the red RGB LED, so you don't get the bright light that is the primary purpose of *flashlight()*. +#### Eliminate the USB stack code + +**Warning:** Although this will free up a fair amount of code and some RAM space, without an active USB interface uploader programs will be unable to automatically force a reset to invoke the bootloader. This means the user will have to manually initiate a reset in order to upload a new sketch. This can be an inconvenience or even frustrating for a user, due to the fact that timing the sequence can sometimes be tricky. Therefore, using this technique should be considered as a last resort. If it is used, the sketch documentation should state clearly what will be involved to upload a new sketch. + +The *ARDUBOY_NO_USB* macro is used to eliminate the USB code. The *exitToBootloader()* function is available to make it easier for a user to invoke the bootloader. For more details, see the documentation provided for these. + ---------- ## What's different from Arduboy library V1.1 diff --git a/keywords.txt b/keywords.txt index 3571172..9c44059 100644 --- a/keywords.txt +++ b/keywords.txt @@ -49,6 +49,7 @@ drawSlowXYBitmap KEYWORD2 drawTriangle KEYWORD2 enabled KEYWORD2 everyXFrames KEYWORD2 +exitToBootloader KEYWORD2 fillCircle KEYWORD2 fillRect KEYWORD2 fillRoundRect KEYWORD2 @@ -149,3 +150,5 @@ RED_LED LITERAL1 RGB_OFF LITERAL1 RGB_ON LITERAL1 +ARDUBOY_NO_USB LITERAL1 + diff --git a/src/Arduboy2Core.cpp b/src/Arduboy2Core.cpp index 69b3416..972185a 100644 --- a/src/Arduboy2Core.cpp +++ b/src/Arduboy2Core.cpp @@ -117,10 +117,10 @@ void Arduboy2Core::bootPins() _BV(B_BUTTON_BIT); // Port B INPUT or LOW (none) // Port B inputs - DDRB &= ~(_BV(B_BUTTON_BIT)); + DDRB &= ~(_BV(B_BUTTON_BIT) | _BV(SPI_MISO_BIT)); // Port B outputs DDRB |= _BV(RED_LED_BIT) | _BV(GREEN_LED_BIT) | _BV(BLUE_LED_BIT) | - _BV(SPI_MOSI_BIT) | _BV(SPI_SCK_BIT); + _BV(SPI_MOSI_BIT) | _BV(SPI_SCK_BIT) | _BV(SPI_SS_BIT); // Port C // Speaker: Not set here. Controlled by audio class @@ -158,9 +158,11 @@ void Arduboy2Core::bootPins() _BV(BLUE_LED_BIT); // Port B INPUT or LOW (none) // Port B inputs - DDRB &= ~(_BV(LEFT_BUTTON_BIT) | _BV(UP_BUTTON_BIT) | _BV(DOWN_BUTTON_BIT)); + DDRB &= ~(_BV(LEFT_BUTTON_BIT) | _BV(UP_BUTTON_BIT) | _BV(DOWN_BUTTON_BIT) | + _BV(SPI_MISO_BIT)); // Port B outputs - DDRB |= _BV(BLUE_LED_BIT) | _BV(SPI_MOSI_BIT) | _BV(SPI_SCK_BIT); + DDRB |= _BV(SPI_MOSI_BIT) | _BV(SPI_SCK_BIT) | _BV(SPI_SS_BIT) | + _BV(BLUE_LED_BIT); // Port C INPUT_PULLUP or HIGH PORTC |= _BV(RIGHT_BUTTON_BIT); @@ -269,10 +271,10 @@ void Arduboy2Core::idle() void Arduboy2Core::bootPowerSaving() { // disable Two Wire Interface (I2C) and the ADC + // All other bits will be written with 0 so will be enabled PRR0 = _BV(PRTWI) | _BV(PRADC); // disable USART1 - PRR1 = _BV(PRUSART1); - // All other bits will be written with 0 so will be enabled + PRR1 |= _BV(PRUSART1); } // Shut down the display @@ -555,3 +557,74 @@ void Arduboy2Core::delayShort(uint16_t ms) delay((unsigned long) ms); } +void Arduboy2Core::exitToBootloader() +{ + cli(); + // set bootloader magic key + // storing two uint8_t instead of one uint16_t saves an instruction + // when high and low bytes of the magic key are the same + *(uint8_t *)MAGIC_KEY_POS = lowByte(MAGIC_KEY); + *(uint8_t *)(MAGIC_KEY_POS + 1) = highByte(MAGIC_KEY); + // enable watchdog timer reset, with 16ms timeout + wdt_reset(); + WDTCSR = (_BV(WDCE) | _BV(WDE)); + WDTCSR = _BV(WDE); + while (true) { } +} + +// Replacement main() that eliminates the USB stack code. +// Used by the ARDUBOY_NO_USB macro. This should not be called +// directly from a sketch. + +void Arduboy2Core::mainNoUSB() +{ + // disable USB + UDCON = _BV(DETACH); + UDIEN = 0; + UDINT = 0; + USBCON = _BV(FRZCLK); + UHWCON = 0; + power_usb_disable(); + + init(); + + // This would normally be done in the USB code that uses the TX and RX LEDs + TX_RX_LED_INIT; + + // Set the DOWN button pin for INPUT_PULLUP + bitSet(DOWN_BUTTON_PORT, DOWN_BUTTON_BIT); + bitClear(DOWN_BUTTON_DDR, DOWN_BUTTON_BIT); + + // Delay to give time for the pin to be pulled high if it was floating + delayShort(10); + + // if the DOWN button is pressed + if (bitRead(DOWN_BUTTON_PORTIN, DOWN_BUTTON_BIT) == 0) { + exitToBootloader(); + } + + // The remainder is a copy of the Arduino main() function with the + // USB code and other unneeded code commented out. + // init() was called above. + // The call to function initVariant() is commented out to fix compiler + // error: "multiple definition of 'main'". + // The return statement is removed since this function is type void. + +// init(); + +// initVariant(); + +//#if defined(USBCON) +// USBDevice.attach(); +//#endif + + setup(); + + for (;;) { + loop(); +// if (serialEventRun) serialEventRun(); + } + +// return 0; +} + diff --git a/src/Arduboy2Core.h b/src/Arduboy2Core.h index 9883d7b..bd02206 100644 --- a/src/Arduboy2Core.h +++ b/src/Arduboy2Core.h @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -252,6 +253,69 @@ #define COLUMN_ADDRESS_END (WIDTH - 1) & 127 // 128 pixels wide #define PAGE_ADDRESS_END ((HEIGHT/8)-1) & 7 // 8 pages high +/** \brief + * Eliminate the USB stack to free up code space. + * + * \note + * **WARNING:** Removing the USB code will make it impossible for sketch + * uploader programs to automatically force a reset into the bootloader! + * This means that a user will manually have to invoke a reset in order to + * upload a new sketch, after one without USB has be been installed. + * Be aware that the timing for the point that a reset must be initiated can + * be tricky, which could lead to some frustration on the user's part. + * + * \details + * \parblock + * This macro will cause the USB code, normally included in the sketch as part + * of the standard Arduino environment, to be eliminated. This will free up a + * fair amount of program space, and some RAM space as well, at the expense of + * disabling all USB functionality within the sketch (except as power input). + * + * The macro should be placed before the `setup()` function definition: + * + * \code{.cpp} + * #include + * + * Arduboy2 arduboy; + * + * // (Other variable declarations, etc.) + * + * // Eliminate the USB stack + * ARDUBOY_NO_USB + * + * void setup() { + * arduboy.begin(); + * // any additional setup code + * } + * \endcode + * + * As stated in the warning above, without the USB code an uploader program + * will be unable to automatically force a reset into the bootloader to upload + * a new sketch. The user will have to manually invoke a reset. In addition to + * eliminating the USB code, this macro will check if the DOWN button is held + * when the sketch first starts and, if so, will call `exitToBootloader()` to + * start the bootloader for uploading. This makes it easier for the user than + * having to press the reset button. + * + * However, to make it even more convenient for a user to invoke the bootloader + * it is highly recommended that a sketch using this macro include a menu or + * prompt that allows the user to press the DOWN button within the sketch, + * which should cause `exitToBootloader()` to be called. + * + * At a minimum, the documentation for the sketch should clearly state that a + * manual reset will be required, and give detailed instructions on what the + * user must do to upload a new sketch. + * \endparblock + * + * \see Arduboy2Core::exitToBootloader() + */ +#define ARDUBOY_NO_USB int main() __attribute__ ((OS_main)); \ +int main() { \ + Arduboy2Core::mainNoUSB(); \ + return 0; \ +} + + /** \brief * Lower level functions generally dealing directly with the hardware. * @@ -762,6 +826,29 @@ class Arduboy2Core */ void static delayShort(uint16_t ms) __attribute__ ((noinline)); + /** \brief + * Exit the sketch and start the bootloader + * + * \details + * The sketch will exit and the bootloader will be started in command mode. + * The effect will be similar to pressing the reset button. + * + * This function is intended to be used to allow uploading a new sketch, + * when the USB code has been removed to gain more code space. + * Ideally, the sketch would present a "New Sketch Upload" menu or prompt + * telling the user to "Press and hold the DOWN button when the procedure + * to upload a new sketch has been initiated". The sketch would then wait + * for the DOWN button to be pressed and then call this function. + * + * \see ARDUBOY_NO_USB + */ + void static exitToBootloader(); + + // Replacement main() that eliminates the USB stack code. + // Used by the ARDUBOY_NO_USB macro. This should not be called + // directly from a sketch. + void static mainNoUSB(); + protected: // internals void static setCPUSpeed8MHz();