From 86185a5688be0a652e18fe7d8a42f2d32ed55c5c Mon Sep 17 00:00:00 2001 From: "Mr.Blinky" Date: Sun, 19 Nov 2017 19:50:22 +0100 Subject: [PATCH] Multiple OLED display support added multiple OLED display support added flexible width and height support optimized buttonsState with optional bootlkey support --- src/Arduboy2.cpp | 61 +++++---- src/Arduboy2Core.cpp | 308 ++++++++++++++++++++++++++++++++++++------- src/Arduboy2Core.h | 70 +++++++--- src/Sprites.cpp | 21 +-- 4 files changed, 359 insertions(+), 101 deletions(-) diff --git a/src/Arduboy2.cpp b/src/Arduboy2.cpp index 2b3d7d2..e02f269 100644 --- a/src/Arduboy2.cpp +++ b/src/Arduboy2.cpp @@ -109,7 +109,7 @@ void Arduboy2Base::bootLogo() void Arduboy2Base::drawLogoBitmap(int16_t y) { - drawBitmap(20, y, arduboy_logo, 88, 16); + drawBitmap(20 - (64 - WIDTH / 2), y, arduboy_logo, 88, 16); } void Arduboy2Base::bootLogoCompressed() @@ -119,7 +119,7 @@ void Arduboy2Base::bootLogoCompressed() void Arduboy2Base::drawLogoCompressed(int16_t y) { - drawCompressed(20, y, arduboy_logo_compressed); + drawCompressed(20 - (64 - WIDTH / 2), y, arduboy_logo_compressed); } void Arduboy2Base::bootLogoSpritesSelfMasked() @@ -129,7 +129,7 @@ void Arduboy2Base::bootLogoSpritesSelfMasked() void Arduboy2Base::drawLogoSpritesSelfMasked(int16_t y) { - Sprites::drawSelfMasked(20, y, arduboy_logo_sprite, 0); + Sprites::drawSelfMasked(20 - (64 - WIDTH / 2), y, arduboy_logo_sprite, 0); } void Arduboy2Base::bootLogoSpritesOverwrite() @@ -139,7 +139,7 @@ void Arduboy2Base::bootLogoSpritesOverwrite() void Arduboy2Base::drawLogoSpritesOverwrite(int16_t y) { - Sprites::drawOverwrite(20, y, arduboy_logo_sprite, 0); + Sprites::drawOverwrite(20 - (64 - WIDTH / 2), y, arduboy_logo_sprite, 0); } // bootLogoText() should be kept in sync with bootLogoShell() @@ -306,25 +306,27 @@ void Arduboy2Base::drawPixel(int16_t x, int16_t y, uint8_t color) asm volatile ( - "mul %[width_offset], %A[y]\n" - "movw %[row_offset], r0\n" - "andi %A[row_offset], 0x80\n" // row_offset &= (~0b01111111); - "clr __zero_reg__\n" - "add %A[row_offset], %[x]\n" - // mask for only 0-7 - "andi %A[y], 0x07\n" - // Z += y - "add r30, %A[y]\n" - "adc r31, __zero_reg__\n" + "mov r16,%A[y] \n" + "andi r16, 0xF8 \n" + "mul %[width_offset], r16 \n" + "movw %[row_offset], r0 \n" + "clr __zero_reg__ \n" + "add %A[row_offset], %[x] \n" + "adc %B[row_offset], __zero_reg__ \n" + // mask for only 0-7 + "andi %A[y], 0x07 \n" + // Z += y + "add r30, %A[y] \n" + "adc r31, __zero_reg__ \n" // load correct bitshift from program RAM - "lpm %[bit], Z\n" + "lpm %[bit], Z \n" : [row_offset] "=&x" (row_offset), // upper register (ANDI) [bit] "=r" (bit), [y] "+d" (y), // upper register (ANDI), must be writable "+z" (bsl) // is modified to point to the proper shift array element : [width_offset] "r" ((uint8_t)(WIDTH/8)), [x] "r" ((uint8_t)x) - : + : "r16" ); if (color) { @@ -624,24 +626,27 @@ void Arduboy2Base::fillScreen(uint8_t color) // if value is zero, skip assigning to 0xff "cpse %[color], __zero_reg__\n" "ldi %[color], 0xFF\n" - // counter = 0 - "clr __tmp_reg__\n" + // counter = WIDTH * HEIGHT / 8 / 8 + "ldi r24, %[cnt]\n" "loopto:\n" - // (4x) push zero into screen buffer, + // (8x) push zero into screen buffer, // then increment buffer position "st Z+, %[color]\n" "st Z+, %[color]\n" "st Z+, %[color]\n" "st Z+, %[color]\n" - // increase counter - "inc __tmp_reg__\n" - // repeat for 256 loops - // (until counter rolls over back to 0) + "st Z+, %[color]\n" + "st Z+, %[color]\n" + "st Z+, %[color]\n" + "st Z+, %[color]\n" + // decrease counter + "subi r24, 1\n" + // repeat for 128, 144 or 192 loops depending on screen resolution "brne loopto\n" : [color] "+d" (color), "+z" (bPtr) - : - : + : [cnt] "M" (WIDTH * HEIGHT / 8 / 8) + : "r24" ); } @@ -1140,9 +1145,9 @@ void Arduboy2::bootLogoText() } clear(); - cursor_x = 23; + cursor_x = 23 - (64 - WIDTH / 2); cursor_y = y; - print("ARDUBOY"); + print(F("ARDUBOY")); display(); delayShort(27); // longer delay post boot, we put it inside the loop to @@ -1173,7 +1178,7 @@ void Arduboy2::bootLogoExtra() if (c != 0xFF && c != 0x00) { uint8_t i = EEPROM_UNIT_NAME; - cursor_x = 50; + cursor_x = 50 - (64 - WIDTH / 2); cursor_y = 56; do diff --git a/src/Arduboy2Core.cpp b/src/Arduboy2Core.cpp index cb6167a..659efa8 100644 --- a/src/Arduboy2Core.cpp +++ b/src/Arduboy2Core.cpp @@ -5,12 +5,41 @@ */ #include "Arduboy2Core.h" +#include const uint8_t PROGMEM lcdBootProgram[] = { // boot defaults are commented out but left here in case they // might prove useful for reference - // // Further reading: https://www.adafruit.com/datasheets/SSD1306.pdf + +#ifdef OLED_SH1106 + 0x8D, 0x14, // Charge Pump Setting v = enable (0x14) + 0xA1, // Set Segment Re-map + 0xC8, // Set COM Output Scan Direction + 0x81, 0xCF, // Set Contrast v = 0xCF + 0xD9, 0xF1, // Set Precharge = 0xF1 + OLED_SET_COLUMN_ADDRESS_LO, //Set column address for left most pixel + 0xAF // Display On +#elif defined(OLED_96X96) + 0x15, 0x10, 0x3f, //Set column start and end address + 0x75, 0x00, 0x5f, //Set row start and end address + 0xA0, 0x55, //set re-map: split odd-even COM signals|COM remap|vertical address increment|column address remap + 0xA1, 0x00, //set display start line + 0xA2, 0x60, //set display offset + //0xA4, //Normal display + 0xA8, 0x5F, //Set MUX ratio 96MUX + //0xB2, 0x23, + //0xB3, 0xF0, //set devider clock | oscillator frequency + //0x81, 0x0F, //Set contrast + //0xBC, 0x1F, //set precharge voltage + //0x82, 0xFE, //set second Precharge speed + 0xB1, 0x21, //reset and 1st precharge phase length phase 2:2 DCLKs, Phase 1: 1 DCLKs + //0xBB, 0x0F, //set 2nd precharge period: 15 DCLKs + //0xbe, 0x1F, //output level high voltage com signal + //0xB8, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1A, 0x1C, 0x1E, 0x20, //set gray scale table + 0xAF //Display on +#else + // for SSD1306 and compatible displays // // Display Off // 0xAE, @@ -68,8 +97,12 @@ const uint8_t PROGMEM lcdBootProgram[] = { // set page address range // 0x22, 0x00, PAGE_ADDRESS_END -}; +#endif +#if defined OLED_SSD1309 //required additionally for SSD1309 + 0x21, 0x00, COLUMN_ADDRESS_END +#endif +}; Arduboy2Core::Arduboy2Core() { } @@ -113,25 +146,39 @@ void Arduboy2Core::bootPins() #ifdef ARDUBOY_10 // Port B INPUT_PULLUP or HIGH - PORTB |= _BV(RED_LED_BIT) | _BV(GREEN_LED_BIT) | _BV(BLUE_LED_BIT) | - _BV(B_BUTTON_BIT); + PORTB |= _BV(RED_LED_BIT) | + #ifndef ARDUINO_AVR_PROMICRO + _BV(GREEN_LED_BIT) | + #endif + _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); + DDRB |= _BV(RED_LED_BIT) | + #ifndef ARDUINO_AVR_PROMICRO + _BV(GREEN_LED_BIT) | + #endif + _BV(BLUE_LED_BIT) | _BV(SPI_MOSI_BIT) | _BV(SPI_SCK_BIT); // Port C // Speaker: Not set here. Controlled by audio class // Port D INPUT_PULLUP or HIGH - PORTD |= _BV(CS_BIT); + #ifdef ARDUINO_AVR_PROMICRO + PORTD |= _BV(CS_BIT) | _BV(GREEN_LED_BIT); + #else + PORTD |= _BV(CS_BIT); + #endif // 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); + DDRD |= _BV(RST_BIT) | _BV(CS_BIT) | + #ifdef ARDUINO_AVR_PROMICRO + _BV(GREEN_LED_BIT) | + #endif + _BV(DC_BIT); // Port E INPUT_PULLUP or HIGH PORTE |= _BV(A_BUTTON_BIT); @@ -305,55 +352,212 @@ void Arduboy2Core::paint8Pixels(uint8_t pixels) void Arduboy2Core::paintScreen(const uint8_t *image) { +#ifdef OLED_SH1106 + for (uint8_t i = 0; i < HEIGHT / 8; i++) + { + LCDCommandMode(); + SPDR = (OLED_SET_PAGE_ADDRESS + i); + while (!(SPSR & _BV(SPIF))); + SPDR = (OLED_SET_COLUMN_ADDRESS_HI); // only reset hi nibble to zero + while (!(SPSR & _BV(SPIF))); + LCDDataMode(); + for (uint8_t j = WIDTH; j > 0; j--) + { + SPDR = pgm_read_byte(image++); + while (!(SPSR & _BV(SPIF))); + } + } +#elif defined(OLED_96X96) + uint16_t i = 0; + for (uint8_t col = 0; col < WIDTH / 2; col++) + { + for (uint8_t row = 0; row < HEIGHT / 8; row++) + { + uint8_t b1 = pgm_read_byte(image + i); + uint8_t b2 = pgm_read_byte(image + i + 1); + for (uint8_t shift = 0; shift < 8; shift++) + { + uint8_t c = 0xFF; + if ((b1 & 1) == 0) c &= 0x0F; + if ((b2 & 1) == 0) c &= 0xF0; + SPDR = c; + b1 = b1 >> 1; + b2 = b2 >> 1; + while (!(SPSR & _BV(SPIF))); + } + i += WIDTH; + } + i -= HEIGHT / 8 * WIDTH - 2; + } + +#else + //OLED SSD1306 and compatibles for (int i = 0; i < (HEIGHT*WIDTH)/8; i++) { SPItransfer(pgm_read_byte(image + i)); } +#endif } // paint from a memory buffer, this should be FAST as it's likely what // will be used by any buffer based subclass void Arduboy2Core::paintScreen(uint8_t image[], bool clear) { - uint8_t c; - int i = 0; - - if (clear) - { - SPDR = image[i]; // set the first SPI data byte to get things started - image[i++] = 0; // clear the first image byte - } - else - SPDR = image[i++]; - - // the code to iterate the loop and get the next byte from the buffer is - // executed while the previous byte is being sent out by the SPI controller - while (i < (HEIGHT * WIDTH) / 8) - { - // get the next byte. It's put in a local variable so it can be sent as - // as soon as possible after the sending of the previous byte has completed - if (clear) - { - c = image[i]; - // clear the byte in the image buffer - image[i++] = 0; - } - else - c = image[i++]; - - while (!(SPSR & _BV(SPIF))) { } // wait for the previous byte to be sent - - // put the next byte in the SPI data register. The SPI controller will - // clock it out while the loop continues and gets the next byte ready - SPDR = c; - } - while (!(SPSR & _BV(SPIF))) { } // wait for the last byte to be sent +#ifdef OLED_SH1106 + //Assembly optimized page mode display code with clear support. + //Each byte transfer takes 18 cycles + asm volatile ( + " ldi r25, %[page_cmd] \n\t" + ".l1: \n\t" + " ldi r24, %[width] ;1 \n\t" + " ldi r20,5 ;1 \n\t" + " cbi %[dc_port], %[dc_bit] ;2 cmd mode \n\t" + " out %[spdr], r25 ;1 \n\t" + ".l2: subi r20,1 ;r20*3-1 : 14 \n\t" + " brne .l2 \n\t" + " rjmp .+0 ;2 \n\t" + " ldi r20,%[col_cmd] ;1 \n\t" + " out %[spdr], r20 ;1 \n\t" + ".l3: rjmp .l7 ;2 \n\t" + ".l4: ld r20, Z ;2 \n\t" + " cp %[clear], __zero_reg__ ;1 \n\t" + " brne .l5 ;1/2 \n\t" + " nop ;1 \n\t" + " rjmp .l6 ;2 \n\t" + ".l5: st Z, __zero_reg__ ;2 : 7 \n\t" + ".l6: sbi %[dc_port], %[dc_bit] ;2 data mode \n\t" + " out %[spdr], r20 ;1 \n\t" + " adiw r30, 1 ;2 \n\t" + ".l7: rjmp .+0 ;2 \n\t" + " nop ;1 \n\t" + " subi r24, 1 ;1 \n\t" + " brne .l4 ;1/2 : 5/6 \n\t" + " rjmp .+0 ;2 \n\t" + " subi r25, -1 ;1 \n\t" + " cpi r25,%[page_end] ;1 \n\t" + " brne .l1 ;1/2 : 5/6 \n\t" + : + : [ptr] "z" (image), + [page_cmd] "M" (OLED_SET_PAGE_ADDRESS), + [page_end] "M" (OLED_SET_PAGE_ADDRESS + (HEIGHT / 8)), + [dc_port] "I" (_SFR_IO_ADDR(DC_PORT)), + [dc_bit] "I" (DC_BIT), + [spdr] "I" (_SFR_IO_ADDR(SPDR)), + [col_cmd] "M" (OLED_SET_COLUMN_ADDRESS_HI), + [width] "M" (WIDTH + 1), + [clear] "a" (clear) + : "r20", "r24", "r25" + ); +#elif defined(OLED_96X96) + // 1 bit to 4-bit display code with clear support. + // Each transfer takes 18 cycles with additional 4 cycles for a column change. + asm volatile( + " ldi r25, %[col] \n\t" + ".lcolumn: \n\t" + " ldi r24, %[row] ;1 \n\t" + ".lrow: \n\t" + " ldi r21, 7 ;1 \n\t" + " ld r22, z ;2 \n\t" + " ldd r23, z+1 ;2 \n\t" + ".lshiftstart: \n\t" + " ldi r20, 0xFF ;1 \n\t" + " sbrs r22, 0 ;1 \n\t" + " andi r20, 0x0f ;1 \n\t" + " sbrs r23, 0 ;1 \n\t" + " andi r20,0xf0 ;1 \n\t" + " out %[spdr], r20 ;1 \n\t" + " \n\t" + " cp %[clear], __zero_reg__ ;1 \n\t" + " brne .lclear1 ;1/2 \n\t" + ".lshiftothers: \n\t" + " movw r18, %A[ptr] ;1 \n\t" + " rjmp .+0 ;2 \n\t" + " rjmp .lshiftnext ;2 \n\t" + ".lclear1: \n\t" + " st z, __zero_reg__ ;2 \n\t" + " std z+1, __zero_reg__ ;2 \n\t" + ".lshiftnext: \n\t" + " \n\t" + " lsr r22 ;1 \n\t" + " lsr r23 ;1 \n\t" + " \n\t" + " ldi r20, 0xFF ;1 \n\t" + " sbrs r22, 0 ;1/2 \n\t" + " andi r20, 0x0f ;1 \n\t" + " sbrs r23, 0 ;1/2 \n\t" + " andi r20,0xf0 ;1 \n\t" + " \n\t" + " subi r18, %[top_lsb] ;1 \n\t" //image - (HEIGHT / 8) * ((WIDTH / 8) - 1) + 2 + " sbci r19, %[top_msb] ;1 \n\t" + " subi r21, 1 ;1 \n\t" + " out %[spdr], r20 ;1 \n\t" + " brne .lshiftothers ;1/2 \n\t" + " \n\t" + " nop ;1 \n\t" + " subi %A[ptr], %[width] ;1 \n\t" //image + width (negated addition) + " sbci %B[ptr], -1 ;1 \n\t" + " subi r24, 1 ;1 \n\t" + " brne .lrow ;1/2 \n\t" + " \n\t" + " movw %A[ptr], r18 ;1 \n\t" + " subi r25, 1 ;1 \n\t" + " brne .lcolumn ;1/2 \n\t" + : + : [ptr] "z" (image), + [spdr] "I" (_SFR_IO_ADDR(SPDR)), + [row] "M" (HEIGHT / 8), + [col] "M" (WIDTH / 2), + [width] "M" (256 - WIDTH), + [top_lsb] "M" ((WIDTH * ((HEIGHT / 8) - 1) - 2) & 0xFF), + [top_msb] "M" ((WIDTH * ((HEIGHT / 8) - 1) - 2) >> 8), + [clear] "a" (clear) + : "r18", "r19", "r20", "r21", "r22", "r23", "r24", "r25" + ); +#else + //OLED SSD1306 and compatibles + //data only transfer with clear support at 18 cycles per transfer + asm volatile ( + " ldi r24,%[len_lsb] \n\t" + " ldi r25,%[len_msb] \n\t" + ".l1: ld r20, Z ;2 \n\t" + " out %[spdr], r20 ;1 \n\t" + " cp %[clear], __zero_reg__ ;1 \n\t" //if (clear) *(image++) = 0 + " breq .l2 ;1/2 : 5/6 \n\t" + " st Z+, __zero_reg__ ;2 \n\t" + " rjmp .l3 ;2 \n\t" + ".l2: \n\t" + " adiw r30, 1 ;2 \n\t" // else *(image++) + " nop ;1 \n\t" + ".l3: \n\t" + " rjmp .+0 ;2 \n\t" + " rjmp .+0 ;2 \n\t" + " rjmp .+0 ;2 \n\t" + " sbiw r24, 1 ;1 \n\t" + " brne .l1 ;1/2 : 18 \n\t" + : + : [ptr] "z" (image), + [spdr] "I" (_SFR_IO_ADDR(SPDR)), + [len_msb] "M" (WIDTH * (HEIGHT / 8) >> 8), + [len_lsb] "M" (WIDTH * (HEIGHT / 8) & 0xFF), + [clear] "a" (clear) + : "r20", "r24", "r25" + ); +#endif + while (!(SPSR & _BV(SPIF))); // wait for the last transfer to finish } void Arduboy2Core::blank() { +#ifdef OLED_SH1106 + for (int i = 0; i < (HEIGHT*132)/8; i++) + SPItransfer(0x00); +#elif defined(OLED_96X96) + for (int i = 0; i < (HEIGHT*WIDTH)/2; i++) + SPItransfer(0x00); +#else //OLED SSD1306 and compatibles for (int i = 0; i < (HEIGHT*WIDTH)/8; i++) SPItransfer(0x00); +#endif } void Arduboy2Core::sendLCDCommand(uint8_t command) @@ -455,16 +659,30 @@ uint8_t Arduboy2Core::buttonsState() // down, left, up buttons = ((~PINB) & B01110000); // right button - buttons = buttons | (((~PINC) & B01000000) >> 4); + if ((PINC & _BV(6)) == 0) buttons |= RIGHT_BUTTON; //compiles to shorter and faster code + // A and B - buttons = buttons | (((~PINF) & B11000000) >> 6); + if ((PINF & _BV(7)) == 0) buttons |= A_BUTTON; + if ((PINF & _BV(6)) == 0) buttons |= B_BUTTON; #elif defined(ARDUBOY_10) // down, up, left right buttons = ((~PINF) & B11110000); // A (left) - buttons = buttons | (((~PINE) & B01000000) >> 3); +if ((PINE & _BV(6)) == 0) {buttons |= A_BUTTON;} // B (right) - buttons = buttons | (((~PINB) & B00010000) >> 2); + if ((PINB & _BV(4)) == 0) {buttons |= B_BUTTON;} +#endif +#ifdef ENABLE_BOOTLOADER_KEYS + //bootloader button combo + if (buttons == (LEFT_BUTTON | UP_BUTTON | A_BUTTON | B_BUTTON)) + { cli(); + //set magic boot key + *(uint8_t *)0x0800 = 0x77;//using uint8_t saves an instruction + *(uint8_t *)0x0801 = 0x77; + //enable and trigger watchdog by timeout + wdt_enable(WDTO_15MS); + while (true); + } #endif return buttons; diff --git a/src/Arduboy2Core.h b/src/Arduboy2Core.h index 324ae15..f7e03b2 100644 --- a/src/Arduboy2Core.h +++ b/src/Arduboy2Core.h @@ -38,9 +38,15 @@ // ----- Arduboy pins ----- #ifdef ARDUBOY_10 -#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 +#ifdef ARDUINO_AVR_PROMICRO + #define PIN_CS 3 // Pro Micro alternative display CS pin + #define CS_PORT PORTD + #define CS_BIT PORTD3 +#else + #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 +#endif #define PIN_DC 4 // Display D/C Arduino pin number #define DC_PORT PORTD // Display D/C port @@ -57,14 +63,23 @@ #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. */ +#ifdef ARDUINO_AVR_PROMICRO + #define GREEN_LED 3 // Pro Micro alternative green LED pin +#else + #define GREEN_LED 11 /**< The pin number for the green color in the RGB LED. */ +#endif #define BLUE_LED 9 /**< The pin number for the blue color in the RGB LED. */ #define RED_LED_PORT PORTB #define RED_LED_BIT PORTB6 -#define GREEN_LED_PORT PORTB -#define GREEN_LED_BIT PORTB7 +#ifdef ARDUINO_AVR_PROMICRO + #define GREEN_LED_PORT PORTD // Pro Micro alternative green LED port + #define GREEN_LED_BIT PORTD0 +#else + #define GREEN_LED_PORT PORTB + #define GREEN_LED_BIT PORTB7 +#endif #define BLUE_LED_PORT PORTB #define BLUE_LED_BIT PORTB5 @@ -103,15 +118,22 @@ #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 SPEAKER_1_PORT PORTC #define SPEAKER_1_DDR DDRC #define SPEAKER_1_BIT PORTC6 -#define SPEAKER_2_PORT PORTC -#define SPEAKER_2_DDR DDRC -#define SPEAKER_2_BIT PORTC7 +#ifdef ARDUINO_AVR_PROMICRO + #define PIN_SPEAKER_2 2 //Pro Micro alternative for 2nd speaker pin + #define SPEAKER_2_PORT PORTD + #define SPEAKER_2_DDR DDRD + #define SPEAKER_2_BIT PORTD1 +#else + #define PIN_SPEAKER_2 13 /**< The pin number of the second lead of the speaker */ + #define SPEAKER_2_PORT PORTC + #define SPEAKER_2_DDR DDRC + #define SPEAKER_2_BIT PORTC7 +#endif #define RAND_SEED_IN A4 // Open analog input used for noise by initRandomSeed() #define RAND_SEED_IN_PORTF @@ -201,7 +223,7 @@ #endif // -------------------- -// OLED hardware (SSD1306) +// OLED hardware (SSD1306,SSD1309,SH1106,OLED_64X64) #define OLED_PIXELS_INVERTED 0xA7 // All pixels inverted #define OLED_PIXELS_NORMAL 0xA6 // All pixels normal @@ -215,10 +237,24 @@ #define OLED_HORIZ_FLIPPED 0xA0 // reversed segment re-map #define OLED_HORIZ_NORMAL 0xA1 // normal segment re-map +#define OLED_SET_PAGE_ADDRESS 0xB0 +#ifdef OLED_SH1106 + #define OLED_SET_COLUMN_ADDRESS_LO 0x02 //SH1106 only: 1st pixel starts on column 2 +#else + #define OLED_SET_COLUMN_ADDRESS_LO 0x00 +#endif +#define OLED_SET_COLUMN_ADDRESS_HI 0x10 // ----- - -#define WIDTH 128 /**< The width of the display in pixels */ -#define HEIGHT 64 /**< The height of the display in pixels */ +#ifdef OLED_96X96 + #define WIDTH 96 + #define HEIGHT 96 +#elif defined(OLED_128X64) + #define WIDTH 128 + #define HEIGHT 96 +#else + #define WIDTH 128 /**< The width of the display in pixels */ + #define HEIGHT 64 /**< The height of the display in pixels */ +#endif #define COLUMN_ADDRESS_END (WIDTH - 1) & 127 // 128 pixels wide #define PAGE_ADDRESS_END ((HEIGHT/8)-1) & 7 // 8 pages high @@ -270,8 +306,7 @@ class Arduboy2Core * * \see LCDCommandMode() SPItransfer() */ - void static LCDDataMode(); - + inline void static LCDDataMode() __attribute__((always_inline)); /** \brief * Put the display into command mode. * @@ -294,8 +329,7 @@ class Arduboy2Core * * \see LCDDataMode() sendLCDCommand() SPItransfer() */ - void static LCDCommandMode(); - + inline void static LCDCommandMode() __attribute__((always_inline)); /** \brief * Transfer a byte to the display. * diff --git a/src/Sprites.cpp b/src/Sprites.cpp index 459e7f9..7c3d8b0 100644 --- a/src/Sprites.cpp +++ b/src/Sprites.cpp @@ -148,7 +148,7 @@ void Sprites::drawBitmap(int16_t x, int16_t y, data |= (uint8_t)(bitmap_data); Arduboy2Base::sBuffer[ofs] = data; } - if (yOffset != 0 && sRow < 7) { + if (yOffset != 0 && sRow < ((HEIGHT / 8) - 1)) { data = Arduboy2Base::sBuffer[ofs + WIDTH]; data &= (*((unsigned char *) (&mask_data) + 1)); data |= (*((unsigned char *) (&bitmap_data) + 1)); @@ -170,7 +170,7 @@ void Sprites::drawBitmap(int16_t x, int16_t y, if (sRow >= 0) { Arduboy2Base::sBuffer[ofs] |= (uint8_t)(bitmap_data); } - if (yOffset != 0 && sRow < 7) { + if (yOffset != 0 && sRow < ((HEIGHT / 8) - 1)) { Arduboy2Base::sBuffer[ofs + WIDTH] |= (*((unsigned char *) (&bitmap_data) + 1)); } ofs++; @@ -189,7 +189,7 @@ void Sprites::drawBitmap(int16_t x, int16_t y, if (sRow >= 0) { Arduboy2Base::sBuffer[ofs] &= ~(uint8_t)(bitmap_data); } - if (yOffset != 0 && sRow < 7) { + if (yOffset != 0 && sRow < ((HEIGHT / 8) - 1)) { Arduboy2Base::sBuffer[ofs + WIDTH] &= ~(*((unsigned char *) (&bitmap_data) + 1)); } ofs++; @@ -223,7 +223,7 @@ void Sprites::drawBitmap(int16_t x, int16_t y, data |= (uint8_t)(bitmap_data); Arduboy2Base::sBuffer[ofs] = data; } - if (yOffset != 0 && sRow < 7) { + if (yOffset != 0 && sRow < ((HEIGHT / 8) - 1)) { data = Arduboy2Base::sBuffer[ofs + WIDTH]; data &= (*((unsigned char *) (&mask_data) + 1)); data |= (*((unsigned char *) (&bitmap_data) + 1)); @@ -251,9 +251,8 @@ void Sprites::drawBitmap(int16_t x, int16_t y, "push r28\n" // save Y "push r29\n" "movw r28, %[buffer_ofs]\n" // Y = buffer_ofs_2 - "adiw r28, 63\n" // buffer_ofs_2 = buffer_ofs + 128 - "adiw r28, 63\n" - "adiw r28, 2\n" + "subi r28, %[neg_width]\n" // buffer_ofs_2 = buffer_ofs + WIDTH + "sbci r29, -1\n" "loop_y:\n" "loop_x:\n" // load bitmap and mask data @@ -269,8 +268,8 @@ void Sprites::drawBitmap(int16_t x, int16_t y, "movw %[mask_data], r0\n" // SECOND PAGE - // if yOffset != 0 && sRow < 7 - "cpi %[sRow], 7\n" + // if yOffset != 0 && sRow < ((HEIGHT / 8) - 1) + "cpi %[sRow], %[row_height]\n" "brge end_second_page\n" // then "ld %[data], Y\n" @@ -346,7 +345,9 @@ void Sprites::drawBitmap(int16_t x, int16_t y, // [sprite_ofs_jump] "r" (0), [yOffset] "l" (yOffset), // lower register - [mul_amt] "l" (mul_amt) // lower register + [mul_amt] "l" (mul_amt), // lower register + [neg_width] "M" (256 - WIDTH), + [row_height] "M" ((HEIGHT / 8) - 1) // NOTE: We also clobber r28 and r29 (y) but sometimes the compiler // won't allow us, so in order to make this work we don't tell it // that we clobber them. Instead, we push/pop to preserve them.