An mbed library for the Embedded Artists QVGA OLED 2.8 inch panel

EAQVGAOLED.cpp

Committer:
gbloice
Date:
2011-03-09
Revision:
5:b3f5d19945ff
Parent:
0:ae3d20db48fc

File content as of revision 5:b3f5d19945ff:

/* mbed library for driving the EA QVGA 2.8" OLED
 * Copyright (c) Graham Bloice 2011
 *
 * 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 Software, 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 "EAQVGAOLED.h"
#include "mbed.h"
 
// Constants
const unsigned int EAQVGAOLED_ID = 0x63D6; // The display controller ID

// Local functions
inline void orderCoords
(
    const uint16_t x0,
    const uint16_t y0,
    const uint16_t x1,
    const uint16_t y1,
    uint16_t &startX,
    uint16_t &startY,
    uint16_t &endX,
    uint16_t &endY
)
{
    // Order the drawing co-ords
    if (x1 > x0) {
        startX = x0;
        endX = x1;
    }
    else {
        startX = x1;
        endX = x0;
    }
    if (y1 > y0) {
        startY = y0;
        endY = y1;
    }
    else {
        startY = y1;
        endY = y0;
    }
}

EAQVGAOLED::EAQVGAOLED
(
    PinName mosi,
    PinName miso,
    PinName sclk,
    PinName cs,
    PinName reset,
    PinName bl
) : _spi(mosi, miso, sclk), _cs(cs), _reset(reset), _bl(bl)
{
    // Initialise the hardware
    initHardware();
    
    // And reset the display
    resetDisplay();
}

void EAQVGAOLED::pixel
(
    int x,
    int y,
    int colour
)
{
    // Set the x and y positions via their registers, then the colour
    writeDataRegister(0x20, x);
    writeDataRegister(0x21, y);
    writeDataRegister(0x22, colour);
}

void EAQVGAOLED::hLine
(
    const uint16_t x0,
    const uint16_t y0,
    const uint16_t x1,
    const uint16_t colour
)
{
    // Make sure we are drawing in the correct direction
    uint16_t startPos;
    uint16_t length; 
    if (x1 > x0) {
        startPos = x0;
        length = x1 - x0;
    }
    else {
        startPos = x1;
        length = x0 - x1;
    }
    
    // Now draw the line, the display is set to auto increment in x
    movePen(startPos, y0);
    for (uint16_t i = 0; i < length; i++) {
        writeData(colour);
    }
}
        
void EAQVGAOLED::vLine
(
    uint16_t x0,
    uint16_t y0,
    uint16_t y1,
    uint16_t colour
)
{
    // Make sure we are drawing in the correct direction
    uint16_t startPos;
    uint16_t endPos; 
    if (y1 > y0) {
        startPos = y0;
        endPos = y1;
    }
    else {
        startPos = y1;
        endPos = y0;
    }
    
    // TODO This might be optimised by setting the Addressing mode bit AM to 1 (reg 03)
    
    // Now draw the line
    for (uint16_t i = startPos; i < endPos; i++) {
        pixel(x0, i, colour);
    }
}

void EAQVGAOLED::rectangle
(
    const uint16_t x0,
    const uint16_t y0,
    const uint16_t x1,
    const uint16_t y1,
    uint16_t colour
)
{
    // Order the drawing co-ords
    uint16_t startX, startY, endX, endY;
    orderCoords(x0, y0, x1, y1, startX, startY, endX, endY);    
    
    // Now draw the 4 lines required
    hLine(startX, startY, endX, colour);
    vLine(endX, startY, endY, colour);
    hLine(startX, endY, endX, colour);
    vLine(startX, startY, endY, colour);
} 

void EAQVGAOLED::fillRectangle
(
    const uint16_t x0,
    const uint16_t y0,
    const uint16_t x1,
    const uint16_t y1,
    uint16_t colour
)
{
    // Order the drawing co-ords
    uint16_t startX, startY, endX, endY;
    orderCoords(x0, y0, x1, y1, startX, startY, endX, endY);    
    
    // Now draw the all lines required
    for (uint16_t i = startY; i < endY; i++) {
        hLine(startX, i, endX, colour);
    }
} 

int EAQVGAOLED::_putc
(
    int value
)
{
    switch (value) {
    case CURSOR_CLS: // Clear the screen
        cls();
        break;
    case CURSOR_UP:
        if (_row == 0)
            _row = rows();
        else {
            _row--;
        }
        break;
    case CURSOR_DOWN:
        if (_row == rows()) {
            _row = 0;
        }
        else {
            _row++;
        }
        break;
    case CURSOR_LEFT:
        if (_column == 0) {
            _column = columns();
        }
        else {
            _column--;
        }
        break;
    case CURSOR_RIGHT:
        if (_column == columns()) {
            _column = 0;
        }
        else {
            _column++;
        }
        break;
    default:    
        GraphicsDisplay::_putc(value);
        break;
    }
    
    return value;
}

/****************************************************************************/
/*                                                                          */
/* Private functions                                                        */
/*                                                                          */
/****************************************************************************/

/**
 * Initialise the hardware required
 *
 */
void EAQVGAOLED::initHardware(void)
{
    // Activate the display reset line
    _reset = 0;
    
    // Turn the chip select and backlight off
    _cs = 1;
    _bl = 0;
    
    // Set the spi port for 8 bits, clk phase and polarity 0
     _spi.format(8, 0);
    // And frequency to 10MHz
    _spi.frequency(10000000);
    
    // Wait for 10uS and release reset
    wait_us(10);
    _reset = 1;
    
    // Wait for 10ms to allow access to controller
    wait_ms(10);
}

/**
 * Reset the display controller
 *
 */
bool EAQVGAOLED::resetDisplay(void)
{
    // Ensure we are connected to the correct hardware
    unsigned int result = readDataRegister(0x0F);
    /* Doesn't seem to wrk ??
    if (EAQVGAOLED_ID != result)
    {
      return false;
    }
    */
    
    // Set the entry mode values for addessing and increment   
    writeDataRegister(0x03, 0x130);
    
    // Set the standby off
    writeDataRegister(0x10, 0);
    
    // Delay till the clocks come up
    wait_ms(100);
    
    // Enable the backlight
    _bl = 1;
    
    // Delay until power is steady
    wait_ms(40);
    
    // Turn the display on
    writeDataRegister(0x05, 1);
    
    return true;
}

/**
 * Set the location of the pen
 *
 */
void EAQVGAOLED::movePen
(
    const uint16_t x,
    const uint16_t y
)
{
    // Set x & y pos and select GRAM register
    writeDataRegister(0x20, x);
    writeDataRegister(0x21, y);
    setRegisterIndex(0x22);
}

/**
 * Read the value of a controller data register
 *
 * @param reg the register address to read
 */
uint16_t EAQVGAOLED::readDataRegister
(
    const uint8_t reg
)
{
    //  Set the register index
    setRegisterIndex(reg);

    // Read the response
    _cs = 0;
    _spi.write(0x71);   // Command byte ID = 011100, RS = 0, R/W = 1
    uint8_t msb = _spi.write(0);
    uint8_t lsb = _spi.write(0);
    _cs = 1;
    
    return (msb << 8) | lsb;
}

/**
 * Write to a controller data register
 *
 * @param reg the register address to read
 * @param data the data to write
 */
void EAQVGAOLED::writeDataRegister
(
    const uint8_t reg,
    const uint16_t data
)
{
    //  Set the register index
    setRegisterIndex(reg);

    // Write the data
    writeData(data);
}

inline void EAQVGAOLED::writeData
(
    const uint16_t data
)
{
    // Write the data
    _cs = 0;
    _spi.write(0x72);   // Command byte, ID = 011100, RS = 1, R/W = 0
    _spi.write(data >> 8);
    _spi.write(data & 0xFF);
    _cs = 1;
}

/** Select a controller register
 *
 * @param reg register to select
 */
inline void EAQVGAOLED::setRegisterIndex
(
    const uint8_t reg
)
{
    //  Write to the register selector (RS = 0)
    _cs = 0;
    _spi.write(0x70);   // Command byte ID = 011100, RS = 0, R/W = 0
    _spi.write(0);
    _spi.write(reg);    
    _cs = 1;
}