18e1027b47c270d366a6e9995dbb9abaf628566d
[clinton/Smoothieware.git] / src / modules / utils / panel / panels / Smoothiepanel.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 "Smoothiepanel.h"
8 #include "smoothiepanel/LCDBang.h"
9
10 #include "smoothiepanel/Colors.h"
11
12 // commands
13 #define LCD_CLEARDISPLAY 0x01
14 #define LCD_RETURNHOME 0x02
15 #define LCD_ENTRYMODESET 0x04
16 #define LCD_DISPLAYCONTROL 0x08
17 #define LCD_CURSORSHIFT 0x10
18 #define LCD_FUNCTIONSET 0x20
19 #define LCD_SETCGRAMADDR 0x40
20 #define LCD_SETDDRAMADDR 0x80
21
22 // flags for display entry mode
23 #define LCD_ENTRYRIGHT 0x00
24 #define LCD_ENTRYLEFT 0x02
25 #define LCD_ENTRYSHIFTINCREMENT 0x01
26 #define LCD_ENTRYSHIFTDECREMENT 0x00
27
28 // flags for display on/off control
29 #define LCD_DISPLAYON 0x04
30 #define LCD_DISPLAYOFF 0x00
31 #define LCD_CURSORON 0x02
32 #define LCD_CURSOROFF 0x00
33 #define LCD_BLINKON 0x01
34 #define LCD_BLINKOFF 0x00
35
36 // flags for display/cursor shift
37 #define LCD_DISPLAYMOVE 0x08
38 #define LCD_CURSORMOVE 0x00
39 #define LCD_MOVERIGHT 0x04
40 #define LCD_MOVELEFT 0x00
41
42 // flags for function set
43 #define LCD_8BITMODE 0x10
44 #define LCD_4BITMODE 0x00
45 #define LCD_2LINE 0x08
46 #define LCD_1LINE 0x00
47 #define LCD_5x10DOTS 0x04
48 #define LCD_5x8DOTS 0x00
49
50 // flags for backlight control
51 #define LCD_BACKLIGHT 0x08
52 #define LCD_NOBACKLIGHT 0x00
53
54
55 #define LCD_WRITE 0x00
56 #define LCD_READ 0x01
57 #define LCD_ACK 0x01
58
59 Smoothiepanel::Smoothiepanel() {
60 // Default values
61 this->backlightval = 0x00;
62 this->displaycontrol = 0x00;
63 this->displayfunction = LCD_4BITMODE | LCD_2LINE | LCD_5x8DOTS; // in case they forget to call begin() at least we have somethin
64 this->displaymode = 0x00;
65 this->_numlines = 4;
66
67 this->wii_connected = false;
68
69 // I2C com
70 int i2c_pins = THEKERNEL->config->value(panel_checksum, i2c_pins_checksum)->by_default(3)->as_number();
71 if(i2c_pins == 0){
72 this->i2c = new mbed::I2C(P0_0, P0_1);
73 }else if(i2c_pins == 1){
74 this->i2c = new mbed::I2C(P0_10, P0_11);
75 }else if(i2c_pins == 2){
76 this->i2c = new mbed::I2C(P0_19, P0_20);
77 }else{ // 3, default
78 this->i2c = new mbed::I2C(P0_27, P0_28);
79 }
80 this->wii = new Wiichuck(this->i2c);
81
82 this->i2c_address = (char)THEKERNEL->config->value(panel_checksum, i2c_address_checksum)->by_default(0)->as_number();
83 this->i2c_address = (this->i2c_address & 0x07) << 1;
84 this->i2c_frequency = THEKERNEL->config->value(panel_checksum, i2c_frequency_checksum)->by_default(20000)->as_number();
85 i2c->frequency(this->i2c_frequency);
86 this->lcd_contrast = THEKERNEL->config->value(panel_checksum, lcd_contrast_checksum)->by_default(0)->as_number();
87 this->backlight_red = THEKERNEL->config->value(panel_checksum, lcd_led_checksum)->by_default(255)->as_number();
88 this->backlight_red = THEKERNEL->config->value(panel_checksum, lcd_led_red_checksum)->by_default(this->backlight_red)->as_number();
89 this->backlight_green = THEKERNEL->config->value(panel_checksum, lcd_led_green_checksum)->by_default(255)->as_number();
90 this->backlight_blue = THEKERNEL->config->value(panel_checksum, lcd_led_blue_checksum)->by_default(255)->as_number();
91 this->playledval = THEKERNEL->config->value(panel_checksum, play_led_brightness_checksum)->by_default(255)->as_number();
92 this->backledval = THEKERNEL->config->value(panel_checksum, back_led_brightness_checksum)->by_default(255)->as_number();
93
94 // this->interrupt_pin.from_string(THEKERNEL->config->value(panel_checksum, i2c_interrupt_pin_checksum)->by_default("nc")->as_string())->as_input();
95 this->encoder_a_pin.from_string(THEKERNEL->config->value( panel_checksum, encoder_a_pin_checksum)->by_default("nc")->as_string())->as_input();
96 this->encoder_b_pin.from_string(THEKERNEL->config->value( panel_checksum, encoder_b_pin_checksum)->by_default("nc")->as_string())->as_input();
97 this->encoder_hue = THEKERNEL->config->value(panel_checksum, encoder_led_hue_checksum)->by_default(220)->as_number();
98 }
99
100 Smoothiepanel::~Smoothiepanel() {
101 delete this->wii;
102 delete this->i2c;
103 }
104
105 void pca9634_init(I2C i2c, int address){
106 const int leds = PCA9634_ADDRESS | (address & 0x0E);
107 char cmd[2];
108
109 // initialize led controller
110 cmd[0] = 0x00;
111 cmd[1] = 0x00;
112 i2c.write(leds, cmd, 2);
113 cmd[0] = 0x01;
114 cmd[1] = 0x02;
115 i2c.write(leds, cmd, 2);
116 cmd[0] = 0x0C;
117 cmd[1] = 0xAA;
118 i2c.write(leds, cmd, 2);
119 cmd[0] = 0x0D;
120 i2c.write(leds, cmd, 2);
121 }
122
123 void pca9634_setLed(I2C *i2c, int address, char led, char val){
124 const int leds = PCA9634_ADDRESS | (address & 0x0E);
125 char cmd[2];
126
127 cmd[0] = led; // lcd blue
128 cmd[1] = val;
129 i2c->write(leds, cmd, 2);
130 }
131
132 void pca9505_write(I2C *i2c, int address, char reg, char val){
133 const int expander = PCA9505_ADDRESS | (address & 0x0E);
134 char cmd[2];
135
136 cmd[0] = reg;
137 cmd[1] = val;
138 i2c->write(expander, cmd, 2);
139 }
140
141 char pca9505_read(I2C *i2c, int address, char reg){
142 const int expander = PCA9505_ADDRESS | (address & 0x0E);
143 char cmd[1];
144
145 cmd[0] = 0x04;
146 i2c->write(expander, cmd, 1, false);
147 i2c->read(expander, cmd, 1);
148 return cmd[0];
149 }
150
151 void Smoothiepanel::init(){
152 // init lcd and buzzer
153 lcdbang_init(*this->i2c);
154 // lcdbang_print(*this->i2c, " Smoothiepanel Beta - design by Logxen -");
155 lcdbang_contrast(*this->i2c, this->lcd_contrast);
156
157 pca9634_init(*this->i2c, this->i2c_address);
158 setEncoderByHue(this->encoder_hue);
159 setBacklightColor(this->backlight_red, this->backlight_green, this->backlight_blue);
160 setPlayLED(this->playledval);
161 setBackLED(this->backledval);
162
163 pca9505_write(this->i2c, this->i2c_address, 0x18, 0xAA); // enable leds for button/led wing on port0
164 pca9505_write(this->i2c, this->i2c_address, 0x08, 0x01); // enable leds for button/led wing on port0
165 // wait_us(3000);
166 // this->clear();
167 }
168
169 void Smoothiepanel::setLed(int led, bool on){
170 // LED turns on when bit is cleared
171 char saved = pca9505_read(this->i2c, this->i2c_address, 0x08);
172 if(on) {
173 switch(led) {
174 case LED_FAN_ON: pca9505_write(this->i2c, this->i2c_address, 0x08, saved | 0x40); break; // on
175 case LED_HOTEND_ON: pca9505_write(this->i2c, this->i2c_address, 0x08, saved | 0x10); break; // on
176 case LED_BED_ON: pca9505_write(this->i2c, this->i2c_address, 0x08, saved | 0x04); break; // on
177 }
178 }else{
179 switch(led) {
180 case LED_FAN_ON: pca9505_write(this->i2c, this->i2c_address, 0x08, saved & ~0x40); break; // off
181 case LED_HOTEND_ON: pca9505_write(this->i2c, this->i2c_address, 0x08, saved & ~0x10); break; // off
182 case LED_BED_ON: pca9505_write(this->i2c, this->i2c_address, 0x08, saved & ~0x04); break; // off
183 }
184 }
185 }
186
187 void Smoothiepanel::setLedBrightness(int led, int val){
188 switch(led){
189 // case LED_FAN_ON: this->backlight_green = val; break; // on
190 // case LED_HOTEND_ON: this->backlight_red = val; break; // on
191 // case LED_BED_ON: this->backlight_blue = val; break; // on
192 }
193 }
194
195 // cycle the buzzer pin at a certain frequency (hz) for a certain duration (ms)
196 void Smoothiepanel::buzz(long duration, uint16_t freq) {
197 const int expander = PCA9505_ADDRESS | this->i2c_address;
198 char cmd[2];
199 char saved;
200
201 // save register state
202 cmd[0] = 0x04;
203 this->i2c->write(expander, cmd, 1, false);
204 this->i2c->read(expander, cmd, 1);
205 saved = cmd[0];
206
207 // buzz
208 cmd[0] = 0x0C;
209 cmd[1] = saved & 0xFE;
210 this->i2c->write(expander, cmd, 2);
211 wait_ms(duration); // TODO: Make this not hold up the whole system
212 cmd[1] = saved;
213 this->i2c->write(expander, cmd, 2);
214 }
215
216 uint8_t Smoothiepanel::readButtons(void) {
217 const int expander = PCA9505_ADDRESS | this->i2c_address;
218 uint8_t button_bits = 0x00;
219 char cmd[1];
220
221 cmd[0] = 0x03;
222 this->i2c->write(expander, cmd, 1, false);
223 this->i2c->read(expander, cmd, 1);
224 //cmd[0] = ~cmd[0];
225 if((cmd[0] & 0x10) > 0) button_bits |= BUTTON_SELECT; // encoder click
226 if((cmd[0] & 0x02) > 0) button_bits |= BUTTON_LEFT; // back button
227 if((cmd[0] & 0x01) > 0) button_bits |= BUTTON_PAUSE; // play button
228 if((cmd[0] & 0x20) > 0){ // wii accessory connected
229 if(!this->wii_connected){
230 this->wii->init_device();
231 if(this->wii->device_type >= 0){
232 this->wii_connected = true;
233 wait_ms(100);
234 }
235 }
236 if(this->wii_connected){
237 this->wii->poll_device();
238 if(this->wii->data_ready){
239 if(this->wii->device_type == DEVICE_NUNCHUCK){
240 if(this->wii->SY > 192) button_bits |= BUTTON_UP;
241 else if(this->wii->SY < 64) button_bits |= BUTTON_DOWN;
242 if(this->wii->SX > 192) button_bits |= BUTTON_RIGHT;
243 else if(this->wii->SX < 64) button_bits |= BUTTON_LEFT;
244 if(!this->wii->BC) button_bits |= BUTTON_SELECT;
245 if(!this->wii->BZ) button_bits |= BUTTON_LEFT;
246 }else if(this->wii->device_type == DEVICE_CLASSIC){
247 if(this->wii->LY > 192) button_bits |= BUTTON_UP;
248 else if(this->wii->LY < 64) button_bits |= BUTTON_DOWN;
249 if(this->wii->LX > 192) button_bits |= BUTTON_RIGHT;
250 else if(this->wii->LX < 64) button_bits |= BUTTON_LEFT;
251 if(!this->wii->BDU) button_bits |= BUTTON_UP;
252 else if(!this->wii->BDD) button_bits |= BUTTON_DOWN;
253 if(!this->wii->BDL) button_bits |= BUTTON_LEFT;
254 else if(!this->wii->BDR) button_bits |= BUTTON_RIGHT;
255 if(!this->wii->BA) button_bits |= BUTTON_SELECT;
256 if(!this->wii->BB) button_bits |= BUTTON_LEFT;
257 }
258 }else this->wii_connected = false;
259 }
260 }else this->wii_connected = false;
261
262 // update the encoder color
263 if(this->encoder_changed){
264 if(this->encoder_hue > 360) this->encoder_hue -= 360;
265 else if(this->encoder_hue < 0) this->encoder_hue += 360;
266 this->encoder_changed = false;
267
268 setEncoderByHue(this->encoder_hue);
269 }
270
271 return button_bits;
272 }
273
274 int Smoothiepanel::readEncoderDelta() {
275 static int8_t enc_states[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};
276 static uint8_t old_AB = 0;
277 int8_t state;
278 old_AB <<= 2; //remember previous state
279 old_AB |= ( this->encoder_a_pin.get() + ( this->encoder_b_pin.get() * 2 ) ); //add current state
280 state = enc_states[(old_AB&0x0f)];
281 if(state != 0){
282 this->encoder_hue += state;
283 this->encoder_changed = true;
284 }
285 return state;
286 }
287
288 void Smoothiepanel::clear()
289 {
290 command(LCD_CLEARDISPLAY); // clear display, set cursor position to zero
291 //#ifndef USE_FASTMODE
292 // wait_ms(50); // this command takes a long time!
293 //#endif
294 }
295
296 void Smoothiepanel::home()
297 {
298 command(LCD_RETURNHOME); // set cursor position to zero
299 //#ifndef USE_FASTMODE
300 // wait_us(2000); // this command takes a long time!
301 //#endif
302 }
303
304 void Smoothiepanel::setCursor(uint8_t col, uint8_t row)
305 {
306 int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 };
307 if ( row > _numlines ) row = _numlines - 1; // we count rows starting w/0
308 command(LCD_SETDDRAMADDR | (col + row_offsets[row]));
309 }
310
311 // Turn the display on/off (quickly)
312 void Smoothiepanel::noDisplay() {
313 displaycontrol &= ~LCD_DISPLAYON;
314 command(LCD_DISPLAYCONTROL | displaycontrol);
315 }
316
317 void Smoothiepanel::display() {
318 displaycontrol |= LCD_DISPLAYON;
319 command(LCD_DISPLAYCONTROL | displaycontrol);
320 }
321
322 // Turns the underline cursor on/off
323 void Smoothiepanel::noCursor() {
324 displaycontrol &= ~LCD_CURSORON;
325 command(LCD_DISPLAYCONTROL | displaycontrol);
326 }
327 void Smoothiepanel::cursor() {
328 displaycontrol |= LCD_CURSORON;
329 command(LCD_DISPLAYCONTROL | displaycontrol);
330 }
331
332 // Turn on and off the blinking cursor
333 void Smoothiepanel::noBlink() {
334 displaycontrol &= ~LCD_BLINKON;
335 command(LCD_DISPLAYCONTROL | displaycontrol);
336 }
337 void Smoothiepanel::blink() {
338 displaycontrol |= LCD_BLINKON;
339 command(LCD_DISPLAYCONTROL | displaycontrol);
340 }
341
342 // These commands scroll the display without changing the RAM
343 void Smoothiepanel::scrollDisplayLeft(void) {
344 command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT);
345 }
346 void Smoothiepanel::scrollDisplayRight(void) {
347 command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT);
348 }
349
350 // This is for text that flows Left to Right
351 void Smoothiepanel::leftToRight(void) {
352 displaymode |= LCD_ENTRYLEFT;
353 command(LCD_ENTRYMODESET | displaymode);
354 }
355
356 // This is for text that flows Right to Left
357 void Smoothiepanel::rightToLeft(void) {
358 displaymode &= ~LCD_ENTRYLEFT;
359 command(LCD_ENTRYMODESET | displaymode);
360 }
361
362 // This will 'right justify' text from the cursor
363 void Smoothiepanel::autoscroll(void) {
364 displaymode |= LCD_ENTRYSHIFTINCREMENT;
365 command(LCD_ENTRYMODESET | displaymode);
366 }
367
368 // This will 'left justify' text from the cursor
369 void Smoothiepanel::noAutoscroll(void) {
370 displaymode &= ~LCD_ENTRYSHIFTINCREMENT;
371 command(LCD_ENTRYMODESET | displaymode);
372 }
373
374 void Smoothiepanel::command(uint8_t value) {
375 lcdbang_write(*this->i2c, value>>4, true);
376 lcdbang_write(*this->i2c, value, true);
377 }
378
379 void Smoothiepanel::write(const char* line, int len) {
380 for (int i = 0; i < len; ++i) {
381 lcdbang_write(*this->i2c, *line++);
382 }
383 }
384
385 // Allows to set the backlight, if the LCD backpack is used
386 void Smoothiepanel::setBacklight(uint8_t status) {
387 /* // LED turns on when bit is cleared
388 _backlightBits = M17_BIT_LB|M17_BIT_LG|M17_BIT_LR; // all off
389 if (status & LED_RED) _backlightBits &= ~M17_BIT_LR; // red on
390 if (status & LED_GREEN) _backlightBits &= ~M17_BIT_LG; // green on
391 if (status & LED_BLUE) _backlightBits &= ~M17_BIT_LB; // blue on
392
393 burstBits16(_backlightBits);
394 */
395 }
396
397 void Smoothiepanel::setBacklightColor(uint8_t r, uint8_t g, uint8_t b) {
398 const int leds = PCA9634_ADDRESS | this->i2c_address;
399 char cmd[2];
400
401 cmd[0] = 0x07; // lcd blue
402 cmd[1] = b;
403 this->i2c->write(leds, cmd, 2);
404 cmd[0] = 0x08; // lcd green
405 cmd[1] = g;
406 this->i2c->write(leds, cmd, 2);
407 cmd[0] = 0x09; // lcd red
408 cmd[1] = r;
409 this->i2c->write(leds, cmd, 2);
410 }
411
412 void Smoothiepanel::setBacklightByHue(int h) {
413 float r, g, b;
414 HSVtoRGB(&r, &g, &b, h, 1.0, 1.0);
415 setBacklightColor(r*0xFF, g*0xFF, b*0xFF);
416 }
417
418 void Smoothiepanel::setEncoderLED(uint8_t r, uint8_t g, uint8_t b) {
419 const int leds = PCA9634_ADDRESS | this->i2c_address;
420 char cmd[2];
421
422 cmd[0] = 0x04; // encoder red
423 cmd[1] = r;
424 this->i2c->write(leds, cmd, 2);
425 cmd[0] = 0x05; // encoder green
426 cmd[1] = g;
427 this->i2c->write(leds, cmd, 2);
428 cmd[0] = 0x06; // encoder blue
429 cmd[1] = b;
430 this->i2c->write(leds, cmd, 2);
431 }
432
433 void Smoothiepanel::setEncoderByHue(int h) {
434 float r, g, b;
435 HSVtoRGB(&r, &g, &b, h, 1.0, 1.0);
436 setEncoderLED(r*0xFF, g*0xFF, b*0xFF);
437 }
438
439 void Smoothiepanel::setPlayLED(uint8_t v) {
440 const int leds = PCA9634_ADDRESS | this->i2c_address;
441 char cmd[2];
442
443 cmd[0] = 0x02; // play
444 cmd[1] = v;
445 this->i2c->write(leds, cmd, 2);
446 }
447
448 void Smoothiepanel::setBackLED(uint8_t v) {
449 const int leds = PCA9634_ADDRESS | this->i2c_address;
450 char cmd[2];
451
452 cmd[0] = 0x03; // back
453 cmd[1] = v;
454 this->i2c->write(leds, cmd, 2);
455 }
456
457 /*
458 // write either command or data, burst it to the expander over I2C.
459 void Smoothiepanel::send(uint8_t value, uint8_t mode) {
460 #ifdef USE_FASTMODE
461 // polls for ready. not sure on I2C this is any faster
462
463 // set Data pins as input
464 char data[2];
465 data[0]= MCP23017_IODIRB;
466 data[1]= 0x1E;
467 i2c->write(this->i2c_address, data, 2);
468 uint8_t b= _backlightBits >> 8;
469 burstBits8b((M17_BIT_RW>>8)|b); // RW hi,RS lo
470 char busy;
471 data[0] = MCP23017_GPIOB;
472 do {
473 burstBits8b(((M17_BIT_RW|M17_BIT_EN)>>8)|b); // EN hi
474 i2c->write(this->i2c_address, data, 1);
475 i2c->read(this->i2c_address, &busy, 1); // Read D7
476 burstBits8b((M17_BIT_RW>>8)|b); // EN lo
477 burstBits8b(((M17_BIT_RW|M17_BIT_EN)>>8)|b); // EN hi
478 burstBits8b((M17_BIT_RW>>8)|b); // EN lo
479 } while ((busy&(M17_BIT_D7>>8)) != 0);
480
481 // reset data bits as output
482 data[0]= MCP23017_IODIRB;
483 data[1]= 0x00;
484 i2c->write(this->i2c_address, data, 2);
485 burstBits8b(b); // RW lo
486
487 #else
488 // wait_us(320);
489 #endif
490
491 // BURST SPEED, OH MY GOD
492 // the (now High Speed!) I/O expander pinout
493 // B7 B6 B5 B4 B3 B2 B1 B0 A7 A6 A5 A4 A3 A2 A1 A0 - MCP23017
494 // 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
495 // RS RW EN D4 D5 D6 D7 B G R B4 B3 B2 B1 B0
496
497 // n.b. RW bit stays LOW to write
498 uint8_t buf = _backlightBits >> 8;
499 // send high 4 bits
500 if (value & 0x10) buf |= M17_BIT_D4 >> 8;
501 if (value & 0x20) buf |= M17_BIT_D5 >> 8;
502 if (value & 0x40) buf |= M17_BIT_D6 >> 8;
503 if (value & 0x80) buf |= M17_BIT_D7 >> 8;
504
505 if (mode) buf |= (M17_BIT_RS|M17_BIT_EN) >> 8; // RS+EN
506 else buf |= M17_BIT_EN >> 8; // EN
507
508 burstBits8b(buf);
509
510 // resend w/ EN turned off
511 buf &= ~(M17_BIT_EN >> 8);
512 burstBits8b(buf);
513
514 // send low 4 bits
515 buf = _backlightBits >> 8;
516 // send high 4 bits
517 if (value & 0x01) buf |= M17_BIT_D4 >> 8;
518 if (value & 0x02) buf |= M17_BIT_D5 >> 8;
519 if (value & 0x04) buf |= M17_BIT_D6 >> 8;
520 if (value & 0x08) buf |= M17_BIT_D7 >> 8;
521
522 if (mode) buf |= (M17_BIT_RS|M17_BIT_EN) >> 8; // RS+EN
523 else buf |= M17_BIT_EN >> 8; // EN
524
525 burstBits8b(buf);
526
527 // resend w/ EN turned off
528 buf &= ~(M17_BIT_EN >> 8);
529 burstBits8b(buf);
530 }
531
532 // We pause the system
533 uint32_t Smoothiepanel::on_pause_release(uint32_t dummy){
534 if(!paused) {
535 THEKERNEL->pauser->take();
536 paused= true;
537 }else{
538 THEKERNEL->pauser->release();
539 paused= false;
540 }
541 return 0;
542 }
543 */
544