Gordons Projects

--> Projects Top-Level GIT

f123db28af813c6ebedf947b72c3ab14aaf04004
[wiringPi] / wiringPi / 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 <stdint.h>
30 #include <stdarg.h>
31
32 #include "wiringPi.h"
33 #include "lcd.h"
34
35 // Commands
36
37 #define LCD_CLEAR       0x01
38 #define LCD_HOME        0x02
39 #define LCD_ENTRY       0x04
40 #define LCD_ON_OFF      0x08
41 #define LCD_CDSHIFT     0x10
42 #define LCD_FUNC        0x20
43 #define LCD_CGRAM       0x40
44 #define LCD_DGRAM       0x80
45
46 #define LCD_ENTRY_SH    0x01
47 #define LCD_ENTRY_ID    0x02
48
49 #define LCD_ON_OFF_B    0x01
50 #define LCD_ON_OFF_C    0x02
51 #define LCD_ON_OFF_D    0x04
52
53 #define LCD_FUNC_F      0x04
54 #define LCD_FUNC_N      0x08
55 #define LCD_FUNC_DL     0x10
56
57 #define LCD_CDSHIFT_RL  0x04
58
59 struct lcdDataStruct
60 {
61   uint8_t bits, rows, cols ;
62   uint8_t rsPin, strbPin ;
63   uint8_t dataPins [8] ;
64 } ;
65
66 struct lcdDataStruct *lcds [MAX_LCDS] ;
67
68
69 /*
70  * strobe:
71  *      Toggle the strobe (Really the "E") pin to the device.
72  *      According to the docs, data is latched on the falling edge.
73  *********************************************************************************
74  */
75
76 static void strobe (struct lcdDataStruct *lcd)
77 {
78
79 // Note timing changes for new version of delayMicroseconds ()
80
81   digitalWrite (lcd->strbPin, 1) ; delayMicroseconds (50) ;
82   digitalWrite (lcd->strbPin, 0) ; delayMicroseconds (50) ;
83 }
84
85
86 /*
87  * sentDataCmd:
88  *      Send an data or command byte to the display.
89  *********************************************************************************
90  */
91
92 static void sendDataCmd (struct lcdDataStruct *lcd, uint8_t data)
93 {
94   uint8_t i, d4 ;
95
96   if (lcd->bits == 4)
97   {
98     d4 = (data >> 4) & 0x0F;
99     for (i = 0 ; i < 4 ; ++i)
100     {
101       digitalWrite (lcd->dataPins [i], (d4 & 1)) ;
102       d4 >>= 1 ;
103     }
104     strobe (lcd) ;
105
106     d4 = data & 0x0F ;
107     for (i = 0 ; i < 4 ; ++i)
108     {
109       digitalWrite (lcd->dataPins [i], (d4 & 1)) ;
110       d4 >>= 1 ;
111     }
112   }
113   else
114   {
115     for (i = 0 ; i < 8 ; ++i)
116     {
117       digitalWrite (lcd->dataPins [i], (data & 1)) ;
118       data >>= 1 ;
119     }
120   }
121   strobe (lcd) ;
122 }
123
124
125 /*
126  * putCommand:
127  *      Send a command byte to the display
128  *********************************************************************************
129  */
130
131 static void putCommand (struct lcdDataStruct *lcd, uint8_t command)
132 {
133   digitalWrite (lcd->rsPin,   0) ;
134   sendDataCmd  (lcd, command) ;
135 }
136
137 static void put4Command (struct lcdDataStruct *lcd, uint8_t command)
138 {
139   uint8_t i ;
140
141   digitalWrite (lcd->rsPin,   0) ;
142
143   for (i = 0 ; i < 4 ; ++i)
144   {
145     digitalWrite (lcd->dataPins [i], (command & 1)) ;
146     command >>= 1 ;
147   }
148   strobe (lcd) ;
149 }
150
151
152 /*
153  *********************************************************************************
154  * User Code below here
155  *********************************************************************************
156  */
157
158 /*
159  * lcdHome: lcdClear:
160  *      Home the cursor or clear the screen.
161  *********************************************************************************
162  */
163
164 void lcdHome (int fd)
165 {
166   struct lcdDataStruct *lcd = lcds [fd] ;
167   putCommand (lcd, LCD_HOME) ;
168 }
169
170 void lcdClear (int fd)
171 {
172   struct lcdDataStruct *lcd = lcds [fd] ;
173   putCommand (lcd, LCD_CLEAR) ;
174 }
175
176
177 /*
178  * lcdSendCommand:
179  *      Send any arbitary command to the display
180  *********************************************************************************
181  */
182
183 void lcdSendCommand (int fd, uint8_t command)
184 {
185   struct lcdDataStruct *lcd = lcds [fd] ;
186   putCommand (lcd, command) ;
187 }
188
189 /*
190  * lcdPosition:
191  *      Update the position of the cursor on the display
192  *********************************************************************************
193  */
194
195
196 void lcdPosition (int fd, int x, int y)
197 {
198   static uint8_t rowOff [4] = { 0x00, 0x40, 0x14, 0x54 } ;
199   struct lcdDataStruct *lcd = lcds [fd] ;
200
201   putCommand (lcd, x + (LCD_DGRAM | rowOff [y])) ;
202 }
203
204
205 /*
206  * lcdPutchar:
207  *      Send a data byte to be displayed on the display
208  *********************************************************************************
209  */
210
211 void lcdPutchar (int fd, uint8_t data)
212 {
213   struct lcdDataStruct *lcd = lcds [fd] ;
214
215   digitalWrite (lcd->rsPin, 1) ;
216   sendDataCmd (lcd, data) ;
217 }
218
219
220 /*
221  * lcdPuts:
222  *      Send a string to be displayed on the display
223  *********************************************************************************
224  */
225
226 void lcdPuts (int fd, char *string)
227 {
228   while (*string)
229     lcdPutchar (fd, *string++) ;
230 }
231
232
233 /*
234  * lcdPrintf:
235  *      Printf to an LCD display
236  *********************************************************************************
237  */
238
239 void lcdPrintf (int fd, char *message, ...)
240 {
241   va_list argp ;
242   char buffer [1024] ;
243
244   va_start (argp, message) ;
245     vsnprintf (buffer, 1023, message, argp) ;
246   va_end (argp) ;
247
248   lcdPuts (fd, buffer) ;
249 }
250
251
252 /*
253  * lcdInit:
254  *      Take a lot of parameters and initialise the LCD, and return a handle to
255  *      that LCD, or -1 if any error.
256  *********************************************************************************
257  */
258
259 int lcdInit (int rows, int cols, int bits, int rs, int strb,
260         int d0, int d1, int d2, int d3, int d4, int d5, int d6, int d7)
261 {
262   static int initialised = 0 ;
263
264   uint8_t func ;
265   int i ;
266   int lcdFd = -1 ;
267   struct lcdDataStruct *lcd ;
268
269   if (initialised == 0)
270   {
271     initialised = 1 ;
272     for (i = 0 ; i < MAX_LCDS ; ++i)
273       lcds [i] = NULL ;
274   }
275
276 // Simple sanity checks
277
278   if (! ((bits == 4) || (bits == 8)))
279     return -1 ;
280
281   if ((rows < 0) || (rows > 20))
282     return -1 ;
283
284   if ((cols < 0) || (cols > 20))
285     return -1 ;
286
287 // Create a new LCD:
288
289   for (i = 0 ; i < MAX_LCDS ; ++i)
290   {
291     if (lcds [i] == NULL)
292     {
293       lcdFd = i ;
294       break ;
295     }
296   }
297
298   if (lcdFd == -1)
299     return -1 ;
300
301   lcd = malloc (sizeof (struct lcdDataStruct)) ;
302   if (lcd == NULL)
303     return -1 ;
304
305   lcd->rsPin   = rs ;
306   lcd->strbPin = strb ;
307   lcd->bits    = 8 ;            // For now - we'll set it properly later.
308   lcd->rows    = rows ;
309   lcd->cols    = cols ;
310
311   lcd->dataPins [0] = d0 ;
312   lcd->dataPins [1] = d1 ;
313   lcd->dataPins [2] = d2 ;
314   lcd->dataPins [3] = d3 ;
315   lcd->dataPins [4] = d4 ;
316   lcd->dataPins [5] = d5 ;
317   lcd->dataPins [6] = d6 ;
318   lcd->dataPins [7] = d7 ;
319
320   lcds [lcdFd] = lcd ;
321
322   digitalWrite (lcd->rsPin,   0) ; pinMode (lcd->rsPin,   OUTPUT) ;
323   digitalWrite (lcd->strbPin, 0) ; pinMode (lcd->strbPin, OUTPUT) ;
324
325   for (i = 0 ; i < bits ; ++i)
326   {
327     digitalWrite (lcd->dataPins [i], 0) ;
328     pinMode      (lcd->dataPins [i], OUTPUT) ;
329   }
330   delay (35) ; // mS
331
332
333 // 4-bit mode?
334 //      OK. This is a PIG and it's not at all obvious from the documentation I had,
335 //      so I guess some others have worked through either with better documentation
336 //      or more trial and error... Anyway here goes:
337 //
338 //      It seems that the controller needs to see the FUNC command at least 3 times
339 //      consecutively - in 8-bit mode. If you're only using 8-bit mode, then it appears
340 //      that you can get away with one func-set, however I'd not rely on it...
341 //
342 //      So to set 4-bit mode, you need to send the commands one nibble at a time,
343 //      the same three times, but send the command to set it into 8-bit mode those
344 //      three times, then send a final 4th command to set it into 4-bit mode, and only
345 //      then can you flip the switch for the rest of the library to work in 4-bit
346 //      mode which sends the commands as 2 x 4-bit values.
347
348   if (bits == 4)
349   {
350     func = LCD_FUNC | LCD_FUNC_DL ;                     // Set 8-bit mode 3 times
351     put4Command (lcd, func >> 4) ; delay (35) ;
352     put4Command (lcd, func >> 4) ; delay (35) ;
353     put4Command (lcd, func >> 4) ; delay (35) ;
354     func = LCD_FUNC ;                                   // 4th set: 4-bit mode
355     put4Command (lcd, func >> 4) ; delay (35) ;
356     lcd->bits = 4 ;
357   }
358   else
359   {
360     func = LCD_FUNC | LCD_FUNC_DL ;
361     putCommand  (lcd, func     ) ; delay (35) ;
362     putCommand  (lcd, func     ) ; delay (35) ;
363     putCommand  (lcd, func     ) ; delay (35) ;
364   }
365
366   if (lcd->rows > 1)
367   {
368     func |= LCD_FUNC_N ;
369     putCommand (lcd, func) ; delay (35) ;
370   }
371
372 // Rest of the initialisation sequence
373
374   putCommand (lcd, LCD_ON_OFF  | LCD_ON_OFF_D) ;   delay (2) ;
375   putCommand (lcd, LCD_ENTRY   | LCD_ENTRY_ID) ;   delay (2) ;
376   putCommand (lcd, LCD_CDSHIFT | LCD_CDSHIFT_RL) ; delay (2) ;
377   putCommand (lcd, LCD_CLEAR) ;                    delay (5) ;
378
379   return lcdFd ;
380 }