/* Trixel.cpp - Liquid Crystal Display 1x3 character graphics Trixel *PRELIMINARY* Arduino library Copyright (c) 2011 Devon Sean McCullough Licensed under the GPL (GNU Public License) require LiquidCrystal library, see http://www.arduino.cc/en/Tutorial/LiquidCrystal Liquid Crystal Display trixel graphics (named after DEC VT-100 "sixel" graphics) shows three "trixels" per character cell. Display three bits (a,b,c) per LCD character like this a a a a a a a a a a . . . . . b b b b b b b b b b . . . . . c c c c c c c c c c where White = 0 = LOW Black = 1 = HIGH This LCD font RAM has only six characters so display tribits (0,0,0) and (1,1,1) as font ROM solid white and solid black (*), not perfect but a fair compromise. (*) SUNPLUS SPLC780D-001 codes 128 and 255. 2011 Jul 18 Mon 11:35 EDT Devon Written thanks to C++ help by Lyle Hazel 2011 Aug 02 Tue 12:34 EDT Devon setChar, moveWindow, boolean_operator POSSIBLE VARIATIONS white on black display option sprite library to animate/blink/throb font permitting, extend to NxM, e.g., 2x3 sixel graphics _tribit_to_lcd support for more LCD controllers than just SUNPLUS SPLC780D-001 CHARACTER GENERATOR ROM 8.1 BUGS Optional malloc destructor would lose anyway given this compiler bug http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1294392281 ... runtime C library version 1.6.4 ... memory leaks ... ... bug 28135 ... fixed in version 1.7.0 ... */ #include "WProgram.h" #include #include "Trixel.h" // LCD hardware grunge #define THREE_BIT_5X8_FONT_DIMENSION 6 #define __ B00000, B00000, /* white background */ #define XX B11111, B11111, /* black ink */ #define _ B00000, /* white border */ static const byte _three_bit_5x8_font[THREE_BIT_5X8_FONT_DIMENSION][8] = { //{__ _ __ _ __}, // B000 = 0 font ROM 128 solid white {XX _ __ _ __}, // B001 = 1 font RAM 0 {__ _ XX _ __}, // B010 = 2 font RAM 1 {XX _ XX _ __}, // B011 = 3 font RAM 2 {__ _ __ _ XX}, // B100 = 4 font RAM 3 {XX _ __ _ XX}, // B101 = 5 font RAM 4 {__ _ XX _ XX}, // B110 = 6 font RAM 5 //{XX X XX X XX}, // B111 = 7 font ROM 255 solid black //{XX _ XX _ XX} desired appearance of B111 compromise }; static const byte _tribit_to_lcd[256] = { // trixel characters 0 ... 7 128, 0, 1, 2, 3, 4, 5, 255, // plain characters 8 ... 255 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 }; // Trixel methods Trixel::Trixel(LiquidCrystal *lcd) { // LCD character device driver _lcd = lcd; _matrix = 0; } void Trixel::begin(byte width, byte height, byte *buffer) { // Trixel rectangle position in LCD character coordinates // BUG - should not need lcd.home() _x = _y = 0; // Trixel rectangle size in LCD characters _width = width; _height = height; // internal trixel buffer #ifdef DEBUG if (_matrix) lose("Trixel: free"); #endif _matrix = buffer ? buffer : (byte*)malloc(width * height); Trixel::clear(); #ifdef DEBUG if (!_matrix) lose("Trixel: malloc"); else #endif // initialize trixel font in physical LCD font RAM for (int i = 0; i < THREE_BIT_5X8_FONT_DIMENSION; i++) _lcd->createChar(i, (byte*)_three_bit_5x8_font[i]); // unconfuse LCD after createChar ??? _lcd->home(); } void Trixel::clear() { // clear the trixel buffer to white int i = _width * _height; while (i) _matrix[--i] = B000; // three white trixels } void Trixel::moveWindow(byte x, byte y) { // display trixel window upper left at lcd character coordinates x, y _x = x; _y = y; } void Trixel::setDot(byte x, byte y, trixel_color color, boolean_operator op) { // set buffer trixel at x,y to color // treat plain characters as all white = B000 byte lcd_x = x; byte lcd_y = y / 3; byte char_y = y % 3; byte lcd_index = lcd_y * _width + lcd_x; byte tribit_mask = 1 << char_y; byte character = _matrix[lcd_index]; if (!IS_TRIXEL(character)) character = B000; #define _(x) (static_cast(!!(x))) #define _1_ (_(character & tribit_mask)) #define _2_ (color) switch (op) { case boole_clr: color = trixel_white; break; case boole_nor: color = _(~(_1_ | _2_)); break; case boole_andc2: color = _(_1_ & ~_2_); break; case boole_c2: color = _(~_2_); break; case boole_andc1: color = _(~_1_ & _2_); break; case boole_c1: color = _(~_1_); break; case boole_xor: color = _(_1_ ^ _2_); break; case boole_nand: color = _(~(_1_ & _2_)); break; case boole_and: color = _(_1_ & _2_); break; case boole_eqv: color = _(~(_1_ ^ _2_)); break; case boole_1: color = _1_; break; case boole_orc2: color = _(_1_ | ~_2_); break; case boole_2: /* color = _2_; */ break; case boole_orc1: color = _(~_1_ | _2_); break; case boole_ior: color = _(_1_ | _2_); break; case boole_set: color = trixel_black; break; } _matrix[lcd_index] = (color ? character | tribit_mask : character & ~tribit_mask); } trixel_color Trixel::getDot(byte x, byte y) { byte lcd_x = x; byte lcd_y = y / 3; byte char_y = y % 3; byte lcd_index = lcd_y * _width + lcd_x; byte tribit_mask = 1 << char_y; byte character = _matrix[lcd_index]; return (!IS_TRIXEL(character) ? trixel_white : character & tribit_mask ? trixel_black : trixel_white); } void Trixel::setChar(byte x, byte y, byte character) { // set buffer trixels near x,y to character byte lcd_x = x; byte lcd_y = y / 3; byte lcd_index = lcd_y * _width + lcd_x; _matrix[lcd_index] = character; } byte Trixel::getChar(byte x, byte y) { byte lcd_x = x; byte lcd_y = y / 3; byte lcd_index = lcd_y * _width + lcd_x; return _matrix[lcd_index]; } #if 0 void Trixel::show() { // render the trixel buffer on the display // extra hair to allow negative offsets byte *matrix = _matrix; for (byte y = 0; y < _height; y++) { byte lcd_y = _y + y; _lcd->setCursor( _x >= 0 ? _x : 0, lcd_y >= 0 ? lcd_y : 0); for (byte x = 0; x < _width; x++) { byte lcd_x = _x + x; byte tribit = *matrix++; if (lcd_x >= 0 && lcd_y >= 0) _lcd->write(_tribit_to_lcd[tribit]); } } } #else void Trixel::show() { // render the trixel buffer on the display // extra hair to allow negative offsets byte *matrix = _matrix; for (byte y = 0; y < _height; y++) { byte lcd_y = _y + y; _lcd->setCursor( _x, lcd_y); for (byte x = 0; x < _width; x++) { byte tribit = *matrix++; _lcd->write(_tribit_to_lcd[tribit]); } } } #endif #ifdef DESTRUCTOR // Arduino destructor? I just met 'er! // Pointless, C runtime has memory leak Trixel::~Trixel() { free(_matrix); } #endif // end Trixel.cpp