diff --git a/Adafruit_SSD1306.cpp b/Adafruit_SSD1306.cpp index 46218d7d..cadc9da1 100644 --- a/Adafruit_SSD1306.cpp +++ b/Adafruit_SSD1306.cpp @@ -326,10 +326,7 @@ Adafruit_SSD1306::Adafruit_SSD1306(int8_t rst_pin) : @brief Destructor for Adafruit_SSD1306 object. */ Adafruit_SSD1306::~Adafruit_SSD1306(void) { - if(buffer) { - free(buffer); - buffer = NULL; - } + deallocateBuffer(); } // LOW-LEVEL UTILS --------------------------------------------------------- @@ -581,6 +578,17 @@ boolean Adafruit_SSD1306::begin(uint8_t vcs, uint8_t addr, boolean reset, return true; // Success } +/*! + @brief If this display owns a display buffer, deallocate it. + @return None (void). +*/ +void Adafruit_SSD1306::deallocateBuffer(void) { + if(buffer && bufferPolicy == Allocated) { + free(buffer); + buffer = NULL; + } +} + // DRAWING FUNCTIONS ------------------------------------------------------- /*! @@ -874,6 +882,20 @@ uint8_t *Adafruit_SSD1306::getBuffer(void) { return buffer; } +/*! + @brief Set the display buffer to be used. + If a buffer was allocated before, it is freed. + @return None (void). + @param buffer + A pointer to the new display buffer. Must be at least + `⌈WIDTH * HEIGHT / 8⌉` bytes in size. +*/ +void Adafruit_SSD1306::setBuffer(uint8_t *buffer) { + deallocateBuffer(); + this->bufferPolicy = Borrowed; + this->buffer = buffer; +} + // REFRESH DISPLAY --------------------------------------------------------- /*! diff --git a/Adafruit_SSD1306.h b/Adafruit_SSD1306.h index 2933cb0b..9dd065cc 100644 --- a/Adafruit_SSD1306.h +++ b/Adafruit_SSD1306.h @@ -148,6 +148,7 @@ class Adafruit_SSD1306 : public Adafruit_GFX { void ssd1306_command(uint8_t c); boolean getPixel(int16_t x, int16_t y); uint8_t *getBuffer(void); + void setBuffer(uint8_t *buffer); private: inline void SPIwrite(uint8_t d) __attribute__((always_inline)); @@ -157,10 +158,12 @@ class Adafruit_SSD1306 : public Adafruit_GFX { uint16_t color); void ssd1306_command1(uint8_t c); void ssd1306_commandList(const uint8_t *c, uint8_t n); + void deallocateBuffer(void); SPIClass *spi; TwoWire *wire; uint8_t *buffer; + enum : bool { Borrowed, Allocated } bufferPolicy = Allocated; int8_t i2caddr, vccstate, page_end; int8_t mosiPin , clkPin , dcPin , csPin, rstPin; #ifdef HAVE_PORTREG @@ -176,4 +179,7 @@ class Adafruit_SSD1306 : public Adafruit_GFX { #endif }; +/// Macro to determine whether this version supports `setBuffer`. +#define ADAFRUIT_SSD1306_HAS_SETBUFFER 1 + #endif // _Adafruit_SSD1306_H_ diff --git a/examples/ssd1306_128x64_spi_static_buffer/ssd1306_128x64_spi_static_buffer.ino b/examples/ssd1306_128x64_spi_static_buffer/ssd1306_128x64_spi_static_buffer.ino new file mode 100644 index 00000000..7fd43391 --- /dev/null +++ b/examples/ssd1306_128x64_spi_static_buffer/ssd1306_128x64_spi_static_buffer.ino @@ -0,0 +1,436 @@ +/************************************************************************** + This is an example for our Monochrome OLEDs based on SSD1306 drivers + + Pick one up today in the adafruit shop! + ------> http://www.adafruit.com/category/63_98 + + This example is for a 128x64 pixel display using SPI to communicate + 4 or 5 pins are required to interface. + + The display buffer is statically allocated, so the dynamic memory usage + can be estimated at compile time more accurately. + You can use the same principle to share one buffer between multiple + displays. + + Adafruit invests time and resources providing this open + source code, please support Adafruit and open-source + hardware by purchasing products from Adafruit! + + Written by Limor Fried/Ladyada for Adafruit Industries, + with contributions from the open source community. + BSD license, check license.txt for more information + All text above, and the splash screen below must be + included in any redistribution. + **************************************************************************/ + +#include +#include +#include +#include + +#define SCREEN_WIDTH 128 // OLED display width, in pixels +#define SCREEN_HEIGHT 64 // OLED display height, in pixels + +// Declaration for SSD1306 display connected using software SPI (default case): +#define OLED_MOSI 9 +#define OLED_CLK 10 +#define OLED_DC 11 +#define OLED_CS 12 +#define OLED_RESET 13 +Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, + OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS); + +/* Comment out above, uncomment this block to use hardware SPI +#define OLED_DC 6 +#define OLED_CS 7 +#define OLED_RESET 8 +Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, + &SPI, OLED_DC, OLED_RESET, OLED_CS); +*/ + +// Create an array of bytes that is used as the display buffer. +// Each pixel occupies one bit of memory, and there are width × height +// pixels in total. A byte stores eight bits, so divide by 8. Adding +// seven rounds up to the next integer. +static uint8_t displayBuffer[(SCREEN_WIDTH * SCREEN_HEIGHT + 7) / 8]; + +#define NUMFLAKES 10 // Number of snowflakes in the animation example + +#define LOGO_HEIGHT 16 +#define LOGO_WIDTH 16 +static const unsigned char PROGMEM logo_bmp[] = +{ B00000000, B11000000, + B00000001, B11000000, + B00000001, B11000000, + B00000011, B11100000, + B11110011, B11100000, + B11111110, B11111000, + B01111110, B11111111, + B00110011, B10011111, + B00011111, B11111100, + B00001101, B01110000, + B00011011, B10100000, + B00111111, B11100000, + B00111111, B11110000, + B01111100, B11110000, + B01110000, B01110000, + B00000000, B00110000 }; + +void setup() { + Serial.begin(9600); + + // Set the display buffer for the display. + display.setBuffer(displayBuffer); + + // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally + // No need to check the return value, because no dynamic allocation is + // performed. + display.begin(SSD1306_SWITCHCAPVCC); + + // Show initial display buffer contents on the screen -- + // the library initializes this with an Adafruit splash screen. + display.display(); + delay(2000); // Pause for 2 seconds + + // Clear the buffer + display.clearDisplay(); + + // Draw a single pixel in white + display.drawPixel(10, 10, WHITE); + + // Show the display buffer on the screen. You MUST call display() after + // drawing commands to make them visible on screen! + display.display(); + delay(2000); + // display.display() is NOT necessary after every single drawing command, + // unless that's what you want...rather, you can batch up a bunch of + // drawing operations and then update the screen all at once by calling + // display.display(). These examples demonstrate both approaches... + + testdrawline(); // Draw many lines + + testdrawrect(); // Draw rectangles (outlines) + + testfillrect(); // Draw rectangles (filled) + + testdrawcircle(); // Draw circles (outlines) + + testfillcircle(); // Draw circles (filled) + + testdrawroundrect(); // Draw rounded rectangles (outlines) + + testfillroundrect(); // Draw rounded rectangles (filled) + + testdrawtriangle(); // Draw triangles (outlines) + + testfilltriangle(); // Draw triangles (filled) + + testdrawchar(); // Draw characters of the default font + + testdrawstyles(); // Draw 'stylized' characters + + testscrolltext(); // Draw scrolling text + + testdrawbitmap(); // Draw a small bitmap image + + // Invert and restore display, pausing in-between + display.invertDisplay(true); + delay(1000); + display.invertDisplay(false); + delay(1000); + + testanimate(logo_bmp, LOGO_WIDTH, LOGO_HEIGHT); // Animate bitmaps +} + +void loop() { +} + +void testdrawline() { + int16_t i; + + display.clearDisplay(); // Clear display buffer + + for(i=0; i=0; i-=4) { + display.drawLine(0, display.height()-1, display.width()-1, i, WHITE); + display.display(); + delay(1); + } + delay(250); + + display.clearDisplay(); + + for(i=display.width()-1; i>=0; i-=4) { + display.drawLine(display.width()-1, display.height()-1, i, 0, WHITE); + display.display(); + delay(1); + } + for(i=display.height()-1; i>=0; i-=4) { + display.drawLine(display.width()-1, display.height()-1, 0, i, WHITE); + display.display(); + delay(1); + } + delay(250); + + display.clearDisplay(); + + for(i=0; i0; i-=3) { + // The INVERSE color is used so circles alternate white/black + display.fillCircle(display.width() / 2, display.height() / 2, i, INVERSE); + display.display(); // Update screen with each newly-drawn circle + delay(1); + } + + delay(2000); +} + +void testdrawroundrect(void) { + display.clearDisplay(); + + for(int16_t i=0; i0; i-=5) { + // The INVERSE color is used so triangles alternate white/black + display.fillTriangle( + display.width()/2 , display.height()/2-i, + display.width()/2-i, display.height()/2+i, + display.width()/2+i, display.height()/2+i, INVERSE); + display.display(); + delay(1); + } + + delay(2000); +} + +void testdrawchar(void) { + display.clearDisplay(); + + display.setTextSize(1); // Normal 1:1 pixel scale + display.setTextColor(WHITE); // Draw white text + display.setCursor(0, 0); // Start at top-left corner + display.cp437(true); // Use full 256 char 'Code Page 437' font + + // Not all the characters will fit on the display. This is normal. + // Library will draw what it can and the rest will be clipped. + for(int16_t i=0; i<256; i++) { + if(i == '\n') display.write(' '); + else display.write(i); + } + + display.display(); + delay(2000); +} + +void testdrawstyles(void) { + display.clearDisplay(); + + display.setTextSize(1); // Normal 1:1 pixel scale + display.setTextColor(WHITE); // Draw white text + display.setCursor(0,0); // Start at top-left corner + display.println(F("Hello, world!")); + + display.setTextColor(BLACK, WHITE); // Draw 'inverse' text + display.println(3.141592); + + display.setTextSize(2); // Draw 2X-scale text + display.setTextColor(WHITE); + display.print(F("0x")); display.println(0xDEADBEEF, HEX); + + display.display(); + delay(2000); +} + +void testscrolltext(void) { + display.clearDisplay(); + + display.setTextSize(2); // Draw 2X-scale text + display.setTextColor(WHITE); + display.setCursor(10, 0); + display.println(F("scroll")); + display.display(); // Show initial text + delay(100); + + // Scroll in various directions, pausing in-between: + display.startscrollright(0x00, 0x0F); + delay(2000); + display.stopscroll(); + delay(1000); + display.startscrollleft(0x00, 0x0F); + delay(2000); + display.stopscroll(); + delay(1000); + display.startscrolldiagright(0x00, 0x07); + delay(2000); + display.startscrolldiagleft(0x00, 0x07); + delay(2000); + display.stopscroll(); + delay(1000); +} + +void testdrawbitmap(void) { + display.clearDisplay(); + + display.drawBitmap( + (display.width() - LOGO_WIDTH ) / 2, + (display.height() - LOGO_HEIGHT) / 2, + logo_bmp, LOGO_WIDTH, LOGO_HEIGHT, 1); + display.display(); + delay(1000); +} + +#define XPOS 0 // Indexes into the 'icons' array in function below +#define YPOS 1 +#define DELTAY 2 + +void testanimate(const uint8_t *bitmap, uint8_t w, uint8_t h) { + int8_t f, icons[NUMFLAKES][3]; + + // Initialize 'snowflake' positions + for(f=0; f< NUMFLAKES; f++) { + icons[f][XPOS] = random(1 - LOGO_WIDTH, display.width()); + icons[f][YPOS] = -LOGO_HEIGHT; + icons[f][DELTAY] = random(1, 6); + Serial.print(F("x: ")); + Serial.print(icons[f][XPOS], DEC); + Serial.print(F(" y: ")); + Serial.print(icons[f][YPOS], DEC); + Serial.print(F(" dy: ")); + Serial.println(icons[f][DELTAY], DEC); + } + + for(;;) { // Loop forever... + display.clearDisplay(); // Clear the display buffer + + // Draw each snowflake: + for(f=0; f< NUMFLAKES; f++) { + display.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, WHITE); + } + + display.display(); // Show the display buffer on the screen + delay(200); // Pause for 1/10 second + + // Then update coordinates of each flake... + for(f=0; f< NUMFLAKES; f++) { + icons[f][YPOS] += icons[f][DELTAY]; + // If snowflake is off the bottom of the screen... + if (icons[f][YPOS] >= display.height()) { + // Reinitialize to a random position, just off the top + icons[f][XPOS] = random(1 - LOGO_WIDTH, display.width()); + icons[f][YPOS] = -LOGO_HEIGHT; + icons[f][DELTAY] = random(1, 6); + } + } + } +}