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/>.
7 #include "Smoothiepanel.h"
8 #include "smoothiepanel/LCDBang.h"
10 #include "smoothiepanel/Colors.h"
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
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
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
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
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
50 // flags for backlight control
51 #define LCD_BACKLIGHT 0x08
52 #define LCD_NOBACKLIGHT 0x00
55 #define LCD_WRITE 0x00
59 Smoothiepanel::Smoothiepanel() {
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;
67 this->wii_connected
= false;
70 int i2c_pins
= THEKERNEL
->config
->value(panel_checksum
, i2c_pins_checksum
)->by_default(3)->as_number();
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
);
78 this->i2c
= new mbed::I2C(P0_27
, P0_28
);
80 this->wii
= new Wiichuck(this->i2c
);
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();
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();
100 Smoothiepanel::~Smoothiepanel() {
105 void pca9634_init(I2C i2c
, int address
){
106 const int leds
= PCA9634_ADDRESS
| (address
& 0x0E);
109 // initialize led controller
112 i2c
.write(leds
, cmd
, 2);
115 i2c
.write(leds
, cmd
, 2);
118 i2c
.write(leds
, cmd
, 2);
120 i2c
.write(leds
, cmd
, 2);
123 void pca9634_setLed(I2C
*i2c
, int address
, char led
, char val
){
124 const int leds
= PCA9634_ADDRESS
| (address
& 0x0E);
127 cmd
[0] = led
; // lcd blue
129 i2c
->write(leds
, cmd
, 2);
132 void pca9505_write(I2C
*i2c
, int address
, char reg
, char val
){
133 const int expander
= PCA9505_ADDRESS
| (address
& 0x0E);
138 i2c
->write(expander
, cmd
, 2);
141 char pca9505_read(I2C
*i2c
, int address
, char reg
){
142 const int expander
= PCA9505_ADDRESS
| (address
& 0x0E);
146 i2c
->write(expander
, cmd
, 1, false);
147 i2c
->read(expander
, cmd
, 1);
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
);
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
);
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
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);
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
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
187 void Smoothiepanel::setLedBrightness(int led
, int val
){
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
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
;
201 // save register state
203 this->i2c
->write(expander
, cmd
, 1, false);
204 this->i2c
->read(expander
, cmd
, 1);
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
213 this->i2c
->write(expander
, cmd
, 2);
216 uint8_t Smoothiepanel::readButtons(void) {
217 const int expander
= PCA9505_ADDRESS
| this->i2c_address
;
218 uint8_t button_bits
= 0x00;
222 this->i2c
->write(expander
, cmd
, 1, false);
223 this->i2c
->read(expander
, cmd
, 1);
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;
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
;
258 }else this->wii_connected
= false;
260 }else this->wii_connected
= false;
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;
268 setEncoderByHue(this->encoder_hue
);
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;
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)];
282 this->encoder_hue
+= state
;
283 this->encoder_changed
= true;
288 void Smoothiepanel::clear()
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!
296 void Smoothiepanel::home()
298 command(LCD_RETURNHOME
); // set cursor position to zero
299 //#ifndef USE_FASTMODE
300 // wait_us(2000); // this command takes a long time!
304 void Smoothiepanel::setCursor(uint8_t col
, uint8_t row
)
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
]));
311 // Turn the display on/off (quickly)
312 void Smoothiepanel::noDisplay() {
313 displaycontrol
&= ~LCD_DISPLAYON
;
314 command(LCD_DISPLAYCONTROL
| displaycontrol
);
317 void Smoothiepanel::display() {
318 displaycontrol
|= LCD_DISPLAYON
;
319 command(LCD_DISPLAYCONTROL
| displaycontrol
);
322 // Turns the underline cursor on/off
323 void Smoothiepanel::noCursor() {
324 displaycontrol
&= ~LCD_CURSORON
;
325 command(LCD_DISPLAYCONTROL
| displaycontrol
);
327 void Smoothiepanel::cursor() {
328 displaycontrol
|= LCD_CURSORON
;
329 command(LCD_DISPLAYCONTROL
| displaycontrol
);
332 // Turn on and off the blinking cursor
333 void Smoothiepanel::noBlink() {
334 displaycontrol
&= ~LCD_BLINKON
;
335 command(LCD_DISPLAYCONTROL
| displaycontrol
);
337 void Smoothiepanel::blink() {
338 displaycontrol
|= LCD_BLINKON
;
339 command(LCD_DISPLAYCONTROL
| displaycontrol
);
342 // These commands scroll the display without changing the RAM
343 void Smoothiepanel::scrollDisplayLeft(void) {
344 command(LCD_CURSORSHIFT
| LCD_DISPLAYMOVE
| LCD_MOVELEFT
);
346 void Smoothiepanel::scrollDisplayRight(void) {
347 command(LCD_CURSORSHIFT
| LCD_DISPLAYMOVE
| LCD_MOVERIGHT
);
350 // This is for text that flows Left to Right
351 void Smoothiepanel::leftToRight(void) {
352 displaymode
|= LCD_ENTRYLEFT
;
353 command(LCD_ENTRYMODESET
| displaymode
);
356 // This is for text that flows Right to Left
357 void Smoothiepanel::rightToLeft(void) {
358 displaymode
&= ~LCD_ENTRYLEFT
;
359 command(LCD_ENTRYMODESET
| displaymode
);
362 // This will 'right justify' text from the cursor
363 void Smoothiepanel::autoscroll(void) {
364 displaymode
|= LCD_ENTRYSHIFTINCREMENT
;
365 command(LCD_ENTRYMODESET
| displaymode
);
368 // This will 'left justify' text from the cursor
369 void Smoothiepanel::noAutoscroll(void) {
370 displaymode
&= ~LCD_ENTRYSHIFTINCREMENT
;
371 command(LCD_ENTRYMODESET
| displaymode
);
374 void Smoothiepanel::command(uint8_t value
) {
375 lcdbang_write(*this->i2c
, value
>>4, true);
376 lcdbang_write(*this->i2c
, value
, true);
379 void Smoothiepanel::write(const char* line
, int len
) {
380 for (int i
= 0; i
< len
; ++i
) {
381 lcdbang_write(*this->i2c
, *line
++);
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
393 burstBits16(_backlightBits);
397 void Smoothiepanel::setBacklightColor(uint8_t r
, uint8_t g
, uint8_t b
) {
398 const int leds
= PCA9634_ADDRESS
| this->i2c_address
;
401 cmd
[0] = 0x07; // lcd blue
403 this->i2c
->write(leds
, cmd
, 2);
404 cmd
[0] = 0x08; // lcd green
406 this->i2c
->write(leds
, cmd
, 2);
407 cmd
[0] = 0x09; // lcd red
409 this->i2c
->write(leds
, cmd
, 2);
412 void Smoothiepanel::setBacklightByHue(int h
) {
414 HSVtoRGB(&r
, &g
, &b
, h
, 1.0, 1.0);
415 setBacklightColor(r
*0xFF, g
*0xFF, b
*0xFF);
418 void Smoothiepanel::setEncoderLED(uint8_t r
, uint8_t g
, uint8_t b
) {
419 const int leds
= PCA9634_ADDRESS
| this->i2c_address
;
422 cmd
[0] = 0x04; // encoder red
424 this->i2c
->write(leds
, cmd
, 2);
425 cmd
[0] = 0x05; // encoder green
427 this->i2c
->write(leds
, cmd
, 2);
428 cmd
[0] = 0x06; // encoder blue
430 this->i2c
->write(leds
, cmd
, 2);
433 void Smoothiepanel::setEncoderByHue(int h
) {
435 HSVtoRGB(&r
, &g
, &b
, h
, 1.0, 1.0);
436 setEncoderLED(r
*0xFF, g
*0xFF, b
*0xFF);
439 void Smoothiepanel::setPlayLED(uint8_t v
) {
440 const int leds
= PCA9634_ADDRESS
| this->i2c_address
;
443 cmd
[0] = 0x02; // play
445 this->i2c
->write(leds
, cmd
, 2);
448 void Smoothiepanel::setBackLED(uint8_t v
) {
449 const int leds
= PCA9634_ADDRESS
| this->i2c_address
;
452 cmd
[0] = 0x03; // back
454 this->i2c
->write(leds
, cmd
, 2);
458 // write either command or data, burst it to the expander over I2C.
459 void Smoothiepanel::send(uint8_t value, uint8_t mode) {
461 // polls for ready. not sure on I2C this is any faster
463 // set Data pins as input
465 data[0]= MCP23017_IODIRB;
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
471 data[0] = MCP23017_GPIOB;
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);
481 // reset data bits as output
482 data[0]= MCP23017_IODIRB;
484 i2c->write(this->i2c_address, data, 2);
485 burstBits8b(b); // RW lo
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
497 // n.b. RW bit stays LOW to write
498 uint8_t buf = _backlightBits >> 8;
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;
505 if (mode) buf |= (M17_BIT_RS|M17_BIT_EN) >> 8; // RS+EN
506 else buf |= M17_BIT_EN >> 8; // EN
510 // resend w/ EN turned off
511 buf &= ~(M17_BIT_EN >> 8);
515 buf = _backlightBits >> 8;
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;
522 if (mode) buf |= (M17_BIT_RS|M17_BIT_EN) >> 8; // RS+EN
523 else buf |= M17_BIT_EN >> 8; // EN
527 // resend w/ EN turned off
528 buf &= ~(M17_BIT_EN >> 8);
532 // We pause the system
533 uint32_t Smoothiepanel::on_pause_release(uint32_t dummy){
535 THEKERNEL->pauser->take();
538 THEKERNEL->pauser->release();