A fork with a fix to work with newer versions of MBED

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)
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 
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 
00433 }
00434 
00435 
00436 /** @brief Write a string to the display using the 8x8 font
00437  *  @brief Start at selected cursor location, text will wrap around until it is done
00438  *  @param uint8_t row  row number    (0...ROWS/FONT_HEIGHT)
00439  *  @param uint8_t col  column number (0...COLUMNS/FONT_WIDTH)
00440  *  @param const char * text pointer to text
00441  */
00442 void SSD1308::writeString(uint8_t row, uint8_t col, const char * text) {
00443   uint16_t index = 0;
00444   uint16_t len = strlen(text);
00445   
00446   setPageAddress (row, MAX_PAGE);
00447   const uint8_t col_addr = FONT8x8_WIDTH*col;
00448   setColumnAddress (col_addr, MAX_COL);
00449 
00450   while ((col+index) < CHARS && (index < len)) {
00451      // write first line, starting at given position
00452      writeChar(text[index++]);
00453   }
00454 
00455   // write remaining lines
00456   // write until the end of memory
00457   // then wrap around again from the top.
00458   if (index + 1 < len) {
00459     setPageAddress (row + 1, MAX_PAGE);
00460     setColumnAddress (0, MAX_COL);
00461     bool wrapEntireScreen = false;
00462     while (index + 1 < len) {
00463        writeChar(text[index++]);
00464        // if we've written the last character space on the screen, 
00465        // reset the page and column address so that it wraps around from the top again
00466        if (!wrapEntireScreen && (row*CHARS + col + index) > 127) {
00467          setPageAddress (0, MAX_PAGE);
00468          setColumnAddress (0, MAX_COL);
00469          wrapEntireScreen = true;
00470        }
00471     }
00472   }
00473 }
00474 
00475 
00476 
00477 /** @brief Write large character (16x24 font)
00478  *  @param uint8_t row  row number    (0...MAX_ROW)
00479  *  @param uint8_t col  column number (0...MAX_COL)
00480  *  @param char chr     Used for displaying numbers 0 - 9 and '+', '-', '.'
00481  */
00482 void SSD1308::writeBigChar(uint8_t row, uint8_t col, char chr) {
00483 
00484   writeBitmap((uint8_t*) font_16x24[int(chr) - FONT16x24_START],
00485               row, (row + FONT16x24_BYTES - 1),
00486               col, (col + FONT16x24_WIDTH - 1));
00487 }
00488 
00489 
00490 /** @brief Write command that has no parameters
00491 */ 
00492 void SSD1308::_sendCommand(uint8_t command) {
00493 //  I2Cdev::writeByte(m_devAddr, COMMAND_MODE, command);
00494 
00495 #if (I2C_OPTIMIZE == 0)
00496   char databytes[2];
00497     
00498   databytes[0] = COMMAND_MODE;
00499   databytes[1] = command;    
00500   _i2c.write(_writeOpcode, databytes, 2);    // Write command   
00501 #else  
00502 
00503   _i2c.start();
00504   _i2c.write(_writeOpcode);
00505   
00506   _i2c.write(COMMAND_MODE);      
00507   _i2c.write(command);       // Write Command   
00508 
00509   _i2c.stop();  
00510 #endif
00511 }
00512 
00513 /** @brief Write command that has one parameter
00514 */ 
00515 void SSD1308::_sendCommand(uint8_t command, uint8_t param1) {
00516 
00517 //  Note continuationbit is set, so COMMAND_MODE must be
00518 //  repeated before each databyte that serves as parameter!
00519 #if (I2C_OPTIMIZE == 0)
00520   char databytes[4];
00521     
00522   databytes[0] = COMMAND_MODE;
00523   databytes[1] = command;    
00524   databytes[2] = COMMAND_MODE;
00525   databytes[3] = param1; 
00526   _i2c.write(_writeOpcode, databytes, 4);    // Write command   
00527 #else  
00528 
00529   _i2c.start();
00530   _i2c.write(_writeOpcode);
00531   
00532   _i2c.write(COMMAND_MODE);      
00533   _i2c.write(command);       // Write Command   
00534   _i2c.write(COMMAND_MODE);      
00535   _i2c.write(param1);        // Write Param1   
00536 
00537   _i2c.stop();
00538 #endif  
00539 }
00540 
00541 /** @brief Write command that has two parameters
00542 */ 
00543 void SSD1308::_sendCommand(uint8_t command, uint8_t param1, uint8_t param2) {
00544 
00545 //  Note continuationbit is set, so COMMAND_MODE must be
00546 //  repeated before each databyte that serves as parameter!
00547 #if (I2C_OPTIMIZE == 0)
00548   char databytes[6];
00549     
00550   databytes[0] = COMMAND_MODE;
00551   databytes[1] = command;    
00552   databytes[2] = COMMAND_MODE;
00553   databytes[3] = param1; 
00554   databytes[4] = COMMAND_MODE;
00555   databytes[5] = param2; 
00556   _i2c.write(_writeOpcode, databytes, 6);    // Write command   
00557 #else  
00558   _i2c.start();
00559   _i2c.write(_writeOpcode);
00560   
00561   _i2c.write(COMMAND_MODE);      
00562   _i2c.write(command);       // Write Command   
00563   _i2c.write(COMMAND_MODE);      
00564   _i2c.write(param1);        // Write Param1   
00565   _i2c.write(COMMAND_MODE);      
00566   _i2c.write(param2);        // Write Param2   
00567 
00568   _i2c.stop();
00569  #endif 
00570 }
00571 
00572 /** @brief Write command that has five parameters
00573 */ 
00574 void SSD1308::_sendCommand(uint8_t command, uint8_t param1, uint8_t param2,
00575                                             uint8_t param3, uint8_t param4,
00576                                             uint8_t param5) {
00577 
00578 //  Note continuationbit is set, so COMMAND_MODE must be
00579 //  repeated before each databyte that serves as parameter!
00580 #if (I2C_OPTIMIZE == 0)
00581   char databytes[12];
00582     
00583   databytes[0] = COMMAND_MODE;
00584   databytes[1] = command;    
00585   databytes[2] = COMMAND_MODE;
00586   databytes[3] = param1; 
00587   databytes[4] = COMMAND_MODE;
00588   databytes[5] = param2; 
00589   databytes[6] = COMMAND_MODE;
00590   databytes[7] = param3; 
00591   databytes[8] = COMMAND_MODE;
00592   databytes[9] = param4; 
00593   databytes[10] = COMMAND_MODE;
00594   databytes[11] = param5;       
00595   _i2c.write(_writeOpcode, databytes, 12);    // Write command   
00596 #else  
00597   _i2c.start();
00598   _i2c.write(_writeOpcode);
00599   
00600   _i2c.write(COMMAND_MODE);      
00601   _i2c.write(command);       // Write Command   
00602   _i2c.write(COMMAND_MODE);      
00603   _i2c.write(param1);        // Write Param1   
00604   _i2c.write(COMMAND_MODE);      
00605   _i2c.write(param2);        // Write Param2   
00606   _i2c.write(COMMAND_MODE);      
00607   _i2c.write(param3);        // Write Param3   
00608   _i2c.write(COMMAND_MODE);      
00609   _i2c.write(param4);        // Write Param4   
00610   _i2c.write(COMMAND_MODE);      
00611   _i2c.write(param5);        // Write Param5   
00612 
00613   _i2c.stop();
00614 #endif  
00615 }
00616 
00617 
00618 /** @brief Write command that has six parameters
00619 */ 
00620 void SSD1308::_sendCommand(uint8_t command, uint8_t param1, uint8_t param2,
00621                                             uint8_t param3, uint8_t param4,
00622                                             uint8_t param5, uint8_t param6) {
00623 
00624 //  Note continuationbit is set, so COMMAND_MODE must be
00625 //  repeated before each databyte that serves as parameter!
00626 #if (I2C_OPTIMIZE == 0)
00627   char databytes[14];
00628     
00629   databytes[0] = COMMAND_MODE;
00630   databytes[1] = command;    
00631   databytes[2] = COMMAND_MODE;
00632   databytes[3] = param1; 
00633   databytes[4] = COMMAND_MODE;
00634   databytes[5] = param2; 
00635   databytes[6] = COMMAND_MODE;
00636   databytes[7] = param3; 
00637   databytes[8] = COMMAND_MODE;
00638   databytes[9] = param4; 
00639   databytes[10] = COMMAND_MODE;
00640   databytes[11] = param5;   
00641   databytes[12] = COMMAND_MODE;
00642   databytes[13] = param6;       
00643   _i2c.write(_writeOpcode, databytes, 14);    // Write command   
00644 #else  
00645   _i2c.start();
00646   _i2c.write(_writeOpcode);
00647   
00648   _i2c.write(COMMAND_MODE);      
00649   _i2c.write(command);       // Write Command   
00650   _i2c.write(COMMAND_MODE);      
00651   _i2c.write(param1);        // Write Param1   
00652   _i2c.write(COMMAND_MODE);      
00653   _i2c.write(param2);        // Write Param2   
00654   _i2c.write(COMMAND_MODE);      
00655   _i2c.write(param3);        // Write Param3   
00656   _i2c.write(COMMAND_MODE);      
00657   _i2c.write(param4);        // Write Param4   
00658   _i2c.write(COMMAND_MODE);      
00659   _i2c.write(param5);        // Write Param5   
00660   _i2c.write(COMMAND_MODE);      
00661   _i2c.write(param6);        // Write Param6   
00662 
00663   _i2c.stop();
00664 #endif  
00665 }
00666 
00667 
00668 #if(0)
00669 /** @brief Write command that has multiple parameters
00670 */ 
00671 void SSD1308::_sendCommands(uint8_t len, uint8_t* commands) {
00672 
00673 //  I2Cdev::writeBytes(m_devAddr, COMMAND_MODE, len, commands);
00674 //  Note this original code is not correct, continuationbit is set, 
00675 //  so COMMAND_MODE must be repeated before each databyte that serves as parameter!
00676 
00677   _i2c.start();
00678   _i2c.write(_writeOpcode);
00679   
00680   for (int i=0; i<len ; i++) {
00681     _i2c.write(COMMAND_MODE);      
00682     _i2c.write(commands[i]);  // Write Commands   
00683   }
00684   _i2c.stop();
00685   
00686 }
00687 #endif
00688 
00689 /** @brief Write databyte to display
00690  *  @brief Start at current cursor location
00691  *  @param uint8_t data databyte to write
00692 */
00693 void SSD1308::_sendData(uint8_t data){
00694 
00695 #if (I2C_OPTIMIZE == 0)
00696 //I2C Blockwrite versions dont seem to work ?
00697 //That may be related to fact that the SSD1308/SSD1306 does NOT return an acknowledge: blockwrite may abort the operation
00698 //Noted for mbed lib v63 on 20/7/13 
00699   char databytes[2];
00700     
00701   databytes[0] = DATA_MODE;
00702   databytes[1] = data;    
00703   _i2c.write(_writeOpcode, databytes, 2);    // Write Data   
00704 
00705 #else
00706   _i2c.start();
00707   _i2c.write(_writeOpcode);
00708   _i2c.write(DATA_MODE);  
00709   _i2c.write(data); 
00710   _i2c.stop();  
00711 #endif
00712 
00713 }
00714 
00715 /** @brief Write len bytes from buffer data to display, 
00716  *  @brief Start at current cursor location
00717  *  @param uint8_t len number of bytes to write 
00718  *  @param uint8_t* data pointer to data
00719 */
00720 void SSD1308::_sendData(uint8_t len, uint8_t* data) {
00721 //  I2Cdev::writeBytes(m_devAddr, DATA_MODE, len, data);
00722 #if (I2C_OPTIMIZE == 0)
00723   for (int i=0; i<len ; i++) {
00724     _sendData(data[i]);  // Write Data   
00725   }
00726 #else  
00727   _i2c.start();
00728   _i2c.write(_writeOpcode);
00729   _i2c.write(DATA_MODE);  
00730   for (int i=0; i<len ; i++) {
00731     _i2c.write(data[i]);  // Write Data   
00732   }
00733   _i2c.stop();
00734 #endif 
00735 }
00736 
00737 
00738 /** @brief Set Horizontal Addressing Mode (cursor incr left-to-right, top-to-bottom)
00739  * 
00740  */
00741 void SSD1308::setHorizontalAddressingMode(){
00742   setMemoryAddressingMode(HORIZONTAL_ADDRESSING_MODE); 
00743 }
00744 
00745 /** @brief Set Vertical Addressing Mode  (cursor incr top-to-bottom, left-to-right)
00746  * 
00747  */
00748 void SSD1308::setVerticalAddressingMode() {
00749   setMemoryAddressingMode(VERTICAL_ADDRESSING_MODE); 
00750 }
00751 
00752 /** @brief Set Page Addressing Mode  (cursor incr left-to-right)
00753  * 
00754  */
00755 void SSD1308::setPageAddressingMode(){
00756   setMemoryAddressingMode(PAGE_ADDRESSING_MODE); 
00757 }
00758     
00759 /** @brief Set Addressing Mode
00760  *  @param uint8_t mode 
00761  */
00762 void SSD1308::setMemoryAddressingMode(uint8_t mode){
00763 
00764   _sendCommand(SET_MEMORY_ADDRESSING_MODE, mode);   
00765 }
00766 
00767 
00768 /** @param uint8_t start startpage (valid range 0..MAX_PAGE)
00769  *  @param uint8_t end   endpage   (valid range start..MAX_PAGE)
00770  */
00771 void SSD1308::setPageAddress (uint8_t start, uint8_t end) {
00772 
00773   _sendCommand(SET_PAGE_ADDRESS, start, end);   
00774 }
00775 
00776 
00777 /** @param uint8_t start startcolumn (valid range 0..MAX_COL)
00778  *  @param uint8_t end   endcolumn   (valid range start..MAX_COL)
00779  */
00780 void SSD1308::setColumnAddress (uint8_t start, uint8_t end) {
00781 
00782   _sendCommand(SET_COLUMN_ADDRESS, start, end);     
00783 }
00784 
00785 /** 
00786  *  @brief Set Display StartLine, takes one byte, 0x00-0x3F
00787  *  @param uint8_t line startline (valid range 0..MAX_ROWS)
00788  */  
00789 void SSD1308::setDisplayStartLine(uint8_t line) {
00790 
00791   line = line & MAX_ROW;
00792    
00793   _sendCommand(SET_DISPLAY_START_LINE | line);     
00794 }
00795 
00796 
00797 /** 
00798  *  @brief Set Column Start (for Page Addressing Mode only)
00799  *  @param uint8_t column column start (valid range 0..MAX_COL)
00800  */  
00801 void SSD1308::setColumnStartForPageAddressingMode(uint8_t column) {
00802 
00803   column = column & MAX_COL;
00804 
00805   _sendCommand(SET_LOWER_COLUMN  | ( column     & 0x0F));  // lower nibble   
00806   _sendCommand(SET_HIGHER_COLUMN | ((column>>4) & 0x0F));  // higher nibble     
00807 }
00808 
00809 
00810 /** 
00811  *  @brief Set Page Start (for Page Addressing Mode only)
00812  *  @param uint8_t page page start (valid range PAGE0 - PAGE7)
00813  */    
00814 void SSD1308::setPageStartForPageAddressingMode(uint8_t page) {
00815 
00816   page = page & MAX_PAGE;
00817 
00818   _sendCommand(SET_PAGE_START_ADDRESS | page);
00819  
00820 }
00821 
00822 
00823 /** @brief Set Contrast
00824  *  @param uint8_t contrast (valid range 0x00 (lowest) - 0xFF (highest))
00825 */
00826 void SSD1308::setContrastControl(uint8_t contrast) {
00827   
00828     _sendCommand(SET_CONTRAST, contrast);  
00829 } 
00830 
00831 /** @brief Enable Display
00832 */ 
00833 void SSD1308::setDisplayOn() {
00834   _sendCommand(SET_DISPLAY_POWER_ON);
00835 }
00836 
00837 /** @brief Disable Display
00838 */ 
00839 void SSD1308::setDisplayOff() {
00840   _sendCommand(SET_DISPLAY_POWER_OFF);
00841 }
00842 
00843 /** @brief Enable or Disable Display
00844  *  @param bool on
00845  */
00846 void SSD1308::setDisplayPower(bool on) {
00847   if (on) {
00848     setDisplayOn();
00849   } else {
00850     setDisplayOff();
00851   }
00852 }
00853 
00854 /** @brief Show White pixels on Black background
00855  */ 
00856 void SSD1308::setDisplayNormal() {
00857   _sendCommand(SET_NORMAL_DISPLAY);
00858 }
00859 
00860 /** @brief Show Black pixels on White background
00861  */ 
00862 void SSD1308::setDisplayInverse() {
00863   _sendCommand(SET_INVERSE_DISPLAY);
00864 }
00865 
00866 /** @brief Blink display by fading in and out over a set number of frames
00867  *  @param bool on
00868  */
00869 void SSD1308::setDisplayBlink(bool on){
00870   if (on) {
00871     _sendCommand(SET_FADE_BLINK, (BLINK_ENABLE | FADE_INTERVAL_128_FRAMES));
00872   }
00873   else {  
00874     _sendCommand(SET_FADE_BLINK, FADE_BLINK_DISABLE);  
00875   }
00876 }       
00877 
00878 
00879 /** @brief Fade out display in set number of frames
00880  *  @param bool on
00881  */
00882 void SSD1308::setDisplayFade(bool on) {
00883   if (on) {
00884     _sendCommand(SET_FADE_BLINK, (FADE_OUT_ENABLE | FADE_INTERVAL_128_FRAMES));
00885   }
00886   else {  
00887     _sendCommand(SET_FADE_BLINK, FADE_BLINK_DISABLE);  
00888   }
00889 }    
00890 
00891 /** @brief Display Flip (Left/Right, Up/Down)
00892  *  @param bool left flip Left/Right
00893  *  @param bool down flip Up/Down
00894  */
00895 void SSD1308::setDisplayFlip(bool left, bool down) {
00896   if (left) {
00897     // column address   0 is mapped to SEG0 (Reset)    
00898     _sendCommand(SET_SEGMENT_REMAP_0);
00899   }
00900   else {
00901     // column address 127 is mapped to SEG0    
00902     _sendCommand(SET_SEGMENT_REMAP_127);
00903   }  
00904 
00905   if (down) {
00906     // Reset mode
00907     _sendCommand(SET_COMMON_REMAP_0);    
00908   }
00909   else {
00910     // Flip Up/Down (Need to rewrite display before H effect shows)
00911     _sendCommand(SET_COMMON_REMAP_63);        
00912   }  
00913 
00914 }
00915 
00916 /** @brief Sets Internal Iref
00917  */
00918 void SSD1308::setInternalIref() {
00919 //  uint8_t cmds[2] = {SET_IREF_SELECTION, INTERNAL_IREF};
00920 //  _sendCommands(2, cmds); 
00921   
00922   _sendCommand(SET_IREF_SELECTION, INTERNAL_IREF);   
00923 }
00924 
00925 /** @brief Sets External Iref (default)
00926  */
00927 void SSD1308::setExternalIref() {
00928 //  uint8_t cmds[2] = {SET_IREF_SELECTION, EXTERNAL_IREF};
00929 //  _sendCommands(2, cmds); 
00930   _sendCommand(SET_IREF_SELECTION, EXTERNAL_IREF); 
00931 }
00932 
00933 
00934 /** @brief Shows All Pixels On
00935  */
00936 void SSD1308::setEntireDisplayOn(){
00937   _sendCommand(SET_ENTIRE_DISPLAY_ON); 
00938 }
00939 
00940 /** @brief Shows Pixels as RAM content
00941  */
00942 void SSD1308::setEntireDisplayRAM(){
00943   _sendCommand(SET_DISPLAY_GDDRAM); 
00944 }
00945 
00946 /** @brief Shows Pixels On or as RAM content
00947  *  @param bool on (true is All on, false is RAM content)
00948  */
00949 void SSD1308::setEntireDisplay(bool on){
00950   if (on) {
00951     setEntireDisplayOn();  // All Pixels on
00952   }
00953   else {  
00954     setEntireDisplayRAM(); // Pixels are RAM content
00955   }
00956 }
00957 
00958 
00959 /** @brief Horizontal scroll by one column per interval
00960  *  @param bool left select Left/Right scroll
00961  *  @param uint8_t start_page begin page   (0..MAX_PAGE)
00962  *  @param uint8_t end_page   end page     (start_page..MAX_PAGE)                     
00963  *  @param uint8_t interval   scroll interval in frames (see codes above)                      
00964  */  
00965 void SSD1308::setContinuousHorizontalScroll(bool left, uint8_t start_page, uint8_t end_page, uint8_t interval) {
00966   if (left) {
00967     _sendCommand(SET_LEFT_HOR_SCROLL, 0x00, start_page, interval, end_page, 0x00, 0xFF);  // Scroll Left
00968   }
00969   else {  
00970     _sendCommand(SET_RIGHT_HOR_SCROLL, 0x00, start_page, interval, end_page, 0x00, 0xFF); // Scroll Right  
00971   }
00972 
00973 }
00974 
00975 
00976 /** @brief Horizontal and Vertical scroll by one column per interval
00977  *  @param bool left select Left/Right scroll
00978  *  @param uint8_t start_page begin page   (0..MAX_PAGE)
00979  *  @param uint8_t end_page   end page     (start_page..MAX_PAGE)                     
00980  *  @param uint8_t offset     vert offset  (0x01..0x63)                       
00981  *  @param uint8_t interval   scroll interval in frames (see codes above)                       
00982  */  
00983 void SSD1308::setContinuousVerticalAndHorizontalScroll(bool left, uint8_t start_page, uint8_t end_page, 
00984                                                        uint8_t offset, uint8_t interval) {
00985   if (left) {
00986     _sendCommand(SET_VERT_LEFT_HOR_SCROLL, 0x00, start_page, interval, end_page, offset);  // Scroll Left
00987   }
00988   else {  
00989     _sendCommand(SET_VERT_RIGHT_HOR_SCROLL, 0x00, start_page, interval, end_page, offset); // Scroll Right  
00990   }
00991                                                        
00992 }    
00993 
00994 /** @brief Set Vertical scroll area
00995  *  @param uint8_t topRowsFixed      fixed rows   (0..MAX_ROW)                     
00996  *  @param uint8_t scrollRowsoffset  scroll rows  (topRowsFixed..ROWS)                       
00997  */  
00998 void SSD1308::setVerticalScrollArea(uint8_t topRowsFixed, uint8_t scrollRows) { 
00999    
01000   if ((topRowsFixed + scrollRows) > ROWS) {
01001      scrollRows = ROWS - topRowsFixed; 
01002   };
01003   
01004   _sendCommand(SET_VERTICAL_SCROLL_AREA, topRowsFixed, scrollRows); 
01005 }
01006 
01007 /** @brief Activate or Deactivate Horizontal and Vertical scroll
01008  *  @brief Note: after deactivating scrolling, the RAM data needs to be rewritten
01009  *  @param bool on activate scroll 
01010  */  
01011 void SSD1308::setDisplayScroll(bool on) {
01012   if (on) {
01013     _sendCommand(SET_ACTIVATE_SCROLL);   // Scroll on
01014   }
01015   else {  
01016     _sendCommand(SET_DEACTIVATE_SCROLL); // Scroll off  
01017   }
01018 }
01019 
01020 
01021 
01022 /** @brief Low level Init
01023  *  @brief Init the configuration registers in accordance with the datasheet
01024  */
01025 void SSD1308::_init() {
01026 
01027   _sendCommand(SET_DISPLAY_POWER_OFF);      // 0xAE
01028   
01029   // column address   0 is mapped to SEG0 (Reset)    
01030   // row address   0 is mapped to COM0 (Reset)      
01031   _sendCommand(SET_SEGMENT_REMAP_0);        // 0xA0 (Reset)
01032   _sendCommand(SET_COMMON_REMAP_0);         // 0xC0 (Reset) 
01033 
01034   setDisplayStartLine(0);                   // 0x40 (Reset) 
01035   
01036   _sendCommand(SET_COMMON_CONF, COMMON_BASE | COMMON_ALTERNATIVE | COMMON_LEFTRIGHT_NORMAL); // 0xDA, 0x12 (Reset)
01037 
01038   // Pagemode or Horizontal mode
01039 //  setPageAddressingMode();                  // 0x20, 0x02 (Reset)  
01040 //  setColumnStartForPageAddressingMode(0);   // 0x00, 0x10 (Reset = Column 0)
01041 //  setPageStartForPageAddressingMode(PAGE_0);// 0xBO       (Reset = Page 0)
01042   setHorizontalAddressingMode();            // 0x20, 0x00 (Non-Reset)
01043   setColumnAddress (0, MAX_COL);             // 0x21, 0x00, 0x37 (Reset)
01044   setPageAddress (0, MAX_PAGE);              // 0x22, 0x00, 0x07 (Reset)
01045 
01046   setExternalIref();                        // 0xAD, 0x10 (Reset)
01047   
01048   _sendCommand(SET_DISPLAY_CLOCK, 0x70);    // 0xD5, 0x70 (Reset = 0x80)
01049   _sendCommand(SET_PRECHARGE_TIME, 0x21);   // 0xD9, 0x21 (Reset = 0x22)
01050   _sendCommand(SET_VCOMH_DESELECT_LEVEL, 0x30); // 0xDB, 0x30 (Reset = 0x20)  
01051   _sendCommand(SET_MULTIPLEX_RATIO, 0x3F);  // 0xA8, 0x3F (Reset)  
01052   _sendCommand(SET_DISPLAY_OFFSET, 0x00);   // 0xD3, 0x00 (Reset)  
01053   
01054   _sendCommand(SET_CONTRAST, 0x7F);         // 0x81, 0x7F (Reset)
01055 
01056   _sendCommand(SET_NORMAL_DISPLAY);         // 0xA6 (Reset)
01057   
01058   setEntireDisplayRAM();                    // 0xA4 (Reset)
01059   setDisplayScroll(false);
01060   
01061   clearDisplay();   
01062   
01063   _sendCommand(SET_DISPLAY_POWER_ON);       // 0xAF
01064 }
01065