SSD1308 128x64 OLED Driver with I2C interface

Dependents:   sense xadow_m0_ada_gps xadow_m0_SD_Hello sense-DHT11 ... more

See http://mbed.org/users/wim/notebook/oled-display-with-ssd1308-driver/#c6729

Revision:
2:16c84a134393
Parent:
1:b7e8f5139026
Child:
4:df92b0c0cb92
--- a/SSD1308.cpp	Sat Jul 21 12:49:33 2012 +0000
+++ b/SSD1308.cpp	Mon Jul 23 20:03:18 2012 +0000
@@ -55,7 +55,6 @@
  *@param uint8_t deviceAddress slaveaddress
  */
 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  
@@ -223,7 +222,7 @@
 #endif
 
 
-/** @brief write a progressbar to the display, Max Width is 52 pixels
+/** @brief write a progressbar to the display, Width is (PRG_MAX_SCALE + 2) pixels
  *  @param uint8_t page begin page   (0..MAX_PAGE)
  *  @param uint8_t col  begin column (0..MAX_COL)
  *  @param int percentage value      (0..100)
@@ -232,6 +231,7 @@
 #define PRG_LEFT_EDGE   0xFF
 #define PRG_RIGHT_EDGE  0xFF
 #define PRG_ACTIVE      0xFF
+//#define PRG_ACTIVE      0xBD
 #define PRG_NOT_ACTIVE  0x81
 
 #if(0)
@@ -239,10 +239,10 @@
 void SSD1308::writeProgressBar(uint8_t page, uint8_t col, int percentage) {
   uint8_t scale_value;
   
-  if (percentage < 0) {
+  if (percentage <= 0) {
     scale_value = 0;
-  } else if (percentage > 100) {
-      scale_value = PRG_MAX_SCALE;
+  } else if (percentage >= 100) {
+      scale_value = PRG_MAX_SCALE - 1;
   }
   else {
     scale_value = (percentage * PRG_MAX_SCALE) / 100; 
@@ -257,8 +257,10 @@
   for (uint8_t col = 0; col < scale_value; col++) {
       _sendData(PRG_ACTIVE);
   }
-
-  for (uint8_t col = scale_value; col < PRG_MAX_SCALE; col++) {
+      
+  _sendData(PRG_ACTIVE);
+  
+  for (uint8_t col = (scale_value+1); col < PRG_MAX_SCALE; col++) {
       _sendData(PRG_NOT_ACTIVE);
   }
 
@@ -275,10 +277,10 @@
 void SSD1308::writeProgressBar(uint8_t page, uint8_t col, int percentage) {
   uint8_t scale_value;
   
-  if (percentage < 0) {
+  if (percentage <= 0) {
     scale_value = 0;
-  } else if (percentage > 100) {
-      scale_value = PRG_MAX_SCALE;
+  } else if (percentage >= 100) {
+      scale_value = PRG_MAX_SCALE - 1 ;
   }
   else {
     scale_value = (percentage * PRG_MAX_SCALE) / 100; 
@@ -298,7 +300,9 @@
      _i2c.write(PRG_ACTIVE);  // Write Data                       
   }
 
-  for (uint8_t col = scale_value; col < PRG_MAX_SCALE; col++) {
+  _i2c.write(PRG_ACTIVE);  // Write Data                       
+     
+  for (uint8_t col = (scale_value+1); col < PRG_MAX_SCALE; col++) {
      _i2c.write(PRG_NOT_ACTIVE);  // Write Data                 
   }
 
@@ -310,8 +314,53 @@
 }
 #endif
 
+/** @brief write a level meter to the display, Width is (PRG_MAX_SCALE + 2) pixels
+ *  @param uint8_t page begin page   (0..MAX_PAGE)
+ *  @param uint8_t col  begin column (0..MAX_COL)
+ *  @param int percentage value      (0..100)
+*/
+//Optimised version
+// Save lots of I2C S,P, address and datacommands:
+// Send S, address, DATA_MODE, data, data, data,...., P
+//
+void SSD1308::writeLevelBar(uint8_t page, uint8_t col, int percentage) {
+  uint8_t scale_value;
+  
+  if (percentage <= 0) {
+    scale_value = 0;
+  } else if (percentage >= 100) {
+      scale_value = PRG_MAX_SCALE - 1;
+  }
+  else {
+    scale_value = (percentage * PRG_MAX_SCALE) / 100; 
+  }      
+      
+  //setDisplayOff();
+  setPageAddress(page, page);  
+  setColumnAddress(col, MAX_COL); 
 
+  _i2c.start();
+  _i2c.write(_writeOpcode);
+  _i2c.write(DATA_MODE);  
 
+  _i2c.write(PRG_LEFT_EDGE);  // Write Data         
+
+  for (uint8_t col = 0; col < scale_value; col++) {
+     _i2c.write(PRG_NOT_ACTIVE);  // Write Data                       
+  }
+
+  _i2c.write(PRG_ACTIVE);  // Write Data at active meterlevel
+  
+  for (uint8_t col = scale_value+1; col < PRG_MAX_SCALE; col++) {
+     _i2c.write(PRG_NOT_ACTIVE);  // Write Data                 
+  }
+
+  _i2c.write(PRG_RIGHT_EDGE);  // Write Data           
+
+  _i2c.stop();
+    
+  //setDisplayOn();
+}
 
 /** @brief Write single character to the display using the 8x8 fontable
  *  @brief Start at current cursor location
@@ -334,14 +383,16 @@
 
 
 /** @brief Write a string to the display using the 8x8 font
- *  @brief Start at selected cursor location
+ *  @brief Start at selected cursor location, text will wrap around until it is done
  *  @param uint8_t row  row number    (0...ROWS/FONT_HEIGHT)
  *  @param uint8_t col  column number (0...COLUMNS/FONT_WIDTH)
  *  @param uint16_t len number of chars in text
  *  @param const char * text pointer to text
  */
-void SSD1308::writeString(uint8_t row, uint8_t col, uint16_t len, const char * text) {
+void SSD1308::writeString(uint8_t row, uint8_t col, const char * text) {
   uint16_t index = 0;
+  uint16_t len = strlen(text);
+  
   setPageAddress(row, MAX_PAGE);
   const uint8_t col_addr = FONT8x8_WIDTH*col;
   setColumnAddress(col_addr, MAX_COL);
@@ -449,6 +500,68 @@
   
 }
 
+/** @brief Write command that has five parameters
+*/ 
+void SSD1308::_sendCommand(uint8_t command, uint8_t param1, uint8_t param2,
+                                            uint8_t param3, uint8_t param4,
+                                            uint8_t param5) {
+
+//  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.write(COMMAND_MODE);      
+  _i2c.write(param3);        // Write Param3   
+  _i2c.write(COMMAND_MODE);      
+  _i2c.write(param4);        // Write Param4   
+  _i2c.write(COMMAND_MODE);      
+  _i2c.write(param5);        // Write Param5   
+
+  _i2c.stop();
+  
+}
+
+
+/** @brief Write command that has six parameters
+*/ 
+void SSD1308::_sendCommand(uint8_t command, uint8_t param1, uint8_t param2,
+                                            uint8_t param3, uint8_t param4,
+                                            uint8_t param5, uint8_t param6) {
+
+//  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.write(COMMAND_MODE);      
+  _i2c.write(param3);        // Write Param3   
+  _i2c.write(COMMAND_MODE);      
+  _i2c.write(param4);        // Write Param4   
+  _i2c.write(COMMAND_MODE);      
+  _i2c.write(param5);        // Write Param5   
+  _i2c.write(COMMAND_MODE);      
+  _i2c.write(param6);        // Write Param6   
+
+  _i2c.stop();
+  
+}
+
+
 #if(0)
 /** @brief Write command that has multiple parameters
 */ 
@@ -504,19 +617,30 @@
 }
 
 
-
+/** @brief Set Horizontal Addressing Mode (cursor incr left-to-right, top-to-bottom)
+ * 
+ */
 void SSD1308::setHorizontalAddressingMode(){
   setMemoryAddressingMode(HORIZONTAL_ADDRESSING_MODE); 
 }
 
+/** @brief Set Vertical Addressing Mode  (cursor incr top-to-bottom, left-to-right)
+ * 
+ */
 void SSD1308::setVerticalAddressingMode() {
   setMemoryAddressingMode(VERTICAL_ADDRESSING_MODE); 
 }
 
+/** @brief Set Page Addressing Mode  (cursor incr left-to-right)
+ * 
+ */
 void SSD1308::setPageAddressingMode(){
   setMemoryAddressingMode(PAGE_ADDRESSING_MODE); 
 }
     
+/** @brief Set Addressing Mode
+ *  @param uint8_t mode 
+ */
 void SSD1308::setMemoryAddressingMode(uint8_t mode){
 
   _sendCommand(SET_MEMORY_ADDRESSING_MODE, mode);   
@@ -540,6 +664,44 @@
   _sendCommand(SET_COLUMN_ADDRESS, start, end);     
 }
 
+/** 
+ *  @brief Set Display StartLine, takes one byte, 0x00-0x3F
+ *  @param uint8_t line startline (valid range 0..MAX_ROWS)
+ */  
+void SSD1308::setDisplayStartLine(uint8_t line) {
+
+  line = line & MAX_ROW;
+   
+  _sendCommand(SET_DISPLAY_START_LINE | line);     
+}
+
+
+/** 
+ *  @brief Set Column Start (for Page Addressing Mode only)
+ *  @param uint8_t column column start (valid range 0..MAX_COL)
+ */  
+void SSD1308::setColumnStartForPageAddressingMode(uint8_t column) {
+
+  column = column & MAX_COL;
+
+  _sendCommand(SET_LOWER_COLUMN  | ( column     & 0x0F));  // lower nibble   
+  _sendCommand(SET_HIGHER_COLUMN | ((column>>4) & 0x0F));  // higher nibble     
+}
+
+
+/** 
+ *  @brief Set Page Start (for Page Addressing Mode only)
+ *  @param uint8_t page page start (valid range PAGE0 - PAGE7)
+ */    
+void SSD1308::setPageStartForPageAddressingMode(uint8_t page) {
+
+  page = page & MAX_PAGE;
+
+  _sendCommand(SET_PAGE_START_ADDRESS | page);
+ 
+}
+
+
 /** @brief Set Contrast
  *  @param uint8_t contrast (valid range 0x00 (lowest) - 0xFF (highest))
 */
@@ -651,31 +813,135 @@
 }
 
 
+/** @brief Shows All Pixels On
+ */
+void SSD1308::setEntireDisplayOn(){
+  _sendCommand(SET_ENTIRE_DISPLAY_ON); 
+}
+
+/** @brief Shows Pixels as RAM content
+ */
+void SSD1308::setEntireDisplayRAM(){
+  _sendCommand(SET_DISPLAY_GDDRAM); 
+}
+
+/** @brief Shows Pixels On or as RAM content
+ *  @param bool on (true is All on, false is RAM content)
+ */
+void SSD1308::setEntireDisplay(bool on){
+  if (on) {
+    setEntireDisplayOn();  // All Pixels on
+  }
+  else {  
+    setEntireDisplayRAM(); // Pixels are RAM content
+  }
+}
+
+
+/** @brief Horizontal scroll by one column per interval
+ *  @param bool left select Left/Right scroll
+ *  @param uint8_t start_page begin page   (0..MAX_PAGE)
+ *  @param uint8_t end_page   end page     (start_page..MAX_PAGE)                     
+ *  @param uint8_t interval   scroll interval in frames (see codes above)                      
+ */  
+void SSD1308::setContinuousHorizontalScroll(bool left, uint8_t start_page, uint8_t end_page, uint8_t interval) {
+  if (left) {
+    _sendCommand(SET_LEFT_HOR_SCROLL, 0x00, start_page, interval, end_page, 0x00, 0xFF);  // Scroll Left
+  }
+  else {  
+    _sendCommand(SET_RIGHT_HOR_SCROLL, 0x00, start_page, interval, end_page, 0x00, 0xFF); // Scroll Right  
+  }
+
+}
+
+
+/** @brief Horizontal and Vertical scroll by one column per interval
+ *  @param bool left select Left/Right scroll
+ *  @param uint8_t start_page begin page   (0..MAX_PAGE)
+ *  @param uint8_t end_page   end page     (start_page..MAX_PAGE)                     
+ *  @param uint8_t offset     vert offset  (0x01..0x63)                       
+ *  @param uint8_t interval   scroll interval in frames (see codes above)                       
+ */  
+void SSD1308::setContinuousVerticalAndHorizontalScroll(bool left, uint8_t start_page, uint8_t end_page, 
+                                                       uint8_t offset, uint8_t interval) {
+  if (left) {
+    _sendCommand(SET_VERT_LEFT_HOR_SCROLL, 0x00, start_page, interval, end_page, offset);  // Scroll Left
+  }
+  else {  
+    _sendCommand(SET_VERT_RIGHT_HOR_SCROLL, 0x00, start_page, interval, end_page, offset); // Scroll Right  
+  }
+                                                       
+}    
+
+/** @brief Set Vertical scroll area
+ *  @param uint8_t topRowsFixed      fixed rows   (0..MAX_ROW)                     
+ *  @param uint8_t scrollRowsoffset  scroll rows  (topRowsFixed..ROWS)                       
+ */  
+void SSD1308::setVerticalScrollArea(uint8_t topRowsFixed, uint8_t scrollRows) { 
+   
+  if ((topRowsFixed + scrollRows) > ROWS) {
+     scrollRows = ROWS - topRowsFixed; 
+  };
+  
+  _sendCommand(SET_VERTICAL_SCROLL_AREA, topRowsFixed, scrollRows); 
+}
+
+/** @brief Activate or Deactivate Horizontal and Vertical scroll
+ *  @brief Note: after deactivating scrolling, the RAM data needs to be rewritten
+ *  @param bool on activate scroll 
+ */  
+void SSD1308::setDisplayScroll(bool on) {
+  if (on) {
+    _sendCommand(SET_ACTIVATE_SCROLL);   // Scroll on
+  }
+  else {  
+    _sendCommand(SET_DEACTIVATE_SCROLL); // Scroll off  
+  }
+}
+
+
+
 /** @brief Low level Init
  *  @brief Init the configuration registers in accordance with the datasheet
  */
 void SSD1308::_init() {
 
-//not complete yet  
-  _sendCommand(SET_DISPLAY_POWER_OFF);
+  _sendCommand(SET_DISPLAY_POWER_OFF);      // 0xAE
   
   // 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);    
+  // row address   0 is mapped to COM0 (Reset)      
+  _sendCommand(SET_SEGMENT_REMAP_0);        // 0xA0 (Reset)
+  _sendCommand(SET_COMMON_REMAP_0);         // 0xC0 (Reset) 
+
+  setDisplayStartLine(0);                   // 0x40 (Reset) 
   
-  _sendCommand(SET_COMMON_CONF, COMMON_BASE | COMMON_ALTERNATIVE | COMMON_LEFTRIGHT_NORMAL);   
+  _sendCommand(SET_COMMON_CONF, COMMON_BASE | COMMON_ALTERNATIVE | COMMON_LEFTRIGHT_NORMAL); // 0xDA, 0x12 (Reset)
 
-  setHorizontalAddressingMode(); // (Non-Reset)
-  
-  setExternalIref(); // (Reset)
+  // Pagemode or Horizontal mode
+//  setPageAddressingMode();                  // 0x20, 0x02 (Reset)  
+//  setColumnStartForPageAddressingMode(0);   // 0x00, 0x10 (Reset = Column 0)
+//  setPageStartForPageAddressingMode(PAGE_0);// 0xBO       (Reset = Page 0)
+  setHorizontalAddressingMode();            // 0x20, 0x00 (Non-Reset)
+  setColumnAddress(0, MAX_COL);             // 0x21, 0x00, 0x37 (Reset)
+  setPageAddress(0, MAX_PAGE);              // 0x22, 0x00, 0x07 (Reset)
 
-  _sendCommand(SET_CONTRAST, 0x7F);  // (Reset)
+  setExternalIref();                        // 0xAD, 0x10 (Reset)
+  
+  _sendCommand(SET_DISPLAY_CLOCK, 0x70);    // 0xD5, 0x70 (Reset = 0x80)
+  _sendCommand(SET_PRECHARGE_TIME, 0x21);   // 0xD9, 0x21 (Reset = 0x22)
+  _sendCommand(SET_VCOMH_DESELECT_LEVEL, 0x30); // 0xDB, 0x30 (Reset = 0x20)  
+  _sendCommand(SET_MULTIPLEX_RATIO, 0x3F);  // 0xA8, 0x3F (Reset)  
+  _sendCommand(SET_DISPLAY_OFFSET, 0x00);   // 0xD3, 0x00 (Reset)  
+  
+  _sendCommand(SET_CONTRAST, 0x7F);         // 0x81, 0x7F (Reset)
 
-  _sendCommand(SET_NORMAL_DISPLAY);  
+  _sendCommand(SET_NORMAL_DISPLAY);         // 0xA6 (Reset)
+  
+  setEntireDisplayRAM();                    // 0xA4 (Reset)
+  setDisplayScroll(false);
   
   clearDisplay();   
   
-  _sendCommand(SET_DISPLAY_POWER_ON);
+  _sendCommand(SET_DISPLAY_POWER_ON);       // 0xAF
 }