diff --git a/board-package-source/libraries/Arduboy2/src/Arduboy2Core.cpp b/board-package-source/libraries/Arduboy2/src/Arduboy2Core.cpp index c6e6ac5..f08ef50 100644 --- a/board-package-source/libraries/Arduboy2/src/Arduboy2Core.cpp +++ b/board-package-source/libraries/Arduboy2/src/Arduboy2Core.cpp @@ -12,7 +12,67 @@ const uint8_t PROGMEM lcdBootProgram[] = { // // Further reading: https://www.adafruit.com/datasheets/SSD1306.pdf -#ifdef OLED_SH1106 +#ifdef OLED_SSD1306_I2C + // Sets all registers to sane defaults since i2c + // displays usually havn't a reset input + + // Display Off + 0xAE, + + // Set Display Clock Divisor v = 0xF0 + // default is 0x80 + 0xD5, 0xF0, + + // Set Multiplex Ratio v = 0x3F + 0xA8, 0x3F, + + // Set Display Offset v = 0 + 0xD3, 0x00, + + // Set Start Line (0) + 0x40, + + // Charge Pump Setting v = enable (0x14) + // default is disabled + 0x8D, 0x14, + + // Set Segment Re-map (A0) | (b0001) + // default is (b0000) + 0xA1, + + // Set COM Output Scan Direction + 0xC8, + + // Set COM Pins v + 0xDA, 0x12, + + // Set Contrast v = 0xCF + 0x81, 0xCF, + + // Set Precharge = 0xF1 + 0xD9, 0xF1, + + // Set VCom Detect + 0xDB, 0x40, + + // Entire Display ON + 0xA4, + + // Set normal/inverse display + 0xA6, + + // Display On + 0xAF, + + // set display mode = horizontal addressing mode (0x00) + 0x20, 0x00, + + // set col address range + 0x21, 0x00, WIDTH-1, + + // set page address range + 0x22, 0x00, 0x07, +#elif defined(OLED_SH1106) 0x8D, 0x14, // Charge Pump Setting v = enable (0x14) 0xA1, // Set Segment Re-map 0xC8, // Set COM Output Scan Direction @@ -292,8 +352,43 @@ void Arduboy2Core::bootPins() #endif } + +#ifdef OLED_SSD1306_I2C +#define I2CADDR 0x3c + +void i2c_send_byte(uint8_t data) { + TWDR = data; + TWCR = _BV(TWINT) | _BV(TWEN); + while( !(TWCR & _BV(TWINT))); +} + +void i2c_start() { + TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN); + while( !(TWCR & _BV(TWINT))); + i2c_send_byte(I2CADDR<<1); +} + +void i2c_stop(void) { + TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWSTO); + while( (TWCR & _BV(TWSTO))); +} +#endif + void Arduboy2Core::bootOLED() { +#ifdef OLED_SSD1306_I2C + TWSR = 0; + TWBR = F_CPU/(2*100000)-8; + + i2c_start(); + i2c_send_byte(0x00); + for (uint8_t i = 0; i < sizeof(lcdBootProgram); i++) + i2c_send_byte(pgm_read_byte(lcdBootProgram + i)); + i2c_stop(); + + // TWBR = F_CPU/(2*400000)-8; + TWBR = 1; // 12 = 400kHz +#else // reset the display uint8_t cmd; const void* ptr = lcdBootProgram; @@ -350,9 +445,11 @@ void Arduboy2Core::LCDCommandMode() // Initialize the SPI interface for the display void Arduboy2Core::bootSPI() { +#ifndef OLED_SSD1306_I2C // master, mode 0, MSB first, CPU clock / 2 (8MHz) SPCR = _BV(SPE) | _BV(MSTR); SPSR = _BV(SPI2X); +#endif } // Write to the SPI bus (MOSI pin) @@ -398,12 +495,16 @@ void Arduboy2Core::idle() void Arduboy2Core::bootPowerSaving() { +#ifdef OLED_SSD1306_I2C + // FIXME +#else // disable Two Wire Interface (I2C) and the ADC // All other bits will be written with 0 so will be enabled PRR0 = _BV(PRTWI) | _BV(PRADC); // disable USART1 PRR1 = _BV(PRUSART1); // All other bits will be written with 0 so will be enabled +#endif } // Shut down the display @@ -432,12 +533,32 @@ uint8_t Arduboy2Core::height() { return HEIGHT; } void Arduboy2Core::paint8Pixels(uint8_t pixels) { +#ifdef OLED_SSD1306_I2C + i2c_start(); + i2c_send_byte(0x40); + i2c_send_byte(pixels); + i2c_stop(); +#else SPItransfer(pixels); +#endif } void Arduboy2Core::paintScreen(const uint8_t *image) { -#if defined(OLED_SH1106) || defined(LCD_ST7565) +#ifdef OLED_SSD1306_I2C + // I2C + for (uint8_t i=0; i<(WIDTH*HEIGHT/(16*8));) { + // send a bunch of data in one xmission + i2c_start(); + i2c_send_byte(0x40); + for(uint8_t x=0;x<16;x++,i++) { + TWDR = pgm_read_byte(image+i); + TWCR = _BV(TWINT) | _BV(TWEN); + while( !(TWCR & _BV(TWINT))); + } + i2c_stop(); + } +#elif defined(OLED_SH1106) || defined(LCD_ST7565) for (uint8_t i = 0; i < HEIGHT / 8; i++) { LCDCommandMode(); @@ -513,7 +634,22 @@ void Arduboy2Core::paintScreen(const uint8_t *image) // will be used by any buffer based subclass void Arduboy2Core::paintScreen(uint8_t image[], bool clear) { -#if defined(OLED_SH1106) || defined(LCD_ST7565) +#ifdef OLED_SSD1306_I2C + // I2C + uint16_t i; + i2c_start(); + TWDR = 0x40; + TWCR = _BV(TWINT) | _BV(TWEN); + for (uint16_t i=0; i<(WIDTH*HEIGHT/8);i++) { + while( !(TWCR & _BV(TWINT))); + TWDR = image[i]; + TWCR = _BV(TWINT) | _BV(TWEN); + if(clear) image[i] = 0; + } + while( !(TWCR & _BV(TWINT))); + i2c_stop(); + +#elif defined(OLED_SH1106) || defined(LCD_ST7565) //Assembly optimized page mode display code with clear support. //Each byte transfer takes 18 cycles asm volatile ( @@ -685,21 +821,43 @@ void Arduboy2Core::paintScreen(uint8_t image[], bool clear) void Arduboy2Core::blank() { -#ifdef OLED_SH1106 +#ifdef OLED_SSD1306_I2C + TWSR = 0; + TWBR = F_CPU/(2*100000)-8; + + i2c_start(); + i2c_send_byte(0x00); + for (uint8_t i = 0; i < sizeof(lcdBootProgram); i++) + i2c_send_byte(pgm_read_byte(lcdBootProgram + i)); + i2c_stop(); + + // TWBR = F_CPU/(2*400000)-8; + TWBR = 1; // 12 = 400kHz +#elif defined(OLED_SH1106) for (int i = 0; i < (HEIGHT * 132) / 8; i++) #elif defined(OLED_96X96) || defined(OLED_128X96) || defined(OLED_128X128)|| defined(OLED_128X64_ON_96X96) || defined(OLED_128X64_ON_128X96) || defined(OLED_128X64_ON_128X128)|| defined(OLED_128X96_ON_128X128) || defined(OLED_96X96_ON_128X128) || defined(OLED_64X128_ON_128X128) for (int i = 0; i < (HEIGHT * WIDTH) / 2; i++) #else //OLED SSD1306 and compatibles for (int i = 0; i < (HEIGHT * WIDTH) / 8; i++) #endif + +#ifndef OLED_SSD1306_I2C SPItransfer(0x00); +#endif } void Arduboy2Core::sendLCDCommand(uint8_t command) { +#ifdef OLED_SSD1306_I2C + i2c_start(); + i2c_send_byte(0x00); + i2c_send_byte(command); + i2c_stop(); +#else LCDCommandMode(); SPItransfer(command); LCDDataMode(); +#endif } // invert the display or set to normal diff --git a/board-package-source/libraries/Arduboy2/src/Sprites.cpp b/board-package-source/libraries/Arduboy2/src/Sprites.cpp index d858944..e2b12a3 100644 --- a/board-package-source/libraries/Arduboy2/src/Sprites.cpp +++ b/board-package-source/libraries/Arduboy2/src/Sprites.cpp @@ -44,24 +44,27 @@ void Sprites::draw(int16_t x, int16_t y, if (bitmap == NULL) return; -// uint8_t width = pgm_read_byte(bitmap); -// uint8_t height = pgm_read_byte(++bitmap); -// bitmap++; -// if (frame > 0 || sprite_frame > 0) { -// frame_offset = (width * ( height / 8 + ( height % 8 == 0 ? 0 : 1))); -// // sprite plus mask uses twice as much space for each frame -// if (drawMode == SPRITE_PLUS_MASK) { -// frame_offset *= 2; -// } else if (mask != NULL) { -// mask += sprite_frame * frame_offset; -// } -// bitmap += frame * frame_offset; -// } -// // if we're detecting the draw mode then base it on whether a mask -// // was passed as a separate object -// if (drawMode == SPRITE_AUTO_MODE) { -// drawMode = mask == NULL ? SPRITE_UNMASKED : SPRITE_MASKED; -// } +#ifdef OLED_SSD1306_I2C + uint8_t width = pgm_read_byte(bitmap); + uint8_t height = pgm_read_byte(++bitmap); + bitmap++; + if (frame > 0 || sprite_frame > 0) { + frame_offset = (width * ( height / 8 + ( height % 8 == 0 ? 0 : 1))); + // sprite plus mask uses twice as much space for each frame + if (drawMode == SPRITE_PLUS_MASK) { + frame_offset *= 2; + } else if (mask != NULL) { + mask += sprite_frame * frame_offset; + } + bitmap += frame * frame_offset; + } + + // if we're detecting the draw mode then base it on whether a mask + // was passed as a separate object + if (drawMode == SPRITE_AUTO_MODE) { + drawMode = mask == NULL ? SPRITE_UNMASKED : SPRITE_MASKED; + } +#else // assembly optimisation of above code saving 20(+) bytes uint8_t width; uint8_t height; @@ -121,6 +124,9 @@ void Sprites::draw(int16_t x, int16_t y, [sprite_masked] "M" (SPRITE_MASKED) : "r20", "r21" ); + +#endif + drawBitmap(x, y, bitmap, mask, width, height, drawMode); }