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