Add the ability to eliminate the USB stack code

Macro ARDUBOY_NO_USB will provide a substitute main() which will cause
the compiler to leave out the USB code. The macro also adds a check
for the DOWN button being pressed and, if so, will call the new
exitToBootloader() function.

New function exitToBootloader() will invoke the bootloader in command
mode, similar to pressing reset.
This commit is contained in:
Scott Allen 2018-03-07 16:23:47 -05:00
parent fb77929126
commit c00fee0a78
4 changed files with 175 additions and 6 deletions

View File

@ -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). - *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()*. - *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 ## What's different from Arduboy library V1.1

View File

@ -49,6 +49,7 @@ drawSlowXYBitmap KEYWORD2
drawTriangle KEYWORD2 drawTriangle KEYWORD2
enabled KEYWORD2 enabled KEYWORD2
everyXFrames KEYWORD2 everyXFrames KEYWORD2
exitToBootloader KEYWORD2
fillCircle KEYWORD2 fillCircle KEYWORD2
fillRect KEYWORD2 fillRect KEYWORD2
fillRoundRect KEYWORD2 fillRoundRect KEYWORD2
@ -149,3 +150,5 @@ RED_LED LITERAL1
RGB_OFF LITERAL1 RGB_OFF LITERAL1
RGB_ON LITERAL1 RGB_ON LITERAL1
ARDUBOY_NO_USB LITERAL1

View File

@ -117,10 +117,10 @@ void Arduboy2Core::bootPins()
_BV(B_BUTTON_BIT); _BV(B_BUTTON_BIT);
// Port B INPUT or LOW (none) // Port B INPUT or LOW (none)
// Port B inputs // Port B inputs
DDRB &= ~(_BV(B_BUTTON_BIT)); DDRB &= ~(_BV(B_BUTTON_BIT) | _BV(SPI_MISO_BIT));
// Port B outputs // Port B outputs
DDRB |= _BV(RED_LED_BIT) | _BV(GREEN_LED_BIT) | _BV(BLUE_LED_BIT) | 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 // Port C
// Speaker: Not set here. Controlled by audio class // Speaker: Not set here. Controlled by audio class
@ -158,9 +158,11 @@ void Arduboy2Core::bootPins()
_BV(BLUE_LED_BIT); _BV(BLUE_LED_BIT);
// Port B INPUT or LOW (none) // Port B INPUT or LOW (none)
// Port B inputs // 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 // 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 // Port C INPUT_PULLUP or HIGH
PORTC |= _BV(RIGHT_BUTTON_BIT); PORTC |= _BV(RIGHT_BUTTON_BIT);
@ -269,10 +271,10 @@ void Arduboy2Core::idle()
void Arduboy2Core::bootPowerSaving() void Arduboy2Core::bootPowerSaving()
{ {
// disable Two Wire Interface (I2C) and the ADC // 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); PRR0 = _BV(PRTWI) | _BV(PRADC);
// disable USART1 // disable USART1
PRR1 = _BV(PRUSART1); PRR1 |= _BV(PRUSART1);
// All other bits will be written with 0 so will be enabled
} }
// Shut down the display // Shut down the display
@ -555,3 +557,74 @@ void Arduboy2Core::delayShort(uint16_t ms)
delay((unsigned long) 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;
}

View File

@ -10,6 +10,7 @@
#include <Arduino.h> #include <Arduino.h>
#include <avr/power.h> #include <avr/power.h>
#include <avr/sleep.h> #include <avr/sleep.h>
#include <avr/wdt.h>
#include <limits.h> #include <limits.h>
@ -252,6 +253,69 @@
#define COLUMN_ADDRESS_END (WIDTH - 1) & 127 // 128 pixels wide #define COLUMN_ADDRESS_END (WIDTH - 1) & 127 // 128 pixels wide
#define PAGE_ADDRESS_END ((HEIGHT/8)-1) & 7 // 8 pages high #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.h>
*
* 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 /** \brief
* Lower level functions generally dealing directly with the hardware. * Lower level functions generally dealing directly with the hardware.
* *
@ -762,6 +826,29 @@ class Arduboy2Core
*/ */
void static delayShort(uint16_t ms) __attribute__ ((noinline)); 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: protected:
// internals // internals
void static setCPUSpeed8MHz(); void static setCPUSpeed8MHz();