UNPKG

25.6 kBJavaScriptView Raw
1var Board = require("./board");
2var Pin = require("./pin");
3var lcdCharacters = require("./lcd-chars");
4var RGB = require("./led/rgb");
5
6var priv = new Map();
7
8/**
9 * This atrocity is unfortunately necessary.
10 * If any other approach can be found, patches
11 * will gratefully be accepted.
12 */
13function sleepus(usDelay) {
14 var startTime = process.hrtime();
15 var deltaTime;
16 var usWaited = 0;
17
18 while (usDelay > usWaited) {
19 deltaTime = process.hrtime(startTime);
20 usWaited = (deltaTime[0] * 1E9 + deltaTime[1]) / 1000;
21 }
22}
23
24/**
25 * This atrocity is unfortunately necessary.
26 * If any other approach can be found, patches
27 * will gratefully be accepted.
28 */
29function sleep(ms) {
30 sleepus(ms * 1000);
31}
32
33
34// TODO: Migrate this to the new codified Expander class.
35//
36// - add portMode to PCF8574 controller
37// - add portWrite to PCF8574 controller
38//
39//
40// TODO: Investigate adding the above methods to
41// all expander controllers.
42//
43function Expander(address, io) {
44 this.address = address;
45 this.mask = 0xFF;
46 this.memory = 0x00;
47 this.io = io;
48}
49
50Expander.prototype.pinMode = function(pin, dir) {
51 if (dir === 0x01) {
52 this.mask &= ~(1 << pin);
53 } else {
54 this.mask |= 1 << pin;
55 }
56};
57
58Expander.prototype.portMode = function(dir) {
59 this.mask = dir === 0x00 ? 0xFF : 0x00;
60};
61
62Expander.prototype.portWrite = function(value) {
63 this.memory = value & ~(this.mask);
64 this.io.i2cWrite(this.address, this.memory);
65};
66
67
68
69
70// const-caps throughout serve to indicate the
71// "const-ness" of the binding to the reader
72// and nothing more.
73
74var REGISTER = {
75 DEFAULT: {
76 SHIFT_LEFT: 0x04,
77
78 CLEAR: 0x01,
79 HOME: 0x02,
80 ENTRY: 0x04,
81 DISPLAY: 0x08,
82 DIMENSIONS: 0x20,
83 CURSORSHIFT: 0x10,
84
85 SETCGRAMADDR: 0x40,
86 SETDDRAMADDR: 0x80,
87
88 // Command And Control
89
90 DATA: 0x40,
91 COMMAND: 0x80,
92
93 // flags for display entry mode
94 ENTRYRIGHT: 0x00,
95 ENTRYLEFT: 0x02,
96 ENTRYSHIFTINCREMENT: 0x01,
97 ENTRYSHIFTDECREMENT: 0x00,
98
99 // flags for display on/off control
100 DISPLAYON: 0x04,
101 DISPLAYOFF: 0x00,
102 CURSORON: 0x02,
103 CURSOROFF: 0x00,
104 BLINKON: 0x01,
105 BLINKOFF: 0x00,
106
107 // flags for display/cursor shift
108 DISPLAYMOVE: 0x08,
109 CURSORMOVE: 0x00,
110 MOVERIGHT: 0x04,
111 MOVELEFT: 0x00,
112
113 // flags for function set
114 BITMODE: {
115 4: 0x00,
116 8: 0x10,
117 },
118
119 LINE: {
120 1: 0x00,
121 2: 0x08
122 },
123
124 DOTS: {
125 "5x10": 0x04,
126 "5x8": 0x00
127 },
128
129 // flags for backlight control
130 BACKLIGHT_ON: 0x08,
131 BACKLIGHT_OFF: 0x00,
132
133 MEMORYLIMIT: 0x08,
134
135 // Control
136 // Enable
137 EN: 0x04,
138 // Read/Write
139 RW: 0x02,
140 // Register Select
141 RS: 0x01,
142
143 // DATA
144 D4: 0x04,
145 D5: 0x05,
146 D6: 0x06,
147 D7: 0x07,
148 }
149};
150
151var Controllers = {
152 JHD1313M1: {
153 REGISTER: {
154 value: REGISTER.DEFAULT,
155 },
156 CHARS: {
157 value: lcdCharacters.DEFAULT,
158 },
159 initialize: {
160 value: function(opts) {
161
162 // LCD: 0x3E
163 // RGB: 0x62
164 this.address = {
165 lcd: opts.address || 0x3E,
166 rgb: 0x62
167 };
168
169 opts.address = this.address;
170
171 this.io.i2cConfig(opts);
172
173 this.lines = opts.lines || 2;
174 this.rows = opts.rows || 2;
175 this.cols = opts.cols || 16;
176 this.dots = opts.dots || "5x8";
177
178
179 var display = this.REGISTER.DISPLAY | this.REGISTER.DISPLAYON | this.REGISTER.CURSOROFF | this.REGISTER.BLINKOFF;
180
181 var state = {
182 display: display,
183 characters: {},
184 index: this.REGISTER.MEMORYLIMIT - 1,
185 backlight: {
186 polarity: 1,
187 pin: null,
188 value: null
189 }
190 };
191
192 priv.set(this, state);
193
194 // Operations within the following labelled block are init-only,
195 // but _do_ block the process negligible number of milliseconds.
196 blocking: {
197 var lines = this.REGISTER.DIMENSIONS | this.REGISTER.LINE[2];
198 // Copied from Grove Studio lib.
199 // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION!
200 // according to datasheet, we need at least 40ms after
201 // power rises above 2.7V before sending commands.
202 // Arduino can turn on way before 4.5V so we'll wait 50
203
204
205
206 sleep(50);
207 this.command(lines);
208 sleep(5);
209 this.command(lines);
210 this.command(lines);
211 this.command(lines);
212 sleep(5);
213
214 this.command(
215 this.REGISTER.ENTRY |
216 this.REGISTER.ENTRYLEFT |
217 this.REGISTER.ENTRYSHIFTDECREMENT
218 );
219
220 this.on();
221 this.clear();
222 this.home();
223 }
224
225 // Backlight initialization
226
227
228
229 this.bgOn();
230
231 if (opts.color) {
232 this.bgColor(opts.color);
233 } else {
234 this.bgColor("black");
235 }
236 },
237 },
238 clear: {
239 value: function() {
240 return this.command(this.REGISTER.CLEAR);
241 }
242 },
243 setCursor: {
244 value: function(col, row) {
245 return this.command(row === 0 ? col | 0x80 : col | 0xc0);
246 }
247 },
248 autoscroll: {
249 value: function() {
250 var state = priv.get(this);
251
252 state.display = this.REGISTER.ENTRYLEFT | this.REGISTER.ENTRYSHIFTINCREMENT;
253 this.command(this.REGISTER.ENTRY | state.display);
254
255 return this;
256 }
257 },
258 bgColor: {
259 value: function(red, green, blue) {
260 var rgb = RGB.ToRGB(red, green, blue);
261 var address = this.address.rgb;
262
263 this.io.i2cWrite(address, [0x00, 0]);
264 this.io.i2cWrite(address, [0x01, 0]);
265
266 // TRY THIS IN ONE CALL!
267 this.io.i2cWrite(address, [0x04, rgb.red]);
268 this.io.i2cWrite(address, [0x03, rgb.green]);
269 this.io.i2cWrite(address, [0x02, rgb.blue]);
270
271 return this;
272 }
273 },
274 bgOn: {
275 value: function() {
276 this.io.i2cWrite(this.address.rgb, [this.REGISTER.BACKLIGHT_ON, 0xAA]);
277 return this;
278 }
279 },
280 bgOff: {
281 value: function() {
282 this.io.i2cWrite(this.address.rgb, [this.REGISTER.BACKLIGHT_ON, 0x00]);
283 return this;
284 }
285 },
286 command: {
287 value: function(mode, value) {
288 if (arguments.length === 1) {
289 value = mode;
290 mode = this.REGISTER.COMMAND;
291 }
292
293 if (mode === this.REGISTER.DATA) {
294 return this.send(value);
295 }
296
297 return this.writeBits(this.REGISTER.COMMAND, value);
298 }
299 },
300 send: {
301 value: function(value) {
302 return this.writeBits(this.REGISTER.DATA, value);
303 }
304 },
305 writeBits: {
306 value: function(mode, value) {
307 this.io.i2cWrite(this.address.lcd, [mode, value]);
308 return this;
309 }
310 },
311 hilo: {
312 value: function(callback) {
313 callback.call(this);
314 }
315 },
316 },
317
318
319 PCF8574: {
320
321 REGISTER: {
322 value: Object.assign({}, REGISTER.DEFAULT, {
323 COMMAND: 0x00,
324 DATA: 0x01,
325 BACKLIGHT_ON: 0xFF,
326 BACKLIGHT_OFF: 0X00
327 }),
328 },
329 CHARS: {
330 value: lcdCharacters.DEFAULT,
331 },
332 initialize: {
333 value: function(opts) {
334
335 this.bitMode = opts.bitMode || 4;
336 this.lines = opts.lines || 2;
337 this.rows = opts.rows || 2;
338 this.cols = opts.cols || 16;
339 this.dots = opts.dots || "5x8";
340
341 if (!opts.address) {
342 opts.address = ["PCF8574A", "PCF8574AT"].includes(opts.controller) ?
343 0x3F : 0x27;
344
345 /*
346 | A2 | A1 | A0 | PCF8574(T) | PCF8574A(T) |
347 |----|----|----|---------|----------|
348 | L | L | L | 0x20 | 0x38 |
349 | L | L | H | 0x21 | 0x39 |
350 | L | H | L | 0x22 | 0x3A |
351 | L | H | H | 0x23 | 0x3B |
352 | H | L | L | 0x24 | 0x3C |
353 | H | L | H | 0x25 | 0x3D |
354 | H | H | L | 0x26 | 0x3E |
355 | H | H | H | 0x27 | 0x3F |
356
357 TODO: move to API docs
358 */
359 }
360
361 this.io.i2cConfig(opts);
362
363 this.address = {
364 lcd: opts.address
365 };
366
367 // Ported from https://bitbucket.org/fmalpartida/new-liquidcrystal
368 this.expander = new Expander(this.address.lcd, this.io);
369 this.expander.portMode(this.io.MODES.OUTPUT);
370 this.expander.portWrite(0);
371
372 var backlight = opts.backlight || {
373 polarity: 0,
374 pin: 3
375 };
376
377 backlight.pin = typeof backlight.pin === "undefined" ? 3 : backlight.pin;
378 backlight.polarity = typeof backlight.polarity === "undefined" ? 0 : backlight.polarity;
379
380 var dimensions = this.REGISTER.BITMODE[this.bitMode] |
381 this.REGISTER.LINE[this.lines] |
382 this.REGISTER.DOTS[this.dots];
383
384 var display = this.REGISTER.DISPLAY |
385 this.REGISTER.DISPLAYON |
386 this.REGISTER.CURSOROFF |
387 this.REGISTER.BLINKOFF;
388
389 var entry = this.REGISTER.ENTRYLEFT |
390 this.REGISTER.ENTRYSHIFTDECREMENT;
391
392
393 var state = {
394 display: display,
395 characters: {},
396 index: this.REGISTER.MEMORYLIMIT - 1,
397 backlight: {
398 polarity: backlight.polarity,
399 pinMask: 1 << backlight.pin,
400 statusMask: 0x00
401 },
402 data: [
403 1 << this.REGISTER.D4,
404 1 << this.REGISTER.D5,
405 1 << this.REGISTER.D6,
406 1 << this.REGISTER.D7
407 ]
408 };
409
410 priv.set(this, state);
411
412 var toggle = 0x03 << this.REGISTER.SHIFT_LEFT;
413
414 // Operations within the following labelled block are init-only,
415 // but _do_ block the process for negligible number of milliseconds.
416 blocking: {
417 //
418 // Toggle write/pulse to reset the LCD component.
419 //
420 this.expander.portWrite(toggle);
421 this.pulse(toggle);
422 sleep(4);
423
424 this.expander.portWrite(toggle);
425 this.pulse(toggle);
426 sleep(4);
427
428 this.expander.portWrite(toggle);
429 this.pulse(toggle);
430
431 toggle = 0x02 << this.REGISTER.SHIFT_LEFT;
432
433 this.expander.portWrite(toggle);
434 this.pulse(toggle);
435
436 // Initialize the reset component
437 this.command(this.REGISTER.DIMENSIONS | dimensions);
438
439 // Set display details
440 this.command(state.display);
441
442 // Now that the initial display is set,
443 // overwrite with the "entry" bits
444 state.display = entry;
445
446 this.command(this.REGISTER.ENTRY | state.display);
447
448 this.on();
449 this.clear();
450 this.backlight();
451 }
452 },
453 },
454 clear: {
455 value: function() {
456 this.command(this.REGISTER.CLEAR);
457 sleep(2);
458 return this;
459
460 }
461 },
462 backlight: {
463 value: function(value) {
464 var state = priv.get(this);
465 var mask;
466
467 value = typeof value === "undefined" ? 255 : value;
468
469 if (state.backlight.pinMask !== 0x00) {
470 if ((state.backlight.polarity === 0 && value > 0) ||
471 (state.backlight.polarity === 1 && value === 0)) {
472
473 mask = 0xFF;
474 } else {
475 mask = 0x00;
476 }
477
478 state.backlight.statusMask = state.backlight.pinMask & mask;
479
480 this.expander.portWrite(state.backlight.statusMask);
481 }
482
483 return this;
484 }
485 },
486
487 createChar: {
488 value: function(name, charMap) {
489 var state = priv.get(this);
490 var address;
491
492 if (typeof name === "number") {
493 address = name & 0x07;
494 } else {
495 address = state.index;
496 state.index--;
497 if (state.index === -1) {
498 state.index = this.REGISTER.MEMORYLIMIT - 1;
499 }
500 }
501
502 this.command(this.REGISTER.SETCGRAMADDR | (address << 3));
503
504 blocking: {
505 sleep(1);
506
507 for (var i = 0; i < 8; i++) {
508 this.command(this.REGISTER.DATA, charMap[i]);
509 sleep(1);
510 }
511 }
512
513 state.characters[name] = address;
514
515 return address;
516 }
517 },
518 noBacklight: {
519 value: function() {
520 this.backlight(0);
521 }
522 },
523 on: {
524 value: function() {
525 var state = priv.get(this);
526
527 state.display |= this.REGISTER.DISPLAYON;
528 this.command(this.REGISTER.DISPLAY | state.display);
529
530 return this;
531 }
532 },
533 off: {
534 value: function() {
535 var state = priv.get(this);
536
537 state.display &= ~this.REGISTER.DISPLAYON;
538 this.command(this.REGISTER.DISPLAY | state.display);
539
540 return this;
541 }
542 },
543 hilo: {
544 value: function(callback) {
545 callback.call(this);
546 }
547 },
548 command: {
549 value: function(mode, value) {
550
551 if (arguments.length === 1) {
552 value = mode;
553 mode = this.REGISTER.COMMAND;
554 }
555
556 this.send(mode, value);
557
558 return this;
559 }
560 },
561 send: {
562 writable: true,
563 value: function(mode, value) {
564
565 this.writeBits(mode, value >> 4);
566 this.writeBits(mode, value & 0x0F);
567
568 return this;
569 }
570 },
571 writeBits: {
572 writable: true,
573 value: function(mode, value) {
574 var state = priv.get(this);
575 var pinMapValue = 0;
576
577 for (var i = 0; i < 4; i++) {
578 if ((value & 0x01) === 1) {
579 pinMapValue |= state.data[i];
580 }
581 value = (value >> 1);
582 }
583
584 if (mode === this.REGISTER.DATA) {
585 mode = this.REGISTER.RS;
586 }
587
588 pinMapValue |= mode | state.backlight.statusMask;
589
590 this.pulse(pinMapValue);
591 return this;
592 }
593 },
594 pulse: {
595 writable: true,
596 value: function(data) {
597 this.expander.portWrite(data | this.REGISTER.EN); // En HIGH
598 this.expander.portWrite(data & ~this.REGISTER.EN); // En LOW
599 }
600 }
601 },
602
603
604 PARALLEL: {
605 REGISTER: {
606 value: REGISTER.DEFAULT,
607 },
608 CHARS: {
609 value: lcdCharacters.DEFAULT,
610 },
611 initialize: {
612 value: function(opts) {
613
614 this.bitMode = opts.bitMode || 4;
615 this.lines = opts.lines || 2;
616 this.rows = opts.rows || 2;
617 this.cols = opts.cols || 16;
618 this.dots = opts.dots || "5x8";
619
620 if (Array.isArray(opts.pins)) {
621 this.pins = {
622 rs: opts.pins[0],
623 en: opts.pins[1],
624 // TODO: Move to device map profile
625 data: [
626 opts.pins[5],
627 opts.pins[4],
628 opts.pins[3],
629 opts.pins[2]
630 ]
631 };
632 } else {
633 this.pins = opts.pins;
634 }
635
636 var display = this.REGISTER.DISPLAY | this.REGISTER.DISPLAYON;
637 var state = {
638 display: display,
639 characters: {},
640 index: this.REGISTER.MEMORYLIMIT - 1,
641 backlight: {
642 polarity: 1,
643 pin: null,
644 value: null
645 }
646 };
647
648 priv.set(this, state);
649
650 opts.pins.forEach(function(pin) {
651 this.io.pinMode(pin, 1);
652 }, this);
653
654 this.io.digitalWrite(this.pins.rs, this.io.LOW);
655 this.io.digitalWrite(this.pins.en, this.io.LOW);
656
657 if (opts.backlight) {
658 if (typeof opts.backlight === "number") {
659 var temp = opts.backlight;
660 opts.backlight = {
661 pin: temp
662 };
663 }
664
665 if (opts.backlight.pin) {
666 state.backlight.pin = new Pin({
667 pin: opts.backlight.pin,
668 board: this.board
669 });
670
671 state.backlight.pin.high();
672 }
673 }
674
675 // Operations within the following labelled block are init-only,
676 // but _do_ block the process negligible number of milliseconds.
677 blocking: {
678 // Send 0b00000011 thrice to make sure LCD
679 // is initialized properly
680 this.command(0x03);
681 sleep(4);
682 this.command(0x03);
683 sleep(4);
684 this.command(0x03);
685
686 // Switch to 4-bit mode
687 if (this.bitMode === 4) {
688 // this.REGISTER.DIMENSIONS |
689 this.command(0x02);
690 }
691
692 // Set number of lines and dots
693 // TODO: Move to device map profile
694 this.command(
695 this.REGISTER.LINE[this.lines] |
696 this.REGISTER.DOTS[this.dots]
697 );
698
699 // Clear display and turn it on
700 this.command(display);
701 this.clear();
702 this.home();
703 }
704 }
705 }
706 }
707};
708
709// Alias controllers
710Controllers.LCM1602 = Controllers.LCD1602 = Controllers.LCM1602IIC = Controllers.LCD2004 = Controllers.PCF8574A = Controllers.PCF8574AT = Controllers.PCF8574T = Controllers.PCF8574;
711
712Controllers.MJKDZ = Object.assign({}, Controllers.PCF8574, {
713 REGISTER: {
714 value: Object.assign({}, REGISTER.DEFAULT, {
715 SHIFT_LEFT: 0x00,
716
717 COMMAND: 0x00,
718 DATA: 0x06,
719
720 // Control
721 // Enable
722 EN: 0x10,
723 // Read/Write
724 RW: 0x05,
725 // Register Select
726 RS: 0x06,
727
728 D4: 0x00,
729 D5: 0x01,
730 D6: 0x02,
731 D7: 0x03
732 })
733 },
734 writeBits: {
735 writable: true,
736 value: function(mode, value) {
737 var state = priv.get(this);
738 var pinMapValue = 0;
739
740 for (var i = 0; i < 4; i++) {
741 if ((value & 0x01) === 1) {
742 pinMapValue |= state.data[i];
743 }
744 value = (value >> 1);
745 }
746
747 if (mode === this.REGISTER.DATA) {
748 mode = (1 << this.REGISTER.RS);
749 }
750
751 pinMapValue |= mode | state.backlight.statusMask;
752
753 this.pulse(pinMapValue);
754 return this;
755 }
756 },
757});
758
759/**
760 * LCD
761 * @param {[type]} opts [description]
762 */
763
764function LCD(opts) {
765
766 if (!(this instanceof LCD)) {
767 return new LCD(opts);
768 }
769
770 Board.Component.call(
771 this, opts = Board.Options(opts)
772 );
773
774 var controller = null;
775
776 if (opts.controller && typeof opts.controller === "string") {
777 controller = Controllers[opts.controller.toUpperCase()];
778 } else {
779 controller = opts.controller;
780 }
781
782 if (controller == null) {
783 controller = Controllers.PARALLEL;
784 }
785
786 Board.Controller.call(this, controller, opts);
787
788 this.ctype = opts.controller;
789
790 if (this.initialize) {
791 this.initialize(opts);
792 }
793
794 Object.defineProperties(this, {
795 characters: {
796 get: function() {
797 return Object.assign({}, priv.get(this).characters);
798 },
799 },
800 });
801}
802
803LCD.prototype.command = function(mode, value) {
804 if (typeof value === "undefined") {
805 value = mode;
806 mode = 0x80;
807 }
808
809 if (this.bitMode === 4) {
810 this.send(value >> 4);
811 }
812
813 this.send(value);
814
815 return this;
816};
817
818LCD.prototype.send = function(value) {
819 var pin = 0;
820 var mask = {
821 4: 8,
822 8: 128
823 }[this.bitMode];
824
825 for (; mask > 0; mask = mask >> 1) {
826 this.io.digitalWrite(
827 this.pins.data[pin],
828 this.io[value & mask ? "HIGH" : "LOW"]
829 );
830 pin++;
831 }
832
833 // At VCC = 3.3V, the minimum enable pulse width is specified as 450
834 // nanoseconds on page 49 of the HD44780 datasheet.
835 // We therefore wait for 1 microsecond here to ensure that fast IO plugins
836 // like Pi-IO generate an enable pulse that's wide enough.
837 this.io.digitalWrite(this.pins.en, this.io.LOW);
838 this.io.digitalWrite(this.pins.en, this.io.HIGH);
839 sleepus(1);
840 this.io.digitalWrite(this.pins.en, this.io.LOW);
841
842 // The execution time for the vast majority of instructions is at least
843 // 37 microseconds. See datasheet pages 24 and 25.
844 // It's important to wait 37 microseconds here to prevent fast IO plugins
845 // like Pi-IO from executing the next instruction before the current
846 // instruction has completed.
847 sleepus(37);
848
849 return this;
850};
851
852LCD.prototype.hilo = function(callback) {
853 // RS High for write mode
854 this.io.digitalWrite(this.pins.rs, this.io.HIGH);
855
856 callback.call(this);
857
858 // RS Low for command mode
859 this.io.digitalWrite(this.pins.rs, this.io.LOW);
860};
861
862
863
864var RE_SPECIALS = /:(\w+):/g;
865
866LCD.prototype.print = function(message, opts) {
867 var state, dontProcessSpecials, hasCharacters, processed;
868
869 message = message + "";
870 opts = opts || {};
871
872 state = priv.get(this);
873 dontProcessSpecials = opts.dontProcessSpecials || false;
874 hasCharacters = !dontProcessSpecials && RE_SPECIALS.test(message);
875
876 if (message.length === 1) {
877 this.hilo(function() {
878 this.command(this.REGISTER.DATA, message.charCodeAt(0));
879 });
880 } else {
881
882 if (hasCharacters) {
883 processed = message.replace(RE_SPECIALS, function(match, name) {
884 var address = state.characters[name];
885
886 return typeof address === "number" ? String.fromCharCode(address) : match;
887 });
888
889 this.print(processed, {
890 dontProcessSpecials: true
891 });
892 } else {
893 this.hilo(function() {
894 Array.from(message).forEach(function(character) {
895 this.command(this.REGISTER.DATA, character.charCodeAt(0));
896 }, this);
897 });
898 }
899 }
900
901 return this;
902};
903
904LCD.prototype.write = function(charCode) {
905 this.hilo.call(this, function() {
906 this.command(this.REGISTER.DATA, charCode);
907 });
908
909 return this;
910};
911
912LCD.prototype.clear = function() {
913 this.command(this.REGISTER.CLEAR);
914 sleep(2);
915 return this;
916};
917
918LCD.prototype.home = function() {
919 this.command(this.REGISTER.HOME);
920 sleep(2);
921 return this;
922};
923
924LCD.prototype.setCursor = function(col, row) {
925 var rowOffsets = [0x00, 0x40, 0x14, 0x54];
926 this.command(this.REGISTER.SETDDRAMADDR | (col + rowOffsets[row]));
927 return this;
928};
929
930LCD.prototype.backlight = function(highOrLow) {
931 var state = priv.get(this);
932
933 highOrLow = typeof highOrLow === "undefined" ? true : false;
934
935 if (state.backlight.pin instanceof Pin) {
936 if (highOrLow) {
937 state.backlight.pin.high();
938 } else {
939 state.backlight.pin.low();
940 }
941 }
942
943 if (highOrLow) {
944 state.display |= this.REGISTER.DISPLAYON;
945 } else {
946 state.display &= ~this.REGISTER.DISPLAYON;
947 }
948
949 this.command(state.display);
950
951 return this;
952};
953
954LCD.prototype.noBacklight = function() {
955 var state = priv.get(this);
956
957 if (state.backlight.pin instanceof Pin) {
958 state.backlight.pin.high();
959 }
960
961 // if (highOrLow) {
962 // state.display |= this.REGISTER.DISPLAYON;
963 // } else {
964 // state.display &= ~this.REGISTER.DISPLAYON;
965 // }
966
967 // this.command(state.display);
968
969 return this.backlight(false);
970};
971
972LCD.prototype.on = function() {
973 var state = priv.get(this);
974
975 state.display |= this.REGISTER.DISPLAYON;
976 this.command(state.display);
977
978 return this;
979};
980
981LCD.prototype.off = function() {
982 var state = priv.get(this);
983
984 state.display &= ~this.REGISTER.DISPLAYON;
985 this.command(state.display);
986
987 return this;
988};
989
990LCD.prototype.cursor = function(row, col) {
991 // When provided with col & row, cursor will behave like setCursor,
992 // except that it has row and col in the order that most people
993 // intuitively expect it to be in.
994 if (typeof col !== "undefined" && typeof row !== "undefined") {
995 return this.setCursor(col, row);
996 }
997 var state = priv.get(this);
998
999 state.display |= this.REGISTER.CURSORON;
1000 this.command(state.display);
1001
1002 return this;
1003};
1004
1005LCD.prototype.noCursor = function() {
1006 var state = priv.get(this);
1007
1008 state.display &= ~this.REGISTER.CURSORON;
1009 this.command(state.display);
1010
1011 return this;
1012};
1013
1014LCD.prototype.blink = function() {
1015 var state = priv.get(this);
1016
1017 state.display |= this.REGISTER.BLINKON;
1018 this.command(state.display);
1019
1020 return this;
1021};
1022
1023LCD.prototype.noBlink = function() {
1024 var state = priv.get(this);
1025
1026 state.display &= ~this.REGISTER.BLINKON;
1027 this.command(state.display);
1028
1029 return this;
1030};
1031
1032LCD.prototype.autoscroll = function() {
1033 var state = priv.get(this);
1034
1035 state.display |= this.REGISTER.ENTRYSHIFTINCREMENT;
1036 this.command(this.REGISTER.ENTRY | state.display);
1037
1038 return this;
1039};
1040
1041LCD.prototype.noAutoscroll = function() {
1042 var state = priv.get(this);
1043
1044 state.display &= ~this.REGISTER.ENTRYSHIFTINCREMENT;
1045 this.command(this.REGISTER.ENTRY | state.display);
1046
1047 return this;
1048};
1049
1050LCD.prototype.createChar = function(name, charMap) {
1051 // Ensure location is never above 7
1052 var state = priv.get(this);
1053 var address;
1054
1055 if (typeof name === "number") {
1056 address = name & 0x07;
1057 } else {
1058 address = state.index;
1059 state.index--;
1060 if (state.index === -1) {
1061 state.index = this.REGISTER.MEMORYLIMIT - 1;
1062 }
1063 }
1064
1065 this.command(this.REGISTER.SETCGRAMADDR | (address << 3));
1066
1067 this.hilo(function() {
1068 for (var i = 0; i < 8; i++) {
1069 this.command(this.REGISTER.DATA, charMap[i]);
1070 }
1071 });
1072
1073 // Fill in address
1074 state.characters[name] = address;
1075
1076 return address;
1077};
1078
1079
1080LCD.prototype.useChar = function(name) {
1081 var state = priv.get(this);
1082
1083 if (typeof state.characters[name] === "undefined") {
1084 // Create the character in LCD memory and
1085 var newCharIndex = this.createChar(name, this.CHARS[name]);
1086
1087 // If character's index already used, remove this character in current LCD character map
1088 // because it's not in LCD memory anymore.
1089 for (var oldName in state.characters) {
1090 if (name !== oldName && state.characters[oldName] === newCharIndex) {
1091 delete state.characters[oldName];
1092 break;
1093 }
1094 }
1095
1096 // Add character to current LCD character map
1097 state.characters[name] = newCharIndex;
1098 }
1099
1100 return this;
1101};
1102
1103
1104/**
1105 *
1106
1107TODO:
1108
1109
1110burst()
1111
1112scrollDisplayLeft()
1113scrollDisplayRight()
1114
1115leftToRight()
1116rightToLeft()
1117
1118
1119*/
1120
1121LCD.POSITIVE = 0;
1122LCD.NEGATIVE = 1;
1123
1124LCD.Characters = lcdCharacters;
1125
1126module.exports = LCD;