SSD1351 library for the STM32F401RE. Uses BurstSPI and a couple of tweaks to improve throughput.

Dependencies:   BurstSPI

Library for the SSD1351 128 x 128 OLED display, specifically for the STM32F401RE Nucleo/STMstation development boards.

/media/uploads/kkado/imgp1229.jpg

See API documentation for more details and code.

SSD1351.cpp

Committer:
kkado
Date:
2017-07-15
Revision:
4:d65b0a5d58f0
Parent:
1:ae4fe66e9c0e

File content as of revision 4:d65b0a5d58f0:

#include "SSD1351.h"
#include "mbed.h"
#include "BurstSPI.h"

const uint16_t font[] = {
    0xF6DE, 0xF6DE, 0xF6DE, 0xF6DE, 0xF6DE, 0xF6DE, 0xF6DE, 0xF6DE, 0xF6DE, 0xF6DE, 0xF6DE, 0xF6DE, 0xF6DE, 0xF6DE, 0xF6DE, 0xF6DE, 
    0xF6DE, 0xF6DE, 0xF6DE, 0xF6DE, 0xF6DE, 0xF6DE, 0xF6DE, 0xF6DE, 0xF6DE, 0xF6DE, 0xF6DE, 0xF6DE, 0xF6DE, 0xF6DE, 0xF6DE, 0xFFFE, 
    0x0000, 0x4904, 0xB400, 0xBEFA, 0x79E4, 0x8542, 0x5556, 0x4800, 0x5244, 0x4494, 0xABAA, 0x0BA0, 0x0028, 0x0380, 0x0024, 0x0540, 
    0xF6DE, 0x4924, 0xE7CE, 0xE59E, 0xB792, 0xF39E, 0xF3DE, 0xE492, 0xF7DE, 0xF792, 0x0820, 0x0828, 0x2A22, 0x1C70, 0x88A8, 0xE504, 
    0x57C6, 0x57DA, 0xD75C, 0x7246, 0xD6DC, 0xF3CE, 0xF3C8, 0x73D6, 0xB7DA, 0xE92E, 0x24D4, 0xB75A, 0x924E, 0xBEDA, 0xBFFA, 0x56D4, 
    0xD748, 0x56F6, 0xD76A, 0x711C, 0xE924, 0xB6D6, 0xB6A4, 0xB7FA, 0xB55A, 0xB524, 0xE54E, 0xF24E, 0x1110, 0xE49E, 0x5400, 0x000E, 
    0x8800, 0x01DE, 0x935C, 0x01C6, 0x25D6, 0x0EE6, 0x2BA4, 0x0F9C, 0x935A, 0x4124, 0x209C, 0x92EA, 0xC92E, 0x03FA, 0x035A, 0x0154, 
    0x0AE8, 0x0AB2, 0x13C8, 0x0F3C, 0x5D24, 0x02D6, 0x02F4, 0x02FE, 0x02AA, 0x159C, 0x1DEE, 0x6A26, 0x4824, 0x6A26, 0x7800, 0xFFFE
};

//Constructors
SSD1351::SSD1351(PinName mosi_pin, PinName sclk_pin, PinName dc_pin, PinName cs_pin, PinName rst_pin)
    :cs(cs_pin), dc(dc_pin), rst(rst_pin), spi(mosi_pin, NC, sclk_pin)
{
    begin();
}
SSD1351::SSD1351()
    :cs(OLED_CS), dc(OLED_DC), rst(OLED_RST), spi(OLED_MOSI, NC, OLED_SCLK)
{
    begin();
}

//Set the buffer pointer
void SSD1351::setBuf(uint8_t* _buf){
    buf = _buf;
}

//Set collision mask pointer
void SSD1351::setCMask(uint8_t* _cmask){
    collisionmask = _cmask;
}

//Basic SPI write command
void SSD1351::spiwrite(uint8_t c){
    spi.fastWrite(c);
}

//Write a command to OLED
void SSD1351::writeCommand(uint8_t c){
    dc = 0;
    cs = 0;
    spiwrite(c);
    wait_us(1); //We need the delay during the init sequence - I don't know why
    cs = 1;
}

//Write delay to OLED
void SSD1351::writeData(uint8_t c){
    dc = 1;
    cs = 0;
    spiwrite(c);
    wait_us(1);
    cs = 1;
}

//Initialize the SSD1351
void SSD1351::begin(){
    spi.format(8,3);
    spi.frequency(20000000);
    
    cs = 0;
    
    rst = 1;
    //SSD1351 datasheet says low pulse width for reset = 2 us, MINIMUM
    wait_ms(5);
    rst = 0;
    wait_ms(5);
    rst = 1;
    wait_ms(5);
    
    writeCommand(SSD1351_CMD_COMMANDLOCK);  // set command lock
    writeData(0x12);  
    writeCommand(SSD1351_CMD_COMMANDLOCK);  // set command lock
    writeData(0xB1);

    writeCommand(SSD1351_CMD_DISPLAYOFF);       // 0xAE

    writeCommand(SSD1351_CMD_CLOCKDIV);         // 0xB3
    writeCommand(0xF1);                         // 7:4 = Oscillator Frequency, 3:0 = CLK Div Ratio (A[3:0]+1 = 1..16)
    
    writeCommand(SSD1351_CMD_MUXRATIO);
    writeData(127);
    
    writeCommand(SSD1351_CMD_SETREMAP);
    //writeData(0x74);
    writeData(0x66);
  
    writeCommand(SSD1351_CMD_SETCOLUMN);
    writeData(0x00);
    writeData(0x7F);
    writeCommand(SSD1351_CMD_SETROW);
    writeData(0x00);
    writeData(0x7F);

    writeCommand(SSD1351_CMD_STARTLINE);        // 0xA1
    writeData(0);

    writeCommand(SSD1351_CMD_DISPLAYOFFSET);    // 0xA2
    writeData(0x0);

    writeCommand(SSD1351_CMD_SETGPIO);
    writeData(0x00);
    
    writeCommand(SSD1351_CMD_FUNCTIONSELECT);
    writeData(0x01); // internal (diode drop)
    //writeData(0x01); // external bias

    //writeCommand(SSSD1351_CMD_SETPHASELENGTH);
    //writeData(0x32);

    writeCommand(SSD1351_CMD_PRECHARGE);
    writeCommand(0x32);
 
    writeCommand(SSD1351_CMD_VCOMH);
    writeCommand(0x05);

    writeCommand(SSD1351_CMD_NORMALDISPLAY);

    writeCommand(SSD1351_CMD_CONTRASTABC);
    writeData(0xC8);
    writeData(0x80);
    writeData(0xC8);

    writeCommand(SSD1351_CMD_CONTRASTMASTER);
    writeData(0x0F);

    writeCommand(SSD1351_CMD_SETVSL );
    writeData(0xA0);
    writeData(0xB5);
    writeData(0x55);
    
    writeCommand(SSD1351_CMD_PRECHARGE2);
    writeData(0x01);
    
    writeCommand(SSD1351_CMD_DISPLAYON);
}

/*
//Fill a rectangular area
void SSD1351::fillRect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t fillcolor){
    writeCommand(SSD1351_CMD_SETCOLUMN);
    writeData(x);
    writeData(x+w-1);
    writeCommand(SSD1351_CMD_SETROW);
    writeData(y);
    writeData(y+h-1);
    writeCommand(SSD1351_CMD_WRITERAM);
    dc = 1;
    cs = 0;
    
    for(uint16_t i=0; i < w*h; i++){
        spiwrite(fillcolor >> 8);
        spiwrite(fillcolor);
    }
}
*/

//Enable writing to the SSD1351 RAM
void SSD1351::enableWrite(){
    cs = 0;
    writeCommand(SSD1351_CMD_SETCOLUMN);
    writeData(0);
    writeData(127);
    writeCommand(SSD1351_CMD_SETROW);
    writeData(0);
    writeData(127);
    writeCommand(SSD1351_CMD_WRITERAM);
    dc = 1;
    cs = 0;
}

//Fill the buffer with a solid color
void SSD1351::fillBuf(uint16_t fillcolor){
    for(uint16_t i=0; i < 128*128; i++){
        buf[2*i] = fillcolor >> 8;
        buf[2*i+1] = fillcolor;
    }
}

//Write the buffer to OLED RAM
void SSD1351::writeBuf(){
    for(uint16_t i=0; i < 32768; i++){
        spiwrite(buf[i]);
    }
}

//Draw a filled rectangle
void SSD1351::fillRect(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t color){
    int16_t start = y*256 + x*2;
    uint16_t xs = 0;
    uint16_t xe = 0;
    uint16_t ys = 0;
    uint16_t ye = 0;
    
    if(x < 0){
        xs = -x;
    }
    if(y < 0){
        ys = -y;
    }
    if(x+w > 127){
        xe = x+w-128;
    }
    if(y+h > 127){
        ye = y+h-128;
    }
    
    for(uint16_t j=0+ys; j<h-ye; j++){
        for(uint16_t i=0+xs; i<w-xe; i++){
            buf[start +j*256 + i*2] = color >> 8;
            buf[start +j*256 + i*2 +1] = color;
        }
    }
}

//Draw an open rectangle
void SSD1351::openRect(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t color){
    int16_t start = y*256 + x*2;
    uint16_t xs = 0;
    uint16_t xe = 0;
    uint16_t ys = 0;
    uint16_t ye = 0;
    
    if(x < 0){
        xs = -x;
    }
    if(y < 0){
        ys = -y;
    }
    if(x+w > 127){
        xe = x+w-128;
    }
    if(y+h > 127){
        ye = y+h-128;
    }
    
    for(uint16_t j=ys; j<h-ye; j++){
        for(uint16_t i=xs; i<w-xe; i++){
            if((j == ys & ys == 0) | (j == h-ye-1 & ye == 0) | (i == xs & xs == 0) |  (i == w-xe-1 & xe == 0)){
                buf[start +j*256 + i*2] = color >> 8;
                buf[start +j*256 + i*2 +1] = color;
            }
        }
    }
}

void SSD1351::drawHLine(int16_t x, int16_t y, int16_t length, uint16_t color){
    if(y < 0 | y > 127){return;}
    
    int32_t start = y*256 + x*2;
    uint16_t xs = 0;
    uint16_t xe = 0;
    int8_t sign;
    
    if(length > 0){
        sign = 1;
    }
    else{
        sign = -1;
    }
    
    if(x < 0){
        xs = -x;
    }
    else if(x > 127){
        xs = x-127;
    }
    if(x+length < 0){
        xe = -(x+length);
    }
    else if(x+length > 127){
        xe = x+length-127;
    }
    
    for(int16_t i=xs; i<abs(length)-xe; i++){
        buf[start + sign*i*2] = color >> 8;
        buf[start + sign*i*2 + 1] = color;
    }
}

void SSD1351::drawVLine(int16_t x, int16_t y, int16_t length, uint16_t color){
    if(x < 0 | x > 127){return;}
    
    int32_t start = y*256 + x*2;
    uint16_t ys = 0;
    uint16_t ye = 0;
    int8_t sign;
    
    if(length > 0){
        sign = 1;
    }
    else{
        sign = -1;
    }
    
    if(y < 0){
        ys = -y;
    }
    else if(y > 127){
        ys = y-127;
    }
    if(y+length < 0){
        ye = -(y+length+1);
    }
    else if(y+length > 127){
        ye = y+length-127;
    }
    
    for(int16_t i=ys; i<abs(length)-ye; i++){
        buf[start + sign*i*256] = color >> 8;
        buf[start + sign*i*256 + 1] = color;
    };
}

void SSD1351::drawLine(int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color){
    bool steep = (abs(y2-y1) > abs(x2-x1));
    int16_t temp;
    if(steep){
        temp = x1;
        x1 = y1;
        y1 = temp;
        
        temp = x2;
        x2 = y2;
        y2 = temp;
    }
    if(x1 > x2){
        temp = x1;
        x1 = x2;
        x2 = temp;
        
        temp = y1;
        y1 = y2;
        y2 = temp;;
    }
    int16_t dx = x2-x1;
    int16_t dy = abs(y2-y1);
    float error = dx / 2.0f;
    int16_t ystep = (y1 < y2) ? 1 : -1;
    int16_t y = y1;
    int16_t maxX = x2;
    
    for(int16_t x = x1; x<maxX; x++){
        if(x >= 0 & x<128 & y >= 0 & y<128){
            if(steep){
                buf[y*2 + x*256] = color >> 8;
                buf[y*2 + x*256 + 1] = color;
            }
            else{
                buf[x*2 + y*256] = color >> 8;
                buf[x*2 + y*256 + 1] = color;
            }
        }
        error -= dy;
        if(error < 0)
        {
            y += ystep;
            error += dx;
        }
    }
}

void SSD1351::openCircle(int16_t x0, int16_t y0, uint16_t radius, uint16_t color){
    int16_t x = radius;
    int16_t y = 0;
    int16_t err = 0;
    
    while(x >= y){
        if(x0+x >= 0 & x0+x <128 & y0+y >= 0 & y0+y<128){
            buf[(x0 + x)*2 + (y0 + y)*256] = color >> 8;
            buf[(x0 + x)*2 + (y0 + y)*256 + 1] = color;
        }
        if(x0+y >= 0 & x0+y <128 & y0+x >= 0 & y0+x<128){
            buf[(x0 + y)*2 + (y0 + x)*256] = color >> 8;
            buf[(x0 + y)*2 + (y0 + x)*256 + 1] = color;
        }
        if(x0-y >= 0 & x0-y <128 & y0+x >= 0 & y0+x<128){    
            buf[(x0 - y)*2 + (y0 + x)*256] = color >> 8;
            buf[(x0 - y)*2 + (y0 + x)*256 + 1] = color;
        }
        if(x0-x >= 0 & x0-x <128 & y0+y >= 0 & y0+y<128){    
            buf[(x0 - x)*2 + (y0 + y)*256] = color >> 8;
            buf[(x0 - x)*2 + (y0 + y)*256 + 1] = color;
        }
        if(x0-x >= 0 & x0-x <128 & y0-y >= 0 & y0-y<128){    
            buf[(x0 - x)*2 + (y0 - y)*256] = color >> 8;
            buf[(x0 - x)*2 + (y0 - y)*256 + 1] = color;
        }
        if(x0-y >= 0 & x0-y <128 & y0-x >= 0 & y0-x<128){    
            buf[(x0 - y)*2 + (y0 - x)*256] = color >> 8;
            buf[(x0 - y)*2 + (y0 - x)*256 + 1] = color;
        }
        if(x0+y >= 0 & x0+y <128 & y0-x >= 0 & y0-x<128){    
            buf[(x0 + y)*2 + (y0 - x)*256] = color >> 8;
            buf[(x0 + y)*2 + (y0 - x)*256 + 1] = color;
        }
        if(x0+x >= 0 & x0+x <128 & y0-y >= 0 & y0-y<128){    
            buf[(x0 + x)*2 + (y0 - y)*256] = color >> 8;
            buf[(x0 + x)*2 + (y0 - y)*256 + 1] = color;
        }
        y += 1;
        if(err <= 0){
            err += 2*y + 1;
        }
        if(err > 0){
            x -= 1;
            err -= 2*x + 1;
        }
    }
}

void SSD1351::fillCircle(int16_t x0, int16_t y0, uint16_t radius, uint16_t color){
    int16_t x = radius;
    int16_t y = 0;
    int16_t err = 0;
    
    while(x >= y){
        drawLine(x0,y0+y,x0+x,y0+y,color);
        drawLine(x0,y0+y,x0-x,y0+y,color);
        drawLine(x0,y0-y,x0+x,y0-y,color);
        drawLine(x0,y0-y,x0-x,y0-y,color);
        
        drawLine(x0,y0+x,x0+y,y0+x,color);
        drawLine(x0,y0+x,x0-y,y0+x,color);
        drawLine(x0,y0-x,x0+y,y0-x,color);
        drawLine(x0,y0-x,x0-y,y0-x,color);
        
        y += 1;
        if(err <= 0){
            err += 2*y + 1;
        }
        if(err > 0){
            x -= 1;
            err -= 2*x + 1;
        }
    }
}

//Display a sprite on the screen
void SSD1351::drawSpritePtr(const uint16_t s[] ,int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t mask){
    int16_t start = y*256 + x*2;
    uint16_t xs = 0;
    uint16_t xe = 0;
    uint16_t ys = 0;
    uint16_t ye = 0;
    
    if(x < 0){
        xs = -x;
    }
    if(y < 0){
        ys = -y;
    }
    if(x+w > 127){
        xe = x+w-128;
    }
    if(y+h > 127){
        ye = y+h-128;
    }
    
    for(uint16_t j=0+ys; j<h-ye; j++){
        for(uint16_t i=0+xs; i<w-xe; i++){
            if(s[j*w +i] != mask){
                buf[start +j*256 +i*2] = s[j*w +i] >> 8;
                buf[start +j*256 +i*2 +1] = s[j*w +i];
            }
        }
    }
}

//Fill the collision mask with data
void SSD1351::fillCMask(uint8_t state){
    for(uint16_t i=0; i < 128*128; i++){
        collisionmask[i] = state;
    }
}

//Write data to the collision mask
void SSD1351::drawCMask(const uint16_t s[], int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t mask, uint8_t state){
    int16_t start = y*128 + x;
    uint16_t xs = 0;
    uint16_t xe = 0;
    uint16_t ys = 0;
    uint16_t ye = 0;
    
    if(x < 0){
        xs = -x;
    }
    if(y < 0){
        ys = -y;
    }
    if(x+w > 127){
        xe = x+w-128;
    }
    if(y+h > 127){
        ye = y+h-128;
    }
    
    for(uint16_t j=0+ys; j<h-ye; j++){
        for(uint16_t i=0+xs; i<w-xe; i++){
            if(s[j*w +i] != mask){
                collisionmask[start +j*128 +i] = state;
            }
        }
    }
}

//Check if a sprite is colliding with anything
uint8_t SSD1351::checkCollision(const uint16_t s[], int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t mask){
    int16_t start = y*128 + x;
    uint16_t xs = 0;
    uint16_t xe = 0;
    uint16_t ys = 0;
    uint16_t ye = 0;
    
    if(x < 0){
        xs = -x;
    }
    if(y < 0){
        ys = -y;
    }
    if(x+w > 127){
        xe = x+w-128;
    }
    if(y+h > 127){
        ye = y+h-128;
    }
    
    for(uint16_t j=0+ys; j<h-ye; j++){
        for(uint16_t i=0+xs; i<w-xe; i++){
            if((s[j*w +i] != mask) && (collisionmask[start+j*128+i] != 0x00)){
                return collisionmask[start+j*128+i];
            }
        }
    }
    return 0;
}

//Draw a character
void SSD1351::drawChar(char c, int16_t x, int16_t y, uint16_t color, uint8_t zoom){
    int16_t start = y*256 + x*2;
    uint16_t xs = 0;
    uint16_t xe = 0;
    uint16_t ys = 0;
    uint16_t ye = 0;
    
    if(x < 0){
        xs = -x;
    }
    if(y < 0){
        ys = -y;
    }
    if(x+3*zoom > 127){
        xe = x+3*zoom-128;
    }
    if(y+5*zoom > 127){
        ye = y+5*zoom-128;
    }
    
    uint16_t letter = font[c];
    for(uint8_t j=0+ys;j<5*zoom-ye;j++){
        for(uint8_t i=0+xs;i<3*zoom-xe;i++){
            if(((letter << (uint8_t(i/zoom)+3*uint8_t(j/zoom))) & 0x8000) == 0x8000){
                for(uint8_t k=0; k<zoom; k++){
                    buf[start+j*256+i*2] = color >> 8;
                    buf[start+j*256+i*2 + 1] = color;
                }
            }
        }
    }
}

//Draw a bunch of characters
void SSD1351::printText(const char c[], int16_t x, int16_t y, uint16_t color, uint8_t zoom){
    for(uint16_t i=0; i<strlen(c);i++){
        drawChar(c[i], x+i*4*zoom, y, color, zoom);
    }
}