tessel-nokia5110
Version:
Nokia 5110 display for Tessel
751 lines (666 loc) • 16.2 kB
JavaScript
var tessel = require('tessel');
var util = require('util');
var EventEmitter = require('events').EventEmitter;
var fonts = require('./fonts.js');
// Screen is six rows (pages) with each byte being eight vertical pixels.
var LCD_WIDTH = 84;
var LCD_HEIGHT = 48;
var LEFT = 0;
var RIGHT = 9999;
var CENTER = 9998;
var LCD_COMMAND = 0;
var LCD_DATA = 1;
var PCD8544_POWERDOWN = 0x04;
var PCD8544_ENTRYMODE = 0x02;
var PCD8544_EXTENDEDINSTRUCTION = 0x01;
var PCD8544_DISPLAYBLANK = 0x00;
var PCD8544_DISPLAYNORMAL = 0x04;
var PCD8544_DISPLAYALLON = 0x01;
var PCD8544_DISPLAYINVERTED = 0x05;
// H = 0
var PCD8544_FUNCTIONSET = 0x20;
var PCD8544_DISPLAYCONTROL = 0x08;
var PCD8544_SETYADDR = 0x40;
var PCD8544_SETXADDR = 0x80;
// H = 1
var PCD8544_SETTEMP = 0x04;
var PCD8544_SETBIAS = 0x10;
var PCD8544_SETVOP = 0x80;
// Display presets
var LCD_BIAS = 0x03;
var LCD_TEMP = 0x02;
var LCD_CONTRAST = 0x46;
function Nokia5110 (hardware, cb){
var self = this;
self.hardware = hardware;
self.sce = hardware.pin['G1'].output().low();
self.dc = hardware.pin['G2'].output().low();
self.backlight = hardware.pin['G3'].output().low();
self.scrbuf = new Buffer(504);
// Default the font to SmallFont in fonts.js
self.fonts = fonts;
self.cfont = {
font:fonts.SmallFont,
x_size:fonts.SmallFont[0],
y_size:fonts.SmallFont[1],
offset:fonts.SmallFont[2],
numchars:fonts.SmallFont[3],
inverted:0
};
self.CENTER = CENTER;
self.LEFT = LEFT;
self.RIGHT = RIGHT;
self.spi = new hardware.SPI({
clockSpeed: 4 * 1000000,
dataMode: 0
});
self._lcdWrite = function(mode, data, cb){
var self = this;
self.dc.output(mode);
self.sce.low();
self.spi.send(data, function (err){
self.sce.high();
cb(err);
});
};
var contrast = 0x3a; //0x3a;
var initCommands = [PCD8544_FUNCTIONSET | PCD8544_EXTENDEDINSTRUCTION,
PCD8544_SETBIAS | LCD_BIAS,
PCD8544_SETVOP | LCD_CONTRAST,
PCD8544_SETTEMP | LCD_TEMP,
PCD8544_FUNCTIONSET,
PCD8544_SETYADDR,
PCD8544_SETXADDR,
PCD8544_DISPLAYCONTROL | PCD8544_DISPLAYNORMAL
];
self._lcdWrite(LCD_COMMAND, new Buffer(initCommands), function(err){
self.emit('ready');
if(cb)
cb(err, self);
});
};
// Inherit event emission
util.inherits(Nokia5110, EventEmitter);
// Define the API
Nokia5110.prototype.setMode = function(mode, cb){
if(mode != PCD8544_DISPLAYALLON && mode != PCD8544_DISPLAYNORMAL && mode != PCD8544_DISPLAYINVERTED && mode != PCD8544_DISPLAYBLANK)
cb("Invalid Mode");
else{
this._lcdWrite(LCD_COMMAND, new Buffer([PCD8544_DISPLAYCONTROL | mode]), function(err){
if(cb)
cb(err);
});
}
};
Nokia5110.prototype.update = function(cb){
var self = this;
self._lcdWrite(LCD_COMMAND, new Buffer([PCD8544_SETYADDR, PCD8544_SETXADDR]), function(err){
if(err)
cb(err);
else{
self._lcdWrite(LCD_DATA, self.scrbuf, function(err){
if(cb)
cb(err);
});
}
});
};
Nokia5110.prototype.clear = function (){
for (var c = 0; c < 504; c++)
this.scrbuf[c] = 0;
};
Nokia5110.prototype.fill = function(){
for (var c = 0; c < 504; c++)
this.scrbuf[c] = 255;
}
Nokia5110.prototype.setContrast = function(contrast, cb){
if(contrast > 0x7F)
contrast = 0x7F;
if(contrast < 0)
contrast = 0;
var commands = new Buffer([PCD8544_FUNCTIONSET | PCD8544_EXTENDEDINSTRUCTION,
PCD8544_SETVOP | contrast,
PCD8544_FUNCTIONSET]);
this._lcdWrite(LCD_COMMAND, commands, function(err){
if(cb)
cb(err);
});
}
Nokia5110.prototype.setPixel = function(x, y) {
if((x >= 0) && (x < 84) && (y >= 0) && (y < 48)){
var by = (parseInt((y/8))*84) + x;
var bi = y % 8;
this.scrbuf[by] = this.scrbuf[by] | (1 << bi);
}
};
Nokia5110.prototype.clrPixel = function(x, y){
if ((x>=0) && (x<84) && (y>=0) && (y<48)){
var by=(parseInt((y/8))*84)+x;
var bi=y % 8;
this.scrbuf[by]=this.scrbuf[by] & ~(1<<bi);
}
};
Nokia5110.prototype.invPixel = function(x, y){
if ((x>=0) && (x<84) && (y>=0) && (y<48)){
var by=(parseInt((y/8))*84)+x;
var bi=y % 8;
if ((this.scrbuf[by] & (1<<bi))==0)
this.scrbuf[by]=this.scrbuf[by] | (1<<bi);
else
this.scrbuf[by]=this.scrbuf[by] & ~(1<<bi);
}
};
Nokia5110.prototype.print = function(st, x, y){
var ch;
var stl = st.length;
if (x == RIGHT)
x = 84-(stl*this.cfont.x_size);
if (x == CENTER)
x = parseInt((84-(stl*this.cfont.x_size))/2);
for (var cnt=0; cnt<stl; cnt++)
this._print_char(st[cnt], x + (cnt*this.cfont.x_size), y);
};
Nokia5110.prototype.printNumI = function(num, x, y, length, filler){
var buf = new Array(25);
var st = new Array(27);
var neg = false;
var c=0;
var f=0;
if (num==0)
{
if (length!=0)
{
for (c=0; c<(length-1); c++)
st[c]=filler;
st[c]=48;
st[c+1]=0;
}
else
{
st[0]=48;
st[1]=0;
}
}
else
{
if (num<0)
{
neg=true;
num=-num;
}
while (num>0)
{
buf[c]=48+(num % 10);
c++;
num=parseInt((num-(num % 10))/10);
}
buf[c]=0;
if (neg)
{
st[0]=45;
}
if (length>(c+neg))
{
for (var i=0; i<(length-c-neg); i++)
{
st[i+neg]=filler;
f++;
}
}
for (var i=0; i<c; i++)
{
st[i+neg+f]=buf[c-i-1];
}
st[c+neg+f]=0;
}
this.print(st,x,y);
};
Nokia5110.prototype._print_char = function(c, x, y){
if ((this.cfont.y_size % 8) == 0)
{
var font_idx = ((c.charCodeAt(0) - this.cfont.offset)*(this.cfont.x_size*(parseInt(this.cfont.y_size/8))))+4;
for (var rowcnt=0; rowcnt<(parseInt(this.cfont.y_size/8)); rowcnt++)
{
for(var cnt=0; cnt<this.cfont.x_size; cnt++)
{
for (var b=0; b<8; b++)
if ((this.cfont.font[font_idx+cnt+(rowcnt*this.cfont.x_size)] & (1<<b))!=0)
if (this.cfont.inverted==0)
this.setPixel(x+cnt, y+(rowcnt*8)+b);
else
this.clrPixel(x+cnt, y+(rowcnt*8)+b);
else
if (this.cfont.inverted==0)
this.clrPixel(x+cnt, y+(rowcnt*8)+b);
else
this.setPixel(x+cnt, y+(rowcnt*8)+b);
}
}
}
else
{
var font_idx = ((c.charCodeAt(0) - this.cfont.offset)*(parseInt(this.cfont.x_size*this.cfont.y_size/8)))+4;
var cbyte=this.cfont.font[font_idx];
var cbit=7;
for (var cx=0; cx<this.cfont.x_size; cx++)
{
for (var cy=0; cy<this.cfont.y_size; cy++)
{
if ((cbyte & (1<<cbit)) != 0)
if (this.cfont.inverted==0)
this.setPixel(x+cx, y+cy);
else
this.clrPixel(x+cx, y+cy);
else
if (this.cfont.inverted==0)
this.clrPixel(x+cx, y+cy);
else
this.setPixel(x+cx, y+cy);
cbit--;
if (cbit<0)
{
cbit=7;
font_idx++;
cbyte=this.cfont.font[font_idx];
}
}
}
}
};
Nokia5110.prototype.setFont = function (font){
this.cfont.font = font;
this.cfont.x_size = font[0];
this.cfont.y_size=font[1];
this.cfont.offset=font[2];
this.cfont.numchars=font[3];
this.cfont.inverted=0;
};
Nokia5110.prototype.drawHLine = function(x, y, l){
if ((x>=0) && (x<84) && (y>=0) && (y<48)){
for(var cx = 0; cx < l; cx++){
var by=(parseInt((y/8))*84)+x;
var bi=y % 8;
this.scrbuf[by+cx] |= (1<<bi);
}
}
};
Nokia5110.prototype.clrHLine = function(x, y, l){
if ((x>=0) && (x<84) && (y>=0) && (y<48)){
for (var cx=0; cx<l; cx++){
var by=(parseInt((y/8))*84)+x;
var bi=y % 8;
this.scrbuf[by+cx] &= ~(1<<bi);
}
}
};
Nokia5110.prototype.drawVLine = function(x, y, l){
if ((x>=0) && (x<84) && (y>=0) && (y<48)){
for (var cy=0; cy<l; cy++){
this.setPixel(x, y+cy);
}
}
};
Nokia5110.prototype.clrVLine = function(x, y, l){
if ((x>=0) && (x<84) && (y>=0) && (y<48)){
for (var cy=0; cy<l; cy++){
this.clrPixel(x, y+cy);
}
}
};
Nokia5110.prototype.drawLine = function(x1, y1, x2, y2){
var tmp = 0,
delta = 0,
tx = 0,
ty = 0,
m = 0,
b = 0,
dx = 0,
dy = 0;
if (((x2-x1)<0)){
tmp=x1;
x1=x2;
x2=tmp;
tmp=y1;
y1=y2;
y2=tmp;
}
if (((y2-y1)<0)){
tmp=x1;
x1=x2;
x2=tmp;
tmp=y1;
y1=y2;
y2=tmp;
}
if (y1==y2){
if (x1>x2){
tmp=x1;
x1=x2;
x2=tmp;
}
this.drawHLine(x1, y1, x2-x1);
}
else if (x1==x2){
if (y1>y2){
tmp=y1;
y1=y2;
y2=tmp;
}
this.drawVLine(x1, y1, y2-y1);
}
else if (Math.abs(x2-x1)>Math.abs(y2-y1)){
delta=(y2-y1)/(x2-x1);
ty=y1;
if (x1>x2){
for (var i=x1; i>=x2; i--){
this.setPixel(i, parseInt(ty+0.5));
ty=ty-delta;
}
}
else{
for (var i=x1; i<=x2; i++){
this.setPixel(i, parseInt(ty+0.5));
ty=ty+delta;
}
}
}
else{
delta=(x2-x1)/(y2-y1);
tx=x1;
if (y1>y2){
for (var i=y2+1; i>y1; i--){
this.setPixel(parseInt(tx+0.5), i);
tx=tx+delta;
}
}
else{
for (var i=y1; i<y2+1; i++){
this.setPixel(parseInt(tx+0.5), i);
tx=tx+delta;
}
}
}
};
Nokia5110.prototype.clrLine = function(x1, y1, x2, y2){
var tmp = 0,
delta = 0,
tx = 0,
ty = 0,
m = 0,
b = 0,
dx = 0,
dy = 0;
if (((x2-x1)<0)){
tmp=x1;
x1=x2;
x2=tmp;
tmp=y1;
y1=y2;
y2=tmp;
}
if (((y2-y1)<0)){
tmp=x1;
x1=x2;
x2=tmp;
tmp=y1;
y1=y2;
y2=tmp;
}
if (y1==y2){
if (x1>x2){
tmp=x1;
x1=x2;
x2=tmp;
}
this.clrHLine(x1, y1, x2-x1);
}
else if (x1==x2){
if (y1>y2){
tmp=y1;
y1=y2;
y2=tmp;
}
this.clrVLine(x1, y1, y2-y1);
}
else if (Math.abs(x2-x1)>Math.abs(y2-y1)){
delta=(y2-y1)/(x2-x1);
ty=y1;
if (x1>x2){
for (var i=x1; i>=x2; i--){
this.clrPixel(i, parseInt(ty+0.5));
ty=ty-delta;
}
}
else{
for (var i=x1; i<=x2; i++){
this.clrPixel(i, parseInt(ty+0.5));
ty=ty+delta;
}
}
}
else{
delta=(x2-x1)/(y2-y1);
tx=x1;
if (y1>y2){
for (var i=y2+1; i>y1; i--){
this.clrPixel(parseInt(tx+0.5), i);
tx=tx+delta;
}
}
else{
for (var i=y1; i<y2+1; i++){
this.clrPixel(parseInt(tx+0.5), i);
tx=tx+delta;
}
}
}
};
Nokia5110.prototype.drawRect = function(x1, y1, x2, y2){
var tmp;
if (x1>x2){
tmp=x1;
x1=x2;
x2=tmp;
}
if (y1>y2){
tmp=y1;
y1=y2;
y2=tmp;
}
this.drawHLine(x1, y1, x2-x1);
this.drawHLine(x1, y2, x2-x1);
this.drawVLine(x1, y1, y2-y1);
this.drawVLine(x2, y1, y2-y1+1);
};
Nokia5110.prototype.clrRect = function (x1, y1, x2, y2){
var tmp;
if (x1>x2)
{
tmp=x1;
x1=x2;
x2=tmp;
}
if (y1>y2)
{
tmp=y1;
y1=y2;
y2=tmp;
}
this.clrHLine(x1, y1, x2-x1);
this.clrHLine(x1, y2, x2-x1);
this.clrVLine(x1, y1, y2-y1);
this.clrVLine(x2, y1, y2-y1+1);
};
Nokia5110.prototype.drawRoundRect = function(x1, y1, x2, y2){
var tmp;
if (x1>x2)
{
tmp=x1;
x1=x2;
x2=tmp;
}
if (y1>y2)
{
tmp=y1;
y1=y2;
y2=tmp;
}
if ((x2-x1)>4 && (y2-y1)>4)
{
this.setPixel(x1+1,y1+1);
this.setPixel(x2-1,y1+1);
this.setPixel(x1+1,y2-1);
this.setPixel(x2-1,y2-1);
this.drawHLine(x1+2, y1, x2-x1-3);
this.drawHLine(x1+2, y2, x2-x1-3);
this.drawVLine(x1, y1+2, y2-y1-3);
this.drawVLine(x2, y1+2, y2-y1-3);
}
};
Nokia5110.prototype.clrRoundRect = function(x1, y1, x2, y2){
var tmp;
if (x1>x2)
{
tmp=x1;
x1=x2;
x2=tmp;
}
if (y1>y2)
{
tmp=y1;
y1=y2;
y2=tmp;
}
if ((x2-x1)>4 && (y2-y1)>4)
{
this.clrPixel(x1+1,y1+1);
this.clrPixel(x2-1,y1+1);
this.clrPixel(x1+1,y2-1);
this.clrPixel(x2-1,y2-1);
this.clrHLine(x1+2, y1, x2-x1-3);
this.clrHLine(x1+2, y2, x2-x1-3);
this.clrVLine(x1, y1+2, y2-y1-3);
this.clrVLine(x2, y1+2, y2-y1-3);
}
};
Nokia5110.prototype.drawCircle = function (x, y, radius){
var f = 1 - radius;
var ddF_x = 1;
var ddF_y = -2 * radius;
var x1 = 0;
var y1 = radius;
var ch, cl;
this.setPixel(x, y + radius);
this.setPixel(x, y - radius);
this.setPixel(x + radius, y);
this.setPixel(x - radius, y);
while(x1 < y1)
{
if(f >= 0)
{
y1--;
ddF_y += 2;
f += ddF_y;
}
x1++;
ddF_x += 2;
f += ddF_x;
this.setPixel(x + x1, y + y1);
this.setPixel(x - x1, y + y1);
this.setPixel(x + x1, y - y1);
this.setPixel(x - x1, y - y1);
this.setPixel(x + y1, y + x1);
this.setPixel(x - y1, y + x1);
this.setPixel(x + y1, y - x1);
this.setPixel(x - y1, y - x1);
}
};
Nokia5110.prototype.clrCircle = function(x, y, radius){
var f = 1 - radius;
var ddF_x = 1;
var ddF_y = -2 * radius;
var x1 = 0;
var y1 = radius;
var ch, cl;
this.clrPixel(x, y + radius);
this.clrPixel(x, y - radius);
this.clrPixel(x + radius, y);
this.clrPixel(x - radius, y);
while(x1 < y1)
{
if(f >= 0)
{
y1--;
ddF_y += 2;
f += ddF_y;
}
x1++;
ddF_x += 2;
f += ddF_x;
this.clrPixel(x + x1, y + y1);
this.clrPixel(x - x1, y + y1);
this.clrPixel(x + x1, y - y1);
this.clrPixel(x - x1, y - y1);
this.clrPixel(x + y1, y + x1);
this.clrPixel(x - y1, y + x1);
this.clrPixel(x + y1, y - x1);
this.clrPixel(x - y1, y - x1);
}
};
// Unlike _print_char this function doesn't allow you to put a character
// absolutely anywhere. It limits you to the 5110's y lines (0-5). This
// function will be the base of the next version of character printing
// as it is significantly faster than _print_char.
// character - a 5 byte encoding of pixels to create a character.
Nokia5110.prototype.rawCharacter = function(character, x, line){
if(x < 0 || x > 83)
x = 0;
if(line < 0 || line > 5)
line = 0;
// Set the start index based on x and line
var scrbufIndex = (line * 84) + x;
for(var i = 0; i < character.length; i++)
this.scrbuf[scrbufIndex++] = character[i];
};
Nokia5110.prototype.rawPrint = function(str, x, line){
var scrbufIndex = (line * 84) + x;
for(var i = 0; i < str.length; i++){
var charIndex = (str[i].charCodeAt(0) - 0x20) * 5;
for(var j = 0; j < 5; j++)
this.scrbuf[scrbufIndex++] = fonts.ASCII[charIndex++];
// Spacing between chars
scrbufIndex++;
}
};
Nokia5110.prototype.drawBitmap = function (x, y, bitmap, sx, sy){
var bit;
var data;
for (var cy=0; cy<sy; cy++)
{
bit= cy % 8;
for(var cx=0; cx<sx; cx++)
{
data=bitmap[cx+(parseInt(cy/8)*sx)];
if ((data & (1<<bit))>0)
this.setPixel(x+cx, y+cy);
else
this.clrPixel(x+cx, y+cy);
}
}
};
Nokia5110.prototype.drawPreSizedBitmap = function(bitmap){
var len = bitmap.length;
for(var c = 0; c < len; c++){
this.scrbuf[c] = bitmap[c];
}
}
Nokia5110.prototype.setBacklight = function (state){
var self = this;
self.backlight.output(state);
};
// Every module needs a use function which calls the constructor
function use (hardware, callback) {
return new Nokia5110(hardware, callback);
}
// Export functions
exports.use = use;