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@intel.com>
8 * Copyright (c) 2014 Intel Corporation.
9 *
10 * Contributions: Jon Trulson <jtrulson@ics.com>
11 * Sergey Kiselev <sergey.kiselev@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
Lcm1602(int bus_in,int addr_in,bool isExpander,uint8_t numColumns,uint8_t numRows)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
Lcm1602(uint8_t rs,uint8_t enable,uint8_t d0,uint8_t d1,uint8_t d2,uint8_t d3,uint8_t numColumns,uint8_t numRows)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
~Lcm1602()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
write(std::string msg)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
setCursor(int row,int column)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
clear()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
home()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
createChar(uint8_t charSlot,uint8_t charData[])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
displayOn()289 mraa::Result Lcm1602::displayOn()
290 {
291 m_displayControl |= LCD_DISPLAYON;
292 return command(LCD_DISPLAYCONTROL | m_displayControl);
293 }
294
displayOff()295 mraa::Result Lcm1602::displayOff()
296 {
297 m_displayControl &= ~LCD_DISPLAYON;
298 return command(LCD_DISPLAYCONTROL | m_displayControl);
299 }
300
cursorOn()301 mraa::Result Lcm1602::cursorOn()
302 {
303 m_displayControl |= LCD_CURSORON;
304 return command(LCD_DISPLAYCONTROL | m_displayControl);
305 }
306
cursorOff()307 mraa::Result Lcm1602::cursorOff()
308 {
309 m_displayControl &= ~LCD_CURSORON;
310 return command(LCD_DISPLAYCONTROL | m_displayControl);
311 }
312
cursorBlinkOn()313 mraa::Result Lcm1602::cursorBlinkOn()
314 {
315 m_displayControl |= LCD_BLINKON;
316 return command(LCD_DISPLAYCONTROL | m_displayControl);
317 }
318
cursorBlinkOff()319 mraa::Result Lcm1602::cursorBlinkOff()
320 {
321 m_displayControl &= ~LCD_BLINKON;
322 return command(LCD_DISPLAYCONTROL | m_displayControl);
323 }
324
scrollDisplayLeft()325 mraa::Result Lcm1602::scrollDisplayLeft()
326 {
327 return command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT);
328 }
329
scrollDisplayRight()330 mraa::Result Lcm1602::scrollDisplayRight()
331 {
332 return command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT);
333 }
334
entryLeftToRight()335 mraa::Result Lcm1602::entryLeftToRight()
336 {
337 m_entryDisplayMode |= LCD_ENTRYLEFT;
338 return command(LCD_ENTRYMODESET | m_entryDisplayMode);
339 }
340
entryRightToLeft()341 mraa::Result Lcm1602::entryRightToLeft()
342 {
343 m_entryDisplayMode &= ~LCD_ENTRYLEFT;
344 return command(LCD_ENTRYMODESET | m_entryDisplayMode);
345 }
346
autoscrollOn()347 mraa::Result Lcm1602::autoscrollOn()
348 {
349 m_entryDisplayMode |= LCD_ENTRYSHIFTINCREMENT;
350 return command(LCD_ENTRYMODESET | m_entryDisplayMode);
351 }
352
autoscrollOff()353 mraa::Result Lcm1602::autoscrollOff()
354 {
355 m_entryDisplayMode &= ~LCD_ENTRYSHIFTINCREMENT;
356 return command(LCD_ENTRYMODESET | m_entryDisplayMode);
357 }
358
command(uint8_t cmd)359 mraa::Result Lcm1602::command(uint8_t cmd)
360 {
361 return send(cmd, 0);
362 }
363
data(uint8_t cmd)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
send(uint8_t value,int mode)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
write4bits(uint8_t value)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
expandWrite(uint8_t value)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
pulseEnable(uint8_t value)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