This includes all known improvements from other people's spins on the Nokia library, including Alistair Popple's fix to the very poor contrast on newer LCD 6100 displays.

Fork of NokiaLCD by Iftekhar Choudhury

NokiaLCD.cpp

Committer:
plskeggs
Date:
2014-01-26
Revision:
4:0a17a8a82c4c
Parent:
3:ea0c085881f3

File content as of revision 4:0a17a8a82c4c:

/* mbed Nokia LCD Library
 * Copyright (c) 2007-2010, sford
 */

#include "NokiaLCD.h"

#include "mbed.h"

#define NOKIALCD_ROWS 16
#define NOKIALCD_COLS 16
#define NOKIALCD_WIDTH 130
#define NOKIALCD_HEIGHT 132
#define NOKIALCD_FREQUENCY 1000000

NokiaLCD::NokiaLCD(PinName mosi, PinName sclk, PinName cs, PinName rst, LCDType type, int contrast)
        : _spi(mosi, NC, sclk)
        , _rst(rst)
        , _cs(cs) {

    _type = type;

    _row = 0;
    _column = 0;
    _foreground = 0x00FFFFFF;
    _background = 0x00000000;
    _rows = NOKIALCD_ROWS;
    _columns = NOKIALCD_COLS;

    //reset(contrast);
}

void NokiaLCD::reset(int contrast) {

    // setup the SPI interface and bring display out of reset
    _cs = 1;
    _rst = 0;
    _spi.format(9);
    _spi.frequency(NOKIALCD_FREQUENCY);
    wait_ms(1);
    _rst = 1;
    wait_ms(1);

    _cs = 0;

    switch (_type) {
        case LCD6100:
            command(0xCA); // display control
            data(0xC);
            data(32);
            data(0);
            command(0xBB);
            data(1);
            command(0xD1); // oscillator on
            command(0x94); // sleep out
            command(0x20); // power control
            data(0x0F);
            command(0xA7); // invert display
            command(0xBC); // data control
            data(0);
            data(1);
            data(4);
            command(0x81); // Voltage control
            data(contrast);// contrast setting: 0..63
            data(3);       // 3 resistance ratio
            wait_ms(10);    // allow power supply to stabilize
            command(0xAF);  // turn on the display
            break;
            
        case LCD6610:
            command(0xCA);    // display control
            data(0);
            data(32);
            data(0);
            command(0xBB);
            data(1);
            command(0xD1); // oscillator on
            command(0x94); // sleep out
            command(0x20); // power control
            data(0x0F);
            command(0xA7); // invert display
            command(0x81); // Voltage control
            data(contrast);// contrast setting: 0..63
            data(3);       // resistance ratio
            wait_ms(1);
            command(0xBC);
            data(0);
            data(0);
            data(2);
            command(0xAF);  // turn on the display
            break;
            
        case PCF8833:
            command(0x11);  // sleep out
            command(0x3A);  // column mode
            data(0x05);
            command(0x36);  // madctl
            data(0x60);     // vertical RAM, flip x
            command(0x25);  // setcon
            data(contrast);// contrast 0x30
            wait_ms(2);
            command(0x29);//DISPON
            command(0x03);//BSTRON
            break;
    }

    _cs = 1;

    cls();
}

void NokiaLCD::command(int value) {
    _spi.write(value & 0xFF);
}

void NokiaLCD::data(int value) {
    _spi.write(value | 0x100);
}

void NokiaLCD::_window(int x, int y, int width, int height) {
    int x1 = x + 0;
    int y1 = y + 0;
    int x2 = x1 + width - 1;
    int y2 = y1 + height - 1;

    switch (_type) {
        case LCD6100:
        case LCD6610:
            command(0x15); // column
            data(x1);
            data(x2);
            command(0x75); // row
            data(y1);
            data(y2);
            command(0x5C); // start write to ram
            break;
        case PCF8833:
            command(0x2A);  // column
            data(x1);
            data(x2);
            command(0x2B); // row
            data(y1);
            data(y2);
            command(0x2C); // start write to ram
            break;
    }
}

void NokiaLCD::_putp(int colour) {
    int gr = ((colour >> 20) & 0x0F)
             | ((colour >> 8 ) & 0xF0);
    int nb = ((colour >> 4 ) & 0x0F);
    data(nb);
    data(gr);
}

const unsigned char FONT8x8[97][8] = {
    0x08,0x08,0x08,0x00,0x00,0x00,0x00,0x00, // columns, rows, num_bytes_per_char
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // space 0x20
    0x30,0x78,0x78,0x30,0x30,0x00,0x30,0x00, // !
    0x6C,0x6C,0x6C,0x00,0x00,0x00,0x00,0x00, // "
    0x6C,0x6C,0xFE,0x6C,0xFE,0x6C,0x6C,0x00, // #
    0x18,0x3E,0x60,0x3C,0x06,0x7C,0x18,0x00, // $
    0x00,0x63,0x66,0x0C,0x18,0x33,0x63,0x00, // %
    0x1C,0x36,0x1C,0x3B,0x6E,0x66,0x3B,0x00, // &
    0x30,0x30,0x60,0x00,0x00,0x00,0x00,0x00, // '
    0x0C,0x18,0x30,0x30,0x30,0x18,0x0C,0x00, // (
    0x30,0x18,0x0C,0x0C,0x0C,0x18,0x30,0x00, // )
    0x00,0x66,0x3C,0xFF,0x3C,0x66,0x00,0x00, // *
    0x00,0x30,0x30,0xFC,0x30,0x30,0x00,0x00, // +
    0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x30, // ,
    0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0x00, // -
    0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00, // .
    0x03,0x06,0x0C,0x18,0x30,0x60,0x40,0x00, // / (forward slash)
    0x3E,0x63,0x63,0x6B,0x63,0x63,0x3E,0x00, // 0 0x30
    0x18,0x38,0x58,0x18,0x18,0x18,0x7E,0x00, // 1
    0x3C,0x66,0x06,0x1C,0x30,0x66,0x7E,0x00, // 2
    0x3C,0x66,0x06,0x1C,0x06,0x66,0x3C,0x00, // 3
    0x0E,0x1E,0x36,0x66,0x7F,0x06,0x0F,0x00, // 4
    0x7E,0x60,0x7C,0x06,0x06,0x66,0x3C,0x00, // 5
    0x1C,0x30,0x60,0x7C,0x66,0x66,0x3C,0x00, // 6
    0x7E,0x66,0x06,0x0C,0x18,0x18,0x18,0x00, // 7
    0x3C,0x66,0x66,0x3C,0x66,0x66,0x3C,0x00, // 8
    0x3C,0x66,0x66,0x3E,0x06,0x0C,0x38,0x00, // 9
    0x00,0x18,0x18,0x00,0x00,0x18,0x18,0x00, // :
    0x00,0x18,0x18,0x00,0x00,0x18,0x18,0x30, // ;
    0x0C,0x18,0x30,0x60,0x30,0x18,0x0C,0x00, // <
    0x00,0x00,0x7E,0x00,0x00,0x7E,0x00,0x00, // =
    0x30,0x18,0x0C,0x06,0x0C,0x18,0x30,0x00, // >
    0x3C,0x66,0x06,0x0C,0x18,0x00,0x18,0x00, // ?
    0x3E,0x63,0x6F,0x69,0x6F,0x60,0x3E,0x00, // @ 0x40
    0x18,0x3C,0x66,0x66,0x7E,0x66,0x66,0x00, // A
    0x7E,0x33,0x33,0x3E,0x33,0x33,0x7E,0x00, // B
    0x1E,0x33,0x60,0x60,0x60,0x33,0x1E,0x00, // C
    0x7C,0x36,0x33,0x33,0x33,0x36,0x7C,0x00, // D
    0x7F,0x31,0x34,0x3C,0x34,0x31,0x7F,0x00, // E
    0x7F,0x31,0x34,0x3C,0x34,0x30,0x78,0x00, // F
    0x1E,0x33,0x60,0x60,0x67,0x33,0x1F,0x00, // G
    0x66,0x66,0x66,0x7E,0x66,0x66,0x66,0x00, // H
    0x3C,0x18,0x18,0x18,0x18,0x18,0x3C,0x00, // I
    0x0F,0x06,0x06,0x06,0x66,0x66,0x3C,0x00, // J
    0x73,0x33,0x36,0x3C,0x36,0x33,0x73,0x00, // K
    0x78,0x30,0x30,0x30,0x31,0x33,0x7F,0x00, // L
    0x63,0x77,0x7F,0x7F,0x6B,0x63,0x63,0x00, // M
    0x63,0x73,0x7B,0x6F,0x67,0x63,0x63,0x00, // N
    0x3E,0x63,0x63,0x63,0x63,0x63,0x3E,0x00, // O
    0x7E,0x33,0x33,0x3E,0x30,0x30,0x78,0x00, // P 0x50
    0x3C,0x66,0x66,0x66,0x6E,0x3C,0x0E,0x00, // Q
    0x7E,0x33,0x33,0x3E,0x36,0x33,0x73,0x00, // R
    0x3C,0x66,0x30,0x18,0x0C,0x66,0x3C,0x00, // S
    0x7E,0x5A,0x18,0x18,0x18,0x18,0x3C,0x00, // T
    0x66,0x66,0x66,0x66,0x66,0x66,0x7E,0x00, // U
    0x66,0x66,0x66,0x66,0x66,0x3C,0x18,0x00, // V
    0x63,0x63,0x63,0x6B,0x7F,0x77,0x63,0x00, // W
    0x63,0x63,0x36,0x1C,0x1C,0x36,0x63,0x00, // X
    0x66,0x66,0x66,0x3C,0x18,0x18,0x3C,0x00, // Y
    0x7F,0x63,0x46,0x0C,0x19,0x33,0x7F,0x00, // Z
    0x3C,0x30,0x30,0x30,0x30,0x30,0x3C,0x00, // [
    0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x00, // \ (back slash)
    0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00, // ]
    0x08,0x1C,0x36,0x63,0x00,0x00,0x00,0x00, // ^
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF, // _
    0x18,0x18,0x0C,0x00,0x00,0x00,0x00,0x00, // ` 0x60
    0x00,0x00,0x3C,0x06,0x3E,0x66,0x3B,0x00, // a
    0x70,0x30,0x3E,0x33,0x33,0x33,0x6E,0x00, // b
    0x00,0x00,0x3C,0x66,0x60,0x66,0x3C,0x00, // c
    0x0E,0x06,0x3E,0x66,0x66,0x66,0x3B,0x00, // d
    0x00,0x00,0x3C,0x66,0x7E,0x60,0x3C,0x00, // e
    0x1C,0x36,0x30,0x78,0x30,0x30,0x78,0x00, // f
    0x00,0x00,0x3B,0x66,0x66,0x3E,0x06,0x7C, // g
    0x70,0x30,0x36,0x3B,0x33,0x33,0x73,0x00, // h
    0x18,0x00,0x38,0x18,0x18,0x18,0x3C,0x00, // i
    0x06,0x00,0x06,0x06,0x06,0x66,0x66,0x3C, // j
    0x70,0x30,0x33,0x36,0x3C,0x36,0x73,0x00, // k
    0x38,0x18,0x18,0x18,0x18,0x18,0x3C,0x00, // l
    0x00,0x00,0x66,0x7F,0x7F,0x6B,0x63,0x00, // m
    0x00,0x00,0x7C,0x66,0x66,0x66,0x66,0x00, // n
    0x00,0x00,0x3C,0x66,0x66,0x66,0x3C,0x00, // o
    0x00,0x00,0x6E,0x33,0x33,0x3E,0x30,0x78, // p
    0x00,0x00,0x3B,0x66,0x66,0x3E,0x06,0x0F, // q
    0x00,0x00,0x6E,0x3B,0x33,0x30,0x78,0x00, // r
    0x00,0x00,0x3E,0x60,0x3C,0x06,0x7C,0x00, // s
    0x08,0x18,0x3E,0x18,0x18,0x1A,0x0C,0x00, // t
    0x00,0x00,0x66,0x66,0x66,0x66,0x3B,0x00, // u
    0x00,0x00,0x66,0x66,0x66,0x3C,0x18,0x00, // v
    0x00,0x00,0x63,0x6B,0x7F,0x7F,0x36,0x00, // w
    0x00,0x00,0x63,0x36,0x1C,0x36,0x63,0x00, // x
    0x00,0x00,0x66,0x66,0x66,0x3E,0x06,0x7C, // y
    0x00,0x00,0x7E,0x4C,0x18,0x32,0x7E,0x00, // z
    0x0E,0x18,0x18,0x70,0x18,0x18,0x0E,0x00, // {
    0x0C,0x0C,0x0C,0x00,0x0C,0x0C,0x0C,0x00, // |
    0x70,0x18,0x18,0x0E,0x18,0x18,0x70,0x00, // }
    0x3B,0x6E,0x00,0x00,0x00,0x00,0x00,0x00, // ~
    0x1C,0x36,0x36,0x1C,0x00,0x00,0x00,0x00
}; // DEL

void NokiaLCD::locate(int column, int row) {
    _column = column;
    _row = row;
}

void NokiaLCD::newline() {
    _column = 0;
    _row++;
    if (_row >= _rows) {
        _row = 0;
    }
}

int NokiaLCD::_putc(int value) {
    int x = _column * 8;  // FIXME: Char sizes
    int y = _row * 8;
    bitblit(x + 1, y + 1, 8, 8, (char*)&(FONT8x8[value - 0x1F][0]));

    _column++;

    if (_column >= NOKIALCD_COLS) {
        _row++;
        _column = 0;
    }

    if (_row >= NOKIALCD_ROWS) {
        _row = 0;
    }

    return value;
}

void NokiaLCD::cls() {
    fill(0, 0, NOKIALCD_WIDTH, NOKIALCD_HEIGHT, _background);
    _row = 0;
    _column = 0;
}


void NokiaLCD::window(int x, int y, int width, int height) {
    _cs = 0;
    _window(x, y, width, height);
    _cs = 1;
}

void NokiaLCD::putp(int colour) {
    _cs = 0;
    _putp(colour);
    _cs = 1;
}

void NokiaLCD::pixel(int x, int y, int colour) {
    _cs = 0;
    _window(x, y, 1, 1);
    _putp(colour);
    _cs = 1;
}

void NokiaLCD::fill(int x, int y, int width, int height, int colour) {
    _cs = 0;
    _window(x, y, width, height);
    switch (_type) {
        case LCD6100:
        case PCF8833:
            for (int i=0; i<width*height; i++) {
                _putp(colour);
            }
            break;
        case LCD6610:
            for (int i=0; i<width*height/2; i++) {
                int r4 = (colour >> (16 + 4)) & 0xF;
                int g4 = (colour >> (8 + 4)) & 0xF;
                int b4 = (colour >> (0 + 4)) & 0xF;
                int d1 = (r4 << 4) | g4;
                int d2 = (b4 << 4) | r4;
                int d3 = (g4 << 4) | b4;
                data(d1); 
                data(d2);   
                data(d3);
            }
            break;
    }
    _window(0, 0, NOKIALCD_WIDTH, NOKIALCD_HEIGHT);
    _cs = 1;
}

void NokiaLCD::circle(int x0, int y0, int r, int colour) {
    int draw_x0, draw_y0;
    int draw_x1, draw_y1;
    int draw_x2, draw_y2;
    int draw_x3, draw_y3;
    int draw_x4, draw_y4;
    int draw_x5, draw_y5;
    int draw_x6, draw_y6;
    int draw_x7, draw_y7;
    int xx, yy;
    int di;    
 
    _cs = 0;
    _window(x0, y0, 1, 1);
 
    if(r == 0)          /* no radius */
    {
        return;
    }
 
    draw_x0 = draw_x1 = x0;
    draw_y0 = draw_y1 = y0 + r;
    if(draw_y0 < NOKIALCD_HEIGHT)
    {
        _window(draw_x0, draw_y0, 1, 1);
        _putp(colour);     /* 90 degree */
    }
 
    draw_x2 = draw_x3 = x0;
    draw_y2 = draw_y3 = y0 - r;
    if(draw_y2 >= 0)
    {
        _window(draw_x2, draw_y2, 1, 1);
        _putp(colour);    /* 270 degree */
    }
    
    draw_x4 = draw_x6 = x0 + r;
    draw_y4 = draw_y6 = y0;
    if(draw_x4 < NOKIALCD_WIDTH)
    {
        _window(draw_x4, draw_y4, 1, 1);
        _putp(colour);     /* 0 degree */
    }    
    
    draw_x5 = draw_x7 = x0 - r;
    draw_y5 = draw_y7 = y0;
    if(draw_x5>=0)
    {
        _window(draw_x5, draw_y5, 1, 1);
        _putp(colour);     /* 90 degree */     /* 180 degree */
    }
        
    if(r == 1)
    {
        return;
    }    
    
    di = 3 - 2*r;
    xx = 0;
    yy = r;
    while(xx < yy)
    {
 
        if(di < 0)
        {
            di += 4*xx + 6;
        }
        else
        {
            di += 4*(xx - yy) + 10;
            yy--;
            draw_y0--;
            draw_y1--;
            draw_y2++;
            draw_y3++;
            draw_x4--;
            draw_x5++;
            draw_x6--;
            draw_x7++;
        }
        xx++;
        draw_x0++;
        draw_x1--;
        draw_x2++;
        draw_x3--;
        draw_y4++;
        draw_y5++;
        draw_y6--;
        draw_y7--;
 
        if( (draw_x0 <= NOKIALCD_WIDTH) && (draw_y0>=0) )
        {
            _window(draw_x0, draw_y0, 1, 1);
            _putp(colour);
        }
 
        if( (draw_x1 >= 0) && (draw_y1 >= 0) )
        {
            _window(draw_x1, draw_y1, 1, 1);
            _putp(colour);
        }
 
        if( (draw_x2 <= NOKIALCD_WIDTH) && (draw_y2 <= NOKIALCD_HEIGHT) )
        {
            _window(draw_x2, draw_y2, 1, 1);
            _putp(colour);
        }
 
        if( (draw_x3 >=0 ) && (draw_y3 <= NOKIALCD_HEIGHT) )
        {
            _window(draw_x3, draw_y3, 1, 1);
            _putp(colour);
        }
 
        if( (draw_x4 <= /*OLED_DISPLAY_HEIGHT*/NOKIALCD_WIDTH) && (draw_y4 >= 0) )
        {
            _window(draw_x4, draw_y4, 1, 1);
            _putp(colour);
        }
 
        if( (draw_x5 >= 0) && (draw_y5 >= 0) )
        {
            _window(draw_x5, draw_y5, 1, 1);
            _putp(colour);
        }
        if( (draw_x6 <= NOKIALCD_WIDTH) && (draw_y6 <= NOKIALCD_HEIGHT) )
        {
            _window(draw_x6, draw_y6, 1, 1);
            _putp(colour);
        }
        if( (draw_x7 >= 0) && (draw_y7 <= NOKIALCD_HEIGHT) )
        {
            _window(draw_x7, draw_y7, 1, 1);
            _putp(colour);
        }
    }
    _cs = 1;
    return;
}
 
void NokiaLCD::line(int x0, int y0, int x1, int y1, int colour) {
    int   dx = 0, dy = 0;
    int   dx_sym = 0, dy_sym = 0;
    int   dx_x2 = 0, dy_x2 = 0;
    int   di = 0;
 
    _cs = 0;            // Chipselect the LCD. 
 
    dx = x1-x0;
    dy = y1-y0;
 
 
    if(dx == 0)           /* vertical line */
    {
        for(int y=y0; y<y1; y++){
            _window(x0, y, 1, 1);
            _putp(colour);
        }
        return;
    }
 
    if(dx > 0)
    {
        dx_sym = 1;
    }
    else
    {
        dx_sym = -1;
    }
 
 
    if(dy == 0)           /* horizontal line */
    {
        for(int x=x0; x<x1; x++){
            _window(x, y0, 1, 1);
            _putp(colour);
        }
        return;
    }
 
 
    if(dy > 0)
    {
        dy_sym = 1;
    }
    else
    {
        dy_sym = -1;
    }
 
    dx = dx_sym*dx;
    dy = dy_sym*dy;
 
    dx_x2 = dx*2;
    dy_x2 = dy*2;
 
    if(dx >= dy)
    {
        di = dy_x2 - dx;
        while(x0 != x1)
        {
 
            _window(x0, y0, 1, 1);
            _putp(colour);
            x0 += dx_sym;
            if(di<0)
            {
                di += dy_x2;
            }
            else
            {
                di += dy_x2 - dx_x2;
                y0 += dy_sym;
            }
        }
        _window(x0, y0, 1, 1);
        _putp(colour);
    }
    else
    {
        di = dx_x2 - dy;
        while(y0 != y1)
        {
            _window(x0, y0, 1, 1);
            _putp(colour);
            y0 += dy_sym;
            if(di < 0)
            {
                di += dx_x2;
            }
            else
            {
                di += dx_x2 - dy_x2;
                x0 += dx_sym;
            }
        }
        _window(x0, y0, 1, 1);
        _putp(colour);
    }
    _cs = 1;
    return;
}

void NokiaLCD::blit(int x, int y, int width, int height, const int* colour) {
    _cs = 0;
    _window(x, y, width, height);

    switch (_type) {
        case LCD6100:
        case PCF8833:
            for (int i=0; i<width*height; i++) {
                 _putp(colour[i]);
             }
             break;
        case LCD6610:
            for (int i=0; i<width*height/2; i++) {
                int r41 = (colour[i*2] >> (16 + 4)) & 0xF;
                int g41 = (colour[i*2] >> (8 + 4)) & 0xF;
                int b41 = (colour[i*2] >> (0 + 4)) & 0xF;
           
                int r42 = (colour[i*2+1] >> (16 + 4)) & 0xF;
                int g42 = (colour[i*2+1] >> (8 + 4)) & 0xF;
                int b42 = (colour[i*2+1] >> (0 + 4)) & 0xF;   
                int d1 = (r41 << 4) | g41;
                int d2 = (b41 << 4) | r42;
                int d3 = (g42 << 4) | b42;               
                   data(d1); 
                data(d2); 
                data(d3); 
            }
            break;
     }            
    _window(0, 0, NOKIALCD_WIDTH, NOKIALCD_HEIGHT);
    _cs = 1;
}

void NokiaLCD::bitblit(int x, int y, int width, int height, const char* bitstream) {
    _cs = 0;
    _window(x, y, width, height);

    switch (_type) {
        case LCD6100:
        case PCF8833:
            for (int i=0; i<height*width; i++) {
                int byte = i / 8;
                int bit = i % 8;
                int colour = ((bitstream[byte] << bit) & 0x80) ? _foreground : _background;
                _putp(colour);
            }
            break;
        case LCD6610:
            for(int i=0; i<height*width/2; i++) {
                int byte1 = (i*2) / 8;
                int bit1 = (i*2) % 8;   
                int colour1 = ((bitstream[byte1] << bit1) & 0x80) ? _foreground : _background;
                int byte2 = (i*2+1) / 8;
                int bit2 = (i*2+1) % 8;   
                int colour2 = ((bitstream[byte2] << bit2) & 0x80) ? _foreground : _background;
    
                int r41 = (colour1 >> (16 + 4)) & 0xF;
                int g41 = (colour1 >> (8 + 4)) & 0xF;
                int b41 = (colour1 >> (0 + 4)) & 0xF;
           
                int r42 = (colour2 >> (16 + 4)) & 0xF;
                int g42 = (colour2 >> (8 + 4)) & 0xF;
                int b42 = (colour2 >> (0 + 4)) & 0xF;   
                int d1 = (r41 << 4) | g41;
                int d2 = (b41 << 4) | r42;
                int d3 = (g42 << 4) | b42;               
                   data(d1); 
                data(d2); 
                data(d3); 
            }
            break;
     }
    _window(0, 0, _width, _height);
    _cs = 1;
}

void NokiaLCD::foreground(int c) {
    _foreground = c;
}

void NokiaLCD::background(int c) {
    _background = c;
}

int NokiaLCD::width() {
    return NOKIALCD_WIDTH;
}

int NokiaLCD::height() {
    return NOKIALCD_HEIGHT;
}

int NokiaLCD::columns() {
    return NOKIALCD_COLS;
}

int NokiaLCD::rows() {
    return NOKIALCD_ROWS;
}