blob: 6c0e474948fded0be758e0cb8cb227afdf120189 [file] [log] [blame]
Deokgyu Yang3f787a12020-03-30 15:25:23 +09001/*
2 * lcd.c:
3 * Text-based LCD driver.
4 * This is designed to drive the parallel interface LCD drivers
5 * based in the Hitachi HD44780U controller and compatables.
6 *
7 * Copyright (c) 2012 Gordon Henderson.
8 ***********************************************************************
9 * This file is part of wiringPi:
10 * https://projects.drogon.net/raspberry-pi/wiringpi/
11 *
12 * wiringPi is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License as published by
14 * the Free Software Foundation, either version 3 of the License, or
15 * (at your option) any later version.
16 *
17 * wiringPi is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Lesser General Public License for more details.
21 *
22 * You should have received a copy of the GNU Lesser General Public License
23 * along with wiringPi. If not, see <http://www.gnu.org/licenses/>.
24 ***********************************************************************
25 */
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <stdarg.h>
30
31#include <wiringPi.h>
32
33#include "lcd.h"
34
35#ifndef TRUE
36# define TRUE (1==1)
37# define FALSE (1==2)
38#endif
39
40// HD44780U Commands
41
42#define LCD_CLEAR 0x01
43#define LCD_HOME 0x02
44#define LCD_ENTRY 0x04
45#define LCD_CTRL 0x08
46#define LCD_CDSHIFT 0x10
47#define LCD_FUNC 0x20
48#define LCD_CGRAM 0x40
49#define LCD_DGRAM 0x80
50
51// Bits in the entry register
52
53#define LCD_ENTRY_SH 0x01
54#define LCD_ENTRY_ID 0x02
55
56// Bits in the control register
57
58#define LCD_BLINK_CTRL 0x01
59#define LCD_CURSOR_CTRL 0x02
60#define LCD_DISPLAY_CTRL 0x04
61
62// Bits in the function register
63
64#define LCD_FUNC_F 0x04
65#define LCD_FUNC_N 0x08
66#define LCD_FUNC_DL 0x10
67
68#define LCD_CDSHIFT_RL 0x04
69
70struct lcdDataStruct
71{
72 int bits, rows, cols ;
73 int rsPin, strbPin ;
74 int dataPins [8] ;
75 int cx, cy ;
76} ;
77
78struct lcdDataStruct *lcds [MAX_LCDS] ;
79
80static int lcdControl ;
81
82// Row offsets
83
84static const int rowOff [4] = { 0x00, 0x40, 0x14, 0x54 } ;
85
86
87/*
88 * strobe:
89 * Toggle the strobe (Really the "E") pin to the device.
90 * According to the docs, data is latched on the falling edge.
91 *********************************************************************************
92 */
93
94static void strobe (const struct lcdDataStruct *lcd)
95{
96
97// Note timing changes for new version of delayMicroseconds ()
98
99 digitalWrite (lcd->strbPin, 1) ; delayMicroseconds (50) ;
100 digitalWrite (lcd->strbPin, 0) ; delayMicroseconds (50) ;
101}
102
103
104/*
105 * sentDataCmd:
106 * Send an data or command byte to the display.
107 *********************************************************************************
108 */
109
110static void sendDataCmd (const struct lcdDataStruct *lcd, unsigned char data)
111{
112 register unsigned char myData = data ;
113 unsigned char i, d4 ;
114
115 if (lcd->bits == 4)
116 {
117 d4 = (myData >> 4) & 0x0F;
118 for (i = 0 ; i < 4 ; ++i)
119 {
120 digitalWrite (lcd->dataPins [i], (d4 & 1)) ;
121 d4 >>= 1 ;
122 }
123 strobe (lcd) ;
124
125 d4 = myData & 0x0F ;
126 for (i = 0 ; i < 4 ; ++i)
127 {
128 digitalWrite (lcd->dataPins [i], (d4 & 1)) ;
129 d4 >>= 1 ;
130 }
131 }
132 else
133 {
134 for (i = 0 ; i < 8 ; ++i)
135 {
136 digitalWrite (lcd->dataPins [i], (myData & 1)) ;
137 myData >>= 1 ;
138 }
139 }
140 strobe (lcd) ;
141}
142
143
144/*
145 * putCommand:
146 * Send a command byte to the display
147 *********************************************************************************
148 */
149
150static void putCommand (const struct lcdDataStruct *lcd, unsigned char command)
151{
152 digitalWrite (lcd->rsPin, 0) ;
153 sendDataCmd (lcd, command) ;
154 delay (2) ;
155}
156
157static void put4Command (const struct lcdDataStruct *lcd, unsigned char command)
158{
159 register unsigned char myCommand = command ;
160 register unsigned char i ;
161
162 digitalWrite (lcd->rsPin, 0) ;
163
164 for (i = 0 ; i < 4 ; ++i)
165 {
166 digitalWrite (lcd->dataPins [i], (myCommand & 1)) ;
167 myCommand >>= 1 ;
168 }
169 strobe (lcd) ;
170}
171
172
173/*
174 *********************************************************************************
175 * User Callable code below here
176 *********************************************************************************
177 */
178
179/*
180 * lcdHome: lcdClear:
181 * Home the cursor or clear the screen.
182 *********************************************************************************
183 */
184
185void lcdHome (const int fd)
186{
187 struct lcdDataStruct *lcd = lcds [fd] ;
188
189 putCommand (lcd, LCD_HOME) ;
190 lcd->cx = lcd->cy = 0 ;
191 delay (5) ;
192}
193
194void lcdClear (const int fd)
195{
196 struct lcdDataStruct *lcd = lcds [fd] ;
197
198 putCommand (lcd, LCD_CLEAR) ;
199 putCommand (lcd, LCD_HOME) ;
200 lcd->cx = lcd->cy = 0 ;
201 delay (5) ;
202}
203
204
205/*
206 * lcdDisplay: lcdCursor: lcdCursorBlink:
207 * Turn the display, cursor, cursor blinking on/off
208 *********************************************************************************
209 */
210
211void lcdDisplay (const int fd, int state)
212{
213 struct lcdDataStruct *lcd = lcds [fd] ;
214
215 if (state)
216 lcdControl |= LCD_DISPLAY_CTRL ;
217 else
218 lcdControl &= ~LCD_DISPLAY_CTRL ;
219
220 putCommand (lcd, LCD_CTRL | lcdControl) ;
221}
222
223void lcdCursor (const int fd, int state)
224{
225 struct lcdDataStruct *lcd = lcds [fd] ;
226
227 if (state)
228 lcdControl |= LCD_CURSOR_CTRL ;
229 else
230 lcdControl &= ~LCD_CURSOR_CTRL ;
231
232 putCommand (lcd, LCD_CTRL | lcdControl) ;
233}
234
235void lcdCursorBlink (const int fd, int state)
236{
237 struct lcdDataStruct *lcd = lcds [fd] ;
238
239 if (state)
240 lcdControl |= LCD_BLINK_CTRL ;
241 else
242 lcdControl &= ~LCD_BLINK_CTRL ;
243
244 putCommand (lcd, LCD_CTRL | lcdControl) ;
245}
246
247
248/*
249 * lcdSendCommand:
250 * Send any arbitary command to the display
251 *********************************************************************************
252 */
253
254void lcdSendCommand (const int fd, unsigned char command)
255{
256 struct lcdDataStruct *lcd = lcds [fd] ;
257 putCommand (lcd, command) ;
258}
259
260
261/*
262 * lcdPosition:
263 * Update the position of the cursor on the display.
264 * Ignore invalid locations.
265 *********************************************************************************
266 */
267
268void lcdPosition (const int fd, int x, int y)
269{
270 struct lcdDataStruct *lcd = lcds [fd] ;
271
272 if ((x > lcd->cols) || (x < 0))
273 return ;
274 if ((y > lcd->rows) || (y < 0))
275 return ;
276
277 putCommand (lcd, x + (LCD_DGRAM | rowOff [y])) ;
278
279 lcd->cx = x ;
280 lcd->cy = y ;
281}
282
283
284/*
285 * lcdCharDef:
286 * Defines a new character in the CGRAM
287 *********************************************************************************
288 */
289
290void lcdCharDef (const int fd, int index, unsigned char data [8])
291{
292 struct lcdDataStruct *lcd = lcds [fd] ;
293 int i ;
294
295 putCommand (lcd, LCD_CGRAM | ((index & 7) << 3)) ;
296
297 digitalWrite (lcd->rsPin, 1) ;
298 for (i = 0 ; i < 8 ; ++i)
299 sendDataCmd (lcd, data [i]) ;
300}
301
302
303/*
304 * lcdPutchar:
305 * Send a data byte to be displayed on the display. We implement a very
306 * simple terminal here - with line wrapping, but no scrolling. Yet.
307 *********************************************************************************
308 */
309
310void lcdPutchar (const int fd, unsigned char data)
311{
312 struct lcdDataStruct *lcd = lcds [fd] ;
313
314 digitalWrite (lcd->rsPin, 1) ;
315 sendDataCmd (lcd, data) ;
316
317 if (++lcd->cx == lcd->cols)
318 {
319 lcd->cx = 0 ;
320 if (++lcd->cy == lcd->rows)
321 lcd->cy = 0 ;
322
323 putCommand (lcd, lcd->cx + (LCD_DGRAM | rowOff [lcd->cy])) ;
324 }
325}
326
327
328/*
329 * lcdPuts:
330 * Send a string to be displayed on the display
331 *********************************************************************************
332 */
333
334void lcdPuts (const int fd, const char *string)
335{
336 while (*string)
337 lcdPutchar (fd, *string++) ;
338}
339
340
341/*
342 * lcdPrintf:
343 * Printf to an LCD display
344 *********************************************************************************
345 */
346
347void lcdPrintf (const int fd, const char *message, ...)
348{
349 va_list argp ;
350 char buffer [1024] ;
351
352 va_start (argp, message) ;
353 vsnprintf (buffer, 1023, message, argp) ;
354 va_end (argp) ;
355
356 lcdPuts (fd, buffer) ;
357}
358
359
360/*
361 * lcdInit:
362 * Take a lot of parameters and initialise the LCD, and return a handle to
363 * that LCD, or -1 if any error.
364 *********************************************************************************
365 */
366
367int lcdInit (const int rows, const int cols, const int bits,
368 const int rs, const int strb,
369 const int d0, const int d1, const int d2, const int d3, const int d4,
370 const int d5, const int d6, const int d7)
371{
372 static int initialised = 0 ;
373
374 unsigned char func ;
375 int i ;
376 int lcdFd = -1 ;
377 struct lcdDataStruct *lcd ;
378
379 if (initialised == 0)
380 {
381 initialised = 1 ;
382 for (i = 0 ; i < MAX_LCDS ; ++i)
383 lcds [i] = NULL ;
384 }
385
386// Simple sanity checks
387
388 if (! ((bits == 4) || (bits == 8)))
389 return -1 ;
390
391 if ((rows < 0) || (rows > 20))
392 return -1 ;
393
394 if ((cols < 0) || (cols > 20))
395 return -1 ;
396
397// Create a new LCD:
398
399 for (i = 0 ; i < MAX_LCDS ; ++i)
400 {
401 if (lcds [i] == NULL)
402 {
403 lcdFd = i ;
404 break ;
405 }
406 }
407
408 if (lcdFd == -1)
409 return -1 ;
410
411 lcd = (struct lcdDataStruct *)malloc (sizeof (struct lcdDataStruct)) ;
412 if (lcd == NULL)
413 return -1 ;
414
415 lcd->rsPin = rs ;
416 lcd->strbPin = strb ;
417 lcd->bits = 8 ; // For now - we'll set it properly later.
418 lcd->rows = rows ;
419 lcd->cols = cols ;
420 lcd->cx = 0 ;
421 lcd->cy = 0 ;
422
423 lcd->dataPins [0] = d0 ;
424 lcd->dataPins [1] = d1 ;
425 lcd->dataPins [2] = d2 ;
426 lcd->dataPins [3] = d3 ;
427 lcd->dataPins [4] = d4 ;
428 lcd->dataPins [5] = d5 ;
429 lcd->dataPins [6] = d6 ;
430 lcd->dataPins [7] = d7 ;
431
432 lcds [lcdFd] = lcd ;
433
434 digitalWrite (lcd->rsPin, 0) ; pinMode (lcd->rsPin, OUTPUT) ;
435 digitalWrite (lcd->strbPin, 0) ; pinMode (lcd->strbPin, OUTPUT) ;
436
437 for (i = 0 ; i < bits ; ++i)
438 {
439 digitalWrite (lcd->dataPins [i], 0) ;
440 pinMode (lcd->dataPins [i], OUTPUT) ;
441 }
442 delay (35) ; // mS
443
444
445// 4-bit mode?
446// OK. This is a PIG and it's not at all obvious from the documentation I had,
447// so I guess some others have worked through either with better documentation
448// or more trial and error... Anyway here goes:
449//
450// It seems that the controller needs to see the FUNC command at least 3 times
451// consecutively - in 8-bit mode. If you're only using 8-bit mode, then it appears
452// that you can get away with one func-set, however I'd not rely on it...
453//
454// So to set 4-bit mode, you need to send the commands one nibble at a time,
455// the same three times, but send the command to set it into 8-bit mode those
456// three times, then send a final 4th command to set it into 4-bit mode, and only
457// then can you flip the switch for the rest of the library to work in 4-bit
458// mode which sends the commands as 2 x 4-bit values.
459
460 if (bits == 4)
461 {
462 func = LCD_FUNC | LCD_FUNC_DL ; // Set 8-bit mode 3 times
463 put4Command (lcd, func >> 4) ; delay (35) ;
464 put4Command (lcd, func >> 4) ; delay (35) ;
465 put4Command (lcd, func >> 4) ; delay (35) ;
466 func = LCD_FUNC ; // 4th set: 4-bit mode
467 put4Command (lcd, func >> 4) ; delay (35) ;
468 lcd->bits = 4 ;
469 }
470 else
471 {
472 func = LCD_FUNC | LCD_FUNC_DL ;
473 putCommand (lcd, func ) ; delay (35) ;
474 putCommand (lcd, func ) ; delay (35) ;
475 putCommand (lcd, func ) ; delay (35) ;
476 }
477
478 if (lcd->rows > 1)
479 {
480 func |= LCD_FUNC_N ;
481 putCommand (lcd, func) ; delay (35) ;
482 }
483
484// Rest of the initialisation sequence
485
486 lcdDisplay (lcdFd, TRUE) ;
487 lcdCursor (lcdFd, FALSE) ;
488 lcdCursorBlink (lcdFd, FALSE) ;
489 lcdClear (lcdFd) ;
490
491 putCommand (lcd, LCD_ENTRY | LCD_ENTRY_ID) ;
492 putCommand (lcd, LCD_CDSHIFT | LCD_CDSHIFT_RL) ;
493
494 return lcdFd ;
495}