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