Commit | Line | Data |
---|---|---|
399cb110 | 1 | /* |
35089dc7 JM |
2 | This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). |
3 | Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. | |
4 | Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. | |
399cb110 | 5 | You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. |
35089dc7 JM |
6 | */ |
7 | #include "VikiLCD.h" | |
8 | ||
a0e79e9b JM |
9 | #include "Button.h" |
10 | ||
ca6effd7 JM |
11 | // config settings for Viki LCD |
12 | #define panel_checksum CHECKSUM("panel") | |
13 | #define encoder_a_pin_checksum CHECKSUM("encoder_a_pin") | |
14 | #define encoder_b_pin_checksum CHECKSUM("encoder_b_pin") | |
15 | #define button_pause_pin_checksum CHECKSUM("button_pause_pin") | |
16 | #define i2c_pins_checksum CHECKSUM("i2c_pins") | |
17 | #define i2c_frequency_checksum CHECKSUM("i2c_frequency") | |
18 | ||
35089dc7 JM |
19 | // if this is defined we use the R/W poll mode instead of fixed delays |
20 | // However at the slower I2C frequency required for Viki long cables it is slower than fixed delay | |
21 | // taken from LiquidCrystalFast.cpp and implemented for Viki LCD here by Jim Morris | |
22 | //#define USE_FASTMODE | |
23 | ||
a0e79e9b JM |
24 | // readButtons() will only return these bit values |
25 | #define VIKI_ALL_BUTTON_BITS (BUTTON_PAUSE|BUTTON_UP|BUTTON_DOWN|BUTTON_LEFT|BUTTON_RIGHT|BUTTON_SELECT) | |
26 | ||
27 | #define MCP23017_ADDRESS 0x20<<1 | |
28 | ||
29 | // registers | |
30 | #define MCP23017_IODIRA 0x00 | |
31 | #define MCP23017_IPOLA 0x02 | |
32 | #define MCP23017_GPINTENA 0x04 | |
33 | #define MCP23017_DEFVALA 0x06 | |
34 | #define MCP23017_INTCONA 0x08 | |
35 | #define MCP23017_IOCONA 0x0A | |
36 | #define MCP23017_GPPUA 0x0C | |
37 | #define MCP23017_INTFA 0x0E | |
38 | #define MCP23017_INTCAPA 0x10 | |
39 | #define MCP23017_GPIOA 0x12 | |
40 | #define MCP23017_OLATA 0x14 | |
41 | ||
42 | ||
43 | #define MCP23017_IODIRB 0x01 | |
44 | #define MCP23017_IPOLB 0x03 | |
45 | #define MCP23017_GPINTENB 0x05 | |
46 | #define MCP23017_DEFVALB 0x07 | |
47 | #define MCP23017_INTCONB 0x09 | |
48 | #define MCP23017_IOCONB 0x0B | |
49 | #define MCP23017_GPPUB 0x0D | |
50 | #define MCP23017_INTFB 0x0F | |
51 | #define MCP23017_INTCAPB 0x11 | |
52 | #define MCP23017_GPIOB 0x13 | |
53 | #define MCP23017_OLATB 0x15 | |
54 | ||
35089dc7 JM |
55 | //MCP23017 - Adafruit RGB LCD Shield and VikiLCD |
56 | // bit pattern for the burstBits function is | |
57 | // | |
399cb110 JM |
58 | // B7 B6 B5 B4 B3 B2 B1 B0 A7 A6 A5 A4 A3 A2 A1 A0 - MCP23017 |
59 | // RS RW EN D4 D5 D6 D7 LB LG LR BZ B4 B3 B2 B1 B0 | |
60 | // 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 | |
35089dc7 JM |
61 | #define M17_BIT_RS 0x8000 |
62 | #define M17_BIT_RW 0x4000 | |
63 | #define M17_BIT_EN 0x2000 | |
64 | #define M17_BIT_D4 0x1000 | |
65 | #define M17_BIT_D5 0x0800 | |
66 | #define M17_BIT_D6 0x0400 | |
67 | #define M17_BIT_D7 0x0200 | |
68 | #define M17_BIT_LB 0x0100 | |
69 | #define M17_BIT_LG 0x0080 | |
70 | #define M17_BIT_LR 0x0040 | |
71 | #define M17_BIT_BZ 0x0020 //Added a buzzer on this pin | |
72 | #define M17_BIT_B4 0x0010 | |
73 | #define M17_BIT_B3 0x0008 | |
74 | #define M17_BIT_B2 0x0004 | |
75 | #define M17_BIT_B1 0x0002 | |
76 | #define M17_BIT_B0 0x0001 | |
77 | ||
3dc1c2aa JM |
78 | // commands |
79 | #define LCD_CLEARDISPLAY 0x01 | |
80 | #define LCD_RETURNHOME 0x02 | |
81 | #define LCD_ENTRYMODESET 0x04 | |
82 | #define LCD_DISPLAYCONTROL 0x08 | |
83 | #define LCD_CURSORSHIFT 0x10 | |
84 | #define LCD_FUNCTIONSET 0x20 | |
85 | #define LCD_SETCGRAMADDR 0x40 | |
86 | #define LCD_SETDDRAMADDR 0x80 | |
87 | ||
88 | // flags for display entry mode | |
89 | #define LCD_ENTRYRIGHT 0x00 | |
90 | #define LCD_ENTRYLEFT 0x02 | |
91 | #define LCD_ENTRYSHIFTINCREMENT 0x01 | |
92 | #define LCD_ENTRYSHIFTDECREMENT 0x00 | |
93 | ||
94 | // flags for display on/off control | |
95 | #define LCD_DISPLAYON 0x04 | |
96 | #define LCD_DISPLAYOFF 0x00 | |
97 | #define LCD_CURSORON 0x02 | |
98 | #define LCD_CURSOROFF 0x00 | |
99 | #define LCD_BLINKON 0x01 | |
100 | #define LCD_BLINKOFF 0x00 | |
101 | ||
102 | // flags for display/cursor shift | |
103 | #define LCD_DISPLAYMOVE 0x08 | |
104 | #define LCD_CURSORMOVE 0x00 | |
105 | #define LCD_MOVERIGHT 0x04 | |
106 | #define LCD_MOVELEFT 0x00 | |
107 | ||
108 | // flags for function set | |
109 | #define LCD_8BITMODE 0x10 | |
110 | #define LCD_4BITMODE 0x00 | |
111 | #define LCD_2LINE 0x08 | |
112 | #define LCD_1LINE 0x00 | |
113 | #define LCD_5x10DOTS 0x04 | |
114 | #define LCD_5x8DOTS 0x00 | |
115 | ||
116 | // flags for backlight control | |
117 | #define LCD_BACKLIGHT 0x08 | |
118 | #define LCD_NOBACKLIGHT 0x00 | |
119 | ||
35089dc7 | 120 | VikiLCD::VikiLCD() { |
63f104d6 JM |
121 | // I2C com |
122 | int i2c_pins = THEKERNEL->config->value(panel_checksum, i2c_pins_checksum)->by_default(3)->as_number(); | |
123 | if(i2c_pins == 0){ | |
124 | this->i2c = new mbed::I2C(P0_0, P0_1); | |
125 | }else if(i2c_pins == 1){ | |
126 | this->i2c = new mbed::I2C(P0_10, P0_11); | |
127 | }else if(i2c_pins == 2){ | |
128 | this->i2c = new mbed::I2C(P0_19, P0_20); | |
129 | }else{ // 3, default | |
130 | this->i2c = new mbed::I2C(P0_27, P0_28); | |
131 | } | |
132 | ||
133 | this->i2c_frequency = THEKERNEL->config->value(panel_checksum, i2c_frequency_checksum)->by_default(60000)->as_number(); | |
134 | i2c->frequency(this->i2c_frequency); | |
135 | ||
136 | // Default values | |
91c45256 JM |
137 | this->i2c_address = MCP23017_ADDRESS; |
138 | this->displaycontrol = 0x00; | |
139 | this->displayfunction = LCD_4BITMODE | LCD_2LINE | LCD_5x8DOTS; // in case they forget to call begin() at least we have somethin | |
140 | this->displaymode = 0x00; | |
141 | this->_numlines = 4; | |
399cb110 | 142 | |
91c45256 JM |
143 | // configure the pins to use |
144 | this->encoder_a_pin.from_string(THEKERNEL->config->value( panel_checksum, encoder_a_pin_checksum)->by_default("nc")->as_string())->as_input(); | |
145 | ||
146 | this->encoder_b_pin.from_string(THEKERNEL->config->value( panel_checksum, encoder_b_pin_checksum)->by_default("nc")->as_string())->as_input(); | |
147 | ||
148 | this->button_pause_pin.from_string(THEKERNEL->config->value( panel_checksum, button_pause_pin_checksum)->by_default("nc")->as_string())->as_input(); | |
35089dc7 JM |
149 | } |
150 | ||
151 | VikiLCD::~VikiLCD() { | |
91c45256 | 152 | delete this->i2c; |
35089dc7 | 153 | } |
399cb110 | 154 | |
35089dc7 JM |
155 | |
156 | void VikiLCD::init(){ | |
91c45256 JM |
157 | // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION! |
158 | // according to datasheet, we need at least 40ms after power rises above 2.7V | |
159 | // before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50 | |
160 | ||
161 | char data[2]; | |
399cb110 | 162 | |
91c45256 JM |
163 | // Setup |
164 | this->displayfunction = LCD_4BITMODE | LCD_2LINE | LCD_5x8DOTS; | |
165 | this->_backlightBits = M17_BIT_LB|M17_BIT_LG|M17_BIT_LR; // all off | |
166 | ||
167 | wait_ms(50); | |
168 | ||
169 | // now set up input/output pins in MCP23017 | |
170 | data[0]= MCP23017_IODIRA; | |
171 | data[1]= 0x1F; // buttons input, all others output | |
172 | i2c->write(this->i2c_address, data, 2); | |
173 | ||
174 | ||
175 | // set the button pullups | |
176 | data[0]= MCP23017_GPPUA; | |
177 | data[1]= 0x1F; | |
178 | i2c->write(this->i2c_address, data, 2); | |
179 | ||
180 | data[0]= MCP23017_IODIRB; | |
181 | data[1]= 0x00; // all pins output | |
182 | i2c->write(this->i2c_address, data, 2); | |
183 | ||
91c45256 JM |
184 | //put the LCD into 4 bit mode |
185 | // start with a non-standard command to make it realize we're speaking 4-bit here | |
186 | // per LCD datasheet, first command is a single 4-bit burst, 0011. | |
187 | //----- | |
188 | // we cannot assume that the LCD panel is powered at the same time as | |
189 | // the arduino, so we have to perform a software reset as per page 45 | |
190 | // of the HD44780 datasheet - (kch) | |
191 | //----- | |
192 | ||
193 | // bit pattern for the burstBits function is | |
194 | // | |
399cb110 JM |
195 | // B7 B6 B5 B4 B3 B2 B1 B0 A7 A6 A5 A4 A3 A2 A1 A0 - MCP23017 |
196 | // 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 | |
197 | // RS RW EN D4 D5 D6 D7 B G R B4 B3 B2 B1 B0 | |
91c45256 JM |
198 | for (uint8_t i=0;i < 3;i++) { |
199 | burstBits8b((M17_BIT_EN|M17_BIT_D5|M17_BIT_D4) >> 8); | |
200 | burstBits8b((M17_BIT_D5|M17_BIT_D4) >> 8); | |
201 | } | |
202 | burstBits8b((M17_BIT_EN|M17_BIT_D5) >> 8); | |
203 | burstBits8b(M17_BIT_D5 >> 8); | |
204 | ||
205 | ||
206 | wait_ms(5); // this shouldn't be necessary, but sometimes 16MHz is stupid-fast. | |
207 | ||
208 | command(LCD_FUNCTIONSET | displayfunction); // then send 0010NF00 (N=lines, F=font) | |
209 | wait_ms(5); // for safe keeping... | |
210 | command(LCD_FUNCTIONSET | displayfunction); // ... twice. | |
211 | wait_ms(5); // done! | |
212 | ||
213 | // turn on the LCD with our defaults. since these libs seem to use personal preference, I like a cursor. | |
214 | displaycontrol = (LCD_DISPLAYON|LCD_BACKLIGHT); | |
215 | display(); | |
216 | // clear it off | |
217 | clear(); | |
218 | ||
219 | displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT; | |
220 | // set the entry mode | |
221 | command(LCD_ENTRYMODESET | displaymode); | |
35089dc7 JM |
222 | } |
223 | ||
224 | // we use this to burst bits to the GPIO chip whenever we need to. avoids repetitive code. | |
225 | void VikiLCD::burstBits8b(uint8_t value) { | |
91c45256 JM |
226 | char data[2]; |
227 | data[0] = MCP23017_GPIOB; | |
228 | data[1]= value; | |
229 | i2c->write(this->i2c_address, data, 2); | |
35089dc7 JM |
230 | } |
231 | ||
232 | // value byte order is BA | |
233 | void VikiLCD::burstBits16(uint16_t value) { | |
91c45256 JM |
234 | char data[3]; |
235 | data[0] = MCP23017_GPIOA; | |
236 | data[1]= value&0xFF; | |
237 | data[2]= value>>8; | |
238 | i2c->write(this->i2c_address, data, 3); | |
35089dc7 JM |
239 | } |
240 | ||
399cb110 | 241 | // cycle the buzzer pin at a certain frequency (hz) for a certain duration (ms) |
35089dc7 | 242 | void VikiLCD::buzz(long duration, uint16_t freq) { |
91c45256 JM |
243 | char data[2]; |
244 | int currentRegister = 0; | |
245 | // read gpio register | |
246 | data[0] = MCP23017_GPIOA; | |
247 | i2c->write(this->i2c_address, data, 1); | |
248 | i2c->read(this->i2c_address, data, 1); // Read from selected Register | |
249 | currentRegister= data[0]; | |
399cb110 | 250 | |
91c45256 JM |
251 | duration *=1000; //convert from ms to us |
252 | long period = 1000000 / freq; // period in us | |
253 | long elapsed_time = 0; | |
254 | while (elapsed_time < duration) { | |
255 | data[0]= MCP23017_GPIOA; | |
256 | data[1]= currentRegister |= M17_BIT_BZ; | |
257 | i2c->write(this->i2c_address, data, 2); | |
258 | ||
259 | wait_us(period / 2); | |
260 | ||
261 | data[0]= MCP23017_GPIOA; | |
262 | data[1]= currentRegister &= ~M17_BIT_BZ; | |
263 | i2c->write(this->i2c_address, data, 2); | |
264 | ||
265 | wait_us(period / 2); | |
266 | elapsed_time += (period); | |
267 | } | |
35089dc7 JM |
268 | } |
269 | ||
270 | uint8_t VikiLCD::readButtons(void) { | |
91c45256 JM |
271 | char data[2]; |
272 | data[0] = MCP23017_GPIOA; | |
273 | i2c->write(this->i2c_address, data, 1); | |
274 | i2c->read(this->i2c_address, data, 1); // Read from selected Register | |
275 | ||
276 | // check the button pause | |
e0107f6b | 277 | if(this->button_pause_pin.connected() && this->button_pause_pin.get()) data[0] |= BUTTON_PAUSE; |
399cb110 | 278 | |
e0107f6b JM |
279 | // if it is the variant Panelolu2 swap the buttons around |
280 | if(this->isPanelolu2) { | |
281 | // the select button bit is on GPA2 not GPA0 | |
282 | if((data[0]&M17_BIT_B2) == 0) return BUTTON_SELECT; | |
283 | return 0; // only one button on Panelolu2 ignore the ena_a and en_b | |
284 | ||
285 | } else { | |
399cb110 | 286 | return (~data[0]) & VIKI_ALL_BUTTON_BITS; |
e0107f6b | 287 | } |
35089dc7 JM |
288 | } |
289 | ||
bd405c80 JM |
290 | void VikiLCD::on_refresh(){ |
291 | // FIXME this is a hack to get around I2C noise | |
292 | // Update Only every 20 refreshes, 1 a second | |
293 | static int update_counts = 0; | |
294 | update_counts++; | |
295 | if( update_counts % 20 == 0 && i2c->is_timed_out()) { | |
296 | // if there was a timeout on i2c then reset the lcd | |
297 | this->init(); | |
298 | } | |
299 | } | |
300 | ||
35089dc7 | 301 | int VikiLCD::readEncoderDelta() { |
91c45256 JM |
302 | static int8_t enc_states[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0}; |
303 | static uint8_t old_AB = 0; | |
304 | old_AB <<= 2; //remember previous state | |
399cb110 | 305 | old_AB |= ( this->encoder_a_pin.get() + ( this->encoder_b_pin.get() * 2 ) ); //add current state |
91c45256 | 306 | return enc_states[(old_AB&0x0f)]; |
35089dc7 JM |
307 | } |
308 | ||
309 | void VikiLCD::clear() | |
310 | { | |
91c45256 | 311 | command(LCD_CLEARDISPLAY); // clear display, set cursor position to zero |
35089dc7 | 312 | #ifndef USE_FASTMODE |
91c45256 | 313 | wait_us(2000); // this command takes a long time! |
35089dc7 JM |
314 | #endif |
315 | } | |
316 | ||
317 | void VikiLCD::home() | |
318 | { | |
91c45256 | 319 | command(LCD_RETURNHOME); // set cursor position to zero |
35089dc7 | 320 | #ifndef USE_FASTMODE |
91c45256 | 321 | wait_us(2000); // this command takes a long time! |
35089dc7 JM |
322 | #endif |
323 | } | |
324 | ||
325 | void VikiLCD::setCursor(uint8_t col, uint8_t row) | |
326 | { | |
91c45256 JM |
327 | int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 }; |
328 | if ( row > _numlines ) row = _numlines - 1; // we count rows starting w/0 | |
329 | command(LCD_SETDDRAMADDR | (col + row_offsets[row])); | |
35089dc7 JM |
330 | } |
331 | ||
332 | // Turn the display on/off (quickly) | |
333 | void VikiLCD::noDisplay() { | |
91c45256 JM |
334 | displaycontrol &= ~LCD_DISPLAYON; |
335 | command(LCD_DISPLAYCONTROL | displaycontrol); | |
35089dc7 JM |
336 | } |
337 | void VikiLCD::display() { | |
91c45256 JM |
338 | displaycontrol |= LCD_DISPLAYON; |
339 | command(LCD_DISPLAYCONTROL | displaycontrol); | |
35089dc7 JM |
340 | } |
341 | ||
342 | // Turns the underline cursor on/off | |
343 | void VikiLCD::noCursor() { | |
91c45256 JM |
344 | displaycontrol &= ~LCD_CURSORON; |
345 | command(LCD_DISPLAYCONTROL | displaycontrol); | |
35089dc7 JM |
346 | } |
347 | void VikiLCD::cursor() { | |
91c45256 JM |
348 | displaycontrol |= LCD_CURSORON; |
349 | command(LCD_DISPLAYCONTROL | displaycontrol); | |
35089dc7 JM |
350 | } |
351 | ||
352 | // Turn on and off the blinking cursor | |
353 | void VikiLCD::noBlink() { | |
91c45256 JM |
354 | displaycontrol &= ~LCD_BLINKON; |
355 | command(LCD_DISPLAYCONTROL | displaycontrol); | |
35089dc7 JM |
356 | } |
357 | void VikiLCD::blink() { | |
91c45256 JM |
358 | displaycontrol |= LCD_BLINKON; |
359 | command(LCD_DISPLAYCONTROL | displaycontrol); | |
35089dc7 JM |
360 | } |
361 | ||
362 | // These commands scroll the display without changing the RAM | |
363 | void VikiLCD::scrollDisplayLeft(void) { | |
91c45256 | 364 | command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT); |
35089dc7 JM |
365 | } |
366 | void VikiLCD::scrollDisplayRight(void) { | |
91c45256 | 367 | command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT); |
35089dc7 JM |
368 | } |
369 | ||
370 | // This is for text that flows Left to Right | |
371 | void VikiLCD::leftToRight(void) { | |
91c45256 JM |
372 | displaymode |= LCD_ENTRYLEFT; |
373 | command(LCD_ENTRYMODESET | displaymode); | |
35089dc7 JM |
374 | } |
375 | ||
376 | // This is for text that flows Right to Left | |
377 | void VikiLCD::rightToLeft(void) { | |
91c45256 JM |
378 | displaymode &= ~LCD_ENTRYLEFT; |
379 | command(LCD_ENTRYMODESET | displaymode); | |
35089dc7 JM |
380 | } |
381 | ||
382 | // This will 'right justify' text from the cursor | |
383 | void VikiLCD::autoscroll(void) { | |
91c45256 JM |
384 | displaymode |= LCD_ENTRYSHIFTINCREMENT; |
385 | command(LCD_ENTRYMODESET | displaymode); | |
35089dc7 JM |
386 | } |
387 | ||
388 | // This will 'left justify' text from the cursor | |
389 | void VikiLCD::noAutoscroll(void) { | |
91c45256 JM |
390 | displaymode &= ~LCD_ENTRYSHIFTINCREMENT; |
391 | command(LCD_ENTRYMODESET | displaymode); | |
35089dc7 JM |
392 | } |
393 | ||
394 | void VikiLCD::command(uint8_t value) { | |
91c45256 | 395 | send(value, 0); |
35089dc7 JM |
396 | } |
397 | ||
f54d25cb JM |
398 | void VikiLCD::write(const char* line, int len) { |
399 | for (int i = 0; i < len; ++i) { | |
400 | send(*line++, 1); | |
401 | } | |
35089dc7 JM |
402 | } |
403 | ||
0effde3d | 404 | // Sets the indicator leds |
91c45256 JM |
405 | void VikiLCD::setLed(int led, bool onoff) { |
406 | // LED turns on when bit is cleared | |
407 | if(onoff) { | |
408 | switch(led) { | |
409 | case LED_FAN_ON: _backlightBits &= ~M17_BIT_LR; break; // on | |
410 | case LED_HOTEND_ON: _backlightBits &= ~M17_BIT_LG; break; // on | |
411 | case LED_BED_ON: _backlightBits &= ~M17_BIT_LB; break; // on | |
412 | } | |
413 | }else{ | |
414 | switch(led) { | |
415 | case LED_FAN_ON: _backlightBits |= M17_BIT_LR; break; // off | |
416 | case LED_HOTEND_ON: _backlightBits |= M17_BIT_LG; break; // off | |
417 | case LED_BED_ON: _backlightBits |= M17_BIT_LB; break; // off | |
418 | } | |
399cb110 | 419 | } |
91c45256 | 420 | burstBits16(_backlightBits); |
35089dc7 JM |
421 | } |
422 | ||
423 | // write either command or data, burst it to the expander over I2C. | |
424 | void VikiLCD::send(uint8_t value, uint8_t mode) { | |
425 | #ifdef USE_FASTMODE | |
91c45256 | 426 | // polls for ready. not sure on I2C this is any faster |
399cb110 | 427 | |
91c45256 JM |
428 | // set Data pins as input |
429 | char data[2]; | |
430 | data[0]= MCP23017_IODIRB; | |
431 | data[1]= 0x1E; | |
432 | i2c->write(this->i2c_address, data, 2); | |
433 | uint8_t b= _backlightBits >> 8; | |
399cb110 | 434 | burstBits8b((M17_BIT_RW>>8)|b); // RW hi,RS lo |
91c45256 JM |
435 | char busy; |
436 | data[0] = MCP23017_GPIOB; | |
437 | do { | |
438 | burstBits8b(((M17_BIT_RW|M17_BIT_EN)>>8)|b); // EN hi | |
439 | i2c->write(this->i2c_address, data, 1); | |
440 | i2c->read(this->i2c_address, &busy, 1); // Read D7 | |
441 | burstBits8b((M17_BIT_RW>>8)|b); // EN lo | |
442 | burstBits8b(((M17_BIT_RW|M17_BIT_EN)>>8)|b); // EN hi | |
443 | burstBits8b((M17_BIT_RW>>8)|b); // EN lo | |
444 | } while ((busy&(M17_BIT_D7>>8)) != 0); | |
445 | ||
446 | // reset data bits as output | |
447 | data[0]= MCP23017_IODIRB; | |
448 | data[1]= 0x00; | |
449 | i2c->write(this->i2c_address, data, 2); | |
399cb110 | 450 | burstBits8b(b); // RW lo |
35089dc7 JM |
451 | |
452 | #else | |
91c45256 | 453 | // wait_us(320); |
35089dc7 | 454 | #endif |
399cb110 | 455 | |
91c45256 JM |
456 | // BURST SPEED, OH MY GOD |
457 | // the (now High Speed!) I/O expander pinout | |
399cb110 JM |
458 | // B7 B6 B5 B4 B3 B2 B1 B0 A7 A6 A5 A4 A3 A2 A1 A0 - MCP23017 |
459 | // 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 | |
460 | // RS RW EN D4 D5 D6 D7 B G R B4 B3 B2 B1 B0 | |
91c45256 JM |
461 | |
462 | // n.b. RW bit stays LOW to write | |
463 | uint8_t buf = _backlightBits >> 8; | |
464 | // send high 4 bits | |
465 | if (value & 0x10) buf |= M17_BIT_D4 >> 8; | |
466 | if (value & 0x20) buf |= M17_BIT_D5 >> 8; | |
467 | if (value & 0x40) buf |= M17_BIT_D6 >> 8; | |
468 | if (value & 0x80) buf |= M17_BIT_D7 >> 8; | |
399cb110 | 469 | |
91c45256 JM |
470 | if (mode) buf |= (M17_BIT_RS|M17_BIT_EN) >> 8; // RS+EN |
471 | else buf |= M17_BIT_EN >> 8; // EN | |
472 | ||
473 | burstBits8b(buf); | |
474 | ||
475 | // resend w/ EN turned off | |
476 | buf &= ~(M17_BIT_EN >> 8); | |
477 | burstBits8b(buf); | |
478 | ||
479 | // send low 4 bits | |
480 | buf = _backlightBits >> 8; | |
481 | // send high 4 bits | |
482 | if (value & 0x01) buf |= M17_BIT_D4 >> 8; | |
483 | if (value & 0x02) buf |= M17_BIT_D5 >> 8; | |
484 | if (value & 0x04) buf |= M17_BIT_D6 >> 8; | |
485 | if (value & 0x08) buf |= M17_BIT_D7 >> 8; | |
399cb110 | 486 | |
91c45256 JM |
487 | if (mode) buf |= (M17_BIT_RS|M17_BIT_EN) >> 8; // RS+EN |
488 | else buf |= M17_BIT_EN >> 8; // EN | |
489 | ||
490 | burstBits8b(buf); | |
491 | ||
492 | // resend w/ EN turned off | |
493 | buf &= ~(M17_BIT_EN >> 8); | |
494 | burstBits8b(buf); | |
35089dc7 | 495 | } |