SSD1351 library for the STM32F401RE. Uses BurstSPI and a couple of tweaks to improve throughput.
Library for the SSD1351 128 x 128 OLED display, specifically for the STM32F401RE Nucleo/STMstation development boards.
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); } }