SMOOTHIEPANEL LIVES! :D
[clinton/Smoothieware.git] / src / modules / utils / panel / panels / Smoothiepanel.cpp
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 "Smoothiepanel.h"
8 #include "smoothiepanel/LCDBang.h"
9
10 #define LCD_WRITE 0x00
11 #define LCD_READ 0x01
12 #define LCD_ACK 0x01
13
14 Smoothiepanel::Smoothiepanel() {
15 // Default values
16 this->backlightval = 0x00;
17 this->displaycontrol = 0x00;
18 this->displayfunction = LCD_4BITMODE | LCD_2LINE | LCD_5x8DOTS; // in case they forget to call begin() at least we have somethin
19 this->displaymode = 0x00;
20 this->_numlines = 4;
21
22 // I2C com
23 int i2c_pins = THEKERNEL->config->value(panel_checksum, i2c_pins_checksum)->by_default(3)->as_number();
24 if(i2c_pins == 0){
25 this->i2c = new mbed::I2C(P0_0, P0_1);
26 }else if(i2c_pins == 1){
27 this->i2c = new mbed::I2C(P0_10, P0_11);
28 }else if(i2c_pins == 2){
29 this->i2c = new mbed::I2C(P0_19, P0_20);
30 }else{ // 3, default
31 this->i2c = new mbed::I2C(P0_27, P0_28);
32 }
33
34 this->i2c_address = (char)THEKERNEL->config->value(panel_checksum, i2c_address_checksum)->by_default(0)->as_number();
35 this->i2c_address = (this->i2c_address & 0x07) << 1;
36 this->i2c_frequency = THEKERNEL->config->value(panel_checksum, i2c_frequency_checksum)->by_default(20000)->as_number();
37 i2c->frequency(this->i2c_frequency);
38 this->lcd_contrast = THEKERNEL->config->value(panel_checksum, lcd_contrast_checksum)->by_default(0)->as_number();
39
40 // this->interrupt_pin.from_string(THEKERNEL->config->value(panel_checksum, i2c_interrupt_pin_checksum)->by_default("nc")->as_string())->as_input();
41 this->encoder_a_pin.from_string(THEKERNEL->config->value( panel_checksum, encoder_a_pin_checksum)->by_default("nc")->as_string())->as_input();
42 this->encoder_b_pin.from_string(THEKERNEL->config->value( panel_checksum, encoder_b_pin_checksum)->by_default("nc")->as_string())->as_input();
43
44 paused= false;
45 }
46
47 Smoothiepanel::~Smoothiepanel() {
48 delete this->i2c;
49 }
50
51 void ledbang_init(I2C i2c, int address){
52 const int leds = PCA9634_ADDRESS | (address & 0x0E);
53 char cmd[2];
54
55 // initialize led controller
56 cmd[0] = 0x00;
57 cmd[1] = 0x00;
58 i2c.write(leds, cmd, 2);
59 cmd[0] = 0x01;
60 cmd[1] = 0x02;
61 i2c.write(leds, cmd, 2);
62 cmd[0] = 0x0C;
63 cmd[1] = 0xAA;
64 i2c.write(leds, cmd, 2);
65 cmd[0] = 0x0D;
66 i2c.write(leds, cmd, 2);
67
68 // set leds
69 cmd[0] = 0x02; // play
70 cmd[1] = 0x10;
71 i2c.write(leds, cmd, 2);
72 cmd[0] = 0x03; // back
73 cmd[1] = 0xC0;
74 i2c.write(leds, cmd, 2);
75 cmd[0] = 0x04; // encoder red
76 cmd[1] = 0x00;
77 i2c.write(leds, cmd, 2);
78 cmd[0] = 0x05; // encoder green
79 cmd[1] = 0x00;
80 i2c.write(leds, cmd, 2);
81 cmd[0] = 0x06; // encoder blue
82 cmd[1] = 0x20;
83 i2c.write(leds, cmd, 2);
84 cmd[0] = 0x07; // lcd blue
85 cmd[1] = 0xA0;
86 i2c.write(leds, cmd, 2);
87 cmd[0] = 0x08; // lcd green
88 cmd[1] = 0xFF;
89 i2c.write(leds, cmd, 2);
90 cmd[0] = 0x09; // lcd red
91 cmd[1] = 0xC0;
92 i2c.write(leds, cmd, 2);
93 }
94
95 void Smoothiepanel::init(){
96 // init lcd and buzzer
97 lcdbang_init(*this->i2c);
98 // lcdbang_print(*this->i2c, " Smoothiepanel Beta - design by Logxen -");
99 lcdbang_contrast(*this->i2c, this->lcd_contrast);
100
101 ledbang_init(*this->i2c, this->i2c_address);
102 // wait_us(3000);
103 // this->clear();
104 }
105
106 // cycle the buzzer pin at a certain frequency (hz) for a certain duration (ms)
107 void Smoothiepanel::buzz(long duration, uint16_t freq) {
108 const int expander = PCA9505_ADDRESS | this->i2c_address;
109 char cmd[2];
110 char saved;
111
112 // save register state
113 cmd[0] = 0x04;
114 this->i2c->write(expander, cmd, 1, false);
115 this->i2c->read(expander, cmd, 1);
116 saved = cmd[0];
117
118 // buzz
119 cmd[0] = 0x0C;
120 cmd[1] = saved & 0xFE;
121 this->i2c->write(expander, cmd, 2);
122 wait_ms(duration); // TODO: Make this not hold up the whole system
123 cmd[1] = saved;
124 this->i2c->write(expander, cmd, 2);
125 }
126
127 uint8_t Smoothiepanel::readButtons(void) {
128 const int expander = PCA9505_ADDRESS | this->i2c_address;
129 uint8_t button_bits = 0x00;
130 char cmd[1];
131
132 cmd[0] = 0x03;
133 this->i2c->write(expander, cmd, 1, false);
134 this->i2c->read(expander, cmd, 1);
135 //cmd[0] = ~cmd[0];
136 if((cmd[0] & 0x10) > 0) button_bits |= BUTTON_SELECT;
137 if((cmd[0] & 0x02) > 0) button_bits |= BUTTON_LEFT; // back button
138 // if((cmd[0] & 0x01) > 0) button_bits |= BUTTON_AUX1; // play button
139
140 // check the button pause
141 // button_pause.check_signal();
142 // this->setCursor(6, 4);
143 // this->printf("Buttons: 0x%02X", button_bits);
144
145 return button_bits;
146 }
147
148 int Smoothiepanel::readEncoderDelta() {
149 static int8_t enc_states[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};
150 static uint8_t old_AB = 0;
151 old_AB <<= 2; //remember previous state
152 old_AB |= ( this->encoder_a_pin.get() + ( this->encoder_b_pin.get() * 2 ) ); //add current state
153 return enc_states[(old_AB&0x0f)];
154 }
155
156 void Smoothiepanel::clear()
157 {
158 command(LCD_CLEARDISPLAY); // clear display, set cursor position to zero
159 //#ifndef USE_FASTMODE
160 // wait_ms(50); // this command takes a long time!
161 //#endif
162 }
163
164 void Smoothiepanel::home()
165 {
166 command(LCD_RETURNHOME); // set cursor position to zero
167 //#ifndef USE_FASTMODE
168 // wait_us(2000); // this command takes a long time!
169 //#endif
170 }
171
172 void Smoothiepanel::setCursor(uint8_t col, uint8_t row)
173 {
174 int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 };
175 if ( row > _numlines ) row = _numlines - 1; // we count rows starting w/0
176 command(LCD_SETDDRAMADDR | (col + row_offsets[row]));
177 }
178
179 // Turn the display on/off (quickly)
180 void Smoothiepanel::noDisplay() {
181 displaycontrol &= ~LCD_DISPLAYON;
182 command(LCD_DISPLAYCONTROL | displaycontrol);
183 }
184
185 void Smoothiepanel::display() {
186 displaycontrol |= LCD_DISPLAYON;
187 command(LCD_DISPLAYCONTROL | displaycontrol);
188 }
189
190 // Turns the underline cursor on/off
191 void Smoothiepanel::noCursor() {
192 displaycontrol &= ~LCD_CURSORON;
193 command(LCD_DISPLAYCONTROL | displaycontrol);
194 }
195 void Smoothiepanel::cursor() {
196 displaycontrol |= LCD_CURSORON;
197 command(LCD_DISPLAYCONTROL | displaycontrol);
198 }
199
200 // Turn on and off the blinking cursor
201 void Smoothiepanel::noBlink() {
202 displaycontrol &= ~LCD_BLINKON;
203 command(LCD_DISPLAYCONTROL | displaycontrol);
204 }
205 void Smoothiepanel::blink() {
206 displaycontrol |= LCD_BLINKON;
207 command(LCD_DISPLAYCONTROL | displaycontrol);
208 }
209
210 // These commands scroll the display without changing the RAM
211 void Smoothiepanel::scrollDisplayLeft(void) {
212 command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT);
213 }
214 void Smoothiepanel::scrollDisplayRight(void) {
215 command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT);
216 }
217
218 // This is for text that flows Left to Right
219 void Smoothiepanel::leftToRight(void) {
220 displaymode |= LCD_ENTRYLEFT;
221 command(LCD_ENTRYMODESET | displaymode);
222 }
223
224 // This is for text that flows Right to Left
225 void Smoothiepanel::rightToLeft(void) {
226 displaymode &= ~LCD_ENTRYLEFT;
227 command(LCD_ENTRYMODESET | displaymode);
228 }
229
230 // This will 'right justify' text from the cursor
231 void Smoothiepanel::autoscroll(void) {
232 displaymode |= LCD_ENTRYSHIFTINCREMENT;
233 command(LCD_ENTRYMODESET | displaymode);
234 }
235
236 // This will 'left justify' text from the cursor
237 void Smoothiepanel::noAutoscroll(void) {
238 displaymode &= ~LCD_ENTRYSHIFTINCREMENT;
239 command(LCD_ENTRYMODESET | displaymode);
240 }
241
242 void Smoothiepanel::command(uint8_t value) {
243 lcdbang_write(*this->i2c, value>>4, true);
244 lcdbang_write(*this->i2c, value, true);
245 }
246
247 void Smoothiepanel::write(char value) {
248 lcdbang_write(*this->i2c, value);
249 }
250
251 // Allows to set the backlight, if the LCD backpack is used
252 void Smoothiepanel::setBacklight(uint8_t status) {
253 /* // LED turns on when bit is cleared
254 _backlightBits = M17_BIT_LB|M17_BIT_LG|M17_BIT_LR; // all off
255 if (status & LED_RED) _backlightBits &= ~M17_BIT_LR; // red on
256 if (status & LED_GREEN) _backlightBits &= ~M17_BIT_LG; // green on
257 if (status & LED_BLUE) _backlightBits &= ~M17_BIT_LB; // blue on
258
259 burstBits16(_backlightBits);
260 */
261 }
262 /*
263 // write either command or data, burst it to the expander over I2C.
264 void Smoothiepanel::send(uint8_t value, uint8_t mode) {
265 #ifdef USE_FASTMODE
266 // polls for ready. not sure on I2C this is any faster
267
268 // set Data pins as input
269 char data[2];
270 data[0]= MCP23017_IODIRB;
271 data[1]= 0x1E;
272 i2c->write(this->i2c_address, data, 2);
273 uint8_t b= _backlightBits >> 8;
274 burstBits8b((M17_BIT_RW>>8)|b); // RW hi,RS lo
275 char busy;
276 data[0] = MCP23017_GPIOB;
277 do {
278 burstBits8b(((M17_BIT_RW|M17_BIT_EN)>>8)|b); // EN hi
279 i2c->write(this->i2c_address, data, 1);
280 i2c->read(this->i2c_address, &busy, 1); // Read D7
281 burstBits8b((M17_BIT_RW>>8)|b); // EN lo
282 burstBits8b(((M17_BIT_RW|M17_BIT_EN)>>8)|b); // EN hi
283 burstBits8b((M17_BIT_RW>>8)|b); // EN lo
284 } while ((busy&(M17_BIT_D7>>8)) != 0);
285
286 // reset data bits as output
287 data[0]= MCP23017_IODIRB;
288 data[1]= 0x00;
289 i2c->write(this->i2c_address, data, 2);
290 burstBits8b(b); // RW lo
291
292 #else
293 // wait_us(320);
294 #endif
295
296 // BURST SPEED, OH MY GOD
297 // the (now High Speed!) I/O expander pinout
298 // B7 B6 B5 B4 B3 B2 B1 B0 A7 A6 A5 A4 A3 A2 A1 A0 - MCP23017
299 // 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
300 // RS RW EN D4 D5 D6 D7 B G R B4 B3 B2 B1 B0
301
302 // n.b. RW bit stays LOW to write
303 uint8_t buf = _backlightBits >> 8;
304 // send high 4 bits
305 if (value & 0x10) buf |= M17_BIT_D4 >> 8;
306 if (value & 0x20) buf |= M17_BIT_D5 >> 8;
307 if (value & 0x40) buf |= M17_BIT_D6 >> 8;
308 if (value & 0x80) buf |= M17_BIT_D7 >> 8;
309
310 if (mode) buf |= (M17_BIT_RS|M17_BIT_EN) >> 8; // RS+EN
311 else buf |= M17_BIT_EN >> 8; // EN
312
313 burstBits8b(buf);
314
315 // resend w/ EN turned off
316 buf &= ~(M17_BIT_EN >> 8);
317 burstBits8b(buf);
318
319 // send low 4 bits
320 buf = _backlightBits >> 8;
321 // send high 4 bits
322 if (value & 0x01) buf |= M17_BIT_D4 >> 8;
323 if (value & 0x02) buf |= M17_BIT_D5 >> 8;
324 if (value & 0x04) buf |= M17_BIT_D6 >> 8;
325 if (value & 0x08) buf |= M17_BIT_D7 >> 8;
326
327 if (mode) buf |= (M17_BIT_RS|M17_BIT_EN) >> 8; // RS+EN
328 else buf |= M17_BIT_EN >> 8; // EN
329
330 burstBits8b(buf);
331
332 // resend w/ EN turned off
333 buf &= ~(M17_BIT_EN >> 8);
334 burstBits8b(buf);
335 }
336
337 // We pause the system
338 uint32_t Smoothiepanel::on_pause_release(uint32_t dummy){
339 if(!paused) {
340 THEKERNEL->pauser->take();
341 paused= true;
342 }else{
343 THEKERNEL->pauser->release();
344 paused= false;
345 }
346 return 0;
347 }
348 */
349