From f07f2267b2f3d323e2e4b5f30fbced27cd6bdbe9 Mon Sep 17 00:00:00 2001 From: Scott Allen Date: Fri, 24 Feb 2017 15:06:10 -0500 Subject: [PATCH 01/16] Add a link to the repository in README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index f97a18e..0ddc77f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # Arduboy2 Library +The Arduboy2 library is maintained in a git repository hosted on [GitHub](https://github.com/) at: + +https://github.com/MLXXXp/Arduboy2 + The **Arduboy2** library is a fork of the [Arduboy library](https://github.com/Arduboy/Arduboy), which provides a standard *application programming interface* (API) to the display, buttons and other hardware of the Arduino based [Arduboy miniature game system](https://www.arduboy.com/). The name *Arduboy2* doesn't indicate that it's for a new "next generation" of the Arduboy hardware. The name was changed so it can coexist in the Arduino IDE with the current *Arduboy* library, without conflict. This way, existing sketches can continue to use the *Arduboy* library and class, without changes, while new sketches can be written (or old ones modified) to use and take advantage of the capabilities of the *Arduboy2* class and library. From 2851dcf4cc05a294505e59891ba3b0afba58e3d0 Mon Sep 17 00:00:00 2001 From: Scott Allen Date: Mon, 13 Mar 2017 17:50:19 -0400 Subject: [PATCH 02/16] Fix Sprites class data array documentation A separate mask array does not include width and height values at the beginning. --- src/Sprites.h | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/Sprites.h b/src/Sprites.h index 58a738b..b24f4ea 100644 --- a/src/Sprites.h +++ b/src/Sprites.h @@ -31,16 +31,21 @@ * various poses for a running or jumping character. By specifying a different * frame each time the sprite is drawn, it can be animated. * - * Each array begins with values for the width and height of the sprite, in - * pixels. The width can be any value. The height must be a multiple of + * Each image array begins with values for the width and height of the sprite, + * in pixels. The width can be any value. The height must be a multiple of * 8 pixels, but with proper masking, a sprite of any height can be created. * - * After the width and height values, the remainder of the array contains the - * image and/or mask data for each frame. Each byte represents a vertical - * column of 8 pixels with the least significant bit (bit 0) at the top. - * The bytes are drawn as 8 pixel high rows from left to right, top to bottom. - * When the end of a row is reached, as specified by the width value, the next - * byte in the array will be the start of the next row. + * For a separate mask array, as is used with `drawExternalMask()`, the width + * and height are not included but must contain data of the same dimensions + * as the corresponding image array. + * + * Following the width and height values for an image array, or the from the + * beginning of a separate mask array, the array contains the image and/or + * mask data for each frame. Each byte represents a vertical column of 8 pixels + * with the least significant bit (bit 0) at the top. The bytes are drawn as + * 8 pixel high rows from left to right, top to bottom. When the end of a row + * is reached, as specified by the width value, the next byte in the array will + * be the start of the next row. * * Data for each frame after the first one immediately follows the previous * frame. Frame numbers start at 0. From f5d3a168f86603851463a463af6753c3bc6be56e Mon Sep 17 00:00:00 2001 From: Josh Goebel Date: Thu, 19 Jan 2017 03:04:11 -0500 Subject: [PATCH 03/16] clean up frame management code (-6 bytes) - saves 6 bytes compiling Mystic Balloon on Arduino 1.8.1 --- src/Arduboy2.cpp | 40 ++++++++++++++++++++++++++-------------- src/Arduboy2.h | 2 +- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/src/Arduboy2.cpp b/src/Arduboy2.cpp index e3666bb..6c4cd4f 100644 --- a/src/Arduboy2.cpp +++ b/src/Arduboy2.cpp @@ -20,9 +20,9 @@ Arduboy2Base::Arduboy2Base() previousButtonState = 0; // frame management setFrameRate(60); - frameCount = 0; + frameCount = -1; nextFrameStart = 0; - post_render = false; + justRendered = false; // init not necessary, will be reset after first use // lastFrameStart // lastFrameDurationMs @@ -145,28 +145,40 @@ bool Arduboy2Base::everyXFrames(uint8_t frames) bool Arduboy2Base::nextFrame() { unsigned long now = millis(); + bool tooSoonForNextFrame = now < nextFrameStart; - // post render - if (post_render) { + if (justRendered) { lastFrameDurationMs = now - lastFrameStart; - frameCount++; - post_render = false; + justRendered = false; + return false; } + else if (tooSoonForNextFrame) { + // if we have MORE than 1ms to spare (hence our comparison with 2), + // lets sleep for power savings. We don't compare against 1 to avoid + // potential rounding errors - say we're actually 0.5 ms away, but a 1 + // is returned if we go to sleep we might sleep a full 1ms and then + // we'd be running the frame slighly late. So the last 1ms we stay + // awake for perfect timing. - // if it's not time for the next frame yet - if (now < nextFrameStart) { - // if we have more than 1ms to spare, lets sleep - // we should be woken up by timer0 every 1ms, so this should be ok - if ((uint8_t)(nextFrameStart - now) > 1) + // This is likely trading power savings for absolute timing precision + // and the power savings might be the better goal. At 60 FPS trusting + // chance here might actually achieve a "truer" 60 FPS than the 16ms + // frame duration we get due to integer math. + + // We should be woken up by timer0 every 1ms, so it's ok to sleep. + if ((uint8_t)(nextFrameStart - now) >= 2) idle(); + return false; } // pre-render - nextFrameStart = now + eachFrameMillis; + justRendered = true; lastFrameStart = now; - post_render = true; - return post_render; + nextFrameStart = now + eachFrameMillis; + frameCount++; + + return true; } bool Arduboy2Base::nextFrameDEV() { diff --git a/src/Arduboy2.h b/src/Arduboy2.h index 88f6cfd..ad03538 100644 --- a/src/Arduboy2.h +++ b/src/Arduboy2.h @@ -1006,7 +1006,7 @@ class Arduboy2Base : public Arduboy2Core uint8_t eachFrameMillis; unsigned long lastFrameStart; unsigned long nextFrameStart; - bool post_render; + bool justRendered; uint8_t lastFrameDurationMs; }; From ca6393098d9befbdced795732f73f8271f303cba Mon Sep 17 00:00:00 2001 From: Josh Goebel Date: Mon, 20 Mar 2017 20:01:15 -0400 Subject: [PATCH 04/16] optimize writeUnitName (-130 bytes) --- src/Arduboy2.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/Arduboy2.cpp b/src/Arduboy2.cpp index e3666bb..d64fe8b 100644 --- a/src/Arduboy2.cpp +++ b/src/Arduboy2.cpp @@ -962,17 +962,14 @@ void Arduboy2Base::writeUnitName(char* name) for (uint8_t src = 0; src < ARDUBOY_UNIT_NAME_LEN; src++) { - if (name[src] != 0x00 && !done) - { - EEPROM.update(dest, name[src]); - } - else - { + if (name[src] == 0x00) { done = true; - EEPROM.update(dest, 0x00); } + // write character or 0 pad if finished + EEPROM.update(dest, done ? 0x00 : name[src]); dest++; } + } void Arduboy2Base::swap(int16_t& a, int16_t& b) From 8f462387c55bd8b5f5de0f2f4b612721ffae01e6 Mon Sep 17 00:00:00 2001 From: Josh Goebel Date: Mon, 20 Mar 2017 20:28:51 -0400 Subject: [PATCH 05/16] optimize readUnitName (-6 bytes) --- src/Arduboy2.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Arduboy2.cpp b/src/Arduboy2.cpp index d64fe8b..21609c3 100644 --- a/src/Arduboy2.cpp +++ b/src/Arduboy2.cpp @@ -943,12 +943,12 @@ uint8_t Arduboy2Base::readUnitName(char* name) for (dest = 0; dest < ARDUBOY_UNIT_NAME_LEN; dest++) { - if ((val = EEPROM.read(src)) == 0x00 || (byte)val == 0xFF) - { - break; - } + val = EEPROM.read(src); name[dest] = val; src++; + if (val == 0x00 || (byte)val == 0xFF) { + break; + } } name[dest] = 0x00; From 1b95f1ce8fffd1a4e56fb822c585bfb9750e4af3 Mon Sep 17 00:00:00 2001 From: Josh Goebel Date: Tue, 21 Mar 2017 14:15:20 -0400 Subject: [PATCH 06/16] Remove extra 50ms delay added by de1725 Removes the 50ms debounce delay that was added when we allow the boot logo to be skipped by reducing the boot logo delay by 50ms. --- src/Arduboy2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Arduboy2.cpp b/src/Arduboy2.cpp index 21609c3..3ae0e74 100644 --- a/src/Arduboy2.cpp +++ b/src/Arduboy2.cpp @@ -121,7 +121,7 @@ void Arduboy2Base::bootLogo() } } - delay(750); + delay(700); digitalWrite(BLUE_LED, RGB_OFF); bootLogoExtra(); From d93795c42b8b3204949d37e7654885b18f0a3f9b Mon Sep 17 00:00:00 2001 From: Josh Goebel Date: Tue, 21 Mar 2017 14:57:41 -0400 Subject: [PATCH 07/16] optimize drawChar (-126 bytes) --- src/Arduboy2.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/Arduboy2.cpp b/src/Arduboy2.cpp index e3666bb..019cd7b 100644 --- a/src/Arduboy2.cpp +++ b/src/Arduboy2.cpp @@ -1046,7 +1046,9 @@ size_t Arduboy2::write(uint8_t c) void Arduboy2::drawChar (int16_t x, int16_t y, unsigned char c, uint8_t color, uint8_t bg, uint8_t size) { + uint8_t line; bool draw_background = bg != color; + uint8_t *bitmap = font + c*5; if ((x >= WIDTH) || // Clip right (y >= HEIGHT) || // Clip bottom @@ -1057,17 +1059,12 @@ void Arduboy2::drawChar return; } - for (int8_t i=0; i<6; i++ ) + for (uint8_t i=0; i<6; i++ ) { - uint8_t line; - if (i == 5) - { + line = pgm_read_byte(bitmap++); + if (i == 5) { line = 0x0; } - else - { - line = pgm_read_byte(font+(c*5)+i); - } for (int8_t j = 0; j<8; j++) { From b560ff9f60e86139bb5953b067928b49f66356a1 Mon Sep 17 00:00:00 2001 From: Josh Goebel Date: Mon, 20 Mar 2017 19:16:29 -0400 Subject: [PATCH 08/16] fix internal drawBitmap call to use correct types --- src/Sprites.cpp | 2 +- src/Sprites.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Sprites.cpp b/src/Sprites.cpp index d4b833e..183ded0 100644 --- a/src/Sprites.cpp +++ b/src/Sprites.cpp @@ -69,7 +69,7 @@ void Sprites::draw(int16_t x, int16_t y, void Sprites::drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, const uint8_t *mask, - int8_t w, int8_t h, uint8_t draw_mode) + uint8_t w, uint8_t h, uint8_t draw_mode) { // no need to draw at all of we're offscreen if (x + w <= 0 || x > WIDTH - 1 || y + h <= 0 || y > HEIGHT - 1) diff --git a/src/Sprites.h b/src/Sprites.h index 58a738b..f652ad1 100644 --- a/src/Sprites.h +++ b/src/Sprites.h @@ -224,7 +224,7 @@ class Sprites // (Not officially part of the API) void drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, const uint8_t *mask, - int8_t w, int8_t h, uint8_t draw_mode); + uint8_t w, uint8_t h, uint8_t draw_mode); }; #endif From 4ff6973a4b8e17422210f7c00c0b49507fe63244 Mon Sep 17 00:00:00 2001 From: Scott Allen Date: Wed, 22 Mar 2017 18:16:10 -0400 Subject: [PATCH 09/16] Change version to 3.1.1 --- LICENSE.txt | 2 +- library.json | 2 +- library.properties | 2 +- src/Arduboy2.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/LICENSE.txt b/LICENSE.txt index 1f21723..3141ce7 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -8,7 +8,7 @@ Software License Agreements Licensed under the BSD 3-clause license: Arduboy2 library: -Copyright (c) 2017, Scott Allen +Copyright (c) 2016-2017, Scott Allen All rights reserved. The Arduboy2 library was forked from the Arduboy library: diff --git a/library.json b/library.json index c83254d..b711d63 100644 --- a/library.json +++ b/library.json @@ -7,7 +7,7 @@ "type": "git", "url": "https://github.com/MLXXXp/Arduboy2.git" }, - "version": "3.1.0", + "version": "3.1.1", "exclude": "extras", "frameworks": "arduino", "platforms": "atmelavr" diff --git a/library.properties b/library.properties index b284bb5..f224705 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=Arduboy2 -version=3.1.0 +version=3.1.1 author=Chris J. Martinez, Kevin Bates, Josh Goebel, Scott Allen, Ross O. Shoger maintainer=Scott Allen sentence=An alternative library for use with the Arduboy game system. diff --git a/src/Arduboy2.h b/src/Arduboy2.h index 88f6cfd..d2e90fc 100644 --- a/src/Arduboy2.h +++ b/src/Arduboy2.h @@ -32,7 +32,7 @@ * #endif * \endcode */ -#define ARDUBOY_LIB_VER 30100 +#define ARDUBOY_LIB_VER 30101 // EEPROM settings #define ARDUBOY_UNIT_NAME_LEN 6 /**< The maximum length of the unit name string. */ From a85c6da431ba7ba6cc96d05009afaf017ec064ee Mon Sep 17 00:00:00 2001 From: Scott Allen Date: Wed, 15 Mar 2017 14:10:19 -0400 Subject: [PATCH 10/16] Direct control of ports and pins This eliminates calls to pinMode(), digitalWrite(), functions to initialize hardware, etc. New 2 parameter version of digitalWriteRGB() added, which sets an individual RGB LED on or off digitally. New function SPItransfer() added to replace Arduino SPI.transfer(). --- keywords.txt | 1 + src/Arduboy2.cpp | 20 ++-- src/Arduboy2Audio.cpp | 18 ++-- src/Arduboy2Core.cpp | 229 +++++++++++++++++++++++++++--------------- src/Arduboy2Core.h | 192 +++++++++++++++++++++++++++-------- 5 files changed, 319 insertions(+), 141 deletions(-) diff --git a/keywords.txt b/keywords.txt index 28442b8..d51ca64 100644 --- a/keywords.txt +++ b/keywords.txt @@ -75,6 +75,7 @@ setTextColor KEYWORD2 setTextBackground KEYWORD2 setTextSize KEYWORD2 setTextWrap KEYWORD2 +SPItransfer KEYWORD2 systemButtons KEYWORD2 toggle KEYWORD2 width KEYWORD2 diff --git a/src/Arduboy2.cpp b/src/Arduboy2.cpp index e3666bb..69b6043 100644 --- a/src/Arduboy2.cpp +++ b/src/Arduboy2.cpp @@ -71,23 +71,23 @@ void Arduboy2Base::flashlight() void Arduboy2Base::systemButtons() { while (pressed(B_BUTTON)) { - digitalWrite(BLUE_LED, RGB_ON); // turn on blue LED + digitalWriteRGB(BLUE_LED, RGB_ON); // turn on blue LED sysCtrlSound(UP_BUTTON + B_BUTTON, GREEN_LED, 0xff); sysCtrlSound(DOWN_BUTTON + B_BUTTON, RED_LED, 0); delay(200); } - digitalWrite(BLUE_LED, RGB_OFF); // turn off blue LED + digitalWriteRGB(BLUE_LED, RGB_OFF); // turn off blue LED } void Arduboy2Base::sysCtrlSound(uint8_t buttons, uint8_t led, uint8_t eeVal) { if (pressed(buttons)) { - digitalWrite(BLUE_LED, RGB_OFF); // turn off blue LED + digitalWriteRGB(BLUE_LED, RGB_OFF); // turn off blue LED delay(200); - digitalWrite(led, RGB_ON); // turn on "acknowledge" LED + digitalWriteRGB(led, RGB_ON); // turn on "acknowledge" LED EEPROM.update(EEPROM_AUDIO_ON_OFF, eeVal); delay(500); - digitalWrite(led, RGB_OFF); // turn off "acknowledge" LED + digitalWriteRGB(led, RGB_OFF); // turn off "acknowledge" LED while (pressed(buttons)) {} // Wait for button release } @@ -95,7 +95,7 @@ void Arduboy2Base::sysCtrlSound(uint8_t buttons, uint8_t led, uint8_t eeVal) { void Arduboy2Base::bootLogo() { - digitalWrite(RED_LED, RGB_ON); + digitalWriteRGB(RED_LED, RGB_ON); for (int8_t y = -18; y <= 24; y++) { if (pressed(RIGHT_BUTTON)) { @@ -104,10 +104,12 @@ void Arduboy2Base::bootLogo() } if (y == -4) { - digitalWriteRGB(RGB_OFF, RGB_ON, RGB_OFF); // green LED on + digitalWriteRGB(RED_LED, RGB_OFF); // red LED off + digitalWriteRGB(GREEN_LED, RGB_ON); // green LED on } else if (y == 24) { - digitalWriteRGB(RGB_OFF, RGB_OFF, RGB_ON); // blue LED on + digitalWriteRGB(GREEN_LED, RGB_OFF); // green LED off + digitalWriteRGB(BLUE_LED, RGB_ON); // blue LED on } clear(); @@ -122,7 +124,7 @@ void Arduboy2Base::bootLogo() } delay(750); - digitalWrite(BLUE_LED, RGB_OFF); + digitalWriteRGB(BLUE_LED, RGB_OFF); bootLogoExtra(); } diff --git a/src/Arduboy2Audio.cpp b/src/Arduboy2Audio.cpp index 901f627..701f806 100644 --- a/src/Arduboy2Audio.cpp +++ b/src/Arduboy2Audio.cpp @@ -11,12 +11,12 @@ bool Arduboy2Audio::audio_enabled = false; void Arduboy2Audio::on() { - // fire up audio pins + // fire up audio pins by seting them as outputs #ifdef ARDUBOY_10 - pinMode(PIN_SPEAKER_1, OUTPUT); - pinMode(PIN_SPEAKER_2, OUTPUT); + bitSet(SPEAKER_1_DDR, SPEAKER_1_BIT); + bitSet(SPEAKER_2_DDR, SPEAKER_2_BIT); #else - pinMode(PIN_SPEAKER_1, OUTPUT); + bitSet(SPEAKER_1_DDR, SPEAKER_1_BIT); #endif audio_enabled = true; } @@ -24,12 +24,12 @@ void Arduboy2Audio::on() void Arduboy2Audio::off() { audio_enabled = false; - // shut off audio pins + // shut off audio pins by setting them as inputs #ifdef ARDUBOY_10 - pinMode(PIN_SPEAKER_1, INPUT); - pinMode(PIN_SPEAKER_2, INPUT); + bitClear(SPEAKER_1_DDR, SPEAKER_1_BIT); + bitClear(SPEAKER_2_DDR, SPEAKER_2_BIT); #else - pinMode(PIN_SPEAKER_1, INPUT); + bitClear(SPEAKER_1_DDR, SPEAKER_1_BIT); #endif } @@ -50,6 +50,8 @@ void Arduboy2Audio::begin() { if (EEPROM.read(EEPROM_AUDIO_ON_OFF)) on(); + else + off(); } bool Arduboy2Audio::enabled() diff --git a/src/Arduboy2Core.cpp b/src/Arduboy2Core.cpp index 0c37a80..4e333f9 100644 --- a/src/Arduboy2Core.cpp +++ b/src/Arduboy2Core.cpp @@ -6,40 +6,6 @@ #include "Arduboy2Core.h" -// need to redeclare these here since we declare them static in .h -volatile uint8_t *Arduboy2Core::csport, *Arduboy2Core::dcport; -uint8_t Arduboy2Core::cspinmask, Arduboy2Core::dcpinmask; - -const uint8_t PROGMEM pinBootProgram[] = { - // buttons - PIN_LEFT_BUTTON, INPUT_PULLUP, - PIN_RIGHT_BUTTON, INPUT_PULLUP, - PIN_UP_BUTTON, INPUT_PULLUP, - PIN_DOWN_BUTTON, INPUT_PULLUP, - PIN_A_BUTTON, INPUT_PULLUP, - PIN_B_BUTTON, INPUT_PULLUP, - - // RGB LED (or single blue LED on the DevKit) -#ifdef ARDUBOY_10 - RED_LED, INPUT_PULLUP, // set INPUT_PULLUP to make the pin high when - RED_LED, OUTPUT, // set to OUTPUT - GREEN_LED, INPUT_PULLUP, - GREEN_LED, OUTPUT, -#endif - BLUE_LED, INPUT_PULLUP, - BLUE_LED, OUTPUT, - - // audio is specifically not included here as those pins are handled - // separately by `audio.begin()`, `audio.on()` and `audio.off()` in order - // to respect the EEPROM audio settings - - // OLED SPI - DC, OUTPUT, - CS, OUTPUT, - RST, OUTPUT, - 0 -}; - const uint8_t PROGMEM lcdBootProgram[] = { // boot defaults are commented out but left here in case they // might prove useful for reference @@ -114,7 +80,6 @@ void Arduboy2Core::boot() setCPUSpeed8MHz(); #endif - SPI.begin(); bootPins(); bootOLED(); @@ -143,58 +108,135 @@ void Arduboy2Core::setCPUSpeed8MHz() } #endif +// Pins are set to the proper modes and levels for the specific hardware. +// This routine must be modified if any pins are moved to a different port void Arduboy2Core::bootPins() { - uint8_t pin, mode; - const uint8_t *i = pinBootProgram; +#ifdef ARDUBOY_10 - while(true) { - pin = pgm_read_byte(i++); - mode = pgm_read_byte(i++); - if (pin==0) break; - pinMode(pin, mode); - } + // Port B INPUT_PULLUP or HIGH + PORTB |= _BV(RED_LED_BIT) | _BV(GREEN_LED_BIT) | _BV(BLUE_LED_BIT) | + _BV(B_BUTTON_BIT); + // Port B INPUT or LOW (none) + // Port B inputs + DDRB &= ~(_BV(B_BUTTON_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); - digitalWrite(RST, HIGH); - delay(1); // VDD (3.3V) goes high at start, lets just chill for a ms - digitalWrite(RST, LOW); // bring reset low - delay(10); // wait 10ms - digitalWrite(RST, HIGH); // bring out of reset + // Port C + // Speaker: Not set here. Controlled by audio class + + // Port D INPUT_PULLUP or HIGH + PORTD |= _BV(CS_BIT); + // Port D INPUT or LOW + PORTD &= ~(_BV(RST_BIT)); + // Port D inputs (none) + // Port D outputs + DDRD |= _BV(RST_BIT) | _BV(CS_BIT) | _BV(DC_BIT); + + // Port E INPUT_PULLUP or HIGH + PORTE |= _BV(A_BUTTON_BIT); + // Port E INPUT or LOW (none) + // Port E inputs + DDRE &= ~(_BV(A_BUTTON_BIT)); + // Port E outputs (none) + + // Port F INPUT_PULLUP or HIGH + PORTF |= _BV(LEFT_BUTTON_BIT) | _BV(RIGHT_BUTTON_BIT) | + _BV(UP_BUTTON_BIT) | _BV(DOWN_BUTTON_BIT); + // Port F INPUT or LOW (none) + // Port F inputs + DDRF &= ~(_BV(LEFT_BUTTON_BIT) | _BV(RIGHT_BUTTON_BIT) | + _BV(UP_BUTTON_BIT) | _BV(DOWN_BUTTON_BIT)); + // Port F outputs (none) + +#elif defined(AB_DEVKIT) + + // Port B INPUT_PULLUP or HIGH + PORTB |= _BV(LEFT_BUTTON_BIT) | _BV(UP_BUTTON_BIT) | _BV(DOWN_BUTTON_BIT) | + _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)); + // Port B outputs + DDRB |= _BV(BLUE_LED_BIT) | _BV(SPI_MOSI_BIT) | _BV(SPI_SCK_BIT); + + // Port C INPUT_PULLUP or HIGH + PORTE |= _BV(RIGHT_BUTTON_BIT); + // Port C INPUT or LOW (none) + // Port C inputs + DDRE &= ~(_BV(RIGHT_BUTTON_BIT)); + // Port C outputs (none) + + // Port D INPUT_PULLUP or HIGH + PORTD |= _BV(CS_BIT); + // Port D INPUT or LOW + PORTD &= ~(_BV(RST_BIT)); + // Port D inputs (none) + // Port D outputs + DDRD |= _BV(RST_BIT) | _BV(CS_BIT) | _BV(DC_BIT); + + // Port E (none) + + // Port F INPUT_PULLUP or HIGH + PORTF |= _BV(A_BUTTON_BIT) | _BV(B_BUTTON_BIT); + // Port F INPUT or LOW (none) + // Port F inputs + DDRF &= ~(_BV(A_BUTTON_BIT) | _BV(B_BUTTON_BIT)); + // Port F outputs (none) + // Speaker: Not set here. Controlled by audio class + +#endif } void Arduboy2Core::bootOLED() { - // setup the ports we need to talk to the OLED - csport = portOutputRegister(digitalPinToPort(CS)); - cspinmask = digitalPinToBitMask(CS); - dcport = portOutputRegister(digitalPinToPort(DC)); - dcpinmask = digitalPinToBitMask(DC); + // init SPI + // master, mode 0, MSB first, CPU clock / 2 (8MHz) + SPCR = _BV(SPE) | _BV(MSTR); + SPSR = _BV(SPI2X); - SPI.setClockDivider(SPI_CLOCK_DIV2); + // reset the display + delay(2); // reset pin should be low here. let it stay low a while + bitSet(RST_PORT, RST_BIT); // set high to come out of reset + delay(10); // wait a while + + // select the display (permanently, since nothing else is using SPI) + bitClear(CS_PORT, CS_BIT); - LCDCommandMode(); // run our customized boot-up command sequence against the // OLED to initialize it properly for Arduboy + LCDCommandMode(); for (uint8_t i = 0; i < sizeof(lcdBootProgram); i++) { - SPI.transfer(pgm_read_byte(lcdBootProgram + i)); + SPItransfer(pgm_read_byte(lcdBootProgram + i)); } LCDDataMode(); } void Arduboy2Core::LCDDataMode() { - *dcport |= dcpinmask; - *csport &= ~cspinmask; + bitSet(DC_PORT, DC_BIT); } void Arduboy2Core::LCDCommandMode() { - *csport |= cspinmask; - *dcport &= ~dcpinmask; - *csport &= ~cspinmask; + bitClear(DC_PORT, DC_BIT); } - +// Write to the SPI bus (MOSI pin) +void Arduboy2Core::SPItransfer(uint8_t data) +{ + SPDR = data; + /* + * The following NOP introduces a small delay that can prevent the wait + * loop form iterating when running at the maximum speed. This gives + * about 10% more speed, even if it seems counter-intuitive. At lower + * speeds it is unnoticed. + */ + asm volatile("nop"); + while (!(SPSR & _BV(SPIF))) { } // wait +} void Arduboy2Core::safeMode() { @@ -215,15 +257,11 @@ void Arduboy2Core::idle() void Arduboy2Core::bootPowerSaving() { - power_adc_disable(); - power_usart0_disable(); - power_twi_disable(); - // timer 0 is for millis() - // timers 1 and 3 are for music and sounds - power_timer2_disable(); - power_usart1_disable(); - // we need USB, for now (to allow triggered reboots to reprogram) - // power_usb_disable() + // disable Two Wire Interface (I2C) and the ADC + PRR0 = _BV(PRTWI) | _BV(PRADC); + // disable USART1 + PRR1 = _BV(PRUSART1); + // All other bits will be written with 0 so will be enabled } uint8_t Arduboy2Core::width() { return WIDTH; } @@ -235,14 +273,14 @@ uint8_t Arduboy2Core::height() { return HEIGHT; } void Arduboy2Core::paint8Pixels(uint8_t pixels) { - SPI.transfer(pixels); + SPItransfer(pixels); } void Arduboy2Core::paintScreen(const uint8_t *image) { for (int i = 0; i < (HEIGHT*WIDTH)/8; i++) { - SPI.transfer(pgm_read_byte(image + i)); + SPItransfer(pgm_read_byte(image + i)); } } @@ -288,13 +326,13 @@ void Arduboy2Core::paintScreen(uint8_t image[], bool clear) void Arduboy2Core::blank() { for (int i = 0; i < (HEIGHT*WIDTH)/8; i++) - SPI.transfer(0x00); + SPItransfer(0x00); } void Arduboy2Core::sendLCDCommand(uint8_t command) { LCDCommandMode(); - SPI.transfer(command); + SPItransfer(command); LCDDataMode(); } @@ -334,19 +372,48 @@ void Arduboy2Core::setRGBled(uint8_t red, uint8_t green, uint8_t blue) analogWrite(GREEN_LED, 255 - green); analogWrite(BLUE_LED, 255 - blue); #elif defined(AB_DEVKIT) - // only blue on devkit - digitalWrite(BLUE_LED, ~blue); + // only blue on DevKit, which is not PWM capable + (void)red; // parameter unused + (void)green; // parameter unused + bitWrite(BLUE_LED_PORT, BLUE_LED_BIT, blue ? RGB_ON : RGB_OFF); #endif } void Arduboy2Core::digitalWriteRGB(uint8_t red, uint8_t green, uint8_t blue) { #ifdef ARDUBOY_10 - digitalWrite(RED_LED, red); - digitalWrite(GREEN_LED, green); - digitalWrite(BLUE_LED, blue); + bitWrite(RED_LED_PORT, RED_LED_BIT, red); + bitWrite(GREEN_LED_PORT, GREEN_LED_BIT, green); + bitWrite(BLUE_LED_PORT, BLUE_LED_BIT, blue); #elif defined(AB_DEVKIT) - digitalWrite(BLUE_LED, blue); + // only blue on DevKit + (void)red; // parameter unused + (void)green; // parameter unused + bitWrite(BLUE_LED_PORT, BLUE_LED_BIT, blue); +#endif +} + +void Arduboy2Core::digitalWriteRGB(uint8_t color, uint8_t val) +{ +#ifdef ARDUBOY_10 + if (color == RED_LED) + { + bitWrite(RED_LED_PORT, RED_LED_BIT, val); + } + else if (color == GREEN_LED) + { + bitWrite(GREEN_LED_PORT, GREEN_LED_BIT, val); + } + else if (color == BLUE_LED) + { + bitWrite(BLUE_LED_PORT, BLUE_LED_BIT, val); + } +#elif defined(AB_DEVKIT) + // only blue on DevKit + if (color == BLUE_LED) + { + bitWrite(BLUE_LED_PORT, BLUE_LED_BIT, val); + } #endif } diff --git a/src/Arduboy2Core.h b/src/Arduboy2Core.h index d179358..011d3c4 100644 --- a/src/Arduboy2Core.h +++ b/src/Arduboy2Core.h @@ -9,7 +9,6 @@ #include #include -#include #include #include @@ -41,27 +40,42 @@ #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() */ +// ----- Arduboy pins ----- #ifdef ARDUBOY_10 -#define CS 12 -#define DC 4 -#define RST 6 +#define PIN_CS 12 // Display CS Arduino pin number +#define CS_PORT PORTD // Display CS port +#define CS_BIT PORTD6 // Display CS physical bit number + +#define PIN_DC 4 // Display D/C Arduino pin number +#define DC_PORT PORTD // Display D/C port +#define DC_BIT PORTD4 // Display D/C physical bit number + +#define PIN_RST 6 // Display reset Arduino pin number +#define RST_PORT PORTD // Display reset port +#define RST_BIT PORTD7 // Display reset physical bit number + +#define SPI_MOSI_PORT PORTB +#define SPI_MOSI_BIT PORTB2 + +#define SPI_SCK_PORT PORTB +#define SPI_SCK_BIT PORTB1 #define RED_LED 10 /**< The pin number for the red color in the RGB LED. */ #define GREEN_LED 11 /**< The pin number for the greem color in the RGB LED. */ #define BLUE_LED 9 /**< The pin number for the blue color in the RGB LED. */ -#define TX_LED 30 /**< The pin number for the transmit indicator LED. */ -#define RX_LED 17 /**< The pin number for the receive indicator LED. */ -// pin values for buttons, probably shouldn't use these -#define PIN_LEFT_BUTTON A2 -#define PIN_RIGHT_BUTTON A1 -#define PIN_UP_BUTTON A0 -#define PIN_DOWN_BUTTON A3 -#define PIN_A_BUTTON 7 -#define PIN_B_BUTTON 8 +#define RED_LED_PORT PORTB +#define RED_LED_BIT PORTB6 + +#define GREEN_LED_PORT PORTB +#define GREEN_LED_BIT PORTB7 + +#define BLUE_LED_PORT PORTB +#define BLUE_LED_BIT PORTB5 // bit values for button states +// these are determined by the buttonsState() function #define LEFT_BUTTON _BV(5) /**< The Left button value for functions requiring a bitmask */ #define RIGHT_BUTTON _BV(6) /**< The Right button value for functions requiring a bitmask */ #define UP_BUTTON _BV(7) /**< The Up button value for functions requiring a bitmask */ @@ -69,37 +83,73 @@ #define A_BUTTON _BV(3) /**< The A button value for functions requiring a bitmask */ #define B_BUTTON _BV(2) /**< The B button value for functions requiring a bitmask */ +#define PIN_LEFT_BUTTON A2 +#define LEFT_BUTTON_PORT PORTF +#define LEFT_BUTTON_BIT PORTF5 + +#define PIN_RIGHT_BUTTON A1 +#define RIGHT_BUTTON_PORT PORTF +#define RIGHT_BUTTON_BIT PORTF6 + +#define PIN_UP_BUTTON A0 +#define UP_BUTTON_PORT PORTF +#define UP_BUTTON_BIT PORTF7 + +#define PIN_DOWN_BUTTON A3 +#define DOWN_BUTTON_PORT PORTF +#define DOWN_BUTTON_BIT PORTF4 + +#define PIN_A_BUTTON 7 +#define A_BUTTON_PORT PORTE +#define A_BUTTON_BIT PORTE6 + +#define PIN_B_BUTTON 8 +#define B_BUTTON_PORT PORTB +#define B_BUTTON_BIT PORTB4 + #define PIN_SPEAKER_1 5 /**< The pin number of the first lead of the speaker */ #define PIN_SPEAKER_2 13 /**< The pin number of the second lead of the speaker */ -#define PIN_SPEAKER_1_PORT &PORTC -#define PIN_SPEAKER_2_PORT &PORTC +#define SPEAKER_1_PORT PORTC +#define SPEAKER_1_DDR DDRC +#define SPEAKER_1_BIT PORTC6 -#define PIN_SPEAKER_1_BITMASK _BV(6) -#define PIN_SPEAKER_2_BITMASK _BV(7) +#define SPEAKER_2_PORT PORTC +#define SPEAKER_2_DDR DDRC +#define SPEAKER_2_BIT PORTC7 +// ----------------------- +// ----- DevKit pins ----- #elif defined(AB_DEVKIT) -#define CS 6 -#define DC 4 -#define RST 12 +#define PIN_CS 6 // Display CS Arduino pin number +#define CS_PORT PORTD // Display CS port +#define CS_BIT PORTD7 // Display CS physical bit number + +#define PIN_DC 4 // Display D/C Arduino pin number +#define DC_PORT PORTD // Display D/C port +#define DC_BIT PORTD4 // Display D/C physical bit number + +#define PIN_RST 12 // Display reset Arduino pin number +#define RST_PORT PORTD // Display reset port +#define RST_BIT PORTD6 // Display reset physical bit number + +#define SPI_MOSI_PORT PORTB +#define SPI_MOSI_BIT PORTB2 + +#define SPI_SCK_PORT PORTB +#define SPI_SCK_BIT PORTB1 // map all LEDs to the single TX LED on DEVKIT #define RED_LED 17 #define GREEN_LED 17 #define BLUE_LED 17 -#define TX_LED 17 -#define RX_LED 17 -// pin values for buttons, probably shouldn't use these -#define PIN_LEFT_BUTTON 9 -#define PIN_RIGHT_BUTTON 5 -#define PIN_UP_BUTTON 8 -#define PIN_DOWN_BUTTON 10 -#define PIN_A_BUTTON A0 -#define PIN_B_BUTTON A1 +#define BLUE_LED_PORT PORTB +#define BLUE_LED_BIT PORTB0 // bit values for button states +// these are determined by the buttonsState() function #define LEFT_BUTTON _BV(5) #define RIGHT_BUTTON _BV(2) #define UP_BUTTON _BV(4) @@ -107,15 +157,42 @@ #define A_BUTTON _BV(1) #define B_BUTTON _BV(0) +// pin values for buttons, probably shouldn't use these +#define PIN_LEFT_BUTTON 9 +#define LEFT_BUTTON_PORT PORTB +#define LEFT_BUTTON_BIT PORTB5 + +#define PIN_RIGHT_BUTTON 5 +#define RIGHT_BUTTON_PORT PORTC +#define RIGHT_BUTTON_BIT PORTC6 + +#define PIN_UP_BUTTON 8 +#define UP_BUTTON_PORT PORTB +#define UP_BUTTON_BIT PORTB4 + +#define PIN_DOWN_BUTTON 10 +#define DOWN_BUTTON_PORT PORTB +#define DOWN_BUTTON_BIT PORTB6 + +#define PIN_A_BUTTON A0 +#define A_BUTTON_PORT PORTF +#define A_BUTTON_BIT PORTF7 + +#define PIN_B_BUTTON A1 +#define B_BUTTON_PORT PORTF +#define B_BUTTON_BIT PORTF6 + #define PIN_SPEAKER_1 A2 -#define PIN_SPEAKER_1_PORT &PORTF -#define PIN_SPEAKER_1_BITMASK _BV(5) +#define SPEAKER_1_PORT PORTF +#define SPEAKER_1_DDR DDRF +#define SPEAKER_1_BIT PORTF5 // SPEAKER_2 is purposely not defined for DEVKIT as it could potentially // be dangerous and fry your hardware (because of the devkit wiring). // // Reference: https://github.com/Arduboy/Arduboy/issues/108 #endif +// -------------------- // OLED hardware (SSD1306) @@ -183,6 +260,8 @@ class Arduboy2Core * This is a low level function that is not intended for general use in a * sketch. It has been made public and documented for use by derived * classes. + * + * \see LCDCommandMode() SPItransfer() */ void static LCDDataMode(); @@ -206,10 +285,25 @@ class Arduboy2Core * sketch. It has been made public and documented for use by derived * classes. * - * \see sendLCDCommand() + * \see LCDDataMode() sendLCDCommand() SPItransfer() */ void static LCDCommandMode(); + /** \brief + * Transfer a byte to the display. + * + * \param data The byte to be sent to the display. + * + * \details + * Transfer one byte to the display over the SPI port and wait for the + * transfer to complete. The byte will either be interpreted as a command + * or as data to be placed on the screen, depending on the command/data + * mode. + * + * \see LCDDataMode() LCDCommandMode() sendLCDCommand() + */ + void static inline SPItransfer(uint8_t data); + /** \brief * Get the width of the display in pixels. * @@ -233,7 +327,7 @@ class Arduboy2Core uint8_t static height(); /** \brief - * get current state of all buttons as a bitmask. + * Get the current state of all buttons as a bitmask. * * \return A bitmask of the state of all the buttons. * @@ -461,9 +555,9 @@ class Arduboy2Core * * \details * The RGB LED is actually individual red, green and blue LEDs placed - * very close together in a single package. This function will set each - * LED either on or off, to set the RGB LED to 7 different colors at their - * highest brightness or turn it off. + * very close together in a single package. This 3 parameter version of the + * function will set each LED either on or off, to set the RGB LED to + * 7 different colors at their highest brightness or turn it off. * * The colors are as follows: * @@ -486,10 +580,28 @@ class Arduboy2Core * light the red LED. If the green LED is turned on, none of the LEDs * will light. * - * \see setRGBled() + * \see digitalWriteRGB(uint8_t, uint8_t) setRGBled() */ void static digitalWriteRGB(uint8_t red, uint8_t green, uint8_t blue); + /** \brief + * Set one of the RGB LEDs digitally, to either fully on or fully off. + * + * \param color The name of the LED to set. The value given should be one + * of RED_LED, GREEN_LED or BLUE_LED. + * + * \param val Indicates whether to turn the specified LED on or off. + * The value given should be RGB_ON or RGB_OFF. + * + * \details + * This 2 parameter version of the function will set a single LED within + * the RGB LED either fully on or fully off. See the description of the + * 3 parameter version of this function for more details on the RGB LED. + * + * \see digitalWriteRGB(uint8_t, uint8_t, uint8_t) setRGBled() + */ + void static digitalWriteRGB(uint8_t color, uint8_t val); + /** \brief * Initialize the Arduboy's hardware. * @@ -525,12 +637,6 @@ class Arduboy2Core void static inline bootOLED() __attribute__((always_inline)); void static inline bootPins() __attribute__((always_inline)); void static inline bootPowerSaving() __attribute__((always_inline)); - - - private: - volatile static uint8_t *csport, *dcport; - uint8_t static cspinmask, dcpinmask; - }; #endif From a8077a756e61534e7256b8a8f32981014b50e6ef Mon Sep 17 00:00:00 2001 From: Scott Allen Date: Fri, 24 Mar 2017 15:53:35 -0400 Subject: [PATCH 11/16] 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)); From 4b74b2c366f0f0d0c40c00e32685cbc60ddefabb Mon Sep 17 00:00:00 2001 From: Scott Allen Date: Mon, 27 Mar 2017 15:58:17 -0400 Subject: [PATCH 12/16] Add bootLogoText() function Displays the boot logo using text instead of a bitmap, as an option to reduce code size. --- README.md | 7 ++++++- keywords.txt | 1 + src/Arduboy2.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++++++ src/Arduboy2.h | 26 +++++++++++++++++++++++--- 4 files changed, 76 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0ddc77f..1f60579 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ For developers who wish to quickly begin testing, or impatient users who want to ### "Flashlight" mode -If the *UP* button is pressed and held when the Arduboy is powered on, it enters *flashlight* mode. This turns the RGB LED fully on, and all the pixels of the screen are lit, resulting in a bright white light suitable as a small flashlight. (For an incorrect RGB LED, only the screen will light). To exit *flashlight* mode, press the *DOWN* button to continue with the sketch. +If the *UP* button is pressed and held when the Arduboy is powered on, it enters *flashlight* mode. This turns the RGB LED fully on, and all the pixels of the screen are lit, resulting in a bright white light suitable as a small flashlight. (For an incorrect RGB LED, only the screen will light). To exit *flashlight* mode the Arduboy must be restarted. *Flashlight* mode is also sometimes useful to allow uploading of new sketches, in case the sketch currently loaded uses a large amount of RAM which creates a bootloader problem. @@ -209,6 +209,11 @@ For example: Let's say a sketch has its own code to enable, disable and save the This saves whatever code *blank()*, *systemButtons()* and *bootLogo()* would use. +There are a few functions provided that are roughly equivalent to the standard functions used by *begin()* but which use less code space. + +- *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()*. + ---------- ## What's different from Arduboy library V1.1 diff --git a/keywords.txt b/keywords.txt index 978b64f..0f843bd 100644 --- a/keywords.txt +++ b/keywords.txt @@ -19,6 +19,7 @@ begin KEYWORD2 blank KEYWORD2 boot KEYWORD2 bootLogo KEYWORD2 +bootLogoText KEYWORD2 buttonsState KEYWORD2 clear KEYWORD2 collide KEYWORD2 diff --git a/src/Arduboy2.cpp b/src/Arduboy2.cpp index d783299..d5fad59 100644 --- a/src/Arduboy2.cpp +++ b/src/Arduboy2.cpp @@ -97,6 +97,8 @@ void Arduboy2Base::sysCtrlSound(uint8_t buttons, uint8_t led, uint8_t eeVal) } } +// bootLogoText() should be kept in sync with bootLogo() +// if changes are made to one, equivalent changes should be made to the other void Arduboy2Base::bootLogo() { digitalWriteRGB(RED_LED, RGB_ON); @@ -1003,6 +1005,50 @@ Arduboy2::Arduboy2() textWrap = 0; } +// bootLogoText() should be kept in sync with bootLogo() +// if changes are made to one, equivalent changes should be made to the other +void Arduboy2::bootLogoText() +{ + digitalWriteRGB(RED_LED, RGB_ON); + + textSize = 2; + + for (int8_t y = -18; y <= 24; y++) { + if (pressed(RIGHT_BUTTON)) { + digitalWriteRGB(RGB_OFF, RGB_OFF, RGB_OFF); // all LEDs off + textSize = 1; + return; + } + + if (y == -4) { + digitalWriteRGB(RED_LED, RGB_OFF); // red LED off + digitalWriteRGB(GREEN_LED, RGB_ON); // green LED on + } + else if (y == 24) { + digitalWriteRGB(GREEN_LED, RGB_OFF); // green LED off + digitalWriteRGB(BLUE_LED, RGB_ON); // blue LED on + } + + clear(); + cursor_x = 23; + cursor_y = y; + print("ARDUBOY"); + display(); + delay(27); + // longer delay post boot, we put it inside the loop to + // save the flash calling clear/delay again outside the loop + if (y==-16) { + delay(250); + } + } + + delay(750); + digitalWriteRGB(BLUE_LED, RGB_OFF); + textSize = 1; + + bootLogoExtra(); +} + void Arduboy2::bootLogoExtra() { uint8_t c = EEPROM.read(EEPROM_UNIT_NAME); diff --git a/src/Arduboy2.h b/src/Arduboy2.h index 1226a32..c53fb4d 100644 --- a/src/Arduboy2.h +++ b/src/Arduboy2.h @@ -259,7 +259,7 @@ class Arduboy2Base : public Arduboy2Core * which derived classes can implement to add additional information to the * logo screen. The `Arduboy2` class uses this to display the unit name. * - * \see begin() boot() Arduboy2::bootLogoExtra() + * \see begin() boot() Arduboy2::bootLogoExtra() Arduboy2::bootLogoText() */ void bootLogo(); @@ -1083,11 +1083,31 @@ class Arduboy2 : public Print, public Arduboy2Base * \see Arduboy2::write() */ + /** \brief + * Display the boot logo sequence using printed text instead of a bitmap. + * + * \details + * This function can be called by a sketch after `boot()` as an alternative + * to `bootLogo()`. + * + * The Arduboy logo scrolls down from the top of the screen to the center + * while the RGB LEDs light in sequence. + * + * This function is the same as `bootLogo()` except the logo is printed as + * text instead of being rendered as a bitmap. It can be used to save some + * code space in a case where the sketch is using the Print class functions + * to display text. However, the logo will not look as good when printed as + * text as it does with the bitmap used by `bootLogo()`. + * + * \see bootLogo() boot() Arduboy2::bootLogoExtra() + */ + void bootLogoText(); + /** \brief * Show the unit name at the bottom of the boot logo screen. * * \details - * This function is called by the `bootLogo()` function. + * This function is called by `bootLogo()` and `bootlogoText()`. * * If a unit name has been saved in system EEPROM, it will be displayed at * the bottom of the screen. This function pauses for a short time to allow @@ -1097,7 +1117,7 @@ class Arduboy2 : public Print, public Arduboy2Base * This function would not normally be called directly from within a sketch * itself. * - * \see readUnitName() writeUnitName() bootLogo() begin() + * \see readUnitName() writeUnitName() bootLogo() bootLogoText() begin() */ virtual void bootLogoExtra(); From 4f58fa710a1d244ae6756fb6f24738873ca51f5e Mon Sep 17 00:00:00 2001 From: Scott Allen Date: Wed, 29 Mar 2017 17:36:13 -0400 Subject: [PATCH 13/16] Make showing the unit name with the logo optional - Added a flag in system EEPROM to indicate if the unit name should be displayed at the end of the boot logo sequence. Function bootLogoExtra() displays the unit name only if the flag is set. - Added functions writeShowUnitNameFlag() and readShowUnitNameFlag() to write and read the "Show Unit Name" flag in EEPROM. - Enhanced the SetNameAndID example sketch to allow setting the "Show Unit Name" flag in EEPROM. --- examples/SetNameAndID/SetNameAndID.ino | 127 ++++++++++++++++++++++--- keywords.txt | 2 + src/Arduboy2.cpp | 27 +++++- src/Arduboy2.h | 43 ++++++++- 4 files changed, 183 insertions(+), 16 deletions(-) diff --git a/examples/SetNameAndID/SetNameAndID.ino b/examples/SetNameAndID/SetNameAndID.ino index 8b4d131..53cf81d 100644 --- a/examples/SetNameAndID/SetNameAndID.ino +++ b/examples/SetNameAndID/SetNameAndID.ino @@ -14,7 +14,7 @@ ---------------------------------------------------------------------------- */ -// Version 1.0 +// Version 2.0 /* ------------------------------------------------------------------------------ @@ -61,14 +61,20 @@ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // All the constant stings const char StrName[] PROGMEM = "NAME"; const char StrID[] PROGMEM = "ID"; +const char StrYes[] PROGMEM = "YES"; +const char StrNo[] PROGMEM = "NO"; const char StrSaveQ[] PROGMEM = "SAVE?"; +const char StrSaved[] PROGMEM = "SAVED"; +const char StrShowNameQ1[] PROGMEM = "Show Unit Name"; +const char StrShowNameQ2[] PROGMEM = "on logo screen?"; const char StrBtnChangeName[] PROGMEM = "UP:change Unit Name"; const char StrBtnChangeID[] PROGMEM = "DOWN:change Unit ID"; +const char StrBtnShowName[] PROGMEM = "LEFT:set \"show name\""; const char StrBtnMenu[] PROGMEM = "A:menu"; const char StrBtnSave[] PROGMEM = "B:save"; const char StrBtnYes[] PROGMEM = "A:yes"; const char StrBtnNo[] PROGMEM = "B:no"; -const char StrBtnLogo[] PROGMEM = "LEFT:show boot logo"; +const char StrBtnTestLogo[] PROGMEM = "DOWN:test boot logo"; const char StrHex[] PROGMEM = "hex"; const char StrDecimal[] PROGMEM = "decimal"; @@ -91,8 +97,8 @@ const char StrDecimal[] PROGMEM = "decimal"; #define MENU_ID_DECIMAL_X (centerStrLen(5) + (WIDTH / 4)) #define MENU_ID_Y (MENU_HEADINGS_Y + CHAR_HEIGHT + 1) -#define MENU_BTN_LOGO_X 0 -#define MENU_BTN_LOGO_Y 56 +#define MENU_BTN_SHOW_NAME_X 0 +#define MENU_BTN_SHOW_NAME_Y 56 #define NAME_TITLE_X centerStr_P(StrName) @@ -151,13 +157,32 @@ const char StrDecimal[] PROGMEM = "decimal"; #define ID_SAVE_X (ID_SAVE_Q_X + ((strlen_P(StrSaveQ) * CHAR_WIDTH) + CHAR_WIDTH)) #define ID_SAVE_Y (ID_LARGE_Y + 1) +#define SHOW_NAME_BTN_MENU_X 0 +#define SHOW_NAME_BTN_MENU_Y 0 +#define SHOW_NAME_BTN_SAVE_X rightStr_P(StrBtnSave) +#define SHOW_NAME_BTN_SAVE_Y SHOW_NAME_BTN_MENU_Y +#define SHOW_NAME_Q_1_X centerStr_P(StrShowNameQ1) +#define SHOW_NAME_Q_1_Y 12 +#define SHOW_NAME_Q_2_X centerStr_P(StrShowNameQ2) +#define SHOW_NAME_Q_2_Y (SHOW_NAME_Q_1_Y + 8) +#define SHOW_NAME_YES_X ((WIDTH / 2) - ((strlen_P(StrYes) + 1) * CHAR_WIDTH * 2)) +#define SHOW_NAME_YES_Y 34 +#define SHOW_NAME_NO_X ((WIDTH / 2) + (CHAR_WIDTH * 2)) +#define SHOW_NAME_NO_Y SHOW_NAME_YES_Y +#define SHOW_NAME_TEST_X 0 +#define SHOW_NAME_TEST_Y 56 +#define SHOW_NAME_SAVED_X centerStr2_P(StrSaved) +#define SHOW_NAME_SAVED_Y ((HEIGHT / 2) - CHAR_HEIGHT) + // Calculation of the number of frames to wait before button auto-repeat starts #define DELAY_FRAMES (REPEAT_DELAY / (1000 / FRAME_RATE)) // The Arduino "magic" has trouble creating prototypes for functions called // by pointers, so they're declared here manually -void stateMain(), stateName(), stateID(), stateSaveName(), stateSaveID(); -void screenMain(), screenName(), screenID(), screenSaveName(), screenSaveID(); +void stateMain(), stateName(), stateID(), stateShowName(); +void stateSaveName(), stateSaveID(); +void screenMain(), screenName(), screenID(), screenShowName(); +void screenSaveName(), screenSaveID(); Arduboy2 arduboy; @@ -167,11 +192,14 @@ byte nameIndex; uint16_t unitID; byte idIndex; +boolean showNameFlag; + // Assign numbers for each state/screen enum State : byte { sMain, sName, sID, + sShowName, sSaveName, sSaveID, sMAX = sSaveID @@ -184,6 +212,7 @@ void (*stateFunc[sMAX + 1])() = { stateMain, stateName, stateID, + stateShowName, stateSaveName, stateSaveID }; @@ -193,6 +222,7 @@ void (*screenFunc[sMAX + 1])() = { screenMain, screenName, screenID, + screenShowName, screenSaveName, screenSaveID }; @@ -246,9 +276,7 @@ void stateMain() { setState(sID); } else if (arduboy.justPressed(LEFT_BUTTON)) { - arduboy.bootLogo(); - delay(1000); - setState(sMain); + setState(sShowName); } } @@ -322,6 +350,29 @@ void stateID() { } } +// STATE: Set "Show Unit Name" flag +void stateShowName() { + if (arduboy.justPressed(RIGHT_BUTTON)) { + showNameToggle(); + } + else if (arduboy.justPressed(LEFT_BUTTON)) { + showNameToggle(); + } + else if (arduboy.justPressed(A_BUTTON)) { + setState(sMain); + } + else if (arduboy.justPressed(B_BUTTON)) { + saveShowName(); + setState(sShowName); + } + else if (arduboy.justPressed(DOWN_BUTTON)) { + showNameFlag = arduboy.readShowUnitNameFlag(); + arduboy.bootLogo(); + delay(1000); + setState(sShowName); + } +} + // STATE: Prompt to save the unit name void stateSaveName() { if (arduboy.justPressed(A_BUTTON)) { @@ -367,7 +418,7 @@ void screenMain() { printIDHex(MENU_ID_HEX_X, MENU_ID_Y); printIDDecimal(MENU_ID_DECIMAL_X, MENU_ID_Y); - printStr_P(MENU_BTN_LOGO_X, MENU_BTN_LOGO_Y, StrBtnLogo); + printStr_P(MENU_BTN_SHOW_NAME_X, MENU_BTN_SHOW_NAME_Y, StrBtnShowName); } // DISPLAY: Change unit name @@ -389,6 +440,20 @@ void screenID() { printIDCursors(); } +// DISPLAY: Set "Show Unit Name" flag +void screenShowName() { + printStr_P(SHOW_NAME_BTN_MENU_X, SHOW_NAME_BTN_MENU_Y, StrBtnMenu); + printStr_P(SHOW_NAME_BTN_SAVE_X, SHOW_NAME_BTN_SAVE_Y, StrBtnSave); + printStr_P(SHOW_NAME_Q_1_X, SHOW_NAME_Q_1_Y, StrShowNameQ1); + printStr_P(SHOW_NAME_Q_2_X, SHOW_NAME_Q_2_Y, StrShowNameQ2); + arduboy.setTextSize(2); + printStr_P(SHOW_NAME_YES_X, SHOW_NAME_YES_Y, StrYes); + printStr_P(SHOW_NAME_NO_X, SHOW_NAME_NO_Y, StrNo); + arduboy.setTextSize(1); + printShowNameCursor(); + printStr_P(SHOW_NAME_TEST_X, SHOW_NAME_TEST_Y, StrBtnTestLogo); +} + // DISPLAY: Prompt to save the unit name void screenSaveName() { printNameScreenCommon(); @@ -407,6 +472,21 @@ void screenSaveID() { printIDLarge(ID_SAVE_X, ID_SAVE_Y); } +// Save the "Show Unit Name" flag and overlay the "SAVED" message on the screen +void saveShowName() { + arduboy.writeShowUnitNameFlag(showNameFlag); + arduboy.fillRect(SHOW_NAME_SAVED_X - 4, SHOW_NAME_SAVED_Y - 4, + strlen_P(StrSaved) * CHAR_WIDTH * 2 + 6, CHAR_HEIGHT * 2 + 6); + arduboy.setTextColor(BLACK); + arduboy.setTextBackground(WHITE); + arduboy.setTextSize(2); + printStr_P(SHOW_NAME_SAVED_X, SHOW_NAME_SAVED_Y, StrSaved); + arduboy.setTextSize(1); + arduboy.setTextColor(WHITE); + arduboy.setTextBackground(BLACK); + arduboy.display(); + delay(1000); +} // --------------------- Printing Functions ------------------------------ @@ -458,6 +538,18 @@ void printIDCursors() { ID_BINARY_Y + CHAR_HEIGHT + 1, CHAR_WIDTH * 4 - 1); } +// Print the current "Show Unit Name" cursor +void printShowNameCursor() { + if (showNameFlag) { + arduboy.fillRect(SHOW_NAME_YES_X, SHOW_NAME_YES_Y + (CHAR_HEIGHT * 2), + (strlen_P(StrYes) * CHAR_WIDTH - 1) * 2, 2); + } + else { + arduboy.fillRect(SHOW_NAME_NO_X, SHOW_NAME_NO_Y + (CHAR_HEIGHT * 2), + (strlen_P(StrNo) * CHAR_WIDTH - 1) * 2, 2); + } +} + // Print the unit name in normal size including an extent underline // at the given location void printName(int x, int y) { @@ -621,11 +713,12 @@ void printBinaryNybble(int x, int y, byte val) { // ---------------- Control and Utility Functions ------------------------ -// Get the current unit name and ID from EEPROM +// Get the current unit name and ID, and the "Show Unit Name" flag, from EEPROM void readEEPROM() { memset(unitName, 0, sizeof(unitName)); arduboy.readUnitName(unitName); unitID = arduboy.readUnitID(); + showNameFlag = arduboy.readShowUnitNameFlag(); } // Increment the name character at the cursor position @@ -701,6 +794,12 @@ void idCursorLeft() { drawScreen(); } +// Toggle the "Show Unit Name" selection +void showNameToggle() { + showNameFlag = !showNameFlag; + drawScreen(); +} + // Start the button auto-repeat delay void startButtonDelay() { delayCount = DELAY_FRAMES; @@ -723,6 +822,12 @@ int centerStr_P(const char* str) { return (WIDTH / 2) - (strlen_P(str) * CHAR_WIDTH / 2); } +// Calculate the X coordinate to center a size 2 string located in +// program memory +int centerStr2_P(const char* str) { + return (WIDTH / 2) - (strlen_P(str) * CHAR_WIDTH); +} + // Calculate the X coordinate to right justify a string in program memory int rightStr_P(const char* str) { return WIDTH - (strlen_P(str) * CHAR_WIDTH) + 1; diff --git a/keywords.txt b/keywords.txt index 0f843bd..620b25e 100644 --- a/keywords.txt +++ b/keywords.txt @@ -66,6 +66,7 @@ paint8Pixels KEYWORD2 paintScreen KEYWORD2 pollButtons KEYWORD2 pressed KEYWORD2 +readShowUnitNameFlag KEYWORD2 readUnitID KEYWORD2 readUnitName KEYWORD2 safeMode KEYWORD2 @@ -81,6 +82,7 @@ SPItransfer KEYWORD2 systemButtons KEYWORD2 toggle KEYWORD2 width KEYWORD2 +writeShowUnitNameFlag KEYWORD2 writeUnitID KEYWORD2 writeUnitName KEYWORD2 diff --git a/src/Arduboy2.cpp b/src/Arduboy2.cpp index d5fad59..71e0088 100644 --- a/src/Arduboy2.cpp +++ b/src/Arduboy2.cpp @@ -983,6 +983,19 @@ void Arduboy2Base::writeUnitName(char* name) } } +bool Arduboy2Base::readShowUnitNameFlag() +{ + return (EEPROM.read(EEPROM_SYS_FLAGS) & SYS_FLAG_UNAME_MASK); +} + +void Arduboy2Base::writeShowUnitNameFlag(bool val) +{ + uint8_t flags = EEPROM.read(EEPROM_SYS_FLAGS); + + bitWrite(flags, SYS_FLAG_UNAME, val); + EEPROM.update(EEPROM_SYS_FLAGS, flags); +} + void Arduboy2Base::swap(int16_t& a, int16_t& b) { int16_t temp = a; @@ -1051,7 +1064,14 @@ void Arduboy2::bootLogoText() void Arduboy2::bootLogoExtra() { - uint8_t c = EEPROM.read(EEPROM_UNIT_NAME); + uint8_t c; + + if (!readShowUnitNameFlag()) + { + return; + } + + c = EEPROM.read(EEPROM_UNIT_NAME); if (c != 0xFF && c != 0x00) { @@ -1063,10 +1083,11 @@ void Arduboy2::bootLogoExtra() { write(c); c = EEPROM.read(++i); - } while (i < EEPROM_UNIT_NAME + ARDUBOY_UNIT_NAME_LEN); + } + while (i < EEPROM_UNIT_NAME + ARDUBOY_UNIT_NAME_LEN); display(); - delay(1500); + delay(1000); } } diff --git a/src/Arduboy2.h b/src/Arduboy2.h index c53fb4d..8bf8c79 100644 --- a/src/Arduboy2.h +++ b/src/Arduboy2.h @@ -38,13 +38,17 @@ #define ARDUBOY_UNIT_NAME_LEN 6 /**< The maximum length of the unit name string. */ #define EEPROM_VERSION 0 -#define EEPROM_BRIGHTNESS 1 +#define EEPROM_SYS_FLAGS 1 #define EEPROM_AUDIO_ON_OFF 2 #define EEPROM_UNIT_ID 8 // A uint16_t binary unit ID #define EEPROM_UNIT_NAME 10 // An up to 6 character unit name. Cannot contain // 0x00 or 0xFF. Lengths less than 6 are padded // with 0x00 +// EEPROM_SYS_FLAGS values +#define SYS_FLAG_UNAME 0 // Display the unit name on the logo screen +#define SYS_FLAG_UNAME_MASK _BV(SYS_FLAG_UNAME) + /** \brief * Start of EEPROM storage space for sketches. * @@ -959,6 +963,38 @@ class Arduboy2Base : public Arduboy2Core */ void writeUnitName(char* name); + /** \brief + * Read the "Show Unit Name" flag in system EEPROM. + * + * \return `true` if the flag is set to indicate that the unit name should + * be displayed. `false` if the flag is set to not display the unit name. + * + * \details + * The "Show Unit Name" flag is used to determine whether the system + * unit name is to be displayed at the end of the boot logo sequence. + * This function returns the value of this flag. + * + * \see writeShowUnitNameFlag() writeUnitName() readUnitName() + * Arduboy2::bootLogoExtra() + */ + bool readShowUnitNameFlag(); + + /** \brief + * Write the "Show Unit Name" flag in system EEPROM. + * + * \param val If `true` the flag is set to indicate that the unit name should + * be displayed. If `false` the flag is set to not display the unit name. + * + * \details + * The "Show Unit Name" flag is used to determine whether the system + * unit name is to be displayed at the end of the boot logo sequence. + * This function allows the flag to be saved with the desired value. + * + * \see readShowUnitNameFlag() writeUnitName() readUnitName() + * Arduboy2::bootLogoExtra() + */ + void writeShowUnitNameFlag(bool val); + /** \brief * A counter which is incremented once per frame. * @@ -1113,11 +1149,14 @@ class Arduboy2 : public Print, public Arduboy2Base * the bottom of the screen. This function pauses for a short time to allow * the name to be seen. * + * The name is not displayed if the "Show Unit Name" flag is not set. + * * \note * This function would not normally be called directly from within a sketch * itself. * - * \see readUnitName() writeUnitName() bootLogo() bootLogoText() begin() + * \see readUnitName() writeUnitName() bootLogo() bootLogoText() + * writeShowUnitNameFlag() begin() */ virtual void bootLogoExtra(); From c962257488bc4c8e7f12c66cfcd9e91990151b39 Mon Sep 17 00:00:00 2001 From: Scott Allen Date: Fri, 31 Mar 2017 17:51:00 -0400 Subject: [PATCH 14/16] Simplify initRandomSeed() to reduce code size Use only an ADC reading from an unconnected pin shifted left 16 bits then added to micros(). --- src/Arduboy2.cpp | 23 +++++++---------------- src/Arduboy2.h | 8 -------- src/Arduboy2Core.cpp | 14 ++++++++++---- src/Arduboy2Core.h | 12 ++++++++++++ 4 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/Arduboy2.cpp b/src/Arduboy2.cpp index 71e0088..460cb36 100644 --- a/src/Arduboy2.cpp +++ b/src/Arduboy2.cpp @@ -197,25 +197,16 @@ int Arduboy2Base::cpuLoad() void Arduboy2Base::initRandomSeed() { power_adc_enable(); // ADC on - randomSeed(~rawADC(ADC_TEMP) * ~rawADC(ADC_VOLTAGE) * ~micros() + micros()); + + // do an ADC read from an unconnected input pin + ADCSRA |= _BV(ADSC); // start conversion (ADMUX has been pre-set in boot()) + while (bit_is_set(ADCSRA, ADSC)) { } // wait for conversion complete + + randomSeed(((unsigned long)ADC << 16) + micros()); + power_adc_disable(); // ADC off } -uint16_t Arduboy2Base::rawADC(uint8_t adc_bits) -{ - ADMUX = adc_bits; - // we also need MUX5 for temperature check - if (adc_bits == ADC_TEMP) { - ADCSRB = _BV(MUX5); - } - - delay(2); // Wait for ADMUX setting to settle - ADCSRA |= _BV(ADSC); // Start conversion - while (bit_is_set(ADCSRA,ADSC)); // measuring - - return ADC; -} - /* Graphics */ void Arduboy2Base::clear() diff --git a/src/Arduboy2.h b/src/Arduboy2.h index 8bf8c79..b4f908b 100644 --- a/src/Arduboy2.h +++ b/src/Arduboy2.h @@ -82,11 +82,6 @@ #define CLEAR_BUFFER true /**< Value to be passed to `display()` to clear the screen buffer. */ -// compare Vcc to 1.1 bandgap -#define ADC_VOLTAGE (_BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1)) -// compare temperature to 2.5 internal reference and _BV(MUX5) -#define ADC_TEMP (_BV(REFS0) | _BV(REFS1) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0)) - /** \brief * A rectangle object for collision functions. @@ -712,9 +707,6 @@ class Arduboy2Base : public Arduboy2Core */ int cpuLoad(); - // Useful for getting raw approximate voltage values. - uint16_t rawADC(uint8_t adc_bits); - /** \brief * Test if the specified buttons are pressed. * diff --git a/src/Arduboy2Core.cpp b/src/Arduboy2Core.cpp index 9955aad..471f067 100644 --- a/src/Arduboy2Core.cpp +++ b/src/Arduboy2Core.cpp @@ -80,6 +80,9 @@ void Arduboy2Core::boot() setCPUSpeed8MHz(); #endif + // Select the ADC input here so a delay isn't required in initRandomSeed() + ADMUX = RAND_SEED_IN_ADMUX; + bootPins(); bootOLED(); bootPowerSaving(); @@ -139,10 +142,12 @@ void Arduboy2Core::bootPins() // Port F INPUT_PULLUP or HIGH PORTF |= _BV(LEFT_BUTTON_BIT) | _BV(RIGHT_BUTTON_BIT) | _BV(UP_BUTTON_BIT) | _BV(DOWN_BUTTON_BIT); - // Port F INPUT or LOW (none) + // Port F INPUT or LOW + PORTF &= ~(_BV(RAND_SEED_IN_BIT)); // Port F inputs DDRF &= ~(_BV(LEFT_BUTTON_BIT) | _BV(RIGHT_BUTTON_BIT) | - _BV(UP_BUTTON_BIT) | _BV(DOWN_BUTTON_BIT)); + _BV(UP_BUTTON_BIT) | _BV(DOWN_BUTTON_BIT) | + _BV(RAND_SEED_IN_BIT)); // Port F outputs (none) #elif defined(AB_DEVKIT) @@ -175,9 +180,10 @@ void Arduboy2Core::bootPins() // Port F INPUT_PULLUP or HIGH PORTF |= _BV(A_BUTTON_BIT) | _BV(B_BUTTON_BIT); - // Port F INPUT or LOW (none) + // Port F INPUT or LOW + PORTF &= ~(_BV(RAND_SEED_IN_BIT)); // Port F inputs - DDRF &= ~(_BV(A_BUTTON_BIT) | _BV(B_BUTTON_BIT)); + DDRF &= ~(_BV(A_BUTTON_BIT) | _BV(B_BUTTON_BIT) | _BV(RAND_SEED_IN_BIT)); // Port F outputs (none) // Speaker: Not set here. Controlled by audio class diff --git a/src/Arduboy2Core.h b/src/Arduboy2Core.h index 17647ad..a7ac697 100644 --- a/src/Arduboy2Core.h +++ b/src/Arduboy2Core.h @@ -112,6 +112,12 @@ #define SPEAKER_2_PORT PORTC #define SPEAKER_2_DDR DDRC #define SPEAKER_2_BIT PORTC7 + +#define RAND_SEED_IN A4 // Open analog input used for noise by initRandomSeed() +#define RAND_SEED_IN_PORTF +#define RAND_SEED_IN_BIT PORTF1 +// Value for ADMUX to read the random seed pin: 2.56V reference, ADC1 +#define RAND_SEED_IN_ADMUX (_BV(REFS0) | _BV(REFS1) | _BV(MUX0)) // ----------------------- // ----- DevKit pins ----- @@ -186,6 +192,12 @@ // // Reference: https://github.com/Arduboy/Arduboy/issues/108 +#define RAND_SEED_IN A4 // Open analog input used for noise by initRandomSeed() +#define RAND_SEED_IN_PORTF +#define RAND_SEED_IN_BIT PORTF1 +// Value for ADMUX to read the random seed pin: 2.56V reference, ADC1 +#define RAND_SEED_IN_ADMUX (_BV(REFS0) | _BV(REFS1) | _BV(MUX0)) + #endif // -------------------- From cca56f91f4658cc97cd8d2fb401906a5d68c42e7 Mon Sep 17 00:00:00 2001 From: Scott Allen Date: Fri, 7 Apr 2017 15:02:11 -0400 Subject: [PATCH 15/16] Adjust location of some braces for consistency This is just a source formatting change. --- src/Arduboy2.cpp | 14 +++++++++----- src/Arduboy2Core.cpp | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/Arduboy2.cpp b/src/Arduboy2.cpp index 460cb36..bd6d182 100644 --- a/src/Arduboy2.cpp +++ b/src/Arduboy2.cpp @@ -93,7 +93,7 @@ void Arduboy2Base::sysCtrlSound(uint8_t buttons, uint8_t led, uint8_t eeVal) delay(500); digitalWriteRGB(led, RGB_OFF); // turn off "acknowledge" LED - while (pressed(buttons)) {} // Wait for button release + while (pressed(buttons)) { } // Wait for button release } } @@ -177,7 +177,8 @@ bool Arduboy2Base::nextFrame() return post_render; } -bool Arduboy2Base::nextFrameDEV() { +bool Arduboy2Base::nextFrameDEV() +{ bool ret = nextFrame(); if (ret) { @@ -1155,11 +1156,13 @@ void Arduboy2::setCursor(int16_t x, int16_t y) cursor_y = y; } -int16_t Arduboy2::getCursorX() { +int16_t Arduboy2::getCursorX() +{ return cursor_x; } -int16_t Arduboy2::getCursorY() { +int16_t Arduboy2::getCursorY() +{ return cursor_y; } @@ -1184,7 +1187,8 @@ void Arduboy2::setTextWrap(bool w) textWrap = w; } -void Arduboy2::clear() { +void Arduboy2::clear() +{ Arduboy2Base::clear(); cursor_x = cursor_y = 0; } diff --git a/src/Arduboy2Core.cpp b/src/Arduboy2Core.cpp index 471f067..ada4611 100644 --- a/src/Arduboy2Core.cpp +++ b/src/Arduboy2Core.cpp @@ -71,7 +71,7 @@ const uint8_t PROGMEM lcdBootProgram[] = { }; -Arduboy2Core::Arduboy2Core() {} +Arduboy2Core::Arduboy2Core() { } void Arduboy2Core::boot() { From 9efa78bf6817573cd29e6b281478bec07386b56e Mon Sep 17 00:00:00 2001 From: Scott Allen Date: Wed, 12 Apr 2017 17:36:28 -0400 Subject: [PATCH 16/16] Add alternative boot logo functions New functions bootLogoCompressed(), bootLogoSpritesSelfMasked() and bootLogoSpritesOverwrite() can be used in place of bootLogo() to reduce code size in cases where their drawing functions are shared with the same functions used by the sketch. New function bootLogoShell() added to provide common code for the above functions. Also, the Sprites class functions, and functions used for drawing the logos, were made static. --- README.md | 1 + keywords.txt | 4 +++ src/Arduboy2.cpp | 50 ++++++++++++++++++++++++--- src/Arduboy2.h | 90 +++++++++++++++++++++++++++++++++++++++++++----- src/Sprites.h | 26 +++++++------- src/ab_logo.c | 83 ++++++++++++++++++++++++++++++++++---------- 6 files changed, 210 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 1f60579..e231157 100644 --- a/README.md +++ b/README.md @@ -211,6 +211,7 @@ This saves whatever code *blank()*, *systemButtons()* and *bootLogo()* would use There are a few functions provided that are roughly equivalent to the standard functions used by *begin()* but which use less code space. +- *bootLogoCompressed()*, *bootLogoSpritesSelfMasked()* and *bootLogoSpritesOverwrite()* will do the same as *bootLogo()* but will use *drawCompressed()*, or *Sprites* class *drawSelfMasked()* or *drawOverwrite()*, functions respectively, instead of *drawBitmask()*, to render the logo. If the sketch uses one of these functions, then using the boot logo function that also uses it may reduce code size. It's best to try each of them to see which one produces the smallest size. - *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()*. diff --git a/keywords.txt b/keywords.txt index 620b25e..a9d50bd 100644 --- a/keywords.txt +++ b/keywords.txt @@ -19,6 +19,10 @@ begin KEYWORD2 blank KEYWORD2 boot KEYWORD2 bootLogo KEYWORD2 +bootLogoCompressed KEYWORD2 +bootLogoShell KEYWORD2 +bootLogoSpritesOverwrite KEYWORD2 +bootLogoSpritesSelfMasked KEYWORD2 bootLogoText KEYWORD2 buttonsState KEYWORD2 clear KEYWORD2 diff --git a/src/Arduboy2.cpp b/src/Arduboy2.cpp index bd6d182..a6d72da 100644 --- a/src/Arduboy2.cpp +++ b/src/Arduboy2.cpp @@ -97,13 +97,53 @@ void Arduboy2Base::sysCtrlSound(uint8_t buttons, uint8_t led, uint8_t eeVal) } } -// bootLogoText() should be kept in sync with bootLogo() -// if changes are made to one, equivalent changes should be made to the other void Arduboy2Base::bootLogo() +{ + bootLogoShell(drawLogoBitmap); +} + +void Arduboy2Base::drawLogoBitmap(int16_t y) +{ + drawBitmap(20, y, arduboy_logo, 88, 16); +} + +void Arduboy2Base::bootLogoCompressed() +{ + bootLogoShell(drawLogoCompressed); +} + +void Arduboy2Base::drawLogoCompressed(int16_t y) +{ + drawCompressed(20, y, arduboy_logo_compressed); +} + +void Arduboy2Base::bootLogoSpritesSelfMasked() +{ + bootLogoShell(drawLogoSpritesSelfMasked); +} + +void Arduboy2Base::drawLogoSpritesSelfMasked(int16_t y) +{ + Sprites::drawSelfMasked(20, y, arduboy_logo_sprite, 0); +} + +void Arduboy2Base::bootLogoSpritesOverwrite() +{ + bootLogoShell(drawLogoSpritesOverwrite); +} + +void Arduboy2Base::drawLogoSpritesOverwrite(int16_t y) +{ + Sprites::drawOverwrite(20, y, arduboy_logo_sprite, 0); +} + +// bootLogoText() should be kept in sync with bootLogoShell() +// if changes are made to one, equivalent changes should be made to the other +void Arduboy2Base::bootLogoShell(void (*drawLogo)(int16_t)) { digitalWriteRGB(RED_LED, RGB_ON); - for (int8_t y = -18; y <= 24; y++) { + for (int16_t y = -18; y <= 24; y++) { if (pressed(RIGHT_BUTTON)) { digitalWriteRGB(RGB_OFF, RGB_OFF, RGB_OFF); // all LEDs off return; @@ -119,7 +159,7 @@ void Arduboy2Base::bootLogo() } clear(); - drawBitmap(20, y, arduboy_logo, 88, 16, WHITE); + (*drawLogo)(y); // call the function that actually draws the logo display(); delay(27); // longer delay post boot, we put it inside the loop to @@ -1010,7 +1050,7 @@ Arduboy2::Arduboy2() textWrap = 0; } -// bootLogoText() should be kept in sync with bootLogo() +// bootLogoText() should be kept in sync with bootLogoShell() // if changes are made to one, equivalent changes should be made to the other void Arduboy2::bootLogoText() { diff --git a/src/Arduboy2.h b/src/Arduboy2.h index b4f908b..e8dc6e2 100644 --- a/src/Arduboy2.h +++ b/src/Arduboy2.h @@ -240,7 +240,7 @@ class Arduboy2Base : public Arduboy2Core void systemButtons(); /** \brief - * Display the boot logo sequence. + * Display the boot logo sequence using `drawBitmap()`. * * \details * This function is called by `begin()` and can be called by a sketch @@ -258,11 +258,78 @@ class Arduboy2Base : public Arduboy2Core * which derived classes can implement to add additional information to the * logo screen. The `Arduboy2` class uses this to display the unit name. * - * \see begin() boot() Arduboy2::bootLogoExtra() Arduboy2::bootLogoText() + * \see begin() boot() Arduboy2::bootLogoExtra() bootLogoShell() + * Arduboy2::bootLogoText() */ void bootLogo(); - // Called by bootLogo() to allow derived classes to display additional + /** \brief + * Display the boot logo sequence using `drawCompressed()`. + * + * \details + * This function can be called by a sketch after `boot()` as an alternative to + * `bootLogo()`. This may reduce code size if the sketch itself uses + * `drawCompressed()`. + * + * \see bootLogo() begin() boot() + */ + void bootLogoCompressed(); + + /** \brief + * Display the boot logo sequence using the `Sprites` class + * `drawSelfMasked()` function. + * + * \details + * This function can be called by a sketch after `boot()` as an alternative to + * `bootLogo()`. This may reduce code size if the sketch itself uses + * `Sprites` class functions. + * + * \see bootLogo() begin() boot() Sprites + */ + void bootLogoSpritesSelfMasked(); + + /** \brief + * Display the boot logo sequence using the `Sprites` class + * `drawOverwrite()` function. + * + * \details + * This function can be called by a sketch after `boot()` as an alternative to + * `bootLogo()`. This may reduce code size if the sketch itself uses + * `Sprites` class functions. + * + * \see bootLogo() begin() boot() Sprites + */ + void bootLogoSpritesOverwrite(); + + /** \brief + * Display the boot logo sequence using the provided function + * + * \param drawLogo A reference to a function which will draw the boot logo + * at the given Y position. + * + * \details + * This common function executes the sequence to display the boot logo. + * It is called by `bootLogo()` and other similar functions which provide it + * with a reference to a function which will do the actual drawing of the + * logo. + * + * The prototype for the function provided to draw the logo is: + + * \code + * void drawLogo(int16_t y); + * \endcode + * + * The y parameter is the Y offset for the top of the logo. It is expected + * that the logo will be 16 pixels high and centered horizontally. This will + * result in the logo stopping in the middle of the screen at the end of the + * sequence. If the logo height is not 16 pixels, the Y value can be adjusted + * to compensate. + * + * \see bootLogo() boot() + */ + void bootLogoShell(void (*drawLogo)(int16_t)); + + // Called by bootLogoShell() to allow derived classes to display additional // information after the logo stops scrolling down. virtual void bootLogoExtra(); @@ -495,7 +562,7 @@ class Arduboy2Base : public Arduboy2Core * * The array must be located in program memory by using the PROGMEM modifier. */ - void drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t w, uint8_t h, uint8_t color = WHITE); + static void drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t w, uint8_t h, uint8_t color = WHITE); /** \brief * Draw a bitmap from a horizontally oriented array in program memory. @@ -533,6 +600,7 @@ class Arduboy2Base : public Arduboy2Core * \param sy The Y coordinate of the top left pixel affected by the bitmap. * \param bitmap A pointer to the compressed bitmap array in program memory. * \param color The color of pixels for bits set to 1 in the bitmap. + * (optional; defaults to WHITE). * * \details * Draw a bitmap starting at the given coordinates from an array that has @@ -547,7 +615,7 @@ class Arduboy2Base : public Arduboy2Core * * The array must be located in program memory by using the PROGMEM modifier. */ - void drawCompressed(int16_t sx, int16_t sy, const uint8_t *bitmap, uint8_t color = WHITE); + static void drawCompressed(int16_t sx, int16_t sy, const uint8_t *bitmap, uint8_t color = WHITE); /** \brief * Get a pointer to the display buffer in RAM. @@ -1040,6 +1108,12 @@ class Arduboy2Base : public Arduboy2Core // helper function for sound enable/disable system control void sysCtrlSound(uint8_t buttons, uint8_t led, uint8_t eeVal); + // functions passed to bootLogoShell() to draw the logo + static void drawLogoBitmap(int16_t y); + static void drawLogoCompressed(int16_t y); + static void drawLogoSpritesSelfMasked(int16_t y); + static void drawLogoSpritesOverwrite(int16_t y); + // For button handling uint8_t currentButtonState; uint8_t previousButtonState; @@ -1135,7 +1209,7 @@ class Arduboy2 : public Print, public Arduboy2Base * Show the unit name at the bottom of the boot logo screen. * * \details - * This function is called by `bootLogo()` and `bootlogoText()`. + * This function is called by `bootLogoShell()` and `bootlogoText()`. * * If a unit name has been saved in system EEPROM, it will be displayed at * the bottom of the screen. This function pauses for a short time to allow @@ -1147,8 +1221,8 @@ class Arduboy2 : public Print, public Arduboy2Base * This function would not normally be called directly from within a sketch * itself. * - * \see readUnitName() writeUnitName() bootLogo() bootLogoText() - * writeShowUnitNameFlag() begin() + * \see readUnitName() writeUnitName() bootLogo() bootLogoShell() + * bootLogoText() writeShowUnitNameFlag() begin() */ virtual void bootLogoExtra(); diff --git a/src/Sprites.h b/src/Sprites.h index 4716b20..d3a95d8 100644 --- a/src/Sprites.h +++ b/src/Sprites.h @@ -87,8 +87,8 @@ class Sprites * ..O.. OOOOO OOOOO ..O.. * ..... .OOO. OOOOO O...O */ - void drawExternalMask(int16_t x, int16_t y, const uint8_t *bitmap, - const uint8_t *mask, uint8_t frame, uint8_t mask_frame); + static void drawExternalMask(int16_t x, int16_t y, const uint8_t *bitmap, + const uint8_t *mask, uint8_t frame, uint8_t mask_frame); /** \brief * Draw a sprite using an array containing both image and mask values. @@ -124,7 +124,7 @@ class Sprites * ..O.. OOOOO OOOOO ..O.. * ..... .OOO. OOOOO O...O */ - void drawPlusMask(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame); + static void drawPlusMask(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame); /** \brief * Draw a sprite by replacing the existing content completely. @@ -155,7 +155,7 @@ class Sprites * ..O.. OOOOO ..O.. * ..... OOOOO ..... */ - void drawOverwrite(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame); + static void drawOverwrite(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame); /** \brief * "Erase" a sprite. @@ -186,7 +186,7 @@ class Sprites * ..O.. OOOOO OO.OO * ..... OOOOO OOOOO */ - void drawErase(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame); + static void drawErase(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame); /** \brief * Draw a sprite using only the bits set to 1. @@ -216,20 +216,20 @@ class Sprites * ..O.. OOOOO OOOOO * ..... OOOOO OOOOO */ - void drawSelfMasked(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame); + static void drawSelfMasked(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame); // Master function. Needs to be abstracted into separate function for // every render type. // (Not officially part of the API) - void draw(int16_t x, int16_t y, - const uint8_t *bitmap, uint8_t frame, - const uint8_t *mask, uint8_t sprite_frame, - uint8_t drawMode); + static void draw(int16_t x, int16_t y, + const uint8_t *bitmap, uint8_t frame, + const uint8_t *mask, uint8_t sprite_frame, + uint8_t drawMode); // (Not officially part of the API) - void drawBitmap(int16_t x, int16_t y, - const uint8_t *bitmap, const uint8_t *mask, - uint8_t w, uint8_t h, uint8_t draw_mode); + static void drawBitmap(int16_t x, int16_t y, + const uint8_t *bitmap, const uint8_t *mask, + uint8_t w, uint8_t h, uint8_t draw_mode); }; #endif diff --git a/src/ab_logo.c b/src/ab_logo.c index 4041df2..e3f6022 100644 --- a/src/ab_logo.c +++ b/src/ab_logo.c @@ -10,27 +10,74 @@ #define ARDUBOY_LOGO_CREATED // arduboy_logo.png +// drawBitmap() format // 88x16 -PROGMEM const unsigned char arduboy_logo[] = { -0xF0, 0xF8, 0x9C, 0x8E, 0x87, 0x83, 0x87, 0x8E, 0x9C, 0xF8, -0xF0, 0x00, 0x00, 0xFE, 0xFF, 0x03, 0x03, 0x03, 0x03, 0x03, -0x07, 0x0E, 0xFC, 0xF8, 0x00, 0x00, 0xFE, 0xFF, 0x03, 0x03, -0x03, 0x03, 0x03, 0x07, 0x0E, 0xFC, 0xF8, 0x00, 0x00, 0xFF, -0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, -0x00, 0x00, 0xFE, 0xFF, 0x83, 0x83, 0x83, 0x83, 0x83, 0xC7, -0xEE, 0x7C, 0x38, 0x00, 0x00, 0xF8, 0xFC, 0x0E, 0x07, 0x03, -0x03, 0x03, 0x07, 0x0E, 0xFC, 0xF8, 0x00, 0x00, 0x3F, 0x7F, -0xE0, 0xC0, 0x80, 0x80, 0xC0, 0xE0, 0x7F, 0x3F, 0xFF, 0xFF, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xFF, 0xFF, 0x00, -0x00, 0xFF, 0xFF, 0x0C, 0x0C, 0x0C, 0x0C, 0x1C, 0x3E, 0x77, -0xE3, 0xC1, 0x00, 0x00, 0x7F, 0xFF, 0xC0, 0xC0, 0xC0, 0xC0, -0xC0, 0xE0, 0x70, 0x3F, 0x1F, 0x00, 0x00, 0x1F, 0x3F, 0x70, -0xE0, 0xC0, 0xC0, 0xC0, 0xE0, 0x70, 0x3F, 0x1F, 0x00, 0x00, -0x7F, 0xFF, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xE3, 0x77, 0x3E, -0x1C, 0x00, 0x00, 0x1F, 0x3F, 0x70, 0xE0, 0xC0, 0xC0, 0xC0, -0xE0, 0x70, 0x3F, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, +const uint8_t arduboy_logo[] PROGMEM = { +0xF0, 0xF8, 0x9C, 0x8E, 0x87, 0x83, 0x87, 0x8E, 0x9C, 0xF8, +0xF0, 0x00, 0x00, 0xFE, 0xFF, 0x03, 0x03, 0x03, 0x03, 0x03, +0x07, 0x0E, 0xFC, 0xF8, 0x00, 0x00, 0xFE, 0xFF, 0x03, 0x03, +0x03, 0x03, 0x03, 0x07, 0x0E, 0xFC, 0xF8, 0x00, 0x00, 0xFF, +0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, +0x00, 0x00, 0xFE, 0xFF, 0x83, 0x83, 0x83, 0x83, 0x83, 0xC7, +0xEE, 0x7C, 0x38, 0x00, 0x00, 0xF8, 0xFC, 0x0E, 0x07, 0x03, +0x03, 0x03, 0x07, 0x0E, 0xFC, 0xF8, 0x00, 0x00, 0x3F, 0x7F, +0xE0, 0xC0, 0x80, 0x80, 0xC0, 0xE0, 0x7F, 0x3F, 0xFF, 0xFF, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xFF, 0xFF, 0x00, +0x00, 0xFF, 0xFF, 0x0C, 0x0C, 0x0C, 0x0C, 0x1C, 0x3E, 0x77, +0xE3, 0xC1, 0x00, 0x00, 0x7F, 0xFF, 0xC0, 0xC0, 0xC0, 0xC0, +0xC0, 0xE0, 0x70, 0x3F, 0x1F, 0x00, 0x00, 0x1F, 0x3F, 0x70, +0xE0, 0xC0, 0xC0, 0xC0, 0xE0, 0x70, 0x3F, 0x1F, 0x00, 0x00, +0x7F, 0xFF, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xE3, 0x77, 0x3E, +0x1C, 0x00, 0x00, 0x1F, 0x3F, 0x70, 0xE0, 0xC0, 0xC0, 0xC0, +0xE0, 0x70, 0x3F, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00 +}; +// arduboy_logo.png +// drawCompressed() format +// 88x16 +const uint8_t arduboy_logo_compressed[] PROGMEM = { +0x57, 0x0F, 0x9C, 0x53, 0x72, 0x75, 0x29, 0xE5, 0x9C, 0x92, +0xCE, 0x95, 0x52, 0xAD, 0x4E, 0x49, 0xE7, 0x08, 0x09, 0xED, +0x76, 0xBB, 0xDD, 0x2A, 0xAB, 0xAC, 0x55, 0x92, 0x90, 0xD0, +0x6E, 0xB7, 0xDB, 0xAD, 0xB2, 0xCA, 0x5A, 0x25, 0xF9, 0xF8, +0xF0, 0xC6, 0x47, 0x48, 0x28, 0x95, 0x54, 0x52, 0x49, 0x25, +0x9D, 0x3A, 0x95, 0x5A, 0x3A, 0x45, 0x2A, 0xB7, 0x29, 0xA7, +0xE4, 0x76, 0xBB, 0x55, 0x56, 0x59, 0xAB, 0x24, 0x9F, 0x5D, +0x5B, 0x65, 0xD7, 0xE9, 0xEC, 0x92, 0x29, 0x3B, 0xA1, 0x4E, +0xA7, 0xD3, 0xE9, 0x74, 0x9A, 0x8F, 0x8F, 0xEF, 0xED, 0x76, +0xBB, 0x55, 0x4E, 0xAE, 0x52, 0xAD, 0x9C, 0x9C, 0x4F, 0xE7, +0xED, 0x76, 0xBB, 0xDD, 0x2E, 0x95, 0x53, 0xD9, 0x25, 0xA5, +0x54, 0xD6, 0x2A, 0xAB, 0xEC, 0x76, 0xBB, 0x54, 0x4E, 0x65, +0x97, 0x94, 0x3A, 0x22, 0xA9, 0xA4, 0x92, 0x4A, 0x2A, 0xE9, +0x94, 0x4D, 0x2D, 0x9D, 0xA2, 0x94, 0xCA, 0x5A, 0x65, 0x95, +0xDD, 0x6E, 0x97, 0xCA, 0xA9, 0xEC, 0x12, 0x55, 0x69, 0x42, +0x7A +}; + +// arduboy_logo.png +// Sprites::drawSelfMasked() format +// 88x16 +const uint8_t arduboy_logo_sprite[] PROGMEM = { +88, 16, +0xF0, 0xF8, 0x9C, 0x8E, 0x87, 0x83, 0x87, 0x8E, 0x9C, 0xF8, +0xF0, 0x00, 0x00, 0xFE, 0xFF, 0x03, 0x03, 0x03, 0x03, 0x03, +0x07, 0x0E, 0xFC, 0xF8, 0x00, 0x00, 0xFE, 0xFF, 0x03, 0x03, +0x03, 0x03, 0x03, 0x07, 0x0E, 0xFC, 0xF8, 0x00, 0x00, 0xFF, +0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, +0x00, 0x00, 0xFE, 0xFF, 0x83, 0x83, 0x83, 0x83, 0x83, 0xC7, +0xEE, 0x7C, 0x38, 0x00, 0x00, 0xF8, 0xFC, 0x0E, 0x07, 0x03, +0x03, 0x03, 0x07, 0x0E, 0xFC, 0xF8, 0x00, 0x00, 0x3F, 0x7F, +0xE0, 0xC0, 0x80, 0x80, 0xC0, 0xE0, 0x7F, 0x3F, 0xFF, 0xFF, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xFF, 0xFF, 0x00, +0x00, 0xFF, 0xFF, 0x0C, 0x0C, 0x0C, 0x0C, 0x1C, 0x3E, 0x77, +0xE3, 0xC1, 0x00, 0x00, 0x7F, 0xFF, 0xC0, 0xC0, 0xC0, 0xC0, +0xC0, 0xE0, 0x70, 0x3F, 0x1F, 0x00, 0x00, 0x1F, 0x3F, 0x70, +0xE0, 0xC0, 0xC0, 0xC0, 0xE0, 0x70, 0x3F, 0x1F, 0x00, 0x00, +0x7F, 0xFF, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xE3, 0x77, 0x3E, +0x1C, 0x00, 0x00, 0x1F, 0x3F, 0x70, 0xE0, 0xC0, 0xC0, 0xC0, +0xE0, 0x70, 0x3F, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, +0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00 }; #endif