Gray oled support (#317)

rename monooled to grayoled and support 1 or 4 bpp - backtested with SH110x and also new SSD1327
bump too
This commit is contained in:
Limor "Ladyada" Fried 2020-08-23 14:18:56 -04:00 committed by GitHub
parent 0752d4bd4f
commit d9220ba7d3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 93 additions and 75 deletions

View File

@ -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 * OLED displays: http://www.adafruit.com/category/63_98
* *
* These displays use I2C or SPI to communicate. I2C requires 2 pins * 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 #if !defined(__AVR_ATtiny85__) // Not for ATtiny, at all
#include "Adafruit_MonoOLED.h" #include "Adafruit_GrayOLED.h"
#include <Adafruit_GFX.h> #include <Adafruit_GFX.h>
// SOME DEFINES AND STATIC VARIABLES USED INTERNALLY ----------------------- // 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 (((a) ^= (b)), ((b) ^= (a)), ((a) ^= (b))) ///< No-temp-var swap operation
// CONSTRUCTORS, DESTRUCTOR ------------------------------------------------ // CONSTRUCTORS, DESTRUCTOR ------------------------------------------------
/*! /*!
@brief Constructor for I2C-interfaced OLED displays. @brief Constructor for I2C-interfaced OLED displays.
@param bpp Bits per pixel, 1 for monochrome, 4 for 16-gray
@param w @param w
Display width in pixels Display width in pixels
@param h @param h
@ -59,18 +60,19 @@
@note Call the object's begin() function before use -- buffer @note Call the object's begin() function before use -- buffer
allocation is performed there! allocation is performed there!
*/ */
Adafruit_MonoOLED::Adafruit_MonoOLED(uint16_t w, uint16_t h, TwoWire *twi, Adafruit_GrayOLED::Adafruit_GrayOLED(uint8_t bpp, uint16_t w, uint16_t h,
int8_t rst_pin, uint32_t clkDuring, TwoWire *twi, int8_t rst_pin,
uint32_t clkAfter) uint32_t clkDuring, uint32_t clkAfter)
: Adafruit_GFX(w, h), i2c_preclk(clkDuring), i2c_postclk(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; i2c_dev = NULL;
_theWire = twi; _theWire = twi;
} }
/*! /*!
@brief Constructor for SPI MonoOLED displays, using software (bitbang) @brief Constructor for SPI GrayOLED displays, using software (bitbang)
SPI. SPI.
@param bpp Bits per pixel, 1 for monochrome, 4 for 16-gray
@param w @param w
Display width in pixels Display width in pixels
@param h @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 @note Call the object's begin() function before use -- buffer
allocation is performed there! allocation is performed there!
*/ */
Adafruit_MonoOLED::Adafruit_MonoOLED(uint16_t w, uint16_t h, int8_t mosi_pin, Adafruit_GrayOLED::Adafruit_GrayOLED(uint8_t bpp, uint16_t w, uint16_t h,
int8_t sclk_pin, int8_t dc_pin, int8_t mosi_pin, int8_t sclk_pin,
int8_t rst_pin, int8_t cs_pin) int8_t dc_pin, int8_t rst_pin,
: Adafruit_GFX(w, h), dcPin(dc_pin), csPin(cs_pin), rstPin(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); 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 @param w
Display width in pixels Display width in pixels
@param h @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 @note Call the object's begin() function before use -- buffer
allocation is performed there! allocation is performed there!
*/ */
Adafruit_MonoOLED::Adafruit_MonoOLED(uint16_t w, uint16_t h, SPIClass *spi, Adafruit_GrayOLED::Adafruit_GrayOLED(uint8_t bpp, uint16_t w, uint16_t h,
int8_t dc_pin, int8_t rst_pin, SPIClass *spi, int8_t dc_pin,
int8_t cs_pin, uint32_t bitrate) int8_t rst_pin, int8_t cs_pin,
: Adafruit_GFX(w, h), dcPin(dc_pin), csPin(cs_pin), rstPin(rst_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_dev = new Adafruit_SPIDevice(cs_pin, bitrate, SPI_BITORDER_MSBFIRST,
SPI_MODE0, spi); 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) { if (buffer) {
free(buffer); free(buffer);
buffer = NULL; buffer = NULL;
@ -157,7 +164,7 @@ Adafruit_MonoOLED::~Adafruit_MonoOLED(void) {
needed. needed.
@param c The single byte command @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 if (i2c_dev) { // I2C
uint8_t buf[2] = {0x00, c}; // Co = 0, D/C = 0 uint8_t buf[2] = {0x00, c}; // Co = 0, D/C = 0
i2c_dev->write(buf, 2); 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 @brief Issue multiple bytes of commands OLED, using I2C or hard/soft SPI as
needed. 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. @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 if (i2c_dev) { // I2C
uint8_t dc_byte = 0x00; // Co = 0, D/C = 0 uint8_t dc_byte = 0x00; // Co = 0, D/C = 0
if (!i2c_dev->write((uint8_t *)c, n, true, &dc_byte, 1)) { 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. proceeding.
@note MUST call this function before any drawing or updates! @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 // 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; return false;
} }
@ -269,17 +277,16 @@ bool Adafruit_MonoOLED::_init(uint8_t addr, bool reset) {
@param color @param color
Pixel color, one of: MONOOLED_BLACK, MONOOLED_WHITE or Pixel color, one of: MONOOLED_BLACK, MONOOLED_WHITE or
MONOOLED_INVERT. MONOOLED_INVERT.
@return None (void).
@note Changes buffer contents only, no immediate effect on display. @note Changes buffer contents only, no immediate effect on display.
Follow up with a call to display(), or with other graphics Follow up with a call to display(), or with other graphics
commands as needed by one's own application. 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())) { if ((x >= 0) && (x < width()) && (y >= 0) && (y < height())) {
// Pixel is in-bounds. Rotate coordinates if needed. // Pixel is in-bounds. Rotate coordinates if needed.
switch (getRotation()) { switch (getRotation()) {
case 1: case 1:
monooled_swap(x, y); grayoled_swap(x, y);
x = WIDTH - x - 1; x = WIDTH - x - 1;
break; break;
case 2: case 2:
@ -287,7 +294,7 @@ void Adafruit_MonoOLED::drawPixel(int16_t x, int16_t y, uint16_t color) {
y = HEIGHT - y - 1; y = HEIGHT - y - 1;
break; break;
case 3: case 3:
monooled_swap(x, y); grayoled_swap(x, y);
y = HEIGHT - y - 1; y = HEIGHT - y - 1;
break; break;
} }
@ -298,6 +305,7 @@ void Adafruit_MonoOLED::drawPixel(int16_t x, int16_t y, uint16_t color) {
window_x2 = max(window_x2, x); window_x2 = max(window_x2, x);
window_y2 = max(window_y2, y); window_y2 = max(window_y2, y);
if (_bpp == 1) {
switch (color) { switch (color) {
case MONOOLED_WHITE: case MONOOLED_WHITE:
buffer[x + (y / 8) * WIDTH] |= (1 << (y & 7)); buffer[x + (y / 8) * WIDTH] |= (1 << (y & 7));
@ -310,17 +318,30 @@ void Adafruit_MonoOLED::drawPixel(int16_t x, int16_t y, uint16_t color) {
break; 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). @brief Clear contents of display buffer (set all pixels to off).
@return None (void).
@note Changes buffer contents only, no immediate effect on display. @note Changes buffer contents only, no immediate effect on display.
Follow up with a call to display(), or with other graphics Follow up with a call to display(), or with other graphics
commands as needed by one's own application. commands as needed by one's own application.
*/ */
void Adafruit_MonoOLED::clearDisplay(void) { void Adafruit_GrayOLED::clearDisplay(void) {
memset(buffer, 0, WIDTH * ((HEIGHT + 7) / 8)); memset(buffer, 0, _bpp * WIDTH * ((HEIGHT + 7) / 8));
// set max dirty window // set max dirty window
window_x1 = 0; window_x1 = 0;
window_y1 = 0; window_y1 = 0;
@ -339,12 +360,12 @@ void Adafruit_MonoOLED::clearDisplay(void) {
@note Reads from buffer contents; may not reflect current contents of @note Reads from buffer contents; may not reflect current contents of
screen if display() has not been called. 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())) { if ((x >= 0) && (x < width()) && (y >= 0) && (y < height())) {
// Pixel is in-bounds. Rotate coordinates if needed. // Pixel is in-bounds. Rotate coordinates if needed.
switch (getRotation()) { switch (getRotation()) {
case 1: case 1:
monooled_swap(x, y); grayoled_swap(x, y);
x = WIDTH - x - 1; x = WIDTH - x - 1;
break; break;
case 2: case 2:
@ -352,7 +373,7 @@ bool Adafruit_MonoOLED::getPixel(int16_t x, int16_t y) {
y = HEIGHT - y - 1; y = HEIGHT - y - 1;
break; break;
case 3: case 3:
monooled_swap(x, y); grayoled_swap(x, y);
y = HEIGHT - y - 1; y = HEIGHT - y - 1;
break; 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 @return Pointer to an unsigned 8-bit array, column-major, columns padded
to full byte boundary if needed. 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 ------------------------------------------------- // OTHER HARDWARE SETTINGS -------------------------------------------------
@ -376,26 +397,24 @@ uint8_t *Adafruit_MonoOLED::getBuffer(void) { return buffer; }
@param i @param i
If true, switch to invert mode (black-on-white), else normal If true, switch to invert mode (black-on-white), else normal
mode (white-on-black). mode (white-on-black).
@return None (void).
@note This has an immediate effect on the display, no need to call the @note This has an immediate effect on the display, no need to call the
display() function -- buffer contents are not changed, rather a display() function -- buffer contents are not changed, rather a
different pixel mode of the display hardware is used. When different pixel mode of the display hardware is used. When
enabled, drawing MONOOLED_BLACK (value 0) pixels will actually draw enabled, drawing MONOOLED_BLACK (value 0) pixels will actually draw
white, MONOOLED_WHITE (value 1) will draw black. white, MONOOLED_WHITE (value 1) will draw black.
*/ */
void Adafruit_MonoOLED::invertDisplay(bool i) { void Adafruit_GrayOLED::invertDisplay(bool i) {
oled_command(i ? MONOOLED_INVERTDISPLAY : MONOOLED_NORMALDISPLAY); oled_command(i ? GRAYOLED_INVERTDISPLAY : GRAYOLED_NORMALDISPLAY);
} }
/*! /*!
@brief Adjust the display contrast. @brief Adjust the display contrast.
@param level The contrast level from 0 to 0x7F @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 @note This has an immediate effect on the display, no need to call the
display() function -- buffer contents are not changed. display() function -- buffer contents are not changed.
*/ */
void Adafruit_MonoOLED::setContrast(uint8_t level) { void Adafruit_GrayOLED::setContrast(uint8_t level) {
uint8_t cmd[] = {MONOOLED_SETCONTRAST, level}; uint8_t cmd[] = {GRAYOLED_SETCONTRAST, level};
oled_commandList(cmd, 2); oled_commandList(cmd, 2);
} }

View File

@ -1,8 +1,8 @@
/*! /*!
* @file Adafruit_MonoOLED.h * @file Adafruit_GrayOLED.h
* *
* This is part of for Adafruit's GFX library, supplying generic support * 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 * 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, * (SCL+SDA) and optionally a RESET pin. SPI requires 4 pins (MOSI, SCK,
@ -21,8 +21,8 @@
* *
*/ */
#ifndef _Adafruit_MONOOLED_H_ #ifndef _Adafruit_GRAYOLED_H_
#define _Adafruit_MONOOLED_H_ #define _Adafruit_GRAYOLED_H_
#if !defined(__AVR_ATtiny85__) // Not for ATtiny, at all #if !defined(__AVR_ATtiny85__) // Not for ATtiny, at all
@ -32,33 +32,31 @@
#include <SPI.h> #include <SPI.h>
#include <Wire.h> #include <Wire.h>
#define MONOOLED_BLACK 0 ///< Draw 'off' pixels #define GRAYOLED_SETCONTRAST 0x81 ///< Generic contrast for almost all OLEDs
#define MONOOLED_WHITE 1 ///< Draw 'on' pixels #define GRAYOLED_NORMALDISPLAY 0xA6 ///< Generic non-invert for almost all OLEDs
#define MONOOLED_INVERSE 2 ///< Invert pixels #define GRAYOLED_INVERTDISPLAY 0xA7 ///< Generic invert for almost all OLEDs
/// These seem to be common commands for OLEDs #define MONOOLED_BLACK 0 ///< Default black 'color' for monochrome OLEDS
#define MONOOLED_SETCONTRAST 0x81 ///< See datasheet #define MONOOLED_WHITE 1 ///< Default white 'color' for monochrome OLEDS
#define MONOOLED_NORMALDISPLAY 0xA6 ///< See datasheet #define MONOOLED_INVERSE 2 ///< Default inversion command for monochrome OLEDS
#define MONOOLED_INVERTDISPLAY 0xA7 ///< See datasheet
#define MONOOLED_DISPLAYOFF 0xAE ///< See datasheet
#define MONOOLED_DISPLAYON 0xAF ///< See datasheet
/*! /*!
@brief Class that stores state and functions for interacting with @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: 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, int8_t rst_pin = -1, uint32_t preclk = 400000,
uint32_t postclk = 100000); uint32_t postclk = 100000);
Adafruit_MonoOLED(uint16_t w, uint16_t h, int8_t mosi_pin, int8_t sclk_pin, Adafruit_GrayOLED(uint8_t bpp, uint16_t w, uint16_t h, int8_t mosi_pin,
int8_t dc_pin, int8_t rst_pin, int8_t cs_pin); int8_t sclk_pin, int8_t dc_pin, int8_t rst_pin,
Adafruit_MonoOLED(uint16_t w, uint16_t h, SPIClass *spi, int8_t dc_pin, int8_t cs_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); uint32_t bitrate = 8000000UL);
~Adafruit_MonoOLED(void); ~Adafruit_GrayOLED(void);
/** /**
@brief The function that sub-classes define that writes out the buffer to @brief The function that sub-classes define that writes out the buffer to
@ -93,9 +91,10 @@ protected:
csPin, ///< The Arduino pin connected to CS (for SPI) csPin, ///< The Arduino pin connected to CS (for SPI)
rstPin; ///< The Arduino pin connected to reset (-1 if unused) rstPin; ///< The Arduino pin connected to reset (-1 if unused)
uint8_t _bpp = 1; ///< Bits per pixel color for this display
private: private:
TwoWire *_theWire = NULL; ///< The underlying hardware I2C TwoWire *_theWire = NULL; ///< The underlying hardware I2C
}; };
#endif // end __AVR_ATtiny85__ #endif // end __AVR_ATtiny85__
#endif // _Adafruit_MonoOLED_H_ #endif // _Adafruit_GrayOLED_H_

View File

@ -1,5 +1,5 @@
name=Adafruit GFX Library name=Adafruit GFX Library
version=1.9.0 version=1.10.0
author=Adafruit author=Adafruit
maintainer=Adafruit <info@adafruit.com> maintainer=Adafruit <info@adafruit.com>
sentence=Adafruit GFX graphics core library, this is the 'core' class that all our other graphics libraries derive from. sentence=Adafruit GFX graphics core library, this is the 'core' class that all our other graphics libraries derive from.