ks0108iLab.cpp

00001 
00002 /* This class was modified by Laurent Itti from the following.
00003    Modifications include:
00004    - timing to work on a 16MHz arduino
00005    - new bargraph graphics
00006    - fix code throughout so that when a width and height are used, they
00007      are the actual drawn width and height (as opposed to width-1 and
00008      height-1). So FillRect(0, 0, 128, 164, BLACK) fills the entire
00009      128x64 screen.
00010 */
00011 
00012 /*
00013   ks0108.h - Arduino library support for ks0108 and compatable graphic LCDs
00014   Copyright (c)2008 Michael Margolis All right reserved
00015   mailto:memargolis@hotmail.com?subject=KS0108_Library 
00016 
00017   This library is based on version 1.1 of the excellent ks0108 graphics routines written and
00018   copyright by Fabian Maximilian Thiele. His sitelink is  
00019   dead but you can obtain a copy of his original work here:
00020   http://www.scienceprog.com/wp-content/uploads/2007/07/glcd_ks0108.zip
00021 
00022   Code changes include conversion to an Arduino C++ library, adding more
00023   flexibility in port addressing and improvements in I/O speed. The interface 
00024   has been made more Arduino friendly and some convenience functions added. 
00025 
00026   This library is distributed in the hope that it will be useful,
00027   but WITHOUT ANY WARRANTY; without even the implied warranty of
00028   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
00029 
00030   Version:   1.0 - May 8 2008
00031   
00032 */
00033 #include "ks0108iLab.h"
00034 extern "C" {
00035 #include <inttypes.h>
00036 #include <avr/io.h>
00037 #include <avr/pgmspace.h>
00038 #include <wiring.h> // added 18 Sept 2008 for Arduino release 0012
00039 
00040 }
00041 //#define GLCD_DEBUG  // uncomment this if you want to slow down drawing to see how pixels are set
00042 
00043 // ######################################################################
00044 void ks0108iLab::DrawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t color) {
00045   uint8_t length, i, y, yAlt, xTmp, yTmp;
00046   int16_t m;
00047   //
00048   // vertical line
00049   //
00050   if (x1 == x2) {
00051     // x1|y1 must be the upper point
00052     if (y1 > y2) this->DrawVertLine(x1, y2, y1-y2+1, color);
00053     else this->DrawVertLine(x1, y1, y2-y1+1, color);
00054     //
00055     // horizontal line
00056     //
00057   } else if (y1 == y2) {        
00058     // x1|y1 must be the left point
00059     if (x1 > x2) this->DrawHoriLine(x2, y1, x1-x2+1, color);
00060     else this->DrawHoriLine(x1, y1, x2-x1+1, color);
00061     //
00062     // schiefe line :)
00063     //
00064   } else {
00065     // angle >= 45deg
00066     if ((y2-y1) >= (x2-x1) || (y1-y2) >= (x2-x1)) {
00067       // x1 must be smaller than x2
00068       if (x1 > x2) { xTmp = x1; yTmp = y1; x1 = x2; y1 = y2; x2 = xTmp; y2 = yTmp; }
00069       
00070       length = x2-x1;                // not really the length :)
00071       m = ((y2-y1)*200) / length;
00072       yAlt = y1;
00073       
00074       for (i = 0; i <= length; ++i) {
00075         y = ((m*i)/200)+y1;
00076         
00077         if ((m*i) % 200 >= 100) ++y;
00078         else if ((m*i) % 200 <= -100) --y;
00079         
00080         this->DrawLine(x1+i, yAlt, x1+i, y, color);
00081         
00082         if (length <= (y2-y1) && y1 < y2) yAlt = y+1;
00083         else if (length <= (y1-y2) && y1 > y2) yAlt = y-1;
00084         else yAlt = y;
00085       }
00086       // angle < 45deg
00087     } else {
00088       // y1 must be smaller than y2
00089       if (y1 > y2) { xTmp = x1; yTmp = y1; x1 = x2; y1 = y2; x2 = xTmp; y2 = yTmp; }
00090       
00091       length = y2 - y1;
00092       m = ((x2-x1)*200) / length;
00093       yAlt = x1;
00094       
00095       for (i = 0; i <= length; ++i) {
00096         y = ((m*i)/200)+x1;
00097         
00098         if ((m*i) % 200 >= 100) ++y;
00099         else if((m*i) % 200 <= -100) --y;
00100         
00101         this->DrawLine(yAlt, y1+i, y, y1+i, color);
00102         if (length <= (x2-x1) && x1 < x2) yAlt = y+1;
00103         else if (length <= (x1-x2) && x1 > x2) yAlt = y-1;
00104         else yAlt = y;
00105       }
00106     }
00107   }
00108 }
00109 
00110 // ######################################################################
00111 void ks0108iLab::DrawRect(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t color) {
00112   DrawHoriLine(x, y, width, color);            // top
00113   DrawHoriLine(x, y+height-1, width, color);   // bottom
00114   DrawVertLine(x, y, height, color);           // left
00115   DrawVertLine(x+width-1, y, height, color);   // right
00116 }
00117 
00118 // ######################################################################
00119 void ks0108iLab::DrawRoundRect(uint8_t x, uint8_t y, uint8_t width, uint8_t height,
00120                            uint8_t radius, uint8_t color) {
00121   int16_t tSwitch, x1 = 0, y1 = radius;
00122   tSwitch = 3 - 2 * radius;
00123   
00124   while (x1 <= y1) {
00125     this->SetDot(x+radius - x1, y+radius - y1, color);
00126     this->SetDot(x+radius - y1, y+radius - x1, color);
00127     
00128     this->SetDot(x+width-1-radius + x1, y+radius - y1, color);
00129     this->SetDot(x+width-1-radius + y1, y+radius - x1, color);
00130     
00131     this->SetDot(x+width-1-radius + x1, y+height-1-radius + y1, color);
00132     this->SetDot(x+width-1-radius + y1, y+height-1-radius + x1, color);
00133     
00134     this->SetDot(x+radius - x1, y+height-1-radius + y1, color);
00135     this->SetDot(x+radius - y1, y+height-1-radius + x1, color);
00136     
00137     if (tSwitch < 0) tSwitch += (4 * x1 + 6);
00138     else { tSwitch += (4 * (x1 - y1) + 10); --y1; }
00139     ++x1;
00140   }
00141   
00142   this->DrawHoriLine(x+radius, y, width-(2*radius), color);           // top
00143   this->DrawHoriLine(x+radius, y+height-1, width-(2*radius), color);  // bottom
00144   this->DrawVertLine(x, y+radius, height-(2*radius), color);          // left
00145   this->DrawVertLine(x+width-1, y+radius, height-(2*radius), color);  // right
00146 }
00147 
00148 /*
00149  * Hardware-Functions 
00150  */
00151 // ######################################################################
00152 void ks0108iLab::FillRect(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t color) {
00153   uint8_t h, i, data;
00154   uint8_t pageOffset = y % 8; // offset of top of rect wrt 8-pixel vertical pages
00155   y -= pageOffset;            // y rounded to lower 8-pixel page boundary
00156 
00157   // top rows: go from pageOffset down, either to the whole byte or
00158   // less if the height is smaller than pageOffset:
00159   uint8_t mask = 0xFF;
00160   if (height < 8 - pageOffset) { mask >>= (8-height); h = height; } else h = 8-pageOffset;
00161   mask <<= pageOffset;
00162         
00163   this->GotoXY(x, y);
00164   if (color == BLACK)
00165     for (i = 0; i < width; ++i) { data = this->ReadData(); data |= mask; this->WriteData(data); }
00166   else {
00167     mask = ~mask;
00168     for (i = 0; i < width; ++i) { data = this->ReadData(); data &= mask; this->WriteData(data); }
00169   }
00170 
00171   // bulk of the rectangle
00172   while (h+8 < height)
00173     { h += 8; y += 8; this->GotoXY(x, y); for (i = 0; i < width; ++i) this->WriteData(color); }
00174 
00175   // bottom rows (if bottom of rect on on an 8-row boundary)
00176   if (h < height) {
00177     mask = ~(0xFF << (height-h));
00178     this->GotoXY(x, y+8);
00179 
00180     if (color == BLACK)
00181       for (i = 0; i < width; ++i) { data = this->ReadData(); data |= mask; this->WriteData(data); }
00182     else {
00183       mask = ~mask;
00184       for (i = 0; i < width; ++i) { data = this->ReadData(); data &= mask; this->WriteData(data); }
00185     }
00186   }
00187 }
00188 
00189 // ######################################################################
00190 void ks0108iLab::InvertRect(uint8_t x, uint8_t y, uint8_t width, uint8_t height) {
00191   uint8_t mask, pageOffset, h, i, data, tmpData;
00192         
00193   pageOffset = y%8;
00194   y -= pageOffset;
00195   mask = 0xFF;
00196   if(height < 8-pageOffset) {
00197     mask >>= (8-height);
00198     h = height;
00199   } else {
00200     h = 8-pageOffset;
00201   }
00202   mask <<= pageOffset;
00203         
00204   this->GotoXY(x, y);
00205   for (i = 0; i < width; ++i) {
00206     data = this->ReadData();
00207     tmpData = ~data;
00208     data = (tmpData & mask) | (data & ~mask);
00209     this->WriteData(data);
00210   }
00211   
00212   while (h+8 < height) {
00213     h += 8;
00214     y += 8;
00215     this->GotoXY(x, y);
00216     
00217     for (i = 0; i < width; ++i) {
00218       data = this->ReadData();
00219       this->WriteData(~data);
00220     }
00221   }
00222         
00223   if(h < height) {
00224     mask = ~(0xFF << (height-h));
00225     this->GotoXY(x, y+8);
00226                 
00227     for (i = 0; i < width; ++i) {
00228       data = this->ReadData();
00229       tmpData = ~data;
00230       data = (tmpData & mask) | (data & ~mask);
00231       this->WriteData(data);
00232     }
00233   }
00234 }
00235 
00236 // ######################################################################
00237 void ks0108iLab::SetInverted(boolean invert) {  // changed type to boolean
00238   if (this->Inverted != invert) {
00239     this->InvertRect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT);
00240     this->Inverted = invert;
00241   }
00242 }
00243 
00244 // ######################################################################
00245 void ks0108iLab::SetDot(uint8_t x, uint8_t y, uint8_t color) {
00246   this->GotoXY(x, y - y % 8);
00247   uint8_t data = this->ReadData();
00248   if (color == BLACK) data |= (0x01 << (y % 8)); else data &= ~(0x01 << (y % 8));
00249   this->WriteData(data);
00250 }
00251 
00252 // ######################################################################
00253 void ks0108iLab::DrawBarGraph(uint8_t y, int8_t val) {
00254   uint8_t xx = 62, yy = y;
00255   uint8_t ww = 4, hh = 4;  // bar width and height
00256   int8_t step = ww + 1; // x step
00257 
00258   if (val >= 0)
00259     {
00260       if (val > 60) val = 60;
00261 
00262       // draw the bars proper:
00263       int8_t i;
00264       for (i = 0; i <= val; i += step) {
00265         this->FillRect(xx, yy, ww, hh, BLACK);
00266         xx += step; yy -= 1; hh += 2;
00267       }
00268       // clear bars that correspond to higher values than val:
00269       for (; i <= 60; i += step) {
00270         this->FillRect(xx, yy, ww, hh, WHITE);
00271         xx += step; yy -= 1; hh += 2;
00272       }
00273       // clear all bars on the negative side:
00274       xx = 62 - step; yy = y - 1; hh = 6;
00275       for (i = -1; i >= -60; i -= step) {
00276         this->FillRect(xx, yy, ww, hh, WHITE);
00277         xx -= step; yy -= 1; hh += 2;
00278       }
00279     }
00280   else
00281     {
00282       if (val < -60) val = -60;
00283 
00284       // draw the bars proper:
00285       int8_t i;
00286       for (i = 0; i >= val; i -= step) {
00287         this->FillRect(xx, yy, ww, hh, BLACK);
00288         xx -= step; yy -= 1; hh += 2;
00289       }
00290       // clear bars that correspond to lower values than val:
00291       for (i = 0; i >= -60; i -= step) {
00292         this->FillRect(xx, yy, ww, hh, WHITE);
00293         xx -= step; yy -= 1; hh += 2;
00294       }
00295       // clear all bars on the positive side:
00296       xx = 62 + step; yy = y - 1; hh = 6;
00297       for (i = 1; i <= 60; i += step) {
00298         this->FillRect(xx, yy, ww, hh, WHITE);
00299         xx += step; yy -= 1; hh += 2;
00300       }
00301     }
00302 }
00303 
00304 //
00305 // Font Functions
00306 //
00307 
00308 // ######################################################################
00309 uint8_t ReadFontData(const uint8_t* ptr) {  // note this is a static function
00310   return pgm_read_byte(ptr);
00311 }
00312 
00313 // ######################################################################
00314 void ks0108iLab::SelectFont(const uint8_t* font,uint8_t color, FontCallback callback) {
00315   this->Font = font;
00316   this->FontRead = callback;
00317   this->FontColor = color;
00318 }
00319 
00320 // ######################################################################
00321 void ks0108iLab::PrintNumber(long n) {
00322   byte buf[10];  // prints up to 10 digits (max for a long)
00323   byte *bptr = buf;
00324   if (n == 0) PutChar('0');
00325   else {
00326     if (n < 0) { PutChar('-'); n = -n; }
00327     while (n > 0) { *bptr++ = n % 10; n /= 10; }
00328     for (--bptr; bptr >= buf; --bptr) this->PutChar((char)('0' + *bptr));
00329   }
00330 }
00331 
00332 // ######################################################################
00333 void ks0108iLab::PrintNumberCentered(long n, uint8_t x, uint8_t y, uint8_t width) {
00334   char str[12];  // prints up to 10 digits (max for a long) + sign + null
00335   char *sptr = &(str[11]);
00336   *sptr = 0; // null string terminator
00337 
00338   if (n == 0) *(--sptr) = '0';
00339   else {
00340     long nn = n;
00341     if (n < 0) n = -n;
00342     while (n > 0) { *(--sptr) = (n % 10) + '0'; n /= 10; }
00343     if (nn < 0) *(--sptr) = '-';
00344   }
00345 
00346   this->PutsCentered(sptr, x, y, width);
00347 }
00348 
00349 // ######################################################################
00350 int ks0108iLab::PutChar(char c) {
00351   uint8_t width = 0;
00352   uint8_t height = this->FontRead(this->Font + FONT_HEIGHT);
00353   uint8_t bytes = (height + 7) / 8;
00354         
00355   uint8_t firstChar = this->FontRead(this->Font + FONT_FIRST_CHAR);
00356   uint8_t charCount = this->FontRead(this->Font + FONT_CHAR_COUNT);
00357         
00358   uint16_t index = 0;
00359   uint8_t x = this->Coord.x, y = this->Coord.y;
00360 
00361   if (c < firstChar || c >= (firstChar+charCount)) return 1;
00362   c -= firstChar;
00363         
00364   // read width data, to get the index
00365   for (uint8_t i = 0; i < c; ++i)
00366     index += this->FontRead(this->Font + FONT_WIDTH_TABLE + i);
00367 
00368   index = index * bytes + charCount + FONT_WIDTH_TABLE;
00369   width = this->FontRead(this->Font + FONT_WIDTH_TABLE + c);
00370         
00371   // last but not least, draw the character
00372   for (uint8_t i = 0; i < bytes; ++i) {
00373     uint8_t page = i * width;
00374     for (uint8_t j = 0; j < width; ++j) {
00375       uint8_t data = this->FontRead(this->Font + index + page + j);
00376       if (height < (i+1)*8) data >>= (i+1)*8 - height;
00377       if (this->FontColor == BLACK) this->WriteData(data); else this->WriteData(~data);
00378     }
00379 
00380     // 1px gap between chars
00381     if (this->FontColor == BLACK) this->WriteData(0x00); else this->WriteData(0xFF);
00382     this->GotoXY(x, this->Coord.y + 8);
00383   }
00384   this->GotoXY(x + width + 1, y);
00385 
00386   return 0;
00387 }
00388 
00389 // ######################################################################
00390 void ks0108iLab::Puts(char* str) {
00391   int x = this->Coord.x;
00392   while (*str != 0) {
00393     if (*str == '\n') this->GotoXY(x, this->Coord.y+this->FontRead(this->Font+FONT_HEIGHT));
00394     else this->PutChar(*str);
00395     ++str;
00396   }
00397 }
00398 
00399 // ######################################################################
00400 void ks0108iLab::PutsCentered(char *str, uint8_t x, uint8_t y, uint8_t width) {
00401   uint16_t w = this->StringWidth(str);
00402 
00403   // is the string is too long (or exactly)? if so, just Puts, it may overflow
00404   if (w >= (uint16_t)(width)) { this->GotoXY(x, y); this->Puts(str); return; }
00405 
00406   // at this point, we know that w < width (and < 256)
00407   uint8_t ww = (uint8_t)(w);
00408   uint8_t ww1 = (width - ww) / 2;
00409   uint8_t ww2 = ww1;
00410   if ((width - ww) & 1) ++ww2; // make sure ww1 + w + ww2 = width
00411   uint8_t h = this->FontRead(this->Font + FONT_HEIGHT);
00412 
00413   if (ww1) this->FillRect(x, y, ww1, h, ~(this->FontColor));
00414   this->GotoXY(x + ww1, y); this->Puts(str);
00415   if (ww2) this->FillRect(x + ww1 + ww, y, ww2, h, ~(this->FontColor));
00416 }
00417 
00418 // ######################################################################
00419 void ks0108iLab::Puts_P(PGM_P str) {
00420   int x = this->Coord.x;
00421   while (pgm_read_byte(str) != 0) {
00422     if (pgm_read_byte(str) == '\n') this->GotoXY(x, this->Coord.y+this->FontRead(this->Font+FONT_HEIGHT));
00423     else this->PutChar(pgm_read_byte(str));
00424     ++str;
00425   }
00426 }
00427 
00428 // ######################################################################
00429 uint8_t ks0108iLab::CharWidth(char c) {
00430   uint8_t width = 0;
00431   uint8_t firstChar = this->FontRead(this->Font+FONT_FIRST_CHAR);
00432   uint8_t charCount = this->FontRead(this->Font+FONT_CHAR_COUNT);
00433 
00434   // read width data
00435   if (c >= firstChar && c < (firstChar+charCount)) {
00436     c -= firstChar;
00437     width = this->FontRead(this->Font+FONT_WIDTH_TABLE+c)+1;
00438   }
00439 
00440   return width;
00441 }
00442 
00443 // ######################################################################
00444 uint16_t ks0108iLab::StringWidth(char* str) {
00445   uint16_t width = 0;
00446   while (*str != 0) width += this->CharWidth(*str++);
00447   return width;
00448 }
00449 
00450 // ######################################################################
00451 uint16_t ks0108iLab::StringWidth_P(PGM_P str) {
00452   uint16_t width = 0;
00453   while(pgm_read_byte(str) != 0) width += this->CharWidth(pgm_read_byte(str++));
00454   return width;
00455 }
00456 
00457 // ######################################################################
00458 void ks0108iLab::GotoXY(uint8_t x, uint8_t y) {
00459   uint8_t chip = CHIP1, cmd;
00460   
00461   if (x >= DISPLAY_WIDTH) x = DISPLAY_WIDTH-1;                 // ensure that coordinates are legal
00462   if (y >= DISPLAY_HEIGHT) y = DISPLAY_HEIGHT-1;
00463   
00464   this->Coord.x = x;                                                                // save new coordinates
00465   this->Coord.y = y;
00466   this->Coord.page = y/8;
00467   
00468   if(x >= CHIP_WIDTH) {                                                        // select the right chip
00469     x -= CHIP_WIDTH;
00470     chip = CHIP2;
00471   }
00472   
00473 #ifdef HD44102 
00474   this->WriteCommand(LCD_DISP_START, CHIP1);                // display start line = 0
00475   this->WriteCommand(LCD_DISP_START, CHIP2);
00476   
00477   cmd = (this->Coord.page << 6 ) | x;
00478   //        this->WriteCommand(cmd,chip);
00479   this->WriteCommand(cmd, CHIP1);                
00480   this->WriteCommand(cmd, CHIP2);
00481 #else
00482   cmd = LCD_SET_ADD | x;
00483   this->WriteCommand(cmd, chip);                                        // set x address on active chip
00484   
00485   cmd = LCD_SET_PAGE | this->Coord.page;                        // set y address on both chips
00486   this->WriteCommand(cmd, CHIP1);
00487   this->WriteCommand(cmd, CHIP2);
00488 #endif
00489 }
00490 
00491 // ######################################################################
00492 void ks0108iLab::Init(boolean invert) {  
00493   pinMode(D_I,OUTPUT);         
00494   pinMode(R_W,OUTPUT);         
00495   pinMode(EN,OUTPUT);         
00496   pinMode(CSEL1,OUTPUT);
00497   pinMode(CSEL2,OUTPUT);
00498   pinMode(13,OUTPUT); // for testing only !!!
00499   
00500   this->Coord.x = 0;
00501   this->Coord.y = 0;
00502   this->Coord.page = 0;
00503         
00504   this->Inverted = invert;
00505   
00506   this->WriteCommand(LCD_ON, CHIP1);                                // power on
00507   this->WriteCommand(LCD_ON, CHIP2);
00508   
00509   this->WriteCommand(LCD_DISP_START, CHIP1);                // display start line = 0
00510   this->WriteCommand(LCD_DISP_START, CHIP2);
00511   
00512   this->ClearScreen();                                                        // display clear
00513   this->GotoXY(0,0);
00514 }
00515 
00516 // ######################################################################
00517 static void delay450ns(void){   // delay 450 nanoseconds
00518   asm volatile("nop\n\t" 
00519                "nop\n\t"
00520                "nop\n\t"
00521                "nop\n\t"  // todo, remove some nops if clock is less than 16mhz
00522                "nop\n\t"
00523                
00524                // LI: default was 5 nops, seems too fast for sparkfun lcd
00525                "nop\n\t"
00526                "nop\n\t"
00527                "nop\n\t"
00528                "nop\n\t"
00529                "nop\n\t"
00530                "nop\n\t"
00531                "nop\n\t"
00532                "nop\n\t"
00533                "nop\n\t"
00534                "nop\n\t"
00535                "nop\n\t"
00536                "nop\n\t"
00537                "nop\n\t"
00538                "nop\n\t"
00539                "nop\n\t"
00540                "nop\n\t"
00541                "nop\n\t"
00542                "nop\n\t"
00543                "nop\n\t"
00544                "nop\n\t"
00545                "nop\n\t"
00546                "nop\n\t"
00547                "nop\n\t"
00548                "nop\n\t" // still too fast
00549                
00550                "nop\n\t"
00551                "nop\n\t"
00552                "nop\n\t"
00553                "nop\n\t" // seems ok
00554                
00555                "nop\n\t"
00556                "nop\n\t"
00557                "nop\n\t"
00558                "nop\n\t"
00559                "nop\n\t"
00560                "nop\n\t"
00561                "nop\n\t"
00562                "nop\n\t"
00563                "nop\n\t"
00564                "nop\n\t"
00565                "nop\n\t"
00566                "nop\n\t"
00567                "nop\n\t"
00568                "nop\n\t"
00569                "nop\n\t"
00570                "nop\n\t"
00571                "nop\n\t"
00572                "nop\n\t"
00573                "nop\n\t"
00574                "nop\n\t"
00575                "nop\n\t"
00576                "nop\n\t"
00577                "nop\n\t"
00578                "nop\n\t"
00579                "nop\n\t"
00580                "nop\n\t"
00581                "nop\n\t"
00582                "nop\n\t"
00583                "nop\n\t"
00584                "nop\n\t"
00585                "nop\n\t"
00586                "nop\n\t"
00587                "nop\n\t"
00588                "nop\n\t"
00589                "nop\n\t"
00590                "nop\n\t"
00591                "nop\n\t"
00592                "nop\n\t"
00593                "nop\n\t"
00594                "nop\n\t"
00595                "nop\n\t"
00596                "nop\n\t"
00597                "nop\n\t" // just to be sure
00598 
00599                ::);
00600 }
00601 
00602  // ######################################################################
00603 __inline__ void ks0108iLab::Enable(void) {  
00604   fastWriteHigh(EN);                                        // EN high level width: min. 450ns
00605   delay450ns();
00606   fastWriteLow(EN);
00607   // 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
00608   delay450ns();
00609 }
00610 
00611 // ######################################################################
00612 uint8_t ks0108iLab::DoReadData(uint8_t first) {
00613   uint8_t data;
00614   
00615   lcdDataOut(0x00);
00616   lcdDataDir(0x00);                                        // data port is input
00617   
00618   if(this->Coord.x < CHIP_WIDTH) {
00619     fastWriteLow(CSEL2);                        // deselect chip 2
00620     fastWriteHigh(CSEL1);                        // select chip 1
00621   } else if(this->Coord.x >= CHIP_WIDTH) {
00622     fastWriteLow(CSEL1);                        // deselect chip 1
00623     fastWriteHigh(CSEL2);                        // select chip 2
00624   }
00625   if(this->Coord.x == CHIP_WIDTH && first) {                // chip2 X-address = 0
00626     this->WriteCommand(LCD_SET_ADD, CHIP2);         // wuff wuff
00627   }
00628   
00629   fastWriteHigh(D_I);                                        // D/I = 1
00630   fastWriteHigh(R_W);                                        // R/W = 1
00631   
00632   fastWriteHigh(EN);                                         // EN high level width: min. 450ns
00633   delay450ns();
00634 #ifdef LCD_DATA_NIBBLES
00635   data = (LCD_DATA_IN_LOW & 0x0F) | (LCD_DATA_IN_HIGH & 0xF0);
00636 #else
00637   data = LCD_DATA_IN_LOW;                           // low and high nibbles on same port so read all 8 bits at once
00638 #endif 
00639   
00640   fastWriteLow(EN); 
00641   // for(volatile uint8_t i=0; i<8; i++);  // big delay loop in Fabian's code, was 5.7us, replaced by 450ns delay below
00642   delay450ns();
00643   
00644   lcdDataDir(0xFF);
00645   
00646   this->GotoXY(this->Coord.x, this->Coord.y);
00647   
00648   if(this->Inverted)
00649     data = ~data;
00650   return data;
00651 }
00652 
00653 // ######################################################################
00654 inline uint8_t ks0108iLab::ReadData(void) {  
00655   this->DoReadData(1);                                // dummy read
00656   return this->DoReadData(0);                        // "real" read
00657 }
00658 
00659 // ######################################################################
00660 void ks0108iLab::WriteCommand(uint8_t cmd, uint8_t chip) {
00661   if(chip == CHIP1) {
00662     fastWriteLow(CSEL2);                        // deselect chip 2
00663     fastWriteHigh(CSEL1);                        // select chip 1
00664   } else if(chip == CHIP2) {
00665     fastWriteLow(CSEL1);                        // deselect chip 1
00666     fastWriteHigh(CSEL2);                        // select chip 2
00667   }
00668   fastWriteLow(D_I);                                        // D/I = 0
00669   fastWriteLow(R_W);                                        // R/W = 0        
00670   
00671   lcdDataDir(0xFF);
00672   lcdDataOut(cmd);
00673   this->Enable();                                                // enable
00674   lcdDataOut(0x00);
00675 }
00676 
00677 // ######################################################################
00678 void ks0108iLab::WriteData(uint8_t data) {
00679   uint8_t displayData, yOffset;
00680 #ifdef LCD_CMD_PORT        
00681   uint8_t cmdPort;        
00682 #endif
00683   
00684 #ifdef GLCD_DEBUG
00685   volatile uint16_t i;
00686   for(i=0; i<5000; i++);
00687 #endif
00688   
00689   if(this->Coord.x >= DISPLAY_WIDTH)
00690     return;
00691   
00692   if(this->Coord.x < CHIP_WIDTH) {
00693     fastWriteLow(CSEL2);                        // deselect chip 2
00694     fastWriteHigh(CSEL1);                        // select chip 1
00695   } else {
00696     fastWriteLow(CSEL1);                        // deselect chip 1
00697     fastWriteHigh(CSEL2);                        // select chip 2
00698   }
00699 #ifndef HD44102
00700   if(this->Coord.x == CHIP_WIDTH)                                                        // chip2 X-address = 0
00701     this->WriteCommand(LCD_SET_ADD, CHIP2);
00702 #endif        
00703   fastWriteHigh(D_I);                                        // D/I = 1
00704   fastWriteLow(R_W);                                  // R/W = 0        
00705   lcdDataDir(0xFF);                                        // data port is output
00706   
00707   
00708   yOffset = this->Coord.y%8;
00709   if(yOffset != 0) {
00710     // first page
00711 #ifdef LCD_CMD_PORT 
00712     cmdPort = LCD_CMD_PORT;                                                // save command port
00713 #endif
00714     displayData = this->ReadData();
00715 #ifdef LCD_CMD_PORT                 
00716     LCD_CMD_PORT = cmdPort;                                                // restore command port
00717 #else
00718     fastWriteHigh(D_I);                                        // D/I = 1
00719     fastWriteLow(R_W);                                        // R/W = 0
00720     if(this->Coord.x < CHIP_WIDTH) {
00721       fastWriteLow(CSEL2);                        // deselect chip 2
00722       fastWriteHigh(CSEL1);                        // select chip 1
00723     } else {
00724       fastWriteLow(CSEL1);                        // deselect chip 1
00725       fastWriteHigh(CSEL2);                        // select chip 2
00726     }
00727 #endif
00728     lcdDataDir(0xFF);                                                // data port is output
00729     
00730     displayData |= data << yOffset;
00731     if(this->Inverted)
00732       displayData = ~displayData;
00733     lcdDataOut( displayData);                                        // write data
00734     this->Enable();                                                                // enable
00735     
00736     // second page
00737     this->GotoXY(this->Coord.x, this->Coord.y+8);
00738     
00739     displayData = this->ReadData();
00740     
00741 #ifdef LCD_CMD_PORT                 
00742     LCD_CMD_PORT = cmdPort;                                                // restore command port
00743 #else                
00744     fastWriteHigh(D_I);                                        // D/I = 1
00745     fastWriteLow(R_W);                                         // R/W = 0        
00746     if(this->Coord.x < CHIP_WIDTH) {
00747       fastWriteLow(CSEL2);                        // deselect chip 2
00748       fastWriteHigh(CSEL1);                        // select chip 1
00749     } else {
00750       fastWriteLow(CSEL1);                        // deselect chip 1
00751       fastWriteHigh(CSEL2);                        // select chip 2
00752     }
00753 #endif
00754     lcdDataDir(0xFF);                                // data port is output
00755     
00756     displayData |= data >> (8-yOffset);
00757     if(this->Inverted)
00758       displayData = ~displayData;
00759     lcdDataOut(displayData);                // write data
00760     this->Enable();                                        // enable
00761     
00762     this->GotoXY(this->Coord.x+1, this->Coord.y-8);
00763   } else {
00764     if(this->Inverted)
00765       data = ~data;
00766     lcdDataOut(data);                                // write data
00767     this->Enable();                                        // enable
00768     this->Coord.x++;
00769   }
00770   lcdDataOut(0x00);
00771 }
00772 
00773 // ######################################################################
00774 // class wrapper
00775 ks0108iLab::ks0108iLab(){
00776   this->Inverted=0;
00777 }
00778 
00779 // ######################################################################
00780 // Make one instance for the user
00781 ks0108iLab GLCDiLab = ks0108iLab();
Generated on Sun May 8 08:04:45 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3