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/>.
12 #include "checksumm.h"
13 #include "ConfigValue.h"
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")
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
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)
31 #define MCP23017_ADDRESS 0x20<<1
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
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
59 //MCP23017 - Adafruit RGB LCD Shield and VikiLCD
60 // bit pattern for the burstBits function is
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
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
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
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
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
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
120 // flags for backlight control
121 #define LCD_BACKLIGHT 0x08
122 #define LCD_NOBACKLIGHT 0x00
126 int i2c_pins
= THEKERNEL
->config
->value(panel_checksum
, i2c_pins_checksum
)->by_default(3)->as_number();
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
);
134 this->i2c
= new mbed::I2C(P0_27
, P0_28
);
137 this->i2c_frequency
= THEKERNEL
->config
->value(panel_checksum
, i2c_frequency_checksum
)->by_default(60000)->as_number();
138 i2c
->frequency(this->i2c_frequency
);
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;
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();
150 this->encoder_b_pin
.from_string(THEKERNEL
->config
->value( panel_checksum
, encoder_b_pin_checksum
)->by_default("nc")->as_string())->as_input();
152 this->button_pause_pin
.from_string(THEKERNEL
->config
->value( panel_checksum
, button_pause_pin_checksum
)->by_default("nc")->as_string())->as_input();
155 VikiLCD::~VikiLCD() {
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
168 this->displayfunction
= LCD_4BITMODE
| LCD_2LINE
| LCD_5x8DOTS
;
169 this->_backlightBits
= M17_BIT_LB
|M17_BIT_LG
|M17_BIT_LR
; // all off
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);
179 // set the button pullups
180 data
[0]= MCP23017_GPPUA
;
182 i2c
->write(this->i2c_address
, data
, 2);
184 data
[0]= MCP23017_IODIRB
;
185 data
[1]= 0x00; // all pins output
186 i2c
->write(this->i2c_address
, data
, 2);
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.
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)
197 // bit pattern for the burstBits function is
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);
206 burstBits8b((M17_BIT_EN
|M17_BIT_D5
) >> 8);
207 burstBits8b(M17_BIT_D5
>> 8);
210 wait_ms(5); // this shouldn't be necessary, but sometimes 16MHz is stupid-fast.
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.
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
);
223 displaymode
= LCD_ENTRYLEFT
| LCD_ENTRYSHIFTDECREMENT
;
224 // set the entry mode
225 command(LCD_ENTRYMODESET
| displaymode
);
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
) {
231 data
[0] = MCP23017_GPIOB
;
233 i2c
->write(this->i2c_address
, data
, 2);
236 // value byte order is BA
237 void VikiLCD::burstBits16(uint16_t value
) {
239 data
[0] = MCP23017_GPIOA
;
242 i2c
->write(this->i2c_address
, data
, 3);
245 // cycle the buzzer pin at a certain frequency (hz) for a certain duration (ms)
246 void VikiLCD::buzz(long duration
, uint16_t freq
) {
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];
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);
265 data
[0]= MCP23017_GPIOA
;
266 data
[1]= currentRegister
&= ~M17_BIT_BZ
;
267 i2c
->write(this->i2c_address
, data
, 2);
270 elapsed_time
+= (period
);
274 uint8_t VikiLCD::readButtons(void) {
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
280 // check the button pause
281 if(this->button_pause_pin
.connected() && this->button_pause_pin
.get()) data
[0] |= BUTTON_PAUSE
;
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
290 return (~data
[0]) & VIKI_ALL_BUTTON_BITS
;
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;
299 if( update_counts
% 20 == 0 && i2c
->is_timed_out()) {
300 // if there was a timeout on i2c then reset the lcd
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)];
313 void VikiLCD::clear()
315 command(LCD_CLEARDISPLAY
); // clear display, set cursor position to zero
317 wait_us(2000); // this command takes a long time!
323 command(LCD_RETURNHOME
); // set cursor position to zero
325 wait_us(2000); // this command takes a long time!
329 void VikiLCD::setCursor(uint8_t col
, uint8_t row
)
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
]));
336 // Turn the display on/off (quickly)
337 void VikiLCD::noDisplay() {
338 displaycontrol
&= ~LCD_DISPLAYON
;
339 command(LCD_DISPLAYCONTROL
| displaycontrol
);
341 void VikiLCD::display() {
342 displaycontrol
|= LCD_DISPLAYON
;
343 command(LCD_DISPLAYCONTROL
| displaycontrol
);
346 // Turns the underline cursor on/off
347 void VikiLCD::noCursor() {
348 displaycontrol
&= ~LCD_CURSORON
;
349 command(LCD_DISPLAYCONTROL
| displaycontrol
);
351 void VikiLCD::cursor() {
352 displaycontrol
|= LCD_CURSORON
;
353 command(LCD_DISPLAYCONTROL
| displaycontrol
);
356 // Turn on and off the blinking cursor
357 void VikiLCD::noBlink() {
358 displaycontrol
&= ~LCD_BLINKON
;
359 command(LCD_DISPLAYCONTROL
| displaycontrol
);
361 void VikiLCD::blink() {
362 displaycontrol
|= LCD_BLINKON
;
363 command(LCD_DISPLAYCONTROL
| displaycontrol
);
366 // These commands scroll the display without changing the RAM
367 void VikiLCD::scrollDisplayLeft(void) {
368 command(LCD_CURSORSHIFT
| LCD_DISPLAYMOVE
| LCD_MOVELEFT
);
370 void VikiLCD::scrollDisplayRight(void) {
371 command(LCD_CURSORSHIFT
| LCD_DISPLAYMOVE
| LCD_MOVERIGHT
);
374 // This is for text that flows Left to Right
375 void VikiLCD::leftToRight(void) {
376 displaymode
|= LCD_ENTRYLEFT
;
377 command(LCD_ENTRYMODESET
| displaymode
);
380 // This is for text that flows Right to Left
381 void VikiLCD::rightToLeft(void) {
382 displaymode
&= ~LCD_ENTRYLEFT
;
383 command(LCD_ENTRYMODESET
| displaymode
);
386 // This will 'right justify' text from the cursor
387 void VikiLCD::autoscroll(void) {
388 displaymode
|= LCD_ENTRYSHIFTINCREMENT
;
389 command(LCD_ENTRYMODESET
| displaymode
);
392 // This will 'left justify' text from the cursor
393 void VikiLCD::noAutoscroll(void) {
394 displaymode
&= ~LCD_ENTRYSHIFTINCREMENT
;
395 command(LCD_ENTRYMODESET
| displaymode
);
398 void VikiLCD::command(uint8_t value
) {
402 void VikiLCD::write(const char* line
, int len
) {
403 for (int i
= 0; i
< len
; ++i
) {
408 // Sets the indicator leds
409 void VikiLCD::setLed(int led
, bool onoff
) {
410 // LED turns on when bit is cleared
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
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
424 burstBits16(_backlightBits
);
427 // write either command or data, burst it to the expander over I2C.
428 void VikiLCD::send(uint8_t value
, uint8_t mode
) {
430 // polls for ready. not sure on I2C this is any faster
432 // set Data pins as input
434 data
[0]= MCP23017_IODIRB
;
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
440 data
[0] = MCP23017_GPIOB
;
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);
450 // reset data bits as output
451 data
[0]= MCP23017_IODIRB
;
453 i2c
->write(this->i2c_address
, data
, 2);
454 burstBits8b(b
); // RW lo
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
466 // n.b. RW bit stays LOW to write
467 uint8_t buf
= _backlightBits
>> 8;
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;
474 if (mode
) buf
|= (M17_BIT_RS
|M17_BIT_EN
) >> 8; // RS+EN
475 else buf
|= M17_BIT_EN
>> 8; // EN
479 // resend w/ EN turned off
480 buf
&= ~(M17_BIT_EN
>> 8);
484 buf
= _backlightBits
>> 8;
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;
491 if (mode
) buf
|= (M17_BIT_RS
|M17_BIT_EN
) >> 8; // RS+EN
492 else buf
|= M17_BIT_EN
>> 8; // EN
496 // resend w/ EN turned off
497 buf
&= ~(M17_BIT_EN
>> 8);