Commit | Line | Data |
---|---|---|
35089dc7 JM |
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 "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 | ||
37 | VikiLCD::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 | ||
68 | VikiLCD::~VikiLCD() { | |
91c45256 | 69 | delete this->i2c; |
35089dc7 | 70 | } |
91c45256 | 71 | |
35089dc7 JM |
72 | |
73 | void 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. | |
147 | void 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 | |
155 | void 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) | |
164 | void 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 | ||
192 | uint8_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 | ||
212 | int 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 | ||
220 | void 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 | ||
228 | void 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 | ||
236 | void 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) | |
244 | void VikiLCD::noDisplay() { | |
91c45256 JM |
245 | displaycontrol &= ~LCD_DISPLAYON; |
246 | command(LCD_DISPLAYCONTROL | displaycontrol); | |
35089dc7 JM |
247 | } |
248 | void VikiLCD::display() { | |
91c45256 JM |
249 | displaycontrol |= LCD_DISPLAYON; |
250 | command(LCD_DISPLAYCONTROL | displaycontrol); | |
35089dc7 JM |
251 | } |
252 | ||
253 | // Turns the underline cursor on/off | |
254 | void VikiLCD::noCursor() { | |
91c45256 JM |
255 | displaycontrol &= ~LCD_CURSORON; |
256 | command(LCD_DISPLAYCONTROL | displaycontrol); | |
35089dc7 JM |
257 | } |
258 | void 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 | |
264 | void VikiLCD::noBlink() { | |
91c45256 JM |
265 | displaycontrol &= ~LCD_BLINKON; |
266 | command(LCD_DISPLAYCONTROL | displaycontrol); | |
35089dc7 JM |
267 | } |
268 | void 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 | |
274 | void VikiLCD::scrollDisplayLeft(void) { | |
91c45256 | 275 | command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT); |
35089dc7 JM |
276 | } |
277 | void 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 | |
282 | void 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 | |
288 | void 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 | |
294 | void 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 | |
300 | void VikiLCD::noAutoscroll(void) { | |
91c45256 JM |
301 | displaymode &= ~LCD_ENTRYSHIFTINCREMENT; |
302 | command(LCD_ENTRYMODESET | displaymode); | |
35089dc7 JM |
303 | } |
304 | ||
305 | void VikiLCD::command(uint8_t value) { | |
91c45256 | 306 | send(value, 0); |
35089dc7 JM |
307 | } |
308 | ||
309 | void VikiLCD::write(char value) { | |
91c45256 | 310 | send(value, 1); |
35089dc7 JM |
311 | } |
312 | ||
0effde3d | 313 | // Sets the indicator leds |
91c45256 JM |
314 | void 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. | |
333 | void 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 | } |