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.h

Committer:
modtronix-com
Date:
2016-08-19
Revision:
27:dd7d538d3849
Parent:
26:ef08580c35df

File content as of revision 27:dd7d538d3849:

/*********************************************************************
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 must be included in any redistribution
*********************************************************************/

/*
 *  Modified by Neal Horman 7/14/2012 for use in mbed
 */

#ifndef _ADAFRUIT_SSD1306_H_
#define _ADAFRUIT_SSD1306_H_
#include "im4oled_default_config.h"
#include "mx_gfx.h"

#if (OLED_USE_VECTOR==1)
#include <vector>
#endif
#include <algorithm>

#define OLED_HAS_RESET      0

// A DigitalOut sub-class that provides a constructed default state
class DigitalOut2 : public DigitalOut
{
public:
	DigitalOut2(PinName pin, bool active = false) : DigitalOut(pin) { write(active); };
	DigitalOut2& operator= (int value) { write(value); return *this; };
	DigitalOut2& operator= (DigitalOut2& rhs) { write(rhs.read()); return *this; };
	operator int() { return read(); };
};

#define SSD1306_EXTERNALVCC 0x1
#define SSD1306_SWITCHCAPVCC 0x2

/** The pure base class for the SSD1306 display driver.
 *
 * You should derive from this for a new transport interface type,
 * such as the SPI and I2C drivers.
 */
class MxSSD1306 : public MxGfx
{
public:
#if (OLED_HAS_RESET==1)
	MxSSD1306(PinName RST, uint8_t rawHeight = 32, uint8_t rawWidth = 128)
		: MxGfx(rawWidth,rawHeight)
        , colBlock(0)
        , rowBlock(0)
		, rst(RST,false)
#else
    MxSSD1306(uint8_t rawHeight = 32, uint8_t rawWidth = 128)
        : MxGfx(rawWidth,rawHeight)
        , colBlock(0)
        , rowBlock(0)
#endif
	{
	    //Initialize as all dirty
	    memset(&dirty[0], 0xff, sizeof(dirty));

#if (OLED_USE_VECTOR==0)
	    memset(&buffer[0], 0, sizeof(buffer));
#else
	    buffer.resize(rawHeight * rawWidth / 8);
#endif
	};

    /** Initialize
     * @return 0 if success, else I2C or SPI error code
     */
	uint8_t begin(uint8_t switchvcc = SSD1306_SWITCHCAPVCC);
	
	// These must be implemented in the derived transport driver
	virtual uint8_t command(uint8_t c) = 0;
	virtual uint8_t data(uint8_t c) = 0;
	virtual void drawPixel(int16_t x, int16_t y, uint16_t color);

	/**
	 * Clear the display buffer. Requires a display() call at some point afterwards.
	 * NOTE that this function will make the WHOLE display as dirty! The next display() call will update the
	 * entire display! This can be prevented by just clearing the required part of the display using fillRect()!
	 */
	void clearDisplay(void);

    /** Set display contrast
     * @return 0 if success, else I2C or SPI error code
     */
    virtual uint8_t setContrast(uint8_t contrast);

    /** Turn display on or off
     * @return 0 if success, else I2C or SPI error code
     */
    virtual uint8_t displayOn(bool on);

    /** Invert Display
	 * @return 0 if success, else I2C or SPI error code
	 */
	virtual uint8_t invertDisplay(bool i);

	/** Cause the display to be updated with the buffer content.
	 * @return 0 if success, else I2C or SPI error code
	 */
	uint8_t display();

    // Fill the buffer with the AdaFruit splash screen.
	virtual void splash();
    
protected:
    /** Write contents of display buffer to OLED display.
     * @return 0 if success, else I2C or SPI error code
     */
	virtual uint8_t sendDisplayBuffer() = 0;

    /** Write a block of display data. The 128x64 pixels are divided into:
     * - 8 RowBlocks, each with 8 rows. This is 1 page of the SSD1206
     * - 8 ColBlocks, each with 16 columns.
     *
     * @param rowBlock Value 0-7 indicating what block of 8 rows to write. 0=0-7,
     *        1=8-15, ...., 7=56-63
     * @param colBlock Value 0-7 indicating what block of 16 columns to write. 0=0-15,
     *        1=16-31, ...., 7=112-127
     * @return 0 if success, else I2C or SPI error code
     */
    virtual uint8_t sendDisplayBuffer(uint8_t rowBlock, uint8_t colBlock) = 0;

public:
    // Set whole display as being dirty
    virtual void setAllDirty();

    // Protected Data
protected:
    uint8_t colBlock, rowBlock;

#if (OLED_WIDTH <= 128 )
    uint8_t dirty[OLED_HEIGHT/8];   //Each bit marks block of "8 Rows x 16 Columns". So, a single byte is enough for up to 128col. One byte for each 8 rows.
#elif (OLED_WIDTH <= 256 )
    uint16_t dirty[OLED_HEIGHT/8];  //Each bit marks block of "8 Rows x 16 Columns". One UINT16 = 16x16 = 256 columns.
#endif

#if (OLED_HAS_RESET==1)
	DigitalOut2 rst;
#endif

	// the memory buffer for the LCD
#if (OLED_USE_VECTOR==1)
    std::vector<uint8_t> buffer;
#else
    uint8_t buffer[OLED_HEIGHT * OLED_WIDTH / 8];
#endif
};


/** This is the SPI SSD1306 display driver transport class
 *
 */
class MxSSD1306_SPI : public MxSSD1306
{
public:
	/** Create a SSD1306 SPI transport display driver instance with the specified DC, RST, and CS pins, as well as the display dimentions
	 *
	 * Required parameters
	 * @param spi - a reference to an initialized SPI object
	 * @param DC (Data/Command) pin name
	 * @param RST (Reset) pin name
	 * @param CS (Chip Select) pin name
	 *
	 * Optional parameters
	 * @param rawHeight - the vertical number of pixels for the display, defaults to 32
	 * @param rawWidth - the horizonal number of pixels for the display, defaults to 128
	 */
#if (OLED_HAS_RESET==1)
    MxSSD1306_SPI(SPI &spi, PinName DC, PinName RST, PinName CS, uint8_t rawHieght = 32, uint8_t rawWidth = 128)
	    : MxSSD1306(RST, rawHieght, rawWidth)
#else
    MxSSD1306_SPI(SPI &spi, PinName DC, PinName CS, uint8_t rawHieght = 32, uint8_t rawWidth = 128)
        : MxSSD1306(rawHieght, rawWidth)
#endif
	    , cs(CS,true)
	    , dc(DC,false)
	    , mspi(spi)
	    {
		    begin();
		    splash();
		    display();
	    };

    /** Send command via SPI
     * @param c The command to send
     * @return 0 if success, else SPI error
     */
	virtual uint8_t command(uint8_t c)
	{
	    cs = 1;
	    dc = 0;
	    cs = 0;
	    mspi.write(c);
	    cs = 1;
	    return 0;
	};

    /** Send Data via SPI
     * @param c The data to send
     * @return 0 if success, else SPI error
     */
	virtual uint8_t data(uint8_t c)
	{
	    cs = 1;
	    dc = 1;
	    cs = 0;
	    mspi.write(c);
	    cs = 1;
	    return 0;
	};

protected:
	virtual uint8_t sendDisplayBuffer()
	{
	    uint8_t retVal;
		cs = 1;
		dc = 1;
		cs = 0;

#if (OLED_USE_VECTOR==0)
		for(uint16_t i=0, q=sizeof(buffer); i<q; i++) {
#else
	    for(uint16_t i=0, q=buffer.size(); i<q; i++) {
#endif
			if((retVal=mspi.write(buffer[i])) != 0) {
			    cs = 1;
			    return retVal;
			}
		}

		if(height() == 32)
		{
#if (OLED_USE_VECTOR==0)
		    for(uint16_t i=0, q=sizeof(buffer); i<q; i++) {
#else
	        for(uint16_t i=0, q=buffer.size(); i<q; i++) {
#endif
		        if((retVal=mspi.write(0)) != 0) {
	                cs = 1;
	                return retVal;
	            }
			}
		}

		cs = 1;
		return 0;
	};

	DigitalOut2 cs, dc;
	SPI &mspi;
};

/** This is the I2C SSD1306 display driver transport class
 *
 */
class MxSSD1306_I2C : public MxSSD1306
{
public:
	#define SSD_I2C_ADDRESS     0x78
	/** Create a SSD1306 I2C transport display driver instance with the specified RST pin name, the I2C address, as well as the display dimensions
	 *
	 * Required parameters
	 * @param i2c - A reference to an initialized I2C object
	 * @param RST - The Reset pin name
	 *
	 * Optional parameters
	 * @param i2cAddress - The i2c address of the display
	 * @param rawHeight - The vertical number of pixels for the display, defaults to 32
	 * @param rawWidth - The horizonal number of pixels for the display, defaults to 128
	 */
#if (OLED_HAS_RESET==1)
    Adafruit_SSD1306_I2c(I2C &i2c, PinName RST, uint8_t i2cAddress = SSD_I2C_ADDRESS, uint8_t rawHeight = 32, uint8_t rawWidth = 128)
	    : MxSSD1306(RST, rawHeight, rawWidth)
#else
    MxSSD1306_I2C(I2C &i2c, uint8_t i2cAddress = SSD_I2C_ADDRESS, uint8_t rawHeight = 32, uint8_t rawWidth = 128)
        : MxSSD1306(rawHeight, rawWidth)
#endif
        , mi2c(i2c)
	    , mi2cAddress(i2cAddress)
    {
        begin();
        splash();
        display();
    };


    /**Constructor without doing any initialization requiring I2C object. Use this constructor if the I2C object must still be
     * initialized, or used after startup delay.
     * !!!!! IMPORTANT !!!!!
     * This constructor must be followed by calling the init() function BEFORE using any other functions!
     *
     * Required parameters
     * @param i2cAddress - The i2c address of the display
     * @param i2c - A reference to an initialized I2C object
     *
     * Optional parameters
     * @param rawHeight - The vertical number of pixels for the display, defaults to 32
     * @param rawWidth - The horizonal number of pixels for the display, defaults to 128
     */
    MxSSD1306_I2C(uint8_t i2cAddress, I2C &i2c, uint8_t rawHeight = 32, uint8_t rawWidth = 128)
        : MxSSD1306(rawHeight, rawWidth), mi2c(i2c), mi2cAddress(i2cAddress)
    {
    };


    /**
     * Initialize with given I2C bus.
     * !!!!! IMPORTANT !!!!!
     * This function must be called after the Adafruit_SSD1306_I2c(rawHeight, rawWidth) constructor!
     *
     * @param i2c I2C Bus to use
     * @return 0 if OK, else error code
     */
    uint8_t init()
    {
        uint8_t retVal;
        if((retVal=begin()) != 0) {
            return retVal;  //Return error code
        }

        //splash();
        if((retVal=display()) != 0) {
            return retVal;  //Return error code
        }
        return 0;
    }

    /** Send command via I2C
     * @param c The command to send
     * @return 0 if success, else I2C error
     */
    virtual uint8_t command(uint8_t c)
	{
		char buff[2];
		buff[0] = 0; // Command Mode
		buff[1] = c;
		return mi2c.write(mi2cAddress, buff, sizeof(buff));
	}

    /** Send Data via I2C
     * @param c The data to send
     * @return 0 if success, else I2C error
     */
	virtual uint8_t data(uint8_t c)
	{
		char buff[2];
		buff[0] = 0x40; // Data Mode
		buff[1] = c;
		return mi2c.write(mi2cAddress, buff, sizeof(buff));
	};

protected:
    virtual uint8_t sendDisplayBuffer()
	{
        char buff[17];
        buff[0] = 0x40; // Data Mode

        // send display buffer in 16 byte chunks
#if (OLED_USE_VECTOR==0)
        for(uint16_t i=0, q=sizeof(buffer); i<q; i+=16 ) {
#else
        for(uint16_t i=0, q=buffer.size(); i<q; i+=16 ) {
#endif
            uint8_t retVal;
            uint8_t x;

            for(x=1; x<sizeof(buff); x++) {
                buff[x] = buffer[i+x-1];
            }

            if((retVal=mi2c.write(mi2cAddress, buff, sizeof(buff))) != 0) {
                return retVal;  //Return error code
            }
        }
        return 0;
	};

    /** Write a block of display data. The 128x64 pixels are divided into:
     * - 8 RowBlocks, each with 8 rows. This is 1 page of the SSD1206
     * - 8 ColBlocks, each with 16 columns.
     *
     * @param rowBlock Value 0-7 indicating what block of 8 rows to write. 0=0-7,
     *        1=8-15, ...., 7=56-63
     * @param colBlock Value 0-7 indicating what block of 16 columns to write. 0=0-15,
     *        1=16-31, ...., 7=112-127
     * @return 0 if success, else I2C or SPI error code
     */
    uint8_t sendDisplayBuffer(uint8_t rowBlock, uint8_t colBlock) {
        uint8_t retVal;
        int idxBuffer;
        char buff[17];

        buff[0] = 0x40; // Data Mode

        //Each byte of buffer contains 8pixels for single column, and 8 rows. For example:
        //buffer[0] contains row 0-7 for column 0
        //buffer[1] contains row 0-7 for column 1
        idxBuffer = (rowBlock*128) + (colBlock*16);

        // Copy requested "row block" and "column block"
        for(uint16_t i=0; i<16; i++) {
            buff[i+1] = buffer[idxBuffer+i];
        }

        //Write all display data
        if((retVal=mi2c.write(mi2cAddress, buff, 17)) != 0) {
            return retVal;  //Return error code
        }

        return 0;
    }

	I2C &mi2c;
	uint8_t mi2cAddress;
};

#endif