Library for Modtronix im4OLED board with 128x64 OLED and 4 buttons. For details, see product page http://modtronix.com/im4oled.html. Is a clone of Adafruit_GFX library, with some additional code added.

Fork of Adafruit_GFX by Neal Horman

mx_ssd1306.cpp

Committer:
modtronix-com
Date:
2015-10-21
Revision:
23:44309099c532
Parent:
22:f63aeb3769b5

File content as of revision 23:44309099c532:

/*********************************************************************
This is a library for our Monochrome OLEDs based on SSD1306 drivers

  Pick one up today in the adafruit shop!
  ------> http://www.adafruit.com/category/63_98

These displays use SPI to communicate, 4 or 5 pins are required to  
interface

Adafruit invests time and resources providing this open source code, 
please support Adafruit and open-source hardware by purchasing 
products from Adafruit!

Written by Limor Fried/Ladyada  for Adafruit Industries.  
BSD license, check license.txt for more information
All text above, and the splash screen below must be included in any redistribution
*********************************************************************/

/*
 *  Modified by Neal Horman 7/14/2012 for use in mbed
 */
#include "mbed.h"
#include "mx_ssd1306.h"

//MODTRONIX BEGIN /////////////////////////////////////////////////////////////
#define DEBUG_ENABLE            0
#if (DEBUG_ENABLE == 1)
    extern Stream* pMxDebug;            //Define Stream for debug output in user code called pMxDebug
    #define MX_DEBUG pMxDebug->printf
#else
    #define MX_DEBUG(format, args...) ((void)0)
#endif
//MODTRONIX END ///////////////////////////////////////////////////////////////


#define SSD1306_SETLOWCOLUMN            0x00    //Only used for "Page Addresss Mode"! Column start, will wrap around to this column
#define SSD1306_SETHIGHCOLUMN           0x10    //Only used for "Page Addresss Mode"! Column end, will wrap around after this column
#define SSD1306_MEMORYMODE              0x20    //0x22=Page Address mode, 0x20=Horizontal Address Mode
//Only used for "Horizontal Address Mode"! Set Col start and end. Two command must follow, Col start(Reset=0) & Col End(Reset=127)
#define SSD1306_SET_COLUMN_ADR          0x21
//Only used for "Horizontal Address Mode"! Set Page start and end. Two command must follow, Page start(Reset=0) & Page End(Reset=7)
#define SSD1306_SET_PAGE_ADR            0x22
#define SSD1306_DISABLE_SCROLLING       0x2E
#define SSD1306_SETSTARTLINE            0x40
#define SSD1306_SETCONTRAST             0x81
#define SSD1306_CHARGEPUMP              0x8D
#define SSD1306_SEGREMAP                0xA0
#define SSD1306_DISPLAYALLON_RESUME     0xA4
#define SSD1306_DISPLAYALLON            0xA5
#define SSD1306_NORMALDISPLAY           0xA6
#define SSD1306_INVERTDISPLAY           0xA7
#define SSD1306_SETMULTIPLEX            0xA8
#define SSD1306_DISPLAYOFF              0xAE
#define SSD1306_DISPLAYON               0xAF
#define SSD1306_SET_PAGE_START_ADR      0xB0    //Only used for "Page Address Mode"! Set Page start address(Reset=0)
#define SSD1306_COMSCANINC              0xC0
#define SSD1306_COMSCANDEC              0xC8
#define SSD1306_SETDISPLAYOFFSET        0xD3
#define SSD1306_SETDISPLAYCLOCKDIV      0xD5
#define SSD1306_SETPRECHARGE            0xD9
#define SSD1306_SETCOMPINS              0xDA
#define SSD1306_SETVCOMDETECT           0xDB

uint8_t MxSSD1306::begin(uint8_t vccstate)
{
    uint8_t retVal;
#if (OLED_HAS_RESET==1)
    rst = 1;
#endif
    // VDD (3.3V) goes high at start, lets just chill for a ms
    wait_ms(1);
#if (OLED_HAS_RESET==1)
    // bring reset low
    rst = 0;
#endif
    // wait 10ms
    wait_ms(10);
#if (OLED_HAS_RESET==1)
    // bring out of reset
    rst = 1;
#endif
    // turn on VCC (9V?)

    if((retVal=command(SSD1306_DISPLAYOFF)) != 0) {
        return retVal;  //Return error code
    }
    command(SSD1306_SETDISPLAYCLOCKDIV);
    command(0x80);                                  // the suggested ratio 0x80

    command(SSD1306_SETMULTIPLEX);
    command(_rawHeight-1);

    command(SSD1306_SETDISPLAYOFFSET);
    command(0x0);                                   // no offset

    command(SSD1306_SETSTARTLINE | 0x0);            // line #0

    command(SSD1306_CHARGEPUMP);
    command((vccstate == SSD1306_EXTERNALVCC) ? 0x10 : 0x14);

    command(SSD1306_MEMORYMODE);                    // Set for "Page addressing mode"
    command(0x02);                                  // 2 = Page addressing mode

    command(SSD1306_SEGREMAP | 0x1);

    command(SSD1306_COMSCANDEC);

    command(SSD1306_SETCOMPINS);
    command(_rawHeight == 32 ? 0x02 : 0x12);        // TODO - calculate based on _rawHieght ?

    command(SSD1306_SETCONTRAST);
    command(_rawHeight == 32 ? 0x8F : ((vccstate == SSD1306_EXTERNALVCC) ? 0x9F : 0xCF) );

    command(SSD1306_SETPRECHARGE);
    command((vccstate == SSD1306_EXTERNALVCC) ? 0x22 : 0xF1);

    command(SSD1306_SETVCOMDETECT);
    command(0x40);

    command(SSD1306_DISABLE_SCROLLING);

    command(SSD1306_DISPLAYALLON_RESUME);

    command(SSD1306_NORMALDISPLAY);
    
    if((retVal=command(SSD1306_DISPLAYON)) != 0) {
        return retVal;  //Return error code
    }
    return 0;   //Success
}

// Set a single pixel
void MxSSD1306::drawPixel(int16_t x, int16_t y, uint16_t color)
{
    bool changed = false;
    uint8_t colMask;
    uint8_t mask;
    uint16_t bufAdr;

    if ((x < 0) || (x >= width()) || (y < 0) || (y >= height()))
        return;
    
    // check rotation, move pixel around if necessary
    switch (getRotation())
    {
        case 1:
            swap(x, y);
            x = _rawWidth - x - 1;
            break;
        case 2:
            x = _rawWidth - x - 1;
            y = _rawHeight - y - 1;
            break;
        case 3:
            swap(x, y);
            y = _rawHeight - y - 1;
            break;
    }  
    
    //Get address of byte in buffer
    bufAdr = x+ ((y/8)*_rawWidth);

    mask = 0x01 << (y%8);

    // x is which column
    if (color == WHITE) {
        //If bit changed
        if((buffer[bufAdr]&mask) == 0) {
            buffer[bufAdr] |= mask; //Set bit
            changed = true;
        }
    }
    // else black
    else {
        //If bit changed
        if((buffer[bufAdr]&mask) != 0) {
            buffer[bufAdr] &= ~mask;    //Clear bit
            changed = true;
        }
    }

    //Set dirty bit IF CHANGED. Each dirty[] element contains dirty bits for 8 rows(y/8).
    //Each bit is for 16 columns(x/16). For example:
    // - bit 0 of dirty[0] will be "Rows 0-7, and Columns 0-15"
    // - bit 1 of dirty[0] will be "Rows 0-7, and Columns 16-31"
    // - bit 7 of dirty[0] will be "Rows 0-7, and Columns 112-127"
    // - bit 0 of dirty[1] will be "Rows 8-15, and Columns 0-15"
    if(changed) {
        colMask = 0x01 << (x/16);
        dirty[y/8] = dirty[y/8] | colMask;  //Set the dirty bit
    }
}

/** Set display contrast
 * @return 0 if success, else I2C or SPI error code
 */
uint8_t MxSSD1306::setContrast(uint8_t contrast) {
    uint8_t retVal;
    if((retVal=command(SSD1306_SETCONTRAST)) != 0) {
        return retVal;
    }
    return command(contrast);
}

/** Turn display on or off
 * @return 0 if success, else I2C or SPI error code
 */
uint8_t MxSSD1306::displayOn(bool on) {
    return command(on ? SSD1306_DISPLAYON : SSD1306_DISPLAYOFF);
}

uint8_t MxSSD1306::invertDisplay(bool i)
{
	return command(i ? SSD1306_INVERTDISPLAY : SSD1306_NORMALDISPLAY);
}

/** Send the display buffer out to the display
 * @return 0 if success, else I2C or SPI error code
 */
uint8_t MxSSD1306::display(void)
{
    uint8_t retVal;
    static uint8_t oldColBlock = 0xfe;  //Do NOT use 0xff, seeing that is incremented by 1 below for test!
    static uint8_t oldRowBlock = 0xfe;

    // Page Address Mode //////////////////////////////////////////////////////
    // Set "Page Address Mode". This mode requires SSD1306_SETLOWCOLUMN, SSD1306_SETHIGHCOLUMN and
    // Only do this after setAllDirty() is called, which sets rowBlock to 0xf0
    if(rowBlock==0xf0) {
        rowBlock = 0;
        if ((retVal=command(SSD1306_MEMORYMODE)) != 0) {return retVal;}     //Set "Page Address Mode"
        if ((retVal=command(2)) != 0) {return retVal;}
        oldColBlock = oldRowBlock = 0xfe;   //Cause column and row command to be sent to OLED again
    }

//dirty is array of 8 x uint8_t elements
#if ((OLED_WIDTH == 128 ) && (OLED_HEIGHT==64))
    uint8_t colMask;    //Width is 128 or less. Required 8 bits or less for colMask. Each bit = 16 columns. 8 bits = 128 columns.
    uint32_t * pDirty = (uint32_t*)&dirty[0];
    //Nothing to do
    if ((pDirty[0]==0) && (pDirty[1]==0)) {
        colBlock = rowBlock = 0;    //Reset
        return 0;   //Return OK
    }

#elif (OLED_WIDTH <= 256 )
    uint16_t colMask;    //Width is 256 or less. Required 16 bits or less for colMask. Each bit = 16 columns. 16 bits = 256 columns.
    bool isDirty = false;
    for(int iDirty=0; iDirty<(OLED_HEIGHT/8); iDirty++) {
        if(dirty[iDirty] != 0) {
            isDirty=true;
            break;
        }
    }
    //Nothing to do
    if (isDirty == false) {
        colBlock = rowBlock = 0;    //Reset
        return 0;   //Return OK
    }
#else
    #error "OLED width and height not supported yet!"
#endif

    //Find dirty block!
    //Check if current block of display data is dirty. Each dirty[] element contains bits for whole
    //row(all columns). Each bit of dirty = 16 columns. For example:
    // - bit 0 of dirty[0] will be "Rows 0-7, and Columns 0-15"
    // - bit 1 of dirty[0] will be "Rows 0-7, and Columns 16-31"
    // - bit 7 of dirty[0] will be "Rows 0-7, and Columns 112-127"
    // - bit 0 of dirty[1] will be "Rows 8-15, and Columns 0-15"
    while(1) {
        colMask = 0x01 << colBlock; //Get current column
        //Found the dirty bit, break!
        if((dirty[rowBlock] & colMask) != 0) {
            //Clear the dirty bit
            dirty[rowBlock] = dirty[rowBlock] & (~colMask);
            break;
        }
        //Increment colBlock and rowBlock
        if (++colBlock >= (OLED_WIDTH/16)) {
            colBlock = 0;
            rowBlock = (rowBlock+1) % (OLED_HEIGHT/8);
        }
    }

    MX_DEBUG("\r\nOLED.display %d,%d", rowBlock, colBlock);

    //Set "Current Page" -  Required by "Page Addressing Mode" (Page is NOT auto incremented in "Page Mode")
    if (rowBlock != oldRowBlock) {
        oldRowBlock = rowBlock;
        // Set current Page. Each page = 8 rows
        if ((retVal=command(SSD1306_SET_PAGE_START_ADR | rowBlock)) != 0) {return retVal;}
    }

    //Set "Current Column" - Required by "Page Addressing Mode"
    //Not required if last written colBlock was previous one. In that case, will automatically increments (columns are auto incremented in "Page Mode")
    if (colBlock != (oldColBlock+1)) {
        uint8_t column  = colBlock * 16;
        // Column Low nibble (bits 0-3) = 0
        if ((retVal=command(SSD1306_SETLOWCOLUMN | (column & 0x0f))) != 0) {return retVal;}
        // Column High nibble (bits 4-7) = 0
        if ((retVal=command(SSD1306_SETHIGHCOLUMN | ((column>>4) & 0x0f))) != 0) {return retVal;}
    }
    oldColBlock = colBlock;

    if ((retVal=sendDisplayBuffer(rowBlock, colBlock)) != 0) {
        return retVal;  //Return error
    }

    return 0;   //Success

    //----- Alternative Method -----
    //If used, MUST ALSO update begin() function to initialize with "Horizontal Address Mode"
//    // Horizontal Address Mode ////////////////////////////////////////////////
//    //Set "Horizontal Address Mode". This mode requires SSD1306_SET_COLUMN_ADR and SSD1306_SET_PAGE_ADR
//    //commands to set start/stop column & page
//    if ((retVal=command(SSD1306_MEMORYMODE)) != 0) {return retVal;}     //Set "Horizontal Address Mode"
//    if ((retVal=command(0)) != 0) {return retVal;}
//
//    //Set column Start and End, used for "Horizontal Address Mode".
//    if ((retVal=command(SSD1306_SET_COLUMN_ADR)) != 0) {return retVal;}
//    if ((retVal=command(0)) != 0) {return retVal;}    //Column 0 - first column
//    if ((retVal=command(127)) != 0) {return retVal;}  //Column 7 - last column
//
//    //Set page Start and End, used for "Horizontal Address Mode".
//    if ((retVal=command(SSD1306_SET_PAGE_ADR)) != 0) {return retVal;}
//    if ((retVal=command(0)) != 0) {return retVal;}    //Page 0 - first page
//    if ((retVal=command(7)) != 0) {return retVal;}    //Page 7 - last page
//
//    for(rowBlock=0; rowBlock<8; rowBlock++) {
//        for(colBlock=0; colBlock<8; colBlock++) {
//            if ((retVal=sendDisplayBuffer(rowBlock, colBlock)) != 0) {
//                return retVal;  //Return error
//            }
//        }
//    }
}


void MxSSD1306::clearDisplay(void)
{
    setAllDirty();  //Set whole display as dirty

#if (OLED_USE_VECTOR==0)
    for(uint i=0; i<sizeof(buffer); i++) {
        buffer[i] = 0;
    }
#else
	std::fill(buffer.begin(),buffer.end(),0);
#endif
}

// Set whole display as being dirty
void MxSSD1306::setAllDirty(void) {
    colBlock = 0;
    rowBlock = 0xf0;    //Mark with 0xf0 so SSD1306_MEMORYMODE command is sent again in display() function
    //Set whole display as Dirty
#if (OLED_WIDTH == 128) //Each entry of dirty[] is a UINT8, and each bit is 16 columns
    memset(&dirty[0], 0xff, sizeof(dirty)); //Test - make ALL dirty
#elif (OLED_WIDTH == 256) //Each entry of dirty[] is a UINT16, and each bit is 16 columns
    memset(&dirty[0], 0xff, sizeof(dirty)); //Test - make ALL dirty
#else
    #error "setAllDirty() does not support current screen width"
#endif
}


void MxSSD1306::splash(void)
{
#if (OLED_HAS_SPLASH==1)
	uint8_t adaFruitLogo[64 * 128 / 8] =
	{ 
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
		0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x80, 0x80, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xF8, 0xE0, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80,
		0x80, 0x80, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0xFF,
		0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00,
		0x80, 0xFF, 0xFF, 0x80, 0x80, 0x00, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x8C, 0x8E, 0x84, 0x00, 0x00, 0x80, 0xF8,
		0xF8, 0xF8, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xE0, 0xE0, 0xC0, 0x80,
		0x00, 0xE0, 0xFC, 0xFE, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xC7, 0x01, 0x01,
		0x01, 0x01, 0x83, 0xFF, 0xFF, 0x00, 0x00, 0x7C, 0xFE, 0xC7, 0x01, 0x01, 0x01, 0x01, 0x83, 0xFF,
		0xFF, 0xFF, 0x00, 0x38, 0xFE, 0xC7, 0x83, 0x01, 0x01, 0x01, 0x83, 0xC7, 0xFF, 0xFF, 0x00, 0x00,
		0x01, 0xFF, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0xFF, 0x07, 0x01, 0x01, 0x01, 0x00, 0x00, 0x7F, 0xFF,
		0x80, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x7F, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x01, 0xFF,
		0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x03, 0x0F, 0x3F, 0x7F, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xC7, 0xC7, 0x8F,
		0x8F, 0x9F, 0xBF, 0xFF, 0xFF, 0xC3, 0xC0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFC, 0xFC,
		0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xF8, 0xF8, 0xF0, 0xF0, 0xE0, 0xC0, 0x00, 0x01, 0x03, 0x03, 0x03,
		0x03, 0x03, 0x01, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x03, 0x03, 0x01, 0x01,
		0x03, 0x01, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x03, 0x03, 0x01, 0x01, 0x03, 0x03, 0x00, 0x00,
		0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
		0x03, 0x03, 0x03, 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x01, 0x03, 0x01, 0x00, 0x00, 0x00, 0x03,
		0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		// 128x32^^^  128x64vvv
		0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x1F, 0x0F,
		0x87, 0xC7, 0xF7, 0xFF, 0xFF, 0x1F, 0x1F, 0x3D, 0xFC, 0xF8, 0xF8, 0xF8, 0xF8, 0x7C, 0x7D, 0xFF,
		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x3F, 0x0F, 0x07, 0x00, 0x30, 0x30, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0xFE, 0xFE, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xC0, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0xC0, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x7F, 0x3F, 0x1F,
		0x0F, 0x07, 0x1F, 0x7F, 0xFF, 0xFF, 0xF8, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xF8, 0xE0,
		0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFE, 0x00, 0x00,
		0x00, 0xFC, 0xFE, 0xFC, 0x0C, 0x06, 0x06, 0x0E, 0xFC, 0xF8, 0x00, 0x00, 0xF0, 0xF8, 0x1C, 0x0E,
		0x06, 0x06, 0x06, 0x0C, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFE, 0xFE, 0x00, 0x00, 0x00, 0x00, 0xFC,
		0xFE, 0xFC, 0x00, 0x18, 0x3C, 0x7E, 0x66, 0xE6, 0xCE, 0x84, 0x00, 0x00, 0x06, 0xFF, 0xFF, 0x06,
		0x06, 0xFC, 0xFE, 0xFC, 0x0C, 0x06, 0x06, 0x06, 0x00, 0x00, 0xFE, 0xFE, 0x00, 0x00, 0xC0, 0xF8,
		0xFC, 0x4E, 0x46, 0x46, 0x46, 0x4E, 0x7C, 0x78, 0x40, 0x18, 0x3C, 0x76, 0xE6, 0xCE, 0xCC, 0x80,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x01, 0x07, 0x0F, 0x1F, 0x1F, 0x3F, 0x3F, 0x3F, 0x3F, 0x1F, 0x0F, 0x03,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00,
		0x00, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x03, 0x07, 0x0E, 0x0C,
		0x18, 0x18, 0x0C, 0x06, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x01, 0x0F, 0x0E, 0x0C, 0x18, 0x0C, 0x0F,
		0x07, 0x01, 0x00, 0x04, 0x0E, 0x0C, 0x18, 0x0C, 0x0F, 0x07, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00,
		0x00, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x07,
		0x07, 0x0C, 0x0C, 0x18, 0x1C, 0x0C, 0x06, 0x06, 0x00, 0x04, 0x0E, 0x0C, 0x18, 0x0C, 0x0F, 0x07,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
	};

	setAllDirty();  //Set whole display as dirty

#if (OLED_USE_VECTOR==0)
    memcpy(&buffer[0], &adaFruitLogo[0], sizeof(adaFruitLogo));
#else
	std::copy(
		&adaFruitLogo[0]
		, &adaFruitLogo[0] + (_rawHeight == 32 ? sizeof(adaFruitLogo)/2 : sizeof(adaFruitLogo))
		, buffer.begin()
		);
#endif
#endif
}