Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
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
