SPKDisplay - A mbed display class and processing imaging tools for 128x64 OLEDs using the SSD1305 driver, connected via SPI.
Dependents: SPK-DVIMXR SPK-DMXer
Revision 0:76bb084fa033, committed 2012-04-15
- Comitter:
- tobyspark
- Date:
- Sun Apr 15 16:51:01 2012 +0000
- Child:
- 1:dd3faa2ab1dd
- Commit message:
- Initial commit after internal dev as part of SPK-DVIMXR
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spk_oled_fontByteMaker--processing.h Sun Apr 15 16:51:01 2012 +0000 @@ -0,0 +1,95 @@ +int w = 132; +int h = 64; +int scaleFactor = 5; + +PImage imgPixel; +color white = color(255, 255, 255, 255); +color black = color(0, 0, 0, 255); + +void setup() +{ + imgPixel = loadImage("spk_dvimxr_font.png"); + w = imgPixel.width; + h = imgPixel.height; + + size(w*scaleFactor, h*scaleFactor); + + noSmooth(); +} + +void draw() +{ + background(0); + image(imgPixel, 0, 0, w*scaleFactor, h*scaleFactor); +} + +void mouseDragged() +{ + int x = mouseX/scaleFactor; + int y = mouseY/scaleFactor; + + imgPixel.set(x, y, white); +} + +void mousePressed() +{ + int x = mouseX/scaleFactor; + int y = mouseY/scaleFactor; + + if (imgPixel.get(x, y) == white) + { + imgPixel.set(x, y, black); + } + else + { + imgPixel.set(x, y, white); + } +} + +void keyPressed() +{ + int startID = 33; + int stopID = 126; + + for (int charID = startID; charID <= stopID; charID++) + { + boolean blank = false; + String output = ""; + int counter = 0; + while (blank == false) + { + int x = (charID-startID)*8 + counter; + + byte theByte = 0; + for (int b = 0; b < 8; b++) + { + if (imgPixel.get(x, b) == white) + { + theByte += pow(2, b); + } + } + + if (theByte > 0) + { + if (output.length() > 0) output = output + ", "; + output = output + "0x" + hex(theByte, 2); + } + else + { + blank = true; + println("const uint8_t char" + charID + "[] = {" + counter + ", " + output + "};"); + } + + counter++; + } + } + println(); + println("const int characterBytesStartChar = " + startID + ";"); + println("const int characterBytesEndChar = " + stopID + ";"); + print("const uint8_t* characterBytes[] = {"); + for (int charID = startID; charID <= stopID; charID++) + { + print("char" + charID + ", "); + } + println("};"); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spk_oled_screenByteMaker--processing.h Sun Apr 15 16:51:01 2012 +0000 @@ -0,0 +1,67 @@ +int w = 132; +int h = 64; +int scaleFactor = 10; + +PImage imgPixel; +color white = color(255, 255, 255, 255); +color black = color(0, 0, 0, 255); + +void setup() +{ + size(w*scaleFactor, h*scaleFactor); + imgPixel = loadImage("spk_dvimxr_screen.png"); + + noSmooth(); +} + +void draw() +{ + background(0); + image(imgPixel, 0, 0, w*scaleFactor, h*scaleFactor); +} + +void mouseDragged() +{ + int x = mouseX/scaleFactor; + int y = mouseY/scaleFactor; + + imgPixel.set(x, y, white); +} + +void mousePressed() +{ + int x = mouseX/scaleFactor; + int y = mouseY/scaleFactor; + + if (imgPixel.get(x, y) == white) + { + imgPixel.set(x, y, black); + } + else + { + imgPixel.set(x, y, white); + } +} + +void keyPressed() +{ + println("{"); + for (int page = 0; page < 8; page++) + { + for (int i = 0; i < w; i++) + { + byte theByte = 0; + for (int j = 0; j < 8; j++) + { + if (imgPixel.get(i, (page*8)+j) == white) + { + theByte += pow(2, j); + } + } + print("0x" + hex(theByte, 2)); + print(", "); + } + println(); + } + println("}"); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spk_oled_ssd1305.cpp Sun Apr 15 16:51:01 2012 +0000 @@ -0,0 +1,234 @@ +// *spark audio-visual +// OLED display using SSD1305 driver +// Copyright *spark audio-visual 2012 + +#include "spk_oled_ssd1305.h" +#include "mbed.h" + +SPKDisplay::SPKDisplay(PinName mosiPin, PinName clkPin, PinName csPin, PinName dcPin, PinName resPin, Serial *debugSerial) +{ + bufferHasChanged = false; + + spi = new SPI(mosiPin, NC, clkPin); + spi->format(8,3); + spi->frequency(2000000); + + cs = new DigitalOut(csPin); + dc = new DigitalOut(dcPin); + res = new DigitalOut(resPin); + + // Link up debug Serial object + // Passing in shared object as debugging is shared between all DVI mixer functions + debug = debugSerial; + + setup(); + + clearBuffer(); + + if (debug) debug->printf("SPKDisplay loaded\n\r"); +} + +void SPKDisplay::clearBuffer() +{ + memset(buffer, 0, bufferCount); + bufferHasChanged = true; +} + +void SPKDisplay::imageToBuffer() +{ + memcpy(buffer, image, bufferCount); + bufferHasChanged = true; +} + +void SPKDisplay::clearBufferRow(int row) +{ + // Range check + if (row >= 8) + { + if (debug) debug->printf("SPKDisplay::clearBufferRow sent out of bounds row"); + return; + } + int bStart = row*bufferWidth; + int bEnd = bStart + pixelWidth; + + for (int bPos = bStart; bPos <= bEnd; bPos++) + { + buffer[bPos] = 0x00; + } + + bufferHasChanged = true; +} + +void SPKDisplay::horizLineToBuffer(int y) +{ + if (y >= pixelHeight) + { + if (debug) debug->printf("SPKDisplay::clearBufferRow sent out of bounds y"); + return; + } + + int row = (y*pixInPage) / pixelHeight; + int posInRow = y % pixInPage; + + int bStart = row*bufferWidth; + int bEnd = bStart + pixelWidth; + + for (int bPos = bStart; bPos <= bEnd; bPos++) + { + // Need to bitwise OR as setting single bit (the line) in byte (the row) + buffer[bPos] = buffer[bPos] | 0x01 << posInRow; + } + + bufferHasChanged = true; +} + +void SPKDisplay::textToBuffer(std::string message, int row) +{ + // Range check + if (row >= 8) row = 7; + int bStart = row*bufferWidth; + int bEnd = bStart + pixelWidth; + + int bPos = bStart; + for (int i = 0; i < message.size(); i++) + { + char character = message.at(i); + + // Is it outside the range we have glyphs for? + if ((character < characterBytesStartChar) || (character > characterBytesEndChar)) + { + // Treat as a space + for (int j = 0; j < 5; j++) + { + if (bPos >= bEnd) break; + buffer[bPos++] = 0x00; + } + + // Warn if not + if (debug) + { + if (character != ' ') debug->printf("No glyph for character %c at position %i", character, i); + } + } + // If not, typeset it! + else + { + // Shift into our array's indexing + character -= characterBytesStartChar; + + // Write each byte's vertical column of 8bits into the buffer. + for (int j = 0; j < characterBytes[character][0]; j++) + { + if (bPos >= bEnd) break; + buffer[bPos++] = characterBytes[character][j+1]; + } + + // Put 1px letter spacing at end + if (bPos >= bEnd) break; + buffer[bPos++] = 0x00; // 1 px letter spacing + } + } + + bufferHasChanged = true; +} + +void SPKDisplay::sendBuffer() +{ + if (bufferHasChanged) + { + // Select the device by seting chip select low + *cs = 0; + + // Set to receive DATA not commands + *dc = 1; + + for (int i = 0; i < bufferCount; i++) + { + spi->write(buffer[i]); + } + + // Deselect the device + *cs = 1; + + bufferHasChanged = false; + } +} + +void SPKDisplay::setup() +{ + // TASK: SCREEN OFF, Run pre-flight + + // Hard reset the OLED + *res = 0; + wait_ms(1); + *res = 1; + + // Select the device by seting chip select low + *cs = 0; + + // Set to receive COMMANDS not data + *dc = 0; + + spi->write(0xAE); // set display off + spi->write(0xD5); // set display clock divide ratio + spi->write(0xA0); + spi->write(0xA8); // set multiplex ratio + spi->write(0x3F); + spi->write(0xD3); // set display offset + spi->write(0x00); + spi->write(0x40); // set display start line + spi->write(0xAD); // set master configuration + spi->write(0x8E); + spi->write(0xD8); // set area color mode + spi->write(0x05); + spi->write(0xA1); // set segment re-map + spi->write(0xC8); // set com output scan direction + spi->write(0xDA); // set com pins hardware configuration + spi->write(0x12); + spi->write(0x91); // set look-up table + spi->write(0x3F); + spi->write(0x3F); + spi->write(0x3F); + spi->write(0x3F); + spi->write(0x81); // set current control for bank 0 + spi->write(0x8F); + spi->write(0xD9); // set pre-charge period + spi->write(0xD2); + spi->write(0xDB); //set vcomh deselect level + spi->write(0x34); + spi->write(0xA4); // set entire display on/off + spi->write(0xA6); // set normal/inverse display + + spi->write(0x20); // page mode + spi->write(0x00); + + // TASK: Clear screen's content buffer + + // Is this neccessary when switching command/data? + *cs = 1; + wait_ms(1); + *cs = 0; + + // Set to receive DATA not commands + *dc = 1; + + for (int i = 0; i < bufferCount; i++) + { + spi->write(0x00); + } + + // TASK: SCREEN ON + + // Is this neccessary when switching command/data? + *cs = 1; + wait_ms(1); + *cs = 0; + + // Set to receive COMMANDS not data + *dc = 0; + + spi->write(0xAF); // set display on + + // Deselect the device + *cs = 1; +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spk_oled_ssd1305.h Sun Apr 15 16:51:01 2012 +0000 @@ -0,0 +1,98 @@ +// OLED display using SSD1305 driver +// Copyright *spark audio-visual 2012 + + +#ifndef SPK_OLED_SSD1305_h +#define SPK_OLED_SSD1305_h + +#include "mbed.h" +#include <string> + +#define bufferCount 1056 +#define bufferWidth 132 +#define pixelWidth 128 +#define pixelHeight 64 +#define pixInPage 8 +#define pageCount 8 + +/** Display class for 128x64 OLEDs using the SSD1305 driver + * ie. DENSITRON - DD-12864YO-3A + * + * This is a ground-up, minimal library. Currently it will display a full-screen image, draw horizontal lines, and display rows of text. + * + * This library includes two processing sketches to create a font and full-screen image in the required byte representations. + * It won't compile without you doing that - it expects const uint8_t image[] and const uint8_t* characterBytes[] + * + * Terminology: + * 'rows' are 8 pixel high rows across the display, 0 being the topmost and 7 the bottom. + * 'lines' are single pixel lines, origin top left + * + * Example: + * @code + * SPKDisplay screen(p5, p7, p8, p10, p9) + * screen.imageToBuffer(); + * screen.textToBuffer("*spark OLED SSD1305",0); + * screen.textToBuffer("v01",1); + * screen.sendBuffer + * @endcode + */ +class SPKDisplay +{ + public: + /** Create a display object connected via SPI + * + * @param mosi SPI MOSI + * @param clk SPI SCK + * @param cs Chip Select - a digital out pin + * @param dc Data/Command - a digital out pin + * @param res Reset - a digital out pin + * @param debugSerial An optional serial object to log to + */ + SPKDisplay(PinName mosi, PinName clk, PinName cs, PinName dc, PinName res, Serial *debugSerial = NULL); + + /** Completely clear the object's display representation */ + void clearBuffer(); + + /** Clear a row of the object's display representation + * + * @param row The row to clear. + */ + void clearBufferRow(int row); + + /** Replace the object\s display representation with the contents of image[] */ + void imageToBuffer(); + + /** Draw a horizontal line in the object's display representation + * + * @param y The y position of the line to draw + */ + void horizLineToBuffer(int y); + + /** Write a line of text in the object's display representation + * + * @param message The text to write. The text will be truncated if longer than the screen's width. + * @param row The row in which to write the text + */ + void textToBuffer(std::string message, int row); + + /** Send the object's display representation to the OLED + * + * You can safely call this once per main loop, it will only transmit the buffer contents if there has been an update + */ + void sendBuffer(); + + private: + SPI *spi; + DigitalOut *cs; + DigitalOut *dc; + DigitalOut *res; + + Serial *debug; + uint8_t buffer[bufferCount]; + + bool bufferHasChanged; + + void setup(); +}; + +#endif