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();