-
Notifications
You must be signed in to change notification settings - Fork 33
/
LiquidTWI2.cpp
719 lines (651 loc) · 21.1 KB
/
LiquidTWI2.cpp
1
2
3
4
5
6
7
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
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
#include "LiquidTWI2.h"
/*
LiquidTWI2 High Performance i2c LCD driver for MCP23008 & MCP23017
hacked by Sam C. Lin / http://www.lincomatic.com
from
LiquidTWI by Matt Falcon (FalconFour) / http://falconfour.com
logic gleaned from Adafruit RGB LCD Shield library
Panelolu2 support by Tony Lock / http://blog.think3dprint3d.com
enhancements by Nick Sayer / https://github.com/nsayer
Compatible with Adafruit I2C LCD backpack (MCP23008) and
Adafruit RGB LCD Shield
*/
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#if defined (__AVR_ATtiny84__) || defined(__AVR_ATtiny85__) || (__AVR_ATtiny2313__)
#include "TinyWireM.h"
#define Wire TinyWireM
#else
#include <Wire.h>
#endif
#if defined(ARDUINO) && (ARDUINO >= 100) //scl
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
//MCP23017 - Adafruit RGB LCD Shield
// bit pattern for the burstBits function is
//
// B7 B6 B5 B4 B3 B2 B1 B0 A7 A6 A5 A4 A3 A2 A1 A0 - MCP23017
// RS RW EN D4 D5 D6 D7 LB LG LR BZ B4 B3 B2 B1 B0
// 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
#define M17_BIT_RS 0x8000
#define M17_BIT_RW 0x4000
#define M17_BIT_EN 0x2000
#define M17_BIT_D4 0x1000
#define M17_BIT_D5 0x0800
#define M17_BIT_D6 0x0400
#define M17_BIT_D7 0x0200
#define M17_BIT_LB 0x0100
#define M17_BIT_LG 0x0080
#define M17_BIT_LR 0x0040
#define M17_BIT_BZ 0x0020 //Added a buzzer on this pin
#define M17_BIT_B4 0x0010
#define M17_BIT_B3 0x0008
#define M17_BIT_B2 0x0004
#define M17_BIT_B1 0x0002
#define M17_BIT_B0 0x0001
static inline void wiresend(uint8_t x) {
#if ARDUINO >= 100
Wire.write((uint8_t)x);
#else
Wire.send(x);
#endif
}
static inline uint8_t wirerecv(void) {
#if ARDUINO >= 100
return Wire.read();
#else
return Wire.receive();
#endif
}
// When the display powers up, it is configured as follows:
//
// 1. Display clear
// 2. Function set:
// DL = 0; 4-bit interface data
// N = 0; 1-line display
// F = 0; 5x8 dot character font
// 3. Display on/off control:
// D = 0; Display off
// C = 0; Cursor off
// B = 0; Blinking off
// 4. Entry mode set:
// I/D = 1; Increment by 1
// S = 0; No shift
//
// Note, however, that resetting the Arduino doesn't reset the LCD, so we
// can't assume that its in that state when a sketch starts (and the
// LiquidTWI2 constructor is called). This is why we save the init commands
// for when the sketch calls begin(), except configuring the expander, which
// is required by any setup.
LiquidTWI2::LiquidTWI2(uint8_t i2cAddr,uint8_t detectDevice, uint8_t backlightInverted) {
// if detectDevice != 0, set _deviceDetected to 2 to flag that we should
// scan for it in begin()
#ifdef DETECT_DEVICE
_deviceDetected = detectDevice ? 2 : 1;
#endif
_backlightInverted = backlightInverted;
// if (i2cAddr > 7) i2cAddr = 7;
_i2cAddr = i2cAddr; // transfer this function call's number into our internal class state
_displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS; // in case they forget to call begin() at least we have something
#if defined(MCP23017)&&defined(MCP23008)
_mcpType = DEFAULT_TYPE; // default
#endif
}
void LiquidTWI2::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) {
// SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION!
// according to datasheet, we need at least 40ms after power rises above 2.7V
// before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50
delay(50);
Wire.begin();
uint8_t result;
if (cols == 20 && lines == 4 ) {
_row_offsets [0] = 0x00;
_row_offsets [1] = 0x40;
_row_offsets [2] = 0x14;
_row_offsets [3] = 0x54;
} else{
_row_offsets [0] = 0x00;
_row_offsets [1] = 0x40;
_row_offsets [2] = 0x10;
_row_offsets [3] = 0x50;
}
#if defined(MCP23017)&&defined(MCP23008)
if (_mcpType == LTI_TYPE_MCP23017) {
#endif
#ifdef MCP23017
/* don't think we need this
// set defaults!
Wire.beginTransmission(MCP23017_ADDRESS | _i2cAddr);
wiresend(MCP23017_IODIRA);
wiresend(0xFF); // all inputs on port A
result = Wire.endTransmission();
#ifdef DETECT_DEVICE
if (result) {
if (_deviceDetected == 2) {
_deviceDetected = 0;
return;
}
}
#endif
Wire.beginTransmission(MCP23017_ADDRESS | _i2cAddr);
wiresend(MCP23017_IODIRB);
wiresend(0xFF); // all inputs on port B
result = Wire.endTransmission();
#ifdef DETECT_DEVICE
if (result) {
if (_deviceDetected == 2) {
_deviceDetected = 0;
return;
}
}
#endif
*/
// now set up input/output pins
Wire.beginTransmission(MCP23017_ADDRESS | _i2cAddr);
wiresend(MCP23017_IODIRA);
wiresend(0x1F); // buttons input, all others output
result = Wire.endTransmission();
#ifdef DETECT_DEVICE
if (result) {
if (_deviceDetected == 2) {
_deviceDetected = 0;
return;
}
}
#endif
// set the button pullups
Wire.beginTransmission(MCP23017_ADDRESS | _i2cAddr);
wiresend(MCP23017_GPPUA);
wiresend(0x1F);
result = Wire.endTransmission();
#ifdef DETECT_DEVICE
if (result) {
if (_deviceDetected == 2) {
_deviceDetected = 0;
return;
}
}
#endif
Wire.beginTransmission(MCP23017_ADDRESS | _i2cAddr);
wiresend(MCP23017_IODIRB);
wiresend(0x00); // all pins output
result = Wire.endTransmission();
#ifdef DETECT_DEVICE
if (result) {
if (_deviceDetected == 2) {
_deviceDetected = 0;
return;
}
}
#endif
#endif // MCP23017
#if defined(MCP23017)&&defined(MCP23008)
}
else { // MCP23008
#endif
#ifdef MCP23008
// first thing we do is get the GPIO expander's head working straight, with a boatload of junk data.
Wire.beginTransmission(MCP23008_ADDRESS | _i2cAddr);
wiresend(MCP23008_IODIR);
wiresend(0xFF);
wiresend(0x00);
wiresend(0x00);
wiresend(0x00);
wiresend(0x00);
wiresend(0x00);
wiresend(0x00);
wiresend(0x00);
wiresend(0x00);
wiresend(0x00);
result = Wire.endTransmission();
#ifdef DETECT_DEVICE
if (result) {
if (_deviceDetected == 2) {
_deviceDetected = 0;
return;
}
}
#endif
// now we set the GPIO expander's I/O direction to output since it's soldered to an LCD output.
Wire.beginTransmission(MCP23008_ADDRESS | _i2cAddr);
wiresend(MCP23008_IODIR);
wiresend(0x00); // all output: 00000000 for pins 1...8
result = Wire.endTransmission();
#ifdef DETECT_DEVICE
if (result) {
if (_deviceDetected == 2) {
_deviceDetected = 0;
return;
}
}
#endif
#endif // MCP23008
#if defined(MCP23017)&&defined(MCP23008)
}
#endif
#ifdef DETECT_DEVICE
// If we haven't failed by now, then we pass
if (_deviceDetected == 2) _deviceDetected = 1;
#endif
if (lines > 1) {
_displayfunction |= LCD_2LINE;
}
_numlines = lines;
_currline = 0;
// for some 1 line displays you can select a 10 pixel high font
if ((dotsize != 0) && (lines == 1)) {
_displayfunction |= LCD_5x10DOTS;
}
//put the LCD into 4 bit mode
// start with a non-standard command to make it realize we're speaking 4-bit here
// per LCD datasheet, first command is a single 4-bit burst, 0011.
//-----
// we cannot assume that the LCD panel is powered at the same time as
// the arduino, so we have to perform a software reset as per page 45
// of the HD44780 datasheet - (kch)
//-----
#if defined(MCP23017)&&defined(MCP23008)
if (_mcpType == LTI_TYPE_MCP23017) {
#endif // defined(MCP23017)&&defined(MCP23008)
#ifdef MCP23017
_backlightBits = 0; // all backlight LED's on
// bit pattern for the burstBits function is
//
// B7 B6 B5 B4 B3 B2 B1 B0 A7 A6 A5 A4 A3 A2 A1 A0 - MCP23017
// 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
// RS RW EN D4 D5 D6 D7 B G R B4 B3 B2 B1 B0
for (uint8_t i=0;i < 3;i++) {
burstBits8b((M17_BIT_EN|M17_BIT_D5|M17_BIT_D4) >> 8);
burstBits8b((M17_BIT_D5|M17_BIT_D4) >> 8);
}
burstBits8b((M17_BIT_EN|M17_BIT_D5) >> 8);
burstBits8b(M17_BIT_D5 >> 8);
#endif // MCP23017
#if defined(MCP23017)&&defined(MCP23008)
}
else {
#endif // defined(MCP23017)&&defined(MCP23008)
#ifdef MCP23008
// bit pattern for the burstBits function is
//
// 7 6 5 4 3 2 1 0
// LT D7 D6 D5 D4 EN RS n/c
//-----
burstBits8(B10011100); // send LITE D4 D5 high with enable
burstBits8(B10011000); // send LITE D4 D5 high with !enable
burstBits8(B10011100); //
burstBits8(B10011000); //
burstBits8(B10011100); // repeat twice more
burstBits8(B10011000); //
burstBits8(B10010100); // send D4 low and LITE D5 high with enable
burstBits8(B10010000); // send D4 low and LITE D5 high with !enable
#endif // MCP23008
#if defined(MCP23017)&&defined(MCP23008)
}
#endif
delay(5); // this shouldn't be necessary, but sometimes 16MHz is stupid-fast.
command(LCD_FUNCTIONSET | _displayfunction); // then send 0010NF00 (N=lines, F=font)
delay(5); // for safe keeping...
command(LCD_FUNCTIONSET | _displayfunction); // ... twice.
delay(5); // done!
// turn on the LCD with our defaults. since these libs seem to use personal preference, I like a cursor.
_displaycontrol = (LCD_DISPLAYON|LCD_BACKLIGHT);
display();
// clear it off
clear();
_displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;
// set the entry mode
command(LCD_ENTRYMODESET | _displaymode);
}
/********** high level commands, for the user! */
void LiquidTWI2::clear()
{
#ifdef DETECT_DEVICE
if (!_deviceDetected) return;
#endif
command(LCD_CLEARDISPLAY); // clear display, set cursor position to zero
delayMicroseconds(2000); // this command takes a long time!
}
void LiquidTWI2::home()
{
#ifdef DETECT_DEVICE
if (!_deviceDetected) return;
#endif
command(LCD_RETURNHOME); // set cursor position to zero
delayMicroseconds(2000); // this command takes a long time!
}
void LiquidTWI2::setCursor(uint8_t col, uint8_t row)
{
#ifdef DETECT_DEVICE
if (!_deviceDetected) return;
#endif
if ( row > _numlines ) row = _numlines - 1; // we count rows starting w/0
command(LCD_SETDDRAMADDR | (col + _row_offsets[row]));
}
// Turn the display on/off (quickly)
void LiquidTWI2::noDisplay() {
#ifdef DETECT_DEVICE
if (!_deviceDetected) return;
#endif
_displaycontrol &= ~LCD_DISPLAYON;
command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void LiquidTWI2::display() {
#ifdef DETECT_DEVICE
if (!_deviceDetected) return;
#endif
_displaycontrol |= LCD_DISPLAYON;
command(LCD_DISPLAYCONTROL | _displaycontrol);
}
// Turns the underline cursor on/off
void LiquidTWI2::noCursor() {
#ifdef DETECT_DEVICE
if (!_deviceDetected) return;
#endif
_displaycontrol &= ~LCD_CURSORON;
command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void LiquidTWI2::cursor() {
#ifdef DETECT_DEVICE
if (!_deviceDetected) return;
#endif
_displaycontrol |= LCD_CURSORON;
command(LCD_DISPLAYCONTROL | _displaycontrol);
}
// Turn on and off the blinking cursor
void LiquidTWI2::noBlink() {
#ifdef DETECT_DEVICE
if (!_deviceDetected) return;
#endif
_displaycontrol &= ~LCD_BLINKON;
command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void LiquidTWI2::blink() {
#ifdef DETECT_DEVICE
if (!_deviceDetected) return;
#endif
_displaycontrol |= LCD_BLINKON;
command(LCD_DISPLAYCONTROL | _displaycontrol);
}
// These commands scroll the display without changing the RAM
void LiquidTWI2::scrollDisplayLeft(void) {
#ifdef DETECT_DEVICE
if (!_deviceDetected) return;
#endif
command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT);
}
void LiquidTWI2::scrollDisplayRight(void) {
#ifdef DETECT_DEVICE
if (!_deviceDetected) return;
#endif
command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT);
}
// This is for text that flows Left to Right
void LiquidTWI2::leftToRight(void) {
#ifdef DETECT_DEVICE
if (!_deviceDetected) return;
#endif
_displaymode |= LCD_ENTRYLEFT;
command(LCD_ENTRYMODESET | _displaymode);
}
// This is for text that flows Right to Left
void LiquidTWI2::rightToLeft(void) {
#ifdef DETECT_DEVICE
if (!_deviceDetected) return;
#endif
_displaymode &= ~LCD_ENTRYLEFT;
command(LCD_ENTRYMODESET | _displaymode);
}
// This will 'right justify' text from the cursor
void LiquidTWI2::autoscroll(void) {
#ifdef DETECT_DEVICE
if (!_deviceDetected) return;
#endif
_displaymode |= LCD_ENTRYSHIFTINCREMENT;
command(LCD_ENTRYMODESET | _displaymode);
}
// This will 'left justify' text from the cursor
void LiquidTWI2::noAutoscroll(void) {
#ifdef DETECT_DEVICE
if (!_deviceDetected) return;
#endif
_displaymode &= ~LCD_ENTRYSHIFTINCREMENT;
command(LCD_ENTRYMODESET | _displaymode);
}
// Allows us to fill the first 8 CGRAM locations
// with custom characters
void LiquidTWI2::createChar(uint8_t location, uint8_t charmap[]) {
#ifdef DETECT_DEVICE
if (!_deviceDetected) return;
#endif
location &= 0x7; // we only have 8 locations 0-7
command(LCD_SETCGRAMADDR | (location << 3));
for (int i=0; i<8; i++) {
write(charmap[i]);
}
}
/*********** mid level commands, for sending data/cmds */
inline void LiquidTWI2::command(uint8_t value) {
send(value, LOW);
}
#if defined(ARDUINO) && (ARDUINO >= 100) //scl
inline size_t LiquidTWI2::write(uint8_t value) {
#ifdef DETECT_DEVICE
if (!_deviceDetected) return 1;
#endif
send(value, HIGH);
return 1;
}
#else
inline void LiquidTWI2::write(uint8_t value) {
#ifdef DETECT_DEVICE
if (!_deviceDetected) return;
#endif
send(value, HIGH);
}
#endif
/************ low level data pushing commands **********/
#ifdef MCP23017
uint8_t LiquidTWI2::readButtons(void) {
#ifdef DETECT_DEVICE
if (!_deviceDetected) return 0;
#endif
Wire.beginTransmission(MCP23017_ADDRESS | _i2cAddr);
wiresend(MCP23017_GPIOA);
Wire.endTransmission();
Wire.requestFrom(MCP23017_ADDRESS | _i2cAddr, 1);
return ~wirerecv() & ALL_BUTTON_BITS;
}
#endif // MCP23017
// Allows to set the backlight, if the LCD backpack is used
void LiquidTWI2::setBacklight(uint8_t status) {
#ifdef DETECT_DEVICE
if (!_deviceDetected) return;
#endif
if (_backlightInverted) status ^= 0x7;
#if defined(MCP23017)&&defined(MCP23008)
if (_mcpType == LTI_TYPE_MCP23017) {
#endif
#ifdef MCP23017
// LED turns on when bit is cleared
_backlightBits = M17_BIT_LB|M17_BIT_LG|M17_BIT_LR; // all off
if (status & RED) _backlightBits &= ~M17_BIT_LR; // red on
if (status & GREEN) _backlightBits &= ~M17_BIT_LG; // green on
if (status & BLUE) _backlightBits &= ~M17_BIT_LB; // blue on
burstBits16(_backlightBits);
#endif // MCP23017
#if defined(MCP23017)&&defined(MCP23008)
}
else {
#endif
#ifdef MCP23008
bitWrite(_displaycontrol,3,status); // flag that the backlight is enabled, for burst commands
burstBits8((_displaycontrol & LCD_BACKLIGHT)?0x80:0x00);
#endif // MCP23008
#if defined(MCP23017)&&defined(MCP23008)
}
#endif
}
// write either command or data, burst it to the expander over I2C.
void LiquidTWI2::send(uint8_t value, uint8_t mode) {
#if defined(MCP23017)&&defined(MCP23008)
if (_mcpType == LTI_TYPE_MCP23017) {
#endif
#ifdef MCP23017
// BURST SPEED, OH MY GOD
// the (now High Speed!) I/O expander pinout
// B7 B6 B5 B4 B3 B2 B1 B0 A7 A6 A5 A4 A3 A2 A1 A0 - MCP23017
// 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
// RS RW EN D4 D5 D6 D7 B G R B4 B3 B2 B1 B0
// n.b. RW bit stays LOW to write
uint8_t buf = _backlightBits >> 8;
// send high 4 bits
if (value & 0x10) buf |= M17_BIT_D4 >> 8;
if (value & 0x20) buf |= M17_BIT_D5 >> 8;
if (value & 0x40) buf |= M17_BIT_D6 >> 8;
if (value & 0x80) buf |= M17_BIT_D7 >> 8;
if (mode) buf |= (M17_BIT_RS|M17_BIT_EN) >> 8; // RS+EN
else buf |= M17_BIT_EN >> 8; // EN
burstBits8b(buf);
// resend w/ EN turned off
buf &= ~(M17_BIT_EN >> 8);
burstBits8b(buf);
// send low 4 bits
buf = _backlightBits >> 8;
// send high 4 bits
if (value & 0x01) buf |= M17_BIT_D4 >> 8;
if (value & 0x02) buf |= M17_BIT_D5 >> 8;
if (value & 0x04) buf |= M17_BIT_D6 >> 8;
if (value & 0x08) buf |= M17_BIT_D7 >> 8;
if (mode) buf |= (M17_BIT_RS|M17_BIT_EN) >> 8; // RS+EN
else buf |= M17_BIT_EN >> 8; // EN
burstBits8b(buf);
// resend w/ EN turned off
buf &= ~(M17_BIT_EN >> 8);
burstBits8b(buf);
#endif // MCP23017
#if defined(MCP23017)&&defined(MCP23008)
}
else {
#endif
#ifdef MCP23008
// BURST SPEED, OH MY GOD
// the (now High Speed!) I/O expander pinout
// RS pin = 1
// Enable pin = 2
// Data pin 4 = 3
// Data pin 5 = 4
// Data pin 6 = 5
// Data pin 7 = 6
byte buf;
// crunch the high 4 bits
buf = (value & B11110000) >> 1; // isolate high 4 bits, shift over to data pins (bits 6-3: x1111xxx)
if (mode) buf |= 3 << 1; // here we can just enable enable, since the value is immediately written to the pins
else buf |= 2 << 1; // if RS (mode), turn RS and enable on. otherwise, just enable. (bits 2-1: xxxxx11x)
buf |= (_displaycontrol & LCD_BACKLIGHT)?0x80:0x00; // using DISPLAYCONTROL command to mask backlight bit in _displaycontrol
burstBits8(buf); // bits are now present at LCD with enable active in the same write
// no need to delay since these things take WAY, WAY longer than the time required for enable to settle (1us in LCD implementation?)
buf &= ~(1<<2); // toggle enable low
burstBits8(buf); // send out the same bits but with enable low now; LCD crunches these 4 bits.
// crunch the low 4 bits
buf = (value & B1111) << 3; // isolate low 4 bits, shift over to data pins (bits 6-3: x1111xxx)
if (mode) buf |= 3 << 1; // here we can just enable enable, since the value is immediately written to the pins
else buf |= 2 << 1; // if RS (mode), turn RS and enable on. otherwise, just enable. (bits 2-1: xxxxx11x)
buf |= (_displaycontrol & LCD_BACKLIGHT)?0x80:0x00; // using DISPLAYCONTROL command to mask backlight bit in _displaycontrol
burstBits8(buf);
buf &= ~( 1 << 2 ); // toggle enable low (1<<2 = 00000100; NOT = 11111011; with "and", this turns off only that one bit)
burstBits8(buf);
#endif // MCP23008
#if defined(MCP23017)&&defined(MCP23008)
}
#endif
}
#ifdef MCP23017
// value byte order is BA
void LiquidTWI2::burstBits16(uint16_t value) {
// we use this to burst bits to the GPIO chip whenever we need to. avoids repetitive code.
Wire.beginTransmission(MCP23017_ADDRESS | _i2cAddr);
wiresend(MCP23017_GPIOA);
wiresend(value & 0xFF); // send A bits
wiresend(value >> 8); // send B bits
while(Wire.endTransmission());
}
/*
void LiquidTWI2::burstBits8a(uint8_t value) {
// we use this to burst bits to the GPIO chip whenever we need to. avoids repetitive code.
Wire.beginTransmission(MCP23017_ADDRESS | _i2cAddr);
wiresend(MCP23017_GPIOA);
wiresend(value); // last bits are crunched, we're done.
while(Wire.endTransmission());
}
*/
void LiquidTWI2::burstBits8b(uint8_t value) {
// we use this to burst bits to the GPIO chip whenever we need to. avoids repetitive code.
Wire.beginTransmission(MCP23017_ADDRESS | _i2cAddr);
wiresend(MCP23017_GPIOB);
wiresend(value); // last bits are crunched, we're done.
while(Wire.endTransmission());
}
#endif // MCP23017
#ifdef MCP23008
void LiquidTWI2::burstBits8(uint8_t value) {
// we use this to burst bits to the GPIO chip whenever we need to. avoids repetitive code.
Wire.beginTransmission(MCP23008_ADDRESS | _i2cAddr);
wiresend(MCP23008_GPIO);
wiresend(value); // last bits are crunched, we're done.
while(Wire.endTransmission());
}
#endif // MCP23008
#ifdef MCP23017
//direct access to the registers for interrupt setting and reading, also the tone function using buzzer pin
uint8_t LiquidTWI2::readRegister(uint8_t reg) {
// read a register
Wire.beginTransmission(MCP23017_ADDRESS | _i2cAddr);
wiresend(reg);
Wire.endTransmission();
Wire.requestFrom(MCP23017_ADDRESS | _i2cAddr, 1);
return wirerecv();
}
//set registers
void LiquidTWI2::setRegister(uint8_t reg, uint8_t value) {
Wire.beginTransmission(MCP23017_ADDRESS | _i2cAddr);
wiresend(reg);
wiresend(value);
Wire.endTransmission();
}
//cycle the buzzer pin at a certain frequency (hz) for a certain duration (ms)
//note: a 100Khz TWI/I2C bus on a 16Mhz Arduino will max out at around 1500Hz freq
void LiquidTWI2::buzz(long duration, uint16_t freq) {
#ifdef DETECT_DEVICE
if (!_deviceDetected) return;
#endif
int currentRegister = 0;
// read gpio register
Wire.beginTransmission(MCP23017_ADDRESS | _i2cAddr);
wiresend(MCP23017_GPIOA);
Wire.endTransmission();
Wire.requestFrom(MCP23017_ADDRESS | _i2cAddr, 1);
currentRegister = wirerecv();
duration *=1000; //convert from ms to us
unsigned long cycletime = 1000000UL / freq; // period in us
unsigned long cycles = (unsigned long)duration / cycletime;
unsigned long ontime;
while (cycles-- > 0)
{
ontime = micros();
Wire.beginTransmission(MCP23017_ADDRESS | _i2cAddr);
wiresend(MCP23017_GPIOA);
wiresend(currentRegister |= M17_BIT_BZ);
while(Wire.endTransmission());
while((long)(ontime + (cycletime/2) - micros()) > 0);
Wire.beginTransmission(MCP23017_ADDRESS | _i2cAddr);
wiresend(MCP23017_GPIOA);
wiresend(currentRegister &= ~M17_BIT_BZ);
while(Wire.endTransmission());
while((long)(ontime + cycletime - micros()) > 0);
}
}
#endif //MCP23017