Merge pull request #10328 from jeroenvermeulen/feature_adafruit_ssd1331

Feature: Adafruit SSD1331 Display support
This commit is contained in:
Theo Arends 2021-01-01 15:10:05 +01:00 committed by GitHub
commit ad82ba78c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 3255 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,520 @@
/*!
* @file Adafruit_SPITFT.h
*
* Part of Adafruit's GFX graphics library. Originally this class was
* written to handle a range of color TFT displays connected via SPI,
* but over time this library and some display-specific subclasses have
* mutated to include some color OLEDs as well as parallel-interfaced
* displays. The name's been kept for the sake of older code.
*
* 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 "ladyada" Fried for Adafruit Industries,
* with contributions from the open source community.
*
* BSD license, all text here must be included in any redistribution.
*/
#ifndef _ADAFRUIT_SPITFT_H_
#define _ADAFRUIT_SPITFT_H_
#if !defined(__AVR_ATtiny85__) // Not for ATtiny, at all
#include <SPI.h>
#include "Adafruit_GFX.h"
#include "renderer.h"
// HARDWARE CONFIG ---------------------------------------------------------
#if defined(__AVR__)
typedef uint8_t ADAGFX_PORT_t; ///< PORT values are 8-bit
#define USE_FAST_PINIO ///< Use direct PORT register access
#elif defined(ARDUINO_STM32_FEATHER) // WICED
typedef class HardwareSPI SPIClass; ///< SPI is a bit odd on WICED
typedef uint32_t ADAGFX_PORT_t; ///< PORT values are 32-bit
#elif defined(__arm__)
#if defined(ARDUINO_ARCH_SAMD)
// Adafruit M0, M4
typedef uint32_t ADAGFX_PORT_t; ///< PORT values are 32-bit
#define USE_FAST_PINIO ///< Use direct PORT register access
#define HAS_PORT_SET_CLR ///< PORTs have set & clear registers
#elif defined(CORE_TEENSY)
// PJRC Teensy 4.x
#if defined(__IMXRT1052__) || defined(__IMXRT1062__) // Teensy 4.x
typedef uint32_t ADAGFX_PORT_t; ///< PORT values are 32-bit
// PJRC Teensy 3.x
#else
typedef uint8_t ADAGFX_PORT_t; ///< PORT values are 8-bit
#endif
#define USE_FAST_PINIO ///< Use direct PORT register access
#define HAS_PORT_SET_CLR ///< PORTs have set & clear registers
#else
// Arduino Due?
typedef uint32_t ADAGFX_PORT_t; ///< PORT values are 32-bit
// USE_FAST_PINIO not available here (yet)...Due has a totally different
// GPIO register set and will require some changes elsewhere (e.g. in
// constructors especially).
#endif
#else // !ARM
// Probably ESP8266 or ESP32. USE_FAST_PINIO is not available here (yet)
// but don't worry about it too much...the digitalWrite() implementation
// on these platforms is reasonably efficient and already RAM-resident,
// only gotcha then is no parallel connection support for now.
typedef uint32_t ADAGFX_PORT_t; ///< PORT values are 32-bit
#endif // end !ARM
typedef volatile ADAGFX_PORT_t* PORTreg_t; ///< PORT register type
#if defined(__AVR__)
#define DEFAULT_SPI_FREQ 8000000L ///< Hardware SPI default speed
#else
#define DEFAULT_SPI_FREQ 16000000L ///< Hardware SPI default speed
#endif
#if defined(ADAFRUIT_PYPORTAL) || defined(ADAFRUIT_PYBADGE_M4_EXPRESS) || defined(ADAFRUIT_PYGAMER_M4_EXPRESS)
#define USE_SPI_DMA ///< Auto DMA if using PyPortal
#else
//#define USE_SPI_DMA ///< If set, use DMA if available
#endif
// Another "oops" name -- this now also handles parallel DMA.
// If DMA is enabled, Arduino sketch MUST #include <Adafruit_ZeroDMA.h>
// Estimated RAM usage:
// 4 bytes/pixel on display major axis + 8 bytes/pixel on minor axis,
// e.g. 320x240 pixels = 320 * 4 + 240 * 8 = 3,200 bytes.
#if !defined(ARDUINO_ARCH_SAMD)
#undef USE_SPI_DMA ///< DMA currently for SAMD chips only
#endif
#if defined(USE_SPI_DMA)
#pragma message ("GFX DMA IS ENABLED. HIGHLY EXPERIMENTAL.")
#include <Adafruit_ZeroDMA.h>
#endif
// This is kind of a kludge. Needed a way to disambiguate the software SPI
// and parallel constructors via their argument lists. Originally tried a
// bool as the first argument to the parallel constructor (specifying 8-bit
// vs 16-bit interface) but the compiler regards this as equivalent to an
// integer and thus still ambiguous. SO...the parallel constructor requires
// an enumerated type as the first argument: tft8 (for 8-bit parallel) or
// tft16 (for 16-bit)...even though 16-bit isn't fully implemented or tested
// and might never be, still needed that disambiguation from soft SPI.
enum tftBusWidth { tft8bitbus, tft16bitbus }; ///< For first arg to parallel constructor
// CLASS DEFINITION --------------------------------------------------------
/*!
@brief Adafruit_SPITFT is an intermediary class between Adafruit_GFX
and various hardware-specific subclasses for different displays.
It handles certain operations that are common to a range of
displays (address window, area fills, etc.). Originally these were
all color TFT displays interfaced via SPI, but it's since expanded
to include color OLEDs and parallel-interfaced TFTs. THE NAME HAS
BEEN KEPT TO AVOID BREAKING A LOT OF SUBCLASSES AND EXAMPLE CODE.
Many of the class member functions similarly live on with names
that don't necessarily accurately describe what they're doing,
again to avoid breaking a lot of other code. If in doubt, read
the comments.
*/
class Adafruit_SPITFT : public Renderer {
public:
// CONSTRUCTORS --------------------------------------------------------
// Software SPI constructor: expects width & height (at default rotation
// setting 0), 4 signal pins (cs, dc, mosi, sclk), 2 optional pins
// (reset, miso). cs argument is required but can be -1 if unused --
// rather than moving it to the optional arguments, it was done this way
// to avoid breaking existing code (-1 option was a later addition).
Adafruit_SPITFT(uint16_t w, uint16_t h,
int8_t cs, int8_t dc, int8_t mosi, int8_t sck,
int8_t rst = -1, int8_t miso = -1);
// Hardware SPI constructor using the default SPI port: expects width &
// height (at default rotation setting 0), 2 signal pins (cs, dc),
// optional reset pin. cs is required but can be -1 if unused -- rather
// than moving it to the optional arguments, it was done this way to
// avoid breaking existing code (-1 option was a later addition).
Adafruit_SPITFT(uint16_t w, uint16_t h,
int8_t cs, int8_t dc, int8_t rst = -1);
#if !defined(ESP8266) // See notes in .cpp
// Hardware SPI constructor using an arbitrary SPI peripheral: expects
// width & height (rotation 0), SPIClass pointer, 2 signal pins (cs, dc)
// and optional reset pin. cs is required but can be -1 if unused.
Adafruit_SPITFT(uint16_t w, uint16_t h, SPIClass *spiClass,
int8_t cs, int8_t dc, int8_t rst = -1);
#endif // end !ESP8266
// Parallel constructor: expects width & height (rotation 0), flag
// indicating whether 16-bit (true) or 8-bit (false) interface, 3 signal
// pins (d0, wr, dc), 3 optional pins (cs, rst, rd). 16-bit parallel
// isn't even fully implemented but the 'wide' flag was added as a
// required argument to avoid ambiguity with other constructors.
Adafruit_SPITFT(uint16_t w, uint16_t h, tftBusWidth busWidth,
int8_t d0, int8_t wr, int8_t dc,
int8_t cs = -1, int8_t rst = -1, int8_t rd = -1);
// CLASS MEMBER FUNCTIONS ----------------------------------------------
// These first two functions MUST be declared by subclasses:
/*!
@brief Display-specific initialization function.
@param freq SPI frequency, in hz (or 0 for default or unused).
*/
virtual void begin(uint32_t freq) = 0;
/*!
@brief Set up the specific display hardware's "address window"
for subsequent pixel-pushing operations.
@param x Leftmost pixel of area to be drawn (MUST be within
display bounds at current rotation setting).
@param y Topmost pixel of area to be drawn (MUST be within
display bounds at current rotation setting).
@param w Width of area to be drawn, in pixels (MUST be >0 and,
added to x, within display bounds at current rotation).
@param h Height of area to be drawn, in pixels (MUST be >0 and,
added to x, within display bounds at current rotation).
*/
virtual void setAddrWindow(
uint16_t x, uint16_t y, uint16_t w, uint16_t h) = 0;
// Remaining functions do not need to be declared in subclasses
// unless they wish to provide hardware-specific optimizations.
// Brief comments here...documented more thoroughly in .cpp file.
// Subclass' begin() function invokes this to initialize hardware.
// freq=0 to use default SPI speed. spiMode must be one of the SPI_MODEn
// values defined in SPI.h, which are NOT the same as 0 for SPI_MODE0,
// 1 for SPI_MODE1, etc...use ONLY the SPI_MODEn defines! Only!
// Name is outdated (interface may be parallel) but for compatibility:
void initSPI(uint32_t freq = 0, uint8_t spiMode = SPI_MODE0);
// Chip select and/or hardware SPI transaction start as needed:
void startWrite(void);
// Chip deselect and/or hardware SPI transaction end as needed:
void endWrite(void);
void sendCommand(uint8_t commandByte, uint8_t *dataBytes = NULL, uint8_t numDataBytes = 0);
void sendCommand(uint8_t commandByte, const uint8_t *dataBytes, uint8_t numDataBytes);
uint8_t readcommand8(uint8_t commandByte, uint8_t index = 0);
// These functions require a chip-select and/or SPI transaction
// around them. Higher-level graphics primitives might start a
// single transaction and then make multiple calls to these functions
// (e.g. circle or text rendering might make repeated lines or rects)
// before ending the transaction. It's more efficient than starting a
// transaction every time.
void writePixel(int16_t x, int16_t y, uint16_t color);
void writePixels(uint16_t *colors, uint32_t len,
bool block=true, bool bigEndian=false);
void writeColor(uint16_t color, uint32_t len);
void writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h,
uint16_t color);
void writeFastHLine(int16_t x, int16_t y, int16_t w,
uint16_t color);
void writeFastVLine(int16_t x, int16_t y, int16_t h,
uint16_t color);
// This is a new function, similar to writeFillRect() except that
// all arguments MUST be onscreen, sorted and clipped. If higher-level
// primitives can handle their own sorting/clipping, it avoids repeating
// such operations in the low-level code, making it potentially faster.
// CALLING THIS WITH UNCLIPPED OR NEGATIVE VALUES COULD BE DISASTROUS.
inline void writeFillRectPreclipped(int16_t x, int16_t y,
int16_t w, int16_t h, uint16_t color);
// Another new function, companion to the new non-blocking
// writePixels() variant.
void dmaWait(void);
// These functions are similar to the 'write' functions above, but with
// a chip-select and/or SPI transaction built-in. They're typically used
// solo -- that is, as graphics primitives in themselves, not invoked by
// higher-level primitives (which should use the functions above).
void drawPixel(int16_t x, int16_t y, uint16_t color);
void fillRect(int16_t x, int16_t y, int16_t w, int16_t h,
uint16_t color);
void drawFastHLine(int16_t x, int16_t y, int16_t w,
uint16_t color);
void drawFastVLine(int16_t x, int16_t y, int16_t h,
uint16_t color);
// A single-pixel push encapsulated in a transaction. I don't think
// this is used anymore (BMP demos might've used it?) but is provided
// for backward compatibility, consider it deprecated:
void pushColor(uint16_t color);
using Adafruit_GFX::drawRGBBitmap; // Check base class first
void drawRGBBitmap(int16_t x, int16_t y,
uint16_t *pcolors, int16_t w, int16_t h);
void invertDisplay(bool i);
uint16_t color565(uint8_t r, uint8_t g, uint8_t b);
// Despite parallel additions, function names kept for compatibility:
void spiWrite(uint8_t b); // Write single byte as DATA
void writeCommand(uint8_t cmd); // Write single byte as COMMAND
uint8_t spiRead(void); // Read single byte of data
// Most of these low-level functions were formerly macros in
// Adafruit_SPITFT_Macros.h. Some have been made into inline functions
// to avoid macro mishaps. Despite the addition of code for a parallel
// display interface, the names have been kept for backward
// compatibility (some subclasses may be invoking these):
void SPI_WRITE16(uint16_t w); // Not inline
void SPI_WRITE32(uint32_t l); // Not inline
// Old code had both a spiWrite16() function and SPI_WRITE16 macro
// in addition to the SPI_WRITE32 macro. The latter two have been
// made into functions here, and spiWrite16() removed (use SPI_WRITE16()
// instead). It looks like most subclasses had gotten comfortable with
// SPI_WRITE16 and SPI_WRITE32 anyway so those names were kept rather
// than the less-obnoxious camelcase variants, oh well.
// Placing these functions entirely in the class definition inlines
// them implicitly them while allowing their use in other code:
/*!
@brief Set the chip-select line HIGH. Does NOT check whether CS pin
is set (>=0), that should be handled in calling function.
Despite function name, this is used even if the display
connection is parallel.
*/
void SPI_CS_HIGH(void) {
#if defined(USE_FAST_PINIO)
#if defined(HAS_PORT_SET_CLR)
#if defined(KINETISK)
*csPortSet = 1;
#else // !KINETISK
*csPortSet = csPinMask;
#endif // end !KINETISK
#else // !HAS_PORT_SET_CLR
*csPort |= csPinMaskSet;
#endif // end !HAS_PORT_SET_CLR
#else // !USE_FAST_PINIO
digitalWrite(_cs, HIGH);
#endif // end !USE_FAST_PINIO
}
/*!
@brief Set the chip-select line LOW. Does NOT check whether CS pin
is set (>=0), that should be handled in calling function.
Despite function name, this is used even if the display
connection is parallel.
*/
void SPI_CS_LOW(void) {
#if defined(USE_FAST_PINIO)
#if defined(HAS_PORT_SET_CLR)
#if defined(KINETISK)
*csPortClr = 1;
#else // !KINETISK
*csPortClr = csPinMask;
#endif // end !KINETISK
#else // !HAS_PORT_SET_CLR
*csPort &= csPinMaskClr;
#endif // end !HAS_PORT_SET_CLR
#else // !USE_FAST_PINIO
digitalWrite(_cs, LOW);
#endif // end !USE_FAST_PINIO
}
/*!
@brief Set the data/command line HIGH (data mode).
*/
void SPI_DC_HIGH(void) {
#if defined(USE_FAST_PINIO)
#if defined(HAS_PORT_SET_CLR)
#if defined(KINETISK)
*dcPortSet = 1;
#else // !KINETISK
*dcPortSet = dcPinMask;
#endif // end !KINETISK
#else // !HAS_PORT_SET_CLR
*dcPort |= dcPinMaskSet;
#endif // end !HAS_PORT_SET_CLR
#else // !USE_FAST_PINIO
digitalWrite(_dc, HIGH);
#endif // end !USE_FAST_PINIO
}
/*!
@brief Set the data/command line LOW (command mode).
*/
void SPI_DC_LOW(void) {
#if defined(USE_FAST_PINIO)
#if defined(HAS_PORT_SET_CLR)
#if defined(KINETISK)
*dcPortClr = 1;
#else // !KINETISK
*dcPortClr = dcPinMask;
#endif // end !KINETISK
#else // !HAS_PORT_SET_CLR
*dcPort &= dcPinMaskClr;
#endif // end !HAS_PORT_SET_CLR
#else // !USE_FAST_PINIO
digitalWrite(_dc, LOW);
#endif // end !USE_FAST_PINIO
}
protected:
// A few more low-level member functions -- some may have previously
// been macros. Shouldn't have a need to access these externally, so
// they've been moved to the protected section. Additionally, they're
// declared inline here and the code is in the .cpp file, since outside
// code doesn't need to see these.
inline void SPI_MOSI_HIGH(void);
inline void SPI_MOSI_LOW(void);
inline void SPI_SCK_HIGH(void);
inline void SPI_SCK_LOW(void);
inline bool SPI_MISO_READ(void);
inline void SPI_BEGIN_TRANSACTION(void);
inline void SPI_END_TRANSACTION(void);
inline void TFT_WR_STROBE(void); // Parallel interface write strobe
inline void TFT_RD_HIGH(void); // Parallel interface read high
inline void TFT_RD_LOW(void); // Parallel interface read low
// CLASS INSTANCE VARIABLES --------------------------------------------
// Here be dragons! There's a big union of three structures here --
// one each for hardware SPI, software (bitbang) SPI, and parallel
// interfaces. This is to save some memory, since a display's connection
// will be only one of these. The order of some things is a little weird
// in an attempt to get values to align and pack better in RAM.
#if defined(USE_FAST_PINIO)
#if defined(HAS_PORT_SET_CLR)
PORTreg_t csPortSet; ///< PORT register for chip select SET
PORTreg_t csPortClr; ///< PORT register for chip select CLEAR
PORTreg_t dcPortSet; ///< PORT register for data/command SET
PORTreg_t dcPortClr; ///< PORT register for data/command CLEAR
#else // !HAS_PORT_SET_CLR
PORTreg_t csPort; ///< PORT register for chip select
PORTreg_t dcPort; ///< PORT register for data/command
#endif // end HAS_PORT_SET_CLR
#endif // end USE_FAST_PINIO
#if defined(__cplusplus) && (__cplusplus >= 201100)
union {
#endif
struct { // Values specific to HARDWARE SPI:
SPIClass *_spi; ///< SPI class pointer
#if defined(SPI_HAS_TRANSACTION)
SPISettings settings; ///< SPI transaction settings
#else
uint32_t _freq; ///< SPI bitrate (if no SPI transactions)
#endif
uint32_t _mode; ///< SPI data mode (transactions or no)
} hwspi; ///< Hardware SPI values
struct { // Values specific to SOFTWARE SPI:
#if defined(USE_FAST_PINIO)
PORTreg_t misoPort; ///< PORT (PIN) register for MISO
#if defined(HAS_PORT_SET_CLR)
PORTreg_t mosiPortSet; ///< PORT register for MOSI SET
PORTreg_t mosiPortClr; ///< PORT register for MOSI CLEAR
PORTreg_t sckPortSet; ///< PORT register for SCK SET
PORTreg_t sckPortClr; ///< PORT register for SCK CLEAR
#if !defined(KINETISK)
ADAGFX_PORT_t mosiPinMask; ///< Bitmask for MOSI
ADAGFX_PORT_t sckPinMask; ///< Bitmask for SCK
#endif // end !KINETISK
#else // !HAS_PORT_SET_CLR
PORTreg_t mosiPort; ///< PORT register for MOSI
PORTreg_t sckPort; ///< PORT register for SCK
ADAGFX_PORT_t mosiPinMaskSet; ///< Bitmask for MOSI SET (OR)
ADAGFX_PORT_t mosiPinMaskClr; ///< Bitmask for MOSI CLEAR (AND)
ADAGFX_PORT_t sckPinMaskSet; ///< Bitmask for SCK SET (OR bitmask)
ADAGFX_PORT_t sckPinMaskClr; ///< Bitmask for SCK CLEAR (AND)
#endif // end HAS_PORT_SET_CLR
#if !defined(KINETISK)
ADAGFX_PORT_t misoPinMask; ///< Bitmask for MISO
#endif // end !KINETISK
#endif // end USE_FAST_PINIO
int8_t _mosi; ///< MOSI pin #
int8_t _miso; ///< MISO pin #
int8_t _sck; ///< SCK pin #
} swspi; ///< Software SPI values
struct { // Values specific to 8-bit parallel:
#if defined(USE_FAST_PINIO)
#if defined(__IMXRT1052__) || defined(__IMXRT1062__) // Teensy 4.x
volatile uint32_t *writePort; ///< PORT register for DATA WRITE
volatile uint32_t *readPort; ///< PORT (PIN) register for DATA READ
#else
volatile uint8_t *writePort; ///< PORT register for DATA WRITE
volatile uint8_t *readPort; ///< PORT (PIN) register for DATA READ
#endif
#if defined(HAS_PORT_SET_CLR)
// Port direction register pointers are always 8-bit regardless of
// PORTreg_t -- even if 32-bit port, we modify a byte-aligned 8 bits.
#if defined(__IMXRT1052__) || defined(__IMXRT1062__) // Teensy 4.x
volatile uint32_t *dirSet; ///< PORT byte data direction SET
volatile uint32_t *dirClr; ///< PORT byte data direction CLEAR
#else
volatile uint8_t *dirSet; ///< PORT byte data direction SET
volatile uint8_t *dirClr; ///< PORT byte data direction CLEAR
#endif
PORTreg_t wrPortSet; ///< PORT register for write strobe SET
PORTreg_t wrPortClr; ///< PORT register for write strobe CLEAR
PORTreg_t rdPortSet; ///< PORT register for read strobe SET
PORTreg_t rdPortClr; ///< PORT register for read strobe CLEAR
#if !defined(KINETISK)
ADAGFX_PORT_t wrPinMask; ///< Bitmask for write strobe
#endif // end !KINETISK
ADAGFX_PORT_t rdPinMask; ///< Bitmask for read strobe
#else // !HAS_PORT_SET_CLR
// Port direction register pointer is always 8-bit regardless of
// PORTreg_t -- even if 32-bit port, we modify a byte-aligned 8 bits.
volatile uint8_t *portDir; ///< PORT direction register
PORTreg_t wrPort; ///< PORT register for write strobe
PORTreg_t rdPort; ///< PORT register for read strobe
ADAGFX_PORT_t wrPinMaskSet; ///< Bitmask for write strobe SET (OR)
ADAGFX_PORT_t wrPinMaskClr; ///< Bitmask for write strobe CLEAR (AND)
ADAGFX_PORT_t rdPinMaskSet; ///< Bitmask for read strobe SET (OR)
ADAGFX_PORT_t rdPinMaskClr; ///< Bitmask for read strobe CLEAR (AND)
#endif // end HAS_PORT_SET_CLR
#endif // end USE_FAST_PINIO
int8_t _d0; ///< Data pin 0 #
int8_t _wr; ///< Write strobe pin #
int8_t _rd; ///< Read strobe pin # (or -1)
bool wide = 0; ///< If true, is 16-bit interface
} tft8; ///< Parallel interface settings
#if defined(__cplusplus) && (__cplusplus >= 201100)
}; ///< Only one interface is active
#endif
#if defined(USE_SPI_DMA) // Used by hardware SPI and tft8
Adafruit_ZeroDMA dma; ///< DMA instance
DmacDescriptor *dptr = NULL; ///< 1st descriptor
DmacDescriptor *descriptor = NULL; ///< Allocated descriptor list
uint16_t *pixelBuf[2]; ///< Working buffers
uint16_t maxFillLen; ///< Max pixels per DMA xfer
uint16_t lastFillColor = 0; ///< Last color used w/fill
uint32_t lastFillLen = 0; ///< # of pixels w/last fill
uint8_t onePixelBuf; ///< For hi==lo fill
#endif
#if defined(USE_FAST_PINIO)
#if defined(HAS_PORT_SET_CLR)
#if !defined(KINETISK)
ADAGFX_PORT_t csPinMask; ///< Bitmask for chip select
ADAGFX_PORT_t dcPinMask; ///< Bitmask for data/command
#endif // end !KINETISK
#else // !HAS_PORT_SET_CLR
ADAGFX_PORT_t csPinMaskSet; ///< Bitmask for chip select SET (OR)
ADAGFX_PORT_t csPinMaskClr; ///< Bitmask for chip select CLEAR (AND)
ADAGFX_PORT_t dcPinMaskSet; ///< Bitmask for data/command SET (OR)
ADAGFX_PORT_t dcPinMaskClr; ///< Bitmask for data/command CLEAR (AND)
#endif // end HAS_PORT_SET_CLR
#endif // end USE_FAST_PINIO
uint8_t connection; ///< TFT_HARD_SPI, TFT_SOFT_SPI, etc.
int8_t _rst; ///< Reset pin # (or -1)
int8_t _cs; ///< Chip select pin # (or -1)
int8_t _dc; ///< Data/command pin #
int16_t _xstart = 0; ///< Internal framebuffer X offset
int16_t _ystart = 0; ///< Internal framebuffer Y offset
uint8_t invertOnCommand = 0; ///< Command to enable invert mode
uint8_t invertOffCommand = 0; ///< Command to disable invert mode
uint32_t _freq = 0; ///< Dummy var to keep subclasses happy
};
#endif // end __AVR_ATtiny85__
#endif // end _ADAFRUIT_SPITFT_H_

View File

@ -0,0 +1,190 @@
/*!
* @file Adafruit_SSD1331.cpp
*
* @mainpage Adafruit SSD1331 Arduino Library
*
* @section intro_sec Introduction
*
* This is a library for the 0.96" 16-bit Color OLED with SSD1331 driver chip
*
* Pick one up today in the adafruit shop!
* ------> http://www.adafruit.com/products/684
*
* These displays use SPI to communicate, 4 or 5 pins are required to
* interface
* Adafruit invests time and resources providing this open source code,
* please support Adafruit and open-source hardware by purchasing
* products from Adafruit!
*
* @section author Author
*
* Written by Limor Fried/Ladyada for Adafruit Industries.
*
* @section license License
*
* BSD license, all text above must be included in any redistribution
*/
#include "Adafruit_SSD1331.h"
#include "pins_arduino.h"
#include "wiring_private.h"
/***********************************/
/*!
@brief SPI displays set an address window rectangle for blitting pixels
@param x Top left corner x coordinate
@param y Top left corner x coordinate
@param w Width of window
@param h Height of window
*/
void Adafruit_SSD1331::setAddrWindow(uint16_t x, uint16_t y, uint16_t w,
uint16_t h) {
uint8_t x1 = x;
uint8_t y1 = y;
if (x1 > 95)
x1 = 95;
if (y1 > 63)
y1 = 63;
uint8_t x2 = (x + w - 1);
uint8_t y2 = (y + h - 1);
if (x2 > 95)
x2 = 95;
if (y2 > 63)
y2 = 63;
if (x1 > x2) {
uint8_t t = x2;
x2 = x1;
x1 = t;
}
if (y1 > y2) {
uint8_t t = y2;
y2 = y1;
y1 = t;
}
sendCommand(0x15); // Column addr set
sendCommand(x1);
sendCommand(x2);
sendCommand(0x75); // Column addr set
sendCommand(y1);
sendCommand(y2);
startWrite();
}
/**************************************************************************/
/*!
@brief Initialize SSD1331 chip
Connects to the SSD1331 over SPI and sends initialization procedure commands
@param freq Desired SPI clock frequency
*/
/**************************************************************************/
void Adafruit_SSD1331::begin(uint32_t freq) {
initSPI(freq);
// Initialization Sequence
sendCommand(SSD1331_CMD_DISPLAYOFF); // 0xAE
sendCommand(SSD1331_CMD_SETREMAP); // 0xA0
#if defined SSD1331_COLORORDER_RGB
sendCommand(0x72); // RGB Color
#else
sendCommand(0x76); // BGR Color
#endif
sendCommand(SSD1331_CMD_STARTLINE); // 0xA1
sendCommand(0x0);
sendCommand(SSD1331_CMD_DISPLAYOFFSET); // 0xA2
sendCommand(0x0);
sendCommand(SSD1331_CMD_NORMALDISPLAY); // 0xA4
sendCommand(SSD1331_CMD_SETMULTIPLEX); // 0xA8
sendCommand(0x3F); // 0x3F 1/64 duty
sendCommand(SSD1331_CMD_SETMASTER); // 0xAD
sendCommand(0x8E);
sendCommand(SSD1331_CMD_POWERMODE); // 0xB0
sendCommand(0x0B);
sendCommand(SSD1331_CMD_PRECHARGE); // 0xB1
sendCommand(0x31);
sendCommand(SSD1331_CMD_CLOCKDIV); // 0xB3
sendCommand(0xF0); // 7:4 = Oscillator Frequency, 3:0 = CLK Div Ratio
// (A[3:0]+1 = 1..16)
sendCommand(SSD1331_CMD_PRECHARGEA); // 0x8A
sendCommand(0x64);
sendCommand(SSD1331_CMD_PRECHARGEB); // 0x8B
sendCommand(0x78);
sendCommand(SSD1331_CMD_PRECHARGEC); // 0x8C
sendCommand(0x64);
sendCommand(SSD1331_CMD_PRECHARGELEVEL); // 0xBB
sendCommand(0x3A);
sendCommand(SSD1331_CMD_VCOMH); // 0xBE
sendCommand(0x3E);
sendCommand(SSD1331_CMD_MASTERCURRENT); // 0x87
sendCommand(0x06);
sendCommand(SSD1331_CMD_CONTRASTA); // 0x81
sendCommand(0x91);
sendCommand(SSD1331_CMD_CONTRASTB); // 0x82
sendCommand(0x50);
sendCommand(SSD1331_CMD_CONTRASTC); // 0x83
sendCommand(0x7D);
sendCommand(SSD1331_CMD_DISPLAYON); //--turn on oled panel
_width = TFTWIDTH;
_height = TFTHEIGHT;
}
/**************************************************************************/
/*!
@brief Instantiate Adafruit SSD1331 driver with software SPI
@param cs Chip select pin #
@param dc Data/Command pin #
@param mosi SPI MOSI pin #
@param sclk SPI Clock pin #
@param rst Reset pin # (optional, pass -1 if unused)
*/
/**************************************************************************/
Adafruit_SSD1331::Adafruit_SSD1331(int8_t cs, int8_t dc, int8_t mosi,
int8_t sclk, int8_t rst)
: Adafruit_SPITFT(TFTWIDTH, TFTHEIGHT, cs, dc, mosi, sclk, rst, -1) {}
/**************************************************************************/
/*!
@brief Instantiate Adafruit SSD1331 driver with hardware SPI
@param cs Chip select pin #
@param dc Data/Command pin #
@param rst Reset pin # (optional, pass -1 if unused)
*/
/**************************************************************************/
Adafruit_SSD1331::Adafruit_SSD1331(int8_t cs, int8_t dc, int8_t rst)
: Adafruit_SPITFT(TFTWIDTH, TFTHEIGHT, cs, dc, rst) {}
/**************************************************************************/
/*!
@brief Instantiate Adafruit SSD1331 driver with hardware SPI
@param spi Pointer to an existing SPIClass instance (e.g. &SPI, the
microcontroller's primary SPI bus).
@param cs Chip select pin #
@param dc Data/Command pin #
@param rst Reset pin # (optional, pass -1 if unused)
*/
/**************************************************************************/
Adafruit_SSD1331::Adafruit_SSD1331(SPIClass *spi, int8_t cs, int8_t dc,
int8_t rst)
:
#if defined(ESP8266)
Adafruit_SPITFT(TFTWIDTH, TFTWIDTH, cs, dc, rst) {
#else
Adafruit_SPITFT(TFTWIDTH, TFTWIDTH, spi, cs, dc, rst) {
#endif
}
/**************************************************************************/
/*!
@brief Change whether display is on or off
@param enable True if you want the display ON, false OFF
*/
/**************************************************************************/
void Adafruit_SSD1331::enableDisplay(boolean enable) {
sendCommand(enable ? SSD1331_CMD_DISPLAYON : SSD1331_CMD_DISPLAYOFF);
}

View File

@ -0,0 +1,76 @@
/*!
* @file Adafruit_SSD1331.h
*/
#include "Arduino.h"
#include <Adafruit_GFX.h>
// Tasmota change: use custom version of Adafruit_SPITFT which extends Renderer instead of Adafruit_GFX
#include <Adafruit_SPITFT_Renderer.h>
#include <Adafruit_SPITFT_Macros.h>
#include <SPI.h>
/*!
* @brief Select one of these defines to set the pixel color order
*/
#define SSD1331_COLORORDER_RGB
// #define SSD1331_COLORORDER_BGR
#if defined SSD1331_COLORORDER_RGB && defined SSD1331_COLORORDER_BGR
#error "RGB and BGR can not both be defined for SSD1331_COLORODER."
#endif
// Timing Delays
#define SSD1331_DELAYS_HWFILL (3) //!< Fill delay
#define SSD1331_DELAYS_HWLINE (1) //!< Line delay
// SSD1331 Commands
#define SSD1331_CMD_DRAWLINE 0x21 //!< Draw line
#define SSD1331_CMD_DRAWRECT 0x22 //!< Draw rectangle
#define SSD1331_CMD_FILL 0x26 //!< Fill enable/disable
#define SSD1331_CMD_SETCOLUMN 0x15 //!< Set column address
#define SSD1331_CMD_SETROW 0x75 //!< Set row adress
#define SSD1331_CMD_CONTRASTA 0x81 //!< Set contrast for color A
#define SSD1331_CMD_CONTRASTB 0x82 //!< Set contrast for color B
#define SSD1331_CMD_CONTRASTC 0x83 //!< Set contrast for color C
#define SSD1331_CMD_MASTERCURRENT 0x87 //!< Master current control
#define SSD1331_CMD_SETREMAP 0xA0 //!< Set re-map & data format
#define SSD1331_CMD_STARTLINE 0xA1 //!< Set display start line
#define SSD1331_CMD_DISPLAYOFFSET 0xA2 //!< Set display offset
#define SSD1331_CMD_NORMALDISPLAY 0xA4 //!< Set display to normal mode
#define SSD1331_CMD_DISPLAYALLON 0xA5 //!< Set entire display ON
#define SSD1331_CMD_DISPLAYALLOFF 0xA6 //!< Set entire display OFF
#define SSD1331_CMD_INVERTDISPLAY 0xA7 //!< Invert display
#define SSD1331_CMD_SETMULTIPLEX 0xA8 //!< Set multiplex ratio
#define SSD1331_CMD_SETMASTER 0xAD //!< Set master configuration
#define SSD1331_CMD_DISPLAYOFF 0xAE //!< Display OFF (sleep mode)
#define SSD1331_CMD_DISPLAYON 0xAF //!< Normal Brightness Display ON
#define SSD1331_CMD_POWERMODE 0xB0 //!< Power save mode
#define SSD1331_CMD_PRECHARGE 0xB1 //!< Phase 1 and 2 period adjustment
#define SSD1331_CMD_CLOCKDIV \
0xB3 //!< Set display clock divide ratio/oscillator frequency
#define SSD1331_CMD_PRECHARGEA 0x8A //!< Set second pre-charge speed for color A
#define SSD1331_CMD_PRECHARGEB 0x8B //!< Set second pre-charge speed for color B
#define SSD1331_CMD_PRECHARGEC 0x8C //!< Set second pre-charge speed for color C
#define SSD1331_CMD_PRECHARGELEVEL 0xBB //!< Set pre-charge voltage
#define SSD1331_CMD_VCOMH 0xBE //!< Set Vcomh voltge
/// Class to manage hardware interface with SSD1331 chipset
class Adafruit_SSD1331 : public Adafruit_SPITFT {
public:
Adafruit_SSD1331(int8_t cs, int8_t dc, int8_t mosi, int8_t sclk, int8_t rst);
Adafruit_SSD1331(int8_t cs, int8_t dc, int8_t rst);
// 3-4 args using hardware SPI (must specify peripheral) (reset optional)
Adafruit_SSD1331(SPIClass *spi, int8_t cs, int8_t dc, int8_t rst = -1);
// commands
void begin(uint32_t begin = 8000000);
void setAddrWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h);
void enableDisplay(boolean enable);
static const int16_t TFTWIDTH = 96; ///< The width of the display
static const int16_t TFTHEIGHT = 64; ///< The height of the display
private:
};

View File

@ -0,0 +1,24 @@
# Adafruit SSD1331 Arduino Library [![Build Status](https://github.com/adafruit/Adafruit-SSD1331-OLED-Driver-Library-for-Arduino/workflows/Arduino%20Library%20CI/badge.svg)](https://github.com/adafruit/Adafruit-SSD1331-OLED-Driver-Library-for-Arduino/actions)[![Documentation](https://github.com/adafruit/ci-arduino/blob/master/assets/doxygen_badge.svg)](http://adafruit.github.io/Adafruit-SSD1331-OLED-Driver-Library-for-Arduino/html/index.html)
This is a library for the 0.96" 16-bit Color OLED with SSD1331 driver chip
Pick one up today in the adafruit shop!
------> http://www.adafruit.com/products/684
These displays use SPI to communicate, 4 or 5 pins are required to
interface
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.
BSD license, check license.txt for more information
All text above must be included in any redistribution
To download. click the DOWNLOADS button in the top right corner, rename the uncompressed folder Adafruit_SSD1131. Check that the Adafruit_SSD1331 folder contains Adafruit_SSD1331.cpp and Adafruit_SSD1331.h
Place the Adafruit_SSD1331 library folder your <arduinosketchfolder>/libraries/ folder. You may need to create the libraries subfolder if its your first library. Restart the IDE.
You will also have to download the Adafruit GFX Graphics core which does all the circles, text, rectangles, etc. You can get it from
https://github.com/adafruit/Adafruit-GFX-Library
and download/install that library as well

View File

@ -0,0 +1,10 @@
name=Adafruit SSD1331 OLED Driver Library for Arduino
version=1.2.0
author=Adafruit
maintainer=Adafruit <info@adafruit.com>
sentence=For 0.96" OLEDs in the Adafruit shop
paragraph=For 0.96" OLEDs in the Adafruit shop
category=Display
url=https://github.com/adafruit/Adafruit-SSD1331-OLED-Driver-Library-for-Arduino
architectures=*
depends=Adafruit GFX Library

View File

@ -0,0 +1,26 @@
Software License Agreement (BSD License)
Copyright (c) 2012, Adafruit Industries
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

192
tasmota/xdsp_14_SSD1331.ino Normal file
View File

@ -0,0 +1,192 @@
/*
xdsp_14_SSD1331.ino - Display SSD1331 support for Tasmota
Copyright (C) 2020 Jeroen Vermeulen, Gerhard Mutz and Theo Arends
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef USE_SPI
#ifdef USE_DISPLAY
#ifdef USE_DISPLAY_SSD1331
#define XDSP_14 14
#define COLORED 1
#define UNCOLORED 0
#define USE_TINY_FONT
#define SSD1331_BLACK 0x0000 // 0, 0, 0
#define SSD1331_WHITE 0xFFFF // 255, 255, 255
#define SSD1331_RED 0xF800 // 255, 0, 0
#define SSD1331_BLUE 0x001F // 0, 0, 255
#include <Adafruit_SSD1331.h>
#include <SPI.h>
extern uint8_t *buffer;
extern uint8_t color_type;
Adafruit_SSD1331 *ssd1331;
/*********************************************************************************************/
void SSD1331_InitDriver() {
if (!Settings.display_model) {
Settings.display_model = XDSP_14;
}
if (XDSP_14 == Settings.display_model) {
if (Settings.display_width != Adafruit_SSD1331::TFTWIDTH) {
Settings.display_width = Adafruit_SSD1331::TFTWIDTH;
}
if (Settings.display_height != Adafruit_SSD1331::TFTHEIGHT) {
Settings.display_height = Adafruit_SSD1331::TFTHEIGHT;
}
buffer=0;
// default colors
fg_color = SSD1331_WHITE;
bg_color = SSD1331_BLACK;
// init renderer
if (PinUsed(GPIO_SSPI_CS) && PinUsed(GPIO_SSPI_DC) && PinUsed(GPIO_SSPI_MOSI) && PinUsed(GPIO_SSPI_SCLK) && PinUsed(GPIO_OLED_RESET)) {
ssd1331 = new Adafruit_SSD1331(Pin(GPIO_SSPI_CS),Pin(GPIO_SSPI_DC),Pin(GPIO_SSPI_MOSI),Pin(GPIO_SSPI_SCLK),Pin(GPIO_OLED_RESET));
} else if (PinUsed(GPIO_SPI_CS) && PinUsed(GPIO_SPI_DC)) {
ssd1331 = new Adafruit_SSD1331(&SPI,Pin(GPIO_SPI_CS),Pin(GPIO_SPI_DC),Pin(GPIO_OLED_RESET));
} else {
return;
}
delay(100);
ssd1331->begin();
renderer = ssd1331;
// Rotation is currently broken, https://github.com/adafruit/Adafruit-SSD1331-OLED-Driver-Library-for-Arduino/issues/26
renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font);
renderer->dim(Settings.display_dimmer);
#ifdef SHOW_SPLASH
// Welcome text
renderer->clearDisplay();
renderer->setTextFont(1);
renderer->DrawStringAt(24, 27, "SSD1331", SSD1331_RED, 0);
delay(1000);
#endif
color_type = COLOR_COLOR;
}
}
#ifdef USE_DISPLAY_MODES1TO5
void SSD1331PrintLog(bool withDateTime)
{
disp_refresh--;
if (!disp_refresh) {
disp_refresh = Settings.display_refresh;
if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); }
char* txt = DisplayLogBuffer('\370');
if (txt != NULL) {
uint8_t last_row = Settings.display_rows -1;
renderer->clearDisplay();
renderer->setCursor(0,0);
if (withDateTime) {
char line[17];
snprintf_P(line, sizeof(line), PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d %02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%04d"), RtcTime.hour, RtcTime.minute, RtcTime.day_of_month, RtcTime.month, RtcTime.year); // [12:34 01-02-2018]
renderer->setTextColor(SSD1331_BLUE);
renderer->println(line);
renderer->setTextColor(fg_color);
last_row--;
}
for (byte i = 0; i < last_row; i++) {
strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols);
renderer->println(disp_screen_buffer[i]);
}
strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols);
DisplayFillScreen(last_row);
AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]);
renderer->println(disp_screen_buffer[last_row]);
renderer->Updateframe();
}
}
}
void SSD1331Time(void)
{
char line[12];
renderer->clearDisplay();
renderer->setCursor(0, 0);
snprintf_P(line, sizeof(line), PSTR(" %02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); // [ 12:34:56 ]
renderer->println(line);
snprintf_P(line, sizeof(line), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%04d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year); // [01-02-2018]
renderer->println(line);
renderer->Updateframe();
}
void SSD1331Refresh(void) // Every second
{
if (Settings.display_mode) { // Mode 0 is User text
switch (Settings.display_mode) {
case 1: // Time
SSD1331Time();
break;
case 2: // Local
case 4: // Mqtt
SSD1331PrintLog(false);
break;
case 3: // Local + Time
case 5: // Mqtt + Time
SSD1331PrintLog(true);
break;
}
}
}
#endif // USE_DISPLAY_MODES1TO5
/*********************************************************************************************/
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
bool Xdsp14(uint8_t function)
{
bool result = false;
if (FUNC_DISPLAY_INIT_DRIVER == function) {
SSD1331_InitDriver();
}
else if (XDSP_14 == Settings.display_model) {
switch (function) {
case FUNC_DISPLAY_MODEL:
result = true;
break;
#ifdef USE_DISPLAY_MODES1TO5
case FUNC_DISPLAY_EVERY_SECOND:
SSD1331Refresh();
break;
#endif // USE_DISPLAY_MODES1TO5
}
}
return result;
}
#endif // USE_DISPLAY_SSD1331
#endif // USE_DISPLAY
#endif // USE_SPI