KS0108 GLCD on I2C

Hi,
I am still developing a code to run the GLCD by using two PCF8574AN IO Expanders.
The main problem i have at the moment is that the programm 'freezes' at a random point after being started.
Is there someone who might want to help me to sort some of the problems out? The code is based on the original KS0108 code in the Playground.

ks0108.h

/*
  ks0108.h - Arduino library support for ks0108 and compatable graphic LCDs
  Copyright (c)2008 Michael Margolis All right reserved
  mailto:memargolis@hotmail.com?subject=KS0108_Library 

  This library is based on version 1.1 of the excellent ks0108 graphics routines written and
  copyright by Fabian Maximilian Thiele. His sitelink is  
  dead but you can obtain a copy of his original work here:
  http://www.scienceprog.com/wp-content/uploads/2007/07/glcd_ks0108.zip

  Code changes include conversion to an Arduino C++ library, adding more
  flexibility in port addressing and improvements in I/O speed. The interface 
  has been made more Arduino friendly and some convenience functions added. 

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 

  Version:   1.0  - May 8 2008 - initial release
  Version:   1.0a - Sept 1 2008 - simplified command pin defines  
  Version:   1.0b - Sept 18 2008 - replaced <wiring.h> with boolean typedef for rel 0012  
*/

#include <inttypes.h>
//#include <wiring.h> // for boolean
typedef uint8_t boolean;
typedef uint8_t byte;
#include <avr/pgmspace.h>


#ifndef      KS0108_H
#define KS0108_H

/*****************************************************************/
/*      Configuration for assigning LCD pins to the PCF8574      */
/*****************************************************************/


//Control Chip
#define TWI1_ADDRESS B0111000  //0x38 //(LLL)
//Data Chip
#define TWI2_ADDRESS B0111001 //0x39 //(LLH)


//The defined bits correspond to the digital pins on the PCF8574. Check the Datasheet for further information.
#define CSEL1                        0            // CS1 Bit   // swap pin assignments with CSEL2 if left/right image is reversed
#define CSEL2                        1            // CS2 Bit
#define R_W                              2            // R/W Bit
#define D_I                              3            // D/I Bit 
#define EN                              4            // EN Bit
#define RES                              5            // check if needed with your controller, otherwise uncomment

//The Databits of the LCD (db0 to db7) correspond to bits 0 to 7 on the second PCF8574. You only have to provide the address






////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////



/*****************************************************************/
/*      Panel Configuration                                      */
/*****************************************************************/

// Chips
#define CHIP1                        0x00
#define CHIP2                        0x01
#ifdef HD44102 
#define CHIP_WIDTH          50          // pixels per chip
#else
#define CHIP_WIDTH          64 
#endif

// Commands
#ifdef HD44102 
#define LCD_ON                        0x39
#define LCD_OFF                        0x38
#define LCD_DISP_START            0x3E   // Display start page 0
#else
#define LCD_ON                        0x3F
#define LCD_OFF                        0x3E
#define LCD_DISP_START            0xC0
#endif

#define LCD_SET_ADD                  0x40
#define LCD_SET_PAGE            0xB8


// Colors
#define BLACK                        0xFF
#define WHITE                        0x00

// useful user contants
#define NON_INVERTED false
#define INVERTED     true

// Font Indices
#define FONT_LENGTH                  0
#define FONT_FIXED_WIDTH      2
#define FONT_HEIGHT                  3
#define FONT_FIRST_CHAR            4
#define FONT_CHAR_COUNT            5
#define FONT_WIDTH_TABLE      6

#ifdef HD44102 
#define DISPLAY_WIDTH 100
#define DISPLAY_HEIGHT 32
#else
#define DISPLAY_WIDTH 128
#define DISPLAY_HEIGHT 64
#endif

// Uncomment for slow drawing
// #define DEBUG

typedef struct {
      uint8_t x;
      uint8_t y;
      uint8_t page;
} lcdCoord;

typedef uint8_t (*FontCallback)(const uint8_t*);

uint8_t ReadFontData(const uint8_t* ptr);      //Standard Read Callback

#define DrawVertLine(x, y, length, color) FillRect(x, y, 0, length, color)
#define DrawHoriLine(x, y, length, color) FillRect(x, y, length, 0, color)
#define DrawCircle(xCenter, yCenter, radius, color) DrawRoundRect(xCenter-radius, yCenter-radius, 2*radius, 2*radius, radius, color)
#define ClearScreen() FillRect(0, 0, (DISPLAY_WIDTH-1), (DISPLAY_HEIGHT-1), WHITE)

class ks0108  // shell class for ks0108 glcd code
{
  private:
    lcdCoord                  Coord;
    boolean                        Inverted;  // changed type to boolean
    FontCallback          FontRead;
    uint8_t                        FontColor;
    const uint8_t*            Font;
      uint8_t ReadData(void);  // TODO this was inline !!!
      uint8_t DoReadData(uint8_t first);
      void WriteData(uint8_t data);
      void WriteCommand(uint8_t cmd, uint8_t chip);
      inline void Enable(void);
  public:
    ks0108();
      // Control functions
      void Init(boolean invert);
    void GotoXY(uint8_t x, uint8_t y);
      // Graphic Functions
    void DrawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t color);
    void DrawRect(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t color);
      void DrawRoundRect(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t radius, uint8_t color);
    void FillRect(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t color);
    void InvertRect(uint8_t x, uint8_t y, uint8_t width, uint8_t height);
    void SetInverted(boolean invert);
    void SetDot(uint8_t x, uint8_t y, uint8_t color);

    // Font Functions
    void SelectFont(const uint8_t* font, uint8_t color=BLACK, FontCallback callback=ReadFontData); // defualt arguments added, callback now last arg
    int PutChar(char c);
    void Puts(char* str);
    void Puts_P(PGM_P str);
      void PrintNumber(long n);

    uint8_t CharWidth(char c);
    uint16_t StringWidth(char* str);
    uint16_t StringWidth_P(PGM_P str);

      //Functions to make TWI work
      void writeCommandTWI(int pin, int val);
      byte getTWIDATA(byte address);
      void setCommandTWI(uint8_t cmd);
      void setDataTWI(uint8_t data);
};

extern ks0108 GLCD;    
#endif

ks0108.cpp (Part 1)

#include "ks0108.h"
extern "C" {
#include <inttypes.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <wiring.h> // added 18 Sept 2008 for Arduino release 0012
}

#include <Wire.h>
//#include "HardwareSerial.h" //for debug
//#define GLCD_DEBUG  // uncomment this if you want to slow down drawing to see how pixels are set

void ks0108::DrawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t color) {
      uint8_t length, i, y, yAlt, xTmp, yTmp;
      int16_t m;
      //
      // vertical line
      //
      if(x1 == x2) {
            // x1|y1 must be the upper point
            if(y1 > y2) {
                  yTmp = y1;
                  y1 = y2;
                  y2 = yTmp;
            }
            this->DrawVertLine(x1, y1, y2-y1, color);
      
      //
      // horizontal line
      //
      } else if(y1 == y2) {      
            // x1|y1 must be the left point
            if(x1 > x2) {
                  xTmp = x1;
                  x1 = x2;
                  x2 = xTmp;
            }
            this->DrawHoriLine(x1, y1, x2-x1, color);
      
      //
      // schiefe line :)
      //
      } else {
            // angle >= 45°
            if((y2-y1) >= (x2-x1) || (y1-y2) >= (x2-x1)) {
                  // x1 must be smaller than x2
                  if(x1 > x2) {
                        xTmp = x1;
                        yTmp = y1;
                        x1 = x2;
                        y1 = y2;
                        x2 = xTmp;
                        y2 = yTmp;
                  }
            
                  length = x2-x1;            // not really the length :)
                  m = ((y2-y1)*200)/length;
                  yAlt = y1;
                  
                  for(i=0; i<=length; i++) {
                        y = ((m*i)/200)+y1;
                        
                        if((m*i)%200 >= 100)
                              y++;
                        else if((m*i)%200 <= -100)
                              y--;
                        
                        this->DrawLine(x1+i, yAlt, x1+i, y, color);
                        
                        if(length <= (y2-y1) && y1 < y2)
                              yAlt = y+1;
                        else if(length <= (y1-y2) && y1 > y2)
                              yAlt = y-1;
                        else
                              yAlt = y;
                  }
            
            // angle < 45°
            } else {
                  // y1 must be smaller than y2
                  if(y1 > y2) {
                        xTmp = x1;
                        yTmp = y1;
                        x1 = x2;
                        y1 = y2;
                        x2 = xTmp;
                        y2 = yTmp;
                  }
                  
                  length = y2-y1;
                  m = ((x2-x1)*200)/length;
                  yAlt = x1;
                  
                  for(i=0; i<=length; i++) {
                        y = ((m*i)/200)+x1;
                        
                        if((m*i)%200 >= 100)
                              y++;
                        else if((m*i)%200 <= -100)
                              y--;
                        
                        this->DrawLine(yAlt, y1+i, y, y1+i, color);
                        if(length <= (x2-x1) && x1 < x2)
                              yAlt = y+1;
                        else if(length <= (x1-x2) && x1 > x2)
                              yAlt = y-1;
                        else
                              yAlt = y;
                  }
            }
      }
}

void ks0108::DrawRect(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t color) {
      DrawHoriLine(x, y, width, color);                        // top
      DrawHoriLine(x, y+height, width, color);            // bottom
      DrawVertLine(x, y, height, color);                  // left
      DrawVertLine(x+width, y, height, color);            // right
}

void ks0108::DrawRoundRect(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t radius, uint8_t color) {
        int16_t tSwitch, x1 = 0, y1 = radius;
        tSwitch = 3 - 2 * radius;
      
      while (x1 <= y1) {
          this->SetDot(x+radius - x1, y+radius - y1, color);
          this->SetDot(x+radius - y1, y+radius - x1, color);

          this->SetDot(x+width-radius + x1, y+radius - y1, color);
          this->SetDot(x+width-radius + y1, y+radius - x1, color);
          
          this->SetDot(x+width-radius + x1, y+height-radius + y1, color);
          this->SetDot(x+width-radius + y1, y+height-radius + x1, color);

          this->SetDot(x+radius - x1, y+height-radius + y1, color);
          this->SetDot(x+radius - y1, y+height-radius + x1, color);

          if (tSwitch < 0) {
                tSwitch += (4 * x1 + 6);
          } else {
                tSwitch += (4 * (x1 - y1) + 10);
                y1--;
          }
          x1++;
      }
              
      this->DrawHoriLine(x+radius, y, width-(2*radius), color);                  // top
      this->DrawHoriLine(x+radius, y+height, width-(2*radius), color);      // bottom
      this->DrawVertLine(x, y+radius, height-(2*radius), color);                  // left
      this->DrawVertLine(x+width, y+radius, height-(2*radius), color);      // right
}

/*
 * Hardware-Functions 
 */
void ks0108::FillRect(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t color) {
      uint8_t mask, pageOffset, h, i, data;
      height++;
      
      pageOffset = y%8;
      y -= pageOffset;
      mask = 0xFF;
      if(height < 8-pageOffset) {
            mask >>= (8-height);
            h = height;
      } else {
            h = 8-pageOffset;
      }
      mask <<= pageOffset;
      
      this->GotoXY(x, y);
      for(i=0; i<=width; i++) {
            data = this->ReadData();
            
            if(color == BLACK) {
                  data |= mask;
            } else {
                  data &= ~mask;
            }

            this->WriteData(data);
      }
      
      while(h+8 <= height) {
            h += 8;
            y += 8;
            this->GotoXY(x, y);
            
            for(i=0; i<=width; i++) {
                  this->WriteData(color);
            }
      }
      
      if(h < height) {
            mask = ~(0xFF << (height-h));
            this->GotoXY(x, y+8);
            
            for(i=0; i<=width; i++) {
                  data = this->ReadData();
            
                  if(color == BLACK) {
                        data |= mask;
                  } else {
                        data &= ~mask;
                  }
      
                  this->WriteData(data);
            }
      }
}

void ks0108::InvertRect(uint8_t x, uint8_t y, uint8_t width, uint8_t height) {
      uint8_t mask, pageOffset, h, i, data, tmpData;
      height++;
      
      pageOffset = y%8;
      y -= pageOffset;
      mask = 0xFF;
      if(height < 8-pageOffset) {
            mask >>= (8-height);
            h = height;
      } else {
            h = 8-pageOffset;
      }
      mask <<= pageOffset;
      
      this->GotoXY(x, y);
      for(i=0; i<=width; i++) {
            data = this->ReadData();
            tmpData = ~data;
            data = (tmpData & mask) | (data & ~mask);
            this->WriteData(data);
      }
      
      while(h+8 <= height) {
            h += 8;
            y += 8;
            this->GotoXY(x, y);
            
            for(i=0; i<=width; i++) {
                  data = this->ReadData();
                  this->WriteData(~data);
            }
      }
      
      if(h < height) {
            mask = ~(0xFF << (height-h));
            this->GotoXY(x, y+8);
            
            for(i=0; i<=width; i++) {
                  data = this->ReadData();
                  tmpData = ~data;
                  data = (tmpData & mask) | (data & ~mask);
                  this->WriteData(data);
            }
      }
}

void ks0108::SetInverted(boolean invert) {  // changed type to boolean
      if(this->Inverted != invert) {
            this->InvertRect(0,0,DISPLAY_WIDTH-1,DISPLAY_HEIGHT-1);
            this->Inverted = invert;
      }
}

void ks0108::SetDot(uint8_t x, uint8_t y, uint8_t color) {
      uint8_t data;
      
      this->GotoXY(x, y-y%8);                              // read data from display memory
      data = this->ReadData();
      
      if(color == BLACK) {
            data |= 0x01 << (y%8);                        // set dot
      } else {
            data &= ~(0x01 << (y%8));                  // clear dot
      }
      
      this->WriteData(data);                              // write data back to display
}

//
// Font Functions
//

uint8_t ReadFontData(const uint8_t* ptr) {  // note this is a static function
      return pgm_read_byte(ptr);
}

void ks0108::SelectFont(const uint8_t* font,uint8_t color, FontCallback callback) {
      this->Font = font;
      this->FontRead = callback;
      this->FontColor = color;
}

void ks0108::PrintNumber(long n){
   byte buf[10];  // prints up to 10 digits  
   byte i=0;
   if(n==0)
         PutChar('0');
   else{
       if(n < 0){
        PutChar('-');
            n = -n;
       }
     while(n>0 && i <= 10){
         buf[i++] = n % 10;  // n % base
         n /= 10;   // n/= base
       }
       for(; i >0; i--)
             this->PutChar((char) (buf[i-1] < 10 ? '0' + buf[i-1] : 'A' + buf[i-1] - 10));        
   }
}

int ks0108::PutChar(char c) {
      uint8_t width = 0;
      uint8_t height = this->FontRead(this->Font+FONT_HEIGHT);
      uint8_t bytes = (height+7)/8;
      
      uint8_t firstChar = this->FontRead(this->Font+FONT_FIRST_CHAR);
      uint8_t charCount = this->FontRead(this->Font+FONT_CHAR_COUNT);
      
      uint16_t index = 0;
      uint8_t x = this->Coord.x, y = this->Coord.y;
      
      if(c < firstChar || c >= (firstChar+charCount)) {
            return 1;
      }
      c-= firstChar;
      
      // read width data, to get the index
      for(uint8_t i=0; i<c; i++) {  
             index += this->FontRead(this->Font+FONT_WIDTH_TABLE+i);
      }

      index = index*bytes+charCount+FONT_WIDTH_TABLE;
      width = this->FontRead(this->Font+FONT_WIDTH_TABLE+c);
      
      // last but not least, draw the character
      for(uint8_t i=0; i<bytes; i++) {
            uint8_t page = i*width;
            for(uint8_t j=0; j<width; j++) {
                  uint8_t data = this->FontRead(this->Font+index+page+j);
                  
                  if(height < (i+1)*8) {
                        data >>= (i+1)*8-height;
                  }
                  
                  if(this->FontColor == BLACK) {
                        this->WriteData(data);
                  } else {
                        this->WriteData(~data);
                  }
            }
            // 1px gap between chars
            if(this->FontColor == BLACK) {
                  this->WriteData(0x00);
            } else {
                  this->WriteData(0xFF);
            }
            this->GotoXY(x, this->Coord.y+8);
      }
      this->GotoXY(x+width+1, y);

      return 0;
}

void ks0108::Puts(char* str) {
      int x = this->Coord.x;
      while(*str != 0) {
            if(*str == '\n') {
                  this->GotoXY(x, this->Coord.y+this->FontRead(this->Font+FONT_HEIGHT));
            } else {
                  this->PutChar(*str);
            }
            str++;
      }
}

void ks0108::Puts_P(PGM_P str) {
      int x = this->Coord.x;
      while(pgm_read_byte(str) != 0) {
            if(pgm_read_byte(str) == '\n') {
                  this->GotoXY(x, this->Coord.y+this->FontRead(this->Font+FONT_HEIGHT));
            } else {
                  this->PutChar(pgm_read_byte(str));
            }
            str++;
      }
}

uint8_t ks0108::CharWidth(char c) {
      uint8_t width = 0;
      uint8_t firstChar = this->FontRead(this->Font+FONT_FIRST_CHAR);
      uint8_t charCount = this->FontRead(this->Font+FONT_CHAR_COUNT);
      
      // read width data
      if(c >= firstChar && c < (firstChar+charCount)) {
            c -= firstChar;
            width = this->FontRead(this->Font+FONT_WIDTH_TABLE+c)+1;
      }
      
      return width;
}

uint16_t ks0108::StringWidth(char* str) {
      uint16_t width = 0;
      
      while(*str != 0) {
            width += this->CharWidth(*str++);
      }
      
      return width;
}

uint16_t ks0108::StringWidth_P(PGM_P str) {
      uint16_t width = 0;
      
      while(pgm_read_byte(str) != 0) {
            width += this->CharWidth(pgm_read_byte(str++));
      }
      
      return width;
}

cpp (part II)

void ks0108::GotoXY(uint8_t x, uint8_t y) {
      uint8_t chip = CHIP1, cmd;
      
      if(x > DISPLAY_WIDTH-1) x = 0;                              // ensure that coordinates are legal
      if(y > DISPLAY_HEIGHT-1)  y = 0;
      
      this->Coord.x = x;                                                // save new coordinates
      this->Coord.y = y;
      this->Coord.page = y/8;
      
      if(x >= CHIP_WIDTH) {                                          // select the right chip
            x -= CHIP_WIDTH;
            chip = CHIP2;
      }

#ifdef HD44102 
      this->WriteCommand(LCD_DISP_START, CHIP1);            // display start line = 0
      this->WriteCommand(LCD_DISP_START, CHIP2);

      cmd = (this->Coord.page << 6 ) | x;
//      this->WriteCommand(cmd,chip);
      this->WriteCommand(cmd, CHIP1);            
      this->WriteCommand(cmd, CHIP2);
#else
      cmd = LCD_SET_ADD | x;
      this->WriteCommand(cmd, chip);                              // set x address on active chip
      
      cmd = LCD_SET_PAGE | this->Coord.page;                  // set y address on both chips
      this->WriteCommand(cmd, CHIP1);
      this->WriteCommand(cmd, CHIP2);
#endif
}

void ks0108::Init(boolean invert) {  
      /*pinMode(D_I,OUTPUT);       
    pinMode(R_W,OUTPUT);       
    pinMode(EN,OUTPUT);       
    pinMode(CSEL1,OUTPUT);
    pinMode(CSEL2,OUTPUT);
      pinMode(13,OUTPUT); // for testing only !!!*/

            //Serial.println("INIT");
      
      this->Coord.x = 0;
      this->Coord.y = 0;
      this->Coord.page = 0;
      
      this->Inverted = invert;
      
            //Serial.println("INIT: WRITE COMMANDS 1");
      this->WriteCommand(LCD_ON, CHIP1);                        // power on
            //Serial.println("INIT: WRITE COMMANDS 2");
      this->WriteCommand(LCD_ON, CHIP2);

            //Serial.println("INIT: WRITE COMMANDS 3");
      this->WriteCommand(LCD_DISP_START, CHIP1);            // display start line = 0
            //Serial.println("INIT: WRITE COMMANDS 4");
      this->WriteCommand(LCD_DISP_START, CHIP2);

            //Serial.println("INIT: CLS");
      this->ClearScreen();                                          // display clear
            //Serial.println("INIT: GOTO");
      this->GotoXY(0,0);
}

static void delay450ns(void){   // delay 450 nanoseconds
    asm volatile("nop\n\t" 
                         "nop\n\t"
                         "nop\n\t"
                         "nop\n\t"  // todo, remove some nops if clock is less than 16mhz
                         "nop\n\t"
                         ::);
}

 __inline__ void ks0108::Enable(void) {  
      writeCommandTWI(EN, 1);
      delay450ns();
      writeCommandTWI(EN, 0);
//      for(volatile uint8_t i=0; i<8; i++);  // big delay loop in Fabian's code, was 5.7us, replaced by call to 450ns delay function
      delay450ns();

}

uint8_t ks0108::DoReadData(uint8_t first) {
      uint8_t data;
      
      /*lcdDataOut(0x00);
      lcdDataDir(0x00);                              // data port is input*/
      //setDataTWI(0x00);
      if(this->Coord.x < CHIP_WIDTH) {
            writeCommandTWI(CSEL2,0);
            writeCommandTWI(CSEL1,1);
      } else if(this->Coord.x >= CHIP_WIDTH) {
      writeCommandTWI(CSEL1,0);
      writeCommandTWI(CSEL2,1);
      }
      if(this->Coord.x == CHIP_WIDTH && first) {            // chip2 X-address = 0
            this->WriteCommand(LCD_SET_ADD, CHIP2);       // wuff wuff
      }
      
      writeCommandTWI(D_I,1);      
      writeCommandTWI(R_W,1);      
      
      writeCommandTWI(EN,1);      
      delay450ns();

      data = getTWIDATA(TWI2_ADDRESS);

            
      writeCommandTWI(EN,0);      
//      for(volatile uint8_t i=0; i<8; i++);  // big delay loop in Fabian's code, was 5.7us, replaced by 450ns delay below
      delay450ns();


      this->GotoXY(this->Coord.x, this->Coord.y);
      
      if(this->Inverted)
            data = ~data;
      return data;
}

inline uint8_t ks0108::ReadData(void) {  
      this->DoReadData(1);                        // dummy read
      return this->DoReadData(0);                  // "real" read
}

void ks0108::WriteCommand(uint8_t cmd, uint8_t chip) {
      //Serial.println("WriteCommand: BEFORE IF");
      if(chip == CHIP1) {      
            //Serial.println("WriteCommand: CHIP1 TWI 1");
            writeCommandTWI(CSEL2,0);                  // deselect chip 2
            //Serial.println("WriteCommand: CHIP1 TWI 2");
            writeCommandTWI(CSEL1,1);                  // select chip 1
      } else if(chip == CHIP2) {
            //Serial.println("WriteCommand: CHIP2 TWI 1");
        writeCommandTWI(CSEL1,0);                  // deselect chip 1
            //Serial.println("WriteCommand: CHIP2 TWI 2");
          writeCommandTWI(CSEL2,1);                  // select chip 2
      }
      
      //Serial.println("WriteCommand: DIRW TWI");
      writeCommandTWI(D_I,0);                              // D/I = 0
      writeCommandTWI(R_W,0);                              // R/W = 0      

      //Serial.println("WriteCommand: SET CMD");
    setDataTWI(cmd);
      
      //Serial.println("WriteCommand: ENABLE");
      this->Enable();                                    // enable
      
      //Serial.println("WriteCommand: SETDATATWI");
      setDataTWI(0x00);
}

void ks0108::WriteData(uint8_t data) {
      uint8_t displayData, yOffset;
#ifdef LCD_CMD_PORT      
      uint8_t cmdPort;      
#endif

#ifdef GLCD_DEBUG
      volatile uint16_t i;
      for(i=0; i<5000; i++);
#endif

      if(this->Coord.x >= DISPLAY_WIDTH)
            return;

      if(this->Coord.x < CHIP_WIDTH) {
            writeCommandTWI(CSEL2,0);                  // deselect chip 2
            writeCommandTWI(CSEL1,1);                  // select chip 1
      } else {
        writeCommandTWI(CSEL1,0);                  // deselect chip 1
          writeCommandTWI(CSEL2,1);                  // select chip 2
      }
#ifndef HD44102
      if(this->Coord.x == CHIP_WIDTH)                                          // chip2 X-address = 0
            this->WriteCommand(LCD_SET_ADD, CHIP2);
#endif      
      writeCommandTWI(D_I,1);      
      writeCommandTWI(R_W,0);      
      
      
      yOffset = this->Coord.y%8;
      if(yOffset != 0) {
            // first page
#ifdef LCD_CMD_PORT 
            cmdPort = LCD_CMD_PORT;                                    // save command port
#endif
            displayData = this->ReadData();
#ifdef LCD_CMD_PORT             
            LCD_CMD_PORT = cmdPort;                                    // restore command port
#else
      writeCommandTWI(D_I,1);      
      writeCommandTWI(R_W,0);      
      if(this->Coord.x < CHIP_WIDTH) {
            writeCommandTWI(CSEL2,0);                  // deselect chip 2
            writeCommandTWI(CSEL1,1);                  // select chip 1
      } else {
        writeCommandTWI(CSEL1,0);                  // deselect chip 1
          writeCommandTWI(CSEL2,1);                  // select chip 2
      }
#endif

            
            displayData |= data << yOffset;
            if(this->Inverted)
                  displayData = ~displayData;
            setDataTWI( displayData);                              // write data
            this->Enable();                                                // enable
            
            // second page
            this->GotoXY(this->Coord.x, this->Coord.y+8);
            
            displayData = this->ReadData();

#ifdef LCD_CMD_PORT             
            LCD_CMD_PORT = cmdPort;                                    // restore command port
#else            
      writeCommandTWI(D_I,1);      
      writeCommandTWI(R_W,0);      
      if(this->Coord.x < CHIP_WIDTH) {
            writeCommandTWI(CSEL2,0);                  // deselect chip 2
            writeCommandTWI(CSEL1,1);                  // select chip 1
      } else {
        writeCommandTWI(CSEL1,0);                  // deselect chip 1
          writeCommandTWI(CSEL2,1);                  // select chip 2
      }
#endif

            
            displayData |= data >> (8-yOffset);
            if(this->Inverted)
                  displayData = ~displayData;
            setDataTWI(displayData);            // write data
            this->Enable();                              // enable
            
            this->GotoXY(this->Coord.x+1, this->Coord.y-8);
      } else {
            if(this->Inverted)
                  data = ~data;
            setDataTWI(data);                        // write data
            this->Enable();                              // enable
            this->Coord.x++;
      }
      setDataTWI(0x00);
      
}

// class wrapper

ks0108::ks0108(){
    this->Inverted=0;
}

// Make one instance for the user
ks0108 GLCD = ks0108();



 void ks0108::setDataTWI(uint8_t data)
{
      //Serial.print("BEGIN: ");
      //Serial.print(TWI2_ADDRESS, BIN);   
  Wire.beginTransmission(TWI2_ADDRESS);
  //Serial.print(" DATA:");  
  Wire.send(data);
  //Serial.println(data, BIN);   
  Wire.endTransmission();  
}

void ks0108::setCommandTWI(uint8_t cmd)
{
      //Serial.print("BEGIN: ");
      //Serial.print(TWI1_ADDRESS, BIN); 
  Wire.beginTransmission(TWI1_ADDRESS);
      //Serial.print(" CMD:");  
      //Serial.println(cmd, BIN);   
 Wire.send(cmd);
  Wire.endTransmission();  
}


byte ks0108::getTWIDATA(byte address)
{
  //Serial.println("getTWI Start");
  byte _tmp = B00000000;
  
  //Serial.print("getTWI TMP = ");
  //Serial.println(_tmp, BIN);
  
  //Serial.print("get TWI: Request from ");
  //Serial.println((int)address, BIN);
  Wire.requestFrom((int)address, (int)1);   
  if(Wire.available()) {
    _tmp = Wire.receive();
  } 
  
  //Serial.print("IN: ");
  //Serial.println((int)_tmp, BIN);
  return _tmp;
}

void ks0108::writeCommandTWI(int pin, int val)
{
      byte tmp = B00000000;
      tmp = getTWIDATA(TWI1_ADDRESS);
//      Serial.print("wcTWI: Old byte:");
//      Serial.println(tmp, BIN);
      
      bitWrite(tmp,pin, val);
      
//      Serial.print("wcTWI: New byte:");
//      Serial.println(tmp, BIN);

      this->setCommandTWI(tmp);            
}

I will be happy to help but I don't have the io expanders. I will see if I can get hold something similar.

If not, I may try to use a seond arduino as the i2c receiver side. Not exactly the same but it may help simulate what you are trying to do.

hm, i dont know if you like to do such things, but i just checked - TI has those as samples. This might help you with that..?

And thanks for the offer of helping :0)

Been thinking about this and was wondering why you didn't choose to use a barebones arduino clone board instead of the io expanders. For $10 worth of components including PCB, or $15 for a complete kit you could get significant performance benefits from running the low level code on the second board.

to be honest: That never came to my mind. Thinking of it - i had in mind making a shield like connector with two sockets for the expanders.. but utilising a stand alone Arduino for it seems even far better.. hmm

edit
but first things first.. beginning with a I2C Expander double would be funny enough ;0)
What i dont see is the performance increase in using an Barebones. Sure - those Macroes that are used all over the place like DataOut(byte) are fast as hell, but from what i understand in the datasheets for the ks0108 you have all in all a bottleneck with that chip as it needs certain pauses all the time. I dont know the exact timing, but i guess it doesnt make such a big difference if you talk to the ks0108 via the expanders or via a barebones which you have to control as well - and most certainly through a protocol that is as fast or as slow as I2C?

The savings comes from two areas:

Not needing the sketch to wait for the chip to accept each command. You could send commands as fast as the i2c can cope and cache them on the receiving side while waiting for the chip to become ready. If the i2c is slower then the maximum time needed to perform any command then this may not matter, but some commands take a long time for the controller to process and you may need to pass the ready status back through i2c.

If you use a 328 chip, you would have enough RAM to hold all the pixels of the display so could avoid the significant overhead of having to do two reads of the GLCD controller for each pixel in order to get the data out of the chip

anyway, I will try to find some time later today to see if I can see whay you are having problems with the i2c.

thanks for taking the time later on :0)

I havent taken those acks into account, youre right about that one... it could be defenetly be faster using a bbArduino.. it'd be interestering to see some sort of benchmark to compare both.. ..or to make one..

Perhaps an even faster and cleaner approach is to support I2C at a higher level.

For example. If the following functions were implemented as single I2C calls then many low level reads and writes across the I2c interface could be eliminated.

void Init(boolean invert);
void GotoXY(uint8_t x, uint8_t y);
void SetDot(uint8_t x, uint8_t y, uint8_t color);
void ClearScreen(uint8_t color = WHITE);
int PutChar(char c);

I think that all the other public functions are implemented through these calls.

Hmm.. didnt know you could define high level I2C functions... or i do understand the meaning of it wrong. i thought one was bound to wire.transmit and so on?

Its high level in the sense that it is passing information for a high level function call rather then just reading and writing bytes to the LCD.

In other words, using I2C to make remote procedure calls.

For example, the SetDot function could write two byte through the wire.transmit interface and the other side would then call the many writeData and readData functions needed to actual move bytes into and out of the LCD. That way the sending side does not need to wait for each individual read and write to the LCD.

Interestering Idea.. that'd be something to follow... it's really interestering...

thats really mean! i begin to think about this idea more than following my current idea ;0)

I won't have the time to do much in the way of coding or testing of this (tied up with work things) but would be happy to post some example code for sending and receiving a remote function call to get you started if you do want to pursue that approach.

BTW, I think nkcelectronics is considering doing a backpack board for GLCD so you may want to contact them first to see if they will have something that meets your needs.

http://www.nkcelectronics.com/

hmm... i think i will speak to them about it.. thanks for that suggestion

i fear that i might have to drop the whole idea with those I2C port expanders. It seems to me i run out of memory in the init sequence with no chance to solve it..
so my next step will be having a 'slave' arduino that communicates via i2c with it's master to perform the duty of a port expander..
happy coding ^^

Alright.. it seems to work with two Arduinos. The whole thing is not exactly perfect but a good first step ^^
I am almost able to run the original example (though i have not yet managed to send strings.. but that will come on sunday).. It is a bit slower than the original code and there are still a few glitches.. but i think it is doable :0)
Current way:

GLCD <-> Arduino <----I2C -----> Arduino

That was fast, well done!

To send strings you need to have a way of knowing how long the string is. One method that works well for this kind of app is to send the string preceded by the number of characters. When the receiving side gets the key byte indicating a string, it uses the next byte to get the number of characters. You can use this in a loop to read that many bytes into a local buffer.

Hope that makes sense

Thats much like i want to do it.. have to see if i find the time today or tomorrow (got some diving and work to do.. ;0))

Sending and receiving strings works now... yet a small problem is still there.
I have also wrapped PrintNumber(long value):

Sender

    void PrintNumber(long n)
    {
      
      byte buf[4];      
        buf[0] = (byte) n;
        buf[1] = (byte) n >> 8;
        buf[2] = (byte) n >> 16;
        buf[3] = (byte) n >> 24;

      Wire.beginTransmission(addr); 
      Wire.send(0x22);      
      Wire.send(buf[0]);
      Wire.send(buf[1]);
      Wire.send(buf[2]);
      Wire.send(buf[3]);      
      Wire.endTransmission();       
    }

Receiver

      long value = (unsigned long)(Received[4] << 24) | (Received[3] << 16) | (Received[2] << 8) | Received[1];
      GLCD.PrintNumber(value);

Well, the code is rather self explainatory - Received is of course what is sent except that R[0] is a identifier for a keyword, nothing more.
Now.. when i send a number something unusual happens - either no text is shown anymore or the numbers are shown arround the 'spinner'.. thats rather strange i think...

edit
Interestering - i think it's a timing problem... When i run a serial.print in every printnumber call at least the numbers are shown right in the FPS area - where the Goto command is already done some time earlier.. the other numbers (showing the iteration) still go arround the spinner.. but then again - if i do these Serial.Printlns other parts of the problem stop working... it is extremely strange...