chiark / gitweb /
Big update here.
[wiringPi.git] / 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  * lcdPosition:
179  *      Update the position of the cursor on the display
180  *********************************************************************************
181  */
182
183
184 void lcdPosition (int fd, int x, int y)
185 {
186   static uint8_t rowOff [4] = { 0x00, 0x40, 0x14, 0x54 } ;
187   struct lcdDataStruct *lcd = lcds [fd] ;
188
189   putCommand (lcd, x + (LCD_DGRAM | rowOff [y])) ;
190 }
191
192
193 /*
194  * lcdPutchar:
195  *      Send a data byte to be displayed on the display
196  *********************************************************************************
197  */
198
199 void lcdPutchar (int fd, uint8_t data)
200 {
201   struct lcdDataStruct *lcd = lcds [fd] ;
202
203   digitalWrite (lcd->rsPin, 1) ;
204   sendDataCmd (lcd, data) ;
205 }
206
207
208 /*
209  * lcdPuts:
210  *      Send a string to be displayed on the display
211  *********************************************************************************
212  */
213
214 void lcdPuts (int fd, char *string)
215 {
216   while (*string)
217     lcdPutchar (fd, *string++) ;
218 }
219
220
221 /*
222  * lcdPrintf:
223  *      Printf to an LCD display
224  *********************************************************************************
225  */
226
227 void lcdPrintf (int fd, char *message, ...)
228 {
229   va_list argp ;
230   char buffer [1024] ;
231
232   va_start (argp, message) ;
233     vsnprintf (buffer, 1023, message, argp) ;
234   va_end (argp) ;
235
236   lcdPuts (fd, buffer) ;
237 }
238
239
240 /*
241  * lcdInit:
242  *      Take a lot of parameters and initialise the LCD, and return a handle to
243  *      that LCD, or -1 if any error.
244  *********************************************************************************
245  */
246
247 int lcdInit (int rows, int cols, int bits, int rs, int strb,
248         int d0, int d1, int d2, int d3, int d4, int d5, int d6, int d7)
249 {
250   static int initialised = 0 ;
251
252   uint8_t func ;
253   int i ;
254   int lcdFd = -1 ;
255   struct lcdDataStruct *lcd ;
256
257   if (initialised == 0)
258   {
259     initialised = 1 ;
260     for (i = 0 ; i < MAX_LCDS ; ++i)
261       lcds [i] = NULL ;
262   }
263
264 // Simple sanity checks
265
266   if (! ((bits == 4) || (bits == 8)))
267     return -1 ;
268
269   if ((rows < 0) || (rows > 20))
270     return -1 ;
271
272   if ((cols < 0) || (cols > 20))
273     return -1 ;
274
275 // Create a new LCD:
276
277   for (i = 0 ; i < MAX_LCDS ; ++i)
278   {
279     if (lcds [i] == NULL)
280     {
281       lcdFd = i ;
282       break ;
283     }
284   }
285
286   if (lcdFd == -1)
287     return -1 ;
288
289   lcd = malloc (sizeof (struct lcdDataStruct)) ;
290   if (lcd == NULL)
291     return -1 ;
292
293   lcd->rsPin   = rs ;
294   lcd->strbPin = strb ;
295   lcd->bits    = 8 ;            // For now - we'll set it properly later.
296   lcd->rows    = rows ;
297   lcd->cols    = cols ;
298
299   lcd->dataPins [0] = d0 ;
300   lcd->dataPins [1] = d1 ;
301   lcd->dataPins [2] = d2 ;
302   lcd->dataPins [3] = d3 ;
303   lcd->dataPins [4] = d4 ;
304   lcd->dataPins [5] = d5 ;
305   lcd->dataPins [6] = d6 ;
306   lcd->dataPins [7] = d7 ;
307
308   lcds [lcdFd] = lcd ;
309
310   digitalWrite (lcd->rsPin,   0) ; pinMode (lcd->rsPin,   OUTPUT) ;
311   digitalWrite (lcd->strbPin, 0) ; pinMode (lcd->strbPin, OUTPUT) ;
312
313   for (i = 0 ; i < bits ; ++i)
314   {
315     digitalWrite (lcd->dataPins [i], 0) ;
316     pinMode      (lcd->dataPins [i], OUTPUT) ;
317   }
318   delay (35) ; // mS
319
320
321 // 4-bit mode?
322 //      OK. This is a PIG and it's not at all obvious from the documentation I had,
323 //      so I guess some others have worked through either with better documentation
324 //      or more trial and error... Anyway here goes:
325 //
326 //      It seems that the controller needs to see the FUNC command at least 3 times
327 //      consecutively - in 8-bit mode. If you're only using 8-bit mode, then it appears
328 //      that you can get away with one func-set, however I'd not rely on it...
329 //
330 //      So to set 4-bit mode, you need to send the commands one nibble at a time,
331 //      the same three times, but send the command to set it into 8-bit mode those
332 //      three times, then send a final 4th command to set it into 4-bit mode, and only
333 //      then can you flip the switch for the rest of the library to work in 4-bit
334 //      mode which sends the commands as 2 x 4-bit values.
335
336   if (bits == 4)
337   {
338     func = LCD_FUNC | LCD_FUNC_DL ;                     // Set 8-bit mode 3 times
339     put4Command (lcd, func >> 4) ; delay (35) ;
340     put4Command (lcd, func >> 4) ; delay (35) ;
341     put4Command (lcd, func >> 4) ; delay (35) ;
342     func = LCD_FUNC ;                                   // 4th set: 4-bit mode
343     put4Command (lcd, func >> 4) ; delay (35) ;
344     lcd->bits = 4 ;
345   }
346   else
347   {
348     func = LCD_FUNC | LCD_FUNC_DL ;
349     putCommand  (lcd, func     ) ; delay (35) ;
350     putCommand  (lcd, func     ) ; delay (35) ;
351     putCommand  (lcd, func     ) ; delay (35) ;
352   }
353
354   if (lcd->rows > 1)
355   {
356     func |= LCD_FUNC_N ;
357     putCommand (lcd, func) ; delay (35) ;
358   }
359
360 // Rest of the initialisation sequence
361
362   putCommand (lcd, LCD_ON_OFF  | LCD_ON_OFF_D) ;   delay (2) ;
363   putCommand (lcd, LCD_ENTRY   | LCD_ENTRY_ID) ;   delay (2) ;
364   putCommand (lcd, LCD_CDSHIFT | LCD_CDSHIFT_RL) ; delay (2) ;
365   putCommand (lcd, LCD_CLEAR) ;                    delay (5) ;
366
367   return lcdFd ;
368 }