Merge Network code with webserver etc
[clinton/Smoothieware.git] / src / modules / utils / panel / panels / VikiLCD.cpp
CommitLineData
399cb110 1/*
35089dc7
JM
2This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
3Smoothie 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.
4Smoothie 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 5You 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 120VikiLCD::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
151VikiLCD::~VikiLCD() {
91c45256 152 delete this->i2c;
35089dc7 153}
399cb110 154
35089dc7
JM
155
156void 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.
225void 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
233void 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 242void 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
270uint8_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
290void 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 301int 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
309void 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
317void 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
325void 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)
333void VikiLCD::noDisplay() {
91c45256
JM
334 displaycontrol &= ~LCD_DISPLAYON;
335 command(LCD_DISPLAYCONTROL | displaycontrol);
35089dc7
JM
336}
337void VikiLCD::display() {
91c45256
JM
338 displaycontrol |= LCD_DISPLAYON;
339 command(LCD_DISPLAYCONTROL | displaycontrol);
35089dc7
JM
340}
341
342// Turns the underline cursor on/off
343void VikiLCD::noCursor() {
91c45256
JM
344 displaycontrol &= ~LCD_CURSORON;
345 command(LCD_DISPLAYCONTROL | displaycontrol);
35089dc7
JM
346}
347void 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
353void VikiLCD::noBlink() {
91c45256
JM
354 displaycontrol &= ~LCD_BLINKON;
355 command(LCD_DISPLAYCONTROL | displaycontrol);
35089dc7
JM
356}
357void 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
363void VikiLCD::scrollDisplayLeft(void) {
91c45256 364 command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT);
35089dc7
JM
365}
366void 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
371void 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
377void 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
383void 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
389void VikiLCD::noAutoscroll(void) {
91c45256
JM
390 displaymode &= ~LCD_ENTRYSHIFTINCREMENT;
391 command(LCD_ENTRYMODESET | displaymode);
35089dc7
JM
392}
393
394void VikiLCD::command(uint8_t value) {
91c45256 395 send(value, 0);
35089dc7
JM
396}
397
f54d25cb
JM
398void 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
405void 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.
424void 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}