Fork of Smoothie to port to mbed non-LPC targets.

Dependencies:   mbed

Fork of Smoothie by Stéphane Cachat

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers VikiLCD.cpp Source File

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 }