chiark / gitweb /
wiringPi Version 2 - First commit (of v2)
[wiringPi.git] / devLib / lcd128x64.c
1 /*
2  * lcd128x64.c:
3  *      Graphics-based LCD driver.
4  *      This is designed to drive the parallel interface LCD drivers
5  *      based on the generic 12864H chips
6  *
7  *      There are many variations on these chips, however they all mostly
8  *      seem to be similar.
9  *      This implementation has the Pins from the Pi hard-wired into it,
10  *      in particular wiringPi pins 0-7 so that we can use
11  *      digitalWriteByete() to speed things up somewhat.
12  *
13  * Copyright (c) 2013 Gordon Henderson.
14  ***********************************************************************
15  * This file is part of wiringPi:
16  *      https://projects.drogon.net/raspberry-pi/wiringpi/
17  *
18  *    wiringPi is free software: you can redistribute it and/or modify
19  *    it under the terms of the GNU Lesser General Public License as published by
20  *    the Free Software Foundation, either version 3 of the License, or
21  *    (at your option) any later version.
22  *
23  *    wiringPi is distributed in the hope that it will be useful,
24  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
25  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26  *    GNU Lesser General Public License for more details.
27  *
28  *    You should have received a copy of the GNU Lesser General Public License
29  *    along with wiringPi.  If not, see <http://www.gnu.org/licenses/>.
30  ***********************************************************************
31  */
32
33 #include <stdio.h>
34 #include <stdlib.h>
35
36 #include <wiringPi.h>
37
38 #include "font.h"
39 #include "lcd128x64.h"
40
41 // Size
42
43 #define LCD_WIDTH       128
44 #define LCD_HEIGHT       64
45
46 // Hardware Pins
47 //      Note pins 0-7 are the 8-bit data port
48
49 #define CS1             10
50 #define CS2             11
51 #define STROBE          12
52 #define RS              13
53
54 // Software copy of the framebuffer
55 //      it's 8-bit deep although the display itself is only 1-bit deep.
56
57 static unsigned char frameBuffer [LCD_WIDTH * LCD_HEIGHT] ;
58
59 static int maxX,    maxY ;
60 static int lastX,   lastY ;
61 static int xOrigin, yOrigin ;
62 static int lcdOrientation = 0 ;
63
64 /*
65  * strobe:
66  *      Toggle the strobe (Really the "E") pin to the device.
67  *      According to the docs, data is latched on the falling edge.
68  *********************************************************************************
69  */
70
71 static void strobe (void)
72 {
73   digitalWrite (STROBE, 1) ; delayMicroseconds (1) ;
74   digitalWrite (STROBE, 0) ; delayMicroseconds (5) ;
75 }
76
77
78 /*
79  * sentData:
80  *      Send an data or command byte to the display.
81  *********************************************************************************
82  */
83
84 static void sendData (const int data, const int chip)
85 {
86   digitalWrite     (chip, 0) ;
87   digitalWriteByte (data) ;
88   strobe           () ;
89   digitalWrite     (chip, 1) ;
90 }
91
92
93 /*
94  * sendCommand:
95  *      Send a command byte to the display
96  *********************************************************************************
97  */
98
99 static void sendCommand (const int command, const int chip)
100 {
101   digitalWrite (RS, 0) ;
102   sendData     (command, chip) ;
103   digitalWrite (RS, 1) ;
104 }
105
106
107 /*
108  * setCol: SetLine:
109  *      Set the column and line addresses
110  *********************************************************************************
111  */
112
113 static void setCol  (int col, const int chip)
114   { sendCommand (0x40 | (col  & 0x3F), chip) ; }
115
116 static void setLine (int line, const int chip)
117   { sendCommand (0xB8 | (line & 0x07), chip) ; }
118
119
120 /*
121  * lcd128x64update:
122  *      Copy our software version to the real display
123  *********************************************************************************
124  */
125
126 void lcd128x64update (void)
127 {
128   int line, x, y, fbLoc ;
129   unsigned char byte ;
130
131 // Left side 
132
133   for (line = 0 ; line < 8 ; ++line)
134   {
135     setCol  (0,    CS1) ;
136     setLine (line, CS1) ;
137
138     for (x = 63 ; x >= 0 ; --x)
139     {
140       byte = 0 ;
141       for (y = 0 ; y < 8 ; ++y)
142       {
143         fbLoc = x + (((7 - line) * 8) + (7 - y)) * LCD_WIDTH ;
144         if (frameBuffer [fbLoc] != 0)
145           byte |= (1 << y) ;
146       }
147       sendData (byte, CS1) ;
148     }
149   }
150
151 // Right side 
152
153   for (line = 0 ; line < 8 ; ++line)
154   {
155     setCol  (0,    CS2) ;
156     setLine (line, CS2) ;
157
158     for (x = 127 ; x >= 64 ; --x)
159     {
160       byte = 0 ;
161       for (y = 0 ; y < 8 ; ++y)
162       {
163         fbLoc = x + (((7 - line) * 8) + (7 - y)) * LCD_WIDTH ;
164         if (frameBuffer [fbLoc] != 0)
165           byte |= (1 << y) ;
166       }
167       sendData (byte, CS2) ;
168     }
169   }
170 }
171
172
173 /*
174  * lcd128x64setOrigin:
175  *      Set the display offset origin
176  *********************************************************************************
177  */
178
179 void lcd128x64setOrigin (int x, int y)
180 {
181   xOrigin = x ;
182   yOrigin = y ;
183 }
184
185
186 /*
187  * lcd128x64setOrientation:
188  *      Set the display orientation:
189  *      0: Normal, the display is portrait mode, 0,0 is top left
190  *      1: Landscape
191  *      2: Portrait, flipped
192  *      3: Landscape, flipped
193  *********************************************************************************
194  */
195
196 void lcd128x64setOrientation (int orientation)
197 {
198   lcdOrientation = orientation & 3 ;
199
200   lcd128x64setOrigin (0,0) ;
201
202   switch (lcdOrientation)
203   {
204     case 0:
205       maxX = LCD_WIDTH ;
206       maxY = LCD_HEIGHT ;
207       break ;
208
209     case 1:
210       maxX = LCD_HEIGHT ;
211       maxY = LCD_WIDTH ;
212       break ;
213
214     case 2:
215       maxX = LCD_WIDTH ;
216       maxY = LCD_HEIGHT ;
217       break ;
218
219     case 3:
220       maxX = LCD_HEIGHT ;
221       maxY = LCD_WIDTH ;
222       break ;
223   }
224 }
225
226
227 /*
228  * lcd128x64orientCoordinates:
229  *      Adjust the coordinates given to the display orientation
230  *********************************************************************************
231  */
232
233 void lcd128x64orientCoordinates (int *x, int *y)
234 {
235   register int tmp ;
236
237   *x += xOrigin ;
238   *y += yOrigin ;
239   *y  = maxY - *y - 1 ;
240
241   switch (lcdOrientation)
242   {
243     case 0:
244       break;
245
246     case 1:
247       tmp = maxY - *y - 1 ;
248       *y = *x ;
249       *x = tmp ;
250       break;
251
252     case 2:
253       *x = maxX - *x - 1 ;
254       *y = maxY - *y - 1 ;
255       break;
256
257     case 3:
258       *x = maxX - *x - 1 ;
259       tmp = *y ;
260       *y = *x ;
261       *x = tmp ;
262       break ;
263   }
264 }
265
266
267 /*
268  * lcd128x64getScreenSize:
269  *      Return the max X & Y screen sizes. Needs to be called again, if you 
270  *      change screen orientation.
271  *********************************************************************************
272  */
273
274 void lcd128x64getScreenSize (int *x, int *y)
275 {
276   *x = maxX ;
277   *y = maxY ;
278 }
279
280
281 /*
282  *********************************************************************************
283  * Standard Graphical Functions
284  *********************************************************************************
285  */
286
287
288 /*
289  * lcd128x64point:
290  *      Plot a pixel.
291  *********************************************************************************
292  */
293
294 void lcd128x64point (int x, int y, int colour)
295 {
296   lastX = x ;
297   lastY = y ;
298
299   lcd128x64orientCoordinates (&x, &y) ;
300
301   if ((x < 0) || (x >= LCD_WIDTH) || (y < 0) || (y >= LCD_HEIGHT))
302     return ;
303
304   frameBuffer [x + y * LCD_WIDTH] = colour ;
305 }
306
307
308 /*
309  * lcd128x64line: lcd128x64lineTo:
310  *      Classic Bressenham Line code
311  *********************************************************************************
312  */
313
314 void lcd128x64line (int x0, int y0, int x1, int y1, int colour)
315 {
316   int dx, dy ;
317   int sx, sy ;
318   int err, e2 ;
319
320   lastX = x1 ;
321   lastY = y1 ;
322
323   dx = abs (x1 - x0) ;
324   dy = abs (y1 - y0) ;
325
326   sx = (x0 < x1) ? 1 : -1 ;
327   sy = (y0 < y1) ? 1 : -1 ;
328
329   err = dx - dy ;
330  
331   for (;;)
332   {
333     lcd128x64point (x0, y0, colour) ;
334
335     if ((x0 == x1) && (y0 == y1))
336       break ;
337
338     e2 = 2 * err ;
339
340     if (e2 > -dy)
341     {
342       err -= dy ;
343       x0  += sx ;
344     }
345
346     if (e2 < dx)
347     {
348       err += dx ;
349       y0  += sy ;
350     }
351   }
352
353 }
354
355 void lcd128x64lineTo (int x, int y, int colour)
356 {
357   lcd128x64line (lastX, lastY, x, y, colour) ;
358 }
359
360
361 /*
362  * lcd128x64rectangle:
363  *      A rectangle is a spoilt days fishing
364  *********************************************************************************
365  */
366
367 void lcd128x64rectangle (int x1, int y1, int x2, int y2, int colour, int filled)
368 {
369   register int x ;
370
371   if (filled)
372   {
373     /**/ if (x1 == x2)
374       lcd128x64line (x1, y1, x2, y2, colour) ;
375     else if (x1 < x2)
376       for (x = x1 ; x <= x2 ; ++x)
377         lcd128x64line (x, y1, x, y2, colour) ;
378     else
379       for (x = x2 ; x <= x1 ; ++x)
380         lcd128x64line (x, y1, x, y2, colour) ;
381   }
382   else
383   {
384     lcd128x64line   (x1, y1, x2, y1, colour) ;
385     lcd128x64lineTo (x2, y2, colour) ;
386     lcd128x64lineTo (x1, y2, colour) ;
387     lcd128x64lineTo (x1, y1, colour) ;
388   }
389 }
390
391
392 /*
393  * lcd128x64circle:
394  *      This is the midpoint circle algorithm.
395  *********************************************************************************
396  */
397
398 void lcd128x64circle (int x, int y, int r, int colour, int filled)
399 {
400   int ddF_x = 1 ;
401   int ddF_y = -2 * r ;
402
403   int f = 1 - r ;
404   int x1 = 0 ;
405   int y1 = r ;
406
407   if (filled)
408   {
409     lcd128x64line (x, y + r, x, y - r, colour) ;
410     lcd128x64line (x + r, y, x - r, y, colour) ;
411   }
412   else
413   {
414     lcd128x64point (x, y + r, colour) ;
415     lcd128x64point (x, y - r, colour) ;
416     lcd128x64point (x + r, y, colour) ;
417     lcd128x64point (x - r, y, colour) ;
418   }
419
420   while (x1 < y1)
421   {
422     if (f >= 0)
423     {
424       y1-- ;
425       ddF_y += 2 ;
426       f += ddF_y ;
427     }
428     x1++ ;
429     ddF_x += 2 ;
430     f += ddF_x ;
431     if (filled)
432     {
433       lcd128x64line (x + x1, y + y1, x - x1, y + y1, colour) ;
434       lcd128x64line (x + x1, y - y1, x - x1, y - y1, colour) ;
435       lcd128x64line (x + y1, y + x1, x - y1, y + x1, colour) ;
436       lcd128x64line (x + y1, y - x1, x - y1, y - x1, colour) ;
437     }
438     else
439     {
440       lcd128x64point (x + x1, y + y1, colour) ; lcd128x64point (x - x1, y + y1, colour) ;
441       lcd128x64point (x + x1, y - y1, colour) ; lcd128x64point (x - x1, y - y1, colour) ;
442       lcd128x64point (x + y1, y + x1, colour) ; lcd128x64point (x - y1, y + x1, colour) ;
443       lcd128x64point (x + y1, y - x1, colour) ; lcd128x64point (x - y1, y - x1, colour) ;
444     }
445   }
446 }
447
448
449 /*
450  * lcd128x64ellipse:
451  *      Fast ellipse drawing algorithm by 
452  *      John Kennedy
453  *      Mathematics Department
454  *      Santa Monica College
455  *      1900 Pico Blvd.
456  *      Santa Monica, CA 90405
457  *      jrkennedy6@gmail.com
458  *      -Confirned in email this algorithm is in the public domain -GH-
459  *********************************************************************************
460  */
461
462 static void plot4ellipsePoints (int cx, int cy, int x, int y, int colour, int filled)
463 {
464   if (filled)
465   {
466     lcd128x64line (cx + x, cy + y, cx - x, cy + y, colour) ;
467     lcd128x64line (cx - x, cy - y, cx + x, cy - y, colour) ;
468   }
469   else
470   {
471     lcd128x64point (cx + x, cy + y, colour) ;
472     lcd128x64point (cx - x, cy + y, colour) ;
473     lcd128x64point (cx - x, cy - y, colour) ;
474     lcd128x64point (cx + x, cy - y, colour) ;
475   }
476 }
477
478 void lcd128x64ellipse (int cx, int cy, int xRadius, int yRadius, int colour, int filled)
479 {
480   int x, y ;
481   int xChange, yChange, ellipseError ;
482   int twoAsquare, twoBsquare ;
483   int stoppingX, stoppingY ;
484
485   twoAsquare = 2 * xRadius * xRadius ;
486   twoBsquare = 2 * yRadius * yRadius ;
487
488   x = xRadius ;
489   y = 0 ;
490
491   xChange = yRadius * yRadius * (1 - 2 * xRadius) ;
492   yChange = xRadius * xRadius ;
493
494   ellipseError = 0 ;
495   stoppingX    = twoBsquare * xRadius ;
496   stoppingY    = 0 ;
497
498   while (stoppingX >= stoppingY)        // 1st set of points
499   {
500     plot4ellipsePoints (cx, cy, x, y, colour, filled) ;
501     ++y ;
502     stoppingY    += twoAsquare ;
503     ellipseError += yChange ;
504     yChange      += twoAsquare ;
505
506     if ((2 * ellipseError + xChange) > 0 )
507     {
508       --x ;
509       stoppingX    -= twoBsquare ;
510       ellipseError += xChange ;
511       xChange      += twoBsquare ;
512     }
513   }
514
515   x = 0 ;
516   y = yRadius ;
517
518   xChange = yRadius * yRadius ;
519   yChange = xRadius * xRadius * (1 - 2 * yRadius) ;
520
521   ellipseError = 0 ;
522   stoppingX    = 0 ;
523   stoppingY    = twoAsquare * yRadius ;
524
525   while (stoppingX <= stoppingY)        //2nd set of points
526   {
527     plot4ellipsePoints (cx, cy, x, y, colour, filled) ;
528     ++x ;
529     stoppingX    += twoBsquare ;
530     ellipseError += xChange ;
531     xChange      += twoBsquare ;
532
533     if ((2 * ellipseError + yChange) > 0 )
534     {
535       --y ;
536       stoppingY -= twoAsquare ;
537       ellipseError += yChange ;
538       yChange += twoAsquare ;
539     }
540   }
541 }
542
543
544 /*
545  * lcd128x64putchar:
546  *      Print a single character to the screen
547  *********************************************************************************
548  */
549
550 void lcd128x64putchar (int x, int y, int c, int bgCol, int fgCol)
551 {
552   int y1, y2 ;
553
554   unsigned char line ;
555   unsigned char *fontPtr ;
556
557 // Can't print if we're offscreen
558
559 //if ((x < 0) || (x >= (maxX - fontWidth)) || (y < 0) || (y >= (maxY - fontHeight)))
560 //  return ;
561
562   fontPtr = font + c * fontHeight ;
563
564   for (y1 = fontHeight - 1 ; y1 >= 0 ; --y1)
565   {
566     y2 = y + y1 ;
567     line = *fontPtr++ ;
568     lcd128x64point (x + 0, y2, (line & 0x80) == 0 ? bgCol : fgCol) ;
569     lcd128x64point (x + 1, y2, (line & 0x40) == 0 ? bgCol : fgCol) ;
570     lcd128x64point (x + 2, y2, (line & 0x20) == 0 ? bgCol : fgCol) ;
571     lcd128x64point (x + 3, y2, (line & 0x10) == 0 ? bgCol : fgCol) ;
572     lcd128x64point (x + 4, y2, (line & 0x08) == 0 ? bgCol : fgCol) ;
573     lcd128x64point (x + 5, y2, (line & 0x04) == 0 ? bgCol : fgCol) ;
574     lcd128x64point (x + 6, y2, (line & 0x02) == 0 ? bgCol : fgCol) ;
575     lcd128x64point (x + 7, y2, (line & 0x01) == 0 ? bgCol : fgCol) ;
576   }
577 }
578
579
580 /*
581  * lcd128x64puts:
582  *      Send a string to the display. Obeys \n and \r formatting
583  *********************************************************************************
584  */
585
586 void lcd128x64puts (int x, int y, const char *str, int bgCol, int fgCol)
587 {
588   int c, mx, my ;
589
590   mx = x ; my = y ;
591
592   while (*str)
593   {
594     c = *str++ ;
595
596     if (c == '\r')
597     {
598       mx = x ;
599       continue ;
600     }
601
602     if (c == '\n')
603     {
604       mx  = x ;
605       my -= fontHeight ;
606       continue ;
607     }
608
609     lcd128x64putchar (mx, my, c, bgCol, fgCol) ;
610
611     mx += fontWidth ;
612     if (mx >= (maxX - fontWidth))
613     {
614       mx  = 0 ;
615       my -= fontHeight ;
616     }
617   }
618 }
619
620
621 /*
622  * lcd128x64clear:
623  *      Clear the display to the given colour.
624  *********************************************************************************
625  */
626
627 void lcd128x64clear (int colour)
628 {
629   register int i ;
630   register unsigned char *ptr = frameBuffer ;
631
632   for (i = 0 ; i < (maxX * maxY) ; ++i)
633     *ptr++ = colour ;
634 }
635
636
637
638
639 /*
640  * lcd128x64setup:
641  *      Initialise the display and GPIO.
642  *********************************************************************************
643  */
644
645 int lcd128x64setup (void)
646 {
647   int i ;
648
649   for (i = 0 ; i < 8 ; ++i)
650     pinMode (i, OUTPUT) ;
651
652   digitalWrite (CS1,    1) ;
653   digitalWrite (CS2,    1) ;
654   digitalWrite (STROBE, 0) ;
655   digitalWrite (RS,     1) ;
656
657   pinMode (CS1,    OUTPUT) ;
658   pinMode (CS2,    OUTPUT) ;
659   pinMode (STROBE, OUTPUT) ;
660   pinMode (RS,     OUTPUT) ;
661
662   sendCommand (0x3F, CS1) ;     // Display ON
663   sendCommand (0xC0, CS1) ;     // Set display start line to 0
664
665   sendCommand (0x3F, CS2) ;     // Display ON
666   sendCommand (0xC0, CS2) ;     // Set display start line to 0
667
668   lcd128x64clear          (0) ;
669   lcd128x64setOrientation (0) ;
670   lcd128x64update         () ;
671
672   return 0 ;
673 }