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