Fork of Smoothie to port to mbed non-LPC targets.
Fork of Smoothie by
VikiLCD.cpp
00001 /* 00002 This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). 00003 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. 00004 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. 00005 You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. 00006 */ 00007 #include "VikiLCD.h" 00008 00009 #include "Button.h" 00010 00011 // config settings for Viki LCD 00012 #define panel_checksum CHECKSUM("panel") 00013 #define encoder_a_pin_checksum CHECKSUM("encoder_a_pin") 00014 #define encoder_b_pin_checksum CHECKSUM("encoder_b_pin") 00015 #define button_pause_pin_checksum CHECKSUM("button_pause_pin") 00016 #define i2c_pins_checksum CHECKSUM("i2c_pins") 00017 #define i2c_frequency_checksum CHECKSUM("i2c_frequency") 00018 00019 // if this is defined we use the R/W poll mode instead of fixed delays 00020 // However at the slower I2C frequency required for Viki long cables it is slower than fixed delay 00021 // taken from LiquidCrystalFast.cpp and implemented for Viki LCD here by Jim Morris 00022 //#define USE_FASTMODE 00023 00024 // readButtons() will only return these bit values 00025 #define VIKI_ALL_BUTTON_BITS (BUTTON_PAUSE|BUTTON_UP|BUTTON_DOWN|BUTTON_LEFT|BUTTON_RIGHT|BUTTON_SELECT) 00026 00027 #define MCP23017_ADDRESS 0x20<<1 00028 00029 // registers 00030 #define MCP23017_IODIRA 0x00 00031 #define MCP23017_IPOLA 0x02 00032 #define MCP23017_GPINTENA 0x04 00033 #define MCP23017_DEFVALA 0x06 00034 #define MCP23017_INTCONA 0x08 00035 #define MCP23017_IOCONA 0x0A 00036 #define MCP23017_GPPUA 0x0C 00037 #define MCP23017_INTFA 0x0E 00038 #define MCP23017_INTCAPA 0x10 00039 #define MCP23017_GPIOA 0x12 00040 #define MCP23017_OLATA 0x14 00041 00042 00043 #define MCP23017_IODIRB 0x01 00044 #define MCP23017_IPOLB 0x03 00045 #define MCP23017_GPINTENB 0x05 00046 #define MCP23017_DEFVALB 0x07 00047 #define MCP23017_INTCONB 0x09 00048 #define MCP23017_IOCONB 0x0B 00049 #define MCP23017_GPPUB 0x0D 00050 #define MCP23017_INTFB 0x0F 00051 #define MCP23017_INTCAPB 0x11 00052 #define MCP23017_GPIOB 0x13 00053 #define MCP23017_OLATB 0x15 00054 00055 //MCP23017 - Adafruit RGB LCD Shield and VikiLCD 00056 // bit pattern for the burstBits function is 00057 // 00058 // B7 B6 B5 B4 B3 B2 B1 B0 A7 A6 A5 A4 A3 A2 A1 A0 - MCP23017 00059 // RS RW EN D4 D5 D6 D7 LB LG LR BZ B4 B3 B2 B1 B0 00060 // 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 00061 #define M17_BIT_RS 0x8000 00062 #define M17_BIT_RW 0x4000 00063 #define M17_BIT_EN 0x2000 00064 #define M17_BIT_D4 0x1000 00065 #define M17_BIT_D5 0x0800 00066 #define M17_BIT_D6 0x0400 00067 #define M17_BIT_D7 0x0200 00068 #define M17_BIT_LB 0x0100 00069 #define M17_BIT_LG 0x0080 00070 #define M17_BIT_LR 0x0040 00071 #define M17_BIT_BZ 0x0020 //Added a buzzer on this pin 00072 #define M17_BIT_B4 0x0010 00073 #define M17_BIT_B3 0x0008 00074 #define M17_BIT_B2 0x0004 00075 #define M17_BIT_B1 0x0002 00076 #define M17_BIT_B0 0x0001 00077 00078 // commands 00079 #define LCD_CLEARDISPLAY 0x01 00080 #define LCD_RETURNHOME 0x02 00081 #define LCD_ENTRYMODESET 0x04 00082 #define LCD_DISPLAYCONTROL 0x08 00083 #define LCD_CURSORSHIFT 0x10 00084 #define LCD_FUNCTIONSET 0x20 00085 #define LCD_SETCGRAMADDR 0x40 00086 #define LCD_SETDDRAMADDR 0x80 00087 00088 // flags for display entry mode 00089 #define LCD_ENTRYRIGHT 0x00 00090 #define LCD_ENTRYLEFT 0x02 00091 #define LCD_ENTRYSHIFTINCREMENT 0x01 00092 #define LCD_ENTRYSHIFTDECREMENT 0x00 00093 00094 // flags for display on/off control 00095 #define LCD_DISPLAYON 0x04 00096 #define LCD_DISPLAYOFF 0x00 00097 #define LCD_CURSORON 0x02 00098 #define LCD_CURSOROFF 0x00 00099 #define LCD_BLINKON 0x01 00100 #define LCD_BLINKOFF 0x00 00101 00102 // flags for display/cursor shift 00103 #define LCD_DISPLAYMOVE 0x08 00104 #define LCD_CURSORMOVE 0x00 00105 #define LCD_MOVERIGHT 0x04 00106 #define LCD_MOVELEFT 0x00 00107 00108 // flags for function set 00109 #define LCD_8BITMODE 0x10 00110 #define LCD_4BITMODE 0x00 00111 #define LCD_2LINE 0x08 00112 #define LCD_1LINE 0x00 00113 #define LCD_5x10DOTS 0x04 00114 #define LCD_5x8DOTS 0x00 00115 00116 // flags for backlight control 00117 #define LCD_BACKLIGHT 0x08 00118 #define LCD_NOBACKLIGHT 0x00 00119 00120 VikiLCD::VikiLCD() { 00121 // I2C com 00122 int i2c_pins = THEKERNEL->config->value(panel_checksum, i2c_pins_checksum)->by_default(3)->as_number(); 00123 if(i2c_pins == 0){ 00124 this->i2c = new mbed::I2C(P0_0, P0_1); 00125 }else if(i2c_pins == 1){ 00126 this->i2c = new mbed::I2C(P0_10, P0_11); 00127 }else if(i2c_pins == 2){ 00128 this->i2c = new mbed::I2C(P0_19, P0_20); 00129 }else{ // 3, default 00130 this->i2c = new mbed::I2C(P0_27, P0_28); 00131 } 00132 00133 this->i2c_frequency = THEKERNEL->config->value(panel_checksum, i2c_frequency_checksum)->by_default(60000)->as_number(); 00134 i2c->frequency(this->i2c_frequency); 00135 00136 // Default values 00137 this->i2c_address = MCP23017_ADDRESS; 00138 this->displaycontrol = 0x00; 00139 this->displayfunction = LCD_4BITMODE | LCD_2LINE | LCD_5x8DOTS; // in case they forget to call begin() at least we have somethin 00140 this->displaymode = 0x00; 00141 this->_numlines = 4; 00142 00143 // configure the pins to use 00144 this->encoder_a_pin.from_string(THEKERNEL->config->value( panel_checksum, encoder_a_pin_checksum)->by_default("nc")->as_string())->as_input(); 00145 00146 this->encoder_b_pin.from_string(THEKERNEL->config->value( panel_checksum, encoder_b_pin_checksum)->by_default("nc")->as_string())->as_input(); 00147 00148 this->button_pause_pin.from_string(THEKERNEL->config->value( panel_checksum, button_pause_pin_checksum)->by_default("nc")->as_string())->as_input(); 00149 } 00150 00151 VikiLCD::~VikiLCD() { 00152 delete this->i2c; 00153 } 00154 00155 00156 void VikiLCD::init(){ 00157 // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION! 00158 // according to datasheet, we need at least 40ms after power rises above 2.7V 00159 // before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50 00160 00161 char data[2]; 00162 00163 // Setup 00164 this->displayfunction = LCD_4BITMODE | LCD_2LINE | LCD_5x8DOTS; 00165 this->_backlightBits = M17_BIT_LB|M17_BIT_LG|M17_BIT_LR; // all off 00166 00167 wait_ms(50); 00168 00169 // now set up input/output pins in MCP23017 00170 data[0]= MCP23017_IODIRA; 00171 data[1]= 0x1F; // buttons input, all others output 00172 i2c->write(this->i2c_address, data, 2); 00173 00174 00175 // set the button pullups 00176 data[0]= MCP23017_GPPUA; 00177 data[1]= 0x1F; 00178 i2c->write(this->i2c_address, data, 2); 00179 00180 data[0]= MCP23017_IODIRB; 00181 data[1]= 0x00; // all pins output 00182 i2c->write(this->i2c_address, data, 2); 00183 00184 //put the LCD into 4 bit mode 00185 // start with a non-standard command to make it realize we're speaking 4-bit here 00186 // per LCD datasheet, first command is a single 4-bit burst, 0011. 00187 //----- 00188 // we cannot assume that the LCD panel is powered at the same time as 00189 // the arduino, so we have to perform a software reset as per page 45 00190 // of the HD44780 datasheet - (kch) 00191 //----- 00192 00193 // bit pattern for the burstBits function is 00194 // 00195 // B7 B6 B5 B4 B3 B2 B1 B0 A7 A6 A5 A4 A3 A2 A1 A0 - MCP23017 00196 // 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 00197 // RS RW EN D4 D5 D6 D7 B G R B4 B3 B2 B1 B0 00198 for (uint8_t i=0;i < 3;i++) { 00199 burstBits8b((M17_BIT_EN|M17_BIT_D5|M17_BIT_D4) >> 8); 00200 burstBits8b((M17_BIT_D5|M17_BIT_D4) >> 8); 00201 } 00202 burstBits8b((M17_BIT_EN|M17_BIT_D5) >> 8); 00203 burstBits8b(M17_BIT_D5 >> 8); 00204 00205 00206 wait_ms(5); // this shouldn't be necessary, but sometimes 16MHz is stupid-fast. 00207 00208 command(LCD_FUNCTIONSET | displayfunction); // then send 0010NF00 (N=lines, F=font) 00209 wait_ms(5); // for safe keeping... 00210 command(LCD_FUNCTIONSET | displayfunction); // ... twice. 00211 wait_ms(5); // done! 00212 00213 // turn on the LCD with our defaults. since these libs seem to use personal preference, I like a cursor. 00214 displaycontrol = (LCD_DISPLAYON|LCD_BACKLIGHT); 00215 display(); 00216 // clear it off 00217 clear(); 00218 00219 displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT; 00220 // set the entry mode 00221 command(LCD_ENTRYMODESET | displaymode); 00222 } 00223 00224 // we use this to burst bits to the GPIO chip whenever we need to. avoids repetitive code. 00225 void VikiLCD::burstBits8b(uint8_t value) { 00226 char data[2]; 00227 data[0] = MCP23017_GPIOB; 00228 data[1]= value; 00229 i2c->write(this->i2c_address, data, 2); 00230 } 00231 00232 // value byte order is BA 00233 void VikiLCD::burstBits16(uint16_t value) { 00234 char data[3]; 00235 data[0] = MCP23017_GPIOA; 00236 data[1]= value&0xFF; 00237 data[2]= value>>8; 00238 i2c->write(this->i2c_address, data, 3); 00239 } 00240 00241 // cycle the buzzer pin at a certain frequency (hz) for a certain duration (ms) 00242 void VikiLCD::buzz(long duration, uint16_t freq) { 00243 char data[2]; 00244 int currentRegister = 0; 00245 // read gpio register 00246 data[0] = MCP23017_GPIOA; 00247 i2c->write(this->i2c_address, data, 1); 00248 i2c->read(this->i2c_address, data, 1); // Read from selected Register 00249 currentRegister= data[0]; 00250 00251 duration *=1000; //convert from ms to us 00252 long period = 1000000 / freq; // period in us 00253 long elapsed_time = 0; 00254 while (elapsed_time < duration) { 00255 data[0]= MCP23017_GPIOA; 00256 data[1]= currentRegister |= M17_BIT_BZ; 00257 i2c->write(this->i2c_address, data, 2); 00258 00259 wait_us(period / 2); 00260 00261 data[0]= MCP23017_GPIOA; 00262 data[1]= currentRegister &= ~M17_BIT_BZ; 00263 i2c->write(this->i2c_address, data, 2); 00264 00265 wait_us(period / 2); 00266 elapsed_time += (period); 00267 } 00268 } 00269 00270 uint8_t VikiLCD::readButtons(void) { 00271 char data[2]; 00272 data[0] = MCP23017_GPIOA; 00273 i2c->write(this->i2c_address, data, 1); 00274 i2c->read(this->i2c_address, data, 1); // Read from selected Register 00275 00276 // check the button pause 00277 if(this->button_pause_pin.connected() && this->button_pause_pin.get()) data[0] |= BUTTON_PAUSE; 00278 00279 // if it is the variant Panelolu2 swap the buttons around 00280 if(this->isPanelolu2) { 00281 // the select button bit is on GPA2 not GPA0 00282 if((data[0]&M17_BIT_B2) == 0) return BUTTON_SELECT; 00283 return 0; // only one button on Panelolu2 ignore the ena_a and en_b 00284 00285 } else { 00286 return (~data[0]) & VIKI_ALL_BUTTON_BITS; 00287 } 00288 } 00289 00290 void VikiLCD::on_refresh(){ 00291 // FIXME this is a hack to get around I2C noise 00292 // Update Only every 20 refreshes, 1 a second 00293 static int update_counts = 0; 00294 update_counts++; 00295 if( update_counts % 20 == 0 && i2c->is_timed_out()) { 00296 // if there was a timeout on i2c then reset the lcd 00297 this->init(); 00298 } 00299 } 00300 00301 int VikiLCD::readEncoderDelta() { 00302 static int8_t enc_states[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0}; 00303 static uint8_t old_AB = 0; 00304 old_AB <<= 2; //remember previous state 00305 old_AB |= ( this->encoder_a_pin.get() + ( this->encoder_b_pin.get() * 2 ) ); //add current state 00306 return enc_states[(old_AB&0x0f)]; 00307 } 00308 00309 void VikiLCD::clear() 00310 { 00311 command(LCD_CLEARDISPLAY); // clear display, set cursor position to zero 00312 #ifndef USE_FASTMODE 00313 wait_us(2000); // this command takes a long time! 00314 #endif 00315 } 00316 00317 void VikiLCD::home() 00318 { 00319 command(LCD_RETURNHOME); // set cursor position to zero 00320 #ifndef USE_FASTMODE 00321 wait_us(2000); // this command takes a long time! 00322 #endif 00323 } 00324 00325 void VikiLCD::setCursor(uint8_t col, uint8_t row) 00326 { 00327 int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 }; 00328 if ( row > _numlines ) row = _numlines - 1; // we count rows starting w/0 00329 command(LCD_SETDDRAMADDR | (col + row_offsets[row])); 00330 } 00331 00332 // Turn the display on/off (quickly) 00333 void VikiLCD::noDisplay() { 00334 displaycontrol &= ~LCD_DISPLAYON; 00335 command(LCD_DISPLAYCONTROL | displaycontrol); 00336 } 00337 void VikiLCD::display() { 00338 displaycontrol |= LCD_DISPLAYON; 00339 command(LCD_DISPLAYCONTROL | displaycontrol); 00340 } 00341 00342 // Turns the underline cursor on/off 00343 void VikiLCD::noCursor() { 00344 displaycontrol &= ~LCD_CURSORON; 00345 command(LCD_DISPLAYCONTROL | displaycontrol); 00346 } 00347 void VikiLCD::cursor() { 00348 displaycontrol |= LCD_CURSORON; 00349 command(LCD_DISPLAYCONTROL | displaycontrol); 00350 } 00351 00352 // Turn on and off the blinking cursor 00353 void VikiLCD::noBlink() { 00354 displaycontrol &= ~LCD_BLINKON; 00355 command(LCD_DISPLAYCONTROL | displaycontrol); 00356 } 00357 void VikiLCD::blink() { 00358 displaycontrol |= LCD_BLINKON; 00359 command(LCD_DISPLAYCONTROL | displaycontrol); 00360 } 00361 00362 // These commands scroll the display without changing the RAM 00363 void VikiLCD::scrollDisplayLeft(void) { 00364 command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT); 00365 } 00366 void VikiLCD::scrollDisplayRight(void) { 00367 command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT); 00368 } 00369 00370 // This is for text that flows Left to Right 00371 void VikiLCD::leftToRight(void) { 00372 displaymode |= LCD_ENTRYLEFT; 00373 command(LCD_ENTRYMODESET | displaymode); 00374 } 00375 00376 // This is for text that flows Right to Left 00377 void VikiLCD::rightToLeft(void) { 00378 displaymode &= ~LCD_ENTRYLEFT; 00379 command(LCD_ENTRYMODESET | displaymode); 00380 } 00381 00382 // This will 'right justify' text from the cursor 00383 void VikiLCD::autoscroll(void) { 00384 displaymode |= LCD_ENTRYSHIFTINCREMENT; 00385 command(LCD_ENTRYMODESET | displaymode); 00386 } 00387 00388 // This will 'left justify' text from the cursor 00389 void VikiLCD::noAutoscroll(void) { 00390 displaymode &= ~LCD_ENTRYSHIFTINCREMENT; 00391 command(LCD_ENTRYMODESET | displaymode); 00392 } 00393 00394 void VikiLCD::command(uint8_t value) { 00395 send(value, 0); 00396 } 00397 00398 void VikiLCD::write(const char* line, int len) { 00399 for (int i = 0; i < len; ++i) { 00400 send(*line++, 1); 00401 } 00402 } 00403 00404 // Sets the indicator leds 00405 void VikiLCD::setLed(int led, bool onoff) { 00406 // LED turns on when bit is cleared 00407 if(onoff) { 00408 switch(led) { 00409 case LED_FAN_ON: _backlightBits &= ~M17_BIT_LR; break; // on 00410 case LED_HOTEND_ON: _backlightBits &= ~M17_BIT_LG; break; // on 00411 case LED_BED_ON: _backlightBits &= ~M17_BIT_LB; break; // on 00412 } 00413 }else{ 00414 switch(led) { 00415 case LED_FAN_ON: _backlightBits |= M17_BIT_LR; break; // off 00416 case LED_HOTEND_ON: _backlightBits |= M17_BIT_LG; break; // off 00417 case LED_BED_ON: _backlightBits |= M17_BIT_LB; break; // off 00418 } 00419 } 00420 burstBits16(_backlightBits); 00421 } 00422 00423 // write either command or data, burst it to the expander over I2C. 00424 void VikiLCD::send(uint8_t value, uint8_t mode) { 00425 #ifdef USE_FASTMODE 00426 // polls for ready. not sure on I2C this is any faster 00427 00428 // set Data pins as input 00429 char data[2]; 00430 data[0]= MCP23017_IODIRB; 00431 data[1]= 0x1E; 00432 i2c->write(this->i2c_address, data, 2); 00433 uint8_t b= _backlightBits >> 8; 00434 burstBits8b((M17_BIT_RW>>8)|b); // RW hi,RS lo 00435 char busy; 00436 data[0] = MCP23017_GPIOB; 00437 do { 00438 burstBits8b(((M17_BIT_RW|M17_BIT_EN)>>8)|b); // EN hi 00439 i2c->write(this->i2c_address, data, 1); 00440 i2c->read(this->i2c_address, &busy, 1); // Read D7 00441 burstBits8b((M17_BIT_RW>>8)|b); // EN lo 00442 burstBits8b(((M17_BIT_RW|M17_BIT_EN)>>8)|b); // EN hi 00443 burstBits8b((M17_BIT_RW>>8)|b); // EN lo 00444 } while ((busy&(M17_BIT_D7>>8)) != 0); 00445 00446 // reset data bits as output 00447 data[0]= MCP23017_IODIRB; 00448 data[1]= 0x00; 00449 i2c->write(this->i2c_address, data, 2); 00450 burstBits8b(b); // RW lo 00451 00452 #else 00453 // wait_us(320); 00454 #endif 00455 00456 // BURST SPEED, OH MY GOD 00457 // the (now High Speed!) I/O expander pinout 00458 // B7 B6 B5 B4 B3 B2 B1 B0 A7 A6 A5 A4 A3 A2 A1 A0 - MCP23017 00459 // 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 00460 // RS RW EN D4 D5 D6 D7 B G R B4 B3 B2 B1 B0 00461 00462 // n.b. RW bit stays LOW to write 00463 uint8_t buf = _backlightBits >> 8; 00464 // send high 4 bits 00465 if (value & 0x10) buf |= M17_BIT_D4 >> 8; 00466 if (value & 0x20) buf |= M17_BIT_D5 >> 8; 00467 if (value & 0x40) buf |= M17_BIT_D6 >> 8; 00468 if (value & 0x80) buf |= M17_BIT_D7 >> 8; 00469 00470 if (mode) buf |= (M17_BIT_RS|M17_BIT_EN) >> 8; // RS+EN 00471 else buf |= M17_BIT_EN >> 8; // EN 00472 00473 burstBits8b(buf); 00474 00475 // resend w/ EN turned off 00476 buf &= ~(M17_BIT_EN >> 8); 00477 burstBits8b(buf); 00478 00479 // send low 4 bits 00480 buf = _backlightBits >> 8; 00481 // send high 4 bits 00482 if (value & 0x01) buf |= M17_BIT_D4 >> 8; 00483 if (value & 0x02) buf |= M17_BIT_D5 >> 8; 00484 if (value & 0x04) buf |= M17_BIT_D6 >> 8; 00485 if (value & 0x08) buf |= M17_BIT_D7 >> 8; 00486 00487 if (mode) buf |= (M17_BIT_RS|M17_BIT_EN) >> 8; // RS+EN 00488 else buf |= M17_BIT_EN >> 8; // EN 00489 00490 burstBits8b(buf); 00491 00492 // resend w/ EN turned off 00493 buf &= ~(M17_BIT_EN >> 8); 00494 burstBits8b(buf); 00495 }
Generated on Tue Jul 12 2022 20:09:03 by 1.7.2