Home | History | Annotate | Download | only in lcd
      1 /*
      2  * The MIT License (MIT)
      3  *
      4  * Author: Daniel Mosquera
      5  * Copyright (c) 2013 Daniel Mosquera
      6  *
      7  * Author: Thomas Ingleby <thomas.c.ingleby (at) intel.com>
      8  * Copyright (c) 2014 Intel Corporation.
      9  *
     10  * Contributions: Jon Trulson <jtrulson (at) ics.com>
     11  *                Sergey Kiselev <sergey.kiselev (at) intel.com>
     12  *
     13  * Permission is hereby granted, free of charge, to any person
     14  * obtaining a copy of this software and associated documentation
     15  * files (the "Software"), to deal in the Software without
     16  * restriction, including without limitation the rights to use, copy,
     17  * modify, merge, publish, distribute, sublicense, and/or sell copies
     18  * of the Software, and to permit persons to whom the Software is
     19  * furnished to do so, subject to the following conditions:
     20  *
     21  * The above copyright notice and this permission notice shall be
     22  * included in all copies or substantial portions of the Software.
     23  *
     24  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     25  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     26  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     27  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
     28  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
     29  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     30  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     31  * SOFTWARE.
     32  */
     33 
     34 #include <string>
     35 #include <stdexcept>
     36 #include <unistd.h>
     37 
     38 #include "hd44780_bits.h"
     39 #include "lcm1602.h"
     40 
     41 using namespace upm;
     42 
     43 Lcm1602::Lcm1602(int bus_in, int addr_in, bool isExpander,
     44                  uint8_t numColumns, uint8_t numRows) :
     45   m_i2c_lcd_control(new mraa::I2c(bus_in)),
     46   m_gpioRS(0), m_gpioEnable(0), m_gpioD0(0),
     47   m_gpioD1(0), m_gpioD2(0), m_gpioD3(0),
     48   m_numColumns(numColumns), m_numRows(numRows)
     49 {
     50     mraa::Result error = mraa::SUCCESS;
     51     m_name = "Lcm1602 (I2C)";
     52     m_isI2C = true;
     53 
     54     m_lcd_control_address = addr_in;
     55 
     56     error = m_i2c_lcd_control->address(m_lcd_control_address);
     57     if (error != mraa::SUCCESS) {
     58         throw std::invalid_argument(std::string(__FUNCTION__) +
     59                                     ": I2c.address() failed");
     60         return;
     61     }
     62 
     63     // default display control
     64     m_displayControl = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;
     65 
     66     // if we are not dealing with an expander (say via a derived class
     67     // like Jhd1313m1), then we do not want to execute the rest of the
     68     // code below.  Rather, the derived class's constructor should
     69     // follow up with any setup required -- we will only initialize
     70     // the I2C context and bail.
     71 
     72     if (!isExpander)
     73       return;
     74 
     75     usleep(50000);
     76     expandWrite(LCD_BACKLIGHT);
     77     usleep(100000);
     78 
     79     write4bits(0x03 << 4);
     80     usleep(4500);
     81     write4bits(0x30);
     82     usleep(4500);
     83     write4bits(0x30);
     84     usleep(150);
     85 
     86     // Put into 4 bit mode
     87     write4bits(0x20);
     88 
     89     m_displayControl = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;
     90     // Set numeber of lines
     91     command(LCD_FUNCTIONSET | 0x0f);
     92     command(LCD_DISPLAYCONTROL | m_displayControl);
     93     clear();
     94 
     95     // Set entry mode.
     96     m_entryDisplayMode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;
     97     command(LCD_ENTRYMODESET | m_entryDisplayMode);
     98 
     99     home();
    100 }
    101 
    102 Lcm1602::Lcm1602(uint8_t rs,  uint8_t enable, uint8_t d0,
    103                  uint8_t d1, uint8_t d2, uint8_t d3,
    104                  uint8_t numColumns, uint8_t numRows) :
    105   m_i2c_lcd_control(0),
    106   m_gpioRS(new mraa::Gpio(rs)), m_gpioEnable(new mraa::Gpio(enable)),
    107   m_gpioD0(new mraa::Gpio(d0)), m_gpioD1(new mraa::Gpio(d1)),
    108   m_gpioD2(new mraa::Gpio(d2)), m_gpioD3(new mraa::Gpio(d3)),
    109   m_numColumns(numColumns), m_numRows(numRows)
    110 {
    111     mraa::Result error = mraa::SUCCESS;
    112     m_name = "Lcm1602 (4-bit GPIO)";
    113     m_isI2C = false;
    114 
    115     // setup our gpios
    116 
    117     m_gpioRS->dir(mraa::DIR_OUT);
    118     m_gpioEnable->dir(mraa::DIR_OUT);
    119 
    120     m_gpioD0->dir(mraa::DIR_OUT);
    121     m_gpioD1->dir(mraa::DIR_OUT);
    122     m_gpioD2->dir(mraa::DIR_OUT);
    123     m_gpioD3->dir(mraa::DIR_OUT);
    124 
    125 
    126     // set RS and Enable low to begin issuing commands
    127     m_gpioRS->write(0);
    128     m_gpioEnable->write(0);
    129 
    130     // wait to stabilize
    131     usleep(100000);
    132 
    133     // set 4bit mode
    134 
    135     // These steps are adapted from the HD44780 datasheet, figure 24
    136 
    137     // try 1
    138     write4bits(0x03);
    139     usleep(4500);
    140 
    141     // try 2
    142     write4bits(0x03);
    143     usleep(4500);
    144 
    145     // try 3
    146     write4bits(0x03);
    147     usleep(150);
    148 
    149     // Finally, put into 4 bit mode
    150     write4bits(0x02);
    151 
    152     // Set number of lines
    153     command(LCD_FUNCTIONSET | LCD_2LINE | LCD_4BITMODE | LCD_5x8DOTS);
    154     m_displayControl = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;
    155     command(LCD_DISPLAYCONTROL | m_displayControl);
    156     usleep(2000);
    157     clear();
    158 
    159     // Set entry mode.
    160     m_entryDisplayMode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;
    161     command(LCD_ENTRYMODESET | m_entryDisplayMode);
    162 
    163     home();
    164 }
    165 
    166 Lcm1602::~Lcm1602()
    167 {
    168   // clean up after ourselves
    169   if (m_isI2C)
    170     {
    171       delete m_i2c_lcd_control;
    172     }
    173   else
    174     {
    175       delete m_gpioRS;
    176       delete m_gpioEnable;
    177 
    178       delete m_gpioD0;
    179       delete m_gpioD1;
    180       delete m_gpioD2;
    181       delete m_gpioD3;
    182     }
    183 }
    184 
    185 /*
    186  * **************
    187  *  virtual area
    188  * **************
    189  */
    190 mraa::Result
    191 Lcm1602::write(std::string msg)
    192 {
    193     mraa::Result error = mraa::SUCCESS;
    194     for (std::string::size_type i = 0; i < msg.size(); ++i) {
    195         error = data(msg[i]);
    196     }
    197     return error;
    198 }
    199 
    200 mraa::Result
    201 Lcm1602::setCursor(int row, int column)
    202 {
    203     mraa::Result error = mraa::SUCCESS;
    204     column = column % m_numColumns;
    205     uint8_t offset = column;
    206 
    207     switch (m_numRows)
    208     {
    209         case 1:
    210             // Single row displays with more than 8 columns usually have their
    211             // DDRAM split in two halves. The first half starts at address 00.
    212             // The second half starts at address 40. E.g. 16x2 DDRAM mapping:
    213             // 00 01 02 03 04 05 06 07 40 41 42 43 44 45 46 47
    214             if (m_numColumns > 8)
    215             {
    216                 offset = (column % (m_numColumns / 2)) +
    217                          (column / (m_numColumns / 2)) * 0x40;
    218             }
    219             break;
    220         case 2:
    221             // this should work for any display with two rows
    222             // DDRAM mapping:
    223             // 00 .. 27
    224             // 40 .. 67
    225             offset += row * 0x40;
    226             break;
    227         case 4:
    228             if (m_numColumns == 16)
    229             {
    230                  // 16x4 display
    231                  // DDRAM mapping:
    232                  // 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
    233                  // 40 41 42 43 43 45 46 47 48 49 4A 4B 4C 4D 4E 4F
    234                  // 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
    235                  // 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
    236                  int row_addr[] = { 0x00, 0x40, 0x10, 0x50 };
    237                  offset += row_addr[row];
    238              }
    239              else
    240              {
    241                  // 20x4 display
    242                  // DDRAM mapping:
    243                  // 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13
    244                  // 40 41 42 43 43 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53
    245                  // 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 26 27
    246                  // 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F 60 61 62 63 64 65 66 67
    247 		 int row_addr[] = { 0x00, 0x40, 0x14, 0x54 };
    248                  offset += row_addr[row];
    249              }
    250              break;
    251     }
    252 
    253     return command(LCD_CMD | offset);
    254 }
    255 
    256 mraa::Result
    257 Lcm1602::clear()
    258 {
    259     mraa::Result ret;
    260     ret = command(LCD_CLEARDISPLAY);
    261     usleep(2000); // this command takes awhile
    262     return ret;
    263 }
    264 
    265 mraa::Result
    266 Lcm1602::home()
    267 {
    268     mraa::Result ret;
    269     ret = command(LCD_RETURNHOME);
    270     usleep(2000); // this command takes awhile
    271     return ret;
    272 }
    273 
    274 mraa::Result
    275 Lcm1602::createChar(uint8_t charSlot, uint8_t charData[])
    276 {
    277     mraa::Result error = mraa::SUCCESS;
    278     charSlot &= 0x07; // only have 8 positions we can set
    279     error = command(LCD_SETCGRAMADDR | (charSlot << 3));
    280     if (error == mraa::SUCCESS) {
    281         for (int i = 0; i < 8; i++) {
    282           error = data(charData[i]);
    283         }
    284     }
    285 
    286     return error;
    287 }
    288 
    289 mraa::Result Lcm1602::displayOn()
    290 {
    291   m_displayControl |= LCD_DISPLAYON;
    292   return command(LCD_DISPLAYCONTROL | m_displayControl);
    293 }
    294 
    295 mraa::Result Lcm1602::displayOff()
    296 {
    297   m_displayControl &= ~LCD_DISPLAYON;
    298   return command(LCD_DISPLAYCONTROL | m_displayControl);
    299 }
    300 
    301 mraa::Result Lcm1602::cursorOn()
    302 {
    303   m_displayControl |= LCD_CURSORON;
    304   return command(LCD_DISPLAYCONTROL | m_displayControl);
    305 }
    306 
    307 mraa::Result Lcm1602::cursorOff()
    308 {
    309   m_displayControl &= ~LCD_CURSORON;
    310   return command(LCD_DISPLAYCONTROL | m_displayControl);
    311 }
    312 
    313 mraa::Result Lcm1602::cursorBlinkOn()
    314 {
    315   m_displayControl |= LCD_BLINKON;
    316   return command(LCD_DISPLAYCONTROL | m_displayControl);
    317 }
    318 
    319 mraa::Result Lcm1602::cursorBlinkOff()
    320 {
    321   m_displayControl &= ~LCD_BLINKON;
    322   return command(LCD_DISPLAYCONTROL | m_displayControl);
    323 }
    324 
    325 mraa::Result Lcm1602::scrollDisplayLeft()
    326 {
    327   return command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT);
    328 }
    329 
    330 mraa::Result Lcm1602::scrollDisplayRight()
    331 {
    332   return command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT);
    333 }
    334 
    335 mraa::Result Lcm1602::entryLeftToRight()
    336 {
    337   m_entryDisplayMode |= LCD_ENTRYLEFT;
    338   return command(LCD_ENTRYMODESET | m_entryDisplayMode);
    339 }
    340 
    341 mraa::Result Lcm1602::entryRightToLeft()
    342 {
    343   m_entryDisplayMode &= ~LCD_ENTRYLEFT;
    344   return command(LCD_ENTRYMODESET | m_entryDisplayMode);
    345 }
    346 
    347 mraa::Result Lcm1602::autoscrollOn()
    348 {
    349   m_entryDisplayMode |= LCD_ENTRYSHIFTINCREMENT;
    350   return command(LCD_ENTRYMODESET | m_entryDisplayMode);
    351 }
    352 
    353 mraa::Result Lcm1602::autoscrollOff()
    354 {
    355   m_entryDisplayMode &= ~LCD_ENTRYSHIFTINCREMENT;
    356   return command(LCD_ENTRYMODESET | m_entryDisplayMode);
    357 }
    358 
    359 mraa::Result Lcm1602::command(uint8_t cmd)
    360 {
    361   return send(cmd, 0);
    362 }
    363 
    364 mraa::Result Lcm1602::data(uint8_t cmd)
    365 {
    366   return send(cmd, LCD_RS); // 1
    367 }
    368 
    369 
    370 /*
    371  * **************
    372  *  private area
    373  * **************
    374  */
    375 mraa::Result
    376 Lcm1602::send(uint8_t value, int mode)
    377 {
    378     mraa::Result ret = mraa::SUCCESS;
    379     uint8_t h;
    380     uint8_t l;
    381 
    382     if (m_isI2C)
    383       {
    384         h = value & 0xf0;
    385         l = (value << 4) & 0xf0;
    386         ret = write4bits(h | mode);
    387         ret = write4bits(l | mode);
    388         return ret;
    389       }
    390 
    391     // else, gpio (4 bit)
    392 
    393     // register select
    394     m_gpioRS->write(mode);
    395 
    396     h = value >> 4;
    397     l = value & 0x0f;
    398 
    399     ret = write4bits(h);
    400     ret = write4bits(l);
    401     return ret;
    402 }
    403 
    404 mraa::Result
    405 Lcm1602::write4bits(uint8_t value)
    406 {
    407     mraa::Result ret = mraa::SUCCESS;
    408 
    409     if (m_isI2C)
    410       {
    411         ret = expandWrite(value);
    412         ret = pulseEnable(value);
    413         return ret;
    414       }
    415 
    416     // else gpio
    417     ret = m_gpioD0->write( ((value >> 0) & 0x01) );
    418     ret = m_gpioD1->write( ((value >> 1) & 0x01) );
    419     ret = m_gpioD2->write( ((value >> 2) & 0x01) );
    420     ret = m_gpioD3->write( ((value >> 3) & 0x01) );
    421 
    422     ret = pulseEnable(value); // value is ignored here for gpio
    423 
    424     return ret;
    425 }
    426 
    427 mraa::Result
    428 Lcm1602::expandWrite(uint8_t value)
    429 {
    430     // invalid for gpio
    431     if (!m_isI2C)
    432         return mraa::ERROR_INVALID_RESOURCE;
    433 
    434     uint8_t buffer = value | LCD_BACKLIGHT;
    435     return m_i2c_lcd_control->writeByte(buffer);
    436 }
    437 
    438 mraa::Result
    439 Lcm1602::pulseEnable(uint8_t value)
    440 {
    441     mraa::Result ret = mraa::SUCCESS;
    442 
    443     if (m_isI2C)
    444       {
    445         ret = expandWrite(value | LCD_EN);
    446         usleep(1);
    447         ret = expandWrite(value & ~LCD_EN);
    448         usleep(50);
    449         return ret;
    450       }
    451 
    452     // else gpio
    453 
    454     ret = m_gpioEnable->write(0);
    455     usleep(1);
    456     ret = m_gpioEnable->write(1);
    457     usleep(1); // must be > 450ns
    458     ret = m_gpioEnable->write(0);
    459     usleep(100); // must be >37us
    460 
    461     return ret;
    462 }
    463