You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
673 lines
14 KiB
673 lines
14 KiB
/* |
|
* lcd128x64.c: |
|
* Graphics-based LCD driver. |
|
* This is designed to drive the parallel interface LCD drivers |
|
* based on the generic 12864H chips |
|
* |
|
* There are many variations on these chips, however they all mostly |
|
* seem to be similar. |
|
* This implementation has the Pins from the Pi hard-wired into it, |
|
* in particular wiringPi pins 0-7 so that we can use |
|
* digitalWriteByete() to speed things up somewhat. |
|
* |
|
* Copyright (c) 2013 Gordon Henderson. |
|
*********************************************************************** |
|
* This file is part of wiringPi: |
|
* https://projects.drogon.net/raspberry-pi/wiringpi/ |
|
* |
|
* wiringPi is free software: you can redistribute it and/or modify |
|
* it under the terms of the GNU Lesser General Public License as published by |
|
* the Free Software Foundation, either version 3 of the License, or |
|
* (at your option) any later version. |
|
* |
|
* wiringPi is distributed in the hope that it will be useful, |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
* GNU Lesser General Public License for more details. |
|
* |
|
* You should have received a copy of the GNU Lesser General Public License |
|
* along with wiringPi. If not, see <http://www.gnu.org/licenses/>. |
|
*********************************************************************** |
|
*/ |
|
|
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
|
|
#include <wiringPi.h> |
|
|
|
#include "font.h" |
|
#include "lcd128x64.h" |
|
|
|
// Size |
|
|
|
#define LCD_WIDTH 128 |
|
#define LCD_HEIGHT 64 |
|
|
|
// Hardware Pins |
|
// Note pins 0-7 are the 8-bit data port |
|
|
|
#define CS1 10 |
|
#define CS2 11 |
|
#define STROBE 12 |
|
#define RS 13 |
|
|
|
// Software copy of the framebuffer |
|
// it's 8-bit deep although the display itself is only 1-bit deep. |
|
|
|
static unsigned char frameBuffer [LCD_WIDTH * LCD_HEIGHT] ; |
|
|
|
static int maxX, maxY ; |
|
static int lastX, lastY ; |
|
static int xOrigin, yOrigin ; |
|
static int lcdOrientation = 0 ; |
|
|
|
/* |
|
* strobe: |
|
* Toggle the strobe (Really the "E") pin to the device. |
|
* According to the docs, data is latched on the falling edge. |
|
********************************************************************************* |
|
*/ |
|
|
|
static void strobe (void) |
|
{ |
|
digitalWrite (STROBE, 1) ; delayMicroseconds (1) ; |
|
digitalWrite (STROBE, 0) ; delayMicroseconds (5) ; |
|
} |
|
|
|
|
|
/* |
|
* sentData: |
|
* Send an data or command byte to the display. |
|
********************************************************************************* |
|
*/ |
|
|
|
static void sendData (const int data, const int chip) |
|
{ |
|
digitalWrite (chip, 0) ; |
|
digitalWriteByte (data) ; |
|
strobe () ; |
|
digitalWrite (chip, 1) ; |
|
} |
|
|
|
|
|
/* |
|
* sendCommand: |
|
* Send a command byte to the display |
|
********************************************************************************* |
|
*/ |
|
|
|
static void sendCommand (const int command, const int chip) |
|
{ |
|
digitalWrite (RS, 0) ; |
|
sendData (command, chip) ; |
|
digitalWrite (RS, 1) ; |
|
} |
|
|
|
|
|
/* |
|
* setCol: SetLine: |
|
* Set the column and line addresses |
|
********************************************************************************* |
|
*/ |
|
|
|
static void setCol (int col, const int chip) |
|
{ sendCommand (0x40 | (col & 0x3F), chip) ; } |
|
|
|
static void setLine (int line, const int chip) |
|
{ sendCommand (0xB8 | (line & 0x07), chip) ; } |
|
|
|
|
|
/* |
|
* lcd128x64update: |
|
* Copy our software version to the real display |
|
********************************************************************************* |
|
*/ |
|
|
|
void lcd128x64update (void) |
|
{ |
|
int line, x, y, fbLoc ; |
|
unsigned char byte ; |
|
|
|
// Left side |
|
|
|
for (line = 0 ; line < 8 ; ++line) |
|
{ |
|
setCol (0, CS1) ; |
|
setLine (line, CS1) ; |
|
|
|
for (x = 63 ; x >= 0 ; --x) |
|
{ |
|
byte = 0 ; |
|
for (y = 0 ; y < 8 ; ++y) |
|
{ |
|
fbLoc = x + (((7 - line) * 8) + (7 - y)) * LCD_WIDTH ; |
|
if (frameBuffer [fbLoc] != 0) |
|
byte |= (1 << y) ; |
|
} |
|
sendData (byte, CS1) ; |
|
} |
|
} |
|
|
|
// Right side |
|
|
|
for (line = 0 ; line < 8 ; ++line) |
|
{ |
|
setCol (0, CS2) ; |
|
setLine (line, CS2) ; |
|
|
|
for (x = 127 ; x >= 64 ; --x) |
|
{ |
|
byte = 0 ; |
|
for (y = 0 ; y < 8 ; ++y) |
|
{ |
|
fbLoc = x + (((7 - line) * 8) + (7 - y)) * LCD_WIDTH ; |
|
if (frameBuffer [fbLoc] != 0) |
|
byte |= (1 << y) ; |
|
} |
|
sendData (byte, CS2) ; |
|
} |
|
} |
|
} |
|
|
|
|
|
/* |
|
* lcd128x64setOrigin: |
|
* Set the display offset origin |
|
********************************************************************************* |
|
*/ |
|
|
|
void lcd128x64setOrigin (int x, int y) |
|
{ |
|
xOrigin = x ; |
|
yOrigin = y ; |
|
} |
|
|
|
|
|
/* |
|
* lcd128x64setOrientation: |
|
* Set the display orientation: |
|
* 0: Normal, the display is portrait mode, 0,0 is top left |
|
* 1: Landscape |
|
* 2: Portrait, flipped |
|
* 3: Landscape, flipped |
|
********************************************************************************* |
|
*/ |
|
|
|
void lcd128x64setOrientation (int orientation) |
|
{ |
|
lcdOrientation = orientation & 3 ; |
|
|
|
lcd128x64setOrigin (0,0) ; |
|
|
|
switch (lcdOrientation) |
|
{ |
|
case 0: |
|
maxX = LCD_WIDTH ; |
|
maxY = LCD_HEIGHT ; |
|
break ; |
|
|
|
case 1: |
|
maxX = LCD_HEIGHT ; |
|
maxY = LCD_WIDTH ; |
|
break ; |
|
|
|
case 2: |
|
maxX = LCD_WIDTH ; |
|
maxY = LCD_HEIGHT ; |
|
break ; |
|
|
|
case 3: |
|
maxX = LCD_HEIGHT ; |
|
maxY = LCD_WIDTH ; |
|
break ; |
|
} |
|
} |
|
|
|
|
|
/* |
|
* lcd128x64orientCoordinates: |
|
* Adjust the coordinates given to the display orientation |
|
********************************************************************************* |
|
*/ |
|
|
|
void lcd128x64orientCoordinates (int *x, int *y) |
|
{ |
|
register int tmp ; |
|
|
|
*x += xOrigin ; |
|
*y += yOrigin ; |
|
*y = maxY - *y - 1 ; |
|
|
|
switch (lcdOrientation) |
|
{ |
|
case 0: |
|
break; |
|
|
|
case 1: |
|
tmp = maxY - *y - 1 ; |
|
*y = *x ; |
|
*x = tmp ; |
|
break; |
|
|
|
case 2: |
|
*x = maxX - *x - 1 ; |
|
*y = maxY - *y - 1 ; |
|
break; |
|
|
|
case 3: |
|
*x = maxX - *x - 1 ; |
|
tmp = *y ; |
|
*y = *x ; |
|
*x = tmp ; |
|
break ; |
|
} |
|
} |
|
|
|
|
|
/* |
|
* lcd128x64getScreenSize: |
|
* Return the max X & Y screen sizes. Needs to be called again, if you |
|
* change screen orientation. |
|
********************************************************************************* |
|
*/ |
|
|
|
void lcd128x64getScreenSize (int *x, int *y) |
|
{ |
|
*x = maxX ; |
|
*y = maxY ; |
|
} |
|
|
|
|
|
/* |
|
********************************************************************************* |
|
* Standard Graphical Functions |
|
********************************************************************************* |
|
*/ |
|
|
|
|
|
/* |
|
* lcd128x64point: |
|
* Plot a pixel. |
|
********************************************************************************* |
|
*/ |
|
|
|
void lcd128x64point (int x, int y, int colour) |
|
{ |
|
lastX = x ; |
|
lastY = y ; |
|
|
|
lcd128x64orientCoordinates (&x, &y) ; |
|
|
|
if ((x < 0) || (x >= LCD_WIDTH) || (y < 0) || (y >= LCD_HEIGHT)) |
|
return ; |
|
|
|
frameBuffer [x + y * LCD_WIDTH] = colour ; |
|
} |
|
|
|
|
|
/* |
|
* lcd128x64line: lcd128x64lineTo: |
|
* Classic Bressenham Line code |
|
********************************************************************************* |
|
*/ |
|
|
|
void lcd128x64line (int x0, int y0, int x1, int y1, int colour) |
|
{ |
|
int dx, dy ; |
|
int sx, sy ; |
|
int err, e2 ; |
|
|
|
lastX = x1 ; |
|
lastY = y1 ; |
|
|
|
dx = abs (x1 - x0) ; |
|
dy = abs (y1 - y0) ; |
|
|
|
sx = (x0 < x1) ? 1 : -1 ; |
|
sy = (y0 < y1) ? 1 : -1 ; |
|
|
|
err = dx - dy ; |
|
|
|
for (;;) |
|
{ |
|
lcd128x64point (x0, y0, colour) ; |
|
|
|
if ((x0 == x1) && (y0 == y1)) |
|
break ; |
|
|
|
e2 = 2 * err ; |
|
|
|
if (e2 > -dy) |
|
{ |
|
err -= dy ; |
|
x0 += sx ; |
|
} |
|
|
|
if (e2 < dx) |
|
{ |
|
err += dx ; |
|
y0 += sy ; |
|
} |
|
} |
|
|
|
} |
|
|
|
void lcd128x64lineTo (int x, int y, int colour) |
|
{ |
|
lcd128x64line (lastX, lastY, x, y, colour) ; |
|
} |
|
|
|
|
|
/* |
|
* lcd128x64rectangle: |
|
* A rectangle is a spoilt days fishing |
|
********************************************************************************* |
|
*/ |
|
|
|
void lcd128x64rectangle (int x1, int y1, int x2, int y2, int colour, int filled) |
|
{ |
|
register int x ; |
|
|
|
if (filled) |
|
{ |
|
/**/ if (x1 == x2) |
|
lcd128x64line (x1, y1, x2, y2, colour) ; |
|
else if (x1 < x2) |
|
for (x = x1 ; x <= x2 ; ++x) |
|
lcd128x64line (x, y1, x, y2, colour) ; |
|
else |
|
for (x = x2 ; x <= x1 ; ++x) |
|
lcd128x64line (x, y1, x, y2, colour) ; |
|
} |
|
else |
|
{ |
|
lcd128x64line (x1, y1, x2, y1, colour) ; |
|
lcd128x64lineTo (x2, y2, colour) ; |
|
lcd128x64lineTo (x1, y2, colour) ; |
|
lcd128x64lineTo (x1, y1, colour) ; |
|
} |
|
} |
|
|
|
|
|
/* |
|
* lcd128x64circle: |
|
* This is the midpoint circle algorithm. |
|
********************************************************************************* |
|
*/ |
|
|
|
void lcd128x64circle (int x, int y, int r, int colour, int filled) |
|
{ |
|
int ddF_x = 1 ; |
|
int ddF_y = -2 * r ; |
|
|
|
int f = 1 - r ; |
|
int x1 = 0 ; |
|
int y1 = r ; |
|
|
|
if (filled) |
|
{ |
|
lcd128x64line (x, y + r, x, y - r, colour) ; |
|
lcd128x64line (x + r, y, x - r, y, colour) ; |
|
} |
|
else |
|
{ |
|
lcd128x64point (x, y + r, colour) ; |
|
lcd128x64point (x, y - r, colour) ; |
|
lcd128x64point (x + r, y, colour) ; |
|
lcd128x64point (x - r, y, colour) ; |
|
} |
|
|
|
while (x1 < y1) |
|
{ |
|
if (f >= 0) |
|
{ |
|
y1-- ; |
|
ddF_y += 2 ; |
|
f += ddF_y ; |
|
} |
|
x1++ ; |
|
ddF_x += 2 ; |
|
f += ddF_x ; |
|
if (filled) |
|
{ |
|
lcd128x64line (x + x1, y + y1, x - x1, y + y1, colour) ; |
|
lcd128x64line (x + x1, y - y1, x - x1, y - y1, colour) ; |
|
lcd128x64line (x + y1, y + x1, x - y1, y + x1, colour) ; |
|
lcd128x64line (x + y1, y - x1, x - y1, y - x1, colour) ; |
|
} |
|
else |
|
{ |
|
lcd128x64point (x + x1, y + y1, colour) ; lcd128x64point (x - x1, y + y1, colour) ; |
|
lcd128x64point (x + x1, y - y1, colour) ; lcd128x64point (x - x1, y - y1, colour) ; |
|
lcd128x64point (x + y1, y + x1, colour) ; lcd128x64point (x - y1, y + x1, colour) ; |
|
lcd128x64point (x + y1, y - x1, colour) ; lcd128x64point (x - y1, y - x1, colour) ; |
|
} |
|
} |
|
} |
|
|
|
|
|
/* |
|
* lcd128x64ellipse: |
|
* Fast ellipse drawing algorithm by |
|
* John Kennedy |
|
* Mathematics Department |
|
* Santa Monica College |
|
* 1900 Pico Blvd. |
|
* Santa Monica, CA 90405 |
|
* jrkennedy6@gmail.com |
|
* -Confirned in email this algorithm is in the public domain -GH- |
|
********************************************************************************* |
|
*/ |
|
|
|
static void plot4ellipsePoints (int cx, int cy, int x, int y, int colour, int filled) |
|
{ |
|
if (filled) |
|
{ |
|
lcd128x64line (cx + x, cy + y, cx - x, cy + y, colour) ; |
|
lcd128x64line (cx - x, cy - y, cx + x, cy - y, colour) ; |
|
} |
|
else |
|
{ |
|
lcd128x64point (cx + x, cy + y, colour) ; |
|
lcd128x64point (cx - x, cy + y, colour) ; |
|
lcd128x64point (cx - x, cy - y, colour) ; |
|
lcd128x64point (cx + x, cy - y, colour) ; |
|
} |
|
} |
|
|
|
void lcd128x64ellipse (int cx, int cy, int xRadius, int yRadius, int colour, int filled) |
|
{ |
|
int x, y ; |
|
int xChange, yChange, ellipseError ; |
|
int twoAsquare, twoBsquare ; |
|
int stoppingX, stoppingY ; |
|
|
|
twoAsquare = 2 * xRadius * xRadius ; |
|
twoBsquare = 2 * yRadius * yRadius ; |
|
|
|
x = xRadius ; |
|
y = 0 ; |
|
|
|
xChange = yRadius * yRadius * (1 - 2 * xRadius) ; |
|
yChange = xRadius * xRadius ; |
|
|
|
ellipseError = 0 ; |
|
stoppingX = twoBsquare * xRadius ; |
|
stoppingY = 0 ; |
|
|
|
while (stoppingX >= stoppingY) // 1st set of points |
|
{ |
|
plot4ellipsePoints (cx, cy, x, y, colour, filled) ; |
|
++y ; |
|
stoppingY += twoAsquare ; |
|
ellipseError += yChange ; |
|
yChange += twoAsquare ; |
|
|
|
if ((2 * ellipseError + xChange) > 0 ) |
|
{ |
|
--x ; |
|
stoppingX -= twoBsquare ; |
|
ellipseError += xChange ; |
|
xChange += twoBsquare ; |
|
} |
|
} |
|
|
|
x = 0 ; |
|
y = yRadius ; |
|
|
|
xChange = yRadius * yRadius ; |
|
yChange = xRadius * xRadius * (1 - 2 * yRadius) ; |
|
|
|
ellipseError = 0 ; |
|
stoppingX = 0 ; |
|
stoppingY = twoAsquare * yRadius ; |
|
|
|
while (stoppingX <= stoppingY) //2nd set of points |
|
{ |
|
plot4ellipsePoints (cx, cy, x, y, colour, filled) ; |
|
++x ; |
|
stoppingX += twoBsquare ; |
|
ellipseError += xChange ; |
|
xChange += twoBsquare ; |
|
|
|
if ((2 * ellipseError + yChange) > 0 ) |
|
{ |
|
--y ; |
|
stoppingY -= twoAsquare ; |
|
ellipseError += yChange ; |
|
yChange += twoAsquare ; |
|
} |
|
} |
|
} |
|
|
|
|
|
/* |
|
* lcd128x64putchar: |
|
* Print a single character to the screen |
|
********************************************************************************* |
|
*/ |
|
|
|
void lcd128x64putchar (int x, int y, int c, int bgCol, int fgCol) |
|
{ |
|
int y1, y2 ; |
|
|
|
unsigned char line ; |
|
unsigned char *fontPtr ; |
|
|
|
// Can't print if we're offscreen |
|
|
|
//if ((x < 0) || (x >= (maxX - fontWidth)) || (y < 0) || (y >= (maxY - fontHeight))) |
|
// return ; |
|
|
|
fontPtr = font + c * fontHeight ; |
|
|
|
for (y1 = fontHeight - 1 ; y1 >= 0 ; --y1) |
|
{ |
|
y2 = y + y1 ; |
|
line = *fontPtr++ ; |
|
lcd128x64point (x + 0, y2, (line & 0x80) == 0 ? bgCol : fgCol) ; |
|
lcd128x64point (x + 1, y2, (line & 0x40) == 0 ? bgCol : fgCol) ; |
|
lcd128x64point (x + 2, y2, (line & 0x20) == 0 ? bgCol : fgCol) ; |
|
lcd128x64point (x + 3, y2, (line & 0x10) == 0 ? bgCol : fgCol) ; |
|
lcd128x64point (x + 4, y2, (line & 0x08) == 0 ? bgCol : fgCol) ; |
|
lcd128x64point (x + 5, y2, (line & 0x04) == 0 ? bgCol : fgCol) ; |
|
lcd128x64point (x + 6, y2, (line & 0x02) == 0 ? bgCol : fgCol) ; |
|
lcd128x64point (x + 7, y2, (line & 0x01) == 0 ? bgCol : fgCol) ; |
|
} |
|
} |
|
|
|
|
|
/* |
|
* lcd128x64puts: |
|
* Send a string to the display. Obeys \n and \r formatting |
|
********************************************************************************* |
|
*/ |
|
|
|
void lcd128x64puts (int x, int y, const char *str, int bgCol, int fgCol) |
|
{ |
|
int c, mx, my ; |
|
|
|
mx = x ; my = y ; |
|
|
|
while (*str) |
|
{ |
|
c = *str++ ; |
|
|
|
if (c == '\r') |
|
{ |
|
mx = x ; |
|
continue ; |
|
} |
|
|
|
if (c == '\n') |
|
{ |
|
mx = x ; |
|
my -= fontHeight ; |
|
continue ; |
|
} |
|
|
|
lcd128x64putchar (mx, my, c, bgCol, fgCol) ; |
|
|
|
mx += fontWidth ; |
|
if (mx >= (maxX - fontWidth)) |
|
{ |
|
mx = 0 ; |
|
my -= fontHeight ; |
|
} |
|
} |
|
} |
|
|
|
|
|
/* |
|
* lcd128x64clear: |
|
* Clear the display to the given colour. |
|
********************************************************************************* |
|
*/ |
|
|
|
void lcd128x64clear (int colour) |
|
{ |
|
register int i ; |
|
register unsigned char *ptr = frameBuffer ; |
|
|
|
for (i = 0 ; i < (maxX * maxY) ; ++i) |
|
*ptr++ = colour ; |
|
} |
|
|
|
|
|
|
|
|
|
/* |
|
* lcd128x64setup: |
|
* Initialise the display and GPIO. |
|
********************************************************************************* |
|
*/ |
|
|
|
int lcd128x64setup (void) |
|
{ |
|
int i ; |
|
|
|
for (i = 0 ; i < 8 ; ++i) |
|
pinMode (i, OUTPUT) ; |
|
|
|
digitalWrite (CS1, 1) ; |
|
digitalWrite (CS2, 1) ; |
|
digitalWrite (STROBE, 0) ; |
|
digitalWrite (RS, 1) ; |
|
|
|
pinMode (CS1, OUTPUT) ; |
|
pinMode (CS2, OUTPUT) ; |
|
pinMode (STROBE, OUTPUT) ; |
|
pinMode (RS, OUTPUT) ; |
|
|
|
sendCommand (0x3F, CS1) ; // Display ON |
|
sendCommand (0xC0, CS1) ; // Set display start line to 0 |
|
|
|
sendCommand (0x3F, CS2) ; // Display ON |
|
sendCommand (0xC0, CS2) ; // Set display start line to 0 |
|
|
|
lcd128x64clear (0) ; |
|
lcd128x64setOrientation (0) ; |
|
lcd128x64update () ; |
|
|
|
return 0 ; |
|
}
|
|
|