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