Library for drawing on the Hexiwear's OLED - more features being actively worked on

Dependents:   Hexidraw_Demo Hexiwear-FinalProject_v2

Discussion: https://developer.mbed.org/forum/team-4615-Hexiwear-community/topic/26595/

Hexidraw is a library for drawing on the Hexiwear's OLED. Be aware that it is not very optimized, so drawing may be slow, especially when drawing on large areas of the screen.

Please see the wiki for API documentation.

Features:

  • Screen fill with a color
  • Drawing filled rectangles
  • Drawing circles with a set radius and line width
  • Setting individual pixels
  • Turning on and off the OLED's sleep mode
  • Drawing images

Example project: https://developer.mbed.org/users/keithm01/code/Hexidraw_Demo/

hexidraw.cpp

Committer:
keithm01
Date:
2016-08-20
Revision:
5:00876457cad4
Parent:
4:9c0b59439725

File content as of revision 5:00876457cad4:

#include "oled_info.h"
#include "hexidraw.h"

#include "mbed.h"

#include <math.h>

/*
    Command descriptions from:
        https://cdn-shop.adafruit.com/datasheets/SSD1351-Revision+1.3.pdf
*/

OLED::OLED () :
    _spi(oled_sdi_pin, NC, oled_sck_pin),
    _cs(oled_cs_pin),
    _reset(oled_rst_pin),
    _dc(oled_dc_pin)
{
    if(USE_BUFFER) {
        for(uint16_t i = 0; i < OLED_WIDTH * OLED_HEIGHT; ++ i) {
            displayBuffer[i] = BLACK;
        }
    }
}

void OLED::begin()
{

#if 0
    // set pin directions
    pinMode(_rs, OUTPUT);

    if (_sclk) {
        pinMode(_sclk, OUTPUT);

        pinMode(_sid, OUTPUT);
    } else {
        // using the hardware SPI
        SPI.begin();
        SPI.setDataMode(SPI_MODE3);
    }

    // Toggle RST low to reset; CS low so it'll listen to us
    pinMode(_cs, OUTPUT);
    digitalWrite(_cs, LOW);

    if (_rst) {
        pinMode(_rst, OUTPUT);
        digitalWrite(_rst, HIGH);
        delay(500);
        digitalWrite(_rst, LOW);
        delay(500);
        digitalWrite(_rst, HIGH);
        delay(500);
    }
#endif
    DigitalOut BOOSTEN(oled_power_enable);   //Enable OLED

    //Set SPI modes
//    _spi.format(8,3);
    _spi.frequency(8000000);

    _dc =0;
    BOOSTEN = 0;
    wait(0.1f);
    _reset = 0 ;
    wait(0.1f);
    _reset = 1 ;
    wait(0.1f);
    BOOSTEN = 1;

    writeCommand(OLED_CMD_SET_CMD_LOCK);
    writeData(0x12);

    writeCommand(OLED_CMD_SET_CMD_LOCK);
    writeData(0xB1);

    writeCommand(OLED_CMD_DISPLAYOFF);


    writeCommand(OLED_CMD_SET_OSC_FREQ_AND_CLOCKDIV);
    writeData(0xF1);

    writeCommand(OLED_CMD_SET_MUX_RATIO);
    writeData(0x5F);

    writeCommand(OLED_CMD_SET_REMAP);
    writeData(OLED_REMAP_SETTINGS);

    writeCommand(OLED_CMD_SET_COLUMN);
    writeData(0x00);
    writeData(0x5F);

    writeCommand(OLED_CMD_SET_ROW);
    writeData(0x00);
    writeData(0x5F);

    writeCommand(OLED_CMD_STARTLINE);
    writeData(0x80);

    writeCommand(OLED_CMD_DISPLAYOFFSET);
    writeData(0x60);

    writeCommand(OLED_CMD_PRECHARGE);
    writeData(0x32);

    writeCommand(OLED_CMD_VCOMH);
    writeData(0x05);

    writeCommand(OLED_CMD_NORMALDISPLAY);

    writeCommand(OLED_CMD_CONTRASTABC);
    writeData(0x8A);
    writeData(0x51);
    writeData(0x8A);

    writeCommand(OLED_CMD_CONTRASTMASTER);
    writeData(0xCF);

    writeCommand(OLED_CMD_SETVSL);
    writeData(0xA0);
    writeData(0xB5);
    writeData(0x55);

    writeCommand(OLED_CMD_PRECHARGE2);
    writeData(0x01);

    writeCommand(OLED_CMD_DISPLAYON);
}

void OLED::writeCommand(uint8_t code)
{
    _dc = 0;

    _cs = 0;
    _spi.write(code);
    _cs = 1;
}

void OLED::writeData(uint8_t value)
{
    _dc = 1;

    _cs = 0;
    _spi.write(value);
    _cs = 1;
}
//Turns on OLED sleep mode
void OLED::sleep()
{
    writeCommand(0xAE);
}
//Turns off OLED sleep mode
void OLED::wake()
{
    writeCommand(0xAF);
}

void OLED::draw()
{
    if(USE_BUFFER) {
        writeCommand(OLED_CMD_SET_COLUMN);
        writeData(16);
        writeData(111);
        writeCommand(OLED_CMD_SET_ROW);
        writeData(0);
        writeData(95);
        writeCommand(OLED_CMD_WRITERAM);

        _dc = 1;
        _cs = 0;

        for(uint32_t i = 0; i < OLED_WIDTH * OLED_HEIGHT; ++ i) {
            writeData(displayBuffer[i] >> 8);
            writeData(displayBuffer[i]);
        }

        _cs = 1;
    }
}

void OLED::cursor(int x, int y)   //goTo
{
    if ((x >= OLED_WIDTH) || (y >= OLED_HEIGHT)) return;
    x+=OLED_COL_OFFSET;
    // set x and y coordinate
    writeCommand(OLED_CMD_SET_COLUMN);
    writeData(x);
    writeData(OLED_WIDTH-1);

    writeCommand(OLED_CMD_SET_ROW);
    writeData(y);
    writeData(OLED_HEIGHT-1);

    writeCommand(OLED_CMD_WRITERAM);
}

void OLED::pixel(int16_t x, int16_t y, uint16_t color)
{
    // Bounds check.
    if ((x >= OLED_WIDTH) || (y >= OLED_HEIGHT)) return;
    if ((x < 0) || (y < 0)) return;
    if(!USE_BUFFER) {
        cursor(x, y);

        writeCommand(OLED_CMD_WRITERAM);
        _dc = 1;
        _cs = 0;

        writeData(color >> 8);
        writeData(color);

        _cs = 1;
    } else {
        displayBuffer[x+OLED_WIDTH*y] = color;
    }
}

void OLED::clear (uint16_t color)
{
    rect(0, 0, OLED_WIDTH, OLED_HEIGHT, color);
}
void OLED::rect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t fillcolor)
{
    // Bounds check
    if ((x >= OLED_WIDTH) || (y >= OLED_HEIGHT))
        return;

    // Y bounds check
    if (y+h > OLED_HEIGHT) {
        h = OLED_HEIGHT - y - 1;
    }

    // X bounds check
    if (x+w > OLED_WIDTH) {
        w = OLED_WIDTH - x - 1;
    }
    if(!USE_BUFFER) {
        x+= OLED_COL_OFFSET;

        // set location
        writeCommand(OLED_CMD_SET_COLUMN);
        writeData(x);
        writeData(x+(w-1));
        writeCommand(OLED_CMD_SET_ROW);
        writeData(y);
        writeData(y+(h-1));
        // fill!
        writeCommand(OLED_CMD_WRITERAM);

        for (uint16_t i=0; i < w*h; i++) {
            writeData(fillcolor >> 8);
            writeData(fillcolor);
        }
    } else {
        for(uint16_t _w = 0; _w < w; ++_w) {
            for(uint16_t _h = 0; _h < h; ++_h) {
                displayBuffer[((x+_w)+OLED_WIDTH*(y+_h))] = fillcolor;
            }
        }
    }
}

void OLED::circle(uint16_t x, uint16_t y, uint16_t radius, uint16_t color, uint16_t width)
{
    const float DEG2RAD = 3.14159f/180;
    for(uint16_t r = 0; r < width; ++r) {
        for(uint16_t i = 0; i < 360; ++i) {
            float degInRad = i*DEG2RAD;
            uint16_t x2 = x + cos(degInRad) * (radius+r);
            uint16_t y2 = y + sin(degInRad) * (radius+r);
            if(USE_BUFFER) {
                displayBuffer[((x2)+OLED_WIDTH*(y2))] = color;
            } else {
                pixel(x2, y2, color);
            }
        }
    }
}

void buffer_swap(
    uint16_t* imgDst,
    const uint8_t* imgSrc,
    uint16_t imgSize
)
{
    for ( int var = 0; var < imgSize; var++ ) {
        *imgDst = *imgSrc << 8;
        imgSrc++;
        *imgDst |= *imgSrc;
        imgDst++;
        imgSrc++;
    }
}


void OLED::image (uint16_t x, uint16_t y, const uint8_t* image)
{
    x+= OLED_COL_OFFSET;
    //cursor(x, y);

    uint16_t w = image[2];
    uint16_t h = image[4];

    uint16_t image_data_size = w * h * OLED_BYTES_PER_PIXEL;

    if(!USE_BUFFER) {
        writeCommand(OLED_CMD_SET_COLUMN);
        writeData(x);
        writeData(x+(w-1));
        writeCommand(OLED_CMD_SET_ROW);
        writeData(y);
        writeData(y+(h-1));

        writeCommand(OLED_CMD_WRITERAM);
        _dc = 1;
        _cs = 0;

        const uint8_t* buffer = OLED_SKIP_IMAGE_HEADER(image);

        for ( uint32_t i = 0; i < image_data_size; i++) {
            _spi.write(*buffer);
            buffer += 1;
        }

    } else {
        const uint8_t* buffer = OLED_SKIP_IMAGE_HEADER(image);
        buffer_swap(displayBuffer, buffer, image_data_size);
    }

}


void OLED::dim()
{
    for ( int i = 0; i < 16; i++ ) {
        writeCommand( OLED_CMD_CONTRASTMASTER);
        writeData( 0xC0 | (0xF-i));
        wait( 20 );
    }
}

void OLED::hline(uint16_t x, uint16_t y, uint16_t width, uint16_t color, uint16_t thickness)
{
    if(!USE_BUFFER) {
        //x+= OLED_COL_OFFSET;
        // set location
        writeCommand(OLED_CMD_SET_COLUMN);
        writeData(x);
        writeData(x+width-1);
        writeCommand(OLED_CMD_SET_ROW);
        writeData(y);
        writeData(y+thickness-1);
        // fill!
        writeCommand(OLED_CMD_WRITERAM);

        for (uint16_t i=0; i < width*thickness; i++) {
            writeData(color >> 8);
            writeData(color);
        }
    } else {

        for (uint16_t w=0; w < width; w++) {
            for (uint16_t h=0; h < thickness; h++) {
                displayBuffer[((x+w)+OLED_WIDTH*(y+h))] = color;
            }
        }
    }

}

void OLED::vline(uint16_t x, uint16_t y, uint16_t height, uint16_t color, uint16_t thickness)
{
    if(!USE_BUFFER) {
        x+= OLED_COL_OFFSET;
        // set location
        writeCommand(OLED_CMD_SET_COLUMN);
        writeData(x);
        writeData(x+thickness-1);
        writeCommand(OLED_CMD_SET_ROW);
        writeData(y);
        writeData(y+height-1);
        // fill!
        writeCommand(OLED_CMD_WRITERAM);

        for (uint16_t i=0; i < height*thickness; i++) {
            writeData(color >> 8);
            writeData(color);
        }
    } else {
        for (uint16_t w=0; w < thickness; w++) {
            for (uint16_t h=0; h < height; h++) {
                displayBuffer[((x+w)+OLED_WIDTH*(y+h))] = color;
            }
        }
    }
}

void OLED::text(uint16_t x, uint16_t y, const uint8_t* text, const uint8_t* font, uint16_t color)
{

    x += OLED_COL_OFFSET;
    uint8_t pad = 2;

    uint8_t firstChar = font[2];
    uint8_t charHeight    = font[6];    
    
    uint16_t current_char = 0;
    while(text[current_char] != 0) {
        
        ++current_char;
    }
}

uint16_t Color565(uint8_t r, uint8_t g, uint8_t b)
{
    uint16_t c;
    c = r >> 3;
    c <<= 6;
    c |= g >> 2;
    c <<= 5;
    c |= b >> 3;

    return c;
}