Sparkfun's Nokia Color LCD Arduino Shield library for FRDM boards

Dependents:   ColorLCDShield_Conway

ColorLCDShield.cpp

Committer:
kayekss
Date:
2015-02-19
Revision:
2:f4f7b91314a0
Parent:
1:2d65bfd1218c

File content as of revision 2:f4f7b91314a0:

// ==================================================== Nov 08 2013, kayeks ==
// ColorLCDShield.cpp
// ===========================================================================
// Sparkfun's Color LCD Shield (LCD-09363) library for mbed FRDM-KL25Z boards.
//   - https://www.sparkfun.com/products/9363
//     This library has modified from Sparkfun's Arduino library code
//   - Command/data transmissions are implemented as software SPI communication
//     because the FRDM board's MCUs are not 9-bit SPI capable :(
//   - License is `CC BY-SA 3.0'
//   - Original descriptions are below

/*
  LCDShield.cpp - Arduino Library to control a Nokia 6100 LCD, 
  specifically that found on SparkFun's Color LCD Shield.
  This code should work for both Epson and Phillips display drivers 
  normally found on the Color LCD Shield.
    
  License: CC BY-SA 3.0: Creative Commons Share-alike 3.0. Feel free 
  to use and abuse this code however you'd like. If you find it useful
  please attribute, and SHARE-ALIKE!
  
  This is based on code by Mark Sproul, and Peter Davenport.
  Thanks to Coleman Sellers and Harold Timmis for help getting it to work with the Phillips Driver 7-31-2011
*/

#include "ColorLCDShield.h"

/*extern "C" {
    #include "wiring.h"
}*/

#include "mbed.h"
// #include "Arduino.h"

// static char x_offset = 0;
// static char y_offset = 0;

DigitalOut res(D8);   // Arduino D8
DigitalOut cs(D9);    // Arduino D9
DigitalOut dio(D11);  // Arduino D11
DigitalOut sck(D13);  // Arduino D13

LCDShield::LCDShield()
{
}

void LCDShield::LCDCommand(unsigned char data)
{
    char jj;

    cs = 0;     // enable chip
    dio = 0;   // output low on data out (9th bit low = command)

    sck = 0;   // send clock pulse
    // wait_us(1);
    sck = 1;

    for (jj = 0; jj < 8; jj++)
    {
        if ((data & 0x80) == 0x80)
            dio = 1;
        else
            dio = 0;

        sck = 0; // send clock pulse
        // wait_us(1);
        sck = 1;

        data <<= 1;
    }

    cs = 1;     // disable
}

void LCDShield::LCDData(unsigned char data)
{
    char j;

    cs = 0;     // enable chip
    dio = 1;   // output high on data out (9th bit high = data)

    sck = 0;   // send clock pulse
    // wait_us(1);
    sck = 1;   // send clock pulse

    for (j = 0; j < 8; j++)
    {
        if ((data & 0x80) == 0x80)
            dio = 1;
        else
            dio = 0;
    
        sck = 0; // send clock pulse
        // wait_us(1);
        sck = 1;

        data <<= 1;
    }

    cs = 1;  // disable
}

void LCDShield::init(int type, bool colorSwap)
{
    driver = type;
    
    // Initialize the control pins, and reset display:
    sck = 0; // CLK = LOW
    dio = 0;     // DIO = LOW
    wait_us(10);      // 10us delay
    cs = 1;       // CS = HIGH
    wait_us(10);      // 10uS Delay
    res = 0; // RESET = LOW
    wait_ms(200);                 // 200ms delay
    res = 1; // RESET = HIGH
    wait_ms(200);                 // 200ms delay
    sck = 1; // SCK_PIN = HIGH
    dio = 1;     // DIO = HIGH
    wait_us(10);      // 10us delay
    
    if (driver == EPSON)
    {
        LCDCommand(DISCTL); // Display control (0xCA)
        LCDData(0x0C);      // 12 = 1100 - CL dividing ratio [don't divide] switching period 8H (default)
        LCDData(0x20);      // nlines/4 - 1 = 132/4 - 1 = 32 duty
        LCDData(0x00);      // No inversely highlighted lines
        
        LCDCommand(COMSCN); // common scanning direction (0xBB)
        LCDData(0x01);      // 1->68, 132<-69 scan direction
        
        LCDCommand(OSCON);  // internal oscialltor ON (0xD1)
        LCDCommand(SLPOUT); // sleep out (0x94)
        
        LCDCommand(PWRCTR); // power ctrl (0x20)
        LCDData(0x0F);      // everything on, no external reference resistors
        
        LCDCommand(DISINV); // invert display mode (0xA7)
        
        LCDCommand(DATCTL); // data control (0xBC)
        LCDData(0x03);      // Inverse page address, reverse rotation column address, column scan-direction !!! try 0x01
        LCDData(0x00);      // normal RGB arrangement
        LCDData(0x02);      // 16-bit Grayscale Type A (12-bit color)
        
        LCDCommand(VOLCTR); // electronic volume, this is the contrast/brightness (0x81)
        LCDData(32);        // volume (contrast) setting - fine tuning, original (0-63)
        LCDData(3);         // internal resistor ratio - coarse adjustment (0-7)
        
        LCDCommand(NOP);    // nop (0x25)

        wait_ms(100);

        LCDCommand(DISON);  // display on (0xAF)
    }
    else if (driver == PHILIPS)
    {
        LCDCommand(SLEEPOUT);   // Sleep Out (0x11)
        LCDCommand(BSTRON);     // Booster voltage on (0x03)
        LCDCommand(DISPON);     // Display on (0x29)
        
        //LCDCommand(INVON);        // Inversion on (0x20)
        
        // 12-bit color pixel format:
        LCDCommand(COLMOD);     // Color interface format (0x3A)
        LCDData(0x03);          // 0b011 is 12-bit/pixel mode
        
        LCDCommand(MADCTL);     // Memory Access Control(PHILLIPS)
        if (colorSwap) 
            LCDData(0x08);
        else
            LCDData(0x00);
        
        LCDCommand(SETCON);     // Set Contrast(PHILLIPS)
        LCDData(0x30);
        
        LCDCommand(NOPP);       // nop(PHILLIPS)
    }
}

void LCDShield::clear(int color)
{
    if (driver) // if it's an Epson
    {
        LCDCommand(PASET);
        LCDData(0);
        LCDData(131);

        LCDCommand(CASET);
        LCDData(0);
        LCDData(131);

        LCDCommand(RAMWR);
    }
    else // otherwise it's a phillips
    {
        LCDCommand(PASETP);
        LCDData(0);
        LCDData(131);

        LCDCommand(CASETP);
        LCDData(0);
        LCDData(131);

        LCDCommand(RAMWRP);
    }

    for(unsigned int i=0; i < (131*131)/2; i++)
    {
        LCDData((color>>4)&0x00FF);
        LCDData(((color&0x0F)<<4)|(color>>8));
        LCDData(color&0x0FF);
    }

    // x_offset = 0;
    // y_offset = 0;
}

void LCDShield::contrast(char setting)
{
    if (driver == EPSON)
    {
        setting &= 0x3F;    // 2 msb's not used, mask out
        LCDCommand(VOLCTR); // electronic volume, this is the contrast/brightness(EPSON)
        LCDData(setting);   // volume (contrast) setting - course adjustment,  -- original was 24
        LCDData(3);         // TODO: Make this coarse adjustment variable, 3's a good place to stay
    }
    else if (driver == PHILIPS)
    {
        setting &= 0x7F;    // msb is not used, mask it out
        LCDCommand(SETCON); // contrast command (PHILLIPS)
        LCDData(setting);   // volume (contrast) setting - course adjustment,  -- original was 24
    }
}

// Added by Steve Sparks @ Big Nerd Ranch.
// This swaps the Epson RGB order into the Philips RGB order. (Or, vice versa, I suppose.)
uint16_t LCDShield::swapColors(uint16_t in) {
    return ((in & 0x000F)<<8)|(in & 0x00F0)|((in & 0x0F00)>>8);
}

void LCDShield::setPixel(int color, unsigned char x, unsigned char y)
{
    y   =   (COL_HEIGHT - 1) - y;
    x = (ROW_LENGTH - 1) - x;

    if (driver == EPSON) // if it's an epson
    {
        LCDCommand(PASET);  // page start/end ram
        LCDData(x);
        LCDData(ENDPAGE);

        LCDCommand(CASET);  // column start/end ram
        LCDData(y);
        LCDData(ENDCOL);

        LCDCommand(RAMWR);  // write
        LCDData((color>>4)&0x00FF);
        LCDData(((color&0x0F)<<4)|(color>>8));
        LCDData(color&0x0FF);
    }
    else if (driver == PHILIPS)  // otherwise it's a phillips
    {
        LCDCommand(PASETP); // page start/end ram
        LCDData(x);
        LCDData(x);

        LCDCommand(CASETP); // column start/end ram
        LCDData(y);
        LCDData(y);

        LCDCommand(RAMWRP); // write

        LCDData((unsigned char)((color>>4)&0x00FF));
        LCDData((unsigned char)(((color&0x0F)<<4)|0x00));
    }
}
// 2/18/2013 This Methos added by Tony Contrada in order to create arc segments in varied line thickness, or Filled
void LCDShield::setArc(int x0, int y0, int radius, int arcSegments[], int numSegments, int lineThickness, int color)
{
    //Line Thickness (Num Pixels)
    if(lineThickness == FILL) lineThickness = radius;
    for(int i = 0; i < lineThickness; i++)
    {
        int f = 1 - radius;
        int ddF_x = 0;
        int ddF_y = -2 * radius;
        int x = 0;
        int y = radius;
        while(x < y)
        {
            if(f >= 0)
            {
                y--;
                ddF_y += 2;
                f += ddF_y;
            }
            x++;
            ddF_x += 2;
            f += ddF_x + 1;

            for(int i = 0; i < numSegments; i++)
            {
                if(arcSegments[i] == NNE) setPixel(color, x0 - y, y0 + x); //SHOW NNE
                if(arcSegments[i] == ENE) setPixel(color, x0 - x, y0 + y); //SHOW ENE
                if(arcSegments[i] == ESE) setPixel(color, x0 + x, y0 + y); //SHOW ESE
                if(arcSegments[i] == SSE) setPixel(color, x0 + y, y0 + x); //SHOW SSE
                if(arcSegments[i] == SSW) setPixel(color, x0 + y, y0 - x); //SHOW SSW
                if(arcSegments[i] == WSW) setPixel(color, x0 + x, y0 - y); //SHOW WSW 
                if(arcSegments[i] == WNW) setPixel(color, x0 - x, y0 - y); //SHOW WNW
                if(arcSegments[i] == NNW) setPixel(color, x0 - y, y0 - x); //SHOW NNW
            }
        
        }
        radius--;
    }

}

// 2/22/2013 - Modified by Tony Contrada to include Line Thickness (in pixels) or a Filled Circle
void LCDShield::setCircle (int x0, int y0, int radius, int color, int lineThickness)
{
    if(lineThickness == FILL) lineThickness = radius;
    
    for(int r = 0; r < lineThickness; r++)
    {
        int f = 1 - radius;
        int ddF_x = 0;
        int ddF_y = -2 * radius;
        int x = 0;
        int y = radius;

        setPixel(color, x0, y0 + radius);
        setPixel(color, x0, y0 - radius);
        setPixel(color, x0 + radius, y0);
        setPixel(color, x0 - radius, y0);

        while(x < y)
        {
            if(f >= 0)
            {
                y--;
                ddF_y += 2;
                f += ddF_y;
            }
            x++;
            ddF_x += 2;
            f += ddF_x + 1;

            setPixel(color, x0 + x, y0 + y);
            setPixel(color, x0 - x, y0 + y);
            setPixel(color, x0 + x, y0 - y);
            setPixel(color, x0 - x, y0 - y);
            setPixel(color, x0 + y, y0 + x);
            setPixel(color, x0 - y, y0 + x);
            setPixel(color, x0 + y, y0 - x);
            setPixel(color, x0 - y, y0 - x);
        }
        radius--;
    }

}

void LCDShield::setChar(char c, int x, int y, int fColor, int bColor)
{
    y   =   (COL_HEIGHT - 1) - y; // make display "right" side up
    x   =   (ROW_LENGTH - 2) - x;

    int             i,j;
    unsigned int    nCols;
    unsigned int    nRows;
    unsigned int    nBytes;
    unsigned char   PixelRow;
    unsigned char   Mask;
    unsigned int    Word0;
    unsigned int    Word1;
    const unsigned char   *pFont;
    const unsigned char   *pChar;

    // get pointer to the beginning of the selected font table
    pFont = *FONT8x16;
    // get the nColumns, nRows and nBytes
    nCols = *pFont;
    nRows = *(pFont + 1);
    nBytes = *(pFont + 2);
    // get pointer to the last byte of the desired character
    pChar = pFont + (nBytes * (c - 0x1F)) + nBytes - 1;

    if (driver) // if it's an epson
    {
        // Row address set (command 0x2B)
        LCDCommand(PASET);
        LCDData(x);
        LCDData(x + nRows - 1);
        // Column address set (command 0x2A)
        LCDCommand(CASET);
        LCDData(y);
        LCDData(y + nCols - 1);
    
        // WRITE MEMORY
        LCDCommand(RAMWR);
        // loop on each row, working backwards from the bottom to the top
        for (i = nRows - 1; i >= 0; i--) {
            // copy pixel row from font table and then decrement row
            PixelRow = *(pChar++);
            // loop on each pixel in the row (left to right)
            // Note: we do two pixels each loop
            Mask = 0x80;
            for (j = 0; j < nCols; j += 2) 
            {
                // if pixel bit set, use foreground color; else use the background color
                // now get the pixel color for two successive pixels
                if ((PixelRow & Mask) == 0)
                    Word0 = bColor;
                else
                    Word0 = fColor;
                Mask = Mask >> 1;
                if ((PixelRow & Mask) == 0)
                    Word1 = bColor;
                else
                    Word1 = fColor;
                Mask = Mask >> 1;
                // use this information to output three data bytes
                LCDData((Word0 >> 4) & 0xFF);
                LCDData(((Word0 & 0xF) << 4) | ((Word1 >> 8) & 0xF));
                LCDData(Word1 & 0xFF);
            }
        }
    }
    else
    {
        fColor = swapColors(fColor);
        bColor = swapColors(bColor);

        // Row address set (command 0x2B)
        LCDCommand(PASETP);
        LCDData(x);
        LCDData(x + nRows - 1);
        // Column address set (command 0x2A)
        LCDCommand(CASETP);
        LCDData(y);
        LCDData(y + nCols - 1);
    
        // WRITE MEMORY
        LCDCommand(RAMWRP);
        // loop on each row, working backwards from the bottom to the top
        pChar+=nBytes-1;  // stick pChar at the end of the row - gonna reverse print on phillips
        for (i = nRows - 1; i >= 0; i--) {
            // copy pixel row from font table and then decrement row
            PixelRow = *(pChar--);
            // loop on each pixel in the row (left to right)
            // Note: we do two pixels each loop
            Mask = 0x01;  // <- opposite of epson
            for (j = 0; j < nCols; j += 2) 
            {
                // if pixel bit set, use foreground color; else use the background color
                // now get the pixel color for two successive pixels
                if ((PixelRow & Mask) == 0)
                    Word0 = bColor;
                else
                    Word0 = fColor;
                Mask = Mask << 1; // <- opposite of epson
                if ((PixelRow & Mask) == 0)
                    Word1 = bColor;
                else
                    Word1 = fColor;
                Mask = Mask << 1; // <- opposite of epson
                // use this information to output three data bytes
                LCDData((Word0 >> 4) & 0xFF);
                LCDData(((Word0 & 0xF) << 4) | ((Word1 >> 8) & 0xF));
                LCDData(Word1 & 0xFF);
            }
        }
    }
}


void LCDShield::setStr(char *pString, int x, int y, int fColor, int bColor)
{
    x = x + 16;
    y = y + 8;
    int originalY = y;

    // loop until null-terminator is seen
    while (*pString != 0x00) {
        // draw the character
        setChar(*pString++, x, y, fColor, bColor);
        // advance the y position
        y = y + 8;
        // bail out if y exceeds 131
        if (y > 131) {
            x = x + 16;
            y = originalY;
        }
        if (x > 123) break;
    }
}

void LCDShield::setLine(int x0, int y0, int x1, int y1, int color)
{
    int dy = y1 - y0; // Difference between y0 and y1
    int dx = x1 - x0; // Difference between x0 and x1
    int stepx, stepy;

    if (dy < 0)
    {
        dy = -dy;
        stepy = -1;
    }
    else
        stepy = 1;

    if (dx < 0)
    {
        dx = -dx;
        stepx = -1;
    }
    else
        stepx = 1;

    dy <<= 1; // dy is now 2*dy
    dx <<= 1; // dx is now 2*dx
    setPixel(color, x0, y0);

    if (dx > dy) 
    {
        int fraction = dy - (dx >> 1);
        while (x0 != x1)
        {
            if (fraction >= 0)
            {
                y0 += stepy;
                fraction -= dx;
            }
            x0 += stepx;
            fraction += dy;
            setPixel(color, x0, y0);
        }
    }
    else
    {
        int fraction = dx - (dy >> 1);
        while (y0 != y1)
        {
            if (fraction >= 0)
            {
                x0 += stepx;
                fraction -= dy;
            }
            y0 += stepy;
            fraction += dx;
            setPixel(color, x0, y0);
        }
    }
}

void LCDShield::setRect(int x0, int y0, int x1, int y1, unsigned char fill, int color)
{
    // check if the rectangle is to be filled
    if (fill == 1)
    {
        int xDiff;
    
        if(x0 > x1)
            xDiff = x0 - x1; //Find the difference between the x vars
        else
            xDiff = x1 - x0;
    
        while(xDiff > 0)
        {
            setLine(x0, y0, x0, y1, color);
        
            if(x0 > x1)
                x0--;
            else
                x0++;
        
            xDiff--;
        }

    }
    else 
    {
        // best way to draw an unfilled rectangle is to draw four lines
        setLine(x0, y0, x1, y0, color);
        setLine(x0, y1, x1, y1, color);
        setLine(x0, y0, x0, y1, color);
        setLine(x1, y0, x1, y1, color);
    }
}

void LCDShield::printLogo(void)
{
    int x = 4, y = 25, logo_ix = 0, z;
    char logo;

    for (logo_ix = 0; logo_ix < 1120; logo_ix++)
    {
        logo = logo_spark[logo_ix];
        for (z = 0; z < 8; z++)
        {
            if ((logo & 0x80) == 0x80) setPixel(RED, y, x);
            x++;
            if (x == 132)
            {
                x = 4;
                y++;
            }
            logo <<= 1;
        }
    }
}

void LCDShield::off(void)
{
    if (driver) // If it's an epson
        LCDCommand(DISOFF);
    else // otherwise it's a phillips
        LCDCommand(DISPOFF);
}

void LCDShield::on(void)
{
    if (driver) // If it's an epson
        LCDCommand(DISON);
    else // otherwise it's a phillips
        LCDCommand(DISPON);
}