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