Demonstration of SSD1308 OLED driver library

Dependencies:   mbed SSD1308_128x64_I2C

SSD1308.cpp

Committer:
wim
Date:
2012-07-09
Revision:
2:d86478c0f5da
Parent:
1:00053cb70ac5
Child:
3:1337e3d65ed0

File content as of revision 2:d86478c0f5da:

// SSD1308 I2C device class file
//   Based on Solomon Systech SSD1308 datasheet, rev. 1, 10/2008
//   The SSD1308 is used for example in the Seeed 128x64 OLED Display
//   http://www.seeedstudio.com/depot/grove-oled-display-12864-p-781.html?cPath=163_167
//
// The original code is using (and has been submitted as a part of) Jeff Rowberg's I2Cdevlib library,
// which should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib
//
// Changelog:
//   2011-08-25 - Initial release by Andrew Schamp <schamp@gmail.com>
//   2012-06-19 - Ported to mbed and optimised (WH)
//       
/* ============================================
I2Cdev device library code is placed under the MIT license
Copyright (c) 2011 Andrew Schamp
Copyright (c) 2012 WH (mbed port)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
===============================================
*/
#include "mbed.h"
#include "SSD1308.h"

//#define SSD1308_USE_FONT
//#ifdef SSD1308_USE_FONT
#include "FixedWidthFont.h"
//#endif

//Constructor
//
SSD1308::SSD1308(I2C &i2c, uint8_t deviceAddress) : _i2c(i2c) {
//  m_devAddr = deviceAddress; 
  
  _writeOpcode = deviceAddress & 0xFE; // low order bit = 0 for write
  _readOpcode  = deviceAddress | 0x01; // low order bit = 1 for read  
  
  initialize(); 
}

// High level Init, most settings remain at Power-On reset value
//
void SSD1308::initialize() {
  setHorizontalAddressingMode();
  clearDisplay();
}


#if(0)
// Standard version
void SSD1308::clearDisplay() {
 
  //setDisplayOff();
  setPageAddress(0, MAX_PAGE);  // all pages
  setColumnAddress(0, MAX_COL); // all columns

  for (uint8_t page = 0; page < PAGES; page++) {
    for (uint8_t col = 0; col < COLUMNS; col++) {
      sendData(0x00);
    }
  }

  //setDisplayOn();
}
#else
//Optimised version
// Save lots of I2C S,P, address and datacommands:
// Send S, address, DATA_MODE, data, data, data,...., P
//
void SSD1308::clearDisplay() {

  //setDisplayOff();
  
  setPageAddress(0, MAX_PAGE);  // all pages
  setColumnAddress(0, MAX_COL); // all columns

  _i2c.start();
  _i2c.write(_writeOpcode);
  _i2c.write(DATA_MODE);  
  for (int i=0; i<(PAGES * COLUMNS); i++) {
    _i2c.write(0x00);  // Write Data   
  }
  _i2c.stop();

  //setDisplayOn();
}
#endif


#if(0)
//Standard version
void SSD1308::fillDisplay(uint8_t pattern) {
  
  //setDisplayOff();  
  
  setPageAddress(0, MAX_PAGE);  // all pages
  setColumnAddress(0, MAX_COL); // all columns

  for (uint8_t page = 0; page < PAGES; page++) {
    for (uint8_t col = 0; col < COLUMNS; col++) {
      sendData(pattern); 
    }
  }
 
  //setDisplayOn();  
}
#else
//Optimised version
// Save lots of I2C S,P, address and datacommands:
// Send S, address, DATA_MODE, data, data, data,...., P
//
void SSD1308::fillDisplay(uint8_t pattern) {

  //setDisplayOff();
  setPageAddress(0, MAX_PAGE);  // all pages
  setColumnAddress(0, MAX_COL); // all columns

  _i2c.start();
  _i2c.write(_writeOpcode);
  _i2c.write(DATA_MODE);  
  for (int i=0; i<(PAGES * COLUMNS); i++) {
    _i2c.write(pattern);  // Write Data   
  }
  _i2c.stop();

  //setDisplayOn();
}
#endif


void SSD1308::writeBitmap(int len, uint8_t* data) {

  //setDisplayOff();
  setPageAddress(0, MAX_PAGE);  // all pages
  setColumnAddress(0, MAX_COL); // all columns

  _i2c.start();
  _i2c.write(_writeOpcode);
  _i2c.write(DATA_MODE);  
  for (int i=0; i<len; i++) {
    _i2c.write(data[i]);  // Write Data   
  }
  _i2c.stop();

  //setDisplayOn();
}


// Write single character to the display using the 8x8 fontable
// Start at current cursor location
//     char chr character
//     bool inverted invert pixels
void SSD1308::writeChar(char chr, bool inverted) {

  const uint8_t char_index = chr - 0x20;
  for (uint8_t i = 0; i < 8; i++) {
//     const uint8_t b = pgm_read_byte( &fontData[char_index][i] );
//     const uint8_t b = fontData[char_index][i];     
//     sendData( b ); 
     if (inverted) {
       sendData( ~fontData[char_index][i] );           
     }
     else {
       sendData( fontData[char_index][i] );
     }  
  }

}


// Write a string to the display using the 8x8 fontable
// Start at current cursor location
void SSD1308::writeString(uint8_t row, uint8_t col, uint16_t len, const char * text, bool inverted) {
  uint16_t index = 0;
  setPageAddress(row, MAX_PAGE);
  const uint8_t col_addr = FONT_WIDTH*col;
  setColumnAddress(col_addr, MAX_COL);

  while ((col+index) < CHARS && (index < len)) {
     // write first line, starting at given position
     writeChar(text[index++], inverted);
  }

  // write remaining lines
  // write until the end of memory
  // then wrap around again from the top.
  if (index + 1 < len) {
    setPageAddress(row + 1, MAX_PAGE);
    setColumnAddress(0, MAX_COL);
    bool wrapEntireScreen = false;
    while (index + 1 < len) {
       writeChar(text[index++], inverted);
       // if we've written the last character space on the screen, 
       // reset the page and column address so that it wraps around from the top again
       if (!wrapEntireScreen && (row*CHARS + col + index) > 127) {
         setPageAddress(0, MAX_PAGE);
         setColumnAddress(0, MAX_COL);
         wrapEntireScreen = true;
       }
    }
  }
}

// Write command that has no parameters
// 
void SSD1308::sendCommand(uint8_t command) {
//  I2Cdev::writeByte(m_devAddr, COMMAND_MODE, command);

  char databytes[2];
    
  databytes[0] = COMMAND_MODE;
  databytes[1] = command;    
  _i2c.write(_writeOpcode, databytes, 2);    // Write command   

}

// Write command that has one parameter
// 
void SSD1308::sendCommand(uint8_t command, uint8_t param1) {

//  Note continuationbit is set, so COMMAND_MODE must be
//  repeated before each databyte that serves as parameter!

  _i2c.start();
  _i2c.write(_writeOpcode);
  
  _i2c.write(COMMAND_MODE);      
  _i2c.write(command);       // Write Command   
  _i2c.write(COMMAND_MODE);      
  _i2c.write(param1);        // Write Param1   

  _i2c.stop();
  
}

// Write command that has two parameters
// 
void SSD1308::sendCommand(uint8_t command, uint8_t param1, uint8_t param2) {

//  Note continuationbit is set, so COMMAND_MODE must be
//  repeated before each databyte that serves as parameter!

  _i2c.start();
  _i2c.write(_writeOpcode);
  
  _i2c.write(COMMAND_MODE);      
  _i2c.write(command);       // Write Command   
  _i2c.write(COMMAND_MODE);      
  _i2c.write(param1);        // Write Param1   
  _i2c.write(COMMAND_MODE);      
  _i2c.write(param2);        // Write Param2   

  _i2c.stop();
  
}



// Write command that has multiple parameters
// 
void SSD1308::sendCommands(uint8_t len, uint8_t* commands) {

//  I2Cdev::writeBytes(m_devAddr, COMMAND_MODE, len, commands);
//  Note this original code is not correct, continuationbit is set, 
//  so COMMAND_MODE must be repeated before each databyte that serves as parameter!

  _i2c.start();
  _i2c.write(_writeOpcode);
  
  for (int i=0; i<len ; i++) {
    _i2c.write(COMMAND_MODE);      
    _i2c.write(commands[i]);  // Write Commands   
  }
  _i2c.stop();
  
}

// Write databyte to display
// Start at current cursor location
void SSD1308::sendData(uint8_t data){
//  I2Cdev::writeByte(m_devAddr, DATA_MODE, data);

  char databytes[2];
    
  databytes[0] = DATA_MODE;
  databytes[1] = data;    
  _i2c.write(_writeOpcode, databytes, 2);    // Write Data   
  
}

// Write len bytes from buffer data to display
// Start at current cursor location
void SSD1308::sendData(uint8_t len, uint8_t* data) {
//  I2Cdev::writeBytes(m_devAddr, DATA_MODE, len, data);
  
  _i2c.start();
  _i2c.write(_writeOpcode);
  _i2c.write(DATA_MODE);  
  for (int i=0; i<len ; i++) {
    _i2c.write(data[i]);  // Write Data   
  }
  _i2c.stop();
    
}



void SSD1308::setHorizontalAddressingMode(){
  setMemoryAddressingMode(HORIZONTAL_ADDRESSING_MODE); 
}

void SSD1308::setVerticalAddressingMode() {
  setMemoryAddressingMode(VERTICAL_ADDRESSING_MODE); 
}

void SSD1308::setPageAddressingMode(){
  setMemoryAddressingMode(PAGE_ADDRESSING_MODE); 
}
    
void SSD1308::setMemoryAddressingMode(uint8_t mode){
  uint8_t cmds[2] = { SET_MEMORY_ADDRESSING_MODE, mode };
  sendCommands(2, cmds); 
}

void SSD1308::setPageAddress(uint8_t start, uint8_t end) {
  uint8_t data[3] = { SET_PAGE_ADDRESS, start, end };
  sendCommands(3, data);  
}

void SSD1308::setColumnAddress(uint8_t start, uint8_t end) {
  uint8_t data[3] = { SET_COLUMN_ADDRESS, start, end };
  sendCommands(3, data);  
}


// takes one byte, 0x00 (lowest) - 0xFF (highest)
void SSD1308::setContrastControl(uint8_t contrast) {
//  uint8_t cmds[2] = { SET_CONTRAST, contrast };
//  sendCommands(2, cmds); 
  
    sendCommand(SET_CONTRAST, contrast);  
} 

// Enable Display
// 
void SSD1308::setDisplayOn() {
  sendCommand(SET_DISPLAY_POWER_ON);
}

// Disable Display
// 
void SSD1308::setDisplayOff() {
  sendCommand(SET_DISPLAY_POWER_OFF);
}

void SSD1308::setDisplayPower(bool on) {
  if (on) {
    setDisplayOn();
  } else {
    setDisplayOff();
  }
}

// White on Black background
// 
void SSD1308::setDisplayNormal() {
  sendCommand(SET_NORMAL_DISPLAY);
}

// Black on White background
// 
void SSD1308::setDisplayInverse() {
  sendCommand(SET_INVERSE_DISPLAY);
}


// Display Flip (Left/Right, Up/Down)
// 
void SSD1308::setDisplayFlip(bool left, bool down) {
  if (left) {
    // column address   0 is mapped to SEG0 (Reset)    
    sendCommand(SET_SEGMENT_REMAP_0);
  }
  else {
    // column address 127 is mapped to SEG0    
    sendCommand(SET_SEGMENT_REMAP_127);
  }  

  if (down) {
    // Reset mode
    sendCommand(SET_COMMON_REMAP_0);    
  }
  else {
    // Flip Up/Down (Need to rewrite display before H effect shows)
    sendCommand(SET_COMMON_REMAP_63);        
  }  

}

// Sets Internal Iref
//
void SSD1308::setInternalIref() {
  uint8_t cmds[2] = {SET_IREF_SELECTION, INTERNAL_IREF};
  sendCommands(2, cmds); 
}

// Sets External Iref
//
void SSD1308::setExternalIref() {
  uint8_t cmds[2] = {SET_IREF_SELECTION, EXTERNAL_IREF};
  sendCommands(2, cmds); 
}


// Low level Init
// Init the configuration registers in accordance with the datasheet
//
void SSD1308::_init() {

//not complete yet  
  sendCommand(SET_DISPLAY_POWER_OFF);
  
  // column address   0 is mapped to SEG0 (Reset)    
  // row address   0 is mapped to COMM0 (Reset)      
  sendCommand(SET_SEGMENT_REMAP_0);
  sendCommand(SET_COMMON_REMAP_0);    
  
  uint8_t cmds[2] = { SET_COMMON_CONF, COMMON_BASE | COMMON_ALTERNATIVE | COMMON_LEFTRIGHT_NORMAL};
  sendCommands(2, cmds); 

  setHorizontalAddressingMode();
  
  setExternalIref();
    
  sendCommand(SET_NORMAL_DISPLAY);  
  
  clearDisplay();   
  
  sendCommand(SET_DISPLAY_POWER_ON);
}