1 /* 2 * Author: Jon Trulson <jtrulson (at) ics.com> 3 * Copyright (c) 2015 Intel Corporation. 4 * 5 * Author: Tyler Gibson <tgibson (at) microsoft.com> 6 * Copyright (c) 2015 Microsoft Corporation. 7 * 8 * Permission is hereby granted, free of charge, to any person obtaining 9 * a copy of this software and associated documentation files (the 10 * "Software"), to deal in the Software without restriction, including 11 * without limitation the rights to use, copy, modify, merge, publish, 12 * distribute, sublicense, and/or sell copies of the Software, and to 13 * permit persons to whom the Software is furnished to do so, subject to 14 * the following conditions: 15 * 16 * The above copyright notice and this permission notice shall be 17 * included in all copies or substantial portions of the Software. 18 * 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 23 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 */ 27 #include <unistd.h> 28 #include <iostream> 29 30 #include "eboled.h" 31 32 using namespace upm; 33 using namespace std; 34 35 static uint16_t screenBuffer[BUFFER_SIZE]; 36 37 EBOLED::EBOLED(int spi, int CD, int reset) : 38 m_spi(spi), m_gpioCD(CD), m_gpioRST(reset) 39 { 40 m_name = "EBOLED"; 41 m_textColor = COLOR_WHITE; 42 m_textWrap = 0; 43 m_textSize = 1; 44 m_cursorX = 0; 45 m_cursorY = 0; 46 47 m_gpioCD.dir(mraa::DIR_OUT); 48 m_gpioRST.dir(mraa::DIR_OUT); 49 50 //1000000 is standard. 51 m_spi.frequency(10000000); 52 53 // reset the device 54 m_gpioRST.write(1); 55 usleep(5000); 56 m_gpioRST.write(0); 57 usleep(10000); 58 m_gpioRST.write(1); 59 60 command(CMD_DISPLAYOFF); 61 62 command(CMD_SETDISPLAYCLOCKDIV); 63 command(0x80); 64 65 command(CMD_SETMULTIPLEX); 66 command(0x2f); 67 68 command(CMD_SETDISPLAYOFFSET); 69 command(0x0); // no offset 70 71 command(CMD_SETSTARTLINE | 0x0); // line #0 72 73 command(CMD_CHARGEPUMP); // enable charge pump 74 command(0x14); 75 76 command(CMD_NORMALDISPLAY); 77 command(CMD_DISPLAYALLONRESUME); 78 79 command(CMD_SEGREMAP | 0x1); // reverse mapping (SEG0==COL127) 80 command(CMD_COMSCANDEC); 81 82 command(CMD_SETCOMPINS); // custom COM PIN mapping 83 command(0x12); 84 85 command(CMD_SETCONTRAST); 86 command(0x8f); 87 88 command(CMD_SETPRECHARGE); 89 command(0xf1); 90 91 command(CMD_SETVCOMDESELECT); 92 command(0x40); 93 94 command(CMD_DISPLAYON); 95 96 usleep(4500); 97 98 setAddressingMode(HORIZONTAL); 99 100 //Set Page Address range, required for horizontal addressing mode. 101 command(CMD_SETPAGEADDRESS); // triple-byte cmd 102 command(0x00); //Initial page address 103 command(0x05); //Final page address 104 105 //Set Column Address range, required for horizontal addressing mode. 106 command(CMD_SETCOLUMNADDRESS); // triple-byte cmd 107 command(0x20); // this display has a horizontal offset of 20 columns 108 command(0x5f); // 64 columns wide - 0 based 63 offset 109 } 110 111 EBOLED::~EBOLED() 112 { 113 clear(); 114 } 115 116 mraa::Result EBOLED::refresh() 117 { 118 mraa::Result error = mraa::SUCCESS;; 119 120 m_gpioCD.write(1); // data mode 121 for(int i=0; i<BUFFER_SIZE; i++) 122 { 123 error = data(screenBuffer[i]); 124 if(error != mraa::SUCCESS) 125 return error; 126 } 127 128 return error; 129 } 130 131 mraa::Result EBOLED::write (std::string msg) 132 { 133 int len = msg.length(); 134 uint8_t temp_cursorX = m_cursorX; 135 for (int idx = 0; idx < len; idx++) 136 { 137 if (msg[idx] == '\n') 138 { 139 m_cursorY += m_textSize * 9; 140 temp_cursorX = m_cursorX; 141 } 142 else if (msg[idx] == '\r') 143 { 144 // skip em 145 } 146 else 147 { 148 drawChar(temp_cursorX, m_cursorY, msg[idx], m_textColor, m_textSize); 149 temp_cursorX += m_textSize * 6; 150 151 //textColor used to avoid wrapping if COLOR_BLACK is set. 152 if (m_textWrap && (m_textColor > OLED_WIDTH - temp_cursorX - 6)) 153 { 154 m_cursorY += m_textSize * 9; 155 temp_cursorX = m_cursorX; 156 } 157 } 158 } 159 return mraa::SUCCESS;; 160 } 161 162 mraa::Result EBOLED::setCursor (int row, int column) { 163 m_cursorX = column; 164 m_cursorY = row; 165 return mraa::SUCCESS;; 166 } 167 168 void EBOLED::setTextColor (uint8_t textColor) { 169 m_textColor = textColor; 170 } 171 172 void EBOLED::setTextSize (uint8_t size) { 173 m_textSize = (size > 0) ? size : 1; 174 } 175 176 void EBOLED::setTextWrap (uint8_t wrap) { 177 m_textWrap = wrap; 178 } 179 180 void EBOLED::drawChar (uint8_t x, uint8_t y, uint8_t data, uint8_t color, uint8_t size) { 181 if( (x >= OLED_WIDTH) || // Clip right 182 (y >= OLED_HEIGHT) || // Clip bottom 183 ((x + 6 * size - 1) < 0) || // Clip left 184 ((y + 8 * size - 1) < 0)) // Clip top 185 return; 186 187 if (data < 0x20 || data > 0x7F) { 188 data = 0x20; // space 189 } 190 191 for (int8_t i=0; i<6; i++ ) { 192 uint8_t line; 193 if (i == 6) 194 line = 0x0; 195 else 196 { 197 //32 offset to align standard ASCII range to index 198 line = BasicFont[data - 32][i+1]; 199 for (int8_t j = 0; j<8; j++) 200 { 201 if (line & 0x1) 202 { 203 if (size == 1) // default size 204 drawPixel(x+i, y+j, color); 205 else 206 drawRectangleFilled(x+(i*size), y+(j*size), size, size, color); // big size 207 } 208 line >>= 1; 209 } 210 } 211 } 212 } 213 214 mraa::Result EBOLED::clear() 215 { 216 mraa::Result error = mraa::SUCCESS;; 217 218 m_gpioCD.write(1); // data mode 219 for(int i=0; i<BUFFER_SIZE; i++) 220 { 221 error = data(0x0000); 222 if(error != mraa::SUCCESS) 223 return error; 224 } 225 226 return mraa::SUCCESS;; 227 } 228 229 mraa::Result EBOLED::home() 230 { 231 return setCursor(0, 0); 232 } 233 234 void EBOLED::drawPixel(int8_t x, int8_t y, uint8_t color) 235 { 236 if(x<0 || x>=OLED_WIDTH || y<0 || y>=OLED_HEIGHT) 237 return; 238 239 /* Screenbuffer is uint16 array, but pages are 8bit high so each buffer 240 * index is two columns. This means the index is based on x/2 and 241 * OLED_WIDTH/2 = VERT_COLUMNS. 242 * 243 * Then to set the appropriate bit, we need to shift based on the y 244 * offset in relation to the page and then adjust for high/low based 245 * on the x position. 246 */ 247 248 switch(color) 249 { 250 case COLOR_XOR: 251 screenBuffer[(x/2) + ((y/8) * VERT_COLUMNS)] ^= (1<<(y%8+(x%2 * 8))); 252 return; 253 case COLOR_WHITE: 254 screenBuffer[(x/2) + ((y/8) * VERT_COLUMNS)] |= (1<<(y%8+(x%2 * 8))); 255 return; 256 case COLOR_BLACK: 257 screenBuffer[(x/2) + ((y/8) * VERT_COLUMNS)] &= ~(1<<(y%8+(x%2 * 8))); 258 return; 259 } 260 } 261 262 void EBOLED::drawLine(int8_t x0, int8_t y0, int8_t x1, int8_t y1, uint8_t color) 263 { 264 int16_t steep = abs(y1 - y0) > abs(x1 - x0); 265 266 if (steep) { 267 swap(x0, y0); 268 swap(x1, y1); 269 } 270 271 if (x0 > x1) { 272 swap(x0, x1); 273 swap(y0, y1); 274 } 275 276 int16_t dx, dy; 277 dx = x1 - x0; 278 dy = abs (y1 - y0); 279 280 int16_t err = dx / 2; 281 int16_t ystep; 282 283 if (y0 < y1) { 284 ystep = 1; 285 } else { 286 ystep = -1; 287 } 288 289 for (; x0 <= x1; x0++) { 290 if (steep) { 291 drawPixel(y0, x0, color); 292 } else { 293 drawPixel(x0, y0, color); 294 } 295 err -= dy; 296 if (err < 0) { 297 y0 += ystep; 298 err += dx; 299 } 300 } 301 } 302 303 void EBOLED::drawLineHorizontal(int8_t x, int8_t y, uint8_t width, uint8_t color) 304 { 305 drawLine(x, y, x+width-1, y, color); 306 } 307 308 void EBOLED::drawLineVertical(int8_t x, int8_t y, uint8_t height, uint8_t color) 309 { 310 drawLine(x, y, x, y+height-1, color); 311 } 312 313 void EBOLED::drawRectangle(int8_t x, int8_t y, uint8_t width, uint8_t height, uint8_t color) 314 { 315 drawLineHorizontal(x, y, width, color); 316 drawLineHorizontal(x, y+height-1, color); 317 318 uint8_t innerHeight = height - 2; 319 if(innerHeight > 0) 320 { 321 drawLineVertical(x, y+1, innerHeight, color); 322 drawLineVertical(x+width-1, y+1, innerHeight, color); 323 } 324 } 325 326 void EBOLED::drawRoundedRectangle(int8_t x, int8_t y, int8_t width, int8_t height, int16_t radius, uint8_t color) { 327 // smarter version 328 drawLineHorizontal(x+radius , y , width-2*radius, color); // Top 329 drawLineHorizontal(x+radius , y+height-1, width-2*radius, color); // Bottom 330 drawLineVertical( x , y+radius , height-2*radius, color); // Left 331 drawLineVertical( x+width-1 , y+radius , height-2*radius, color); // Right 332 // draw four corners 333 drawRoundCorners(x+radius , y+radius , radius, 1, color); 334 drawRoundCorners(x+width-radius-1, y+radius , radius, 2, color); 335 drawRoundCorners(x+width-radius-1, y+height-radius-1, radius, 4, color); 336 drawRoundCorners(x+radius , y+height-radius-1, radius, 8, color); 337 } 338 339 void EBOLED::drawRectangleFilled(int8_t x, int8_t y, uint8_t width, uint8_t height, uint8_t color) 340 { 341 for (uint8_t i=x; i<x+width; i++) { 342 drawLineVertical(i, y, height, color); 343 } 344 } 345 346 void EBOLED::drawTriangle(int8_t x0, int8_t y0, int8_t x1, int8_t y1, int8_t x2, int8_t y2, uint8_t color) 347 { 348 drawLine(x0, y0, x1, y1, color); 349 drawLine(x1, y1, x2, y2, color); 350 drawLine(x2, y2, x0, y0, color); 351 } 352 353 void EBOLED::drawTriangleFilled ( int8_t x0, int8_t y0, int8_t x1, int8_t y1, int8_t x2, int8_t y2, uint8_t color) { 354 355 int16_t a, b, y, last; 356 357 // Sort coordinates by Y order (y2 >= y1 >= y0) 358 if (y0 > y1) { 359 swap(y0, y1); swap(x0, x1); 360 } 361 if (y1 > y2) { 362 swap(y2, y1); swap(x2, x1); 363 } 364 if (y0 > y1) { 365 swap(y0, y1); swap(x0, x1); 366 } 367 368 if(y0 == y2) { // Handle awkward all-on-same-line case as its own thing 369 a = b = x0; 370 if(x1 < a) a = x1; 371 else if(x1 > b) b = x1; 372 if(x2 < a) a = x2; 373 else if(x2 > b) b = x2; 374 drawLineHorizontal(a, y0, b-a+1, color); 375 return; 376 } 377 378 int16_t 379 dx01 = x1 - x0, 380 dy01 = y1 - y0, 381 dx02 = x2 - x0, 382 dy02 = y2 - y0, 383 dx12 = x2 - x1, 384 dy12 = y2 - y1; 385 int32_t 386 sa = 0, 387 sb = 0; 388 389 // For upper part of triangle, find scanline crossings for segments 390 // 0-1 and 0-2. If y1=y2 (flat-bottomed triangle), the scanline y1 391 // is included here (and second loop will be skipped, avoiding a /0 392 // error there), otherwise scanline y1 is skipped here and handled 393 // in the second loop...which also avoids a /0 error here if y0=y1 394 // (flat-topped triangle). 395 if(y1 == y2) last = y1; // Include y1 scanline 396 else last = y1-1; // Skip it 397 398 for(y=y0; y<=last; y++) { 399 a = x0 + sa / dy01; 400 b = x0 + sb / dy02; 401 sa += dx01; 402 sb += dx02; 403 /* longhand: 404 a = x0 + (x1 - x0) * (y - y0) / (y1 - y0); 405 b = x0 + (x2 - x0) * (y - y0) / (y2 - y0); 406 */ 407 if(a > b) swap(a,b); 408 drawLineHorizontal(a, y, b-a+1, color); 409 } 410 411 // For lower part of triangle, find scanline crossings for segments 412 // 0-2 and 1-2. This loop is skipped if y1=y2. 413 sa = dx12 * (y - y1); 414 sb = dx02 * (y - y0); 415 for(; y<=y2; y++) { 416 a = x1 + sa / dy12; 417 b = x0 + sb / dy02; 418 sa += dx12; 419 sb += dx02; 420 /* longhand: 421 a = x1 + (x2 - x1) * (y - y1) / (y2 - y1); 422 b = x0 + (x2 - x0) * (y - y0) / (y2 - y0); 423 */ 424 if(a > b) swap(a,b); 425 drawLineHorizontal(a, y, b-a+1, color); 426 } 427 } 428 429 void EBOLED::drawCircle(int16_t x0, int16_t y0, int16_t radius, uint8_t color) 430 { 431 int16_t f = 1 - radius; 432 int16_t ddF_x = 1; 433 int16_t ddF_y = -2 * radius; 434 int16_t x = 0; 435 int16_t y = radius; 436 437 drawPixel(x0 , y0+radius, color); 438 drawPixel(x0 , y0-radius, color); 439 drawPixel(x0+radius, y0 , color); 440 drawPixel(x0-radius, y0 , color); 441 442 while (x<y) 443 { 444 if (f >= 0) 445 { 446 y--; 447 ddF_y += 2; 448 f += ddF_y; 449 } 450 x++; 451 452 ddF_x += 2; 453 f += ddF_x; 454 455 drawPixel(x0 + x, y0 + y, color); 456 drawPixel(x0 - x, y0 + y, color); 457 drawPixel(x0 + x, y0 - y, color); 458 drawPixel(x0 - x, y0 - y, color); 459 drawPixel(x0 + y, y0 + x, color); 460 drawPixel(x0 - y, y0 + x, color); 461 drawPixel(x0 + y, y0 - x, color); 462 drawPixel(x0 - y, y0 - x, color); 463 } 464 } 465 466 void EBOLED::drawRoundCorners( int8_t x0, int8_t y0, int16_t radius, uint8_t cornername, uint8_t color) { 467 int16_t f = 1 - radius; 468 int16_t ddF_x = 1; 469 int16_t ddF_y = -2 * radius; 470 int16_t x = 0; 471 int16_t y = radius; 472 473 while (x<y) { 474 if (f >= 0) { 475 y--; 476 ddF_y += 2; 477 f += ddF_y; 478 } 479 x++; 480 ddF_x += 2; 481 f += ddF_x; 482 if (cornername & 0x4) { 483 drawPixel(x0 + x, y0 + y, color); 484 drawPixel(x0 + y, y0 + x, color); 485 } 486 if (cornername & 0x2) { 487 drawPixel(x0 + x, y0 - y, color); 488 drawPixel(x0 + y, y0 - x, color); 489 } 490 if (cornername & 0x8) { 491 drawPixel(x0 - y, y0 + x, color); 492 drawPixel(x0 - x, y0 + y, color); 493 } 494 if (cornername & 0x1) { 495 drawPixel(x0 - y, y0 - x, color); 496 drawPixel(x0 - x, y0 - y, color); 497 } 498 } 499 } 500 501 void EBOLED::drawCircleFilled(int8_t x0, int8_t y0, int16_t radius, uint8_t color) { 502 drawLineVertical(x0, y0-radius, 2*radius+1, color); 503 drawRoundedCornersFilled(x0, y0, radius, 3, 0, color); 504 } 505 506 void EBOLED::drawRoundedCornersFilled(int8_t x0, int8_t y0, int16_t radius, uint8_t cornername, int16_t delta, uint8_t color) { 507 508 int16_t f = 1 - radius; 509 int16_t ddF_x = 1; 510 int16_t ddF_y = -2 * radius; 511 int16_t x = 0; 512 int16_t y = radius; 513 514 while (x<y) { 515 if (f >= 0) { 516 y--; 517 ddF_y += 2; 518 f += ddF_y; 519 } 520 x++; 521 ddF_x += 2; 522 f += ddF_x; 523 524 if (cornername & 0x1) { 525 drawLineVertical(x0+x, y0-y, 2*y+1+delta, color); 526 drawLineVertical(x0+y, y0-x, 2*x+1+delta, color); 527 } 528 if (cornername & 0x2) { 529 drawLineVertical(x0-x, y0-y, 2*y+1+delta, color); 530 drawLineVertical(x0-y, y0-x, 2*x+1+delta, color); 531 } 532 } 533 } 534 535 void EBOLED::fillScreen(uint8_t color) 536 { 537 drawRectangleFilled(0, 0, OLED_WIDTH-1, OLED_HEIGHT-1, color); 538 } 539 540 mraa::Result EBOLED::setAddressingMode(displayAddressingMode mode) 541 { 542 mraa::Result rv; 543 544 rv = command(CMD_MEMORYADDRMODE); 545 rv = command(mode); 546 547 return rv; 548 } 549 550 mraa::Result EBOLED::command(uint8_t cmd) 551 { 552 m_gpioCD.write(0); // command mode 553 m_spi.writeByte(cmd); 554 return mraa::SUCCESS; 555 } 556 557 mraa::Result EBOLED::data(uint16_t data) 558 { 559 m_spi.write_word(data); 560 return mraa::SUCCESS; 561 } 562 563 void EBOLED::clearScreenBuffer() 564 { 565 for(int i=0; i<BUFFER_SIZE;i++) 566 screenBuffer[i] = 0x0000; 567 } 568