From d9220ba7d3cb49915f6aca0feef680e3fed0c400 Mon Sep 17 00:00:00 2001 From: "Limor \"Ladyada\" Fried" Date: Sun, 23 Aug 2020 14:18:56 -0400 Subject: [PATCH] Gray oled support (#317) rename monooled to grayoled and support 1 or 4 bpp - backtested with SH110x and also new SSD1327 bump too --- ...ruit_MonoOLED.cpp => Adafruit_GrayOLED.cpp | 123 ++++++++++-------- Adafruit_MonoOLED.h => Adafruit_GrayOLED.h | 43 +++--- library.properties | 2 +- 3 files changed, 93 insertions(+), 75 deletions(-) rename Adafruit_MonoOLED.cpp => Adafruit_GrayOLED.cpp (80%) rename Adafruit_MonoOLED.h => Adafruit_GrayOLED.h (69%) diff --git a/Adafruit_MonoOLED.cpp b/Adafruit_GrayOLED.cpp similarity index 80% rename from Adafruit_MonoOLED.cpp rename to Adafruit_GrayOLED.cpp index 7cf54363..f487861b 100644 --- a/Adafruit_MonoOLED.cpp +++ b/Adafruit_GrayOLED.cpp @@ -1,7 +1,7 @@ /*! - * @file Adafruit_MonoOLED.cpp + * @file Adafruit_GrayOLED.cpp * - * This is documentation for Adafruit's generic library for monochrome + * This is documentation for Adafruit's generic library for grayscale * OLED displays: http://www.adafruit.com/category/63_98 * * These displays use I2C or SPI to communicate. I2C requires 2 pins @@ -17,18 +17,19 @@ #if !defined(__AVR_ATtiny85__) // Not for ATtiny, at all -#include "Adafruit_MonoOLED.h" +#include "Adafruit_GrayOLED.h" #include // SOME DEFINES AND STATIC VARIABLES USED INTERNALLY ----------------------- -#define monooled_swap(a, b) \ +#define grayoled_swap(a, b) \ (((a) ^= (b)), ((b) ^= (a)), ((a) ^= (b))) ///< No-temp-var swap operation // CONSTRUCTORS, DESTRUCTOR ------------------------------------------------ /*! @brief Constructor for I2C-interfaced OLED displays. + @param bpp Bits per pixel, 1 for monochrome, 4 for 16-gray @param w Display width in pixels @param h @@ -59,18 +60,19 @@ @note Call the object's begin() function before use -- buffer allocation is performed there! */ -Adafruit_MonoOLED::Adafruit_MonoOLED(uint16_t w, uint16_t h, TwoWire *twi, - int8_t rst_pin, uint32_t clkDuring, - uint32_t clkAfter) +Adafruit_GrayOLED::Adafruit_GrayOLED(uint8_t bpp, uint16_t w, uint16_t h, + TwoWire *twi, int8_t rst_pin, + uint32_t clkDuring, uint32_t clkAfter) : Adafruit_GFX(w, h), i2c_preclk(clkDuring), i2c_postclk(clkAfter), - buffer(NULL), dcPin(-1), csPin(-1), rstPin(rst_pin) { + buffer(NULL), dcPin(-1), csPin(-1), rstPin(rst_pin), _bpp(bpp) { i2c_dev = NULL; _theWire = twi; } /*! - @brief Constructor for SPI MonoOLED displays, using software (bitbang) + @brief Constructor for SPI GrayOLED displays, using software (bitbang) SPI. + @param bpp Bits per pixel, 1 for monochrome, 4 for 16-gray @param w Display width in pixels @param h @@ -94,16 +96,19 @@ Adafruit_MonoOLED::Adafruit_MonoOLED(uint16_t w, uint16_t h, TwoWire *twi, @note Call the object's begin() function before use -- buffer allocation is performed there! */ -Adafruit_MonoOLED::Adafruit_MonoOLED(uint16_t w, uint16_t h, int8_t mosi_pin, - int8_t sclk_pin, int8_t dc_pin, - int8_t rst_pin, int8_t cs_pin) - : Adafruit_GFX(w, h), dcPin(dc_pin), csPin(cs_pin), rstPin(rst_pin) { +Adafruit_GrayOLED::Adafruit_GrayOLED(uint8_t bpp, uint16_t w, uint16_t h, + int8_t mosi_pin, int8_t sclk_pin, + int8_t dc_pin, int8_t rst_pin, + int8_t cs_pin) + : Adafruit_GFX(w, h), dcPin(dc_pin), csPin(cs_pin), rstPin(rst_pin), + _bpp(bpp) { spi_dev = new Adafruit_SPIDevice(cs_pin, sclk_pin, -1, mosi_pin, 1000000); } /*! - @brief Constructor for SPI MonoOLED displays, using native hardware SPI. + @brief Constructor for SPI GrayOLED displays, using native hardware SPI. + @param bpp Bits per pixel, 1 for monochrome, 4 for 16-gray @param w Display width in pixels @param h @@ -127,19 +132,21 @@ Adafruit_MonoOLED::Adafruit_MonoOLED(uint16_t w, uint16_t h, int8_t mosi_pin, @note Call the object's begin() function before use -- buffer allocation is performed there! */ -Adafruit_MonoOLED::Adafruit_MonoOLED(uint16_t w, uint16_t h, SPIClass *spi, - int8_t dc_pin, int8_t rst_pin, - int8_t cs_pin, uint32_t bitrate) - : Adafruit_GFX(w, h), dcPin(dc_pin), csPin(cs_pin), rstPin(rst_pin) { +Adafruit_GrayOLED::Adafruit_GrayOLED(uint8_t bpp, uint16_t w, uint16_t h, + SPIClass *spi, int8_t dc_pin, + int8_t rst_pin, int8_t cs_pin, + uint32_t bitrate) + : Adafruit_GFX(w, h), dcPin(dc_pin), csPin(cs_pin), rstPin(rst_pin), + _bpp(bpp) { spi_dev = new Adafruit_SPIDevice(cs_pin, bitrate, SPI_BITORDER_MSBFIRST, SPI_MODE0, spi); } /*! - @brief Destructor for Adafruit_MonoOLED object. + @brief Destructor for Adafruit_GrayOLED object. */ -Adafruit_MonoOLED::~Adafruit_MonoOLED(void) { +Adafruit_GrayOLED::~Adafruit_GrayOLED(void) { if (buffer) { free(buffer); buffer = NULL; @@ -157,7 +164,7 @@ Adafruit_MonoOLED::~Adafruit_MonoOLED(void) { needed. @param c The single byte command */ -void Adafruit_MonoOLED::oled_command(uint8_t c) { +void Adafruit_GrayOLED::oled_command(uint8_t c) { if (i2c_dev) { // I2C uint8_t buf[2] = {0x00, c}; // Co = 0, D/C = 0 i2c_dev->write(buf, 2); @@ -167,7 +174,7 @@ void Adafruit_MonoOLED::oled_command(uint8_t c) { } } -// Issue list of commands to MonoOLED +// Issue list of commands to GrayOLED /*! @brief Issue multiple bytes of commands OLED, using I2C or hard/soft SPI as needed. @@ -176,7 +183,7 @@ void Adafruit_MonoOLED::oled_command(uint8_t c) { @returns True for success on ability to write the data in I2C. */ -bool Adafruit_MonoOLED::oled_commandList(const uint8_t *c, uint8_t n) { +bool Adafruit_GrayOLED::oled_commandList(const uint8_t *c, uint8_t n) { if (i2c_dev) { // I2C uint8_t dc_byte = 0x00; // Co = 0, D/C = 0 if (!i2c_dev->write((uint8_t *)c, n, true, &dc_byte, 1)) { @@ -213,10 +220,11 @@ bool Adafruit_MonoOLED::oled_commandList(const uint8_t *c, uint8_t n) { proceeding. @note MUST call this function before any drawing or updates! */ -bool Adafruit_MonoOLED::_init(uint8_t addr, bool reset) { +bool Adafruit_GrayOLED::_init(uint8_t addr, bool reset) { // attempt to malloc the bitmap framebuffer - if ((!buffer) && !(buffer = (uint8_t *)malloc(WIDTH * ((HEIGHT + 7) / 8)))) { + if ((!buffer) && + !(buffer = (uint8_t *)malloc(_bpp * WIDTH * ((HEIGHT + 7) / 8)))) { return false; } @@ -269,17 +277,16 @@ bool Adafruit_MonoOLED::_init(uint8_t addr, bool reset) { @param color Pixel color, one of: MONOOLED_BLACK, MONOOLED_WHITE or MONOOLED_INVERT. - @return None (void). @note Changes buffer contents only, no immediate effect on display. Follow up with a call to display(), or with other graphics commands as needed by one's own application. */ -void Adafruit_MonoOLED::drawPixel(int16_t x, int16_t y, uint16_t color) { +void Adafruit_GrayOLED::drawPixel(int16_t x, int16_t y, uint16_t color) { if ((x >= 0) && (x < width()) && (y >= 0) && (y < height())) { // Pixel is in-bounds. Rotate coordinates if needed. switch (getRotation()) { case 1: - monooled_swap(x, y); + grayoled_swap(x, y); x = WIDTH - x - 1; break; case 2: @@ -287,7 +294,7 @@ void Adafruit_MonoOLED::drawPixel(int16_t x, int16_t y, uint16_t color) { y = HEIGHT - y - 1; break; case 3: - monooled_swap(x, y); + grayoled_swap(x, y); y = HEIGHT - y - 1; break; } @@ -298,29 +305,43 @@ void Adafruit_MonoOLED::drawPixel(int16_t x, int16_t y, uint16_t color) { window_x2 = max(window_x2, x); window_y2 = max(window_y2, y); - switch (color) { - case MONOOLED_WHITE: - buffer[x + (y / 8) * WIDTH] |= (1 << (y & 7)); - break; - case MONOOLED_BLACK: - buffer[x + (y / 8) * WIDTH] &= ~(1 << (y & 7)); - break; - case MONOOLED_INVERSE: - buffer[x + (y / 8) * WIDTH] ^= (1 << (y & 7)); - break; + if (_bpp == 1) { + switch (color) { + case MONOOLED_WHITE: + buffer[x + (y / 8) * WIDTH] |= (1 << (y & 7)); + break; + case MONOOLED_BLACK: + buffer[x + (y / 8) * WIDTH] &= ~(1 << (y & 7)); + break; + case MONOOLED_INVERSE: + buffer[x + (y / 8) * WIDTH] ^= (1 << (y & 7)); + break; + } + } + if (_bpp == 4) { + uint8_t *pixelptr = &buffer[x / 2 + (y * WIDTH / 2)]; + // Serial.printf("(%d, %d) -> offset %d\n", x, y, x/2 + (y * WIDTH / 2)); + if (x % 2 == 0) { // even, left nibble + uint8_t t = pixelptr[0] & 0x0F; + t |= (color & 0xF) << 4; + pixelptr[0] = t; + } else { // odd, right lower nibble + uint8_t t = pixelptr[0] & 0xF0; + t |= color & 0xF; + pixelptr[0] = t; + } } } } /*! @brief Clear contents of display buffer (set all pixels to off). - @return None (void). @note Changes buffer contents only, no immediate effect on display. Follow up with a call to display(), or with other graphics commands as needed by one's own application. */ -void Adafruit_MonoOLED::clearDisplay(void) { - memset(buffer, 0, WIDTH * ((HEIGHT + 7) / 8)); +void Adafruit_GrayOLED::clearDisplay(void) { + memset(buffer, 0, _bpp * WIDTH * ((HEIGHT + 7) / 8)); // set max dirty window window_x1 = 0; window_y1 = 0; @@ -339,12 +360,12 @@ void Adafruit_MonoOLED::clearDisplay(void) { @note Reads from buffer contents; may not reflect current contents of screen if display() has not been called. */ -bool Adafruit_MonoOLED::getPixel(int16_t x, int16_t y) { +bool Adafruit_GrayOLED::getPixel(int16_t x, int16_t y) { if ((x >= 0) && (x < width()) && (y >= 0) && (y < height())) { // Pixel is in-bounds. Rotate coordinates if needed. switch (getRotation()) { case 1: - monooled_swap(x, y); + grayoled_swap(x, y); x = WIDTH - x - 1; break; case 2: @@ -352,7 +373,7 @@ bool Adafruit_MonoOLED::getPixel(int16_t x, int16_t y) { y = HEIGHT - y - 1; break; case 3: - monooled_swap(x, y); + grayoled_swap(x, y); y = HEIGHT - y - 1; break; } @@ -366,7 +387,7 @@ bool Adafruit_MonoOLED::getPixel(int16_t x, int16_t y) { @return Pointer to an unsigned 8-bit array, column-major, columns padded to full byte boundary if needed. */ -uint8_t *Adafruit_MonoOLED::getBuffer(void) { return buffer; } +uint8_t *Adafruit_GrayOLED::getBuffer(void) { return buffer; } // OTHER HARDWARE SETTINGS ------------------------------------------------- @@ -376,26 +397,24 @@ uint8_t *Adafruit_MonoOLED::getBuffer(void) { return buffer; } @param i If true, switch to invert mode (black-on-white), else normal mode (white-on-black). - @return None (void). @note This has an immediate effect on the display, no need to call the display() function -- buffer contents are not changed, rather a different pixel mode of the display hardware is used. When enabled, drawing MONOOLED_BLACK (value 0) pixels will actually draw white, MONOOLED_WHITE (value 1) will draw black. */ -void Adafruit_MonoOLED::invertDisplay(bool i) { - oled_command(i ? MONOOLED_INVERTDISPLAY : MONOOLED_NORMALDISPLAY); +void Adafruit_GrayOLED::invertDisplay(bool i) { + oled_command(i ? GRAYOLED_INVERTDISPLAY : GRAYOLED_NORMALDISPLAY); } /*! @brief Adjust the display contrast. @param level The contrast level from 0 to 0x7F - @return None (void). @note This has an immediate effect on the display, no need to call the display() function -- buffer contents are not changed. */ -void Adafruit_MonoOLED::setContrast(uint8_t level) { - uint8_t cmd[] = {MONOOLED_SETCONTRAST, level}; +void Adafruit_GrayOLED::setContrast(uint8_t level) { + uint8_t cmd[] = {GRAYOLED_SETCONTRAST, level}; oled_commandList(cmd, 2); } diff --git a/Adafruit_MonoOLED.h b/Adafruit_GrayOLED.h similarity index 69% rename from Adafruit_MonoOLED.h rename to Adafruit_GrayOLED.h index 70fc6f90..8ec7b23d 100644 --- a/Adafruit_MonoOLED.h +++ b/Adafruit_GrayOLED.h @@ -1,8 +1,8 @@ /*! - * @file Adafruit_MonoOLED.h + * @file Adafruit_GrayOLED.h * * This is part of for Adafruit's GFX library, supplying generic support - * for monochrome OLED displays: http://www.adafruit.com/category/63_98 + * for grayscale OLED displays: http://www.adafruit.com/category/63_98 * * These displays use I2C or SPI to communicate. I2C requires 2 pins * (SCL+SDA) and optionally a RESET pin. SPI requires 4 pins (MOSI, SCK, @@ -21,8 +21,8 @@ * */ -#ifndef _Adafruit_MONOOLED_H_ -#define _Adafruit_MONOOLED_H_ +#ifndef _Adafruit_GRAYOLED_H_ +#define _Adafruit_GRAYOLED_H_ #if !defined(__AVR_ATtiny85__) // Not for ATtiny, at all @@ -32,33 +32,31 @@ #include #include -#define MONOOLED_BLACK 0 ///< Draw 'off' pixels -#define MONOOLED_WHITE 1 ///< Draw 'on' pixels -#define MONOOLED_INVERSE 2 ///< Invert pixels +#define GRAYOLED_SETCONTRAST 0x81 ///< Generic contrast for almost all OLEDs +#define GRAYOLED_NORMALDISPLAY 0xA6 ///< Generic non-invert for almost all OLEDs +#define GRAYOLED_INVERTDISPLAY 0xA7 ///< Generic invert for almost all OLEDs -/// These seem to be common commands for OLEDs -#define MONOOLED_SETCONTRAST 0x81 ///< See datasheet -#define MONOOLED_NORMALDISPLAY 0xA6 ///< See datasheet -#define MONOOLED_INVERTDISPLAY 0xA7 ///< See datasheet -#define MONOOLED_DISPLAYOFF 0xAE ///< See datasheet -#define MONOOLED_DISPLAYON 0xAF ///< See datasheet +#define MONOOLED_BLACK 0 ///< Default black 'color' for monochrome OLEDS +#define MONOOLED_WHITE 1 ///< Default white 'color' for monochrome OLEDS +#define MONOOLED_INVERSE 2 ///< Default inversion command for monochrome OLEDS /*! @brief Class that stores state and functions for interacting with - generic monochrome OLED displays. + generic grayscale OLED displays. */ -class Adafruit_MonoOLED : public Adafruit_GFX { +class Adafruit_GrayOLED : public Adafruit_GFX { public: - Adafruit_MonoOLED(uint16_t w, uint16_t h, TwoWire *twi = &Wire, + Adafruit_GrayOLED(uint8_t bpp, uint16_t w, uint16_t h, TwoWire *twi = &Wire, int8_t rst_pin = -1, uint32_t preclk = 400000, uint32_t postclk = 100000); - Adafruit_MonoOLED(uint16_t w, uint16_t h, int8_t mosi_pin, int8_t sclk_pin, - int8_t dc_pin, int8_t rst_pin, int8_t cs_pin); - Adafruit_MonoOLED(uint16_t w, uint16_t h, SPIClass *spi, int8_t dc_pin, - int8_t rst_pin, int8_t cs_pin, + Adafruit_GrayOLED(uint8_t bpp, uint16_t w, uint16_t h, int8_t mosi_pin, + int8_t sclk_pin, int8_t dc_pin, int8_t rst_pin, + int8_t cs_pin); + Adafruit_GrayOLED(uint8_t bpp, uint16_t w, uint16_t h, SPIClass *spi, + int8_t dc_pin, int8_t rst_pin, int8_t cs_pin, uint32_t bitrate = 8000000UL); - ~Adafruit_MonoOLED(void); + ~Adafruit_GrayOLED(void); /** @brief The function that sub-classes define that writes out the buffer to @@ -93,9 +91,10 @@ class Adafruit_MonoOLED : public Adafruit_GFX { csPin, ///< The Arduino pin connected to CS (for SPI) rstPin; ///< The Arduino pin connected to reset (-1 if unused) + uint8_t _bpp = 1; ///< Bits per pixel color for this display private: TwoWire *_theWire = NULL; ///< The underlying hardware I2C }; #endif // end __AVR_ATtiny85__ -#endif // _Adafruit_MonoOLED_H_ +#endif // _Adafruit_GrayOLED_H_ diff --git a/library.properties b/library.properties index e15f5ddd..33cd57b2 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=Adafruit GFX Library -version=1.9.0 +version=1.10.0 author=Adafruit maintainer=Adafruit sentence=Adafruit GFX graphics core library, this is the 'core' class that all our other graphics libraries derive from.