PCD8544LCD.cpp

Committer:
Wimpie
Date:
2011-05-19
Revision:
1:ce391193b822
Parent:
0:6468a28a7b7d

File content as of revision 1:ce391193b822:

/* mbed PCD8544 - Graphic Library for driving monochrome displays based on
 *  the PCD8544  48 x 84 pixels matrix LCD controller/driver
 *  used in Nokia 3310, 3315, 3330, 3350, 3410, 3210,  5110, 5120, 5130, 5160, 6110, 6150
 *
 * Copyright (c) 2011, Wim De Roeve
 * partial port of the code found on http://serdisplib.sourceforge.net/ser/pcd8544.html#links
 * and by Petras Saduikis <petras@petras.co.uk>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the updaSoftware, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "PCD8544LCD.h"

#include "fonts/font_3x5.h"
#include "fonts/font_5x7.h"
#include "fonts/font_6x8.h"
#include "fonts/font_8x8.h"
#include "fonts/font_8x12.h"
#include "fonts/font_16x20.h"
#include "fonts/font_16x24.h"

#include "DebugTrace.h"
#include "sstream"
#include "stdio.h"
#include "stringman.h"

DebugTrace pc_PCD8544(ON, TO_SERIAL);

/*
       PCD8544 from Philips Semiconductors is
       48 x 84 pixels monochrome matrix LCD controller/driver

       generic for LPH7366, LPH7677, and LPH7779; no backlight

       model name (of display)     type     used in cellphones
       LPH 7366         2     Nokia 5110, 5120, 5130, 5160, 6110, 6150
       LPH 7677         1     Nokia 3210
       LPH 7779         1     Nokia 3310, 3315, 3330, 3350, 3410


       +-------------------------+
       |     1 2 3 4 5 6 7 8     |
       |     # # # # # # # #     |
       |  ===#=#=#=#=#=#=#=#===  |  Red     1 .. VDD  - chip power supply +3.3V
       +--=====================--+  Green   2 .. SCLK - serial clock line of LCD
       |                         |  Yellow  3 .. SI   - serial data input of LCD
       |                         |  Gray    4 .. D/C  - command/data switch
       |        rear view        |  Blue    5 .. /CS  - active low chip select
       |  connector is visible   |  Black   6 .. GND  - for VDD
       |                         |          7 .. Vout - output of display-internal dc/dc converter
       |         LPH7779         |  White   8 .. /RES - active low reset
       |                         |
       +-------------------------+

*/


PCD8544LCD::PCD8544LCD (PinName mosi, PinName miso, PinName sck,
                        PinName cs, PinName data_cmd, PinName reset):
        _spi(mosi, miso, sck),
        _cs(cs),
        _dc(data_cmd),
        _reset(reset) {

    _cs    = HIGH;
    _reset = HIGH;

    init();
}

void PCD8544LCD::init() {

    pc_PCD8544.traceOut("Init PCD8544\r\n");
    _spi.format(8,0);
    _spi.frequency(1000000);

    /* reset lcd

       After reset, the LCD driver has the following state:
       - Power-down mode (bit PD = 1)
       - Horizontal addressing (bit V = 0)
       - normal instruction set (bit H = 0)
       - Display blank (bit E = D = 0)
       - Address counter X6 to X0 = 0; Y2 to Y0 = 0
       - Temperature control mode (TC1 TC0 = 0)
       - Bias system (BS2 to BS0 = 0)
       - VLCD is equal to 0, the HV generator is switched off
        (VOP6 to VOP0 = 0)
       - After power-on, the RAM contents are undefined.
    */

    wait_ms(1);
    _reset = LOW;  // reset
    wait_ms(1);
    _reset = HIGH;

    writeCmd(EXTENDEDSET);   // folowing commands are extended ones
    writeCmd(0xc8);          // Set Voltage 0x80+value: set contrast
    writeCmd(0x06);          // set temp coefficient
    writeCmd(0x13);          // set BIAS mode 1:48
    writeCmd(STANDARDSET);   // STANDARDSET: following commands are standard ones

    writeCmd(NORMAL_MODE);


    _LoMark = 0;
    _HiMark = LCD_CACHE_SIZE - 1;
    //_LoMark = LCD_CACHE_SIZE; // Reset watermark pointers.
    // _HiMark = 0;

    cls();
}

void PCD8544LCD::writeCmd(BYTE data) {
    _cs = LOW;
    _dc = LOW;
    _spi.write(data);
    _cs = HIGH;
}

void PCD8544LCD::writeData(BYTE data) {
    _cs = LOW;
    _dc = HIGH;
    _spi.write(data);
    _cs = HIGH;
}

void PCD8544LCD::close() {
    writeCmd(DISPLAYOFF);
    _cs    = HIGH;
    _reset = HIGH;
}

//  GRAPHICAL functions

void PCD8544LCD::cls(bool fupdate) {
    for (int i = 0; i < LCD_CACHE_SIZE ; i++) {
        LcdCache[i]=0x00;
    }
    _LoMark = 0;
    _HiMark = LCD_CACHE_SIZE - 1;
    if (fupdate)
        update();
}

void PCD8544LCD::update() {

    if ( _LoMark < 0 )
        _LoMark = 0;
    else if ( _LoMark >= LCD_CACHE_SIZE )
        _LoMark = LCD_CACHE_SIZE - 1;
    if ( _HiMark < 0 )
        _HiMark = 0;
    else if ( _HiMark >= LCD_CACHE_SIZE )
        _HiMark = LCD_CACHE_SIZE - 1;

    writeCmd(SET_ADDRES_X | (_LoMark % LCD_X_RES));
    writeCmd(SET_ADDRES_Y | (_LoMark / LCD_X_RES));

    for (int i = _LoMark; i <= _HiMark; i++ ) {
        writeData( LcdCache[i]);
    }
    _LoMark = LCD_CACHE_SIZE - 1;
    _HiMark = 0;
}



void PCD8544LCD::locate(BYTE x0, BYTE y0) {
    LcdCacheIdx = x0*LCD_BANKS + y0 * LCD_X_RES;
}

// Bitmap

void PCD8544LCD::drawBitmap(BYTE x0, BYTE y0, const unsigned char* bitmap, BYTE bmpXSize, BYTE bmpYSize,BYTE fupdate) {
    BYTE row;

    if (0 == bmpYSize % 8)
        row = bmpYSize/8;
    else
        row = bmpYSize/8 + 1;

    _LoMark= 0;
    _HiMark= LCD_CACHE_SIZE - 1;

    for (BYTE n = 0; n < row; n++) {
        locate(x0, y0);

        for (BYTE i = 0; i < bmpXSize; i++) {
            LcdCache[LcdCacheIdx+ i]=bitmap[i + (n * bmpXSize)];
        }
        y0++;
    }
    if (fupdate==TRUE)
        update();
}

void PCD8544LCD::writeString(BYTE x0, BYTE y0, char* string,  eFonts font,eDisplayMode dmode,eSpaceMode smode, BYTE fupdate) {
    locate(x0, y0);
    chooseFont(font);

    while (*string) {
        writeChar(x0,y0,*string++,font, dmode, FALSE);
        x0+=_font_width;   // width +1;
        if (smode==SPACE_NORMAL)
            x0++;
    }
    if (fupdate==TRUE)
        update();
}

void PCD8544LCD::chooseFont(eFonts font) {

    switch (font) {

        case VERYSMALLFONT: {
            _font_width  = FONT3x5_WIDTH;
            _font_height = FONT3x5_HEIGHT;
            _font_start  = FONT3x5_START;
            _font_end    = FONT3x5_END;

            _pFont = (unsigned char*) font_3x5;

            break;
        }
        case TINYFONT: {
            _font_width  = FONT5x7_WIDTH;
            _font_height = FONT5x7_HEIGHT;
            _font_start  = FONT5x7_START;
            _font_end    = FONT5x7_END;

            _pFont = (unsigned char*) font_5x7;

            break;
        }

        case SMALLFONT: {
            _font_width  = FONT6x8_WIDTH;
            _font_height = FONT6x8_HEIGHT;
            _font_start  = FONT6x8_START;
            _font_end    = FONT6x8_END;

            _pFont = (unsigned char*) font_6x8;

            break;
        }
        case NORMALFONT: {
            _font_width  = FONT8x8_WIDTH;
            _font_height = FONT8x8_HEIGHT;
            _font_start  = FONT8x8_START;
            _font_end    = FONT8x8_END;

            _pFont = (unsigned char*) font_8x8;

            break;
        }
        case BIGFONT: {
            _font_width  = FONT8x12_WIDTH;
            _font_height = FONT8x12_HEIGHT;
            _font_start  = FONT8x12_START;
            _font_end    = FONT8x12_END;

            _pFont = (unsigned char*) font_8x12;

            break;
        }

        case TIMENUMBERFONT: {
            _font_width  = FONT16x20_WIDTH;
            _font_height = FONT16x20_HEIGHT;
            _font_start  = FONT16x20_START;
            _font_end    = FONT16x20_END;

            _pFont = (unsigned char*) font_16x20;

            break;
        }

        case BIGNUMBERFONT: {
            _font_width  = FONT16x24_WIDTH;
            _font_height = FONT16x24_HEIGHT;
            _font_start  = FONT16x24_START;
            _font_end    = FONT16x24_END;

            _pFont = (unsigned char*) font_16x24;

            break;
        }
    }
}

void PCD8544LCD::writeChar(BYTE x0, BYTE y0, BYTE ch,  eFonts font, eDisplayMode mode,BYTE fupdate) {
    BYTE sendByte;

    chooseFont(font);

    if ((ch <= _font_start) || (ch > _font_end))
        ch=_font_start;

    ch -= _font_start;

    for (int i = 0; i < _font_width; i++ ) {

        sendByte = *(_pFont + ch*_font_width +i);
        sendByte = ((mode == NORMAL)? sendByte:(sendByte ^ 0xff));

        for (int j=0 ; j<_font_height; j++) {
            if ((sendByte & 0x01) == 0x01) {
                drawpixel(x0,y0+j,PIXEL_ON,FALSE);
            } else {
                drawpixel(x0,y0+j,PIXEL_OFF,FALSE);
            }
            sendByte=sendByte>>1;
        }
        x0++;
    }
    if (fupdate==TRUE)
        update();
}


void PCD8544LCD::drawpixel(BYTE x0, BYTE  y0, ePixelMode mode,BYTE fupdate) {
    uint16_t index;
    BYTE offset;
    BYTE data;

    if ( x0 > LCD_X_RES-1 ) return;
    if ( y0 > LCD_Y_RES-1 ) return;

    index = ((y0 / 8) * LCD_X_RES) + x0;
    offset = y0 - ((y0 / 8) * 8);

    data = LcdCache[index];

    if ( mode == PIXEL_OFF ) {
        data &= (~(0x01 << offset));
    } else if ( mode == PIXEL_ON ) {
        data |= (0x01 << offset);
    } else if ( mode == PIXEL_XOR ) {
        data ^= (0x01 << offset);
    }
    LcdCache[index] = data;

    if ( index < _LoMark ) {
        _LoMark = index;
    }
    if ( index > _HiMark ) {
        _HiMark = index;
    }
    if (fupdate==TRUE)
        update();
}

void PCD8544LCD::drawline(BYTE  x0, BYTE y0, BYTE x1, BYTE y1, ePixelMode mode,BYTE fupdate) {
    int dx, dy, stepx, stepy, fraction;

    dy = y1 - y0;
    dx = x1 - x0;
    if ( dy < 0 ) {
        dy = -dy;
        stepy = -1;
    } else {
        stepy = 1;
    }
    if ( dx < 0 ) {
        dx = -dx;
        stepx = -1;
    } else {
        stepx = 1;
    }
    dx <<= 1;
    dy <<= 1;

    drawpixel( x0, y0, mode , FALSE);
    if ( dx > dy ) {
        fraction = dy - (dx >> 1);
        while ( x0 != x1 ) {
            if ( fraction >= 0 ) {
                y0 += stepy;
                fraction -= dx;
            }
            x0 += stepx;
            fraction += dy;
            drawpixel( x0, y0, mode , FALSE);
        }
    } else {
        fraction = dx - (dy >> 1);
        while ( y0 != y1 ) {
            if ( fraction >= 0 ) {
                x0 += stepx;
                fraction -= dy;
            }
            y0 += stepy;
            fraction += dx;
            drawpixel( x0, y0, mode , FALSE);
        }
    }
    if (fupdate==TRUE)
        update();
}

void PCD8544LCD::drawrectangle(BYTE  x0, BYTE y0, BYTE x1, BYTE y1, eFillMode fill, ePixelMode mode,BYTE fupdate) {
    if (fill==1) {
        BYTE i, xmin, xmax, ymin, ymax;
        if (x0 < x1) { // Find x min and max
            xmin = x0;
            xmax = x1;
        } else {
            xmin = x1;
            xmax = x0;
        }
        if (y0 < y1) { // Find the y min and max
            ymin = y0;
            ymax = y1;
        } else {
            ymin = y1;
            ymax = y0;
        }
        for (; xmin <= xmax; ++xmin) {
            for (i=ymin; i<=ymax; ++i) {
                drawpixel(xmin, i, mode, FALSE);
            }
        }
    } else {
        drawline(x0, y0, x1, y0, mode, FALSE); // Draw the 4 sides
        drawline(x0, y1, x1, y1, mode, FALSE);
        drawline(x0, y0, x0, y1, mode, FALSE);
        drawline(x1, y0, x1, y1, mode, FALSE);
    }
    if (fupdate==TRUE)
        update();
}

void PCD8544LCD::drawprogressbar(BYTE  x0, BYTE y0, BYTE w, BYTE h, BYTE percentage,BYTE fupdate) {
    drawrectangle(x0,y0,x0+w,y0+h,FILL_OFF,PIXEL_ON, FALSE);
    drawrectangle(x0+2,y0+2,x0+w-2,y0+h-2,FILL_ON,PIXEL_OFF, FALSE);
    drawrectangle(x0+2,y0+2,x0+2+(percentage*(w-4)/100),y0+h-2,FILL_ON,PIXEL_ON, FALSE);
    if (fupdate==TRUE)
        update();
}

void PCD8544LCD::drawchart(BYTE  x0, BYTE y0, BYTE w, BYTE h, BYTE unitx, BYTE unity,
                           eRasterMode rMode, eChartMode cMode,eDrawMode dMode, int16_t * val, int size, int t)  {
    int maxy;
    int _scale=1;
    int prescale=1;

    signed char v1,v2;
    char buffer[4];

    if (size>w)
        size=w;

    // search maximum value to calculate scale
    if (dMode==DRAW_OVERWRITE) {
        maxy=0;
        for (int i=0; i<size;i++) {
            if (val[i]>maxy)
                maxy=val[i];

        }

        if (maxy>h) {  //scale can be 1,2,5  *10i
            prescale= ((maxy-1)/((h/unity)*unity));
            _scale=1;
            while (prescale>10) {
                _scale=_scale*10;
                prescale=prescale/10;
            }
            if (prescale>1)
                _scale=_scale*5;
            else
                if (prescale==1)
                    _scale  =_scale*2;


        }
        Scale=_scale;
    }

    if (dMode==DRAW_OVERWRITE) {
        drawrectangle(x0-11,y0-h,x0+w,y0+4+7,FILL_ON,PIXEL_OFF,FALSE);
        drawline(x0,y0,x0,y0-h,PIXEL_ON,FALSE);
        drawline(x0,y0,x0+w,y0,PIXEL_ON,FALSE);

        //drawrectangle(x0,y0-h,x0+w,y0,FILL_OFF,PIXEL_ON,FALSE);

        for (int i=0;i<=h;i++) {
            if ((i % unity) == 0) {
                drawpixel(x0-2,y0-i,PIXEL_ON,FALSE);
                //    drawpixel(x0+w+2,y0-i,PIXEL_ON,FALSE);

                if (rMode==RASTER_ON) {
                    for (int r=0;r<=w;r++) {
                        if ((r % 2) ==0)
                            drawpixel(x0+r,y0-i,PIXEL_ON,FALSE);
                    }
                }
                // draw vertical axis labels

              itostr(buffer,i*Scale);

                //  pc_PCD8544.traceOut(" %i %s |",i*Scale,buffer);
                writeString(x0-11,y0-i+1,buffer,VERYSMALLFONT,NORMAL,SPACE_NONE,FALSE);

            }
            if ((i % 2) == 0) {
                drawpixel(x0-1,y0-i,PIXEL_ON,FALSE);
                //      drawpixel(x0+w+1,y0-i,PIXEL_ON,FALSE);
            }
        }

        for (int i=0;i<=w;i++) {
            if (((i+(t % unitx)) % unitx) == 0) {
                drawpixel(x0+i,y0+2,PIXEL_ON,FALSE);

                if (rMode==RASTER_ON) {
                    for (int r=0;r<=h;r++) {
                        if ((r % 2) ==0)
                            drawpixel(x0+i,y0-r,PIXEL_ON,FALSE);
                    }
                }
                if (((t-w+i)/unitx)>=0)
                    snprintf(buffer,3,"%i",((t-w+i)/unitx));
                else
                    snprintf(buffer,3,"%i",24+((t-w+i)/unitx));

                // pc_PCD8544.traceOut(" %i %s ",(t-w+i)/unitx,buffer);
                writeString(x0+i-3,y0+4,buffer,VERYSMALLFONT,NORMAL,SPACE_NORMAL,FALSE);
            }
            if ((i % 2) == 0) {
                drawpixel(x0+i,y0+1,PIXEL_ON,FALSE);
            }
        }
   //     update();
    }

     for (int i=0;i<size;i++) {
     //   pc_PCD8544.traceOut(" %i ",val[i]);
        v1 = val[i] / Scale;
        if (v1>h)
            v1=h;

        if (i!=(size-1)) {
            v2 = val[i+1] / Scale;
            if (v2>h)
                v2=h;
        } else
            v2=v1;


        switch (cMode) {
            case C_POINT: {
                drawpixel(x0+i,y0-v1,PIXEL_ON,FALSE);
                break;
            }
            case C_LINE: {
                drawline(x0+i,y0-v1,x0+i+1,y0-v2,PIXEL_ON,FALSE);
                break;
            }
            case C_VLINE: {
                drawline(x0+i,y0-v1,x0+i,y0,PIXEL_ON,FALSE);
                break;
            }
        }
    }

    update();
}

/*
void PCD8544LCD::writeCharBig(BYTE x, BYTE y, BYTE ch, eDisplayMode mode) {
    BYTE sendByte;

    unsigned char* pFont = (unsigned char *) font_bignumber;

    if ('.' == ch)
        ch = 10;
    else if ('+' == ch)
        ch = 11;
    else if ('-' == ch)
        ch = 12;
    else
        ch = ch & 0x0f;

    for (BYTE i = 0; i < 3; i++) {
        locate(x, y + i);

        for (BYTE j = 0; j < 16; j++) {
            sendByte =  *(pFont + ch*48 + i*16 + j);
            writeData((mode == NORMAL)? sendByte : (sendByte^0xff));
        }
    }
}


void PCD8544LCD::writeStringBig(BYTE x0, BYTE y0, char* string, eDisplayMode mode, BYTE fupdate) {
    while (*string) {
        writeCharBig(x0, y0, *string , mode);

        if ('.' == *string++)
            x0 += 5;
        else
            x0 += 12;
    }
}
*/