/***************************************************************************************
ThrottleX2011
A demonstration of a very basic OpenLCB throttle.
Copyright (C)2010 Limor Fried, Adafruit Industries, 2011 D.E. Goodman-Wilson
This file is part of ThrottleX2011.
ThrottleX2011 is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Foobar 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with ThrottleX2011. If not, see .
----------------
This file is based on the PCD8554 library by Limor Fried, available here:
https://github.com/adafruit/PCD8544-Nokia-5110-LCD-library/
***************************************************************************************/
#include
#include
#include
#include
#include "PCD8544.h"
#include "glcdfont.c"
uint8_t is_reversed = 0;
// a 5x7 font table
extern uint8_t PROGMEM font[];
// the memory buffer for the LCD
uint8_t pcd8544_buffer[LCDWIDTH * LCDHEIGHT / 8] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x1C,
0x3F, 0x3F, 0x7E, 0x7E, 0xFC, 0xFC, 0xF8, 0XF0, 0XF0, 0xE0, 0x20, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F,
0x20, 0xE0, 0XF0, 0XF0, 0xF8, 0xFC, 0xFC, 0x7E, 0x7E, 0x3F, 0x3F, 0x1C, 0x08, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x80, 0x80, 0xC0, 0xC0, 0xE0, 0xE0, 0XF0, 0XF0, 0xF9, 0xFF, 0xF9, 0x70, 0x40, 0x80,
0x80, 0x80, 0x80, 0x80, 0x40, 0x70, 0xF9, 0xFF, 0xF9, 0XF0, 0XF0, 0xE0, 0xE0, 0xC0, 0xC0, 0x80,
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x0F, 0x0F, 0x07, 0x07, 0x03, 0x03, 0x01, 0x00,
0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x07,
0x07, 0x0F, 0x0F, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0x00, 0XF0, 0XF0, 0x00, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x03, 0xF3, 0xF3, 0x83, 0x83, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x00, 0x00, 0x7F, 0x7F, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00,
0x7D, 0x7D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x7D, 0x7D, 0x00, 0x7F, 0x7F, 0x00, 0x7F, 0x7F,
0x00, 0x6F, 0x6F, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x7D, 0x7D, 0x00, 0x7F, 0x7F, 0x61, 0x61,
0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x00, 0x7D, 0x7D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x7D,
0x7D, 0x00, 0x7F, 0x7F, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x6F, 0x6F, 0x6D,
0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x7D, 0x7D, 0x00,
};
// reduces how much is refreshed, which speeds it up!
// originally derived from Steve Evans/JCW's mod but cleaned up and
// optimized
//#define enablePartialUpdate
#ifdef enablePartialUpdate
static uint8_t xUpdateMin, xUpdateMax, yUpdateMin, yUpdateMax;
#endif
static void updateBoundingBox(uint8_t xmin, uint8_t ymin, uint8_t xmax, uint8_t ymax) {
#ifdef enablePartialUpdate
if (xmin < xUpdateMin) xUpdateMin = xmin;
if (xmax > xUpdateMax) xUpdateMax = xmax;
if (ymin < yUpdateMin) yUpdateMin = ymin;
if (ymax > yUpdateMax) yUpdateMax = ymax;
#endif
}
void PCD8544::drawbitmap(uint8_t x, uint8_t y,
const uint8_t *bitmap, uint8_t w, uint8_t h,
uint8_t color) {
for (uint8_t j=0; j LCDWIDTH) {
x = 0; // ran out of this line
line++;
}
if (line >= (LCDHEIGHT/8))
return; // ran out of space :(
}
}
void PCD8544::drawstring(uint8_t x, uint8_t line, String str) {
for(uint8_t i = 0; i < str.length(); ++i)
{
drawchar(x, line, str[i]);
x += 6; // 6 pixels wide
if (x + 6 > LCDWIDTH) {
x = 0; // ran out of this line
line++;
}
if (line >= (LCDHEIGHT/8))
return; // ran out of space :(
}
}
void PCD8544::drawstring_P(uint8_t x, uint8_t line, const char *str) {
while (1) {
char c = pgm_read_byte(str++);
if (! c)
return;
drawchar(x, line, c);
x += 6; // 6 pixels wide
if (x + 6 > LCDWIDTH) {
x = 0; // ran out of this line
line++;
}
if (line >= (LCDHEIGHT/8))
return; // ran out of space :(
}
}
void PCD8544::drawstringinverse(uint8_t x, uint8_t line, char *c) {
while (c[0] != 0) {
drawcharinverse(x, line, c[0]);
c++;
x += 6; // 6 pixels wide
if (x + 6 > LCDWIDTH) {
x = 0; // ran out of this line
line++;
}
if (line >= (LCDHEIGHT/8))
return; // ran out of space :(
}
}
void PCD8544::drawstringinverse(uint8_t x, uint8_t line, String str) {
for(uint8_t i = 0; i < str.length(); ++i)
{
drawcharinverse(x, line, str[i]);
x += 6; // 6 pixels wide
if (x + 6 > LCDWIDTH) {
x = 0; // ran out of this line
line++;
}
if (line >= (LCDHEIGHT/8))
return; // ran out of space :(
}
}
void PCD8544::drawstringinverse_P(uint8_t x, uint8_t line, const char *str) {
while (1) {
char c = pgm_read_byte(str++);
if (! c)
return;
drawcharinverse(x, line, c);
x += 6; // 6 pixels wide
if (x + 6 > LCDWIDTH) {
x = 0; // ran out of this line
line++;
}
if (line >= (LCDHEIGHT/8))
return; // ran out of space :(
}
}
void PCD8544::drawchar(uint8_t x, uint8_t line, char c) {
if (line >= LCDHEIGHT/8) return;
if ((x+5) >= LCDWIDTH) return;
for (uint8_t i =0; i<5; i++ ) {
pcd8544_buffer[x + (line*LCDWIDTH) ] = pgm_read_byte(font+(c*5)+i);
x++;
}
updateBoundingBox(x, line*8, x+5, line*8 + 8);
}
void PCD8544::drawcharinverse(uint8_t x, uint8_t line, char c) {
if (line >= LCDHEIGHT/8) return;
if ((x+5) >= LCDWIDTH) return;
for (uint8_t i =0; i<5; i++ ) {
pcd8544_buffer[x + (line*LCDWIDTH) ] = (pgm_read_byte(font+(c*5)+i)^0xFF);
x++;
}
updateBoundingBox(x, line*8, x+5, line*8 + 8);
}
// bresenham's algorithm - thx wikpedia
void PCD8544::drawline(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1,
uint8_t color) {
uint8_t steep = abs(y1 - y0) > abs(x1 - x0);
if (steep) {
swap(x0, y0);
swap(x1, y1);
}
if (x0 > x1) {
swap(x0, x1);
swap(y0, y1);
}
// much faster to put the test here, since we've already sorted the points
updateBoundingBox(x0, y0, x1, y1);
uint8_t dx, dy;
dx = x1 - x0;
dy = abs(y1 - y0);
int8_t err = dx / 2;
int8_t ystep;
if (y0 < y1) {
ystep = 1;
} else {
ystep = -1;}
for (; x0<=x1; x0++) {
if (steep) {
my_setpixel(y0, x0, color);
} else {
my_setpixel(x0, y0, color);
}
err -= dy;
if (err < 0) {
y0 += ystep;
err += dx;
}
}
}
// filled rectangle
void PCD8544::fillrect(uint8_t x, uint8_t y, uint8_t w, uint8_t h,
uint8_t color) {
// stupidest version - just pixels - but fast with internal buffer!
for (uint8_t i=x; i= 0) {
y--;
ddF_y += 2;
f += ddF_y;
}
x++;
ddF_x += 2;
f += ddF_x;
my_setpixel(x0 + x, y0 + y, color);
my_setpixel(x0 - x, y0 + y, color);
my_setpixel(x0 + x, y0 - y, color);
my_setpixel(x0 - x, y0 - y, color);
my_setpixel(x0 + y, y0 + x, color);
my_setpixel(x0 - y, y0 + x, color);
my_setpixel(x0 + y, y0 - x, color);
my_setpixel(x0 - y, y0 - x, color);
}
}
void PCD8544::fillcircle(uint8_t x0, uint8_t y0, uint8_t r,
uint8_t color) {
updateBoundingBox(x0-r, y0-r, x0+r, y0+r);
int8_t f = 1 - r;
int8_t ddF_x = 1;
int8_t ddF_y = -2 * r;
int8_t x = 0;
int8_t y = r;
for (uint8_t i=y0-r; i<=y0+r; i++) {
my_setpixel(x0, i, color);
}
while (x= 0) {
y--;
ddF_y += 2;
f += ddF_y;
}
x++;
ddF_x += 2;
f += ddF_x;
for (uint8_t i=y0-y; i<=y0+y; i++) {
my_setpixel(x0+x, i, color);
my_setpixel(x0-x, i, color);
}
for (uint8_t i=y0-x; i<=y0+x; i++) {
my_setpixel(x0+y, i, color);
my_setpixel(x0-y, i, color);
}
}
}
void PCD8544::my_setpixel(uint8_t x, uint8_t y, uint8_t color) {
if ((x >= LCDWIDTH) || (y >= LCDHEIGHT))
return;
// x is which column
if (color)
pcd8544_buffer[x+ (y/8)*LCDWIDTH] |= _BV(y%8);
else
pcd8544_buffer[x+ (y/8)*LCDWIDTH] &= ~_BV(y%8);
}
// the most basic function, set a single pixel
void PCD8544::setPixel(uint8_t x, uint8_t y, uint8_t color) {
if ((x >= LCDWIDTH) || (y >= LCDHEIGHT))
return;
// x is which column
if (color)
pcd8544_buffer[x+ (y/8)*LCDWIDTH] |= _BV(y%8);
else
pcd8544_buffer[x+ (y/8)*LCDWIDTH] &= ~_BV(y%8);
updateBoundingBox(x,y,x,y);
}
// the most basic function, get a single pixel
uint8_t PCD8544::getPixel(uint8_t x, uint8_t y) {
if ((x >= LCDWIDTH) || (y >= LCDHEIGHT))
return 0;
return (pcd8544_buffer[x+ (y/8)*LCDWIDTH] >> (7-(y%8))) & 0x1;
}
void PCD8544::begin(uint8_t contrast) {
init();
// st7565_command(CMD_DISPLAY_ON);
//st7565_command(CMD_SET_ALLPTS_NORMAL);
//st7565_set_brightness(contrast);
}
void PCD8544::init(void) {
// set pin directions
pinMode(_din, OUTPUT);
pinMode(_sclk, OUTPUT);
pinMode(_dc, OUTPUT);
pinMode(_rst, OUTPUT);
pinMode(_cs, OUTPUT);
// toggle RST low to reset; CS low so it'll listen to us
if (_cs > 0)
digitalWrite(_cs, LOW);
digitalWrite(_rst, LOW);
_delay_ms(500);
digitalWrite(_rst, HIGH);
// get into the EXTENDED mode!
command(PCD8544_FUNCTIONSET | PCD8544_EXTENDEDINSTRUCTION );
// LCD bias select (4 is optimal?)
command(PCD8544_SETBIAS | 0x4);
// set VOP
command( PCD8544_SETVOP | 50); // Experimentally determined
// normal mode
command(PCD8544_FUNCTIONSET);
// turn all the pixels on
command(PCD8544_DISPLAYCONTROL | PCD8544_DISPLAYALLON);
// initial display line
// set page address
// set column address
// write display data
// set up a bounding box for screen updates
updateBoundingBox(0, 0, LCDWIDTH-1, LCDHEIGHT-1);
}
inline void PCD8544::spiwrite(uint8_t c) {
shiftOut(_din, _sclk, MSBFIRST, c);
}
void PCD8544::command(uint8_t c) {
digitalWrite(_dc, LOW);
spiwrite(c);
}
void PCD8544::data(uint8_t c) {
digitalWrite(_dc, HIGH);
spiwrite(c);
}
void PCD8544::setContrast(uint8_t val) {
}
void PCD8544::display(void) {
cli();
uint8_t col, maxcol, p;
for(p = 0; p < 6; p++) {
#ifdef enablePartialUpdate
// check if this page is part of update
if ( yUpdateMin >= ((p+1)*8) ) {
continue; // nope, skip it!
}
if (yUpdateMax < p*8) {
break;
}
#endif
command(PCD8544_SETYADDR | p);
#ifdef enablePartialUpdate
col = xUpdateMin;
maxcol = xUpdateMax;
#else
// start at the beginning of the row
col = 0;
maxcol = LCDWIDTH-1;
#endif
command(PCD8544_SETXADDR | col);
for(; col <= maxcol; col++) {
//uart_putw_dec(col);
//uart_putchar(' ');
data(pcd8544_buffer[(LCDWIDTH*p)+col]);
}
}
#ifdef enablePartialUpdate
xUpdateMin = LCDWIDTH - 1;
xUpdateMax = 0;
yUpdateMin = LCDHEIGHT-1;
yUpdateMax = 0;
#endif
sei();
}
// clear everything
void PCD8544::clear(void) {
memset(pcd8544_buffer, 0, LCDWIDTH*LCDHEIGHT/8);
updateBoundingBox(0, 0, LCDWIDTH-1, LCDHEIGHT-1);
}
/*
// this doesnt touch the buffer, just clears the display RAM - might be handy
void PCD8544::clearDisplay(void) {
uint8_t p, c;
for(p = 0; p < 8; p++) {
st7565_command(CMD_SET_PAGE | p);
for(c = 0; c < 129; c++) {
//uart_putw_dec(c);
//uart_putchar(' ');
st7565_command(CMD_SET_COLUMN_LOWER | (c & 0xf));
st7565_command(CMD_SET_COLUMN_UPPER | ((c >> 4) & 0xf));
st7565_data(0x0);
}
}
}
*/