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"
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")
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
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)
30 #define MCP23017_ADDRESS 0x20<<1
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
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
58 //MCP23017 - Adafruit RGB LCD Shield and VikiLCD
59 // bit pattern for the burstBits function is
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
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
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
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
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
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
119 // flags for backlight control
120 #define LCD_BACKLIGHT 0x08
121 #define LCD_NOBACKLIGHT 0x00
125 int i2c_pins
= THEKERNEL
->config
->value(panel_checksum
, i2c_pins_checksum
)->by_default(3)->as_number();
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
);
133 this->i2c
= new mbed::I2C(P0_27
, P0_28
);
136 this->i2c_frequency
= THEKERNEL
->config
->value(panel_checksum
, i2c_frequency_checksum
)->by_default(60000)->as_number();
137 i2c
->frequency(this->i2c_frequency
);
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;
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();
149 this->encoder_b_pin
.from_string(THEKERNEL
->config
->value( panel_checksum
, encoder_b_pin_checksum
)->by_default("nc")->as_string())->as_input();
151 this->button_pause_pin
.from_string(THEKERNEL
->config
->value( panel_checksum
, button_pause_pin_checksum
)->by_default("nc")->as_string())->as_input();
154 VikiLCD::~VikiLCD() {
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
167 this->displayfunction
= LCD_4BITMODE
| LCD_2LINE
| LCD_5x8DOTS
;
168 this->_backlightBits
= M17_BIT_LB
|M17_BIT_LG
|M17_BIT_LR
; // all off
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);
178 // set the button pullups
179 data
[0]= MCP23017_GPPUA
;
181 i2c
->write(this->i2c_address
, data
, 2);
183 data
[0]= MCP23017_IODIRB
;
184 data
[1]= 0x00; // all pins output
185 i2c
->write(this->i2c_address
, data
, 2);
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.
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)
196 // bit pattern for the burstBits function is
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);
205 burstBits8b((M17_BIT_EN
|M17_BIT_D5
) >> 8);
206 burstBits8b(M17_BIT_D5
>> 8);
209 wait_ms(5); // this shouldn't be necessary, but sometimes 16MHz is stupid-fast.
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.
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
);
222 displaymode
= LCD_ENTRYLEFT
| LCD_ENTRYSHIFTDECREMENT
;
223 // set the entry mode
224 command(LCD_ENTRYMODESET
| displaymode
);
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
) {
230 data
[0] = MCP23017_GPIOB
;
232 i2c
->write(this->i2c_address
, data
, 2);
235 // value byte order is BA
236 void VikiLCD::burstBits16(uint16_t value
) {
238 data
[0] = MCP23017_GPIOA
;
241 i2c
->write(this->i2c_address
, data
, 3);
244 // cycle the buzzer pin at a certain frequency (hz) for a certain duration (ms)
245 void VikiLCD::buzz(long duration
, uint16_t freq
) {
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];
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);
264 data
[0]= MCP23017_GPIOA
;
265 data
[1]= currentRegister
&= ~M17_BIT_BZ
;
266 i2c
->write(this->i2c_address
, data
, 2);
269 elapsed_time
+= (period
);
273 uint8_t VikiLCD::readButtons(void) {
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
279 // check the button pause
280 if(this->button_pause_pin
.connected() && this->button_pause_pin
.get()) data
[0] |= BUTTON_PAUSE
;
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
289 return (~data
[0]) & VIKI_ALL_BUTTON_BITS
;
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;
298 if( update_counts
% 20 == 0 && i2c
->is_timed_out()) {
299 // if there was a timeout on i2c then reset the lcd
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)];
312 void VikiLCD::clear()
314 command(LCD_CLEARDISPLAY
); // clear display, set cursor position to zero
316 wait_us(2000); // this command takes a long time!
322 command(LCD_RETURNHOME
); // set cursor position to zero
324 wait_us(2000); // this command takes a long time!
328 void VikiLCD::setCursor(uint8_t col
, uint8_t row
)
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
]));
335 // Turn the display on/off (quickly)
336 void VikiLCD::noDisplay() {
337 displaycontrol
&= ~LCD_DISPLAYON
;
338 command(LCD_DISPLAYCONTROL
| displaycontrol
);
340 void VikiLCD::display() {
341 displaycontrol
|= LCD_DISPLAYON
;
342 command(LCD_DISPLAYCONTROL
| displaycontrol
);
345 // Turns the underline cursor on/off
346 void VikiLCD::noCursor() {
347 displaycontrol
&= ~LCD_CURSORON
;
348 command(LCD_DISPLAYCONTROL
| displaycontrol
);
350 void VikiLCD::cursor() {
351 displaycontrol
|= LCD_CURSORON
;
352 command(LCD_DISPLAYCONTROL
| displaycontrol
);
355 // Turn on and off the blinking cursor
356 void VikiLCD::noBlink() {
357 displaycontrol
&= ~LCD_BLINKON
;
358 command(LCD_DISPLAYCONTROL
| displaycontrol
);
360 void VikiLCD::blink() {
361 displaycontrol
|= LCD_BLINKON
;
362 command(LCD_DISPLAYCONTROL
| displaycontrol
);
365 // These commands scroll the display without changing the RAM
366 void VikiLCD::scrollDisplayLeft(void) {
367 command(LCD_CURSORSHIFT
| LCD_DISPLAYMOVE
| LCD_MOVELEFT
);
369 void VikiLCD::scrollDisplayRight(void) {
370 command(LCD_CURSORSHIFT
| LCD_DISPLAYMOVE
| LCD_MOVERIGHT
);
373 // This is for text that flows Left to Right
374 void VikiLCD::leftToRight(void) {
375 displaymode
|= LCD_ENTRYLEFT
;
376 command(LCD_ENTRYMODESET
| displaymode
);
379 // This is for text that flows Right to Left
380 void VikiLCD::rightToLeft(void) {
381 displaymode
&= ~LCD_ENTRYLEFT
;
382 command(LCD_ENTRYMODESET
| displaymode
);
385 // This will 'right justify' text from the cursor
386 void VikiLCD::autoscroll(void) {
387 displaymode
|= LCD_ENTRYSHIFTINCREMENT
;
388 command(LCD_ENTRYMODESET
| displaymode
);
391 // This will 'left justify' text from the cursor
392 void VikiLCD::noAutoscroll(void) {
393 displaymode
&= ~LCD_ENTRYSHIFTINCREMENT
;
394 command(LCD_ENTRYMODESET
| displaymode
);
397 void VikiLCD::command(uint8_t value
) {
401 void VikiLCD::write(const char* line
, int len
) {
402 for (int i
= 0; i
< len
; ++i
) {
407 // Sets the indicator leds
408 void VikiLCD::setLed(int led
, bool onoff
) {
409 // LED turns on when bit is cleared
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
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
423 burstBits16(_backlightBits
);
426 // write either command or data, burst it to the expander over I2C.
427 void VikiLCD::send(uint8_t value
, uint8_t mode
) {
429 // polls for ready. not sure on I2C this is any faster
431 // set Data pins as input
433 data
[0]= MCP23017_IODIRB
;
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
439 data
[0] = MCP23017_GPIOB
;
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);
449 // reset data bits as output
450 data
[0]= MCP23017_IODIRB
;
452 i2c
->write(this->i2c_address
, data
, 2);
453 burstBits8b(b
); // RW lo
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
465 // n.b. RW bit stays LOW to write
466 uint8_t buf
= _backlightBits
>> 8;
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;
473 if (mode
) buf
|= (M17_BIT_RS
|M17_BIT_EN
) >> 8; // RS+EN
474 else buf
|= M17_BIT_EN
>> 8; // EN
478 // resend w/ EN turned off
479 buf
&= ~(M17_BIT_EN
>> 8);
483 buf
= _backlightBits
>> 8;
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;
490 if (mode
) buf
|= (M17_BIT_RS
|M17_BIT_EN
) >> 8; // RS+EN
491 else buf
|= M17_BIT_EN
>> 8; // EN
495 // resend w/ EN turned off
496 buf
&= ~(M17_BIT_EN
>> 8);