PlatformIO package of the Teensy core framework compatible with GCC 10 & C++20
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

733 Zeilen
18KB

  1. /*
  2. ks0108.cpp - Arduino library support for ks0108 and compatable graphic LCDs
  3. Copyright (c)2008 Michael Margolis All right reserved
  4. mailto:memargolis@hotmail.com?subject=KS0108_Library
  5. The high level functions of this library are based on version 1.1 of ks0108 graphics routines
  6. written and copyright by Fabian Maximilian Thiele. His sitelink is dead but
  7. you can obtain a copy of his original work here:
  8. http://www.scienceprog.com/wp-content/uploads/2007/07/glcd_ks0108.zip
  9. Code changes include conversion to an Arduino C++ library, rewriting the low level routines
  10. to read busy status flag and support a wider range of displays, adding more flexibility
  11. in port addressing and improvements in I/O speed. The interface has been made more Arduino friendly
  12. and some convenience functions added.
  13. This library is distributed in the hope that it will be useful,
  14. but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  16. Version: 1.0 - May 8 2008 - first release
  17. Version: 1.1 - Nov 7 2008 - restructured low level code to adapt to panel speed
  18. - moved chip and panel configuration into seperate header files
  19. - added fixed width system font
  20. Version: 2 - May 26 2009 - second release
  21. - added support for Mega and Sanguino, improved panel speed tolerance, added bitmap support
  22. */
  23. #include <inttypes.h>
  24. #include <avr/io.h>
  25. #include <avr/pgmspace.h>
  26. #include <wiring.h>
  27. #define ksSOURCE
  28. #include "ks0108.h"
  29. #define EN_DELAY() asm volatile( "ldi r24, %0 \n\t" "subi r24, 0x1 \n\t" "and r24, r24 \n\t" "brne .-6 \n\t" ::"M" (EN_DELAY_VALUE) : "r24" )
  30. // the (EN_DELAY_VALUE) argument for the above delay is in ks-0108_panel.h
  31. //#define GLCD_DEBUG // uncomment this if you want to slow down drawing to see how pixels are set
  32. void ks0108::ClearPage(uint8_t page, uint8_t color){
  33. for(uint8_t x=0; x < DISPLAY_WIDTH; x++){
  34. GotoXY(x, page * 8);
  35. this->WriteData(color);
  36. }
  37. }
  38. void ks0108::ClearScreen(uint8_t color){
  39. uint8_t page;
  40. for( page = 0; page < 8; page++){
  41. GotoXY(0, page * 8);
  42. ClearPage(page, color);
  43. }
  44. }
  45. void ks0108::DrawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t color) {
  46. uint8_t length, i, y, yAlt, xTmp, yTmp;
  47. int16_t m;
  48. //
  49. // vertical line
  50. //
  51. if(x1 == x2) {
  52. // x1|y1 must be the upper point
  53. if(y1 > y2) {
  54. yTmp = y1;
  55. y1 = y2;
  56. y2 = yTmp;
  57. }
  58. this->DrawVertLine(x1, y1, y2-y1, color);
  59. //
  60. // horizontal line
  61. //
  62. } else if(y1 == y2) {
  63. // x1|y1 must be the left point
  64. if(x1 > x2) {
  65. xTmp = x1;
  66. x1 = x2;
  67. x2 = xTmp;
  68. }
  69. this->DrawHoriLine(x1, y1, x2-x1, color);
  70. //
  71. // angled line :)
  72. //
  73. } else {
  74. // angle >= 45°
  75. if((y2-y1) >= (x2-x1) || (y1-y2) >= (x2-x1)) {
  76. // x1 must be smaller than x2
  77. if(x1 > x2) {
  78. xTmp = x1;
  79. yTmp = y1;
  80. x1 = x2;
  81. y1 = y2;
  82. x2 = xTmp;
  83. y2 = yTmp;
  84. }
  85. length = x2-x1; // not really the length :)
  86. m = ((y2-y1)*200)/length;
  87. yAlt = y1;
  88. for(i=0; i<=length; i++) {
  89. y = ((m*i)/200)+y1;
  90. if((m*i)%200 >= 100)
  91. y++;
  92. else if((m*i)%200 <= -100)
  93. y--;
  94. this->DrawLine(x1+i, yAlt, x1+i, y, color);
  95. if(length <= (y2-y1) && y1 < y2)
  96. yAlt = y+1;
  97. else if(length <= (y1-y2) && y1 > y2)
  98. yAlt = y-1;
  99. else
  100. yAlt = y;
  101. }
  102. // angle < 45°
  103. } else {
  104. // y1 must be smaller than y2
  105. if(y1 > y2) {
  106. xTmp = x1;
  107. yTmp = y1;
  108. x1 = x2;
  109. y1 = y2;
  110. x2 = xTmp;
  111. y2 = yTmp;
  112. }
  113. length = y2-y1;
  114. m = ((x2-x1)*200)/length;
  115. yAlt = x1;
  116. for(i=0; i<=length; i++) {
  117. y = ((m*i)/200)+x1;
  118. if((m*i)%200 >= 100)
  119. y++;
  120. else if((m*i)%200 <= -100)
  121. y--;
  122. this->DrawLine(yAlt, y1+i, y, y1+i, color);
  123. if(length <= (x2-x1) && x1 < x2)
  124. yAlt = y+1;
  125. else if(length <= (x1-x2) && x1 > x2)
  126. yAlt = y-1;
  127. else
  128. yAlt = y;
  129. }
  130. }
  131. }
  132. }
  133. void ks0108::DrawRect(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t color) {
  134. DrawHoriLine(x, y, width, color); // top
  135. DrawHoriLine(x, y+height, width, color); // bottom
  136. DrawVertLine(x, y, height, color); // left
  137. DrawVertLine(x+width, y, height, color); // right
  138. }
  139. void ks0108::DrawRoundRect(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t radius, uint8_t color) {
  140. int16_t tSwitch, x1 = 0, y1 = radius;
  141. tSwitch = 3 - 2 * radius;
  142. while (x1 <= y1) {
  143. this->SetDot(x+radius - x1, y+radius - y1, color);
  144. this->SetDot(x+radius - y1, y+radius - x1, color);
  145. this->SetDot(x+width-radius + x1, y+radius - y1, color);
  146. this->SetDot(x+width-radius + y1, y+radius - x1, color);
  147. this->SetDot(x+width-radius + x1, y+height-radius + y1, color);
  148. this->SetDot(x+width-radius + y1, y+height-radius + x1, color);
  149. this->SetDot(x+radius - x1, y+height-radius + y1, color);
  150. this->SetDot(x+radius - y1, y+height-radius + x1, color);
  151. if (tSwitch < 0) {
  152. tSwitch += (4 * x1 + 6);
  153. } else {
  154. tSwitch += (4 * (x1 - y1) + 10);
  155. y1--;
  156. }
  157. x1++;
  158. }
  159. this->DrawHoriLine(x+radius, y, width-(2*radius), color); // top
  160. this->DrawHoriLine(x+radius, y+height, width-(2*radius), color); // bottom
  161. this->DrawVertLine(x, y+radius, height-(2*radius), color); // left
  162. this->DrawVertLine(x+width, y+radius, height-(2*radius), color); // right
  163. }
  164. /*
  165. * Hardware-Functions
  166. */
  167. void ks0108::FillRect(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t color) {
  168. uint8_t mask, pageOffset, h, i, data;
  169. height++;
  170. pageOffset = y%8;
  171. y -= pageOffset;
  172. mask = 0xFF;
  173. if(height < 8-pageOffset) {
  174. mask >>= (8-height);
  175. h = height;
  176. } else {
  177. h = 8-pageOffset;
  178. }
  179. mask <<= pageOffset;
  180. this->GotoXY(x, y);
  181. for(i=0; i<=width; i++) {
  182. data = this->ReadData();
  183. if(color == BLACK) {
  184. data |= mask;
  185. } else {
  186. data &= ~mask;
  187. }
  188. this->WriteData(data);
  189. }
  190. while(h+8 <= height) {
  191. h += 8;
  192. y += 8;
  193. this->GotoXY(x, y);
  194. for(i=0; i<=width; i++) {
  195. this->WriteData(color);
  196. }
  197. }
  198. if(h < height) {
  199. mask = ~(0xFF << (height-h));
  200. this->GotoXY(x, y+8);
  201. for(i=0; i<=width; i++) {
  202. data = this->ReadData();
  203. if(color == BLACK) {
  204. data |= mask;
  205. } else {
  206. data &= ~mask;
  207. }
  208. this->WriteData(data);
  209. }
  210. }
  211. }
  212. void ks0108::InvertRect(uint8_t x, uint8_t y, uint8_t width, uint8_t height) {
  213. uint8_t mask, pageOffset, h, i, data, tmpData;
  214. height++;
  215. pageOffset = y%8;
  216. y -= pageOffset;
  217. mask = 0xFF;
  218. if(height < 8-pageOffset) {
  219. mask >>= (8-height);
  220. h = height;
  221. } else {
  222. h = 8-pageOffset;
  223. }
  224. mask <<= pageOffset;
  225. this->GotoXY(x, y);
  226. for(i=0; i<=width; i++) {
  227. data = this->ReadData();
  228. tmpData = ~data;
  229. data = (tmpData & mask) | (data & ~mask);
  230. this->WriteData(data);
  231. }
  232. while(h+8 <= height) {
  233. h += 8;
  234. y += 8;
  235. this->GotoXY(x, y);
  236. for(i=0; i<=width; i++) {
  237. data = this->ReadData();
  238. this->WriteData(~data);
  239. }
  240. }
  241. if(h < height) {
  242. mask = ~(0xFF << (height-h));
  243. this->GotoXY(x, y+8);
  244. for(i=0; i<=width; i++) {
  245. data = this->ReadData();
  246. tmpData = ~data;
  247. data = (tmpData & mask) | (data & ~mask);
  248. this->WriteData(data);
  249. }
  250. }
  251. }
  252. void ks0108::SetInverted(boolean invert) { // changed type to boolean
  253. if(this->Inverted != invert) {
  254. this->InvertRect(0,0,DISPLAY_WIDTH-1,DISPLAY_HEIGHT-1);
  255. this->Inverted = invert;
  256. }
  257. }
  258. void ks0108::SetDot(uint8_t x, uint8_t y, uint8_t color) {
  259. uint8_t data;
  260. this->GotoXY(x, y-y%8); // read data from display memory
  261. data = this->ReadData();
  262. if(color == BLACK) {
  263. data |= 0x01 << (y%8); // set dot
  264. } else {
  265. data &= ~(0x01 << (y%8)); // clear dot
  266. }
  267. this->WriteData(data); // write data back to display
  268. }
  269. //
  270. // Font Functions
  271. //
  272. uint8_t ReadPgmData(const uint8_t* ptr) { // note this is a static function
  273. return pgm_read_byte(ptr);
  274. }
  275. void ks0108::SelectFont(const uint8_t* font,uint8_t color, FontCallback callback) {
  276. this->Font = font;
  277. this->FontRead = callback;
  278. this->FontColor = color;
  279. }
  280. void ks0108::PrintNumber(long n){
  281. byte buf[10]; // prints up to 10 digits
  282. byte i=0;
  283. if(n==0)
  284. PutChar('0');
  285. else{
  286. if(n < 0){
  287. PutChar('-');
  288. n = -n;
  289. }
  290. while(n>0 && i <= 10){
  291. buf[i++] = n % 10; // n % base
  292. n /= 10; // n/= base
  293. }
  294. for(; i >0; i--)
  295. this->PutChar((char) (buf[i-1] < 10 ? '0' + buf[i-1] : 'A' + buf[i-1] - 10));
  296. }
  297. }
  298. int ks0108::PutChar(char c) {
  299. uint8_t width = 0;
  300. uint8_t height = this->FontRead(this->Font+FONT_HEIGHT);
  301. uint8_t bytes = (height+7)/8;
  302. uint8_t firstChar = this->FontRead(this->Font+FONT_FIRST_CHAR);
  303. uint8_t charCount = this->FontRead(this->Font+FONT_CHAR_COUNT);
  304. uint16_t index = 0;
  305. uint8_t x = this->Coord.x, y = this->Coord.y;
  306. if(c < firstChar || c >= (firstChar+charCount)) {
  307. return 1;
  308. }
  309. c-= firstChar;
  310. if( this->FontRead(this->Font+FONT_LENGTH) == 0 && this->FontRead(this->Font+FONT_LENGTH+1) == 0) {
  311. // zero length is flag indicating fixed width font (array does not contain width data entries)
  312. width = this->FontRead(this->Font+FONT_FIXED_WIDTH);
  313. index = c*bytes*width+FONT_WIDTH_TABLE;
  314. }
  315. else{
  316. // variable width font, read width data, to get the index
  317. for(uint8_t i=0; i<c; i++) {
  318. index += this->FontRead(this->Font+FONT_WIDTH_TABLE+i);
  319. }
  320. index = index*bytes+charCount+FONT_WIDTH_TABLE;
  321. width = this->FontRead(this->Font+FONT_WIDTH_TABLE+c);
  322. }
  323. // last but not least, draw the character
  324. for(uint8_t i=0; i<bytes; i++) {
  325. uint8_t page = i*width;
  326. for(uint8_t j=0; j<width; j++) {
  327. uint8_t data = this->FontRead(this->Font+index+page+j);
  328. if(height > 8 && height < (i+1)*8) {
  329. data >>= (i+1)*8-height;
  330. }
  331. if(this->FontColor == BLACK) {
  332. this->WriteData(data);
  333. } else {
  334. this->WriteData(~data);
  335. }
  336. }
  337. // 1px gap between chars
  338. if(this->FontColor == BLACK) {
  339. this->WriteData(0x00);
  340. } else {
  341. this->WriteData(0xFF);
  342. }
  343. this->GotoXY(x, this->Coord.y+8);
  344. }
  345. this->GotoXY(x+width+1, y);
  346. return 0;
  347. }
  348. void ks0108::Puts(const char* str) {
  349. int x = this->Coord.x;
  350. while(*str != 0) {
  351. if(*str == '\n') {
  352. this->GotoXY(x, this->Coord.y+this->FontRead(this->Font+FONT_HEIGHT));
  353. } else {
  354. this->PutChar(*str);
  355. }
  356. str++;
  357. }
  358. }
  359. void ks0108::Puts_P(PGM_P str) {
  360. int x = this->Coord.x;
  361. while(pgm_read_byte(str) != 0) {
  362. if(pgm_read_byte(str) == '\n') {
  363. this->GotoXY(x, this->Coord.y+this->FontRead(this->Font+FONT_HEIGHT));
  364. } else {
  365. this->PutChar(pgm_read_byte(str));
  366. }
  367. str++;
  368. }
  369. }
  370. uint8_t ks0108::CharWidth(char c) {
  371. uint8_t width = 0;
  372. uint8_t firstChar = this->FontRead(this->Font+FONT_FIRST_CHAR);
  373. uint8_t charCount = this->FontRead(this->Font+FONT_CHAR_COUNT);
  374. // read width data
  375. if(c >= firstChar && c < (firstChar+charCount)) {
  376. c -= firstChar;
  377. width = this->FontRead(this->Font+FONT_WIDTH_TABLE+c)+1;
  378. }
  379. return width;
  380. }
  381. uint16_t ks0108::StringWidth(char* str) {
  382. uint16_t width = 0;
  383. while(*str != 0) {
  384. width += this->CharWidth(*str++);
  385. }
  386. return width;
  387. }
  388. uint16_t ks0108::StringWidth_P(PGM_P str) {
  389. uint16_t width = 0;
  390. while(pgm_read_byte(str) != 0) {
  391. width += this->CharWidth(pgm_read_byte(str++));
  392. }
  393. return width;
  394. }
  395. void ks0108::CursorTo( uint8_t x, uint8_t y){ // 0 based coordinates for fixed width fonts (i.e. systemFont5x7)
  396. GotoXY( x * (this->FontRead(this->Font+FONT_FIXED_WIDTH)+1),
  397. y * (this->FontRead(this->Font+FONT_HEIGHT)+1)
  398. ) ;
  399. }
  400. void ks0108::GotoXY(uint8_t x, uint8_t y) {
  401. uint8_t chip, cmd;
  402. if( (x > DISPLAY_WIDTH-1) || (y > DISPLAY_HEIGHT-1) ) // exit if coordinates are not legal
  403. return;
  404. this->Coord.x = x; // save new coordinates
  405. this->Coord.y = y;
  406. if(y/8 != this->Coord.page) {
  407. this->Coord.page = y/8;
  408. cmd = LCD_SET_PAGE | this->Coord.page; // set y address on all chips
  409. for(chip=0; chip < DISPLAY_WIDTH/CHIP_WIDTH; chip++){
  410. this->WriteCommand(cmd, chip);
  411. }
  412. }
  413. chip = this->Coord.x/CHIP_WIDTH;
  414. x = x % CHIP_WIDTH;
  415. cmd = LCD_SET_ADD | x;
  416. this->WriteCommand(cmd, chip); // set x address on active chip
  417. }
  418. void ks0108::Init(boolean invert) {
  419. pinMode(D_I,OUTPUT);
  420. pinMode(R_W,OUTPUT);
  421. pinMode(EN,OUTPUT);
  422. pinMode(CSEL1,OUTPUT);
  423. pinMode(CSEL2,OUTPUT);
  424. delay(10);
  425. fastWriteLow(D_I);
  426. fastWriteLow(R_W);
  427. fastWriteLow(EN);
  428. this->Coord.x = 0;
  429. this->Coord.y = 0;
  430. this->Coord.page = 0;
  431. this->Inverted = invert;
  432. for(uint8_t chip=0; chip < DISPLAY_WIDTH/CHIP_WIDTH; chip++){
  433. delay(10);
  434. this->WriteCommand(LCD_ON, chip); // power on
  435. this->WriteCommand(LCD_DISP_START, chip); // display start line = 0
  436. }
  437. delay(50);
  438. this->ClearScreen(invert ? BLACK : WHITE); // display clear
  439. this->GotoXY(0,0);
  440. }
  441. __inline__ void ks0108::SelectChip(uint8_t chip) {
  442. //static uint8_t prevchip;
  443. if(chipSelect[chip] & 1)
  444. fastWriteHigh(CSEL1);
  445. else
  446. fastWriteLow(CSEL1);
  447. if(chipSelect[chip] & 2)
  448. fastWriteHigh(CSEL2);
  449. else
  450. fastWriteLow(CSEL2);
  451. }
  452. void ks0108::WaitReady( uint8_t chip){
  453. // wait until LCD busy bit goes to zero
  454. SelectChip(chip);
  455. lcdDataDir(0x00);
  456. fastWriteLow(D_I);
  457. fastWriteHigh(R_W);
  458. fastWriteHigh(EN);
  459. EN_DELAY();
  460. while(LCD_DATA_IN_HIGH & LCD_BUSY_FLAG)
  461. ;
  462. fastWriteLow(EN);
  463. }
  464. __inline__ void ks0108::Enable(void) {
  465. EN_DELAY();
  466. fastWriteHigh(EN); // EN high level width min 450 ns
  467. EN_DELAY();
  468. fastWriteLow(EN);
  469. //EN_DELAY(); // some displays may need this delay at the end of the enable pulse
  470. }
  471. uint8_t ks0108::DoReadData(uint8_t first) {
  472. uint8_t data, chip;
  473. chip = this->Coord.x/CHIP_WIDTH;
  474. this->WaitReady(chip);
  475. if(first){
  476. if(this->Coord.x % CHIP_WIDTH == 0 && chip > 0){ // todo , remove this test and call GotoXY always?
  477. this->GotoXY(this->Coord.x, this->Coord.y);
  478. this->WaitReady(chip);
  479. }
  480. }
  481. fastWriteHigh(D_I); // D/I = 1
  482. fastWriteHigh(R_W); // R/W = 1
  483. fastWriteHigh(EN); // EN high level width: min. 450ns
  484. EN_DELAY();
  485. #ifdef LCD_DATA_NIBBLES
  486. data = (LCD_DATA_IN_LOW & 0x0F) | (LCD_DATA_IN_HIGH & 0xF0);
  487. #else
  488. data = LCD_DATA_IN_LOW; // low and high nibbles on same port so read all 8 bits at once
  489. #endif
  490. fastWriteLow(EN);
  491. if(first == 0)
  492. this->GotoXY(this->Coord.x, this->Coord.y);
  493. if(this->Inverted)
  494. data = ~data;
  495. return data;
  496. }
  497. inline uint8_t ks0108::ReadData(void) {
  498. this->DoReadData(1); // dummy read
  499. return this->DoReadData(0); // "real" read
  500. this->Coord.x++;
  501. }
  502. void ks0108::WriteCommand(uint8_t cmd, uint8_t chip) {
  503. if(this->Coord.x % CHIP_WIDTH == 0 && chip > 0){ // todo , ignore address 0???
  504. EN_DELAY();
  505. }
  506. this->WaitReady(chip);
  507. fastWriteLow(D_I); // D/I = 0
  508. fastWriteLow(R_W); // R/W = 0
  509. lcdDataDir(0xFF);
  510. EN_DELAY();
  511. lcdDataOut(cmd);
  512. this->Enable(); // enable
  513. EN_DELAY();
  514. EN_DELAY();
  515. lcdDataOut(0x00);
  516. }
  517. void ks0108::WriteData(uint8_t data) {
  518. uint8_t displayData, yOffset, chip;
  519. //showHex("wrData",data);
  520. //showXY("wr", this->Coord.x,this->Coord.y);
  521. #ifdef LCD_CMD_PORT
  522. uint8_t cmdPort;
  523. #endif
  524. #ifdef GLCD_DEBUG
  525. volatile uint16_t i;
  526. for(i=0; i<5000; i++);
  527. #endif
  528. if(this->Coord.x >= DISPLAY_WIDTH)
  529. return;
  530. chip = this->Coord.x/CHIP_WIDTH;
  531. this->WaitReady(chip);
  532. if(this->Coord.x % CHIP_WIDTH == 0 && chip > 0){ // todo , ignore address 0???
  533. this->GotoXY(this->Coord.x, this->Coord.y);
  534. }
  535. fastWriteHigh(D_I); // D/I = 1
  536. fastWriteLow(R_W); // R/W = 0
  537. lcdDataDir(0xFF); // data port is output
  538. yOffset = this->Coord.y%8;
  539. if(yOffset != 0) {
  540. // first page
  541. #ifdef LCD_CMD_PORT
  542. cmdPort = LCD_CMD_PORT; // save command port
  543. #endif
  544. displayData = this->ReadData();
  545. #ifdef LCD_CMD_PORT
  546. LCD_CMD_PORT = cmdPort; // restore command port
  547. #else
  548. fastWriteHigh(D_I); // D/I = 1
  549. fastWriteLow(R_W); // R/W = 0
  550. SelectChip(chip);
  551. #endif
  552. lcdDataDir(0xFF); // data port is output
  553. displayData |= data << yOffset;
  554. if(this->Inverted)
  555. displayData = ~displayData;
  556. lcdDataOut( displayData); // write data
  557. this->Enable(); // enable
  558. // second page
  559. this->GotoXY(this->Coord.x, this->Coord.y+8);
  560. displayData = this->ReadData();
  561. #ifdef LCD_CMD_PORT
  562. LCD_CMD_PORT = cmdPort; // restore command port
  563. #else
  564. fastWriteHigh(D_I); // D/I = 1
  565. fastWriteLow(R_W); // R/W = 0
  566. SelectChip(chip);
  567. #endif
  568. lcdDataDir(0xFF); // data port is output
  569. displayData |= data >> (8-yOffset);
  570. if(this->Inverted)
  571. displayData = ~displayData;
  572. lcdDataOut(displayData); // write data
  573. this->Enable(); // enable
  574. this->GotoXY(this->Coord.x+1, this->Coord.y-8);
  575. }else
  576. {
  577. // just this code gets executed if the write is on a single page
  578. if(this->Inverted)
  579. data = ~data;
  580. EN_DELAY();
  581. lcdDataOut(data); // write data
  582. this->Enable(); // enable
  583. this->Coord.x++;
  584. //showXY("WrData",this->Coord.x, this->Coord.y);
  585. }
  586. }
  587. void ks0108::DrawBitmap(const uint8_t * bitmap, uint8_t x, uint8_t y, uint8_t color){
  588. uint8_t width, height;
  589. uint8_t i, j;
  590. width = ReadPgmData(bitmap++);
  591. height = ReadPgmData(bitmap++);
  592. for(j = 0; j < height / 8; j++) {
  593. this->GotoXY(x, y + (j*8) );
  594. for(i = 0; i < width; i++) {
  595. uint8_t displayData = ReadPgmData(bitmap++);
  596. if(color == BLACK)
  597. this->WriteData(displayData);
  598. else
  599. this->WriteData(~displayData);
  600. }
  601. }
  602. }
  603. // class wrapper
  604. ks0108::ks0108(){
  605. this->Inverted=0;
  606. }
  607. // Make one instance for the user
  608. ks0108 GLCD = ks0108();