A fork with some I2C optimizations that speed up the display.

Fork of SSD1308_128x64_I2C by Wim Huiskamp

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers SSD1308.cpp Source File

SSD1308.cpp

00001 /**  @file SSD1308 I2C device class file
00002  *   Based on Solomon Systech SSD1308 datasheet, rev. 1, 10/2008
00003  *   The SSD1308 is used for example in the Seeed 128x64 OLED Display
00004  *   http://www.seeedstudio.com/depot/grove-oled-display-12864-p-781.html?cPath=163_167
00005 */
00006 // The original code by Andrew Schamp is using (and has been submitted as a part of) Jeff Rowberg's I2Cdevlib library,
00007 // which should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib
00008 // Some parts also mashed up from Graphic Library for driving monochrome displays based on the PCD8544,
00009 // Copyright (c) 2011, Wim De Roeve, who in turn did partial port of code found on
00010 // http://serdisplib.sourceforge.net/ser/pcd8544.html#links and by Petras Saduikis <petras@petras.co.uk>
00011 //
00012 // Changelog:
00013 //   2011-08-25 - Initial release by Andrew Schamp <schamp@gmail.com>
00014 //   2012-06-19 - Ported to mbed and optimised (WH)
00015 //   2013-07-12 - Minor comment fix and placeholder for SSD1306 (WH)
00016 //   2015-01-01 - Switch for optimised I2C calls to test on F401 (WH)
00017 //       
00018 /* 
00019 ================================================================================
00020 I2Cdev device library code is placed under the MIT license
00021 Copyright (c) 2011 Andrew Schamp
00022 Copyright (c) 2012,2013 WH (mbed port)
00023 
00024 Permission is hereby granted, free of charge, to any person obtaining a copy
00025 of this software and associated documentation files (the "Software"), to deal
00026 in the Software without restriction, including without limitation the rights
00027 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00028 copies of the Software, and to permit persons to whom the Software is
00029 furnished to do so, subject to the following conditions:
00030 
00031 The above copyright notice and this permission notice shall be included in
00032 all copies or substantial portions of the Software.
00033 
00034 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00035 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00036 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00037 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00038 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00039 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
00040 THE SOFTWARE.
00041 ================================================================================
00042 */
00043 #include "mbed.h"
00044 #include "SSD1308.h"
00045 
00046 //#include "font_3x5.h"
00047 //#include "font_5x7.h"
00048 //#include "font_6x8.h"
00049 #include "font_8x8.h"
00050 //#include "font_8x12.h"
00051 //#include "font_16x20.h"
00052 #include "font_16x24.h"
00053 
00054 #if (defined(TARGET_LPC1768)) || (defined(TARGET_LPC11UXX) || defined(TARGET_LPC11U6X) || defined(TARGET_LPC1347) || defined(TARGET_LPC1549))
00055 #define I2C_OPTIMIZE   1
00056 #else
00057 #define I2C_OPTIMIZE   0
00058 #endif
00059 
00060 /**
00061  *@brief Constructor
00062  *@param I2C &i2c reference to i2c
00063  *@param uint8_t deviceAddress slaveaddress
00064  */
00065 SSD1308::SSD1308(I2C &i2c, uint8_t deviceAddress) : _i2c(i2c) {
00066   
00067   _writeOpcode = deviceAddress & 0xFE; // low order bit = 0 for write
00068   _readOpcode  = deviceAddress | 0x01; // low order bit = 1 for read  
00069   
00070   initialize(); 
00071 }
00072 
00073 /** @brief High level Init, most settings remain at Power-On reset value
00074  */
00075 void SSD1308::initialize() {
00076   setHorizontalAddressingMode();
00077 
00078   clearDisplay();
00079 
00080   setInverted(false);
00081   
00082   setDisplayOn();  
00083 }
00084 
00085 
00086 /** @brief clear the display
00087 */
00088 #if (I2C_OPTIMIZE == 0)
00089 // Standard version
00090 void SSD1308::clearDisplay() {
00091  
00092   //setDisplayOff();
00093   setPageAddress (0, MAX_PAGE);  // all pages
00094   setColumnAddress (0, MAX_COL); // all columns
00095 
00096   for (uint8_t page = 0; page < PAGES; page++) {
00097     for (uint8_t col = 0; col < COLUMNS; col++) {
00098       _sendData(0x00);
00099     }
00100   }
00101 
00102   //setDisplayOn();
00103 }
00104 #else
00105 //Optimised version
00106 // Save lots of I2C S,P, address and datacommands:
00107 // Send S, address, DATA_MODE, data, data, data,...., P
00108 //
00109 void SSD1308::clearDisplay() {
00110 
00111   //setDisplayOff();
00112   
00113   setPageAddress (0, MAX_PAGE);  // all pages
00114   setColumnAddress (0, MAX_COL); // all columns
00115 
00116   _i2c.start();
00117   _i2c.write(_writeOpcode);
00118   _i2c.write(DATA_MODE);  
00119   for (int i=0; i<(PAGES * COLUMNS); i++) {
00120     _i2c.write(0x00);  // Write Data   
00121   }
00122   _i2c.stop();
00123 
00124   //setDisplayOn();
00125 }
00126 #endif
00127 
00128 
00129 /** @brief fill the display
00130  *  @param uint8_t pattern fillpattern vertical patch or 8 bits 
00131  *  @param uint8_t start_page begin page   (0..MAX_PAGE)
00132  *  @param uint8_t end_page   end page     (start_page..MAX_PAGE)                     
00133  *  @param uint8_t start_col  begin column (0..MAX_COL)
00134  *  @param uint8_t end_col    end column   (start_col..MAX_COL)
00135 */
00136 #if (I2C_OPTIMIZE == 0)
00137 //Standard version
00138 
00139 void SSD1308::fillDisplay(uint8_t pattern,
00140                           uint8_t start_page, uint8_t end_page,
00141                           uint8_t start_col, uint8_t end_col) {
00142   
00143   int count = (end_page - start_page + 1) * (end_col - start_col + 1);
00144   
00145   //setDisplayOff();
00146   setPageAddress (start_page, end_page);  // set page window
00147   setColumnAddress (start_col, end_col);  // set column window
00148  
00149   for (int i=0; i<count; i++) {
00150     _sendData(pattern); // Write Data    
00151   }
00152 
00153   //setDisplayOn();
00154 }
00155 
00156 #else
00157 
00158 //Optimised version
00159 // Save lots of I2C S,P, address and datacommands:
00160 // Send S, address, DATA_MODE, data, data, data,...., P
00161 //
00162 void SSD1308::fillDisplay(uint8_t pattern,
00163                           uint8_t start_page, uint8_t end_page,
00164                           uint8_t start_col, uint8_t end_col) {
00165   
00166   int count = (end_page - start_page + 1) * (end_col - start_col + 1);
00167   
00168   //setDisplayOff();
00169   setPageAddress (start_page, end_page);  // set page window
00170   setColumnAddress (start_col, end_col);  // set column window
00171  
00172   _i2c.start();
00173   _i2c.write(_writeOpcode);
00174   _i2c.write(DATA_MODE);  
00175   for (int i=0; i<count; i++) {
00176     _i2c.write(pattern);  // Write Data   
00177   }
00178   _i2c.stop();
00179 
00180   //setDisplayOn();
00181 }
00182 
00183 #endif
00184 
00185 
00186 /** @brief write a bitmap to the display
00187  *  @param uint8_t* data pointer to bitmap
00188  *  @param uint8_t start_page begin page   (0..MAX_PAGE)
00189  *  @param uint8_t end_page   end page     (start_page..MAX_PAGE)                     
00190  *  @param uint8_t start_col  begin column (0..MAX_COL)
00191  *  @param uint8_t end_col    end column   (start_col..MAX_COL)
00192 */
00193 #if (I2C_OPTIMIZE == 0)
00194 //Standard version
00195 void SSD1308::writeBitmap(uint8_t* data,
00196                           uint8_t start_page, uint8_t end_page,
00197                           uint8_t start_col, uint8_t end_col){
00198   
00199   int count = (end_page - start_page + 1) * (end_col - start_col + 1);
00200 
00201   //setDisplayOff();
00202   setPageAddress (start_page, end_page);  // set page window
00203   setColumnAddress (start_col, end_col);  // set column window
00204 
00205   for (int i=0; i<count; i++) {
00206     _sendData(data[i]); // Write Data   
00207   }
00208 
00209   //setDisplayOn();
00210 }
00211 
00212 #else
00213 //Optimised version
00214 // Save lots of I2C S,P, address and datacommands:
00215 // Send S, address, DATA_MODE, data, data, data,...., P
00216 //
00217 void SSD1308::writeBitmap(uint8_t* data,
00218                           uint8_t start_page, uint8_t end_page,
00219                           uint8_t start_col, uint8_t end_col){
00220   
00221   int count = (end_page - start_page + 1) * (end_col - start_col + 1);
00222 
00223   //setDisplayOff();
00224   setPageAddress (start_page, end_page);  // set page window
00225   setColumnAddress (start_col, end_col);  // set column window
00226 
00227   _i2c.start();
00228   _i2c.write(_writeOpcode);
00229   _i2c.write(DATA_MODE);  
00230   for (int i=0; i<count; i++) {
00231     _i2c.write(data[i]);  // Write Data       
00232   }
00233   _i2c.stop();
00234 
00235   //setDisplayOn();
00236 }
00237 
00238 #endif
00239 
00240 
00241 /** @brief write a progressbar to the display, Width is (PRG_MAX_SCALE + 2) pixels
00242  *  @param uint8_t page begin page   (0..MAX_PAGE)
00243  *  @param uint8_t col  begin column (0..MAX_COL)
00244  *  @param int percentage value      (0..100)
00245 */
00246 #define PRG_MAX_SCALE     50
00247 #define PRG_LEFT_EDGE   0xFF
00248 #define PRG_RIGHT_EDGE  0xFF
00249 #define PRG_ACTIVE      0xFF
00250 //#define PRG_ACTIVE      0xBD
00251 #define PRG_NOT_ACTIVE  0x81
00252 
00253 #if (I2C_OPTIMIZE == 0)
00254 //Standard version
00255 void SSD1308::writeProgressBar(uint8_t page, uint8_t col, int percentage) {
00256   uint8_t scale_value;
00257   
00258   if (percentage <= 0) {
00259     scale_value = 0;
00260   } else if (percentage >= 100) {
00261       scale_value = PRG_MAX_SCALE - 1;
00262   }
00263   else {
00264     scale_value = (percentage * PRG_MAX_SCALE) / 100; 
00265   }      
00266       
00267   //setDisplayOff();
00268   setPageAddress (page, page);  
00269   setColumnAddress (col, MAX_COL); 
00270   
00271   _sendData(PRG_LEFT_EDGE);
00272 
00273   for (uint8_t col = 0; col < scale_value; col++) {
00274       _sendData(PRG_ACTIVE);
00275   }
00276       
00277   _sendData(PRG_ACTIVE);
00278   
00279   for (uint8_t col = (scale_value+1); col < PRG_MAX_SCALE; col++) {
00280       _sendData(PRG_NOT_ACTIVE);
00281   }
00282 
00283   _sendData(PRG_RIGHT_EDGE);    
00284   
00285   //setDisplayOn();
00286 }
00287 #else
00288 
00289 //Optimised version
00290 // Save lots of I2C S,P, address and datacommands:
00291 // Send S, address, DATA_MODE, data, data, data,...., P
00292 //
00293 void SSD1308::writeProgressBar(uint8_t page, uint8_t col, int percentage) {
00294   uint8_t scale_value;
00295   
00296   if (percentage <= 0) {
00297     scale_value = 0;
00298   } else if (percentage >= 100) {
00299       scale_value = PRG_MAX_SCALE - 1 ;
00300   }
00301   else {
00302     scale_value = (percentage * PRG_MAX_SCALE) / 100; 
00303   }      
00304       
00305   //setDisplayOff();
00306   setPageAddress (page, page);  
00307   setColumnAddress (col, MAX_COL); 
00308 
00309   _i2c.start();
00310   _i2c.write(_writeOpcode);
00311   _i2c.write(DATA_MODE);  
00312 
00313   _i2c.write(PRG_LEFT_EDGE);  // Write Data         
00314 
00315   for (uint8_t col = 0; col < scale_value; col++) {
00316      _i2c.write(PRG_ACTIVE);  // Write Data                       
00317   }
00318 
00319   _i2c.write(PRG_ACTIVE);  // Write Data                       
00320      
00321   for (uint8_t col = (scale_value+1); col < PRG_MAX_SCALE; col++) {
00322      _i2c.write(PRG_NOT_ACTIVE);  // Write Data                 
00323   }
00324 
00325   _i2c.write(PRG_RIGHT_EDGE);  // Write Data           
00326 
00327   _i2c.stop();
00328     
00329   //setDisplayOn();
00330 }
00331 #endif
00332 
00333 /** @brief write a level meter to the display, Width is (PRG_MAX_SCALE + 2) pixels
00334  *  @param uint8_t page begin page   (0..MAX_PAGE)
00335  *  @param uint8_t col  begin column (0..MAX_COL)
00336  *  @param int percentage value      (0..100)
00337 */
00338 #if (I2C_OPTIMIZE == 0)
00339 void SSD1308::writeLevelBar(uint8_t page, uint8_t col, int percentage) {
00340   uint8_t scale_value;
00341   
00342   if (percentage <= 0) {
00343     scale_value = 0;
00344   } else if (percentage >= 100) {
00345       scale_value = PRG_MAX_SCALE - 1;
00346   }
00347   else {
00348     scale_value = (percentage * PRG_MAX_SCALE) / 100; 
00349   }      
00350       
00351   //setDisplayOff();
00352   setPageAddress (page, page);  
00353   setColumnAddress (col, MAX_COL); 
00354  
00355   _sendData(PRG_LEFT_EDGE);   
00356 
00357   for (uint8_t col = 0; col < scale_value; col++) {
00358      _sendData(PRG_NOT_ACTIVE);  // Write Data                       
00359   }
00360 
00361   _sendData(PRG_ACTIVE);  // Write Data at active meterlevel
00362 
00363   for (uint8_t col = scale_value+1; col < PRG_MAX_SCALE; col++) {
00364       _sendData(PRG_NOT_ACTIVE);                
00365   }
00366          
00367   _sendData(PRG_RIGHT_EDGE);
00368     
00369   //setDisplayOn();
00370 }
00371 #else
00372 //Optimised version
00373 // Save lots of I2C S,P, address and datacommands:
00374 // Send S, address, DATA_MODE, data, data, data,...., P
00375 //
00376 void SSD1308::writeLevelBar(uint8_t page, uint8_t col, int percentage) {
00377   uint8_t scale_value;
00378   
00379   if (percentage <= 0) {
00380     scale_value = 0;
00381   } else if (percentage >= 100) {
00382       scale_value = PRG_MAX_SCALE - 1;
00383   }
00384   else {
00385     scale_value = (percentage * PRG_MAX_SCALE) / 100; 
00386   }      
00387       
00388   //setDisplayOff();
00389   setPageAddress (page, page);  
00390   setColumnAddress (col, MAX_COL); 
00391 
00392   _i2c.start();
00393   _i2c.write(_writeOpcode);
00394   _i2c.write(DATA_MODE);  
00395 
00396   _i2c.write(PRG_LEFT_EDGE);  // Write Data         
00397 
00398   for (uint8_t col = 0; col < scale_value; col++) {
00399      _i2c.write(PRG_NOT_ACTIVE);  // Write Data                       
00400   }
00401 
00402   _i2c.write(PRG_ACTIVE);  // Write Data at active meterlevel
00403   
00404   for (uint8_t col = scale_value+1; col < PRG_MAX_SCALE; col++) {
00405      _i2c.write(PRG_NOT_ACTIVE);  // Write Data                 
00406   }
00407 
00408   _i2c.write(PRG_RIGHT_EDGE);  // Write Data           
00409 
00410   _i2c.stop();
00411     
00412   //setDisplayOn();
00413 }
00414 #endif
00415 
00416 /** @brief Write single character to the display using the 8x8 fontable
00417  *  @brief Start at current cursor location
00418  *  @param char chr character to write
00419 */
00420 void SSD1308::writeChar(char chr) {
00421 #if (I2C_OPTIMIZE == 0)
00422   const uint8_t char_index = chr - 0x20;
00423 
00424   for (uint8_t i = 0; i < 8; i++) {
00425      if (_inverted) {
00426        _sendData( ~font_8x8[char_index][i] );           
00427      }
00428      else {
00429        _sendData( font_8x8[char_index][i] );
00430      }  
00431   }
00432 #else
00433   _sendData(8, (uint8_t *)&(font_8x8[chr-0x20][0]));
00434 #endif
00435 }
00436 
00437 
00438 /** @brief Write a string to the display using the 8x8 font
00439  *  @brief Start at selected cursor location, text will wrap around until it is done
00440  *  @param uint8_t row  row number    (0...ROWS/FONT_HEIGHT)
00441  *  @param uint8_t col  column number (0...COLUMNS/FONT_WIDTH)
00442  *  @param const char * text pointer to text
00443  */
00444 void SSD1308::writeString(uint8_t row, uint8_t col, const char * text) {
00445   uint16_t index = 0;
00446   uint16_t len = strlen(text);
00447   
00448   setPageAddress (row, MAX_PAGE);
00449   const uint8_t col_addr = FONT8x8_WIDTH*col;
00450   setColumnAddress (col_addr, MAX_COL);
00451 
00452   while ((col+index) < CHARS && (index < len)) {
00453      // write first line, starting at given position
00454      writeChar(text[index++]);
00455   }
00456 
00457   // write remaining lines
00458   // write until the end of memory
00459   // then wrap around again from the top.
00460   if (index + 1 < len) {
00461     setPageAddress (row + 1, MAX_PAGE);
00462     setColumnAddress (0, MAX_COL);
00463     bool wrapEntireScreen = false;
00464     while (index + 1 < len) {
00465        writeChar(text[index++]);
00466        // if we've written the last character space on the screen, 
00467        // reset the page and column address so that it wraps around from the top again
00468        if (!wrapEntireScreen && (row*CHARS + col + index) > 127) {
00469          setPageAddress (0, MAX_PAGE);
00470          setColumnAddress (0, MAX_COL);
00471          wrapEntireScreen = true;
00472        }
00473     }
00474   }
00475 }
00476 
00477 
00478 
00479 /** @brief Write large character (16x24 font)
00480  *  @param uint8_t row  row number    (0...MAX_ROW)
00481  *  @param uint8_t col  column number (0...MAX_COL)
00482  *  @param char chr     Used for displaying numbers 0 - 9 and '+', '-', '.'
00483  */
00484 void SSD1308::writeBigChar(uint8_t row, uint8_t col, char chr) {
00485 
00486   writeBitmap((uint8_t*) font_16x24[int(chr) - FONT16x24_START],
00487               row, (row + FONT16x24_BYTES - 1),
00488               col, (col + FONT16x24_WIDTH - 1));
00489 }
00490 
00491 
00492 /** @brief Write command that has no parameters
00493 */ 
00494 void SSD1308::_sendCommand(uint8_t command) {
00495 //  I2Cdev::writeByte(m_devAddr, COMMAND_MODE, command);
00496 
00497 #if (I2C_OPTIMIZE == 0)
00498   char databytes[2];
00499     
00500   databytes[0] = COMMAND_MODE;
00501   databytes[1] = command;    
00502   _i2c.write(_writeOpcode, databytes, 2);    // Write command   
00503 #else  
00504 
00505   _i2c.start();
00506   _i2c.write(_writeOpcode);
00507   
00508   _i2c.write(COMMAND_MODE);      
00509   _i2c.write(command);       // Write Command   
00510 
00511   _i2c.stop();  
00512 #endif
00513 }
00514 
00515 /** @brief Write command that has one parameter
00516 */ 
00517 void SSD1308::_sendCommand(uint8_t command, uint8_t param1) {
00518 
00519 //  Note continuationbit is set, so COMMAND_MODE must be
00520 //  repeated before each databyte that serves as parameter!
00521 #if (I2C_OPTIMIZE == 0)
00522   char databytes[4];
00523     
00524   databytes[0] = COMMAND_MODE;
00525   databytes[1] = command;    
00526   databytes[2] = COMMAND_MODE;
00527   databytes[3] = param1; 
00528   _i2c.write(_writeOpcode, databytes, 4);    // Write command   
00529 #else  
00530 
00531   _i2c.start();
00532   _i2c.write(_writeOpcode);
00533   
00534   _i2c.write(COMMAND_MODE);      
00535   _i2c.write(command);       // Write Command   
00536   _i2c.write(COMMAND_MODE);      
00537   _i2c.write(param1);        // Write Param1   
00538 
00539   _i2c.stop();
00540 #endif  
00541 }
00542 
00543 /** @brief Write command that has two parameters
00544 */ 
00545 void SSD1308::_sendCommand(uint8_t command, uint8_t param1, uint8_t param2) {
00546 
00547 //  Note continuationbit is set, so COMMAND_MODE must be
00548 //  repeated before each databyte that serves as parameter!
00549 #if (I2C_OPTIMIZE == 0)
00550   char databytes[6];
00551     
00552   databytes[0] = COMMAND_MODE;
00553   databytes[1] = command;    
00554   databytes[2] = COMMAND_MODE;
00555   databytes[3] = param1; 
00556   databytes[4] = COMMAND_MODE;
00557   databytes[5] = param2; 
00558   _i2c.write(_writeOpcode, databytes, 6);    // Write command   
00559 #else  
00560   _i2c.start();
00561   _i2c.write(_writeOpcode);
00562   
00563   _i2c.write(COMMAND_MODE);      
00564   _i2c.write(command);       // Write Command   
00565   _i2c.write(COMMAND_MODE);      
00566   _i2c.write(param1);        // Write Param1   
00567   _i2c.write(COMMAND_MODE);      
00568   _i2c.write(param2);        // Write Param2   
00569 
00570   _i2c.stop();
00571  #endif 
00572 }
00573 
00574 /** @brief Write command that has five parameters
00575 */ 
00576 void SSD1308::_sendCommand(uint8_t command, uint8_t param1, uint8_t param2,
00577                                             uint8_t param3, uint8_t param4,
00578                                             uint8_t param5) {
00579 
00580 //  Note continuationbit is set, so COMMAND_MODE must be
00581 //  repeated before each databyte that serves as parameter!
00582 #if (I2C_OPTIMIZE == 0)
00583   char databytes[12];
00584     
00585   databytes[0] = COMMAND_MODE;
00586   databytes[1] = command;    
00587   databytes[2] = COMMAND_MODE;
00588   databytes[3] = param1; 
00589   databytes[4] = COMMAND_MODE;
00590   databytes[5] = param2; 
00591   databytes[6] = COMMAND_MODE;
00592   databytes[7] = param3; 
00593   databytes[8] = COMMAND_MODE;
00594   databytes[9] = param4; 
00595   databytes[10] = COMMAND_MODE;
00596   databytes[11] = param5;       
00597   _i2c.write(_writeOpcode, databytes, 12);    // Write command   
00598 #else  
00599   _i2c.start();
00600   _i2c.write(_writeOpcode);
00601   
00602   _i2c.write(COMMAND_MODE);      
00603   _i2c.write(command);       // Write Command   
00604   _i2c.write(COMMAND_MODE);      
00605   _i2c.write(param1);        // Write Param1   
00606   _i2c.write(COMMAND_MODE);      
00607   _i2c.write(param2);        // Write Param2   
00608   _i2c.write(COMMAND_MODE);      
00609   _i2c.write(param3);        // Write Param3   
00610   _i2c.write(COMMAND_MODE);      
00611   _i2c.write(param4);        // Write Param4   
00612   _i2c.write(COMMAND_MODE);      
00613   _i2c.write(param5);        // Write Param5   
00614 
00615   _i2c.stop();
00616 #endif  
00617 }
00618 
00619 
00620 /** @brief Write command that has six parameters
00621 */ 
00622 void SSD1308::_sendCommand(uint8_t command, uint8_t param1, uint8_t param2,
00623                                             uint8_t param3, uint8_t param4,
00624                                             uint8_t param5, uint8_t param6) {
00625 
00626 //  Note continuationbit is set, so COMMAND_MODE must be
00627 //  repeated before each databyte that serves as parameter!
00628 #if (I2C_OPTIMIZE == 0)
00629   char databytes[14];
00630     
00631   databytes[0] = COMMAND_MODE;
00632   databytes[1] = command;    
00633   databytes[2] = COMMAND_MODE;
00634   databytes[3] = param1; 
00635   databytes[4] = COMMAND_MODE;
00636   databytes[5] = param2; 
00637   databytes[6] = COMMAND_MODE;
00638   databytes[7] = param3; 
00639   databytes[8] = COMMAND_MODE;
00640   databytes[9] = param4; 
00641   databytes[10] = COMMAND_MODE;
00642   databytes[11] = param5;   
00643   databytes[12] = COMMAND_MODE;
00644   databytes[13] = param6;       
00645   _i2c.write(_writeOpcode, databytes, 14);    // Write command   
00646 #else  
00647   _i2c.start();
00648   _i2c.write(_writeOpcode);
00649   
00650   _i2c.write(COMMAND_MODE);      
00651   _i2c.write(command);       // Write Command   
00652   _i2c.write(COMMAND_MODE);      
00653   _i2c.write(param1);        // Write Param1   
00654   _i2c.write(COMMAND_MODE);      
00655   _i2c.write(param2);        // Write Param2   
00656   _i2c.write(COMMAND_MODE);      
00657   _i2c.write(param3);        // Write Param3   
00658   _i2c.write(COMMAND_MODE);      
00659   _i2c.write(param4);        // Write Param4   
00660   _i2c.write(COMMAND_MODE);      
00661   _i2c.write(param5);        // Write Param5   
00662   _i2c.write(COMMAND_MODE);      
00663   _i2c.write(param6);        // Write Param6   
00664 
00665   _i2c.stop();
00666 #endif  
00667 }
00668 
00669 
00670 #if(0)
00671 /** @brief Write command that has multiple parameters
00672 */ 
00673 void SSD1308::_sendCommands(uint8_t len, uint8_t* commands) {
00674 
00675 //  I2Cdev::writeBytes(m_devAddr, COMMAND_MODE, len, commands);
00676 //  Note this original code is not correct, continuationbit is set, 
00677 //  so COMMAND_MODE must be repeated before each databyte that serves as parameter!
00678 
00679   _i2c.start();
00680   _i2c.write(_writeOpcode);
00681   
00682   for (int i=0; i<len ; i++) {
00683     _i2c.write(COMMAND_MODE);      
00684     _i2c.write(commands[i]);  // Write Commands   
00685   }
00686   _i2c.stop();
00687   
00688 }
00689 #endif
00690 
00691 /** @brief Write databyte to display
00692  *  @brief Start at current cursor location
00693  *  @param uint8_t data databyte to write
00694 */
00695 void SSD1308::_sendData(uint8_t data){
00696   char databytes[2];
00697     
00698   databytes[0] = DATA_MODE;
00699   databytes[1] = data;    
00700   _i2c.write(_writeOpcode, databytes, 2);    // Write Data   
00701 }
00702 
00703 /** @brief Write len bytes from buffer data to display, 
00704  *  @brief Start at current cursor location
00705  *  @param uint8_t len number of bytes to write 
00706  *  @param uint8_t* data pointer to data
00707 */
00708 void SSD1308::_sendData(uint8_t len, uint8_t* data) {
00709 //  I2Cdev::writeBytes(m_devAddr, DATA_MODE, len, data);
00710 #if (I2C_OPTIMIZE == 0)
00711   for (int i=0; i<len ; i++) {
00712     _sendData(data[i]);  // Write Data   
00713   }
00714 #else  
00715   _i2c.start();
00716   _i2c.write(_writeOpcode);
00717   _i2c.write(DATA_MODE);  
00718   for (int i=0; i<len ; i++) {
00719     _i2c.write(data[i]);  // Write Data   
00720   }
00721   _i2c.stop();
00722 #endif 
00723 }
00724 
00725 
00726 /** @brief Set Horizontal Addressing Mode (cursor incr left-to-right, top-to-bottom)
00727  * 
00728  */
00729 void SSD1308::setHorizontalAddressingMode(){
00730   setMemoryAddressingMode(HORIZONTAL_ADDRESSING_MODE); 
00731 }
00732 
00733 /** @brief Set Vertical Addressing Mode  (cursor incr top-to-bottom, left-to-right)
00734  * 
00735  */
00736 void SSD1308::setVerticalAddressingMode() {
00737   setMemoryAddressingMode(VERTICAL_ADDRESSING_MODE); 
00738 }
00739 
00740 /** @brief Set Page Addressing Mode  (cursor incr left-to-right)
00741  * 
00742  */
00743 void SSD1308::setPageAddressingMode(){
00744   setMemoryAddressingMode(PAGE_ADDRESSING_MODE); 
00745 }
00746     
00747 /** @brief Set Addressing Mode
00748  *  @param uint8_t mode 
00749  */
00750 void SSD1308::setMemoryAddressingMode(uint8_t mode){
00751 
00752   _sendCommand(SET_MEMORY_ADDRESSING_MODE, mode);   
00753 }
00754 
00755 
00756 /** @param uint8_t start startpage (valid range 0..MAX_PAGE)
00757  *  @param uint8_t end   endpage   (valid range start..MAX_PAGE)
00758  */
00759 void SSD1308::setPageAddress (uint8_t start, uint8_t end) {
00760 
00761   _sendCommand(SET_PAGE_ADDRESS, start, end);   
00762 }
00763 
00764 
00765 /** @param uint8_t start startcolumn (valid range 0..MAX_COL)
00766  *  @param uint8_t end   endcolumn   (valid range start..MAX_COL)
00767  */
00768 void SSD1308::setColumnAddress (uint8_t start, uint8_t end) {
00769 
00770   _sendCommand(SET_COLUMN_ADDRESS, start, end);     
00771 }
00772 
00773 /** 
00774  *  @brief Set Display StartLine, takes one byte, 0x00-0x3F
00775  *  @param uint8_t line startline (valid range 0..MAX_ROWS)
00776  */  
00777 void SSD1308::setDisplayStartLine(uint8_t line) {
00778 
00779   line = line & MAX_ROW;
00780    
00781   _sendCommand(SET_DISPLAY_START_LINE | line);     
00782 }
00783 
00784 
00785 /** 
00786  *  @brief Set Column Start (for Page Addressing Mode only)
00787  *  @param uint8_t column column start (valid range 0..MAX_COL)
00788  */  
00789 void SSD1308::setColumnStartForPageAddressingMode(uint8_t column) {
00790 
00791   column = column & MAX_COL;
00792 
00793   _sendCommand(SET_LOWER_COLUMN  | ( column     & 0x0F));  // lower nibble   
00794   _sendCommand(SET_HIGHER_COLUMN | ((column>>4) & 0x0F));  // higher nibble     
00795 }
00796 
00797 
00798 /** 
00799  *  @brief Set Page Start (for Page Addressing Mode only)
00800  *  @param uint8_t page page start (valid range PAGE0 - PAGE7)
00801  */    
00802 void SSD1308::setPageStartForPageAddressingMode(uint8_t page) {
00803 
00804   page = page & MAX_PAGE;
00805 
00806   _sendCommand(SET_PAGE_START_ADDRESS | page);
00807  
00808 }
00809 
00810 
00811 /** @brief Set Contrast
00812  *  @param uint8_t contrast (valid range 0x00 (lowest) - 0xFF (highest))
00813 */
00814 void SSD1308::setContrastControl(uint8_t contrast) {
00815   
00816     _sendCommand(SET_CONTRAST, contrast);  
00817 } 
00818 
00819 /** @brief Enable Display
00820 */ 
00821 void SSD1308::setDisplayOn() {
00822   _sendCommand(SET_DISPLAY_POWER_ON);
00823 }
00824 
00825 /** @brief Disable Display
00826 */ 
00827 void SSD1308::setDisplayOff() {
00828   _sendCommand(SET_DISPLAY_POWER_OFF);
00829 }
00830 
00831 /** @brief Enable or Disable Display
00832  *  @param bool on
00833  */
00834 void SSD1308::setDisplayPower(bool on) {
00835   if (on) {
00836     setDisplayOn();
00837   } else {
00838     setDisplayOff();
00839   }
00840 }
00841 
00842 /** @brief Show White pixels on Black background
00843  */ 
00844 void SSD1308::setDisplayNormal() {
00845   _sendCommand(SET_NORMAL_DISPLAY);
00846 }
00847 
00848 /** @brief Show Black pixels on White background
00849  */ 
00850 void SSD1308::setDisplayInverse() {
00851   _sendCommand(SET_INVERSE_DISPLAY);
00852 }
00853 
00854 /** @brief Blink display by fading in and out over a set number of frames
00855  *  @param bool on
00856  */
00857 void SSD1308::setDisplayBlink(bool on){
00858   if (on) {
00859     _sendCommand(SET_FADE_BLINK, (BLINK_ENABLE | FADE_INTERVAL_128_FRAMES));
00860   }
00861   else {  
00862     _sendCommand(SET_FADE_BLINK, FADE_BLINK_DISABLE);  
00863   }
00864 }       
00865 
00866 
00867 /** @brief Fade out display in set number of frames
00868  *  @param bool on
00869  */
00870 void SSD1308::setDisplayFade(bool on) {
00871   if (on) {
00872     _sendCommand(SET_FADE_BLINK, (FADE_OUT_ENABLE | FADE_INTERVAL_128_FRAMES));
00873   }
00874   else {  
00875     _sendCommand(SET_FADE_BLINK, FADE_BLINK_DISABLE);  
00876   }
00877 }    
00878 
00879 /** @brief Display Flip (Left/Right, Up/Down)
00880  *  @param bool left flip Left/Right
00881  *  @param bool down flip Up/Down
00882  */
00883 void SSD1308::setDisplayFlip(bool left, bool down) {
00884   if (left) {
00885     // column address   0 is mapped to SEG0 (Reset)    
00886     _sendCommand(SET_SEGMENT_REMAP_0);
00887   }
00888   else {
00889     // column address 127 is mapped to SEG0    
00890     _sendCommand(SET_SEGMENT_REMAP_127);
00891   }  
00892 
00893   if (down) {
00894     // Reset mode
00895     _sendCommand(SET_COMMON_REMAP_0);    
00896   }
00897   else {
00898     // Flip Up/Down (Need to rewrite display before H effect shows)
00899     _sendCommand(SET_COMMON_REMAP_63);        
00900   }  
00901 
00902 }
00903 
00904 /** @brief Sets Internal Iref
00905  */
00906 void SSD1308::setInternalIref() {
00907 //  uint8_t cmds[2] = {SET_IREF_SELECTION, INTERNAL_IREF};
00908 //  _sendCommands(2, cmds); 
00909   
00910   _sendCommand(SET_IREF_SELECTION, INTERNAL_IREF);   
00911 }
00912 
00913 /** @brief Sets External Iref (default)
00914  */
00915 void SSD1308::setExternalIref() {
00916 //  uint8_t cmds[2] = {SET_IREF_SELECTION, EXTERNAL_IREF};
00917 //  _sendCommands(2, cmds); 
00918   _sendCommand(SET_IREF_SELECTION, EXTERNAL_IREF); 
00919 }
00920 
00921 
00922 /** @brief Shows All Pixels On
00923  */
00924 void SSD1308::setEntireDisplayOn(){
00925   _sendCommand(SET_ENTIRE_DISPLAY_ON); 
00926 }
00927 
00928 /** @brief Shows Pixels as RAM content
00929  */
00930 void SSD1308::setEntireDisplayRAM(){
00931   _sendCommand(SET_DISPLAY_GDDRAM); 
00932 }
00933 
00934 /** @brief Shows Pixels On or as RAM content
00935  *  @param bool on (true is All on, false is RAM content)
00936  */
00937 void SSD1308::setEntireDisplay(bool on){
00938   if (on) {
00939     setEntireDisplayOn();  // All Pixels on
00940   }
00941   else {  
00942     setEntireDisplayRAM(); // Pixels are RAM content
00943   }
00944 }
00945 
00946 
00947 /** @brief Horizontal scroll by one column per interval
00948  *  @param bool left select Left/Right scroll
00949  *  @param uint8_t start_page begin page   (0..MAX_PAGE)
00950  *  @param uint8_t end_page   end page     (start_page..MAX_PAGE)                     
00951  *  @param uint8_t interval   scroll interval in frames (see codes above)                      
00952  */  
00953 void SSD1308::setContinuousHorizontalScroll(bool left, uint8_t start_page, uint8_t end_page, uint8_t interval) {
00954   if (left) {
00955     _sendCommand(SET_LEFT_HOR_SCROLL, 0x00, start_page, interval, end_page, 0x00, 0xFF);  // Scroll Left
00956   }
00957   else {  
00958     _sendCommand(SET_RIGHT_HOR_SCROLL, 0x00, start_page, interval, end_page, 0x00, 0xFF); // Scroll Right  
00959   }
00960 
00961 }
00962 
00963 
00964 /** @brief Horizontal and Vertical scroll by one column per interval
00965  *  @param bool left select Left/Right scroll
00966  *  @param uint8_t start_page begin page   (0..MAX_PAGE)
00967  *  @param uint8_t end_page   end page     (start_page..MAX_PAGE)                     
00968  *  @param uint8_t offset     vert offset  (0x01..0x63)                       
00969  *  @param uint8_t interval   scroll interval in frames (see codes above)                       
00970  */  
00971 void SSD1308::setContinuousVerticalAndHorizontalScroll(bool left, uint8_t start_page, uint8_t end_page, 
00972                                                        uint8_t offset, uint8_t interval) {
00973   if (left) {
00974     _sendCommand(SET_VERT_LEFT_HOR_SCROLL, 0x00, start_page, interval, end_page, offset);  // Scroll Left
00975   }
00976   else {  
00977     _sendCommand(SET_VERT_RIGHT_HOR_SCROLL, 0x00, start_page, interval, end_page, offset); // Scroll Right  
00978   }
00979                                                        
00980 }    
00981 
00982 /** @brief Set Vertical scroll area
00983  *  @param uint8_t topRowsFixed      fixed rows   (0..MAX_ROW)                     
00984  *  @param uint8_t scrollRowsoffset  scroll rows  (topRowsFixed..ROWS)                       
00985  */  
00986 void SSD1308::setVerticalScrollArea(uint8_t topRowsFixed, uint8_t scrollRows) { 
00987    
00988   if ((topRowsFixed + scrollRows) > ROWS) {
00989      scrollRows = ROWS - topRowsFixed; 
00990   };
00991   
00992   _sendCommand(SET_VERTICAL_SCROLL_AREA, topRowsFixed, scrollRows); 
00993 }
00994 
00995 /** @brief Activate or Deactivate Horizontal and Vertical scroll
00996  *  @brief Note: after deactivating scrolling, the RAM data needs to be rewritten
00997  *  @param bool on activate scroll 
00998  */  
00999 void SSD1308::setDisplayScroll(bool on) {
01000   if (on) {
01001     _sendCommand(SET_ACTIVATE_SCROLL);   // Scroll on
01002   }
01003   else {  
01004     _sendCommand(SET_DEACTIVATE_SCROLL); // Scroll off  
01005   }
01006 }
01007 
01008 
01009 
01010 /** @brief Low level Init
01011  *  @brief Init the configuration registers in accordance with the datasheet
01012  */
01013 void SSD1308::_init() {
01014 
01015   _sendCommand(SET_DISPLAY_POWER_OFF);      // 0xAE
01016   
01017   // column address   0 is mapped to SEG0 (Reset)    
01018   // row address   0 is mapped to COM0 (Reset)      
01019   _sendCommand(SET_SEGMENT_REMAP_0);        // 0xA0 (Reset)
01020   _sendCommand(SET_COMMON_REMAP_0);         // 0xC0 (Reset) 
01021 
01022   setDisplayStartLine(0);                   // 0x40 (Reset) 
01023   
01024   _sendCommand(SET_COMMON_CONF, COMMON_BASE | COMMON_ALTERNATIVE | COMMON_LEFTRIGHT_NORMAL); // 0xDA, 0x12 (Reset)
01025 
01026   // Pagemode or Horizontal mode
01027 //  setPageAddressingMode();                  // 0x20, 0x02 (Reset)  
01028 //  setColumnStartForPageAddressingMode(0);   // 0x00, 0x10 (Reset = Column 0)
01029 //  setPageStartForPageAddressingMode(PAGE_0);// 0xBO       (Reset = Page 0)
01030   setHorizontalAddressingMode();            // 0x20, 0x00 (Non-Reset)
01031   setColumnAddress (0, MAX_COL);             // 0x21, 0x00, 0x37 (Reset)
01032   setPageAddress (0, MAX_PAGE);              // 0x22, 0x00, 0x07 (Reset)
01033 
01034   setExternalIref();                        // 0xAD, 0x10 (Reset)
01035   
01036   _sendCommand(SET_DISPLAY_CLOCK, 0x70);    // 0xD5, 0x70 (Reset = 0x80)
01037   _sendCommand(SET_PRECHARGE_TIME, 0x21);   // 0xD9, 0x21 (Reset = 0x22)
01038   _sendCommand(SET_VCOMH_DESELECT_LEVEL, 0x30); // 0xDB, 0x30 (Reset = 0x20)  
01039   _sendCommand(SET_MULTIPLEX_RATIO, 0x3F);  // 0xA8, 0x3F (Reset)  
01040   _sendCommand(SET_DISPLAY_OFFSET, 0x00);   // 0xD3, 0x00 (Reset)  
01041   
01042   _sendCommand(SET_CONTRAST, 0x7F);         // 0x81, 0x7F (Reset)
01043 
01044   _sendCommand(SET_NORMAL_DISPLAY);         // 0xA6 (Reset)
01045   
01046   setEntireDisplayRAM();                    // 0xA4 (Reset)
01047   setDisplayScroll(false);
01048   
01049   clearDisplay();   
01050   
01051   _sendCommand(SET_DISPLAY_POWER_ON);       // 0xAF
01052 }
01053