Gordons Projects

--> Projects Top-Level GIT

tidied and tested DRC Serial (renamed it it drcSerial too)
[wiringPi] / devLib / lcd.c
1 /*
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
70 struct lcdDataStruct
71 {
72   int bits, rows, cols ;
73   int rsPin, strbPin ;
74   int dataPins [8] ;
75   int cx, cy ;
76 } ;
77
78 struct lcdDataStruct *lcds [MAX_LCDS] ;
79
80 static int lcdControl ;
81
82 // Row offsets
83
84 static 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
94 static 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
110 static 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
150 static void putCommand (const struct lcdDataStruct *lcd, unsigned char command)
151 {
152   digitalWrite (lcd->rsPin,   0) ;
153   sendDataCmd  (lcd, command) ;
154   delay (2) ;
155 }
156
157 static 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
185 void 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
194 void 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
211 void 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
223 void 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
235 void 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
254 void 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
268 void 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
290 void 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
310 void 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
334 void 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
347 void 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
367 int 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 }