371 lines
10 KiB
C++
371 lines
10 KiB
C++
/*********************************************************************
|
|
This is a library for our Monochrome Nokia 5110 LCD Displays
|
|
|
|
Pick one up today in the adafruit shop!
|
|
------> http://www.adafruit.com/products/338
|
|
|
|
These displays use SPI to communicate, 4 or 5 pins are required to
|
|
interface
|
|
|
|
Adafruit invests time and resources providing this open source code,
|
|
please support Adafruit and open-source hardware by purchasing
|
|
products from Adafruit!
|
|
|
|
Written by Limor Fried/Ladyada for Adafruit Industries.
|
|
BSD license, check license.txt for more information
|
|
All text above, and the splash screen below must be included in any redistribution
|
|
*********************************************************************/
|
|
|
|
//#include <Wire.h>
|
|
#include <avr/pgmspace.h>
|
|
#if defined(ARDUINO) && ARDUINO >= 100
|
|
#include "Arduino.h"
|
|
#else
|
|
#include "WProgram.h"
|
|
#endif
|
|
|
|
#ifdef __AVR__
|
|
#include <util/delay.h>
|
|
#endif
|
|
|
|
#ifndef _BV
|
|
#define _BV(x) (1 << (x))
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <Adafruit_GFX.h>
|
|
#include "Adafruit_PCD8544.h"
|
|
|
|
#ifndef _BV
|
|
#define _BV(bit) (1<<(bit))
|
|
#endif
|
|
|
|
|
|
// 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, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFC, 0xFE, 0xFF, 0xFC, 0xE0,
|
|
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, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8,
|
|
0xF8, 0xF0, 0xF0, 0xE0, 0xE0, 0xC0, 0x80, 0xC0, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x3F, 0x7F,
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 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, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xC7, 0xC7, 0x87, 0x8F, 0x9F, 0x9F, 0xFF, 0xFF, 0xFF,
|
|
0xC1, 0xC0, 0xE0, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFC, 0xFC, 0xFC, 0xFE, 0xFE, 0xFE,
|
|
0xFC, 0xFC, 0xF8, 0xF8, 0xF0, 0xE0, 0xC0, 0xC0, 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, 0xC0, 0xE0, 0xF1, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x1F, 0x0F, 0x0F, 0x87,
|
|
0xE7, 0xFF, 0xFF, 0xFF, 0x1F, 0x1F, 0x3F, 0xF9, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xFD, 0xFF,
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x3F, 0x0F, 0x07, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0xF0, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
|
|
0x7E, 0x3F, 0x3F, 0x0F, 0x1F, 0xFF, 0xFF, 0xFF, 0xFC, 0xF0, 0xE0, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0xFC, 0xF0, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x01,
|
|
0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0F, 0x1F, 0x3F, 0x7F, 0x7F,
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x7F, 0x1F, 0x03, 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,
|
|
};
|
|
|
|
|
|
// 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
|
|
}
|
|
|
|
Adafruit_PCD8544::Adafruit_PCD8544(int8_t SCLK, int8_t DIN, int8_t DC,
|
|
int8_t CS, int8_t RST) : Adafruit_GFX(LCDWIDTH, LCDHEIGHT) {
|
|
_din = DIN;
|
|
_sclk = SCLK;
|
|
_dc = DC;
|
|
_rst = RST;
|
|
_cs = CS;
|
|
}
|
|
|
|
Adafruit_PCD8544::Adafruit_PCD8544(int8_t SCLK, int8_t DIN, int8_t DC,
|
|
int8_t RST) : Adafruit_GFX(LCDWIDTH, LCDHEIGHT) {
|
|
_din = DIN;
|
|
_sclk = SCLK;
|
|
_dc = DC;
|
|
_rst = RST;
|
|
_cs = -1;
|
|
}
|
|
|
|
Adafruit_PCD8544::Adafruit_PCD8544(int8_t DC, int8_t CS, int8_t RST):
|
|
Adafruit_GFX(LCDWIDTH, LCDHEIGHT) {
|
|
// -1 for din and sclk specify using hardware SPI
|
|
_din = -1;
|
|
_sclk = -1;
|
|
_dc = DC;
|
|
_rst = RST;
|
|
_cs = CS;
|
|
}
|
|
|
|
|
|
// the most basic function, set a single pixel
|
|
void Adafruit_PCD8544::drawPixel(int16_t x, int16_t y, uint16_t color) {
|
|
if ((x < 0) || (x >= _width) || (y < 0) || (y >= _height))
|
|
return;
|
|
|
|
int16_t t;
|
|
switch(rotation){
|
|
case 1:
|
|
t = x;
|
|
x = y;
|
|
y = LCDHEIGHT - 1 - t;
|
|
break;
|
|
case 2:
|
|
x = LCDWIDTH - 1 - x;
|
|
y = LCDHEIGHT - 1 - y;
|
|
break;
|
|
case 3:
|
|
t = x;
|
|
x = LCDWIDTH - 1 - y;
|
|
y = t;
|
|
break;
|
|
}
|
|
|
|
if ((x < 0) || (x >= LCDWIDTH) || (y < 0) || (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 Adafruit_PCD8544::getPixel(int8_t x, int8_t y) {
|
|
if ((x < 0) || (x >= LCDWIDTH) || (y < 0) || (y >= LCDHEIGHT))
|
|
return 0;
|
|
|
|
return (pcd8544_buffer[x+ (y/8)*LCDWIDTH] >> (y%8)) & 0x1;
|
|
}
|
|
|
|
|
|
void Adafruit_PCD8544::begin(uint8_t contrast, uint8_t bias) {
|
|
if (isHardwareSPI()) {
|
|
// Setup hardware SPI.
|
|
SPI.begin();
|
|
SPI.setClockDivider(PCD8544_SPI_CLOCK_DIV);
|
|
SPI.setDataMode(SPI_MODE0);
|
|
SPI.setBitOrder(MSBFIRST);
|
|
}
|
|
else {
|
|
// Setup software SPI.
|
|
|
|
// Set software SPI specific pin outputs.
|
|
pinMode(_din, OUTPUT);
|
|
pinMode(_sclk, OUTPUT);
|
|
|
|
// Set software SPI ports and masks.
|
|
clkport = portOutputRegister(digitalPinToPort(_sclk));
|
|
clkpinmask = digitalPinToBitMask(_sclk);
|
|
mosiport = portOutputRegister(digitalPinToPort(_din));
|
|
mosipinmask = digitalPinToBitMask(_din);
|
|
}
|
|
|
|
// Set common pin outputs.
|
|
pinMode(_dc, OUTPUT);
|
|
if (_rst > 0)
|
|
pinMode(_rst, OUTPUT);
|
|
if (_cs > 0)
|
|
pinMode(_cs, OUTPUT);
|
|
|
|
// toggle RST low to reset
|
|
if (_rst > 0) {
|
|
digitalWrite(_rst, LOW);
|
|
delay(500);
|
|
digitalWrite(_rst, HIGH);
|
|
}
|
|
|
|
// get into the EXTENDED mode!
|
|
command(PCD8544_FUNCTIONSET | PCD8544_EXTENDEDINSTRUCTION );
|
|
|
|
// LCD bias select (4 is optimal?)
|
|
command(PCD8544_SETBIAS | bias);
|
|
|
|
// set VOP
|
|
if (contrast > 0x7f)
|
|
contrast = 0x7f;
|
|
|
|
command( PCD8544_SETVOP | contrast); // Experimentally determined
|
|
|
|
|
|
// normal mode
|
|
command(PCD8544_FUNCTIONSET);
|
|
|
|
// Set display to Normal
|
|
command(PCD8544_DISPLAYCONTROL | PCD8544_DISPLAYNORMAL);
|
|
|
|
// 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);
|
|
// Push out pcd8544_buffer to the Display (will show the AFI logo)
|
|
display();
|
|
}
|
|
|
|
|
|
inline void Adafruit_PCD8544::spiWrite(uint8_t d) {
|
|
if (isHardwareSPI()) {
|
|
// Hardware SPI write.
|
|
SPI.transfer(d);
|
|
}
|
|
else {
|
|
// Software SPI write with bit banging.
|
|
for(uint8_t bit = 0x80; bit; bit >>= 1) {
|
|
*clkport &= ~clkpinmask;
|
|
if(d & bit) *mosiport |= mosipinmask;
|
|
else *mosiport &= ~mosipinmask;
|
|
*clkport |= clkpinmask;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Adafruit_PCD8544::isHardwareSPI() {
|
|
return (_din == -1 && _sclk == -1);
|
|
}
|
|
|
|
void Adafruit_PCD8544::command(uint8_t c) {
|
|
digitalWrite(_dc, LOW);
|
|
if (_cs > 0)
|
|
digitalWrite(_cs, LOW);
|
|
spiWrite(c);
|
|
if (_cs > 0)
|
|
digitalWrite(_cs, HIGH);
|
|
}
|
|
|
|
void Adafruit_PCD8544::data(uint8_t c) {
|
|
digitalWrite(_dc, HIGH);
|
|
if (_cs > 0)
|
|
digitalWrite(_cs, LOW);
|
|
spiWrite(c);
|
|
if (_cs > 0)
|
|
digitalWrite(_cs, HIGH);
|
|
}
|
|
|
|
void Adafruit_PCD8544::setContrast(uint8_t val) {
|
|
if (val > 0x7f) {
|
|
val = 0x7f;
|
|
}
|
|
command(PCD8544_FUNCTIONSET | PCD8544_EXTENDEDINSTRUCTION );
|
|
command( PCD8544_SETVOP | val);
|
|
command(PCD8544_FUNCTIONSET);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Adafruit_PCD8544::display(void) {
|
|
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);
|
|
|
|
digitalWrite(_dc, HIGH);
|
|
if (_cs > 0)
|
|
digitalWrite(_cs, LOW);
|
|
for(; col <= maxcol; col++) {
|
|
spiWrite(pcd8544_buffer[(LCDWIDTH*p)+col]);
|
|
}
|
|
if (_cs > 0)
|
|
digitalWrite(_cs, HIGH);
|
|
|
|
}
|
|
|
|
command(PCD8544_SETYADDR ); // no idea why this is necessary but it is to finish the last byte?
|
|
#ifdef enablePartialUpdate
|
|
xUpdateMin = LCDWIDTH - 1;
|
|
xUpdateMax = 0;
|
|
yUpdateMin = LCDHEIGHT-1;
|
|
yUpdateMax = 0;
|
|
#endif
|
|
|
|
}
|
|
|
|
// clear everything
|
|
void Adafruit_PCD8544::clearDisplay(void) {
|
|
memset(pcd8544_buffer, 0, LCDWIDTH*LCDHEIGHT/8);
|
|
updateBoundingBox(0, 0, LCDWIDTH-1, LCDHEIGHT-1);
|
|
cursor_y = cursor_x = 0;
|
|
}
|
|
|
|
/*
|
|
// this doesnt touch the buffer, just clears the display RAM - might be handy
|
|
void Adafruit_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);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
*/
|